8889841cWebSocket/ConnContext.php000066600000000656150441735170011427 0ustar00connection = $conn; $this->buffer = $buffer; } } WebSocket/WsServer.php000066600000016324150441735170010744 0ustar00msgCb = 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(Message::toString($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); } }); } } WebSocket/MessageCallableInterface.php000066600000000355150441735170014006 0ustar00WebSocket->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; } } Http/NoOpHttpServerController.php000066600000000740150441735170013156 0ustar00httpBuffer)) { $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 Message::parseRequest($headers); } } Http/HttpServer.php000066600000003631150441735170010320 0ustar00_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); } } } Http/OriginCheck.php000066600000003525150441735170010401 0ustar00_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); } }Http/Router.php000066600000005727150441735170007502 0ustar00_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, Query::parse($uri->getQuery() ?: '')); $request = $request->withUri($uri->withQuery(Query::build($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); } } } Http/HttpServerInterface.php000066600000001105150441735170012133 0ustar00 \Ratchet\VERSION ], $additional_headers)); $conn->send(Message::toString($response)); $conn->close(); } } ConnectionInterface.php000066600000001004150441735170011203 0ustar00wrappedConn = $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); } } App.php000066600000012621150441735170006012 0ustar00httpHost = $httpHost; $this->port = $port; $socket = new Reactor($address . ':' . $port, $loop, $context); $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 || $controller instanceof WsMessageComponentInterface) { $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(); } } Server/IoServer.php000066600000010612150441735170010274 0ustar00loop = $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); } } Server/IpBlackList.php000066600000005312150441735170010700 0ustar00_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); } } } Server/IoConnection.php000066600000001243150441735170011125 0ustar00conn = $conn; } /** * {@inheritdoc} */ public function send($data) { $this->conn->write($data); return $this; } /** * {@inheritdoc} */ public function close() { $this->conn->end(); } } Server/EchoServer.php000066600000001120150441735170010575 0ustar00send($msg); } public function onClose(ConnectionInterface $conn) { } public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } } Server/FlashPolicy.php000066600000014220150441735170010752 0ustar00'; /** * 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); } } Wamp/WampServer.php000066600000003502150441735170010267 0ustar00wampProtocol = 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(); } } Wamp/ServerProtocol.php000066600000011235150441735170011166 0ustar00_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); } } Wamp/TopicManager.php000066600000006030150441735170010544 0ustar00app = $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()]); } } } Wamp/Topic.php000066600000004604150441735170007256 0ustar00id = $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} */ #[\ReturnTypeWillChange] public function getIterator() { return $this->subscribers; } /** * {@inheritdoc} */ #[\ReturnTypeWillChange] public function count() { return $this->subscribers->count(); } } Wamp/WampServerInterface.php000066600000003662150441735170012117 0ustar00WAMP = 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); } } Wamp/Exception.php000066600000000106150441735170010127 0ustar00 $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; } } Session/Serialize/HandlerInterface.php000066600000000405150441735170014037 0ustar00setSaveHandler($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; } } Session/Storage/Proxy/VirtualProxy.php000066600000002070150441735170014127 0ustar00saveHandlerName = '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"); } } Session/SessionProvider.php000066600000020063150441735170012052 0ustar00_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; } } MessageInterface.php000066600000000622150441735170010475 0ustar00