From d75113ec5e2f6047fbdcfa8b70c579acac488303 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Thu, 24 Nov 2011 20:59:19 -0500 Subject: [PATCH] WebSocket versions Allowed user to disable WebSocket versions Change how versions are detected, responsibility is on the concrete version class instead of factory --- lib/Ratchet/Application/WebSocket/App.php | 34 +++++++++++-------- .../Application/WebSocket/Version/Hixie76.php | 4 +++ .../Application/WebSocket/Version/HyBi10.php | 13 +++++++ .../WebSocket/Version/VersionInterface.php | 9 +++++ 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/lib/Ratchet/Application/WebSocket/App.php b/lib/Ratchet/Application/WebSocket/App.php index 7a12c55..46aaba3 100644 --- a/lib/Ratchet/Application/WebSocket/App.php +++ b/lib/Ratchet/Application/WebSocket/App.php @@ -7,6 +7,7 @@ use Ratchet\Resource\Command\Factory; use Ratchet\Resource\Command\CommandInterface; use Ratchet\Resource\Command\Action\SendMessage; use Ratchet\Application\WebSocket\Util\HTTP; +use Ratchet\Application\WebSocket\Version; /** * The adapter to handle WebSocket requests/responses @@ -185,11 +186,10 @@ class App implements ApplicationInterface, ConfiguratorInterface { /** * Detect the WebSocket protocol version a client is using based on the HTTP header request - * @param array of HTTP headers + * @param string HTTP handshake request * @return Version\VersionInterface * @throws UnderFlowException If we think the entire header message hasn't been buffered yet * @throws InvalidArgumentException If we can't understand protocol version request - * @todo Can/will add more versions later, but perhaps a chain of responsibility, ask each version if they want to handle the request */ protected function getVersion($message) { if (false === strstr($message, "\r\n\r\n")) { // This CAN fail with Hixie, depending on the TCP buffer in between @@ -198,27 +198,33 @@ class App implements ApplicationInterface, ConfiguratorInterface { $headers = HTTP::getHeaders($message); - if (isset($headers['Sec-Websocket-Version'])) { // HyBi - if ((int)$headers['Sec-Websocket-Version'] >= 6) { - return $this->versionFactory('HyBi10'); + foreach ($this->_versions as $name => $instance) { + if (null !== $instance) { + if ($instance::isProtocol($headers)) { + return $instance; + } + } else { + $ns = __NAMESPACE__ . "\\Version\\{$name}"; + if ($ns::isProtocol($headers)) { + $this->_version[$name] = new $ns; + return $this->_version[$name]; + } } - } elseif (isset($headers['Sec-Websocket-Key2'])) { // Hixie - return $this->versionFactory('Hixie76'); } throw new \InvalidArgumentException('Could not identify WebSocket protocol'); } /** - * Create and return the instance of a version class - * @return Version\VersionInterface + * Disable a version of the WebSocket protocol *cough*Hixie76*cough* + * @param string The name of the version to disable + * @throws InvalidArgumentException If the given version does not exist */ - protected function versionFactory($version) { - if (null === $this->_versions[$version]) { - $ns = __NAMESPACE__ . "\\Version\\{$version}"; - $this->_version[$version] = new $ns; + public function disableVersion($name) { + if (!array_key_exists($name, $this->_versions)) { + throw new \InvalidArgumentException("Version {$name} not found"); } - return $this->_version[$version]; + unset($this->_versions[$name]); } } \ 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 22574a2..38e94d8 100644 --- a/lib/Ratchet/Application/WebSocket/Version/Hixie76.php +++ b/lib/Ratchet/Application/WebSocket/Version/Hixie76.php @@ -14,6 +14,10 @@ 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']); + } + /** * @param string * @return string diff --git a/lib/Ratchet/Application/WebSocket/Version/HyBi10.php b/lib/Ratchet/Application/WebSocket/Version/HyBi10.php index f6cb4ea..80316b3 100644 --- a/lib/Ratchet/Application/WebSocket/Version/HyBi10.php +++ b/lib/Ratchet/Application/WebSocket/Version/HyBi10.php @@ -10,6 +10,19 @@ use Ratchet\Application\WebSocket\Util\HTTP; class HyBi10 implements VersionInterface { const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; + /** + * @todo When I support later version (that implement extension) change >= 6 to 6 through 10 (or w/e #) + */ + public static function isProtocol(array $headers) { + if (isset($headers['Sec-Websocket-Version'])) { + if ((int)$headers['Sec-Websocket-Version'] >= 6) { + return true; + } + } + + return false; + } + /** * @return array * I kept this as an array and combined in App for future considerations...easier to add a subprotol as a key value than edit a string diff --git a/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php b/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php index 1e34a4f..b6078d9 100644 --- a/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php +++ b/lib/Ratchet/Application/WebSocket/Version/VersionInterface.php @@ -8,6 +8,14 @@ namespace Ratchet\Application\WebSocket\Version; * The current method names suggest you could create a new message/frame to send, which they can not do */ interface VersionInterface { + /** + * Given an HTTP header, determine if this version should handle the protocol + * @param array + * @return bool + * @throws UnderflowException If the protocol thinks the headers are still fragmented + */ + static function isProtocol(array $headers); + /** * Perform the handshake and return the response headers * @param string @@ -28,6 +36,7 @@ interface VersionInterface { /** * @param string * @return string + * @todo Change to use other classes, this will be removed eventually */ function frame($message); } \ No newline at end of file