[WebSocket] Refactoring
Separated handshake negotiation into its own class `HandshakeNegotiator` deals with Request/Response classes These changes are geared towards separate responsibility Refs #29
This commit is contained in:
parent
d075b99c26
commit
935866c036
140
src/Ratchet/WebSocket/HandshakeNegotiator.php
Normal file
140
src/Ratchet/WebSocket/HandshakeNegotiator.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory;
|
||||
use Ratchet\WebSocket\Version\VersionInterface;
|
||||
use Ratchet\WebSocket\Version;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
use Guzzle\Http\Message\Response;
|
||||
|
||||
class HandshakeNegotiator {
|
||||
const EOM = "\r\n\r\n";
|
||||
|
||||
/**
|
||||
* The maximum number of bytes the request can be
|
||||
* This is a security measure to prevent attacks
|
||||
* @var int
|
||||
*/
|
||||
public $maxSize = 4096;
|
||||
|
||||
private $versionString = '';
|
||||
|
||||
protected $versions = array();
|
||||
|
||||
public function __construct($autoloadVersions = true) {
|
||||
if ($autoloadVersions) {
|
||||
$this->enableVersion(new Version\RFC6455);
|
||||
$this->enableVersion(new Version\HyBi10);
|
||||
$this->enableVersion(new Version\Hixie76);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WsConnection
|
||||
*/
|
||||
public function onOpen(WsConnection $conn) {
|
||||
$conn->WebSocket->handshakeBuffer = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WsConnection
|
||||
* @param string Data stream to buffer
|
||||
* @return Guzzle\Http\Message\Response|null Response object if it's done parsing, null if there's more to be buffered
|
||||
* @throws HttpException
|
||||
*/
|
||||
public function onData(WsConnection $conn, $data) {
|
||||
$conn->WebSocket->handshakeBuffer .= $data;
|
||||
|
||||
if (mb_strlen($conn->WebSocket->handshakeBuffer, '8bit') >= (int)$this->maxSize) {
|
||||
return new Response(413, array('X-Powered-By' => \Ratchet\VERSION));
|
||||
}
|
||||
|
||||
if ($this->isEom($conn->WebSocket->handshakeBuffer)) {
|
||||
$conn->WebSocket->request = RequestFactory::getInstance()->fromMessage($conn->WebSocket->handshakeBuffer);
|
||||
|
||||
if (null === ($version = $this->getVersion($conn->WebSocket->request))) {
|
||||
return new Response(400, array(
|
||||
'Sec-WebSocket-Version' => $this->getSupportedVersionString()
|
||||
, 'X-Powered-By' => \Ratchet\VERSION
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: confirm message is buffered
|
||||
// Hixie requires the body to complete the handshake (6 characters long)
|
||||
// Update VersionInterface to check for this, ::canHandshake() maybe
|
||||
// return if can't, continue buffering
|
||||
|
||||
$response = $version->handshake($conn->WebSocket->request);
|
||||
$response->setHeader('X-Powered-By', \Ratchet\VERSION);
|
||||
|
||||
$conn->setVersion($version);
|
||||
unset($conn->WebSocket->handshakeBuffer);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message has been buffered as per the HTTP specification
|
||||
* @param string
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEom($message) {
|
||||
return (static::EOM === substr($message, 0 - strlen(static::EOM)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the protocol negotiator for the request, if supported
|
||||
* @param Guzzle\Http\Message\RequestInterface
|
||||
* @return Ratchet\WebSocket\Version\VersionInterface
|
||||
*/
|
||||
public function getVersion(RequestInterface $request) {
|
||||
foreach ($this->versions as $version) {
|
||||
if ($version->isProtocol($request)) {
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable support for a specific version of the WebSocket protocol
|
||||
* @param Ratchet\WebSocket\Vesion\VersionInterface
|
||||
* @return HandshakeNegotiator
|
||||
*/
|
||||
public function enableVersion(VersionInterface $version) {
|
||||
$this->versions[$version->getVersionNumber()] = $version;
|
||||
|
||||
if (empty($this->versionString)) {
|
||||
$this->versionString = (string)$version->getVersionNumber();
|
||||
} else {
|
||||
$this->versionString .= ", {$version->getVersionNumber()}";
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable support for a specific WebSocket protocol version
|
||||
* @param int The version ID to un-support
|
||||
* @return HandshakeNegotiator
|
||||
*/
|
||||
public function disableVersion($versionId) {
|
||||
unset($this->versions[$versionId]);
|
||||
|
||||
$this->versionString = '';
|
||||
|
||||
foreach ($this->versions as $id => $object) {
|
||||
$this->versionString .= "{$id}, ";
|
||||
}
|
||||
$this->versionString = substr($this->versionString, 0, -2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string of version numbers supported (comma delimited)
|
||||
* @return string
|
||||
*/
|
||||
public function getSupportedVersionString() {
|
||||
return $this->versionString;
|
||||
}
|
||||
}
|
@ -12,17 +12,20 @@ use Guzzle\Http\Message\Response;
|
||||
* man-in-the-middle attack on 10%-15% of the people who saw their ad who had a browser (currently only Safari) supporting the Hixie76 protocol.
|
||||
* This was exploited by taking advantage of proxy servers in front of the user who ignored some HTTP headers in the handshake
|
||||
* The Hixie76 is currently implemented by Safari
|
||||
* Handshake from Andrea Giammarchi (http://webreflection.blogspot.com/2010/06/websocket-handshake-76-simplified.html)
|
||||
* @link http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
||||
*/
|
||||
class Hixie76 implements VersionInterface {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function isProtocol(RequestInterface $request) {
|
||||
public function isProtocol(RequestInterface $request) {
|
||||
return !(null === $request->getHeader('Sec-WebSocket-Key2', true));
|
||||
}
|
||||
|
||||
public function getVersionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Guzzle\Http\Message\RequestInterface
|
||||
* @return Guzzle\Http\Message\Response
|
||||
|
@ -3,11 +3,15 @@ namespace Ratchet\WebSocket\Version;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
|
||||
class HyBi10 extends RFC6455 {
|
||||
public static function isProtocol(RequestInterface $request) {
|
||||
public function isProtocol(RequestInterface $request) {
|
||||
$version = (int)$request->getHeader('Sec-WebSocket-Version', -1);
|
||||
return ($version >= 6 && $version < 13);
|
||||
}
|
||||
|
||||
public function getVersionNumber() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HyBi10\Message
|
||||
* /
|
||||
|
@ -23,9 +23,14 @@ class RFC6455 implements VersionInterface {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function isProtocol(RequestInterface $request) {
|
||||
public function isProtocol(RequestInterface $request) {
|
||||
$version = (int)$request->getHeader('Sec-WebSocket-Version', -1);
|
||||
return (13 === $version);
|
||||
|
||||
return ($this->getVersionNumber() === $version);
|
||||
}
|
||||
|
||||
public function getVersionNumber() {
|
||||
return 13;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,7 +48,7 @@ class RFC6455 implements VersionInterface {
|
||||
, 'Sec-WebSocket-Accept' => $this->sign($request->getHeader('Sec-WebSocket-Key'))
|
||||
);
|
||||
|
||||
return new Response('101', $headers);
|
||||
return new Response(101, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,10 +3,7 @@ namespace Ratchet\WebSocket\Version;
|
||||
use Guzzle\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Despite the version iterations of WebInterface the actions they go through are similar
|
||||
* This standardizes how the server handles communication with each protocol version
|
||||
* @todo Need better naming conventions...newMessage and newFrame are for reading incoming framed messages (action is unframing)
|
||||
* The current method names suggest you could create a new message/frame to send, which they can not do
|
||||
* A standard interface for interacting with the various version of the WebSocket protocol
|
||||
*/
|
||||
interface VersionInterface {
|
||||
/**
|
||||
@ -15,15 +12,20 @@ interface VersionInterface {
|
||||
* @return bool
|
||||
* @throws UnderflowException If the protocol thinks the headers are still fragmented
|
||||
*/
|
||||
static function isProtocol(RequestInterface $request);
|
||||
function isProtocol(RequestInterface $request);
|
||||
|
||||
/**
|
||||
* Although the version has a name associated with it the integer returned is the proper identification
|
||||
* @return int
|
||||
*/
|
||||
function getVersionNumber();
|
||||
|
||||
/**
|
||||
* Perform the handshake and return the response headers
|
||||
* @param Guzzle\Http\Message\RequestInterface
|
||||
* @return array|string
|
||||
* @return Guzzle\Http\Message\Response
|
||||
* @throws InvalidArgumentException If the HTTP handshake is mal-formed
|
||||
* @throws UnderflowException If the message hasn't finished buffering (not yet implemented, theoretically will only happen with Hixie version)
|
||||
* @todo Change param to accept a Guzzle RequestInterface object
|
||||
*/
|
||||
function handshake(RequestInterface $request);
|
||||
|
||||
|
@ -1,16 +1,30 @@
|
||||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\AbstractConnectionDecorator;
|
||||
use Ratchet\WebSocket\Version\VersionInterface;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @property stdClass $WebSocket
|
||||
*/
|
||||
class WsConnection extends AbstractConnectionDecorator {
|
||||
public function send($data) {
|
||||
// need frame caching
|
||||
/**
|
||||
* @var Ratchet\WebSocket\Version\VersionInterface
|
||||
*/
|
||||
protected $version = null;
|
||||
|
||||
$data = $this->WebSocket->version->frame($data, false);
|
||||
public function __construct(ConnectionInterface $conn) {
|
||||
parent::__construct($conn);
|
||||
|
||||
$this->WebSocket = new \StdClass;
|
||||
}
|
||||
|
||||
public function send($data) {
|
||||
if ($this->hasVersion()) {
|
||||
// need frame caching
|
||||
$data = $this->WebSocket->version->frame($data, false);
|
||||
}
|
||||
|
||||
$this->getConnection()->send($data);
|
||||
}
|
||||
@ -30,4 +44,22 @@ class WsConnection extends AbstractConnectionDecorator {
|
||||
|
||||
public function pong() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasVersion() {
|
||||
return (null === $this->version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the WebSocket protocol version to communicate with
|
||||
* @param Ratchet\WebSocket\Version\VersionInterface
|
||||
* @internal
|
||||
*/
|
||||
public function setVersion(VersionInterface $version) {
|
||||
$this->WebSocket->version = $version;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -13,6 +13,13 @@ use Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory;
|
||||
* @link http://dev.w3.org/html5/websockets/
|
||||
*/
|
||||
class WsServer implements MessageComponentInterface {
|
||||
/**
|
||||
* Negotiates upgrading the HTTP connection to a WebSocket connection
|
||||
* It contains useful configuration properties and methods
|
||||
* @var HandshakeNegotiator
|
||||
*/
|
||||
public $handshaker;
|
||||
|
||||
/**
|
||||
* Decorated component
|
||||
* @var Ratchet\MessageComponentInterface|WsServerInterface
|
||||
@ -24,6 +31,11 @@ class WsServer implements MessageComponentInterface {
|
||||
*/
|
||||
protected $connections;
|
||||
|
||||
/**
|
||||
* @var MessageParser
|
||||
*/
|
||||
protected $messager;
|
||||
|
||||
/**
|
||||
* Re-entrant instances of protocol version classes
|
||||
* @internal
|
||||
@ -53,6 +65,12 @@ class WsServer implements MessageComponentInterface {
|
||||
* @param Ratchet\MessageComponentInterface Your application to run with WebSockets
|
||||
*/
|
||||
public function __construct(MessageComponentInterface $component) {
|
||||
// This will be enabled shortly, causing problems
|
||||
// mb_internal_encoding('UTF-8');
|
||||
|
||||
$this->handshaker = new HandshakeNegotiator;
|
||||
$this->messager = new MessageParser;
|
||||
|
||||
$this->_decorating = $component;
|
||||
$this->connections = new \SplObjectStorage;
|
||||
}
|
||||
@ -61,46 +79,50 @@ class WsServer implements MessageComponentInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onOpen(ConnectionInterface $conn) {
|
||||
$conn->WebSocket = new \stdClass;
|
||||
$conn->WebSocket->handshake = false;
|
||||
$conn->WebSocket->headers = '';
|
||||
$wsConn = new WsConnection($conn);
|
||||
|
||||
$this->connections->attach($conn, $wsConn);
|
||||
|
||||
$this->handshaker->onOpen($wsConn);
|
||||
|
||||
$conn->WebSocket->established = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do handshake, frame/unframe messages coming/going in stack
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onMessage(ConnectionInterface $from, $msg) {
|
||||
if (true !== $from->WebSocket->handshake) {
|
||||
if (!isset($from->WebSocket->version)) {
|
||||
$from->WebSocket->headers .= $msg;
|
||||
if (!$this->isMessageComplete($from->WebSocket->headers)) {
|
||||
return;
|
||||
}
|
||||
$conn = $this->connections[$from];
|
||||
|
||||
$headers = RequestFactory::getInstance()->fromMessage($from->WebSocket->headers);
|
||||
$from->WebSocket->version = $this->getVersion($headers);
|
||||
$from->WebSocket->headers = $headers;
|
||||
if (true !== $conn->WebSocket->established) {
|
||||
if (null === ($response = $this->handshaker->onData($conn, $msg))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = $from->WebSocket->version->handshake($from->WebSocket->headers);
|
||||
$from->WebSocket->handshake = true;
|
||||
|
||||
if ('' !== ($agreedSubProtocols = $this->getSubProtocolString($from->WebSocket->headers->getTokenizedHeader('Sec-WebSocket-Protocol', ',')))) {
|
||||
// This needs to be refactored later on, incorporated with routing
|
||||
if ('' !== ($agreedSubProtocols = $this->getSubProtocolString($from->WebSocket->request->getTokenizedHeader('Sec-WebSocket-Protocol', ',')))) {
|
||||
$response->setHeader('Sec-WebSocket-Protocol', $agreedSubProtocols);
|
||||
}
|
||||
|
||||
$response->setHeader('X-Powered-By', \Ratchet\VERSION);
|
||||
$header = (string)$response;
|
||||
$from->send((string)$response);
|
||||
|
||||
$from->send($header);
|
||||
if (101 != $response->getStatusCode()) {
|
||||
return $from->close();
|
||||
}
|
||||
|
||||
$conn = new WsConnection($from);
|
||||
$this->connections->attach($from, $conn);
|
||||
$conn->WebSocket->established = true;
|
||||
|
||||
return $this->_decorating->onOpen($conn);
|
||||
}
|
||||
|
||||
/*
|
||||
if (null !== ($parsed = $this->messager->onData($from, $msg))) {
|
||||
$this->_decorating->onMessage($from, $parsed);
|
||||
}
|
||||
/**/
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
if (!isset($from->WebSocket->message)) {
|
||||
$from->WebSocket->message = $from->WebSocket->version->newMessage();
|
||||
}
|
||||
@ -135,12 +157,12 @@ class WsServer implements MessageComponentInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onClose(ConnectionInterface $conn) {
|
||||
$decor = $this->connections[$conn];
|
||||
$this->connections->detach($conn);
|
||||
|
||||
// WS::onOpen is not called when the socket connects, it's call when the handshake is done
|
||||
// The socket could close before WS calls onOpen, so we need to check if we've "opened" it for the developer yet
|
||||
if ($this->connections->contains($conn)) {
|
||||
$decor = $this->connections[$conn];
|
||||
$this->connections->detach($conn);
|
||||
|
||||
if ($decor->WebSocket->established) {
|
||||
$this->_decorating->onClose($decor);
|
||||
}
|
||||
}
|
||||
@ -149,62 +171,14 @@ class WsServer implements MessageComponentInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||
if ($this->connections->contains($conn)) {
|
||||
var_dump($e);
|
||||
if ($conn->WebSocket->established) {
|
||||
$this->_decorating->onError($this->connections[$conn], $e);
|
||||
} else {
|
||||
$conn->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the WebSocket protocol version a client is using based on the HTTP header request
|
||||
* @param string HTTP handshake request
|
||||
* @return Version\VersionInterface
|
||||
* @throws UnderFlowException If we think the entire header message hasn't been buffered yet
|
||||
* @throws InvalidArgumentException If we can't understand protocol version request
|
||||
* @todo Verify the first line of the HTTP header as per page 16 of RFC 6455
|
||||
*/
|
||||
protected function getVersion(RequestInterface $request) {
|
||||
foreach ($this->_versions as $name => $instance) {
|
||||
if (null !== $instance) {
|
||||
if ($instance::isProtocol($request)) {
|
||||
return $instance;
|
||||
}
|
||||
} else {
|
||||
$ns = __NAMESPACE__ . "\\Version\\{$name}";
|
||||
if ($ns::isProtocol($request)) {
|
||||
$this->_versions[$name] = new $ns;
|
||||
return $this->_versions[$name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Could not identify WebSocket protocol');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return bool
|
||||
* @todo Abstract, some hard coding done for (stupid) Hixie protocol
|
||||
*/
|
||||
protected function isMessageComplete($message) {
|
||||
static $crlf = "\r\n\r\n";
|
||||
|
||||
$headers = (boolean)strstr($message, $crlf);
|
||||
if (!$headers) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strstr($message, 'Sec-WebSocket-Key2')) {
|
||||
if (8 !== strlen(substr($message, strpos($message, $crlf) + strlen($crlf)))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return boolean
|
||||
@ -240,27 +214,4 @@ class WsServer implements MessageComponentInterface {
|
||||
|
||||
return substr($string, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a version of the WebSocket protocol *cough*Hixie76*cough*
|
||||
* @param string The name of the version to disable
|
||||
* @throws InvalidArgumentException If the given version does not exist
|
||||
*/
|
||||
public function disableVersion($name) {
|
||||
if (!array_key_exists($name, $this->_versions)) {
|
||||
throw new \InvalidArgumentException("Version {$name} not found");
|
||||
}
|
||||
|
||||
unset($this->_versions[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the option to mask the payload upon sending to client
|
||||
* If WebSocket is used as server, this should be false, client to true
|
||||
* @param bool
|
||||
* @todo User shouldn't have to know/set this, need to figure out how to do this automatically
|
||||
*/
|
||||
public function setMaskPayload($opt) {
|
||||
$this->_mask_payload = (boolean)$opt;
|
||||
}
|
||||
}
|
103
tests/Ratchet/Tests/WebSocket/HandshakeNegotiatorTest.php
Normal file
103
tests/Ratchet/Tests/WebSocket/HandshakeNegotiatorTest.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
namespace Ratchet\Tests\WebSocket;
|
||||
use Ratchet\WebSocket\HandshakeNegotiator;
|
||||
use Ratchet\WebSocket\WsConnection;
|
||||
use Ratchet\Tests\Mock\Connection as ConnectionStub;
|
||||
use Ratchet\WebSocket\Version\RFC6455;
|
||||
use Ratchet\WebSocket\Version\HyBi10;
|
||||
use Ratchet\WebSocket\Version\Hixie76;
|
||||
use Guzzle\Http\Message\EntityEnclosingRequest;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\WebSocket\HandshakeNegotiator
|
||||
*/
|
||||
class HandshakeNegotiatorTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $parser;
|
||||
|
||||
public function setUp() {
|
||||
$this->parser = new HandshakeNegotiator();
|
||||
}
|
||||
|
||||
public function headersProvider() {
|
||||
return array(
|
||||
array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n")
|
||||
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n")
|
||||
, array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n1")
|
||||
, array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖")
|
||||
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖\r\n\r\n")
|
||||
, array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie\r\n")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider headersProvider
|
||||
*/
|
||||
public function testIsEom($expected, $message) {
|
||||
$this->assertEquals($expected, $this->parser->isEom($message));
|
||||
}
|
||||
|
||||
public function testFluentInterface() {
|
||||
$rfc = new RFC6455;
|
||||
|
||||
$this->assertSame($this->parser, $this->parser->disableVersion(13));
|
||||
$this->assertSame($this->parser, $this->parser->enableVersion($rfc));
|
||||
}
|
||||
|
||||
public function testGetVersion() {
|
||||
$this->parser->disableVersion(13);
|
||||
$rfc = new RFC6455;
|
||||
$this->parser->enableVersion($rfc);
|
||||
|
||||
$req = new EntityEnclosingRequest('get', '/', array(
|
||||
'Host' => 'socketo.me'
|
||||
, 'Sec-WebSocket-Version' => 13
|
||||
));
|
||||
|
||||
$this->assertSame($rfc, $this->parser->getVersion($req));
|
||||
}
|
||||
|
||||
public function testGetNopeVersionAndDisable() {
|
||||
$this->parser->disableVersion(13);
|
||||
|
||||
$req = new EntityEnclosingRequest('get', '/', array(
|
||||
'Host' => 'socketo.me'
|
||||
, 'Sec-WebSocket-Version' => 13
|
||||
));
|
||||
|
||||
$this->assertNull($this->parser->getVersion($req));
|
||||
}
|
||||
|
||||
public function testGetSupportedVersionString() {
|
||||
$v1 = new RFC6455;
|
||||
$v2 = new HyBi10;
|
||||
|
||||
$parser = new HandshakeNegotiator();
|
||||
$parser->enableVersion($v1);
|
||||
$parser->enableVersion($v2);
|
||||
|
||||
$string = $parser->getSupportedVersionString();
|
||||
$values = explode(',', $string);
|
||||
|
||||
$this->assertContains($v1->getVersionNumber(), $values);
|
||||
$this->assertContains($v2->getVersionNumber(), $values);
|
||||
}
|
||||
|
||||
public function testGetSupportedVersionAfterRemoval() {
|
||||
$this->parser->disableVersion(0);
|
||||
|
||||
$values = explode(',', $this->parser->getSupportedVersionString());
|
||||
|
||||
$this->assertEquals(2, count($values));
|
||||
$this->assertFalse(array_search(0, $values));
|
||||
}
|
||||
|
||||
public function testBufferOverflowResponse() {
|
||||
$conn = new WsConnection(new ConnectionStub);
|
||||
$this->parser->onOpen($conn);
|
||||
|
||||
$this->parser->maxSize = 20;
|
||||
|
||||
$this->assertNull($this->parser->onData($conn, "GET / HTTP/1.1\r\n"));
|
||||
$this->assertGreaterThan(400, $this->parser->onData($conn, "Header-Is: Too Big")->getStatusCode());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user