/** * {@inheritdoc} */ public function upgradeConnection(BufferedDuplexStreamInterface $socket, HttpRequest $request, HttpResponse $response, HttpEndpoint $endpoint, callable $action) : \Generator { $response = $this->assertUpgradePossible($request, $response); if ($response instanceof HttpResponse) { return $response; } // Discard HTTP request body before switching to web socket protocol. $body = (yield from $request->getBody()->getInputStream()); try { while (!$body->eof()) { yield from $body->read(); } } finally { $body->close(); } ((yield currentTask()))->setAutoShutdown(true); try { yield from $this->sendHandshake($socket, $request); if ($this->logger) { $this->logger->debug('Upgraded HTTP/{version} connection to {protocol}', ['version' => $request->getProtocolVersion(), 'protocol' => 'WebSocket']); } $client = new Client($socket, $this->logger); $result = $this->app->open($client); if ($result instanceof \Generator) { yield from $result; } $decoder = new MessageDecoder($socket, true, $this->logger); while (!$socket->eof()) { $message = (yield from $decoder->readNextMessage($client->id)); if ($message === false) { break; } $client->time = time(); if ($message instanceof Frame) { switch ($message->opcode) { case Frame::CONNECTION_CLOSE: break 2; case Frame::PING: yield from $socket->write((new Frame(Frame::PONG, $message->data))->encode(), 10000); break; } continue; } if (\is_string($message)) { $result = $this->app->onMessage($message, $client); } elseif ($message instanceof InputStreamInterface) { $result = $this->app->onBinaryMessage($message, $client); } if ($result instanceof \Generator) { $task = (yield runTask($result)); $task->onError(function (ExecutorInterface $executor, Task $task, \Throwable $e) { $this->app->onError($e); }); } } } catch (TaskCanceledException $e) { yield from $this->sendCloseFrame($socket, $e); $client->disconnect(); $result = $this->app->close($client); if ($result instanceof \Generator) { yield from $result; } } finally { $socket->close(); } }
protected function handleFrames() : \Generator { ((yield currentTask()))->setAutoShutdown(true); $decoder = new MessageDecoder($this->stream, false, $this->logger); try { while (true) { $message = (yield from $decoder->readNextMessage()); if ($message === false) { break; } if ($message instanceof Frame) { switch ($message->opcode) { case Frame::CONNECTION_CLOSE: break 2; case Frame::PING: yield from $this->stream->write((new Frame(Frame::PONG, $message->data))->encode(), 10000); break; } continue; } $this->events->emit(new MessageReceivedEvent($message, $this)); } } catch (TaskCanceledException $e) { $frame = new Frame(Frame::CONNECTION_CLOSE, pack('n', Frame::CODE_NORMAL_CLOSURE)); yield from $this->stream->write($frame->encode(random_bytes(4)), 100000); } finally { $this->watcher = NULL; $this->stream->close(); } }