diff --git a/LICENSE b/LICENSE index 0423176..36193c8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,19 @@ -Copyright (c) 2011 Chris Boden +Copyright (c) 2011-2012 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: +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 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. \ No newline at end of file +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/README.md b/README.md index 570456d..7998e62 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,13 @@ #Ratchet -A PHP 5.3 (PSR-0 compliant) component library for serving sockets and building socket based applications. -Build up your application through simple interfaces using the decorator and command patterns. -Re-use your application without changing any of its code just by combining different components. +A PHP 5.3 (PSR-0) library for serving WebSockets and building socket based applications. +Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components. ##WebSockets -* Supports the RFC6455, HyBi-10, and Hixie76 protocol versions (at the same time) -* Tested on Chrome 18 - 16, Firefox 6 - 8, Safari 5, iOS 4.2, iOS 5 +* Supports the RFC6455, HyBi-10+, and Hixie76 protocol versions (at the same time) +* Tested on Chrome 18 - 16, Firefox 6 - 9, Safari 5, iOS 4.2, iOS 5 ##Requirements @@ -18,7 +17,7 @@ To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80, Note that you can not run two applications (Apache and Ratchet) on the same port, thus the requirement for a separate machine (for now). Cookies from your domain will be passed to the socket server, allowing you to identify users. -Accessing your website's session data in Ratchet requires you to use [Symfony2 HttpFoundation Sessions](http://symfony.com/doc/master/components/http_foundation/sessions.html) on your website. +Accessing your website's session data in Ratchet requires you to use [Symfony2 Sessions](http://symfony.com/doc/master/components/http_foundation/sessions.html) on your website. ### Documentation @@ -32,57 +31,46 @@ See https://github.com/cboden/Ratchet-examples for some out-of-the-box working d ```php _clients = new \SplObjectStorage; + public function __construct() { + $this->clients = new \SplObjectStorage; } public function onOpen(ConnectionInterface $conn) { - $this->_clients->attach($conn); + $this->clients->attach($conn); } public function onMessage(ConnectionInterface $from, $msg) { - $commands = new Cmds; - - foreach ($this->_clients as $client) { + foreach ($this->clients as $client) { if ($from != $client) { - $msg_cmd = new SendMessage($client); - $msg_cmd->setMessage($msg); - - $commands->enqueue($msg_cmd); + $client->send($msg); } } - - return $commands; } public function onClose(ConnectionInterface $conn) { - $this->_clients->detach($conn); + $this->clients->detach($conn); } public function onError(ConnectionInterface $conn, \Exception $e) { - return new CloseConnection($conn); + $conn->close(); } } -// Run the server application through the WebSocket protocol -$server = new IOServerComponent(new WebSocketComponent(new Chat)); -$server->run(8000); + // Run the server application through the WebSocket protocol on port 8000 + $server = IoServer::factory(new WsServer(new Chat), 8000); + $server->run(); ``` # php chat.php \ No newline at end of file diff --git a/composer.json b/composer.json index 40a7dba..1ad9eed 100644 --- a/composer.json +++ b/composer.json @@ -17,11 +17,14 @@ "psr-0": { "Ratchet\\Tests": "tests" , "Ratchet": "src" + , "React": "vendor/cboden/react/src" } } , "require": { "php": ">=5.3.2" - , "guzzle/guzzle": "v2.0.2" + , "guzzle/guzzle": "2.5.*" , "symfony/http-foundation": "2.1.*" + , "react/event-loop": "dev-master" + , "react/socket": "dev-master" } } \ No newline at end of file diff --git a/composer.lock b/composer.lock index 496e1fb..8846011 100644 --- a/composer.lock +++ b/composer.lock @@ -1,14 +1,31 @@ { - "hash": "c4bc28d46c32e18713efab25a77428e6", + "hash": "b7d2ee4e3fd11f2e9c862b6b2ca2372f", "packages": [ { - "package": "doctrine/common", - "version": "2.2.x-dev", - "source-reference": "1e0aa60d109c630d19543d999f12e2852ef8f932" + "package": "evenement/evenement", + "version": "dev-master", + "source-reference": "808e3aaea8d4f908e455b0e047cc1acc46b38d44" }, { "package": "guzzle/guzzle", - "version": "v2.0.2" + "version": "v2.5.0" + }, + { + "package": "react/event-loop", + "version": "dev-master", + "source-reference": "e9850fe37b04a34cb4d6b59861d3a6c680fb1aa1" + }, + { + "package": "react/socket", + "version": "dev-master", + "source-reference": "a2b7d74eef48f9fec44bf727d19b0385101b62a2" + }, + { + "package": "symfony/event-dispatcher", + "version": "dev-master", + "source-reference": "0c1ae4898196f5e96b79028d8d2f35de4b584659", + "alias-pretty-version": "2.1.x-dev", + "alias-version": "2.1.9999999.9999999-dev" }, { "package": "symfony/event-dispatcher", @@ -19,12 +36,13 @@ "package": "symfony/http-foundation", "version": "dev-master", "source-reference": "54c22f4bf8625303503a117dcc68544d3f8ac876", - "alias": "2.1.9999999.9999999-dev" + "alias-pretty-version": "2.1.x-dev", + "alias-version": "2.1.9999999.9999999-dev" }, { - "package": "symfony/validator", + "package": "symfony/http-foundation", "version": "dev-master", - "source-reference": "704f655d060b14475d7bd2a0b6d653c70f88218a" + "source-reference": "54c22f4bf8625303503a117dcc68544d3f8ac876" } ], "packages-dev": null, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e093f23..78b3128 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,7 @@ wrappedConn = $conn; + } + + /** + * @return ConnectionInterface + */ + protected function getConnection() { + return $this->wrappedConn; + } + + public function __set($name, $value) { + $this->wrappedConn->$name = $value; + } + + public function __get($name) { + return $this->wrappedConn->$name; + } + + public function __isset($name) { + return isset($this->wrappedConn->$name); + } + + public function __unset($name) { + unset($this->wrappedConn->$name); + } +} \ No newline at end of file diff --git a/src/Ratchet/Component/Server/IOServerComponent.php b/src/Ratchet/Component/Server/IOServerComponent.php deleted file mode 100644 index 15bb5f5..0000000 --- a/src/Ratchet/Component/Server/IOServerComponent.php +++ /dev/null @@ -1,199 +0,0 @@ -_decorating = $component; - } - - /** - * 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 int The port to listen to connections on (make sure to run as root if < 1000) - * @param mixed The address to listen for incoming connections on. "0.0.0.0" to listen from anywhere - * @param Ratchet\Resource\Socket\SocketInterface - * @throws Ratchet\Exception - */ - public function run($port, $address = '0.0.0.0', SocketInterface $host = null) { - if (null === $host) { - $host = new BSDSocket; - $host->set_option(SOL_SOCKET, SO_REUSEADDR, 1); - } - - $this->_connections[$host->getResource()] = new Connection($host); - $this->_resources[] = $host->getResource(); - - gc_enable(); - set_time_limit(0); - ob_implicit_flush(); - - declare(ticks = 1); - - $host->set_option(SOL_SOCKET, SO_SNDBUF, $this->_buffer_size); - $host->set_nonblock()->bind($address, (int)$port)->listen(); - - do { - $this->loop($host); - } while ($this->_run); - } - - protected function loop(SocketInterface $host) { - $changed = $this->_resources; - - try { - $write = $except = null; - - $num_changed = $host->select($changed, $write, $except, null); - } catch (Exception $e) { - // master had a problem?...what to do? - return; - } - - foreach($changed as $resource) { - try { - $conn = $this->_connections[$resource]; - - if ($host->getResource() === $resource) { - $res = $this->onOpen($conn); - } else { - $data = $buf = ''; - $bytes = $conn->getSocket()->recv($buf, $this->_buffer_size, MSG_DONTWAIT); - if ($bytes > 0) { - $data = $buf; - - // This idea works* but... - // 1) A single DDOS attack will block the entire application (I think) - // 2) What if the last message in the frame is equal to $recv_bytes? Would loop until another msg is sent - // 3) This failed...an intermediary can set their buffer lower and this still propagates a fragment - // Need to 1) proc_open the recv() calls. 2) ??? - - /* - while ($bytes === $recv_bytes) { - $bytes = $conn->recv($buf, $recv_bytes, 0); - $data .= $buf; - } - */ - - $res = $this->onMessage($conn, $data); - } else { - $res = $this->onClose($conn); - } - } - } catch (\Exception $e) { - $res = $this->onError($conn, $e); - } - - while ($res instanceof CommandInterface) { - try { - $new_res = $res->execute($this); - } catch (\Exception $e) { - break; - // trigger new error - // $new_res = $this->onError($e->getSocket()); ??? - // this is dangerous territory...could get in an infinte loop...Exception might not be Ratchet\Exception...$new_res could be ActionInterface|Composite|NULL... - } - - $res = $new_res; - } - } - } - - /** - * {@inheritdoc} - */ - public function onOpen(ConnectionInterface $conn) { - $new_socket = clone $conn->getSocket(); - $new_socket->set_nonblock(); - $new_connection = new Connection($new_socket); - - $new_connection->remoteAddress = $new_socket->getRemoteAddress(); - $new_connection->resourceId = (int)substr((string)$new_socket->getResource(), strrpos((string)$new_socket->getResource(), '#') + 1); - - $this->_resources[] = $new_connection->getSocket()->getResource(); - $this->_connections[$new_connection->getSocket()->getResource()] = $new_connection; - - return $this->_decorating->onOpen($new_connection); - } - - /** - * {@inheritdoc} - */ - public function onMessage(ConnectionInterface $from, $msg) { - return $this->_decorating->onMessage($from, $msg); - } - - /** - * {@inheritdoc} - */ - public function onClose(ConnectionInterface $conn) { - $resource = $conn->getSocket()->getResource(); - - $cmd = $this->_decorating->onClose($conn); - - unset($this->_connections[$resource], $this->_resources[array_search($resource, $this->_resources)]); - - return $cmd; - } - - /** - * {@inheritdoc} - */ - public function onError(ConnectionInterface $conn, \Exception $e) { - return $this->_decorating->onError($conn, $e); - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/WAMP/Command/Action/CallError.php b/src/Ratchet/Component/WAMP/Command/Action/CallError.php deleted file mode 100644 index 4aafbee..0000000 --- a/src/Ratchet/Component/WAMP/Command/Action/CallError.php +++ /dev/null @@ -1,79 +0,0 @@ -_id = $callId; - $this->_uri = $uri; - $this->_desc = $desc; - - $data = array(WAMP::MSG_CALL_ERROR, $callId, $uri, $desc); - - if (null !== $details) { - $data[] = $details; - $this->_details = $details; - } - - return $this->setMessage(json_encode($data)); - } - - /** - * @return string|null - */ - public function getId() { - return $this->_id; - } - - /** - * @return string|null - */ - public function getUri() { - return $this->_uri; - } - - /** - * @return string - */ - public function getDescription() { - return $this->_desc; - } - - /** - * @return string|null - */ - public function getDetails() { - return $this->_details; - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/WAMP/Command/Action/CallResult.php b/src/Ratchet/Component/WAMP/Command/Action/CallResult.php deleted file mode 100644 index b65d062..0000000 --- a/src/Ratchet/Component/WAMP/Command/Action/CallResult.php +++ /dev/null @@ -1,45 +0,0 @@ -_id = $callId; - $this->_data = $data; - - return $this->setMessage(json_encode(array(WAMP::MSG_CALL_RESULT, $callId, $data))); - } - - /** - * @return string|null - */ - public function getId() { - return $this->_id; - } - - /** - * @return array|null - */ - public function getData() { - return $this->_data; - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/WAMP/Command/Action/Event.php b/src/Ratchet/Component/WAMP/Command/Action/Event.php deleted file mode 100644 index a26a18d..0000000 --- a/src/Ratchet/Component/WAMP/Command/Action/Event.php +++ /dev/null @@ -1,19 +0,0 @@ -setMessage(json_encode(array(WAMP::MSG_EVENT, $uri, $msg))); - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/WAMP/Command/Action/Prefix.php b/src/Ratchet/Component/WAMP/Command/Action/Prefix.php deleted file mode 100644 index 047c1ea..0000000 --- a/src/Ratchet/Component/WAMP/Command/Action/Prefix.php +++ /dev/null @@ -1,40 +0,0 @@ -_curie = $curie; - $this->_uri = $uri; - - return $this->setMessage(json_encode(array(WAMP::MSG_PREFIX, $curie, $uri))); - } - - /** - * @return string - */ - public function getCurie() { - return $this->_curie; - } - - /** - * @return string - */ - public function getUri() { - return $this->_uri; - } -} diff --git a/src/Ratchet/Component/WAMP/Command/Action/Welcome.php b/src/Ratchet/Component/WAMP/Command/Action/Welcome.php deleted file mode 100644 index c9c8974..0000000 --- a/src/Ratchet/Component/WAMP/Command/Action/Welcome.php +++ /dev/null @@ -1,18 +0,0 @@ -setMessage(json_encode(array(WAMP::MSG_WELCOME, $sessionId, 1, $serverIdent))); - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/WAMP/WAMPServerComponent.php b/src/Ratchet/Component/WAMP/WAMPServerComponent.php deleted file mode 100644 index 6e3e353..0000000 --- a/src/Ratchet/Component/WAMP/WAMPServerComponent.php +++ /dev/null @@ -1,194 +0,0 @@ -WAMP->prefixes[$curie] = $uri; - - if ($from_server) { - $prefix = new Prefix($conn); - $prefix->setPrefix($curie, $uri); - - $this->_msg_buffer->enqueue($prefix); - } - } - - /** - * {@inheritdoc} - */ - public function onOpen(ConnectionInterface $conn) { - $conn->WAMP = new \StdClass; - $conn->WAMP->sessionId = uniqid(); - $conn->WAMP->prefixes = array(); - - $wamp = $this; - $conn->WAMP->addPrefix = function($curie, $uri) use ($wamp, $conn) { - $wamp->addPrefix($conn, $curie, $uri, true); - }; - - $welcome = new Welcome($conn); - $welcome->setWelcome($conn->WAMP->sessionId, \Ratchet\Resource\VERSION); - $this->_msg_buffer->enqueue($welcome); - - return $this->attachStack($this->_decorating->onOpen($conn)); - } - - /** - * @{inheritdoc} - * @throws Exception - * @throws JSONException - */ - public function onMessage(ConnectionInterface $from, $msg) { - if (null === ($json = @json_decode($msg, true))) { - throw new JSONException; - } - - switch ($json[0]) { - case static::MSG_PREFIX: - $ret = $this->addPrefix($from, $json[1], $json[2]); - break; - - case static::MSG_CALL: - array_shift($json); - $callID = array_shift($json); - $procURI = array_shift($json); - - if (count($json) == 1 && is_array($json[0])) { - $json = $json[0]; - } - - $ret = $this->_decorating->onCall($from, $callID, $procURI, $json); - break; - - case static::MSG_SUBSCRIBE: - $ret = $this->_decorating->onSubscribe($from, $this->getUri($from, $json[1])); - break; - - case static::MSG_UNSUBSCRIBE: - $ret = $this->_decorating->onUnSubscribe($from, $this->getUri($from, $json[1])); - break; - - case static::MSG_PUBLISH: - $ret = $this->_decorating->onPublish($from, $this->getUri($from, $json[1]), $json[2]); - break; - - default: - throw new Exception('Invalid message type'); - } - - return $this->attachStack($ret); - } - - /** - * Get the full request URI from the connection object if a prefix has been established for it - * @param Ratchet\Resource\Connection - * @param ... - * @return string - */ - protected function getUri(ConnectionInterface $conn, $uri) { - return (isset($conn->WAMP->prefixes[$uri]) ? $conn->WAMP->prefixes[$uri] : $uri); - } - - /** - * If the developer's application as set some server-to-client prefixes to be set, - * this method ensures those are taxied to the next outgoing message - * @param Ratchet\Resource\Command\CommandInterface|NULL - * @return Ratchet\Resource\Command\Composite - */ - protected function attachStack(CommandInterface $command = null) { - $stack = $this->_msg_buffer; - $stack->enqueue($command); - - $this->_msg_buffer = new Composite; - - return $stack; - } - - /** - * @param WAMPServerComponentInterface An class to propagate calls through - */ - public function __construct(WAMPServerComponentInterface $server_component) { - CmdFactory::registerActionPath(__NAMESPACE__ . '\\Command\\Action'); - - $this->_decorating = $server_component; - $this->_msg_buffer = new Composite; - } - - /** - * {@inheritdoc} - */ - public function onClose(ConnectionInterface $conn) { - return $this->_decorating->onClose($conn); - } - - /** - * {@inheritdoc} - */ - public function onError(ConnectionInterface $conn, \Exception $e) { - return $this->_decorating->onError($conn, $e); - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/WebSocket/Command/Action/Disconnect.php b/src/Ratchet/Component/WebSocket/Command/Action/Disconnect.php deleted file mode 100644 index 3b2768b..0000000 --- a/src/Ratchet/Component/WebSocket/Command/Action/Disconnect.php +++ /dev/null @@ -1,22 +0,0 @@ -_code = (int)$code; - - // re-do message based on code - } - - public function execute(ComponentInterface $scope = null) { - parent::execute(); - $this->_socket->close(); - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/WebSocket/Command/Action/Ping.php b/src/Ratchet/Component/WebSocket/Command/Action/Ping.php deleted file mode 100644 index bac184d..0000000 --- a/src/Ratchet/Component/WebSocket/Command/Action/Ping.php +++ /dev/null @@ -1,12 +0,0 @@ -setProtocolVersion($protocolVersion); - } - - protected static function requestCreate($method, $url, $headers = null, $body = null) { - $c = static::$entityEnclosingRequestClass; - $request = new $c($method, $url, $headers); - $request->setBody($body); - - return $request; - } -} \ No newline at end of file diff --git a/src/Ratchet/Component/ComponentInterface.php b/src/Ratchet/ComponentInterface.php similarity index 66% rename from src/Ratchet/Component/ComponentInterface.php rename to src/Ratchet/ComponentInterface.php index 63ed81c..abebe3f 100644 --- a/src/Ratchet/Component/ComponentInterface.php +++ b/src/Ratchet/ComponentInterface.php @@ -1,6 +1,6 @@ _conn = $conn; - } - - public function getConnection() { - return $this->_conn; - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Command/Action/CloseConnection.php b/src/Ratchet/Resource/Command/Action/CloseConnection.php deleted file mode 100644 index 6966a89..0000000 --- a/src/Ratchet/Resource/Command/Action/CloseConnection.php +++ /dev/null @@ -1,31 +0,0 @@ -onClose($this->getConnection()); - - if ($ret instanceof CommandInterface) { - $comp = new Composite; - $comp->enqueue($ret); - - $rt = new Runtime($this->getConnection()); - $rt->setCommand(function(ConnectionInterface $conn, ComponentInterface $scope) { - $conn->getSocket()->close(); - }); - $comp->enqueue($rt); - - return $comp; - } - - $this->getConnection()->getSocket()->close(); - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Command/Action/Null.php b/src/Ratchet/Resource/Command/Action/Null.php deleted file mode 100644 index 0b8df4e..0000000 --- a/src/Ratchet/Resource/Command/Action/Null.php +++ /dev/null @@ -1,11 +0,0 @@ -_command = $callback; - } - - /** - * {@inheritdoc} - */ - public function execute(ComponentInterface $scope = null) { - $cmd = $this->_command; - - return $cmd($this->getConnection(), $scope); - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Command/Action/SendMessage.php b/src/Ratchet/Resource/Command/Action/SendMessage.php deleted file mode 100644 index 3eca5fe..0000000 --- a/src/Ratchet/Resource/Command/Action/SendMessage.php +++ /dev/null @@ -1,43 +0,0 @@ -_message = (string)$msg; - return $this; - } - - /** - * Get the message from setMessage() - * @return string - */ - public function getMessage() { - return $this->_message; - } - - /** - * {@inheritdoc} - * @throws \UnexpectedValueException if a message was not set with setMessage() - */ - public function execute(ComponentInterface $scope = null) { - if (empty($this->_message)) { - throw new \UnexpectedValueException("Message is empty"); - } - - $this->getConnection()->getSocket()->deliver($this->_message); - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Command/CommandInterface.php b/src/Ratchet/Resource/Command/CommandInterface.php deleted file mode 100644 index 5ea31c6..0000000 --- a/src/Ratchet/Resource/Command/CommandInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -enqueue($cmd); - } - - return; - } - - parent::enqueue($command); - } - - /** - * {@inheritdoc} - */ - public function execute(ComponentInterface $scope = null) { - $this->setIteratorMode(static::IT_MODE_DELETE); - - $recursive = new self; - - foreach ($this as $command) { - $recursive->enqueue($command->execute($scope)); - } - - if (count($recursive) > 0) { - return $recursive; - } - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Command/Factory.php b/src/Ratchet/Resource/Command/Factory.php deleted file mode 100644 index ebd2cc8..0000000 --- a/src/Ratchet/Resource/Command/Factory.php +++ /dev/null @@ -1,82 +0,0 @@ -addActionPath(__NAMESPACE__ . '\\Action'); - $this->_ignoreGlobals = (boolean)$ignoreGlobals; - } - - /** - * Add a new namespace of which CommandInterfaces reside under to autoload with $this->newCommand() - * @param string - */ - public function addActionPath($namespace) { - $this->_paths[] = $this->slashIt($namespace); - } - - public static function registerActionPath($namespace) { - static::$globalPaths[$namespace] = 1; - } - - /** - * @return Composite - */ - public function newComposite() { - return new Composite; - } - - /** - * @param string - * @return CommandInterface - * @throws UnexpectedValueException - */ - public function newCommand($name, ConnectionInterface $conn) { - if (isset($this->_mapped_commands[$name])) { - $cmd = $this->_mapped_commands[$name]; - return new $cmd($conn); - } - - foreach ($this->_paths as $path) { - if (class_exists($path . $name)) { - $this->_mapped_commands[$name] = $path . $name; - return $this->newCommand($name, $conn); - } - } - - if (false === $this->_ignoreGlobals) { - foreach (static::$globalPaths as $path => $one) { - $path = $this->slashIt($path); - if (class_exists($path . $name)) { - $this->_mapped_commands[$name] = $path . $name; - return $this->newCommand($name, $conn); - } - } - } - - throw new \UnexepctedValueException("Command {$name} not found"); - } - - /** - * @param string - * @return string - */ - protected function slashIt($ns) { - return (substr($ns, -1) == '\\' ? $ns : $ns . '\\'); - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Connection.php b/src/Ratchet/Resource/Connection.php deleted file mode 100644 index 4f1485c..0000000 --- a/src/Ratchet/Resource/Connection.php +++ /dev/null @@ -1,29 +0,0 @@ -_socket = $socket; - } - - /** - * This is here because I couldn't figure out a better/easier way to tie a connection and socket together for the server and commands - * Anyway, if you're here, it's not recommended you use this/directly interact with the socket in your App... - * The command pattern (which is fully flexible, see Runtime) is the safest, desired way to interact with the socket(s). - * @return Ratchet\SocketInterface - * @todo Figure out a better way to match Socket/Connection in Application and Commands - */ - public function getSocket() { - return $this->_socket; - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/ConnectionInterface.php b/src/Ratchet/Resource/ConnectionInterface.php deleted file mode 100644 index e754de8..0000000 --- a/src/Ratchet/Resource/ConnectionInterface.php +++ /dev/null @@ -1,7 +0,0 @@ - - * @link http://ca2.php.net/manual/en/book.sockets.php - */ -class BSDSocket implements SocketInterface { - /** - * @type resource - */ - protected $_resource; - - public static $_defaults = array( - 'domain' => AF_INET - , 'type' => SOCK_STREAM - , 'protocol' => SOL_TCP - ); - - /** - * @param int Specifies the protocol family to be used by the socket. - * @param int The type of communication to be used by the socket - * @param int Sets the specific protocol within the specified domain to be used when communicating on the returned socket - * @throws BSDSocketException - */ - public function __construct($domain = null, $type = null, $protocol = null) { - list($domain, $type, $protocol) = static::getConfig($domain, $type, $protocol); - - $this->_resource = @socket_create($domain, $type, $protocol); - - if (!is_resource($this->_resource)) { - throw new BSDSocketException($this); - } - } - - public function __destruct() { - @socket_close($this->_resource); - } - - public function __toString() { - $id = (string)$this->getResource(); - return (string)substr($id, strrpos($id, '#') + 1); - } - - /** - * @return resource (Socket) - */ - public function getResource() { - return $this->_resource; - } - - public function __clone() { - $this->_resource = @socket_accept($this->_resource); - - if (false === $this->_resource) { - throw new BSDSocketException($this); - } - } - - public function deliver($message) { - $len = strlen($message); - - do { - $sent = $this->write($message, $len); - $len -= $sent; - $message = substr($message, $sent); - } while ($len > 0); - } - - public function bind($address, $port = 0) { - if (false === @socket_bind($this->getResource(), $address, $port)) { - throw new BSDSocketException($this); - } - - return $this; - } - - public function close() { - @socket_close($this->getResource()); - unset($this->_resource); - } - - public function connect($address, $port = 0) { - if (false === @socket_connect($this->getResource(), $address, $port)) { - throw new BSDSocketException($this); - } - - return $this; - } - - public function getRemoteAddress() { - $address = $port = ''; - if (false === @socket_getpeername($this->getResource(), $address, $port)) { - throw new BSDSocketException($this); - } - - return $address; - } - - public function get_option($level, $optname) { - if (false === ($res = @socket_get_option($this->getResource(), $level, $optname))) { - throw new BSDSocketException($this); - } - - return $res; - } - - public function listen($backlog = 0) { - if (false === @socket_listen($this->getResource(), $backlog)) { - throw new BSDSocketException($this); - } - - return $this; - } - - public function read($length, $type = PHP_BINARY_READ) { - if (false === ($res = @socket_read($this->getResource(), $length, $type))) { - throw new BSDSocketException($this); - } - - return $res; - } - - /** - * @see http://ca3.php.net/manual/en/function.socket-recv.php - * @param string Variable to write data to - * @param int Number of bytes to read - * @param int - * @return int Number of bytes received - * @throws BSDSocketException - */ - public function recv(&$buf, $len, $flags) { - if (false === ($bytes = @socket_recv($this->_resource, $buf, $len, $flags))) { - throw new BSDSocketException($this); - } - - return $bytes; - } - - /** - * Since PHP is retarded and their golden hammer, the array, doesn't implement any interfaces I have to hackishly overload socket_select - * @see http://ca3.php.net/manual/en/function.socket-select.php - * @param Iterator|array|NULL The sockets listed in the read array will be watched to see if characters become available for reading (more precisely, to see if a read will not block - in particular, a socket resource is also ready on end-of-file, in which case a socket_read() will return a zero length string). - * @param Iterator|array|NULL The sockets listed in the write array will be watched to see if a write will not block. - * @param Iterator|array|NULL The sockets listed in the except array will be watched for exceptions. - * @param int The tv_sec and tv_usec together form the timeout parameter. The timeout is an upper bound on the amount of time elapsed before socket_select() return. tv_sec may be zero , causing socket_select() to return immediately. This is useful for polling. If tv_sec is NULL (no timeout), socket_select() can block indefinitely. - * @param int - * @throws \InvalidArgumentException - * @throws BSDSocketException - */ - public function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0) { - $read = static::mungForSelect($read); - $write = static::mungForSelect($write); - $except = static::mungForSelect($except); - - $num = socket_select($read, $write, $except, $tv_sec, $tv_usec); - - if (false === $num) { - throw new BSDException($this); - } - - return $num; - } - - public function set_block() { - if (false === @socket_set_block($this->getResource())) { - throw new BSDSocketException($this); - } - - return $this; - } - - public function set_nonblock() { - if (false === @socket_set_nonblock($this->getResource())) { - throw new BSDSocketException($this); - } - - return $this; - } - - public function set_option($level, $optname, $optval) { - if (false === @socket_set_option($this->getResource(), $level, $optname, $optval)) { - throw new BSDSocketException($this); - } - - return $this; - } - - public function shutdown($how = 2) { - if (false === @socket_shutdown($this->getResource(), $how)) { - throw new BSDSocketException($this); - } - - return $this; - } - - public function write($buffer, $length = 0) { - if (false === ($res = @socket_write($this->getResource(), $buffer, $length))) { - throw new BSDSocketException($this); - } - - return $res; - } - - /** - * @internal - * @param int Specifies the protocol family to be used by the socket. - * @param int The type of communication to be used by the socket - * @param int Sets the specific protocol within the specified domain to be used when communicating on the returned socket - * @return array - */ - protected static function getConfig($domain = null, $type = null, $protocol = null) { - foreach (static::$_defaults as $key => $val) { - if (null === $$key) { - $$key = $val; - } - } - - return array($domain, $type, $protocol); - } - - /** - * @internal - * @param Iterator|array|NULL - * @return array|NULL - * @throws \InvalidArgumentException - */ - protected static function mungForSelect($collection) { - if (null === $collection || is_array($collection)) { - return $collection; - } - - if (!($collection instanceof \Traversable)) { - throw new \InvalidArgumentException('Object pass is not traversable'); - } - - $return = array(); - foreach ($collection as $key => $socket) { - $return[$key] = ($socket instanceof $this ? $socket->getResource() : $socket); - } - - return $return; - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Socket/BSDSocketException.php b/src/Ratchet/Resource/Socket/BSDSocketException.php deleted file mode 100644 index b3ef30e..0000000 --- a/src/Ratchet/Resource/Socket/BSDSocketException.php +++ /dev/null @@ -1,26 +0,0 @@ -_socket = $socket; - //@socket_clear_error($socket->getResource()); - - parent::__construct($msg, $int); - } - - public function getSocket() { - return $this->_socket; - } -} \ No newline at end of file diff --git a/src/Ratchet/Resource/Socket/SocketInterface.php b/src/Ratchet/Resource/Socket/SocketInterface.php deleted file mode 100644 index a01a5b8..0000000 --- a/src/Ratchet/Resource/Socket/SocketInterface.php +++ /dev/null @@ -1,151 +0,0 @@ -validateDomain($domain)) { @@ -81,7 +77,7 @@ class FlashPolicyComponent implements MessageComponentInterface { * crossdomain.xml. * * @param string - * @return FlashPolicyComponent + * @return FlashPolicy */ public function setSiteControl($permittedCrossDomainPolicies = 'all') { if (!$this->validateSiteControl($permittedCrossDomainPolicies)) { @@ -109,10 +105,7 @@ class FlashPolicyComponent implements MessageComponentInterface { $this->_cacheValid = true; } - $cmd = new SendMessage($from); - $cmd->setMessage($this->_cache . "\0"); - - return $cmd; + $from->send($this->_cache . "\0"); } /** @@ -125,7 +118,7 @@ class FlashPolicyComponent implements MessageComponentInterface { * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { - return new CloseConnection($conn); + $conn->close(); } /** diff --git a/src/Ratchet/Server/IoConnection.php b/src/Ratchet/Server/IoConnection.php new file mode 100644 index 0000000..e7492b8 --- /dev/null +++ b/src/Ratchet/Server/IoConnection.php @@ -0,0 +1,39 @@ +conn = $conn; + $this->server = $server; + } + + /** + * {@inheritdoc} + */ + public function send($data) { + return $this->conn->write($data); + } + + /** + * {@inheritdoc} + */ + public function close() { + $this->server->onClose($this); + $this->conn->end(); + } +} \ No newline at end of file diff --git a/src/Ratchet/Server/IoServer.php b/src/Ratchet/Server/IoServer.php new file mode 100644 index 0000000..daf9b12 --- /dev/null +++ b/src/Ratchet/Server/IoServer.php @@ -0,0 +1,96 @@ +loop = $loop; + $this->app = $app; + + $socket->on('connect', array($this, 'handleConnect')); + + $this->handlers['data'] = array($this, 'handleData'); + $this->handlers['end'] = array($this, 'handleEnd'); + $this->handlers['error'] = array($this, 'handleError'); + } + + public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') { + // Enable this after we fix a bug with libevent + // $loop = LoopFactory::create(); + + $loop = new StreamSelectLoop; + + $socket = new Reactor($loop); + $socket->listen($port, $address); + $server = new static($component, $socket, $loop); + + return $server; + } + + public function run() { + $this->loop->run(); + } + + public function handleConnect($conn) { + $conn->decor = new IoConnection($conn, $this); + + $conn->decor->resourceId = (int)$conn->socket; + $conn->decor->remoteAddress = '127.0.0.1'; // todo + + $this->app->onOpen($conn->decor); + + $conn->on('data', $this->handlers['data']); + $conn->on('end', $this->handlers['end']); + $conn->on('error', $this->handlers['error']); + } + + public function handleData($data, $conn) { + try { + $this->app->onMessage($conn->decor, $data); + } catch (\Exception $e) { + $this->handleError($e, $conn); + } + } + + public function handleEnd($conn) { + try { + $this->app->onClose($conn->decor); + } catch (\Exception $e) { + $this->handleError($e, $conn); + } + } + + public function handleError(\Exception $e, $conn) { + $this->app->onError($conn->decor, $e); + } +} \ No newline at end of file diff --git a/src/Ratchet/Component/Server/IpBlackListComponent.php b/src/Ratchet/Server/IpBlackList.php similarity index 79% rename from src/Ratchet/Component/Server/IpBlackListComponent.php rename to src/Ratchet/Server/IpBlackList.php index 5e2d06c..150e560 100644 --- a/src/Ratchet/Component/Server/IpBlackListComponent.php +++ b/src/Ratchet/Server/IpBlackList.php @@ -1,17 +1,16 @@ isBlocked($conn->remoteAddress)) { - return new CloseConnection($conn); + return $conn->close(); } return $this->_decorating->onOpen($conn); @@ -91,17 +90,17 @@ class IpBlackListComponent implements MessageComponentInterface { * {@inheritdoc} */ function onClose(ConnectionInterface $conn) { - if ($this->isBlocked($conn->remoteAddress)) { - return null; + if (!$this->isBlocked($conn->remoteAddress)) { + $this->_decorating->onClose($conn); } - - return $this->_decorating->onClose($conn); } /** * {@inheritdoc} */ function onError(ConnectionInterface $conn, \Exception $e) { - return $this->_decorating->onError($conn, $e); + if (!$this->isBlocked($conn->remoteAddress)) { + $this->_decorating->onError($conn, $e); + } } } \ No newline at end of file diff --git a/src/Ratchet/Component/Session/Serialize/HandlerInterface.php b/src/Ratchet/Session/Serialize/HandlerInterface.php similarity index 82% rename from src/Ratchet/Component/Session/Serialize/HandlerInterface.php rename to src/Ratchet/Session/Serialize/HandlerInterface.php index 42774b1..f1c7c0c 100644 --- a/src/Ratchet/Component/Session/Serialize/HandlerInterface.php +++ b/src/Ratchet/Session/Serialize/HandlerInterface.php @@ -1,5 +1,5 @@ setSaveHandler($handler); diff --git a/src/Ratchet/Component/WAMP/Exception.php b/src/Ratchet/Wamp/Exception.php similarity index 55% rename from src/Ratchet/Component/WAMP/Exception.php rename to src/Ratchet/Wamp/Exception.php index f66097e..9b0ca24 100644 --- a/src/Ratchet/Component/WAMP/Exception.php +++ b/src/Ratchet/Wamp/Exception.php @@ -1,5 +1,5 @@ WAMP = new \StdClass; + $this->WAMP->sessionId = uniqid(); + $this->WAMP->prefixes = array(); + + $this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION))); + } + + /** + * @param string The unique ID given by the client to respond to + * @param array An array of data to return to the client + */ + public function callResult($id, array $data = array()) { + $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data))); + } + + /** + * @param string The unique ID given by the client to respond to + * @param string The URI given by the client ot respond to + * @param string A developer-oriented description of the error + * @param string|null An optional human readable detail message to send back + */ + public function callError($id, $uri, $desc = '', $details = null) { + $data = array(WAMP::MSG_CALL_ERROR, $id, $uri, $desc); + + if (null !== $details) { + $data[] = $details; + } + + $this->send(json_encode($data)); + } + + /** + * @param string The URI or CURIE to broadcast to + * @param mixed Data to send with the event. Anything that is json'able + */ + public function event($uri, $msg) { + $this->send(json_encode(array(WAMP::MSG_EVENT, $uri, $msg))); + } + + /** + * @param string + * @param string + */ + public function prefix($curie, $uri) { + $this->WAMP->prefixes[$curie] = $uri; + $this->send(json_encode(array(WAMP::MSG_PREFIX, $curie, $uri))); + } + + /** + * Get the full request URI from the connection object if a prefix has been established for it + * @param string + * @return string + */ + public function getUri($uri) { + return (isset($this->WAMP->prefixes[$uri]) ? $this->WAMP->prefixes[$uri] : $uri); + } + + /** + * @internal + */ + public function send($data) { + $this->getConnection()->send($data); + } + + /** + * {@inheritdoc} + */ + public function close() { + $this->getConnection()->close(); + } +} \ No newline at end of file diff --git a/src/Ratchet/Wamp/WampServer.php b/src/Ratchet/Wamp/WampServer.php new file mode 100644 index 0000000..ae2e03b --- /dev/null +++ b/src/Ratchet/Wamp/WampServer.php @@ -0,0 +1,137 @@ +_decorating = $server_component; + $this->connections = new \SplObjectStorage; + } + + /** + * {@inheritdoc} + */ + public function getSubProtocol() { + return 'wamp'; + } + + /** + * {@inheritdoc} + */ + public function onOpen(ConnectionInterface $conn) { + $decor = new WampConnection($conn); + $this->connections->attach($conn, $decor); + + $this->_decorating->onOpen($decor); + } + + /** + * @{inheritdoc} + * @throws Exception + * @throws JsonException + */ + public function onMessage(ConnectionInterface $from, $msg) { + $from = $this->connections[$from]; + + if (null === ($json = @json_decode($msg, true))) { + throw new JsonException; + } + + switch ($json[0]) { + case static::MSG_PREFIX: + $from->WAMP->prefixes[$json[1]] = $json[2]; + break; + + case static::MSG_CALL: + array_shift($json); + $callID = array_shift($json); + $procURI = array_shift($json); + + if (count($json) == 1 && is_array($json[0])) { + $json = $json[0]; + } + + $this->_decorating->onCall($from, $callID, $procURI, $json); + break; + + case static::MSG_SUBSCRIBE: + $this->_decorating->onSubscribe($from, $from->getUri($json[1])); + break; + + case static::MSG_UNSUBSCRIBE: + $this->_decorating->onUnSubscribe($from, $from->getUri($json[1])); + break; + + case static::MSG_PUBLISH: + $exclude = (array_key_exists(3, $json) ? $json[3] : null); + $eligible = (array_key_exists(4, $json) ? $json[4] : null); + + $this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible); + break; + + default: + throw new Exception('Invalid message type'); + } + } + + /** + * {@inheritdoc} + */ + public function onClose(ConnectionInterface $conn) { + $decor = $this->connections[$conn]; + $this->connections->detach($conn); + + $this->_decorating->onClose($decor); + } + + /** + * {@inheritdoc} + */ + public function onError(ConnectionInterface $conn, \Exception $e) { + return $this->_decorating->onError($this->connections[$conn], $e); + } +} \ No newline at end of file diff --git a/src/Ratchet/Component/WAMP/WAMPServerComponentInterface.php b/src/Ratchet/Wamp/WampServerInterface.php similarity index 53% rename from src/Ratchet/Component/WAMP/WAMPServerComponentInterface.php rename to src/Ratchet/Wamp/WampServerInterface.php index 180ad2a..528d627 100644 --- a/src/Ratchet/Component/WAMP/WAMPServerComponentInterface.php +++ b/src/Ratchet/Wamp/WampServerInterface.php @@ -1,46 +1,42 @@ entityEnclosingRequestClass; + $request = new $c($method, $url, $headers); + if ($body) { + $request->setBody(EntityBody::factory($body)); + } + + return $request; + } +} \ No newline at end of file diff --git a/src/Ratchet/Component/WebSocket/Version/FrameInterface.php b/src/Ratchet/WebSocket/Version/FrameInterface.php similarity index 95% rename from src/Ratchet/Component/WebSocket/Version/FrameInterface.php rename to src/Ratchet/WebSocket/Version/FrameInterface.php index 3e9882b..57b27ea 100644 --- a/src/Ratchet/Component/WebSocket/Version/FrameInterface.php +++ b/src/Ratchet/WebSocket/Version/FrameInterface.php @@ -1,5 +1,5 @@ getHeader('Sec-WebSocket-Key2')); + return !(null === $request->getHeader('Sec-WebSocket-Key2', true)); } /** @@ -28,13 +28,13 @@ class Hixie76 implements VersionInterface { * @return Guzzle\Http\Message\Response */ public function handshake(RequestInterface $request) { - $body = $this->sign($request->getHeader('Sec-WebSocket-Key1'), $request->getHeader('Sec-WebSocket-Key2'), $request->getBody()); + $body = $this->sign($request->getHeader('Sec-WebSocket-Key1', true), $request->getHeader('Sec-WebSocket-Key2', true), (string)$request->getBody()); $headers = array( 'Upgrade' => 'WebSocket' , 'Connection' => 'Upgrade' - , 'Sec-WebSocket-Origin' => $request->getHeader('Origin') - , 'Sec-WebSocket-Location' => 'ws://' . $request->getHeader('Host') . $request->getPath() + , 'Sec-WebSocket-Origin' => $request->getHeader('Origin', true) + , 'Sec-WebSocket-Location' => 'ws://' . $request->getHeader('Host', true) . $request->getPath() ); $response = new Response('101', $headers, $body); diff --git a/src/Ratchet/Component/WebSocket/Version/Hixie76/Frame.php b/src/Ratchet/WebSocket/Version/Hixie76/Frame.php similarity index 93% rename from src/Ratchet/Component/WebSocket/Version/Hixie76/Frame.php rename to src/Ratchet/WebSocket/Version/Hixie76/Frame.php index 8be42c7..b9af87d 100644 --- a/src/Ratchet/Component/WebSocket/Version/Hixie76/Frame.php +++ b/src/Ratchet/WebSocket/Version/Hixie76/Frame.php @@ -1,6 +1,6 @@ getHeaders(); - $passes = 0; $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->verifyHost($request->getHeader('Host', true)); + $passes += (int)$this->verifyUpgradeRequest($request->getHeader('Upgrade', true)); + $passes += (int)$this->verifyConnection($request->getHeader('Connection', true)); + $passes += (int)$this->verifyKey($request->getHeader('Sec-WebSocket-Key', true)); //$passes += (int)$this->verifyVersion($headers['Sec-WebSocket-Version']); // Temporarily breaking functionality return (7 === $passes); diff --git a/src/Ratchet/Component/WebSocket/Version/RFC6455/Message.php b/src/Ratchet/WebSocket/Version/RFC6455/Message.php similarity index 92% rename from src/Ratchet/Component/WebSocket/Version/RFC6455/Message.php rename to src/Ratchet/WebSocket/Version/RFC6455/Message.php index 1123d25..5385475 100644 --- a/src/Ratchet/Component/WebSocket/Version/RFC6455/Message.php +++ b/src/Ratchet/WebSocket/Version/RFC6455/Message.php @@ -1,7 +1,7 @@ WebSocket->version->frame($data, false); + + $this->getConnection()->send($data); + } + + public function close() { + // send close frame + + // ??? + + // profit + + $this->getConnection()->close(); // temporary + } + + public function ping() { + } + + public function pong() { + } +} \ No newline at end of file diff --git a/src/Ratchet/Component/WebSocket/WebSocketComponent.php b/src/Ratchet/WebSocket/WsServer.php similarity index 67% rename from src/Ratchet/Component/WebSocket/WebSocketComponent.php rename to src/Ratchet/WebSocket/WsServer.php index c85b28a..c3b1a48 100644 --- a/src/Ratchet/Component/WebSocket/WebSocketComponent.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -1,31 +1,28 @@ _decorating = $component; - $this->_factory = new Factory; + $this->connections = new \SplObjectStorage; } /** @@ -72,7 +69,7 @@ class WebSocketComponent implements MessageComponentInterface { return; } - $headers = RequestFactory::fromRequest($from->WebSocket->headers); + $headers = RequestFactory::getInstance()->fromMessage($from->WebSocket->headers); $from->WebSocket->version = $this->getVersion($headers); $from->WebSocket->headers = $headers; } @@ -93,14 +90,15 @@ class WebSocketComponent implements MessageComponentInterface { if (count($agreed_protocols) > 0) { $response->setHeader('Sec-WebSocket-Protocol', implode(',', $agreed_protocols)); } - $response->setHeader('X-Powered-By', \Ratchet\Resource\VERSION); + $response->setHeader('X-Powered-By', \Ratchet\VERSION); $header = (string)$response; - $comp = $this->_factory->newComposite(); - $comp->enqueue($this->_factory->newCommand('SendMessage', $from)->setMessage($header)); - $comp->enqueue($this->prepareCommand($this->_decorating->onOpen($from, $msg))); // Need to send headers/handshake to application, let it have the cookies, etc + $from->send($header); - return $comp; + $conn = new WsConnection($from); + $this->connections->attach($from, $conn); + + return $this->_decorating->onOpen($conn); } if (!isset($from->WebSocket->message)) { @@ -115,6 +113,7 @@ class WebSocketComponent implements MessageComponentInterface { $from->WebSocket->frame->addBuffer($msg); if ($from->WebSocket->frame->isCoalesced()) { if ($from->WebSocket->frame->getOpcode() > 2) { + $from->end(); throw new \UnexpectedValueException('Control frame support coming soon!'); } // Check frame @@ -127,10 +126,8 @@ class WebSocketComponent implements MessageComponentInterface { } if ($from->WebSocket->message->isCoalesced()) { - $cmds = $this->prepareCommand($this->_decorating->onMessage($from, (string)$from->WebSocket->message)); + $this->_decorating->onMessage($this->connections[$from], (string)$from->WebSocket->message); unset($from->WebSocket->message); - - return $cmds; } } @@ -138,57 +135,25 @@ class WebSocketComponent implements MessageComponentInterface { * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { - return $this->prepareCommand($this->_decorating->onClose($conn)); + // WS::onOpen is not called when the socket connects, it's call when the handshake is done + // The socket could close before WS calls onOpen, so we need to check if we've "opened" it for the developer yet + if ($this->connections->contains($conn)) { + $decor = $this->connections[$conn]; + $this->connections->detach($conn); + + $this->_decorating->onClose($decor); + } } /** * {@inheritdoc} - * @todo Shouldn't I be using prepareCommand() on the return? look into this */ public function onError(ConnectionInterface $conn, \Exception $e) { - return $this->_decorating->onError($conn, $e); - } - - /** - * Checks if a return Command from your application is a message, if so encode it/them - * @param Ratchet\Resource\Command\CommandInterface|NULL - * @return Ratchet\Resource\Command\CommandInterface|NULL - */ - protected function prepareCommand(CommandInterface $command = null) { - $cache = array(); - return $this->mungCommand($command, $cache); - } - - /** - * Does the actual work of prepareCommand - * Separated to pass the cache array by reference, so we're not framing the same stirng over and over - * @param Ratchet\Resource\Command\CommandInterface|NULL - * @param array - * @return Ratchet\Resource\Command\CommandInterface|NULL - */ - protected function mungCommand(CommandInterface $command = null, &$cache) { - 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; - $hash = md5($command->getMessage()) . '-' . spl_object_hash($version); - - if (!isset($cache[$hash])) { - $cache[$hash] = $version->frame($command->getMessage(), $this->_mask_payload); - } - - return $command->setMessage($cache[$hash]); + if ($this->connections->contains($conn)) { + $this->_decorating->onError($this->connections[$conn], $e); + } else { + $conn->close(); } - - if ($command instanceof \Traversable) { - foreach ($command as $cmd) { - $cmd = $this->mungCommand($cmd, $cache); - } - } - - return $command; } /** diff --git a/src/Ratchet/Component/WebSocket/WebSocketComponentInterface.php b/src/Ratchet/WebSocket/WsServerInterface.php similarity index 62% rename from src/Ratchet/Component/WebSocket/WebSocketComponentInterface.php rename to src/Ratchet/WebSocket/WsServerInterface.php index 14f70d5..30b9ba4 100644 --- a/src/Ratchet/Component/WebSocket/WebSocketComponentInterface.php +++ b/src/Ratchet/WebSocket/WsServerInterface.php @@ -1,8 +1,8 @@ mock = new Connection; + $this->l1 = new ConnectionDecorator($this->mock); + $this->l2 = new ConnectionDecorator($this->l1); + } + + public function testGet() { + $var = 'hello'; + $val = 'world'; + + $this->mock->$var = $val; + + $this->assertEquals($val, $this->l1->$var); + $this->assertEquals($val, $this->l2->$var); + } + + public function testSet() { + $var = 'Chris'; + $val = 'Boden'; + + $this->l1->$var = $val; + + $this->assertEquals($val, $this->mock->$var); + } + + public function testSetLevel2() { + $var = 'Try'; + $val = 'Again'; + + $this->l2->$var = $val; + + $this->assertEquals($val, $this->mock->$var); + } + + public function testIsSetTrue() { + $var = 'PHP'; + $val = 'Ratchet'; + + $this->mock->$var = $val; + + $this->assertTrue(isset($this->l1->$var)); + $this->assertTrue(isset($this->l2->$var)); + } + + public function testIsSetFalse() { + $var = 'herp'; + $val = 'derp'; + + $this->assertFalse(isset($this->l1->$var)); + $this->assertFalse(isset($this->l2->$var)); + } + + public function testUnset() { + $var = 'Flying'; + $val = 'Monkey'; + + $this->mock->$var = $val; + unset($this->l1->$var); + + $this->assertFalse(isset($this->mock->$var)); + } + + public function testUnsetLevel2() { + $var = 'Flying'; + $val = 'Monkey'; + + $this->mock->$var = $val; + unset($this->l2->$var); + + $this->assertFalse(isset($this->mock->$var)); + } + + public function testGetConnection() { + $class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator'); + $method = $class->getMethod('getConnection'); + $method->setAccessible(true); + + $conn = $method->invokeArgs($this->l1, array()); + + $this->assertSame($this->mock, $conn); + } + + public function testGetConnectionLevel2() { + $class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator'); + $method = $class->getMethod('getConnection'); + $method->setAccessible(true); + + $conn = $method->invokeArgs($this->l2, array()); + + $this->assertSame($this->l1, $conn); + } + + public function testWrapperCanStoreSelfInDecorator() { + $this->mock->decorator = $this->l1; + + $this->assertSame($this->l1, $this->l2->decorator); + } + + public function testDecoratorRecursion() { + $this->mock->decorator = new \stdClass; + $this->mock->decorator->conn = $this->l1; + + $this->assertSame($this->l1, $this->mock->decorator->conn); + $this->assertSame($this->l1, $this->l1->decorator->conn); + $this->assertSame($this->l1, $this->l2->decorator->conn); + } + + public function testDecoratorRecursionLevel2() { + $this->mock->decorator = new \stdClass; + $this->mock->decorator->conn = $this->l2; + + $this->assertSame($this->l2, $this->mock->decorator->conn); + $this->assertSame($this->l2, $this->l1->decorator->conn); + $this->assertSame($this->l2, $this->l2->decorator->conn); + + // just for fun + $this->assertSame($this->l2, $this->l2->decorator->conn->decorator->conn->decorator->conn); + } + + public function testWarningGettingNothing() { + $this->setExpectedException('PHPUnit_Framework_Error'); + $var = $this->mock->nonExistant; + } + + public function testWarningGettingNothingLevel1() { + $this->setExpectedException('PHPUnit_Framework_Error'); + $var = $this->l1->nonExistant; + } + + public function testWarningGettingNothingLevel2() { + $this->setExpectedException('PHPUnit_Framework_Error'); + $var = $this->l2->nonExistant; + } +} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/Server/IOServerComponentTest.php b/tests/Ratchet/Tests/Component/Server/IOServerComponentTest.php deleted file mode 100644 index f217043..0000000 --- a/tests/Ratchet/Tests/Component/Server/IOServerComponentTest.php +++ /dev/null @@ -1,65 +0,0 @@ -_catalyst = new Socket; - $this->_decorated = new TestApp; - $this->_server = new IOServerComponent($this->_decorated); - - $ref = new \ReflectionClass('\\Ratchet\\Component\\Server\\IOServerComponent'); - $prop = $ref->getProperty('_run'); - $prop->setAccessible(true); - $prop->setValue($this->_server, false); - } - - protected function getPrivateProperty($class, $name) { - $reflectedClass = new \ReflectionClass($class); - $property = $reflectedClass->getProperty($name); - $property->setAccessible(true); - - return $property->getValue($class); - } - - protected function getMasterConnection() { - $connections = $this->getPrivateProperty($this->_server, '_connections'); - return array_pop($connections); - } - - public function testOnOpenPassesClonedSocket() { - $this->_server->run(1025, '127.0.0.1', $this->_catalyst); - $master = $this->getMasterConnection(); - - $this->_server->onOpen($master); - $clone = $this->_decorated->last['onOpen'][0]; - - $this->assertEquals($master->resourceId + 1, $clone->resourceId); - } - - public function testOnMessageSendsToApp() { - $this->_server->run(1025, '127.0.0.1', $this->_catalyst); - $master = $this->getMasterConnection(); - - // todo, make FakeSocket better, set data in select, recv to pass data when called, then do this check - // that way can mimic the TCP fragmentation/buffer situation - - $this->_server->onOpen($master); - $clone = $this->_decorated->last['onOpen'][0]; - - // $this->_server->run($this->_catalyst); - $msg = 'Hello World!'; - $this->_server->onMessage($clone, $msg); - - $this->assertEquals($msg, $this->_decorated->last['onMessage'][1]); - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/WAMP/Command/Action/CallErrorTest.php b/tests/Ratchet/Tests/Component/WAMP/Command/Action/CallErrorTest.php deleted file mode 100644 index 5d93349..0000000 --- a/tests/Ratchet/Tests/Component/WAMP/Command/Action/CallErrorTest.php +++ /dev/null @@ -1,72 +0,0 @@ -setError($callId, $uri); - $resultString = $error->getMessage(); - - $this->assertEquals(array(4, $callId, $uri, ''), json_decode($resultString, true)); - } - - public function testDetailedCallError() { - $error = new CallError(new Connection); - - $callId = uniqid(); - $uri = 'http://example.com/end/point'; - $desc = 'beep boop beep'; - $detail = 'Error: Too much awesome'; - - $error->setError($callId, $uri, $desc, $detail); - $resultString = $error->getMessage(); - - $this->assertEquals(array(4, $callId, $uri, $desc, $detail), json_decode($resultString, true)); - } - - public function testGetId() { - $id = uniqid(); - - $error = new CallError(new Connection); - $error->setError($id, 'http://example.com'); - - $this->assertEquals($id, $error->getId()); - } - - public function testGetUri() { - $uri = 'http://example.com/end/point'; - - $error = new CallError(new Connection); - $error->setError(uniqid(), $uri); - - $this->assertEquals($uri, $error->getUri()); - } - - public function testGetDescription() { - $desc = uniqid(); - - $error = new CallError(new Connection); - $error->setError(uniqid(), 'curie', $desc); - - $this->assertEquals($desc, $error->getDescription()); - } - - public function testGetDetails() { - $detail = uniqid(); - - $error = new CallError(new Connection); - $this->assertNull($error->getDetails()); - $error->setError(uniqid(), 'http://socketo.me', 'desc', $detail); - - $this->assertEquals($detail, $error->getDetails()); - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/WAMP/Command/Action/CallResultTest.php b/tests/Ratchet/Tests/Component/WAMP/Command/Action/CallResultTest.php deleted file mode 100644 index a931d4b..0000000 --- a/tests/Ratchet/Tests/Component/WAMP/Command/Action/CallResultTest.php +++ /dev/null @@ -1,46 +0,0 @@ - 'world', 'herp' => 'derp'); - - $result->setResult($callId, $data); - $resultString = $result->getMessage(); - - $this->assertEquals(array(3, $callId, $data), json_decode($resultString, true)); - } - - public function testGetId() { - $id = uniqid(); - - $result = new CallResult(new Connection); - $result->setResult($id, array()); - - $this->assertEquals($id, $result->getId()); - } - - public function testGetData() { - $data = array( - 'hello' => 'world' - , 'recursive' => array( - 'the' => 'quick' - , 'brown' => 'fox' - ) - , 'jumps' - ); - - $result = new CallResult(new Connection); - $result->setResult(uniqid(), $data); - - $this->assertEquals($data, $result->getData()); - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/WAMP/Command/Action/EventTest.php b/tests/Ratchet/Tests/Component/WAMP/Command/Action/EventTest.php deleted file mode 100644 index e69de29..0000000 diff --git a/tests/Ratchet/Tests/Mock/Component.php b/tests/Ratchet/Tests/Mock/Component.php index bcf0fbb..d0064d0 100644 --- a/tests/Ratchet/Tests/Mock/Component.php +++ b/tests/Ratchet/Tests/Mock/Component.php @@ -1,8 +1,8 @@ '' + , 'close' => false + ); + public $remoteAddress = '127.0.0.1'; + + public function send($data) { + $this->last[__FUNCTION__] = $data; + } + + public function close() { + $this->last[__FUNCTION__] = true; + } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/Mock/ConnectionDecorator.php b/tests/Ratchet/Tests/Mock/ConnectionDecorator.php new file mode 100644 index 0000000..72a430c --- /dev/null +++ b/tests/Ratchet/Tests/Mock/ConnectionDecorator.php @@ -0,0 +1,22 @@ + '' + , 'end' => false + ); + + public function send($data) { + $this->last[__FUNCTION__] = $data; + + $this->getConnection()->send($data); + } + + public function close() { + $this->last[__FUNCTION__] = true; + + $this->getConnection()->close(); + } +} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Mock/FakeSocket.php b/tests/Ratchet/Tests/Mock/FakeSocket.php deleted file mode 100644 index 1ef7e63..0000000 --- a/tests/Ratchet/Tests/Mock/FakeSocket.php +++ /dev/null @@ -1,100 +0,0 @@ -_id}"; - } - - public function __toString() { - return (string)$this->_id; - } - - public function __construct($domain = null, $type = null, $protocol = null) { - list($this->_arguments['domain'], $this->_arguments['type'], $this->_arguments['protocol']) = array(1, 1, 1); - } - - public function __clone() { - $this->_id++; - } - - public function deliver($message) { - $this->write($message, strlen($message)); - } - - public function bind($address, $port = 0) { - $this->_last['bind'] = array($address, $port); - return $this; - } - - public function close() { - } - - public function connect($address, $port = 0) { - $this->_last['connect'] = array($address, $port = 0); - return $this; - } - - public function getRemoteAddress() { - return '127.0.0.1'; - } - - public function get_option($level, $optname) { - return $this->_options[$level][$optname]; - } - - public function listen($backlog = 0) { - $this->_last['listen'] = array($backlog); - return $this; - } - - public function read($length, $type = PHP_BINARY_READ) { - $this->_last['read'] = array($length, $type); - return 0; - } - - public function recv(&$buf, $len, $flags) { - $this->_last['recv'] = array($buf, $len, $flags); - return 0; - } - - public function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0) { - $this->_last['select'] = array($read, $write, $except, $tv_sec, $tv_usec); - return 0; - } - - public function set_block() { - return $this; - } - - public function set_nonblock() { - return $this; - } - - public function set_option($level, $optname, $optval) { - if (!isset($this->_options[$level])) { - $this->_options[$level] = array(); - } - - $this->_options[$level][$optname] = $optval; - } - - public function shutdown($how = 2) { - $this->_last['shutdown'] = array($how); - return $this; - } - - public function write($buffer, $length = 0) { - $this->_last['write'] = array($buffer, $length); - return $this; - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Mock/WAMPComponent.php b/tests/Ratchet/Tests/Mock/WampComponent.php similarity index 84% rename from tests/Ratchet/Tests/Mock/WAMPComponent.php rename to tests/Ratchet/Tests/Mock/WampComponent.php index 9473e85..8d1460d 100644 --- a/tests/Ratchet/Tests/Mock/WAMPComponent.php +++ b/tests/Ratchet/Tests/Mock/WampComponent.php @@ -1,9 +1,9 @@ last[__FUNCTION__] = func_get_args(); } - public function onPublish(ConnectionInterface $conn, $uri, $event) { + public function onPublish(ConnectionInterface $conn, $uri, $event, $exclude, $eligible) { $this->last[__FUNCTION__] = func_get_args(); } diff --git a/tests/Ratchet/Tests/Resource/Command/Action/SendMessageTest.php b/tests/Ratchet/Tests/Resource/Command/Action/SendMessageTest.php deleted file mode 100644 index fca0055..0000000 --- a/tests/Ratchet/Tests/Resource/Command/Action/SendMessageTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertInstanceOf('\\Ratchet\\Resource\\Command\\Action\\SendMessage', $cmd->setMessage('Hello World!')); - } - - public function testGetMessageMatchesSet() { - $msg = 'The quick brown fox jumps over the lazy dog.'; - $cmd = new SendMessage(new Connection); - $cmd->setMessage($msg); - - $this->assertEquals($msg, $cmd->getMessage()); - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Resource/Command/CompositeTest.php b/tests/Ratchet/Tests/Resource/Command/CompositeTest.php deleted file mode 100644 index 1febbd8..0000000 --- a/tests/Ratchet/Tests/Resource/Command/CompositeTest.php +++ /dev/null @@ -1,64 +0,0 @@ -_comp = new Composite; - } - - protected function newNull() { - return new NullAction(new Connection(new FakeSocket)); - } - - public function testCanEnqueueNull() { - $count = $this->_comp->count(); - - $this->_comp->enqueue(null); - - $this->assertEquals($count, $this->_comp->count()); - } - - public function testEnqueueCommand() { - $count = $this->_comp->count(); - - $this->_comp->enqueue($this->newNull()); - - $this->assertEquals($count + 1, $this->_comp->count()); - } - - public function badEnqueueProviders() { - return array( - array(array()) - , array('string') - ); - } - - /** - * @dataProvider badEnqueueProviders - */ - public function testCanNotPassOtherThings($object) { - $this->setExpectedException('InvalidArgumentException'); - - $this->_comp->enqueue($object); - } - - public function testCompositeComposite() { - $compTwo = new Composite; - $compTwo->enqueue($this->newNull()); - $compTwo->enqueue($this->newNull()); - - $this->_comp->enqueue($this->newNull()); - $this->_comp->enqueue($compTwo); - - $this->assertEquals(3, $this->_comp->count()); - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Resource/ConnectionTest.php b/tests/Ratchet/Tests/Resource/ConnectionTest.php deleted file mode 100644 index 97c3994..0000000 --- a/tests/Ratchet/Tests/Resource/ConnectionTest.php +++ /dev/null @@ -1,62 +0,0 @@ -_fs = new FakeSocket; - $this->_c = new Connection($this->_fs); - } - - public static function keyAndValProvider() { - return array( - array('hello', 'world') - , array('herp', 'derp') - , array('depth', array('hell', 'yes')) - , array('moar', array('hellz' => 'yes')) - ); - } - - public function testGetSocketReturnsWhatIsSetInConstruct() { - $this->assertSame($this->_fs, $this->_c->getSocket()); - } - - /** - * @dataProvider keyAndValProvider - */ - public function testCanGetWhatIsSet($key, $val) { - $this->_c->{$key} = $val; - $this->assertEquals($val, $this->_c->{$key}); - } - - /** - * @dataProvider keyAndValProvider - */ - public function testIssetWorksOnOverloadedVariables($key, $val) { - $this->_c->{$key} = $val; - $this->assertTrue(isset($this->_c->{$key})); - } - - /** - * @dataProvider keyAndValProvider - */ - public function testUnsetMakesIssetReturnFalse($key, $val) { - $this->_c->{$key} = $val; - unset($this->_c->{$key}); - $this->assertFalse(isset($this->_c->{$key})); - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Resource/Socket/BSDSocketTest.php b/tests/Ratchet/Tests/Resource/Socket/BSDSocketTest.php deleted file mode 100644 index a7ac510..0000000 --- a/tests/Ratchet/Tests/Resource/Socket/BSDSocketTest.php +++ /dev/null @@ -1,68 +0,0 @@ -getMethod($name); - $method->setAccessible(true); - - return $method; - } - - public function setUp() { - $this->_socket = new Socket(); - } - - /* (1): I may or may not re-enable this test (need to add code back to FakeSocket), not sure if I'll keep this feature at all - public function testGetDefaultConfigForConstruct() { - $ref_conf = static::getMethod('getConfig'); - $config = $ref_conf->invokeArgs($this->_socket, array()); - - $this->assertEquals(array_values(Socket::$_defaults), $config); - } - /**/ - - public function testInvalidConstructorArguments() { - $this->setExpectedException('\\Ratchet\\Resource\\Socket\\BSDSocketException'); - $socket = new RealSocket('invalid', 'param', 'derp'); - } - - public function testConstructAndCallByOpenAndClose() { - $socket = new RealSocket(); - $socket->close(); - } - - public function asArrayProvider() { - return array( - array(array('hello' => 'world'), array('hello' => 'world')) - , array(null, null) - , array(array('hello' => 'world'), new \ArrayObject(array('hello' => 'world'))) - ); - } - - /** - * (1) - * @dataProvider asArrayProvider - * / - public function testMethodMungforselectReturnsExpectedValues($output, $input) { - $method = static::getMethod('mungForSelect'); - $return = $method->invokeArgs($this->_socket, array($input)); - - $this->assertEquals($return, $output); - } - - public function NOPEtestMethodMungforselectRejectsNonTraversable() { - $this->setExpectedException('\\InvalidArgumentException'); - $method = static::getMethod('mungForSelect'); - $method->invokeArgs($this->_socket, array('I am upset with PHP ATM')); - } - */ -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/Server/FlashPolicyComponentTest.php b/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php similarity index 93% rename from tests/Ratchet/Tests/Component/Server/FlashPolicyComponentTest.php rename to tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php index 1c152b1..51597ca 100644 --- a/tests/Ratchet/Tests/Component/Server/FlashPolicyComponentTest.php +++ b/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php @@ -1,16 +1,16 @@ _policy = new FlashPolicyComponent(); + $this->_policy = new FlashPolicy(); } public function testPolicyRender() { diff --git a/tests/Ratchet/Tests/Component/Server/IpBlackListComponentTest.php b/tests/Ratchet/Tests/Server/IpBlackListComponentTest.php similarity index 80% rename from tests/Ratchet/Tests/Component/Server/IpBlackListComponentTest.php rename to tests/Ratchet/Tests/Server/IpBlackListComponentTest.php index c164803..2d1f662 100644 --- a/tests/Ratchet/Tests/Component/Server/IpBlackListComponentTest.php +++ b/tests/Ratchet/Tests/Server/IpBlackListComponentTest.php @@ -1,19 +1,19 @@ _mock = new MockComponent; - $this->_comp = new IpBlackListComponent($this->_mock); + $this->_comp = new IpBlackList($this->_mock); } public function testBlockAndCloseOnOpen() { @@ -21,9 +21,9 @@ class IpBlackListComponentTest extends \PHPUnit_Framework_TestCase { $this->_comp->blockAddress($conn->remoteAddress); - $ret = $this->_comp->onOpen($conn); + $ret = $this->_comp->onOpen($conn); - $this->assertInstanceOf('\\Ratchet\\Resource\\Command\\Action\\CloseConnection', $ret); + $this->assertTrue($conn->last['close']); } public function testAddAndRemoveWithFluentInterfaces() { @@ -86,6 +86,6 @@ class IpBlackListComponentTest extends \PHPUnit_Framework_TestCase { } public function testUnblockingSilentlyFails() { - $this->assertInstanceOf('\\Ratchet\\Component\\Server\\IpBlackListComponent', $this->_comp->unblockAddress('localhost')); + $this->assertInstanceOf('\\Ratchet\\Server\\IpBlackList', $this->_comp->unblockAddress('localhost')); } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/Session/Serialize/PhpHandlerTest.php b/tests/Ratchet/Tests/Session/Serialize/PhpHandlerTest.php similarity index 83% rename from tests/Ratchet/Tests/Component/Session/Serialize/PhpHandlerTest.php rename to tests/Ratchet/Tests/Session/Serialize/PhpHandlerTest.php index 0528817..c8bf0dc 100644 --- a/tests/Ratchet/Tests/Component/Session/Serialize/PhpHandlerTest.php +++ b/tests/Ratchet/Tests/Session/Serialize/PhpHandlerTest.php @@ -1,9 +1,9 @@ markTestSkipped('Dependency of Symfony HttpFoundation failed'); @@ -31,11 +31,11 @@ class SessionComponentTest extends \PHPUnit_Framework_TestCase { * @dataProvider classCaseProvider */ public function testToClassCase($in, $out) { - $ref = new \ReflectionClass('\\Ratchet\\Component\\Session\\SessionComponent'); + $ref = new \ReflectionClass('\\Ratchet\\Session\\SessionProvider'); $method = $ref->getMethod('toClassCase'); $method->setAccessible(true); - $component = new SessionComponent(new MockComponent, new MemorySessionHandler); + $component = new SessionProvider(new MockComponent, new MemorySessionHandler); $this->assertEquals($out, $method->invokeArgs($component, array($in))); } @@ -56,7 +56,7 @@ class SessionComponentTest extends \PHPUnit_Framework_TestCase { $pdo->exec(vsprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY, %s TEXT, %s INTEGER)", $dbOptions)); $pdo->prepare(vsprintf("INSERT INTO %s (%s, %s, %s) VALUES (?, ?, ?)", $dbOptions))->execute(array($sessionId, base64_encode('_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'), time())); - $component = new SessionComponent(new MockComponent, new PdoSessionHandler($pdo, $dbOptions), array('auto_start' => 1)); + $component = new SessionProvider(new MockComponent, new PdoSessionHandler($pdo, $dbOptions), array('auto_start' => 1)); $connection = new Connection(); $headers = $this->getMock('Guzzle\\Http\\Message\\Request', array('getCookie'), array('POST', '/', array())); @@ -83,7 +83,7 @@ class SessionComponentTest extends \PHPUnit_Framework_TestCase { } $mock = new MockComponent; - $comp = new SessionComponent($mock, new NullSessionHandler); + $comp = new SessionProvider($mock, new NullSessionHandler); $comp->onOpen($conns[1]); $comp->onOpen($conns[3]); diff --git a/tests/Ratchet/Tests/Wamp/WampConnectionTest.php b/tests/Ratchet/Tests/Wamp/WampConnectionTest.php new file mode 100644 index 0000000..43a1127 --- /dev/null +++ b/tests/Ratchet/Tests/Wamp/WampConnectionTest.php @@ -0,0 +1,67 @@ + 'world', 'herp' => 'derp'); + + + $decor->callResult($callId, $data); + $resultString = $conn->last['send']; + + $this->assertEquals(array(3, $callId, $data), json_decode($resultString, true)); + } + + public function testCallError() { + $conn = new Connection; + $decor = new WampConnection($conn); + + $callId = uniqid(); + $uri = 'http://example.com/end/point'; + + $decor->callError($callId, $uri); + $resultString = $conn->last['send']; + + $this->assertEquals(array(4, $callId, $uri, ''), json_decode($resultString, true)); + } + + public function testDetailedCallError() { + $conn = new Connection; + $decor = new WampConnection($conn); + + $callId = uniqid(); + $uri = 'http://example.com/end/point'; + $desc = 'beep boop beep'; + $detail = 'Error: Too much awesome'; + + $decor->callError($callId, $uri, $desc, $detail); + $resultString = $conn->last['send']; + + $this->assertEquals(array(4, $callId, $uri, $desc, $detail), json_decode($resultString, true)); + } + + public function testPrefix() { + $conn = new WampConnection(new Connection); + + $shortOut = 'outgoing'; + $longOut = 'http://example.com/outoing'; + + $conn->prefix($shortOut, $longOut); + } + + public function testGetUriWhenNoCurieGiven() { + $conn = new WampConnection(new Connection); + $uri = 'http://example.com/noshort'; + + $this->assertEquals($uri, $conn->getUri($uri)); + } +} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/WAMP/WAMPServerComponentTest.php b/tests/Ratchet/Tests/Wamp/WampServerTest.php similarity index 54% rename from tests/Ratchet/Tests/Component/WAMP/WAMPServerComponentTest.php rename to tests/Ratchet/Tests/Wamp/WampServerTest.php index b4f3875..f829859 100644 --- a/tests/Ratchet/Tests/Component/WAMP/WAMPServerComponentTest.php +++ b/tests/Ratchet/Tests/Wamp/WampServerTest.php @@ -1,29 +1,27 @@ _app = new TestComponent; - $this->_comp = new WAMPServerComponent($this->_app); + $this->_comp = new WampServer($this->_app); } protected function newConn() { - return new Connection(new FakeSocket); + return new Connection; } public function invalidMessageProvider() { @@ -40,20 +38,19 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase { * @dataProvider invalidMessageProvider */ public function testInvalidMessages($type) { - $this->setExpectedException('\\Ratchet\\Component\\WAMP\\Exception'); + $this->setExpectedException('\\Ratchet\\Wamp\\Exception'); - $this->_comp->onMessage($this->newConn(), json_encode(array($type))); + $conn = $this->newConn(); + $this->_comp->onOpen($conn); + $this->_comp->onMessage($conn, json_encode(array($type))); } - /** - * @covers Ratchet\Component\WAMP\Command\Action\Welcome - */ public function testWelcomeMessage() { - $conn = new Connection(new FakeSocket); + $conn = $this->newConn(); - $return = $this->_comp->onOpen($conn); - $action = $return->pop(); - $message = $action->getMessage(); + $this->_comp->onOpen($conn); + + $message = $conn->last['send']; $json = json_decode($message); $this->assertEquals(4, count($json)); @@ -66,7 +63,10 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase { $uri = 'http://example.com'; $clientMessage = array(5, $uri); - $this->_comp->onMessage($this->newConn(), json_encode($clientMessage)); + $conn = $this->newConn(); + + $this->_comp->onOpen($conn); + $this->_comp->onMessage($conn, json_encode($clientMessage)); $this->assertEquals($uri, $this->_app->last['onSubscribe'][1]); } @@ -75,7 +75,10 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase { $uri = 'http://example.com/endpoint'; $clientMessage = array(6, $uri); - $this->_comp->onMessage($this->newConn(), json_encode($clientMessage)); + $conn = $this->newConn(); + + $this->_comp->onOpen($conn); + $this->_comp->onMessage($conn, json_encode($clientMessage)); $this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]); } @@ -104,7 +107,10 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase { $id = uniqid(); $clientMessage = array_merge(array(2, $id, $uri), $args); - $this->_comp->onMessage($this->newConn(), json_encode($clientMessage)); + $conn = $this->newConn(); + + $this->_comp->onOpen($conn); + $this->_comp->onMessage($conn, json_encode($clientMessage)); $this->assertEquals($id, $this->_app->last['onCall'][1]); $this->assertEquals($uri, $this->_app->last['onCall'][2]); @@ -133,70 +139,68 @@ class WAMPServerComponentTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider eventProvider - * @covers Ratchet\Component\WAMP\Command\Action\Event */ public function testEvent($topic, $payload) { - $event = new Event($this->newConn()); - $event->setEvent($topic, $payload); + $conn = new WampConnection($this->newConn()); + $conn->event($topic, $payload); - $eventString = $event->getMessage(); + $eventString = $conn->last['send']; $this->assertSame(array(8, $topic, $payload), json_decode($eventString, true)); } public function testOnClosePropagation() { - $conn = $this->newConn(); + $conn = new Connection; + $this->_comp->onOpen($conn); $this->_comp->onClose($conn); - $this->assertSame($conn, $this->_app->last['onClose'][0]); + $class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection'); + $method = $class->getMethod('getConnection'); + $method->setAccessible(true); + + $check = $method->invokeArgs($this->_app->last['onClose'][0], array()); + + $this->assertSame($conn, $check); } public function testOnErrorPropagation() { - $conn = $this->newConn(); + $conn = new Connection; - try { - throw new \Exception('Nope'); - } catch (\Exception $e) { - } + $e = new \Exception('Nope'); + $this->_comp->onOpen($conn); $this->_comp->onError($conn, $e); - $this->assertSame($conn, $this->_app->last['onError'][0]); + $class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection'); + $method = $class->getMethod('getConnection'); + $method->setAccessible(true); + + $check = $method->invokeArgs($this->_app->last['onError'][0], array()); + + $this->assertSame($conn, $check); $this->assertSame($e, $this->_app->last['onError'][1]); } - /** - * @covers Ratchet\Component\WAMP\Command\Action\Prefix - */ public function testPrefix() { - $conn = $this->newConn(); + $conn = new WampConnection($this->newConn()); $this->_comp->onOpen($conn); - $shortOut = 'outgoing'; - $longOut = 'http://example.com/outoing'; + $shortIn = 'incoming'; + $longIn = 'http://example.com/incoming/'; - $shortIn = 'incoming'; - $shortIn = 'http://example.com/incoming/'; + $this->_comp->onMessage($conn, json_encode(array(1, $shortIn, $longIn))); - $this->assertTrue(is_callable($conn->WAMP->addPrefix)); - - $cb = $conn->WAMP->addPrefix; - $cb($shortOut, $longOut); - - $return = $this->_comp->onMessage($conn, json_encode(array(1, $shortIn, $shortOut))); - $command = $return->pop(); - - $this->assertInstanceOf('Ratchet\\Component\\WAMP\\Command\\Action\\Prefix', $command); - $this->assertEquals($shortOut, $command->getCurie()); - $this->assertEquals($longOut, $command->getUri()); - - $this->assertEquals(array(1, $shortOut, $longOut), json_decode($command->getMessage())); + $this->assertEquals($longIn, $conn->WAMP->prefixes[$shortIn]); + $this->assertEquals($longIn, $conn->getUri($shortIn)); } public function testMessageMustBeJson() { - $this->setExpectedException('\\Ratchet\\Component\\WAMP\\JsonException'); + $this->setExpectedException('\\Ratchet\\Wamp\\JsonException'); - $this->_comp->onMessage($this->newConn(), 'Hello World!'); + $conn = new Connection; + + $this->_comp->onOpen($conn); + $this->_comp->onMessage($conn, 'Hello World!'); } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php b/tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php new file mode 100644 index 0000000..01d5df0 --- /dev/null +++ b/tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php @@ -0,0 +1,67 @@ +factory = RequestFactory::getInstance(); + } + + public function testMessageProvider() { + return array( + 'status' => 'GET / HTTP/1.1' + , 'headers' => array( + 'Upgrade' => 'WebSocket' + , 'Connection' => 'Upgrade' + , 'Host' => 'localhost:8000' + , 'Sec-WebSocket-Key1' => '> b3lU Z0 fh f 3+83394 6 (zG4' + , 'Sec-WebSocket-Key2' => ',3Z0X0677 dV-d [159 Z*4' + ) + , 'body' => "123456\r\n\r\n" + ); + } + + public function combineMessage($status, array $headers, $body = '') { + $message = $status . "\r\n"; + + foreach ($headers as $key => $val) { + $message .= "{$key}: {$val}\r\n"; + } + + $message .= "\r\n{$body}"; + + return $message; + } + + public function testExpectedDataFromGuzzleHeaders() { + $parts = $this->testMessageProvider(); + $message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']); + $object = $this->factory->fromMessage($message); + + foreach ($parts['headers'] as $key => $val) { + $this->assertEquals($val, $object->getHeader($key, true)); + } + } + + public function testExpectedDataFromNonGuzzleHeaders() { + $parts = $this->testMessageProvider(); + $message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']); + $object = $this->factory->fromMessage($message); + + $this->assertNull($object->getHeader('Nope', true)); + $this->assertNull($object->getHeader('Nope')); + } + + public function testExpectedDataFromNonGuzzleBody() { + $parts = $this->testMessageProvider(); + $message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']); + $object = $this->factory->fromMessage($message); + + $this->assertEquals($parts['body'], (string)$object->getBody()); + } +} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Component/WebSocket/Version/Hixie76Test.php b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php similarity index 82% rename from tests/Ratchet/Tests/Component/WebSocket/Version/Hixie76Test.php rename to tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php index bf08215..9773c13 100644 --- a/tests/Ratchet/Tests/Component/WebSocket/Version/Hixie76Test.php +++ b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php @@ -1,9 +1,9 @@ isInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\VersionInterface'); + $constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface'); $this->assertThat($this->_version, $constraint); } diff --git a/tests/Ratchet/Tests/Component/WebSocket/Version/HyBi10Test.php b/tests/Ratchet/Tests/WebSocket/Version/HyBi10Test.php similarity index 87% rename from tests/Ratchet/Tests/Component/WebSocket/Version/HyBi10Test.php rename to tests/Ratchet/Tests/WebSocket/Version/HyBi10Test.php index cac5280..c8ded8b 100644 --- a/tests/Ratchet/Tests/Component/WebSocket/Version/HyBi10Test.php +++ b/tests/Ratchet/Tests/WebSocket/Version/HyBi10Test.php @@ -1,10 +1,10 @@ isInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\VersionInterface'); + $constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface'); $this->assertThat($this->_version, $constraint); } diff --git a/tests/Ratchet/Tests/Component/WebSocket/Version/RFC6455/FrameTest.php b/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php similarity index 98% rename from tests/Ratchet/Tests/Component/WebSocket/Version/RFC6455/FrameTest.php rename to tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php index 820bf6c..84352fb 100644 --- a/tests/Ratchet/Tests/Component/WebSocket/Version/RFC6455/FrameTest.php +++ b/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php @@ -1,9 +1,9 @@ isInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\VersionInterface'); + $constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface'); $this->assertThat($this->_version, $constraint); } @@ -116,7 +116,7 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase { * @dataProvider headerHandshakeProvider */ public function testVariousHeadersToCheckHandshakeTolerance($pass, $header) { - $request = RequestFactory::fromMessage($header); + $request = RequestFactory::getInstance()->fromMessage($header); if ($pass) { $this->assertInstanceOf('\\Guzzle\\Http\\Message\\Response', $this->_version->handshake($request)); @@ -125,4 +125,12 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase { $this->_version->handshake($request); } } + + public function testNewMessage() { + $this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Message', $this->_version->newMessage()); + } + + public function testNewFrame() { + $this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Frame', $this->_version->newFrame()); + } } \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index 9c15fdc..0000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,5 +0,0 @@ -