diff --git a/composer.json b/composer.json index e21a3fc..f59061d 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,8 @@ "require-dev": { "mockery/mockery": "^1.2", "orchestra/testbench": "3.7.* || 3.8.* || ^4.0", - "phpunit/phpunit": "^7.0 || ^8.0" + "phpunit/phpunit": "^7.0 || ^8.0", + "predis/predis": "^1.1" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5102c74..e1226ec 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -27,5 +27,6 @@ + diff --git a/src/Console/StartWebSocketServer.php b/src/Console/StartWebSocketServer.php index 4b68be3..b88ec76 100644 --- a/src/Console/StartWebSocketServer.php +++ b/src/Console/StartWebSocketServer.php @@ -12,7 +12,6 @@ use React\Dns\Resolver\Factory as DnsFactory; use BeyondCode\LaravelWebSockets\Statistics\DnsResolver; use BeyondCode\LaravelWebSockets\Facades\StatisticsLogger; use BeyondCode\LaravelWebSockets\Facades\WebSocketsRouter; -use BeyondCode\LaravelWebSockets\PubSub\Redis\RedisClient; use BeyondCode\LaravelWebSockets\Server\Logger\HttpLogger; use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface; use BeyondCode\LaravelWebSockets\Server\WebSocketServerFactory; @@ -117,7 +116,6 @@ class StartWebSocketServer extends Command protected function registerCustomRoutes() { WebSocketsRouter::customRoutes(); - return $this; } @@ -140,15 +138,7 @@ class StartWebSocketServer extends Command protected function configurePubSubReplication() { - if (config('websockets.replication.enabled') !== true) { - return $this; - } - - if (config('websockets.replication.driver') === 'redis') { - $this->laravel->singleton(ReplicationInterface::class, function () { - return (new RedisClient())->boot($this->loop); - }); - } + app(ReplicationInterface::class)->boot($this->loop); return $this; } diff --git a/src/PubSub/Redis/RedisPusherBroadcaster.php b/src/PubSub/Broadcasters/RedisPusherBroadcaster.php similarity index 98% rename from src/PubSub/Redis/RedisPusherBroadcaster.php rename to src/PubSub/Broadcasters/RedisPusherBroadcaster.php index 9905914..f1be3a5 100644 --- a/src/PubSub/Redis/RedisPusherBroadcaster.php +++ b/src/PubSub/Broadcasters/RedisPusherBroadcaster.php @@ -1,6 +1,6 @@ publishClient->__call('hset', ["$appId:$channel", 541561516, "qsgdqgsd"]); if (! isset($this->subscribedChannels["$appId:$channel"])) { // We're not subscribed to the channel yet, subscribe and set the count to 1 $this->subscribeClient->__call('subscribe', ["$appId:$channel"]); diff --git a/src/WebSockets/Channels/Channel.php b/src/WebSockets/Channels/Channel.php index 87e81e0..1d4d984 100644 --- a/src/WebSockets/Channels/Channel.php +++ b/src/WebSockets/Channels/Channel.php @@ -14,12 +14,18 @@ class Channel /** @var string */ protected $channelName; + /** + * @var ReplicationInterface + */ + protected $pubSub; + /** @var \Ratchet\ConnectionInterface[] */ protected $subscribedConnections = []; public function __construct(string $channelName) { $this->channelName = $channelName; + $this->pubSub = app(ReplicationInterface::class); } public function getChannelName(): string @@ -48,7 +54,7 @@ class Channel $signature .= ":{$payload->channel_data}"; } - if (! hash_equals( + if (!hash_equals( hash_hmac('sha256', $signature, $connection->app->secret), Str::after($payload->auth, ':')) ) { @@ -63,11 +69,8 @@ class Channel { $this->saveConnection($connection); - if (config('websockets.replication.enabled') === true) { - // Subscribe for broadcasted messages from the pub/sub backend - app(ReplicationInterface::class) - ->subscribe($connection->app->id, $this->channelName); - } + // Subscribe to broadcasted messages from the pub/sub backend + $this->pubSub->subscribe($connection->app->id, $this->channelName); $connection->send(json_encode([ 'event' => 'pusher_internal:subscription_succeeded', @@ -79,13 +82,10 @@ class Channel { unset($this->subscribedConnections[$connection->socketId]); - if (config('websockets.replication.enabled') === true) { - // Unsubscribe from the pub/sub backend - app(ReplicationInterface::class) - ->unsubscribe($connection->app->id, $this->channelName); - } + // Unsubscribe from the pub/sub backend + $this->pubSub->unsubscribe($connection->app->id, $this->channelName); - if (! $this->hasConnections()) { + if (!$this->hasConnections()) { DashboardLogger::vacated($connection, $this->channelName); } } @@ -96,7 +96,7 @@ class Channel $this->subscribedConnections[$connection->socketId] = $connection; - if (! $hadConnectionsPreviously) { + if (!$hadConnectionsPreviously) { DashboardLogger::occupied($connection, $this->channelName); } @@ -112,11 +112,8 @@ class Channel public function broadcastToOthers(ConnectionInterface $connection, $payload) { - if (config('websockets.replication.enabled') === true) { - // Also broadcast via the other websocket servers - app(ReplicationInterface::class) - ->publish($connection->app->id, $this->channelName, $payload); - } + // Also broadcast via the other websocket servers + $this->pubSub->publish($connection->app->id, $this->channelName, $payload); $this->broadcastToEveryoneExcept($payload, $connection->socketId); } diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index e9ce735..bca9939 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -2,6 +2,10 @@ namespace BeyondCode\LaravelWebSockets; +use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster; +use BeyondCode\LaravelWebSockets\PubSub\Drivers\EmptyClient; +use BeyondCode\LaravelWebSockets\PubSub\Drivers\RedisClient; +use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface; use Pusher\Pusher; use Psr\Log\LoggerInterface; use Illuminate\Support\Facades\Gate; @@ -11,7 +15,6 @@ use Illuminate\Broadcasting\BroadcastManager; use BeyondCode\LaravelWebSockets\Server\Router; use BeyondCode\LaravelWebSockets\Apps\AppProvider; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; -use BeyondCode\LaravelWebSockets\PubSub\Redis\RedisPusherBroadcaster; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\SendMessage; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowDashboard; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\AuthenticateDashboard; @@ -23,15 +26,15 @@ use BeyondCode\LaravelWebSockets\Statistics\Http\Controllers\WebSocketStatistics class WebSocketsServiceProvider extends ServiceProvider { - public function boot(BroadcastManager $broadcastManager) + public function boot() { $this->publishes([ - __DIR__.'/../config/websockets.php' => base_path('config/websockets.php'), + __DIR__ . '/../config/websockets.php' => base_path('config/websockets.php'), ], 'config'); - if (! class_exists('CreateWebSocketsStatisticsEntries')) { + if (!class_exists('CreateWebSocketsStatisticsEntries')) { $this->publishes([ - __DIR__.'/../database/migrations/create_websockets_statistics_entries_table.php.stub' => database_path('migrations/'.date('Y_m_d_His', time()).'_create_websockets_statistics_entries_table.php'), + __DIR__ . '/../database/migrations/create_websockets_statistics_entries_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_websockets_statistics_entries_table.php'), ], 'migrations'); } @@ -39,14 +42,31 @@ class WebSocketsServiceProvider extends ServiceProvider ->registerRoutes() ->registerDashboardGate(); - $this->loadViewsFrom(__DIR__.'/../resources/views/', 'websockets'); + $this->loadViewsFrom(__DIR__ . '/../resources/views/', 'websockets'); $this->commands([ Console\StartWebSocketServer::class, Console\CleanStatistics::class, ]); - $broadcastManager->extend('redis-pusher', function ($app, array $config) { + $this->configurePubSub(); + + } + + protected function configurePubSub() + { + if (config('websockets.replication.enabled') !== true || config('websockets.replication.driver') !== 'redis') { + $this->app->singleton(ReplicationInterface::class, function () { + return (new EmptyClient()); + }); + return; + } + + $this->app->singleton(ReplicationInterface::class, function () { + return (new RedisClient())->boot($this->loop); + }); + + app(BroadcastManager::class)->extend('redis-pusher', function ($app, array $config) { $pusher = new Pusher( $config['key'], $config['secret'], $config['app_id'], $config['options'] ?? [] @@ -67,7 +87,7 @@ class WebSocketsServiceProvider extends ServiceProvider public function register() { - $this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets'); + $this->mergeConfigFrom(__DIR__ . '/../config/websockets.php', 'websockets'); $this->app->singleton('websockets.router', function () { return new Router(); @@ -88,7 +108,7 @@ class WebSocketsServiceProvider extends ServiceProvider Route::prefix(config('websockets.path'))->group(function () { Route::middleware(config('websockets.middleware', [AuthorizeDashboard::class]))->group(function () { Route::get('/', ShowDashboard::class); - Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics']); + Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics']); Route::post('auth', AuthenticateDashboard::class); Route::post('event', SendMessage::class); }); diff --git a/src/PubSub/Fake/FakeReplication.php b/tests/Mocks/FakeReplicationClient.php similarity index 96% rename from src/PubSub/Fake/FakeReplication.php rename to tests/Mocks/FakeReplicationClient.php index 5b3e429..5ad21b3 100644 --- a/src/PubSub/Fake/FakeReplication.php +++ b/tests/Mocks/FakeReplicationClient.php @@ -1,6 +1,6 @@ singleton(ReplicationInterface::class, function () { - return (new FakeReplication())->boot(Factory::create()); + return (new FakeReplicationClient())->boot(Factory::create()); }); - config([ + Config::set([ 'websockets.replication.enabled' => true, - 'websockets.replication.driver' => 'fake', + 'websockets.replication.driver' => 'redis', ]); } }