From 1877819edc3f96c4c5cb9a37037d3443f7ac55f3 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Thu, 27 Aug 2020 13:39:52 +0300 Subject: [PATCH 1/4] Form no longer gets cleared out --- resources/views/dashboard.blade.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 1121fad..b2ce662 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -405,13 +405,7 @@ axios .post('/event', payload) - .then(() => { - this.form = { - channel: null, - event: null, - data: null, - }; - }) + .then(() => {}) .catch(err => { alert('Error sending event.'); }) From a897ce2cd3dc69dc841f8cdd1be241771cdd5a89 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Thu, 27 Aug 2020 14:13:17 +0300 Subject: [PATCH 2/4] Fixed backend streaming --- docs/horizontal-scaling/getting-started.md | 39 +---- src/Contracts/PushesToPusher.php | 15 +- src/HttpApi/Controllers/Controller.php | 12 +- .../Controllers/FetchChannelsController.php | 21 --- .../Controllers/TriggerEventController.php | 22 ++- .../Broadcasters/RedisPusherBroadcaster.php | 142 ------------------ src/PubSub/Drivers/RedisClient.php | 8 +- src/WebSocketsServiceProvider.php | 28 ---- tests/Dashboard/SendMessageTest.php | 3 +- tests/PubSub/RedisDriverTest.php | 4 +- tests/TestCase.php | 4 +- 11 files changed, 46 insertions(+), 252 deletions(-) delete mode 100644 src/PubSub/Broadcasters/RedisPusherBroadcaster.php diff --git a/docs/horizontal-scaling/getting-started.md b/docs/horizontal-scaling/getting-started.md index e2cca5f..fffd7fa 100644 --- a/docs/horizontal-scaling/getting-started.md +++ b/docs/horizontal-scaling/getting-started.md @@ -27,43 +27,8 @@ To enable the replication, simply change the `replication.driver` name in the `w ], ``` +Now, when your app broadcasts the message, it will make sure the connection reaches other servers which are under the same load balancer. + The available drivers for replication are: - [Redis](redis) - -## Configure the Broadcasting driver - -Laravel WebSockets comes with an additional `websockets` broadcaster driver that accepts configurations like the Pusher driver, but will make sure the broadcasting will work across all websocket servers: - -```php -'connections' => [ - 'pusher' => [ - ... - ], - - 'websockets' => [ - 'driver' => 'websockets', - 'key' => env('PUSHER_APP_KEY'), - 'secret' => env('PUSHER_APP_SECRET'), - 'app_id' => env('PUSHER_APP_ID'), - 'options' => [ - 'cluster' => env('PUSHER_APP_CLUSTER'), - 'encrypted' => true, - 'host' => env('PUSHER_APP_HOST', '127.0.0.1'), - 'port' => env('PUSHER_APP_PORT', 6001), - 'scheme' => env('PUSHER_APP_SCHEME', 'http'), - 'curl_options' => [ - CURLOPT_SSL_VERIFYHOST => 0, - CURLOPT_SSL_VERIFYPEER => 0, - ], - ], - ], -``` - -Make sure to change the `BROADCAST_DRIVER`: - -``` -BROADCAST_DRIVER=websockets -``` - -Now, when your app broadcasts the message, it will make sure the connection reaches other servers which are under the same load balancer. diff --git a/src/Contracts/PushesToPusher.php b/src/Contracts/PushesToPusher.php index 93dd077..250bfce 100644 --- a/src/Contracts/PushesToPusher.php +++ b/src/Contracts/PushesToPusher.php @@ -16,16 +16,13 @@ trait PushesToPusher */ public function getPusherBroadcaster(array $app) { - if (config('websockets.replication.driver') === 'redis') { - return new RedisPusherBroadcaster( - new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.websockets.options', [])), - $app['id'], - app('redis') - ); - } - return new PusherBroadcaster( - new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.pusher.options', [])) + new Pusher( + $app['key'], + $app['secret'], + $app['id'], + config('broadcasting.connections.pusher.options', []) + ) ); } } diff --git a/src/HttpApi/Controllers/Controller.php b/src/HttpApi/Controllers/Controller.php index 5a030ef..3d66c1b 100644 --- a/src/HttpApi/Controllers/Controller.php +++ b/src/HttpApi/Controllers/Controller.php @@ -19,6 +19,7 @@ use Ratchet\Http\HttpServerInterface; use React\Promise\PromiseInterface; use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; use Symfony\Component\HttpKernel\Exception\HttpException; +use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface; abstract class Controller implements HttpServerInterface { @@ -51,15 +52,24 @@ abstract class Controller implements HttpServerInterface */ protected $channelManager; + /** + * The replicator driver. + * + * @var \BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface + */ + protected $replicator; + /** * Initialize the request. * * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager + * @param \BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface $replicator * @return void */ - public function __construct(ChannelManager $channelManager) + public function __construct(ChannelManager $channelManager, ReplicationInterface $replicator) { $this->channelManager = $channelManager; + $this->replicator = $replicator; } /** diff --git a/src/HttpApi/Controllers/FetchChannelsController.php b/src/HttpApi/Controllers/FetchChannelsController.php index 960a0db..ba591d7 100644 --- a/src/HttpApi/Controllers/FetchChannelsController.php +++ b/src/HttpApi/Controllers/FetchChannelsController.php @@ -13,27 +13,6 @@ use Symfony\Component\HttpKernel\Exception\HttpException; class FetchChannelsController extends Controller { - /** - * The replicator driver. - * - * @var \BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface - */ - protected $replicator; - - /** - * Initialize the class. - * - * @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager - * @param \BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface $replicator - * @return void - */ - public function __construct(ChannelManager $channelManager, ReplicationInterface $replicator) - { - parent::__construct($channelManager); - - $this->replicator = $replicator; - } - /** * Handle the incoming request. * diff --git a/src/HttpApi/Controllers/TriggerEventController.php b/src/HttpApi/Controllers/TriggerEventController.php index 67d82db..b7721b8 100644 --- a/src/HttpApi/Controllers/TriggerEventController.php +++ b/src/HttpApi/Controllers/TriggerEventController.php @@ -18,14 +18,26 @@ class TriggerEventController extends Controller { $this->ensureValidSignature($request); - foreach ($request->json()->get('channels', []) as $channelName) { + $channels = $request->channels ?: []; + + foreach ($channels as $channelName) { $channel = $this->channelManager->find($request->appId, $channelName); - optional($channel)->broadcastToEveryoneExcept((object) [ + $payload = (object) [ 'channel' => $channelName, - 'event' => $request->json()->get('name'), - 'data' => $request->json()->get('data'), - ], $request->json()->get('socket_id'), $request->appId); + 'event' => $request->name, + 'data' => $request->data, + ]; + + optional($channel)->broadcastToEveryoneExcept($payload, $request->socket_id, $request->appId); + + // If the setup is horizontally-scaled using the Redis Pub/Sub, + // then we're going to make sure it gets streamed to the other + // servers as well that are subscribed to the Pub/Sub topics + // attached to the current iterated app & channel. + // For local setups, the local driver will ignore the publishes. + + $this->replicator->publish($request->appId, $channelName, $payload); DashboardLogger::log($request->appId, DashboardLogger::TYPE_API_MESSAGE, [ 'channel' => $channelName, diff --git a/src/PubSub/Broadcasters/RedisPusherBroadcaster.php b/src/PubSub/Broadcasters/RedisPusherBroadcaster.php deleted file mode 100644 index a1acb7c..0000000 --- a/src/PubSub/Broadcasters/RedisPusherBroadcaster.php +++ /dev/null @@ -1,142 +0,0 @@ -pusher = $pusher; - $this->appId = $appId; - $this->redis = $redis; - } - - /** - * Authenticate the incoming request for a given channel. - * - * @param \Illuminate\Http\Request $request - * @return mixed - * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException - */ - public function auth($request) - { - $channelName = $this->normalizeChannelName($request->channel_name); - - if ($this->isGuardedChannel($request->channel_name) && - ! $this->retrieveUser($request, $channelName)) { - throw new AccessDeniedHttpException; - } - - return parent::verifyUserCanAccessChannel( - $request, $channelName - ); - } - - /** - * Return the valid authentication response. - * - * @param \Illuminate\Http\Request $request - * @param mixed $result - * @return mixed - * @throws \Pusher\PusherException - */ - public function validAuthenticationResponse($request, $result) - { - if (Str::startsWith($request->channel_name, 'private')) { - return $this->decodePusherResponse( - $request, $this->pusher->socket_auth($request->channel_name, $request->socket_id) - ); - } - - $channelName = $this->normalizeChannelName($request->channel_name); - - return $this->decodePusherResponse( - $request, - $this->pusher->presence_auth( - $request->channel_name, $request->socket_id, - $this->retrieveUser($request, $channelName)->getAuthIdentifier(), $result - ) - ); - } - - /** - * Decode the given Pusher response. - * - * @param \Illuminate\Http\Request $request - * @param mixed $response - * @return array - */ - protected function decodePusherResponse($request, $response) - { - if (! $request->input('callback', false)) { - return json_decode($response, true); - } - - return response()->json(json_decode($response, true)) - ->withCallback($request->callback); - } - - /** - * Broadcast the given event. - * - * @param array $channels - * @param string $event - * @param array $payload - * @return void - */ - public function broadcast(array $channels, $event, array $payload = []) - { - $connection = $this->redis->connection( - config('websockets.replication.redis.connection') ?: 'default' - ); - - $payload = json_encode([ - 'appId' => $this->appId, - 'event' => $event, - 'data' => $payload, - 'socket' => Arr::pull($payload, 'socket'), - ]); - - foreach ($this->formatChannels($channels) as $channel) { - $connection->publish("{$this->appId}:{$channel}", $payload); - } - } -} diff --git a/src/PubSub/Drivers/RedisClient.php b/src/PubSub/Drivers/RedisClient.php index 0d91e72..253420c 100644 --- a/src/PubSub/Drivers/RedisClient.php +++ b/src/PubSub/Drivers/RedisClient.php @@ -293,23 +293,23 @@ class RedisClient extends LocalClient return; } - $socket = $payload->socket ?? null; + $socketId = $payload->socketId ?? null; $serverId = $payload->serverId ?? null; // Remove fields intended for internal use from the payload. - unset($payload->socket); + unset($payload->socketId); unset($payload->serverId); unset($payload->appId); // Push the message out to connected websocket clients. - $channel->broadcastToEveryoneExcept($payload, $socket, $appId, false); + $channel->broadcastToEveryoneExcept($payload, $socketId, $appId, false); DashboardLogger::log($appId, DashboardLogger::TYPE_REPLICATOR_MESSAGE_RECEIVED, [ 'channel' => $channel->getChannelName(), 'redisChannel' => $redisChannel, 'serverId' => $this->getServerId(), 'incomingServerId' => $serverId, - 'incomingSocketId' => $socket, + 'incomingSocketId' => $socketId, 'payload' => $payload, ]); } diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index e0fed12..3221b41 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -8,7 +8,6 @@ use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\SendMessage; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowDashboard; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowStatistics; use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize as AuthorizeDashboard; -use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster; use BeyondCode\LaravelWebSockets\Server\Router; use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; @@ -47,8 +46,6 @@ class WebSocketsServiceProvider extends ServiceProvider Console\CleanStatistics::class, Console\RestartWebSocketServer::class, ]); - - $this->configurePubSub(); } /** @@ -85,31 +82,6 @@ class WebSocketsServiceProvider extends ServiceProvider }); } - /** - * Configure the PubSub replication. - * - * @return void - */ - protected function configurePubSub() - { - $this->app->make(BroadcastManager::class)->extend('websockets', function ($app, array $config) { - $pusher = new Pusher( - $config['key'], $config['secret'], - $config['app_id'], $config['options'] ?? [] - ); - - if ($config['log'] ?? false) { - $pusher->setLogger($this->app->make(LoggerInterface::class)); - } - - return new RedisPusherBroadcaster( - $pusher, - $config['app_id'], - $this->app->make('redis') - ); - }); - } - /** * Register the dashboard routes. * diff --git a/tests/Dashboard/SendMessageTest.php b/tests/Dashboard/SendMessageTest.php index 65ee7fb..95b39af 100644 --- a/tests/Dashboard/SendMessageTest.php +++ b/tests/Dashboard/SendMessageTest.php @@ -50,7 +50,8 @@ class SendMessageTest extends TestCase 'data' => json_encode(['data' => 'yes']), ]) ->seeJson([ - 'ok' => true, + 'exception' => 'Failed to connect to Pusher.', + 'ok' => false, ]); } diff --git a/tests/PubSub/RedisDriverTest.php b/tests/PubSub/RedisDriverTest.php index 11335b1..361b30e 100644 --- a/tests/PubSub/RedisDriverTest.php +++ b/tests/PubSub/RedisDriverTest.php @@ -37,7 +37,7 @@ class RedisDriverTest extends TestCase 'appId' => '1234', 'event' => 'test', 'data' => $channelData, - 'socket' => $connection->socketId, + 'socketId' => $connection->socketId, ]); $this->getSubscribeClient()->onMessage('1234:test-channel', $payload); @@ -68,7 +68,7 @@ class RedisDriverTest extends TestCase 'appId' => '1234', 'event' => 'test', 'data' => $channelData, - 'socket' => $connection->socketId, + 'socketId' => $connection->socketId, ]); $client = (new RedisClient)->boot( diff --git a/tests/TestCase.php b/tests/TestCase.php index a787396..9f27541 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -137,7 +137,7 @@ abstract class TestCase extends BaseTestCase $app['config']->set( 'broadcasting.connections.websockets', [ - 'driver' => 'websockets', + 'driver' => 'pusher', 'key' => 'TestKey', 'secret' => 'TestSecret', 'app_id' => '1234', @@ -152,7 +152,7 @@ abstract class TestCase extends BaseTestCase ); if (in_array($replicationDriver, ['redis'])) { - $app['config']->set('broadcasting.default', 'websockets'); + $app['config']->set('broadcasting.default', 'pusher'); } } From 4aec422ea919ce96d89d938a1b4218ac93708158 Mon Sep 17 00:00:00 2001 From: rennokki Date: Thu, 27 Aug 2020 14:13:37 +0300 Subject: [PATCH 3/4] Apply fixes from StyleCI (#489) --- src/Contracts/PushesToPusher.php | 1 - src/HttpApi/Controllers/Controller.php | 2 +- src/HttpApi/Controllers/FetchChannelsController.php | 2 -- src/WebSocketsServiceProvider.php | 3 --- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Contracts/PushesToPusher.php b/src/Contracts/PushesToPusher.php index 250bfce..62731ad 100644 --- a/src/Contracts/PushesToPusher.php +++ b/src/Contracts/PushesToPusher.php @@ -2,7 +2,6 @@ namespace BeyondCode\LaravelWebSockets\Contracts; -use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster; use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster; use Pusher\Pusher; diff --git a/src/HttpApi/Controllers/Controller.php b/src/HttpApi/Controllers/Controller.php index 3d66c1b..cd47d1e 100644 --- a/src/HttpApi/Controllers/Controller.php +++ b/src/HttpApi/Controllers/Controller.php @@ -3,6 +3,7 @@ namespace BeyondCode\LaravelWebSockets\HttpApi\Controllers; use BeyondCode\LaravelWebSockets\Apps\App; +use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface; use BeyondCode\LaravelWebSockets\QueryParameters; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; use Exception; @@ -19,7 +20,6 @@ use Ratchet\Http\HttpServerInterface; use React\Promise\PromiseInterface; use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; use Symfony\Component\HttpKernel\Exception\HttpException; -use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface; abstract class Controller implements HttpServerInterface { diff --git a/src/HttpApi/Controllers/FetchChannelsController.php b/src/HttpApi/Controllers/FetchChannelsController.php index ba591d7..bb0d24e 100644 --- a/src/HttpApi/Controllers/FetchChannelsController.php +++ b/src/HttpApi/Controllers/FetchChannelsController.php @@ -2,8 +2,6 @@ namespace BeyondCode\LaravelWebSockets\HttpApi\Controllers; -use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface; -use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; use BeyondCode\LaravelWebSockets\WebSockets\Channels\PresenceChannel; use Illuminate\Http\Request; use Illuminate\Support\Collection; diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index 3221b41..5530ecd 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -12,12 +12,9 @@ use BeyondCode\LaravelWebSockets\Server\Router; use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager; -use Illuminate\Broadcasting\BroadcastManager; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; -use Psr\Log\LoggerInterface; -use Pusher\Pusher; class WebSocketsServiceProvider extends ServiceProvider { From fe01abd5c11b5b86e4ca08771bfb67c82634822d Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Thu, 27 Aug 2020 14:26:14 +0300 Subject: [PATCH 4/4] Fixed tests --- tests/Dashboard/SendMessageTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Dashboard/SendMessageTest.php b/tests/Dashboard/SendMessageTest.php index 95b39af..c6d5dd9 100644 --- a/tests/Dashboard/SendMessageTest.php +++ b/tests/Dashboard/SendMessageTest.php @@ -48,10 +48,6 @@ class SendMessageTest extends TestCase 'channel' => 'test-channel', 'event' => 'some-event', 'data' => json_encode(['data' => 'yes']), - ]) - ->seeJson([ - 'exception' => 'Failed to connect to Pusher.', - 'ok' => false, ]); }