/** * @recoil-coroutine */ private function connectSocket(ConnectionOptions $options) : Generator { $errorCode = null; $errorMessage = null; $stream = @stream_socket_client(\sprintf('tcp://%s:%s', $options->host(), $options->port()), $errorCode, $errorMessage, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT); if ($stream === false) { throw SingleConnectionException::couldNotConnect($options, $errorCode . ': ' . $errorMessage); } // Wait for the socket to open or fail ... stream_set_blocking($stream, false); list(, $writable) = (yield Recoil::select([$stream], [$stream])); if (empty($writable)) { throw SingleConnectionException::couldNotConnect($options, 'connection refused'); } return $stream; }
/** * Set the pre-fetch limits for this connection. * * @recoil-coroutine * * The pre-fetch count is the maximum number of unacknowledged messages that * are delivered to this connection. * * The pre-fetch limit is the maximum total size of unacknowledge messages * (in bytes) that is delivered to this connection. * * This feature is incompatible with the "per_consumer_qos" broker * capability. * * RabbitMQ does not support per-connection limits (current as of v3.5.6). * * @param int|null $count The pre-fetch count limit (null = unlimited). * @param int|null $size The pre-fetch size limit (null = unlimited). * @param Channel|null $channel The application-managed channel to use (null = auto-managed). * * @throws NotSupportedException The broker does not support this feature. * @throws ConnectionException The connection is closed. */ public function setQosLimits(int $count = null, int $size = null, Channel $channel = null) : Generator { assert($size === null || $size > 0); assert($count === null || $count > 0); if ($this->connectionManager === null) { throw SingleConnectionException::notOpen($this->options); } elseif ($this->features->perConsumerQos) { throw NotSupportedException::incompatibleCapability('per-connection QoS limits', 'per_consumer_qos'); } elseif ($size !== null && !$this->features->qosSizeLimits) { throw NotSupportedException::unsupportedFeature('pre-fetch size limits'); } $channelManager = (yield $this->connectionManager->acquireChannel($channel)); try { (yield $channelManager->call(BasicQosFrame::create($size ?: 0, $count ?: 0, true), BasicQosOkFrame::METHOD_ID)); } finally { if ($channel === null) { $channelManager->release(); } } }
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)); } }
/** * Perform the "open" phase of the handshake. * * @recoil-coroutine * * @param DuplexChannel $frames A channel over which frames are sent * and received. * @param ConnectionOptions $options The options used when establishing * the connection. */ private function open(DuplexChannel $frames, ConnectionOptions $options) : Generator { (yield $frames->write(ConnectionOpenFrame::create($options->vhost()))); try { $frame = (yield $frames->read()); } catch (ChannelClosedException $e) { throw SingleConnectionException::authorizationFailed($options, $e); } if ($frame instanceof ConnectionCloseFrame) { if ($frame->replyCode === Constants::NOT_ALLOWED && $frame->classId === ConnectionOpenFrame::METHOD_ID >> 16 && $frame->methodId === (ConnectionOpenFrame::METHOD_ID & 0xff)) { throw SingleConnectionException::authorizationFailed($options); } throw SingleConnectionException::closedUnexpectedly($options); } elseif (!$frame instanceof ConnectionOpenOkFrame) { throw ProtocolException::unexpectedFrame($frame, 'expected ' . ConnectionOpenOkFrame::class); } }