private function readLoop() : Generator { try { while ($this->state === ConnectionState::OPEN) { $frame = (yield $this->transport->read()); $this->heartbeatsSinceFrameReceived = 0; if ($frame instanceof HeartbeatFrame) { continue; } elseif ($frame->frameChannelId > 0) { $channelManager = $this->channels[$frame->frameChannelId] ?? null; if ($channelManager === null) { throw ProtocolException::unexpectedFrame($frame, \sprintf('channel #%s is closed', $frame->frameChannelId)); } (yield $channelManager->dispatch($frame)); } elseif ($frame instanceof ConnectionCloseFrame) { (yield $this->transport->close()); throw AmqpException::create($frame->replyText, $frame->replyCode); } else { throw ProtocolException::unexpectedFrame($frame, 'received on channel #0'); } } while ($this->state === ConnectionState::CLOSING) { $frame = (yield $this->transport->read()); if ($frame instanceof ConnectionCloseOkFrame) { (yield $this->transport->close()); (yield $this->onClose()); } } } catch (ChannelClosedException $e) { (yield $this->onClose(SingleConnectionException::closedUnexpectedly($this->options, $e))); } catch (Throwable $e) { assert(Debug::dumpException('read-loop', $e)); (yield $this->onClose($e)); } }
/** * @recoil-coroutine */ public function onCloseByBroker(ChannelCloseFrame $frame) : Generator { $this->connectionManager->removeChannel($this->channelId); $exception = AmqpException::create($frame->replyText, $frame->replyCode); // The closure was triggered by a previously sent frame. Find the // offending strand and resume it with an exception specific to this // closure ... if ($frame->classId && $frame->methodId) { $sentMethod = $frame->classId << 16 | $frame->methodId; ($callers =& $this->callersBySentMethod[$sentMethod]) ?? null; if ($callers !== null) { foreach ($callers as $id => $waitMethod) { $strand = $this->callers[$waitMethod][$id]; unset($this->callers[$waitMethod][$id], $this->callersBySentMethod[$sentMethod][$id], $this->callersByWaitMethod[$waitMethod][$id]); (yield Recoil::throw($strand, $exception)); break; } } } // Acknowledge the closure ... $frame = new ChannelCloseOkFrame(); $frame->frameChannelId = $this->channelId; (yield $this->transport->write($frame)); // Procee to the regular on-close logic. To all remaining callers and // listeners this is an unexpected closure ... (yield $this->onClose(ChannelException::closedUnexpectedly($this->channelId, $exception))); }