diff --git a/CHANGELOG.md b/CHANGELOG.md index a59cc7c..e0771e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,8 @@ CHANGELOG * 0.3.0 (2013-xx-xx) - * BC: Requre hostname and do Origin HTTP header check against it, disabling CORS by default for security reasons - * BC: Added Routing to HTTP allowing for a single Ratchet server to handle multiple apps + * BC: Require hostname and do Origin HTTP header check against it by default, helping prevent CSRF attacks + * Added HTTP Router component to allowing for a single Ratchet server to handle multiple apps * BC: Decoupled HTTP from WebSocket component * 0.2.5 (2013-04-01) diff --git a/README.md b/README.md index 32c96fd..e089404 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,15 @@ Need help? Have a question? Want to provide feedback? Write a message on the add('chatRoute', new Route('/chat', array( + '_controller' => new WsServer(new Chat) + ))); + + $server = IoServer::factory(new HttpServer(new Router(new UrlMatcher($routes, new RequestContext))), 8080); $server->run(); ``` diff --git a/src/Ratchet/Http/HttpServer.php b/src/Ratchet/Http/HttpServer.php index a6d2d5e..867d008 100644 --- a/src/Ratchet/Http/HttpServer.php +++ b/src/Ratchet/Http/HttpServer.php @@ -19,61 +19,30 @@ class HttpServer implements MessageComponentInterface { protected $_reqParser; /** - * @var \Symfony\Component\Routing\RouteCollection A collection with \Ratchet\MessageComponentInterface controllers + * @var \Ratchet\Http\HttpServerInterface */ - protected $_routes; + protected $_httpServer; /** - * @param string $host - * @param RouteCollection $collection - * @throws \UnexpectedValueException If a Route Controller does not map to a \Ratchet\MessageComponentInterface + * @param HttpServerInterface */ - public function __construct($host, RouteCollection $collection = null) { - if (null === $collection) { - $collection = new RouteCollection; - } else { - foreach ($collection as $routeName => $route) { - if (is_string($route['_controller']) && class_exists($route['_controller'])) { - $route['_controller'] = new $route['_controller']; - } - - if (!($route['_controller'] instanceof HttpServerInterface)) { - throw new \UnexpectedValueException('All routes must implement Ratchet\MessageComponentInterface'); - } - } - } - - $collection->setHost($host); - - $this->_routes = $collection; - $this->_reqParser = new HttpRequestParser; - } - - /** - * @param string - * @param string - * @param Ratchet\Http\HttpServerInterface - * @param array - */ - public function addRoute($name, $path, HttpServerInterface $controller) { - $this->_routes->add($name, new Route($path, array( - '_controller' => $controller - ))); + public function __construct(HttpServerInterface $server) { + $this->_httpServer = $server; + $this->_reqParser = new HttpRequestParser; } /** * @{inheritdoc} */ public function onOpen(ConnectionInterface $conn) { - $conn->Http = new \StdClass; - $conn->Http->headers = false; + $conn->httpHeadersReceived = false; } /** * @{inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { - if (true !== $from->Http->headers) { + if (true !== $from->httpHeadersReceived) { try { if (null === ($request = $this->_reqParser->onMessage($from, $msg))) { return; @@ -82,32 +51,20 @@ class HttpServer implements MessageComponentInterface { return $this->close($from, 413); } - $context = new RequestContext($request->getUrl(), $request->getMethod(), $request->getHost(), $request->getScheme(), $request->getPort()); - $matcher = new UrlMatcher($this->_routes, $context); + $from->httpHeadersReceived = true; - try { - $route = $matcher->match($request->getPath()); - } catch (MethodNotAllowedException $nae) { - return $this->close($from, 403); - } catch (ResourceNotFoundException $nfe) { - return $this->close($from, 404); - } - - $from->Http->headers = true; - $from->Http->controller = $route['_controller']; - - return $from->Http->controller->onOpen($from, $request); + return $this->_httpServer->onOpen($from, $request); } - $from->Http->controller->onMessage($from, $msg); + $this->_httpServer->onMessage($from, $msg); } /** * @{inheritdoc} */ public function onClose(ConnectionInterface $conn) { - if ($conn->Http->headers) { - $conn->Http->controller->onClose($conn); + if ($conn->httpHeadersReceived) { + $this->_httpServer->onClose($conn); } } @@ -115,10 +72,10 @@ class HttpServer implements MessageComponentInterface { * @{inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { - if ($conn->Http->headers) { - $conn->Http->controller->onError($conn, $e); + if ($conn->httpHeadersReceived) { + $this->_httpServer->onError($conn, $e); } else { - $conn->close(); + $this->close($conn, 500); } } diff --git a/src/Ratchet/Http/HttpServerInterface.php b/src/Ratchet/Http/HttpServerInterface.php index 2f62238..0730680 100644 --- a/src/Ratchet/Http/HttpServerInterface.php +++ b/src/Ratchet/Http/HttpServerInterface.php @@ -7,8 +7,7 @@ use Guzzle\Http\Message\RequestInterface; interface HttpServerInterface extends MessageComponentInterface { /** * @param \Ratchet\ConnectionInterface $conn - * @param \Guzzle\Http\Message\RequestInterface $headers null is default because PHP won't let me overload; don't pass null!!! - * @return mixed + * @param \Guzzle\Http\Message\RequestInterface $request null is default because PHP won't let me overload; don't pass null!!! */ - public function onOpen(ConnectionInterface $conn, RequestInterface $headers = null); + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null); } diff --git a/src/Ratchet/Http/Router.php b/src/Ratchet/Http/Router.php new file mode 100644 index 0000000..9946d7e --- /dev/null +++ b/src/Ratchet/Http/Router.php @@ -0,0 +1,61 @@ +_matcher = $matcher; + } + + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { + try { + $route = $this->_matcher->match($request->getPath()); + } catch (MethodNotAllowedException $nae) { + return $this->close($from, 403); + } catch (ResourceNotFoundException $nfe) { + return $this->close($from, 404); + } + + if (is_string($route['_controller']) && class_exists($route['_controller'])) { + $route['_controller'] = new $route['_controller']; + } + + if (!($route['_controller'] instanceof HttpServerInterface)) { + throw new \UnexpectedValueException('All routes must implement Ratchet\HttpServerInterface'); + } + + $conn->controller = $route['_controller']; + + $conn->controller->onOpen($conn, $request); + } + + /** + * @{inheritdoc} + */ + function onMessage(ConnectionInterface $from, $msg) { + $from->controller->onMessage($from, $msg); + } + + /** + * @{inheritdoc} + */ + function onClose(ConnectionInterface $conn) { + $conn->controller->onClose($conn); + } + + /** + * @{inheritdoc} + */ + function onError(ConnectionInterface $conn, \Exception $e) { + $conn->controller->onError($conn, $e); + } +} \ No newline at end of file diff --git a/src/Ratchet/WebSocket/WsServer.php b/src/Ratchet/WebSocket/WsServer.php index 6329cf9..77e3fe1 100644 --- a/src/Ratchet/WebSocket/WsServer.php +++ b/src/Ratchet/WebSocket/WsServer.php @@ -73,9 +73,9 @@ class WsServer implements HttpServerInterface { /** * {@inheritdoc} */ - public function onOpen(ConnectionInterface $conn, RequestInterface $headers = null) { + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { $conn->WebSocket = new \StdClass; - $conn->WebSocket->request = $headers; + $conn->WebSocket->request = $request; $conn->WebSocket->established = false; $this->attemptUpgrade($conn);