From 9bb24bbfb808b7f3d0cba4aa66e479f5faea0b89 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 25 Feb 2018 15:20:00 +0800 Subject: [PATCH 01/39] modify swoole event name --- src/Server/Manager.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 381b1621..e97f06cf 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -131,7 +131,7 @@ protected function setSwooleHttpServerListeners() $this->server->on($event, [$this, $listener]); } else { $this->server->on($event, function () use ($event) { - $event = sprintf('http.%s', $event); + $event = sprintf('swoole.%s', $event); $this->container['events']->fire($event, func_get_args()); }); @@ -147,7 +147,7 @@ public function onStart() $this->setProcessName('master process'); $this->createPidFile(); - $this->container['events']->fire('http.start', func_get_args()); + $this->container['events']->fire('swoole.start', func_get_args()); } /** @@ -158,7 +158,7 @@ public function onWorkerStart() $this->clearCache(); $this->setProcessName('worker process'); - $this->container['events']->fire('http.workerStart', func_get_args()); + $this->container['events']->fire('swoole.workerStart', func_get_args()); $this->createApplication(); $this->setLaravelApp(); @@ -173,7 +173,7 @@ public function onWorkerStart() */ public function onRequest($swooleRequest, $swooleResponse) { - $this->container['events']->fire('http.onRequest'); + $this->container['events']->fire('swoole.onRequest'); // Reset user-customized providers $this->getApplication()->resetProviders(); @@ -203,7 +203,7 @@ public function onShutdown() { $this->removePidFile(); - $this->container['events']->fire('http.showdown', func_get_args()); + $this->container['events']->fire('swoole.shutdown', func_get_args()); } /** From 73c4a2104b4edb6ae3c71df121b3aad5eeb4baa6 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 25 Feb 2018 19:58:52 +0800 Subject: [PATCH 02/39] try to add basic websocket support --- config/swoole_http.php | 5 ++- src/Server/Manager.php | 57 +++++++++++++++++++----------- src/Server/Traits/CanWebsocket.php | 54 ++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 src/Server/Traits/CanWebsocket.php diff --git a/config/swoole_http.php b/config/swoole_http.php index cf52775a..447789ff 100644 --- a/config/swoole_http.php +++ b/config/swoole_http.php @@ -15,9 +15,12 @@ 'options' => [ 'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('storage/logs/swoole_http.pid')), 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')), - 'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', 0), + 'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', false), ], ], + 'websocket' => [ + 'enabled' => env('SWOOLE_HTTP_WEBSOCKET', false), + ], 'providers' => [ // App\Providers\AuthServiceProvider::class, ] diff --git a/src/Server/Manager.php b/src/Server/Manager.php index e97f06cf..cd4c2da5 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -3,14 +3,18 @@ namespace SwooleTW\Http\Server; use Illuminate\Contracts\Container\Container; -use Swoole\Http\Server; +use Swoole\Http\Server as HttpServer; +use Swoole\WebSocket\Server as WebSocketServer; +use SwooleTW\Http\Server\Traits\CanWebsocket; class Manager { + use CanWebsocket; + const MAC_OSX = 'Darwin'; /** - * @var \Swoole\Http\Server + * @var \Swoole\Http\Server | \Swoole\Websocket\Server */ protected $server; @@ -71,7 +75,7 @@ public function __construct(Container $container, $framework, $basePath = null) } /** - * Run swoole_http_server. + * Run swoole server. */ public function run() { @@ -79,7 +83,7 @@ public function run() } /** - * Stop swoole_http_server. + * Stop swoole server. */ public function stop() { @@ -93,26 +97,41 @@ protected function initialize() { $this->setProcessName('manager process'); - $this->createSwooleHttpServer(); - $this->configureSwooleHttpServer(); - $this->setSwooleHttpServerListeners(); + $this->prepareWebsocket(); + $this->createSwooleServer(); + $this->configureSwooleServer(); + $this->setSwooleServerListeners(); + } + + /** + * Prepare settings if websocket is enabled. + */ + protected function prepareWebsocket() + { + $isWebsocket = $this->container['config']->get('swoole_http.websocket.enabled'); + + if ($isWebsocket) { + array_push($this->events, ...$this->wsEvents); + $this->isWebsocket = true; + } } /** - * Creates swoole_http_server. + * Create swoole erver. */ - protected function createSwooleHttpServer() + protected function createSwooleServer() { + $server = $this->isWebsocket ? WebsocketServer::class : HttpServer::class; $host = $this->container['config']->get('swoole_http.server.host'); $port = $this->container['config']->get('swoole_http.server.port'); - $this->server = new Server($host, $port); + $this->server = new $server($host, $port); } /** - * Sets swoole_http_server configurations. + * Set swoole server configurations. */ - protected function configureSwooleHttpServer() + protected function configureSwooleServer() { $config = $this->container['config']->get('swoole_http.server.options'); @@ -120,9 +139,9 @@ protected function configureSwooleHttpServer() } /** - * Sets swoole_http_server listeners. + * Set swoole server listeners. */ - protected function setSwooleHttpServerListeners() + protected function setSwooleServerListeners() { foreach ($this->events as $event) { $listener = 'on' . ucfirst($event); @@ -162,7 +181,7 @@ public function onWorkerStart() $this->createApplication(); $this->setLaravelApp(); - $this->bindSwooleHttpServer(); + $this->bindSwooleServer(); } /** @@ -182,10 +201,6 @@ public function onRequest($swooleRequest, $swooleResponse) $illuminateResponse = $this->getApplication()->run($illuminateRequest); $response = Response::make($illuminateResponse, $swooleResponse); - // To prevent 'connection[...] is closed' error. - if (! $this->server->exist($swooleRequest->fd)) { - return; - } $response->send(); // Unset request and response. @@ -239,7 +254,7 @@ protected function setLaravelApp() /** * Bind swoole server to Laravel app container. */ - protected function bindSwooleHttpServer() + protected function bindSwooleServer() { $this->app->singleton('swoole.server', function () { return $this->server; @@ -290,7 +305,7 @@ protected function clearCache() } /** - * Sets process name. + * Set process name. * * @param $process */ diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php new file mode 100644 index 00000000..4e154789 --- /dev/null +++ b/src/Server/Traits/CanWebsocket.php @@ -0,0 +1,54 @@ +container['events']->fire('swoole.onOpen', func_get_args()); + } + + /** + * "onMessage" listener. + * + * @param \Swoole\Websocket\Server $server + * @param \Swoole\Websocket\Frame $frame + */ + public function onMessage($server, $frame) + { + $this->container['events']->fire('swoole.onMessage', func_get_args()); + } + + /** + * "onClose" listener. + * + * @param \Swoole\Websocket\Server $server + * @param int $fd + */ + public function onClose($server, $fd) + { + $this->container['events']->fire('swoole.onClose', func_get_args()); + } +} From dc999cd0462403a638cd4f65543aac4a84aebe72 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 25 Feb 2018 20:53:01 +0800 Subject: [PATCH 03/39] modify event fire arguments --- src/Server/Traits/CanWebsocket.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php index 4e154789..b68fbc4f 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Traits/CanWebsocket.php @@ -2,8 +2,7 @@ namespace SwooleTW\Http\Server\Traits; -use Swoole\WebSocket\Server; -use Swoole\WebSocket\Frame; +use SwooleTW\Http\Server\Request; trait CanWebsocket { @@ -27,7 +26,10 @@ trait CanWebsocket */ public function onOpen($server, $swooleRequest) { - $this->container['events']->fire('swoole.onOpen', func_get_args()); + $illuminateRequest = Request::make($swooleRequest)->toIlluminate(); + $this->container['events']->fire('swoole.onOpen', $illuminateRequest); + + $illuminateRequest = null; } /** @@ -38,7 +40,7 @@ public function onOpen($server, $swooleRequest) */ public function onMessage($server, $frame) { - $this->container['events']->fire('swoole.onMessage', func_get_args()); + $this->container['events']->fire('swoole.onMessage', $frame); } /** @@ -49,6 +51,11 @@ public function onMessage($server, $frame) */ public function onClose($server, $fd) { - $this->container['events']->fire('swoole.onClose', func_get_args()); + $info = $server->connection_info($fd); + if (array_key_exists('websocket_status', $info) && $info['websocket_status']) { + $this->container['events']->fire('swoole.onClose', $fd); + } + + $info = null; } } From b7d19e3e4c61b757fd9acd86c3a80edb9c7e1301 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 25 Feb 2018 22:47:50 +0800 Subject: [PATCH 04/39] delete unnecessary unset --- src/Server/Manager.php | 7 ------- src/Server/Traits/CanWebsocket.php | 7 ++----- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index cd4c2da5..82eb8aa1 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -202,13 +202,6 @@ public function onRequest($swooleRequest, $swooleResponse) $response = Response::make($illuminateResponse, $swooleResponse); $response->send(); - - // Unset request and response. - $response = null; - $swooleRequest = null; - $swooleResponse = null; - $illuminateRequest = null; - $illuminateResponse = null; } /** diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php index b68fbc4f..56d2122e 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Traits/CanWebsocket.php @@ -26,10 +26,9 @@ trait CanWebsocket */ public function onOpen($server, $swooleRequest) { - $illuminateRequest = Request::make($swooleRequest)->toIlluminate(); - $this->container['events']->fire('swoole.onOpen', $illuminateRequest); + $this->container['events']->fire('swoole.onOpen', $swooleRequest); - $illuminateRequest = null; + $swooleRequest = null; } /** @@ -55,7 +54,5 @@ public function onClose($server, $fd) if (array_key_exists('websocket_status', $info) && $info['websocket_status']) { $this->container['events']->fire('swoole.onClose', $fd); } - - $info = null; } } From ab8d8ee9378db7311009663cb022a8b02f06a509 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 25 Feb 2018 23:01:56 +0800 Subject: [PATCH 05/39] remove onClose event from http server --- src/Server/Manager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 82eb8aa1..673e93e0 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -53,7 +53,7 @@ class Manager * @var array */ protected $events = [ - 'start', 'shutDown', 'workerStart', 'workerStop', 'packet', 'close', + 'start', 'shutDown', 'workerStart', 'workerStop', 'packet', 'bufferFull', 'bufferEmpty', 'task', 'finish', 'pipeMessage', 'workerError', 'managerStart', 'managerStop', 'request', ]; From be1bd854f58f30083dbc18fb9994b88f7af9ddf9 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Mon, 26 Feb 2018 11:38:49 +0800 Subject: [PATCH 06/39] clear events instance in worker start in case of repeated listeners in worker process --- src/Server/Manager.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 673e93e0..79512fd5 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -179,6 +179,9 @@ public function onWorkerStart() $this->container['events']->fire('swoole.workerStart', func_get_args()); + // clear events instance in case of repeated listeners in worker process + Facade::clearResolvedInstance('events'); + $this->createApplication(); $this->setLaravelApp(); $this->bindSwooleServer(); From 8766265a66d7e08ebae8bfb5bbc79cc3982cc992 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Mon, 26 Feb 2018 11:43:49 +0800 Subject: [PATCH 07/39] add facade namespace --- src/Server/Manager.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 79512fd5..f0823c53 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -2,10 +2,11 @@ namespace SwooleTW\Http\Server; -use Illuminate\Contracts\Container\Container; use Swoole\Http\Server as HttpServer; -use Swoole\WebSocket\Server as WebSocketServer; +use Illuminate\Support\Facades\Facade; +use Illuminate\Contracts\Container\Container; use SwooleTW\Http\Server\Traits\CanWebsocket; +use Swoole\WebSocket\Server as WebSocketServer; class Manager { From 3ab11192f0cdaf61dde307764d68770b5bf549d5 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Mon, 26 Feb 2018 21:00:10 +0800 Subject: [PATCH 08/39] delete redundant code --- src/Server/Traits/CanWebsocket.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php index 56d2122e..c7c12681 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Traits/CanWebsocket.php @@ -27,8 +27,6 @@ trait CanWebsocket public function onOpen($server, $swooleRequest) { $this->container['events']->fire('swoole.onOpen', $swooleRequest); - - $swooleRequest = null; } /** From 1b3b07a306b6c685d8096a7a71b5a1e835107cbd Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Mon, 26 Feb 2018 21:28:29 +0800 Subject: [PATCH 09/39] add more dedug info to infos command --- src/Commands/HttpServerCommand.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Commands/HttpServerCommand.php b/src/Commands/HttpServerCommand.php index ed62e4b6..d5303c3c 100644 --- a/src/Commands/HttpServerCommand.php +++ b/src/Commands/HttpServerCommand.php @@ -176,13 +176,21 @@ protected function showInfos() { $pid = $this->getPid(); $isRunning = $this->isRunning($pid); + $host = $this->configs['server']['host']; + $port = $this->configs['server']['port']; + $isWebsocket = $this->configs['websocket']['enabled']; + $logFile = $this->configs['server']['options']['log_file']; $this->table(['Name', 'Value'], [ ['PHP Version', 'Version' => phpversion()], ['Swoole Version', 'Version' => swoole_version()], ['Laravel Version', $this->getApplication()->getVersion()], ['Server Status', $isRunning ? 'Online' : 'Offline'], + ['Listen IP', $host], + ['Listen Port', $port], + ['Websocket Mode', $isWebsocket ? 'On' : 'Off'], ['PID', $isRunning ? $pid : 'None'], + ['Log Path', $logFile], ]); } From d2808f3412af9132475130bf88a7a2b6df7ebd88 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 14:55:17 +0800 Subject: [PATCH 10/39] add table room for websockets --- src/Server/Room/RoomContract.php | 57 +++++++++++++ src/Server/Room/TableRoom.php | 137 +++++++++++++++++++++++++++++++ tests/Server/TableRoomTest.php | 116 ++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 src/Server/Room/RoomContract.php create mode 100644 src/Server/Room/TableRoom.php create mode 100644 tests/Server/TableRoomTest.php diff --git a/src/Server/Room/RoomContract.php b/src/Server/Room/RoomContract.php new file mode 100644 index 00000000..67747b3e --- /dev/null +++ b/src/Server/Room/RoomContract.php @@ -0,0 +1,57 @@ +initRoomsTable(); + $this->initSidsTable(); + } + + public function add(int $fd, string $room) + { + $this->addAll($fd, [$room]); + } + + public function addAll(int $fd, array $roomNames) + { + $rooms = $this->getRooms($fd); + + foreach ($roomNames as $room) { + $room = $this->encode($room); + $sids = $this->getClients($room, false); + + if (in_array($fd, $sids)) { + continue; + } + + $sids[] = $fd; + $rooms[] = $room; + + $this->setValue($room, $sids, 'rooms'); + } + + $this->setValue($fd, $rooms, 'sids'); + } + + public function delete(int $fd, string $room) + { + $this->deleteAll($fd, [$room]); + } + + public function deleteAll(int $fd, array $roomNames = []) + { + $allRooms = $this->getRooms($fd); + $rooms = count($roomNames) ? $this->encode($roomNames) : $allRooms; + + $removeRooms = []; + foreach ($rooms as $room) { + $sids = $this->getClients($room, false); + + if (! in_array($fd, $sids)) { + continue; + } + + $this->setValue($room, array_values(array_diff($sids, [$fd])), 'rooms'); + $removeRooms[] = $room; + } + + $this->setValue($fd, array_values(array_diff($allRooms, $removeRooms)), 'sids'); + } + + public function getClients(string $room, $hash = true) + { + if ($hash) { + $room = $this->encode($room); + } + + return $this->getValue($room, 'rooms'); + } + + public function getRooms(int $fd) + { + return $this->getValue($fd, 'sids'); + } + + protected function initRoomsTable() + { + $this->rooms = new Table(2048); + $this->rooms->column('value', Table::TYPE_STRING, 2048); + $this->rooms->create(); + } + + protected function initSidsTable() + { + $this->sids = new Table(8192); + $this->sids->column('value', Table::TYPE_STRING, 2048); + $this->sids->create(); + } + + protected function encode($keys) + { + if (is_array($keys)) { + $result = []; + foreach ($keys as $value) { + $result[] = md5($value); + } + + return $result; + } + + return md5($keys); + } + + public function setValue($key, array $value, $table) + { + $this->checkTable($table); + + $this->$table->set($key, [ + 'value' => json_encode($value) + ]); + } + + public function getValue($key, $table) + { + $this->checkTable($table); + + $value = $this->$table->get($key); + + return $value ? json_decode($value['value'], true) : []; + } + + protected function checkTable($table) + { + if (! property_exists($this, $table) || ! $this->$table instanceof Table) { + throw new \InvalidArgumentException('invalid table name.'); + } + } +} diff --git a/tests/Server/TableRoomTest.php b/tests/Server/TableRoomTest.php new file mode 100644 index 00000000..916cb8fb --- /dev/null +++ b/tests/Server/TableRoomTest.php @@ -0,0 +1,116 @@ +tableRoom = new TableRoom(); + $this->tableRoom->prepare(); + } + + public function testPrepare() + { + $reflection = new \ReflectionClass($this->tableRoom); + $method = $reflection->getMethod('prepare'); + $method->invoke($this->tableRoom); + + $rooms = $reflection->getProperty('rooms'); + $rooms->setAccessible(true); + + $sids = $reflection->getProperty('sids'); + $sids->setAccessible(true); + + $this->assertInstanceOf(Table::class, $rooms->getValue($this->tableRoom)); + $this->assertInstanceOf(Table::class, $sids->getValue($this->tableRoom)); + } + + public function testInvalidTableName() + { + $this->expectException(\InvalidArgumentException::class); + + $this->tableRoom->getValue(1, 'foo'); + } + + public function testSetValue() + { + $this->tableRoom->setValue($key = 1, $value = ['foo', 'bar'], $table = 'sids'); + + $this->assertSame($value, $this->tableRoom->getValue($key, $table)); + } + + public function testAddAll() + { + $this->tableRoom->addAll($key = 1, $values = ['foo', 'bar']); + + $this->assertSame($this->encode($values), $this->tableRoom->getValue($key, $table = 'sids')); + $this->assertSame([$key], $this->tableRoom->getValue($this->encode('foo'), 'rooms')); + $this->assertSame([$key], $this->tableRoom->getValue($this->encode('bar'), 'rooms')); + } + + public function testAdd() + { + $this->tableRoom->add($key = 1, $value = 'foo'); + + $this->assertSame([$this->encode($value)], $this->tableRoom->getValue($key, $table = 'sids')); + $this->assertSame([$key], $this->tableRoom->getValue($this->encode($value), 'rooms')); + } + + public function testDeleteAll() + { + $this->tableRoom->addAll($key = 1, $values = ['foo', 'bar']); + $this->tableRoom->deleteAll($key); + + $this->assertSame([], $this->tableRoom->getValue($key, $table = 'sids')); + $this->assertSame([], $this->tableRoom->getValue($this->encode('foo'), 'rooms')); + $this->assertSame([], $this->tableRoom->getValue($this->encode('bar'), 'rooms')); + } + + public function testDelete() + { + $this->tableRoom->addAll($key = 1, $values = ['foo', 'bar']); + $this->tableRoom->delete($key, 'foo'); + + $this->assertSame([$this->encode('bar')], $this->tableRoom->getValue($key, $table = 'sids')); + $this->assertSame([], $this->tableRoom->getValue($this->encode('foo'), 'rooms')); + $this->assertSame([$key], $this->tableRoom->getValue($this->encode('bar'), 'rooms')); + } + + public function testGetRooms() + { + $this->tableRoom->addAll($key = 1, $values = ['foo', 'bar']); + + $this->assertSame( + $this->tableRoom->getValue($key, $table = 'sids'), + $this->tableRoom->getRooms($key) + ); + } + + public function testGetClients() + { + $keys = [1, 2]; + $this->tableRoom->add($keys[0], $room = 'foo'); + $this->tableRoom->add($keys[1], $room); + + $this->assertSame( + $this->tableRoom->getValue($this->encode($room), $table = 'rooms'), + $this->tableRoom->getClients($room) + ); + } + + protected function encode($keys) + { + $reflection = new \ReflectionClass($this->tableRoom); + $method = $reflection->getMethod('encode'); + $method->setAccessible(true); + + return $method->invokeArgs($this->tableRoom, [$keys]); + } +} From 2572834d85e67180758f043c43c6ba4dee8393ec Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 15:21:10 +0800 Subject: [PATCH 11/39] add swoole_websocket.php config file --- config/swoole_websocket.php | 25 +++++++++++++++++++++++++ src/HttpServiceProvider.php | 8 +++++--- src/Server/Room/TableRoom.php | 15 +++++++++++---- tests/Server/TableRoomTest.php | 8 +++++++- 4 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 config/swoole_websocket.php diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php new file mode 100644 index 00000000..52b45bcc --- /dev/null +++ b/config/swoole_websocket.php @@ -0,0 +1,25 @@ + 'table', + + /* + |-------------------------------------------------------------------------- + | Drivers Settings + |-------------------------------------------------------------------------- + */ + 'drivers' => [ + + 'table' => [ + 'room_rows' => 2048, + 'room_size' => 2048, + 'client_rows' => 8192, + 'client_size' => 2048 + ] + ] +]; diff --git a/src/HttpServiceProvider.php b/src/HttpServiceProvider.php index db7d5b9b..6e5af29c 100644 --- a/src/HttpServiceProvider.php +++ b/src/HttpServiceProvider.php @@ -21,7 +21,7 @@ abstract class HttpServiceProvider extends ServiceProvider */ public function register() { - $this->mergeConfig(); + $this->mergeConfigs(); $this->registerManager(); $this->registerCommands(); } @@ -41,16 +41,18 @@ abstract protected function registerManager(); public function boot() { $this->publishes([ - __DIR__ . '/../config/swoole_http.php' => base_path('config/swoole_http.php') + __DIR__ . '/../config/swoole_http.php' => base_path('config/swoole_http.php'), + __DIR__ . '/../config/swoole_websocket.php' => base_path('config/swoole_websocket.php') ], 'config'); } /** * Merge configurations. */ - protected function mergeConfig() + protected function mergeConfigs() { $this->mergeConfigFrom(__DIR__ . '/../config/swoole_http.php', 'swoole_http'); + $this->mergeConfigFrom(__DIR__ . '/../config/swoole_websocket.php', 'swoole_websocket'); } /** diff --git a/src/Server/Room/TableRoom.php b/src/Server/Room/TableRoom.php index 9c108470..5cd33f94 100644 --- a/src/Server/Room/TableRoom.php +++ b/src/Server/Room/TableRoom.php @@ -7,10 +7,17 @@ class TableRoom implements RoomContract { + protected $config; + protected $rooms; protected $sids; + public function __construct(array $config) + { + $this->config = $config; + } + public function prepare() { $this->initRoomsTable(); @@ -84,15 +91,15 @@ public function getRooms(int $fd) protected function initRoomsTable() { - $this->rooms = new Table(2048); - $this->rooms->column('value', Table::TYPE_STRING, 2048); + $this->rooms = new Table($this->config['room_rows']); + $this->rooms->column('value', Table::TYPE_STRING, $this->config['room_size']); $this->rooms->create(); } protected function initSidsTable() { - $this->sids = new Table(8192); - $this->sids->column('value', Table::TYPE_STRING, 2048); + $this->sids = new Table($this->config['client_rows']); + $this->sids->column('value', Table::TYPE_STRING, $this->config['client_size']); $this->sids->create(); } diff --git a/tests/Server/TableRoomTest.php b/tests/Server/TableRoomTest.php index 916cb8fb..1fe0b397 100644 --- a/tests/Server/TableRoomTest.php +++ b/tests/Server/TableRoomTest.php @@ -12,7 +12,13 @@ class TableRoomTest extends TestCase public function setUp() { - $this->tableRoom = new TableRoom(); + $config = [ + 'room_rows' => 2048, + 'room_size' => 2048, + 'client_rows' => 8192, + 'client_size' => 2048 + ]; + $this->tableRoom = new TableRoom($config); $this->tableRoom->prepare(); } From 6927b9d681b46e7ac31bfd04db916fbcce45acb2 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 15:28:07 +0800 Subject: [PATCH 12/39] midify default room_rows --- config/swoole_websocket.php | 2 +- tests/Server/TableRoomTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php index 52b45bcc..faf84b48 100644 --- a/config/swoole_websocket.php +++ b/config/swoole_websocket.php @@ -16,7 +16,7 @@ 'drivers' => [ 'table' => [ - 'room_rows' => 2048, + 'room_rows' => 4096, 'room_size' => 2048, 'client_rows' => 8192, 'client_size' => 2048 diff --git a/tests/Server/TableRoomTest.php b/tests/Server/TableRoomTest.php index 1fe0b397..7c7a81cb 100644 --- a/tests/Server/TableRoomTest.php +++ b/tests/Server/TableRoomTest.php @@ -13,7 +13,7 @@ class TableRoomTest extends TestCase public function setUp() { $config = [ - 'room_rows' => 2048, + 'room_rows' => 4096, 'room_size' => 2048, 'client_rows' => 8192, 'client_size' => 2048 From 0fd9c7ed2ac602bdf8f991a0d243a270d1a5f1b4 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 16:03:30 +0800 Subject: [PATCH 13/39] set websocket while server started --- src/Server/Manager.php | 1 + src/Server/Traits/CanWebsocket.php | 21 ++++++++++ src/Server/Websocket.php | 64 ++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 src/Server/Websocket.php diff --git a/src/Server/Manager.php b/src/Server/Manager.php index f0823c53..213ced7b 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -114,6 +114,7 @@ protected function prepareWebsocket() if ($isWebsocket) { array_push($this->events, ...$this->wsEvents); $this->isWebsocket = true; + $this->setWebsocket(); } } diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php index c7c12681..45910b01 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Traits/CanWebsocket.php @@ -3,9 +3,15 @@ namespace SwooleTW\Http\Server\Traits; use SwooleTW\Http\Server\Request; +use SwooleTW\Http\Server\Websocket; trait CanWebsocket { + /** + * @var string + */ + protected $roomNamespace = '\\SwooleTW\Http\Server\Room\\'; + /** * @var boolean */ @@ -53,4 +59,19 @@ public function onClose($server, $fd) $this->container['events']->fire('swoole.onClose', $fd); } } + + /** + * Prepare websocket and room instances. + */ + protected function setWebsocket() + { + $driver = $this->container['config']->get('swoole_websocket.default'); + $configs = $this->container['config']->get("swoole_websocket.drivers.{$driver}"); + $className = $this->roomNamespace . ucfirst($driver) . 'Room'; + + $room = new $className($configs); + call_user_func([$room, 'prepare']); + + $this->websocket = new Websocket($room); + } } diff --git a/src/Server/Websocket.php b/src/Server/Websocket.php new file mode 100644 index 00000000..ad6813fc --- /dev/null +++ b/src/Server/Websocket.php @@ -0,0 +1,64 @@ +room = $room; + } + + public function broadcast() + { + // + } + + // room, fd | array + public function to() + { + // + } + + public function join(string $room) + { + // + } + + public function leave() + { + // + } + + public function on(string $event, callable $callback) + { + // + } + + public function emit(string $event, $data) + { + // + } + + public function in(string $room) + { + // + } +} From 1cf9d6e12fff07a697292d040377c1b8842a039e Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 16:36:01 +0800 Subject: [PATCH 14/39] improve table room --- src/Server/Room/TableRoom.php | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Server/Room/TableRoom.php b/src/Server/Room/TableRoom.php index 5cd33f94..46c670d7 100644 --- a/src/Server/Room/TableRoom.php +++ b/src/Server/Room/TableRoom.php @@ -44,10 +44,10 @@ public function addAll(int $fd, array $roomNames) $sids[] = $fd; $rooms[] = $room; - $this->setValue($room, $sids, 'rooms'); + $this->setClients($room, $sids); } - $this->setValue($fd, $rooms, 'sids'); + $this->setRooms($fd, $rooms); } public function delete(int $fd, string $room) @@ -68,11 +68,11 @@ public function deleteAll(int $fd, array $roomNames = []) continue; } - $this->setValue($room, array_values(array_diff($sids, [$fd])), 'rooms'); + $this->setClients($room, array_values(array_diff($sids, [$fd])), 'rooms'); $removeRooms[] = $room; } - $this->setValue($fd, array_values(array_diff($allRooms, $removeRooms)), 'sids'); + $this->setRooms($fd, array_values(array_diff($allRooms, $removeRooms)), 'sids'); } public function getClients(string $room, $hash = true) @@ -89,6 +89,16 @@ public function getRooms(int $fd) return $this->getValue($fd, 'sids'); } + protected function setClients(string $room, array $sids) + { + return $this->setValue($room, $sids, 'rooms'); + } + + protected function setRooms(int $fd, array $rooms) + { + return $this->setValue($fd, $rooms, 'sids'); + } + protected function initRoomsTable() { $this->rooms = new Table($this->config['room_rows']); @@ -106,12 +116,9 @@ protected function initSidsTable() protected function encode($keys) { if (is_array($keys)) { - $result = []; - foreach ($keys as $value) { - $result[] = md5($value); - } - - return $result; + return array_map(function ($key) { + return md5($key); + }, $keys); } return md5($keys); @@ -124,6 +131,8 @@ public function setValue($key, array $value, $table) $this->$table->set($key, [ 'value' => json_encode($value) ]); + + return $this; } public function getValue($key, $table) From 38e2341ddf0026df9ff7ffad10549613aa065b17 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 16:38:03 +0800 Subject: [PATCH 15/39] add type hinting to the rest of table room --- src/Server/Room/TableRoom.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Server/Room/TableRoom.php b/src/Server/Room/TableRoom.php index 46c670d7..6ed514e5 100644 --- a/src/Server/Room/TableRoom.php +++ b/src/Server/Room/TableRoom.php @@ -124,7 +124,7 @@ protected function encode($keys) return md5($keys); } - public function setValue($key, array $value, $table) + public function setValue($key, array $value, string $table) { $this->checkTable($table); @@ -135,7 +135,7 @@ public function setValue($key, array $value, $table) return $this; } - public function getValue($key, $table) + public function getValue(string $key, string $table) { $this->checkTable($table); @@ -144,7 +144,7 @@ public function getValue($key, $table) return $value ? json_decode($value['value'], true) : []; } - protected function checkTable($table) + protected function checkTable(string $table) { if (! property_exists($this, $table) || ! $this->$table instanceof Table) { throw new \InvalidArgumentException('invalid table name.'); From 327868da4ab5855e06cf0c0f04b2d124488fbb9b Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 22:43:17 +0800 Subject: [PATCH 16/39] catch and log error while on request --- src/Server/Manager.php | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 213ced7b..c5f65ce2 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -2,6 +2,7 @@ namespace SwooleTW\Http\Server; +use Exception; use Swoole\Http\Server as HttpServer; use Illuminate\Support\Facades\Facade; use Illuminate\Contracts\Container\Container; @@ -201,12 +202,18 @@ public function onRequest($swooleRequest, $swooleResponse) // Reset user-customized providers $this->getApplication()->resetProviders(); - $illuminateRequest = Request::make($swooleRequest)->toIlluminate(); - $illuminateResponse = $this->getApplication()->run($illuminateRequest); - $response = Response::make($illuminateResponse, $swooleResponse); - $response->send(); + try { + $illuminateResponse = $this->getApplication()->run($illuminateRequest); + $response = Response::make($illuminateResponse, $swooleResponse); + $response->send(); + } catch (Exception $e) { + $this->logServerError($e); + + $swooleResponse->status(500); + $swooleResponse->end('Oops! An unexpected error occurred.'); + } } /** @@ -319,4 +326,24 @@ protected function setProcessName($process) swoole_set_process_name($name); } + + /** + * Log server error. + * + * @param Exception + */ + protected function logServerError(Exception $e) + { + $logFile = $this->container['config']->get('swoole_http.server.options.log_file'); + + try { + $output = fopen($logFile ,'w'); + } catch (Exception $e) { + $output = STDOUT; + } + + $prefix = sprintf("[%s #%d *%d]\tERROR\t", date('Y-m-d H:i:s'), $this->server->master_pid, $this->server->worker_id); + + fwrite($output, sprintf('%s%s(%d): %s', $prefix, $e->getFile(), $e->getLine(), $e->getMessage()) . PHP_EOL); + } } From 6446b44c599a03cbc90ec0ac553963c85a799d80 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 4 Mar 2018 22:56:22 +0800 Subject: [PATCH 17/39] improve 500 response while on request --- src/Server/Manager.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index c5f65ce2..25eaf417 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -211,8 +211,14 @@ public function onRequest($swooleRequest, $swooleResponse) } catch (Exception $e) { $this->logServerError($e); - $swooleResponse->status(500); - $swooleResponse->end('Oops! An unexpected error occurred.'); + try { + $swooleResponse->status(500); + $swooleResponse->end('Oops! An unexpected error occurred.'); + } catch (Exception $e) { + // Catch: zm_deactivate_swoole: Fatal error: Uncaught exception + // 'ErrorException' with message 'swoole_http_response::status(): + // http client#2 is not exist. + } } } From 264598804d1d3eb5afdb52617a5812120117bb17 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Fri, 13 Apr 2018 18:47:37 +0800 Subject: [PATCH 18/39] implement partial websocket feature --- composer.json | 3 +- config/swoole_websocket.php | 11 ++- src/HttpServiceProvider.php | 34 +++++++- src/Server/Manager.php | 35 +++++++- src/Server/Traits/CanWebsocket.php | 22 +---- src/Server/Websocket.php | 124 ++++++++++++++++++++++++++--- tests/Server/WebsocketTest.php | 93 ++++++++++++++++++++++ tests/TestCase.php | 7 +- 8 files changed, 293 insertions(+), 36 deletions(-) create mode 100644 tests/Server/WebsocketTest.php diff --git a/composer.json b/composer.json index 2f5bd938..0c69bc90 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ }, "require-dev": { "laravel/lumen-framework": "~5.1", - "phpunit/phpunit": "~6.0" + "phpunit/phpunit": "^6.1", + "mockery/mockery": "~1.0" }, "autoload": { "psr-4": { diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php index faf84b48..5bb0fffb 100644 --- a/config/swoole_websocket.php +++ b/config/swoole_websocket.php @@ -10,10 +10,19 @@ /* |-------------------------------------------------------------------------- - | Drivers Settings + | Drivers Mapping |-------------------------------------------------------------------------- */ 'drivers' => [ + 'table' => SwooleTW\Http\Server\Room\TableRoom::class, + ], + + /* + |-------------------------------------------------------------------------- + | Drivers Settings + |-------------------------------------------------------------------------- + */ + 'settings' => [ 'table' => [ 'room_rows' => 4096, diff --git a/src/HttpServiceProvider.php b/src/HttpServiceProvider.php index 6e5af29c..b0dfa580 100644 --- a/src/HttpServiceProvider.php +++ b/src/HttpServiceProvider.php @@ -2,8 +2,10 @@ namespace SwooleTW\Http; -use SwooleTW\Http\Commands\HttpServerCommand; +use SwooleTW\Http\Server\Websocket; use Illuminate\Support\ServiceProvider; +use SwooleTW\Http\Server\Room\RoomContract; +use SwooleTW\Http\Commands\HttpServerCommand; abstract class HttpServiceProvider extends ServiceProvider { @@ -24,6 +26,7 @@ public function register() $this->mergeConfigs(); $this->registerManager(); $this->registerCommands(); + $this->registerWebsocket(); } /** @@ -64,4 +67,33 @@ protected function registerCommands() HttpServerCommand::class, ]); } + + /** + * Register websocket. + */ + protected function registerWebsocket() + { + if (! $this->app['config']->get('swoole_http.websocket.enabled')) { + return; + } + + // bind room instance + $this->app->singleton(RoomContract::class, function ($app) { + $driver = $app['config']->get('swoole_websocket.default'); + $configs = $app['config']->get("swoole_websocket.settings.{$driver}"); + $className = $app['config']->get("swoole_websocket.drivers.{$driver}"); + + $room = new $className($configs); + $room->prepare(); + + return $room; + }); + $this->app->alias(RoomContract::class, 'swoole.room'); + + // bind websocket instance + $this->app->singleton(Websocket::class, function ($app) { + return new Websocket($app['swoole.room']); + }); + $this->app->alias(Websocket::class, 'swoole.websocket'); + } } diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 25eaf417..7abfbfda 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -115,7 +115,6 @@ protected function prepareWebsocket() if ($isWebsocket) { array_push($this->events, ...$this->wsEvents); $this->isWebsocket = true; - $this->setWebsocket(); } } @@ -198,7 +197,7 @@ public function onWorkerStart() */ public function onRequest($swooleRequest, $swooleResponse) { - $this->container['events']->fire('swoole.onRequest'); + $this->container['events']->fire('swoole.request'); // Reset user-customized providers $this->getApplication()->resetProviders(); @@ -222,6 +221,38 @@ public function onRequest($swooleRequest, $swooleResponse) } } + /** + * Set onTask listener. + */ + public function onTask(HttpServer $server, $taskId, $fromId, $data) + { + $this->container['events']->fire('swoole.task', func_get_args()); + + // push websocket message + if ($data['action'] === 'push') { + $opcode = $data['opcode'] ?? 1; + $sender = $data['sender'] ?? 0; + $fds = $data['fds'] ?? []; + $broadcast = $data['broadcast'] ?? false; + $message = is_array($data['message']) ? json_encode($data['message']) : $data['message']; + + foreach($fds as $fd) { + if ($broadcast && $sender === (integer) $fd) { + continue; + } + $server->push($fd, $message, $opcode); + } + } + } + + /** + * Set onFinish listener. + */ + public function onFinish(HttpServer $server, $taskId, $data) + { + // task worker callback + } + /** * Set onShutdown listener. */ diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php index 45910b01..0c148d95 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Traits/CanWebsocket.php @@ -3,15 +3,9 @@ namespace SwooleTW\Http\Server\Traits; use SwooleTW\Http\Server\Request; -use SwooleTW\Http\Server\Websocket; trait CanWebsocket { - /** - * @var string - */ - protected $roomNamespace = '\\SwooleTW\Http\Server\Room\\'; - /** * @var boolean */ @@ -43,6 +37,7 @@ public function onOpen($server, $swooleRequest) */ public function onMessage($server, $frame) { + $this->container['swoole.websocket']->setSender($frame->fd); $this->container['events']->fire('swoole.onMessage', $frame); } @@ -59,19 +54,4 @@ public function onClose($server, $fd) $this->container['events']->fire('swoole.onClose', $fd); } } - - /** - * Prepare websocket and room instances. - */ - protected function setWebsocket() - { - $driver = $this->container['config']->get('swoole_websocket.default'); - $configs = $this->container['config']->get("swoole_websocket.drivers.{$driver}"); - $className = $this->roomNamespace . ucfirst($driver) . 'Room'; - - $room = new $className($configs); - call_user_func([$room, 'prepare']); - - $this->websocket = new Websocket($room); - } } diff --git a/src/Server/Websocket.php b/src/Server/Websocket.php index ad6813fc..ea9dd47c 100644 --- a/src/Server/Websocket.php +++ b/src/Server/Websocket.php @@ -6,7 +6,7 @@ class Websocket { - protected $isBroadcast; + protected $isBroadcast = false; protected $sender; @@ -28,23 +28,73 @@ public function __construct(RoomContract $room) public function broadcast() { - // + $this->isBroadcast = true; + + return $this; } - // room, fd | array - public function to() + /** + * @param integer, string (fd or room) + */ + public function to($value) { - // + $this->toAll([$value]); + + return $this; + } + + /** + * @param array (fds or rooms) + */ + public function toAll(array $values) + { + foreach ($values as $value) { + if (! in_array($value, $this->to)) { + $this->to[] = $value; + } + } + + return $this; } + /** + * @param string + */ public function join(string $room) { - // + $this->room->add($this->sender, $room); + + return $this; } - public function leave() + /** + * @param array + */ + public function joinAll(array $rooms) { - // + $this->room->addAll($this->sender, $rooms); + + return $this; + } + + /** + * @param string + */ + public function leave(string $room) + { + $this->room->delete($this->sender, $room); + + return $this; + } + + /** + * @param array + */ + public function leaveAll(array $rooms) + { + $this->room->deleteAll($this->sender, $rooms); + + return $this; } public function on(string $event, callable $callback) @@ -54,11 +104,67 @@ public function on(string $event, callable $callback) public function emit(string $event, $data) { - // + app('swoole.server')->task([ + 'action' => 'push', + 'data' => [ + 'sender' => $this->sender, + 'fds' => $this->getFds(), + 'broadcast' => $this->isBroadcast, + 'message' => [ + 'event' => $event, + 'data' => $data + ] + ] + ]); + + $this->cleanData(); } public function in(string $room) { // } + + public function setSender(int $fd) + { + $this->sender = $fd; + + return $this; + } + + public function getSender() + { + return $this->sender; + } + + public function getIsBroadcast() + { + return $this->isBroadcast; + } + + public function getTo() + { + return $this->to; + } + + protected function getFds() + { + $fds = array_filter($this->to, function ($value) { + return is_integer($value); + }); + $rooms = array_diff($this->to, $fds); + + foreach ($rooms as $room) { + $fds = array_unique(array_merge($fds, $this->room->getClients($room))); + } + + return array_values($fds); + } + + protected function cleanData() + { + $this->isBroadcast = false; + $this->sender = null; + $this->to = []; + } } diff --git a/tests/Server/WebsocketTest.php b/tests/Server/WebsocketTest.php new file mode 100644 index 00000000..8069afd5 --- /dev/null +++ b/tests/Server/WebsocketTest.php @@ -0,0 +1,93 @@ +getWebsocket(); + $this->assertFalse($websocket->getIsBroadcast()); + + $websocket->broadcast(); + $this->assertTrue($websocket->getIsBroadcast()); + } + + public function testSetTo() + { + $websocket = $this->getWebsocket()->to($foo = 'foo'); + $this->assertTrue(in_array($foo, $websocket->getTo())); + + $websocket->toAll($bar = ['foo', 'bar', 'seafood']); + $this->assertSame($bar, $websocket->getTo()); + } + + public function testSetSender() + { + $websocket = $this->getWebsocket()->setSender($fd = 1); + $this->assertSame($fd, $websocket->getSender()); + } + + public function testJoin() + { + $room = m::mock(RoomContract::class); + $room->shouldReceive('add') + ->with($sender = 1, $name = 'room') + ->once(); + + $websocket = $this->getWebsocket($room) + ->setSender($sender) + ->join($name); + } + + public function testJoinAll() + { + $room = m::mock(RoomContract::class); + $room->shouldReceive('addAll') + ->with($sender = 1, $names = ['room1', 'room2']) + ->once(); + + $websocket = $this->getWebsocket($room) + ->setSender($sender) + ->joinAll($names); + } + + public function testLeave() + { + $room = m::mock(RoomContract::class); + $room->shouldReceive('delete') + ->with($sender = 1, $name = 'room') + ->once(); + + $websocket = $this->getWebsocket($room) + ->setSender($sender) + ->leave($name); + } + + public function testLeaveAll() + { + $room = m::mock(RoomContract::class); + $room->shouldReceive('deleteAll') + ->with($sender = 1, $names = ['room1', 'room2']) + ->once(); + + $websocket = $this->getWebsocket($room) + ->setSender($sender) + ->leaveAll($names); + } + + protected function getWebsocket(RoomContract $room = null) + { + return new Websocket($room ?? m::mock(RoomContract::class)); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index f03bbb42..71615442 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -6,5 +6,10 @@ class TestCase extends BaseTestCase { - + public function tearDown() + { + $this->addToAssertionCount( + \Mockery::getContainer()->mockery_getExpectationCount() + ); + } } From 71f1274f38c3f5fa66dc85470844965eb4ce8898 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Fri, 13 Apr 2018 18:52:23 +0800 Subject: [PATCH 19/39] implement in function for websocket --- src/Server/Websocket.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Server/Websocket.php b/src/Server/Websocket.php index ea9dd47c..2578aeda 100644 --- a/src/Server/Websocket.php +++ b/src/Server/Websocket.php @@ -122,7 +122,9 @@ public function emit(string $event, $data) public function in(string $room) { - // + $this->join($room); + + return $this; } public function setSender(int $fd) From a4efc895a43ed578cca381cff99a1243f23b15cb Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Fri, 13 Apr 2018 20:34:07 +0800 Subject: [PATCH 20/39] try to fix pecl install --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6af4ba4f..df45e832 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,10 +66,10 @@ matrix: env: FRAMEWORK_VERSION=laravel/lumen-framework:5.5.* before_install: - - pecl install swoole + - printf "\n" | pecl install swoole install: - composer require "${FRAMEWORK_VERSION}" --no-update -n - travis_retry composer install --no-suggest --prefer-dist -n -o -script: vendor/bin/phpunit \ No newline at end of file +script: vendor/bin/phpunit From 381e9fd8ee806275533de5ab9e982cce8cbcb3cd Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Fri, 13 Apr 2018 22:59:59 +0800 Subject: [PATCH 21/39] add websocket formatter --- config/swoole_websocket.php | 9 +++- src/HttpServiceProvider.php | 4 +- src/Server/Manager.php | 18 ++----- src/Server/Traits/CanWebsocket.php | 49 +++++++++++++++++++ .../Websocket/Formatter/DefaultFormatter.php | 27 ++++++++++ .../Websocket/Formatter/FormatterContract.php | 16 ++++++ .../{ => Websocket}/Room/RoomContract.php | 2 +- src/Server/{ => Websocket}/Room/TableRoom.php | 4 +- src/Server/{ => Websocket}/Websocket.php | 17 ++++--- tests/Server/TableRoomTest.php | 2 +- tests/Server/WebsocketTest.php | 4 +- 11 files changed, 122 insertions(+), 30 deletions(-) create mode 100644 src/Server/Websocket/Formatter/DefaultFormatter.php create mode 100644 src/Server/Websocket/Formatter/FormatterContract.php rename src/Server/{ => Websocket}/Room/RoomContract.php (95%) rename src/Server/{ => Websocket}/Room/TableRoom.php (97%) rename src/Server/{ => Websocket}/Websocket.php (89%) diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php index 5bb0fffb..b27febd9 100644 --- a/config/swoole_websocket.php +++ b/config/swoole_websocket.php @@ -8,13 +8,20 @@ */ 'default' => 'table', + /* + |-------------------------------------------------------------------------- + | Default Message Formatter for Websocket + |-------------------------------------------------------------------------- + */ + 'formatter' => SwooleTW\Http\Server\Websocket\Formatter\DefaultFormatter::class, + /* |-------------------------------------------------------------------------- | Drivers Mapping |-------------------------------------------------------------------------- */ 'drivers' => [ - 'table' => SwooleTW\Http\Server\Room\TableRoom::class, + 'table' => SwooleTW\Http\Server\Websocket\Room\TableRoom::class, ], /* diff --git a/src/HttpServiceProvider.php b/src/HttpServiceProvider.php index b0dfa580..a8de1c75 100644 --- a/src/HttpServiceProvider.php +++ b/src/HttpServiceProvider.php @@ -2,10 +2,10 @@ namespace SwooleTW\Http; -use SwooleTW\Http\Server\Websocket; use Illuminate\Support\ServiceProvider; -use SwooleTW\Http\Server\Room\RoomContract; use SwooleTW\Http\Commands\HttpServerCommand; +use SwooleTW\Http\Server\Websocket\Websocket; +use SwooleTW\Http\Server\Websocket\Room\RoomContract; abstract class HttpServiceProvider extends ServiceProvider { diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 7abfbfda..30f4eedc 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Facade; use Illuminate\Contracts\Container\Container; use SwooleTW\Http\Server\Traits\CanWebsocket; +use SwooleTW\Http\Server\Websocket\Websocket; use Swoole\WebSocket\Server as WebSocketServer; class Manager @@ -111,10 +112,12 @@ protected function initialize() protected function prepareWebsocket() { $isWebsocket = $this->container['config']->get('swoole_http.websocket.enabled'); + $formatter = $this->container['config']->get('swoole_http.websocket.formatter'); if ($isWebsocket) { array_push($this->events, ...$this->wsEvents); $this->isWebsocket = true; + $this->setFormatter(new $formatter); } } @@ -229,19 +232,8 @@ public function onTask(HttpServer $server, $taskId, $fromId, $data) $this->container['events']->fire('swoole.task', func_get_args()); // push websocket message - if ($data['action'] === 'push') { - $opcode = $data['opcode'] ?? 1; - $sender = $data['sender'] ?? 0; - $fds = $data['fds'] ?? []; - $broadcast = $data['broadcast'] ?? false; - $message = is_array($data['message']) ? json_encode($data['message']) : $data['message']; - - foreach($fds as $fd) { - if ($broadcast && $sender === (integer) $fd) { - continue; - } - $server->push($fd, $message, $opcode); - } + if (array_key_exists('action', $data) && $data['action'] === Websocket::PUSH_ACTION) { + $this->pushMessage($server, $data['data'] ?? []); } } diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php index 0c148d95..04990b4b 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Traits/CanWebsocket.php @@ -3,6 +3,7 @@ namespace SwooleTW\Http\Server\Traits; use SwooleTW\Http\Server\Request; +use SwooleTW\Http\Server\Websocket\Formatter\FormatterContract; trait CanWebsocket { @@ -11,6 +12,11 @@ trait CanWebsocket */ protected $isWebsocket = false; + /** + * @var SwooleTW\Http\Server\Websocket\Formatter\FormatterContract + */ + protected $formatter; + /** * Websocket server events. * @@ -52,6 +58,49 @@ public function onClose($server, $fd) $info = $server->connection_info($fd); if (array_key_exists('websocket_status', $info) && $info['websocket_status']) { $this->container['events']->fire('swoole.onClose', $fd); + $this->container['swoole.websocket']->setSender($fd)->leaveAll(); + } + } + + /** + * Push websocket message to clients. + * + * @param \Swoole\Websocket\Server $server + * @param mixed $data + */ + public function pushMessage($server, array $data) + { + $opcode = $data['opcode'] ?? 1; + $sender = $data['sender'] ?? 0; + $fds = $data['fds'] ?? []; + $broadcast = $data['broadcast'] ?? false; + $message = $this->formatter->output($data['message']); + + foreach ($fds as $fd) { + if ($broadcast && $sender === (integer) $fd) { + continue; + } + $server->push($fd, $message, $opcode); } } + + /** + * Set message formatter for websocket. + * + * @param \SwooleTW\Http\Server\Websocket\Formatter\FormatterContract $formatter + */ + public function setFormatter(FormatterContract $formatter) + { + $this->formatter = $formatter; + + return $this; + } + + /** + * Get message formatter for websocket. + */ + public function getFormatter() + { + return $this->formatter; + } } diff --git a/src/Server/Websocket/Formatter/DefaultFormatter.php b/src/Server/Websocket/Formatter/DefaultFormatter.php new file mode 100644 index 00000000..65c5905b --- /dev/null +++ b/src/Server/Websocket/Formatter/DefaultFormatter.php @@ -0,0 +1,27 @@ + $event, + 'data' => $data + ]; + } + + /** + * Output message for websocket push. + */ + public function output($data) + { + return is_array($data) ? json_encode($data) : $data; + } +} diff --git a/src/Server/Websocket/Formatter/FormatterContract.php b/src/Server/Websocket/Formatter/FormatterContract.php new file mode 100644 index 00000000..ec8c9490 --- /dev/null +++ b/src/Server/Websocket/Formatter/FormatterContract.php @@ -0,0 +1,16 @@ +room->deleteAll($this->sender, $rooms); @@ -105,15 +107,14 @@ public function on(string $event, callable $callback) public function emit(string $event, $data) { app('swoole.server')->task([ - 'action' => 'push', + 'action' => static::PUSH_ACTION, 'data' => [ 'sender' => $this->sender, 'fds' => $this->getFds(), 'broadcast' => $this->isBroadcast, - 'message' => [ - 'event' => $event, - 'data' => $data - ] + 'message' => app('swoole.http') + ->getFormatter() + ->input($event, $data) ] ]); diff --git a/tests/Server/TableRoomTest.php b/tests/Server/TableRoomTest.php index 7c7a81cb..e474f8df 100644 --- a/tests/Server/TableRoomTest.php +++ b/tests/Server/TableRoomTest.php @@ -4,7 +4,7 @@ use Swoole\Table; use SwooleTW\Http\Tests\TestCase; -use SwooleTW\Http\Server\Room\TableRoom; +use SwooleTW\Http\Server\Websocket\Room\TableRoom; class TableRoomTest extends TestCase { diff --git a/tests/Server/WebsocketTest.php b/tests/Server/WebsocketTest.php index 8069afd5..9baea2fe 100644 --- a/tests/Server/WebsocketTest.php +++ b/tests/Server/WebsocketTest.php @@ -4,8 +4,8 @@ use Mockery as m; use SwooleTW\Http\Tests\TestCase; -use SwooleTW\Http\Server\Websocket; -use SwooleTW\Http\Server\Room\RoomContract; +use SwooleTW\Http\Server\Websocket\Websocket; +use SwooleTW\Http\Server\Websocket\Room\RoomContract; class WebsocketTest extends TestCase { From 53bb54a2e1bc2a2d706aec749bc0c3bc9aa7bb0c Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 00:24:31 +0800 Subject: [PATCH 22/39] improve pushMessage for websocket --- config/swoole_http.php | 1 + src/Server/Traits/CanWebsocket.php | 30 +++++++++++++++++-- .../Websocket/Formatter/DefaultFormatter.php | 16 +++------- .../Websocket/Formatter/FormatterContract.php | 7 +---- src/Server/Websocket/Websocket.php | 5 ++-- 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/config/swoole_http.php b/config/swoole_http.php index 447789ff..48afc951 100644 --- a/config/swoole_http.php +++ b/config/swoole_http.php @@ -16,6 +16,7 @@ 'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('storage/logs/swoole_http.pid')), 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')), 'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', false), + 'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', 4) ], ], 'websocket' => [ diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Traits/CanWebsocket.php index 04990b4b..54712838 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Traits/CanWebsocket.php @@ -55,8 +55,7 @@ public function onMessage($server, $frame) */ public function onClose($server, $fd) { - $info = $server->connection_info($fd); - if (array_key_exists('websocket_status', $info) && $info['websocket_status']) { + if ($this->isWebsocket($fd)) { $this->container['events']->fire('swoole.onClose', $fd); $this->container['swoole.websocket']->setSender($fd)->leaveAll(); } @@ -74,7 +73,22 @@ public function pushMessage($server, array $data) $sender = $data['sender'] ?? 0; $fds = $data['fds'] ?? []; $broadcast = $data['broadcast'] ?? false; - $message = $this->formatter->output($data['message']); + $event = $data['event'] ?? null; + $message = $this->formatter->output($event, $data['message']); + + // attach sender if not broadcast + if (! $broadcast && ! in_array($sender, $fds)) { + $fds[] = $sender; + } + + // check if to broadcast all clients + if ($broadcast && empty($fds)) { + foreach ($server->connections as $fd) { + if ($this->isWebsocket($fd)) { + $fds[] = $fd; + } + } + } foreach ($fds as $fd) { if ($broadcast && $sender === (integer) $fd) { @@ -103,4 +117,14 @@ public function getFormatter() { return $this->formatter; } + + /** + * Check if is a websocket fd. + */ + protected function isWebsocket(int $fd) + { + $info = $this->server->connection_info($fd); + + return array_key_exists('websocket_status', $info) && $info['websocket_status']; + } } diff --git a/src/Server/Websocket/Formatter/DefaultFormatter.php b/src/Server/Websocket/Formatter/DefaultFormatter.php index 65c5905b..ea414769 100644 --- a/src/Server/Websocket/Formatter/DefaultFormatter.php +++ b/src/Server/Websocket/Formatter/DefaultFormatter.php @@ -7,21 +7,13 @@ class DefaultFormatter implements FormatterContract { /** - * Input from emit task. + * Output message for websocket push. */ - public function input($event, $data) + public function output($event, $data) { - return [ + return json_encode([ 'event' => $event, 'data' => $data - ]; - } - - /** - * Output message for websocket push. - */ - public function output($data) - { - return is_array($data) ? json_encode($data) : $data; + ]); } } diff --git a/src/Server/Websocket/Formatter/FormatterContract.php b/src/Server/Websocket/Formatter/FormatterContract.php index ec8c9490..aed860cc 100644 --- a/src/Server/Websocket/Formatter/FormatterContract.php +++ b/src/Server/Websocket/Formatter/FormatterContract.php @@ -4,13 +4,8 @@ interface FormatterContract { - /** - * Input from emit task - */ - public function input($event, $data); - /** * Outpur for websocket push */ - public function output($data); + public function output($event, $data); } diff --git a/src/Server/Websocket/Websocket.php b/src/Server/Websocket/Websocket.php index 2758140d..951103b2 100644 --- a/src/Server/Websocket/Websocket.php +++ b/src/Server/Websocket/Websocket.php @@ -112,9 +112,8 @@ public function emit(string $event, $data) 'sender' => $this->sender, 'fds' => $this->getFds(), 'broadcast' => $this->isBroadcast, - 'message' => app('swoole.http') - ->getFormatter() - ->input($event, $data) + 'event' => $event, + 'message' => $data ] ]); From 132196f36d0485cfa752f9f317915e7b4d1151ba Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 00:29:45 +0800 Subject: [PATCH 23/39] install PHPUnitPrettyResultPrinter --- composer.json | 3 ++- phpunit.xml.dist | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 0c69bc90..18005584 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "illuminate/console": "~5.1", "illuminate/contracts": "~5.1", "illuminate/http": "~5.1", - "illuminate/support": "~5.1" + "illuminate/support": "~5.1", + "codedungeon/phpunit-result-printer": "^0.14.0" }, "require-dev": { "laravel/lumen-framework": "~5.1", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 8f339800..6c4a1dbb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,7 +14,7 @@ stopOnError="false" stopOnFailure="false" verbose="true" -> + printerClass="Codedungeon\PHPUnitPrettyResultPrinter\Printer"> ./tests From 0abbf3c600d7e7e899db5397f607faa4873625c2 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 10:02:23 +0800 Subject: [PATCH 24/39] move canWebsocket --- src/Server/Manager.php | 2 +- src/Server/{Traits => Websocket}/CanWebsocket.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Server/{Traits => Websocket}/CanWebsocket.php (98%) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 30f4eedc..776a1912 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -5,8 +5,8 @@ use Exception; use Swoole\Http\Server as HttpServer; use Illuminate\Support\Facades\Facade; +use SwooleTW\Http\Server\CanWebsocket; use Illuminate\Contracts\Container\Container; -use SwooleTW\Http\Server\Traits\CanWebsocket; use SwooleTW\Http\Server\Websocket\Websocket; use Swoole\WebSocket\Server as WebSocketServer; diff --git a/src/Server/Traits/CanWebsocket.php b/src/Server/Websocket/CanWebsocket.php similarity index 98% rename from src/Server/Traits/CanWebsocket.php rename to src/Server/Websocket/CanWebsocket.php index 54712838..7c3ab926 100644 --- a/src/Server/Traits/CanWebsocket.php +++ b/src/Server/Websocket/CanWebsocket.php @@ -1,6 +1,6 @@ Date: Sat, 14 Apr 2018 15:49:14 +0800 Subject: [PATCH 25/39] refactor websocket --- composer.json | 2 +- config/swoole_websocket.php | 32 ++++++-- src/Server/Manager.php | 4 +- src/Server/Websocket/CanWebsocket.php | 82 ++++++++++++++++--- .../Websocket/Formatter/DefaultFormatter.php | 19 ----- .../Websocket/Formatter/FormatterContract.php | 11 --- .../Websocket/Formatters/DefaultFormatter.php | 38 +++++++++ .../Formatters/FormatterContract.php | 21 +++++ src/Server/Websocket/HandlerContract.php | 33 ++++++++ .../{Room => Rooms}/RoomContract.php | 0 .../Websocket/{Room => Rooms}/TableRoom.php | 0 src/Server/Websocket/WebsocketHandler.php | 43 ++++++++++ 12 files changed, 233 insertions(+), 52 deletions(-) delete mode 100644 src/Server/Websocket/Formatter/DefaultFormatter.php delete mode 100644 src/Server/Websocket/Formatter/FormatterContract.php create mode 100644 src/Server/Websocket/Formatters/DefaultFormatter.php create mode 100644 src/Server/Websocket/Formatters/FormatterContract.php create mode 100644 src/Server/Websocket/HandlerContract.php rename src/Server/Websocket/{Room => Rooms}/RoomContract.php (100%) rename src/Server/Websocket/{Room => Rooms}/TableRoom.php (100%) create mode 100644 src/Server/Websocket/WebsocketHandler.php diff --git a/composer.json b/composer.json index 18005584..03465c70 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "illuminate/contracts": "~5.1", "illuminate/http": "~5.1", "illuminate/support": "~5.1", - "codedungeon/phpunit-result-printer": "^0.14.0" + "codedungeon/phpunit-result-printer": "0.12.0" }, "require-dev": { "laravel/lumen-framework": "~5.1", diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php index b27febd9..9e00cced 100644 --- a/config/swoole_websocket.php +++ b/config/swoole_websocket.php @@ -3,30 +3,48 @@ return [ /* |-------------------------------------------------------------------------- - | Default Websocket Driver + | Websocket handler for onOpen and onClose callback + | Replace this handler before you start it + |-------------------------------------------------------------------------- + */ + 'handler' => SwooleTW\Http\Server\Websocket\WebsocketHandler::class, + + /* + |-------------------------------------------------------------------------- + | Websocket handlers mapping for onMessage callback + |-------------------------------------------------------------------------- + */ + 'handlers' => [ + // 'event_name' => 'App\Handlers\ExampleHandler@function', + ], + + /* + |-------------------------------------------------------------------------- + | Default websocket driver |-------------------------------------------------------------------------- */ 'default' => 'table', /* |-------------------------------------------------------------------------- - | Default Message Formatter for Websocket + | Default message formatter + | Replace it if you want to customize your websocket payload |-------------------------------------------------------------------------- */ - 'formatter' => SwooleTW\Http\Server\Websocket\Formatter\DefaultFormatter::class, + 'formatter' => SwooleTW\Http\Server\Websocket\Formatters\DefaultFormatter::class, /* |-------------------------------------------------------------------------- - | Drivers Mapping + | Drivers mapping |-------------------------------------------------------------------------- */ 'drivers' => [ - 'table' => SwooleTW\Http\Server\Websocket\Room\TableRoom::class, + 'table' => SwooleTW\Http\Server\Websocket\Rooms\TableRoom::class, ], /* |-------------------------------------------------------------------------- - | Drivers Settings + | Drivers settings |-------------------------------------------------------------------------- */ 'settings' => [ @@ -37,5 +55,5 @@ 'client_rows' => 8192, 'client_size' => 2048 ] - ] + ], ]; diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 776a1912..a39b2b6d 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -5,10 +5,9 @@ use Exception; use Swoole\Http\Server as HttpServer; use Illuminate\Support\Facades\Facade; -use SwooleTW\Http\Server\CanWebsocket; use Illuminate\Contracts\Container\Container; -use SwooleTW\Http\Server\Websocket\Websocket; use Swoole\WebSocket\Server as WebSocketServer; +use SwooleTW\Http\Server\Websocket\CanWebsocket; class Manager { @@ -118,6 +117,7 @@ protected function prepareWebsocket() array_push($this->events, ...$this->wsEvents); $this->isWebsocket = true; $this->setFormatter(new $formatter); + $this->setsetWebsocketHandler(); } } diff --git a/src/Server/Websocket/CanWebsocket.php b/src/Server/Websocket/CanWebsocket.php index 7c3ab926..1ed4e42a 100644 --- a/src/Server/Websocket/CanWebsocket.php +++ b/src/Server/Websocket/CanWebsocket.php @@ -1,9 +1,13 @@ container['events']->fire('swoole.onOpen', $swooleRequest); + $illuminateRequest = Request::make($swooleRequest)->toIlluminate(); + + try { + $this->websocketHandler->onOpen($swooleRequest->fd, $illuminateRequest); + } catch (Exception $e) { + $this->logServerError($e); + } } /** @@ -41,10 +56,22 @@ public function onOpen($server, $swooleRequest) * @param \Swoole\Websocket\Server $server * @param \Swoole\Websocket\Frame $frame */ - public function onMessage($server, $frame) + public function onMessage(Server $server, Frame $frame) { $this->container['swoole.websocket']->setSender($frame->fd); - $this->container['events']->fire('swoole.onMessage', $frame); + + $payload = $this->formatter->input($frame); + $handler = $this->container['config']->get("swoole_websocket.handlers.{$payload['event']}"); + + try { + if ($handler) { + $this->container->call($handler, [$frame->fd, $payload['data']]); + } else { + $this->websocketHandler->onMessage($frame); + } + } catch (Exception $e) { + $this->logServerError($e); + } } /** @@ -52,13 +79,22 @@ public function onMessage($server, $frame) * * @param \Swoole\Websocket\Server $server * @param int $fd + * @param int $reactorId */ - public function onClose($server, $fd) + public function onClose(Server $server, $fd, $reactorId) { - if ($this->isWebsocket($fd)) { - $this->container['events']->fire('swoole.onClose', $fd); - $this->container['swoole.websocket']->setSender($fd)->leaveAll(); + if (! $this->isWebsocket($fd)) { + return; + } + + try { + $this->websocketHandler->onClose($fd, $reactorId); + } catch (Exception $e) { + $this->logServerError($e); } + + $this->container['swoole.websocket']->setSender($fd)->leaveAll(); + } /** @@ -67,7 +103,7 @@ public function onClose($server, $fd) * @param \Swoole\Websocket\Server $server * @param mixed $data */ - public function pushMessage($server, array $data) + public function pushMessage(Server $server, array $data) { $opcode = $data['opcode'] ?? 1; $sender = $data['sender'] ?? 0; @@ -127,4 +163,26 @@ protected function isWebsocket(int $fd) return array_key_exists('websocket_status', $info) && $info['websocket_status']; } + + /** + * Set websocket handler for onOpen and onClose callback. + * + * @return SwooleTW\Http\Server\Websocket\HandlerContract + */ + protected function setWebsocketHandler() + { + $handlerClass = $this->container['config']->get('swoole_http.websocket.handler'); + + if (! $handlerClass) { + throw new Exception('websocket handler not set in swoole_http.websocket config'); + } + + $handler = $this->container->make($handlerClass); + + if (! $handler instanceof HandlerContract) { + throw new Exception(sprintf('%s must implement %s', get_class($handler), HandlerContract::class)); + } + + $this->websocketHandler = $handler; + } } diff --git a/src/Server/Websocket/Formatter/DefaultFormatter.php b/src/Server/Websocket/Formatter/DefaultFormatter.php deleted file mode 100644 index ea414769..00000000 --- a/src/Server/Websocket/Formatter/DefaultFormatter.php +++ /dev/null @@ -1,19 +0,0 @@ - $event, - 'data' => $data - ]); - } -} diff --git a/src/Server/Websocket/Formatter/FormatterContract.php b/src/Server/Websocket/Formatter/FormatterContract.php deleted file mode 100644 index aed860cc..00000000 --- a/src/Server/Websocket/Formatter/FormatterContract.php +++ /dev/null @@ -1,11 +0,0 @@ -data); + + return [ + 'event' => $data['event'] ?? null, + 'data' => $data['data'] ?? null + ]; + } + + /** + * Output message for websocket push. + * + * @return mixed + */ + public function output($event, $data) + { + return json_encode([ + 'event' => $event, + 'data' => $data + ]); + } +} diff --git a/src/Server/Websocket/Formatters/FormatterContract.php b/src/Server/Websocket/Formatters/FormatterContract.php new file mode 100644 index 00000000..f938a4dd --- /dev/null +++ b/src/Server/Websocket/Formatters/FormatterContract.php @@ -0,0 +1,21 @@ +fire('swoole.onOpen', compact('fd', 'request')); + } + + /** + * "onMessage" listener. + * only triggered when event handler not found + * + * @param \Swoole\Websocket\Frame $frame + */ + public function onMessage(Frame $frame) + { + app('events')->fire('swoole.onMessage', compact('frame')); + } + + /** + * "onClose" listener. + * + * @param int $fd + * @param int $reactorId + */ + public function onClose($fd, $reactorId) + { + app('events')->fire('swoole.onClose', compact('fd', 'reactorId')); + } +} From 3370c35f5919078d92b36b94bcb18bbcdea11117 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 15:56:25 +0800 Subject: [PATCH 26/39] fix namespace --- src/Server/Websocket/Formatters/DefaultFormatter.php | 2 +- src/Server/Websocket/Formatters/FormatterContract.php | 2 +- src/Server/Websocket/Rooms/RoomContract.php | 2 +- src/Server/Websocket/Rooms/TableRoom.php | 4 ++-- src/Server/Websocket/Websocket.php | 2 +- tests/Server/TableRoomTest.php | 2 +- tests/Server/WebsocketTest.php | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Server/Websocket/Formatters/DefaultFormatter.php b/src/Server/Websocket/Formatters/DefaultFormatter.php index bacd9311..9f305b35 100644 --- a/src/Server/Websocket/Formatters/DefaultFormatter.php +++ b/src/Server/Websocket/Formatters/DefaultFormatter.php @@ -1,6 +1,6 @@ Date: Sat, 14 Apr 2018 16:09:12 +0800 Subject: [PATCH 27/39] reuqire php 7.1 --- .travis.yml | 20 -------------------- composer.json | 3 ++- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index df45e832..83809e71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,26 +4,6 @@ sudo: false matrix: include: - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/framework:5.1.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/framework:5.2.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/framework:5.3.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/framework:5.4.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/framework:5.5.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/lumen-framework:5.1.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/lumen-framework:5.2.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/lumen-framework:5.3.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/lumen-framework:5.4.* - - php: 7.0 - env: FRAMEWORK_VERSION=laravel/lumen-framework:5.5.* - php: 7.1 env: FRAMEWORK_VERSION=laravel/framework:5.1.* - php: 7.1 diff --git a/composer.json b/composer.json index 03465c70..7ead4fbe 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,12 @@ } ], "require": { + "php": "^7.1", "illuminate/console": "~5.1", "illuminate/contracts": "~5.1", "illuminate/http": "~5.1", "illuminate/support": "~5.1", - "codedungeon/phpunit-result-printer": "0.12.0" + "codedungeon/phpunit-result-printer": "^0.14.0" }, "require-dev": { "laravel/lumen-framework": "~5.1", From a15cee323bb3ea134e2762a5c02dffea0406abf5 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 16:11:23 +0800 Subject: [PATCH 28/39] update composer.json --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 7ead4fbe..b1738e47 100644 --- a/composer.json +++ b/composer.json @@ -18,13 +18,13 @@ "illuminate/console": "~5.1", "illuminate/contracts": "~5.1", "illuminate/http": "~5.1", - "illuminate/support": "~5.1", - "codedungeon/phpunit-result-printer": "^0.14.0" + "illuminate/support": "~5.1" }, "require-dev": { "laravel/lumen-framework": "~5.1", "phpunit/phpunit": "^6.1", - "mockery/mockery": "~1.0" + "mockery/mockery": "~1.0", + "codedungeon/phpunit-result-printer": "^0.14.0" }, "autoload": { "psr-4": { From ea8e1c2025216c76bc30f46e1f9d7c9126c706fc Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 16:27:20 +0800 Subject: [PATCH 29/39] fix typo --- src/Server/Manager.php | 4 ++-- src/Server/Websocket/CanWebsocket.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index a39b2b6d..a39fa1ed 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -111,13 +111,13 @@ protected function initialize() protected function prepareWebsocket() { $isWebsocket = $this->container['config']->get('swoole_http.websocket.enabled'); - $formatter = $this->container['config']->get('swoole_http.websocket.formatter'); + $formatter = $this->container['config']->get('swoole_websocket.formatter'); if ($isWebsocket) { array_push($this->events, ...$this->wsEvents); $this->isWebsocket = true; $this->setFormatter(new $formatter); - $this->setsetWebsocketHandler(); + $this->setWebsocketHandler(); } } diff --git a/src/Server/Websocket/CanWebsocket.php b/src/Server/Websocket/CanWebsocket.php index 1ed4e42a..cca691fc 100644 --- a/src/Server/Websocket/CanWebsocket.php +++ b/src/Server/Websocket/CanWebsocket.php @@ -171,10 +171,10 @@ protected function isWebsocket(int $fd) */ protected function setWebsocketHandler() { - $handlerClass = $this->container['config']->get('swoole_http.websocket.handler'); + $handlerClass = $this->container['config']->get('swoole_websocket.handler'); if (! $handlerClass) { - throw new Exception('websocket handler not set in swoole_http.websocket config'); + throw new Exception('websocket handler not set in swoole_websocket config'); } $handler = $this->container->make($handlerClass); From b312ba0845db5b4567048f2cf818afc5641dd5a4 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 17:17:33 +0800 Subject: [PATCH 30/39] force json_decode to array in default formatter --- src/Server/Websocket/Formatters/DefaultFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/Websocket/Formatters/DefaultFormatter.php b/src/Server/Websocket/Formatters/DefaultFormatter.php index 9f305b35..24cf0b19 100644 --- a/src/Server/Websocket/Formatters/DefaultFormatter.php +++ b/src/Server/Websocket/Formatters/DefaultFormatter.php @@ -15,7 +15,7 @@ class DefaultFormatter implements FormatterContract */ public function input($frame) { - $data = json_decode($frame->data); + $data = json_decode($frame->data, true); return [ 'event' => $data['event'] ?? null, From 31f3b9f76357780b4dd945ee1cec7361d719def8 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 19:29:54 +0800 Subject: [PATCH 31/39] fix load sequence of websocket instance --- src/HttpServiceProvider.php | 32 ----------------- src/Server/Manager.php | 8 +++++ src/Server/Websocket/CanWebsocket.php | 51 ++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/HttpServiceProvider.php b/src/HttpServiceProvider.php index a8de1c75..79f8f5bc 100644 --- a/src/HttpServiceProvider.php +++ b/src/HttpServiceProvider.php @@ -4,8 +4,6 @@ use Illuminate\Support\ServiceProvider; use SwooleTW\Http\Commands\HttpServerCommand; -use SwooleTW\Http\Server\Websocket\Websocket; -use SwooleTW\Http\Server\Websocket\Room\RoomContract; abstract class HttpServiceProvider extends ServiceProvider { @@ -26,7 +24,6 @@ public function register() $this->mergeConfigs(); $this->registerManager(); $this->registerCommands(); - $this->registerWebsocket(); } /** @@ -67,33 +64,4 @@ protected function registerCommands() HttpServerCommand::class, ]); } - - /** - * Register websocket. - */ - protected function registerWebsocket() - { - if (! $this->app['config']->get('swoole_http.websocket.enabled')) { - return; - } - - // bind room instance - $this->app->singleton(RoomContract::class, function ($app) { - $driver = $app['config']->get('swoole_websocket.default'); - $configs = $app['config']->get("swoole_websocket.settings.{$driver}"); - $className = $app['config']->get("swoole_websocket.drivers.{$driver}"); - - $room = new $className($configs); - $room->prepare(); - - return $room; - }); - $this->app->alias(RoomContract::class, 'swoole.room'); - - // bind websocket instance - $this->app->singleton(Websocket::class, function ($app) { - return new Websocket($app['swoole.room']); - }); - $this->app->alias(Websocket::class, 'swoole.websocket'); - } } diff --git a/src/Server/Manager.php b/src/Server/Manager.php index a39fa1ed..7361f6f5 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -6,8 +6,10 @@ use Swoole\Http\Server as HttpServer; use Illuminate\Support\Facades\Facade; use Illuminate\Contracts\Container\Container; +use SwooleTW\Http\Server\Websocket\Websocket; use Swoole\WebSocket\Server as WebSocketServer; use SwooleTW\Http\Server\Websocket\CanWebsocket; +use SwooleTW\Http\Server\Websocket\Rooms\RoomContract; class Manager { @@ -118,6 +120,7 @@ protected function prepareWebsocket() $this->isWebsocket = true; $this->setFormatter(new $formatter); $this->setWebsocketHandler(); + $this->setWebsocketRoom(); } } @@ -190,6 +193,11 @@ public function onWorkerStart() $this->createApplication(); $this->setLaravelApp(); $this->bindSwooleServer(); + + if ($this->isWebsocket) { + $this->bindRoom(); + $this->bindWebsocket(); + } } /** diff --git a/src/Server/Websocket/CanWebsocket.php b/src/Server/Websocket/CanWebsocket.php index cca691fc..74621376 100644 --- a/src/Server/Websocket/CanWebsocket.php +++ b/src/Server/Websocket/CanWebsocket.php @@ -6,7 +6,9 @@ use Swoole\Websocket\Frame; use Swoole\Websocket\Server; use SwooleTW\Http\Server\Request; +use SwooleTW\Http\Server\Websocket\Websocket; use SwooleTW\Http\Server\Websocket\HandlerContract; +use SwooleTW\Http\Server\Websocket\Rooms\RoomContract; use SwooleTW\Http\Server\Websocket\Formatters\FormatterContract; trait CanWebsocket @@ -21,6 +23,11 @@ trait CanWebsocket */ protected $websocketHandler; + /** + * @var SwooleTW\Http\Server\Websocket\Rooms\RoomContract + */ + protected $websocketRoom; + /** * @var SwooleTW\Http\Server\Websocket\Formatters\FormatterContract */ @@ -58,14 +65,14 @@ public function onOpen(Server $server, $swooleRequest) */ public function onMessage(Server $server, Frame $frame) { - $this->container['swoole.websocket']->setSender($frame->fd); + $this->app['swoole.websocket']->setSender($frame->fd); $payload = $this->formatter->input($frame); $handler = $this->container['config']->get("swoole_websocket.handlers.{$payload['event']}"); try { if ($handler) { - $this->container->call($handler, [$frame->fd, $payload['data']]); + $this->app->call($handler, [$frame->fd, $payload['data']]); } else { $this->websocketHandler->onMessage($frame); } @@ -93,8 +100,7 @@ public function onClose(Server $server, $fd, $reactorId) $this->logServerError($e); } - $this->container['swoole.websocket']->setSender($fd)->leaveAll(); - + $this->app['swoole.websocket']->setSender($fd)->leaveAll(); } /** @@ -164,6 +170,21 @@ protected function isWebsocket(int $fd) return array_key_exists('websocket_status', $info) && $info['websocket_status']; } + /** + * Set websocket handler for onOpen and onClose callback. + * + * @return SwooleTW\Http\Server\Websocket\HandlerContract + */ + protected function setWebsocketRoom() + { + $driver = $this->container['config']->get('swoole_websocket.default'); + $configs = $this->container['config']->get("swoole_websocket.settings.{$driver}"); + $className = $this->container['config']->get("swoole_websocket.drivers.{$driver}"); + + $this->websocketRoom = new $className($configs); + $this->websocketRoom->prepare(); + } + /** * Set websocket handler for onOpen and onClose callback. * @@ -185,4 +206,26 @@ protected function setWebsocketHandler() $this->websocketHandler = $handler; } + + /** + * Bind room instance to Laravel app container. + */ + protected function bindRoom() + { + $this->app->singleton(RoomContract::class, function ($app) { + return $this->websocketRoom; + }); + $this->app->alias(RoomContract::class, 'swoole.room'); + } + + /** + * Bind websocket instance to Laravel app container. + */ + protected function bindWebsocket() + { + $this->app->singleton(Websocket::class, function ($app) { + return new Websocket($this->websocketRoom); + }); + $this->app->alias(Websocket::class, 'swoole.websocket'); + } } From e1944179dddda34d8f4b586ba4e0b66eba9bd3e2 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 19:38:01 +0800 Subject: [PATCH 32/39] enhance code --- src/Server/Manager.php | 4 +++- src/Server/Websocket/CanWebsocket.php | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 7361f6f5..daf8fb4d 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -240,7 +240,9 @@ public function onTask(HttpServer $server, $taskId, $fromId, $data) $this->container['events']->fire('swoole.task', func_get_args()); // push websocket message - if (array_key_exists('action', $data) && $data['action'] === Websocket::PUSH_ACTION) { + if ($this->isWebsocket + && array_key_exists('action', $data) + && $data['action'] === Websocket::PUSH_ACTION) { $this->pushMessage($server, $data['data'] ?? []); } } diff --git a/src/Server/Websocket/CanWebsocket.php b/src/Server/Websocket/CanWebsocket.php index 74621376..0d10ad9a 100644 --- a/src/Server/Websocket/CanWebsocket.php +++ b/src/Server/Websocket/CanWebsocket.php @@ -172,8 +172,6 @@ protected function isWebsocket(int $fd) /** * Set websocket handler for onOpen and onClose callback. - * - * @return SwooleTW\Http\Server\Websocket\HandlerContract */ protected function setWebsocketRoom() { @@ -187,8 +185,6 @@ protected function setWebsocketRoom() /** * Set websocket handler for onOpen and onClose callback. - * - * @return SwooleTW\Http\Server\Websocket\HandlerContract */ protected function setWebsocketHandler() { From cf7567eb9ec10014e416d6e67e2e6dd66b49945f Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 19:52:12 +0800 Subject: [PATCH 33/39] add facades for room and websocket --- src/Server/Websocket/Facades/Room.php | 18 ++++++++++++++++++ src/Server/Websocket/Facades/Websocket.php | 18 ++++++++++++++++++ .../Websocket/Formatters/DefaultFormatter.php | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/Server/Websocket/Facades/Room.php create mode 100644 src/Server/Websocket/Facades/Websocket.php diff --git a/src/Server/Websocket/Facades/Room.php b/src/Server/Websocket/Facades/Room.php new file mode 100644 index 00000000..c57472f6 --- /dev/null +++ b/src/Server/Websocket/Facades/Room.php @@ -0,0 +1,18 @@ + Date: Sat, 14 Apr 2018 20:00:07 +0800 Subject: [PATCH 34/39] move websocket namespace to an upper layer --- config/swoole_websocket.php | 6 +++--- src/{Server => }/Websocket/CanWebsocket.php | 18 +++++++++--------- src/{Server => }/Websocket/Facades/Room.php | 2 +- .../Websocket/Facades/Websocket.php | 2 +- .../Websocket/Formatters/DefaultFormatter.php | 4 ++-- .../Websocket/Formatters/FormatterContract.php | 2 +- src/{Server => }/Websocket/HandlerContract.php | 2 +- .../Websocket/Rooms/RoomContract.php | 2 +- src/{Server => }/Websocket/Rooms/TableRoom.php | 4 ++-- src/{Server => }/Websocket/Websocket.php | 6 +++--- .../Websocket/WebsocketHandler.php | 4 ++-- tests/{Server => Websocket}/TableRoomTest.php | 2 +- tests/{Server => Websocket}/WebsocketTest.php | 4 ++-- 13 files changed, 29 insertions(+), 29 deletions(-) rename src/{Server => }/Websocket/CanWebsocket.php (91%) rename src/{Server => }/Websocket/Facades/Room.php (84%) rename src/{Server => }/Websocket/Facades/Websocket.php (84%) rename src/{Server => }/Websocket/Formatters/DefaultFormatter.php (85%) rename src/{Server => }/Websocket/Formatters/FormatterContract.php (87%) rename src/{Server => }/Websocket/HandlerContract.php (93%) rename src/{Server => }/Websocket/Rooms/RoomContract.php (95%) rename src/{Server => }/Websocket/Rooms/TableRoom.php (97%) rename src/{Server => }/Websocket/Websocket.php (95%) rename src/{Server => }/Websocket/WebsocketHandler.php (90%) rename tests/{Server => Websocket}/TableRoomTest.php (98%) rename tests/{Server => Websocket}/WebsocketTest.php (95%) diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php index 9e00cced..bd04c0a8 100644 --- a/config/swoole_websocket.php +++ b/config/swoole_websocket.php @@ -7,7 +7,7 @@ | Replace this handler before you start it |-------------------------------------------------------------------------- */ - 'handler' => SwooleTW\Http\Server\Websocket\WebsocketHandler::class, + 'handler' => SwooleTW\Http\Websocket\WebsocketHandler::class, /* |-------------------------------------------------------------------------- @@ -31,7 +31,7 @@ | Replace it if you want to customize your websocket payload |-------------------------------------------------------------------------- */ - 'formatter' => SwooleTW\Http\Server\Websocket\Formatters\DefaultFormatter::class, + 'formatter' => SwooleTW\Http\Websocket\Formatters\DefaultFormatter::class, /* |-------------------------------------------------------------------------- @@ -39,7 +39,7 @@ |-------------------------------------------------------------------------- */ 'drivers' => [ - 'table' => SwooleTW\Http\Server\Websocket\Rooms\TableRoom::class, + 'table' => SwooleTW\Http\Websocket\Rooms\TableRoom::class, ], /* diff --git a/src/Server/Websocket/CanWebsocket.php b/src/Websocket/CanWebsocket.php similarity index 91% rename from src/Server/Websocket/CanWebsocket.php rename to src/Websocket/CanWebsocket.php index 0d10ad9a..f68caa3d 100644 --- a/src/Server/Websocket/CanWebsocket.php +++ b/src/Websocket/CanWebsocket.php @@ -1,15 +1,15 @@ Date: Sat, 14 Apr 2018 20:08:52 +0800 Subject: [PATCH 35/39] add swoole server facade --- src/Server/Facades/Server.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/Server/Facades/Server.php diff --git a/src/Server/Facades/Server.php b/src/Server/Facades/Server.php new file mode 100644 index 00000000..7a34102a --- /dev/null +++ b/src/Server/Facades/Server.php @@ -0,0 +1,18 @@ + Date: Sat, 14 Apr 2018 20:14:25 +0800 Subject: [PATCH 36/39] fix namespace --- src/Server/Manager.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index daf8fb4d..d4dff6af 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -6,10 +6,10 @@ use Swoole\Http\Server as HttpServer; use Illuminate\Support\Facades\Facade; use Illuminate\Contracts\Container\Container; -use SwooleTW\Http\Server\Websocket\Websocket; use Swoole\WebSocket\Server as WebSocketServer; -use SwooleTW\Http\Server\Websocket\CanWebsocket; -use SwooleTW\Http\Server\Websocket\Rooms\RoomContract; +use SwooleTW\Http\Websocket\Websocket; +use SwooleTW\Http\Websocket\CanWebsocket; +use SwooleTW\Http\Websocket\Rooms\RoomContract; class Manager { From ad8e9626f2e2228f0bc5617e45ad575374ade50f Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 21:49:32 +0800 Subject: [PATCH 37/39] try to add table --- config/swoole_http.php | 25 +++++++++++++++++ src/Server/Facades/Table.php | 18 +++++++++++++ src/Server/Manager.php | 52 +++++++++++++++++++++++++++++++++++- src/Server/Table.php | 25 +++++++++++++++++ tests/Server/TableTest.php | 34 +++++++++++++++++++++++ 5 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 src/Server/Facades/Table.php create mode 100644 src/Server/Table.php create mode 100644 tests/Server/TableTest.php diff --git a/config/swoole_http.php b/config/swoole_http.php index 48afc951..6b585667 100644 --- a/config/swoole_http.php +++ b/config/swoole_http.php @@ -1,5 +1,7 @@ env('SWOOLE_HTTP_TASK_WORKER_NUM', 4) ], ], + 'websocket' => [ 'enabled' => env('SWOOLE_HTTP_WEBSOCKET', false), ], + + /* + |-------------------------------------------------------------------------- + | Providers here will be registered on every request. + |-------------------------------------------------------------------------- + */ 'providers' => [ // App\Providers\AuthServiceProvider::class, + ], + + /* + |-------------------------------------------------------------------------- + | Define your swoole tables here. + | + | @see https://wiki.swoole.com/wiki/page/p-table.html + |-------------------------------------------------------------------------- + */ + 'tables' => [ + 'table_name' => [ + 'size' => 1024, + 'columns' => [ + ['name' => 'column_name', 'type' => Table::TYPE_STRING, 'size' => 1024], + ] + ], ] ]; diff --git a/src/Server/Facades/Table.php b/src/Server/Facades/Table.php new file mode 100644 index 00000000..a9bc6028 --- /dev/null +++ b/src/Server/Facades/Table.php @@ -0,0 +1,18 @@ +setProcessName('manager process'); + $this->createTables(); $this->prepareWebsocket(); $this->createSwooleServer(); $this->configureSwooleServer(); $this->setSwooleServerListeners(); } + /** + * Prepare settings if websocket is enabled. + */ + protected function createTables() + { + $this->table = new Table; + $this->registerTables(); + } + /** * Prepare settings if websocket is enabled. */ @@ -125,7 +141,7 @@ protected function prepareWebsocket() } /** - * Create swoole erver. + * Create swoole server. */ protected function createSwooleServer() { @@ -193,6 +209,7 @@ public function onWorkerStart() $this->createApplication(); $this->setLaravelApp(); $this->bindSwooleServer(); + $this->bindSwooleTable(); if ($this->isWebsocket) { $this->bindRoom(); @@ -305,6 +322,16 @@ protected function bindSwooleServer() }); } + /** + * Bind swoole table to Laravel app container. + */ + protected function bindSwooleTable() + { + $this->app->singleton('swoole.table', function () { + return $this->table; + }); + } + /** * Gets pid file path. * @@ -385,4 +412,27 @@ protected function logServerError(Exception $e) fwrite($output, sprintf('%s%s(%d): %s', $prefix, $e->getFile(), $e->getLine(), $e->getMessage()) . PHP_EOL); } + + /** + * Register user-defined swoole tables. + */ + protected function registerTables() + { + $tables = $this->container['config']->get('swoole_http.tables') ?? []; + + foreach ($tables as $key => $value) { + $table = new SwooleTable($value['size']); + $columns = $value['columns'] ?? []; + foreach ($columns as $column) { + if (isset($column['size'])) { + $table->column($column['name'], $column['type'], $column['size']); + } else { + $table->column($column['name'], $column['type']); + } + } + $table->create(); + + $this->table->add($key, $table); + } + } } diff --git a/src/Server/Table.php b/src/Server/Table.php new file mode 100644 index 00000000..edc280e4 --- /dev/null +++ b/src/Server/Table.php @@ -0,0 +1,25 @@ +tables[$name] = $table; + } + + public function get(string $name) + { + return $this->tables[$name] ?? null; + } + + public function getAll() + { + return $this->tables; + } +} diff --git a/tests/Server/TableTest.php b/tests/Server/TableTest.php new file mode 100644 index 00000000..9af19080 --- /dev/null +++ b/tests/Server/TableTest.php @@ -0,0 +1,34 @@ +add($name = 'foo', $swooleTable); + + $this->assertSame($swooleTable, $table->get($name)); + } + + public function testGetAll() + { + $swooleTable = m::mock(SwooleTable::class); + + $table = new Table; + $table->add($foo = 'foo', $swooleTable); + $table->add($bar = 'bar', $swooleTable); + + $this->assertSame(2, count($table->getAll())); + $this->assertSame($swooleTable, $table->getAll()[$foo]); + $this->assertSame($swooleTable, $table->getAll()[$bar]); + } +} From d135a3a6ee05be3385304300634845a37979ea84 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 21:51:32 +0800 Subject: [PATCH 38/39] return this for add function of Table --- src/Server/Table.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Server/Table.php b/src/Server/Table.php index edc280e4..71ea0db0 100644 --- a/src/Server/Table.php +++ b/src/Server/Table.php @@ -11,6 +11,8 @@ class Table public function add(string $name, SwooleTable $table) { $this->tables[$name] = $table; + + return $this; } public function get(string $name) From 237b7b5684cefb7ec5ec3e766d9c253f504ecb6f Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sat, 14 Apr 2018 22:02:15 +0800 Subject: [PATCH 39/39] comment example settings for tables --- config/swoole_http.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config/swoole_http.php b/config/swoole_http.php index 6b585667..45bd979f 100644 --- a/config/swoole_http.php +++ b/config/swoole_http.php @@ -43,11 +43,11 @@ |-------------------------------------------------------------------------- */ 'tables' => [ - 'table_name' => [ - 'size' => 1024, - 'columns' => [ - ['name' => 'column_name', 'type' => Table::TYPE_STRING, 'size' => 1024], - ] - ], + // 'table_name' => [ + // 'size' => 1024, + // 'columns' => [ + // ['name' => 'column_name', 'type' => Table::TYPE_STRING, 'size' => 1024], + // ] + // ], ] ];