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/rfc6455/src/Handshake | |
parent | 286d643180672f20526f3dc3bd19d7b751e2fa97 (diff) |
Initial Commit
Diffstat (limited to 'assets/php/vendor/ratchet/rfc6455/src/Handshake')
5 files changed, 428 insertions, 0 deletions
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; + } +} |