/** * Coroutine that handles incoming WebSocket frames. */ protected function processIncomingFrames() : \Generator { $e = null; try { while (true) { $frame = (yield from $this->readNextFrame()); if ($frame->isControlFrame()) { if (!(yield from $this->handleControlFrame($frame))) { break; } } else { switch ($frame->opcode) { case Frame::TEXT: yield from $this->handleTextFrame($frame); break; case Frame::BINARY: yield from $this->handleBinaryFrame($frame); break; case Frame::CONTINUATION: yield from $this->handleContinuationFrame($frame); break; } } } } catch (\Throwable $e) { $this->messages->close($e); } finally { $this->messages->close(); try { foreach ($this->pings as $defer) { $defer->fail($e ?? new \RuntimeException('WebSocket connection closed')); } } finally { $this->pings = []; } try { if ($this->socket->isAlive()) { $reason = $e === null || $e->getCode() === 0 ? Frame::NORMAL_CLOSURE : $e->getCode(); (yield $this->writer->sendFrame(new Frame(Frame::CONNECTION_CLOSE, \pack('n', $reason)))); } } finally { if ($this->logger) { $this->logger->debug('WebSocket connection to {peer} closed', ['peer' => $this->socket->getRemoteAddress()]); } $this->socket->close(); } } }