diff options
author | marvin-borner@live.com | 2018-04-10 21:50:16 +0200 |
---|---|---|
committer | marvin-borner@live.com | 2018-04-10 21:54:48 +0200 |
commit | fc9401f04a3aca5abb22f87ebc210de8afe11d32 (patch) | |
tree | b0b310f3581764ec3955f4e496a05137a32951c3 /assets/php/vendor/ratchet | |
parent | 286d643180672f20526f3dc3bd19d7b751e2fa97 (diff) |
Initial Commit
Diffstat (limited to 'assets/php/vendor/ratchet')
31 files changed, 2837 insertions, 0 deletions
diff --git a/assets/php/vendor/ratchet/rfc6455/.gitignore b/assets/php/vendor/ratchet/rfc6455/.gitignore new file mode 100644 index 0000000..42ab5d5 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/.gitignore @@ -0,0 +1,4 @@ +composer.lock +vendor +tests/ab/reports +reports diff --git a/assets/php/vendor/ratchet/rfc6455/.travis.yml b/assets/php/vendor/ratchet/rfc6455/.travis.yml new file mode 100644 index 0000000..11d51b4 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/.travis.yml @@ -0,0 +1,20 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7 + - hhvm + +before_install: + - export PATH=$HOME/.local/bin:$PATH + - pip install --user autobahntestsuite + - pip list --user autobahntestsuite + +before_script: + - composer install + - sh tests/ab/run_ab_tests.sh + +script: + - vendor/bin/phpunit diff --git a/assets/php/vendor/ratchet/rfc6455/LICENSE b/assets/php/vendor/ratchet/rfc6455/LICENSE new file mode 100644 index 0000000..7f8c128 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2016 Chris Boden + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/assets/php/vendor/ratchet/rfc6455/README.md b/assets/php/vendor/ratchet/rfc6455/README.md new file mode 100644 index 0000000..7c09148 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/README.md @@ -0,0 +1,13 @@ +# RFC6455 - The WebSocket Protocol + +[](https://travis-ci.org/ratchetphp/RFC6455) + + +This library a protocol handler for the RFC6455 specification. +It contains components for both server and client side handshake and messaging protocol negotation. + +Aspects that are left open to interpertation in the specification are also left open in this library. +It is up to the implementation to determine how those interpertations are to be dealt with. + +This library is independent, framework agnostic, and does not deal with any I/O. +HTTP upgrade negotiation integration points are handled with PSR-7 interfaces. diff --git a/assets/php/vendor/ratchet/rfc6455/composer.json b/assets/php/vendor/ratchet/rfc6455/composer.json new file mode 100644 index 0000000..224066b --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/composer.json @@ -0,0 +1,32 @@ +{ + "name": "ratchet/rfc6455", + "type": "library", + "description": "RFC6455 WebSocket protocol handler", + "keywords": ["WebSockets", "websocket", "RFC6455"], + "homepage": "http://socketo.me", + "license": "MIT", + "authors": [{ + "name": "Chris Boden" + , "email": "cboden@gmail.com" + , "role": "Developer" + }], + "support": { + "forum": "https://groups.google.com/forum/#!forum/ratchet-php" + , "issues": "https://github.com/ratchetphp/RFC6455/issues" + , "irc": "irc://irc.freenode.org/reactphp" + }, + "autoload": { + "psr-4": { + "Ratchet\\RFC6455\\": "src" + } + }, + "require": { + "php": ">=5.4.2", + "guzzlehttp/psr7": "^1.0" + }, + "require-dev": { + "react/http": "^0.4.1", + "react/socket-client": "^0.4.3", + "phpunit/phpunit": "4.8.*" + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/phpunit.xml.dist b/assets/php/vendor/ratchet/rfc6455/phpunit.xml.dist new file mode 100644 index 0000000..8f2e7d1 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/phpunit.xml.dist @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit + forceCoversAnnotation="true" + mapTestClassNameToCoveredClassName="true" + bootstrap="tests/bootstrap.php" + colors="true" + backupGlobals="false" + backupStaticAttributes="false" + syntaxCheck="false" + stopOnError="false" + > + + <testsuites> + <testsuite name="tests"> + <directory>tests</directory> + <exclude> + <directory>test/ab</directory> + </exclude> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory>./src/</directory> + </whitelist> + </filter> +</phpunit>
\ No newline at end of file diff --git a/assets/php/vendor/ratchet/rfc6455/src/Handshake/ClientNegotiator.php b/assets/php/vendor/ratchet/rfc6455/src/Handshake/ClientNegotiator.php new file mode 100644 index 0000000..70856df --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Handshake/ClientNegotiator.php @@ -0,0 +1,53 @@ +<?php +namespace Ratchet\RFC6455\Handshake; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\UriInterface; +use GuzzleHttp\Psr7\Request; + +class ClientNegotiator { + /** + * @var ResponseVerifier + */ + private $verifier; + + /** + * @var \Psr\Http\Message\RequestInterface + */ + private $defaultHeader; + + function __construct() { + $this->verifier = new ResponseVerifier; + + $this->defaultHeader = new Request('GET', '', [ + 'Connection' => 'Upgrade' + , 'Upgrade' => 'websocket' + , 'Sec-WebSocket-Version' => $this->getVersion() + , 'User-Agent' => "Ratchet" + ]); + } + + public function generateRequest(UriInterface $uri) { + return $this->defaultHeader->withUri($uri) + ->withHeader("Sec-WebSocket-Key", $this->generateKey()); + } + + public function validateResponse(RequestInterface $request, ResponseInterface $response) { + return $this->verifier->verifyAll($request, $response); + } + + public 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); + } + + public function getVersion() { + return 13; + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Handshake/NegotiatorInterface.php b/assets/php/vendor/ratchet/rfc6455/src/Handshake/NegotiatorInterface.php new file mode 100644 index 0000000..c152eca --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Handshake/NegotiatorInterface.php @@ -0,0 +1,47 @@ +<?php +namespace Ratchet\RFC6455\Handshake; +use Psr\Http\Message\RequestInterface; + +/** + * A standard interface for interacting with the various version of the WebSocket protocol + * @todo Look in to extension support + */ +interface NegotiatorInterface { + const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; + + /** + * Given an HTTP header, determine if this version should handle the protocol + * @param RequestInterface $request + * @return bool + */ + 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 RequestInterface $request + * @return \Psr\Http\Message\ResponseInterface + */ + function handshake(RequestInterface $request); + + /** + * Add supported protocols. If the request has any matching the response will include one + * @param array $protocols + */ + function setSupportedSubProtocols(array $protocols); + + /** + * If enabled and support for a subprotocol has been added handshake + * will not upgrade if a match between request and supported subprotocols + * @param boolean $enable + * @todo Consider extending this interface and moving this there. + * The spec does says the server can fail for this reason, but + * it is not a requirement. This is an implementation detail. + */ + function setStrictSubProtocolCheck($enable); +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Handshake/RequestVerifier.php b/assets/php/vendor/ratchet/rfc6455/src/Handshake/RequestVerifier.php new file mode 100644 index 0000000..1ace489 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Handshake/RequestVerifier.php @@ -0,0 +1,140 @@ +<?php +namespace Ratchet\RFC6455\Handshake; +use Psr\Http\Message\RequestInterface; + +/** + * These are checks to ensure the client requested handshake are valid + * Verification rules come from section 4.2.1 of the RFC6455 document + * @todo Currently just returning invalid - should consider returning appropriate HTTP status code error #s + */ +class RequestVerifier { + const VERSION = 13; + + /** + * Given an array of the headers this method will run through all verification methods + * @param RequestInterface $request + * @return bool TRUE if all headers are valid, FALSE if 1 or more were invalid + */ + public function verifyAll(RequestInterface $request) { + $passes = 0; + + $passes += (int)$this->verifyMethod($request->getMethod()); + $passes += (int)$this->verifyHTTPVersion($request->getProtocolVersion()); + $passes += (int)$this->verifyRequestURI($request->getUri()->getPath()); + $passes += (int)$this->verifyHost($request->getHeader('Host')); + $passes += (int)$this->verifyUpgradeRequest($request->getHeader('Upgrade')); + $passes += (int)$this->verifyConnection($request->getHeader('Connection')); + $passes += (int)$this->verifyKey($request->getHeader('Sec-WebSocket-Key')); + $passes += (int)$this->verifyVersion($request->getHeader('Sec-WebSocket-Version')); + + return (8 === $passes); + } + + /** + * Test the HTTP method. MUST be "GET" + * @param string + * @return bool + */ + public function verifyMethod($val) { + return ('get' === strtolower($val)); + } + + /** + * Test the HTTP version passed. MUST be 1.1 or greater + * @param string|int + * @return bool + */ + public function verifyHTTPVersion($val) { + return (1.1 <= (double)$val); + } + + /** + * @param string + * @return bool + */ + public function verifyRequestURI($val) { + if ($val[0] !== '/') { + return false; + } + + if (false !== strstr($val, '#')) { + return false; + } + + if (!extension_loaded('mbstring')) { + return true; + } + + return mb_check_encoding($val, 'US-ASCII'); + } + + /** + * @param array $hostHeader + * @return bool + * @todo Once I fix HTTP::getHeaders just verify this isn't NULL or empty...or maybe need to verify it's a valid domain??? Or should it equal $_SERVER['HOST'] ? + */ + public function verifyHost(array $hostHeader) { + return (1 === count($hostHeader)); + } + + /** + * Verify the Upgrade request to WebSockets. + * @param array $upgradeHeader MUST equal "websocket" + * @return bool + */ + public function verifyUpgradeRequest(array $upgradeHeader) { + return (1 === count($upgradeHeader) && 'websocket' === strtolower($upgradeHeader[0])); + } + + /** + * Verify the Connection header + * @param array $connectionHeader MUST include "Upgrade" + * @return bool + */ + public function verifyConnection(array $connectionHeader) { + foreach ($connectionHeader as $l) { + $upgrades = array_filter( + array_map('trim', array_map('strtolower', explode(',', $l))), + function ($x) { + return 'upgrade' === $x; + } + ); + if (count($upgrades) > 0) { + return true; + } + } + return false; + } + + /** + * This function verifies the nonce is valid (64 big encoded, 16 bytes random string) + * @param array $keyHeader + * @return bool + * @todo The spec says we don't need to base64_decode - can I just check if the length is 24 and not decode? + * @todo Check the spec to see what the encoding of the key could be + */ + public function verifyKey(array $keyHeader) { + return (1 === count($keyHeader) && 16 === strlen(base64_decode($keyHeader[0]))); + } + + /** + * Verify the version passed matches this RFC + * @param string|int $versionHeader MUST equal 13|"13" + * @return bool + */ + public function verifyVersion($versionHeader) { + return (1 === count($versionHeader) && static::VERSION === (int)$versionHeader[0]); + } + + /** + * @todo Write logic for this method. See section 4.2.1.8 + */ + public function verifyProtocol($val) { + } + + /** + * @todo Write logic for this method. See section 4.2.1.9 + */ + public function verifyExtensions($val) { + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Handshake/ResponseVerifier.php b/assets/php/vendor/ratchet/rfc6455/src/Handshake/ResponseVerifier.php new file mode 100644 index 0000000..de03f53 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Handshake/ResponseVerifier.php @@ -0,0 +1,52 @@ +<?php +namespace Ratchet\RFC6455\Handshake; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +class ResponseVerifier { + public function verifyAll(RequestInterface $request, ResponseInterface $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') + ); + $passes += (int)$this->verifySubProtocol( + $request->getHeader('Sec-WebSocket-Protocol') + , $response->getHeader('Sec-WebSocket-Protocol') + ); + + return (5 === $passes); + } + + public function verifyStatus($status) { + return ((int)$status === 101); + } + + public function verifyUpgrade(array $upgrade) { + return (in_array('websocket', array_map('strtolower', $upgrade))); + } + + public function verifyConnection(array $connection) { + return (in_array('upgrade', array_map('strtolower', $connection))); + } + + public function verifySecWebSocketAccept($swa, $key) { + return ( + 1 === count($swa) && + 1 === count($key) && + $swa[0] === $this->sign($key[0]) + ); + } + + public function sign($key) { + 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; + } +}
\ No newline at end of file diff --git a/assets/php/vendor/ratchet/rfc6455/src/Handshake/ServerNegotiator.php b/assets/php/vendor/ratchet/rfc6455/src/Handshake/ServerNegotiator.php new file mode 100644 index 0000000..5a0073b --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Handshake/ServerNegotiator.php @@ -0,0 +1,136 @@ +<?php +namespace Ratchet\RFC6455\Handshake; +use Psr\Http\Message\RequestInterface; +use GuzzleHttp\Psr7\Response; + +/** + * The latest version of the WebSocket protocol + * @todo Unicode: return mb_convert_encoding(pack("N",$u), mb_internal_encoding(), 'UCS-4BE'); + */ +class ServerNegotiator implements NegotiatorInterface { + /** + * @var \Ratchet\RFC6455\Handshake\RequestVerifier + */ + private $verifier; + + private $_supportedSubProtocols = []; + + private $_strictSubProtocols = false; + + public function __construct(RequestVerifier $requestVerifier) { + $this->verifier = $requestVerifier; + } + + /** + * {@inheritdoc} + */ + public function isProtocol(RequestInterface $request) { + return $this->verifier->verifyVersion($request->getHeader('Sec-WebSocket-Version')); + } + + /** + * {@inheritdoc} + */ + public function getVersionNumber() { + return RequestVerifier::VERSION; + } + + /** + * {@inheritdoc} + */ + public function handshake(RequestInterface $request) { + if (true !== $this->verifier->verifyMethod($request->getMethod())) { + return new Response(405, ['Allow' => 'GET']); + } + + if (true !== $this->verifier->verifyHTTPVersion($request->getProtocolVersion())) { + return new Response(505); + } + + if (true !== $this->verifier->verifyRequestURI($request->getUri()->getPath())) { + return new Response(400); + } + + if (true !== $this->verifier->verifyHost($request->getHeader('Host'))) { + return new Response(400); + } + + $upgradeSuggestion = [ + 'Connection' => 'Upgrade', + 'Upgrade' => 'websocket', + 'Sec-WebSocket-Version' => $this->getVersionNumber() + ]; + if (count($this->_supportedSubProtocols) > 0) { + $upgradeSuggestion['Sec-WebSocket-Protocol'] = implode(', ', $this->_supportedSubProtocols); + } + if (true !== $this->verifier->verifyUpgradeRequest($request->getHeader('Upgrade'))) { + return new Response(426, $upgradeSuggestion, null, '1.1', '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'); + } + + if (true !== $this->verifier->verifyKey($request->getHeader('Sec-WebSocket-Key'))) { + return new Response(400, [], null, '1.1', 'Invalid Sec-WebSocket-Key'); + } + + if (true !== $this->verifier->verifyVersion($request->getHeader('Sec-WebSocket-Version'))) { + return new Response(426, $upgradeSuggestion); + } + + $headers = []; + $subProtocols = $request->getHeader('Sec-WebSocket-Protocol'); + if (count($subProtocols) > 0 || (count($this->_supportedSubProtocols) > 0 && $this->_strictSubProtocols)) { + $subProtocols = array_map('trim', explode(',', implode(',', $subProtocols))); + + $match = array_reduce($subProtocols, function($accumulator, $protocol) { + return $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'); + } + + if (null !== $match) { + $headers['Sec-WebSocket-Protocol'] = $match; + } + } + + return 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' + ])); + } + + /** + * Used when doing the handshake to encode the key, verifying client/server are speaking the same language + * @param string $key + * @return string + * @internal + */ + public function sign($key) { + return base64_encode(sha1($key . static::GUID, true)); + } + + /** + * @param array $protocols + */ + function setSupportedSubProtocols(array $protocols) { + $this->_supportedSubProtocols = array_flip($protocols); + } + + /** + * If enabled and support for a subprotocol has been added handshake + * will not upgrade if a match between request and supported subprotocols + * @param boolean $enable + * @todo Consider extending this interface and moving this there. + * The spec does says the server can fail for this reason, but + * it is not a requirement. This is an implementation detail. + */ + function setStrictSubProtocolCheck($enable) { + $this->_strictSubProtocols = (boolean)$enable; + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Messaging/CloseFrameChecker.php b/assets/php/vendor/ratchet/rfc6455/src/Messaging/CloseFrameChecker.php new file mode 100644 index 0000000..3d800e5 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Messaging/CloseFrameChecker.php @@ -0,0 +1,24 @@ +<?php +namespace Ratchet\RFC6455\Messaging; + +class CloseFrameChecker { + private $validCloseCodes = []; + + public function __construct() { + $this->validCloseCodes = [ + Frame::CLOSE_NORMAL, + Frame::CLOSE_GOING_AWAY, + Frame::CLOSE_PROTOCOL, + Frame::CLOSE_BAD_DATA, + Frame::CLOSE_BAD_PAYLOAD, + Frame::CLOSE_POLICY, + Frame::CLOSE_TOO_BIG, + Frame::CLOSE_MAND_EXT, + Frame::CLOSE_SRV_ERR, + ]; + } + + public function __invoke($val) { + return ($val >= 3000 && $val <= 4999) || in_array($val, $this->validCloseCodes); + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Messaging/DataInterface.php b/assets/php/vendor/ratchet/rfc6455/src/Messaging/DataInterface.php new file mode 100644 index 0000000..18aa2e3 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Messaging/DataInterface.php @@ -0,0 +1,34 @@ +<?php +namespace Ratchet\RFC6455\Messaging; + +interface DataInterface { + /** + * Determine if the message is complete or still fragmented + * @return bool + */ + function isCoalesced(); + + /** + * Get the number of bytes the payload is set to be + * @return int + */ + function getPayloadLength(); + + /** + * Get the payload (message) sent from peer + * @return string + */ + function getPayload(); + + /** + * Get raw contents of the message + * @return string + */ + function getContents(); + + /** + * Should return the unmasked payload received from peer + * @return string + */ + function __toString(); +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Messaging/Frame.php b/assets/php/vendor/ratchet/rfc6455/src/Messaging/Frame.php new file mode 100644 index 0000000..40f9eb2 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Messaging/Frame.php @@ -0,0 +1,473 @@ +<?php +namespace Ratchet\RFC6455\Messaging; + +class Frame implements FrameInterface { + const OP_CONTINUE = 0; + const OP_TEXT = 1; + const OP_BINARY = 2; + const OP_CLOSE = 8; + const OP_PING = 9; + const OP_PONG = 10; + + const CLOSE_NORMAL = 1000; + const CLOSE_GOING_AWAY = 1001; + const CLOSE_PROTOCOL = 1002; + const CLOSE_BAD_DATA = 1003; + const CLOSE_NO_STATUS = 1005; + const CLOSE_ABNORMAL = 1006; + const CLOSE_BAD_PAYLOAD = 1007; + const CLOSE_POLICY = 1008; + const CLOSE_TOO_BIG = 1009; + const CLOSE_MAND_EXT = 1010; + const CLOSE_SRV_ERR = 1011; + const CLOSE_TLS = 1015; + + const MASK_LENGTH = 4; + + /** + * The contents of the frame + * @var string + */ + protected $data = ''; + + /** + * Number of bytes received from the frame + * @var int + */ + public $bytesRecvd = 0; + + /** + * Number of bytes in the payload (as per framing protocol) + * @var int + */ + protected $defPayLen = -1; + + /** + * If the frame is coalesced this is true + * This is to prevent doing math every time ::isCoalesced is called + * @var boolean + */ + private $isCoalesced = false; + + /** + * The unpacked first byte of the frame + * @var int + */ + protected $firstByte = -1; + + /** + * The unpacked second byte of the frame + * @var int + */ + protected $secondByte = -1; + + /** + * @var callable + * @returns \UnderflowException + */ + private $ufeg; + + /** + * @param string|null $payload + * @param bool $final + * @param int $opcode + * @param callable<\UnderflowException> $ufExceptionFactory + */ + public function __construct($payload = null, $final = true, $opcode = 1, callable $ufExceptionFactory = null) { + $this->ufeg = $ufExceptionFactory ?: function($msg = '') { + return new \UnderflowException($msg); + }; + + if (null === $payload) { + return; + } + + $this->defPayLen = strlen($payload); + $this->firstByte = ($final ? 128 : 0) + $opcode; + $this->secondByte = $this->defPayLen; + $this->isCoalesced = true; + + $ext = ''; + if ($this->defPayLen > 65535) { + $ext = pack('NN', 0, $this->defPayLen); + $this->secondByte = 127; + } elseif ($this->defPayLen > 125) { + $ext = pack('n', $this->defPayLen); + $this->secondByte = 126; + } + + $this->data = chr($this->firstByte) . chr($this->secondByte) . $ext . $payload; + $this->bytesRecvd = 2 + strlen($ext) + $this->defPayLen; + } + + /** + * {@inheritdoc} + */ + public function isCoalesced() { + if (true === $this->isCoalesced) { + return true; + } + + try { + $payload_length = $this->getPayloadLength(); + $payload_start = $this->getPayloadStartingByte(); + } catch (\UnderflowException $e) { + return false; + } + + $this->isCoalesced = $this->bytesRecvd >= $payload_length + $payload_start; + + return $this->isCoalesced; + } + + /** + * {@inheritdoc} + */ + public function addBuffer($buf) { + $len = strlen($buf); + + $this->data .= $buf; + $this->bytesRecvd += $len; + + if ($this->firstByte === -1 && $this->bytesRecvd !== 0) { + $this->firstByte = ord($this->data[0]); + } + + if ($this->secondByte === -1 && $this->bytesRecvd >= 2) { + $this->secondByte = ord($this->data[1]); + } + } + + /** + * {@inheritdoc} + */ + public function isFinal() { + if (-1 === $this->firstByte) { + throw call_user_func($this->ufeg, 'Not enough bytes received to determine if this is the final frame in message'); + } + + return 128 === ($this->firstByte & 128); + } + + /** + * @return boolean + * @throws \UnderflowException + */ + public function getRsv1() { + if (-1 === $this->firstByte) { + throw call_user_func($this->ufeg, 'Not enough bytes received to determine reserved bit'); + } + + return 64 === ($this->firstByte & 64); + } + + /** + * @return boolean + * @throws \UnderflowException + */ + public function getRsv2() { + if (-1 === $this->firstByte) { + throw call_user_func($this->ufeg, 'Not enough bytes received to determine reserved bit'); + } + + return 32 === ($this->firstByte & 32); + } + + /** + * @return boolean + * @throws \UnderflowException + */ + public function getRsv3() { + if (-1 === $this->firstByte) { + throw call_user_func($this->ufeg, 'Not enough bytes received to determine reserved bit'); + } + + return 16 === ($this->firstByte & 16); + } + + /** + * {@inheritdoc} + */ + public function isMasked() { + if (-1 === $this->secondByte) { + throw call_user_func($this->ufeg, "Not enough bytes received ({$this->bytesRecvd}) to determine if mask is set"); + } + + return 128 === ($this->secondByte & 128); + } + + /** + * {@inheritdoc} + */ + public function getMaskingKey() { + if (!$this->isMasked()) { + return ''; + } + + $start = 1 + $this->getNumPayloadBytes(); + + if ($this->bytesRecvd < $start + static::MASK_LENGTH) { + throw call_user_func($this->ufeg, 'Not enough data buffered to calculate the masking key'); + } + + return substr($this->data, $start, static::MASK_LENGTH); + } + + /** + * Create a 4 byte masking key + * @return string + */ + public function generateMaskingKey() { + $mask = ''; + + for ($i = 1; $i <= static::MASK_LENGTH; $i++) { + $mask .= chr(rand(32, 126)); + } + + return $mask; + } + + /** + * Apply a mask to the payload + * @param string|null If NULL is passed a masking key will be generated + * @throws \OutOfBoundsException + * @throws \InvalidArgumentException If there is an issue with the given masking key + * @return Frame + */ + public function maskPayload($maskingKey = null) { + if (null === $maskingKey) { + $maskingKey = $this->generateMaskingKey(); + } + + if (static::MASK_LENGTH !== strlen($maskingKey)) { + throw new \InvalidArgumentException("Masking key must be " . static::MASK_LENGTH ." characters"); + } + + if (extension_loaded('mbstring') && true !== mb_check_encoding($maskingKey, 'US-ASCII')) { + throw new \OutOfBoundsException("Masking key MUST be ASCII"); + } + + $this->unMaskPayload(); + + $this->secondByte = $this->secondByte | 128; + $this->data[1] = chr($this->secondByte); + + $this->data = substr_replace($this->data, $maskingKey, $this->getNumPayloadBytes() + 1, 0); + + $this->bytesRecvd += static::MASK_LENGTH; + $this->data = substr_replace($this->data, $this->applyMask($maskingKey), $this->getPayloadStartingByte(), $this->getPayloadLength()); + + return $this; + } + + /** + * Remove a mask from the payload + * @throws \UnderFlowException If the frame is not coalesced + * @return Frame + */ + public function unMaskPayload() { + if (!$this->isCoalesced()) { + throw call_user_func($this->ufeg, 'Frame must be coalesced before applying mask'); + } + + if (!$this->isMasked()) { + return $this; + } + + $maskingKey = $this->getMaskingKey(); + + $this->secondByte = $this->secondByte & ~128; + $this->data[1] = chr($this->secondByte); + + $this->data = substr_replace($this->data, '', $this->getNumPayloadBytes() + 1, static::MASK_LENGTH); + + $this->bytesRecvd -= static::MASK_LENGTH; + $this->data = substr_replace($this->data, $this->applyMask($maskingKey), $this->getPayloadStartingByte(), $this->getPayloadLength()); + + return $this; + } + + /** + * Apply a mask to a string or the payload of the instance + * @param string $maskingKey The 4 character masking key to be applied + * @param string|null $payload A string to mask or null to use the payload + * @throws \UnderflowException If using the payload but enough hasn't been buffered + * @return string The masked string + */ + public function applyMask($maskingKey, $payload = null) { + if (null === $payload) { + if (!$this->isCoalesced()) { + throw call_user_func($this->ufeg, 'Frame must be coalesced to apply a mask'); + } + + $payload = substr($this->data, $this->getPayloadStartingByte(), $this->getPayloadLength()); + } + + $len = strlen($payload); + + if (0 === $len) { + return ''; + } + + return $payload ^ str_pad('', $len, $maskingKey, STR_PAD_RIGHT); + + // TODO: Remove this before publish - keeping methods here to compare performance (above is faster but need control against v0.3.3) + + $applied = ''; + for ($i = 0, $len = strlen($payload); $i < $len; $i++) { + $applied .= $payload[$i] ^ $maskingKey[$i % static::MASK_LENGTH]; + } + + return $applied; + } + + /** + * {@inheritdoc} + */ + public function getOpcode() { + if (-1 === $this->firstByte) { + throw call_user_func($this->ufeg, 'Not enough bytes received to determine opcode'); + } + + return ($this->firstByte & ~240); + } + + /** + * Gets the decimal value of bits 9 (10th) through 15 inclusive + * @return int + * @throws \UnderflowException If the buffer doesn't have enough data to determine this + */ + protected function getFirstPayloadVal() { + if (-1 === $this->secondByte) { + throw call_user_func($this->ufeg, 'Not enough bytes received'); + } + + return $this->secondByte & 127; + } + + /** + * @return int (7|23|71) Number of bits defined for the payload length in the fame + * @throws \UnderflowException + */ + protected function getNumPayloadBits() { + if (-1 === $this->secondByte) { + throw call_user_func($this->ufeg, 'Not enough bytes received'); + } + + // By default 7 bits are used to describe the payload length + // These are bits 9 (10th) through 15 inclusive + $bits = 7; + + // Get the value of those bits + $check = $this->getFirstPayloadVal(); + + // If the value is 126 the 7 bits plus the next 16 are used to describe the payload length + if ($check >= 126) { + $bits += 16; + } + + // If the value of the initial payload length are is 127 an additional 48 bits are used to describe length + // Note: The documentation specifies the length is to be 63 bits, but I think that's a typo and is 64 (16+48) + if ($check === 127) { + $bits += 48; + } + + return $bits; + } + + /** + * This just returns the number of bytes used in the frame to describe the payload length (as opposed to # of bits) + * @see getNumPayloadBits + */ + protected function getNumPayloadBytes() { + return (1 + $this->getNumPayloadBits()) / 8; + } + + /** + * {@inheritdoc} + */ + public function getPayloadLength() { + if ($this->defPayLen !== -1) { + return $this->defPayLen; + } + + $this->defPayLen = $this->getFirstPayloadVal(); + if ($this->defPayLen <= 125) { + return $this->getPayloadLength(); + } + + $byte_length = $this->getNumPayloadBytes(); + if ($this->bytesRecvd < 1 + $byte_length) { + $this->defPayLen = -1; + throw call_user_func($this->ufeg, 'Not enough data buffered to determine payload length'); + } + + $len = 0; + for ($i = 2; $i <= $byte_length; $i++) { + $len <<= 8; + $len += ord($this->data[$i]); + } + + $this->defPayLen = $len; + + return $this->getPayloadLength(); + } + + /** + * {@inheritdoc} + */ + public function getPayloadStartingByte() { + return 1 + $this->getNumPayloadBytes() + ($this->isMasked() ? static::MASK_LENGTH : 0); + } + + /** + * {@inheritdoc} + * @todo Consider not checking mask, always returning the payload, masked or not + */ + public function getPayload() { + if (!$this->isCoalesced()) { + throw call_user_func($this->ufeg, 'Can not return partial message'); + } + + return $this->__toString(); + } + + /** + * Get the raw contents of the frame + * @todo This is untested, make sure the substr is right - trying to return the frame w/o the overflow + */ + public function getContents() { + return substr($this->data, 0, $this->getPayloadStartingByte() + $this->getPayloadLength()); + } + + public function __toString() { + $payload = (string)substr($this->data, $this->getPayloadStartingByte(), $this->getPayloadLength()); + + if ($this->isMasked()) { + $payload = $this->applyMask($this->getMaskingKey(), $payload); + } + + return $payload; + } + + /** + * Sometimes clients will concatenate more than one frame over the wire + * This method will take the extra bytes off the end and return them + * @return string + */ + public function extractOverflow() { + if ($this->isCoalesced()) { + $endPoint = $this->getPayloadLength(); + $endPoint += $this->getPayloadStartingByte(); + + if ($this->bytesRecvd > $endPoint) { + $overflow = substr($this->data, $endPoint); + $this->data = substr($this->data, 0, $endPoint); + + return $overflow; + } + } + + return ''; + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Messaging/FrameInterface.php b/assets/php/vendor/ratchet/rfc6455/src/Messaging/FrameInterface.php new file mode 100644 index 0000000..dc24091 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Messaging/FrameInterface.php @@ -0,0 +1,38 @@ +<?php +namespace Ratchet\RFC6455\Messaging; + +interface FrameInterface extends DataInterface { + /** + * Add incoming data to the frame from peer + * @param string + */ + function addBuffer($buf); + + /** + * Is this the final frame in a fragmented message? + * @return bool + */ + function isFinal(); + + /** + * Is the payload masked? + * @return bool + */ + function isMasked(); + + /** + * @return int + */ + function getOpcode(); + + /** + * @return int + */ + //function getReceivedPayloadLength(); + + /** + * 32-big string + * @return string + */ + function getMaskingKey(); +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Messaging/Message.php b/assets/php/vendor/ratchet/rfc6455/src/Messaging/Message.php new file mode 100644 index 0000000..4f3b014 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Messaging/Message.php @@ -0,0 +1,123 @@ +<?php +namespace Ratchet\RFC6455\Messaging; + +class Message implements \IteratorAggregate, MessageInterface { + /** + * @var \SplDoublyLinkedList + */ + private $_frames; + + public function __construct() { + $this->_frames = new \SplDoublyLinkedList; + } + + public function getIterator() { + return $this->_frames; + } + + /** + * {@inheritdoc} + */ + public function count() { + return count($this->_frames); + } + + /** + * {@inheritdoc} + */ + public function isCoalesced() { + if (count($this->_frames) == 0) { + return false; + } + + $last = $this->_frames->top(); + + return ($last->isCoalesced() && $last->isFinal()); + } + + /** + * {@inheritdoc} + */ + public function addFrame(FrameInterface $fragment) { + $this->_frames->push($fragment); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getOpcode() { + if (count($this->_frames) == 0) { + throw new \UnderflowException('No frames have been added to this message'); + } + + return $this->_frames->bottom()->getOpcode(); + } + + /** + * {@inheritdoc} + */ + public function getPayloadLength() { + $len = 0; + + foreach ($this->_frames as $frame) { + try { + $len += $frame->getPayloadLength(); + } catch (\UnderflowException $e) { + // Not an error, want the current amount buffered + } + } + + return $len; + } + + /** + * {@inheritdoc} + */ + public function getPayload() { + if (!$this->isCoalesced()) { + throw new \UnderflowException('Message has not been put back together yet'); + } + + return $this->__toString(); + } + + /** + * {@inheritdoc} + */ + public function getContents() { + if (!$this->isCoalesced()) { + throw new \UnderflowException("Message has not been put back together yet"); + } + + $buffer = ''; + + foreach ($this->_frames as $frame) { + $buffer .= $frame->getContents(); + } + + return $buffer; + } + + public function __toString() { + $buffer = ''; + + foreach ($this->_frames as $frame) { + $buffer .= $frame->getPayload(); + } + + return $buffer; + } + + /** + * @return boolean + */ + public function isBinary() { + if ($this->_frames->isEmpty()) { + throw new \UnderflowException('Not enough data has been received to determine if message is binary'); + } + + return Frame::OP_BINARY === $this->_frames->bottom()->getOpcode(); + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php b/assets/php/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php new file mode 100644 index 0000000..07ff4f1 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php @@ -0,0 +1,231 @@ +<?php +namespace Ratchet\RFC6455\Messaging; + +class MessageBuffer { + /** + * @var \Ratchet\RFC6455\Messaging\CloseFrameChecker + */ + private $closeFrameChecker; + + /** + * @var callable + */ + private $exceptionFactory; + + /** + * @var \Ratchet\RFC6455\Messaging\Message + */ + private $messageBuffer; + + /** + * @var \Ratchet\RFC6455\Messaging\Frame + */ + private $frameBuffer; + + /** + * @var callable + */ + private $onMessage; + + /** + * @var callable + */ + private $onControl; + + /** + * @var bool + */ + private $checkForMask; + + function __construct( + CloseFrameChecker $frameChecker, + callable $onMessage, + callable $onControl = null, + $expectMask = true, + $exceptionFactory = null + ) { + $this->closeFrameChecker = $frameChecker; + $this->checkForMask = (bool)$expectMask; + + $this->exceptionFactory ?: $this->exceptionFactory = function($msg) { + return new \UnderflowException($msg); + }; + + $this->onMessage = $onMessage; + $this->onControl = $onControl ?: function() {}; + } + + public function onData($data) { + while (strlen($data) > 0) { + $data = $this->processData($data); + } + } + + /** + * @param string $data + * @return null + */ + private function processData($data) { + $this->messageBuffer ?: $this->messageBuffer = $this->newMessage(); + $this->frameBuffer ?: $this->frameBuffer = $this->newFrame(); + + $this->frameBuffer->addBuffer($data); + if (!$this->frameBuffer->isCoalesced()) { + return ''; + } + + $onMessage = $this->onMessage; + $onControl = $this->onControl; + + $this->frameBuffer = $this->frameCheck($this->frameBuffer); + + $overflow = $this->frameBuffer->extractOverflow(); + $this->frameBuffer->unMaskPayload(); + + $opcode = $this->frameBuffer->getOpcode(); + + if ($opcode > 2) { + $onControl($this->frameBuffer); + + if (Frame::OP_CLOSE === $opcode) { + return ''; + } + } else { + $this->messageBuffer->addFrame($this->frameBuffer); + } + + $this->frameBuffer = null; + + if ($this->messageBuffer->isCoalesced()) { + $msgCheck = $this->checkMessage($this->messageBuffer); + if (true !== $msgCheck) { + $onControl($this->newCloseFrame($msgCheck, 'Ratchet detected an invalid UTF-8 payload')); + } else { + $onMessage($this->messageBuffer); + } + + $this->messageBuffer = null; + } + + return $overflow; + } + + /** + * Check a frame to be added to the current message buffer + * @param \Ratchet\RFC6455\Messaging\FrameInterface|FrameInterface $frame + * @return \Ratchet\RFC6455\Messaging\FrameInterface|FrameInterface + */ + public function frameCheck(FrameInterface $frame) { + if (false !== $frame->getRsv1() || + false !== $frame->getRsv2() || + false !== $frame->getRsv3() + ) { + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid reserve code'); + } + + if ($this->checkForMask && !$frame->isMasked()) { + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an incorrect frame mask'); + } + + $opcode = $frame->getOpcode(); + + if ($opcode > 2) { + if ($frame->getPayloadLength() > 125 || !$frame->isFinal()) { + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected a mismatch between final bit and indicated payload length'); + } + + switch ($opcode) { + case Frame::OP_CLOSE: + $closeCode = 0; + + $bin = $frame->getPayload(); + + if (empty($bin)) { + return $this->newCloseFrame(Frame::CLOSE_NORMAL); + } + + if (strlen($bin) === 1) { + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid close code'); + } + + if (strlen($bin) >= 2) { + list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2))); + } + + $checker = $this->closeFrameChecker; + if (!$checker($closeCode)) { + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid close code'); + } + + if (!$this->checkUtf8(substr($bin, 2))) { + return $this->newCloseFrame(Frame::CLOSE_BAD_PAYLOAD, 'Ratchet detected an invalid UTF-8 payload in the close reason'); + } + + return $frame; + break; + case Frame::OP_PING: + case Frame::OP_PONG: + break; + default: + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected an invalid OP code'); + break; + } + + return $frame; + } + + if (Frame::OP_CONTINUE === $frame->getOpcode() && 0 === count($this->messageBuffer)) { + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected the first frame of a message was a continue'); + } + + if (count($this->messageBuffer) > 0 && Frame::OP_CONTINUE !== $frame->getOpcode()) { + return $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Ratchet detected invalid OP code when expecting continue frame'); + } + + return $frame; + } + + /** + * Determine if a message is valid + * @param \Ratchet\RFC6455\Messaging\MessageInterface + * @return bool|int true if valid - false if incomplete - int of recommended close code + */ + public function checkMessage(MessageInterface $message) { + if (!$message->isBinary()) { + if (!$this->checkUtf8($message->getPayload())) { + return Frame::CLOSE_BAD_PAYLOAD; + } + } + + return true; + } + + private function checkUtf8($string) { + if (extension_loaded('mbstring')) { + return mb_check_encoding($string, 'UTF-8'); + } + + return preg_match('//u', $string); + } + + /** + * @return \Ratchet\RFC6455\Messaging\MessageInterface + */ + public function newMessage() { + return new Message; + } + + /** + * @param string|null $payload + * @param bool|null $final + * @param int|null $opcode + * @return \Ratchet\RFC6455\Messaging\FrameInterface + */ + public function newFrame($payload = null, $final = null, $opcode = null) { + return new Frame($payload, $final, $opcode, $this->exceptionFactory); + } + + public function newCloseFrame($code, $reason = '') { + return $this->newFrame(pack('n', $code) . $reason, true, Frame::OP_CLOSE); + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/src/Messaging/MessageInterface.php b/assets/php/vendor/ratchet/rfc6455/src/Messaging/MessageInterface.php new file mode 100644 index 0000000..fd7212e --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/src/Messaging/MessageInterface.php @@ -0,0 +1,20 @@ +<?php +namespace Ratchet\RFC6455\Messaging; + +interface MessageInterface extends DataInterface, \Traversable, \Countable { + /** + * @param FrameInterface $fragment + * @return MessageInterface + */ + function addFrame(FrameInterface $fragment); + + /** + * @return int + */ + function getOpcode(); + + /** + * @return bool + */ + function isBinary(); +} diff --git a/assets/php/vendor/ratchet/rfc6455/tests/AbResultsTest.php b/assets/php/vendor/ratchet/rfc6455/tests/AbResultsTest.php new file mode 100644 index 0000000..9bc502d --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/AbResultsTest.php @@ -0,0 +1,30 @@ +<?php +namespace Ratchet\RFC6455\Test; + +class AbResultsTest extends \PHPUnit_Framework_TestCase { + private function verifyAutobahnResults($fileName) { + if (!file_exists($fileName)) { + return $this->markTestSkipped('Autobahn TestSuite results not found'); + } + + $resultsJson = file_get_contents($fileName); + $results = json_decode($resultsJson); + $agentName = array_keys(get_object_vars($results))[0]; + + foreach ($results->$agentName as $name => $result) { + if ($result->behavior === "INFORMATIONAL") { + continue; + } + + $this->assertTrue(in_array($result->behavior, ["OK", "NON-STRICT"]), "Autobahn test case " . $name . " in " . $fileName); + } + } + + public function testAutobahnClientResults() { + $this->verifyAutobahnResults(__DIR__ . '/ab/reports/clients/index.json'); + } + + public function testAutobahnServerResults() { + $this->verifyAutobahnResults(__DIR__ . '/ab/reports/servers/index.json'); + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/tests/ab/clientRunner.php b/assets/php/vendor/ratchet/rfc6455/tests/ab/clientRunner.php new file mode 100644 index 0000000..0c5578a --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/ab/clientRunner.php @@ -0,0 +1,228 @@ +<?php +use GuzzleHttp\Psr7\Uri; +use React\Promise\Deferred; +use Ratchet\RFC6455\Messaging\Frame; + +require __DIR__ . '/../bootstrap.php'; + +define('AGENT', 'RatchetRFC/0.0.0'); + +$testServer = "127.0.0.1"; + +$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 echoStreamerFactory($conn) +{ + return new \Ratchet\RFC6455\Messaging\MessageBuffer( + new \Ratchet\RFC6455\Messaging\CloseFrameChecker, + function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($conn) { + /** @var Frame $frame */ + foreach ($msg as $frame) { + $frame->maskPayload(); + } + $conn->write($msg->getContents()); + }, + function (\Ratchet\RFC6455\Messaging\FrameInterface $frame) use ($conn) { + switch ($frame->getOpcode()) { + case Frame::OP_PING: + return $conn->write((new Frame($frame->getPayload(), true, Frame::OP_PONG))->maskPayload()->getContents()); + break; + case Frame::OP_CLOSE: + return $conn->end((new Frame($frame->getPayload(), true, Frame::OP_CLOSE))->maskPayload()->getContents()); + break; + } + }, + false + ); +} + +function getTestCases() { + global $factory; + global $testServer; + + $deferred = new Deferred(); + + $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) { + $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator(); + $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001/getCaseCount')); + + $rawResponse = ""; + $response = null; + + /** @var \Ratchet\RFC6455\Messaging\Streaming\MessageBuffer $ms */ + $ms = null; + + $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) { + 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($cnRequest, $response)) { + $stream->end(); + $deferred->reject(); + } else { + $ms = new \Ratchet\RFC6455\Messaging\MessageBuffer( + new \Ratchet\RFC6455\Messaging\CloseFrameChecker, + function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($deferred, $stream) { + $deferred->resolve($msg->getPayload()); + $stream->close(); + }, + null, + false + ); + } + } + } + + // feed the message streamer + if ($ms) { + $ms->onData($data); + } + }); + + $stream->write(\GuzzleHttp\Psr7\str($cnRequest)); + }); + + return $deferred->promise(); +} + +function runTest($case) +{ + global $factory; + global $testServer; + + $casePath = "/runCase?case={$case}&agent=" . AGENT; + + $deferred = new Deferred(); + + $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred, $casePath, $case) { + $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator(); + $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $casePath)); + + $rawResponse = ""; + $response = null; + + $ms = null; + + $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) { + 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($cnRequest, $response)) { + $stream->end(); + $deferred->reject(); + } else { + $ms = echoStreamerFactory($stream); + } + } + } + + // feed the message streamer + if ($ms) { + $ms->onData($data); + } + }); + + $stream->on('close', function () use ($deferred) { + $deferred->resolve(); + }); + + $stream->write(\GuzzleHttp\Psr7\str($cnRequest)); + }); + + return $deferred->promise(); +} + +function createReport() { + global $factory; + global $testServer; + + $deferred = new Deferred(); + + $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) { + $reportPath = "/updateReports?agent=" . AGENT . "&shutdownOnComplete=true"; + $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator(); + $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $reportPath)); + + $rawResponse = ""; + $response = null; + + /** @var \Ratchet\RFC6455\Messaging\MessageBuffer $ms */ + $ms = null; + + $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) { + 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($cnRequest, $response)) { + $stream->end(); + $deferred->reject(); + } else { + $ms = new \Ratchet\RFC6455\Messaging\MessageBuffer( + new \Ratchet\RFC6455\Messaging\CloseFrameChecker, + function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($deferred, $stream) { + $deferred->resolve($msg->getPayload()); + $stream->close(); + }, + null, + false + ); + } + } + } + + // feed the message streamer + if ($ms) { + $ms->onData($data); + } + }); + + $stream->write(\GuzzleHttp\Psr7\str($cnRequest)); + }); + + return $deferred->promise(); +} + + +$testPromises = []; + +getTestCases()->then(function ($count) use ($loop) { + $allDeferred = new Deferred(); + + $runNextCase = function () use (&$i, &$runNextCase, $count, $allDeferred) { + $i++; + if ($i > $count) { + $allDeferred->resolve(); + return; + } + runTest($i)->then($runNextCase); + }; + + $i = 0; + $runNextCase(); + + $allDeferred->promise()->then(function () { + createReport(); + }); +}); + +$loop->run(); diff --git a/assets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingclient.json b/assets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingclient.json new file mode 100644 index 0000000..d2fd0d0 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingclient.json @@ -0,0 +1,14 @@ +{ + "options": { + "failByDrop": false + } + , "outdir": "./reports/servers" + , "servers": [{ + "agent": "RatchetRFC/0.1.0" + , "url": "ws://localhost:9001" + , "options": {"version": 18} + }] + , "cases": ["*"] + , "exclude-cases": ["6.4.*", "12.*","13.*"] + , "exclude-agent-cases": {} +} diff --git a/assets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingserver.json b/assets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingserver.json new file mode 100644 index 0000000..0422560 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/ab/fuzzingserver.json @@ -0,0 +1,10 @@ +{ + "url": "ws://127.0.0.1:9001" + , "options": { + "failByDrop": false + } + , "outdir": "./reports/clients" + , "cases": ["*"] + , "exclude-cases": ["6.4.*", "12.*", "13.*"] + , "exclude-agent-cases": {} +} diff --git a/assets/php/vendor/ratchet/rfc6455/tests/ab/run_ab_tests.sh b/assets/php/vendor/ratchet/rfc6455/tests/ab/run_ab_tests.sh new file mode 100644 index 0000000..8fa9ced --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/ab/run_ab_tests.sh @@ -0,0 +1,11 @@ +cd tests/ab + +wstest -m fuzzingserver -s fuzzingserver.json & +sleep 5 +php clientRunner.php + +sleep 2 + +php startServer.php & +sleep 3 +wstest -m fuzzingclient -s fuzzingclient.json diff --git a/assets/php/vendor/ratchet/rfc6455/tests/ab/startServer.php b/assets/php/vendor/ratchet/rfc6455/tests/ab/startServer.php new file mode 100644 index 0000000..b256ec2 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/ab/startServer.php @@ -0,0 +1,55 @@ +<?php +use Ratchet\RFC6455\Messaging\MessageInterface; +use Ratchet\RFC6455\Messaging\FrameInterface; +use Ratchet\RFC6455\Messaging\Frame; + +require_once __DIR__ . "/../bootstrap.php"; + +$loop = \React\EventLoop\Factory::create(); + +$socket = new \React\Socket\Server($loop); +$server = new \React\Http\Server($socket); + +$closeFrameChecker = new \Ratchet\RFC6455\Messaging\CloseFrameChecker; +$negotiator = new \Ratchet\RFC6455\Handshake\ServerNegotiator(new \Ratchet\RFC6455\Handshake\RequestVerifier); + +$uException = new \UnderflowException; + +$server->on('request', function (\React\Http\Request $request, \React\Http\Response $response) use ($negotiator, $closeFrameChecker, $uException) { + $psrRequest = new \GuzzleHttp\Psr7\Request($request->getMethod(), $request->getPath(), $request->getHeaders()); + + $negotiatorResponse = $negotiator->handshake($psrRequest); + + $response->writeHead( + $negotiatorResponse->getStatusCode(), + array_merge( + $negotiatorResponse->getHeaders(), + ["Content-Length" => "0"] + ) + ); + + if ($negotiatorResponse->getStatusCode() !== 101) { + $response->end(); + return; + } + + $parser = new \Ratchet\RFC6455\Messaging\MessageBuffer($closeFrameChecker, function(MessageInterface $message) use ($response) { + $response->write($message->getContents()); + }, function(FrameInterface $frame) use ($response, &$parser) { + switch ($frame->getOpCode()) { + case Frame::OP_CLOSE: + $response->end($frame->getContents()); + break; + case Frame::OP_PING: + $response->write($parser->newFrame($frame->getPayload(), true, Frame::OP_PONG)->getContents()); + break; + } + }, true, function() use ($uException) { + return $uException; + }); + + $request->on('data', [$parser, 'onData']); +}); + +$socket->listen(9001, '0.0.0.0'); +$loop->run(); diff --git a/assets/php/vendor/ratchet/rfc6455/tests/bootstrap.php b/assets/php/vendor/ratchet/rfc6455/tests/bootstrap.php new file mode 100644 index 0000000..511b041 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/bootstrap.php @@ -0,0 +1,19 @@ +<?php + +/** + * Find the auto loader file + */ +$files = [ + __DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../vendor/autoload.php', + __DIR__ . '/../../../vendor/autoload.php', + __DIR__ . '/../../../../vendor/autoload.php', +]; + +foreach ($files as $file) { + if (file_exists($file)) { + $loader = require $file; + $loader->addPsr4('Ratchet\\RFC6455\\Test\\', __DIR__); + break; + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/RequestVerifierTest.php b/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/RequestVerifierTest.php new file mode 100644 index 0000000..239de33 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/RequestVerifierTest.php @@ -0,0 +1,177 @@ +<?php +namespace Ratchet\RFC6455\Test\Unit\Handshake; +use Ratchet\RFC6455\Handshake\RequestVerifier; + +/** + * @covers Ratchet\RFC6455\Handshake\RequestVerifier + */ +class RequestVerifierTest extends \PHPUnit_Framework_TestCase { + /** + * @var RequestVerifier + */ + protected $_v; + + public function setUp() { + $this->_v = new RequestVerifier(); + } + + public static function methodProvider() { + return array( + array(true, 'GET'), + array(true, 'get'), + array(true, 'Get'), + array(false, 'POST'), + array(false, 'DELETE'), + array(false, 'PUT'), + array(false, 'PATCH') + ); + } + /** + * @dataProvider methodProvider + */ + public function testMethodMustBeGet($result, $in) { + $this->assertEquals($result, $this->_v->verifyMethod($in)); + } + + public static function httpVersionProvider() { + return array( + array(true, 1.1), + array(true, '1.1'), + array(true, 1.2), + array(true, '1.2'), + array(true, 2), + array(true, '2'), + array(true, '2.0'), + array(false, '1.0'), + array(false, 1), + array(false, '0.9'), + array(false, ''), + array(false, 'hello') + ); + } + + /** + * @dataProvider httpVersionProvider + */ + public function testHttpVersionIsAtLeast1Point1($expected, $in) { + $this->assertEquals($expected, $this->_v->verifyHTTPVersion($in)); + } + + public static function uRIProvider() { + return array( + array(true, '/chat'), + array(true, '/hello/world?key=val'), + array(false, '/chat#bad'), + array(false, 'nope'), + array(false, '/ ಠ_ಠ '), + array(false, '/✖') + ); + } + + /** + * @dataProvider URIProvider + */ + public function testRequestUri($expected, $in) { + $this->assertEquals($expected, $this->_v->verifyRequestURI($in)); + } + + public static function hostProvider() { + return array( + array(true, ['server.example.com']), + array(false, []) + ); + } + + /** + * @dataProvider HostProvider + */ + public function testVerifyHostIsSet($expected, $in) { + $this->assertEquals($expected, $this->_v->verifyHost($in)); + } + + public static function upgradeProvider() { + return array( + array(true, ['websocket']), + array(true, ['Websocket']), + array(true, ['webSocket']), + array(false, []), + array(false, ['']) + ); + } + + /** + * @dataProvider upgradeProvider + */ + public function testVerifyUpgradeIsWebSocket($expected, $val) { + $this->assertEquals($expected, $this->_v->verifyUpgradeRequest($val)); + } + + public static function connectionProvider() { + return array( + array(true, ['Upgrade']), + array(true, ['upgrade']), + array(true, ['keep-alive', 'Upgrade']), + array(true, ['Upgrade', 'keep-alive']), + array(true, ['keep-alive', 'Upgrade', 'something']), + // as seen in Firefox 47.0.1 - see https://github.com/ratchetphp/RFC6455/issues/14 + array(true, ['keep-alive, Upgrade']), + array(true, ['Upgrade, keep-alive']), + array(true, ['keep-alive, Upgrade, something']), + array(true, ['keep-alive, Upgrade', 'something']), + array(false, ['']), + array(false, []) + ); + } + + /** + * @dataProvider connectionProvider + */ + public function testConnectionHeaderVerification($expected, $val) { + $this->assertEquals($expected, $this->_v->verifyConnection($val)); + } + + public static function keyProvider() { + return array( + array(true, ['hkfa1L7uwN6DCo4IS3iWAw==']), + array(true, ['765vVoQpKSGJwPzJIMM2GA==']), + array(true, ['AQIDBAUGBwgJCgsMDQ4PEC==']), + array(true, ['axa2B/Yz2CdpfQAY2Q5P7w==']), + array(false, [0]), + array(false, ['Hello World']), + array(false, ['1234567890123456']), + array(false, ['123456789012345678901234']), + array(true, [base64_encode('UTF8allthngs+✓')]), + array(true, ['dGhlIHNhbXBsZSBub25jZQ==']), + array(false, []), + array(false, ['dGhlIHNhbXBsZSBub25jZQ==', 'Some other value']), + array(false, ['Some other value', 'dGhlIHNhbXBsZSBub25jZQ==']) + ); + } + + /** + * @dataProvider keyProvider + */ + public function testKeyIsBase64Encoded16BitNonce($expected, $val) { + $this->assertEquals($expected, $this->_v->verifyKey($val)); + } + + public static function versionProvider() { + return array( + array(true, [13]), + array(true, ['13']), + array(false, [12]), + array(false, [14]), + array(false, ['14']), + array(false, ['hi']), + array(false, ['']), + array(false, []) + ); + } + + /** + * @dataProvider versionProvider + */ + public function testVersionEquals13($expected, $in) { + $this->assertEquals($expected, $this->_v->verifyVersion($in)); + } +}
\ No newline at end of file diff --git a/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ResponseVerifierTest.php b/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ResponseVerifierTest.php new file mode 100644 index 0000000..312930e --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ResponseVerifierTest.php @@ -0,0 +1,34 @@ +<?php +namespace Ratchet\RFC6455\Test\Unit\Handshake; +use Ratchet\RFC6455\Handshake\ResponseVerifier; + +/** + * @covers Ratchet\RFC6455\Handshake\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)); + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ServerNegotiatorTest.php b/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ServerNegotiatorTest.php new file mode 100644 index 0000000..9c9aa8d --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/unit/Handshake/ServerNegotiatorTest.php @@ -0,0 +1,175 @@ +<?php + +namespace Ratchet\RFC6455\Test\Unit\Handshake; + +use Ratchet\RFC6455\Handshake\RequestVerifier; +use Ratchet\RFC6455\Handshake\ServerNegotiator; + +class ServerNegotiatorTest extends \PHPUnit_Framework_TestCase +{ + public function testNoUpgradeRequested() { + $negotiator = new ServerNegotiator(new RequestVerifier()); + + $requestText = 'GET / HTTP/1.1 +Host: 127.0.0.1:6789 +Connection: keep-alive +Pragma: no-cache +Cache-Control: no-cache +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Accept-Encoding: gzip, deflate, sdch, br +Accept-Language: en-US,en;q=0.8'; + + $request = \GuzzleHttp\Psr7\parse_request($requestText); + + $response = $negotiator->handshake($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertEquals(426, $response->getStatusCode()); + $this->assertEquals('Upgrade header MUST be provided', $response->getReasonPhrase()); + $this->assertEquals('Upgrade', $response->getHeaderLine('Connection')); + $this->assertEquals('websocket', $response->getHeaderLine('Upgrade')); + $this->assertEquals('13', $response->getHeaderLine('Sec-WebSocket-Version')); + } + + public function testNoConnectionUpgradeRequested() { + $negotiator = new ServerNegotiator(new RequestVerifier()); + + $requestText = 'GET / HTTP/1.1 +Host: 127.0.0.1:6789 +Connection: keep-alive +Pragma: no-cache +Cache-Control: no-cache +Upgrade: websocket +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Accept-Encoding: gzip, deflate, sdch, br +Accept-Language: en-US,en;q=0.8'; + + $request = \GuzzleHttp\Psr7\parse_request($requestText); + + $response = $negotiator->handshake($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Connection Upgrade MUST be requested', $response->getReasonPhrase()); + } + + public function testInvalidSecWebsocketKey() { + $negotiator = new ServerNegotiator(new RequestVerifier()); + + $requestText = 'GET / HTTP/1.1 +Host: 127.0.0.1:6789 +Connection: Upgrade +Pragma: no-cache +Cache-Control: no-cache +Upgrade: websocket +Sec-WebSocket-Key: 12345 +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Accept-Encoding: gzip, deflate, sdch, br +Accept-Language: en-US,en;q=0.8'; + + $request = \GuzzleHttp\Psr7\parse_request($requestText); + + $response = $negotiator->handshake($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Invalid Sec-WebSocket-Key', $response->getReasonPhrase()); + } + + public function testInvalidSecWebsocketVersion() { + $negotiator = new ServerNegotiator(new RequestVerifier()); + + $requestText = 'GET / HTTP/1.1 +Host: 127.0.0.1:6789 +Connection: Upgrade +Pragma: no-cache +Cache-Control: no-cache +Upgrade: websocket +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Accept-Encoding: gzip, deflate, sdch, br +Accept-Language: en-US,en;q=0.8'; + + $request = \GuzzleHttp\Psr7\parse_request($requestText); + + $response = $negotiator->handshake($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertEquals(426, $response->getStatusCode()); + $this->assertEquals('Upgrade Required', $response->getReasonPhrase()); + $this->assertEquals('Upgrade', $response->getHeaderLine('Connection')); + $this->assertEquals('websocket', $response->getHeaderLine('Upgrade')); + $this->assertEquals('13', $response->getHeaderLine('Sec-WebSocket-Version')); + } + + public function testBadSubprotocolResponse() { + $negotiator = new ServerNegotiator(new RequestVerifier()); + $negotiator->setStrictSubProtocolCheck(true); + $negotiator->setSupportedSubProtocols([]); + + $requestText = 'GET / HTTP/1.1 +Host: 127.0.0.1:6789 +Connection: Upgrade +Pragma: no-cache +Cache-Control: no-cache +Upgrade: websocket +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Sec-WebSocket-Version: 13 +Sec-WebSocket-Protocol: someprotocol +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Accept-Encoding: gzip, deflate, sdch, br +Accept-Language: en-US,en;q=0.8'; + + $request = \GuzzleHttp\Psr7\parse_request($requestText); + + $response = $negotiator->handshake($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertEquals(426, $response->getStatusCode()); + $this->assertEquals('No Sec-WebSocket-Protocols requested supported', $response->getReasonPhrase()); + $this->assertEquals('Upgrade', $response->getHeaderLine('Connection')); + $this->assertEquals('websocket', $response->getHeaderLine('Upgrade')); + $this->assertEquals('13', $response->getHeaderLine('Sec-WebSocket-Version')); + } + + public function testNonStrictSubprotocolDoesNotIncludeHeaderWhenNoneAgreedOn() { + $negotiator = new ServerNegotiator(new RequestVerifier()); + $negotiator->setStrictSubProtocolCheck(false); + $negotiator->setSupportedSubProtocols(['someproto']); + + $requestText = 'GET / HTTP/1.1 +Host: 127.0.0.1:6789 +Connection: Upgrade +Pragma: no-cache +Cache-Control: no-cache +Upgrade: websocket +Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== +Sec-WebSocket-Version: 13 +Sec-WebSocket-Protocol: someotherproto +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Accept-Encoding: gzip, deflate, sdch, br +Accept-Language: en-US,en;q=0.8'; + + $request = \GuzzleHttp\Psr7\parse_request($requestText); + + $response = $negotiator->handshake($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertEquals(101, $response->getStatusCode()); + $this->assertEquals('Upgrade', $response->getHeaderLine('Connection')); + $this->assertEquals('websocket', $response->getHeaderLine('Upgrade')); + $this->assertFalse($response->hasHeader('Sec-WebSocket-Protocol')); + } +}
\ No newline at end of file diff --git a/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/FrameTest.php b/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/FrameTest.php new file mode 100644 index 0000000..b73f600 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/FrameTest.php @@ -0,0 +1,501 @@ +<?php +namespace Ratchet\RFC6455\Test\Unit\Messaging; +use Ratchet\RFC6455\Messaging\Frame; + +/** + * @covers Ratchet\RFC6455\Messaging\Frame + * @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. + */ +class FrameTest extends \PHPUnit_Framework_TestCase { + protected $_firstByteFinText = '10000001'; + + protected $_secondByteMaskedSPL = '11111101'; + + /** @var Frame */ + protected $_frame; + + protected $_packer; + + public function setUp() { + $this->_frame = new Frame; + } + + /** + * Encode the fake binary string to send over the wire + * @param string of 1's and 0's + * @return string + */ + public static function encode($in) { + if (strlen($in) > 8) { + $out = ''; + while (strlen($in) >= 8) { + $out .= static::encode(substr($in, 0, 8)); + $in = substr($in, 8); + } + return $out; + } + return chr(bindec($in)); + } + + /** + * This is a data provider + * param string The UTF8 message + * param string The WebSocket framed message, then base64_encoded + */ + public static function UnframeMessageProvider() { + return array( + array('Hello World!', 'gYydAIfa1WXrtvIg0LXvbOP7'), + array('!@#$%^&*()-=_+[]{}\|/.,<>`~', 'gZv+h96r38f9j9vZ+IHWrvOWoayF9oX6gtfRqfKXwOeg'), + array('ಠ_ಠ', 'gYfnSpu5B/g75gf4Ow=='), + array( + "The quick brown fox jumps over the lazy dog. All work and no play makes Chris a dull boy. I'm trying to get past 128 characters for a unit test here...", + 'gf4Amahb14P8M7Kj2S6+4MN7tfHHLLmjzjSvo8IuuvPbe7j1zSn398A+9+/JIa6jzDSwrYh7lu/Ee6Ds2jD34sY/9+3He6fvySL37skwsvCIGL/xwSj34og/ou/Ee7Xs0XX3o+F8uqPcKa7qxjz398d7sObce6fi2y/3sppj9+DAOqXiyy+y8dt7sezae7aj3TW+94gvsvDce7/m2j75rYY=' + ) + ); + } + + public static function underflowProvider() { + return array( + array('isFinal', ''), + array('getRsv1', ''), + array('getRsv2', ''), + array('getRsv3', ''), + array('getOpcode', ''), + array('isMasked', '10000001'), + array('getPayloadLength', '10000001'), + array('getPayloadLength', '1000000111111110'), + array('getMaskingKey', '1000000110000111'), + array('getPayload', '100000011000000100011100101010101001100111110100') + ); + } + + /** + * @dataProvider underflowProvider + * + * @covers Ratchet\RFC6455\Messaging\Frame::isFinal + * @covers Ratchet\RFC6455\Messaging\Frame::getRsv1 + * @covers Ratchet\RFC6455\Messaging\Frame::getRsv2 + * @covers Ratchet\RFC6455\Messaging\Frame::getRsv3 + * @covers Ratchet\RFC6455\Messaging\Frame::getOpcode + * @covers Ratchet\RFC6455\Messaging\Frame::isMasked + * @covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength + * @covers Ratchet\RFC6455\Messaging\Frame::getMaskingKey + * @covers Ratchet\RFC6455\Messaging\Frame::getPayload + */ + public function testUnderflowExceptionFromAllTheMethodsMimickingBuffering($method, $bin) { + $this->setExpectedException('\UnderflowException'); + if (!empty($bin)) { + $this->_frame->addBuffer(static::encode($bin)); + } + call_user_func(array($this->_frame, $method)); + } + + /** + * A data provider for testing the first byte of a WebSocket frame + * param bool Given, is the byte indicate this is the final frame + * param int Given, what is the expected opcode + * param string of 0|1 Each character represents a bit in the byte + */ + public static function firstByteProvider() { + return array( + array(false, false, false, true, 8, '00011000'), + array(true, false, true, false, 10, '10101010'), + array(false, false, false, false, 15, '00001111'), + array(true, false, false, false, 1, '10000001'), + array(true, true, true, true, 15, '11111111'), + array(true, true, false, false, 7, '11000111') + ); + } + + /** + * @dataProvider firstByteProvider + * covers Ratchet\RFC6455\Messaging\Frame::isFinal + */ + public function testFinCodeFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) { + $this->_frame->addBuffer(static::encode($bin)); + $this->assertEquals($fin, $this->_frame->isFinal()); + } + + /** + * @dataProvider firstByteProvider + * covers Ratchet\RFC6455\Messaging\Frame::getRsv1 + * covers Ratchet\RFC6455\Messaging\Frame::getRsv2 + * covers Ratchet\RFC6455\Messaging\Frame::getRsv3 + */ + public function testGetRsvFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) { + $this->_frame->addBuffer(static::encode($bin)); + $this->assertEquals($rsv1, $this->_frame->getRsv1()); + $this->assertEquals($rsv2, $this->_frame->getRsv2()); + $this->assertEquals($rsv3, $this->_frame->getRsv3()); + } + + /** + * @dataProvider firstByteProvider + * covers Ratchet\RFC6455\Messaging\Frame::getOpcode + */ + public function testOpcodeFromBits($fin, $rsv1, $rsv2, $rsv3, $opcode, $bin) { + $this->_frame->addBuffer(static::encode($bin)); + $this->assertEquals($opcode, $this->_frame->getOpcode()); + } + + /** + * @dataProvider UnframeMessageProvider + * covers Ratchet\RFC6455\Messaging\Frame::isFinal + */ + public function testFinCodeFromFullMessage($msg, $encoded) { + $this->_frame->addBuffer(base64_decode($encoded)); + $this->assertTrue($this->_frame->isFinal()); + } + + /** + * @dataProvider UnframeMessageProvider + * covers Ratchet\RFC6455\Messaging\Frame::getOpcode + */ + public function testOpcodeFromFullMessage($msg, $encoded) { + $this->_frame->addBuffer(base64_decode($encoded)); + $this->assertEquals(1, $this->_frame->getOpcode()); + } + + public static function payloadLengthDescriptionProvider() { + return array( + array(7, '01110101'), + array(7, '01111101'), + array(23, '01111110'), + array(71, '01111111'), + array(7, '00000000'), // Should this throw an exception? Can a payload be empty? + array(7, '00000001') + ); + } + + /** + * @dataProvider payloadLengthDescriptionProvider + * covers Ratchet\RFC6455\Messaging\Frame::addBuffer + * covers Ratchet\RFC6455\Messaging\Frame::getFirstPayloadVal + */ + public function testFirstPayloadDesignationValue($bits, $bin) { + $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); + $this->_frame->addBuffer(static::encode($bin)); + $ref = new \ReflectionClass($this->_frame); + $cb = $ref->getMethod('getFirstPayloadVal'); + $cb->setAccessible(true); + $this->assertEquals(bindec($bin), $cb->invoke($this->_frame)); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::getFirstPayloadVal + */ + public function testFirstPayloadValUnderflow() { + $ref = new \ReflectionClass($this->_frame); + $cb = $ref->getMethod('getFirstPayloadVal'); + $cb->setAccessible(true); + $this->setExpectedException('UnderflowException'); + $cb->invoke($this->_frame); + } + + /** + * @dataProvider payloadLengthDescriptionProvider + * covers Ratchet\RFC6455\Messaging\Frame::getNumPayloadBits + */ + public function testDetermineHowManyBitsAreUsedToDescribePayload($expected_bits, $bin) { + $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); + $this->_frame->addBuffer(static::encode($bin)); + $ref = new \ReflectionClass($this->_frame); + $cb = $ref->getMethod('getNumPayloadBits'); + $cb->setAccessible(true); + $this->assertEquals($expected_bits, $cb->invoke($this->_frame)); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::getNumPayloadBits + */ + public function testgetNumPayloadBitsUnderflow() { + $ref = new \ReflectionClass($this->_frame); + $cb = $ref->getMethod('getNumPayloadBits'); + $cb->setAccessible(true); + $this->setExpectedException('UnderflowException'); + $cb->invoke($this->_frame); + } + + public function secondByteProvider() { + return array( + array(true, 1, '10000001'), + array(false, 1, '00000001'), + array(true, 125, $this->_secondByteMaskedSPL) + ); + } + /** + * @dataProvider secondByteProvider + * covers Ratchet\RFC6455\Messaging\Frame::isMasked + */ + public function testIsMaskedReturnsExpectedValue($masked, $payload_length, $bin) { + $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); + $this->_frame->addBuffer(static::encode($bin)); + $this->assertEquals($masked, $this->_frame->isMasked()); + } + + /** + * @dataProvider UnframeMessageProvider + * covers Ratchet\RFC6455\Messaging\Frame::isMasked + */ + public function testIsMaskedFromFullMessage($msg, $encoded) { + $this->_frame->addBuffer(base64_decode($encoded)); + $this->assertTrue($this->_frame->isMasked()); + } + + /** + * @dataProvider secondByteProvider + * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength + */ + public function testGetPayloadLengthWhenOnlyFirstFrameIsUsed($masked, $payload_length, $bin) { + $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); + $this->_frame->addBuffer(static::encode($bin)); + $this->assertEquals($payload_length, $this->_frame->getPayloadLength()); + } + + /** + * @dataProvider UnframeMessageProvider + * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength + * @todo Not yet testing when second additional payload length descriptor + */ + public function testGetPayloadLengthFromFullMessage($msg, $encoded) { + $this->_frame->addBuffer(base64_decode($encoded)); + $this->assertEquals(strlen($msg), $this->_frame->getPayloadLength()); + } + + public function maskingKeyProvider() { + $frame = new Frame; + return array( + array($frame->generateMaskingKey()), + array($frame->generateMaskingKey()), + array($frame->generateMaskingKey()) + ); + } + + /** + * @dataProvider maskingKeyProvider + * covers Ratchet\RFC6455\Messaging\Frame::getMaskingKey + * @todo I I wrote the dataProvider incorrectly, skipping for now + */ + public function testGetMaskingKey($mask) { + $this->_frame->addBuffer(static::encode($this->_firstByteFinText)); + $this->_frame->addBuffer(static::encode($this->_secondByteMaskedSPL)); + $this->_frame->addBuffer($mask); + $this->assertEquals($mask, $this->_frame->getMaskingKey()); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::getMaskingKey + */ + public function testGetMaskingKeyOnUnmaskedPayload() { + $frame = new Frame('Hello World!'); + $this->assertEquals('', $frame->getMaskingKey()); + } + + /** + * @dataProvider UnframeMessageProvider + * covers Ratchet\RFC6455\Messaging\Frame::getPayload + * @todo Move this test to bottom as it requires all methods of the class + */ + public function testUnframeFullMessage($unframed, $base_framed) { + $this->_frame->addBuffer(base64_decode($base_framed)); + $this->assertEquals($unframed, $this->_frame->getPayload()); + } + + public static function messageFragmentProvider() { + return array( + array(false, '', '', '', '', '') + ); + } + + /** + * @dataProvider UnframeMessageProvider + * covers Ratchet\RFC6455\Messaging\Frame::getPayload + */ + public function testCheckPiecingTogetherMessage($msg, $encoded) { + $framed = base64_decode($encoded); + for ($i = 0, $len = strlen($framed);$i < $len; $i++) { + $this->_frame->addBuffer(substr($framed, $i, 1)); + } + $this->assertEquals($msg, $this->_frame->getPayload()); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::__construct + * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength + * covers Ratchet\RFC6455\Messaging\Frame::getPayload + */ + public function testLongCreate() { + $len = 65525; + $pl = $this->generateRandomString($len); + $frame = new Frame($pl, true, Frame::OP_PING); + $this->assertTrue($frame->isFinal()); + $this->assertEquals(Frame::OP_PING, $frame->getOpcode()); + $this->assertFalse($frame->isMasked()); + $this->assertEquals($len, $frame->getPayloadLength()); + $this->assertEquals($pl, $frame->getPayload()); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::__construct + * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength + */ + public function testReallyLongCreate() { + $len = 65575; + $frame = new Frame($this->generateRandomString($len)); + $this->assertEquals($len, $frame->getPayloadLength()); + } + /** + * covers Ratchet\RFC6455\Messaging\Frame::__construct + * covers Ratchet\RFC6455\Messaging\Frame::extractOverflow + */ + public function testExtractOverflow() { + $string1 = $this->generateRandomString(); + $frame1 = new Frame($string1); + $string2 = $this->generateRandomString(); + $frame2 = new Frame($string2); + $cat = new Frame; + $cat->addBuffer($frame1->getContents() . $frame2->getContents()); + $this->assertEquals($frame1->getContents(), $cat->getContents()); + $this->assertEquals($string1, $cat->getPayload()); + $uncat = new Frame; + $uncat->addBuffer($cat->extractOverflow()); + $this->assertEquals($string1, $cat->getPayload()); + $this->assertEquals($string2, $uncat->getPayload()); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::extractOverflow + */ + public function testEmptyExtractOverflow() { + $string = $this->generateRandomString(); + $frame = new Frame($string); + $this->assertEquals($string, $frame->getPayload()); + $this->assertEquals('', $frame->extractOverflow()); + $this->assertEquals($string, $frame->getPayload()); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::getContents + */ + public function testGetContents() { + $msg = 'The quick brown fox jumps over the lazy dog.'; + $frame1 = new Frame($msg); + $frame2 = new Frame($msg); + $frame2->maskPayload(); + $this->assertNotEquals($frame1->getContents(), $frame2->getContents()); + $this->assertEquals(strlen($frame1->getContents()) + 4, strlen($frame2->getContents())); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::maskPayload + */ + public function testMasking() { + $msg = 'The quick brown fox jumps over the lazy dog.'; + $frame = new Frame($msg); + $frame->maskPayload(); + $this->assertTrue($frame->isMasked()); + $this->assertEquals($msg, $frame->getPayload()); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::unMaskPayload + */ + public function testUnMaskPayload() { + $string = $this->generateRandomString(); + $frame = new Frame($string); + $frame->maskPayload()->unMaskPayload(); + $this->assertFalse($frame->isMasked()); + $this->assertEquals($string, $frame->getPayload()); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::generateMaskingKey + */ + public function testGenerateMaskingKey() { + $dupe = false; + $done = array(); + for ($i = 0; $i < 10; $i++) { + $new = $this->_frame->generateMaskingKey(); + if (in_array($new, $done)) { + $dupe = true; + } + $done[] = $new; + } + $this->assertEquals(4, strlen($new)); + $this->assertFalse($dupe); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::maskPayload + */ + public function testGivenMaskIsValid() { + $this->setExpectedException('InvalidArgumentException'); + $this->_frame->maskPayload('hello world'); + } + + /** + * covers Ratchet\RFC6455\Messaging\Frame::maskPayload + */ + public function testGivenMaskIsValidAscii() { + if (!extension_loaded('mbstring')) { + $this->markTestSkipped("mbstring required for this test"); + return; + } + $this->setExpectedException('OutOfBoundsException'); + $this->_frame->maskPayload('x✖'); + } + + protected function generateRandomString($length = 10, $addSpaces = true, $addNumbers = true) { + $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$%&/()=[]{}'; // ยง + $useChars = array(); + for($i = 0; $i < $length; $i++) { + $useChars[] = $characters[mt_rand(0, strlen($characters) - 1)]; + } + if($addSpaces === true) { + array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' '); + } + if($addNumbers === true) { + array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9)); + } + shuffle($useChars); + $randomString = trim(implode('', $useChars)); + $randomString = substr($randomString, 0, $length); + return $randomString; + } + + /** + * There was a frame boundary issue when the first 3 bytes of a frame with a payload greater than + * 126 was added to the frame buffer and then Frame::getPayloadLength was called. It would cause the frame + * to set the payload length to 126 and then not recalculate it once the full length information was available. + * + * This is fixed by setting the defPayLen back to -1 before the underflow exception is thrown. + * + * covers Ratchet\RFC6455\Messaging\Frame::getPayloadLength + * covers Ratchet\RFC6455\Messaging\Frame::extractOverflow + */ + public function testFrameDeliveredOneByteAtATime() { + $startHeader = "\x01\x7e\x01\x00"; // header for a text frame of 256 - non-final + $framePayload = str_repeat("*", 256); + $rawOverflow = "xyz"; + $rawFrame = $startHeader . $framePayload . $rawOverflow; + $frame = new Frame(); + $payloadLen = 256; + for ($i = 0; $i < strlen($rawFrame); $i++) { + $frame->addBuffer($rawFrame[$i]); + try { + // payloadLen will + $payloadLen = $frame->getPayloadLength(); + } catch (\UnderflowException $e) { + if ($i > 2) { // we should get an underflow on 0,1,2 + $this->fail("Underflow exception when the frame length should be available"); + } + } + if ($payloadLen !== 256) { + $this->fail("Payload length of " . $payloadLen . " should have been 256."); + } + } + // make sure the overflow is good + $this->assertEquals($rawOverflow, $frame->extractOverflow()); + } +} diff --git a/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageBufferTest.php b/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageBufferTest.php new file mode 100644 index 0000000..c33ff0c --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageBufferTest.php @@ -0,0 +1,39 @@ +<?php + +namespace Ratchet\RFC6455\Test\Unit\Messaging; + +use Ratchet\RFC6455\Messaging\CloseFrameChecker; +use Ratchet\RFC6455\Messaging\Frame; +use Ratchet\RFC6455\Messaging\Message; +use Ratchet\RFC6455\Messaging\MessageBuffer; + +class MessageBufferTest extends \PHPUnit_Framework_TestCase +{ + /** + * This is to test that MessageBuffer can handle a large receive + * buffer with many many frames without blowing the stack (pre-v0.4 issue) + */ + public function testProcessingLotsOfFramesInASingleChunk() { + $frame = new Frame('a', true, Frame::OP_TEXT); + + $frameRaw = $frame->getContents(); + + $data = str_repeat($frameRaw, 1000); + + $messageCount = 0; + + $messageBuffer = new MessageBuffer( + new CloseFrameChecker(), + function (Message $message) use (&$messageCount) { + $messageCount++; + $this->assertEquals('a', $message->getPayload()); + }, + null, + false + ); + + $messageBuffer->onData($data); + + $this->assertEquals(1000, $messageCount); + } +}
\ No newline at end of file diff --git a/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageTest.php b/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageTest.php new file mode 100644 index 0000000..1f7eab5 --- /dev/null +++ b/assets/php/vendor/ratchet/rfc6455/tests/unit/Messaging/MessageTest.php @@ -0,0 +1,58 @@ +<?php +namespace Ratchet\RFC6455\Test\Unit\Messaging; +use Ratchet\RFC6455\Messaging\Frame; +use Ratchet\RFC6455\Messaging\Message; + +/** + * @covers Ratchet\RFC6455\Messaging\Message + */ +class MessageTest extends \PHPUnit_Framework_TestCase { + /** @var Message */ + protected $message; + + public function setUp() { + $this->message = new Message; + } + + public function testNoFrames() { + $this->assertFalse($this->message->isCoalesced()); + } + + public function testNoFramesOpCode() { + $this->setExpectedException('UnderflowException'); + $this->message->getOpCode(); + } + + public function testFragmentationPayload() { + $a = 'Hello '; + $b = 'World!'; + $f1 = new Frame($a, false); + $f2 = new Frame($b, true, Frame::OP_CONTINUE); + $this->message->addFrame($f1)->addFrame($f2); + $this->assertEquals(strlen($a . $b), $this->message->getPayloadLength()); + $this->assertEquals($a . $b, $this->message->getPayload()); + } + + public function testUnbufferedFragment() { + $this->message->addFrame(new Frame('The quick brow', false)); + $this->setExpectedException('UnderflowException'); + $this->message->getPayload(); + } + + public function testGetOpCode() { + $this->message + ->addFrame(new Frame('The quick brow', false, Frame::OP_TEXT)) + ->addFrame(new Frame('n fox jumps ov', false, Frame::OP_CONTINUE)) + ->addFrame(new Frame('er the lazy dog', true, Frame::OP_CONTINUE)) + ; + $this->assertEquals(Frame::OP_TEXT, $this->message->getOpCode()); + } + + public function testGetUnBufferedPayloadLength() { + $this->message + ->addFrame(new Frame('The quick brow', false, Frame::OP_TEXT)) + ->addFrame(new Frame('n fox jumps ov', false, Frame::OP_CONTINUE)) + ; + $this->assertEquals(28, $this->message->getPayloadLength()); + } +}
\ No newline at end of file |