diff --git a/Readme.md b/Readme.md index 1bbfbb9..acf2944 100644 --- a/Readme.md +++ b/Readme.md @@ -39,6 +39,12 @@ return [ ]; ``` +Run migrations: + +```shell +php artisan migrate +``` + ## FastCGI Proxy This server proxy the request to a FastCGI Socket or TCP service. IT is capable of serving WordPress websites, advanced Laravel applications or even single PHP files if needed. It can serve HTTPS and HTTP. diff --git a/composer.json b/composer.json index 3ea9158..0ecd983 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "ext-openswoole": "22.*", "adoy/fastcgi-client": "^1.0", "guzzlehttp/guzzle": "^7.7", - "kanata-php/socket-conveyor": "^1.3" + "kanata-php/socket-conveyor": "^1.3", + "textalk/websocket": "^1.5" }, "extra": { "laravel": { diff --git a/composer.lock b/composer.lock index 4fdbabb..e4344da 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9b52293c99452346ec86e3d494ee9ea9", + "content-hash": "0eb6be5e5618e5c8a8e850574fe82f62", "packages": [ { "name": "adoy/fastcgi-client", @@ -374,16 +374,16 @@ }, { "name": "kanata-php/socket-conveyor", - "version": "1.3.0", + "version": "1.3.2", "source": { "type": "git", "url": "https://github.com/kanata-php/socket-conveyor.git", - "reference": "c4daeb5ec518490456af29675fef46e9300b58aa" + "reference": "7f690a1b656e5dec2952fffb47092e4733933e07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kanata-php/socket-conveyor/zipball/c4daeb5ec518490456af29675fef46e9300b58aa", - "reference": "c4daeb5ec518490456af29675fef46e9300b58aa", + "url": "https://api.github.com/repos/kanata-php/socket-conveyor/zipball/7f690a1b656e5dec2952fffb47092e4733933e07", + "reference": "7f690a1b656e5dec2952fffb47092e4733933e07", "shasum": "" }, "require": { @@ -417,7 +417,7 @@ "description": "A WebSocket/Socket message Router", "support": { "issues": "https://github.com/kanata-php/socket-conveyor/issues", - "source": "https://github.com/kanata-php/socket-conveyor/tree/1.3.0" + "source": "https://github.com/kanata-php/socket-conveyor/tree/1.3.2" }, "funding": [ { @@ -425,7 +425,7 @@ "type": "github" } ], - "time": "2023-01-15T22:53:14+00:00" + "time": "2023-08-26T23:30:27+00:00" }, { "name": "league/pipeline", @@ -484,6 +484,112 @@ }, "time": "2018-06-05T21:06:51+00:00" }, + { + "name": "phrity/net-uri", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-net-uri.git", + "reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-net-uri/zipball/3f458e0c4d1ddc0e218d7a5b9420127c63925f43", + "reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0 | ^2.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^9.0 | ^10.0", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Phrity\\Net\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "PSR-7 Uri and PSR-17 UriFactory implementation", + "homepage": "https://phrity.sirn.se/net-uri", + "keywords": [ + "psr-17", + "psr-7", + "uri", + "uri factory" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-net-uri/issues", + "source": "https://github.com/sirn-se/phrity-net-uri/tree/1.3.0" + }, + "time": "2023-08-21T10:33:06+00:00" + }, + { + "name": "phrity/util-errorhandler", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sirn-se/phrity-util-errorhandler.git", + "reference": "dc9ac8fb70d733c48a9d9d1eb50f7022172da6bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/dc9ac8fb70d733c48a9d9d1eb50f7022172da6bc", + "reference": "dc9ac8fb70d733c48a9d9d1eb50f7022172da6bc", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sören Jensen", + "email": "sirn@sirn.se", + "homepage": "https://phrity.sirn.se" + } + ], + "description": "Inline error handler; catch and resolve errors for code block.", + "homepage": "https://phrity.sirn.se/util-errorhandler", + "keywords": [ + "error", + "warning" + ], + "support": { + "issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues", + "source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.0.1" + }, + "time": "2022-10-27T12:14:42+00:00" + }, { "name": "psr/http-client", "version": "1.0.2", @@ -593,16 +699,16 @@ }, { "name": "psr/http-message", - "version": "2.0", + "version": "1.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "shasum": "" }, "require": { @@ -611,7 +717,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -626,7 +732,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -640,9 +746,59 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, + "time": "2023-04-04T09:50:52+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2023-04-04T09:54:51+00:00" + "time": "2021-07-14T16:46:02+00:00" }, { "name": "ralouphie/getallheaders", @@ -754,6 +910,57 @@ } ], "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "textalk/websocket", + "version": "1.6.3", + "source": { + "type": "git", + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "67de79745b1a357caf812bfc44e0abf481cee012" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/67de79745b1a357caf812bfc44e0abf481cee012", + "reference": "67de79745b1a357caf812bfc44e0abf481cee012", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "phrity/net-uri": "^1.0", + "phrity/util-errorhandler": "^1.0", + "psr/http-message": "^1.0", + "psr/log": "^1.0 | ^2.0 | ^3.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^9.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen" + } + ], + "description": "WebSocket client and server", + "support": { + "issues": "https://github.com/Textalk/websocket-php/issues", + "source": "https://github.com/Textalk/websocket-php/tree/1.6.3" + }, + "time": "2022-11-07T18:59:33+00:00" } ], "packages-dev": [ @@ -3867,56 +4074,6 @@ }, "time": "2019-01-08T18:20:26+00:00" }, - { - "name": "psr/log", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" - }, - "time": "2021-07-14T16:46:02+00:00" - }, { "name": "psr/simple-cache", "version": "3.0.0", @@ -5336,16 +5493,16 @@ }, { "name": "symfony/console", - "version": "v6.3.2", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898" + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/aa5d64ad3f63f2e48964fc81ee45cb318a723898", - "reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898", + "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", "shasum": "" }, "require": { @@ -5406,7 +5563,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.2" + "source": "https://github.com/symfony/console/tree/v6.3.4" }, "funding": [ { @@ -5422,7 +5579,7 @@ "type": "tidelift" } ], - "time": "2023-07-19T20:17:28+00:00" + "time": "2023-08-16T10:10:12+00:00" }, { "name": "symfony/css-selector", @@ -5785,16 +5942,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.3.2", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3" + "reference": "cac1556fdfdf6719668181974104e6fcfa60e844" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3", - "reference": "43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cac1556fdfdf6719668181974104e6fcfa60e844", + "reference": "cac1556fdfdf6719668181974104e6fcfa60e844", "shasum": "" }, "require": { @@ -5842,7 +5999,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.2" + "source": "https://github.com/symfony/http-foundation/tree/v6.3.4" }, "funding": [ { @@ -5858,20 +6015,20 @@ "type": "tidelift" } ], - "time": "2023-07-23T21:58:39+00:00" + "time": "2023-08-22T08:20:46+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.3", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "d3b567f0addf695e10b0c6d57564a9bea2e058ee" + "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d3b567f0addf695e10b0c6d57564a9bea2e058ee", - "reference": "d3b567f0addf695e10b0c6d57564a9bea2e058ee", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", + "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", "shasum": "" }, "require": { @@ -5880,7 +6037,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.3", "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^6.2.7", + "symfony/http-foundation": "^6.3.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -5888,7 +6045,7 @@ "symfony/cache": "<5.4", "symfony/config": "<6.1", "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.3", + "symfony/dependency-injection": "<6.3.4", "symfony/doctrine-bridge": "<5.4", "symfony/form": "<5.4", "symfony/http-client": "<5.4", @@ -5912,7 +6069,7 @@ "symfony/config": "^6.1", "symfony/console": "^5.4|^6.0", "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.3", + "symfony/dependency-injection": "^6.3.4", "symfony/dom-crawler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", @@ -5955,7 +6112,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.3.3" + "source": "https://github.com/symfony/http-kernel/tree/v6.3.4" }, "funding": [ { @@ -5971,7 +6128,7 @@ "type": "tidelift" } ], - "time": "2023-07-31T10:33:00+00:00" + "time": "2023-08-26T13:54:49+00:00" }, { "name": "symfony/mailer", @@ -6957,16 +7114,16 @@ }, { "name": "symfony/process", - "version": "v6.3.2", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d" + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d", - "reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d", + "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", "shasum": "" }, "require": { @@ -6998,7 +7155,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.2" + "source": "https://github.com/symfony/process/tree/v6.3.4" }, "funding": [ { @@ -7014,7 +7171,7 @@ "type": "tidelift" } ], - "time": "2023-07-12T16:00:22+00:00" + "time": "2023-08-07T10:39:22+00:00" }, { "name": "symfony/routing", @@ -7578,16 +7735,16 @@ }, { "name": "symfony/var-dumper", - "version": "v6.3.3", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "77fb4f2927f6991a9843633925d111147449ee7a" + "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/77fb4f2927f6991a9843633925d111147449ee7a", - "reference": "77fb4f2927f6991a9843633925d111147449ee7a", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2027be14f8ae8eae999ceadebcda5b4909b81d45", + "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45", "shasum": "" }, "require": { @@ -7642,7 +7799,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.3" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.4" }, "funding": [ { @@ -7658,7 +7815,7 @@ "type": "tidelift" } ], - "time": "2023-07-31T07:08:24+00:00" + "time": "2023-08-24T14:51:05+00:00" }, { "name": "symfony/yaml", diff --git a/database/migrations/2023_08_26_073700_create_wslisteners_table.php b/database/migrations/2023_08_26_073700_create_wslisteners_table.php new file mode 100644 index 0000000..0b270af --- /dev/null +++ b/database/migrations/2023_08_26_073700_create_wslisteners_table.php @@ -0,0 +1,29 @@ +id(); + $table->integer('fd'); + $table->string('action'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('wslisteners'); + } +}; diff --git a/database/migrations/2023_08_26_073701_create_wschannels_table.php b/database/migrations/2023_08_26_073701_create_wschannels_table.php new file mode 100644 index 0000000..29c0a1c --- /dev/null +++ b/database/migrations/2023_08_26_073701_create_wschannels_table.php @@ -0,0 +1,29 @@ +id(); + $table->integer('fd'); + $table->string('channel'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('wschannels'); + } +}; diff --git a/database/migrations/2023_08_26_073702_create_wsassociations_table.php b/database/migrations/2023_08_26_073702_create_wsassociations_table.php new file mode 100644 index 0000000..87ad3bd --- /dev/null +++ b/database/migrations/2023_08_26_073702_create_wsassociations_table.php @@ -0,0 +1,29 @@ +id(); + $table->integer('fd'); + $table->integer('user_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('wsassociations'); + } +}; diff --git a/src/JackedServerProvider.php b/src/JackedServerProvider.php index 89b8d24..0ce7ec1 100644 --- a/src/JackedServerProvider.php +++ b/src/JackedServerProvider.php @@ -14,6 +14,8 @@ public function register(): void public function boot(): void { + $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); + $this->publishes([ __DIR__.'/../config/jacked-server.php' => config_path('jacked-server.php'), ], 'jacked-server'); diff --git a/src/Models/WebSockets/AssociationsPersistence.php b/src/Models/WebSockets/AssociationsPersistence.php new file mode 100644 index 0000000..68575ae --- /dev/null +++ b/src/Models/WebSockets/AssociationsPersistence.php @@ -0,0 +1,92 @@ +disassoc($userId); + try { + WsAssociation::create([ + 'fd' => $fd, + 'user_id' => $userId, + ]); + } catch (Exception|Error $e) { + // -- + } + } + + /** + * Disassociate a user from a userId. + * + * @param int $userId + * @return void + */ + public function disassoc(int $userId): void + { + try { + WsAssociation::where('user_id', '=', $userId)?->delete(); + } catch (Exception|Error $e) { + // -- + } + } + + /** + * Get user-id for a fd. + * + * @param ?int $fd + * @return int + */ + public function getAssoc(int $fd): ?int + { + return WsAssociation::where('fd', '=', $fd)->get()->first()?->user_id; + } + + /** + * Retrieve all associations. + * + * @return array Format: + */ + public function getAllAssocs(): array + { + try { + $associations = WsAssociation::all()->toArray(); + } catch (Exception|Error $e) { + return []; + } + + if (empty($associations)) { + return []; + } + + $connections = []; + foreach ($associations as $association) { + $connections[$association['fd']] = $association['user_id']; + } + + return $connections; + } + + public function refresh(bool $fresh = false): static + { + if (!$fresh) { + return $this; + } + + WsAssociation::truncate(); + return $this; + } +} diff --git a/src/Models/WebSockets/ChannelsPersistence.php b/src/Models/WebSockets/ChannelsPersistence.php new file mode 100644 index 0000000..183f0ed --- /dev/null +++ b/src/Models/WebSockets/ChannelsPersistence.php @@ -0,0 +1,63 @@ +disconnect($fd); + try { + WsChannel::create([ + 'fd' => $fd, + 'channel' => $channel, + ]); + } catch (Exception|Error $e) { + // -- + } + } + + public function disconnect(int $fd): void + { + try { + WsChannel::where('fd', '=', $fd)->first()?->delete(); + } catch (Exception|Error $e) { + // -- + } + } + + public function getAllConnections(): array + { + try { + $channels = WsChannel::all()->toArray(); + } catch (Exception|Error $e) { + return []; + } + + if (empty($channels)) { + return []; + } + + $connections = []; + foreach ($channels as $channel) { + $connections[$channel['fd']] = $channel['channel']; + } + + return $connections; + } + + public function refresh(bool $fresh = false): static + { + if (!$fresh) { + return $this; + } + + WsChannel::truncate(); + return $this; + } +} diff --git a/src/Models/WebSockets/ListenersPersistence.php b/src/Models/WebSockets/ListenersPersistence.php new file mode 100644 index 0000000..8cde752 --- /dev/null +++ b/src/Models/WebSockets/ListenersPersistence.php @@ -0,0 +1,105 @@ + $fd, + 'action' => $action, + ]); + } catch (Exception|Error $e) { + // -- + } + } + + public function getListener(int $fd): ?array + { + return WsListener::where('fd', '=', $fd)->first()?->toArray(); + } + + /** + * @return array Format: [fd => [listener1, listener2, ...]] + */ + public function getAllListeners(): array + { + try { + $listeners = WsListener::all()->toArray(); + } catch (Exception|Error $e) { + return []; + } + + if (empty($listeners)) { + return []; + } + + $listenersArray = []; + foreach ($listeners as $listener) { + if (!isset($listenersArray[$listener['fd']])) { + $listenersArray[$listener['fd']] = []; + } + + if (!in_array($listener['action'], $listenersArray[$listener['fd']])) { + $listenersArray[$listener['fd']][] = $listener['action']; + } + } + + return $listenersArray; + } + + public function stopListener(int $fd, string $action): bool + { + try { + return WsListener::where('fd', '=', $fd) + ->where('action', '=', $action) + ->first() + ?->delete(); + } catch (Exception|Error $e) { + // -- + } + + return false; + } + + public function stopListenersForFd(int $fd): bool + { + try { + return WsListener::where('fd', '=', $fd) + ->first() + ?->delete(); + } catch (Exception|Error $e) { + // -- + } + + return false; + } + + public function cleanListeners(): bool + { + try { + return WsListener::all()->delete(); + } catch (Exception|Error $e) { + // -- + } + + return false; + } + + public function refresh(bool $fresh = false): static + { + if (!$fresh) { + return $this; + } + + WsListener::truncate(); + return $this; + } +} diff --git a/src/Models/WsAssociation.php b/src/Models/WsAssociation.php new file mode 100644 index 0000000..f7d30e9 --- /dev/null +++ b/src/Models/WsAssociation.php @@ -0,0 +1,21 @@ + config('jacked-server.log.path', storage_path('logs/jacked-server.log')), 'replace_placeholders' => config('jacked-server.log.replace-placeholders', 'single'), ]); + $this->wsPersistence = [new ChannelsPersistence, new AssociationsPersistence, new ListenersPersistence]; } public function run(): void @@ -67,6 +72,8 @@ public function run(): void 'open_http_protocol' => true, ] : [])); + Conveyor::refresh($this->wsPersistence); + $server = new OpenSwooleServer( $this->host ?? config('jacked-server.host', '0.0.0.0'), $this->port ?? $primaryPort, @@ -106,6 +113,7 @@ public function handleWsOpen(OpenSwooleServer $server, Request $request): void public function handleWsHandshake(Request $request, Response $response): bool { + $this->logger->info($this->logPrefix . ' Handshake received from ' . $request->fd); // evaluate intention to upgrade to websocket try { $headers = $this->processSecWebSocketKey($request); @@ -128,7 +136,9 @@ public function handleWsHandshake(Request $request, Response $response): bool public function handleWsMessage(OpenSwooleServer $server, Frame $frame): void { $this->logger->info($this->logPrefix . ' Message received from ' . $frame->fd); - Conveyor::run($frame->data, $frame->fd, $server); + Conveyor::run($frame->data, $frame->fd, $server, [ + 'persistence' => $this->wsPersistence, + ]); } public function sslRedirectRequest(Request $request, Response $response): void diff --git a/tests/Unit/WebSocketServerTest.php b/tests/Unit/WebSocketServerTest.php new file mode 100644 index 0000000..71e8f4d --- /dev/null +++ b/tests/Unit/WebSocketServerTest.php @@ -0,0 +1,50 @@ +startServer( + rtrim(__DIR__, '/Unit') . '/Assets/assert-ok.php', + rtrim(__DIR__, '/Unit') . '/Assets', + ); + + $response = Http::get('http://127.0.0.1:' . $this->port . '/assert-ok.php'); + $this->assertEquals(200, $response->status()); + $this->assertEquals('ok', $response->body()); + + $client = new Client('ws://127.0.0.1:' . $this->port); + $client->text($expectedMessage); + $result = $client->receive(); + $client->close(); + $this->assertEquals($expectedMessage, json_decode($result, true)['data']); + } + + public function test_can_connect_to_ws_server_with_default_auth() + { + $expectedMessage = 'text-message'; + + $this->startServer( + rtrim(__DIR__, '/Unit') . '/Assets/assert-ok.php', + rtrim(__DIR__, '/Unit') . '/Assets', + ); + + $response = Http::get('http://127.0.0.1:' . $this->port . '/assert-ok.php'); + $this->assertEquals(200, $response->status()); + $this->assertEquals('ok', $response->body()); + + $client = new Client('ws://127.0.0.1:' . $this->port); + $client->text($expectedMessage); + $result = $client->receive(); + $client->close(); + $this->assertEquals($expectedMessage, json_decode($result, true)['data']); + } +}