diff --git a/.travis.yml b/.travis.yml index 6161d58..5625a26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 5.3.3 - 5.3 - 5.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 13fb90f..e0771e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ CHANGELOG --- +* 0.3.0 (2013-xx-xx) + + * BC: Require hostname and do Origin HTTP header check against it by default, helping prevent CSRF attacks + * Added HTTP Router component to allowing for a single Ratchet server to handle multiple apps + * BC: Decoupled HTTP from WebSocket component + * 0.2.5 (2013-04-01) * Fixed Hixie-76 handshake bug diff --git a/Makefile b/Makefile index a1b8a5b..d5e12a2 100644 --- a/Makefile +++ b/Makefile @@ -32,4 +32,5 @@ apidocs: -s vendor/react \ -s vendor/guzzle \ -s vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session \ + -s vendor/symfony/http-foundation/Symfony/Component/Routing \ -s vendor/evenement/evenement/src/Evenement diff --git a/README.md b/README.md index 5cd3fa9..3f78953 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,9 @@ Need help? Have a question? Want to provide feedback? Write a message on the addRoute('/echo', new AbFuzzyServer); + $router->addRoute('/chat', new Chat); + + $server = IoServer::factory($router, 8000); $server->run(); ``` diff --git a/composer.json b/composer.json index f83a0b0..89eec01 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,10 @@ } } , "require": { - "php": ">=5.3.3" - , "react/socket": "0.2.*" + "php": ">=5.3.9" + , "react/socket": "~0.2" , "guzzle/http": "~3.0" - , "symfony/http-foundation": "~2.1" + , "symfony/http-foundation": "~2.2" + , "symfony/routing": "~2.2" } } diff --git a/composer.lock b/composer.lock index 9df5a60..f7408e1 100644 --- a/composer.lock +++ b/composer.lock @@ -1,5 +1,9 @@ { - "hash": "9ccce99ef687cb79dad8a4c581f38cc5", + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "6006ea344879ef78bf1a490e630a74fc", "packages": [ { "name": "evenement/evenement", @@ -42,17 +46,17 @@ }, { "name": "guzzle/common", - "version": "v3.3.0", + "version": "v3.4.1", "target-dir": "Guzzle/Common", "source": { "type": "git", "url": "https://github.com/guzzle/common.git", - "reference": "v3.3.0" + "reference": "v3.4.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/common/zipball/v3.3.0", - "reference": "v3.3.0", + "url": "https://api.github.com/repos/guzzle/common/zipball/v3.4.1", + "reference": "v3.4.1", "shasum": "" }, "require": { @@ -62,7 +66,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -82,21 +86,21 @@ "event", "exception" ], - "time": "2013-03-04 00:41:45" + "time": "2013-04-16 20:56:26" }, { "name": "guzzle/http", - "version": "v3.3.0", + "version": "v3.4.1", "target-dir": "Guzzle/Http", "source": { "type": "git", "url": "https://github.com/guzzle/http.git", - "reference": "v3.3.0" + "reference": "v3.4.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/http/zipball/v3.3.0", - "reference": "v3.3.0", + "url": "https://api.github.com/repos/guzzle/http/zipball/v3.4.1", + "reference": "v3.4.1", "shasum": "" }, "require": { @@ -111,7 +115,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -139,21 +143,21 @@ "http", "http client" ], - "time": "2013-03-03 21:40:51" + "time": "2013-04-16 20:27:11" }, { "name": "guzzle/parser", - "version": "v3.3.0", + "version": "v3.4.1", "target-dir": "Guzzle/Parser", "source": { "type": "git", "url": "https://github.com/guzzle/parser.git", - "reference": "v3.3.0" + "reference": "v3.4.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.3.0", - "reference": "v3.3.0", + "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.4.1", + "reference": "v3.4.1", "shasum": "" }, "require": { @@ -162,7 +166,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -183,31 +187,34 @@ "message", "url" ], - "time": "2013-01-12 21:43:21" + "time": "2013-03-07 22:13:59" }, { "name": "guzzle/stream", - "version": "v3.3.0", + "version": "v3.4.1", "target-dir": "Guzzle/Stream", "source": { "type": "git", "url": "https://github.com/guzzle/stream.git", - "reference": "v3.3.0" + "reference": "v3.4.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.3.0", - "reference": "v3.3.0", + "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.4.1", + "reference": "v3.4.1", "shasum": "" }, "require": { "guzzle/common": "self.version", "php": ">=5.3.2" }, + "suggest": { + "guzzle/http": "To convert Guzzle request objects to PHP streams" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -233,21 +240,21 @@ "component", "stream" ], - "time": "2013-03-03 03:07:02" + "time": "2013-04-06 18:28:51" }, { "name": "react/event-loop", - "version": "v0.2.7", + "version": "v0.3.1", "target-dir": "React/EventLoop", "source": { "type": "git", - "url": "https://github.com/reactphp/event-loop", - "reference": "v0.2.7" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "v0.3.1" }, "dist": { "type": "zip", - "url": "https://github.com/reactphp/event-loop/archive/v0.2.7.zip", - "reference": "v0.2.7", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/v0.3.1", + "reference": "v0.3.1", "shasum": "" }, "require": { @@ -260,7 +267,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.2-dev" + "dev-master": "0.3-dev" } }, "autoload": { @@ -276,33 +283,33 @@ "keywords": [ "event-loop" ], - "time": "2013-01-05 11:41:26" + "time": "2013-01-14 23:11:47" }, { "name": "react/socket", - "version": "v0.2.7", + "version": "v0.3.1", "target-dir": "React/Socket", "source": { "type": "git", - "url": "https://github.com/reactphp/socket", - "reference": "v0.2.7" + "url": "https://github.com/reactphp/socket.git", + "reference": "v0.3.1" }, "dist": { "type": "zip", - "url": "https://github.com/reactphp/socket/archive/v0.2.7.zip", - "reference": "v0.2.7", + "url": "https://api.github.com/repos/reactphp/socket/zipball/v0.3.1", + "reference": "v0.3.1", "shasum": "" }, "require": { "evenement/evenement": "1.0.*", "php": ">=5.3.3", - "react/event-loop": "0.2.*", - "react/stream": "0.2.*" + "react/event-loop": "0.3.*", + "react/stream": "0.3.*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.2-dev" + "dev-master": "0.3-dev" } }, "autoload": { @@ -318,21 +325,21 @@ "keywords": [ "Socket" ], - "time": "2012-12-14 00:58:14" + "time": "2013-04-20 14:53:10" }, { "name": "react/stream", - "version": "v0.2.7", + "version": "v0.3.1", "target-dir": "React/Stream", "source": { "type": "git", - "url": "https://github.com/reactphp/stream", - "reference": "v0.2.7" + "url": "https://github.com/reactphp/stream.git", + "reference": "v0.3.1" }, "dist": { "type": "zip", - "url": "https://github.com/reactphp/stream/archive/v0.2.7.zip", - "reference": "v0.2.7", + "url": "https://api.github.com/repos/reactphp/stream/zipball/v0.3.1", + "reference": "v0.3.1", "shasum": "" }, "require": { @@ -340,13 +347,13 @@ "php": ">=5.3.3" }, "suggest": { - "react/event-loop": "0.2.*", - "react/promise": "1.0.*" + "react/event-loop": "0.3.*", + "react/promise": "~1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.2-dev" + "dev-master": "0.3-dev" } }, "autoload": { @@ -363,21 +370,21 @@ "pipe", "stream" ], - "time": "2012-12-14 00:58:14" + "time": "2013-04-21 13:25:49" }, { "name": "symfony/event-dispatcher", - "version": "v2.2.0", + "version": "v2.2.1", "target-dir": "Symfony/Component/EventDispatcher", "source": { "type": "git", "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "v2.2.0-RC3" + "reference": "v2.2.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.2.0-RC3", - "reference": "v2.2.0-RC3", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.2.1", + "reference": "v2.2.1", "shasum": "" }, "require": { @@ -421,17 +428,17 @@ }, { "name": "symfony/http-foundation", - "version": "v2.2.0", + "version": "v2.2.1", "target-dir": "Symfony/Component/HttpFoundation", "source": { "type": "git", "url": "https://github.com/symfony/HttpFoundation.git", - "reference": "v2.2.0" + "reference": "v2.2.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.2.0", - "reference": "v2.2.0", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.2.1", + "reference": "v2.2.1", "shasum": "" }, "require": { @@ -467,7 +474,65 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "http://symfony.com", - "time": "2013-02-26 09:42:13" + "time": "2013-04-06 10:15:43" + }, + { + "name": "symfony/routing", + "version": "v2.2.1", + "target-dir": "Symfony/Component/Routing", + "source": { + "type": "git", + "url": "https://github.com/symfony/Routing.git", + "reference": "v2.2.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.2.1", + "reference": "v2.2.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "doctrine/common": ">=2.2,<3.0", + "psr/log": ">=1.0,<2.0", + "symfony/config": ">=2.2,<2.3-dev", + "symfony/yaml": ">=2.0,<3.0" + }, + "suggest": { + "doctrine/common": "~2.2", + "symfony/config": "2.2.*", + "symfony/yaml": "2.2.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Routing\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Routing Component", + "homepage": "http://symfony.com", + "time": "2013-03-23 12:03:22" } ], "packages-dev": [ @@ -481,7 +546,7 @@ ], "platform": { - "php": ">=5.3.3" + "php": ">=5.3.9" }, "platform-dev": [ diff --git a/src/Ratchet/ConnectionInterface.php b/src/Ratchet/ConnectionInterface.php index cc4d74b..088823d 100644 --- a/src/Ratchet/ConnectionInterface.php +++ b/src/Ratchet/ConnectionInterface.php @@ -5,7 +5,7 @@ namespace Ratchet; * The version of Ratchet being used * @var string */ -const VERSION = 'Ratchet/0.2.5'; +const VERSION = 'Ratchet/0.3-beta'; /** * A proxy object representing a connection to the application diff --git a/src/Ratchet/WebSocket/Guzzle/Http/Message/RequestFactory.php b/src/Ratchet/Http/Guzzle/Http/Message/RequestFactory.php similarity index 89% rename from src/Ratchet/WebSocket/Guzzle/Http/Message/RequestFactory.php rename to src/Ratchet/Http/Guzzle/Http/Message/RequestFactory.php index c1f6b45..0ebcc35 100644 --- a/src/Ratchet/WebSocket/Guzzle/Http/Message/RequestFactory.php +++ b/src/Ratchet/Http/Guzzle/Http/Message/RequestFactory.php @@ -1,5 +1,5 @@ _httpServer = $component; + $this->_reqParser = new HttpRequestParser; + } + + /** + * {@inheritdoc} + */ + public function onOpen(ConnectionInterface $conn) { + $conn->httpHeadersReceived = false; + } + + /** + * {@inheritdoc} + */ + public function onMessage(ConnectionInterface $from, $msg) { + if (true !== $from->httpHeadersReceived) { + try { + if (null === ($request = $this->_reqParser->onMessage($from, $msg))) { + return; + } + } catch (\OverflowException $oe) { + return $this->close($from, 413); + } + + $from->httpHeadersReceived = true; + + if (isset($from->remoteAddress) && '127.0.0.1' == $from->remoteAddress && $request->hasHeader('X-Forwarded-For')) { + $from->remoteAddress = $request->getHeader('X-Forwarded-For', true); + } + + return $this->_httpServer->onOpen($from, $request); + } + + $this->_httpServer->onMessage($from, $msg); + } + + /** + * {@inheritdoc} + */ + public function onClose(ConnectionInterface $conn) { + if ($conn->httpHeadersReceived) { + $this->_httpServer->onClose($conn); + } + } + + /** + * {@inheritdoc} + */ + public function onError(ConnectionInterface $conn, \Exception $e) { + if ($conn->httpHeadersReceived) { + $this->_httpServer->onError($conn, $e); + } else { + $this->close($conn, 500); + } + } + + /** + * Close a connection with an HTTP response + * @param \Ratchet\ConnectionInterface $conn + * @param int $code HTTP status code + * @return null + */ + protected function close(ConnectionInterface $conn, $code = 400) { + $response = new Response($code, array( + 'X-Powered-By' => \Ratchet\VERSION + )); + + $conn->send((string)$response); + $conn->close(); + } +} diff --git a/src/Ratchet/Http/HttpServerInterface.php b/src/Ratchet/Http/HttpServerInterface.php new file mode 100644 index 0000000..79b7d55 --- /dev/null +++ b/src/Ratchet/Http/HttpServerInterface.php @@ -0,0 +1,14 @@ +_routes = $routes; + $this->_server = new HttpServer(new Router(new UrlMatcher($routes, new RequestContext))); + } + + public function addRoute($path, MessageComponentInterface $controller) { + $this->_routes->add(uniqid(), new Route($path, array( + '_controller' => new WsServer($controller) + ))); + } + + public function addHttpRoute($path, HttpServerInterface $controller) { + $this->_routes->add(uniqid(), new Route($path, array( + '_controller' => $controller + ))); + } + + /** + * {@inheritdoc} + */ + function onOpen(ConnectionInterface $conn) { + $this->_server->onOpen($conn); + } + + /** + * {@inheritdoc} + */ + function onMessage(ConnectionInterface $from, $msg) { + $this->_server->onMessage($from, $msg); + } + + /** + * {@inheritdoc} + */ + function onClose(ConnectionInterface $conn) { + $this->_server->onClose($conn); + } + + /** + * {@inheritdoc} + */ + function onError(ConnectionInterface $conn, \Exception $e) { + $this->_server->onError($conn, $e); + } +} \ No newline at end of file diff --git a/src/Ratchet/Http/Router.php b/src/Ratchet/Http/Router.php new file mode 100644 index 0000000..08e0c1a --- /dev/null +++ b/src/Ratchet/Http/Router.php @@ -0,0 +1,84 @@ +_matcher = $matcher; + } + + /** + * {@inheritdoc} + * @throws \UnexpectedValueException If a controller is not \Ratchet\Http\HttpServerInterface + */ + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { + if (null === $request) { + throw new \UnexpectedValueException('$request can not be null'); + } + + try { + $route = $this->_matcher->match($request->getPath()); + } catch (MethodNotAllowedException $nae) { + return $this->close($conn, 403); + } catch (ResourceNotFoundException $nfe) { + return $this->close($conn, 404); + } + + if (is_string($route['_controller']) && class_exists($route['_controller'])) { + $route['_controller'] = new $route['_controller']; + } + + if (!($route['_controller'] instanceof HttpServerInterface)) { + throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface'); + } + + $conn->controller = $route['_controller']; + $conn->controller->onOpen($conn, $request); + } + + /** + * {@inheritdoc} + */ + function onMessage(ConnectionInterface $from, $msg) { + $from->controller->onMessage($from, $msg); + } + + /** + * {@inheritdoc} + */ + function onClose(ConnectionInterface $conn) { + $conn->controller->onClose($conn); + } + + /** + * {@inheritdoc} + */ + function onError(ConnectionInterface $conn, \Exception $e) { + $conn->controller->onError($conn, $e); + } + + /** + * Close a connection with an HTTP response + * @param \Ratchet\ConnectionInterface $conn + * @param int $code HTTP status code + * @return null + */ + protected function close(ConnectionInterface $conn, $code = 400) { + $response = new Response($code, array( + 'X-Powered-By' => \Ratchet\VERSION + )); + + $conn->send((string)$response); + $conn->close(); + } +} \ No newline at end of file diff --git a/src/Ratchet/Session/SessionProvider.php b/src/Ratchet/Session/SessionProvider.php index 7203dda..9cd4dd3 100644 --- a/src/Ratchet/Session/SessionProvider.php +++ b/src/Ratchet/Session/SessionProvider.php @@ -42,7 +42,7 @@ class SessionProvider implements MessageComponentInterface, WsServerInterface { * @param \SessionHandlerInterface $handler * @param array $options * @param \Ratchet\Session\Serialize\HandlerInterface $serializer - * @throws \RuntimeExcpetion + * @throws \RuntimeException */ public function __construct(MessageComponentInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) { $this->_app = $app; @@ -71,7 +71,7 @@ class SessionProvider implements MessageComponentInterface, WsServerInterface { * {@inheritdoc} */ function onOpen(ConnectionInterface $conn) { - if (null === ($id = $conn->WebSocket->request->getCookie(ini_get('session.name')))) { + if (!isset($conn->WebSocket) || null === ($id = $conn->WebSocket->request->getCookie(ini_get('session.name')))) { $saveHandler = $this->_null; $id = ''; } else { diff --git a/src/Ratchet/WebSocket/Version/HyBi10.php b/src/Ratchet/WebSocket/Version/HyBi10.php index f557a18..5d89078 100644 --- a/src/Ratchet/WebSocket/Version/HyBi10.php +++ b/src/Ratchet/WebSocket/Version/HyBi10.php @@ -4,7 +4,8 @@ use Guzzle\Http\Message\RequestInterface; class HyBi10 extends RFC6455 { public function isProtocol(RequestInterface $request) { - $version = (int)$request->getHeader('Sec-WebSocket-Version', -1); + $version = $request->hasHeader('Sec-WebSocket-Version') ? (int)$request->getHeader('Sec-WebSocket-Version', true) : -1; + return ($version >= 6 && $version < 13); } diff --git a/src/Ratchet/WebSocket/Version/RFC6455.php b/src/Ratchet/WebSocket/Version/RFC6455.php index 94416c3..22a96a0 100644 --- a/src/Ratchet/WebSocket/Version/RFC6455.php +++ b/src/Ratchet/WebSocket/Version/RFC6455.php @@ -50,7 +50,7 @@ class RFC6455 implements VersionInterface { * {@inheritdoc} */ public function isProtocol(RequestInterface $request) { - $version = (int)$request->getHeader('Sec-WebSocket-Version', -1); + $version = $request->hasHeader('Sec-WebSocket-Version') ? (int)$request->getHeader('Sec-WebSocket-Version', true) : -1; return ($this->getVersionNumber() === $version); } @@ -73,7 +73,7 @@ class RFC6455 implements VersionInterface { return new Response(101, array( 'Upgrade' => 'websocket' , 'Connection' => 'Upgrade' - , 'Sec-WebSocket-Accept' => $this->sign($request->getHeader('Sec-WebSocket-Key')) + , 'Sec-WebSocket-Accept' => $this->sign($request->getHeader('Sec-WebSocket-Key', true)) )); } diff --git a/src/Ratchet/WebSocket/Version/RFC6455/HandshakeVerifier.php b/src/Ratchet/WebSocket/Version/RFC6455/HandshakeVerifier.php index 7fbf452..ea17d08 100644 --- a/src/Ratchet/WebSocket/Version/RFC6455/HandshakeVerifier.php +++ b/src/Ratchet/WebSocket/Version/RFC6455/HandshakeVerifier.php @@ -113,22 +113,6 @@ class HandshakeVerifier { return (16 === strlen(base64_decode((string)$val))); } - /** - * Verify Origin matches RFC6454 IF it is set - * Origin is an optional field - * @param string|null - * @return bool - * @todo Implement verification functionality - see section 4.2.1.7 - */ - public function verifyOrigin($val) { - if (null === $val) { - return true; - } - - // logic here - return true; - } - /** * Verify the version passed matches this RFC * @param string|int MUST equal 13|"13" diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 281f16c..0373a14 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -2,9 +2,11 @@ namespace Ratchet\WebSocket; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; +use Ratchet\Http\HttpServerInterface; +use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; use Ratchet\WebSocket\Version; use Ratchet\WebSocket\Encoding\ToggleableValidator; -use Guzzle\Http\Message\Response; /** * The adapter to handle WebSocket requests/responses @@ -12,14 +14,7 @@ use Guzzle\Http\Message\Response; * @link http://ca.php.net/manual/en/ref.http.php * @link http://dev.w3.org/html5/websockets/ */ -class WsServer implements MessageComponentInterface { - /** - * Buffers incoming HTTP requests returning a Guzzle Request when coalesced - * @var HttpRequestParser - * @note May not expose this in the future, may do through facade methods - */ - public $reqParser; - +class WsServer implements HttpServerInterface { /** * Manage the various WebSocket versions to support * @var VersionManager @@ -31,7 +26,7 @@ class WsServer implements MessageComponentInterface { * Decorated component * @var \Ratchet\MessageComponentInterface */ - protected $_decorating; + public $component; /** * @var \SplObjectStorage @@ -39,9 +34,7 @@ class WsServer implements MessageComponentInterface { protected $connections; /** - * For now, array_push accepted subprotocols to this array - * @deprecated - * @temporary + * Holder of accepted protocols, implement through WampServerInterface */ protected $acceptedSubProtocols = array(); @@ -62,7 +55,6 @@ class WsServer implements MessageComponentInterface { * If you want to enable sub-protocols have your component implement WsServerInterface as well */ public function __construct(MessageComponentInterface $component) { - $this->reqParser = new HttpRequestParser; $this->versioner = new VersionManager; $this->validator = new ToggleableValidator; @@ -72,16 +64,23 @@ class WsServer implements MessageComponentInterface { ->enableVersion(new Version\Hixie76) ; - $this->_decorating = $component; + $this->component = $component; $this->connections = new \SplObjectStorage; } /** * {@inheritdoc} */ - public function onOpen(ConnectionInterface $conn) { - $conn->WebSocket = new \StdClass; + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { + if (null === $request) { + throw new \UnexpectedValueException('$request can not be null'); + } + + $conn->WebSocket = new \StdClass; + $conn->WebSocket->request = $request; $conn->WebSocket->established = false; + + $this->attemptUpgrade($conn); } /** @@ -92,50 +91,44 @@ class WsServer implements MessageComponentInterface { return $from->WebSocket->version->onMessage($this->connections[$from], $msg); } - if (isset($from->WebSocket->request)) { - $from->WebSocket->request->getBody()->write($msg); + $this->attemptUpgrade($from, $msg); + } + + protected function attemptUpgrade(ConnectionInterface $conn, $data = '') { + if ('' !== $data) { + $conn->WebSocket->request->getBody()->write($data); } else { - try { - if (null === ($request = $this->reqParser->onMessage($from, $msg))) { - return; - } - } catch (\OverflowException $oe) { - return $this->close($from, 413); + if (!$this->versioner->isVersionEnabled($conn->WebSocket->request)) { + return $this->close($conn); } - if (!$this->versioner->isVersionEnabled($request)) { - return $this->close($from); - } - - $from->WebSocket->request = $request; - $from->WebSocket->version = $this->versioner->getVersion($request); + $conn->WebSocket->version = $this->versioner->getVersion($conn->WebSocket->request); } try { - $response = $from->WebSocket->version->handshake($from->WebSocket->request); + $response = $conn->WebSocket->version->handshake($conn->WebSocket->request); } catch (\UnderflowException $e) { return; } - // This needs to be refactored later on, incorporated with routing - if ('' !== ($agreedSubProtocols = $this->getSubProtocolString($from->WebSocket->request->getTokenizedHeader('Sec-WebSocket-Protocol', ',')))) { + if ('' !== ($agreedSubProtocols = $this->getSubProtocolString($conn->WebSocket->request->getTokenizedHeader('Sec-WebSocket-Protocol', ',')))) { $response->setHeader('Sec-WebSocket-Protocol', $agreedSubProtocols); } $response->setHeader('X-Powered-By', \Ratchet\VERSION); - $from->send((string)$response); + $conn->send((string)$response); if (101 != $response->getStatusCode()) { - return $from->close(); + return $conn->close(); } - $upgraded = $from->WebSocket->version->upgradeConnection($from, $this->_decorating); + $upgraded = $conn->WebSocket->version->upgradeConnection($conn, $this->component); - $this->connections->attach($from, $upgraded); + $this->connections->attach($conn, $upgraded); $upgraded->WebSocket->established = true; - return $this->_decorating->onOpen($upgraded); + return $this->component->onOpen($upgraded); } /** @@ -146,7 +139,7 @@ class WsServer implements MessageComponentInterface { $decor = $this->connections[$conn]; $this->connections->detach($conn); - $this->_decorating->onClose($decor); + $this->component->onClose($decor); } } @@ -155,7 +148,7 @@ class WsServer implements MessageComponentInterface { */ public function onError(ConnectionInterface $conn, \Exception $e) { if ($conn->WebSocket->established) { - $this->_decorating->onError($this->connections[$conn], $e); + $this->component->onError($this->connections[$conn], $e); } else { $conn->close(); } @@ -189,8 +182,8 @@ class WsServer implements MessageComponentInterface { */ public function isSubProtocolSupported($name) { if (!$this->isSpGenerated) { - if ($this->_decorating instanceof WsServerInterface) { - $this->acceptedSubProtocols = array_flip($this->_decorating->getSubProtocols()); + if ($this->component instanceof WsServerInterface) { + $this->acceptedSubProtocols = array_flip($this->component->getSubProtocols()); } $this->isSpGenerated = true; @@ -223,7 +216,6 @@ class WsServer implements MessageComponentInterface { * Close a connection with an HTTP response * @param \Ratchet\ConnectionInterface $conn * @param int $code HTTP status code - * @return void */ protected function close(ConnectionInterface $conn, $code = 400) { $response = new Response($code, array( diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-libev.php b/tests/AutobahnTestSuite/bin/fuzzingserver-libev.php index ccd0613..fb6d03f 100644 --- a/tests/AutobahnTestSuite/bin/fuzzingserver-libev.php +++ b/tests/AutobahnTestSuite/bin/fuzzingserver-libev.php @@ -4,7 +4,7 @@ $loop = new React\EventLoop\LibEvLoop; $sock = new React\Socket\Server($loop); - $app = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); $port = $argc > 1 ? $argv[1] : 8000; $sock->listen($port, '0.0.0.0'); diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php b/tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php index 166bd45..ea1fbad 100644 --- a/tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php +++ b/tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php @@ -4,7 +4,7 @@ $loop = new React\EventLoop\LibEventLoop; $sock = new React\Socket\Server($loop); - $app = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); $port = $argc > 1 ? $argv[1] : 8000; $sock->listen($port, '0.0.0.0'); diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-libuv.php b/tests/AutobahnTestSuite/bin/fuzzingserver-libuv.php index ccd0613..fb6d03f 100644 --- a/tests/AutobahnTestSuite/bin/fuzzingserver-libuv.php +++ b/tests/AutobahnTestSuite/bin/fuzzingserver-libuv.php @@ -4,7 +4,7 @@ $loop = new React\EventLoop\LibEvLoop; $sock = new React\Socket\Server($loop); - $app = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); $port = $argc > 1 ? $argv[1] : 8000; $sock->listen($port, '0.0.0.0'); diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php b/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php index 589cdd9..b20b3a1 100644 --- a/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php +++ b/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php @@ -4,8 +4,9 @@ $loop = new React\EventLoop\StreamSelectLoop; $sock = new React\Socket\Server($loop); - $app = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer); - $app->setEncodingChecks(false); + $web = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer); + $app = new Ratchet\Http\HttpServer($web); + $web->setEncodingChecks(false); $port = $argc > 1 ? $argv[1] : 8000; $sock->listen($port, '0.0.0.0'); diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php b/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php index 4e3e797..98b4448 100644 --- a/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php +++ b/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php @@ -4,7 +4,7 @@ $loop = new React\EventLoop\StreamSelectLoop; $sock = new React\Socket\Server($loop); - $app = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); $port = $argc > 1 ? $argv[1] : 8000; $sock->listen($port, '0.0.0.0'); diff --git a/tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php b/tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php index 707460c..f196c79 100644 --- a/tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php +++ b/tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php @@ -1,7 +1,6 @@ mock = new Connection; + $this->mock = $this->getMock('\Ratchet\ConnectionInterface'); $this->l1 = new ConnectionDecorator($this->mock); $this->l2 = new ConnectionDecorator($this->l1); } diff --git a/tests/Ratchet/Tests/AbstractMessageComponentTestCase.php b/tests/Ratchet/Tests/AbstractMessageComponentTestCase.php new file mode 100644 index 0000000..a9223af --- /dev/null +++ b/tests/Ratchet/Tests/AbstractMessageComponentTestCase.php @@ -0,0 +1,41 @@ +_app = $this->getMock($this->getComponentClassString()); + $decorator = $this->getDecoratorClassString(); + $this->_serv = new $decorator($this->_app); + $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); + + $this->_serv->onOpen($this->_conn); + } + + public function isExpectedConnection() { + return new \PHPUnit_Framework_Constraint_IsInstanceOf($this->getConnectionClassString()); + } + + public function testOpen() { + $this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection()); + $this->_serv->onOpen($this->getMock('\Ratchet\ConnectionInterface')); + } + + public function testOnClose() { + $this->_app->expects($this->once())->method('onClose')->with($this->isExpectedConnection()); + $this->_serv->onClose($this->_conn); + } + + public function testOnError() { + $e = new \Exception('Whoops!'); + $this->_app->expects($this->once())->method('onError')->with($this->isExpectedConnection(), $e); + $this->_serv->onError($this->_conn, $e); + } +} \ No newline at end of file diff --git a/tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php b/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php similarity index 92% rename from tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php rename to tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php index 01d5df0..9709745 100644 --- a/tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php +++ b/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php @@ -1,9 +1,9 @@ getMock('\Ratchet\ConnectionInterface'); $this->parser->maxSize = 20; @@ -42,4 +41,11 @@ class HttpRequestParserTest extends \PHPUnit_Framework_TestCase { $this->parser->onMessage($conn, "Header-Is: Too Big"); } + + public function testReturnTypeIsRequest() { + $conn = $this->getMock('\Ratchet\ConnectionInterface'); + $return = $this->parser->onMessage($conn, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"); + + $this->assertInstanceOf('\Guzzle\Http\Message\RequestInterface', $return); + } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/Http/HttpServerTest.php b/tests/Ratchet/Tests/Http/HttpServerTest.php new file mode 100644 index 0000000..203172a --- /dev/null +++ b/tests/Ratchet/Tests/Http/HttpServerTest.php @@ -0,0 +1,64 @@ +_conn->httpHeadersReceived = true; + } + + public function getConnectionClassString() { + return '\Ratchet\ConnectionInterface'; + } + + public function getDecoratorClassString() { + return '\Ratchet\Http\HttpServer'; + } + + public function getComponentClassString() { + return '\Ratchet\Http\HttpServerInterface'; + } + + public function testOpen() { + $headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"; + + $this->_conn->httpHeadersReceived = false; + $this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection()); + $this->_serv->onMessage($this->_conn, $headers); + } + + public function testOnMessageAfterHeaders() { + $headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"; + $this->_conn->httpHeadersReceived = false; + $this->_serv->onMessage($this->_conn, $headers); + + $message = "Hello World!"; + $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message); + $this->_serv->onMessage($this->_conn, $message); + } + + public function testBufferOverflow() { + $this->_conn->expects($this->once())->method('close'); + $this->_conn->httpHeadersReceived = false; + + $this->_serv->onMessage($this->_conn, str_repeat('a', 5000)); + } + + public function testCloseIfNotEstablished() { + $this->_conn->httpHeadersReceived = false; + $this->_conn->expects($this->once())->method('close'); + $this->_serv->onError($this->_conn, new \Exception('Whoops!')); + } + + public function testBufferHeaders() { + $this->_conn->httpHeadersReceived = false; + $this->_app->expects($this->never())->method('onOpen'); + $this->_app->expects($this->never())->method('onMessage'); + + $this->_serv->onMessage($this->_conn, "GET / HTTP/1.1"); + } +} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Http/RouterTest.php b/tests/Ratchet/Tests/Http/RouterTest.php new file mode 100644 index 0000000..cc3309b --- /dev/null +++ b/tests/Ratchet/Tests/Http/RouterTest.php @@ -0,0 +1,84 @@ +_conn = $this->getMock('\Ratchet\ConnectionInterface'); + $this->_req = $this->getMock('\Guzzle\Http\Message\RequestInterface'); + $this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); + $this->_router = new Router($this->_matcher); + + $this->_req->expects($this->any())->method('getPath')->will($this->returnValue('/whatever')); + } + + public function testFourOhFour() { + $this->_conn->expects($this->once())->method('close'); + + $nope = new ResourceNotFoundException; + $this->_matcher->expects($this->any())->method('match')->will($this->throwException($nope)); + + $this->_router->onOpen($this->_conn, $this->_req); + } + + public function testNullRequest() { + $this->setExpectedException('\UnexpectedValueException'); + $this->_router->onOpen($this->_conn); + } + + public function testControllerIsMessageComponentInterface() { + $this->setExpectedException('\UnexpectedValueException'); + $this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => new \StdClass))); + $this->_router->onOpen($this->_conn, $this->_req); + } + + public function testControllerOnOpen() { + $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); + $this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller))); + $this->_router->onOpen($this->_conn, $this->_req); + + $expectedConn = new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\ConnectionInterface'); + $controller->expects($this->once())->method('onOpen')->with($expectedConn, $this->_req); + + $this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller))); + $this->_router->onOpen($this->_conn, $this->_req); + } + + public function testControllerOnMessageBubbles() { + $message = "The greatest trick the Devil ever pulled was convincing the world he didn't exist"; + $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); + $controller->expects($this->once())->method('onMessage')->with($this->_conn, $message); + + $this->_conn->controller = $controller; + + $this->_router->onMessage($this->_conn, $message); + } + + public function testControllerOnCloseBubbles() { + $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); + $controller->expects($this->once())->method('onClose')->with($this->_conn); + + $this->_conn->controller = $controller; + + $this->_router->onClose($this->_conn); + } + + public function testControllerOnErrorBubbles() { + $e= new \Exception('One cannot be betrayed if one has no exceptions'); + $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); + $controller->expects($this->once())->method('onError')->with($this->_conn, $e); + + $this->_conn->controller = $controller; + + $this->_router->onError($this->_conn, $e); + } +} diff --git a/tests/Ratchet/Tests/Mock/MemorySessionHandler.php b/tests/Ratchet/Tests/Mock/MemorySessionHandler.php deleted file mode 100644 index 87e9953..0000000 --- a/tests/Ratchet/Tests/Mock/MemorySessionHandler.php +++ /dev/null @@ -1,39 +0,0 @@ -_sessions[$session_id])) { - unset($this->_sessions[$session_id]); - } - - return true; - } - - public function gc($maxlifetime) { - return true; - } - - public function open($save_path, $session_id) { - if (!isset($this->_sessions[$session_id])) { - $this->_sessions[$session_id] = ''; - } - - return true; - } - - public function read($session_id) { - return $this->_sessions[$session_id]; - } - - public function write($session_id, $session_data) { - $this->_sessions[$session_id] = $session_data; - - return true; - } -} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Mock/NullComponent.php b/tests/Ratchet/Tests/Mock/NullComponent.php new file mode 100644 index 0000000..73a44a1 --- /dev/null +++ b/tests/Ratchet/Tests/Mock/NullComponent.php @@ -0,0 +1,28 @@ +_policy->onMessage($conn, ' '); } + + public function testOnOpenExists() { + $this->assertTrue(method_exists($this->_policy, 'onOpen')); + $conn = $this->getMock('\Ratchet\ConnectionInterface'); + $this->_policy->onOpen($conn); + } + + public function testOnCloseExists() { + $this->assertTrue(method_exists($this->_policy, 'onClose')); + $conn = $this->getMock('\Ratchet\ConnectionInterface'); + $this->_policy->onClose($conn); + } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/Session/SessionComponentTest.php b/tests/Ratchet/Tests/Session/SessionComponentTest.php index ed75191..b97d273 100644 --- a/tests/Ratchet/Tests/Session/SessionComponentTest.php +++ b/tests/Ratchet/Tests/Session/SessionComponentTest.php @@ -1,5 +1,6 @@ markTestSkipped('Dependency of Symfony HttpFoundation failed'); } + + parent::setUp(); + $this->_serv = new SessionProvider($this->_app, new NullSessionHandler); + } + + public function tearDown() { + ini_set('session.serialize_handler', 'php'); + } + + public function getConnectionClassString() { + return '\Ratchet\ConnectionInterface'; + } + + public function getDecoratorClassString() { + return '\Ratchet\Tests\Mock\NullComponent'; + } + + public function getComponentClassString() { + return '\Ratchet\MessageComponentInterface'; } public function classCaseProvider() { @@ -33,7 +53,7 @@ class SessionProviderTest extends \PHPUnit_Framework_TestCase { $method = $ref->getMethod('toClassCase'); $method->setAccessible(true); - $component = new SessionProvider($this->getMock('Ratchet\\MessageComponentInterface'), new MemorySessionHandler); + $component = new SessionProvider($this->getMock('Ratchet\\MessageComponentInterface'), $this->getMock('\SessionHandlerInterface')); $this->assertEquals($out, $method->invokeArgs($component, array($in))); } @@ -73,9 +93,9 @@ class SessionProviderTest extends \PHPUnit_Framework_TestCase { } protected function newConn() { - $conn = $this->getMock('Ratchet\\ConnectionInterface'); + $conn = $this->getMock('Ratchet\ConnectionInterface'); - $headers = $this->getMock('Guzzle\\Http\\Message\\Request', array('getCookie'), array('POST', '/', array())); + $headers = $this->getMock('Guzzle\Http\Message\Request', array('getCookie'), array('POST', '/', array())); $headers->expects($this->once())->method('getCookie', array(ini_get('session.name')))->will($this->returnValue(null)); $conn->WebSocket = new \StdClass; @@ -84,46 +104,10 @@ class SessionProviderTest extends \PHPUnit_Framework_TestCase { return $conn; } - public function testOnOpenBubbles() { - $conn = $this->newConn(); - $mock = $this->getMock('Ratchet\\MessageComponentInterface'); - $comp = new SessionProvider($mock, new NullSessionHandler); - - $mock->expects($this->once())->method('onOpen')->with($conn); - $comp->onOpen($conn); - } - - protected function getOpenConn() { - $conn = $this->newConn(); - $mock = $this->getMock('Ratchet\\MessageComponentInterface'); - $prov = new SessionProvider($mock, new NullSessionHandler); - - $prov->onOpen($conn); - - return array($conn, $mock, $prov); - } - - public function testOnMessageBubbles() { - list($conn, $mock, $prov) = $this->getOpenConn(); - $msg = 'No sessions here'; - - $mock->expects($this->once())->method('onMessage')->with($conn, $msg); - $prov->onMessage($conn, $msg); - } - - public function testOnCloseBubbles() { - list($conn, $mock, $prov) = $this->getOpenConn(); - - $mock->expects($this->once())->method('onClose')->with($conn); - $prov->onClose($conn); - } - - public function testOnErrorBubbles() { - list($conn, $mock, $prov) = $this->getOpenConn(); - $e = new \Exception('I made a boo boo'); - - $mock->expects($this->once())->method('onError')->with($conn, $e); - $prov->onError($conn, $e); + public function testOnMessageDecorator() { + $message = "Database calls are usually blocking :("; + $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message); + $this->_serv->onMessage($this->_conn, $message); } public function testGetSubProtocolsReturnsArray() { @@ -140,4 +124,14 @@ class SessionProviderTest extends \PHPUnit_Framework_TestCase { $this->assertGreaterThanOrEqual(2, count($comp->getSubProtocols())); } + + public function testRejectInvalidSeralizers() { + if (!function_exists('wddx_serialize_value')) { + $this->markTestSkipped(); + } + + ini_set('session.serialize_handler', 'wddx'); + $this->setExpectedException('\RuntimeException'); + new SessionProvider($this->getMock('\Ratchet\MessageComponentInterface'), $this->getMock('\SessionHandlerInterface')); + } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/Wamp/ServerProtocolTest.php b/tests/Ratchet/Tests/Wamp/ServerProtocolTest.php index cf5d803..4217ea3 100644 --- a/tests/Ratchet/Tests/Wamp/ServerProtocolTest.php +++ b/tests/Ratchet/Tests/Wamp/ServerProtocolTest.php @@ -38,7 +38,7 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { * @dataProvider invalidMessageProvider */ public function testInvalidMessages($type) { - $this->setExpectedException('\\Ratchet\\Wamp\\Exception'); + $this->setExpectedException('\Ratchet\Wamp\Exception'); $conn = $this->newConn(); $this->_comp->onOpen($conn); diff --git a/tests/Ratchet/Tests/Wamp/WampServerTest.php b/tests/Ratchet/Tests/Wamp/WampServerTest.php index 6a92d57..e83dc28 100644 --- a/tests/Ratchet/Tests/Wamp/WampServerTest.php +++ b/tests/Ratchet/Tests/Wamp/WampServerTest.php @@ -1,59 +1,44 @@ mock = $this->getMock('\\Ratchet\\Wamp\\WampServerInterface'); - $this->serv = new WampServer($this->mock); - $this->conn = $this->getMock('\\Ratchet\\ConnectionInterface'); - - $this->serv->onOpen($this->conn); + public function getConnectionClassString() { + return '\Ratchet\Wamp\WampConnection'; } - public function isWampConn() { - return new \PHPUnit_Framework_Constraint_IsInstanceOf('\\Ratchet\\Wamp\\WampConnection'); + public function getDecoratorClassString() { + return 'Ratchet\Wamp\WampServer'; } - public function testOpen() { - $this->mock->expects($this->once())->method('onOpen')->with($this->isWampConn()); - $this->serv->onOpen($this->getMock('\\Ratchet\\ConnectionInterface')); - } - - public function testOnClose() { - $this->mock->expects($this->once())->method('onClose')->with($this->isWampConn()); - $this->serv->onClose($this->conn); - } - - public function testOnError() { - $e = new \Exception('hurr hurr'); - $this->mock->expects($this->once())->method('onError')->with($this->isWampConn(), $e); - $this->serv->onError($this->conn, $e); + public function getComponentClassString() { + return '\Ratchet\Wamp\WampServerInterface'; } public function testOnMessageToEvent() { $published = 'Client published this message'; - $this->mock->expects($this->once())->method('onPublish')->with( - $this->isWampConn() - , new \PHPUnit_Framework_Constraint_IsInstanceOf('\\Ratchet\\Wamp\\Topic') + $this->_app->expects($this->once())->method('onPublish')->with( + $this->isExpectedConnection() + , new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\Wamp\Topic') , $published , array() , array() ); - $this->serv->onMessage($this->conn, json_encode(array(7, 'topic', $published))); + $this->_serv->onMessage($this->_conn, json_encode(array(7, 'topic', $published))); } public function testGetSubProtocols() { // todo: could expand on this - $this->assertInternalType('array', $this->serv->getSubProtocols()); + $this->assertInternalType('array', $this->_serv->getSubProtocols()); } } \ No newline at end of file diff --git a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php index d31ce64..3ac1f4a 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php +++ b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php @@ -1,6 +1,7 @@ _crlf}"; $headers .= "Connection: Upgrade{$this->_crlf}"; - $headers .= "Host: home.chrisboden.ca{$this->_crlf}"; + $headers .= "Host: socketo.me{$this->_crlf}"; $headers .= "Origin: http://fiddle.jshell.net{$this->_crlf}"; $headers .= "Sec-WebSocket-Key1:17 Z4< F94 N3 7P41 7{$this->_crlf}"; $headers .= "Sec-WebSocket-Key2:1 23C3:,2% 1-29 4 f0{$this->_crlf}"; @@ -55,12 +56,11 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { public function testNoUpgradeBeforeBody() { $headers = $this->headerProvider(); - $body = base64_decode($this->_body); - $mockConn = $this->getMock('\\Ratchet\\ConnectionInterface'); - $mockApp = $this->getMock('\\Ratchet\\MessageComponentInterface'); + $mockConn = $this->getMock('\Ratchet\ConnectionInterface'); + $mockApp = $this->getMock('\Ratchet\MessageComponentInterface'); - $server = new WsServer($mockApp); + $server = new HttpServer(new WsServer($mockApp)); $server->onOpen($mockConn); $mockApp->expects($this->exactly(0))->method('onOpen'); $server->onMessage($mockConn, $headers); @@ -70,10 +70,10 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { $headers = $this->headerProvider(); $body = base64_decode($this->_body); - $mockConn = $this->getMock('\\Ratchet\\ConnectionInterface'); - $mockApp = $this->getMock('\\Ratchet\\MessageComponentInterface'); + $mockConn = $this->getMock('\Ratchet\ConnectionInterface'); + $mockApp = $this->getMock('\Ratchet\MessageComponentInterface'); - $server = new WsServer($mockApp); + $server = new HttpServer(new WsServer($mockApp)); $server->onOpen($mockConn); $server->onMessage($mockConn, $headers);