diff --git a/lib/Ratchet/Application/WebSocket/App.php b/lib/Ratchet/Application/WebSocket/App.php index 362039c..75e7207 100644 --- a/lib/Ratchet/Application/WebSocket/App.php +++ b/lib/Ratchet/Application/WebSocket/App.php @@ -6,7 +6,8 @@ use Ratchet\Resource\Connection; use Ratchet\Resource\Command\Factory; use Ratchet\Resource\Command\CommandInterface; use Ratchet\Resource\Command\Action\SendMessage; -use Ratchet\Application\WebSocket\Util\HTTP; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\RequestFactory; /** * The adapter to handle WebSocket requests/responses @@ -80,12 +81,14 @@ class App implements ApplicationInterface, ConfiguratorInterface { public function onMessage(Connection $from, $msg) { if (true !== $from->WebSocket->handshake) { if (!isset($from->WebSocket->version)) { - try { - $from->WebSocket->headers .= $msg; - $from->WebSocket->version = $this->getVersion($from->WebSocket->headers); - } catch (\UnderflowException $e) { + $from->WebSocket->headers .= $msg; + if (!$this->isMessageComplete($from->WebSocket->headers)) { return; } + + $headers = RequestFactory::fromMessage($from->WebSocket->headers); + $from->WebSocket->version = $this->getVersion($headers); + $from->WebSocket->headers = $headers; } $response = $from->WebSocket->version->handshake($from->WebSocket->headers); @@ -212,12 +215,8 @@ class App implements ApplicationInterface, ConfiguratorInterface { * @throws InvalidArgumentException If we can't understand protocol version request * @todo Verify the first line of the HTTP header as per page 16 of RFC 6455 */ - protected function getVersion($message) { - if (false === strstr($message, "\r\n\r\n")) { // This CAN fail with Hixie, depending on the TCP buffer in between - throw new \UnderflowException; - } - - $headers = HTTP::getHeaders($message); + protected function getVersion(RequestInterface $request) { + $headers = $request->getHeaders(); foreach ($this->_versions as $name => $instance) { if (null !== $instance) { @@ -236,6 +235,15 @@ class App implements ApplicationInterface, ConfiguratorInterface { throw new \InvalidArgumentException('Could not identify WebSocket protocol'); } + /** + * @param string + * @return bool + * @todo Method is flawed, this CAN result in an error with the Hixie protocol + */ + protected function isMessageComplete($message) { + return (boolean)strstr($message, "\r\n\r\n"); + } + /** * Disable a version of the WebSocket protocol *cough*Hixie76*cough* * @param string The name of the version to disable diff --git a/lib/Ratchet/Application/WebSocket/Util/HTTP.php b/lib/Ratchet/Application/WebSocket/Util/HTTP.php deleted file mode 100644 index c8f711f..0000000 --- a/lib/Ratchet/Application/WebSocket/Util/HTTP.php +++ /dev/null @@ -1,56 +0,0 @@ - null - , 'Upgrade' => null - , 'Connection' => null - , 'Sec-Websocket-Key' => null - , 'Origin' => null - , 'Sec-Websocket-Protocol' => null - , 'Sec-Websocket-Version' => null - , 'Sec-Websocket-Origin' => null - ); - } - - /** - * @param string - * @return array - * This is a fallback method for http_parse_headers as not all php installs have the HTTP module present - * @internal - */ - protected static function http_parse_headers($http_message) { - $retVal = array(); - $fields = explode("br", preg_replace("%(<|/\>|>)%", "", nl2br($http_message))); - - foreach ($fields as $field) { - if (preg_match('%^(GET|POST|PUT|DELETE|PATCH)(\s)(.*)%', $field, $matchReq)) { - $retVal["Request Method"] = $matchReq[1]; - $retVal["Request Url"] = $matchReq[3]; - } elseif (preg_match('/([^:]+): (.+)/m', $field, $match) ) { - $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1]))); - if (isset($retVal[$match[1]])) { - $retVal[$match[1]] = array($retVal[$match[1]], $match[2]); - } else { - $retVal[$match[1]] = trim($match[2]); - } - } - } - - return $retVal; - } -} \ No newline at end of file diff --git a/lib/Ratchet/Application/WebSocket/Version/Hixie76.php b/lib/Ratchet/Application/WebSocket/Version/Hixie76.php index 0777701..4540714 100644 --- a/lib/Ratchet/Application/WebSocket/Version/Hixie76.php +++ b/lib/Ratchet/Application/WebSocket/Version/Hixie76.php @@ -14,8 +14,8 @@ namespace Ratchet\Application\WebSocket\Version; * @link http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 */ class Hixie76 implements VersionInterface { - public static function isProtocol(array $headers) { - return isset($headers['Sec-Websocket-Key2']); + public static function isProtocol($headers) { + return isset($headers['Sec-WebSocket-Key2']); } /** diff --git a/lib/Ratchet/Application/WebSocket/Version/HyBi10.php b/lib/Ratchet/Application/WebSocket/Version/HyBi10.php index 39f20ea..ee24fe2 100644 --- a/lib/Ratchet/Application/WebSocket/Version/HyBi10.php +++ b/lib/Ratchet/Application/WebSocket/Version/HyBi10.php @@ -5,9 +5,9 @@ namespace Ratchet\Application\WebSocket\Version; * @todo Note: Even though this is the "legacy" HyBi version, it's using the RFC Message and Frame classes - change if needed */ class HyBi10 extends RFC6455 { - public static function isProtocol(array $headers) { - if (isset($headers['Sec-Websocket-Version'])) { - if ((int)$headers['Sec-Websocket-Version'] >= 6 && (int)$headers['Sec-Websocket-Version'] < 13) { + public static function isProtocol($headers) { + if (isset($headers['Sec-WebSocket-Version'])) { + if ((int)$headers['Sec-WebSocket-Version'] >= 6 && (int)$headers['Sec-WebSocket-Version'] < 13) { return true; } } diff --git a/lib/Ratchet/Application/WebSocket/Version/RFC6455.php b/lib/Ratchet/Application/WebSocket/Version/RFC6455.php index d1c7d83..fbb003f 100644 --- a/lib/Ratchet/Application/WebSocket/Version/RFC6455.php +++ b/lib/Ratchet/Application/WebSocket/Version/RFC6455.php @@ -1,7 +1,7 @@ _verifier = new HandshakeVerifier; } - public static function isProtocol(array $headers) { - if (isset($headers['Sec-Websocket-Version'])) { - if ((int)$headers['Sec-Websocket-Version'] == 13) { + /** + * @todo Change the request to be a Guzzle RequestInterface + */ + public static function isProtocol($headers) { + if (isset($headers['Sec-WebSocket-Version'])) { + if ((int)$headers['Sec-WebSocket-Version'] == 13) { return true; } } @@ -34,10 +37,10 @@ class RFC6455 implements VersionInterface { * @todo Decide what to do on failure...currently throwing an exception and I think socket connection is closed. Should be sending 40x error - but from where? */ public function handshake($message) { - $headers = HTTP::getHeaders($message); - $key = $this->sign($headers['Sec-Websocket-Key']); + $headers = $message->getHeaders(); + $key = $this->sign($headers['Sec-WebSocket-Key']); - if (true !== $this->_verifier->verifyAll($headers)) { + if (true !== $this->_verifier->verifyAll($message)) { throw new \InvalidArgumentException('Invalid HTTP header'); } @@ -45,7 +48,7 @@ class RFC6455 implements VersionInterface { '' => 'HTTP/1.1 101 Switching Protocols' , 'Upgrade' => 'websocket' , 'Connection' => 'Upgrade' - , 'Sec-WebSocket-Accept' => $this->sign($headers['Sec-Websocket-Key']) + , 'Sec-WebSocket-Accept' => $this->sign($headers['Sec-WebSocket-Key']) // , 'Sec-WebSocket-Protocol' => '' ); } diff --git a/lib/Ratchet/Application/WebSocket/Version/RFC6455/HandshakeVerifier.php b/lib/Ratchet/Application/WebSocket/Version/RFC6455/HandshakeVerifier.php index 224e870..331f62f 100644 --- a/lib/Ratchet/Application/WebSocket/Version/RFC6455/HandshakeVerifier.php +++ b/lib/Ratchet/Application/WebSocket/Version/RFC6455/HandshakeVerifier.php @@ -1,5 +1,6 @@ getHeaders(); + $passes = 0; - $passes += (int)$this->verifyMethod($headers['Request Method']); - //$passes += (int)$this->verifyHTTPVersion($headers['???']); // This isn't in the array! - $passes += (int)$this->verifyRequestURI($headers['Request Url']); + $passes += (int)$this->verifyMethod($request->getMethod()); + $passes += (int)$this->verifyHTTPVersion($request->getProtocolVersion()); + $passes += (int)$this->verifyRequestURI($request->getPath()); $passes += (int)$this->verifyHost($headers['Host']); $passes += (int)$this->verifyUpgradeRequest($headers['Upgrade']); $passes += (int)$this->verifyConnection($headers['Connection']); - $passes += (int)$this->verifyKey($headers['Sec-Websocket-Key']); - //$passes += (int)$this->verifyVersion($headers['Sec-Websocket-Version']); // Temporarily breaking functionality + $passes += (int)$this->verifyKey($headers['Sec-WebSocket-Key']); + //$passes += (int)$this->verifyVersion($headers['Sec-WebSocket-Version']); // Temporarily breaking functionality - return (6 === $passes); + return (7 === $passes); } /** diff --git a/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php b/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php index 53ce979..ebc9d8f 100644 --- a/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php +++ b/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php @@ -14,7 +14,7 @@ interface VersionInterface { * @return bool * @throws UnderflowException If the protocol thinks the headers are still fragmented */ - static function isProtocol(array $headers); + static function isProtocol($headers); /** * Perform the handshake and return the response headers @@ -22,6 +22,7 @@ interface VersionInterface { * @return array|string * @throws InvalidArgumentException If the HTTP handshake is mal-formed * @throws UnderflowException If the message hasn't finished buffering (not yet implemented, theoretically will only happen with Hixie version) + * @todo Change param to accept a Guzzle RequestInterface object */ function handshake($message); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 2f02d40..cb1e591 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -5,4 +5,6 @@ $app->register(); $app = new SplClassLoader('Ratchet', dirname(__DIR__) . DIRECTORY_SEPARATOR . 'lib'); - $app->register(); \ No newline at end of file + $app->register(); + + $app = new SplClassLoader('Guzzle', dirname(__DIR__) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'guzzle' . DIRECTORY_SEPARATOR . 'src'); \ No newline at end of file