Client side tests and components

This commit is contained in:
matt 2015-03-16 16:23:01 -04:00
parent 1970699b75
commit 9890a8d7e1
5 changed files with 378 additions and 0 deletions

View File

@ -0,0 +1,84 @@
<?php
namespace Ratchet\RFC6455\Handshake;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\RequestInterface;
class ClientNegotiator implements ClientNegotiatorInterface {
public $defaultHeaders = [
'Connection' => 'Upgrade'
, 'Cache-Control' => 'no-cache'
, 'Pragma' => 'no-cache'
, 'Upgrade' => 'websocket'
, 'Sec-WebSocket-Version' => 13
, '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() {
foreach ($this->defaultHeaders as $k => $v) {
// remove any header that is there now
$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() {
$this->addRequiredHeaders();
return $this->request;
}
public function getResponse() {
return $this->response;
}
public function validateResponse(Response $response) {
$this->response = $response;
return $this->verifier->verifyAll($this->getRequest(), $response);
}
protected function generateKey() {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwzyz1234567890+/=';
$charRange = strlen($chars) - 1;
$key = '';
for ($i = 0;$i < 16;$i++) {
$key .= $chars[mt_rand(0, $charRange)];
}
return base64_encode($key);
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Ratchet\RFC6455\Handshake;
interface ClientNegotiatorInterface {
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
}

View File

@ -0,0 +1,44 @@
<?php
namespace Ratchet\RFC6455\Handshake;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
class ResponseVerifier {
public function verifyAll(Request $request, Response $response) {
$passes = 0;
$passes += (int)$this->verifyStatus($response->getStatusCode());
$passes += (int)$this->verifyUpgrade($response->getHeader('Upgrade'));
$passes += (int)$this->verifyConnection($response->getHeader('Connection'));
$passes += (int)$this->verifySecWebSocketAccept(
$response->getHeader('Sec-WebSocket-Accept'),
$request->getHeader('sec-websocket-key')
);
return (4 == $passes);
}
public function verifyStatus($status) {
return ($status == 101);
}
public function verifyUpgrade($upgrade) {
return (strtolower($upgrade) == "websocket");
}
public function verifyConnection($connection) {
return (strtolower($connection) == "upgrade");
}
public function verifySecWebSocketAccept($swa, $key) {
return ($swa == $this->sign($key));
}
public function sign($key) {
return base64_encode(sha1($key . Negotiator::GUID, true));
}
}

227
tests/ab/clientRunner.php Normal file
View File

@ -0,0 +1,227 @@
<?php
use React\Promise\Deferred;
use Ratchet\RFC6455\Messaging\Protocol\Frame;
use Ratchet\RFC6455\Messaging\Protocol\Message;
require __DIR__ . '/../bootstrap.php';
define('AGENT', 'RatchetRFC/0.0.0');
$loop = React\EventLoop\Factory::create();
$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dnsResolver = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$factory = new \React\SocketClient\Connector($loop, $dnsResolver);
function getTestCases() {
global $factory;
$deferred = new Deferred();
$factory->create('127.0.0.1', 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator("/getCaseCount");
$cnRequest = $cn->getRequest();
$rawResponse = "";
$response = null;
$ms = new \Ratchet\RFC6455\Messaging\Streaming\MessageStreamer();
$ms->on('message', function (Message $msg) use ($stream, $deferred) {
$deferred->resolve($msg->getPayload());
$closeFrame = new Frame(pack('n', Frame::CLOSE_NORMAL), true, Frame::OP_CLOSE);
$closeFrame->maskPayload();
$stream->end($closeFrame->getContents());
});
$ms->on('close', function ($code) use ($stream) {
if ($code === null) {
$stream->close();
return;
}
$frame = new Frame(pack('n', $code), true, Frame::OP_CLOSE);
$frame->maskPayload();
$stream->end($frame->getContents());
});
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, $ms, $cn, $deferred) {
if ($response === null) {
$rawResponse .= $data;
$pos = strpos($rawResponse, "\r\n\r\n");
if ($pos) {
$data = substr($rawResponse, $pos + 4);
$rawResponse = substr($rawResponse, 0, $pos + 4);
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
if (!$cn->validateResponse($response)) {
$stream->end();
$deferred->reject();
}
}
}
// feed the message streamer
if ($response) {
$ms->onData($data);
}
});
$stream->write(\GuzzleHttp\Psr7\str($cnRequest));
});
return $deferred->promise();
}
function runTest($case)
{
global $factory;
$casePath = "/runCase?case={$case}&agent=" . AGENT;
$deferred = new Deferred();
$factory->create('127.0.0.1', 9001)->then(function (\React\Stream\Stream $stream) use ($deferred, $casePath) {
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator($casePath);
$cnRequest = $cn->getRequest();
$rawResponse = "";
$response = null;
$ms = new \Ratchet\RFC6455\Messaging\Streaming\MessageStreamer();
$ms->on('message', function (Message $msg) use ($stream, $deferred) {
$opcode = $msg->isBinary() ? Frame::OP_BINARY : Frame::OP_TEXT;
$frame = new Frame($msg->getPayload(), true, $opcode);
$frame->maskPayload();
$stream->write($frame->getContents());
});
$ms->on('ping', function (Frame $frame) use ($stream) {
$response = new Frame($frame->getPayload(), true, Frame::OP_PONG);
$response->maskPayload();
$stream->write($response->getContents());
});
$ms->on('close', function ($code) use ($stream, $deferred) {
if ($code === null) {
$stream->close();
return;
}
$frame = new Frame(pack('n', $code), true, Frame::OP_CLOSE);
$frame->maskPayload();
$stream->end($frame->getContents());
});
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, $ms, $cn, $deferred) {
if ($response === null) {
$rawResponse .= $data;
$pos = strpos($rawResponse, "\r\n\r\n");
if ($pos) {
$data = substr($rawResponse, $pos + 4);
$rawResponse = substr($rawResponse, 0, $pos + 4);
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
if (!$cn->validateResponse($response)) {
$stream->end();
$deferred->reject();
}
}
}
// feed the message streamer
if ($response) {
$ms->onData($data);
}
});
$stream->on('close', function () use ($deferred) {
$deferred->resolve();
});
$stream->write(\GuzzleHttp\Psr7\str($cnRequest));
});
return $deferred->promise();
}
function createReport() {
global $factory;
$deferred = new Deferred();
$factory->create('127.0.0.1', 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
$cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator('/updateReports?agent=' . AGENT);
$cnRequest = $cn->getRequest();
$rawResponse = "";
$response = null;
$ms = new \Ratchet\RFC6455\Messaging\Streaming\MessageStreamer();
$ms->on('message', function (Message $msg) use ($stream, $deferred) {
$deferred->resolve($msg->getPayload());
$closeFrame = new Frame(pack('n', Frame::CLOSE_NORMAL), true, Frame::OP_CLOSE);
$closeFrame->maskPayload();
$stream->end($closeFrame->getContents());
});
$ms->on('close', function ($code) use ($stream) {
if ($code === null) {
$stream->close();
return;
}
$frame = new Frame(pack('n', $code), true, Frame::OP_CLOSE);
$frame->maskPayload();
$stream->end($frame->getContents());
});
$stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, $ms, $cn, $deferred) {
if ($response === null) {
$rawResponse .= $data;
$pos = strpos($rawResponse, "\r\n\r\n");
if ($pos) {
$data = substr($rawResponse, $pos + 4);
$rawResponse = substr($rawResponse, 0, $pos + 4);
$response = \GuzzleHttp\Psr7\parse_response($rawResponse);
if (!$cn->validateResponse($response)) {
$stream->end();
$deferred->reject();
}
}
}
// feed the message streamer
if ($response) {
$ms->onData($data);
}
});
$stream->write(\GuzzleHttp\Psr7\str($cnRequest));
});
return $deferred->promise();
}
$testPromises = [];
getTestCases()->then(function ($count) {
echo "Running " . $count . " test cases.\n";
for ($i = 0; $i < $count; $i++) {
$testPromises[] = runTest($i + 1);
}
\React\Promise\all($testPromises)->then(function () {
createReport();
});
});
$loop->run();

View File

@ -0,0 +1,12 @@
{
"url": "ws://127.0.0.1:9001"
, "options": {
"failByDrop": false
}
, "outdir": "./reports/clients"
, "casesy": ["*"]
, "cases": ["1.*", "2.*", "3.*", "4.*", "5.*"]
, "casesx": ["1.1.8"]
, "exclude-cases": ["9.8.6"]
, "exclude-agent-cases": {}
}