From 7b2b6897e5d4bfce1e38032a48b6deacd91d21d2 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sun, 14 Apr 2013 18:22:35 -0400 Subject: [PATCH 01/48] 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 @@ +<?php +namespace Ratchet\Http; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; + +// @todo This class will move to this namespace +use Ratchet\WebSocket\HttpRequestParser; + +use Symfony\Component\Routing\RouteCollection; + +class HttpServer implements MessageComponentInterface { + /** + * Decorated component + * @var HttpServerInterface + */ + protected $_decorating; + + protected $_reqParser; + + /** + * @var Symfony\Component\Routing\RouteCollection + */ + protected $_routes; + + /** + * @todo Change parameter from HttpServerInterface to RouteCollection + */ + public function __construct(HttpServerInterface $component) { + $this->_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 @@ +<?php +namespace Ratchet\Http; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; +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 + */ + public function onOpen(ConnectionInterface $conn, RequestInterface $headers = null); +} diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 281f16c..e766d1b 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -6,13 +6,16 @@ use Ratchet\WebSocket\Version; use Ratchet\WebSocket\Encoding\ToggleableValidator; use Guzzle\Http\Message\Response; +use Guzzle\Http\Message\RequestInterface; +use Ratchet\Http\HttpServerInterface; + /** * The adapter to handle WebSocket requests/responses * This is a mediator between the Server and your application to handle real-time messaging through a web browser * @link http://ca.php.net/manual/en/ref.http.php * @link http://dev.w3.org/html5/websockets/ */ -class WsServer implements MessageComponentInterface { +class WsServer implements HttpServerInterface { /** * Buffers incoming HTTP requests returning a Guzzle Request when coalesced * @var HttpRequestParser @@ -79,9 +82,12 @@ class WsServer implements MessageComponentInterface { /** * {@inheritdoc} */ - public function onOpen(ConnectionInterface $conn) { - $conn->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 <cboden@gmail.com> Date: Sun, 14 Apr 2013 18:34:28 -0400 Subject: [PATCH 02/48] [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 @@ <?php -namespace Ratchet\WebSocket\Guzzle\Http\Message; +namespace Ratchet\Http\Guzzle\Http\Message; use Guzzle\Http\Message\RequestFactory as GuzzleRequestFactory; use Guzzle\Http\EntityBody; diff --git a/src/Ratchet/WebSocket/HttpRequestParser.php b/src/Ratchet/Http/HttpRequestParser.php similarity index 94% rename from src/Ratchet/WebSocket/HttpRequestParser.php rename to src/Ratchet/Http/HttpRequestParser.php index 0922ef7..c6296bc 100644 --- a/src/Ratchet/WebSocket/HttpRequestParser.php +++ b/src/Ratchet/Http/HttpRequestParser.php @@ -1,8 +1,8 @@ <?php -namespace Ratchet\WebSocket; +namespace Ratchet\Http; use Ratchet\MessageInterface; use Ratchet\ConnectionInterface; -use Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory; +use Ratchet\Http\Guzzle\Http\Message\RequestFactory; /** * This class receives streaming data from a client request diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index 3af5aed..ecac919 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -3,9 +3,6 @@ namespace Ratchet\Http; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; -// @todo This class will move to this namespace -use Ratchet\WebSocket\HttpRequestParser; - use Symfony\Component\Routing\RouteCollection; class HttpServer implements MessageComponentInterface { @@ -15,6 +12,11 @@ class HttpServer implements MessageComponentInterface { */ protected $_decorating; + /** + * 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 + */ protected $_reqParser; /** diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index e766d1b..6329cf9 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -2,12 +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; - -use Guzzle\Http\Message\RequestInterface; -use Ratchet\Http\HttpServerInterface; /** * The adapter to handle WebSocket requests/responses @@ -16,13 +15,6 @@ use Ratchet\Http\HttpServerInterface; * @link http://dev.w3.org/html5/websockets/ */ class WsServer implements HttpServerInterface { - /** - * 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; - /** * Manage the various WebSocket versions to support * @var VersionManager @@ -65,7 +57,6 @@ class WsServer implements HttpServerInterface { * 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; From 69afb3de1f26b1f80443387f12845b12fcb98cbb Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sun, 14 Apr 2013 18:42:30 -0400 Subject: [PATCH 03/48] [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 @@ <?php -namespace Ratchet\Tests\WebSocket\Guzzle\Http\Message; -use Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory; +namespace Ratchet\Tests\Http\Guzzle\Http\Message; +use Ratchet\Http\Guzzle\Http\Message\RequestFactory; /** * @covers Ratchet\WebSocket\Guzzle\Http\Message\RequestFactory diff --git a/tests/Ratchet/Tests/WebSocket/HtpRequestParserTest.php b/tests/Ratchet/Tests/Http/HtpRequestParserTest.php similarity index 88% rename from tests/Ratchet/Tests/WebSocket/HtpRequestParserTest.php rename to tests/Ratchet/Tests/Http/HtpRequestParserTest.php index b3f1600..a32a6da 100644 --- a/tests/Ratchet/Tests/WebSocket/HtpRequestParserTest.php +++ b/tests/Ratchet/Tests/Http/HtpRequestParserTest.php @@ -1,7 +1,6 @@ <?php -namespace Ratchet\Tests\WebSocket; -use Ratchet\WebSocket\HttpRequestParser; -use Ratchet\Tests\Mock\Connection as ConnectionStub; +namespace Ratchet\Tests\Http; +use Ratchet\Http\HttpRequestParser; /** * @covers Ratchet\WebSocket\HttpRequestParser @@ -32,7 +31,7 @@ class HttpRequestParserTest extends \PHPUnit_Framework_TestCase { } public function testBufferOverflowResponse() { - $conn = new ConnectionStub; + $conn = $this->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 @@ <?php namespace Ratchet\Tests\WebSocket\Version; use Ratchet\WebSocket\Version\Hixie76; +use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; /** @@ -60,7 +61,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); $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 <cboden@gmail.com> Date: Sun, 14 Apr 2013 19:47:25 -0400 Subject: [PATCH 04/48] 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 <cboden@gmail.com> Date: Mon, 15 Apr 2013 20:48:32 -0400 Subject: [PATCH 05/48] 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 <cboden@gmail.com> Date: Tue, 16 Apr 2013 22:37:15 -0400 Subject: [PATCH 06/48] [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 <cboden@gmail.com> Date: Fri, 19 Apr 2013 22:40:16 -0400 Subject: [PATCH 07/48] [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 <cboden@gmail.com> Date: Sat, 20 Apr 2013 16:16:47 -0400 Subject: [PATCH 08/48] [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 <cboden@gmail.com> Date: Fri, 26 Apr 2013 19:19:47 -0400 Subject: [PATCH 09/48] [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 <cboden@gmail.com> Date: Fri, 26 Apr 2013 20:00:11 -0400 Subject: [PATCH 10/48] [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 <cboden@gmail.com> Date: Fri, 26 Apr 2013 21:06:34 -0400 Subject: [PATCH 11/48] [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 <cboden@gmail.com> Date: Fri, 26 Apr 2013 23:01:28 -0400 Subject: [PATCH 12/48] [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 <?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; + use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; +use Ratchet\Http\Router; use Ratchet\WebSocket\WsServer; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; require __DIR__ . '/vendor/autoload.php'; @@ -77,7 +83,12 @@ class Chat implements MessageComponentInterface { } // Run the server application through the WebSocket protocol on port 8080 - $server = IoServer::factory(new HttpServer(new WsServer(new Chat)), 8080); + // This will be made easier soon, routing currently in development + $routes->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 @@ +<?php +namespace Ratchet\Http; +use Ratchet\ConnectionInterface; +use Guzzle\Http\Message\RequestInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; + +class Router implements HttpServerInterface { + /** + * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface + */ + protected $_matcher; + + public function __construct(UrlMatcherInterface $matcher) { + $this->_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 <cboden@gmail.com> Date: Sat, 27 Apr 2013 00:05:10 -0400 Subject: [PATCH 13/48] [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 <?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; - +use Ratchet\Http\RoutedHttpServer; use Ratchet\Server\IoServer; -use Ratchet\Http\HttpServer; -use Ratchet\Http\Router; -use Ratchet\WebSocket\WsServer; -use Symfony\Component\Routing\Route; -use Symfony\Component\Routing\Matcher\UrlMatcher; -use Symfony\Component\Routing\RouteCollection; -use Symfony\Component\Routing\RequestContext; +use Ratchet\Tests\AbFuzzyServer; require __DIR__ . '/vendor/autoload.php'; @@ -83,12 +77,11 @@ class Chat implements MessageComponentInterface { } // Run the server application through the WebSocket protocol on port 8080 - // This will be made easier soon, routing currently in development - $routes->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 @@ +<?php +namespace Ratchet\Http; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; +use Ratchet\WebSocket\WsServer; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Matcher\UrlMatcher; + +/** + */ +class RoutedHttpServer implements MessageComponentInterface { + protected $_routes; + protected $_server; + + public function __construct(RouteCollection $routes = null) { + if (null == $routes) { + $routes = new RouteCollection; + } + + $this->_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 <cboden@gmail.com> Date: Sat, 27 Apr 2013 00:11:34 -0400 Subject: [PATCH 14/48] 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 <cboden@gmail.com> Date: Sat, 27 Apr 2013 10:27:45 -0400 Subject: [PATCH 15/48] [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 @@ <?php namespace Ratchet\Tests; use Ratchet\Tests\Mock\ConnectionDecorator; -use Ratchet\Tests\Mock\Connection; /** * @covers Ratchet\AbstractConnectionDecorator @@ -13,7 +12,7 @@ class AbstractConnectionDecoratorTest extends \PHPUnit_Framework_TestCase { protected $l2; public function setUp() { - $this->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 @@ +<?php +namespace Ratchet\Tests; + +abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestCase { + protected $_app; + protected $_serv; + protected $_conn; + + abstract public function getConnectionClassString(); + abstract public function getDecoratorClassString(); + abstract public function getComponentClassString(); + + public function setUp() { + $this->_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 @@ +<?php +namespace Ratchet\Tests\Http; +use Ratchet\Tests\AbstractMessageComponentTestCase; + +/** + * @covers Ratchet\Http\HttpServer + */ +class HttpServerTest extends AbstractMessageComponentTestCase { + public function setUp() { + parent::setUp(); + $this->_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 @@ -<?php -namespace Ratchet\Tests\Mock; - -class MemorySessionHandler implements \SessionHandlerInterface { - protected $_sessions = array(); - - public function close() { - } - - public function destroy($session_id) { - if (isset($this->_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 @@ +<?php +namespace Ratchet\Tests\Mock; +use Ratchet\ConnectionInterface; +use Ratchet\MessageComponentInterface; +use Ratchet\WebSocket\WsServerInterface; +use Ratchet\Wamp\WampServerInterface; + +class NullComponent implements MessageComponentInterface, WsServerInterface, WampServerInterface { + public function onOpen(ConnectionInterface $conn) {} + + public function onMessage(ConnectionInterface $conn, $msg) {} + + public function onClose(ConnectionInterface $conn) {} + + public function onError(ConnectionInterface $conn, \Exception $e) {} + + public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {} + + public function onSubscribe(ConnectionInterface $conn, $topic) {} + + public function onUnSubscribe(ConnectionInterface $conn, $topic) {} + + public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude = array(), array $eligible = array()) {} + + public function getSubProtocols() { + return array(); + } +} \ No newline at end of file diff --git a/tests/Ratchet/Tests/Session/SessionComponentTest.php b/tests/Ratchet/Tests/Session/SessionComponentTest.php index ed75191..4b4b41d 100644 --- a/tests/Ratchet/Tests/Session/SessionComponentTest.php +++ b/tests/Ratchet/Tests/Session/SessionComponentTest.php @@ -1,5 +1,6 @@ <?php namespace Ratchet\Tests\Session; +use Ratchet\Tests\AbstractMessageComponentTestCase; use Ratchet\Session\SessionProvider; use Ratchet\Tests\Mock\MemorySessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; @@ -11,11 +12,26 @@ use Guzzle\Http\Message\Request; * @covers Ratchet\Session\Storage\VirtualSessionStorage * @covers Ratchet\Session\Storage\Proxy\VirtualProxy */ -class SessionProviderTest extends \PHPUnit_Framework_TestCase { +class SessionProviderTest extends AbstractMessageComponentTestCase { public function setUp() { - if (!class_exists('Symfony\\Component\\HttpFoundation\\Session\\Session')) { + if (!class_exists('Symfony\Component\HttpFoundation\Session\Session')) { return $this->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 @@ <?php namespace Ratchet\Tests\Wamp; use Ratchet\Wamp\WampServer; +use Ratchet\Tests\AbstractMessageComponentTestCase; /** * @covers Ratchet\Wamp\WampServer */ -class WampServerTest extends \PHPUnit_Framework_TestCase { +class WampServerTest extends AbstractMessageComponentTestCase { private $serv; private $mock; private $conn; - public function setUp() { - $this->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 <cboden@gmail.com> Date: Sat, 27 Apr 2013 11:22:56 -0400 Subject: [PATCH 16/48] [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 <cboden@gmail.com> Date: Sat, 27 Apr 2013 16:51:24 -0400 Subject: [PATCH 17/48] [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 <cboden@gmail.com> Date: Sun, 28 Apr 2013 09:55:43 -0400 Subject: [PATCH 18/48] [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 <cboden@gmail.com> Date: Sun, 28 Apr 2013 15:25:16 -0400 Subject: [PATCH 19/48] [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 <cboden@gmail.com> Date: Sun, 28 Apr 2013 15:58:22 -0400 Subject: [PATCH 20/48] [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 <cboden@gmail.com> Date: Tue, 30 Apr 2013 21:09:49 -0400 Subject: [PATCH 21/48] [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 @@ +<?php +namespace Ratchet\Tests\Http; +use Ratchet\Http\Router; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; + +/** + * @covers Ratchet\Http\Router + */ +class RouterTest extends \PHPUnit_Framework_TestCase { + protected $_router; + protected $_matcher; + protected $_conn; + protected $_req; + + public function setUp() { + $this->_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 <cboden@gmail.com> Date: Tue, 30 Apr 2013 21:24:39 -0400 Subject: [PATCH 22/48] [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 <cboden@gmail.com> Date: Tue, 30 Apr 2013 21:30:16 -0400 Subject: [PATCH 23/48] [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 <cboden@gmail.com> Date: Wed, 1 May 2013 19:42:15 -0400 Subject: [PATCH 24/48] 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 <cboden@gmail.com> Date: Thu, 2 May 2013 19:17:47 -0400 Subject: [PATCH 25/48] [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); } From 37d13393596bdfb125db4c5377938d5b5ac92ce9 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Wed, 8 May 2013 20:31:01 -0400 Subject: [PATCH 26/48] [Tests] Refactor AB Fuzzing AbFuzzyServer is now first class EchoServer Updated meta files Cleaned up a WAMP unit test slightly composer alpha7 --- CHANGELOG.md | 6 +-- Makefile | 6 +-- README.md | 2 +- composer.lock | 48 +++++++++---------- .../Ratchet/Server/EchoServer.php | 4 +- .../bin/fuzzingserver-libev.php | 2 +- .../bin/fuzzingserver-libevent.php | 2 +- .../bin/fuzzingserver-libuv.php | 2 +- .../bin/fuzzingserver-noutf8.php | 2 +- .../bin/fuzzingserver-stream.php | 2 +- .../AutobahnTestSuite/fuzzingclient-all.json | 8 ++-- tests/Ratchet/Tests/Server/EchoServerTest.php | 26 ++++++++++ tests/Ratchet/Tests/Wamp/TopicManagerTest.php | 12 ++++- 13 files changed, 76 insertions(+), 46 deletions(-) rename tests/Ratchet/Tests/AbFuzzyServer.php => src/Ratchet/Server/EchoServer.php (83%) create mode 100644 tests/Ratchet/Tests/Server/EchoServerTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index e0771e2..cada583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,9 @@ 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 + * Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router + * BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer + * Updated dependency to React/0.3 * 0.2.5 (2013-04-01) diff --git a/Makefile b/Makefile index d5e12a2..3a1ac95 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,8 @@ cover: phpunit --coverage-text --coverage-html=reports/coverage abtests: - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php 8002 & - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-stream.php 8001 & - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-libev.php 8004 & - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-libuv.php 8005 & + ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php 8001 & + ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-stream.php 8002 & ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php 8003 & wstest -m testeeserver -w ws://localhost:8000 & wstest -m fuzzingclient -s tests/AutobahnTestSuite/fuzzingclient-all.json diff --git a/README.md b/README.md index 3f78953..aa60b78 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ Shell access is required and root access is recommended. To avoid proxy/firewall blockage it's recommended WebSockets are run on port 80, which requires root access. In order to do this, along with your sync web stack, you can either use a proxy or two separate machines. You can find more details in the [server conf docs](http://socketo.me/docs/deploy#sconf). -PHP 5.3.3 (or higher) is required. If you have access, PHP 5.4 is *highly* recommended for its performance improvements. Cookies from your domain will be passed to the socket server, allowing you to identify users. Accessing your website's session data in Ratchet requires you to use [Symfony2 Sessions](http://symfony.com/doc/master/components/http_foundation/sessions.html) on your website. +PHP 5.3.9 (or higher) is required. If you have access, PHP 5.4 is *highly* recommended for its performance improvements. ### Documentation diff --git a/composer.lock b/composer.lock index f7408e1..e12161a 100644 --- a/composer.lock +++ b/composer.lock @@ -46,17 +46,17 @@ }, { "name": "guzzle/common", - "version": "v3.4.1", + "version": "v3.4.3", "target-dir": "Guzzle/Common", "source": { "type": "git", "url": "https://github.com/guzzle/common.git", - "reference": "v3.4.1" + "reference": "v3.4.3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/common/zipball/v3.4.1", - "reference": "v3.4.1", + "url": "https://api.github.com/repos/guzzle/common/zipball/v3.4.3", + "reference": "v3.4.3", "shasum": "" }, "require": { @@ -66,7 +66,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -86,21 +86,21 @@ "event", "exception" ], - "time": "2013-04-16 20:56:26" + "time": "2013-04-30 20:30:19" }, { "name": "guzzle/http", - "version": "v3.4.1", + "version": "v3.4.3", "target-dir": "Guzzle/Http", "source": { "type": "git", "url": "https://github.com/guzzle/http.git", - "reference": "v3.4.1" + "reference": "v3.4.3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/http/zipball/v3.4.1", - "reference": "v3.4.1", + "url": "https://api.github.com/repos/guzzle/http/zipball/v3.4.3", + "reference": "v3.4.3", "shasum": "" }, "require": { @@ -115,7 +115,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -143,21 +143,21 @@ "http", "http client" ], - "time": "2013-04-16 20:27:11" + "time": "2013-04-30 20:30:19" }, { "name": "guzzle/parser", - "version": "v3.4.1", + "version": "v3.4.3", "target-dir": "Guzzle/Parser", "source": { "type": "git", "url": "https://github.com/guzzle/parser.git", - "reference": "v3.4.1" + "reference": "v3.4.3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.4.1", - "reference": "v3.4.1", + "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.4.3", + "reference": "v3.4.3", "shasum": "" }, "require": { @@ -166,7 +166,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -187,21 +187,21 @@ "message", "url" ], - "time": "2013-03-07 22:13:59" + "time": "2013-04-26 15:47:38" }, { "name": "guzzle/stream", - "version": "v3.4.1", + "version": "v3.4.3", "target-dir": "Guzzle/Stream", "source": { "type": "git", "url": "https://github.com/guzzle/stream.git", - "reference": "v3.4.1" + "reference": "v3.4.3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.4.1", - "reference": "v3.4.1", + "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.4.3", + "reference": "v3.4.3", "shasum": "" }, "require": { @@ -214,7 +214,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -240,7 +240,7 @@ "component", "stream" ], - "time": "2013-04-06 18:28:51" + "time": "2013-04-26 15:47:38" }, { "name": "react/event-loop", diff --git a/tests/Ratchet/Tests/AbFuzzyServer.php b/src/Ratchet/Server/EchoServer.php similarity index 83% rename from tests/Ratchet/Tests/AbFuzzyServer.php rename to src/Ratchet/Server/EchoServer.php index 025cb4b..3f5069b 100644 --- a/tests/Ratchet/Tests/AbFuzzyServer.php +++ b/src/Ratchet/Server/EchoServer.php @@ -1,9 +1,9 @@ <?php -namespace Ratchet\Tests; +namespace Ratchet\Server; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; -class AbFuzzyServer implements MessageComponentInterface { +class EchoServer implements MessageComponentInterface { public function onOpen(ConnectionInterface $conn) { } diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-libev.php b/tests/AutobahnTestSuite/bin/fuzzingserver-libev.php index fb6d03f..b3dec26 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\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer)); $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 ea1fbad..729e7d7 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\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer)); $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 fb6d03f..b3dec26 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\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer)); $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 b20b3a1..01205f5 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\Server\EchoServer); $app = new Ratchet\Http\HttpServer($web); $web->setEncodingChecks(false); diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php b/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php index 98b4448..b10dc10 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\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Tests\AbFuzzyServer)); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoyServer)); $port = $argc > 1 ? $argv[1] : 8000; $sock->listen($port, '0.0.0.0'); diff --git a/tests/AutobahnTestSuite/fuzzingclient-all.json b/tests/AutobahnTestSuite/fuzzingclient-all.json index 8ebe6d8..5dbd204 100644 --- a/tests/AutobahnTestSuite/fuzzingclient-all.json +++ b/tests/AutobahnTestSuite/fuzzingclient-all.json @@ -3,11 +3,9 @@ , "outdir": "reports/ab" , "servers": [ - {"agent": "Ratchet/0.2.5 libevent", "url": "ws://localhost:8002", "options": {"version": 18}} - , {"agent": "Ratchet/0.2.5 libuv", "url": "ws://localhost:8005", "options": {"version": 18}} - , {"agent": "Ratchet/0.2.5 libev", "url": "ws://localhost:8004", "options": {"version": 18}} - , {"agent": "Ratchet/0.2.5 -utf8", "url": "ws://localhost:8003", "options": {"version": 18}} - , {"agent": "Ratchet/0.2.5 streams", "url": "ws://localhost:8001", "options": {"version": 18}} + {"agent": "Ratchet/0.3 libevent", "url": "ws://localhost:8001", "options": {"version": 18}} + , {"agent": "Ratchet/0.3 streams", "url": "ws://localhost:8002", "options": {"version": 18}} + , {"agent": "Ratchet/0.3 -utf8", "url": "ws://localhost:8003", "options": {"version": 18}} , {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}} ] diff --git a/tests/Ratchet/Tests/Server/EchoServerTest.php b/tests/Ratchet/Tests/Server/EchoServerTest.php new file mode 100644 index 0000000..4910dad --- /dev/null +++ b/tests/Ratchet/Tests/Server/EchoServerTest.php @@ -0,0 +1,26 @@ +<?php +namespace Ratchet\Tests\Server; +use Ratchet\Server\EchoServer; + +class EchoServerTest extends \PHPUnit_Framework_TestCase { + protected $_conn; + protected $_comp; + + public function setUp() { + $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); + $this->_comp = new EchoServer; + } + + public function testMessageEchod() { + $message = 'Tillsonburg, my back still aches when I hear that word.'; + $this->_conn->expects($this->once())->method('send')->with($message); + $this->_comp->onMessage($this->_conn, $message); + } + + public function testErrorClosesConnection() { + ob_start(); + $this->_conn->expects($this->once())->method('close'); + $this->_comp->onError($this->_conn, new \Exception); + ob_end_clean(); + } +} diff --git a/tests/Ratchet/Tests/Wamp/TopicManagerTest.php b/tests/Ratchet/Tests/Wamp/TopicManagerTest.php index 8388550..3c00cd1 100644 --- a/tests/Ratchet/Tests/Wamp/TopicManagerTest.php +++ b/tests/Ratchet/Tests/Wamp/TopicManagerTest.php @@ -7,12 +7,20 @@ use Ratchet\Wamp\TopicManager; */ class TopicManagerTest extends \PHPUnit_Framework_TestCase { private $mock; + + /** + * @var \Ratchet\Wamp\TopicManager + */ private $mngr; + + /** + * @var \Ratchet\ConnectionInterface + */ private $conn; public function setUp() { - $this->conn = $this->getMock('Ratchet\\ConnectionInterface'); - $this->mock = $this->getMock('Ratchet\\Wamp\\WampServerInterface'); + $this->conn = $this->getMock('\Ratchet\ConnectionInterface'); + $this->mock = $this->getMock('\Ratchet\Wamp\\WampServerInterface'); $this->mngr = new TopicManager($this->mock); $this->conn->WAMP = new \StdClass; From 8b3591ce0d03c3ab325d73c353e055f6711d9f15 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Wed, 8 May 2013 20:42:49 -0400 Subject: [PATCH 27/48] App class --- CHANGELOG.md | 4 +- README.md | 13 ++-- composer.lock | 1 + src/Ratchet/App.php | 96 +++++++++++++++++++++++++++ src/Ratchet/Http/RoutedHttpServer.php | 65 ------------------ 5 files changed, 104 insertions(+), 75 deletions(-) create mode 100644 src/Ratchet/App.php delete mode 100644 src/Ratchet/Http/RoutedHttpServer.php diff --git a/CHANGELOG.md b/CHANGELOG.md index cada583..aac7b5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * 0.3.0 (2013-xx-xx) + * Sugar and spice and everything nice: Added the Ratchet\App class for ease of use * Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router * BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer * Updated dependency to React/0.3 @@ -79,4 +80,5 @@ CHANGELOG * 0.1 (2012-05-11) * First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList - * I/O now handled by React, making Ratchet fully asynchronous \ No newline at end of file + * I/O now handled by React, making Ratchet fully asynchronous + diff --git a/README.md b/README.md index ca1a448..767e45c 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,6 @@ Need help? Have a question? Want to provide feedback? Write a message on the <?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; -use Ratchet\Http\RoutedHttpServer; -use Ratchet\Server\IoServer; -use Ratchet\Tests\AbFuzzyServer; require __DIR__ . '/vendor/autoload.php'; @@ -75,12 +72,10 @@ class Chat implements MessageComponentInterface { } // Run the server application through the WebSocket protocol on port 8080 - $router = new RoutedHttpServer; - $router->addRoute('/echo', new AbFuzzyServer); - $router->addRoute('/chat', new Chat); - - $server = IoServer::factory($router, 8000); - $server->run(); + $app = new Ratchet\App('example.com', 8080); + $app->route('/chat', new Chat); + $app->route('/echo', new Ratchet\Server\EchoServer); + $app->run(); ``` $ php chat.php \ No newline at end of file diff --git a/composer.lock b/composer.lock index e12161a..f49c13e 100644 --- a/composer.lock +++ b/composer.lock @@ -28,6 +28,7 @@ "Evenement": "src" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], diff --git a/src/Ratchet/App.php b/src/Ratchet/App.php new file mode 100644 index 0000000..2aeca35 --- /dev/null +++ b/src/Ratchet/App.php @@ -0,0 +1,96 @@ +<?php +namespace Ratchet; +use React\EventLoop\LoopInterface; +use React\EventLoop\Factory as LoopFactory; +use React\Socket\Server as Reactor; +use Ratchet\Http\HttpServerInterface; +use Ratchet\Wamp\WampServerInterface; +use Ratchet\Server\IoServer; +use Ratchet\Server\FlashPolicy; +use Ratchet\Http\HttpServer; +use Ratchet\Http\Router; +use Ratchet\WebSocket\WsServer; +use Ratchet\Wamp\WampServer; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Matcher\UrlMatcher; + +/** + * @todo Security - same origin by default + */ +class App { + /** + * @var \Symfony\Component\Routing\RouteCollection + */ + public $routes; + + /** + * @var \Ratchet\Server\IoServer + */ + public $flashServer; + + /** + * @var \Ratchet\Server\IoServer + */ + protected $_server; + + /** + * @param string $httpHost + * @param int $port + * @param string $address + * @param LoopInterface $loop + */ + public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) { + if (extension_loaded('xdebug')) { + echo "XDebug extension detected. Remember to disable this if performance testing or going live!\n"; + } + + if (null === $loop) { + $loop = LoopFactory::create(); + } + + $socket = new Reactor($loop); + $socket->listen($port, $address); + + $this->routes = new RouteCollection; + $this->_server = new IoServer(new HttpServer(new Router(new UrlMatcher($this->routes, new RequestContext))), $socket, $loop); + + $policy = new FlashPolicy; + $policy->addAllowedAccess($httpHost, 80); + $policy->addAllowedAccess($httpHost, $port); + $flashSock = new Reactor($loop); + $this->flashServer = new IoServer($policy, $flashSock); + + if (80 == $port) { + $flashSock->listen(843, '0.0.0.0'); + } else { + $flashSock->listen(8843); + } + } + + /** + * @param string $path + * @param ComponentInterface $controller + * @return ComponentInterface + */ + public function route($path, ComponentInterface $controller) { + if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) { + $decorated = $controller; + } elseif ($controller instanceof WampServerInterface) { + $decorated = new WsServer(new WampServer($controller)); + } elseif ($controller instanceof MessageComponentInterface) { + $decorated = new WsServer($controller); + } else { + $decorated = $controller; + } + + $this->routes->add(uniqid(), new Route($path, array('_controller' => $decorated))); + + return $decorated; + } + + public function run() { + $this->_server->run(); + } +} \ No newline at end of file diff --git a/src/Ratchet/Http/RoutedHttpServer.php b/src/Ratchet/Http/RoutedHttpServer.php deleted file mode 100644 index 728424c..0000000 --- a/src/Ratchet/Http/RoutedHttpServer.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -namespace Ratchet\Http; -use Ratchet\MessageComponentInterface; -use Ratchet\ConnectionInterface; -use Ratchet\WebSocket\WsServer; -use Symfony\Component\Routing\RouteCollection; -use Symfony\Component\Routing\Route; -use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Routing\Matcher\UrlMatcher; - -/** - */ -class RoutedHttpServer implements MessageComponentInterface { - protected $_routes; - protected $_server; - - public function __construct(RouteCollection $routes = null) { - if (null == $routes) { - $routes = new RouteCollection; - } - - $this->_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 f4ddea5f44bc64c06016acea9da80e5e87830a7a Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Wed, 29 May 2013 07:51:33 -0400 Subject: [PATCH 28/48] Removed updating remoteAddress via proxy header --- src/Ratchet/Http/HttpServer.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index 1ce78a8..5788807 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -50,10 +50,6 @@ class HttpServer implements MessageComponentInterface { $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); } From 0edd37af3f98032c9f02c129297f493ef0553eac Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sun, 9 Jun 2013 14:09:57 -0400 Subject: [PATCH 29/48] Hostname check verification refs #102 host parameter of App now verified on incoming connections --- src/Ratchet/App.php | 15 ++++++++++----- src/Ratchet/Http/Router.php | 12 ++++++++++-- .../bin/fuzzingserver-stream.php | 2 +- tests/Ratchet/Tests/Http/RouterTest.php | 4 ++++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Ratchet/App.php b/src/Ratchet/App.php index 2aeca35..7382653 100644 --- a/src/Ratchet/App.php +++ b/src/Ratchet/App.php @@ -16,9 +16,6 @@ use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Matcher\UrlMatcher; -/** - * @todo Security - same origin by default - */ class App { /** * @var \Symfony\Component\Routing\RouteCollection @@ -35,6 +32,12 @@ class App { */ protected $_server; + /** + * The Host passed in construct used for same origin policy + * @var string + */ + protected $httpHost; + /** * @param string $httpHost * @param int $port @@ -50,6 +53,8 @@ class App { $loop = LoopFactory::create(); } + $this->httpHost = $httpHost; + $socket = new Reactor($loop); $socket->listen($port, $address); @@ -72,7 +77,7 @@ class App { /** * @param string $path * @param ComponentInterface $controller - * @return ComponentInterface + * @return Symfony\Component\Routing\Route */ public function route($path, ComponentInterface $controller) { if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) { @@ -85,7 +90,7 @@ class App { $decorated = $controller; } - $this->routes->add(uniqid(), new Route($path, array('_controller' => $decorated))); + $this->routes->add(uniqid(), new Route($path, array('_controller' => $decorated), array(), array(), $this->httpHost)); return $decorated; } diff --git a/src/Ratchet/Http/Router.php b/src/Ratchet/Http/Router.php index 08e0c1a..8e6611e 100644 --- a/src/Ratchet/Http/Router.php +++ b/src/Ratchet/Http/Router.php @@ -26,6 +26,10 @@ class Router implements HttpServerInterface { throw new \UnexpectedValueException('$request can not be null'); } + $context = $this->_matcher->getContext(); + $context->setMethod($request->getMethod()); + $context->setHost($request->getHost()); + try { $route = $this->_matcher->match($request->getPath()); } catch (MethodNotAllowedException $nae) { @@ -57,14 +61,18 @@ class Router implements HttpServerInterface { * {@inheritdoc} */ function onClose(ConnectionInterface $conn) { - $conn->controller->onClose($conn); + if (isset($conn->controller)) { + $conn->controller->onClose($conn); + } } /** * {@inheritdoc} */ function onError(ConnectionInterface $conn, \Exception $e) { - $conn->controller->onError($conn, $e); + if (isset($conn->controller)) { + $conn->controller->onError($conn, $e); + } } /** diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php b/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php index b10dc10..ae3db8d 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\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoyServer)); + $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer)); $port = $argc > 1 ? $argv[1] : 8000; $sock->listen($port, '0.0.0.0'); diff --git a/tests/Ratchet/Tests/Http/RouterTest.php b/tests/Ratchet/Tests/Http/RouterTest.php index cc3309b..30abfed 100644 --- a/tests/Ratchet/Tests/Http/RouterTest.php +++ b/tests/Ratchet/Tests/Http/RouterTest.php @@ -16,6 +16,10 @@ class RouterTest extends \PHPUnit_Framework_TestCase { $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); $this->_req = $this->getMock('\Guzzle\Http\Message\RequestInterface'); $this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); + $this->_matcher + ->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext'))); $this->_router = new Router($this->_matcher); $this->_req->expects($this->any())->method('getPath')->will($this->returnValue('/whatever')); From e7541fdfcf6a1e27370be89dc14421457a0ea2ad Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 15 Jun 2013 14:09:19 -0400 Subject: [PATCH 30/48] App uses counter for ID instead of random --- src/Ratchet/App.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ratchet/App.php b/src/Ratchet/App.php index 7382653..6728c30 100644 --- a/src/Ratchet/App.php +++ b/src/Ratchet/App.php @@ -38,6 +38,8 @@ class App { */ protected $httpHost; + protected $_routeCounter = 0; + /** * @param string $httpHost * @param int $port @@ -90,7 +92,7 @@ class App { $decorated = $controller; } - $this->routes->add(uniqid(), new Route($path, array('_controller' => $decorated), array(), array(), $this->httpHost)); + $this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array(), array(), $this->httpHost)); return $decorated; } From a876770cda0b9ce96cd1bfc31d88116779dccc31 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sun, 16 Jun 2013 10:12:48 -0400 Subject: [PATCH 31/48] [Tests] Restructured test filesystem layout --- Makefile | 16 +++++------ composer.json | 3 +- composer.lock | 10 +++---- phpunit.xml.dist | 13 ++++----- .../bin/fuzzingserver-libev.php | 0 .../bin/fuzzingserver-libevent.php | 0 .../bin/fuzzingserver-libuv.php | 0 .../bin/fuzzingserver-noutf8.php | 0 .../bin/fuzzingserver-stream.php | 0 .../fuzzingclient-all.json | 0 .../fuzzingclient-profile.json | 0 .../fuzzingclient-quick.json | 0 tests/bootstrap.php | 5 ++++ .../AbstractMessageComponentTestCase.php | 2 +- .../Ratchet}/Mock/Component.php | 2 +- .../Ratchet}/Mock/Connection.php | 2 +- .../Ratchet}/Mock/ConnectionDecorator.php | 2 +- .../Ratchet}/Mock/WampComponent.php | 2 +- .../Ratchet}/NullComponent.php | 2 +- .../Wamp/Stub/WsWampServerInterface.php | 2 +- .../Stub/WsMessageComponentInterface.php | 2 +- .../AbstractConnectionDecoratorTest.php | 4 +-- .../Http/Message/RequestFactoryTest.php | 2 +- .../Http/HttpRequestParserTest.php | 2 +- .../Tests => unit}/Http/HttpServerTest.php | 4 +-- .../Tests => unit}/Http/RouterTest.php | 2 +- .../Tests => unit}/Server/EchoServerTest.php | 2 +- .../Server/FlashPolicyComponentTest.php | 2 +- .../Server/IoConnectionTest.php | 2 +- .../Tests => unit}/Server/IoServerTest.php | 2 +- .../Server/IpBlackListComponentTest.php | 2 +- .../Session/Serialize/PhpHandlerTest.php | 2 +- .../Session/SessionComponentTest.php | 10 +++---- .../Wamp/ServerProtocolTest.php | 6 ++-- .../Tests => unit}/Wamp/TopicManagerTest.php | 28 +++++++++---------- .../Tests => unit}/Wamp/TopicTest.php | 2 +- .../Wamp/WampConnectionTest.php | 2 +- .../Tests => unit}/Wamp/WampServerTest.php | 4 +-- .../WebSocket/Version/Hixie76Test.php | 2 +- .../WebSocket/Version/HyBi10Test.php | 2 +- .../WebSocket/Version/RFC6455/FrameTest.php | 2 +- .../Version/RFC6455/HandshakeVerifierTest.php | 2 +- .../WebSocket/Version/RFC6455/MessageTest.php | 2 +- .../WebSocket/Version/RFC6455Test.php | 2 +- .../WebSocket/VersionManagerTest.php | 2 +- .../Tests => unit}/WebSocket/WsServerTest.php | 4 +-- 46 files changed, 81 insertions(+), 78 deletions(-) rename tests/{AutobahnTestSuite => autobahn}/bin/fuzzingserver-libev.php (100%) rename tests/{AutobahnTestSuite => autobahn}/bin/fuzzingserver-libevent.php (100%) rename tests/{AutobahnTestSuite => autobahn}/bin/fuzzingserver-libuv.php (100%) rename tests/{AutobahnTestSuite => autobahn}/bin/fuzzingserver-noutf8.php (100%) rename tests/{AutobahnTestSuite => autobahn}/bin/fuzzingserver-stream.php (100%) rename tests/{AutobahnTestSuite => autobahn}/fuzzingclient-all.json (100%) rename tests/{AutobahnTestSuite => autobahn}/fuzzingclient-profile.json (100%) rename tests/{AutobahnTestSuite => autobahn}/fuzzingclient-quick.json (100%) create mode 100644 tests/bootstrap.php rename tests/{Ratchet/Tests => helpers/Ratchet}/AbstractMessageComponentTestCase.php (98%) rename tests/{Ratchet/Tests => helpers/Ratchet}/Mock/Component.php (96%) rename tests/{Ratchet/Tests => helpers/Ratchet}/Mock/Connection.php (92%) rename tests/{Ratchet/Tests => helpers/Ratchet}/Mock/ConnectionDecorator.php (93%) rename tests/{Ratchet/Tests => helpers/Ratchet}/Mock/WampComponent.php (97%) rename tests/{Ratchet/Tests/Mock => helpers/Ratchet}/NullComponent.php (96%) rename tests/{Ratchet/Tests => helpers/Ratchet}/Wamp/Stub/WsWampServerInterface.php (82%) rename tests/{Ratchet/Tests => helpers/Ratchet}/WebSocket/Stub/WsMessageComponentInterface.php (81%) rename tests/{Ratchet/Tests => unit}/AbstractConnectionDecoratorTest.php (98%) rename tests/{Ratchet/Tests => unit}/Http/Guzzle/Http/Message/RequestFactoryTest.php (97%) rename tests/{Ratchet/Tests => unit}/Http/HttpRequestParserTest.php (98%) rename tests/{Ratchet/Tests => unit}/Http/HttpServerTest.php (96%) rename tests/{Ratchet/Tests => unit}/Http/RouterTest.php (99%) rename tests/{Ratchet/Tests => unit}/Server/EchoServerTest.php (96%) rename tests/{Ratchet/Tests => unit}/Server/FlashPolicyComponentTest.php (99%) rename tests/{Ratchet/Tests => unit}/Server/IoConnectionTest.php (94%) rename tests/{Ratchet/Tests => unit}/Server/IoServerTest.php (99%) rename tests/{Ratchet/Tests => unit}/Server/IpBlackListComponentTest.php (99%) rename tests/{Ratchet/Tests => unit}/Session/Serialize/PhpHandlerTest.php (95%) rename tests/{Ratchet/Tests => unit}/Session/SessionComponentTest.php (95%) rename tests/{Ratchet/Tests => unit}/Wamp/ServerProtocolTest.php (98%) rename tests/{Ratchet/Tests => unit}/Wamp/TopicManagerTest.php (85%) rename tests/{Ratchet/Tests => unit}/Wamp/TopicTest.php (98%) rename tests/{Ratchet/Tests => unit}/Wamp/WampConnectionTest.php (98%) rename tests/{Ratchet/Tests => unit}/Wamp/WampServerTest.php (93%) rename tests/{Ratchet/Tests => unit}/WebSocket/Version/Hixie76Test.php (98%) rename tests/{Ratchet/Tests => unit}/WebSocket/Version/HyBi10Test.php (98%) rename tests/{Ratchet/Tests => unit}/WebSocket/Version/RFC6455/FrameTest.php (99%) rename tests/{Ratchet/Tests => unit}/WebSocket/Version/RFC6455/HandshakeVerifierTest.php (98%) rename tests/{Ratchet/Tests => unit}/WebSocket/Version/RFC6455/MessageTest.php (96%) rename tests/{Ratchet/Tests => unit}/WebSocket/Version/RFC6455Test.php (99%) rename tests/{Ratchet/Tests => unit}/WebSocket/VersionManagerTest.php (98%) rename tests/{Ratchet/Tests => unit}/WebSocket/WsServerTest.php (94%) diff --git a/Makefile b/Makefile index 3a1ac95..aa143cc 100644 --- a/Makefile +++ b/Makefile @@ -8,21 +8,21 @@ cover: phpunit --coverage-text --coverage-html=reports/coverage abtests: - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php 8001 & - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-stream.php 8002 & - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php 8003 & + ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver-libevent.php 8001 & + ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver-stream.php 8002 & + ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver-noutf8.php 8003 & wstest -m testeeserver -w ws://localhost:8000 & - wstest -m fuzzingclient -s tests/AutobahnTestSuite/fuzzingclient-all.json + wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json killall php wstest abtest: - ulimit -n 2048 && php tests/AutobahnTestSuite/bin/fuzzingserver-stream.php & - wstest -m fuzzingclient -s tests/AutobahnTestSuite/fuzzingclient-quick.json + ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver-stream.php & + wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json killall php profile: - php -d 'xdebug.profiler_enable=1' tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php & - wstest -m fuzzingclient -s tests/AutobahnTestSuite/fuzzingclient-profile.json + php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver-libevent.php & + wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json killall php apidocs: diff --git a/composer.json b/composer.json index a790d55..293c6be 100644 --- a/composer.json +++ b/composer.json @@ -20,8 +20,7 @@ } , "autoload": { "psr-0": { - "Ratchet\\Tests": "tests" - , "Ratchet": "src" + "Ratchet": "src" } } , "require": { diff --git a/composer.lock b/composer.lock index a803718..1ed2a55 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": "d5a238bcdccc33fab13dd3c65b663beb", + "hash": "f5d2fe5b143f2c5d2e8f674dd2f6bc16", "packages": [ { "name": "evenement/evenement", @@ -429,17 +429,17 @@ }, { "name": "symfony/http-foundation", - "version": "v2.3.0", + "version": "v2.3.1", "target-dir": "Symfony/Component/HttpFoundation", "source": { "type": "git", "url": "https://github.com/symfony/HttpFoundation.git", - "reference": "v2.3.0-RC1" + "reference": "v2.3.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.3.0-RC1", - "reference": "v2.3.0-RC1", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.3.1", + "reference": "v2.3.1", "shasum": "" }, "require": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 78b3128..117227a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,7 @@ <phpunit forceCoversAnnotation="true" mapTestClassNameToCoveredClassName="true" - bootstrap="vendor/autoload.php" + bootstrap="tests/bootstrap.php" colors="true" backupGlobals="false" backupStaticAttributes="false" @@ -11,15 +11,14 @@ > <testsuites> - <testsuite name="Ratchet Test Suite"> - <directory>./tests/Ratchet/</directory> + <testsuite name="unit"> + <directory>./tests/unit/</directory> </testsuite> </testsuites> <filter> - <blacklist> - <directory>./tests/</directory> - <directory>./vendor/</directory> - </blacklist> + <whitelist> + <directory>./src/</directory> + </whitelist> </filter> </phpunit> \ No newline at end of file diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-libev.php b/tests/autobahn/bin/fuzzingserver-libev.php similarity index 100% rename from tests/AutobahnTestSuite/bin/fuzzingserver-libev.php rename to tests/autobahn/bin/fuzzingserver-libev.php diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php b/tests/autobahn/bin/fuzzingserver-libevent.php similarity index 100% rename from tests/AutobahnTestSuite/bin/fuzzingserver-libevent.php rename to tests/autobahn/bin/fuzzingserver-libevent.php diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-libuv.php b/tests/autobahn/bin/fuzzingserver-libuv.php similarity index 100% rename from tests/AutobahnTestSuite/bin/fuzzingserver-libuv.php rename to tests/autobahn/bin/fuzzingserver-libuv.php diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php b/tests/autobahn/bin/fuzzingserver-noutf8.php similarity index 100% rename from tests/AutobahnTestSuite/bin/fuzzingserver-noutf8.php rename to tests/autobahn/bin/fuzzingserver-noutf8.php diff --git a/tests/AutobahnTestSuite/bin/fuzzingserver-stream.php b/tests/autobahn/bin/fuzzingserver-stream.php similarity index 100% rename from tests/AutobahnTestSuite/bin/fuzzingserver-stream.php rename to tests/autobahn/bin/fuzzingserver-stream.php diff --git a/tests/AutobahnTestSuite/fuzzingclient-all.json b/tests/autobahn/fuzzingclient-all.json similarity index 100% rename from tests/AutobahnTestSuite/fuzzingclient-all.json rename to tests/autobahn/fuzzingclient-all.json diff --git a/tests/AutobahnTestSuite/fuzzingclient-profile.json b/tests/autobahn/fuzzingclient-profile.json similarity index 100% rename from tests/AutobahnTestSuite/fuzzingclient-profile.json rename to tests/autobahn/fuzzingclient-profile.json diff --git a/tests/AutobahnTestSuite/fuzzingclient-quick.json b/tests/autobahn/fuzzingclient-quick.json similarity index 100% rename from tests/AutobahnTestSuite/fuzzingclient-quick.json rename to tests/autobahn/fuzzingclient-quick.json diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..9d21c77 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,5 @@ +<?php + + $loader = require __DIR__ . '/../vendor/autoload.php'; + $loader->add('Ratchet', __DIR__ . '/helpers'); + $loader->register(); diff --git a/tests/Ratchet/Tests/AbstractMessageComponentTestCase.php b/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php similarity index 98% rename from tests/Ratchet/Tests/AbstractMessageComponentTestCase.php rename to tests/helpers/Ratchet/AbstractMessageComponentTestCase.php index a9223af..990e98c 100644 --- a/tests/Ratchet/Tests/AbstractMessageComponentTestCase.php +++ b/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests; +namespace Ratchet; abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestCase { protected $_app; diff --git a/tests/Ratchet/Tests/Mock/Component.php b/tests/helpers/Ratchet/Mock/Component.php similarity index 96% rename from tests/Ratchet/Tests/Mock/Component.php rename to tests/helpers/Ratchet/Mock/Component.php index 26427b4..d2ef27d 100644 --- a/tests/Ratchet/Tests/Mock/Component.php +++ b/tests/helpers/Ratchet/Mock/Component.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Mock; +namespace Ratchet\Mock; use Ratchet\MessageComponentInterface; use Ratchet\WebSocket\WsServerInterface; use Ratchet\ConnectionInterface; diff --git a/tests/Ratchet/Tests/Mock/Connection.php b/tests/helpers/Ratchet/Mock/Connection.php similarity index 92% rename from tests/Ratchet/Tests/Mock/Connection.php rename to tests/helpers/Ratchet/Mock/Connection.php index 8707266..525487f 100644 --- a/tests/Ratchet/Tests/Mock/Connection.php +++ b/tests/helpers/Ratchet/Mock/Connection.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Mock; +namespace Ratchet\Mock; use Ratchet\ConnectionInterface; class Connection implements ConnectionInterface { diff --git a/tests/Ratchet/Tests/Mock/ConnectionDecorator.php b/tests/helpers/Ratchet/Mock/ConnectionDecorator.php similarity index 93% rename from tests/Ratchet/Tests/Mock/ConnectionDecorator.php rename to tests/helpers/Ratchet/Mock/ConnectionDecorator.php index 72a430c..c8a0d17 100644 --- a/tests/Ratchet/Tests/Mock/ConnectionDecorator.php +++ b/tests/helpers/Ratchet/Mock/ConnectionDecorator.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Mock; +namespace Ratchet\Mock; use Ratchet\AbstractConnectionDecorator; class ConnectionDecorator extends AbstractConnectionDecorator { diff --git a/tests/Ratchet/Tests/Mock/WampComponent.php b/tests/helpers/Ratchet/Mock/WampComponent.php similarity index 97% rename from tests/Ratchet/Tests/Mock/WampComponent.php rename to tests/helpers/Ratchet/Mock/WampComponent.php index 7493d6f..87af27c 100644 --- a/tests/Ratchet/Tests/Mock/WampComponent.php +++ b/tests/helpers/Ratchet/Mock/WampComponent.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Mock; +namespace Ratchet\Mock; use Ratchet\Wamp\WampServerInterface; use Ratchet\WebSocket\WsServerInterface; use Ratchet\ConnectionInterface; diff --git a/tests/Ratchet/Tests/Mock/NullComponent.php b/tests/helpers/Ratchet/NullComponent.php similarity index 96% rename from tests/Ratchet/Tests/Mock/NullComponent.php rename to tests/helpers/Ratchet/NullComponent.php index 73a44a1..4352830 100644 --- a/tests/Ratchet/Tests/Mock/NullComponent.php +++ b/tests/helpers/Ratchet/NullComponent.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Mock; +namespace Ratchet; use Ratchet\ConnectionInterface; use Ratchet\MessageComponentInterface; use Ratchet\WebSocket\WsServerInterface; diff --git a/tests/Ratchet/Tests/Wamp/Stub/WsWampServerInterface.php b/tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php similarity index 82% rename from tests/Ratchet/Tests/Wamp/Stub/WsWampServerInterface.php rename to tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php index d1f8341..45cb35f 100644 --- a/tests/Ratchet/Tests/Wamp/Stub/WsWampServerInterface.php +++ b/tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Wamp\Stub; +namespace Ratchet\Wamp\Stub; use Ratchet\WebSocket\WsServerInterface; use Ratchet\Wamp\WampServerInterface; diff --git a/tests/Ratchet/Tests/WebSocket/Stub/WsMessageComponentInterface.php b/tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php similarity index 81% rename from tests/Ratchet/Tests/WebSocket/Stub/WsMessageComponentInterface.php rename to tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php index 2e39a0b..40e6928 100644 --- a/tests/Ratchet/Tests/WebSocket/Stub/WsMessageComponentInterface.php +++ b/tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket\Stub; +namespace Ratchet\WebSocket\Stub; use Ratchet\MessageComponentInterface; use Ratchet\WebSocket\WsServerInterface; diff --git a/tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php b/tests/unit/AbstractConnectionDecoratorTest.php similarity index 98% rename from tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php rename to tests/unit/AbstractConnectionDecoratorTest.php index f196c79..905cce1 100644 --- a/tests/Ratchet/Tests/AbstractConnectionDecoratorTest.php +++ b/tests/unit/AbstractConnectionDecoratorTest.php @@ -1,6 +1,6 @@ <?php -namespace Ratchet\Tests; -use Ratchet\Tests\Mock\ConnectionDecorator; +namespace Ratchet; +use Ratchet\Mock\ConnectionDecorator; /** * @covers Ratchet\AbstractConnectionDecorator diff --git a/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php b/tests/unit/Http/Guzzle/Http/Message/RequestFactoryTest.php similarity index 97% rename from tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php rename to tests/unit/Http/Guzzle/Http/Message/RequestFactoryTest.php index 9709745..8cada8c 100644 --- a/tests/Ratchet/Tests/Http/Guzzle/Http/Message/RequestFactoryTest.php +++ b/tests/unit/Http/Guzzle/Http/Message/RequestFactoryTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Http\Guzzle\Http\Message; +namespace Ratchet\Http\Guzzle\Http\Message; use Ratchet\Http\Guzzle\Http\Message\RequestFactory; /** diff --git a/tests/Ratchet/Tests/Http/HttpRequestParserTest.php b/tests/unit/Http/HttpRequestParserTest.php similarity index 98% rename from tests/Ratchet/Tests/Http/HttpRequestParserTest.php rename to tests/unit/Http/HttpRequestParserTest.php index 3a540ec..89a0e80 100644 --- a/tests/Ratchet/Tests/Http/HttpRequestParserTest.php +++ b/tests/unit/Http/HttpRequestParserTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Http; +namespace Ratchet\Http; use Ratchet\Http\HttpRequestParser; /** diff --git a/tests/Ratchet/Tests/Http/HttpServerTest.php b/tests/unit/Http/HttpServerTest.php similarity index 96% rename from tests/Ratchet/Tests/Http/HttpServerTest.php rename to tests/unit/Http/HttpServerTest.php index 203172a..2c069f1 100644 --- a/tests/Ratchet/Tests/Http/HttpServerTest.php +++ b/tests/unit/Http/HttpServerTest.php @@ -1,6 +1,6 @@ <?php -namespace Ratchet\Tests\Http; -use Ratchet\Tests\AbstractMessageComponentTestCase; +namespace Ratchet\Http; +use Ratchet\AbstractMessageComponentTestCase; /** * @covers Ratchet\Http\HttpServer diff --git a/tests/Ratchet/Tests/Http/RouterTest.php b/tests/unit/Http/RouterTest.php similarity index 99% rename from tests/Ratchet/Tests/Http/RouterTest.php rename to tests/unit/Http/RouterTest.php index 30abfed..8656dd3 100644 --- a/tests/Ratchet/Tests/Http/RouterTest.php +++ b/tests/unit/Http/RouterTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Http; +namespace Ratchet\Http; use Ratchet\Http\Router; use Symfony\Component\Routing\Exception\ResourceNotFoundException; diff --git a/tests/Ratchet/Tests/Server/EchoServerTest.php b/tests/unit/Server/EchoServerTest.php similarity index 96% rename from tests/Ratchet/Tests/Server/EchoServerTest.php rename to tests/unit/Server/EchoServerTest.php index 4910dad..47fb0e2 100644 --- a/tests/Ratchet/Tests/Server/EchoServerTest.php +++ b/tests/unit/Server/EchoServerTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Server; +namespace Ratchet\Server; use Ratchet\Server\EchoServer; class EchoServerTest extends \PHPUnit_Framework_TestCase { diff --git a/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php b/tests/unit/Server/FlashPolicyComponentTest.php similarity index 99% rename from tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php rename to tests/unit/Server/FlashPolicyComponentTest.php index 60088b4..67f88a7 100644 --- a/tests/Ratchet/Tests/Server/FlashPolicyComponentTest.php +++ b/tests/unit/Server/FlashPolicyComponentTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Application\Server; +namespace Ratchet\Application\Server; use Ratchet\Server\FlashPolicy; /** diff --git a/tests/Ratchet/Tests/Server/IoConnectionTest.php b/tests/unit/Server/IoConnectionTest.php similarity index 94% rename from tests/Ratchet/Tests/Server/IoConnectionTest.php rename to tests/unit/Server/IoConnectionTest.php index d2ff568..0cb7fb4 100644 --- a/tests/Ratchet/Tests/Server/IoConnectionTest.php +++ b/tests/unit/Server/IoConnectionTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Application\Server; +namespace Ratchet\Application\Server; use Ratchet\Server\IoConnection; /** diff --git a/tests/Ratchet/Tests/Server/IoServerTest.php b/tests/unit/Server/IoServerTest.php similarity index 99% rename from tests/Ratchet/Tests/Server/IoServerTest.php rename to tests/unit/Server/IoServerTest.php index d375da8..2b0258c 100644 --- a/tests/Ratchet/Tests/Server/IoServerTest.php +++ b/tests/unit/Server/IoServerTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Server; +namespace Ratchet\Server; use Ratchet\Server\IoServer; use React\EventLoop\StreamSelectLoop; use React\Socket\Server; diff --git a/tests/Ratchet/Tests/Server/IpBlackListComponentTest.php b/tests/unit/Server/IpBlackListComponentTest.php similarity index 99% rename from tests/Ratchet/Tests/Server/IpBlackListComponentTest.php rename to tests/unit/Server/IpBlackListComponentTest.php index c847bce..419f077 100644 --- a/tests/Ratchet/Tests/Server/IpBlackListComponentTest.php +++ b/tests/unit/Server/IpBlackListComponentTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Server; +namespace Ratchet\Server; use Ratchet\Server\IpBlackList; /** diff --git a/tests/Ratchet/Tests/Session/Serialize/PhpHandlerTest.php b/tests/unit/Session/Serialize/PhpHandlerTest.php similarity index 95% rename from tests/Ratchet/Tests/Session/Serialize/PhpHandlerTest.php rename to tests/unit/Session/Serialize/PhpHandlerTest.php index c8bf0dc..cf1ef48 100644 --- a/tests/Ratchet/Tests/Session/Serialize/PhpHandlerTest.php +++ b/tests/unit/Session/Serialize/PhpHandlerTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Session\Serialize; +namespace Ratchet\Session\Serialize; use Ratchet\Session\Serialize\PhpHandler; /** diff --git a/tests/Ratchet/Tests/Session/SessionComponentTest.php b/tests/unit/Session/SessionComponentTest.php similarity index 95% rename from tests/Ratchet/Tests/Session/SessionComponentTest.php rename to tests/unit/Session/SessionComponentTest.php index a411eb1..4b3f87b 100644 --- a/tests/Ratchet/Tests/Session/SessionComponentTest.php +++ b/tests/unit/Session/SessionComponentTest.php @@ -1,8 +1,8 @@ <?php -namespace Ratchet\Tests\Session; -use Ratchet\Tests\AbstractMessageComponentTestCase; +namespace Ratchet\Session; +use Ratchet\AbstractMessageComponentTestCase; use Ratchet\Session\SessionProvider; -use Ratchet\Tests\Mock\MemorySessionHandler; +use Ratchet\Mock\MemorySessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; use Guzzle\Http\Message\Request; @@ -31,7 +31,7 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { } public function getDecoratorClassString() { - return '\Ratchet\Tests\Mock\NullComponent'; + return '\Ratchet\NullComponent'; } public function getComponentClassString() { @@ -119,7 +119,7 @@ class SessionProviderTest extends AbstractMessageComponentTestCase { } public function testGetSubProtocolsGetFromApp() { - $mock = $this->getMock('Ratchet\\Tests\\WebSocket\\Stub\\WsMessageComponentInterface'); + $mock = $this->getMock('Ratchet\WebSocket\Stub\WsMessageComponentInterface'); $mock->expects($this->once())->method('getSubProtocols')->will($this->returnValue(array('hello', 'world'))); $comp = new SessionProvider($mock, new NullSessionHandler); diff --git a/tests/Ratchet/Tests/Wamp/ServerProtocolTest.php b/tests/unit/Wamp/ServerProtocolTest.php similarity index 98% rename from tests/Ratchet/Tests/Wamp/ServerProtocolTest.php rename to tests/unit/Wamp/ServerProtocolTest.php index 4217ea3..9ecd89d 100644 --- a/tests/Ratchet/Tests/Wamp/ServerProtocolTest.php +++ b/tests/unit/Wamp/ServerProtocolTest.php @@ -1,9 +1,9 @@ <?php -namespace Ratchet\Tests\Wamp; +namespace Ratchet\Wamp; use Ratchet\Wamp\ServerProtocol; use Ratchet\Wamp\WampConnection; -use Ratchet\Tests\Mock\Connection; -use Ratchet\Tests\Mock\WampComponent as TestComponent; +use Ratchet\Mock\Connection; +use Ratchet\Mock\WampComponent as TestComponent; /** * @covers Ratchet\Wamp\ServerProtocol diff --git a/tests/Ratchet/Tests/Wamp/TopicManagerTest.php b/tests/unit/Wamp/TopicManagerTest.php similarity index 85% rename from tests/Ratchet/Tests/Wamp/TopicManagerTest.php rename to tests/unit/Wamp/TopicManagerTest.php index 3c00cd1..e096a28 100644 --- a/tests/Ratchet/Tests/Wamp/TopicManagerTest.php +++ b/tests/unit/Wamp/TopicManagerTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Wamp; +namespace Ratchet\Wamp; use Ratchet\Wamp\TopicManager; /** @@ -20,7 +20,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->conn = $this->getMock('\Ratchet\ConnectionInterface'); - $this->mock = $this->getMock('\Ratchet\Wamp\\WampServerInterface'); + $this->mock = $this->getMock('\Ratchet\Wamp\WampServerInterface'); $this->mngr = new TopicManager($this->mock); $this->conn->WAMP = new \StdClass; @@ -28,19 +28,19 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { } public function testGetTopicReturnsTopicObject() { - $class = new \ReflectionClass('Ratchet\\Wamp\\TopicManager'); + $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); $topic = $method->invokeArgs($this->mngr, array('The Topic')); - $this->assertInstanceOf('Ratchet\\Wamp\\Topic', $topic); + $this->assertInstanceOf('Ratchet\Wamp\Topic', $topic); } public function testGetTopicCreatesTopicWithSameName() { $name = 'The Topic'; - $class = new \ReflectionClass('Ratchet\\Wamp\\TopicManager'); + $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); @@ -50,7 +50,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { } public function testGetTopicReturnsSameObject() { - $class = new \ReflectionClass('Ratchet\\Wamp\\TopicManager'); + $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); @@ -71,7 +71,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { $this->mock->expects($this->once())->method('onCall')->with( $this->conn , $id - , $this->isInstanceOf('Ratchet\\Wamp\\Topic') + , $this->isInstanceOf('Ratchet\Wamp\Topic') , array() ); @@ -80,7 +80,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { public function testOnSubscribeCreatesTopicObject() { $this->mock->expects($this->once())->method('onSubscribe')->with( - $this->conn, $this->isInstanceOf('Ratchet\\Wamp\\Topic') + $this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic') ); $this->mngr->onSubscribe($this->conn, 'new topic'); @@ -89,7 +89,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { public function testTopicIsInConnectionOnSubscribe() { $name = 'New Topic'; - $class = new \ReflectionClass('Ratchet\\Wamp\\TopicManager'); + $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); @@ -110,7 +110,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { public function testUnsubscribeEvent() { $name = 'in and out'; $this->mock->expects($this->once())->method('onUnsubscribe')->with( - $this->conn, $this->isInstanceOf('Ratchet\\Wamp\\Topic') + $this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic') ); $this->mngr->onSubscribe($this->conn, $name); @@ -129,7 +129,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { public function testUnsubscribeRemovesTopicFromConnection() { $name = 'Bye Bye Topic'; - $class = new \ReflectionClass('Ratchet\\Wamp\\TopicManager'); + $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); @@ -146,7 +146,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { $this->mock->expects($this->once())->method('onPublish')->with( $this->conn - , $this->isInstanceOf('Ratchet\\Wamp\\Topic') + , $this->isInstanceOf('Ratchet\Wamp\Topic') , $msg , $this->isType('array') , $this->isType('array') @@ -163,7 +163,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { public function testConnIsRemovedFromTopicOnClose() { $name = 'State testing'; - $class = new \ReflectionClass('Ratchet\\Wamp\\TopicManager'); + $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); @@ -188,7 +188,7 @@ class TopicManagerTest extends \PHPUnit_Framework_TestCase { public function testGetSubProtocolsBubbles() { $subs = array('hello', 'world'); - $app = $this->getMock('Ratchet\\Tests\\Wamp\\Stub\\WsWampServerInterface'); + $app = $this->getMock('Ratchet\Wamp\Stub\WsWampServerInterface'); $app->expects($this->once())->method('getSubProtocols')->will($this->returnValue($subs)); $mngr = new TopicManager($app); diff --git a/tests/Ratchet/Tests/Wamp/TopicTest.php b/tests/unit/Wamp/TopicTest.php similarity index 98% rename from tests/Ratchet/Tests/Wamp/TopicTest.php rename to tests/unit/Wamp/TopicTest.php index d5358d1..0a2102b 100644 --- a/tests/Ratchet/Tests/Wamp/TopicTest.php +++ b/tests/unit/Wamp/TopicTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Wamp; +namespace Ratchet\Wamp; use Ratchet\Wamp\Topic; use Ratchet\Wamp\WampConnection; diff --git a/tests/Ratchet/Tests/Wamp/WampConnectionTest.php b/tests/unit/Wamp/WampConnectionTest.php similarity index 98% rename from tests/Ratchet/Tests/Wamp/WampConnectionTest.php rename to tests/unit/Wamp/WampConnectionTest.php index a1679ed..7f82f98 100644 --- a/tests/Ratchet/Tests/Wamp/WampConnectionTest.php +++ b/tests/unit/Wamp/WampConnectionTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\Wamp; +namespace Ratchet\Wamp; use Ratchet\Wamp\WampConnection; /** diff --git a/tests/Ratchet/Tests/Wamp/WampServerTest.php b/tests/unit/Wamp/WampServerTest.php similarity index 93% rename from tests/Ratchet/Tests/Wamp/WampServerTest.php rename to tests/unit/Wamp/WampServerTest.php index e83dc28..b1ee6f1 100644 --- a/tests/Ratchet/Tests/Wamp/WampServerTest.php +++ b/tests/unit/Wamp/WampServerTest.php @@ -1,7 +1,7 @@ <?php -namespace Ratchet\Tests\Wamp; +namespace Ratchet\Wamp; use Ratchet\Wamp\WampServer; -use Ratchet\Tests\AbstractMessageComponentTestCase; +use Ratchet\AbstractMessageComponentTestCase; /** * @covers Ratchet\Wamp\WampServer diff --git a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php b/tests/unit/WebSocket/Version/Hixie76Test.php similarity index 98% rename from tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php rename to tests/unit/WebSocket/Version/Hixie76Test.php index 3ac1f4a..07d863b 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/Hixie76Test.php +++ b/tests/unit/WebSocket/Version/Hixie76Test.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket\Version; +namespace Ratchet\WebSocket\Version; use Ratchet\WebSocket\Version\Hixie76; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; diff --git a/tests/Ratchet/Tests/WebSocket/Version/HyBi10Test.php b/tests/unit/WebSocket/Version/HyBi10Test.php similarity index 98% rename from tests/Ratchet/Tests/WebSocket/Version/HyBi10Test.php rename to tests/unit/WebSocket/Version/HyBi10Test.php index 11ebaa2..74044df 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/HyBi10Test.php +++ b/tests/unit/WebSocket/Version/HyBi10Test.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket\Version; +namespace Ratchet\WebSocket\Version; use Ratchet\WebSocket\Version\HyBi10; use Ratchet\WebSocket\Version\RFC6455\Frame; diff --git a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php b/tests/unit/WebSocket/Version/RFC6455/FrameTest.php similarity index 99% rename from tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php rename to tests/unit/WebSocket/Version/RFC6455/FrameTest.php index 7b860f3..ca4447c 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/FrameTest.php +++ b/tests/unit/WebSocket/Version/RFC6455/FrameTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket\Version\RFC6455; +namespace Ratchet\WebSocket\Version\RFC6455; use Ratchet\WebSocket\Version\RFC6455\Frame; /** diff --git a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/HandshakeVerifierTest.php b/tests/unit/WebSocket/Version/RFC6455/HandshakeVerifierTest.php similarity index 98% rename from tests/Ratchet/Tests/WebSocket/Version/RFC6455/HandshakeVerifierTest.php rename to tests/unit/WebSocket/Version/RFC6455/HandshakeVerifierTest.php index 3822ff3..cdaec6d 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/HandshakeVerifierTest.php +++ b/tests/unit/WebSocket/Version/RFC6455/HandshakeVerifierTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket\Version\RFC6455; +namespace Ratchet\WebSocket\Version\RFC6455; use Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier; /** diff --git a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/MessageTest.php b/tests/unit/WebSocket/Version/RFC6455/MessageTest.php similarity index 96% rename from tests/Ratchet/Tests/WebSocket/Version/RFC6455/MessageTest.php rename to tests/unit/WebSocket/Version/RFC6455/MessageTest.php index 5cc2a58..4b72170 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/RFC6455/MessageTest.php +++ b/tests/unit/WebSocket/Version/RFC6455/MessageTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket\Version\RFC6455\Message; +namespace Ratchet\WebSocket\Version\RFC6455\Message; use Ratchet\WebSocket\Version\RFC6455\Message; use Ratchet\WebSocket\Version\RFC6455\Frame; diff --git a/tests/Ratchet/Tests/WebSocket/Version/RFC6455Test.php b/tests/unit/WebSocket/Version/RFC6455Test.php similarity index 99% rename from tests/Ratchet/Tests/WebSocket/Version/RFC6455Test.php rename to tests/unit/WebSocket/Version/RFC6455Test.php index 007eb83..89a9ae6 100644 --- a/tests/Ratchet/Tests/WebSocket/Version/RFC6455Test.php +++ b/tests/unit/WebSocket/Version/RFC6455Test.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket\Version; +namespace Ratchet\WebSocket\Version; use Ratchet\WebSocket\Version\RFC6455; use Ratchet\WebSocket\Version\RFC6455\Frame; use Guzzle\Http\Message\RequestFactory; diff --git a/tests/Ratchet/Tests/WebSocket/VersionManagerTest.php b/tests/unit/WebSocket/VersionManagerTest.php similarity index 98% rename from tests/Ratchet/Tests/WebSocket/VersionManagerTest.php rename to tests/unit/WebSocket/VersionManagerTest.php index 68fa336..6083548 100644 --- a/tests/Ratchet/Tests/WebSocket/VersionManagerTest.php +++ b/tests/unit/WebSocket/VersionManagerTest.php @@ -1,5 +1,5 @@ <?php -namespace Ratchet\Tests\WebSocket; +namespace Ratchet\WebSocket; use Ratchet\WebSocket\VersionManager; use Ratchet\WebSocket\Version\RFC6455; use Ratchet\WebSocket\Version\HyBi10; diff --git a/tests/Ratchet/Tests/WebSocket/WsServerTest.php b/tests/unit/WebSocket/WsServerTest.php similarity index 94% rename from tests/Ratchet/Tests/WebSocket/WsServerTest.php rename to tests/unit/WebSocket/WsServerTest.php index 510b5aa..1a3626c 100644 --- a/tests/Ratchet/Tests/WebSocket/WsServerTest.php +++ b/tests/unit/WebSocket/WsServerTest.php @@ -1,7 +1,7 @@ <?php -namespace Ratchet\Tests\WebSocket; +namespace Ratchet\WebSocket; use Ratchet\WebSocket\WsServer; -use Ratchet\Tests\Mock\Component as MockComponent; +use Ratchet\Mock\Component as MockComponent; /** * @covers Ratchet\WebSocket\WsServer From b1b5fbe1a7c2d22e59f3d4b8092c6cf84f67fce3 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sun, 16 Jun 2013 17:40:38 -0400 Subject: [PATCH 32/48] [Tests] Added Guzzle integration tests --- phpunit.xml.dist | 6 ++++ tests/integration/GuzzleTest.php | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tests/integration/GuzzleTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 117227a..0cc5451 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,6 +16,12 @@ </testsuite> </testsuites> + <testsuites> + <testsuite name="integration"> + <directory>./tests/integration/</directory> + </testsuite> + </testsuites> + <filter> <whitelist> <directory>./src/</directory> diff --git a/tests/integration/GuzzleTest.php b/tests/integration/GuzzleTest.php new file mode 100644 index 0000000..6d3a544 --- /dev/null +++ b/tests/integration/GuzzleTest.php @@ -0,0 +1,53 @@ +<?php +use Guzzle\Http\Message\Request; + +class GuzzleTest extends \PHPUnit_Framework_TestCase { + protected $_request; + + protected $_headers = array( + 'Upgrade' => 'websocket' + , 'Connection' => 'Upgrade' + , 'Host' => 'localhost:8080' + , 'Origin' => 'chrome://newtab' + , 'Sec-WebSocket-Protocol' => 'one, two, three' + , 'Sec-WebSocket-Key' => '9bnXNp3ae6FbFFRtPdiPXA==' + , 'Sec-WebSocket-Version' => '13' + ); + + public function setUp() { + $this->_request = new Request('GET', 'http://localhost', $this->_headers); + } + + public function testGetHeaderString() { + $this->assertEquals('Upgrade', (string)$this->_request->getHeader('connection')); + $this->assertEquals('9bnXNp3ae6FbFFRtPdiPXA==', (string)$this->_request->getHeader('Sec-Websocket-Key')); + } + + public function testGetHeaderInteger() { + $this->assertSame('13', (string)$this->_request->getHeader('Sec-Websocket-Version')); + $this->assertSame(13, (int)(string)$this->_request->getHeader('Sec-WebSocket-Version')); + } + + public function testGetHeaderObject() { + $this->assertInstanceOf('Guzzle\Http\Message\Header', $this->_request->getHeader('Origin')); + $this->assertNull($this->_request->getHeader('Non-existant-header')); + } + + public function testHeaderObjectNormalizeValues() { + $expected = 1 + substr_count($this->_headers['Sec-WebSocket-Protocol'], ','); + $protocols = $this->_request->getHeader('Sec-WebSocket-Protocol')->normalize(); + $count = 0; + + foreach ($protocols as $protocol) { + $count++; + } + + $this->assertEquals($expected, $count); + $this->assertEquals($expected, count($protocols)); + } + + public function testRequestFactoryCreateSignature() { + $ref = new \ReflectionMethod('Guzzle\Http\Message\RequestFactory', 'create'); + $this->assertEquals(2, $ref->getNumberOfRequiredParameters()); + } +} \ No newline at end of file From b658ae6e7eb42c4b222044df579d858b4f181916 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 11:11:34 -0400 Subject: [PATCH 33/48] Composer restrictions + update --- composer.json | 2 +- composer.lock | 108 ++++++++++++++++++++++---------------------- src/Ratchet/App.php | 2 +- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/composer.json b/composer.json index 293c6be..12c95f0 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ , "require": { "php": ">=5.3.9" , "react/socket": "0.3.*" - , "guzzle/http": "3.6.*" + , "guzzle/http": ">=3.6.0,<3.8.0-dev" , "symfony/http-foundation": "~2.2" , "symfony/routing": "~2.2" } diff --git a/composer.lock b/composer.lock index 1ed2a55..4800ed7 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": "f5d2fe5b143f2c5d2e8f674dd2f6bc16", + "hash": "a45c5bcb9c18e390adc2a60ffd059a52", "packages": [ { "name": "evenement/evenement", @@ -47,17 +47,17 @@ }, { "name": "guzzle/common", - "version": "v3.6.0", + "version": "v3.7.1", "target-dir": "Guzzle/Common", "source": { "type": "git", "url": "https://github.com/guzzle/common.git", - "reference": "v3.6.0" + "reference": "v3.7.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/common/zipball/v3.6.0", - "reference": "v3.6.0", + "url": "https://api.github.com/repos/guzzle/common/zipball/v3.7.1", + "reference": "v3.7.1", "shasum": "" }, "require": { @@ -67,7 +67,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.6-dev" + "dev-master": "3.7-dev" } }, "autoload": { @@ -87,21 +87,21 @@ "event", "exception" ], - "time": "2013-05-30 07:01:25" + "time": "2013-07-05 20:17:54" }, { "name": "guzzle/http", - "version": "v3.6.0", + "version": "v3.7.1", "target-dir": "Guzzle/Http", "source": { "type": "git", "url": "https://github.com/guzzle/http.git", - "reference": "v3.6.0" + "reference": "v3.7.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/http/zipball/v3.6.0", - "reference": "v3.6.0", + "url": "https://api.github.com/repos/guzzle/http/zipball/v3.7.1", + "reference": "v3.7.1", "shasum": "" }, "require": { @@ -116,7 +116,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.6-dev" + "dev-master": "3.7-dev" } }, "autoload": { @@ -144,21 +144,21 @@ "http", "http client" ], - "time": "2013-05-30 07:01:25" + "time": "2013-07-02 19:53:26" }, { "name": "guzzle/parser", - "version": "v3.6.0", + "version": "v3.7.1", "target-dir": "Guzzle/Parser", "source": { "type": "git", "url": "https://github.com/guzzle/parser.git", - "reference": "v3.6.0" + "reference": "v3.7.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.6.0", - "reference": "v3.6.0", + "url": "https://api.github.com/repos/guzzle/parser/zipball/v3.7.1", + "reference": "v3.7.1", "shasum": "" }, "require": { @@ -167,7 +167,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.6-dev" + "dev-master": "3.7-dev" } }, "autoload": { @@ -188,21 +188,21 @@ "message", "url" ], - "time": "2013-05-30 07:01:25" + "time": "2013-06-11 00:24:07" }, { "name": "guzzle/stream", - "version": "v3.6.0", + "version": "v3.7.1", "target-dir": "Guzzle/Stream", "source": { "type": "git", "url": "https://github.com/guzzle/stream.git", - "reference": "v3.6.0" + "reference": "v3.7.1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.6.0", - "reference": "v3.6.0", + "url": "https://api.github.com/repos/guzzle/stream/zipball/v3.7.1", + "reference": "v3.7.1", "shasum": "" }, "require": { @@ -215,7 +215,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.6-dev" + "dev-master": "3.7-dev" } }, "autoload": { @@ -241,21 +241,21 @@ "component", "stream" ], - "time": "2013-05-30 07:01:25" + "time": "2013-06-27 00:50:43" }, { "name": "react/event-loop", - "version": "v0.3.2", + "version": "v0.3.3", "target-dir": "React/EventLoop", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "v0.3.2" + "reference": "v0.3.3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/v0.3.2", - "reference": "v0.3.2", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/v0.3.3", + "reference": "v0.3.3", "shasum": "" }, "require": { @@ -284,7 +284,7 @@ "keywords": [ "event-loop" ], - "time": "2013-01-14 23:11:47" + "time": "2013-07-08 22:38:22" }, { "name": "react/socket", @@ -330,17 +330,17 @@ }, { "name": "react/stream", - "version": "v0.3.2", + "version": "v0.3.3", "target-dir": "React/Stream", "source": { "type": "git", "url": "https://github.com/reactphp/stream.git", - "reference": "v0.3.2" + "reference": "v0.3.3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/v0.3.2", - "reference": "v0.3.2", + "url": "https://api.github.com/repos/reactphp/stream/zipball/v0.3.3", + "reference": "v0.3.3", "shasum": "" }, "require": { @@ -371,28 +371,28 @@ "pipe", "stream" ], - "time": "2013-05-10 15:12:22" + "time": "2013-07-09 00:44:12" }, { "name": "symfony/event-dispatcher", - "version": "v2.3.0", + "version": "v2.3.2", "target-dir": "Symfony/Component/EventDispatcher", "source": { "type": "git", "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "v2.3.0-RC1" + "reference": "v2.3.2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.0-RC1", - "reference": "v2.3.0-RC1", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.2", + "reference": "v2.3.2", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "symfony/dependency-injection": ">=2.0,<3.0" + "symfony/dependency-injection": "~2.0" }, "suggest": { "symfony/dependency-injection": "", @@ -429,17 +429,17 @@ }, { "name": "symfony/http-foundation", - "version": "v2.3.1", + "version": "v2.3.2", "target-dir": "Symfony/Component/HttpFoundation", "source": { "type": "git", "url": "https://github.com/symfony/HttpFoundation.git", - "reference": "v2.3.1" + "reference": "v2.3.2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.3.1", - "reference": "v2.3.1", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/v2.3.2", + "reference": "v2.3.2", "shasum": "" }, "require": { @@ -475,31 +475,31 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "http://symfony.com", - "time": "2013-05-10 06:00:03" + "time": "2013-07-17 05:57:53" }, { "name": "symfony/routing", - "version": "v2.3.0", + "version": "v2.3.2", "target-dir": "Symfony/Component/Routing", "source": { "type": "git", "url": "https://github.com/symfony/Routing.git", - "reference": "v2.3.0" + "reference": "v2.3.2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.0", - "reference": "v2.3.0", + "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.2", + "reference": "v2.3.2", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "doctrine/common": ">=2.2,<3.0", - "psr/log": ">=1.0,<2.0", - "symfony/config": ">=2.2,<3.0", - "symfony/yaml": ">=2.0,<3.0" + "doctrine/common": "~2.2", + "psr/log": "~1.0", + "symfony/config": "~2.2", + "symfony/yaml": "~2.0" }, "suggest": { "doctrine/common": "", @@ -533,7 +533,7 @@ ], "description": "Symfony Routing Component", "homepage": "http://symfony.com", - "time": "2013-05-20 08:57:26" + "time": "2013-06-23 08:16:02" } ], "packages-dev": [ diff --git a/src/Ratchet/App.php b/src/Ratchet/App.php index 6728c30..a3db257 100644 --- a/src/Ratchet/App.php +++ b/src/Ratchet/App.php @@ -79,7 +79,7 @@ class App { /** * @param string $path * @param ComponentInterface $controller - * @return Symfony\Component\Routing\Route + * @return \Symfony\Component\Routing\Route */ public function route($path, ComponentInterface $controller) { if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) { From 0dad9f1c03413cf59b040b572a336dbcc3c8e4f1 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 11:14:20 -0400 Subject: [PATCH 34/48] Removed libuv test --- tests/autobahn/bin/fuzzingserver-libuv.php | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 tests/autobahn/bin/fuzzingserver-libuv.php diff --git a/tests/autobahn/bin/fuzzingserver-libuv.php b/tests/autobahn/bin/fuzzingserver-libuv.php deleted file mode 100644 index b3dec26..0000000 --- a/tests/autobahn/bin/fuzzingserver-libuv.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - - require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; - - $loop = new React\EventLoop\LibEvLoop; - $sock = new React\Socket\Server($loop); - $app = new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer)); - - $port = $argc > 1 ? $argv[1] : 8000; - $sock->listen($port, '0.0.0.0'); - - $server = new Ratchet\Server\IoServer($app, $sock, $loop); - $server->run(); From 9a508356376ebeeb8fc8353ea57e1a9f58e94e6f Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 12:31:44 -0400 Subject: [PATCH 35/48] [WAMP] Message verification, fixes #105 Validate all messages follow WAMP protocol --- src/Ratchet/Wamp/ServerProtocol.php | 6 +++++- src/Ratchet/Wamp/WampConnection.php | 6 +++--- src/Ratchet/Wamp/WampServer.php | 8 +++++++- tests/unit/Wamp/ServerProtocolTest.php | 19 +++++++++++++++++++ tests/unit/Wamp/WampServerTest.php | 14 ++++++++++---- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/Ratchet/Wamp/ServerProtocol.php b/src/Ratchet/Wamp/ServerProtocol.php index 7900659..7842c82 100644 --- a/src/Ratchet/Wamp/ServerProtocol.php +++ b/src/Ratchet/Wamp/ServerProtocol.php @@ -6,7 +6,7 @@ use Ratchet\ConnectionInterface; /** * WebSocket Application Messaging Protocol - * + * * @link http://wamp.ws/spec * @link https://github.com/oberstet/AutobahnJS * @@ -89,6 +89,10 @@ class ServerProtocol implements MessageComponentInterface, WsServerInterface { throw new JsonException; } + if (!is_array($json) || $json !== array_values($json)) { + throw new \UnexpectedValueException("Invalid WAMP message format"); + } + switch ($json[0]) { case static::MSG_PREFIX: $from->WAMP->prefixes[$json[1]] = $json[2]; diff --git a/src/Ratchet/Wamp/WampConnection.php b/src/Ratchet/Wamp/WampConnection.php index b5eb04a..de13844 100644 --- a/src/Ratchet/Wamp/WampConnection.php +++ b/src/Ratchet/Wamp/WampConnection.php @@ -6,7 +6,7 @@ use Ratchet\Wamp\ServerProtocol as WAMP; /** * A ConnectionInterface object wrapper that is passed to your WAMP application - * representing a client. Methods on this Connection are therefore different. + * representing a client. Methods on this Connection are therefore different. * @property \stdClass $WAMP */ class WampConnection extends AbstractConnectionDecorator { @@ -96,7 +96,7 @@ class WampConnection extends AbstractConnectionDecorator { /** * {@inheritdoc} */ - public function close() { - $this->getConnection()->close(); + public function close($opt = null) { + $this->getConnection()->close($opt); } } \ No newline at end of file diff --git a/src/Ratchet/Wamp/WampServer.php b/src/Ratchet/Wamp/WampServer.php index d2bfdcf..9e7951f 100644 --- a/src/Ratchet/Wamp/WampServer.php +++ b/src/Ratchet/Wamp/WampServer.php @@ -37,7 +37,13 @@ class WampServer implements MessageComponentInterface, WsServerInterface { * {@inheritdoc} */ public function onMessage(ConnectionInterface $conn, $msg) { - $this->wampProtocol->onMessage($conn, $msg); + try { + $this->wampProtocol->onMessage($conn, $msg); + } catch (JsonException $je) { + $conn->close(1007); + } catch (\UnexpectedValueException $uve) { + $conn->close(1007); + } } /** diff --git a/tests/unit/Wamp/ServerProtocolTest.php b/tests/unit/Wamp/ServerProtocolTest.php index 9ecd89d..b4f57a4 100644 --- a/tests/unit/Wamp/ServerProtocolTest.php +++ b/tests/unit/Wamp/ServerProtocolTest.php @@ -247,4 +247,23 @@ class ServerProtocolTest extends \PHPUnit_Framework_TestCase { $this->assertContains('wamp', $wamp->getSubProtocols()); } + + public function badFormatProvider() { + return array( + array(json_encode(true)) + , array('{"valid":"json", "invalid": "message"}') + , array('{"0": "fail", "hello": "world"}') + ); + } + + /** + * @dataProvider badFormatProvider + */ + public function testValidJsonButInvalidProtocol($message) { + $this->setExpectedException('\UnexpectedValueException'); + + $conn = $this->newConn(); + $this->_comp->onOpen($conn); + $this->_comp->onMessage($conn, $message); + } } \ No newline at end of file diff --git a/tests/unit/Wamp/WampServerTest.php b/tests/unit/Wamp/WampServerTest.php index b1ee6f1..e76d693 100644 --- a/tests/unit/Wamp/WampServerTest.php +++ b/tests/unit/Wamp/WampServerTest.php @@ -7,10 +7,6 @@ use Ratchet\AbstractMessageComponentTestCase; * @covers Ratchet\Wamp\WampServer */ class WampServerTest extends AbstractMessageComponentTestCase { - private $serv; - private $mock; - private $conn; - public function getConnectionClassString() { return '\Ratchet\Wamp\WampConnection'; } @@ -41,4 +37,14 @@ class WampServerTest extends AbstractMessageComponentTestCase { // todo: could expand on this $this->assertInternalType('array', $this->_serv->getSubProtocols()); } + + public function testConnectionClosesOnInvalidJson() { + $this->_conn->expects($this->once())->method('close'); + $this->_serv->onMessage($this->_conn, 'invalid json'); + } + + public function testConnectionClosesOnProtocolError() { + $this->_conn->expects($this->once())->method('close'); + $this->_serv->onMessage($this->_conn, json_encode(array('valid' => 'json', 'invalid' => 'protocol'))); + } } \ No newline at end of file From 5676161277bae08e11103a188c43395e8ba8f758 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 12:38:23 -0400 Subject: [PATCH 36/48] [WebSocket] Only select one sub-protocol, fixes #103 --- src/Ratchet/WebSocket/WsServer.php | 16 ++++++---------- tests/unit/WebSocket/WsServerTest.php | 3 ++- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 460e328..9337934 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -199,19 +199,15 @@ class WsServer implements HttpServerInterface { * @return string */ protected function getSubProtocolString(\Traversable $requested = null) { - if (null === $requested) { - return ''; - } - - $result = array(); - - foreach ($requested as $sub) { - if ($this->isSubProtocolSupported($sub)) { - $result[] = $sub; + if (null !== $requested) { + foreach ($requested as $sub) { + if ($this->isSubProtocolSupported($sub)) { + return $sub; + } } } - return implode(',', $result); + return ''; } /** diff --git a/tests/unit/WebSocket/WsServerTest.php b/tests/unit/WebSocket/WsServerTest.php index 1a3626c..c923e16 100644 --- a/tests/unit/WebSocket/WsServerTest.php +++ b/tests/unit/WebSocket/WsServerTest.php @@ -27,10 +27,11 @@ class WsServerTest extends \PHPUnit_Framework_TestCase { public function protocolProvider() { return array( - array('hello,world', array('hello', 'world'), array('hello', 'world')) + array('hello', array('hello', 'world'), array('hello', 'world')) , array('', array('hello', 'world'), array('wamp')) , array('', array(), null) , array('wamp', array('hello', 'wamp', 'world'), array('herp', 'derp', 'wamp')) + , array('wamp', array('wamp'), array('wamp')) ); } From 0cf4b614a11fe7b584d7c34008937d405dd82bd6 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 14:52:18 -0400 Subject: [PATCH 37/48] 5.5 tests --- .travis.yml | 1 + README.md | 2 +- src/Ratchet/Http/HttpServer.php | 3 --- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5625a26..7604e9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 5.3 - 5.4 + - 5.5 before_script: - curl -s http://getcomposer.org/installer | php diff --git a/README.md b/README.md index 8de8cc1..a9cefad 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Build up your application through simple interfaces and re-use your application ##WebSocket Compliance * Supports the RFC6455, HyBi-10+, and Hixie76 protocol versions (at the same time) -* Tested on Chrome 13 - 27, Firefox 6 - 21, Safari 5.0.1 - 6, iOS 4.2 - 6 +* Tested on Chrome 13 - 28, Firefox 6 - 22, Safari 5.0.1 - 6, iOS 4.2 - 7 * Ratchet [passes](http://socketo.me/reports/ab/) the [Autobahn Testsuite](http://autobahn.ws/testsuite) (non-binary messages) ##Requirements diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index 5788807..f1b8f69 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -4,9 +4,6 @@ use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Guzzle\Http\Message\Response; -/** - * @todo Detect HTTP proxy header IP, re-map remoteAddress - */ class HttpServer implements MessageComponentInterface { /** * Buffers incoming HTTP requests returning a Guzzle Request when coalesced From f50af83fa7bbeb260ad9ec3288e1d2b53fbc4ea5 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 18:41:08 -0400 Subject: [PATCH 38/48] [Http][Security] OriginCheck + same host/origin policy Added the Http\OriginCheck component that will only allow connections coming from desired Origins Same host+origin policy by default: App.php, by default, will only allow connections from the given hostname Fixes #102 --- CHANGELOG.md | 5 +- src/Ratchet/App.php | 21 ++++-- src/Ratchet/Http/OriginCheck.php | 69 +++++++++++++++++++ .../AbstractMessageComponentTestCase.php | 13 +++- tests/unit/Http/OriginCheckTest.php | 46 +++++++++++++ 5 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 src/Ratchet/Http/OriginCheck.php create mode 100644 tests/unit/Http/OriginCheckTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 6066d00..b42cc47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,13 @@ CHANGELOG * 0.3.0 (2013-xx-xx) - * Sugar and spice and everything nice: Added the Ratchet\App class for ease of use + * Added the `App` class to help making Ratchet so easy to use it's silly + * BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks * Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router * BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer * Updated dependency to React/0.3 + * BF: Single sub-protocol selection to conform with RFC6455 + * BF: Sanity checks on WAMP protocol to prevent errors * 0.2.7 (2013-06-09) diff --git a/src/Ratchet/App.php b/src/Ratchet/App.php index a3db257..5a00fdd 100644 --- a/src/Ratchet/App.php +++ b/src/Ratchet/App.php @@ -4,6 +4,7 @@ use React\EventLoop\LoopInterface; use React\EventLoop\Factory as LoopFactory; use React\Socket\Server as Reactor; use Ratchet\Http\HttpServerInterface; +use Ratchet\Http\OriginCheck; use Ratchet\Wamp\WampServerInterface; use Ratchet\Server\IoServer; use Ratchet\Server\FlashPolicy; @@ -77,11 +78,13 @@ class App { } /** - * @param string $path + * @param $path * @param ComponentInterface $controller - * @return \Symfony\Component\Routing\Route + * @param array $allowedOrigins An array of hosts allowed to connect (same host by default), [*] for any + * @param string $httpHost Override the $httpHost variable provided in the __construct + * @return ComponentInterface|WsServer */ - public function route($path, ComponentInterface $controller) { + public function route($path, ComponentInterface $controller, array $allowedOrigins = array(), $httpHost = null) { if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) { $decorated = $controller; } elseif ($controller instanceof WampServerInterface) { @@ -92,7 +95,17 @@ class App { $decorated = $controller; } - $this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array(), array(), $this->httpHost)); + $httpHost = $httpHost ?: $this->httpHost; + + if (0 === count($allowedOrigins)) { + $allowedOrigins[] = $httpHost; + } + $allowedOrigins = array_values($allowedOrigins); + if ('*' !== $allowedOrigins[0]) { + $decorated = new OriginCheck($decorated, $allowedOrigins); + } + + $this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost)); return $decorated; } diff --git a/src/Ratchet/Http/OriginCheck.php b/src/Ratchet/Http/OriginCheck.php new file mode 100644 index 0000000..e043fd8 --- /dev/null +++ b/src/Ratchet/Http/OriginCheck.php @@ -0,0 +1,69 @@ +<?php +namespace Ratchet\Http; +use Guzzle\Http\Message\RequestInterface; +use Ratchet\ConnectionInterface; +use Ratchet\MessageComponentInterface; +use Guzzle\Http\Message\Response; + +class OriginCheck implements HttpServerInterface { + /** + * @var \Ratchet\MessageComponentInterface + */ + protected $_component; + + public $allowedOrigins = array(); + + public function __construct(MessageComponentInterface $component, array $allowed = array()) { + $this->_component = $component; + $this->allowedOrigins += $allowed; + } + + /** + * {@inheritdoc} + */ + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { + $origin = (string)$request->getHeader('Origin'); + + if (!in_array($origin, $this->allowedOrigins)) { + return $this->close($conn, 403); + } + + return $this->_component->onOpen($conn, $request); + } + + /** + * {@inheritdoc} + */ + function onMessage(ConnectionInterface $from, $msg) { + return $this->_component->onMessage($from, $msg); + } + + /** + * {@inheritdoc} + */ + function onClose(ConnectionInterface $conn) { + return $this->_component->onClose($conn); + } + + /** + * {@inheritdoc} + */ + function onError(ConnectionInterface $conn, \Exception $e) { + return $this->_component->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/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php b/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php index 990e98c..2ab458e 100644 --- a/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php +++ b/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php @@ -16,7 +16,11 @@ abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestC $this->_serv = new $decorator($this->_app); $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); - $this->_serv->onOpen($this->_conn); + $this->doOpen($this->_conn); + } + + protected function doOpen($conn) { + $this->_serv->onOpen($conn); } public function isExpectedConnection() { @@ -25,7 +29,7 @@ abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestC public function testOpen() { $this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection()); - $this->_serv->onOpen($this->getMock('\Ratchet\ConnectionInterface')); + $this->doOpen($this->getMock('\Ratchet\ConnectionInterface')); } public function testOnClose() { @@ -38,4 +42,9 @@ abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestC $this->_app->expects($this->once())->method('onError')->with($this->isExpectedConnection(), $e); $this->_serv->onError($this->_conn, $e); } + + public function passthroughMessageTest($value) { + $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $value); + $this->_serv->onMessage($this->_conn, $value); + } } \ No newline at end of file diff --git a/tests/unit/Http/OriginCheckTest.php b/tests/unit/Http/OriginCheckTest.php new file mode 100644 index 0000000..34db439 --- /dev/null +++ b/tests/unit/Http/OriginCheckTest.php @@ -0,0 +1,46 @@ +<?php +namespace Ratchet\Http; +use Ratchet\AbstractMessageComponentTestCase; + +/** + * @covers Ratchet\Http\OriginCheck + */ +class OriginCheckTest extends AbstractMessageComponentTestCase { + protected $_reqStub; + + public function setUp() { + $this->_reqStub = $this->getMock('Guzzle\Http\Message\RequestInterface'); + $this->_reqStub->expects($this->any())->method('getHeader')->will($this->returnValue('localhost')); + + parent::setUp(); + + $this->_serv->allowedOrigins[] = 'localhost'; + } + + protected function doOpen($conn) { + $this->_serv->onOpen($conn, $this->_reqStub); + } + + public function getConnectionClassString() { + return '\Ratchet\ConnectionInterface'; + } + + public function getDecoratorClassString() { + return '\Ratchet\Http\OriginCheck'; + } + + public function getComponentClassString() { + return '\Ratchet\Http\HttpServerInterface'; + } + + public function testCloseOnNonMatchingOrigin() { + $this->_serv->allowedOrigins = array('socketo.me'); + $this->_conn->expects($this->once())->method('close'); + + $this->_serv->onOpen($this->_conn, $this->_reqStub); + } + + public function testOnMessage() { + $this->passthroughMessageTest('Hello World!'); + } +} From eb77cf139ef54729b7e8dd97664fe48850bd9b20 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 22:03:46 -0400 Subject: [PATCH 39/48] Updated example, slight Origin order change in app --- README.md | 20 ++++++++++++++------ src/Ratchet/App.php | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a9cefad..1ea2fca 100644 --- a/README.md +++ b/README.md @@ -30,20 +30,21 @@ Need help? Have a question? Want to provide feedback? Write a message on the --- -###A quick server example +###A quick example ```php <?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; + // Make sure composer dependencies have been installed require __DIR__ . '/vendor/autoload.php'; /** * chat.php * Send any incoming messages to all connected clients (except sender) */ -class Chat implements MessageComponentInterface { +class MyChat implements MessageComponentInterface { protected $clients; public function __construct() { @@ -72,10 +73,17 @@ class Chat implements MessageComponentInterface { } // Run the server application through the WebSocket protocol on port 8080 - $app = new Ratchet\App('example.com', 8080); - $app->route('/chat', new Chat); - $app->route('/echo', new Ratchet\Server\EchoServer); + $app = new Ratchet\App('localhost', 8080); + $app->route('/chat', new MyChat); + $app->route('/echo', new Ratchet\Server\EchoServer, array(*)); $app->run(); ``` - $ php chat.php \ No newline at end of file + $ php chat.php + +```javascript + // Then some JavaScript in the browser: + var conn = new WebSocket('ws://localhost/echo'); + conn.onmessage = function(e) { console.log(e.data); }; + conn.send('Hello Me!'); +``` \ No newline at end of file diff --git a/src/Ratchet/App.php b/src/Ratchet/App.php index 5a00fdd..3d8dec0 100644 --- a/src/Ratchet/App.php +++ b/src/Ratchet/App.php @@ -97,10 +97,10 @@ class App { $httpHost = $httpHost ?: $this->httpHost; + $allowedOrigins = array_values($allowedOrigins); if (0 === count($allowedOrigins)) { $allowedOrigins[] = $httpHost; } - $allowedOrigins = array_values($allowedOrigins); if ('*' !== $allowedOrigins[0]) { $decorated = new OriginCheck($decorated, $allowedOrigins); } From 55c880a1002fc67a64ce7c32f6ba79168e5afee6 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Sat, 20 Jul 2013 22:04:23 -0400 Subject: [PATCH 40/48] ReadMe port fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ea2fca..5ac53b6 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ class MyChat implements MessageComponentInterface { ```javascript // Then some JavaScript in the browser: - var conn = new WebSocket('ws://localhost/echo'); + var conn = new WebSocket('ws://localhost:8080/echo'); conn.onmessage = function(e) { console.log(e.data); }; conn.send('Hello Me!'); ``` \ No newline at end of file From 4039a643eae9f1386bb4e2c78cb834cd798b3249 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Mon, 22 Jul 2013 21:03:19 -0400 Subject: [PATCH 41/48] API docs on new features --- src/Ratchet/App.php | 23 +++++++++++++++++------ src/Ratchet/Http/OriginCheck.php | 9 +++++++++ src/Ratchet/Server/EchoServer.php | 5 +++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Ratchet/App.php b/src/Ratchet/App.php index 3d8dec0..cc44694 100644 --- a/src/Ratchet/App.php +++ b/src/Ratchet/App.php @@ -17,6 +17,10 @@ use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Matcher\UrlMatcher; +/** + * An opinionated facade class to quickly and easily create a WebSocket server. + * A few configuration assumptions are made and some best-practice security conventions are applied by default. + */ class App { /** * @var \Symfony\Component\Routing\RouteCollection @@ -39,13 +43,16 @@ class App { */ protected $httpHost; + /** + * @var int + */ protected $_routeCounter = 0; /** - * @param string $httpHost - * @param int $port - * @param string $address - * @param LoopInterface $loop + * @param string $httpHost HTTP hostname clients intend to connect to. MUST match JS `new WebSocket('ws://$httpHost');` + * @param int $port Port to listen on. If 80, assuming production, Flash on 843 otherwise expecting Flash to be proxied through 8843 + * @param string $address IP address to bind to. Default is localhost/proxy only. '0.0.0.0' for any machine. + * @param LoopInterface $loop Specific React\EventLoop to bind the application to. null will create one for you. */ public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) { if (extension_loaded('xdebug')) { @@ -78,8 +85,9 @@ class App { } /** - * @param $path - * @param ComponentInterface $controller + * Add an endpiont/application to the server + * @param string $path The URI the client will connect to + * @param ComponentInterface $controller Your application to server for the route. If not specified, assumed to be for a WebSocket * @param array $allowedOrigins An array of hosts allowed to connect (same host by default), [*] for any * @param string $httpHost Override the $httpHost variable provided in the __construct * @return ComponentInterface|WsServer @@ -110,6 +118,9 @@ class App { return $decorated; } + /** + * Run the server by entering the event loop + */ public function run() { $this->_server->run(); } diff --git a/src/Ratchet/Http/OriginCheck.php b/src/Ratchet/Http/OriginCheck.php index e043fd8..580935a 100644 --- a/src/Ratchet/Http/OriginCheck.php +++ b/src/Ratchet/Http/OriginCheck.php @@ -5,6 +5,11 @@ use Ratchet\ConnectionInterface; use Ratchet\MessageComponentInterface; use Guzzle\Http\Message\Response; +/** + * A middleware to ensure JavaScript clients connecting are from the expected domain. + * This protects other websites from open WebSocket connections to your application. + * Note: This can be spoofed from non-web browser clients + */ class OriginCheck implements HttpServerInterface { /** * @var \Ratchet\MessageComponentInterface @@ -13,6 +18,10 @@ class OriginCheck implements HttpServerInterface { public $allowedOrigins = array(); + /** + * @param MessageComponentInterface $component Component/Application to decorate + * @param array $allowed An array of allowed domains that are allowed to connect from + */ public function __construct(MessageComponentInterface $component, array $allowed = array()) { $this->_component = $component; $this->allowedOrigins += $allowed; diff --git a/src/Ratchet/Server/EchoServer.php b/src/Ratchet/Server/EchoServer.php index 3f5069b..4011d66 100644 --- a/src/Ratchet/Server/EchoServer.php +++ b/src/Ratchet/Server/EchoServer.php @@ -3,6 +3,9 @@ namespace Ratchet\Server; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; +/** + * A simple Ratchet application that will reply to all messages with the message it received + */ class EchoServer implements MessageComponentInterface { public function onOpen(ConnectionInterface $conn) { } @@ -15,8 +18,6 @@ class EchoServer implements MessageComponentInterface { } public function onError(ConnectionInterface $conn, \Exception $e) { - echo $e->getMessage() . "\n"; - $conn->close(); } } \ No newline at end of file From 6559aaf1d45b4bcdde7fdf69e967efd8ea521357 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Mon, 5 Aug 2013 16:23:38 -0400 Subject: [PATCH 42/48] [Http] Fixed bug in Origin check - browser may pass http:// --- src/Ratchet/Http/OriginCheck.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ratchet/Http/OriginCheck.php b/src/Ratchet/Http/OriginCheck.php index 580935a..8d5f7a1 100644 --- a/src/Ratchet/Http/OriginCheck.php +++ b/src/Ratchet/Http/OriginCheck.php @@ -31,7 +31,8 @@ class OriginCheck implements HttpServerInterface { * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { - $origin = (string)$request->getHeader('Origin'); + $header = (string)$request->getHeader('Origin'); + $origin = parse_url($header, PHP_URL_HOST) ?: $header; if (!in_array($origin, $this->allowedOrigins)) { return $this->close($conn, 403); From 25ff6deb77c8877136d0305fc0e39a094ce10ca9 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Mon, 5 Aug 2013 17:20:05 -0400 Subject: [PATCH 43/48] [Flash] Close connection after delivering policy --- src/Ratchet/Server/FlashPolicy.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ratchet/Server/FlashPolicy.php b/src/Ratchet/Server/FlashPolicy.php index ec64c78..f0ee26f 100644 --- a/src/Ratchet/Server/FlashPolicy.php +++ b/src/Ratchet/Server/FlashPolicy.php @@ -108,6 +108,7 @@ class FlashPolicy implements MessageComponentInterface { } $from->send($this->_cache . "\0"); + $from->close(); } /** From 1370cb6109e8e061dcda43bd38cd424b37ac727b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Doigni=C3=A9?= <loic.doignie.ext@nordnet.fr> Date: Tue, 8 Oct 2013 08:51:25 +0200 Subject: [PATCH 44/48] This modification gives Ratchet its own RequestFactory instance. This way you can use guzzle as REST Client. --- .../Http/Guzzle/Http/Message/RequestFactory.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Ratchet/Http/Guzzle/Http/Message/RequestFactory.php b/src/Ratchet/Http/Guzzle/Http/Message/RequestFactory.php index abf8aac..7457871 100644 --- a/src/Ratchet/Http/Guzzle/Http/Message/RequestFactory.php +++ b/src/Ratchet/Http/Guzzle/Http/Message/RequestFactory.php @@ -4,6 +4,23 @@ use Guzzle\Http\Message\RequestFactory as GuzzleRequestFactory; use Guzzle\Http\EntityBody; class RequestFactory extends GuzzleRequestFactory { + + protected static $ratchetInstance; + + /** + * {@inheritdoc} + */ + public static function getInstance() + { + // @codeCoverageIgnoreStart + if (!static::$ratchetInstance) { + static::$ratchetInstance = new static(); + } + // @codeCoverageIgnoreEnd + + return static::$ratchetInstance; + } + /** * {@inheritdoc} */ From f40bb94a922f81197964e409f5cd2321ddc82863 Mon Sep 17 00:00:00 2001 From: Florian Rey <nervo@nervo.net> Date: Thu, 10 Oct 2013 13:40:26 +0200 Subject: [PATCH 45/48] Test for connection attachement before cascading error --- src/Ratchet/WebSocket/WsServer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 9337934..1b7c199 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -150,7 +150,9 @@ class WsServer implements HttpServerInterface { */ public function onError(ConnectionInterface $conn, \Exception $e) { if ($conn->WebSocket->established) { - $this->component->onError($this->connections[$conn], $e); + if ($this->connections->contains($conn)) { + $this->component->onError($this->connections[$conn], $e); + } } else { $conn->close(); } From 069133ca352525a90840d71918b0067f6a73eb25 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Mon, 14 Oct 2013 10:20:32 -0400 Subject: [PATCH 46/48] Updated ops files for 0.3 release --- CHANGELOG.md | 8 +---- Makefile | 2 +- README.md | 2 +- composer.lock | 46 ++++++++++++++--------------- src/Ratchet/ConnectionInterface.php | 2 +- 5 files changed, 27 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b55312..2c16bde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,6 @@ CHANGELOG * BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks * Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router * BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer - * Updated dependency to React/0.3 * BF: Single sub-protocol selection to conform with RFC6455 * BF: Sanity checks on WAMP protocol to prevent errors @@ -95,9 +94,4 @@ CHANGELOG * 0.1 (2012-05-11) * First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList -<<<<<<< HEAD - * I/O now handled by React, making Ratchet fully asynchronous - -======= - * I/O now handled by React, making Ratchet fully asynchronous ->>>>>>> refs/heads/master + * I/O now handled by React, making Ratchet fully asynchronous \ No newline at end of file diff --git a/Makefile b/Makefile index aa143cc..0595b04 100644 --- a/Makefile +++ b/Makefile @@ -30,5 +30,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/symfony/routing/Symfony/Component/Routing \ -s vendor/evenement/evenement/src/Evenement diff --git a/README.md b/README.md index 5ac53b6..fa127b6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Build up your application through simple interfaces and re-use your application ##WebSocket Compliance * Supports the RFC6455, HyBi-10+, and Hixie76 protocol versions (at the same time) -* Tested on Chrome 13 - 28, Firefox 6 - 22, Safari 5.0.1 - 6, iOS 4.2 - 7 +* Tested on Chrome 13 - 30, Firefox 6 - 24, Safari 5.0.1 - 6, iOS 4.2 - 7 * Ratchet [passes](http://socketo.me/reports/ab/) the [Autobahn Testsuite](http://autobahn.ws/testsuite) (non-binary messages) ##Requirements diff --git a/composer.lock b/composer.lock index fba7c14..ec801f4 100644 --- a/composer.lock +++ b/composer.lock @@ -47,17 +47,17 @@ }, { "name": "guzzle/common", - "version": "v3.7.3", + "version": "v3.7.4", "target-dir": "Guzzle/Common", "source": { "type": "git", "url": "https://github.com/guzzle/common.git", - "reference": "bf73c87375f60861f8c7ccc7b95878023ade5306" + "reference": "5126e268446c7e7df961b89128d71878e0652432" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/common/zipball/bf73c87375f60861f8c7ccc7b95878023ade5306", - "reference": "bf73c87375f60861f8c7ccc7b95878023ade5306", + "url": "https://api.github.com/repos/guzzle/common/zipball/5126e268446c7e7df961b89128d71878e0652432", + "reference": "5126e268446c7e7df961b89128d71878e0652432", "shasum": "" }, "require": { @@ -87,21 +87,21 @@ "event", "exception" ], - "time": "2013-09-08 21:09:18" + "time": "2013-10-02 20:47:00" }, { "name": "guzzle/http", - "version": "v3.7.3", + "version": "v3.7.4", "target-dir": "Guzzle/Http", "source": { "type": "git", "url": "https://github.com/guzzle/http.git", - "reference": "1034125dfd906b73119e535f03153a62fccb1989" + "reference": "3420035adcf312d62a2e64f3e6b3e3e590121786" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/http/zipball/1034125dfd906b73119e535f03153a62fccb1989", - "reference": "1034125dfd906b73119e535f03153a62fccb1989", + "url": "https://api.github.com/repos/guzzle/http/zipball/3420035adcf312d62a2e64f3e6b3e3e590121786", + "reference": "3420035adcf312d62a2e64f3e6b3e3e590121786", "shasum": "" }, "require": { @@ -144,11 +144,11 @@ "http", "http client" ], - "time": "2013-09-06 11:34:26" + "time": "2013-09-20 22:05:53" }, { "name": "guzzle/parser", - "version": "v3.7.3", + "version": "v3.7.4", "target-dir": "Guzzle/Parser", "source": { "type": "git", @@ -192,7 +192,7 @@ }, { "name": "guzzle/stream", - "version": "v3.7.3", + "version": "v3.7.4", "target-dir": "Guzzle/Stream", "source": { "type": "git", @@ -375,7 +375,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.3.5", + "version": "v2.3.6", "target-dir": "Symfony/Component/EventDispatcher", "source": { "type": "git", @@ -429,17 +429,17 @@ }, { "name": "symfony/http-foundation", - "version": "v2.3.4", + "version": "v2.3.6", "target-dir": "Symfony/Component/HttpFoundation", "source": { "type": "git", "url": "https://github.com/symfony/HttpFoundation.git", - "reference": "fdf130fe65457aedbc4639a22f4ef9d3be5c002c" + "reference": "59e712338cd05463ebcb8da6422a01b1291871e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/fdf130fe65457aedbc4639a22f4ef9d3be5c002c", - "reference": "fdf130fe65457aedbc4639a22f4ef9d3be5c002c", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/59e712338cd05463ebcb8da6422a01b1291871e3", + "reference": "59e712338cd05463ebcb8da6422a01b1291871e3", "shasum": "" }, "require": { @@ -475,21 +475,21 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "http://symfony.com", - "time": "2013-08-26 05:49:51" + "time": "2013-09-29 19:41:41" }, { "name": "symfony/routing", - "version": "v2.3.5", + "version": "v2.3.6", "target-dir": "Symfony/Component/Routing", "source": { "type": "git", "url": "https://github.com/symfony/Routing.git", - "reference": "6d1f7b101337594fe790c47166068583b60b6460" + "reference": "7d41463094752e87a0fae60316d236abecb8a034" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Routing/zipball/6d1f7b101337594fe790c47166068583b60b6460", - "reference": "6d1f7b101337594fe790c47166068583b60b6460", + "url": "https://api.github.com/repos/symfony/Routing/zipball/7d41463094752e87a0fae60316d236abecb8a034", + "reference": "7d41463094752e87a0fae60316d236abecb8a034", "shasum": "" }, "require": { @@ -533,7 +533,7 @@ ], "description": "Symfony Routing Component", "homepage": "http://symfony.com", - "time": "2013-09-19 09:45:20" + "time": "2013-09-29 19:41:41" } ], "packages-dev": [ diff --git a/src/Ratchet/ConnectionInterface.php b/src/Ratchet/ConnectionInterface.php index 088823d..5cf8e71 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.3-beta'; +const VERSION = 'Ratchet/0.3'; /** * A proxy object representing a connection to the application From ef5ece0739ea62cedbd9f865e122b5f41975fcfe Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Mon, 14 Oct 2013 10:25:51 -0400 Subject: [PATCH 47/48] [ws] Updated onError handle to make sure to close if conditions aren't met --- src/Ratchet/WebSocket/WsServer.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 1b7c199..c76fd3f 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -149,10 +149,8 @@ class WsServer implements HttpServerInterface { * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { - if ($conn->WebSocket->established) { - if ($this->connections->contains($conn)) { - $this->component->onError($this->connections[$conn], $e); - } + if ($conn->WebSocket->established && $this->connections->contains($conn)) { + $this->component->onError($this->connections[$conn], $e); } else { $conn->close(); } From 7336bc71ac2654dd3251e31f0b5ec147cc9c8490 Mon Sep 17 00:00:00 2001 From: Chris Boden <cboden@gmail.com> Date: Mon, 14 Oct 2013 10:38:06 -0400 Subject: [PATCH 48/48] Added release date on changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c16bde..7aae881 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ CHANGELOG --- -* 0.3.0 (2013-xx-xx) +* 0.3.0 (2013-10-14) * Added the `App` class to help making Ratchet so easy to use it's silly * BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks