Merge pull request #8 from ratchetphp/psr7-client-refactor
Client refactor
This commit is contained in:
commit
71f10cb9ee
@ -1,84 +1,53 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace Ratchet\RFC6455\Handshake;
|
namespace Ratchet\RFC6455\Handshake;
|
||||||
|
|
||||||
|
|
||||||
use GuzzleHttp\Psr7\Request;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use GuzzleHttp\Psr7\Uri;
|
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
use GuzzleHttp\Psr7\Request;
|
||||||
|
|
||||||
class ClientNegotiator implements ClientNegotiatorInterface {
|
class ClientNegotiator {
|
||||||
public $defaultHeaders = [
|
/**
|
||||||
|
* @var ResponseVerifier
|
||||||
|
*/
|
||||||
|
private $verifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Psr\Http\Message\RequestInterface
|
||||||
|
*/
|
||||||
|
private $defaultHeader;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
$this->verifier = new ResponseVerifier;
|
||||||
|
|
||||||
|
$this->defaultHeader = new Request('GET', '', [
|
||||||
'Connection' => 'Upgrade'
|
'Connection' => 'Upgrade'
|
||||||
, 'Cache-Control' => 'no-cache'
|
|
||||||
, 'Pragma' => 'no-cache'
|
|
||||||
, 'Upgrade' => 'websocket'
|
, 'Upgrade' => 'websocket'
|
||||||
, 'Sec-WebSocket-Version' => 13
|
, 'Sec-WebSocket-Version' => $this->getVersion()
|
||||||
, 'User-Agent' => "RatchetRFC/0.0.0"
|
, 'User-Agent' => "RatchetRFC/0.0.0"
|
||||||
];
|
]);
|
||||||
|
|
||||||
/** @var Request */
|
|
||||||
public $request;
|
|
||||||
|
|
||||||
/** @var Response */
|
|
||||||
public $response;
|
|
||||||
|
|
||||||
/** @var ResponseVerifier */
|
|
||||||
public $verifier;
|
|
||||||
|
|
||||||
private $websocketKey = '';
|
|
||||||
|
|
||||||
function __construct($path = null)
|
|
||||||
{
|
|
||||||
if (!is_string($path)) $path = "/";
|
|
||||||
$request = new Request("GET", $path);
|
|
||||||
|
|
||||||
$request = $request->withUri(new Uri("ws://127.0.0.1:9001" . $path));
|
|
||||||
|
|
||||||
$this->request = $request;
|
|
||||||
|
|
||||||
$this->verifier = new ResponseVerifier();
|
|
||||||
|
|
||||||
$this->websocketKey = $this->generateKey();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addRequiredHeaders() {
|
public function generateRequest(UriInterface $uri) {
|
||||||
foreach ($this->defaultHeaders as $k => $v) {
|
return $this->defaultHeader->withUri($uri)
|
||||||
// remove any header that is there now
|
->withHeader("Sec-WebSocket-Key", $this->generateKey());
|
||||||
$this->request = $this->request->withoutHeader($k);
|
|
||||||
$this->request = $this->request->withHeader($k, $v);
|
|
||||||
}
|
|
||||||
$this->request = $this->request->withoutHeader("Sec-WebSocket-Key");
|
|
||||||
$this->request = $this->request->withHeader("Sec-WebSocket-Key", $this->websocketKey);
|
|
||||||
$this->request = $this->request->withoutHeader("Host")
|
|
||||||
->withHeader("Host", $this->request->getUri()->getHost() . ":" . $this->request->getUri()->getPort());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequest() {
|
public function validateResponse(RequestInterface $request, ResponseInterface $response) {
|
||||||
$this->addRequiredHeaders();
|
return $this->verifier->verifyAll($request, $response);
|
||||||
return $this->request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResponse() {
|
public function generateKey() {
|
||||||
return $this->response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validateResponse(Response $response) {
|
|
||||||
$this->response = $response;
|
|
||||||
|
|
||||||
return $this->verifier->verifyAll($this->getRequest(), $response);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function generateKey() {
|
|
||||||
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwzyz1234567890+/=';
|
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwzyz1234567890+/=';
|
||||||
$charRange = strlen($chars) - 1;
|
$charRange = strlen($chars) - 1;
|
||||||
$key = '';
|
$key = '';
|
||||||
for ($i = 0; $i < 16; $i++) {
|
for ($i = 0; $i < 16; $i++) {
|
||||||
$key .= $chars[mt_rand(0, $charRange)];
|
$key .= $chars[mt_rand(0, $charRange)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return base64_encode($key);
|
return base64_encode($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getVersion() {
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace Ratchet\RFC6455\Handshake;
|
|
||||||
|
|
||||||
|
|
||||||
interface ClientNegotiatorInterface {
|
|
||||||
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +1,29 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace Ratchet\RFC6455\Handshake;
|
namespace Ratchet\RFC6455\Handshake;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use GuzzleHttp\Psr7\Request;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
|
|
||||||
class ResponseVerifier {
|
class ResponseVerifier {
|
||||||
public function verifyAll(Request $request, Response $response) {
|
public function verifyAll(RequestInterface $request, ResponseInterface $response) {
|
||||||
$passes = 0;
|
$passes = 0;
|
||||||
|
|
||||||
$passes += (int)$this->verifyStatus($response->getStatusCode());
|
$passes += (int)$this->verifyStatus($response->getStatusCode());
|
||||||
$passes += (int)$this->verifyUpgrade($response->getHeader('Upgrade'));
|
$passes += (int)$this->verifyUpgrade($response->getHeader('Upgrade'));
|
||||||
$passes += (int)$this->verifyConnection($response->getHeader('Connection'));
|
$passes += (int)$this->verifyConnection($response->getHeader('Connection'));
|
||||||
$passes += (int)$this->verifySecWebSocketAccept(
|
$passes += (int)$this->verifySecWebSocketAccept(
|
||||||
$response->getHeader('Sec-WebSocket-Accept'),
|
$response->getHeader('Sec-WebSocket-Accept')
|
||||||
$request->getHeader('sec-websocket-key')
|
, $request->getHeader('Sec-WebSocket-Key')
|
||||||
|
);
|
||||||
|
$passes += (int)$this->verifySubProtocol(
|
||||||
|
$request->getHeader('Sec-WebSocket-Protocol')
|
||||||
|
, $response->getHeader('Sec-WebSocket-Protocol')
|
||||||
);
|
);
|
||||||
|
|
||||||
return (4 == $passes);
|
return (5 === $passes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verifyStatus($status) {
|
public function verifyStatus($status) {
|
||||||
return ($status == 101);
|
return ((int)$status === 101);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verifyUpgrade(array $upgrade) {
|
public function verifyUpgrade(array $upgrade) {
|
||||||
@ -38,10 +38,15 @@ class ResponseVerifier {
|
|||||||
return (
|
return (
|
||||||
1 === count($swa) &&
|
1 === count($swa) &&
|
||||||
1 === count($key) &&
|
1 === count($key) &&
|
||||||
$swa[0] == $this->sign($key[0]));
|
$swa[0] === $this->sign($key[0])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sign($key) {
|
public function sign($key) {
|
||||||
return base64_encode(sha1($key . Negotiator::GUID, true));
|
return base64_encode(sha1($key . NegotiatorInterface::GUID, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verifySubProtocol(array $requestHeader, array $responseHeader) {
|
||||||
|
return 0 === count($responseHeader) || count(array_intersect($responseHeader, $requestHeader)) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use GuzzleHttp\Psr7\Uri;
|
||||||
use React\Promise\Deferred;
|
use React\Promise\Deferred;
|
||||||
use Ratchet\RFC6455\Messaging\Protocol\Frame;
|
use Ratchet\RFC6455\Messaging\Protocol\Frame;
|
||||||
use Ratchet\RFC6455\Messaging\Protocol\Message;
|
|
||||||
|
|
||||||
require __DIR__ . '/../bootstrap.php';
|
require __DIR__ . '/../bootstrap.php';
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ function getTestCases() {
|
|||||||
$deferred = new Deferred();
|
$deferred = new Deferred();
|
||||||
|
|
||||||
$factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
|
$factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
|
||||||
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator("/getCaseCount");
|
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
|
||||||
$cnRequest = $cn->getRequest();
|
$cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001/getCaseCount'));
|
||||||
|
|
||||||
$rawResponse = "";
|
$rawResponse = "";
|
||||||
$response = null;
|
$response = null;
|
||||||
@ -57,7 +57,7 @@ function getTestCases() {
|
|||||||
/** @var \Ratchet\RFC6455\Messaging\Streaming\MessageStreamer $ms */
|
/** @var \Ratchet\RFC6455\Messaging\Streaming\MessageStreamer $ms */
|
||||||
$ms = null;
|
$ms = null;
|
||||||
|
|
||||||
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context) {
|
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
|
||||||
if ($response === null) {
|
if ($response === null) {
|
||||||
$rawResponse .= $data;
|
$rawResponse .= $data;
|
||||||
$pos = strpos($rawResponse, "\r\n\r\n");
|
$pos = strpos($rawResponse, "\r\n\r\n");
|
||||||
@ -66,7 +66,7 @@ function getTestCases() {
|
|||||||
$rawResponse = substr($rawResponse, 0, $pos + 4);
|
$rawResponse = substr($rawResponse, 0, $pos + 4);
|
||||||
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
|
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
|
||||||
|
|
||||||
if (!$cn->validateResponse($response)) {
|
if (!$cn->validateResponse($cnRequest, $response)) {
|
||||||
$stream->end();
|
$stream->end();
|
||||||
$deferred->reject();
|
$deferred->reject();
|
||||||
} else {
|
} else {
|
||||||
@ -105,15 +105,15 @@ function runTest($case)
|
|||||||
$deferred = new Deferred();
|
$deferred = new Deferred();
|
||||||
|
|
||||||
$factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred, $casePath, $case) {
|
$factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred, $casePath, $case) {
|
||||||
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator($casePath);
|
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
|
||||||
$cnRequest = $cn->getRequest();
|
$cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $casePath));
|
||||||
|
|
||||||
$rawResponse = "";
|
$rawResponse = "";
|
||||||
$response = null;
|
$response = null;
|
||||||
|
|
||||||
$ms = null;
|
$ms = null;
|
||||||
|
|
||||||
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context) {
|
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
|
||||||
if ($response === null) {
|
if ($response === null) {
|
||||||
$rawResponse .= $data;
|
$rawResponse .= $data;
|
||||||
$pos = strpos($rawResponse, "\r\n\r\n");
|
$pos = strpos($rawResponse, "\r\n\r\n");
|
||||||
@ -122,7 +122,7 @@ function runTest($case)
|
|||||||
$rawResponse = substr($rawResponse, 0, $pos + 4);
|
$rawResponse = substr($rawResponse, 0, $pos + 4);
|
||||||
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
|
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
|
||||||
|
|
||||||
if (!$cn->validateResponse($response)) {
|
if (!$cn->validateResponse($cnRequest, $response)) {
|
||||||
$stream->end();
|
$stream->end();
|
||||||
$deferred->reject();
|
$deferred->reject();
|
||||||
} else {
|
} else {
|
||||||
@ -155,8 +155,8 @@ function createReport() {
|
|||||||
|
|
||||||
$factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
|
$factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
|
||||||
$reportPath = "/updateReports?agent=" . AGENT . "&shutdownOnComplete=true";
|
$reportPath = "/updateReports?agent=" . AGENT . "&shutdownOnComplete=true";
|
||||||
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator($reportPath);
|
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
|
||||||
$cnRequest = $cn->getRequest();
|
$cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $reportPath));
|
||||||
|
|
||||||
$rawResponse = "";
|
$rawResponse = "";
|
||||||
$response = null;
|
$response = null;
|
||||||
@ -164,7 +164,7 @@ function createReport() {
|
|||||||
/** @var \Ratchet\RFC6455\Messaging\Streaming\MessageStreamer $ms */
|
/** @var \Ratchet\RFC6455\Messaging\Streaming\MessageStreamer $ms */
|
||||||
$ms = null;
|
$ms = null;
|
||||||
|
|
||||||
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context) {
|
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
|
||||||
if ($response === null) {
|
if ($response === null) {
|
||||||
$rawResponse .= $data;
|
$rawResponse .= $data;
|
||||||
$pos = strpos($rawResponse, "\r\n\r\n");
|
$pos = strpos($rawResponse, "\r\n\r\n");
|
||||||
@ -173,7 +173,7 @@ function createReport() {
|
|||||||
$rawResponse = substr($rawResponse, 0, $pos + 4);
|
$rawResponse = substr($rawResponse, 0, $pos + 4);
|
||||||
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
|
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
|
||||||
|
|
||||||
if (!$cn->validateResponse($response)) {
|
if (!$cn->validateResponse($cnRequest, $response)) {
|
||||||
$stream->end();
|
$stream->end();
|
||||||
$deferred->reject();
|
$deferred->reject();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"options": {"failByDrop": false},
|
"options": {
|
||||||
"outdir": "./reports/servers",
|
"failByDrop": false
|
||||||
|
}
|
||||||
"servers": [{
|
, "outdir": "./reports/servers"
|
||||||
"agent": "RatchetRFC/0.1.0",
|
, "servers": [{
|
||||||
"url": "ws://localhost:9001",
|
"agent": "RatchetRFC/0.1.0"
|
||||||
"options": {"version": 18}
|
, "url": "ws://localhost:9001"
|
||||||
}],
|
, "options": {"version": 18}
|
||||||
"cases": ["*"],
|
}]
|
||||||
"exclude-cases": ["12.*","13.*"],
|
, "cases": ["*"]
|
||||||
"exclude-agent-cases": {}
|
, "exclude-cases": ["6.4.*", "12.*","13.*"]
|
||||||
|
, "exclude-agent-cases": {}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
}
|
}
|
||||||
, "outdir": "./reports/clients"
|
, "outdir": "./reports/clients"
|
||||||
, "cases": ["*"]
|
, "cases": ["*"]
|
||||||
, "exclude-cases": ["12.*", "13.*"]
|
, "exclude-cases": ["6.4.*", "12.*", "13.*"]
|
||||||
, "exclude-agent-cases": {}
|
, "exclude-agent-cases": {}
|
||||||
}
|
}
|
@ -1,11 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Ratchet\RFC6455\Test\Unit\Handshake;
|
namespace Ratchet\RFC6455\Test\Unit\Handshake;
|
||||||
|
|
||||||
use Ratchet\RFC6455\Handshake\RequestVerifier;
|
use Ratchet\RFC6455\Handshake\RequestVerifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* covers Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier
|
* @covers Ratchet\RFC6455\Handshake\RequestVerifier
|
||||||
*/
|
*/
|
||||||
class RequestVerifierTest extends \PHPUnit_Framework_TestCase {
|
class RequestVerifierTest extends \PHPUnit_Framework_TestCase {
|
||||||
/**
|
/**
|
||||||
|
34
tests/unit/Handshake/ResponseVerifierTest.php
Normal file
34
tests/unit/Handshake/ResponseVerifierTest.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\RFC6455\Test\Unit\Handshake;
|
||||||
|
use Ratchet\RFC6455\Handshake\ResponseVerifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\WebSocket\Version\RFC6455\ResponseVerifier
|
||||||
|
*/
|
||||||
|
class ResponseVerifierTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
/**
|
||||||
|
* @var ResponseVerifier
|
||||||
|
*/
|
||||||
|
protected $_v;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_v = new ResponseVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function subProtocolsProvider() {
|
||||||
|
return [
|
||||||
|
[true, ['a'], ['a']]
|
||||||
|
, [true, ['b', 'a'], ['c', 'd', 'a']]
|
||||||
|
, [false, ['a', 'b', 'c'], ['d']]
|
||||||
|
, [true, [], []]
|
||||||
|
, [true, ['a', 'b'], []]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider subProtocolsProvider
|
||||||
|
*/
|
||||||
|
public function testVerifySubProtocol($expected, $response, $request) {
|
||||||
|
$this->assertEquals($expected, $this->_v->verifySubProtocol($response, $request));
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Ratchet\RFC6455\Test\Unit\Messaging\Protocol;
|
namespace Ratchet\RFC6455\Test\Unit\Messaging\Protocol;
|
||||||
|
|
||||||
use Ratchet\RFC6455\Messaging\Protocol\Frame;
|
use Ratchet\RFC6455\Messaging\Protocol\Frame;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @covers Ratchet\RFC6455\Messaging\Protocol\Frame
|
||||||
* @todo getMaskingKey, getPayloadStartingByte don't have tests yet
|
* @todo getMaskingKey, getPayloadStartingByte don't have tests yet
|
||||||
* @todo Could use some clean up in general, I had to rush to fix a bug for a deadline, sorry.
|
* @todo Could use some clean up in general, I had to rush to fix a bug for a deadline, sorry.
|
||||||
*/
|
*/
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Ratchet\RFC6455\Test\Unit\Messaging\Protocol;
|
namespace Ratchet\RFC6455\Test\Unit\Messaging\Protocol;
|
||||||
|
|
||||||
use Ratchet\RFC6455\Messaging\Protocol\Frame;
|
use Ratchet\RFC6455\Messaging\Protocol\Frame;
|
||||||
use Ratchet\RFC6455\Messaging\Protocol\Message;
|
use Ratchet\RFC6455\Messaging\Protocol\Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* covers Ratchet\WebSocket\Version\RFC6455\Message
|
* @covers Ratchet\RFC6455\Messaging\Protocol\Message
|
||||||
*/
|
*/
|
||||||
class MessageTest extends \PHPUnit_Framework_TestCase {
|
class MessageTest extends \PHPUnit_Framework_TestCase {
|
||||||
/** @var Message */
|
/** @var Message */
|
||||||
|
Loading…
Reference in New Issue
Block a user