-
Notifications
You must be signed in to change notification settings - Fork 11.5k
[5.4] Broadcasting Improvements #13653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…iance on cache keys, added rebound driver
if (! $request->hasSession()) { | ||
return; | ||
} | ||
|
||
return $this->app['cache']->get( | ||
'pusher:socket:'.$request->session()->getId() | ||
'realtime:socket:'.$request->session()->getId() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just "broadcast" since that is the related component? broadcast:socket
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, makes sense, ive just updated that now. also of note, ive updated my rebound package to have csrf, and socket remembering functionality, it now works as-is with this setup.
in the last commit ive added an abstract broadcaster to reduce duplication for checks and auth callbacks. however log and basic redis dont/wont need this sort of stuff, for testing etc do you think the |
If you use Skype or Slack would be interested in IMing about this. Send me an e-mail if you don't mind. |
If the abstract class calls a method it should probably exist on that abstract class. |
makes sense, ive added that now, got it returning false as it wont really do anything for drivers that dont support it anyway. |
I already submitted a PR to add auth support to the RedisBroadcaster. #13639 Don't really think there would be breaking changes here since the RedisBroadcaster has only had the |
that was only on the redis broadcaster, didnt cater for log or any other broadcasters that may be added. this extracts the common methods to an abstract class, imagine using log driver when in tests and using the new Broadcast::auth() function in a provider that fails because the driver being used doesnt have the method. |
*/ | ||
public function validAuthenticationResponse($request, $result) | ||
{ | ||
return ['status' => 'success', 'user_id' => $request->user()->id, 'user_info' => $request->user()]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to use this in the BroadcastServericeProvider like in @taylorotwell's demo and allow the developer to choose what data is presented in the auth response. In different situations, the user model may contain information that isn't necessary and you may want to pass along additional data. I believe that is what the $result
variable was intended for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a custom broadcast driver, not the core redis driver. agreed what your saying is true, but this is a custom build package, the default redis broadcaster returns false. i will update this to reflect the same api.
just a quick note, dont think the style checks actually failed, i think github was down when the test was run, should clear up with other commits if needed. |
Just changed the ability for Broadcast::auth to use Like event system. |
…r, to be extended in laravel/laravel
This entire PR needs to have a more thorough description and title of what it actually does. How "Rebound" works, and so on. |
agreed, its gone beyond a small amendment, ill edit the main comment. |
@taylorotwell does the overview comment suffice in detailing the PR well enough now do you think? |
@@ -81,12 +82,20 @@ public function socket($request = null) | |||
return $request->header('X-Socket-Id'); | |||
} | |||
|
|||
if ($request->hasCookie('io')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this cookie sent from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original request sent from engine io includes a set cookie header for that. And in rebound I pass the original request headers to the save socket post. This includes the io header which is the socket I'd.
On 27 May 2016, at 14:37, Taylor Otwell [email protected] wrote:
In src/Illuminate/Broadcasting/BroadcastManager.php:
@@ -81,12 +82,20 @@ public function socket($request = null)
return $request->header('X-Socket-Id');
}
Where is this cookie sent from?if ($request->hasCookie('io')) {
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.
One question I have is why call the PHP Rebound Driver "ReboundDriver" at all. Nothing it does really needs to be specific to Rebound. It functions in a pretty generic way that potentially other libraries could leverage at some time in the future. Though, I'm not sure what to call it. |
Yeah I suppose really it's just the redis driver with the extra methods to authenticate channels. Maybe I should just add the authentication methods to the redis driver and remove this? It wouldn't break any existing redis driver users. Just add the new methods available for authenticating. What do you think? Sent from my iPhone
|
Or maybe add the auth/save methods to the abstract driver so all drivers inherit the methods. But can still overwrite them, like pusher? Thoughts? Sent from my iPhone
|
I agree that it should probably just be part of the Redis driver?
|
PR updated to use default responses unless otherwise provided, removed not needed new driver. Ive added the methods to the abstract driver, not just redis as in the abstract driver they can be used as a "convension" for any client, not just redis. This way any websocket client built for interaction with laravel, can expect to recieve the standard responses unless they need to overwrite them. |
Also updated any reference to |
@leemason one thing I'm a little confused about is it looks like on valid authentication responses you are returning |
Also curious how your implementation is handling the "dont broadcast to the current user" situation? This similar PR handles it: #13639 |
@taylorotwell the valid response returns user_info so that presence channels can populate member info. the auth callbacks "can" just return true yes, but then the "members" info even with pusher will contain no data, i wasnt aware you were using it with just a true response. from the code and reading the pusher api its clear this response should contain user info. how are you populating member info with the pusher api if not via returning data on the callback? The auth callbacks as i understood it should return user info, or false. As for the dont broadcast to current user. This pr doesnt need to anything with it. Its the clients job to handle that data. Pusher looks for the socket property on the event, as does rebound. Its their job to "not" send them the events, the laravel side just sets the socket property in the event. |
ahh i see what you mean, the socket is extracted from the event data in the other pr. do you think thats needed? its really up to you wether we include in the event data, or pull it out seperatley. There are no clients which consume this data yet (except rebound, which can be changed) so the data structure is up to you. Personally i think keeping it as part of the payload has some benefits over extracting it.
|
Talking about that, i think we also need to update the docs on events returning their own data structure. As if they do the socket property wont get sent unless explicitly declared in the broadcastWith() method. |
I do return the user data for pusher but only when authenticating presence channels. When authenticating plain private channels you can just return true or false. Pusher doesn't need any user data for a plain private channel. On Sat, May 28, 2016 at 8:52 AM -0700, "Lee Mason" [email protected] wrote: @taylorotwell the valid response returns user_info so that presence channels can populate member info. the auth callbacks "can" just return true yes, but then the "members" info even with pusher will contain no data, i wasnt aware you were using it with just a true response. from the code and reading the pusher api its clear this response should contain user info. how are you populating member info with the pusher api if not via returning data on the callback? The auth callbacks as i understood it should return user info, or false. As for the dont broadcast to current user. This pr doesnt need to anything with it. Its the clients job to handle that data. Pusher looks for the socket property on the event, as does rebound. Its their job to "not" send them the events, the laravel side just sets the socket property in the event. — |
surely there is no reason to return false at all, as if the result isnt true a 403 http exception is thrown? |
I mean sure you could return the user info regardless. On Sat, May 28, 2016 at 12:30 PM -0700, "Lee Mason" [email protected] wrote: surely there is no reason to return false at all, as if the result isnt true a 403 http exception is thrown? — |
i know user_info is generally for presence channels, but there could be implementations where a private channel "could" use the user info returned, it would depend on the client using the response. its up to the auth callback what gets returned anyway, only allowing it for presence we can do for sure, but isnt it just a limitation that has no benefit, and wouldnt cause any issues keeping it as it is? |
Sure I agree On Sat, May 28, 2016 at 12:37 PM -0700, "Lee Mason" [email protected] wrote: i know user_info is generally for presence channels, but there could be implementations where a private channel "could" use the user info returned, it would depend on the client using the response. its up to the auth callback what gets returned anyway, only allowing it for presence we can do for sure, but isnt it just a limitation that has no benefit, and wouldnt cause any issues keeping it as it is? — |
So can you explain in more detail how you implement the "don't broadcast to current user" stuff? On Sat, May 28, 2016 at 12:37 PM -0700, "Lee Mason" [email protected] wrote: i know user_info is generally for presence channels, but there could be implementations where a private channel "could" use the user info returned, it would depend on the client using the response. its up to the auth callback what gets returned anyway, only allowing it for presence we can do for sure, but isnt it just a limitation that has no benefit, and wouldnt cause any issues keeping it as it is? — |
The dont broadcast to current user isnt actually handled in any specific way like it is with pusher. because pusher is an api driven implementation you explicitly send the socket id to the api. However using the functionality via redis puts the implementation of this on top the consuming application, usually a node js proccess. The Laravel apps only responsibility is to send along the socket id with the event info into a redis db. Which it already does when using the trait by having a public property called $socket. we dont need to alter anything as it will automatically get added as part of the event payload in the data property. When the event gets sent to redis, its sent to a channel like this:
Its then up to the client to deal with this data and send it off to who needs it. For example in rebound it looks like this:
before sending the event out, it loops through the connected sockets, and checks if the socket id matches the socket property on the event payload, if it does it wont send it to them. |
Just a quick note that our master branch is now L5.4. |
so would this be delayed until 5.4? whats happened to 5.3? and should i create a new pull request to the right branch? |
It needs to be sent to 5.3 branch now. Regards,
|
Closing to retarget 5.3 |
The PR contains the following changes:
1. "de-vendors" and enhances the new broadcast authentication features of 5.3.
1.1. Cached socket/session ids are changed from
pusher:socket:$id
tobroadcast:socket:$id
.1.2. Common broadcast driver functions are refactored into an external abstract class, namely auth(), check(), extractAuthParameters(), rememberSocket() and validAuthenticationResponse().
1.2.1. Specific drivers need only override validAuthenticationResponse() and/or rememberSocket() as the others are pretty general (but provided in the abstract class to prevent method not found issues, for example when testing). If not overwritten a default json response will be returned with status => success, user_id and user_info.
1.2.2 All drivers extending the abstract class (all existing core drivers) have been updated to require a container instance in there constructor, this is due to the abstract classes ability to ioc resolve classes mentioned in the next point.
This function returns the "member info" and anything else needed by a specific driver implementation. Pusher for example doesnt return anything, it does an api call, whereas Rebound (discussed further down) expects a response with the member info. Allowing this to be overwritten allows for different implementations without much hassle.
1.3. The auth('channel', function(){}) functionality has been extended to allow declaration just like other areas of laravel with the
class@method
syntax, or a callable. This gives the developer the choice to inject required classes via the class constructor (as it uses the ioc container to resolve the class before calling the "check" method.1.3.1. Where just a class name is provided the class is expected to have an authenticate() method.
1.4. A new Illuminate\Foundation\Support\Providers\BroadcastServiceProvider has been created with the same functionality signature as the EventServiceProvider.
1.4.1 This provider is extended within laravel/laravel via another pull request: laravel/laravel#3784 and in addition to using auth('channel', function(){}); you can now declare channel_name => [AuthClass::class] for auth callbacks. This will be familiar to users as it exists with the EventServiceProvider.
2. A new core broadcast driver has been added, called Rebound. This is a package i have developed in response to Laravel Echo and the authentication features coming in 5.3.Rebound is NOT built ontop of socket.io. it is a package built ontop engine.io.For those who dont know engine.io is the work horse of socket.io. engine.io is responsible for the transport upgrading, sending messages, reconnections, etc. Socket.io provides the namespaces, rooms, etc functionality.The default responses added are to allow a generic api to using the broadcasting routes, namely for a socket client i built, detailed below. As they are "default" responses unless overwritten by a specific driver they could be used by any client following the same conventions.
I chose to build Rebound to have an open source alternative to Pusher, that shares the same API as Laravel Echo.
The way pusher has "channels" and socket.io has namespaces/rooms just didnt make sense when trying to build a unified api.
Rebound works by adding messages to a redis db, which are handled by a node rebound-server proccess, and pushed out to all the connected sockets. (similar to socket.io and redis in 5.2).
The rebound-client has the same API as laravel echo, just a different engine underneath.
For example echo:
and Rebound:
When a socket connects the server sends a post request to /broadcasting/socket just like pusher does to "remember" the socket against the session.
The way rebound authenticates channels is very similar to pusher. The node server gets a request from a browser socket to join. the node server then sends a post request to the laravel app (on the same urls used for pusher /broadcasting/auth). this is where the auth('channel', function(){}); functions take over and either return data, or throw a 403 error.
if no error is thrown and data returned, the node server puts the socket into a "channel" group. If an error is thrown they dont get added, this is important.
The node server also caches the responses in redis to prevent multiple post requests on every page load (its configurable for expiry).
When a message comes in via redis, the channel name is collected.
The node server then loops through all the browser sockets within the channel group of the same name and sends them the event.
whats key here is sockets that requested access to that channel have to be validated by laravel before they get put into the node servers channel group. Then when events on that channel get fired we only send to sockets in that channel group. there is no way for the client to bypass this check.
The client api will be mainatined along with Laravel echo, and the server is pretty opinionated so doesnt need a public api at present, so both are stable as we speak (active development still happening, but nothing is likley to need changing).
The "server" can be found here: https://github.com/leemason/rebound-server
And the "client" here: https://github.com/leemason/rebound-server
More docs to come with time.