From d8c208ed6a405eefa9364f3989e470ad5e661f5b Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 29 Apr 2018 22:18:29 +0800 Subject: [PATCH 01/17] Update README.md --- README.md | 154 +++++++----------------------------------------------- 1 file changed, 18 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 9c54a80a..2b64303b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # Laravel-Swoole -![php-badge](https://img.shields.io/badge/php-%3E%3D%205.5.9-8892BF.svg) +![php-badge](https://img.shields.io/badge/php-%3E%3D%207.1-8892BF.svg) [![packagist-badge](https://img.shields.io/packagist/v/swooletw/laravel-swoole.svg)](https://packagist.org/packages/swooletw/laravel-swoole) [![Total Downloads](https://poser.pugx.org/swooletw/laravel-swoole/downloads)](https://packagist.org/packages/swooletw/laravel-swoole) +[![travis-badge](https://api.travis-ci.org/swooletw/laravel-swoole.svg?branch=master)](https://travis-ci.org/swooletw/laravel-swoole) This package provides a high performance HTTP server to speed up your laravel/lumen application based on [Swoole](http://www.swoole.com/). @@ -10,139 +11,21 @@ This package provides a high performance HTTP server to speed up your laravel/lu | PHP | Laravel | Lumen | Swoole | |:-------:|:-------:|:-----:|:-------:| -| >=5.5.9 | ~5.1 | ~5.1 | >=1.9.3 | +| >=7.1 | ~5.1 | ~5.1 | >=1.9.3 | -## Installation +## Features -Require this package with composer by using the following command: +* Run Laravel/Lumen application on top of Swoole. +* Sandbox mode to decrease technical gap for beginners. +* Support running websocket server in Laravel. +* Support `Socket.io` protocol. +* Support Swoole table. -``` -$ composer require swooletw/laravel-swoole -``` - -> This package relies on Swoole. Please make sure your machine has been installed the Swoole extension. Using this command to install quickly: `pecl install swoole`. Visit the [official website](https://wiki.swoole.com/wiki/page/6.html) for more information. - -Then, add the service provider: - -If you are using Laravel, add the service provider to the providers array in `config/app.php`: - -```php -[ - 'providers' => [ - SwooleTW\Http\LaravelServiceProvider::class, - ], -] -``` - -If you are using Lumen, append the following code to `bootstrap/app.php`: - -```php -$app->register(SwooleTW\Http\LumenServiceProvider::class); -``` - -## Configuration - -If you want to change the default configurations, please run the following command to generate a configuration file `http.php` in directory `config/`: - -``` -$ php artisan vendor:publish -``` +## Documentation -`server.host`: The swoole_http_server host. +Please see [Wiki](https://github.com/swooletw/laravel-swoole/wiki) -`server.port`: The swoole_http_server port. - -`server.options`: The configurations for `Swoole\Server`. To get more information about swoole server, please read [the official documentation](https://wiki.swoole.com/wiki/page/274.html). - -For example, if you want to set the 'max_request': - -```php -[ - 'server' => [ - 'options' => [ - 'max_request' => 1000, - ], - ] -] -``` - -## Command - -> The swoole_http_server can only run in cli environment, and this package provides convenient artisan commands to manage it. -> By default, you can visit your site at http://127.0.0.1:1215 - -Start the swoole_http_server: - -``` -$ php artisan swoole:http start -``` - -Stop the swoole_http_server: - -``` -$ php artisan swoole:http stop -``` - -Restart the swoole_http_server: - -``` -$ php artisan swoole:http restart -``` - -Reload the swoole_http_server: - -``` -$ php artisan swoole:http reload -``` - -Now, you can run the following command to start the **swoole_http_server**. - -``` -$ php artisan swoole:http start -``` - -## Nginx Configuration - -> The support of swoole_http_server for Http is not complete. So, you should configure the domains via nginx proxy in your production environment. - -```nginx -server { - listen 80; - server_name your.domain.com; - root /path/to/laravel/public; - index index.php; - - location = /index.php { - # Ensure that there is no such file named "not_exists" - # in your "public" directory. - try_files /not_exists @swoole; - } - - location / { - try_files $uri $uri/ @swoole; - } - - location @swoole { - set $suffix ""; - - if ($uri = /index.php) { - set $suffix "/"; - } - - proxy_set_header Host $host; - proxy_set_header SERVER_PORT $server_port; - proxy_set_header REMOTE_ADDR $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - # IF https - # proxy_set_header HTTPS "on"; - - proxy_pass http://127.0.0.1:1215$suffix; - } -} -``` - -## Performance Reference +## Benchmark Test with clean Lumen 5.5, using MacBook Air 13, 2015. Benchmarking Tool: [wrk](https://github.com/wg/wrk) @@ -177,20 +60,19 @@ Requests/sec: 8717.00 Transfer/sec: 1.55MB ``` -## Notices - -1. Please reload or restart the swoole_http_server after released your code. Because the Laravel program will be kept in memory after the swoole_http_server started. That's why the swoole_http_server has high performance. -2. Never use `dd()`, `exit()` or `die()` function to print your debug message. It will terminate your swoole worker unexpectedly. -3. You should have basic knowledge of multi-process programming and swoole. If you still write your code with a single-process conception, your app might have unexpected bugs. - ## Support Bugs and feature request are tracked on [Github](https://github.com/swooletw/laravel-swoole-http/issues). ## Credits -The original author of this package: [Huang-Yi](https://github.com/huang-yi) +[Huang-Yi](https://github.com/huang-yi) ## License The Laravel-Swoole-Http package is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). + +## Support on Beerpay +Hey dude! Help me out for a couple of :beers:! + +[![Beerpay](https://beerpay.io/swooletw/laravel-swoole/badge.svg?style=beer-square)](https://beerpay.io/swooletw/laravel-swoole) [![Beerpay](https://beerpay.io/swooletw/laravel-swoole/make-wish.svg?style=flat-square)](https://beerpay.io/swooletw/laravel-swoole?focus=wish) From 094da49ac3e60b14df163afa1692ffe2a57f7861 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Sun, 29 Apr 2018 22:20:14 +0800 Subject: [PATCH 02/17] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b64303b..44ae45fb 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Transfer/sec: 1.55MB ## Support -Bugs and feature request are tracked on [Github](https://github.com/swooletw/laravel-swoole-http/issues). +Bugs and feature request are tracked on [Github](https://github.com/swooletw/laravel-swoole/issues). ## Credits @@ -70,7 +70,7 @@ Bugs and feature request are tracked on [Github](https://github.com/swooletw/lar ## License -The Laravel-Swoole-Http package is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). +The Laravel-Swoole package is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). ## Support on Beerpay Hey dude! Help me out for a couple of :beers:! From 6344d7e45df4207b196e070b82dd8aa8e1517bf3 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 1 May 2018 11:36:10 +0800 Subject: [PATCH 03/17] make base Parser abstract --- src/Websocket/Parser.php | 22 +++-------- src/Websocket/SimpleParser.php | 47 +++++++++++++++++++++++ src/Websocket/SocketIO/SocketIOParser.php | 6 ++- tests/SocketIO/ParserTest.php | 30 +-------------- tests/Websocket/SimpleParserTest.php | 37 ++++++++++++++++++ 5 files changed, 95 insertions(+), 47 deletions(-) create mode 100644 src/Websocket/SimpleParser.php create mode 100644 tests/Websocket/SimpleParserTest.php diff --git a/src/Websocket/Parser.php b/src/Websocket/Parser.php index 35ba113a..3e0c26a3 100644 --- a/src/Websocket/Parser.php +++ b/src/Websocket/Parser.php @@ -6,7 +6,7 @@ use Swoole\Websocket\Server; use Illuminate\Support\Facades\App; -class Parser +abstract class Parser { /** * Strategy classes need to implement handle method. @@ -40,15 +40,11 @@ public function execute(Server $server, Frame $frame) /** * Encode output payload for websocket push. * + * @param string $event + * @param mixed $data * @return mixed */ - public function encode($event, $data) - { - return json_encode([ - 'event' => $event, - 'data' => $data - ]); - } + abstract public function encode(string $event, $data); /** * Input message on websocket connected. @@ -57,13 +53,5 @@ public function encode($event, $data) * @param \Swoole\Websocket\Frame $frame * @return array */ - public function decode(Frame $frame) - { - $data = json_decode($frame->data, true); - - return [ - 'event' => $data['event'] ?? null, - 'data' => $data['data'] ?? null - ]; - } + abstract public function decode(Frame $frame); } diff --git a/src/Websocket/SimpleParser.php b/src/Websocket/SimpleParser.php new file mode 100644 index 00000000..51dfbf54 --- /dev/null +++ b/src/Websocket/SimpleParser.php @@ -0,0 +1,47 @@ + $event, + 'data' => $data + ]); + } + + /** + * Input message on websocket connected. + * Define and return event name and payload data here. + * + * @param \Swoole\Websocket\Frame $frame + * @return array + */ + public function decode(Frame $frame) + { + $data = json_decode($frame->data, true); + + return [ + 'event' => $data['event'] ?? null, + 'data' => $data['data'] ?? null + ]; + } +} diff --git a/src/Websocket/SocketIO/SocketIOParser.php b/src/Websocket/SocketIO/SocketIOParser.php index 2a7d89d1..f1fab2cf 100644 --- a/src/Websocket/SocketIO/SocketIOParser.php +++ b/src/Websocket/SocketIO/SocketIOParser.php @@ -17,11 +17,13 @@ class SocketIOParser extends Parser ]; /** - * Encode output message for websocket push. + * Encode output payload for websocket push. * + * @param string $event + * @param mixed $data * @return mixed */ - public function encode($event, $data) + public function encode(string $event, $data) { $packet = Packet::MESSAGE . Packet::EVENT; $shouldEncode = is_array($data) || is_object($data); diff --git a/tests/SocketIO/ParserTest.php b/tests/SocketIO/ParserTest.php index f1550cae..1298a067 100644 --- a/tests/SocketIO/ParserTest.php +++ b/tests/SocketIO/ParserTest.php @@ -7,38 +7,12 @@ use Swoole\Websocket\Server; use SwooleTW\Http\Tests\TestCase; use Illuminate\Support\Facades\App; -use SwooleTW\Http\Websocket\Parser; use SwooleTW\Http\Websocket\SocketIO\SocketIOParser; use SwooleTW\Http\Websocket\SocketIO\Strategies\HeartbeatStrategy; class ParserTest extends TestCase { - public function testBaseEncode() - { - $event = 'foo'; - $data = 'bar'; - - $parser = new Parser; - $this->assertSame(json_encode([ - 'event' => $event, - 'data' => $data - ]), $parser->encode($event, $data)); - } - - public function testBaseDecode() - { - $payload = json_encode($data = [ - 'event' => 'foo', - 'data' => 'bar' - ]); - $frame = m::mock(Frame::class); - $frame->data = $payload; - - $parser = new Parser; - $this->assertSame($data, $parser->decode($frame)); - } - - public function testSocketEncode() + public function testEncode() { $event = 'foo'; $data = 'bar'; @@ -53,7 +27,7 @@ public function testSocketEncode() $this->assertSame('42["foo",{"message":"test"}]', $parser->encode($event, $data)); } - public function testSocketDecode() + public function testDecode() { $payload = '42["foo",{"message":"test"}]'; $frame = m::mock(Frame::class); diff --git a/tests/Websocket/SimpleParserTest.php b/tests/Websocket/SimpleParserTest.php new file mode 100644 index 00000000..2ef763cd --- /dev/null +++ b/tests/Websocket/SimpleParserTest.php @@ -0,0 +1,37 @@ +assertSame(json_encode([ + 'event' => $event, + 'data' => $data + ]), $parser->encode($event, $data)); + } + + public function testDecode() + { + $payload = json_encode($data = [ + 'event' => 'foo', + 'data' => 'bar' + ]); + $frame = m::mock(Frame::class); + $frame->data = $payload; + + $parser = new SimpleParser; + $this->assertSame($data, $parser->decode($frame)); + } +} From 03130d4b9513fa3123d39cfa22d1f1c88ce6abeb Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 1 May 2018 11:49:33 +0800 Subject: [PATCH 04/17] rename sid to fd in room --- src/Websocket/Rooms/RedisRoom.php | 8 +++---- src/Websocket/Rooms/TableRoom.php | 36 +++++++++++++++---------------- src/Websocket/Websocket.php | 7 +++--- tests/Websocket/RedisRoomTest.php | 4 ++-- tests/Websocket/TableRoomTest.php | 18 ++++++++-------- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/Websocket/Rooms/RedisRoom.php b/src/Websocket/Rooms/RedisRoom.php index db3d2d21..b0e9417f 100644 --- a/src/Websocket/Rooms/RedisRoom.php +++ b/src/Websocket/Rooms/RedisRoom.php @@ -70,7 +70,7 @@ public function add(int $fd, string $room) public function addAll(int $fd, array $roomNames) { - $this->addValue($fd, $roomNames, 'sids'); + $this->addValue($fd, $roomNames, 'fds'); foreach ($roomNames as $room) { $this->addValue($room, [$fd], 'rooms'); @@ -85,7 +85,7 @@ public function delete(int $fd, string $room) public function deleteAll(int $fd, array $roomNames = []) { $roomNames = count($roomNames) ? $roomNames : $this->getRooms($fd); - $this->removeValue($fd, $roomNames, 'sids'); + $this->removeValue($fd, $roomNames, 'fds'); foreach ($roomNames as $room) { $this->removeValue($room, [$fd], 'rooms'); @@ -127,12 +127,12 @@ public function getClients(string $room) public function getRooms(int $fd) { - return $this->getValue($fd, 'sids'); + return $this->getValue($fd, 'fds'); } protected function checkTable(string $table) { - if (! in_array($table, ['rooms', 'sids'])) { + if (! in_array($table, ['rooms', 'fds'])) { throw new \InvalidArgumentException('invalid table name.'); } } diff --git a/src/Websocket/Rooms/TableRoom.php b/src/Websocket/Rooms/TableRoom.php index 5327ab89..6c69f7cd 100644 --- a/src/Websocket/Rooms/TableRoom.php +++ b/src/Websocket/Rooms/TableRoom.php @@ -11,7 +11,7 @@ class TableRoom implements RoomContract protected $rooms; - protected $sids; + protected $fds; public function __construct(array $config) { @@ -21,7 +21,7 @@ public function __construct(array $config) public function prepare() { $this->initRoomsTable(); - $this->initSidsTable(); + $this->initFdsTable(); } public function add(int $fd, string $room) @@ -34,16 +34,16 @@ public function addAll(int $fd, array $roomNames) $rooms = $this->getRooms($fd); foreach ($roomNames as $room) { - $sids = $this->getClients($room); + $fds = $this->getClients($room); - if (in_array($fd, $sids)) { + if (in_array($fd, $fds)) { continue; } - $sids[] = $fd; + $fds[] = $fd; $rooms[] = $room; - $this->setClients($room, $sids); + $this->setClients($room, $fds); } $this->setRooms($fd, $rooms); @@ -61,17 +61,17 @@ public function deleteAll(int $fd, array $roomNames = []) $removeRooms = []; foreach ($rooms as $room) { - $sids = $this->getClients($room); + $fds = $this->getClients($room); - if (! in_array($fd, $sids)) { + if (! in_array($fd, $fds)) { continue; } - $this->setClients($room, array_values(array_diff($sids, [$fd])), 'rooms'); + $this->setClients($room, array_values(array_diff($fds, [$fd])), 'rooms'); $removeRooms[] = $room; } - $this->setRooms($fd, array_values(array_diff($allRooms, $removeRooms)), 'sids'); + $this->setRooms($fd, array_values(array_diff($allRooms, $removeRooms)), 'fds'); } public function getClients(string $room) @@ -81,17 +81,17 @@ public function getClients(string $room) public function getRooms(int $fd) { - return $this->getValue($fd, 'sids'); + return $this->getValue($fd, 'fds'); } - protected function setClients(string $room, array $sids) + protected function setClients(string $room, array $fds) { - return $this->setValue($room, $sids, 'rooms'); + return $this->setValue($room, $fds, 'rooms'); } protected function setRooms(int $fd, array $rooms) { - return $this->setValue($fd, $rooms, 'sids'); + return $this->setValue($fd, $rooms, 'fds'); } protected function initRoomsTable() @@ -101,11 +101,11 @@ protected function initRoomsTable() $this->rooms->create(); } - protected function initSidsTable() + protected function initFdsTable() { - $this->sids = new Table($this->config['client_rows']); - $this->sids->column('value', Table::TYPE_STRING, $this->config['client_size']); - $this->sids->create(); + $this->fds = new Table($this->config['client_rows']); + $this->fds->column('value', Table::TYPE_STRING, $this->config['client_size']); + $this->fds->create(); } public function setValue($key, array $value, string $table) diff --git a/src/Websocket/Websocket.php b/src/Websocket/Websocket.php index 4208cb76..7c747f12 100644 --- a/src/Websocket/Websocket.php +++ b/src/Websocket/Websocket.php @@ -45,8 +45,9 @@ class Websocket */ protected $room; - // https://gist.github.com/alexpchin/3f257d0bb813e2c8c476 - // https://github.com/socketio/socket.io/blob/master/docs/emit.md + /** + * Websocket constructor. + */ public function __construct(RoomContract $room) { $this->room = $room; @@ -75,7 +76,7 @@ public function to($value) } /** - * Set multiple recepients' fdd or room names. + * Set multiple recepients' fd or room names. * * @param array (fds or rooms) */ diff --git a/tests/Websocket/RedisRoomTest.php b/tests/Websocket/RedisRoomTest.php index bee24bfe..17a3f97c 100644 --- a/tests/Websocket/RedisRoomTest.php +++ b/tests/Websocket/RedisRoomTest.php @@ -41,7 +41,7 @@ public function testAddValue() ->once(); $redisRoom = $this->getRedisRoom($redis); - $redisRoom->addValue(1, ['foo', 'bar'], 'sids'); + $redisRoom->addValue(1, ['foo', 'bar'], 'fds'); } public function testAddAll() @@ -88,7 +88,7 @@ public function testGetRooms() { $redis = $this->getRedis(); $redis->shouldReceive('smembers') - ->with('swoole:sids:1') + ->with('swoole:fds:1') ->once(); $redisRoom = $this->getRedisRoom($redis); diff --git a/tests/Websocket/TableRoomTest.php b/tests/Websocket/TableRoomTest.php index ece54d5e..3b4f2e17 100644 --- a/tests/Websocket/TableRoomTest.php +++ b/tests/Websocket/TableRoomTest.php @@ -31,11 +31,11 @@ public function testPrepare() $rooms = $reflection->getProperty('rooms'); $rooms->setAccessible(true); - $sids = $reflection->getProperty('sids'); - $sids->setAccessible(true); + $fds = $reflection->getProperty('fds'); + $fds->setAccessible(true); $this->assertInstanceOf(Table::class, $rooms->getValue($this->tableRoom)); - $this->assertInstanceOf(Table::class, $sids->getValue($this->tableRoom)); + $this->assertInstanceOf(Table::class, $fds->getValue($this->tableRoom)); } public function testInvalidTableName() @@ -47,7 +47,7 @@ public function testInvalidTableName() public function testSetValue() { - $this->tableRoom->setValue($key = 1, $value = ['foo', 'bar'], $table = 'sids'); + $this->tableRoom->setValue($key = 1, $value = ['foo', 'bar'], $table = 'fds'); $this->assertSame($value, $this->tableRoom->getValue($key, $table)); } @@ -56,7 +56,7 @@ public function testAddAll() { $this->tableRoom->addAll($key = 1, $values = ['foo', 'bar']); - $this->assertSame($values, $this->tableRoom->getValue($key, $table = 'sids')); + $this->assertSame($values, $this->tableRoom->getValue($key, $table = 'fds')); $this->assertSame([$key], $this->tableRoom->getValue('foo', 'rooms')); $this->assertSame([$key], $this->tableRoom->getValue('bar', 'rooms')); } @@ -65,7 +65,7 @@ public function testAdd() { $this->tableRoom->add($key = 1, $value = 'foo'); - $this->assertSame([$value], $this->tableRoom->getValue($key, $table = 'sids')); + $this->assertSame([$value], $this->tableRoom->getValue($key, $table = 'fds')); $this->assertSame([$key], $this->tableRoom->getValue($value, 'rooms')); } @@ -74,7 +74,7 @@ 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($key, $table = 'fds')); $this->assertSame([], $this->tableRoom->getValue('foo', 'rooms')); $this->assertSame([], $this->tableRoom->getValue('bar', 'rooms')); } @@ -84,7 +84,7 @@ public function testDelete() $this->tableRoom->addAll($key = 1, $values = ['foo', 'bar']); $this->tableRoom->delete($key, 'foo'); - $this->assertSame(['bar'], $this->tableRoom->getValue($key, $table = 'sids')); + $this->assertSame(['bar'], $this->tableRoom->getValue($key, $table = 'fds')); $this->assertSame([], $this->tableRoom->getValue('foo', 'rooms')); $this->assertSame([$key], $this->tableRoom->getValue('bar', 'rooms')); } @@ -94,7 +94,7 @@ public function testGetRooms() $this->tableRoom->addAll($key = 1, $values = ['foo', 'bar']); $this->assertSame( - $this->tableRoom->getValue($key, $table = 'sids'), + $this->tableRoom->getValue($key, $table = 'fds'), $this->tableRoom->getRooms($key) ); } From 16cacc9f13302140299c73a640df43407842db35 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 1 May 2018 13:32:33 +0800 Subject: [PATCH 05/17] inject request param while on connect --- routes/websocket.php | 9 +++++++++ src/Websocket/Websocket.php | 7 ++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/routes/websocket.php b/routes/websocket.php index f029c08d..c8977a86 100644 --- a/routes/websocket.php +++ b/routes/websocket.php @@ -1,5 +1,6 @@ emit('message', $data); }); diff --git a/src/Websocket/Websocket.php b/src/Websocket/Websocket.php index 7c747f12..f07e243c 100644 --- a/src/Websocket/Websocket.php +++ b/src/Websocket/Websocket.php @@ -10,6 +10,8 @@ class Websocket { const PUSH_ACTION = 'push'; + const EVENT_CONNECT = 'connect'; + /** * Determine if to broadcast. * @@ -227,9 +229,12 @@ public function call(string $event, $data = null) return null; } + // inject request param while on connect event + $dataKey = $event === static::EVENT_CONNECT ? 'request' : 'data'; + return App::call($this->callbacks[$event], [ 'websocket' => $this, - 'data' => $data + $dataKey => $data ]); } From fb0a669f5448e518541c211de9706c621becdce5 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 1 May 2018 23:47:57 +0800 Subject: [PATCH 06/17] support pipeline for middlewares in websocket --- src/Websocket/CanWebsocket.php | 3 +- src/Websocket/Websocket.php | 65 +++++++++++++++++++++++++++++-- tests/Websocket/WebsocketTest.php | 35 ++++++++++++++++- 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/Websocket/CanWebsocket.php b/src/Websocket/CanWebsocket.php index 4324df6c..f3656442 100644 --- a/src/Websocket/CanWebsocket.php +++ b/src/Websocket/CanWebsocket.php @@ -5,6 +5,7 @@ use Exception; use Swoole\Websocket\Frame; use Swoole\Websocket\Server; +use Illuminate\Pipeline\Pipeline; use SwooleTW\Http\Server\Request; use SwooleTW\Http\Websocket\Parser; use SwooleTW\Http\Websocket\Websocket; @@ -241,7 +242,7 @@ protected function bindRoom() protected function bindWebsocket() { $this->app->singleton(Websocket::class, function ($app) { - return $this->websocket = new Websocket($app['swoole.room']); + return $this->websocket = new Websocket($app['swoole.room'], new Pipeline($app)); }); $this->app->alias(Websocket::class, 'swoole.websocket'); } diff --git a/src/Websocket/Websocket.php b/src/Websocket/Websocket.php index f07e243c..f186ca04 100644 --- a/src/Websocket/Websocket.php +++ b/src/Websocket/Websocket.php @@ -5,6 +5,7 @@ use InvalidArgumentException; use Illuminate\Support\Facades\App; use SwooleTW\Http\Websocket\Rooms\RoomContract; +use Illuminate\Contracts\Pipeline\Pipeline as PipelineContract; class Websocket { @@ -40,6 +41,20 @@ class Websocket */ protected $callbacks = []; + /** + * Middlewares for on connect request. + * + * @var array + */ + protected $middlewares = []; + + /** + * Pipeline instance. + * + * @var Illuminate\Contracts\Pipeline\Pipeline + */ + protected $pipeline; + /** * Room adapter. * @@ -49,10 +64,14 @@ class Websocket /** * Websocket constructor. + * + * @var SwooleTW\Http\Websocket\Rooms\RoomContract $rrom + * @var Illuminate\Contracts\Pipeline\Pipeline $pipeline */ - public function __construct(RoomContract $room) + public function __construct(RoomContract $room, PipelineContract $pipeline) { $this->room = $room; + $this->pipeline = $pipeline; } /** @@ -229,8 +248,14 @@ public function call(string $event, $data = null) return null; } - // inject request param while on connect event - $dataKey = $event === static::EVENT_CONNECT ? 'request' : 'data'; + // inject request param on connect event + $isConnect = $event === static::EVENT_CONNECT; + $dataKey = $isConnect ? 'request' : 'data'; + + // dispatch request to pipeline if middlewares are set + if ($isConnect && count($this->middlewares)) { + $data = $this->setRequestThroughMiddleware($data); + } return App::call($this->callbacks[$event], [ 'websocket' => $this, @@ -315,4 +340,38 @@ public function reset($force = false) return $this; } + + /** + * Get or set middlewares. + */ + public function middleware($middleware = null) + { + if (is_null($middleware)) { + return $this->middlewares; + } + + if (is_string($middleware)) { + $middleware = func_get_args(); + } + + $this->middlewares = array_unique(array_merge($this->middlewares, $middleware)); + + return $this; + } + + /** + * Set the given request through the middleware. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Request + */ + protected function setRequestThroughMiddleware($request) + { + return $this->pipeline + ->send($request) + ->through($this->middlewares) + ->then(function ($request) { + return $request; + }); + } } diff --git a/tests/Websocket/WebsocketTest.php b/tests/Websocket/WebsocketTest.php index b4ea9f65..81d0a7ce 100644 --- a/tests/Websocket/WebsocketTest.php +++ b/tests/Websocket/WebsocketTest.php @@ -3,7 +3,9 @@ namespace SwooleTW\Http\Tests\Websocket; use Mockery as m; +use Illuminate\Http\Request; use InvalidArgumentException; +use Illuminate\Pipeline\Pipeline; use SwooleTW\Http\Tests\TestCase; use SwooleTW\Http\Websocket\Websocket; use SwooleTW\Http\Websocket\Rooms\RoomContract; @@ -111,8 +113,37 @@ public function testReset() $this->assertSame([], $websocket->getTo()); } - protected function getWebsocket(RoomContract $room = null) + public function testPipeline() { - return new Websocket($room ?? m::mock(RoomContract::class)); + $request = m::mock(Request::class); + $middlewares = ['foo', 'bar']; + $pipeline = m::mock(Pipeline::class); + $pipeline->shouldReceive('send') + ->with($request) + ->once() + ->andReturnSelf(); + $pipeline->shouldReceive('through') + ->with($middlewares) + ->once() + ->andReturnSelf(); + $pipeline->shouldReceive('then') + ->once() + ->andReturn($request); + + $websocket = $this->getWebsocket(null, $pipeline); + $websocket->middleware($middlewares); + $websocket->on('connect', function () { + return 'connect'; + }); + + $websocket->call('connect', $request); + } + + protected function getWebsocket(RoomContract $room = null, $pipeline = null) + { + $room = $room ?: m::mock(RoomContract::class); + $pipeline = $pipeline ?: m::mock(Pipeline::class); + + return new Websocket($room, $pipeline); } } From 75b2ca842f3b17361139f8fc383204e73ed5eacf Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Tue, 1 May 2018 23:59:53 +0800 Subject: [PATCH 07/17] improve ob for debugging while handling request --- src/Server/Application.php | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Server/Application.php b/src/Server/Application.php index 91300af9..93225b53 100644 --- a/src/Server/Application.php +++ b/src/Server/Application.php @@ -247,7 +247,11 @@ public function getFramework() */ public function run(Request $request) { - ob_start(); + $shouldUseOb = $this->application['config']->get('swoole_http.ob_output', true); + + if ($shouldUseOb) { + ob_start(); + } // handle request with laravel or lumen $method = sprintf('run%s', ucfirst($this->framework)); @@ -255,14 +259,15 @@ public function run(Request $request) // prepare content for ob $content = ''; - $shouldUseOb = $this->application['config']->get('swoole_http.ob_output', true); - if ($response instanceof StreamedResponse || - $response instanceof BinaryFileResponse) { - $shouldUseOb = false; - } elseif ($response instanceof SymfonyResponse) { - $content = $response->getContent(); - } else { - $content = (string) $response; + if ($shouldUseOb) { + if ($response instanceof StreamedResponse || + $response instanceof BinaryFileResponse) { + $shouldUseOb = false; + } elseif ($response instanceof SymfonyResponse) { + $content = $response->getContent(); + } else { + $content = (string) $response; + } } // process terminating logics @@ -273,7 +278,9 @@ public function run(Request $request) $response->setContent(ob_get_contents()); } - ob_end_clean(); + if ($shouldUseOb) { + ob_end_clean(); + } return $response; } From 03e0f110dba4e49347a411b52dfbc850f0958bfa Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 10:02:13 +0800 Subject: [PATCH 08/17] apply sandbox to websocket pipeline --- src/Server/Manager.php | 2 +- src/Websocket/CanWebsocket.php | 34 ++++++++++++++++++++++++---------- src/Websocket/Websocket.php | 16 ++++++++++++++++ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 29a80bc6..93659292 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -217,7 +217,7 @@ public function onWorkerStart(HttpServer $server) $this->bindToLaravelApp(); // set application to sandbox environment - if ($this->isSandbox) { + if ($this->isSandbox || $this->isWebsocket) { $this->sandbox = Sandbox::make($this->getApplication()); } diff --git a/src/Websocket/CanWebsocket.php b/src/Websocket/CanWebsocket.php index f3656442..06b6b352 100644 --- a/src/Websocket/CanWebsocket.php +++ b/src/Websocket/CanWebsocket.php @@ -58,12 +58,13 @@ public function onOpen(Server $server, $swooleRequest) try { // check if socket.io connection established - if ($this->websocketHandler->onOpen($swooleRequest->fd, $illuminateRequest)) { - $this->websocket->reset(true)->setSender($swooleRequest->fd); - // trigger 'connect' websocket event - if ($this->websocket->eventExists('connect')) { - $this->websocket->call('connect', $illuminateRequest); - } + if (! $this->websocketHandler->onOpen($swooleRequest->fd, $illuminateRequest)) { + return; + } + $this->websocket->reset(true)->setSender($swooleRequest->fd); + // trigger 'connect' websocket event + if ($this->websocket->eventExists('connect')) { + $this->callOnConnect($illuminateRequest); } } catch (Exception $e) { $this->logServerError($e); @@ -81,16 +82,17 @@ public function onMessage(Server $server, Frame $frame) $data = $frame->data; try { - $skip = $this->parser->execute($server, $frame); - - if ($skip) { + // execute parser strategies and skip non-message packet + if ($this->parser->execute($server, $frame)) { return; } + // decode raw message via parser $payload = $this->parser->decode($frame); $this->websocket->reset(true)->setSender($frame->fd); + // dispatch message to registered event callback if ($this->websocket->eventExists($payload['event'])) { $this->websocket->call($payload['event'], $payload['data']); } else { @@ -242,7 +244,7 @@ protected function bindRoom() protected function bindWebsocket() { $this->app->singleton(Websocket::class, function ($app) { - return $this->websocket = new Websocket($app['swoole.room'], new Pipeline($app)); + return $this->websocket = new Websocket($app['swoole.room'], new Pipeline); }); $this->app->alias(Websocket::class, 'swoole.websocket'); } @@ -276,4 +278,16 @@ protected function normalizePushData(array $data) return [$opcode, $sender, $fds, $broadcast, $assigned, $event, $message]; } + + /** + * Call on connect event callback . + */ + protected function callOnConnect($illuminateRequest) + { + // set sandbox container to websocket pipeline + $this->websocket->setContainer($this->sandbox->getLaravelApp()); + $this->sandbox->enable(); + $this->websocket->call('connect', $illuminateRequest); + $this->sandbox->disable(); + } } diff --git a/src/Websocket/Websocket.php b/src/Websocket/Websocket.php index f186ca04..7737c1db 100644 --- a/src/Websocket/Websocket.php +++ b/src/Websocket/Websocket.php @@ -4,6 +4,7 @@ use InvalidArgumentException; use Illuminate\Support\Facades\App; +use Illuminate\Contracts\Container\Container; use SwooleTW\Http\Websocket\Rooms\RoomContract; use Illuminate\Contracts\Pipeline\Pipeline as PipelineContract; @@ -359,6 +360,21 @@ public function middleware($middleware = null) return $this; } + /** + * Set container to pipeline. + */ + public function setContainer(Container $container) + { + $pipeline = $this->pipeline; + + $closure = function () use ($container) { + $this->container = $container; + }; + + $resetPipeline = $closure->bindTo($pipeline, $pipeline); + $resetPipeline(); + } + /** * Set the given request through the middleware. * From 1fc4506b2623c0b300794b9430c1e83084f05b72 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 10:57:12 +0800 Subject: [PATCH 09/17] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 44ae45fb..8701768d 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,12 @@ This package provides a high performance HTTP server to speed up your laravel/lu ## Features -* Run Laravel/Lumen application on top of Swoole. -* Sandbox mode to decrease technical gap for beginners. -* Support running websocket server in Laravel. +* Run **Laravel/Lumen** application on top of **Swoole**. +* Outstanding performance boosting up to **30x**. +* Sandbox mode to isolate app container. +* Support running websocket server in **Laravel**. * Support `Socket.io` protocol. -* Support Swoole table. +* Support Swoole table for cross-process data sharing. ## Documentation From e05e2c15b09726b5fb7e622e30729bd4ac62efa1 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 22:01:03 +0800 Subject: [PATCH 10/17] add cookie to pre-resolved list in sandbox --- src/Server/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/Application.php b/src/Server/Application.php index 93225b53..1e964e91 100644 --- a/src/Server/Application.php +++ b/src/Server/Application.php @@ -69,7 +69,7 @@ class Application */ protected $resolves = [ 'view', 'files', 'session', 'session.store', 'routes', - 'db', 'db.factory', 'cache', 'cache.store', 'config', + 'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie', 'encrypter', 'hash', 'router', 'translator', 'url', 'log' ]; From 561fb5a87b3b664d2282d54e1dfda485d92bf34c Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 22:04:55 +0800 Subject: [PATCH 11/17] remove session clear since flush is available from 5.1 --- src/Server/Application.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Server/Application.php b/src/Server/Application.php index 1e964e91..649a58dc 100644 --- a/src/Server/Application.php +++ b/src/Server/Application.php @@ -379,9 +379,7 @@ protected function terminateLaravel(Request $request, $response) // clean laravel session if ($request->hasSession()) { $session = $request->getSession(); - if (method_exists($session, 'clear')) { - $session->clear(); - } elseif (method_exists($session, 'flush')) { + if (method_exists($session, 'flush')) { $session->flush(); } } From b137786f6b9bb08166f0104f19baf685f10479ed Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 22:10:48 +0800 Subject: [PATCH 12/17] remove flush function check --- src/Server/Application.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Server/Application.php b/src/Server/Application.php index 649a58dc..9ae1c007 100644 --- a/src/Server/Application.php +++ b/src/Server/Application.php @@ -379,9 +379,7 @@ protected function terminateLaravel(Request $request, $response) // clean laravel session if ($request->hasSession()) { $session = $request->getSession(); - if (method_exists($session, 'flush')) { - $session->flush(); - } + $session->flush(); } // clean laravel cookie queue From 9be0d560f4c84aebdc32d2680c6b9ff76de3c06f Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 23:20:43 +0800 Subject: [PATCH 13/17] log server error with laravel's exception handler --- src/Server/Manager.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Server/Manager.php b/src/Server/Manager.php index 93659292..8c1609b2 100644 --- a/src/Server/Manager.php +++ b/src/Server/Manager.php @@ -466,16 +466,6 @@ protected function setProcessName($process) */ 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); + $this->app[ExceptionHandler::class]->report($e); } } From dc1b04fc06986a24658470c7716ca7cdfc8bd4ce Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 23:21:53 +0800 Subject: [PATCH 14/17] improve callOnConnect function --- src/Websocket/CanWebsocket.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Websocket/CanWebsocket.php b/src/Websocket/CanWebsocket.php index 06b6b352..09b61b0e 100644 --- a/src/Websocket/CanWebsocket.php +++ b/src/Websocket/CanWebsocket.php @@ -8,6 +8,7 @@ use Illuminate\Pipeline\Pipeline; use SwooleTW\Http\Server\Request; use SwooleTW\Http\Websocket\Parser; +use Illuminate\Support\Facades\Facade; use SwooleTW\Http\Websocket\Websocket; use SwooleTW\Http\Websocket\HandlerContract; use SwooleTW\Http\Websocket\Rooms\RoomContract; @@ -284,8 +285,19 @@ protected function normalizePushData(array $data) */ protected function callOnConnect($illuminateRequest) { + $application = $this->sandbox->getLaravelApp(); + + // bind illuminate request to laravel/lumen + $application->instance('request', $illuminateRequest); + Facade::clearResolvedInstance('request'); + + // reset session + if (isset($application['session'])) { + $application['session']->flush(); + } + // set sandbox container to websocket pipeline - $this->websocket->setContainer($this->sandbox->getLaravelApp()); + $this->websocket->setContainer($application); $this->sandbox->enable(); $this->websocket->call('connect', $illuminateRequest); $this->sandbox->disable(); From 279de96a6babb2645bb057a085533c2322ab14f7 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 23:32:56 +0800 Subject: [PATCH 15/17] add websocket middleware --- config/swoole_websocket.php | 21 +++- src/Websocket/Middleware/Authenticate.php | 47 ++++++++ src/Websocket/Middleware/DecryptCookies.php | 126 ++++++++++++++++++++ src/Websocket/Middleware/StartSession.php | 85 +++++++++++++ src/Websocket/Websocket.php | 9 ++ 5 files changed, 283 insertions(+), 5 deletions(-) create mode 100644 src/Websocket/Middleware/Authenticate.php create mode 100644 src/Websocket/Middleware/DecryptCookies.php create mode 100644 src/Websocket/Middleware/StartSession.php diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php index 5566bb45..140fd2bb 100644 --- a/config/swoole_websocket.php +++ b/config/swoole_websocket.php @@ -9,6 +9,14 @@ */ 'handler' => SwooleTW\Http\Websocket\SocketIO\WebsocketHandler::class, + /* + |-------------------------------------------------------------------------- + | Default frame parser + | Replace it if you want to customize your websocket payload + |-------------------------------------------------------------------------- + */ + 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class, + /* |-------------------------------------------------------------------------- | Websocket route file path @@ -18,18 +26,21 @@ /* |-------------------------------------------------------------------------- - | Default websocket driver + | Default middlewares for on connect request |-------------------------------------------------------------------------- */ - 'default' => 'table', + 'middlewares' => [ + // SwooleTW\Http\Websocket\Middleware\DecryptCookies::class, + // SwooleTW\Http\Websocket\Middleware\StartSession::class, + // SwooleTW\Http\Websocket\Middleware\Authenticate::class, + ], /* |-------------------------------------------------------------------------- - | Default frame parser - | Replace it if you want to customize your websocket payload + | Default websocket driver |-------------------------------------------------------------------------- */ - 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class, + 'default' => 'table', /* |-------------------------------------------------------------------------- diff --git a/src/Websocket/Middleware/Authenticate.php b/src/Websocket/Middleware/Authenticate.php new file mode 100644 index 00000000..032221f6 --- /dev/null +++ b/src/Websocket/Middleware/Authenticate.php @@ -0,0 +1,47 @@ +auth = $auth; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + * + * @throws \Illuminate\Auth\AuthenticationException + */ + public function handle($request, Closure $next) + { + if ($user = $this->auth->authenticate()) { + $request->setUserResolver(function () use ($user) { + return $user; + }); + } + + return $next($request); + } +} diff --git a/src/Websocket/Middleware/DecryptCookies.php b/src/Websocket/Middleware/DecryptCookies.php new file mode 100644 index 00000000..6a51177b --- /dev/null +++ b/src/Websocket/Middleware/DecryptCookies.php @@ -0,0 +1,126 @@ +encrypter = $encrypter; + } + + /** + * Disable encryption for the given cookie name(s). + * + * @param string|array $name + * @return void + */ + public function disableFor($name) + { + $this->except = array_merge($this->except, (array) $name); + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle($request, Closure $next) + { + return $next($this->decrypt($request)); + } + + /** + * Decrypt the cookies on the request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Request + */ + protected function decrypt(Request $request) + { + foreach ($request->cookies as $key => $cookie) { + if ($this->isDisabled($key)) { + continue; + } + + try { + $request->cookies->set($key, $this->decryptCookie($cookie)); + } catch (DecryptException $e) { + $request->cookies->set($key, null); + } + } + + return $request; + } + + /** + * Decrypt the given cookie and return the value. + * + * @param string|array $cookie + * @return string|array + */ + protected function decryptCookie($cookie) + { + return is_array($cookie) + ? $this->decryptArray($cookie) + : $this->encrypter->decrypt($cookie); + } + + /** + * Decrypt an array based cookie. + * + * @param array $cookie + * @return array + */ + protected function decryptArray(array $cookie) + { + $decrypted = []; + + foreach ($cookie as $key => $value) { + if (is_string($value)) { + $decrypted[$key] = $this->encrypter->decrypt($value); + } + } + + return $decrypted; + } + + /** + * Determine whether encryption has been disabled for the given cookie. + * + * @param string $name + * @return bool + */ + public function isDisabled($name) + { + return in_array($name, $this->except); + } +} diff --git a/src/Websocket/Middleware/StartSession.php b/src/Websocket/Middleware/StartSession.php new file mode 100644 index 00000000..bb5de3eb --- /dev/null +++ b/src/Websocket/Middleware/StartSession.php @@ -0,0 +1,85 @@ +manager = $manager; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle($request, Closure $next) + { + if ($this->sessionConfigured()) { + $request->setLaravelSession( + $session = $this->startSession($request) + ); + } + + return $next($request); + } + + /** + * Start the session for the given request. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\Session\Session + */ + protected function startSession(Request $request) + { + return tap($this->getSession($request), function ($session) use ($request) { + $session->setRequestOnHandler($request); + + $session->start(); + }); + } + + /** + * Get the session implementation from the manager. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\Session\Session + */ + public function getSession(Request $request) + { + return tap($this->manager->driver(), function ($session) use ($request) { + $session->setId($request->cookies->get($session->getName())); + }); + } + + /** + * Determine if a session driver has been configured. + * + * @return bool + */ + protected function sessionConfigured() + { + return ! is_null($this->manager->getSessionConfig()['driver'] ?? null); + } +} diff --git a/src/Websocket/Websocket.php b/src/Websocket/Websocket.php index 7737c1db..09a7c9a7 100644 --- a/src/Websocket/Websocket.php +++ b/src/Websocket/Websocket.php @@ -73,6 +73,7 @@ public function __construct(RoomContract $room, PipelineContract $pipeline) { $this->room = $room; $this->pipeline = $pipeline; + $this->setDafaultMiddlewares(); } /** @@ -360,6 +361,14 @@ public function middleware($middleware = null) return $this; } + /** + * Set default middlewares. + */ + protected function setDafaultMiddlewares() + { + $this->middlewares = app('config')->get('swoole_websocket.middlewares', []); + } + /** * Set container to pipeline. */ From 20e669120d8a415ea256428e82f0fea8951286eb Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Wed, 2 May 2018 23:38:28 +0800 Subject: [PATCH 16/17] fix plural word for middleware --- config/swoole_websocket.php | 4 ++-- src/Websocket/Websocket.php | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php index 140fd2bb..a9c770da 100644 --- a/config/swoole_websocket.php +++ b/config/swoole_websocket.php @@ -26,10 +26,10 @@ /* |-------------------------------------------------------------------------- - | Default middlewares for on connect request + | Default middleware for on connect request |-------------------------------------------------------------------------- */ - 'middlewares' => [ + 'middleware' => [ // SwooleTW\Http\Websocket\Middleware\DecryptCookies::class, // SwooleTW\Http\Websocket\Middleware\StartSession::class, // SwooleTW\Http\Websocket\Middleware\Authenticate::class, diff --git a/src/Websocket/Websocket.php b/src/Websocket/Websocket.php index 09a7c9a7..8825c383 100644 --- a/src/Websocket/Websocket.php +++ b/src/Websocket/Websocket.php @@ -73,7 +73,7 @@ public function __construct(RoomContract $room, PipelineContract $pipeline) { $this->room = $room; $this->pipeline = $pipeline; - $this->setDafaultMiddlewares(); + $this->setDafaultMiddleware(); } /** @@ -254,8 +254,8 @@ public function call(string $event, $data = null) $isConnect = $event === static::EVENT_CONNECT; $dataKey = $isConnect ? 'request' : 'data'; - // dispatch request to pipeline if middlewares are set - if ($isConnect && count($this->middlewares)) { + // dispatch request to pipeline if middleware are set + if ($isConnect && count($this->middleware)) { $data = $this->setRequestThroughMiddleware($data); } @@ -344,29 +344,29 @@ public function reset($force = false) } /** - * Get or set middlewares. + * Get or set middleware. */ public function middleware($middleware = null) { if (is_null($middleware)) { - return $this->middlewares; + return $this->middleware; } if (is_string($middleware)) { $middleware = func_get_args(); } - $this->middlewares = array_unique(array_merge($this->middlewares, $middleware)); + $this->middleware = array_unique(array_merge($this->middleware, $middleware)); return $this; } /** - * Set default middlewares. + * Set default middleware. */ - protected function setDafaultMiddlewares() + protected function setDafaultMiddleware() { - $this->middlewares = app('config')->get('swoole_websocket.middlewares', []); + $this->middleware = app('config')->get('swoole_websocket.middleware', []); } /** @@ -394,7 +394,7 @@ protected function setRequestThroughMiddleware($request) { return $this->pipeline ->send($request) - ->through($this->middlewares) + ->through($this->middleware) ->then(function ($request) { return $request; }); From 3186d7c67b6c04fc42ba4c351f76e9bbe49e2bd2 Mon Sep 17 00:00:00 2001 From: Albert Chen Date: Thu, 3 May 2018 00:28:17 +0800 Subject: [PATCH 17/17] catch AuthenticationException in Authenticate middleware --- src/Websocket/Middleware/Authenticate.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Websocket/Middleware/Authenticate.php b/src/Websocket/Middleware/Authenticate.php index 032221f6..27315d7b 100644 --- a/src/Websocket/Middleware/Authenticate.php +++ b/src/Websocket/Middleware/Authenticate.php @@ -3,6 +3,7 @@ namespace SwooleTW\Http\Websocket\Middleware; use Closure; +use Illuminate\Auth\AuthenticationException; use Illuminate\Contracts\Auth\Factory as Auth; class Authenticate @@ -36,10 +37,14 @@ public function __construct(Auth $auth) */ public function handle($request, Closure $next) { - if ($user = $this->auth->authenticate()) { - $request->setUserResolver(function () use ($user) { - return $user; - }); + try { + if ($user = $this->auth->authenticate()) { + $request->setUserResolver(function () use ($user) { + return $user; + }); + } + } catch (AuthenticationException $e) { + // do nothing } return $next($request);