/** * Callback after content body has been completely received. */ protected function onBodyComplete() { if ($this->returnFrame) { $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); $message = new Message(null, null, false, $this->returnFrame->exchange, $this->returnFrame->routingKey, $this->headerFrame->toArray(), $content); foreach ($this->returnCallbacks as $callback) { $callback($message, $this->returnFrame); } $this->returnFrame = null; $this->headerFrame = null; } elseif ($this->deliverFrame) { $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); if (isset($this->deliverCallbacks[$this->deliverFrame->consumerTag])) { $message = new Message($this->deliverFrame->consumerTag, $this->deliverFrame->deliveryTag, $this->deliverFrame->redelivered, $this->deliverFrame->exchange, $this->deliverFrame->routingKey, $this->headerFrame->toArray(), $content); $callback = $this->deliverCallbacks[$this->deliverFrame->consumerTag]; $callback($message, $this, $this->client); } $this->deliverFrame = null; $this->headerFrame = null; } elseif ($this->getOkFrame) { $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); // deferred has to be first nullified and then resolved, otherwise results in race condition $deferred = $this->getDeferred; $this->getDeferred = null; $deferred->resolve(new Message(null, $this->getOkFrame->deliveryTag, $this->getOkFrame->redelivered, $this->getOkFrame->exchange, $this->getOkFrame->routingKey, $this->headerFrame->toArray(), $content)); $this->getOkFrame = null; $this->headerFrame = null; } else { throw new \LogicException("Either return or deliver frame has to be handled here."); } }
/** * Writes data from {@link writeBuffer} to stream. */ protected function write() { if (($written = @fwrite($this->getStream(), $this->writeBuffer->read($this->writeBuffer->getLength()))) === false) { throw new ClientException("Could not write data to socket."); } if ($written === 0) { throw new ClientException("Broken pipe or closed connection."); } fflush($this->getStream()); // flush internal PHP buffers $this->writeBuffer->discard($written); $this->lastWrite = microtime(true); }
/** * Callback after content body has been completely received. */ protected function onBodyComplete() { if ($this->returnFrame) { // TODO } elseif ($this->deliverFrame) { $content = $this->bodyBuffer->consume($this->bodyBuffer->getLength()); if (isset($this->deliverCallbacks[$this->deliverFrame->consumerTag])) { $msg = new Message($this->deliverFrame->consumerTag, $this->deliverFrame->deliveryTag, $this->deliverFrame->redelivered, $this->deliverFrame->exchange, $this->deliverFrame->routingKey, $this->headerFrame->toArray(), $content); $callback = $this->deliverCallbacks[$this->deliverFrame->consumerTag]; $callback($msg, $this, $this->client); } $this->deliverFrame = null; $this->headerFrame = null; } else { throw new \LogicException("Either return or deliver frame has to be handled here."); } }
public function publish($channel, $body, array $headers, $exchange = '', $routingKey = '', $mandatory = false, $immediate = false) { $buffer = $this->getWriteBuffer(); $ck = serialize([$channel, $headers, $exchange, $routingKey, $mandatory, $immediate]); $c = isset($this->cache[$ck]) ? $this->cache[$ck] : null; $flags = 0; $off0 = 0; $len0 = 0; $off1 = 0; $len1 = 0; $contentTypeLength = null; $contentType = null; $contentEncodingLength = null; $contentEncoding = null; $headersBuffer = null; $deliveryMode = null; $priority = null; $correlationIdLength = null; $correlationId = null; $replyToLength = null; $replyTo = null; $expirationLength = null; $expiration = null; $messageIdLength = null; $messageId = null; $timestamp = null; $typeLength = null; $type = null; $userIdLength = null; $userId = null; $appIdLength = null; $appId = null; $clusterIdLength = null; $clusterId = null; if ($c) { $buffer->append($c[0]); } else { $off0 = $buffer->getLength(); $buffer->appendUint8(1); $buffer->appendUint16($channel); $buffer->appendUint32(9 + strlen($exchange) + strlen($routingKey)); $buffer->appendUint16(60); $buffer->appendUint16(40); $buffer->appendInt16(0); $buffer->appendUint8(strlen($exchange)); $buffer->append($exchange); $buffer->appendUint8(strlen($routingKey)); $buffer->append($routingKey); $this->getWriter()->appendBits([$mandatory, $immediate], $buffer); $buffer->appendUint8(206); $s = 14; if (isset($headers['content-type'])) { $flags |= 32768; $contentType = $headers['content-type']; $s += 1; $s += $contentTypeLength = strlen($contentType); unset($headers['content-type']); } if (isset($headers['content-encoding'])) { $flags |= 16384; $contentEncoding = $headers['content-encoding']; $s += 1; $s += $contentEncodingLength = strlen($contentEncoding); unset($headers['content-encoding']); } if (isset($headers['delivery-mode'])) { $flags |= 4096; $deliveryMode = $headers['delivery-mode']; $s += 1; unset($headers['delivery-mode']); } if (isset($headers['priority'])) { $flags |= 2048; $priority = $headers['priority']; $s += 1; unset($headers['priority']); } if (isset($headers['correlation-id'])) { $flags |= 1024; $correlationId = $headers['correlation-id']; $s += 1; $s += $correlationIdLength = strlen($correlationId); unset($headers['correlation-id']); } if (isset($headers['reply-to'])) { $flags |= 512; $replyTo = $headers['reply-to']; $s += 1; $s += $replyToLength = strlen($replyTo); unset($headers['reply-to']); } if (isset($headers['expiration'])) { $flags |= 256; $expiration = $headers['expiration']; $s += 1; $s += $expirationLength = strlen($expiration); unset($headers['expiration']); } if (isset($headers['message-id'])) { $flags |= 128; $messageId = $headers['message-id']; $s += 1; $s += $messageIdLength = strlen($messageId); unset($headers['message-id']); } if (isset($headers['timestamp'])) { $flags |= 64; $timestamp = $headers['timestamp']; $s += 8; unset($headers['timestamp']); } if (isset($headers['type'])) { $flags |= 32; $type = $headers['type']; $s += 1; $s += $typeLength = strlen($type); unset($headers['type']); } if (isset($headers['user-id'])) { $flags |= 16; $userId = $headers['user-id']; $s += 1; $s += $userIdLength = strlen($userId); unset($headers['user-id']); } if (isset($headers['app-id'])) { $flags |= 8; $appId = $headers['app-id']; $s += 1; $s += $appIdLength = strlen($appId); unset($headers['app-id']); } if (isset($headers['cluster-id'])) { $flags |= 4; $clusterId = $headers['cluster-id']; $s += 1; $s += $clusterIdLength = strlen($clusterId); unset($headers['cluster-id']); } if (!empty($headers)) { $flags |= 8192; $this->getWriter()->appendTable($headers, $headersBuffer = new Buffer()); $s += $headersBuffer->getLength(); } $buffer->appendUint8(2); $buffer->appendUint16($channel); $buffer->appendUint32($s); $buffer->appendUint16(60); $buffer->appendUint16(0); $len0 = $buffer->getLength() - $off0; } $buffer->appendUint64(strlen($body)); if ($c) { $buffer->append($c[1]); } else { $off1 = $buffer->getLength(); $buffer->appendUint16($flags); if ($flags & 32768) { $buffer->appendUint8($contentTypeLength); $buffer->append($contentType); } if ($flags & 16384) { $buffer->appendUint8($contentEncodingLength); $buffer->append($contentEncoding); } if ($flags & 8192) { $buffer->append($headersBuffer); } if ($flags & 4096) { $buffer->appendUint8($deliveryMode); } if ($flags & 2048) { $buffer->appendUint8($priority); } if ($flags & 1024) { $buffer->appendUint8($correlationIdLength); $buffer->append($correlationId); } if ($flags & 512) { $buffer->appendUint8($replyToLength); $buffer->append($replyTo); } if ($flags & 256) { $buffer->appendUint8($expirationLength); $buffer->append($expiration); } if ($flags & 128) { $buffer->appendUint8($messageIdLength); $buffer->append($messageId); } if ($flags & 64) { $this->getWriter()->appendTimestamp($timestamp, $buffer); } if ($flags & 32) { $buffer->appendUint8($typeLength); $buffer->append($type); } if ($flags & 16) { $buffer->appendUint8($userIdLength); $buffer->append($userId); } if ($flags & 8) { $buffer->appendUint8($appIdLength); $buffer->append($appId); } if ($flags & 4) { $buffer->appendUint8($clusterIdLength); $buffer->append($clusterId); } $buffer->appendUint8(206); $len1 = $buffer->getLength() - $off1; } if (!$c) { $this->cache[$ck] = [$buffer->read($len0, $off0), $buffer->read($len1, $off1)]; if (count($this->cache) > 100) { reset($this->cache); unset($this->cache[key($this->cache)]); } } for ($payloadMax = $this->getFrameMax() - 8, $i = 0, $l = strlen($body); $i < $l; $i += $payloadMax) { $payloadSize = $l - $i; if ($payloadSize > $payloadMax) { $payloadSize = $payloadMax; } $buffer->appendUint8(3); $buffer->appendUint16($channel); $buffer->appendUint32($payloadSize); $buffer->append(substr($body, $i, $payloadSize)); $buffer->appendUint8(206); } return $this->flushWriteBuffer(); }
/** * Consumes AMQP frame from buffer. * * Returns NULL if there are not enough data to construct whole frame. * * @param Buffer $buffer * @return AbstractFrame */ public function consumeFrame(Buffer $buffer) { // not enough data if ($buffer->getLength() < 7) { return null; } $type = $buffer->readUint8(0); $channel = $buffer->readUint16(1); $payloadSize = $buffer->readUint32(3); $payloadOffset = 7; // type:uint8=>1 + channel:uint16=>2 + payloadSize:uint32=>4 ==> 7 // not enough data if ($buffer->getLength() < $payloadOffset + $payloadSize + 1) { return null; } $buffer->consume(7); $payload = $buffer->consume($payloadSize); $frameEnd = $buffer->consumeUint8(); if ($frameEnd !== Constants::FRAME_END) { throw new ProtocolException(sprintf("Frame end byte invalid - expected 0x%02x, got 0x%02x.", Constants::FRAME_END, $frameEnd)); } $frameBuffer = new Buffer($payload); if ($type === Constants::FRAME_METHOD) { $frame = $this->consumeMethodFrame($frameBuffer); } elseif ($type === Constants::FRAME_HEADER) { // see https://github.com/pika/pika/blob/master/pika/spec.py class BasicProperties $frame = new ContentHeaderFrame(); $frame->classId = $frameBuffer->consumeUint16(); $frame->weight = $frameBuffer->consumeUint16(); $frame->bodySize = $frameBuffer->consumeUint64(); $frame->flags = $flags = $frameBuffer->consumeUint16(); if ($flags & ContentHeaderFrame::FLAG_CONTENT_TYPE) { $frame->contentType = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_CONTENT_ENCODING) { $frame->contentEncoding = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_HEADERS) { $frame->headers = $this->consumeTable($frameBuffer); } if ($flags & ContentHeaderFrame::FLAG_DELIVERY_MODE) { $frame->deliveryMode = $frameBuffer->consumeUint8(); } if ($flags & ContentHeaderFrame::FLAG_PRIORITY) { $frame->priority = $frameBuffer->consumeUint8(); } if ($flags & ContentHeaderFrame::FLAG_CORRELATION_ID) { $frame->correlationId = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_REPLY_TO) { $frame->replyTo = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_EXPIRATION) { $frame->expiration = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_MESSAGE_ID) { $frame->messageId = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_TIMESTAMP) { $frame->timestamp = $this->consumeTimestamp($frameBuffer); } if ($flags & ContentHeaderFrame::FLAG_TYPE) { $frame->typeHeader = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_USER_ID) { $frame->userId = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_APP_ID) { $frame->appId = $frameBuffer->consume($frameBuffer->consumeUint8()); } if ($flags & ContentHeaderFrame::FLAG_CLUSTER_ID) { $frame->clusterId = $frameBuffer->consume($frameBuffer->consumeUint8()); } } elseif ($type === Constants::FRAME_BODY) { $frame = new ContentBodyFrame(); $frame->payload = $frameBuffer->consume($frameBuffer->getLength()); } elseif ($type === Constants::FRAME_HEARTBEAT) { $frame = new HeartbeatFrame(); if (!$frameBuffer->isEmpty()) { throw new ProtocolException("Heartbeat frame must be empty."); } } else { throw new ProtocolException("Unhandled frame type '{$type}'."); } if (!$frameBuffer->isEmpty()) { throw new ProtocolException("Frame buffer not entirely consumed."); } /** @var AbstractFrame $frame */ $frame->type = $type; $frame->channel = $channel; $frame->payloadSize = $payloadSize; // DO NOT CALL! ContentBodyFrame uses payload for body // $frame->setPayload($payload); return $frame; }