22
33namespace BeyondCode \LaravelWebSockets \ChannelManagers ;
44
5- use BeyondCode \LaravelWebSockets \Channels \Channel ;
65use BeyondCode \LaravelWebSockets \DashboardLogger ;
76use BeyondCode \LaravelWebSockets \Helpers ;
87use BeyondCode \LaravelWebSockets \Server \MockableConnection ;
@@ -145,31 +144,18 @@ public function subscribeToChannel(ConnectionInterface $connection, string $chan
145144 */
146145 public function unsubscribeFromChannel (ConnectionInterface $ connection , string $ channelName , stdClass $ payload ): PromiseInterface
147146 {
148- return $ this ->getGlobalConnectionsCount ($ connection ->app ->id , $ channelName )
147+ return parent ::unsubscribeFromChannel ($ connection , $ channelName , $ payload )
148+ ->then (function () use ($ connection , $ channelName ) {
149+ return $ this ->decrementSubscriptionsCount ($ connection ->app ->id , $ channelName );
150+ })
149151 ->then (function ($ count ) use ($ connection , $ channelName ) {
150- if ($ count === 0 ) {
151- // Make sure to not stay subscribed to the PubSub topic
152- // if there are no connections.
152+ $ this ->removeConnectionFromSet ($ connection );
153+ // If the total connections count gets to 0 after unsubscribe,
154+ // try again to check & unsubscribe from the PubSub topic if needed.
155+ if ($ count < 1 ) {
156+ $ this ->removeChannelFromSet ($ connection ->app ->id , $ channelName );
153157 $ this ->unsubscribeFromTopic ($ connection ->app ->id , $ channelName );
154158 }
155-
156- $ this ->decrementSubscriptionsCount ($ connection ->app ->id , $ channelName )
157- ->then (function ($ count ) use ($ connection , $ channelName ) {
158- // If the total connections count gets to 0 after unsubscribe,
159- // try again to check & unsubscribe from the PubSub topic if needed.
160- if ($ count < 1 ) {
161- $ this ->unsubscribeFromTopic ($ connection ->app ->id , $ channelName );
162- }
163- });
164- })
165- ->then (function () use ($ connection , $ channelName ) {
166- return $ this ->removeChannelFromSet ($ connection ->app ->id , $ channelName );
167- })
168- ->then (function () use ($ connection ) {
169- return $ this ->removeConnectionFromSet ($ connection );
170- })
171- ->then (function () use ($ connection , $ channelName , $ payload ) {
172- return parent ::unsubscribeFromChannel ($ connection , $ channelName , $ payload );
173159 });
174160 }
175161
@@ -363,6 +349,16 @@ public function connectionPonged(ConnectionInterface $connection): PromiseInterf
363349 {
364350 // This will update the score with the current timestamp.
365351 return $ this ->addConnectionToSet ($ connection , Carbon::now ())
352+ ->then (function () use ($ connection ) {
353+ $ payload = [
354+ 'socketId ' => $ connection ->socketId ,
355+ 'appId ' => $ connection ->app ->id ,
356+ 'serverId ' => $ this ->getServerId (),
357+ ];
358+
359+ return $ this ->publishClient
360+ ->publish ($ this ->getPongRedisHash ($ connection ->app ->id ), json_encode ($ payload ));
361+ })
366362 ->then (function () use ($ connection ) {
367363 return parent ::connectionPonged ($ connection );
368364 });
@@ -375,18 +371,23 @@ public function connectionPonged(ConnectionInterface $connection): PromiseInterf
375371 */
376372 public function removeObsoleteConnections (): PromiseInterface
377373 {
378- $ this ->lock ()->get (function () {
379- $ this ->getConnectionsFromSet (0 , now ()->subMinutes (2 )->format ('U ' ))
380- ->then (function ($ connections ) {
381- foreach ($ connections as $ socketId => $ appId ) {
382- $ connection = $ this ->fakeConnectionForApp ($ appId , $ socketId );
374+ $ lock = $ this ->lock ();
375+ try {
376+ $ lock ->get (function () {
377+ $ this ->getConnectionsFromSet (0 , now ()->subMinutes (2 )->format ('U ' ))
378+ ->then (function ($ connections ) {
379+ foreach ($ connections as $ socketId => $ appId ) {
380+ $ connection = $ this ->fakeConnectionForApp ($ appId , $ socketId );
383381
384- $ this ->unsubscribeFromAllChannels ($ connection );
385- }
386- });
387- });
382+ $ this ->unsubscribeFromAllChannels ($ connection );
383+ }
384+ });
385+ });
388386
389- return parent ::removeObsoleteConnections ();
387+ return parent ::removeObsoleteConnections ();
388+ } finally {
389+ optional ($ lock )->forceRelease ();
390+ }
390391 }
391392
392393 /**
@@ -404,6 +405,12 @@ public function onMessage(string $redisChannel, string $payload)
404405 return ;
405406 }
406407
408+ if ($ redisChannel == $ this ->getPongRedisHash ($ payload ->appId )) {
409+ $ connection = $ this ->fakeConnectionForApp ($ payload ->appId , $ payload ->socketId );
410+
411+ return parent ::connectionPonged ($ connection );
412+ }
413+
407414 $ payload ->channel = Str::after ($ redisChannel , "{$ payload ->appId }: " );
408415
409416 if (! $ channel = $ this ->find ($ payload ->appId , $ payload ->channel )) {
@@ -429,6 +436,16 @@ public function onMessage(string $redisChannel, string $payload)
429436 $ channel ->broadcastLocallyToEveryoneExcept ($ payload , $ socketId , $ appId );
430437 }
431438
439+ public function find ($ appId , string $ channel )
440+ {
441+ if (! $ channelInstance = parent ::find ($ appId , $ channel )) {
442+ $ class = $ this ->getChannelClassName ($ channel );
443+ $ this ->channels [$ appId ][$ channel ] = new $ class ($ channel );
444+ }
445+
446+ return parent ::find ($ appId , $ channel );
447+ }
448+
432449 /**
433450 * Build the Redis connection URL from Laravel database config.
434451 *
@@ -601,6 +618,20 @@ public function removeChannelFromSet($appId, string $channel): PromiseInterface
601618 );
602619 }
603620
621+ /**
622+ * Check if channel is on the list.
623+ *
624+ * @param string|int $appId
625+ * @param string $channel
626+ * @return PromiseInterface
627+ */
628+ public function isChannelInSet ($ appId , string $ channel ): PromiseInterface
629+ {
630+ return $ this ->publishClient ->sismember (
631+ $ this ->getChannelsRedisHash ($ appId ), $ channel
632+ );
633+ }
634+
604635 /**
605636 * Set data for a topic. Might be used for the presence channels.
606637 *
@@ -729,6 +760,16 @@ public function getRedisKey($appId = null, string $channel = null, array $suffix
729760 return $ hash ;
730761 }
731762
763+ /**
764+ * Get the pong Redis hash.
765+ *
766+ * @param string|int $appId
767+ */
768+ public function getPongRedisHash ($ appId ): string
769+ {
770+ return $ this ->getRedisKey ($ appId , null , ['pong ' ]);
771+ }
772+
732773 /**
733774 * Get the statistics Redis hash.
734775 *
0 commit comments