diff options
author | marvin-borner@live.com | 2018-04-10 21:50:16 +0200 |
---|---|---|
committer | marvin-borner@live.com | 2018-04-10 21:54:48 +0200 |
commit | fc9401f04a3aca5abb22f87ebc210de8afe11d32 (patch) | |
tree | b0b310f3581764ec3955f4e496a05137a32951c3 /assets/php/vendor/cboden | |
parent | 286d643180672f20526f3dc3bd19d7b751e2fa97 (diff) |
Initial Commit
Diffstat (limited to 'assets/php/vendor/cboden')
77 files changed, 5191 insertions, 0 deletions
diff --git a/assets/php/vendor/cboden/ratchet/.gitignore b/assets/php/vendor/cboden/ratchet/.gitignore new file mode 100644 index 0000000..793ef58 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/.gitignore @@ -0,0 +1,5 @@ +phpunit.xml
+reports
+sandbox
+vendor
+composer.lock
\ No newline at end of file diff --git a/assets/php/vendor/cboden/ratchet/.travis.yml b/assets/php/vendor/cboden/ratchet/.travis.yml new file mode 100644 index 0000000..6c0dc15 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/.travis.yml @@ -0,0 +1,20 @@ +language: 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 diff --git a/assets/php/vendor/cboden/ratchet/CHANGELOG.md b/assets/php/vendor/cboden/ratchet/CHANGELOG.md new file mode 100644 index 0000000..5169993 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/CHANGELOG.md @@ -0,0 +1,135 @@ +CHANGELOG +========= + +### 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 diff --git a/assets/php/vendor/cboden/ratchet/LICENSE b/assets/php/vendor/cboden/ratchet/LICENSE new file mode 100644 index 0000000..24abba1 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/LICENSE @@ -0,0 +1,19 @@ +Copyright (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. diff --git a/assets/php/vendor/cboden/ratchet/Makefile b/assets/php/vendor/cboden/ratchet/Makefile new file mode 100644 index 0000000..a2526c0 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/Makefile @@ -0,0 +1,42 @@ +# 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 \ diff --git a/assets/php/vendor/cboden/ratchet/README.md b/assets/php/vendor/cboden/ratchet/README.md new file mode 100644 index 0000000..3f1a345 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/README.md @@ -0,0 +1,83 @@ +# Ratchet + +[](http://travis-ci.org/ratchetphp/Ratchet) +[](http://socketo.me/reports/ab/index.html) +[](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 +<?php +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; + + // Make sure composer dependencies have been installed + require __DIR__ . '/vendor/autoload.php'; + +/** + * chat.php + * Send any incoming messages to all connected clients (except sender) + */ +class MyChat implements MessageComponentInterface { + protected $clients; + + public function __construct() { + $this->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!'); }; +``` diff --git a/assets/php/vendor/cboden/ratchet/composer.json b/assets/php/vendor/cboden/ratchet/composer.json new file mode 100644 index 0000000..9529618 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/composer.json @@ -0,0 +1,36 @@ +{ + "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" + } +} diff --git a/assets/php/vendor/cboden/ratchet/phpunit.xml.dist b/assets/php/vendor/cboden/ratchet/phpunit.xml.dist new file mode 100644 index 0000000..0cc5451 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/phpunit.xml.dist @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit + forceCoversAnnotation="true" + mapTestClassNameToCoveredClassName="true" + bootstrap="tests/bootstrap.php" + colors="true" + backupGlobals="false" + backupStaticAttributes="false" + syntaxCheck="false" + stopOnError="false" +> + + <testsuites> + <testsuite name="unit"> + <directory>./tests/unit/</directory> + </testsuite> + </testsuites> + + <testsuites> + <testsuite name="integration"> + <directory>./tests/integration/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory>./src/</directory> + </whitelist> + </filter> +</phpunit>
\ No newline at end of file diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php new file mode 100644 index 0000000..9707951 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php @@ -0,0 +1,41 @@ +<?php +namespace Ratchet; + +/** + * Wraps ConnectionInterface objects via the decorator pattern but allows + * parameters to bubble through with magic methods + * @todo It sure would be nice if I could make most of this a trait... + */ +abstract class AbstractConnectionDecorator implements ConnectionInterface { + /** + * @var ConnectionInterface + */ + protected $wrappedConn; + + public function __construct(ConnectionInterface $conn) { + $this->wrappedConn = $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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/App.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/App.php new file mode 100644 index 0000000..f378534 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/App.php @@ -0,0 +1,145 @@ +<?php +namespace Ratchet; +use React\EventLoop\LoopInterface; +use React\EventLoop\Factory as LoopFactory; +use React\Socket\Server as Reactor; +use React\Socket\SecureServer as SecureReactor; +use Ratchet\Http\HttpServerInterface; +use Ratchet\Http\OriginCheck; +use Ratchet\Wamp\WampServerInterface; +use Ratchet\Server\IoServer; +use Ratchet\Server\FlashPolicy; +use Ratchet\Http\HttpServer; +use Ratchet\Http\Router; +use Ratchet\WebSocket\WsServer; +use Ratchet\Wamp\WampServer; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Matcher\UrlMatcher; + +/** + * An opinionated facade class to quickly and easily create a WebSocket server. + * A few configuration assumptions are made and some best-practice security conventions are applied by default. + */ +class App { + /** + * @var \Symfony\Component\Routing\RouteCollection + */ + public $routes; + + /** + * @var \Ratchet\Server\IoServer + */ + public $flashServer; + + /** + * @var \Ratchet\Server\IoServer + */ + protected $_server; + + /** + * The Host passed in construct used for same origin policy + * @var string + */ + protected $httpHost; + + /*** + * The port the socket is listening + * @var int + */ + protected $port; + + /** + * @var int + */ + protected $_routeCounter = 0; + + /** + * @param string $httpHost HTTP hostname clients intend to connect to. MUST match JS `new WebSocket('ws://$httpHost');` + * @param int $port Port to listen on. If 80, assuming production, Flash on 843 otherwise expecting Flash to be proxied through 8843 + * @param string $address IP address to bind to. Default is localhost/proxy only. '0.0.0.0' for any machine. + * @param LoopInterface $loop Specific React\EventLoop to bind the application to. null will create one for you. + */ + public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) { + if (extension_loaded('xdebug')) { + trigger_error('XDebug extension detected. Remember to disable this if performance testing or going live!', E_USER_WARNING); + } + + if (null === $loop) { + $loop = LoopFactory::create(); + } + + $this->httpHost = $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(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php new file mode 100644 index 0000000..37e41b1 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php @@ -0,0 +1,31 @@ +<?php +namespace Ratchet; + +/** + * This is the interface to build a Ratchet application with. + * It implements the decorator pattern to build an application stack + */ +interface ComponentInterface { + /** + * When a new connection is opened it will be passed to this method + * @param ConnectionInterface $conn The socket/connection that just connected to your application + * @throws \Exception + */ + function onOpen(ConnectionInterface $conn); + + /** + * This is called before or after a socket is closed (depends on how it's closed). SendMessage to $conn will not result in an error if it has already been closed. + * @param ConnectionInterface $conn The socket/connection that is closing/closed + * @throws \Exception + */ + function onClose(ConnectionInterface $conn); + + /** + * If there is an error with one of the sockets, or somewhere in the application where an Exception is thrown, + * the Exception is sent back down the stack, handled by the Server and bubbled back up the application through this method + * @param ConnectionInterface $conn + * @param \Exception $e + * @throws \Exception + */ + function onError(ConnectionInterface $conn, \Exception $e); +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/ConnectionInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/ConnectionInterface.php new file mode 100644 index 0000000..26fb8a4 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/ConnectionInterface.php @@ -0,0 +1,26 @@ +<?php +namespace Ratchet; + +/** + * The version of Ratchet being used + * @var string + */ +const VERSION = 'Ratchet/0.4.1'; + +/** + * A proxy object representing a connection to the application + * This acts as a container to store data (in memory) about the connection + */ +interface ConnectionInterface { + /** + * Send data to the connection + * @param string $data + * @return \Ratchet\ConnectionInterface + */ + function send($data); + + /** + * Close the connection + */ + function close(); +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/CloseResponseTrait.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/CloseResponseTrait.php new file mode 100644 index 0000000..abdf5c4 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/CloseResponseTrait.php @@ -0,0 +1,22 @@ +<?php +namespace Ratchet\Http; +use Ratchet\ConnectionInterface; +use GuzzleHttp\Psr7 as gPsr; +use GuzzleHttp\Psr7\Response; + +trait CloseResponseTrait { + /** + * Close a connection with an HTTP response + * @param \Ratchet\ConnectionInterface $conn + * @param int $code HTTP status code + * @return null + */ + private function close(ConnectionInterface $conn, $code = 400, array $additional_headers = []) { + $response = new Response($code, array_merge([ + 'X-Powered-By' => \Ratchet\VERSION + ], $additional_headers)); + + $conn->send(gPsr\str($response)); + $conn->close(); + } +}
\ No newline at end of file diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php new file mode 100644 index 0000000..9c44114 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php @@ -0,0 +1,64 @@ +<?php +namespace Ratchet\Http; +use Ratchet\MessageInterface; +use Ratchet\ConnectionInterface; +use GuzzleHttp\Psr7 as gPsr; + +/** + * This class receives streaming data from a client request + * and parses HTTP headers, returning a PSR-7 Request object + * once it's been buffered + */ +class HttpRequestParser implements MessageInterface { + const EOM = "\r\n\r\n"; + + /** + * The maximum number of bytes the request can be + * This is a security measure to prevent attacks + * @var int + */ + public $maxSize = 4096; + + /** + * @param \Ratchet\ConnectionInterface $context + * @param string $data Data stream to buffer + * @return \Psr\Http\Message\RequestInterface + * @throws \OverflowException If the message buffer has become too large + */ + public function onMessage(ConnectionInterface $context, $data) { + if (!isset($context->httpBuffer)) { + $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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php new file mode 100644 index 0000000..bbd8d53 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php @@ -0,0 +1,76 @@ +<?php +namespace Ratchet\Http; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; + +class HttpServer implements MessageComponentInterface { + use CloseResponseTrait; + + /** + * Buffers incoming HTTP requests returning a Guzzle Request when coalesced + * @var HttpRequestParser + * @note May not expose this in the future, may do through facade methods + */ + protected $_reqParser; + + /** + * @var \Ratchet\Http\HttpServerInterface + */ + protected $_httpServer; + + /** + * @param HttpServerInterface + */ + public function __construct(HttpServerInterface $component) { + $this->_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); + } + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php new file mode 100644 index 0000000..2c37c49 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php @@ -0,0 +1,14 @@ +<?php +namespace Ratchet\Http; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; +use Psr\Http\Message\RequestInterface; + +interface HttpServerInterface extends MessageComponentInterface { + /** + * @param \Ratchet\ConnectionInterface $conn + * @param \Psr\Http\Message\RequestInterface $request null is default because PHP won't let me overload; don't pass null!!! + * @throws \UnexpectedValueException if a RequestInterface is not passed + */ + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null); +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/NoOpHttpServerController.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/NoOpHttpServerController.php new file mode 100644 index 0000000..4f72e66 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/NoOpHttpServerController.php @@ -0,0 +1,18 @@ +<?php +namespace Ratchet\Http; +use Ratchet\ConnectionInterface; +use Psr\Http\Message\RequestInterface; + +class NoOpHttpServerController implements HttpServerInterface { + public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) { + } + + public function onMessage(ConnectionInterface $from, $msg) { + } + + public function onClose(ConnectionInterface $conn) { + } + + public function onError(ConnectionInterface $conn, \Exception $e) { + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/OriginCheck.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/OriginCheck.php new file mode 100644 index 0000000..2bdc0f7 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/OriginCheck.php @@ -0,0 +1,65 @@ +<?php +namespace Ratchet\Http; +use Ratchet\ConnectionInterface; +use Ratchet\MessageComponentInterface; +use Psr\Http\Message\RequestInterface; + +/** + * A middleware to ensure JavaScript clients connecting are from the expected domain. + * This protects other websites from open WebSocket connections to your application. + * Note: This can be spoofed from non-web browser clients + */ +class OriginCheck implements HttpServerInterface { + use CloseResponseTrait; + + /** + * @var \Ratchet\MessageComponentInterface + */ + protected $_component; + + public $allowedOrigins = []; + + /** + * @param MessageComponentInterface $component Component/Application to decorate + * @param array $allowed An array of allowed domains that are allowed to connect from + */ + public function __construct(MessageComponentInterface $component, array $allowed = []) { + $this->_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); + } +}
\ No newline at end of file diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/Router.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/Router.php new file mode 100644 index 0000000..df7fe82 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Http/Router.php @@ -0,0 +1,96 @@ +<?php +namespace Ratchet\Http; +use Ratchet\ConnectionInterface; +use Psr\Http\Message\RequestInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use GuzzleHttp\Psr7 as gPsr; + +class Router implements HttpServerInterface { + use CloseResponseTrait; + + /** + * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface + */ + protected $_matcher; + + private $_noopController; + + public function __construct(UrlMatcherInterface $matcher) { + $this->_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); + } + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php new file mode 100644 index 0000000..b4a92e2 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php @@ -0,0 +1,5 @@ +<?php +namespace Ratchet; + +interface MessageComponentInterface extends ComponentInterface, MessageInterface { +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/MessageInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/MessageInterface.php new file mode 100644 index 0000000..1486323 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/MessageInterface.php @@ -0,0 +1,12 @@ +<?php +namespace Ratchet; + +interface MessageInterface { + /** + * Triggered when a client sends data through the socket + * @param \Ratchet\ConnectionInterface $from The socket/connection that sent the message to your application + * @param string $msg The message received + * @throws \Exception + */ + function onMessage(ConnectionInterface $from, $msg); +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/EchoServer.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/EchoServer.php new file mode 100644 index 0000000..2918e73 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/EchoServer.php @@ -0,0 +1,23 @@ +<?php +namespace Ratchet\Server; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; + +/** + * A simple Ratchet application that will reply to all messages with the message it received + */ +class EchoServer implements MessageComponentInterface { + public function onOpen(ConnectionInterface $conn) { + } + + public function onMessage(ConnectionInterface $from, $msg) { + $from->send($msg); + } + + public function onClose(ConnectionInterface $conn) { + } + + public function onError(ConnectionInterface $conn, \Exception $e) { + $conn->close(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php new file mode 100644 index 0000000..4a1b8bd --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php @@ -0,0 +1,200 @@ +<?php +namespace Ratchet\Server; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; + +/** + * An app to go on a server stack to pass a policy file to a Flash socket + * Useful if you're using Flash as a WebSocket polyfill on IE + * Be sure to run your server instance on port 843 + * By default this lets accepts everything, make sure you tighten the rules up for production + * @final + * @link http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html + * @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1 + * @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd + */ +class FlashPolicy implements MessageComponentInterface { + + /** + * Contains the root policy node + * @var string + */ + protected $_policy = '<?xml version="1.0"?><!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy></cross-domain-policy>'; + + /** + * 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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php new file mode 100644 index 0000000..9f864bb --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php @@ -0,0 +1,38 @@ +<?php +namespace Ratchet\Server; +use Ratchet\ConnectionInterface; +use React\Socket\ConnectionInterface as ReactConn; + +/** + * {@inheritdoc} + */ +class IoConnection implements ConnectionInterface { + /** + * @var \React\Socket\ConnectionInterface + */ + protected $conn; + + + /** + * @param \React\Socket\ConnectionInterface $conn + */ + public function __construct(ReactConn $conn) { + $this->conn = $conn; + } + + /** + * {@inheritdoc} + */ + public function send($data) { + $this->conn->write($data); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function close() { + $this->conn->end(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php new file mode 100644 index 0000000..b3fb7e0 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php @@ -0,0 +1,140 @@ +<?php +namespace Ratchet\Server; +use Ratchet\MessageComponentInterface; +use React\EventLoop\LoopInterface; +use React\Socket\ServerInterface; +use React\EventLoop\Factory as LoopFactory; +use React\Socket\Server as Reactor; +use React\Socket\SecureServer as SecureReactor; + +/** + * Creates an open-ended socket to listen on a port for incoming connections. + * Events are delegated through this to attached applications + */ +class IoServer { + /** + * @var \React\EventLoop\LoopInterface + */ + public $loop; + + /** + * @var \Ratchet\MessageComponentInterface + */ + public $app; + + /** + * The socket server the Ratchet Application is run off of + * @var \React\Socket\ServerInterface + */ + public $socket; + + /** + * @param \Ratchet\MessageComponentInterface $app The Ratchet application stack to host + * @param \React\Socket\ServerInterface $socket The React socket server to run the Ratchet application off of + * @param \React\EventLoop\LoopInterface|null $loop The React looper to run the Ratchet application off of + */ + public function __construct(MessageComponentInterface $app, ServerInterface $socket, LoopInterface $loop = null) { + if (false === strpos(PHP_VERSION, "hiphop")) { + gc_enable(); + } + + set_time_limit(0); + ob_implicit_flush(); + + $this->loop = $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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php new file mode 100644 index 0000000..9342254 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php @@ -0,0 +1,111 @@ +<?php +namespace Ratchet\Server; +use Ratchet\MessageComponentInterface; +use Ratchet\ConnectionInterface; + +class IpBlackList implements MessageComponentInterface { + /** + * @var array + */ + protected $_blacklist = array(); + + /** + * @var \Ratchet\MessageComponentInterface + */ + protected $_decorating; + + /** + * @param \Ratchet\MessageComponentInterface $component + */ + public function __construct(MessageComponentInterface $component) { + $this->_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); + } + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php new file mode 100644 index 0000000..b83635f --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php @@ -0,0 +1,16 @@ +<?php +namespace Ratchet\Session\Serialize; + +interface HandlerInterface { + /** + * @param array + * @return string + */ + function serialize(array $data); + + /** + * @param string + * @return array + */ + function unserialize($raw); +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpBinaryHandler.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpBinaryHandler.php new file mode 100644 index 0000000..ba80551 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpBinaryHandler.php @@ -0,0 +1,33 @@ +<?php +namespace Ratchet\Session\Serialize; + +class PhpBinaryHandler implements HandlerInterface { + /** + * {@inheritdoc} + */ + function serialize(array $data) { + throw new \RuntimeException("Serialize PhpHandler:serialize code not written yet, write me!"); + } + + /** + * {@inheritdoc} + * @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net + */ + public function unserialize($raw) { + $returnData = array(); + $offset = 0; + + while ($offset < strlen($raw)) { + $num = ord($raw[$offset]); + $offset += 1; + $varname = substr($raw, $offset, $num); + $offset += $num; + $data = unserialize(substr($raw, $offset)); + + $returnData[$varname] = $data; + $offset += strlen(serialize($data)); + } + + return $returnData; + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpHandler.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpHandler.php new file mode 100644 index 0000000..b1df356 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpHandler.php @@ -0,0 +1,49 @@ +<?php +namespace Ratchet\Session\Serialize; + +class PhpHandler implements HandlerInterface { + /** + * Simply reverse behaviour of unserialize method. + * {@inheritdoc} + */ + function serialize(array $data) { + $preSerialized = array(); + $serialized = ''; + + if (count($data)) { + foreach ($data as $bucket => $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; + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php new file mode 100644 index 0000000..44276c5 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php @@ -0,0 +1,243 @@ +<?php +namespace Ratchet\Session; +use Ratchet\ConnectionInterface; +use Ratchet\Http\HttpServerInterface; +use Psr\Http\Message\RequestInterface; +use Ratchet\Session\Storage\VirtualSessionStorage; +use Ratchet\Session\Serialize\HandlerInterface; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; + +/** + * This component will allow access to session data from your website for each user connected + * Symfony HttpFoundation is required for this component to work + * Your website must also use Symfony HttpFoundation Sessions to read your sites session data + * If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer) + */ +class SessionProvider implements HttpServerInterface { + /** + * @var \Ratchet\MessageComponentInterface + */ + protected $_app; + + /** + * Selected handler storage assigned by the developer + * @var \SessionHandlerInterface + */ + protected $_handler; + + /** + * Null storage handler if no previous session was found + * @var \SessionHandlerInterface + */ + protected $_null; + + /** + * @var \Ratchet\Session\Serialize\HandlerInterface + */ + protected $_serializer; + + /** + * @param \Ratchet\Http\HttpServerInterface $app + * @param \SessionHandlerInterface $handler + * @param array $options + * @param \Ratchet\Session\Serialize\HandlerInterface $serializer + * @throws \RuntimeException + */ + public function __construct(HttpServerInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) { + $this->_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; + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php new file mode 100644 index 0000000..b478d03 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php @@ -0,0 +1,54 @@ +<?php +namespace Ratchet\Session\Storage\Proxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +class VirtualProxy extends SessionHandlerProxy { + /** + * @var string + */ + protected $_sessionId; + + /** + * @var string + */ + protected $_sessionName; + + /** + * {@inheritdoc} + */ + public function __construct(\SessionHandlerInterface $handler) { + parent::__construct($handler); + + $this->saveHandlerName = '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"); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php new file mode 100644 index 0000000..daa10bb --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php @@ -0,0 +1,88 @@ +<?php +namespace Ratchet\Session\Storage; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Ratchet\Session\Storage\Proxy\VirtualProxy; +use Ratchet\Session\Serialize\HandlerInterface; + +class VirtualSessionStorage extends NativeSessionStorage { + /** + * @var \Ratchet\Session\Serialize\HandlerInterface + */ + protected $_serializer; + + /** + * @param \SessionHandlerInterface $handler + * @param string $sessionId The ID of the session to retrieve + * @param \Ratchet\Session\Serialize\HandlerInterface $serializer + */ + public function __construct(\SessionHandlerInterface $handler, $sessionId, HandlerInterface $serializer) { + $this->setSaveHandler($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; + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php new file mode 100644 index 0000000..6c824da --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php @@ -0,0 +1,5 @@ +<?php +namespace Ratchet\Wamp; + +class Exception extends \Exception { +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/JsonException.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/JsonException.php new file mode 100644 index 0000000..8f05d28 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/JsonException.php @@ -0,0 +1,31 @@ +<?php +namespace Ratchet\Wamp; + +class JsonException extends Exception { + public function __construct() { + $code = json_last_error(); + + switch ($code) { + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + break; + } + + parent::__construct($msg, $code); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/ServerProtocol.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/ServerProtocol.php new file mode 100644 index 0000000..2d6d799 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/ServerProtocol.php @@ -0,0 +1,161 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\MessageComponentInterface; +use Ratchet\WebSocket\WsServerInterface; +use Ratchet\ConnectionInterface; + +/** + * WebSocket Application Messaging Protocol + * + * @link http://wamp.ws/spec + * @link https://github.com/oberstet/autobahn-js + * + * +--------------+----+------------------+ + * | Message Type | ID | DIRECTION | + * |--------------+----+------------------+ + * | WELCOME | 0 | Server-to-Client | + * | PREFIX | 1 | Bi-Directional | + * | CALL | 2 | Client-to-Server | + * | CALL RESULT | 3 | Server-to-Client | + * | CALL ERROR | 4 | Server-to-Client | + * | SUBSCRIBE | 5 | Client-to-Server | + * | UNSUBSCRIBE | 6 | Client-to-Server | + * | PUBLISH | 7 | Client-to-Server | + * | EVENT | 8 | Server-to-Client | + * +--------------+----+------------------+ + */ +class ServerProtocol implements MessageComponentInterface, WsServerInterface { + const MSG_WELCOME = 0; + const MSG_PREFIX = 1; + const MSG_CALL = 2; + const MSG_CALL_RESULT = 3; + const MSG_CALL_ERROR = 4; + const MSG_SUBSCRIBE = 5; + const MSG_UNSUBSCRIBE = 6; + const MSG_PUBLISH = 7; + const MSG_EVENT = 8; + + /** + * @var WampServerInterface + */ + protected $_decorating; + + /** + * @var \SplObjectStorage + */ + protected $connections; + + /** + * @param WampServerInterface $serverComponent An class to propagate calls through + */ + public function __construct(WampServerInterface $serverComponent) { + $this->_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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php new file mode 100644 index 0000000..bca8f67 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php @@ -0,0 +1,99 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\ConnectionInterface; + +/** + * A topic/channel containing connections that have subscribed to it + */ +class Topic implements \IteratorAggregate, \Countable { + private $id; + + private $subscribers; + + /** + * @param string $topicId Unique ID for this object + */ + public function __construct($topicId) { + $this->id = $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(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php new file mode 100644 index 0000000..dd06ada --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php @@ -0,0 +1,125 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\ConnectionInterface; +use Ratchet\WebSocket\WsServerInterface; + +class TopicManager implements WsServerInterface, WampServerInterface { + /** + * @var WampServerInterface + */ + protected $app; + + /** + * @var array + */ + protected $topicLookup = array(); + + public function __construct(WampServerInterface $app) { + $this->app = $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()]); + } + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php new file mode 100644 index 0000000..dda1e4e --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php @@ -0,0 +1,115 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\ConnectionInterface; +use Ratchet\AbstractConnectionDecorator; +use Ratchet\Wamp\ServerProtocol as WAMP; + +/** + * A ConnectionInterface object wrapper that is passed to your WAMP application + * representing a client. Methods on this Connection are therefore different. + * @property \stdClass $WAMP + */ +class WampConnection extends AbstractConnectionDecorator { + /** + * {@inheritdoc} + */ + public function __construct(ConnectionInterface $conn) { + parent::__construct($conn); + + $this->WAMP = 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); + } + +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php new file mode 100644 index 0000000..5d710aa --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php @@ -0,0 +1,67 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\MessageComponentInterface; +use Ratchet\WebSocket\WsServerInterface; +use Ratchet\ConnectionInterface; + +/** + * Enable support for the official WAMP sub-protocol in your application + * WAMP allows for Pub/Sub and RPC + * @link http://wamp.ws The WAMP specification + * @link https://github.com/oberstet/autobahn-js Souce for client side library + * @link http://autobahn.s3.amazonaws.com/js/autobahn.min.js Minified client side library + */ +class WampServer implements MessageComponentInterface, WsServerInterface { + /** + * @var ServerProtocol + */ + protected $wampProtocol; + + /** + * This class just makes it 1 step easier to use Topic objects in WAMP + * If you're looking at the source code, look in the __construct of this + * class and use that to make your application instead of using this + */ + public function __construct(WampServerInterface $app) { + $this->wampProtocol = 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(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php new file mode 100644 index 0000000..15c521d --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php @@ -0,0 +1,43 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\ComponentInterface; +use Ratchet\ConnectionInterface; + +/** + * An extension of Ratchet\ComponentInterface to server a WAMP application + * onMessage is replaced by various types of messages for this protocol (pub/sub or rpc) + */ +interface WampServerInterface extends ComponentInterface { + /** + * An RPC call has been received + * @param \Ratchet\ConnectionInterface $conn + * @param string $id The unique ID of the RPC, required to respond to + * @param string|Topic $topic The topic to execute the call against + * @param array $params Call parameters received from the client + */ + function onCall(ConnectionInterface $conn, $id, $topic, array $params); + + /** + * A request to subscribe to a topic has been made + * @param \Ratchet\ConnectionInterface $conn + * @param string|Topic $topic The topic to subscribe to + */ + function onSubscribe(ConnectionInterface $conn, $topic); + + /** + * A request to unsubscribe from a topic has been made + * @param \Ratchet\ConnectionInterface $conn + * @param string|Topic $topic The topic to unsubscribe from + */ + function onUnSubscribe(ConnectionInterface $conn, $topic); + + /** + * A client is attempting to publish content to a subscribed connections on a URI + * @param \Ratchet\ConnectionInterface $conn + * @param string|Topic $topic The topic the user has attempted to publish to + * @param string $event Payload of the 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) + */ + function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible); +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/ConnContext.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/ConnContext.php new file mode 100644 index 0000000..2eba782 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/ConnContext.php @@ -0,0 +1,20 @@ +<?php +namespace Ratchet\WebSocket; +use Ratchet\RFC6455\Messaging\MessageBuffer; + +class ConnContext { + /** + * @var \Ratchet\WebSocket\WsConnection + */ + public $connection; + + /** + * @var \Ratchet\RFC6455\Messaging\MessageBuffer; + */ + public $buffer; + + public function __construct(WsConnection $conn, MessageBuffer $buffer) { + $this->connection = $conn; + $this->buffer = $buffer; + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php new file mode 100644 index 0000000..b5c094e --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php @@ -0,0 +1,8 @@ +<?php +namespace Ratchet\WebSocket; +use Ratchet\ConnectionInterface; +use Ratchet\RFC6455\Messaging\MessageInterface; + +interface MessageCallableInterface { + public function onMessage(ConnectionInterface $conn, MessageInterface $msg); +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageComponentInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageComponentInterface.php new file mode 100644 index 0000000..fccd4e6 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageComponentInterface.php @@ -0,0 +1,6 @@ +<?php +namespace Ratchet\WebSocket; +use Ratchet\ComponentInterface; + +interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface { +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsConnection.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsConnection.php new file mode 100644 index 0000000..d2d04ef --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsConnection.php @@ -0,0 +1,45 @@ +<?php +namespace Ratchet\WebSocket; +use Ratchet\AbstractConnectionDecorator; +use Ratchet\RFC6455\Messaging\DataInterface; +use Ratchet\RFC6455\Messaging\Frame; + +/** + * {@inheritdoc} + * @property \StdClass $WebSocket + */ +class WsConnection extends AbstractConnectionDecorator { + /** + * {@inheritdoc} + */ + public function send($msg) { + if (!$this->WebSocket->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; + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php new file mode 100644 index 0000000..8030604 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php @@ -0,0 +1,225 @@ +<?php +namespace Ratchet\WebSocket; +use Ratchet\ComponentInterface; +use Ratchet\ConnectionInterface; +use Ratchet\MessageComponentInterface as DataComponentInterface; +use Ratchet\Http\HttpServerInterface; +use Ratchet\Http\CloseResponseTrait; +use Psr\Http\Message\RequestInterface; +use Ratchet\RFC6455\Messaging\MessageInterface; +use Ratchet\RFC6455\Messaging\FrameInterface; +use Ratchet\RFC6455\Messaging\Frame; +use Ratchet\RFC6455\Messaging\MessageBuffer; +use Ratchet\RFC6455\Messaging\CloseFrameChecker; +use Ratchet\RFC6455\Handshake\ServerNegotiator; +use Ratchet\RFC6455\Handshake\RequestVerifier; +use React\EventLoop\LoopInterface; +use GuzzleHttp\Psr7 as gPsr; + +/** + * The adapter to handle WebSocket requests/responses + * This is a mediator between the Server and your application to handle real-time messaging through a web browser + * @link http://ca.php.net/manual/en/ref.http.php + * @link http://dev.w3.org/html5/websockets/ + */ +class WsServer implements HttpServerInterface { + use CloseResponseTrait; + + /** + * Decorated component + * @var \Ratchet\ComponentInterface + */ + private $delegate; + + /** + * @var \SplObjectStorage + */ + protected $connections; + + /** + * @var \Ratchet\RFC6455\Messaging\CloseFrameChecker + */ + private $closeFrameChecker; + + /** + * @var \Ratchet\RFC6455\Handshake\ServerNegotiator + */ + private $handshakeNegotiator; + + /** + * @var \Closure + */ + private $ueFlowFactory; + + /** + * @var \Closure + */ + private $pongReceiver; + + /** + * @var \Closure + */ + private $msgCb; + + /** + * @param \Ratchet\WebSocket\MessageComponentInterface|\Ratchet\MessageComponentInterface $component Your application to run with WebSockets + * @note If you want to enable sub-protocols have your component implement WsServerInterface as well + */ + public function __construct(ComponentInterface $component) { + if ($component instanceof MessageComponentInterface) { + $this->msgCb = 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); + } + }); + } +} diff --git a/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php new file mode 100644 index 0000000..15d1f7b --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php @@ -0,0 +1,14 @@ +<?php +namespace Ratchet\WebSocket; + +/** + * WebSocket Server Interface + */ +interface WsServerInterface { + /** + * If any component in a stack supports a WebSocket sub-protocol return each supported in an array + * @return array + * @todo This method may be removed in future version (note that will not break code, just make some code obsolete) + */ + function getSubProtocols(); +} diff --git a/assets/php/vendor/cboden/ratchet/tests/autobahn/bin/fuzzingserver.php b/assets/php/vendor/cboden/ratchet/tests/autobahn/bin/fuzzingserver.php new file mode 100644 index 0000000..66d3704 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/autobahn/bin/fuzzingserver.php @@ -0,0 +1,36 @@ +<?php +use Ratchet\ConnectionInterface; + + require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; + +class BinaryEcho implements \Ratchet\WebSocket\MessageComponentInterface { + public function onMessage(ConnectionInterface $from, \Ratchet\RFC6455\Messaging\MessageInterface $msg) { + $from->send($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(); diff --git a/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json b/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json new file mode 100644 index 0000000..0494cf3 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json @@ -0,0 +1,15 @@ +{ + "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": {} +} diff --git a/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json b/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json new file mode 100644 index 0000000..e81a9fd --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json @@ -0,0 +1,12 @@ +{ + "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": {} +} diff --git a/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json b/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json new file mode 100644 index 0000000..c92e805 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json @@ -0,0 +1,12 @@ +{ + "options": {"failByDrop": false} + , "outdir": "reports/rfc" + + , "servers": [ + {"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}} + ] + + , "cases": ["*"] + , "exclude-cases": [] + , "exclude-agent-cases": {} +} diff --git a/assets/php/vendor/cboden/ratchet/tests/bootstrap.php b/assets/php/vendor/cboden/ratchet/tests/bootstrap.php new file mode 100644 index 0000000..40791ba --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/bootstrap.php @@ -0,0 +1,4 @@ +<?php + + $loader = require __DIR__ . '/../vendor/autoload.php'; + $loader->addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet'); diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php new file mode 100644 index 0000000..8c298e5 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php @@ -0,0 +1,50 @@ +<?php +namespace Ratchet; + +abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestCase { + protected $_app; + protected $_serv; + protected $_conn; + + abstract public function getConnectionClassString(); + abstract public function getDecoratorClassString(); + abstract public function getComponentClassString(); + + public function setUp() { + $this->_app = $this->getMock($this->getComponentClassString()); + $decorator = $this->getDecoratorClassString(); + $this->_serv = new $decorator($this->_app); + $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); + + $this->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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php new file mode 100644 index 0000000..e152988 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php @@ -0,0 +1,35 @@ +<?php +namespace Ratchet\Mock; +use Ratchet\MessageComponentInterface; +use Ratchet\WebSocket\WsServerInterface; +use Ratchet\ConnectionInterface; + +class Component implements MessageComponentInterface, WsServerInterface { + public $last = array(); + + public $protocols = array(); + + public function __construct(ComponentInterface $app = null) { + $this->last[__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; + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php new file mode 100644 index 0000000..5918296 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php @@ -0,0 +1,20 @@ +<?php +namespace Ratchet\Mock; +use Ratchet\ConnectionInterface; + +class Connection implements ConnectionInterface { + public $last = array( + 'send' => '' + , 'close' => false + ); + + public $remoteAddress = '127.0.0.1'; + + public function send($data) { + $this->last[__FUNCTION__] = $data; + } + + public function close() { + $this->last[__FUNCTION__] = true; + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php new file mode 100644 index 0000000..5570c07 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php @@ -0,0 +1,22 @@ +<?php +namespace Ratchet\Mock; +use Ratchet\AbstractConnectionDecorator; + +class ConnectionDecorator extends AbstractConnectionDecorator { + public $last = array( + 'write' => '' + , 'end' => false + ); + + public function send($data) { + $this->last[__FUNCTION__] = $data; + + $this->getConnection()->send($data); + } + + public function close() { + $this->last[__FUNCTION__] = true; + + $this->getConnection()->close(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php new file mode 100644 index 0000000..cd526cb --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php @@ -0,0 +1,43 @@ +<?php +namespace Ratchet\Mock; +use Ratchet\Wamp\WampServerInterface; +use Ratchet\WebSocket\WsServerInterface; +use Ratchet\ConnectionInterface; + +class WampComponent implements WampServerInterface, WsServerInterface { + public $last = array(); + + public $protocols = array(); + + public function getSubProtocols() { + return $this->protocols; + } + + 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(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php new file mode 100644 index 0000000..90def21 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php @@ -0,0 +1,28 @@ +<?php +namespace Ratchet; +use Ratchet\ConnectionInterface; +use Ratchet\MessageComponentInterface; +use Ratchet\WebSocket\WsServerInterface; +use Ratchet\Wamp\WampServerInterface; + +class NullComponent implements MessageComponentInterface, WsServerInterface, WampServerInterface { + public function onOpen(ConnectionInterface $conn) {} + + public function onMessage(ConnectionInterface $conn, $msg) {} + + public function onClose(ConnectionInterface $conn) {} + + public function onError(ConnectionInterface $conn, \Exception $e) {} + + public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {} + + public function onSubscribe(ConnectionInterface $conn, $topic) {} + + public function onUnSubscribe(ConnectionInterface $conn, $topic) {} + + public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude = array(), array $eligible = array()) {} + + public function getSubProtocols() { + return array(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php new file mode 100644 index 0000000..197bbd3 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php @@ -0,0 +1,7 @@ +<?php +namespace Ratchet\Wamp\Stub; +use Ratchet\WebSocket\WsServerInterface; +use Ratchet\Wamp\WampServerInterface; + +interface WsWampServerInterface extends WsServerInterface, WampServerInterface { +} diff --git a/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php new file mode 100644 index 0000000..ef88325 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php @@ -0,0 +1,7 @@ +<?php +namespace Ratchet\WebSocket\Stub; +use Ratchet\MessageComponentInterface; +use Ratchet\WebSocket\WsServerInterface; + +interface WsMessageComponentInterface extends MessageComponentInterface, WsServerInterface { +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/AbstractConnectionDecoratorTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/AbstractConnectionDecoratorTest.php new file mode 100644 index 0000000..0887d3e --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/AbstractConnectionDecoratorTest.php @@ -0,0 +1,147 @@ +<?php +namespace Ratchet; +use Ratchet\Mock\ConnectionDecorator; + +/** + * @covers Ratchet\AbstractConnectionDecorator + * @covers Ratchet\ConnectionInterface + */ +class AbstractConnectionDecoratorTest extends \PHPUnit_Framework_TestCase { + protected $mock; + protected $l1; + protected $l2; + + public function setUp() { + $this->mock = $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; + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php new file mode 100644 index 0000000..6af8402 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php @@ -0,0 +1,50 @@ +<?php +namespace Ratchet\Http; + +/** + * @covers Ratchet\Http\HttpRequestParser + */ +class HttpRequestParserTest extends \PHPUnit_Framework_TestCase { + protected $parser; + + public function setUp() { + $this->parser = 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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php new file mode 100644 index 0000000..7041d66 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php @@ -0,0 +1,64 @@ +<?php +namespace Ratchet\Http; +use Ratchet\AbstractMessageComponentTestCase; + +/** + * @covers Ratchet\Http\HttpServer + */ +class HttpServerTest extends AbstractMessageComponentTestCase { + public function setUp() { + parent::setUp(); + $this->_conn->httpHeadersReceived = true; + } + + public function getConnectionClassString() { + return '\Ratchet\ConnectionInterface'; + } + + public function getDecoratorClassString() { + return '\Ratchet\Http\HttpServer'; + } + + public function getComponentClassString() { + return '\Ratchet\Http\HttpServerInterface'; + } + + public function testOpen() { + $headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n"; + + $this->_conn->httpHeadersReceived = false; + $this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection()); + $this->_serv->onMessage($this->_conn, $headers); + } + + 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"); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php new file mode 100644 index 0000000..c1c4012 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php @@ -0,0 +1,46 @@ +<?php +namespace Ratchet\Http; +use Ratchet\AbstractMessageComponentTestCase; + +/** + * @covers Ratchet\Http\OriginCheck + */ +class OriginCheckTest extends AbstractMessageComponentTestCase { + protected $_reqStub; + + public function setUp() { + $this->_reqStub = $this->getMock('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!'); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Http/RouterTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Http/RouterTest.php new file mode 100644 index 0000000..1ca4cbc --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Http/RouterTest.php @@ -0,0 +1,165 @@ +<?php +namespace Ratchet\Http; +use Ratchet\WebSocket\WsServerInterface; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Matcher\UrlMatcher; + + +/** + * @covers Ratchet\Http\Router + */ +class RouterTest extends \PHPUnit_Framework_TestCase { + protected $_router; + protected $_matcher; + protected $_conn; + protected $_uri; + protected $_req; + + public function setUp() { + $this->_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'); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php new file mode 100644 index 0000000..47fb0e2 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php @@ -0,0 +1,26 @@ +<?php +namespace Ratchet\Server; +use Ratchet\Server\EchoServer; + +class EchoServerTest extends \PHPUnit_Framework_TestCase { + protected $_conn; + protected $_comp; + + public function setUp() { + $this->_conn = $this->getMock('\Ratchet\ConnectionInterface'); + $this->_comp = new EchoServer; + } + + public function testMessageEchod() { + $message = 'Tillsonburg, my back still aches when I hear that word.'; + $this->_conn->expects($this->once())->method('send')->with($message); + $this->_comp->onMessage($this->_conn, $message); + } + + public function testErrorClosesConnection() { + ob_start(); + $this->_conn->expects($this->once())->method('close'); + $this->_comp->onError($this->_conn, new \Exception); + ob_end_clean(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php new file mode 100644 index 0000000..38fc96a --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php @@ -0,0 +1,152 @@ +<?php +namespace Ratchet\Application\Server; +use Ratchet\Server\FlashPolicy; + +/** + * @covers Ratchet\Server\FlashPolicy + */ +class FlashPolicyTest extends \PHPUnit_Framework_TestCase { + + protected $_policy; + + public function setUp() { + $this->_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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php new file mode 100644 index 0000000..07130f6 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php @@ -0,0 +1,32 @@ +<?php +namespace Ratchet\Application\Server; +use Ratchet\Server\IoConnection; + +/** + * @covers Ratchet\Server\IoConnection + */ +class IoConnectionTest extends \PHPUnit_Framework_TestCase { + protected $sock; + protected $conn; + + public function setUp() { + $this->sock = $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')); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php new file mode 100644 index 0000000..284fbde --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php @@ -0,0 +1,118 @@ +<?php +namespace Ratchet\Server; +use Ratchet\Server\IoServer; +use React\EventLoop\StreamSelectLoop; +use React\Socket\Server; + +/** + * @covers Ratchet\Server\IoServer + */ +class IoServerTest extends \PHPUnit_Framework_TestCase { + protected $server; + + protected $app; + + protected $port; + + protected $reactor; + + public function setUp() { + $this->app = $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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php new file mode 100644 index 0000000..90f4185 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php @@ -0,0 +1,125 @@ +<?php +namespace Ratchet\Server; +use Ratchet\Server\IpBlackList; + +/** + * @covers Ratchet\Server\IpBlackList + */ +class IpBlackListTest extends \PHPUnit_Framework_TestCase { + protected $blocker; + protected $mock; + + public function setUp() { + $this->mock = $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; + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php new file mode 100644 index 0000000..4acf5bc --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php @@ -0,0 +1,43 @@ +<?php +namespace Ratchet\Session\Serialize; +use Ratchet\Session\Serialize\PhpHandler; + +/** + * @covers Ratchet\Session\Serialize\PhpHandler + */ +class PhpHandlerTest extends \PHPUnit_Framework_TestCase { + protected $_handler; + + public function setUp() { + $this->_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)); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php new file mode 100644 index 0000000..ebfdde4 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php @@ -0,0 +1,124 @@ +<?php +namespace Ratchet\Session; +use Ratchet\AbstractMessageComponentTestCase; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; + +/** + * @covers Ratchet\Session\SessionProvider + * @covers Ratchet\Session\Storage\VirtualSessionStorage + * @covers Ratchet\Session\Storage\Proxy\VirtualProxy + */ +class SessionProviderTest extends AbstractMessageComponentTestCase { + public function setUp() { + if (!class_exists('Symfony\Component\HttpFoundation\Session\Session')) { + return $this->markTestSkipped('Dependency of Symfony HttpFoundation failed'); + } + + parent::setUp(); + $this->_serv = new SessionProvider($this->_app, new NullSessionHandler); + } + + public function 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); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php new file mode 100644 index 0000000..2727484 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php @@ -0,0 +1,53 @@ +<?php +namespace Ratchet\Session\Storage; +use Ratchet\Session\Serialize\PhpHandler; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; + +class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase { + /** + * @var VirtualSessionStorage + */ + protected $_virtualSessionStorage; + + protected $_pathToDB; + + public function setUp() { + if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) { + return $this->markTestSkipped('Session test requires PDO and pdo_sqlite'); + } + + $schema = <<<SQL +CREATE TABLE `sessions` ( + `sess_id` VARBINARY(128) NOT NULL PRIMARY KEY, + `sess_data` BLOB NOT NULL, + `sess_time` INTEGER UNSIGNED NOT NULL, + `sess_lifetime` MEDIUMINT NOT NULL +); +SQL; + $this->_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()); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php new file mode 100644 index 0000000..8ff68c2 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php @@ -0,0 +1,295 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\Mock\Connection; +use Ratchet\Mock\WampComponent as TestComponent; + +/** + * @covers \Ratchet\Wamp\ServerProtocol + * @covers \Ratchet\Wamp\WampServerInterface + * @covers \Ratchet\Wamp\WampConnection + */ +class ServerProtocolTest extends \PHPUnit_Framework_TestCase { + protected $_comp; + + protected $_app; + + public function setUp() { + $this->_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'])); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php new file mode 100644 index 0000000..b21b6bc --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php @@ -0,0 +1,226 @@ +<?php +namespace Ratchet\Wamp; + +/** + * @covers Ratchet\Wamp\TopicManager + */ +class TopicManagerTest extends \PHPUnit_Framework_TestCase { + private $mock; + + /** + * @var \Ratchet\Wamp\TopicManager + */ + private $mngr; + + /** + * @var \Ratchet\ConnectionInterface + */ + private $conn; + + public function setUp() { + $this->conn = $this->getMock('\Ratchet\ConnectionInterface'); + $this->mock = $this->getMock('\Ratchet\Wamp\WampServerInterface'); + $this->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()); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php new file mode 100644 index 0000000..b8685b7 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php @@ -0,0 +1,164 @@ +<?php +namespace Ratchet\Wamp; + +/** + * @covers Ratchet\Wamp\Topic + */ +class TopicTest extends \PHPUnit_Framework_TestCase { + public function testGetId() { + $id = uniqid(); + $topic = new Topic($id); + + $this->assertEquals($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')); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php new file mode 100644 index 0000000..adf59d5 --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php @@ -0,0 +1,77 @@ +<?php +namespace Ratchet\Wamp; + +/** + * @covers Ratchet\Wamp\WampConnection + */ +class WampConnectionTest extends \PHPUnit_Framework_TestCase { + protected $conn; + protected $mock; + + public function setUp() { + $this->mock = $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(); + } +} diff --git a/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php new file mode 100644 index 0000000..626b1ce --- /dev/null +++ b/assets/php/vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php @@ -0,0 +1,49 @@ +<?php +namespace Ratchet\Wamp; +use Ratchet\AbstractMessageComponentTestCase; + +/** + * @covers Ratchet\Wamp\WampServer + */ +class WampServerTest extends AbstractMessageComponentTestCase { + public function getConnectionClassString() { + return '\Ratchet\Wamp\WampConnection'; + } + + public function getDecoratorClassString() { + return 'Ratchet\Wamp\WampServer'; + } + + public function getComponentClassString() { + return '\Ratchet\Wamp\WampServerInterface'; + } + + public function testOnMessageToEvent() { + $published = 'Client published this message'; + + $this->_app->expects($this->once())->method('onPublish')->with( + $this->isExpectedConnection() + , new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\Wamp\Topic') + , $published + , array() + , array() + ); + + $this->_serv->onMessage($this->_conn, json_encode(array(7, 'topic', $published))); + } + + 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'))); + } +} |