2
2
3
3
namespace BeyondCode \LaravelWebSockets \ChannelManagers ;
4
4
5
- use BeyondCode \LaravelWebSockets \Channels \Channel ;
6
5
use BeyondCode \LaravelWebSockets \DashboardLogger ;
7
6
use BeyondCode \LaravelWebSockets \Helpers ;
8
7
use BeyondCode \LaravelWebSockets \Server \MockableConnection ;
@@ -145,31 +144,18 @@ public function subscribeToChannel(ConnectionInterface $connection, string $chan
145
144
*/
146
145
public function unsubscribeFromChannel (ConnectionInterface $ connection , string $ channelName , stdClass $ payload ): PromiseInterface
147
146
{
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
+ })
149
151
->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 );
153
157
$ this ->unsubscribeFromTopic ($ connection ->app ->id , $ channelName );
154
158
}
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 );
173
159
});
174
160
}
175
161
@@ -363,6 +349,16 @@ public function connectionPonged(ConnectionInterface $connection): PromiseInterf
363
349
{
364
350
// This will update the score with the current timestamp.
365
351
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
+ })
366
362
->then (function () use ($ connection ) {
367
363
return parent ::connectionPonged ($ connection );
368
364
});
@@ -375,18 +371,23 @@ public function connectionPonged(ConnectionInterface $connection): PromiseInterf
375
371
*/
376
372
public function removeObsoleteConnections (): PromiseInterface
377
373
{
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 );
383
381
384
- $ this ->unsubscribeFromAllChannels ($ connection );
385
- }
386
- });
387
- });
382
+ $ this ->unsubscribeFromAllChannels ($ connection );
383
+ }
384
+ });
385
+ });
388
386
389
- return parent ::removeObsoleteConnections ();
387
+ return parent ::removeObsoleteConnections ();
388
+ } finally {
389
+ optional ($ lock )->forceRelease ();
390
+ }
390
391
}
391
392
392
393
/**
@@ -404,6 +405,12 @@ public function onMessage(string $redisChannel, string $payload)
404
405
return ;
405
406
}
406
407
408
+ if ($ redisChannel == $ this ->getPongRedisHash ($ payload ->appId )) {
409
+ $ connection = $ this ->fakeConnectionForApp ($ payload ->appId , $ payload ->socketId );
410
+
411
+ return parent ::connectionPonged ($ connection );
412
+ }
413
+
407
414
$ payload ->channel = Str::after ($ redisChannel , "{$ payload ->appId }: " );
408
415
409
416
if (! $ channel = $ this ->find ($ payload ->appId , $ payload ->channel )) {
@@ -429,6 +436,16 @@ public function onMessage(string $redisChannel, string $payload)
429
436
$ channel ->broadcastLocallyToEveryoneExcept ($ payload , $ socketId , $ appId );
430
437
}
431
438
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
+
432
449
/**
433
450
* Build the Redis connection URL from Laravel database config.
434
451
*
@@ -601,6 +618,20 @@ public function removeChannelFromSet($appId, string $channel): PromiseInterface
601
618
);
602
619
}
603
620
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
+
604
635
/**
605
636
* Set data for a topic. Might be used for the presence channels.
606
637
*
@@ -729,6 +760,16 @@ public function getRedisKey($appId = null, string $channel = null, array $suffix
729
760
return $ hash ;
730
761
}
731
762
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
+
732
773
/**
733
774
* Get the statistics Redis hash.
734
775
*
0 commit comments