From 63f1758fd0a85d49705a98b4e158efd255ea76e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Wed, 20 Nov 2024 18:01:44 +0100 Subject: [PATCH] Update elephant.io (#3) * Remove polling fallback * Update elephantio * Update phpstan * Replace deprecated method for elephant.io client * Replace deprecated method * Update elephant.io client * Update elephantio * Fix update * Update elephantio to latest version and use 'wait' instead of 'drain' * Update elephantio to latest version --- composer.json | 2 +- composer.lock | 30 +-- src/Service/ScannerService.php | 210 +----------------- .../fixture/reverse_proxy_no_websocket.txt | 2 +- 4 files changed, 23 insertions(+), 221 deletions(-) diff --git a/composer.json b/composer.json index eba87ac..780f699 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "ext-json": "*", "guzzlehttp/guzzle": "^7.8", "symfony/console": "^6.4|^7.0", - "elephantio/elephant.io": "4.3.*" + "elephantio/elephant.io": "^4.8" }, "require-dev": { "phpstan/phpstan": "^1.10" diff --git a/composer.lock b/composer.lock index e90b8db..ada9f91 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8469744c3865329a8a4bdc97ed8c960d", + "content-hash": "66b67730c0ff27c7713935ba8cf337fe", "packages": [ { "name": "elephantio/elephant.io", - "version": "v4.3.2", + "version": "v4.12.0", "source": { "type": "git", "url": "https://github.com/ElephantIO/elephant.io.git", - "reference": "ac2e107353cab22cb864b406aa6718163258f3c7" + "reference": "b10a7f68ae044b25b31c89ca7a7484853c1d2bd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ElephantIO/elephant.io/zipball/ac2e107353cab22cb864b406aa6718163258f3c7", - "reference": "ac2e107353cab22cb864b406aa6718163258f3c7", + "url": "https://api.github.com/repos/ElephantIO/elephant.io/zipball/b10a7f68ae044b25b31c89ca7a7484853c1d2bd7", + "reference": "b10a7f68ae044b25b31c89ca7a7484853c1d2bd7", "shasum": "" }, "require": { @@ -67,18 +67,20 @@ "role": "Contributors :)" } ], - "description": "Send events to a real time websocket engine through PHP", + "description": "Send events to a socket.io server through PHP", "homepage": "https://elephantio.github.io/elephant.io/", "keywords": [ + "Socket.io", "dialog", "nodejs", "real time", "websocket" ], "support": { - "source": "https://github.com/ElephantIO/elephant.io/tree/v4.3.2" + "issues": "https://github.com/ElephantIO/elephant.io/issues", + "source": "https://github.com/ElephantIO/elephant.io/tree/v4.12.0" }, - "time": "2024-01-29T02:54:29+00:00" + "time": "2024-11-19T00:45:41+00:00" }, { "name": "guzzlehttp/guzzle", @@ -620,16 +622,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -664,9 +666,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "ralouphie/getallheaders", diff --git a/src/Service/ScannerService.php b/src/Service/ScannerService.php index 6234225..a689554 100644 --- a/src/Service/ScannerService.php +++ b/src/Service/ScannerService.php @@ -221,17 +221,6 @@ private function scanPad(ScannerServiceCallbackInterface $callback): void $this->doSocketWebsocket($socketIoVersion, $cookieString, $callback, $token); } catch (Exception $e) { $callback->onScanPadException($e); - - try { - if ($socketIoVersion === ElephantClient::CLIENT_4X) { - $this->doSocketPolling4($cookies, $token, $callback); - return; - } - - $this->doSocketPolling($socketIoVersion, $cookies, $token, $callback); - } catch (Exception $e) { - $callback->onScanPadException($e); - } } } @@ -356,191 +345,6 @@ private function scanHealth(ScannerServiceCallbackInterface $callback): void } } - private function doSocketPolling( - int $socketIoVersion, - CookieJar $cookies, - string $token, - ScannerServiceCallbackInterface $callback - ): void { - $engine = ElephantClient::engine($socketIoVersion, ''); - - $queryParameters = [ - 'padId' => $this->padId, - 'EIO' => $engine->getOptions()['version'], - 'transport' => 'polling', - 't' => Yeast::yeast(), - 'b64' => 1, - ]; - - $response = $this->client->get($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'cookies' => $cookies, - ]); - $body = (string)$response->getBody(); - if ($body === 'Welcome to socket.io.') { - $this->packageVersion = '1.4.0'; - throw new Exception('Socket.io 1 not supported'); - } - $curlyBracketPos = strpos($body, '{'); - if ($curlyBracketPos === false) { - throw new Exception('No JSON response: ' . $body); - } - $body = substr($body, $curlyBracketPos); - $data = json_decode($body, true, 512, JSON_THROW_ON_ERROR); - $sid = $data['sid']; - - $queryParameters['sid'] = $sid; - $queryParameters['t'] = Yeast::yeast(); - - $response = $this->client->get($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'cookies' => $cookies, - ]); - $body = (string)$response->getBody(); - if ($body !== '2:40') { - throw new Exception('Invalid response: ' . $body); - } - - $postData = json_encode([ - 'message', - [ - 'component' => 'pad', - 'type' => 'CLIENT_READY', - 'padId' => $this->padId, - 'sessionID' => 'null', - 'token' => $token, - 'password' => null, - 'protocolVersion' => 2, - ] - ]); - - $queryParameters['t'] = Yeast::yeast(); - $response = $this->client->post($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'body' => (mb_strlen($postData) + 2) . ':42' . $postData, - 'cookies' => $cookies, - ]); - $body = (string)$response->getBody(); - if ($body !== 'ok') { - throw new Exception('Invalid response: ' . $body); - } - - $queryParameters['t'] = Yeast::yeast(); - $response = $this->client->get($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'cookies' => $cookies, - ]); - $this->handleClientVarsResponse($response, $callback); - } - - private function doSocketPolling4( - CookieJar $cookies, - string $token, - ScannerServiceCallbackInterface $callback - ): void { - $queryParameters = [ - 'padId' => $this->padId, - 'EIO' => 4, - 'transport' => 'polling', - 't' => Yeast::yeast(), - 'b64' => 1, - ]; - - $response = $this->client->get($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'cookies' => $cookies, - ]); - $body = (string)$response->getBody(); - $curlyBracketPos = strpos($body, '{'); - if ($curlyBracketPos === false) { - throw new Exception('No JSON response: ' . $body); - } - $body = substr($body, $curlyBracketPos); - $data = json_decode($body, true, 512, JSON_THROW_ON_ERROR); - $sid = $data['sid']; - - $queryParameters['sid'] = $sid; - $queryParameters['t'] = Yeast::yeast(); - - $response = $this->client->post($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'cookies' => $cookies, - 'body' => '40', - ]); - $body = (string)$response->getBody(); - if ($body !== 'ok') { - throw new Exception('Invalid response: ' . $body); - } - - $queryParameters['t'] = Yeast::yeast(); - - $response = $this->client->get($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'cookies' => $cookies, - ]); - $body = (string)$response->getBody(); - - if (str_starts_with($body, '40') === false) { - throw new Exception('Invalid response: ' . $body); - } - - $postData = json_encode([ - 'message', - [ - 'component' => 'pad', - 'type' => 'CLIENT_READY', - 'padId' => $this->padId, - 'sessionID' => 'null', - 'token' => $token, - 'password' => null, - 'protocolVersion' => 2, - ] - ]); - - $queryParameters['t'] = Yeast::yeast(); - $response = $this->client->post($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'body' => '42' . $postData, - 'cookies' => $cookies, - ]); - $body = (string)$response->getBody(); - if ($body !== 'ok') { - throw new Exception('Invalid response: ' . $body); - } - - $queryParameters['t'] = Yeast::yeast(); - $response = $this->client->get($this->baseUrl . 'socket.io/', [ - 'query' => $queryParameters, - 'cookies' => $cookies, - ]); - $this->handleClientVarsResponse($response, $callback); - } - - private function handleClientVarsResponse( - ResponseInterface $response, - ScannerServiceCallbackInterface $callback, - ): void - { - $body = (string)$response->getBody(); - $body = substr($body, strpos($body, '[')); - $data = json_decode($body, true, 512, JSON_THROW_ON_ERROR); - $data = $data[1]; - $accessStatus = $data['accessStatus'] ?? null; - if ($accessStatus === 'deny') { - $callback->onScanPadException(new EtherpadServiceNotPublicException('Pads are not publicly accessible')); - return; - } - - $version = $data['data']['plugins']['plugins']['ep_etherpad-lite']['package']['version']; - $onlyPlugins = $data['data']['plugins']['plugins']; - unset($onlyPlugins['ep_etherpad-lite']); - - $this->packageVersion = $version; - $callback->onClientVars($version, $data); - $callback->onScanPluginsList($onlyPlugins); - $callback->onScanPadSuccess(); - } - private function doSocketWebsocket( int $socketIoVersion, string $cookieString, @@ -560,7 +364,7 @@ private function doSocketWebsocket( ] ]), $callback->getConsoleLogger()); - $socketIoClient->initialize(); + $socketIoClient->connect(); $socketIoClient->of('/'); $socketIoClient->emit('message', [ 'component' => 'pad', @@ -572,12 +376,8 @@ private function doSocketWebsocket( 'protocolVersion' => 2, ]); - $expirationTime = microtime(true) + 2; - - while (microtime(true) < $expirationTime) { - usleep(10000); - $result = $socketIoClient->drain(); - if ($result !== null && is_array($result->data)) { + while ($result = $socketIoClient->wait('message', 2)) { + if (is_array($result->data)) { $accessStatus = $result->data['accessStatus'] ?? null; if ($accessStatus === 'deny') { $callback->onScanPadException(new EtherpadServiceNotPublicException('Pads are not publicly accessible')); @@ -600,11 +400,11 @@ private function doSocketWebsocket( } } - $socketIoClient->close(); + $socketIoClient->disconnect(); } public function getBaseUrl(): string { return $this->baseUrl; } -} \ No newline at end of file +} diff --git a/tests/e2e/fixture/reverse_proxy_no_websocket.txt b/tests/e2e/fixture/reverse_proxy_no_websocket.txt index 722950c..0c5adf9 100644 --- a/tests/e2e/fixture/reverse_proxy_no_websocket.txt +++ b/tests/e2e/fixture/reverse_proxy_no_websocket.txt @@ -1,2 +1,2 @@ -[debug] Receive: HTTP/1.1 400 Bad Request +[debug] Stream receive: HTTP/1.1 400 Bad Request [INFO] Package version: 1.8.17