pax_global_header00006660000000000000000000000064132136236330014514gustar00rootroot0000000000000052 comment=0d31f3a8ad4795fd48397712709e55cd07f51360 Ratchet-0.4.1/000077500000000000000000000000001321362363300131105ustar00rootroot00000000000000Ratchet-0.4.1/.gitignore000066400000000000000000000000641321362363300151000ustar00rootroot00000000000000phpunit.xml reports sandbox vendor composer.lockRatchet-0.4.1/.travis.yml000066400000000000000000000005421321362363300152220ustar00rootroot00000000000000language: php php: - 5.4 - 5.5 - 5.6 - 7.0 - 7.1 - hhvm dist: trusty matrix: allow_failures: - php: hhvm before_script: - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "session.serialize_handler = php" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;' - php -m - composer install --dev --prefer-source Ratchet-0.4.1/CHANGELOG.md000066400000000000000000000121351321362363300147230ustar00rootroot00000000000000CHANGELOG ========= ### Legend * "BC": Backwards compatibility break (from public component APIs) * "BF": Bug fix --- * 0.4.1 (2017-12-11) * Only enableKeepAlive in App if no WsServer passed allowing user to set their own timeout duration * Support Symfony 4 * BF: Plug NOOP controller in connection from router in case of misbehaving client * BF: Raise error from invalid WAMP payload * 0.4 (2017-09-14) * BC: $conn->WebSocket->request replaced with $conn->httpRequest which is a PSR-7 object * Binary messages now supported via Ratchet\WebSocket\MessageComponentInterface * Added heartbeat support via ping/pong in WsServer * BC: No longer support old (and insecure) Hixie76 and Hybi protocols * BC: No longer support disabling UTF-8 checks * BC: The Session component implements HttpServerInterface instead of WsServerInterface * BC: PHP 5.3 no longer supported * BC: Update to newer version of react/socket dependency * BC: WAMP topics reduced to 0 subscriptions are deleted, new subs to same name will result in new Topic instance * Significant performance enhancements * 0.3.6 (2017-01-06) * BF: Keep host and scheme in HTTP request object attatched to connection * BF: Return correct HTTP response (405) when non-GET request made * 0.3.5 (2016-05-25) * BF: Unmask responding close frame * Added write handler for PHP session serializer * 0.3.4 (2015-12-23) * BF: Edge case where version check wasn't run on message coalesce * BF: Session didn't start when using pdo_sqlite * BF: WAMP currie prefix check when using '#' * Compatibility with Symfony 3 * 0.3.3 (2015-05-26) * BF: Framing bug on large messages upon TCP fragmentation * BF: Symfony Router query parameter defaults applied to Request * BF: WAMP CURIE on all URIs * OriginCheck rules applied to FlashPolicy * Switched from PSR-0 to PSR-4 * 0.3.2 (2014-06-08) * BF: No messages after closing handshake (fixed rare race condition causing 100% CPU) * BF: Fixed accidental BC break from v0.3.1 * Added autoDelete parameter to Topic to destroy when empty of connections * Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App) * Normalized Exceptions in WAMP * 0.3.1 (2014-05-26) * Added query parameter support to Router, set in HTTP request (ws://server?hello=world) * HHVM compatibility * BF: React/0.4 support; CPU starvation bug fixes * BF: Allow App::route to ignore Host header * Added expected filters to WAMP Topic broadcast method * Resource cleanup in WAMP TopicManager * 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 * 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 * BF: Single sub-protocol selection to conform with RFC6455 * BF: Sanity checks on WAMP protocol to prevent errors * 0.2.8 (2013-09-19) * React 0.3 support * 0.2.7 (2013-06-09) * BF: Sub-protocol negotation with Guzzle 3.6 * 0.2.6 (2013-06-01) * Guzzle 3.6 support * 0.2.5 (2013-04-01) * Fixed Hixie-76 handshake bug * 0.2.4 (2013-03-09) * Support for Symfony 2.2 and Guzzle 2.3 * Minor bug fixes when handling errors * 0.2.3 (2012-11-21) * Bumped dep: Guzzle to v3, React to v0.2.4 * More tests * 0.2.2 (2012-10-20) * Bumped deps to use React v0.2 * 0.2.1 (2012-10-13) * BF: No more UTF-8 warnings in browsers (no longer sending empty sub-protocol string) * Documentation corrections * Using new composer structure * 0.2 (2012-09-07) * Ratchet passes every non-binary-frame test from the Autobahn Testsuite * Major performance improvements * BC: Renamed "WampServer" to "ServerProtocol" * BC: New "WampServer" component passes Topic container objects of subscribed Connections * Option to turn off UTF-8 checks in order to increase performance * Switched dependency guzzle/guzzle to guzzle/http (no API changes) * mbstring no longer required * 0.1.5 (2012-07-12) * BF: Error where service wouldn't run on PHP <= 5.3.8 * Dependency library updates * 0.1.4 (2012-06-17) * Fixed dozens of failing AB tests * BF: Proper socket buffer handling * 0.1.3 (2012-06-15) * Major refactor inside WebSocket protocol handling, more loosley coupled * BF: Proper error handling on failed WebSocket connections * BF: Handle TCP message concatenation * Inclusion of the AutobahnTestSuite checking WebSocket protocol compliance * mb_string now a requirement * 0.1.2 (2012-05-19) * BC/BF: Updated WAMP API to coincide with the official spec * Tweaks to improve running as a long lived process * 0.1.1 (2012-05-14) * Separated interfaces allowing WebSockets to support multiple sub protocols * BF: remoteAddress variable on connections returns proper value * 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 Ratchet-0.4.1/LICENSE000066400000000000000000000020441321362363300141150ustar00rootroot00000000000000Copyright (c) 2011-2017 Chris Boden Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Ratchet-0.4.1/Makefile000066400000000000000000000025431321362363300145540ustar00rootroot00000000000000# This file is intended to ease the author's development and testing process # Users do not need to use `make`; Ratchet does not need to be compiled test: phpunit cover: phpunit --coverage-text --coverage-html=reports/coverage abtests: ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8001 LibEvent & ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8002 StreamSelect & ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8004 LibEv & wstest -m testeeserver -w ws://localhost:8000 & sleep 1 wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json killall php wstest abtest: ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8000 StreamSelect & sleep 1 wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json killall php profile: php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver.php 8000 LibEvent & sleep 1 wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json killall php apidocs: apigen --title Ratchet -d reports/api \ -s src/ \ -s vendor/ratchet/rfc6455/src \ -s vendor/react/event-loop/src \ -s vendor/react/socket/src \ -s vendor/react/stream/src \ -s vendor/psr/http-message/src \ -s vendor/symfony/http-foundation/Session \ -s vendor/symfony/routing \ -s vendor/evenement/evenement/src/Evenement \ --exclude=vendor/symfony/routing/Tests \ Ratchet-0.4.1/README.md000066400000000000000000000055231321362363300143740ustar00rootroot00000000000000# Ratchet [![Build Status](https://secure.travis-ci.org/ratchetphp/Ratchet.png?branch=master)](http://travis-ci.org/ratchetphp/Ratchet) [![Autobahn Testsuite](https://img.shields.io/badge/Autobahn-passing-brightgreen.svg)](http://socketo.me/reports/ab/index.html) [![Latest Stable Version](https://poser.pugx.org/cboden/ratchet/v/stable.png)](https://packagist.org/packages/cboden/ratchet) A PHP library for asynchronously serving WebSockets. Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components. ## Requirements Shell access is required and root access is recommended. To avoid proxy/firewall blockage it's recommended WebSockets are requested on port 80 or 443 (SSL), which requires root access. In order to do this, along with your sync web stack, you can either use a reverse proxy or two separate machines. You can find more details in the [server conf docs](http://socketo.me/docs/deploy#serverconfiguration). ### Documentation User and API documentation is available on Ratchet's website: http://socketo.me See https://github.com/cboden/Ratchet-examples for some out-of-the-box working demos using Ratchet. Need help? Have a question? Want to provide feedback? Write a message on the [Google Groups Mailing List](https://groups.google.com/forum/#!forum/ratchet-php). --- ### A quick example ```php clients = new \SplObjectStorage; } public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); } public function onMessage(ConnectionInterface $from, $msg) { foreach ($this->clients as $client) { if ($from != $client) { $client->send($msg); } } } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); } public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } } // Run the server application through the WebSocket protocol on port 8080 $app = new Ratchet\App('localhost', 8080); $app->route('/chat', new MyChat); $app->route('/echo', new Ratchet\Server\EchoServer, array('*')); $app->run(); ``` $ php chat.php ```javascript // Then some JavaScript in the browser: var conn = new WebSocket('ws://localhost:8080/echo'); conn.onmessage = function(e) { console.log(e.data); }; conn.onopen = function(e) { conn.send('Hello Me!'); }; ``` Ratchet-0.4.1/composer.json000066400000000000000000000017741321362363300156430ustar00rootroot00000000000000{ "name": "cboden/ratchet" , "type": "library" , "description": "PHP WebSocket library" , "keywords": ["WebSockets", "Server", "Ratchet", "Sockets", "WebSocket"] , "homepage": "http://socketo.me" , "license": "MIT" , "authors": [ { "name": "Chris Boden" , "email": "cboden@gmail.com" , "role": "Developer" } ] , "support": { "forum": "https://groups.google.com/forum/#!forum/ratchet-php" , "issues": "https://github.com/ratchetphp/Ratchet/issues" , "irc": "irc://irc.freenode.org/reactphp" } , "autoload": { "psr-4": { "Ratchet\\": "src/Ratchet" } } , "require": { "php": ">=5.4.2" , "ratchet/rfc6455": "^0.2" , "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5" , "guzzlehttp/psr7": "^1.0" , "symfony/http-foundation": "^2.6|^3.0|^4.0" , "symfony/routing": "^2.6|^3.0|^4.0" } , "require-dev": { "phpunit/phpunit": "~4.8" } } Ratchet-0.4.1/phpunit.xml.dist000066400000000000000000000012771321362363300162720ustar00rootroot00000000000000 ./tests/unit/ ./tests/integration/ ./src/ Ratchet-0.4.1/src/000077500000000000000000000000001321362363300136775ustar00rootroot00000000000000Ratchet-0.4.1/src/Ratchet/000077500000000000000000000000001321362363300152715ustar00rootroot00000000000000Ratchet-0.4.1/src/Ratchet/AbstractConnectionDecorator.php000066400000000000000000000017231321362363300234330ustar00rootroot00000000000000wrappedConn = $conn; } /** * @return ConnectionInterface */ protected function getConnection() { return $this->wrappedConn; } public function __set($name, $value) { $this->wrappedConn->$name = $value; } public function __get($name) { return $this->wrappedConn->$name; } public function __isset($name) { return isset($this->wrappedConn->$name); } public function __unset($name) { unset($this->wrappedConn->$name); } } Ratchet-0.4.1/src/Ratchet/App.php000066400000000000000000000122251321362363300165240ustar00rootroot00000000000000httpHost = $httpHost; $this->port = $port; $socket = new Reactor($address . ':' . $port, $loop); $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); if (80 == $port) { $flashUri = '0.0.0.0:843'; } else { $flashUri = 8843; } $flashSock = new Reactor($flashUri, $loop); $this->flashServer = new IoServer($policy, $flashSock); } /** * Add an endpoint/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 */ public function route($path, ComponentInterface $controller, array $allowedOrigins = array(), $httpHost = null) { if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) { $decorated = $controller; } elseif ($controller instanceof WampServerInterface) { $decorated = new WsServer(new WampServer($controller)); $decorated->enableKeepAlive($this->_server->loop); } elseif ($controller instanceof MessageComponentInterface) { $decorated = new WsServer($controller); $decorated->enableKeepAlive($this->_server->loop); } else { $decorated = $controller; } if ($httpHost === null) { $httpHost = $this->httpHost; } $allowedOrigins = array_values($allowedOrigins); if (0 === count($allowedOrigins)) { $allowedOrigins[] = $httpHost; } if ('*' !== $allowedOrigins[0]) { $decorated = new OriginCheck($decorated, $allowedOrigins); } //allow origins in flash policy server if(empty($this->flashServer) === false) { foreach($allowedOrigins as $allowedOrgin) { $this->flashServer->app->addAllowedAccess($allowedOrgin, $this->port); } } $this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost, array(), array('GET'))); return $decorated; } /** * Run the server by entering the event loop */ public function run() { $this->_server->run(); } } Ratchet-0.4.1/src/Ratchet/ComponentInterface.php000066400000000000000000000023261321362363300215700ustar00rootroot00000000000000 \Ratchet\VERSION ], $additional_headers)); $conn->send(gPsr\str($response)); $conn->close(); } }Ratchet-0.4.1/src/Ratchet/Http/HttpRequestParser.php000066400000000000000000000034421321362363300223710ustar00rootroot00000000000000httpBuffer)) { $context->httpBuffer = ''; } $context->httpBuffer .= $data; if (strlen($context->httpBuffer) > (int)$this->maxSize) { throw new \OverflowException("Maximum buffer size of {$this->maxSize} exceeded parsing HTTP header"); } if ($this->isEom($context->httpBuffer)) { $request = $this->parse($context->httpBuffer); unset($context->httpBuffer); return $request; } } /** * Determine if the message has been buffered as per the HTTP specification * @param string $message * @return boolean */ public function isEom($message) { return (boolean)strpos($message, static::EOM); } /** * @param string $headers * @return \Psr\Http\Message\RequestInterface */ public function parse($headers) { return gPsr\parse_request($headers); } } Ratchet-0.4.1/src/Ratchet/Http/HttpServer.php000066400000000000000000000036311321362363300210320ustar00rootroot00000000000000_httpServer = $component; $this->_reqParser = new HttpRequestParser; } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn) { $conn->httpHeadersReceived = false; } /** * {@inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { if (true !== $from->httpHeadersReceived) { try { if (null === ($request = $this->_reqParser->onMessage($from, $msg))) { return; } } catch (\OverflowException $oe) { return $this->close($from, 413); } $from->httpHeadersReceived = true; return $this->_httpServer->onOpen($from, $request); } $this->_httpServer->onMessage($from, $msg); } /** * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { if ($conn->httpHeadersReceived) { $this->_httpServer->onClose($conn); } } /** * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { if ($conn->httpHeadersReceived) { $this->_httpServer->onError($conn, $e); } else { $this->close($conn, 500); } } } Ratchet-0.4.1/src/Ratchet/Http/HttpServerInterface.php000066400000000000000000000011051321362363300226450ustar00rootroot00000000000000_component = $component; $this->allowedOrigins += $allowed; } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { $header = (string)$request->getHeader('Origin')[0]; $origin = parse_url($header, PHP_URL_HOST) ?: $header; 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); } }Ratchet-0.4.1/src/Ratchet/Http/Router.php000066400000000000000000000057411321362363300202100ustar00rootroot00000000000000_matcher = $matcher; $this->_noopController = new NoOpHttpServerController; } /** * {@inheritdoc} * @throws \UnexpectedValueException If a controller is not \Ratchet\Http\HttpServerInterface */ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { if (null === $request) { throw new \UnexpectedValueException('$request can not be null'); } $conn->controller = $this->_noopController; $uri = $request->getUri(); $context = $this->_matcher->getContext(); $context->setMethod($request->getMethod()); $context->setHost($uri->getHost()); try { $route = $this->_matcher->match($uri->getPath()); } catch (MethodNotAllowedException $nae) { return $this->close($conn, 405, array('Allow' => $nae->getAllowedMethods())); } catch (ResourceNotFoundException $nfe) { return $this->close($conn, 404); } if (is_string($route['_controller']) && class_exists($route['_controller'])) { $route['_controller'] = new $route['_controller']; } if (!($route['_controller'] instanceof HttpServerInterface)) { throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface'); } $parameters = []; foreach($route as $key => $value) { if ((is_string($key)) && ('_' !== substr($key, 0, 1))) { $parameters[$key] = $value; } } $parameters = array_merge($parameters, gPsr\parse_query($uri->getQuery() ?: '')); $request = $request->withUri($uri->withQuery(gPsr\build_query($parameters))); $conn->controller = $route['_controller']; $conn->controller->onOpen($conn, $request); } /** * {@inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { $from->controller->onMessage($from, $msg); } /** * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { if (isset($conn->controller)) { $conn->controller->onClose($conn); } } /** * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { if (isset($conn->controller)) { $conn->controller->onError($conn, $e); } } } Ratchet-0.4.1/src/Ratchet/MessageComponentInterface.php000066400000000000000000000001571321362363300230750ustar00rootroot00000000000000send($msg); } public function onClose(ConnectionInterface $conn) { } public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } } Ratchet-0.4.1/src/Ratchet/Server/FlashPolicy.php000066400000000000000000000142201321362363300214640ustar00rootroot00000000000000'; /** * Stores an array of allowed domains and their ports * @var array */ protected $_access = array(); /** * @var string */ protected $_siteControl = ''; /** * @var string */ protected $_cache = ''; /** * @var string */ protected $_cacheValid = false; /** * Add a domain to an allowed access list. * * @param string $domain Specifies a requesting domain to be granted access. Both named domains and IP * addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can * be used to match all domains when used alone, or multiple domains (subdomains) when used as a * prefix for an explicit, second-level domain name separated with a dot (.) * @param string $ports A comma-separated list of ports or range of ports that a socket connection * is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers. * Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can * be used to allow all ports. * @param bool $secure * @throws \UnexpectedValueException * @return FlashPolicy */ public function addAllowedAccess($domain, $ports = '*', $secure = false) { if (!$this->validateDomain($domain)) { throw new \UnexpectedValueException('Invalid domain'); } if (!$this->validatePorts($ports)) { throw new \UnexpectedValueException('Invalid Port'); } $this->_access[] = array($domain, $ports, (boolean)$secure); $this->_cacheValid = false; return $this; } /** * Removes all domains from the allowed access list. * * @return \Ratchet\Server\FlashPolicy */ public function clearAllowedAccess() { $this->_access = array(); $this->_cacheValid = false; return $this; } /** * site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable * domain policy files other than the master policy file located in the target domain's root and named * crossdomain.xml. * * @param string $permittedCrossDomainPolicies * @throws \UnexpectedValueException * @return FlashPolicy */ public function setSiteControl($permittedCrossDomainPolicies = 'all') { if (!$this->validateSiteControl($permittedCrossDomainPolicies)) { throw new \UnexpectedValueException('Invalid site control set'); } $this->_siteControl = $permittedCrossDomainPolicies; $this->_cacheValid = false; return $this; } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn) { } /** * {@inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { if (!$this->_cacheValid) { $this->_cache = $this->renderPolicy()->asXML(); $this->_cacheValid = true; } $from->send($this->_cache . "\0"); $from->close(); } /** * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { } /** * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } /** * Builds the crossdomain file based on the template policy * * @throws \UnexpectedValueException * @return \SimpleXMLElement */ public function renderPolicy() { $policy = new \SimpleXMLElement($this->_policy); $siteControl = $policy->addChild('site-control'); if ($this->_siteControl == '') { $this->setSiteControl(); } $siteControl->addAttribute('permitted-cross-domain-policies', $this->_siteControl); if (empty($this->_access)) { throw new \UnexpectedValueException('You must add a domain through addAllowedAccess()'); } foreach ($this->_access as $access) { $tmp = $policy->addChild('allow-access-from'); $tmp->addAttribute('domain', $access[0]); $tmp->addAttribute('to-ports', $access[1]); $tmp->addAttribute('secure', ($access[2] === true) ? 'true' : 'false'); } return $policy; } /** * Make sure the proper site control was passed * * @param string $permittedCrossDomainPolicies * @return bool */ public function validateSiteControl($permittedCrossDomainPolicies) { //'by-content-type' and 'by-ftp-filename' are not available for sockets return (bool)in_array($permittedCrossDomainPolicies, array('none', 'master-only', 'all')); } /** * Validate for proper domains (wildcards allowed) * * @param string $domain * @return bool */ public function validateDomain($domain) { return (bool)preg_match("/^((http(s)?:\/\/)?([a-z0-9-_]+\.|\*\.)*([a-z0-9-_\.]+)|\*)$/i", $domain); } /** * Make sure valid ports were passed * * @param string $port * @return bool */ public function validatePorts($port) { return (bool)preg_match('/^(\*|(\d+[,-]?)*\d+)$/', $port); } } Ratchet-0.4.1/src/Ratchet/Server/IoConnection.php000066400000000000000000000012431321362363300216370ustar00rootroot00000000000000conn = $conn; } /** * {@inheritdoc} */ public function send($data) { $this->conn->write($data); return $this; } /** * {@inheritdoc} */ public function close() { $this->conn->end(); } } Ratchet-0.4.1/src/Ratchet/Server/IoServer.php000066400000000000000000000106121321362363300210060ustar00rootroot00000000000000loop = $loop; $this->app = $app; $this->socket = $socket; $socket->on('connection', array($this, 'handleConnect')); } /** * @param \Ratchet\MessageComponentInterface $component The application that I/O will call when events are received * @param int $port The port to server sockets on * @param string $address The address to receive sockets on (0.0.0.0 means receive connections from any) * @return IoServer */ public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') { $loop = LoopFactory::create(); $socket = new Reactor($address . ':' . $port, $loop); return new static($component, $socket, $loop); } /** * Run the application by entering the event loop * @throws \RuntimeException If a loop was not previously specified */ public function run() { if (null === $this->loop) { throw new \RuntimeException("A React Loop was not provided during instantiation"); } // @codeCoverageIgnoreStart $this->loop->run(); // @codeCoverageIgnoreEnd } /** * Triggered when a new connection is received from React * @param \React\Socket\ConnectionInterface $conn */ public function handleConnect($conn) { $conn->decor = new IoConnection($conn); $conn->decor->resourceId = (int)$conn->stream; $uri = $conn->getRemoteAddress(); $conn->decor->remoteAddress = trim( parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_HOST), '[]' ); $this->app->onOpen($conn->decor); $conn->on('data', function ($data) use ($conn) { $this->handleData($data, $conn); }); $conn->on('close', function () use ($conn) { $this->handleEnd($conn); }); $conn->on('error', function (\Exception $e) use ($conn) { $this->handleError($e, $conn); }); } /** * Data has been received from React * @param string $data * @param \React\Socket\ConnectionInterface $conn */ public function handleData($data, $conn) { try { $this->app->onMessage($conn->decor, $data); } catch (\Exception $e) { $this->handleError($e, $conn); } } /** * A connection has been closed by React * @param \React\Socket\ConnectionInterface $conn */ public function handleEnd($conn) { try { $this->app->onClose($conn->decor); } catch (\Exception $e) { $this->handleError($e, $conn); } unset($conn->decor); } /** * An error has occurred, let the listening application know * @param \Exception $e * @param \React\Socket\ConnectionInterface $conn */ public function handleError(\Exception $e, $conn) { $this->app->onError($conn->decor, $e); } } Ratchet-0.4.1/src/Ratchet/Server/IpBlackList.php000066400000000000000000000053121321362363300214120ustar00rootroot00000000000000_decorating = $component; } /** * Add an address to the blacklist that will not be allowed to connect to your application * @param string $ip IP address to block from connecting to your application * @return IpBlackList */ public function blockAddress($ip) { $this->_blacklist[$ip] = true; return $this; } /** * Unblock an address so they can access your application again * @param string $ip IP address to unblock from connecting to your application * @return IpBlackList */ public function unblockAddress($ip) { if (isset($this->_blacklist[$this->filterAddress($ip)])) { unset($this->_blacklist[$this->filterAddress($ip)]); } return $this; } /** * @param string $address * @return bool */ public function isBlocked($address) { return (isset($this->_blacklist[$this->filterAddress($address)])); } /** * Get an array of all the addresses blocked * @return array */ public function getBlockedAddresses() { return array_keys($this->_blacklist); } /** * @param string $address * @return string */ public function filterAddress($address) { if (strstr($address, ':') && substr_count($address, '.') == 3) { list($address, $port) = explode(':', $address); } return $address; } /** * {@inheritdoc} */ function onOpen(ConnectionInterface $conn) { if ($this->isBlocked($conn->remoteAddress)) { return $conn->close(); } return $this->_decorating->onOpen($conn); } /** * {@inheritdoc} */ function onMessage(ConnectionInterface $from, $msg) { return $this->_decorating->onMessage($from, $msg); } /** * {@inheritdoc} */ function onClose(ConnectionInterface $conn) { if (!$this->isBlocked($conn->remoteAddress)) { $this->_decorating->onClose($conn); } } /** * {@inheritdoc} */ function onError(ConnectionInterface $conn, \Exception $e) { if (!$this->isBlocked($conn->remoteAddress)) { $this->_decorating->onError($conn, $e); } } } Ratchet-0.4.1/src/Ratchet/Session/000077500000000000000000000000001321362363300167145ustar00rootroot00000000000000Ratchet-0.4.1/src/Ratchet/Session/Serialize/000077500000000000000000000000001321362363300206435ustar00rootroot00000000000000Ratchet-0.4.1/src/Ratchet/Session/Serialize/HandlerInterface.php000066400000000000000000000004051321362363300245510ustar00rootroot00000000000000 $bucketData) { $preSerialized[] = $bucket . '|' . serialize($bucketData); } $serialized = implode('', $preSerialized); } return $serialized; } /** * {@inheritdoc} * @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net * @throws \UnexpectedValueException If there is a problem parsing the data */ public function unserialize($raw) { $returnData = array(); $offset = 0; while ($offset < strlen($raw)) { if (!strstr(substr($raw, $offset), "|")) { throw new \UnexpectedValueException("invalid data, remaining: " . substr($raw, $offset)); } $pos = strpos($raw, "|", $offset); $num = $pos - $offset; $varname = substr($raw, $offset, $num); $offset += $num + 1; $data = unserialize(substr($raw, $offset)); $returnData[$varname] = $data; $offset += strlen(serialize($data)); } return $returnData; } } Ratchet-0.4.1/src/Ratchet/Session/SessionProvider.php000066400000000000000000000200631321362363300225640ustar00rootroot00000000000000_app = $app; $this->_handler = $handler; $this->_null = new NullSessionHandler; ini_set('session.auto_start', 0); ini_set('session.cache_limiter', ''); ini_set('session.use_cookies', 0); $this->setOptions($options); if (null === $serializer) { $serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler"; // awesome/terrible hack, eh? if (!class_exists($serialClass)) { throw new \RuntimeException('Unable to parse session serialize handler'); } $serializer = new $serialClass; } $this->_serializer = $serializer; } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { $sessionName = ini_get('session.name'); $id = array_reduce($request->getHeader('Cookie'), function($accumulator, $cookie) use ($sessionName) { if ($accumulator) { return $accumulator; } $crumbs = $this->parseCookie($cookie); return isset($crumbs['cookies'][$sessionName]) ? $crumbs['cookies'][$sessionName] : false; }, false); if (null === $request || false === $id) { $saveHandler = $this->_null; $id = ''; } else { $saveHandler = $this->_handler; } $conn->Session = new Session(new VirtualSessionStorage($saveHandler, $id, $this->_serializer)); if (ini_get('session.auto_start')) { $conn->Session->start(); } return $this->_app->onOpen($conn, $request); } /** * {@inheritdoc} */ function onMessage(ConnectionInterface $from, $msg) { return $this->_app->onMessage($from, $msg); } /** * {@inheritdoc} */ function onClose(ConnectionInterface $conn) { // "close" session for Connection return $this->_app->onClose($conn); } /** * {@inheritdoc} */ function onError(ConnectionInterface $conn, \Exception $e) { return $this->_app->onError($conn, $e); } /** * Set all the php session. ini options * © Symfony * @param array $options * @return array */ protected function setOptions(array $options) { $all = array( 'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly', 'cookie_lifetime', 'cookie_path', 'cookie_secure', 'entropy_file', 'entropy_length', 'gc_divisor', 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', 'hash_function', 'name', 'referer_check', 'serialize_handler', 'use_cookies', 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags' ); foreach ($all as $key) { if (!array_key_exists($key, $options)) { $options[$key] = ini_get("session.{$key}"); } else { ini_set("session.{$key}", $options[$key]); } } return $options; } /** * @param string $langDef Input to convert * @return string */ protected function toClassCase($langDef) { return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef))); } /** * Taken from Guzzle3 */ private static $cookieParts = array( 'domain' => 'Domain', 'path' => 'Path', 'max_age' => 'Max-Age', 'expires' => 'Expires', 'version' => 'Version', 'secure' => 'Secure', 'port' => 'Port', 'discard' => 'Discard', 'comment' => 'Comment', 'comment_url' => 'Comment-Url', 'http_only' => 'HttpOnly' ); /** * Taken from Guzzle3 */ private function parseCookie($cookie, $host = null, $path = null, $decode = false) { // Explode the cookie string using a series of semicolons $pieces = array_filter(array_map('trim', explode(';', $cookie))); // The name of the cookie (first kvp) must include an equal sign. if (empty($pieces) || !strpos($pieces[0], '=')) { return false; } // Create the default return array $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( 'cookies' => array(), 'data' => array(), 'path' => $path ?: '/', 'http_only' => false, 'discard' => false, 'domain' => $host )); $foundNonCookies = 0; // Add the cookie pieces into the parsed data array foreach ($pieces as $part) { $cookieParts = explode('=', $part, 2); $key = trim($cookieParts[0]); if (count($cookieParts) == 1) { // Can be a single value (e.g. secure, httpOnly) $value = true; } else { // Be sure to strip wrapping quotes $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); if ($decode) { $value = urldecode($value); } } // Only check for non-cookies when cookies have been found if (!empty($data['cookies'])) { foreach (self::$cookieParts as $mapValue => $search) { if (!strcasecmp($search, $key)) { $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; $foundNonCookies++; continue 2; } } } // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data. $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; } // Calculate the expires date if (!$data['expires'] && $data['max_age']) { $data['expires'] = time() + (int) $data['max_age']; } return $data; } } Ratchet-0.4.1/src/Ratchet/Session/Storage/000077500000000000000000000000001321362363300203205ustar00rootroot00000000000000Ratchet-0.4.1/src/Ratchet/Session/Storage/Proxy/000077500000000000000000000000001321362363300214415ustar00rootroot00000000000000Ratchet-0.4.1/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php000066400000000000000000000020701321362363300246410ustar00rootroot00000000000000saveHandlerName = 'user'; $this->_sessionName = ini_get('session.name'); } /** * {@inheritdoc} */ public function getId() { return $this->_sessionId; } /** * {@inheritdoc} */ public function setId($id) { $this->_sessionId = $id; } /** * {@inheritdoc} */ public function getName() { return $this->_sessionName; } /** * DO NOT CALL THIS METHOD * @internal */ public function setName($name) { throw new \RuntimeException("Can not change session name in VirtualProxy"); } } Ratchet-0.4.1/src/Ratchet/Session/Storage/VirtualSessionStorage.php000066400000000000000000000056451321362363300253620ustar00rootroot00000000000000setSaveHandler($handler); $this->saveHandler->setId($sessionId); $this->_serializer = $serializer; $this->setMetadataBag(null); } /** * {@inheritdoc} */ public function start() { if ($this->started && !$this->closed) { return true; } // You have to call Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::open() to use // pdo_sqlite (and possible pdo_*) as session storage, if you are using a DSN string instead of a \PDO object // in the constructor. The method arguments are filled with the values, which are also used by the symfony // framework in this case. This must not be the best choice, but it works. $this->saveHandler->open(session_save_path(), session_name()); $rawData = $this->saveHandler->read($this->saveHandler->getId()); $sessionData = $this->_serializer->unserialize($rawData); $this->loadSession($sessionData); if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { $this->saveHandler->setActive(false); } return true; } /** * {@inheritdoc} */ public function regenerate($destroy = false, $lifetime = null) { // .. ? } /** * {@inheritdoc} */ public function save() { // get the data from the bags? // serialize the data // save the data using the saveHandler // $this->saveHandler->write($this->saveHandler->getId(), if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) { $this->saveHandler->setActive(false); } $this->closed = true; } /** * {@inheritdoc} */ public function setSaveHandler($saveHandler = null) { if (!($saveHandler instanceof \SessionHandlerInterface)) { throw new \InvalidArgumentException('Handler must be instance of SessionHandlerInterface'); } if (!($saveHandler instanceof VirtualProxy)) { $saveHandler = new VirtualProxy($saveHandler); } $this->saveHandler = $saveHandler; } } Ratchet-0.4.1/src/Ratchet/Wamp/000077500000000000000000000000001321362363300161755ustar00rootroot00000000000000Ratchet-0.4.1/src/Ratchet/Wamp/Exception.php000066400000000000000000000001061321362363300206410ustar00rootroot00000000000000_decorating = $serverComponent; $this->connections = new \SplObjectStorage; } /** * {@inheritdoc} */ public function getSubProtocols() { if ($this->_decorating instanceof WsServerInterface) { $subs = $this->_decorating->getSubProtocols(); $subs[] = 'wamp'; return $subs; } return ['wamp']; } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn) { $decor = new WampConnection($conn); $this->connections->attach($conn, $decor); $this->_decorating->onOpen($decor); } /** * {@inheritdoc} * @throws \Ratchet\Wamp\Exception * @throws \Ratchet\Wamp\JsonException */ public function onMessage(ConnectionInterface $from, $msg) { $from = $this->connections[$from]; if (null === ($json = @json_decode($msg, true))) { throw new JsonException; } if (!is_array($json) || $json !== array_values($json)) { throw new Exception("Invalid WAMP message format"); } if (isset($json[1]) && !(is_string($json[1]) || is_numeric($json[1]))) { throw new Exception('Invalid Topic, must be a string'); } switch ($json[0]) { case static::MSG_PREFIX: $from->WAMP->prefixes[$json[1]] = $json[2]; break; case static::MSG_CALL: array_shift($json); $callID = array_shift($json); $procURI = array_shift($json); if (count($json) == 1 && is_array($json[0])) { $json = $json[0]; } $this->_decorating->onCall($from, $callID, $from->getUri($procURI), $json); break; case static::MSG_SUBSCRIBE: $this->_decorating->onSubscribe($from, $from->getUri($json[1])); break; case static::MSG_UNSUBSCRIBE: $this->_decorating->onUnSubscribe($from, $from->getUri($json[1])); break; case static::MSG_PUBLISH: $exclude = (array_key_exists(3, $json) ? $json[3] : null); if (!is_array($exclude)) { if (true === (boolean)$exclude) { $exclude = [$from->WAMP->sessionId]; } else { $exclude = []; } } $eligible = (array_key_exists(4, $json) ? $json[4] : []); $this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible); break; default: throw new Exception('Invalid WAMP message type'); } } /** * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { $decor = $this->connections[$conn]; $this->connections->detach($conn); $this->_decorating->onClose($decor); } /** * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { return $this->_decorating->onError($this->connections[$conn], $e); } } Ratchet-0.4.1/src/Ratchet/Wamp/Topic.php000066400000000000000000000045121321362363300177660ustar00rootroot00000000000000id = $topicId; $this->subscribers = new \SplObjectStorage; } /** * @return string */ public function getId() { return $this->id; } public function __toString() { return $this->getId(); } /** * Send a message to all the connections in this topic * @param string|array $msg Payload to publish * @param array $exclude A list of session IDs the message should be excluded from (blacklist) * @param array $eligible A list of session Ids the message should be send to (whitelist) * @return Topic The same Topic object to chain */ public function broadcast($msg, array $exclude = array(), array $eligible = array()) { $useEligible = (bool)count($eligible); foreach ($this->subscribers as $client) { if (in_array($client->WAMP->sessionId, $exclude)) { continue; } if ($useEligible && !in_array($client->WAMP->sessionId, $eligible)) { continue; } $client->event($this->id, $msg); } return $this; } /** * @param WampConnection $conn * @return boolean */ public function has(ConnectionInterface $conn) { return $this->subscribers->contains($conn); } /** * @param WampConnection $conn * @return Topic */ public function add(ConnectionInterface $conn) { $this->subscribers->attach($conn); return $this; } /** * @param WampConnection $conn * @return Topic */ public function remove(ConnectionInterface $conn) { if ($this->subscribers->contains($conn)) { $this->subscribers->detach($conn); } return $this; } /** * {@inheritdoc} */ public function getIterator() { return $this->subscribers; } /** * {@inheritdoc} */ public function count() { return $this->subscribers->count(); } } Ratchet-0.4.1/src/Ratchet/Wamp/TopicManager.php000066400000000000000000000060301321362363300212560ustar00rootroot00000000000000app = $app; } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn) { $conn->WAMP->subscriptions = new \SplObjectStorage; $this->app->onOpen($conn); } /** * {@inheritdoc} */ public function onCall(ConnectionInterface $conn, $id, $topic, array $params) { $this->app->onCall($conn, $id, $this->getTopic($topic), $params); } /** * {@inheritdoc} */ public function onSubscribe(ConnectionInterface $conn, $topic) { $topicObj = $this->getTopic($topic); if ($conn->WAMP->subscriptions->contains($topicObj)) { return; } $this->topicLookup[$topic]->add($conn); $conn->WAMP->subscriptions->attach($topicObj); $this->app->onSubscribe($conn, $topicObj); } /** * {@inheritdoc} */ public function onUnsubscribe(ConnectionInterface $conn, $topic) { $topicObj = $this->getTopic($topic); if (!$conn->WAMP->subscriptions->contains($topicObj)) { return; } $this->cleanTopic($topicObj, $conn); $this->app->onUnsubscribe($conn, $topicObj); } /** * {@inheritdoc} */ public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) { $this->app->onPublish($conn, $this->getTopic($topic), $event, $exclude, $eligible); } /** * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { $this->app->onClose($conn); foreach ($this->topicLookup as $topic) { $this->cleanTopic($topic, $conn); } } /** * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { $this->app->onError($conn, $e); } /** * {@inheritdoc} */ public function getSubProtocols() { if ($this->app instanceof WsServerInterface) { return $this->app->getSubProtocols(); } return array(); } /** * @param string * @return Topic */ protected function getTopic($topic) { if (!array_key_exists($topic, $this->topicLookup)) { $this->topicLookup[$topic] = new Topic($topic); } return $this->topicLookup[$topic]; } protected function cleanTopic(Topic $topic, ConnectionInterface $conn) { if ($conn->WAMP->subscriptions->contains($topic)) { $conn->WAMP->subscriptions->detach($topic); } $this->topicLookup[$topic->getId()]->remove($conn); if (0 === $topic->count()) { unset($this->topicLookup[$topic->getId()]); } } } Ratchet-0.4.1/src/Ratchet/Wamp/WampConnection.php000066400000000000000000000066601321362363300216420ustar00rootroot00000000000000WAMP = new \StdClass; $this->WAMP->sessionId = str_replace('.', '', uniqid(mt_rand(), true)); $this->WAMP->prefixes = array(); $this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION))); } /** * Successfully respond to a call made by the client * @param string $id The unique ID given by the client to respond to * @param array $data an object or array * @return WampConnection */ public function callResult($id, $data = array()) { return $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data))); } /** * Respond with an error to a client call * @param string $id The unique ID given by the client to respond to * @param string $errorUri The URI given to identify the specific error * @param string $desc A developer-oriented description of the error * @param string $details An optional human readable detail message to send back * @return WampConnection */ public function callError($id, $errorUri, $desc = '', $details = null) { if ($errorUri instanceof Topic) { $errorUri = (string)$errorUri; } $data = array(WAMP::MSG_CALL_ERROR, $id, $errorUri, $desc); if (null !== $details) { $data[] = $details; } return $this->send(json_encode($data)); } /** * @param string $topic The topic to broadcast to * @param mixed $msg Data to send with the event. Anything that is json'able * @return WampConnection */ public function event($topic, $msg) { return $this->send(json_encode(array(WAMP::MSG_EVENT, (string)$topic, $msg))); } /** * @param string $curie * @param string $uri * @return WampConnection */ public function prefix($curie, $uri) { $this->WAMP->prefixes[$curie] = (string)$uri; return $this->send(json_encode(array(WAMP::MSG_PREFIX, $curie, (string)$uri))); } /** * Get the full request URI from the connection object if a prefix has been established for it * @param string $uri * @return string */ public function getUri($uri) { $curieSeperator = ':'; if (preg_match('/http(s*)\:\/\//', $uri) == false) { if (strpos($uri, $curieSeperator) !== false) { list($prefix, $action) = explode($curieSeperator, $uri); if(isset($this->WAMP->prefixes[$prefix]) === true){ return $this->WAMP->prefixes[$prefix] . '#' . $action; } } } return $uri; } /** * @internal */ public function send($data) { $this->getConnection()->send($data); return $this; } /** * {@inheritdoc} */ public function close($opt = null) { $this->getConnection()->close($opt); } } Ratchet-0.4.1/src/Ratchet/Wamp/WampServer.php000066400000000000000000000035021321362363300210010ustar00rootroot00000000000000wampProtocol = new ServerProtocol(new TopicManager($app)); } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn) { $this->wampProtocol->onOpen($conn); } /** * {@inheritdoc} */ public function onMessage(ConnectionInterface $conn, $msg) { try { $this->wampProtocol->onMessage($conn, $msg); } catch (Exception $we) { $conn->close(1007); } } /** * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { $this->wampProtocol->onClose($conn); } /** * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { $this->wampProtocol->onError($conn, $e); } /** * {@inheritdoc} */ public function getSubProtocols() { return $this->wampProtocol->getSubProtocols(); } } Ratchet-0.4.1/src/Ratchet/Wamp/WampServerInterface.php000066400000000000000000000036621321362363300226310ustar00rootroot00000000000000connection = $conn; $this->buffer = $buffer; } } Ratchet-0.4.1/src/Ratchet/WebSocket/MessageCallableInterface.php000066400000000000000000000003551321362363300245200ustar00rootroot00000000000000WebSocket->closing) { if (!($msg instanceof DataInterface)) { $msg = new Frame($msg); } $this->getConnection()->send($msg->getContents()); } return $this; } /** * @param int|\Ratchet\RFC6455\Messaging\DataInterface */ public function close($code = 1000) { if ($this->WebSocket->closing) { return; } if ($code instanceof DataInterface) { $this->send($code); } else { $this->send(new Frame(pack('n', $code), true, Frame::OP_CLOSE)); } $this->getConnection()->close(); $this->WebSocket->closing = true; } } Ratchet-0.4.1/src/Ratchet/WebSocket/WsServer.php000066400000000000000000000163131321362363300214540ustar00rootroot00000000000000msgCb = function(ConnectionInterface $conn, MessageInterface $msg) { $this->delegate->onMessage($conn, $msg); }; } elseif ($component instanceof DataComponentInterface) { $this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) { $this->delegate->onMessage($conn, $msg->getPayload()); }; } else { throw new \UnexpectedValueException('Expected instance of \Ratchet\WebSocket\MessageComponentInterface or \Ratchet\MessageComponentInterface'); } if (bin2hex('✓') !== 'e29c93') { throw new \DomainException('Bad encoding, unicode character ✓ did not match expected value. Ensure charset UTF-8 and check ini val mbstring.func_autoload'); } $this->delegate = $component; $this->connections = new \SplObjectStorage; $this->closeFrameChecker = new CloseFrameChecker; $this->handshakeNegotiator = new ServerNegotiator(new RequestVerifier); $this->handshakeNegotiator->setStrictSubProtocolCheck(true); if ($component instanceof WsServerInterface) { $this->handshakeNegotiator->setSupportedSubProtocols($component->getSubProtocols()); } $this->pongReceiver = function() {}; $reusableUnderflowException = new \UnderflowException; $this->ueFlowFactory = function() use ($reusableUnderflowException) { return $reusableUnderflowException; }; } /** * {@inheritdoc} */ public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { if (null === $request) { throw new \UnexpectedValueException('$request can not be null'); } $conn->httpRequest = $request; $conn->WebSocket = new \StdClass; $conn->WebSocket->closing = false; $response = $this->handshakeNegotiator->handshake($request)->withHeader('X-Powered-By', \Ratchet\VERSION); $conn->send(gPsr\str($response)); if (101 !== $response->getStatusCode()) { return $conn->close(); } $wsConn = new WsConnection($conn); $streamer = new MessageBuffer( $this->closeFrameChecker, function(MessageInterface $msg) use ($wsConn) { $cb = $this->msgCb; $cb($wsConn, $msg); }, function(FrameInterface $frame) use ($wsConn) { $this->onControlFrame($frame, $wsConn); }, true, $this->ueFlowFactory ); $this->connections->attach($conn, new ConnContext($wsConn, $streamer)); return $this->delegate->onOpen($wsConn); } /** * {@inheritdoc} */ public function onMessage(ConnectionInterface $from, $msg) { if ($from->WebSocket->closing) { return; } $this->connections[$from]->buffer->onData($msg); } /** * {@inheritdoc} */ public function onClose(ConnectionInterface $conn) { if ($this->connections->contains($conn)) { $context = $this->connections[$conn]; $this->connections->detach($conn); $this->delegate->onClose($context->connection); } } /** * {@inheritdoc} */ public function onError(ConnectionInterface $conn, \Exception $e) { if ($this->connections->contains($conn)) { $this->delegate->onError($this->connections[$conn]->connection, $e); } else { $conn->close(); } } public function onControlFrame(FrameInterface $frame, WsConnection $conn) { switch ($frame->getOpCode()) { case Frame::OP_CLOSE: $conn->close($frame); break; case Frame::OP_PING: $conn->send(new Frame($frame->getPayload(), true, Frame::OP_PONG)); break; case Frame::OP_PONG: $pongReceiver = $this->pongReceiver; $pongReceiver($frame, $conn); break; } } public function setStrictSubProtocolCheck($enable) { $this->handshakeNegotiator->setStrictSubProtocolCheck($enable); } public function enableKeepAlive(LoopInterface $loop, $interval = 30) { $lastPing = new Frame(uniqid(), true, Frame::OP_PING); $pingedConnections = new \SplObjectStorage; $splClearer = new \SplObjectStorage; $this->pongReceiver = function(FrameInterface $frame, $wsConn) use ($pingedConnections, &$lastPing) { if ($frame->getPayload() === $lastPing->getPayload()) { $pingedConnections->detach($wsConn); } }; $loop->addPeriodicTimer((int)$interval, function() use ($pingedConnections, &$lastPing, $splClearer) { foreach ($pingedConnections as $wsConn) { $wsConn->close(); } $pingedConnections->removeAllExcept($splClearer); $lastPing = new Frame(uniqid(), true, Frame::OP_PING); foreach ($this->connections as $key => $conn) { $wsConn = $this->connections[$conn]->connection; $wsConn->send($lastPing); $pingedConnections->attach($wsConn); } }); } } Ratchet-0.4.1/src/Ratchet/WebSocket/WsServerInterface.php000066400000000000000000000006161321362363300232740ustar00rootroot00000000000000send($msg); } public function onOpen(ConnectionInterface $conn) { } public function onClose(ConnectionInterface $conn) { } public function onError(ConnectionInterface $conn, \Exception $e) { } } $port = $argc > 1 ? $argv[1] : 8000; $impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect'); $loop = new $impl; $sock = new React\Socket\Server('0.0.0.0:' . $port, $loop); $wsServer = new Ratchet\WebSocket\WsServer(new BinaryEcho); // This is enabled to test https://github.com/ratchetphp/Ratchet/issues/430 // The time is left at 10 minutes so that it will not try to every ping anything // This causes the Ratchet server to crash on test 2.7 $wsServer->enableKeepAlive($loop, 600); $app = new Ratchet\Http\HttpServer($wsServer); $server = new Ratchet\Server\IoServer($app, $sock, $loop); $server->run(); Ratchet-0.4.1/tests/autobahn/fuzzingclient-all.json000066400000000000000000000010651321362363300224110ustar00rootroot00000000000000{ "options": {"failByDrop": false} , "outdir": "reports/ab" , "servers": [ {"agent": "Ratchet/0.4 libevent", "url": "ws://localhost:8001", "options": {"version": 18}} , {"agent": "Ratchet/0.4 libev", "url": "ws://localhost:8004", "options": {"version": 18}} , {"agent": "Ratchet/0.4 streams", "url": "ws://localhost:8002", "options": {"version": 18}} , {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}} ] , "cases": ["*"] , "exclude-cases": [] , "exclude-agent-cases": {} } Ratchet-0.4.1/tests/autobahn/fuzzingclient-profile.json000066400000000000000000000005061321362363300233000ustar00rootroot00000000000000{ "options": {"failByDrop": false} , "outdir": "reports/profile" , "servers": [ {"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}} ] , "cases": ["9.7.4"] , "exclude-cases": ["1.2.*", "2.3", "2.4", "2.6", "9.2.*", "9.4.*", "9.6.*", "9.8.*"] , "exclude-agent-cases": {} } Ratchet-0.4.1/tests/autobahn/fuzzingclient-quick.json000066400000000000000000000003751321362363300227600ustar00rootroot00000000000000{ "options": {"failByDrop": false} , "outdir": "reports/rfc" , "servers": [ {"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}} ] , "cases": ["*"] , "exclude-cases": [] , "exclude-agent-cases": {} } Ratchet-0.4.1/tests/bootstrap.php000066400000000000000000000002031321362363300167730ustar00rootroot00000000000000addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet'); Ratchet-0.4.1/tests/helpers/000077500000000000000000000000001321362363300157145ustar00rootroot00000000000000Ratchet-0.4.1/tests/helpers/Ratchet/000077500000000000000000000000001321362363300173065ustar00rootroot00000000000000Ratchet-0.4.1/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php000066400000000000000000000033551321362363300264140ustar00rootroot00000000000000_app = $this->getMock($this->getComponentClassString()); $decorator = $this->getDecoratorClassString(); $this->_serv = new $decorator($this->_app); $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); $this->doOpen($this->_conn); } protected function doOpen($conn) { $this->_serv->onOpen($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->doOpen($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); } public function passthroughMessageTest($value) { $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $value); $this->_serv->onMessage($this->_conn, $value); } } Ratchet-0.4.1/tests/helpers/Ratchet/Mock/000077500000000000000000000000001321362363300201775ustar00rootroot00000000000000Ratchet-0.4.1/tests/helpers/Ratchet/Mock/Component.php000066400000000000000000000017221321362363300226540ustar00rootroot00000000000000last[__FUNCTION__] = func_get_args(); } public function onOpen(ConnectionInterface $conn) { $this->last[__FUNCTION__] = func_get_args(); } public function onMessage(ConnectionInterface $from, $msg) { $this->last[__FUNCTION__] = func_get_args(); } public function onClose(ConnectionInterface $conn) { $this->last[__FUNCTION__] = func_get_args(); } public function onError(ConnectionInterface $conn, \Exception $e) { $this->last[__FUNCTION__] = func_get_args(); } public function getSubProtocols() { return $this->protocols; } } Ratchet-0.4.1/tests/helpers/Ratchet/Mock/Connection.php000066400000000000000000000006211321362363300230060ustar00rootroot00000000000000 '' , 'close' => false ); public $remoteAddress = '127.0.0.1'; public function send($data) { $this->last[__FUNCTION__] = $data; } public function close() { $this->last[__FUNCTION__] = true; } } Ratchet-0.4.1/tests/helpers/Ratchet/Mock/ConnectionDecorator.php000066400000000000000000000007251321362363300246560ustar00rootroot00000000000000 '' , 'end' => false ); public function send($data) { $this->last[__FUNCTION__] = $data; $this->getConnection()->send($data); } public function close() { $this->last[__FUNCTION__] = true; $this->getConnection()->close(); } } Ratchet-0.4.1/tests/helpers/Ratchet/Mock/WampComponent.php000066400000000000000000000024221321362363300234770ustar00rootroot00000000000000protocols; } public function onCall(ConnectionInterface $conn, $id, $procURI, array $params) { $this->last[__FUNCTION__] = func_get_args(); } public function onSubscribe(ConnectionInterface $conn, $topic) { $this->last[__FUNCTION__] = func_get_args(); } public function onUnSubscribe(ConnectionInterface $conn, $topic) { $this->last[__FUNCTION__] = func_get_args(); } public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) { $this->last[__FUNCTION__] = func_get_args(); } public function onOpen(ConnectionInterface $conn) { $this->last[__FUNCTION__] = func_get_args(); } public function onClose(ConnectionInterface $conn) { $this->last[__FUNCTION__] = func_get_args(); } public function onError(ConnectionInterface $conn, \Exception $e) { $this->last[__FUNCTION__] = func_get_args(); } } Ratchet-0.4.1/tests/helpers/Ratchet/NullComponent.php000066400000000000000000000017061321362363300226200ustar00rootroot00000000000000mock = $this->getMock('\Ratchet\ConnectionInterface'); $this->l1 = new ConnectionDecorator($this->mock); $this->l2 = new ConnectionDecorator($this->l1); } public function testGet() { $var = 'hello'; $val = 'world'; $this->mock->$var = $val; $this->assertEquals($val, $this->l1->$var); $this->assertEquals($val, $this->l2->$var); } public function testSet() { $var = 'Chris'; $val = 'Boden'; $this->l1->$var = $val; $this->assertEquals($val, $this->mock->$var); } public function testSetLevel2() { $var = 'Try'; $val = 'Again'; $this->l2->$var = $val; $this->assertEquals($val, $this->mock->$var); } public function testIsSetTrue() { $var = 'PHP'; $val = 'Ratchet'; $this->mock->$var = $val; $this->assertTrue(isset($this->l1->$var)); $this->assertTrue(isset($this->l2->$var)); } public function testIsSetFalse() { $var = 'herp'; $val = 'derp'; $this->assertFalse(isset($this->l1->$var)); $this->assertFalse(isset($this->l2->$var)); } public function testUnset() { $var = 'Flying'; $val = 'Monkey'; $this->mock->$var = $val; unset($this->l1->$var); $this->assertFalse(isset($this->mock->$var)); } public function testUnsetLevel2() { $var = 'Flying'; $val = 'Monkey'; $this->mock->$var = $val; unset($this->l2->$var); $this->assertFalse(isset($this->mock->$var)); } public function testGetConnection() { $class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator'); $method = $class->getMethod('getConnection'); $method->setAccessible(true); $conn = $method->invokeArgs($this->l1, array()); $this->assertSame($this->mock, $conn); } public function testGetConnectionLevel2() { $class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator'); $method = $class->getMethod('getConnection'); $method->setAccessible(true); $conn = $method->invokeArgs($this->l2, array()); $this->assertSame($this->l1, $conn); } public function testWrapperCanStoreSelfInDecorator() { $this->mock->decorator = $this->l1; $this->assertSame($this->l1, $this->l2->decorator); } public function testDecoratorRecursion() { $this->mock->decorator = new \stdClass; $this->mock->decorator->conn = $this->l1; $this->assertSame($this->l1, $this->mock->decorator->conn); $this->assertSame($this->l1, $this->l1->decorator->conn); $this->assertSame($this->l1, $this->l2->decorator->conn); } public function testDecoratorRecursionLevel2() { $this->mock->decorator = new \stdClass; $this->mock->decorator->conn = $this->l2; $this->assertSame($this->l2, $this->mock->decorator->conn); $this->assertSame($this->l2, $this->l1->decorator->conn); $this->assertSame($this->l2, $this->l2->decorator->conn); // just for fun $this->assertSame($this->l2, $this->l2->decorator->conn->decorator->conn->decorator->conn); } public function testWarningGettingNothing() { $this->setExpectedException('PHPUnit_Framework_Error'); $var = $this->mock->nonExistant; } public function testWarningGettingNothingLevel1() { $this->setExpectedException('PHPUnit_Framework_Error'); $var = $this->l1->nonExistant; } public function testWarningGettingNothingLevel2() { $this->setExpectedException('PHPUnit_Framework_Error'); $var = $this->l2->nonExistant; } } Ratchet-0.4.1/tests/unit/Http/000077500000000000000000000000001321362363300161505ustar00rootroot00000000000000Ratchet-0.4.1/tests/unit/Http/HttpRequestParserTest.php000066400000000000000000000031451321362363300231710ustar00rootroot00000000000000parser = new HttpRequestParser; } public function headersProvider() { return array( array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n") , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n") , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n1") , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖") , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖\r\n\r\n") , array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie\r\n") ); } /** * @dataProvider headersProvider */ public function testIsEom($expected, $message) { $this->assertEquals($expected, $this->parser->isEom($message)); } public function testBufferOverflowResponse() { $conn = $this->getMock('\Ratchet\ConnectionInterface'); $this->parser->maxSize = 20; $this->assertNull($this->parser->onMessage($conn, "GET / HTTP/1.1\r\n")); $this->setExpectedException('OverflowException'); $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('\Psr\Http\Message\RequestInterface', $return); } } Ratchet-0.4.1/tests/unit/Http/HttpServerTest.php000066400000000000000000000041221321362363300216260ustar00rootroot00000000000000_conn->httpHeadersReceived = true; } public function getConnectionClassString() { return '\Ratchet\ConnectionInterface'; } public function getDecoratorClassString() { return '\Ratchet\Http\HttpServer'; } public function getComponentClassString() { return '\Ratchet\Http\HttpServerInterface'; } public function testOpen() { $headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"; $this->_conn->httpHeadersReceived = false; $this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection()); $this->_serv->onMessage($this->_conn, $headers); } public function testOnMessageAfterHeaders() { $headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"; $this->_conn->httpHeadersReceived = false; $this->_serv->onMessage($this->_conn, $headers); $message = "Hello World!"; $this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message); $this->_serv->onMessage($this->_conn, $message); } public function testBufferOverflow() { $this->_conn->expects($this->once())->method('close'); $this->_conn->httpHeadersReceived = false; $this->_serv->onMessage($this->_conn, str_repeat('a', 5000)); } public function testCloseIfNotEstablished() { $this->_conn->httpHeadersReceived = false; $this->_conn->expects($this->once())->method('close'); $this->_serv->onError($this->_conn, new \Exception('Whoops!')); } public function testBufferHeaders() { $this->_conn->httpHeadersReceived = false; $this->_app->expects($this->never())->method('onOpen'); $this->_app->expects($this->never())->method('onMessage'); $this->_serv->onMessage($this->_conn, "GET / HTTP/1.1"); } } Ratchet-0.4.1/tests/unit/Http/OriginCheckTest.php000066400000000000000000000023651321362363300217140ustar00rootroot00000000000000_reqStub = $this->getMock('Psr\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 = ['socketo.me']; $this->_conn->expects($this->once())->method('close'); $this->_serv->onOpen($this->_conn, $this->_reqStub); } public function testOnMessage() { $this->passthroughMessageTest('Hello World!'); } } Ratchet-0.4.1/tests/unit/Http/RouterTest.php000066400000000000000000000161201321362363300210010ustar00rootroot00000000000000_conn = $this->getMock('\Ratchet\ConnectionInterface'); $this->_uri = $this->getMock('Psr\Http\Message\UriInterface'); $this->_req = $this->getMock('\Psr\Http\Message\RequestInterface'); $this->_req ->expects($this->any()) ->method('getUri') ->will($this->returnValue($this->_uri)); $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->_uri->expects($this->any())->method('getPath')->will($this->returnValue('ws://doesnt.matter/')); $this->_uri->expects($this->any())->method('withQuery')->with($this->callback(function($val) { $this->setResult($val); return true; }))->will($this->returnSelf()); $this->_uri->expects($this->any())->method('getQuery')->will($this->returnCallback([$this, 'getResult'])); $this->_req->expects($this->any())->method('withUri')->will($this->returnSelf()); } 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); } public function testRouterGeneratesRouteParameters() { /** @var $controller WsServerInterface */ $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); /** @var $matcher UrlMatcherInterface */ $this->_matcher->expects($this->any())->method('match')->will( $this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux']) ); $conn = $this->getMock('Ratchet\Mock\Connection'); $router = new Router($this->_matcher); $router->onOpen($conn, $this->_req); $this->assertEquals('foo=bar&baz=qux', $this->_req->getUri()->getQuery()); } public function testQueryParams() { $controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock(); $this->_matcher->expects($this->any())->method('match')->will( $this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux']) ); $conn = $this->getMock('Ratchet\Mock\Connection'); $request = $this->getMock('Psr\Http\Message\RequestInterface'); $uri = new \GuzzleHttp\Psr7\Uri('ws://doesnt.matter/endpoint?hello=world&foo=nope'); $request->expects($this->any())->method('getUri')->will($this->returnCallback(function() use (&$uri) { return $uri; })); $request->expects($this->any())->method('withUri')->with($this->callback(function($url) use (&$uri) { $uri = $url; return true; }))->will($this->returnSelf()); $router = new Router($this->_matcher); $router->onOpen($conn, $request); $this->assertEquals('foo=nope&baz=qux&hello=world', $request->getUri()->getQuery()); $this->assertEquals('ws', $request->getUri()->getScheme()); $this->assertEquals('doesnt.matter', $request->getUri()->getHost()); } public function testImpatientClientOverflow() { $this->_conn->expects($this->once())->method('close'); $header = "GET /nope HTTP/1.1 Upgrade: websocket Connection: upgrade Host: localhost Origin: http://localhost Sec-WebSocket-Version: 13\r\n\r\n"; $app = new HttpServer(new Router(new UrlMatcher(new RouteCollection, new RequestContext))); $app->onOpen($this->_conn); $app->onMessage($this->_conn, $header); $app->onMessage($this->_conn, 'Silly body'); } } Ratchet-0.4.1/tests/unit/Server/000077500000000000000000000000001321362363300164775ustar00rootroot00000000000000Ratchet-0.4.1/tests/unit/Server/EchoServerTest.php000066400000000000000000000014421321362363300221160ustar00rootroot00000000000000_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(); } } Ratchet-0.4.1/tests/unit/Server/FlashPolicyComponentTest.php000066400000000000000000000113331321362363300241510ustar00rootroot00000000000000_policy = new FlashPolicy(); } public function testPolicyRender() { $this->_policy->setSiteControl('all'); $this->_policy->addAllowedAccess('example.com', '*'); $this->_policy->addAllowedAccess('dev.example.com', '*'); $this->assertInstanceOf('SimpleXMLElement', $this->_policy->renderPolicy()); } public function testInvalidPolicyReader() { $this->setExpectedException('UnexpectedValueException'); $this->_policy->renderPolicy(); } public function testInvalidDomainPolicyReader() { $this->setExpectedException('UnexpectedValueException'); $this->_policy->setSiteControl('all'); $this->_policy->addAllowedAccess('dev.example.*', '*'); $this->_policy->renderPolicy(); } /** * @dataProvider siteControl */ public function testSiteControlValidation($accept, $permittedCrossDomainPolicies) { $this->assertEquals($accept, $this->_policy->validateSiteControl($permittedCrossDomainPolicies)); } public static function siteControl() { return array( array(true, 'all') , array(true, 'none') , array(true, 'master-only') , array(false, 'by-content-type') , array(false, 'by-ftp-filename') , array(false, '') , array(false, 'all ') , array(false, 'asdf') , array(false, '@893830') , array(false, '*') ); } /** * @dataProvider URI */ public function testDomainValidation($accept, $domain) { $this->assertEquals($accept, $this->_policy->validateDomain($domain)); } public static function URI() { return array( array(true, '*') , array(true, 'example.com') , array(true, 'exam-ple.com') , array(true, '*.example.com') , array(true, 'www.example.com') , array(true, 'dev.dev.example.com') , array(true, 'http://example.com') , array(true, 'https://example.com') , array(true, 'http://*.example.com') , array(false, 'exam*ple.com') , array(true, '127.0.255.1') , array(true, 'localhost') , array(false, 'www.example.*') , array(false, 'www.exa*le.com') , array(false, 'www.example.*com') , array(false, '*.example.*') , array(false, 'gasldf*$#a0sdf0a8sdf') ); } /** * @dataProvider ports */ public function testPortValidation($accept, $ports) { $this->assertEquals($accept, $this->_policy->validatePorts($ports)); } public static function ports() { return array( array(true, '*') , array(true, '80') , array(true, '80,443') , array(true, '507,516-523') , array(true, '507,516-523,333') , array(true, '507,516-523,507,516-523') , array(false, '516-') , array(true, '516-523,11') , array(false, '516,-523,11') , array(false, 'example') , array(false, 'asdf,123') , array(false, '--') , array(false, ',,,') , array(false, '838*') ); } public function testAddAllowedAccessOnlyAcceptsValidPorts() { $this->setExpectedException('UnexpectedValueException'); $this->_policy->addAllowedAccess('*', 'nope'); } public function testSetSiteControlThrowsException() { $this->setExpectedException('UnexpectedValueException'); $this->_policy->setSiteControl('nope'); } public function testErrorClosesConnection() { $conn = $this->getMock('\\Ratchet\\ConnectionInterface'); $conn->expects($this->once())->method('close'); $this->_policy->onError($conn, new \Exception); } public function testOnMessageSendsString() { $this->_policy->addAllowedAccess('*', '*'); $conn = $this->getMock('\\Ratchet\\ConnectionInterface'); $conn->expects($this->once())->method('send')->with($this->isType('string')); $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); } } Ratchet-0.4.1/tests/unit/Server/IoConnectionTest.php000066400000000000000000000015431321362363300224420ustar00rootroot00000000000000sock = $this->getMock('\\React\\Socket\\ConnectionInterface'); $this->conn = new IoConnection($this->sock); } public function testCloseBubbles() { $this->sock->expects($this->once())->method('end'); $this->conn->close(); } public function testSendBubbles() { $msg = '6 hour rides are productive'; $this->sock->expects($this->once())->method('write')->with($msg); $this->conn->send($msg); } public function testSendReturnsSelf() { $this->assertSame($this->conn, $this->conn->send('fluent interface')); } } Ratchet-0.4.1/tests/unit/Server/IoServerTest.php000066400000000000000000000075451321362363300216210ustar00rootroot00000000000000app = $this->getMock('\\Ratchet\\MessageComponentInterface'); $loop = new StreamSelectLoop; $this->reactor = new Server(0, $loop); $uri = $this->reactor->getAddress(); $this->port = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_PORT); $this->server = new IoServer($this->app, $this->reactor, $loop); } public function testOnOpen() { $this->app->expects($this->once())->method('onOpen')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface')); $client = stream_socket_client("tcp://localhost:{$this->port}"); $this->server->loop->tick(); //$this->assertTrue(is_string($this->app->last['onOpen'][0]->remoteAddress)); //$this->assertTrue(is_int($this->app->last['onOpen'][0]->resourceId)); } public function testOnData() { $msg = 'Hello World!'; $this->app->expects($this->once())->method('onMessage')->with( $this->isInstanceOf('\\Ratchet\\ConnectionInterface') , $msg ); $client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1); socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096); socket_set_block($client); socket_connect($client, 'localhost', $this->port); $this->server->loop->tick(); socket_write($client, $msg); $this->server->loop->tick(); socket_shutdown($client, 1); socket_shutdown($client, 0); socket_close($client); $this->server->loop->tick(); } public function testOnClose() { $this->app->expects($this->once())->method('onClose')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface')); $client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1); socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096); socket_set_block($client); socket_connect($client, 'localhost', $this->port); $this->server->loop->tick(); socket_shutdown($client, 1); socket_shutdown($client, 0); socket_close($client); $this->server->loop->tick(); } public function testFactory() { $this->assertInstanceOf('\\Ratchet\\Server\\IoServer', IoServer::factory($this->app, 0)); } public function testNoLoopProvidedError() { $this->setExpectedException('RuntimeException'); $io = new IoServer($this->app, $this->reactor); $io->run(); } public function testOnErrorPassesException() { $conn = $this->getMock('\\React\\Socket\\ConnectionInterface'); $conn->decor = $this->getMock('\\Ratchet\\ConnectionInterface'); $err = new \Exception("Nope"); $this->app->expects($this->once())->method('onError')->with($conn->decor, $err); $this->server->handleError($err, $conn); } public function onErrorCalledWhenExceptionThrown() { $this->markTestIncomplete("Need to learn how to throw an exception from a mock"); $conn = $this->getMock('\\React\\Socket\\ConnectionInterface'); $this->server->handleConnect($conn); $e = new \Exception; $this->app->expects($this->once())->method('onMessage')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'), 'f')->will($e); $this->app->expects($this->once())->method('onError')->with($this->instanceOf('\\Ratchet\\ConnectionInterface', $e)); $this->server->handleData('f', $conn); } } Ratchet-0.4.1/tests/unit/Server/IpBlackListComponentTest.php000066400000000000000000000067001321362363300240770ustar00rootroot00000000000000mock = $this->getMock('\\Ratchet\\MessageComponentInterface'); $this->blocker = new IpBlackList($this->mock); } public function testOnOpen() { $this->mock->expects($this->exactly(3))->method('onOpen'); $conn1 = $this->newConn(); $conn2 = $this->newConn(); $conn3 = $this->newConn(); $this->blocker->onOpen($conn1); $this->blocker->onOpen($conn3); $this->blocker->onOpen($conn2); } public function testBlockDoesNotTriggerOnOpen() { $conn = $this->newConn(); $this->blocker->blockAddress($conn->remoteAddress); $this->mock->expects($this->never())->method('onOpen'); $ret = $this->blocker->onOpen($conn); } public function testBlockDoesNotTriggerOnClose() { $conn = $this->newConn(); $this->blocker->blockAddress($conn->remoteAddress); $this->mock->expects($this->never())->method('onClose'); $ret = $this->blocker->onOpen($conn); } public function testOnMessageDecoration() { $conn = $this->newConn(); $msg = 'Hello not being blocked'; $this->mock->expects($this->once())->method('onMessage')->with($conn, $msg); $this->blocker->onMessage($conn, $msg); } public function testOnCloseDecoration() { $conn = $this->newConn(); $this->mock->expects($this->once())->method('onClose')->with($conn); $this->blocker->onClose($conn); } public function testBlockClosesConnection() { $conn = $this->newConn(); $this->blocker->blockAddress($conn->remoteAddress); $conn->expects($this->once())->method('close'); $this->blocker->onOpen($conn); } public function testAddAndRemoveWithFluentInterfaces() { $blockOne = '127.0.0.1'; $blockTwo = '192.168.1.1'; $unblock = '75.119.207.140'; $this->blocker ->blockAddress($unblock) ->blockAddress($blockOne) ->unblockAddress($unblock) ->blockAddress($blockTwo) ; $this->assertEquals(array($blockOne, $blockTwo), $this->blocker->getBlockedAddresses()); } public function testDecoratorPassesErrors() { $conn = $this->newConn(); $e = new \Exception('I threw an error'); $this->mock->expects($this->once())->method('onError')->with($conn, $e); $this->blocker->onError($conn, $e); } public function addressProvider() { return array( array('127.0.0.1', '127.0.0.1') , array('localhost', 'localhost') , array('fe80::1%lo0', 'fe80::1%lo0') , array('127.0.0.1', '127.0.0.1:6392') ); } /** * @dataProvider addressProvider */ public function testFilterAddress($expected, $input) { $this->assertEquals($expected, $this->blocker->filterAddress($input)); } public function testUnblockingSilentlyFails() { $this->assertInstanceOf('\\Ratchet\\Server\\IpBlackList', $this->blocker->unblockAddress('localhost')); } protected function newConn() { $conn = $this->getMock('\\Ratchet\\ConnectionInterface'); $conn->remoteAddress = '127.0.0.1'; return $conn; } } Ratchet-0.4.1/tests/unit/Session/000077500000000000000000000000001321362363300166545ustar00rootroot00000000000000Ratchet-0.4.1/tests/unit/Session/Serialize/000077500000000000000000000000001321362363300206035ustar00rootroot00000000000000Ratchet-0.4.1/tests/unit/Session/Serialize/PhpHandlerTest.php000066400000000000000000000022251321362363300242020ustar00rootroot00000000000000_handler = new PhpHandler; } public function serializedProvider() { return array( array( '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}' , array( '_sf2_attributes' => array( 'hello' => 'world' , 'last' => 1332872102 ) , '_sf2_flashes' => array() ) ) ); } /** * @dataProvider serializedProvider */ public function testUnserialize($in, $expected) { $this->assertEquals($expected, $this->_handler->unserialize($in)); } /** * @dataProvider serializedProvider */ public function testSerialize($serialized, $original) { $this->assertEquals($serialized, $this->_handler->serialize($original)); } } Ratchet-0.4.1/tests/unit/Session/SessionComponentTest.php000066400000000000000000000113211321362363300235310ustar00rootroot00000000000000markTestSkipped('Dependency of Symfony HttpFoundation failed'); } parent::setUp(); $this->_serv = new SessionProvider($this->_app, new NullSessionHandler); } public function tearDown() { ini_set('session.serialize_handler', 'php'); } public function getConnectionClassString() { return '\Ratchet\ConnectionInterface'; } public function getDecoratorClassString() { return '\Ratchet\NullComponent'; } public function getComponentClassString() { return '\Ratchet\Http\HttpServerInterface'; } public function classCaseProvider() { return array( array('php', 'Php') , array('php_binary', 'PhpBinary') ); } /** * @dataProvider classCaseProvider */ public function testToClassCase($in, $out) { $ref = new \ReflectionClass('\\Ratchet\\Session\\SessionProvider'); $method = $ref->getMethod('toClassCase'); $method->setAccessible(true); $component = new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface')); $this->assertEquals($out, $method->invokeArgs($component, array($in))); } /** * I think I have severely butchered this test...it's not so much of a unit test as it is a full-fledged component test */ public function testConnectionValueFromPdo() { if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { return $this->markTestSkipped('Session test requires PDO and pdo_sqlite'); } $sessionId = md5('testSession'); $dbOptions = array( 'db_table' => 'sessions' , 'db_id_col' => 'sess_id' , 'db_data_col' => 'sess_data' , 'db_time_col' => 'sess_time' , 'db_lifetime_col' => 'sess_lifetime' ); $pdo = new \PDO("sqlite::memory:"); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $pdo->exec(vsprintf("CREATE TABLE %s (%s TEXT NOT NULL PRIMARY KEY, %s BLOB NOT NULL, %s INTEGER NOT NULL, %s INTEGER)", $dbOptions)); $pdoHandler = new PdoSessionHandler($pdo, $dbOptions); $pdoHandler->write($sessionId, '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'); $component = new SessionProvider($this->getMock($this->getComponentClassString()), $pdoHandler, array('auto_start' => 1)); $connection = $this->getMock('Ratchet\\ConnectionInterface'); $headers = $this->getMock('Psr\Http\Message\RequestInterface'); $headers->expects($this->once())->method('getHeader')->will($this->returnValue([ini_get('session.name') . "={$sessionId};"])); $component->onOpen($connection, $headers); $this->assertEquals('world', $connection->Session->get('hello')); } protected function newConn() { $conn = $this->getMock('Ratchet\ConnectionInterface'); $headers = $this->getMock('Psr\Http\Message\Request', array('getCookie'), array('POST', '/', array())); $headers->expects($this->once())->method('getCookie', array(ini_get('session.name')))->will($this->returnValue(null)); 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 testRejectInvalidSeralizers() { if (!function_exists('wddx_serialize_value')) { $this->markTestSkipped(); } ini_set('session.serialize_handler', 'wddx'); $this->setExpectedException('\RuntimeException'); new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface')); } protected function doOpen($conn) { $request = $this->getMock('Psr\Http\Message\RequestInterface'); $request->expects($this->any())->method('getHeader')->will($this->returnValue([])); $this->_serv->onOpen($conn, $request); } } Ratchet-0.4.1/tests/unit/Session/Storage/000077500000000000000000000000001321362363300202605ustar00rootroot00000000000000Ratchet-0.4.1/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php000066400000000000000000000033471321362363300265220ustar00rootroot00000000000000markTestSkipped('Session test requires PDO and pdo_sqlite'); } $schema = <<_pathToDB = tempnam(sys_get_temp_dir(), 'SQ3');; $dsn = 'sqlite:' . $this->_pathToDB; $pdo = new \PDO($dsn); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $pdo->exec($schema); $pdo = null; $sessionHandler = new PdoSessionHandler($dsn); $serializer = new PhpHandler(); $this->_virtualSessionStorage = new VirtualSessionStorage($sessionHandler, 'foobar', $serializer); $this->_virtualSessionStorage->registerBag(new FlashBag()); $this->_virtualSessionStorage->registerBag(new AttributeBag()); } public function tearDown() { unlink($this->_pathToDB); } public function testStartWithDSN() { $this->_virtualSessionStorage->start(); $this->assertTrue($this->_virtualSessionStorage->isStarted()); } } Ratchet-0.4.1/tests/unit/Wamp/000077500000000000000000000000001321362363300161355ustar00rootroot00000000000000Ratchet-0.4.1/tests/unit/Wamp/ServerProtocolTest.php000066400000000000000000000212511321362363300224770ustar00rootroot00000000000000_app = new TestComponent; $this->_comp = new ServerProtocol($this->_app); } protected function newConn() { return new Connection; } public function invalidMessageProvider() { return [ [0] , [3] , [4] , [8] , [9] ]; } /** * @dataProvider invalidMessageProvider */ public function testInvalidMessages($type) { $this->setExpectedException('\Ratchet\Wamp\Exception'); $conn = $this->newConn(); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode([$type])); } public function testWelcomeMessage() { $conn = $this->newConn(); $this->_comp->onOpen($conn); $message = $conn->last['send']; $json = json_decode($message); $this->assertEquals(4, count($json)); $this->assertEquals(0, $json[0]); $this->assertTrue(is_string($json[1])); $this->assertEquals(1, $json[2]); } public function testSubscribe() { $uri = 'http://example.com'; $clientMessage = array(5, $uri); $conn = $this->newConn(); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode($clientMessage)); $this->assertEquals($uri, $this->_app->last['onSubscribe'][1]); } public function testUnSubscribe() { $uri = 'http://example.com/endpoint'; $clientMessage = array(6, $uri); $conn = $this->newConn(); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode($clientMessage)); $this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]); } public function callProvider() { return [ [2, 'a', 'b'] , [2, ['a', 'b']] , [1, 'one'] , [3, 'one', 'two', 'three'] , [3, ['un', 'deux', 'trois']] , [2, 'hi', ['hello', 'world']] , [2, ['hello', 'world'], 'hi'] , [2, ['hello' => 'world', 'herp' => 'derp']] ]; } /** * @dataProvider callProvider */ public function testCall() { $args = func_get_args(); $paramNum = array_shift($args); $uri = 'http://example.com/endpoint/' . rand(1, 100); $id = uniqid('', false); $clientMessage = array_merge(array(2, $id, $uri), $args); $conn = $this->newConn(); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode($clientMessage)); $this->assertEquals($id, $this->_app->last['onCall'][1]); $this->assertEquals($uri, $this->_app->last['onCall'][2]); $this->assertEquals($paramNum, count($this->_app->last['onCall'][3])); } public function testPublish() { $conn = $this->newConn(); $topic = 'pubsubhubbub'; $event = 'Here I am, publishing data'; $clientMessage = array(7, $topic, $event); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode($clientMessage)); $this->assertEquals($topic, $this->_app->last['onPublish'][1]); $this->assertEquals($event, $this->_app->last['onPublish'][2]); $this->assertEquals(array(), $this->_app->last['onPublish'][3]); $this->assertEquals(array(), $this->_app->last['onPublish'][4]); } public function testPublishAndExcludeMe() { $conn = $this->newConn(); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', true))); $this->assertEquals($conn->WAMP->sessionId, $this->_app->last['onPublish'][3][0]); } public function testPublishAndEligible() { $conn = $this->newConn(); $buddy = uniqid('', false); $friend = uniqid('', false); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', false, array($buddy, $friend)))); $this->assertEquals(array(), $this->_app->last['onPublish'][3]); $this->assertEquals(2, count($this->_app->last['onPublish'][4])); } public function eventProvider() { return array( array('http://example.com', array('one', 'two')) , array('curie', array(array('hello' => 'world', 'herp' => 'derp'))) ); } /** * @dataProvider eventProvider */ public function testEvent($topic, $payload) { $conn = new WampConnection($this->newConn()); $conn->event($topic, $payload); $eventString = $conn->last['send']; $this->assertSame(array(8, $topic, $payload), json_decode($eventString, true)); } public function testOnClosePropagation() { $conn = new Connection; $this->_comp->onOpen($conn); $this->_comp->onClose($conn); $class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection'); $method = $class->getMethod('getConnection'); $method->setAccessible(true); $check = $method->invokeArgs($this->_app->last['onClose'][0], array()); $this->assertSame($conn, $check); } public function testOnErrorPropagation() { $conn = new Connection; $e = new \Exception('Nope'); $this->_comp->onOpen($conn); $this->_comp->onError($conn, $e); $class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection'); $method = $class->getMethod('getConnection'); $method->setAccessible(true); $check = $method->invokeArgs($this->_app->last['onError'][0], array()); $this->assertSame($conn, $check); $this->assertSame($e, $this->_app->last['onError'][1]); } public function testPrefix() { $conn = new WampConnection($this->newConn()); $this->_comp->onOpen($conn); $prefix = 'incoming'; $fullURI = "http://example.com/$prefix"; $method = 'call'; $this->_comp->onMessage($conn, json_encode(array(1, $prefix, $fullURI))); $this->assertEquals($fullURI, $conn->WAMP->prefixes[$prefix]); $this->assertEquals("$fullURI#$method", $conn->getUri("$prefix:$method")); } public function testMessageMustBeJson() { $this->setExpectedException('\\Ratchet\\Wamp\\JsonException'); $conn = new Connection; $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, 'Hello World!'); } public function testGetSubProtocolsReturnsArray() { $this->assertTrue(is_array($this->_comp->getSubProtocols())); } public function testGetSubProtocolsGetFromApp() { $this->_app->protocols = array('hello', 'world'); $this->assertGreaterThanOrEqual(3, count($this->_comp->getSubProtocols())); } public function testWampOnMessageApp() { $app = $this->getMock('\\Ratchet\\Wamp\\WampServerInterface'); $wamp = new ServerProtocol($app); $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('\Ratchet\Wamp\Exception'); $conn = $this->newConn(); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, $message); } public function testBadClientInputFromNonStringTopic() { $this->setExpectedException('\Ratchet\Wamp\Exception'); $conn = new WampConnection($this->newConn()); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode([5, ['hells', 'nope']])); } public function testBadPrefixWithNonStringTopic() { $this->setExpectedException('\Ratchet\Wamp\Exception'); $conn = new WampConnection($this->newConn()); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode([1, ['hells', 'nope'], ['bad', 'input']])); } public function testBadPublishWithNonStringTopic() { $this->setExpectedException('\Ratchet\Wamp\Exception'); $conn = new WampConnection($this->newConn()); $this->_comp->onOpen($conn); $this->_comp->onMessage($conn, json_encode([7, ['bad', 'input'], 'Hider'])); } } Ratchet-0.4.1/tests/unit/Wamp/TopicManagerTest.php000066400000000000000000000161341321362363300220640ustar00rootroot00000000000000conn = $this->getMock('\Ratchet\ConnectionInterface'); $this->mock = $this->getMock('\Ratchet\Wamp\WampServerInterface'); $this->mngr = new TopicManager($this->mock); $this->conn->WAMP = new \StdClass; $this->mngr->onOpen($this->conn); } public function testGetTopicReturnsTopicObject() { $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); } public function testGetTopicCreatesTopicWithSameName() { $name = 'The Topic'; $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); $topic = $method->invokeArgs($this->mngr, array($name)); $this->assertEquals($name, $topic->getId()); } public function testGetTopicReturnsSameObject() { $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); $topic = $method->invokeArgs($this->mngr, array('No copy')); $again = $method->invokeArgs($this->mngr, array('No copy')); $this->assertSame($topic, $again); } public function testOnOpen() { $this->mock->expects($this->once())->method('onOpen'); $this->mngr->onOpen($this->conn); } public function testOnCall() { $id = uniqid(); $this->mock->expects($this->once())->method('onCall')->with( $this->conn , $id , $this->isInstanceOf('Ratchet\Wamp\Topic') , array() ); $this->mngr->onCall($this->conn, $id, 'new topic', array()); } public function testOnSubscribeCreatesTopicObject() { $this->mock->expects($this->once())->method('onSubscribe')->with( $this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic') ); $this->mngr->onSubscribe($this->conn, 'new topic'); } public function testTopicIsInConnectionOnSubscribe() { $name = 'New Topic'; $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); $topic = $method->invokeArgs($this->mngr, array($name)); $this->mngr->onSubscribe($this->conn, $name); $this->assertTrue($this->conn->WAMP->subscriptions->contains($topic)); } public function testDoubleSubscriptionFiresOnce() { $this->mock->expects($this->exactly(1))->method('onSubscribe'); $this->mngr->onSubscribe($this->conn, 'same topic'); $this->mngr->onSubscribe($this->conn, 'same topic'); } public function testUnsubscribeEvent() { $name = 'in and out'; $this->mock->expects($this->once())->method('onUnsubscribe')->with( $this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic') ); $this->mngr->onSubscribe($this->conn, $name); $this->mngr->onUnsubscribe($this->conn, $name); } public function testUnsubscribeFiresOnce() { $name = 'getting sleepy'; $this->mock->expects($this->exactly(1))->method('onUnsubscribe'); $this->mngr->onSubscribe($this->conn, $name); $this->mngr->onUnsubscribe($this->conn, $name); $this->mngr->onUnsubscribe($this->conn, $name); } public function testUnsubscribeRemovesTopicFromConnection() { $name = 'Bye Bye Topic'; $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); $topic = $method->invokeArgs($this->mngr, array($name)); $this->mngr->onSubscribe($this->conn, $name); $this->mngr->onUnsubscribe($this->conn, $name); $this->assertFalse($this->conn->WAMP->subscriptions->contains($topic)); } public function testOnPublishBubbles() { $msg = 'Cover all the code!'; $this->mock->expects($this->once())->method('onPublish')->with( $this->conn , $this->isInstanceOf('Ratchet\Wamp\Topic') , $msg , $this->isType('array') , $this->isType('array') ); $this->mngr->onPublish($this->conn, 'topic coverage', $msg, array(), array()); } public function testOnCloseBubbles() { $this->mock->expects($this->once())->method('onClose')->with($this->conn); $this->mngr->onClose($this->conn); } protected function topicProvider($name) { $class = new \ReflectionClass('Ratchet\Wamp\TopicManager'); $method = $class->getMethod('getTopic'); $method->setAccessible(true); $attribute = $class->getProperty('topicLookup'); $attribute->setAccessible(true); $topic = $method->invokeArgs($this->mngr, array($name)); return array($topic, $attribute); } public function testConnIsRemovedFromTopicOnClose() { $name = 'State Testing'; list($topic, $attribute) = $this->topicProvider($name); $this->assertCount(1, $attribute->getValue($this->mngr)); $this->mngr->onSubscribe($this->conn, $name); $this->mngr->onClose($this->conn); $this->assertFalse($topic->has($this->conn)); } public static function topicConnExpectationProvider() { return [ [ 'onClose', 0] , ['onUnsubscribe', 0] ]; } /** * @dataProvider topicConnExpectationProvider */ public function testTopicRetentionFromLeavingConnections($methodCall, $expectation) { $topicName = 'checkTopic'; list($topic, $attribute) = $this->topicProvider($topicName); $this->mngr->onSubscribe($this->conn, $topicName); call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName)); $this->assertCount($expectation, $attribute->getValue($this->mngr)); } public function testOnErrorBubbles() { $e = new \Exception('All work and no play makes Chris a dull boy'); $this->mock->expects($this->once())->method('onError')->with($this->conn, $e); $this->mngr->onError($this->conn, $e); } public function testGetSubProtocolsReturnsArray() { $this->assertInternalType('array', $this->mngr->getSubProtocols()); } public function testGetSubProtocolsBubbles() { $subs = array('hello', 'world'); $app = $this->getMock('Ratchet\Wamp\Stub\WsWampServerInterface'); $app->expects($this->once())->method('getSubProtocols')->will($this->returnValue($subs)); $mngr = new TopicManager($app); $this->assertEquals($subs, $mngr->getSubProtocols()); } } Ratchet-0.4.1/tests/unit/Wamp/TopicTest.php000066400000000000000000000117731321362363300205750ustar00rootroot00000000000000assertEquals($id, $topic->getId()); } public function testAddAndCount() { $topic = new Topic('merp'); $topic->add($this->newConn()); $topic->add($this->newConn()); $topic->add($this->newConn()); $this->assertEquals(3, count($topic)); } public function testRemove() { $topic = new Topic('boop'); $tracked = $this->newConn(); $topic->add($this->newConn()); $topic->add($tracked); $topic->add($this->newConn()); $topic->remove($tracked); $this->assertEquals(2, count($topic)); } public function testBroadcast() { $msg = 'Hello World!'; $name = 'Batman'; $protocol = json_encode(array(8, $name, $msg)); $first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $first->expects($this->once()) ->method('send') ->with($this->equalTo($protocol)); $second->expects($this->once()) ->method('send') ->with($this->equalTo($protocol)); $topic = new Topic($name); $topic->add($first); $topic->add($second); $topic->broadcast($msg); } public function testBroadcastWithExclude() { $msg = 'Hello odd numbers'; $name = 'Excluding'; $protocol = json_encode(array(8, $name, $msg)); $first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $first->expects($this->once()) ->method('send') ->with($this->equalTo($protocol)); $second->expects($this->never())->method('send'); $third->expects($this->once()) ->method('send') ->with($this->equalTo($protocol)); $topic = new Topic($name); $topic->add($first); $topic->add($second); $topic->add($third); $topic->broadcast($msg, array($second->WAMP->sessionId)); } public function testBroadcastWithEligible() { $msg = 'Hello white list'; $name = 'Eligible'; $protocol = json_encode(array(8, $name, $msg)); $first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface'))); $first->expects($this->once()) ->method('send') ->with($this->equalTo($protocol)); $second->expects($this->never())->method('send'); $third->expects($this->once()) ->method('send') ->with($this->equalTo($protocol)); $topic = new Topic($name); $topic->add($first); $topic->add($second); $topic->add($third); $topic->broadcast($msg, array(), array($first->WAMP->sessionId, $third->WAMP->sessionId)); } public function testIterator() { $first = $this->newConn(); $second = $this->newConn(); $third = $this->newConn(); $topic = new Topic('Joker'); $topic->add($first)->add($second)->add($third); $check = array($first, $second, $third); foreach ($topic as $mock) { $this->assertNotSame(false, array_search($mock, $check)); } } public function testToString() { $name = 'Bane'; $topic = new Topic($name); $this->assertEquals($name, (string)$topic); } public function testDoesHave() { $conn = $this->newConn(); $topic = new Topic('Two Face'); $topic->add($conn); $this->assertTrue($topic->has($conn)); } public function testDoesNotHave() { $conn = $this->newConn(); $topic = new Topic('Alfred'); $this->assertFalse($topic->has($conn)); } public function testDoesNotHaveAfterRemove() { $conn = $this->newConn(); $topic = new Topic('Ras'); $topic->add($conn)->remove($conn); $this->assertFalse($topic->has($conn)); } protected function newConn() { return new WampConnection($this->getMock('\\Ratchet\\ConnectionInterface')); } } Ratchet-0.4.1/tests/unit/Wamp/WampConnectionTest.php000066400000000000000000000044061321362363300224360ustar00rootroot00000000000000mock = $this->getMock('\\Ratchet\\ConnectionInterface'); $this->conn = new WampConnection($this->mock); } public function testCallResult() { $callId = uniqid(); $data = array('hello' => 'world', 'herp' => 'derp'); $this->mock->expects($this->once())->method('send')->with(json_encode(array(3, $callId, $data))); $this->conn->callResult($callId, $data); } public function testCallError() { $callId = uniqid(); $uri = 'http://example.com/end/point'; $this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, ''))); $this->conn->callError($callId, $uri); } public function testCallErrorWithTopic() { $callId = uniqid(); $uri = 'http://example.com/end/point'; $this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, ''))); $this->conn->callError($callId, new Topic($uri)); } public function testDetailedCallError() { $callId = uniqid(); $uri = 'http://example.com/end/point'; $desc = 'beep boop beep'; $detail = 'Error: Too much awesome'; $this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, $desc, $detail))); $this->conn->callError($callId, $uri, $desc, $detail); } public function testPrefix() { $shortOut = 'outgoing'; $longOut = 'http://example.com/outgoing'; $this->mock->expects($this->once())->method('send')->with(json_encode(array(1, $shortOut, $longOut))); $this->conn->prefix($shortOut, $longOut); } public function testGetUriWhenNoCurieGiven() { $uri = 'http://example.com/noshort'; $this->assertEquals($uri, $this->conn->getUri($uri)); } public function testClose() { $mock = $this->getMock('\\Ratchet\\ConnectionInterface'); $conn = new WampConnection($mock); $mock->expects($this->once())->method('close'); $conn->close(); } } Ratchet-0.4.1/tests/unit/Wamp/WampServerTest.php000066400000000000000000000030271321362363300216030ustar00rootroot00000000000000_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))); } public function testGetSubProtocols() { // 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'))); } }