From 84484f6603ad82c044a97649e0c9a9141b545bf1 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Mon, 21 Nov 2011 10:51:28 -0500 Subject: [PATCH 1/7] Docs Updated details in readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 435d819..f9d0e8e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ #Ratchet -A PHP 5.3 (PSR-0 compliant) application for serving and consuming sockets. +A PHP 5.3 (PSR-0 compliant) component library for serving/consuming sockets and building socket based applications. Build up your application (like Lego!) through simple interfaces using the decorator and command patterns. Re-use your application without changing any of its code just by wrapping it in a different protocol. @@ -15,8 +15,8 @@ Shell access is required and a dedicated (virtual) machine with root access is r To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80, which requires root access. Note that you can not run two applications (Apache and Ratchet) on the same port, thus the requirement for a separate machine. -Cookies from your Apache/Nginx/IIS server will be passed to the socket server, allowing you to identify users. -It's recommended using a database/cache solution to store session data, so it's accessible on both servers. +Cookies from your domain will be passed to the socket server, allowing you to identify users. +It's recommended using a database/cache solution to store session data, so it's accessible on both web and socket servers. A demonstration of this will be posted (eventually). See https://github.com/cboden/socket-demos for some out-of-the-box working demos using Ratchet. From 62962bb27f5f9c9bfb0c19216f51a6eefb816079 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Mon, 21 Nov 2011 11:02:04 -0500 Subject: [PATCH 2/7] Removed legacy code Removed all traces of ObserverInterface Added getRemoteAddress method to socket --- .../Application/ApplicationInterface.php | 2 +- lib/Ratchet/ObserverInterface.php | 41 ------------------- .../Resource/Command/CommandInterface.php | 2 +- lib/Ratchet/Socket.php | 9 ++++ lib/Ratchet/SocketInterface.php | 7 ++++ 5 files changed, 18 insertions(+), 43 deletions(-) delete mode 100644 lib/Ratchet/ObserverInterface.php diff --git a/lib/Ratchet/Application/ApplicationInterface.php b/lib/Ratchet/Application/ApplicationInterface.php index 0666995..00da591 100644 --- a/lib/Ratchet/Application/ApplicationInterface.php +++ b/lib/Ratchet/Application/ApplicationInterface.php @@ -5,7 +5,7 @@ use Ratchet\Resource\Connection; interface ApplicationInterface { /** * Decorator pattern - * @param Ratchet\ObserverInterface Application to wrap in protocol + * @param Ratchet\ApplicationInterface Application to wrap in protocol * @throws UnexpectedValueException */ public function __construct(ApplicationInterface $app = null); diff --git a/lib/Ratchet/ObserverInterface.php b/lib/Ratchet/ObserverInterface.php deleted file mode 100644 index ca87e95..0000000 --- a/lib/Ratchet/ObserverInterface.php +++ /dev/null @@ -1,41 +0,0 @@ -getResource(), $address, $port)) { + throw new Exception; + } + + return $address; + } + public function get_option($level, $optname) { if (false === ($res = @socket_get_option($this->getResource(), $level, $optname))) { throw new Exception; diff --git a/lib/Ratchet/SocketInterface.php b/lib/Ratchet/SocketInterface.php index e95e3a1..3f0a1e1 100644 --- a/lib/Ratchet/SocketInterface.php +++ b/lib/Ratchet/SocketInterface.php @@ -54,6 +54,13 @@ interface SocketInterface { */ function connect($address, $port = 0); + /** + * Get the address the socket connected from + * @return string + * @throws Exception + */ + function getRemoteAddress(); + /** * @param int * @param int From c5597edd557209563f85bdf3e46a4f30bd2043cd Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Mon, 21 Nov 2011 11:13:41 -0500 Subject: [PATCH 3/7] Bug fix Fixed Socket Exception bug, forgot to pass context --- lib/Ratchet/Socket.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/Ratchet/Socket.php b/lib/Ratchet/Socket.php index 0f18a61..ba511b3 100644 --- a/lib/Ratchet/Socket.php +++ b/lib/Ratchet/Socket.php @@ -72,7 +72,7 @@ class Socket implements SocketInterface { public function bind($address, $port = 0) { if (false === @socket_bind($this->getResource(), $address, $port)) { - throw new Exception; + throw new Exception($this); } return $this; @@ -85,7 +85,7 @@ class Socket implements SocketInterface { public function connect($address, $port = 0) { if (false === @socket_connect($this->getResource(), $address, $port)) { - throw new Exception; + throw new Exception($this); } return $this; @@ -94,7 +94,7 @@ class Socket implements SocketInterface { public function getRemoteAddress() { $address = $port = ''; if (false === @socket_getpeername($this->getResource(), $address, $port)) { - throw new Exception; + throw new Exception($this); } return $address; @@ -102,7 +102,7 @@ class Socket implements SocketInterface { public function get_option($level, $optname) { if (false === ($res = @socket_get_option($this->getResource(), $level, $optname))) { - throw new Exception; + throw new Exception($this); } return $res; @@ -110,7 +110,7 @@ class Socket implements SocketInterface { public function listen($backlog = 0) { if (false === @socket_listen($this->getResource(), $backlog)) { - throw new Exception; + throw new Exception($this); } return $this; @@ -159,7 +159,7 @@ class Socket implements SocketInterface { public function set_block() { if (false === @socket_set_block($this->getResource())) { - throw new Exception; + throw new Exception($this); } return $this; @@ -167,7 +167,7 @@ class Socket implements SocketInterface { public function set_nonblock() { if (false === @socket_set_nonblock($this->getResource())) { - throw new Exception; + throw new Exception($this); } return $this; @@ -175,7 +175,7 @@ class Socket implements SocketInterface { public function set_option($level, $optname, $optval) { if (false === @socket_set_option($this->getResource(), $level, $optname, $optval)) { - throw new Exception; + throw new Exception($this); } return $this; @@ -183,7 +183,7 @@ class Socket implements SocketInterface { public function shutdown($how = 2) { if (false === @socket_shutdown($this->getResource(), $how)) { - throw new Exception; + throw new Exception($this); } return $this; @@ -191,7 +191,7 @@ class Socket implements SocketInterface { public function write($buffer, $length = 0) { if (false === ($res = @socket_write($this->getResource(), $buffer, $length))) { - throw new Exception; + throw new Exception($this); } return $res; From cc507e821efcd108fb1d4d178f055e7f218d27b0 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Tue, 22 Nov 2011 11:33:41 -0500 Subject: [PATCH 4/7] Documentation A bunch of API updates Added read() to SocketInterface Return self for fluid interface in Server --- README.md | 2 +- .../Application/ApplicationInterface.php | 17 ++++++++++++----- lib/Ratchet/Application/Server/App.php | 13 +++++++++++-- lib/Ratchet/Application/WebSocket/App.php | 19 +++++++++++++++++++ .../WebSocket/Command/Action/Disconnect.php | 3 +++ .../WebSocket/Command/Action/Ping.php | 3 +++ .../WebSocket/Command/Action/Pong.php | 3 +++ .../Application/WebSocket/Util/HTTP.php | 1 + .../WebSocket/WebSocketAppInterface.php | 1 + lib/Ratchet/Socket.php | 8 ++++++++ lib/Ratchet/SocketInterface.php | 15 +++++++++++++++ 11 files changed, 77 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f9d0e8e..a056e9c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Re-use your application without changing any of its code just by wrapping it in ##WebSockets * Supports the HyBi-10 and Hixie76 protocol versions (at the same time) -* Tested on Chrome 14, Firefox 7, Safari 5, iOS 4.2 +* Tested on Chrome 13 - 15, Firefox 6 - 8, Safari 5, iOS 4.2, iOS 5 ##Requirements diff --git a/lib/Ratchet/Application/ApplicationInterface.php b/lib/Ratchet/Application/ApplicationInterface.php index 00da591..4c0c919 100644 --- a/lib/Ratchet/Application/ApplicationInterface.php +++ b/lib/Ratchet/Application/ApplicationInterface.php @@ -2,42 +2,49 @@ namespace Ratchet\Application; use Ratchet\Resource\Connection; +/** + * This is the interface to build a Ratchet application with + * It impelemtns the decorator and command pattern to build an application stack + */ interface ApplicationInterface { /** * Decorator pattern * @param Ratchet\ApplicationInterface Application to wrap in protocol - * @throws UnexpectedValueException */ public function __construct(ApplicationInterface $app = null); /** * When a new connection is opened it will be passed to this method - * @param SocketInterface The socket/connection that just connected to your application + * @param Ratchet\Resource\Connection The socket/connection that just connected to your application * @return Ratchet\Resource\Command\CommandInterface|null + * @throws Exception */ function onOpen(Connection $conn); /** * Triggered when a client sends data through the socket - * @param SocketInterface The socket/connection that sent the message to your application + * @param Ratchet\Resource\Connection The socket/connection that sent the message to your application * @param string The message received * @return Ratchet\Resource\Command\CommandInterface|null + * @throws Exception */ function onMessage(Connection $from, $msg); /** * This is called before or after a socket is closed (depends on how it's closed). SendMessage to $conn will not result in an error if it has already been closed. - * @param SocketInterface The socket/connection that is closing/closed + * @param Ratchet\Resource\Connection The socket/connection that is closing/closed * @return Ratchet\Resource\Command\CommandInterface|null + * @throws Exception */ function onClose(Connection $conn); /** * If there is an error with one of the sockets, or somewhere in the application where an Exception is thrown, * the Exception is sent back down the stack, handled by the Server and bubbled back up the application through this method - * @param SocketInterface + * @param Ratchet\Resource\Connection * @param \Exception * @return Ratchet\Resource\Command\CommandInterface|null + * @throws Exception */ function onError(Connection $conn, \Exception $e); } \ No newline at end of file diff --git a/lib/Ratchet/Application/Server/App.php b/lib/Ratchet/Application/Server/App.php index cb80669..bd7188c 100644 --- a/lib/Ratchet/Application/Server/App.php +++ b/lib/Ratchet/Application/Server/App.php @@ -21,8 +21,8 @@ class App implements ApplicationInterface { protected $_connections = array(); /** + * The decorated application to send events to * @var Ratchet\Application\ApplicationInterface - * Maybe temporary? */ protected $_app; @@ -41,18 +41,27 @@ class App implements ApplicationInterface { $this->_app = $application; } + /** + * Set the incoming buffer size in bytes + * @param int + * @return App + * @throws InvalidArgumentException If the parameter is less than 1 + */ public function setBufferSize($recv_bytes) { if ((int)$recv_bytes < 1) { throw new \InvalidArgumentException('Invalid number of bytes set, must be more than 0'); } $this->_buffer_size = (int)$recv_bytes; + + return $this; } /* + * Run the server infinitely * @param Ratchet\SocketInterface * @param mixed The address to listen for incoming connections on. "0.0.0.0" to listen from anywhere - * @param int The port to listen to connections on + * @param int The port to listen to connections on (make sure to run as root if < 1000) * @throws Ratchet\Exception * @todo Validate address. Use socket_get_option, if AF_INET must be IP, if AF_UNIX must be path * @todo Consider making the 4kb listener changable diff --git a/lib/Ratchet/Application/WebSocket/App.php b/lib/Ratchet/Application/WebSocket/App.php index 29eb303..7a12c55 100644 --- a/lib/Ratchet/Application/WebSocket/App.php +++ b/lib/Ratchet/Application/WebSocket/App.php @@ -15,6 +15,7 @@ use Ratchet\Application\WebSocket\Util\HTTP; * @todo Make sure this works both ways (client/server) as stack needs to exist on client for framing * @todo Learn about closing the socket. A message has to be sent prior to closing - does the message get sent onClose event or CloseConnection command? * @todo Consider chaning this class to a State Pattern. If a WS App interface is passed use different state for additional methods used + * @todo I think I need to overhaul the architecture of this...more onus should be on the VersionInterfaces in case of changes...let them handle more decisions, not just parsing */ class App implements ApplicationInterface, ConfiguratorInterface { /** @@ -24,6 +25,7 @@ class App implements ApplicationInterface, ConfiguratorInterface { protected $_app; /** + * Creates commands/composites instead of calling several classes manually * @var Ratchet\Resource\Command\Factory */ protected $_factory; @@ -47,6 +49,8 @@ class App implements ApplicationInterface, ConfiguratorInterface { } /** + * Return the desired socket configuration if hosting a WebSocket server + * This method may be removed * @return array */ public static function getDefaultConfig() { @@ -66,6 +70,10 @@ class App implements ApplicationInterface, ConfiguratorInterface { $conn->WebSocket->headers = ''; } + /** + * Do handshake, frame/unframe messages coming/going in stack + * @todo This needs some major refactoring + */ public function onMessage(Connection $from, $msg) { if (true !== $from->WebSocket->handshake) { if (!isset($from->WebSocket->version)) { @@ -136,12 +144,17 @@ class App implements ApplicationInterface, ConfiguratorInterface { return $this->prepareCommand($this->_app->onClose($conn)); } + /** + * @todo Shouldn't I be using prepareCommand() on the return? look into this + */ public function onError(Connection $conn, \Exception $e) { return $this->_app->onError($conn, $e); } /** + * Incomplete, WebSocket protocol allows client to ask to use a sub-protocol, I'm thinking/wanting to somehow implement this in an application decorated class * @param string + * @todo Implement or delete... */ public function setSubProtocol($name) { } @@ -153,6 +166,10 @@ class App implements ApplicationInterface, ConfiguratorInterface { */ protected function prepareCommand(CommandInterface $command = null) { if ($command instanceof SendMessage) { + if (!isset($command->getConnection()->WebSocket->version)) { // Client could close connection before handshake complete or invalid handshake + return $command; + } + $version = $command->getConnection()->WebSocket->version; return $command->setMessage($version->frame($command->getMessage())); } @@ -167,6 +184,7 @@ 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 * @return Version\VersionInterface * @throws UnderFlowException If we think the entire header message hasn't been buffered yet @@ -192,6 +210,7 @@ class App implements ApplicationInterface, ConfiguratorInterface { } /** + * Create and return the instance of a version class * @return Version\VersionInterface */ protected function versionFactory($version) { diff --git a/lib/Ratchet/Application/WebSocket/Command/Action/Disconnect.php b/lib/Ratchet/Application/WebSocket/Command/Action/Disconnect.php index e8035f2..f2e6a8d 100644 --- a/lib/Ratchet/Application/WebSocket/Command/Action/Disconnect.php +++ b/lib/Ratchet/Application/WebSocket/Command/Action/Disconnect.php @@ -3,6 +3,9 @@ namespace Ratchet\Application\WebSocket\Command\Action; use Ratchet\Resource\Command\Action\SendMessage; use Ratchet\Application\ApplicationInterface; +/** + * Not yet implemented/completed + */ class Disconnect extends SendMessage { protected $_code = 1000; diff --git a/lib/Ratchet/Application/WebSocket/Command/Action/Ping.php b/lib/Ratchet/Application/WebSocket/Command/Action/Ping.php index 5fc0737..1644bf3 100644 --- a/lib/Ratchet/Application/WebSocket/Command/Action/Ping.php +++ b/lib/Ratchet/Application/WebSocket/Command/Action/Ping.php @@ -3,6 +3,9 @@ namespace Ratchet\Application\WebSocket\Command\Action; use Ratchet\Resource\Command\ActionTemplate; use Ratchet\Application\ApplicationInterface; +/** + * Not yet implemented/completed + */ class Ping extends ActionTemplate { public function execute(ApplicationInterface $scope = null) { } diff --git a/lib/Ratchet/Application/WebSocket/Command/Action/Pong.php b/lib/Ratchet/Application/WebSocket/Command/Action/Pong.php index e010fc6..fe390e9 100644 --- a/lib/Ratchet/Application/WebSocket/Command/Action/Pong.php +++ b/lib/Ratchet/Application/WebSocket/Command/Action/Pong.php @@ -3,6 +3,9 @@ namespace Ratchet\Application\WebSocket\Command\Action; use Ratchet\Resource\Command\ActionTemplate; use Ratchet\Application\ApplicationInterface; +/** + * Not yet implemented/completed + */ class Pong extends ActionTemplate { public function execute(ApplicationInterface $scope = null) { } diff --git a/lib/Ratchet/Application/WebSocket/Util/HTTP.php b/lib/Ratchet/Application/WebSocket/Util/HTTP.php index fefb70d..57dcc55 100644 --- a/lib/Ratchet/Application/WebSocket/Util/HTTP.php +++ b/lib/Ratchet/Application/WebSocket/Util/HTTP.php @@ -3,6 +3,7 @@ namespace Ratchet\Application\WebSocket\Util; /** * A helper class for handling HTTP requests + * @todo Needs re-write...http_parse_headers is a PECL extension that changes the case to unexpected values */ class HTTP { /** diff --git a/lib/Ratchet/Application/WebSocket/WebSocketAppInterface.php b/lib/Ratchet/Application/WebSocket/WebSocketAppInterface.php index 4b85a49..f78ca6c 100644 --- a/lib/Ratchet/Application/WebSocket/WebSocketAppInterface.php +++ b/lib/Ratchet/Application/WebSocket/WebSocketAppInterface.php @@ -9,6 +9,7 @@ use Ratchet\Application\ApplicationInterface; */ interface WebSocketAppInterface extends ApplicationInterface { /** + * Currently instead of this, I'm setting header in the Connection object passed around...not sure which I like more * @param string */ function setHeaders($headers); diff --git a/lib/Ratchet/Socket.php b/lib/Ratchet/Socket.php index ba511b3..20969b0 100644 --- a/lib/Ratchet/Socket.php +++ b/lib/Ratchet/Socket.php @@ -116,6 +116,14 @@ class Socket implements SocketInterface { return $this; } + public function read($length, $type = PHP_BINARY_READ) { + if (false === ($res = @socket_read($this->getResource(), $length, $type))) { + throw new Exception($this); + } + + return $res; + } + /** * @see http://ca3.php.net/manual/en/function.socket-recv.php * @param string Variable to write data to diff --git a/lib/Ratchet/SocketInterface.php b/lib/Ratchet/SocketInterface.php index 3f0a1e1..8e54361 100644 --- a/lib/Ratchet/SocketInterface.php +++ b/lib/Ratchet/SocketInterface.php @@ -34,6 +34,7 @@ interface SocketInterface { // function accept(); /** + * Bind the socket instance to an address/port * @param string * @param int * @return SocketInterface @@ -47,6 +48,7 @@ interface SocketInterface { function close(); /** + * Initiates a connection to a socket * @param string * @param int * @return SocketInterface @@ -70,12 +72,22 @@ interface SocketInterface { function get_option($level, $optname); /** + * Listen for incoming connections on this socket * @param int * @return SocketInterface * @throws Exception */ function listen($backlog = 0); + /** + * Read a maximum of length bytes from a socket + * @param int Number of bytes to read + * @param int Flags + * @return string Data read from the socket + * @throws Exception + */ + function read($length, $type = PHP_BINARY_READ); + /** * Called when the client sends data to the server through the socket * @param string Variable to write data to @@ -91,12 +103,15 @@ interface SocketInterface { // function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0); /** + * Sets the blocking mode on the socket resource + * Wen an operation (receive, send, connect, accept, etc) is performed after set_block() the script will pause execution until the operation is completed * @return SocketInterface * @throws Exception */ function set_block(); /** + * Sets nonblocking mode for socket resource * @return SocketInterface * @throws Exception */ From edec9aea85b0734a2626e60d6b5bc96ef6654355 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Tue, 22 Nov 2011 15:15:02 -0500 Subject: [PATCH 5/7] Dev Bug Fix Fixed the stupidest development testing bug I've ever left in...wow --- lib/Ratchet/Socket.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ratchet/Socket.php b/lib/Ratchet/Socket.php index 20969b0..a0900f7 100644 --- a/lib/Ratchet/Socket.php +++ b/lib/Ratchet/Socket.php @@ -64,7 +64,7 @@ class Socket implements SocketInterface { $len = strlen($message); do { - $sent = $this->write($message, 4); + $sent = $this->write($message, $len); $len -= $sent; $message = substr($message, $sent); } while ($len > 0); From 4da707b6539e7708fe434236a3580e701e781cd8 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Wed, 23 Nov 2011 10:16:45 -0500 Subject: [PATCH 6/7] Setting non-block on accepted sockets Hopefully fixing a bug --- lib/Ratchet/Application/Server/App.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ratchet/Application/Server/App.php b/lib/Ratchet/Application/Server/App.php index bd7188c..8f62237 100644 --- a/lib/Ratchet/Application/Server/App.php +++ b/lib/Ratchet/Application/Server/App.php @@ -143,6 +143,7 @@ class App implements ApplicationInterface { public function onOpen(Connection $conn) { $new_socket = clone $conn->getSocket(); + $new_socket->set_nonblock(); $new_connection = new Connection($new_socket); $this->_resources[] = $new_connection->getSocket()->getResource(); From d75113ec5e2f6047fbdcfa8b70c579acac488303 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Thu, 24 Nov 2011 20:59:19 -0500 Subject: [PATCH 7/7] 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