Merge commit '6df0bd1b68df5b9774b79c036b9ba31d85ad0d01' into v0.4
# Conflicts: # composer.json # src/Handshake/ClientNegotiator.php # src/Handshake/ServerNegotiator.php # tests/ab/clientRunner.php # tests/ab/startServer.php # tests/unit/Handshake/ServerNegotiatorTest.php
This commit is contained in:
commit
772e465650
@ -27,7 +27,7 @@
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"guzzlehttp/psr7": "^2 || ^1.7",
|
||||
"psr/http-factory-implementation": "^1.0",
|
||||
"symfony/polyfill-php80": "^1.15"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -3,22 +3,28 @@ namespace Ratchet\RFC6455\Handshake;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
|
||||
class ClientNegotiator {
|
||||
private ResponseVerifier $verifier;
|
||||
|
||||
private RequestInterface $defaultHeader;
|
||||
|
||||
public function __construct(PermessageDeflateOptions $perMessageDeflateOptions = null) {
|
||||
$this->verifier = new ResponseVerifier;
|
||||
private RequestFactoryInterface $requestFactory;
|
||||
|
||||
$this->defaultHeader = new Request('GET', '', [
|
||||
'Connection' => 'Upgrade'
|
||||
, 'Upgrade' => 'websocket'
|
||||
, 'Sec-WebSocket-Version' => $this->getVersion()
|
||||
, 'User-Agent' => "Ratchet"
|
||||
]);
|
||||
public function __construct(
|
||||
RequestFactoryInterface $requestFactory,
|
||||
PermessageDeflateOptions $perMessageDeflateOptions = null
|
||||
) {
|
||||
$this->verifier = new ResponseVerifier;
|
||||
$this->requestFactory = $requestFactory;
|
||||
|
||||
$this->defaultHeader = $this->requestFactory
|
||||
->createRequest('GET', '')
|
||||
->withHeader('Connection' , 'Upgrade')
|
||||
->withHeader('Upgrade' , 'websocket')
|
||||
->withHeader('Sec-WebSocket-Version', $this->getVersion())
|
||||
->withHeader('User-Agent' , 'Ratchet');
|
||||
|
||||
$perMessageDeflateOptions ??= PermessageDeflateOptions::createDisabled();
|
||||
|
||||
@ -38,7 +44,7 @@ class ClientNegotiator {
|
||||
|
||||
public function generateRequest(UriInterface $uri): RequestInterface {
|
||||
return $this->defaultHeader->withUri($uri)
|
||||
->withHeader("Sec-WebSocket-Key", $this->generateKey());
|
||||
->withHeader('Sec-WebSocket-Key', $this->generateKey());
|
||||
}
|
||||
|
||||
public function validateResponse(RequestInterface $request, ResponseInterface $response): bool {
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Ratchet\RFC6455\Handshake;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
@ -11,14 +12,21 @@ use Psr\Http\Message\ResponseInterface;
|
||||
class ServerNegotiator implements NegotiatorInterface {
|
||||
private RequestVerifier $verifier;
|
||||
|
||||
private ResponseFactoryInterface $responseFactory;
|
||||
|
||||
private array $_supportedSubProtocols = [];
|
||||
|
||||
private bool $_strictSubProtocols = false;
|
||||
|
||||
private bool $enablePerMessageDeflate = false;
|
||||
|
||||
public function __construct(RequestVerifier $requestVerifier, $enablePerMessageDeflate = false) {
|
||||
public function __construct(
|
||||
RequestVerifier $requestVerifier,
|
||||
ResponseFactoryInterface $responseFactory,
|
||||
$enablePerMessageDeflate = false
|
||||
) {
|
||||
$this->verifier = $requestVerifier;
|
||||
$this->responseFactory = $responseFactory;
|
||||
|
||||
// https://bugs.php.net/bug.php?id=73373
|
||||
// https://bugs.php.net/bug.php?id=74240 - need >=7.1.4 or >=7.0.18
|
||||
@ -51,47 +59,49 @@ class ServerNegotiator implements NegotiatorInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handshake(RequestInterface $request): ResponseInterface {
|
||||
$response = $this->responseFactory->createResponse();
|
||||
if (true !== $this->verifier->verifyMethod($request->getMethod())) {
|
||||
return new Response(405, ['Allow' => 'GET']);
|
||||
return $response->withHeader('Allow', 'GET')->withStatus(405);
|
||||
}
|
||||
|
||||
if (true !== $this->verifier->verifyHTTPVersion($request->getProtocolVersion())) {
|
||||
return new Response(505);
|
||||
return $response->withStatus(505);
|
||||
}
|
||||
|
||||
if (true !== $this->verifier->verifyRequestURI($request->getUri()->getPath())) {
|
||||
return new Response(400);
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
if (true !== $this->verifier->verifyHost($request->getHeader('Host'))) {
|
||||
return new Response(400);
|
||||
return $response->withStatus(400);
|
||||
}
|
||||
|
||||
$upgradeSuggestion = [
|
||||
'Connection' => 'Upgrade',
|
||||
'Upgrade' => 'websocket',
|
||||
'Sec-WebSocket-Version' => $this->getVersionNumber()
|
||||
];
|
||||
$upgradeResponse = $response
|
||||
->withHeader('Connection' , 'Upgrade')
|
||||
->withHeader('Upgrade' , 'websocket')
|
||||
->withHeader('Sec-WebSocket-Version', $this->getVersionNumber());
|
||||
|
||||
if (count($this->_supportedSubProtocols) > 0) {
|
||||
$upgradeSuggestion['Sec-WebSocket-Protocol'] = implode(', ', array_keys($this->_supportedSubProtocols));
|
||||
$upgradeResponse = $upgradeResponse->withHeader(
|
||||
'Sec-WebSocket-Protocol', implode(', ', array_keys($this->_supportedSubProtocols))
|
||||
);
|
||||
}
|
||||
if (true !== $this->verifier->verifyUpgradeRequest($request->getHeader('Upgrade'))) {
|
||||
return new Response(426, $upgradeSuggestion, null, '1.1', 'Upgrade header MUST be provided');
|
||||
return $upgradeResponse->withStatus(426, 'Upgrade header MUST be provided');
|
||||
}
|
||||
|
||||
if (true !== $this->verifier->verifyConnection($request->getHeader('Connection'))) {
|
||||
return new Response(400, [], null, '1.1', 'Connection Upgrade MUST be requested');
|
||||
return $response->withStatus(400, 'Connection Upgrade MUST be requested');
|
||||
}
|
||||
|
||||
if (true !== $this->verifier->verifyKey($request->getHeader('Sec-WebSocket-Key'))) {
|
||||
return new Response(400, [], null, '1.1', 'Invalid Sec-WebSocket-Key');
|
||||
return $response->withStatus(400, 'Invalid Sec-WebSocket-Key');
|
||||
}
|
||||
|
||||
if (true !== $this->verifier->verifyVersion($request->getHeader('Sec-WebSocket-Version'))) {
|
||||
return new Response(426, $upgradeSuggestion);
|
||||
return $upgradeResponse->withStatus(426);
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
$subProtocols = $request->getHeader('Sec-WebSocket-Protocol');
|
||||
if (count($subProtocols) > 0 || (count($this->_supportedSubProtocols) > 0 && $this->_strictSubProtocols)) {
|
||||
$subProtocols = array_map('trim', explode(',', implode(',', $subProtocols)));
|
||||
@ -99,20 +109,20 @@ class ServerNegotiator implements NegotiatorInterface {
|
||||
$match = array_reduce($subProtocols, fn ($accumulator, $protocol) => $accumulator ?: (isset($this->_supportedSubProtocols[$protocol]) ? $protocol : null), null);
|
||||
|
||||
if ($this->_strictSubProtocols && null === $match) {
|
||||
return new Response(426, $upgradeSuggestion, null, '1.1', 'No Sec-WebSocket-Protocols requested supported');
|
||||
return $upgradeResponse->withStatus(426, 'No Sec-WebSocket-Protocols requested supported');
|
||||
}
|
||||
|
||||
if (null !== $match) {
|
||||
$headers['Sec-WebSocket-Protocol'] = $match;
|
||||
$response = $response->withHeader('Sec-WebSocket-Protocol', $match);
|
||||
}
|
||||
}
|
||||
|
||||
$response = new Response(101, array_merge($headers, [
|
||||
'Upgrade' => 'websocket'
|
||||
, 'Connection' => 'Upgrade'
|
||||
, 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key')[0])
|
||||
, 'X-Powered-By' => 'Ratchet'
|
||||
]));
|
||||
$response = $response
|
||||
->withStatus(101)
|
||||
->withHeader('Upgrade' , 'websocket')
|
||||
->withHeader('Connection' , 'Upgrade')
|
||||
->withHeader('Sec-WebSocket-Accept', $this->sign((string)$request->getHeader('Sec-WebSocket-Key')[0]))
|
||||
->withHeader('X-Powered-By' , 'Ratchet');
|
||||
|
||||
try {
|
||||
$perMessageDeflateRequest = PermessageDeflateOptions::fromRequestOrResponse($request)[0];
|
||||
|
@ -12,6 +12,7 @@ use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||
use React\Promise\Deferred;
|
||||
use Ratchet\RFC6455\Messaging\Frame;
|
||||
use React\Promise\PromiseInterface;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use React\Socket\ConnectionInterface;
|
||||
use React\Socket\Connector;
|
||||
|
||||
@ -58,7 +59,7 @@ function getTestCases(): PromiseInterface {
|
||||
$deferred = new Deferred();
|
||||
|
||||
$connector->connect($testServer . ':9002')->then(static function (ConnectionInterface $connection) use ($deferred, $testServer): void {
|
||||
$cn = new ClientNegotiator();
|
||||
$cn = new ClientNegotiator(new HttpFactory());
|
||||
$cnRequest = $cn->generateRequest(new Uri('ws://' . $testServer . ':9002/getCaseCount'));
|
||||
|
||||
$rawResponse = "";
|
||||
@ -110,6 +111,7 @@ function getTestCases(): PromiseInterface {
|
||||
}
|
||||
|
||||
$cn = new ClientNegotiator(
|
||||
new HttpFactory(),
|
||||
PermessageDeflateOptions::permessageDeflateSupported() ? PermessageDeflateOptions::createEnabled() : null);
|
||||
|
||||
function runTest(int $case)
|
||||
@ -124,6 +126,7 @@ function runTest(int $case)
|
||||
|
||||
$connector->connect($testServer . ':9002')->then(static function (ConnectionInterface $connection) use ($deferred, $casePath, $case, $testServer): void {
|
||||
$cn = new ClientNegotiator(
|
||||
new HttpFactory(),
|
||||
PermessageDeflateOptions::permessageDeflateSupported() ? PermessageDeflateOptions::createEnabled() : null);
|
||||
$cnRequest = $cn->generateRequest(new Uri('ws://' . $testServer . ':9002' . $casePath));
|
||||
|
||||
@ -185,7 +188,7 @@ function createReport(): PromiseInterface {
|
||||
// $reportPath = "/updateReports?agent=" . AGENT . "&shutdownOnComplete=true";
|
||||
// we will stop it using docker now instead of just shutting down
|
||||
$reportPath = "/updateReports?agent=" . AGENT;
|
||||
$cn = new ClientNegotiator();
|
||||
$cn = new ClientNegotiator(new HttpFactory());
|
||||
$cnRequest = $cn->generateRequest(new Uri('ws://' . $testServer . ':9002' . $reportPath));
|
||||
|
||||
$rawResponse = "";
|
||||
|
0
tests/ab/run_ab_tests.sh
Normal file → Executable file
0
tests/ab/run_ab_tests.sh
Normal file → Executable file
@ -10,6 +10,7 @@ use Ratchet\RFC6455\Messaging\MessageBuffer;
|
||||
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||
use Ratchet\RFC6455\Messaging\FrameInterface;
|
||||
use Ratchet\RFC6455\Messaging\Frame;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
|
||||
require_once __DIR__ . "/../bootstrap.php";
|
||||
|
||||
@ -18,7 +19,12 @@ $loop = \React\EventLoop\Factory::create();
|
||||
$socket = new \React\Socket\Server('0.0.0.0:9001', $loop);
|
||||
|
||||
$closeFrameChecker = new CloseFrameChecker;
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier, PermessageDeflateOptions::permessageDeflateSupported());
|
||||
|
||||
$negotiator = new ServerNegotiator(
|
||||
new RequestVerifier,
|
||||
new HttpFactory(),
|
||||
PermessageDeflateOptions::permessageDeflateSupported()
|
||||
);
|
||||
|
||||
$uException = new \UnderflowException;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Ratchet\RFC6455\Test\Unit\Handshake;
|
||||
|
||||
use GuzzleHttp\Psr7\Message;
|
||||
use GuzzleHttp\Psr7\HttpFactory;
|
||||
use Ratchet\RFC6455\Handshake\RequestVerifier;
|
||||
use Ratchet\RFC6455\Handshake\ServerNegotiator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@ -13,7 +14,7 @@ use PHPUnit\Framework\TestCase;
|
||||
class ServerNegotiatorTest extends TestCase
|
||||
{
|
||||
public function testNoUpgradeRequested(): void {
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier());
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier(), new HttpFactory());
|
||||
|
||||
$requestText = 'GET / HTTP/1.1
|
||||
Host: 127.0.0.1:6789
|
||||
@ -41,7 +42,7 @@ Accept-Language: en-US,en;q=0.8
|
||||
}
|
||||
|
||||
public function testNoConnectionUpgradeRequested(): void {
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier());
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier(), new HttpFactory());
|
||||
|
||||
$requestText = 'GET / HTTP/1.1
|
||||
Host: 127.0.0.1:6789
|
||||
@ -67,7 +68,7 @@ Accept-Language: en-US,en;q=0.8
|
||||
}
|
||||
|
||||
public function testInvalidSecWebsocketKey(): void {
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier());
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier(), new HttpFactory());
|
||||
|
||||
$requestText = 'GET / HTTP/1.1
|
||||
Host: 127.0.0.1:6789
|
||||
@ -94,7 +95,7 @@ Accept-Language: en-US,en;q=0.8
|
||||
}
|
||||
|
||||
public function testInvalidSecWebsocketVersion(): void {
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier());
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier(), new HttpFactory());
|
||||
|
||||
$requestText = 'GET / HTTP/1.1
|
||||
Host: 127.0.0.1:6789
|
||||
@ -124,7 +125,7 @@ Accept-Language: en-US,en;q=0.8
|
||||
}
|
||||
|
||||
public function testBadSubprotocolResponse(): void {
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier());
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier(), new HttpFactory());
|
||||
$negotiator->setStrictSubProtocolCheck(true);
|
||||
$negotiator->setSupportedSubProtocols([]);
|
||||
|
||||
@ -158,7 +159,7 @@ Accept-Language: en-US,en;q=0.8
|
||||
}
|
||||
|
||||
public function testNonStrictSubprotocolDoesNotIncludeHeaderWhenNoneAgreedOn(): void {
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier());
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier(), new HttpFactory());
|
||||
$negotiator->setStrictSubProtocolCheck(false);
|
||||
$negotiator->setSupportedSubProtocols(['someproto']);
|
||||
|
||||
@ -192,7 +193,7 @@ Accept-Language: en-US,en;q=0.8
|
||||
|
||||
public function testSuggestsAppropriateSubprotocol(): void
|
||||
{
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier());
|
||||
$negotiator = new ServerNegotiator(new RequestVerifier(), new HttpFactory());
|
||||
$negotiator->setStrictSubProtocolCheck(true);
|
||||
$negotiator->setSupportedSubProtocols(['someproto']);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user