getSocket(); if ($socket instanceof Socket) { $endPoint = $socket->getEndPoint(); } } $this->setEndPoint($endPoint); if (null === $response) { $response = new Http\Response(false); } $this->setResponse($response); return; } /** * Connect, i.e. open the connection and do handshake. * * @return void */ public function connect() { return $this->doHandshake(); } /** * Override the parent run() method to open the connection. * * @return void */ public function run() { $this->connect(); return parent::run(); } /** * Receive data. Fire listeners. * * @return void */ public function receive() { $connection = $this->getConnection(); $node = $connection->getCurrentNode(); do { $this->_run($node); } while ( false === $connection->isDisconnected() && true !== $node->isMessageComplete() ); } /** * Try the handshake by trying different protocol implementation. * * @return void * @throws \Hoa\Websocket\Exception * @throws \Hoa\Websocket\Exception\BadProtocol */ protected function doHandshake() { $connection = $this->getConnection(); $connection->connect(); if (true === $connection->getSocket()->isSecured()) { $connection->enableEncryption(true, $connection::ENCRYPTION_TLS); } $connection->setStreamBlocking(true); $key = $this->getNewChallenge(); $expected = base64_encode(sha1($key . Protocol\Rfc6455::GUID, true)); if (null === $host = $this->getHost()) { throw new Exception( 'Host name is null. Please, use the %s::setHost() method.', 0, __CLASS__ ); } $connection->writeAll( $request = 'GET ' . $this->getEndPoint() . ' HTTP/1.1' . CRLF . 'Host: ' . $host . CRLF . 'User-Agent: Hoa' . CRLF . 'Upgrade: WebSocket' . CRLF . 'Connection: Upgrade' . CRLF . 'Pragma: no-cache' . CRLF . 'Cache-Control: no-cache' . CRLF . 'Sec-WebSocket-Key: ' . $key . CRLF . 'Sec-WebSocket-Version: 13' . CRLF . CRLF ); $buffer = $connection->read(2048); $response = $this->getResponse(); $response->parse($buffer); if ($response::STATUS_SWITCHING_PROTOCOLS !== $response['status'] || 'websocket' !== strtolower($response['upgrade']) || 'upgrade' !== strtolower($response['connection']) || $expected !== $response['sec-websocket-accept']) { throw new Exception\BadProtocol( 'Handshake has failed, the server did not return a valid ' . 'response.' . "\n\n" . 'Client:' . "\n" . ' %s' . "\n" . 'Server:' . "\n" . ' %s', 0, [ str_replace("\n", "\n" . ' ', $request), str_replace("\n", "\n" . ' ', $buffer) ] ); } $currentNode = $connection->getCurrentNode(); $currentNode->setHandshake(SUCCEED); $currentNode->setProtocolImplementation(new Protocol\Rfc6455($connection)); $this->getListener()->fire( 'open', new Event\Bucket() ); return; } /** * Generate a challenge. * * @return string */ public function getNewChallenge() { static $_tail = ['A', 'Q', 'g', 'w']; return substr(base64_encode(Consistency::uuid()), 0, 21) . $_tail[mt_rand(0, 3)] . '=='; } /** * Close a specific node/connection. * * @param int $code Code (please, see * self::CLOSE_* constants). * @param string $reason Reason. * @return void */ public function close($code = self::CLOSE_NORMAL, $reason = null) { $connection = $this->getConnection(); $protocol = $connection->getCurrentNode()->getProtocolImplementation(); if (null !== $protocol) { $protocol->close($code, $reason, true); } $connection->mute(); $connection->setStreamTimeout(0, 2 * 15000); // 2 * MLS (on FreeBSD) $connection->read(1); return $connection->disconnect(); } /** * Set end-point. * * @param string $endPoint End-point. * @return string */ protected function setEndPoint($endPoint) { $old = $this->_endPoint; $this->_endPoint = $endPoint; return $old; } /** * Get end-point. * * @return string */ public function getEndPoint() { return $this->_endPoint; } /** * Set response (mainly parser). * * @param \Hoa\Http\Response $response Response. * @return \Hoa\Http\Response */ public function setResponse(Http\Response $response) { $old = $this->_response; $this->_response = $response; return $old; } /** * Get response. * * @return \Hoa\Http\Response */ public function getResponse() { return $this->_response; } /** * Set host. * * @param string $host Host. * @return string */ public function setHost($host) { $old = $this->_host; $this->_host = $host; return $old; } /** * Get host. * * @return string */ public function getHost() { return null !== $this->_host ? $this->_host : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null); } }