From 7b2b6897e5d4bfce1e38032a48b6deacd91d21d2 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 14 Apr 2013 18:22:35 -0400 Subject: [PATCH 01/25] HTTP Decouple spike --- src/Ratchet/Http/HttpServer.php | 99 ++++++++++++++++++++++++ src/Ratchet/Http/HttpServerInterface.php | 14 ++++ src/Ratchet/WebSocket/WsServer.php | 48 ++++++------ 3 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 src/Ratchet/Http/HttpServer.php create mode 100644 src/Ratchet/Http/HttpServerInterface.php diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php new file mode 100644 index 0000000..3af5aed --- /dev/null +++ b/src/Ratchet/Http/HttpServer.php @@ -0,0 +1,99 @@ +_decorating = $component; + $this->_reqParser = new HttpRequestParser; + } + + /** + * @{inheritdoc} + */ + public function onOpen(ConnectionInterface $conn) { + $conn->Http = new \StdClass; + $conn->Http->headers = false; + } + + /** + * @{inheritdoc} + */ + public function onMessage(ConnectionInterface $from, $msg) { + if (true !== $from->Http->headers) { + try { + if (null === ($request = $this->_reqParser->onMessage($from, $msg))) { + return; + } + } catch (\OverflowException $oe) { + return $this->close($from, 413); + } + + // check routes, return 404 or onOpen the route + + $from->Http->headers = true; + $from->Http->request = $request; + + return $this->_decorating->onOpen($from, $request); + } + + $this->_decorating->onMessage($from, $msg); + } + + /** + * @{inheritdoc} + */ + public function onClose(ConnectionInterface $conn) { + if ($conn->Http->headers) { + $this->_decorating->onClose($conn); + } + } + + /** + * @{inheritdoc} + */ + public function onError(ConnectionInterface $conn, \Exception $e) { + if ($conn->Http->headers) { + $this->_decorating->onError($conn, $e); + } else { + $conn->close(); + } + } + + /** + * 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( + '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..2f62238 --- /dev/null +++ b/src/Ratchet/Http/HttpServerInterface.php @@ -0,0 +1,14 @@ +WebSocket = new \StdClass; + public function onOpen(ConnectionInterface $conn, RequestInterface $headers = null) { + $conn->WebSocket = new \StdClass; + $conn->WebSocket->request = $headers; $conn->WebSocket->established = false; + + $this->attemptUpgrade($conn); } /** @@ -92,46 +98,42 @@ 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->request = $conn->WebSocket->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->_decorating); - $this->connections->attach($from, $upgraded); + $this->connections->attach($conn, $upgraded); $upgraded->WebSocket->established = true; From 423b5cc35528249d331f40f4802ca90b08bbf3e3 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 14 Apr 2013 18:34:28 -0400 Subject: [PATCH 02/25] [Http] Refactor header parsing from WS to HTTP --- .../Guzzle/Http/Message/RequestFactory.php | 2 +- .../{WebSocket => Http}/HttpRequestParser.php | 4 ++-- src/Ratchet/Http/HttpServer.php | 8 +++++--- src/Ratchet/WebSocket/WsServer.php | 15 +++------------ 4 files changed, 11 insertions(+), 18 deletions(-) rename src/Ratchet/{WebSocket => Http}/Guzzle/Http/Message/RequestFactory.php (89%) rename src/Ratchet/{WebSocket => Http}/HttpRequestParser.php (94%) 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 @@ reqParser = new HttpRequestParser; $this->versioner = new VersionManager; $this->validator = new ToggleableValidator; From 69afb3de1f26b1f80443387f12845b12fcb98cbb Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 14 Apr 2013 18:42:30 -0400 Subject: [PATCH 03/25] [Http] Fixed broken unit tests --- .../Guzzle/Http/Message/RequestFactoryTest.php | 4 ++-- .../Tests/{WebSocket => Http}/HtpRequestParserTest.php | 7 +++---- tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php | 5 +++-- 3 files changed, 8 insertions(+), 8 deletions(-) rename tests/Ratchet/Tests/{WebSocket => Http}/Guzzle/Http/Message/RequestFactoryTest.php (94%) rename tests/Ratchet/Tests/{WebSocket => Http}/HtpRequestParserTest.php (88%) diff --git a/tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php b/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php similarity index 94% rename from tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php rename to tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php index 01d5df0..cb67bf5 100644 --- a/tests/Ratchet/Tests/WebSocket/Guzzle/Http/Message/RequestFactoryTest.php +++ b/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php @@ -1,6 +1,6 @@ getMock('\\Ratchet\\ConnectionInterface'); $this->parser->maxSize = 20; diff --git a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php index d31ce64..02d959d 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php +++ b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php @@ -1,6 +1,7 @@ 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); @@ -73,7 +74,7 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { $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); From 7b5b215c44e5d9fc702850f7c7bd5d013b7d5e9b Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 14 Apr 2013 19:47:25 -0400 Subject: [PATCH 04/25] Updated example to match decoupling --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5cd3fa9..32c96fd 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Need help? Have a question? Want to provide feedback? Write a message on the use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Ratchet\Server\IoServer; +use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; require __DIR__ . '/vendor/autoload.php'; @@ -76,7 +77,7 @@ class Chat implements MessageComponentInterface { } // Run the server application through the WebSocket protocol on port 8080 - $server = IoServer::factory(new WsServer(new Chat), 8080); + $server = IoServer::factory(new HttpServer(new WsServer(new Chat)), 8080); $server->run(); ``` From dc59a0a3c01019100b6a96ce2938eaa695609473 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Mon, 15 Apr 2013 20:48:32 -0400 Subject: [PATCH 05/25] Bump min version req to 5.3.9 to avoid Interface parse error --- .travis.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6161d58..80698e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: php php: - - 5.3.3 + - 5.3.9 - 5.3 - 5.4 diff --git a/composer.json b/composer.json index f83a0b0..364b9fe 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ } } , "require": { - "php": ">=5.3.3" + "php": ">=5.3.9" , "react/socket": "0.2.*" , "guzzle/http": "~3.0" , "symfony/http-foundation": "~2.1" From 18bc1144089cf886394f906b0f5f7c64363f6099 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Tue, 16 Apr 2013 22:37:15 -0400 Subject: [PATCH 06/25] [Http] Routing spike --- composer.json | 3 +- composer.lock | 135 +++++++++++++++++++++++--------- src/Ratchet/Http/HttpServer.php | 53 +++++++++---- 3 files changed, 138 insertions(+), 53 deletions(-) diff --git a/composer.json b/composer.json index 364b9fe..a7a7953 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "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..882649e 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": "b51075c0a68acb956c32e59869790571", "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,7 +240,7 @@ "component", "stream" ], - "time": "2013-03-03 03:07:02" + "time": "2013-04-06 18:28:51" }, { "name": "react/event-loop", @@ -367,17 +374,17 @@ }, { "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/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index ecac919..f1ba4dd 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -2,16 +2,15 @@ namespace Ratchet\Http; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; - +use Guzzle\Http\Message\Response; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; class HttpServer implements MessageComponentInterface { - /** - * Decorated component - * @var HttpServerInterface - */ - protected $_decorating; - /** * Buffers incoming HTTP requests returning a Guzzle Request when coalesced * @var HttpRequestParser @@ -24,14 +23,24 @@ class HttpServer implements MessageComponentInterface { */ protected $_routes; - /** - * @todo Change parameter from HttpServerInterface to RouteCollection - */ - public function __construct(HttpServerInterface $component) { - $this->_decorating = $component; + public function __construct() { + $this->_routes = new RouteCollection; $this->_reqParser = new HttpRequestParser; } + /** + * @param string + * @param string + * @param Ratchet\Http\HttpServerInterface + * @param array + */ + public function addRoute($name, $path, MessageComponentInterface $controller, $allowedOrigins = array()) { + $this->_routes->add($name, new Route($path, array( + '_controller' => $controller + , 'allowedOrigins' => $allowedOrigins + ))); + } + /** * @{inheritdoc} */ @@ -53,15 +62,25 @@ class HttpServer implements MessageComponentInterface { return $this->close($from, 413); } - // check routes, return 404 or onOpen the route + $context = new RequestContext($request->getUrl(), $request->getMethod(), $request->getHost(), $request->getScheme(), $request->getPort()); + $matcher = new UrlMatcher($this->_routes, $context); - $from->Http->headers = true; - $from->Http->request = $request; + try { + $route = $matcher->match($request->getPath()); + } catch (MethodNotAllowedException $nae) { + return $this->close($from, 403); + } catch (ResourceNotFoundException $nfe) { + return $this->close($from, 404); + } - return $this->_decorating->onOpen($from, $request); + $from->Http->headers = true; + $from->Http->request = $request; + $from->Http->controller = $route['_controller']; + + return $from->Http->controller->onOpen($from, $request); } - $this->_decorating->onMessage($from, $msg); + $from->Http->controller->onMessage($from, $msg); } /** From ba76600214a06daf5a0e4daf1f95ec9e483d5ba3 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Fri, 19 Apr 2013 22:40:16 -0400 Subject: [PATCH 07/25] [Http] Fixed broken unit tests --- tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php index 02d959d..05716c6 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php +++ b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php @@ -61,7 +61,8 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { $mockConn = $this->getMock('\\Ratchet\\ConnectionInterface'); $mockApp = $this->getMock('\\Ratchet\\MessageComponentInterface'); - $server = new HttpServer(new WsServer($mockApp)); + $server = new HttpServer; + $server->addRoute('test', '/', new WsServer($mockApp)); $server->onOpen($mockConn); $mockApp->expects($this->exactly(0))->method('onOpen'); $server->onMessage($mockConn, $headers); @@ -74,7 +75,8 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { $mockConn = $this->getMock('\\Ratchet\\ConnectionInterface'); $mockApp = $this->getMock('\\Ratchet\\MessageComponentInterface'); - $server = new HttpServer(new WsServer($mockApp)); + $server = new HttpServer; + $server->addRoute('test', '/', new WsServer($mockApp)); $server->onOpen($mockConn); $server->onMessage($mockConn, $headers); From 67dbf46879182671448e81ab1c540627a1b9f7c8 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 20 Apr 2013 16:16:47 -0400 Subject: [PATCH 08/25] [Http] Docs --- CHANGELOG.md | 5 +++++ Makefile | 1 + 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13fb90f..f61291c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ CHANGELOG --- +* 0.3.0 (2013-xx-xx) + + * BC: Added Routing to HTTP 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 From 43003d69f93cd6891743448722be02314f374fbd Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Fri, 26 Apr 2013 19:19:47 -0400 Subject: [PATCH 09/25] [Http] Only pass Request, don't store (twice) --- src/Ratchet/Http/HttpServer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index f1ba4dd..b72fd98 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -74,7 +74,6 @@ class HttpServer implements MessageComponentInterface { } $from->Http->headers = true; - $from->Http->request = $request; $from->Http->controller = $route['_controller']; return $from->Http->controller->onOpen($from, $request); From 4a87375f1019f6d1434155d20d45ba17d8bb9ea5 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Fri, 26 Apr 2013 20:00:11 -0400 Subject: [PATCH 10/25] [Http] HttpServerInterface Force HttpServerInterface on route Call controller on all events --- src/Ratchet/Http/HttpServer.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index b72fd98..9d306e7 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -34,9 +34,9 @@ class HttpServer implements MessageComponentInterface { * @param Ratchet\Http\HttpServerInterface * @param array */ - public function addRoute($name, $path, MessageComponentInterface $controller, $allowedOrigins = array()) { + public function addRoute($name, $path, HttpServerInterface $controller, $allowedOrigins = array()) { $this->_routes->add($name, new Route($path, array( - '_controller' => $controller + '_controller' => $controller , 'allowedOrigins' => $allowedOrigins ))); } @@ -87,7 +87,7 @@ class HttpServer implements MessageComponentInterface { */ public function onClose(ConnectionInterface $conn) { if ($conn->Http->headers) { - $this->_decorating->onClose($conn); + $conn->Http->controller->onClose($conn); } } @@ -96,7 +96,7 @@ class HttpServer implements MessageComponentInterface { */ public function onError(ConnectionInterface $conn, \Exception $e) { if ($conn->Http->headers) { - $this->_decorating->onError($conn, $e); + $conn->Http->controller->onError($conn, $e); } else { $conn->close(); } From c24cdf379e01fe5fd06173057b772c0ee24319ce Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Fri, 26 Apr 2013 21:06:34 -0400 Subject: [PATCH 11/25] [Http] Accept RouteCollection, spike CORS protection --- CHANGELOG.md | 1 + src/Ratchet/Http/HttpServer.php | 32 +++++++++++++++---- .../Tests/WebSocket/Version/Hixie76Test.php | 4 ++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f61291c..a59cc7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * 0.3.0 (2013-xx-xx) + * BC: Requre hostname and do Origin HTTP header check against it, disabling CORS by default for security reasons * BC: Added Routing to HTTP allowing for a single Ratchet server to handle multiple apps * BC: Decoupled HTTP from WebSocket component diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index 9d306e7..a6d2d5e 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -19,12 +19,33 @@ class HttpServer implements MessageComponentInterface { protected $_reqParser; /** - * @var Symfony\Component\Routing\RouteCollection + * @var \Symfony\Component\Routing\RouteCollection A collection with \Ratchet\MessageComponentInterface controllers */ protected $_routes; - public function __construct() { - $this->_routes = new RouteCollection; + /** + * @param string $host + * @param RouteCollection $collection + * @throws \UnexpectedValueException If a Route Controller does not map to a \Ratchet\MessageComponentInterface + */ + public function __construct($host, RouteCollection $collection = null) { + if (null === $collection) { + $collection = new RouteCollection; + } else { + foreach ($collection as $routeName => $route) { + 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\MessageComponentInterface'); + } + } + } + + $collection->setHost($host); + + $this->_routes = $collection; $this->_reqParser = new HttpRequestParser; } @@ -34,10 +55,9 @@ class HttpServer implements MessageComponentInterface { * @param Ratchet\Http\HttpServerInterface * @param array */ - public function addRoute($name, $path, HttpServerInterface $controller, $allowedOrigins = array()) { + public function addRoute($name, $path, HttpServerInterface $controller) { $this->_routes->add($name, new Route($path, array( - '_controller' => $controller - , 'allowedOrigins' => $allowedOrigins + '_controller' => $controller ))); } diff --git a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php index 05716c6..2e80307 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php +++ b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php @@ -44,7 +44,7 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { $headers = "GET / HTTP/1.1"; $headers .= "Upgrade: WebSocket{$this->_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}"; @@ -54,6 +54,7 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { return $headers; } +/* @todo Re-enable and fix these tests later - bad functional tests atm, break into units public function testNoUpgradeBeforeBody() { $headers = $this->headerProvider(); $body = base64_decode($this->_body); @@ -83,4 +84,5 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { $mockApp->expects($this->once())->method('onOpen'); $server->onMessage($mockConn, $body . $this->_crlf . $this->_crlf); } +*/ } \ No newline at end of file From 4df71c3a359af3bf756834dae23f7626337872f6 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Fri, 26 Apr 2013 23:01:28 -0400 Subject: [PATCH 12/25] [Http] ROUTING BABY Decoupled routing from HTTP Added Router implement HttpServerInterface Fully functional Symfony routes in application! As a result, this drastically decreased backwards compatibility breaks while introducing new functionality --- CHANGELOG.md | 4 +- README.md | 13 +++- src/Ratchet/Http/HttpServer.php | 75 +++++------------------- src/Ratchet/Http/HttpServerInterface.php | 5 +- src/Ratchet/Http/Router.php | 61 +++++++++++++++++++ src/Ratchet/WebSocket/WsServer.php | 4 +- 6 files changed, 95 insertions(+), 67 deletions(-) create mode 100644 src/Ratchet/Http/Router.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a59cc7c..e0771e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,8 @@ CHANGELOG * 0.3.0 (2013-xx-xx) - * BC: Requre hostname and do Origin HTTP header check against it, disabling CORS by default for security reasons - * BC: Added Routing to HTTP allowing for a single Ratchet server to handle multiple apps + * 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) diff --git a/README.md b/README.md index 32c96fd..e089404 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,15 @@ Need help? Have a question? Want to provide feedback? Write a message on the add('chatRoute', new Route('/chat', array( + '_controller' => new WsServer(new Chat) + ))); + + $server = IoServer::factory(new HttpServer(new Router(new UrlMatcher($routes, new RequestContext))), 8080); $server->run(); ``` diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index a6d2d5e..867d008 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -19,61 +19,30 @@ class HttpServer implements MessageComponentInterface { protected $_reqParser; /** - * @var \Symfony\Component\Routing\RouteCollection A collection with \Ratchet\MessageComponentInterface controllers + * @var \Ratchet\Http\HttpServerInterface */ - protected $_routes; + protected $_httpServer; /** - * @param string $host - * @param RouteCollection $collection - * @throws \UnexpectedValueException If a Route Controller does not map to a \Ratchet\MessageComponentInterface + * @param HttpServerInterface */ - public function __construct($host, RouteCollection $collection = null) { - if (null === $collection) { - $collection = new RouteCollection; - } else { - foreach ($collection as $routeName => $route) { - 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\MessageComponentInterface'); - } - } - } - - $collection->setHost($host); - - $this->_routes = $collection; - $this->_reqParser = new HttpRequestParser; - } - - /** - * @param string - * @param string - * @param Ratchet\Http\HttpServerInterface - * @param array - */ - public function addRoute($name, $path, HttpServerInterface $controller) { - $this->_routes->add($name, new Route($path, array( - '_controller' => $controller - ))); + public function __construct(HttpServerInterface $server) { + $this->_httpServer = $server; + $this->_reqParser = new HttpRequestParser; } /** * @{inheritdoc} */ public function onOpen(ConnectionInterface $conn) { - $conn->Http = new \StdClass; - $conn->Http->headers = false; + $conn->httpHeadersReceived = false; } /** * @{inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { - if (true !== $from->Http->headers) { + if (true !== $from->httpHeadersReceived) { try { if (null === ($request = $this->_reqParser->onMessage($from, $msg))) { return; @@ -82,32 +51,20 @@ class HttpServer implements MessageComponentInterface { return $this->close($from, 413); } - $context = new RequestContext($request->getUrl(), $request->getMethod(), $request->getHost(), $request->getScheme(), $request->getPort()); - $matcher = new UrlMatcher($this->_routes, $context); + $from->httpHeadersReceived = true; - try { - $route = $matcher->match($request->getPath()); - } catch (MethodNotAllowedException $nae) { - return $this->close($from, 403); - } catch (ResourceNotFoundException $nfe) { - return $this->close($from, 404); - } - - $from->Http->headers = true; - $from->Http->controller = $route['_controller']; - - return $from->Http->controller->onOpen($from, $request); + return $this->_httpServer->onOpen($from, $request); } - $from->Http->controller->onMessage($from, $msg); + $this->_httpServer->onMessage($from, $msg); } /** * @{inheritdoc} */ public function onClose(ConnectionInterface $conn) { - if ($conn->Http->headers) { - $conn->Http->controller->onClose($conn); + if ($conn->httpHeadersReceived) { + $this->_httpServer->onClose($conn); } } @@ -115,10 +72,10 @@ class HttpServer implements MessageComponentInterface { * @{inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { - if ($conn->Http->headers) { - $conn->Http->controller->onError($conn, $e); + if ($conn->httpHeadersReceived) { + $this->_httpServer->onError($conn, $e); } else { - $conn->close(); + $this->close($conn, 500); } } diff --git a/src/Ratchet/Http/HttpServerInterface.php b/src/Ratchet/Http/HttpServerInterface.php index 2f62238..0730680 100644 --- a/src/Ratchet/Http/HttpServerInterface.php +++ b/src/Ratchet/Http/HttpServerInterface.php @@ -7,8 +7,7 @@ use Guzzle\Http\Message\RequestInterface; interface HttpServerInterface extends MessageComponentInterface { /** * @param \Ratchet\ConnectionInterface $conn - * @param \Guzzle\Http\Message\RequestInterface $headers null is default because PHP won't let me overload; don't pass null!!! - * @return mixed + * @param \Guzzle\Http\Message\RequestInterface $request null is default because PHP won't let me overload; don't pass null!!! */ - public function onOpen(ConnectionInterface $conn, RequestInterface $headers = null); + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null); } diff --git a/src/Ratchet/Http/Router.php b/src/Ratchet/Http/Router.php new file mode 100644 index 0000000..9946d7e --- /dev/null +++ b/src/Ratchet/Http/Router.php @@ -0,0 +1,61 @@ +_matcher = $matcher; + } + + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { + try { + $route = $this->_matcher->match($request->getPath()); + } catch (MethodNotAllowedException $nae) { + return $this->close($from, 403); + } catch (ResourceNotFoundException $nfe) { + return $this->close($from, 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\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); + } +} \ No newline at end of file diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 6329cf9..77e3fe1 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -73,9 +73,9 @@ class WsServer implements HttpServerInterface { /** * {@inheritdoc} */ - public function onOpen(ConnectionInterface $conn, RequestInterface $headers = null) { + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { $conn->WebSocket = new \StdClass; - $conn->WebSocket->request = $headers; + $conn->WebSocket->request = $request; $conn->WebSocket->established = false; $this->attemptUpgrade($conn); From 26a760709f24c11e66658e0ef7dea3176f132e13 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 27 Apr 2013 00:05:10 -0400 Subject: [PATCH 13/25] [Http] Facade class for routing Not sure if it'll stick or not, but easier way to route for now Refs #15 --- README.md | 19 +++----- src/Ratchet/Http/RoutedHttpServer.php | 65 +++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 src/Ratchet/Http/RoutedHttpServer.php diff --git a/README.md b/README.md index e089404..3f78953 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,9 @@ Need help? Have a question? Want to provide feedback? Write a message on the add('chatRoute', new Route('/chat', array( - '_controller' => new WsServer(new Chat) - ))); + $router = new RoutedHttpServer; + $router->addRoute('/echo', new AbFuzzyServer); + $router->addRoute('/chat', new Chat); - $server = IoServer::factory(new HttpServer(new Router(new UrlMatcher($routes, new RequestContext))), 8080); + $server = IoServer::factory($router, 8000); $server->run(); ``` diff --git a/src/Ratchet/Http/RoutedHttpServer.php b/src/Ratchet/Http/RoutedHttpServer.php new file mode 100644 index 0000000..728424c --- /dev/null +++ b/src/Ratchet/Http/RoutedHttpServer.php @@ -0,0 +1,65 @@ +_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 From 9d389b14c8b2f444038b1339146a7d9a21a3e738 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 27 Apr 2013 00:11:34 -0400 Subject: [PATCH 14/25] Don't test old version of PHP 5 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 80698e0..5625a26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 5.3.9 - 5.3 - 5.4 From 5c41b2fbe126c5b5eb70b0171c078f0fc828f193 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 27 Apr 2013 10:27:45 -0400 Subject: [PATCH 15/25] [Http][Tests] Refactored unit tests, added new for HTTP --- src/Ratchet/Session/SessionProvider.php | 2 +- src/Ratchet/WebSocket/WsServer.php | 1 - .../Tests/AbstractConnectionDecoratorTest.php | 3 +- .../AbstractMessageComponentTestCase.php | 41 ++++++++++++ tests/Ratchet/Tests/Http/HttpServerTest.php | 33 ++++++++++ .../Tests/Mock/MemorySessionHandler.php | 39 ----------- tests/Ratchet/Tests/Mock/NullComponent.php | 28 ++++++++ .../Tests/Session/SessionComponentTest.php | 64 ++++++------------- .../Ratchet/Tests/Wamp/ServerProtocolTest.php | 2 +- tests/Ratchet/Tests/Wamp/WampServerTest.php | 41 ++++-------- .../Tests/WebSocket/Version/Hixie76Test.php | 17 ++--- 11 files changed, 143 insertions(+), 128 deletions(-) create mode 100644 tests/Ratchet/Tests/AbstractMessageComponentTestCase.php create mode 100644 tests/Ratchet/Tests/Http/HttpServerTest.php delete mode 100644 tests/Ratchet/Tests/Mock/MemorySessionHandler.php create mode 100644 tests/Ratchet/Tests/Mock/NullComponent.php diff --git a/src/Ratchet/Session/SessionProvider.php b/src/Ratchet/Session/SessionProvider.php index 7203dda..b2c549b 100644 --- a/src/Ratchet/Session/SessionProvider.php +++ b/src/Ratchet/Session/SessionProvider.php @@ -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/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 77e3fe1..5245019 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -110,7 +110,6 @@ class WsServer implements HttpServerInterface { return; } - // This needs to be refactored later on, incorporated with routing if ('' !== ($agreedSubProtocols = $this->getSubProtocolString($conn->WebSocket->request->getTokenizedHeader('Sec-WebSocket-Protocol', ',')))) { $response->setHeader('Sec-WebSocket-Protocol', $agreedSubProtocols); } 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/Http/HttpServerTest.php b/tests/Ratchet/Tests/Http/HttpServerTest.php new file mode 100644 index 0000000..c7fef74 --- /dev/null +++ b/tests/Ratchet/Tests/Http/HttpServerTest.php @@ -0,0 +1,33 @@ +_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); + } +} \ No newline at end of file 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 @@ +markTestSkipped('Dependency of Symfony HttpFoundation failed'); } + + parent::setUp(); + $this->_serv = new SessionProvider($this->_app, new NullSessionHandler); + } + + 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 +49,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))); } @@ -84,48 +100,6 @@ 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 testGetSubProtocolsReturnsArray() { $mock = $this->getMock('Ratchet\\MessageComponentInterface'); $comp = new SessionProvider($mock, new NullSessionHandler); 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 2e80307..3ac1f4a 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php +++ b/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php @@ -54,16 +54,13 @@ class Hixie76Test extends \PHPUnit_Framework_TestCase { return $headers; } -/* @todo Re-enable and fix these tests later - bad functional tests atm, break into units 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 HttpServer; - $server->addRoute('test', '/', new WsServer($mockApp)); + $server = new HttpServer(new WsServer($mockApp)); $server->onOpen($mockConn); $mockApp->expects($this->exactly(0))->method('onOpen'); $server->onMessage($mockConn, $headers); @@ -73,16 +70,14 @@ 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 HttpServer; - $server->addRoute('test', '/', new WsServer($mockApp)); + $server = new HttpServer(new WsServer($mockApp)); $server->onOpen($mockConn); $server->onMessage($mockConn, $headers); $mockApp->expects($this->once())->method('onOpen'); $server->onMessage($mockConn, $body . $this->_crlf . $this->_crlf); } -*/ } \ No newline at end of file From 38df97676833303faf56a22026b119132dadb9b5 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 27 Apr 2013 11:22:56 -0400 Subject: [PATCH 16/25] [Http] Fixed tests, upgraded React --- composer.json | 2 +- composer.lock | 52 +++++++++---------- .../bin/fuzzingserver-libev.php | 2 +- .../bin/fuzzingserver-libevent.php | 2 +- .../bin/fuzzingserver-libuv.php | 2 +- .../bin/fuzzingserver-noutf8.php | 5 +- .../bin/fuzzingserver-stream.php | 2 +- .../Http/Message/RequestFactoryTest.php | 2 +- .../Tests/Http/HtpRequestParserTest.php | 2 +- 9 files changed, 36 insertions(+), 35 deletions(-) diff --git a/composer.json b/composer.json index a7a7953..89eec01 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ } , "require": { "php": ">=5.3.9" - , "react/socket": "0.2.*" + , "react/socket": "~0.2" , "guzzle/http": "~3.0" , "symfony/http-foundation": "~2.2" , "symfony/routing": "~2.2" diff --git a/composer.lock b/composer.lock index 882649e..f7408e1 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "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": "b51075c0a68acb956c32e59869790571", + "hash": "6006ea344879ef78bf1a490e630a74fc", "packages": [ { "name": "evenement/evenement", @@ -244,17 +244,17 @@ }, { "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": { @@ -267,7 +267,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.2-dev" + "dev-master": "0.3-dev" } }, "autoload": { @@ -283,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": { @@ -325,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": { @@ -347,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": { @@ -370,7 +370,7 @@ "pipe", "stream" ], - "time": "2012-12-14 00:58:14" + "time": "2013-04-21 13:25:49" }, { "name": "symfony/event-dispatcher", 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..f63893a 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/Http/Guzzle/Http/Message/RequestFactoryTest.php b/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php index cb67bf5..9709745 100644 --- a/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php +++ b/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php @@ -3,7 +3,7 @@ namespace Ratchet\Tests\Http\Guzzle\Http\Message; use Ratchet\Http\Guzzle\Http\Message\RequestFactory; /** - * @covers Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory + * @covers Ratchet\Http\Guzzle\Http\Message\RequestFactory */ class RequestFactoryTest extends \PHPUnit_Framework_TestCase { protected $factory; diff --git a/tests/Ratchet/Tests/Http/HtpRequestParserTest.php b/tests/Ratchet/Tests/Http/HtpRequestParserTest.php index a32a6da..4de396c 100644 --- a/tests/Ratchet/Tests/Http/HtpRequestParserTest.php +++ b/tests/Ratchet/Tests/Http/HtpRequestParserTest.php @@ -3,7 +3,7 @@ namespace Ratchet\Tests\Http; use Ratchet\Http\HttpRequestParser; /** - * @covers Ratchet\WebSocket\HttpRequestParser + * @covers Ratchet\Http\HttpRequestParser */ class HttpRequestParserTest extends \PHPUnit_Framework_TestCase { protected $parser; From 1ea0bd32559a3dd3084172a6616daee7a3a64bb9 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sat, 27 Apr 2013 16:51:24 -0400 Subject: [PATCH 17/25] [Http] Cleanup and parse error fix --- src/Ratchet/Http/HttpServer.php | 15 ++++++--------- .../bin/fuzzingserver-noutf8.php | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index 867d008..d4e8967 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -3,13 +3,10 @@ namespace Ratchet\Http; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Guzzle\Http\Message\Response; -use Symfony\Component\Routing\RouteCollection; -use Symfony\Component\Routing\Route; -use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Routing\Matcher\UrlMatcher; -use Symfony\Component\Routing\Exception\MethodNotAllowedException; -use Symfony\Component\Routing\Exception\ResourceNotFoundException; +/** + * @todo Detect HTTP proxy header IP, re-map remoteAddress + */ class HttpServer implements MessageComponentInterface { /** * Buffers incoming HTTP requests returning a Guzzle Request when coalesced @@ -26,8 +23,8 @@ class HttpServer implements MessageComponentInterface { /** * @param HttpServerInterface */ - public function __construct(HttpServerInterface $server) { - $this->_httpServer = $server; + public function __construct(HttpServerInterface $component) { + $this->_httpServer = $component; $this->_reqParser = new HttpRequestParser; } @@ -83,7 +80,7 @@ class HttpServer implements MessageComponentInterface { * Close a connection with an HTTP response * @param \Ratchet\ConnectionInterface $conn * @param int $code HTTP status code - * @return void + * @return null */ protected function close(ConnectionInterface $conn, $code = 400) { $response = new Response($code, array( diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php b/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php index f63893a..b20b3a1 100644 --- a/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php +++ b/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php @@ -4,7 +4,7 @@ $loop = new React\EventLoop\StreamSelectLoop; $sock = new React\Socket\Server($loop); - $web = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer) + $web = new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer); $app = new Ratchet\Http\HttpServer($web); $web->setEncodingChecks(false); From 8df459548fd7e46d9cef3e02cd15675072743d9f Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 28 Apr 2013 09:55:43 -0400 Subject: [PATCH 18/25] [WebSocket][Http] Bug fix WS require nullifying, more strict on Request parameter --- src/Ratchet/Http/HttpServerInterface.php | 1 + src/Ratchet/Http/Router.php | 14 +++++++++---- src/Ratchet/WebSocket/WsServer.php | 25 ++++++++++++------------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Ratchet/Http/HttpServerInterface.php b/src/Ratchet/Http/HttpServerInterface.php index 0730680..79b7d55 100644 --- a/src/Ratchet/Http/HttpServerInterface.php +++ b/src/Ratchet/Http/HttpServerInterface.php @@ -8,6 +8,7 @@ interface HttpServerInterface extends MessageComponentInterface { /** * @param \Ratchet\ConnectionInterface $conn * @param \Guzzle\Http\Message\RequestInterface $request null is default because PHP won't let me overload; don't pass null!!! + * @throws \UnexpectedValueException if a RequestInterface is not passed */ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null); } diff --git a/src/Ratchet/Http/Router.php b/src/Ratchet/Http/Router.php index 9946d7e..e1c1f6d 100644 --- a/src/Ratchet/Http/Router.php +++ b/src/Ratchet/Http/Router.php @@ -16,7 +16,14 @@ class Router implements HttpServerInterface { $this->_matcher = $matcher; } + /** + * {@inheritdoc} + */ 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) { @@ -34,26 +41,25 @@ class Router implements HttpServerInterface { } $conn->controller = $route['_controller']; - $conn->controller->onOpen($conn, $request); } /** - * @{inheritdoc} + * {@inheritdoc} */ function onMessage(ConnectionInterface $from, $msg) { $from->controller->onMessage($from, $msg); } /** - * @{inheritdoc} + * {@inheritdoc} */ function onClose(ConnectionInterface $conn) { $conn->controller->onClose($conn); } /** - * @{inheritdoc} + * {@inheritdoc} */ function onError(ConnectionInterface $conn, \Exception $e) { $conn->controller->onError($conn, $e); diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 5245019..24ea580 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -26,7 +26,7 @@ class WsServer implements HttpServerInterface { * Decorated component * @var \Ratchet\MessageComponentInterface */ - protected $_decorating; + public $component; /** * @var \SplObjectStorage @@ -34,9 +34,7 @@ class WsServer implements HttpServerInterface { protected $connections; /** - * For now, array_push accepted subprotocols to this array - * @deprecated - * @temporary + * Holder of accepted protocols, implement through WampServerInterface */ protected $acceptedSubProtocols = array(); @@ -66,7 +64,7 @@ class WsServer implements HttpServerInterface { ->enableVersion(new Version\Hixie76) ; - $this->_decorating = $component; + $this->component = $component; $this->connections = new \SplObjectStorage; } @@ -74,6 +72,10 @@ class WsServer implements HttpServerInterface { * {@inheritdoc} */ 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; @@ -100,7 +102,6 @@ class WsServer implements HttpServerInterface { return $this->close($conn); } - $conn->WebSocket->request = $conn->WebSocket->request; $conn->WebSocket->version = $this->versioner->getVersion($conn->WebSocket->request); } @@ -121,13 +122,13 @@ class WsServer implements HttpServerInterface { return $conn->close(); } - $upgraded = $conn->WebSocket->version->upgradeConnection($conn, $this->_decorating); + $upgraded = $conn->WebSocket->version->upgradeConnection($conn, $this->component); $this->connections->attach($conn, $upgraded); $upgraded->WebSocket->established = true; - return $this->_decorating->onOpen($upgraded); + return $this->component->onOpen($upgraded); } /** @@ -138,7 +139,7 @@ class WsServer implements HttpServerInterface { $decor = $this->connections[$conn]; $this->connections->detach($conn); - $this->_decorating->onClose($decor); + $this->component->onClose($decor); } } @@ -147,7 +148,7 @@ class WsServer implements HttpServerInterface { */ 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(); } @@ -181,8 +182,8 @@ class WsServer implements HttpServerInterface { */ 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; From 344818d0f56d1c22d5accf6385cb70d656041b51 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 28 Apr 2013 15:25:16 -0400 Subject: [PATCH 19/25] [Tests] Bring up unit testing coverage Coverage on Http and Session Set version to 0.3-beta Cleaned up a couple API doc things Removed Origin code from RFC, will be moved to Http --- src/Ratchet/ConnectionInterface.php | 2 +- src/Ratchet/Http/HttpServer.php | 8 ++--- src/Ratchet/Session/SessionProvider.php | 2 +- .../Version/RFC6455/HandshakeVerifier.php | 16 ---------- src/Ratchet/WebSocket/WsServer.php | 1 - ...rserTest.php => HttpRequestParserTest.php} | 9 +++++- tests/Ratchet/Tests/Http/HttpServerTest.php | 31 +++++++++++++++++++ .../Tests/Session/SessionComponentTest.php | 20 ++++++++++-- 8 files changed, 63 insertions(+), 26 deletions(-) rename tests/Ratchet/Tests/Http/{HtpRequestParserTest.php => HttpRequestParserTest.php} (78%) 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/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index d4e8967..5788807 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -29,14 +29,14 @@ class HttpServer implements MessageComponentInterface { } /** - * @{inheritdoc} + * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn) { $conn->httpHeadersReceived = false; } /** - * @{inheritdoc} + * {@inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { if (true !== $from->httpHeadersReceived) { @@ -57,7 +57,7 @@ class HttpServer implements MessageComponentInterface { } /** - * @{inheritdoc} + * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { if ($conn->httpHeadersReceived) { @@ -66,7 +66,7 @@ class HttpServer implements MessageComponentInterface { } /** - * @{inheritdoc} + * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { if ($conn->httpHeadersReceived) { diff --git a/src/Ratchet/Session/SessionProvider.php b/src/Ratchet/Session/SessionProvider.php index b2c549b..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; 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 24ea580..0373a14 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -216,7 +216,6 @@ class WsServer implements HttpServerInterface { * 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/Ratchet/Tests/Http/HtpRequestParserTest.php b/tests/Ratchet/Tests/Http/HttpRequestParserTest.php similarity index 78% rename from tests/Ratchet/Tests/Http/HtpRequestParserTest.php rename to tests/Ratchet/Tests/Http/HttpRequestParserTest.php index 4de396c..3a540ec 100644 --- a/tests/Ratchet/Tests/Http/HtpRequestParserTest.php +++ b/tests/Ratchet/Tests/Http/HttpRequestParserTest.php @@ -31,7 +31,7 @@ class HttpRequestParserTest extends \PHPUnit_Framework_TestCase { } public function testBufferOverflowResponse() { - $conn = $this->getMock('\\Ratchet\\ConnectionInterface'); + $conn = $this->getMock('\Ratchet\ConnectionInterface'); $this->parser->maxSize = 20; @@ -41,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 index c7fef74..203172a 100644 --- a/tests/Ratchet/Tests/Http/HttpServerTest.php +++ b/tests/Ratchet/Tests/Http/HttpServerTest.php @@ -30,4 +30,35 @@ class HttpServerTest extends AbstractMessageComponentTestCase { $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/Session/SessionComponentTest.php b/tests/Ratchet/Tests/Session/SessionComponentTest.php index 4b4b41d..290e7dd 100644 --- a/tests/Ratchet/Tests/Session/SessionComponentTest.php +++ b/tests/Ratchet/Tests/Session/SessionComponentTest.php @@ -22,6 +22,10 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { $this->_serv = new SessionProvider($this->_app, new NullSessionHandler); } + public function tearDown() { + ini_set('session.serialize_handler', 'php'); + } + public function getConnectionClassString() { return '\Ratchet\ConnectionInterface'; } @@ -89,9 +93,9 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { } 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; @@ -100,6 +104,12 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { return $conn; } + 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() { $mock = $this->getMock('Ratchet\\MessageComponentInterface'); $comp = new SessionProvider($mock, new NullSessionHandler); @@ -114,4 +124,10 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { $this->assertGreaterThanOrEqual(2, count($comp->getSubProtocols())); } + + public function testRejectInvalidSeralizers() { + ini_set('session.serialize_handler', 'wddx'); + $this->setExpectedException('\RuntimeException'); + new SessionProvider($this->getMock('\Ratchet\MessageComponentInterface'), $this->getMock('\SessionHandlerInterface')); + } } \ No newline at end of file From 4c1073883a3465c438f7e5b225cb203972122685 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Sun, 28 Apr 2013 15:58:22 -0400 Subject: [PATCH 20/25] [Tests] Skip serializer if not available (TravisCI) --- tests/Ratchet/Tests/Session/SessionComponentTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Ratchet/Tests/Session/SessionComponentTest.php b/tests/Ratchet/Tests/Session/SessionComponentTest.php index 290e7dd..b97d273 100644 --- a/tests/Ratchet/Tests/Session/SessionComponentTest.php +++ b/tests/Ratchet/Tests/Session/SessionComponentTest.php @@ -126,6 +126,10 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { } 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')); From ec9bef4ee1419e656715488a177e7779c5b9743f Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Tue, 30 Apr 2013 21:09:49 -0400 Subject: [PATCH 21/25] [Http] Router tests --- tests/Ratchet/Tests/Http/RouterTest.php | 84 +++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tests/Ratchet/Tests/Http/RouterTest.php 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); + } +} From 1622caadada49c42a1f0a1cf4b83afff130934a8 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Tue, 30 Apr 2013 21:24:39 -0400 Subject: [PATCH 22/25] [Http][Router] Fixed bugs found from unit tests --- src/Ratchet/Http/Router.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Ratchet/Http/Router.php b/src/Ratchet/Http/Router.php index e1c1f6d..08e0c1a 100644 --- a/src/Ratchet/Http/Router.php +++ b/src/Ratchet/Http/Router.php @@ -2,6 +2,7 @@ namespace Ratchet\Http; use Ratchet\ConnectionInterface; use Guzzle\Http\Message\RequestInterface; +use Guzzle\Http\Message\Response; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -18,6 +19,7 @@ class Router implements HttpServerInterface { /** * {@inheritdoc} + * @throws \UnexpectedValueException If a controller is not \Ratchet\Http\HttpServerInterface */ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { if (null === $request) { @@ -27,9 +29,9 @@ class Router implements HttpServerInterface { try { $route = $this->_matcher->match($request->getPath()); } catch (MethodNotAllowedException $nae) { - return $this->close($from, 403); + return $this->close($conn, 403); } catch (ResourceNotFoundException $nfe) { - return $this->close($from, 404); + return $this->close($conn, 404); } if (is_string($route['_controller']) && class_exists($route['_controller'])) { @@ -37,7 +39,7 @@ class Router implements HttpServerInterface { } if (!($route['_controller'] instanceof HttpServerInterface)) { - throw new \UnexpectedValueException('All routes must implement Ratchet\HttpServerInterface'); + throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface'); } $conn->controller = $route['_controller']; @@ -64,4 +66,19 @@ class Router implements HttpServerInterface { 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 From 8c05486740ddab2af54221bf7fdc9998cc62609c Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Tue, 30 Apr 2013 21:30:16 -0400 Subject: [PATCH 23/25] [Server] Full coverage on flash --- .../Tests/Server/FlashPolicyComponentTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php b/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php index 15fb45d..60088b4 100644 --- a/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php +++ b/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php @@ -137,4 +137,16 @@ class FlashPolicyTest extends \PHPUnit_Framework_TestCase { $this->_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 From cd0efed085d386bc6f20488780bc065719111b98 Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Wed, 1 May 2013 19:42:15 -0400 Subject: [PATCH 24/25] Set $conn->remoteAddress if proxy forwarded Refs #89 --- src/Ratchet/Http/HttpServer.php | 4 ++++ src/Ratchet/WebSocket/Version/HyBi10.php | 3 ++- src/Ratchet/WebSocket/Version/RFC6455.php | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index 5788807..4568067 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -50,6 +50,10 @@ class HttpServer implements MessageComponentInterface { $from->httpHeadersReceived = true; + if ($request->hasHeader('X-Forwarded-For')) { + $from->remoteAddress = $request->getHeader('X-Forwarded-For', true); + } + return $this->_httpServer->onOpen($from, $request); } 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)) )); } From 1ff7998f586e90a2f0e1a0f55afabc67c11b749d Mon Sep 17 00:00:00 2001 From: Chris Boden Date: Thu, 2 May 2013 19:17:47 -0400 Subject: [PATCH 25/25] [Http] Only update remoteAddress if local and header --- src/Ratchet/Http/HttpServer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index 4568067..1ce78a8 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -50,7 +50,7 @@ class HttpServer implements MessageComponentInterface { $from->httpHeadersReceived = true; - if ($request->hasHeader('X-Forwarded-For')) { + if (isset($from->remoteAddress) && '127.0.0.1' == $from->remoteAddress && $request->hasHeader('X-Forwarded-For')) { $from->remoteAddress = $request->getHeader('X-Forwarded-For', true); }