#include "mqmessaging.h" #include "mqconsumer.h" #ifdef nullptr #undef nullptr #endif #include namespace { template idMQMethod* Decode(idMQFrame& frame) { T* value = new T(); value->Populate(frame); return value; } void SendMethod(idMQSession* session, idMQMethod& method) { if (session == nullptr) return; idMQCommand command; command.method = &method; session->SendCommand(command); } } // namespace amqpBasicGetResultOk_t::amqpBasicGetResultOk_t( const amqpBasicGetResultOk_t& other) : amqpBasicGetResultOk_t() { *this = other; } amqpBasicGetResultOk_t& amqpBasicGetResultOk_t::operator=( const amqpBasicGetResultOk_t& other) { if (this == &other) return *this; deliveryTag = other.deliveryTag; redelivered = other.redelivered; exchange = other.exchange; routingKey = other.routingKey; messageCount = other.messageCount; delete header; header = other.header == nullptr ? nullptr : new AMQPBasicProperties(*other.header); body = other.body; return *this; } amqpBasicGetResultOk_t::~amqpBasicGetResultOk_t() { delete header; } idMQChannel::idMQChannel(idMQSession* sessionValue) : session(sessionValue), protocolWait(), deferredConnectionStart(), deferredConnectionTune(), deferredConnectionOpenOk(), deferredChannelOpenOk(), deferredChannelFlow(), deferredChannelFlowOk(), deferredExchangeDeclareOk(), deferredExchangeDeleteOk(), deferredQueueDeclareOk(), deferredQueueBindOk(), deferredQueuePurgeOk(), deferredQueueDeleteOk(), deferredBasicConsumeOk(), deferredBasicCancelOk(), deferredBasicGetResultOk(), deferredAMQPBasicRecoverOk(), consumers() { } idMQChannel::~idMQChannel() = default; void idMQChannel::Init() { deferredConnectionStart.Clear(); deferredConnectionTune.Clear(); deferredConnectionOpenOk.Clear(); deferredChannelOpenOk.Clear(); } idMQMethod* idMQChannel::DecodeMethod(idMQFrame& frame) { const int classId = frame.ReadUInt16(); const int methodId = frame.ReadUInt16(); switch (classId) { case AMQP_CLASS_CONNECTION: switch (methodId) { case 10: return Decode(frame); case 11: return Decode(frame); case 20: return Decode(frame); case 21: return Decode(frame); case 30: return Decode(frame); case 31: return Decode(frame); case 40: return Decode(frame); case 41: return Decode(frame); case 50: return Decode(frame); case 60: return Decode(frame); case 61: return Decode(frame); } break; case AMQP_CLASS_CHANNEL: switch (methodId) { case 10: return Decode(frame); case 11: return Decode(frame); case 20: return Decode(frame); case 21: return Decode(frame); case 30: return Decode(frame); case 40: return Decode(frame); case 41: return Decode(frame); } break; case AMQP_CLASS_EXCHANGE: switch (methodId) { case 10: return Decode(frame); case 11: return Decode(frame); case 20: return Decode(frame); case 21: return Decode(frame); } break; case AMQP_CLASS_QUEUE: switch (methodId) { case 10: return Decode(frame); case 11: return Decode(frame); case 20: return Decode(frame); case 21: return Decode(frame); case 30: return Decode(frame); case 31: return Decode(frame); case 40: return Decode(frame); case 41: return Decode(frame); case 50: return Decode(frame); case 51: return Decode(frame); } break; case AMQP_CLASS_BASIC: switch (methodId) { case 10: return Decode(frame); case 11: return Decode(frame); case 20: return Decode(frame); case 21: return Decode(frame); case 30: return Decode(frame); case 31: return Decode(frame); case 40: return Decode(frame); case 50: return Decode(frame); case 60: return Decode(frame); case 70: return Decode(frame); case 71: return Decode(frame); case 72: return Decode(frame); case 80: return Decode(frame); case 90: return Decode(frame); case 100: return Decode(frame); case 110: return Decode(frame); case 111: return Decode(frame); } break; } return nullptr; } idMQContentHeader* idMQChannel::DecodeContentHeader(idMQFrame& frame) { const int classId = frame.ReadUInt16(); frame.ReadUInt16(); // weight, reserved and always zero const std::uint64_t bodyLength = frame.ReadUInt64(); if (classId != AMQP_CLASS_BASIC) return nullptr; AMQPBasicProperties* header = new AMQPBasicProperties(); header->bodyLength = bodyLength; header->Populate(frame); return header; } idMQConsumer* idMQChannel::LookupConsumer(const idStr& tag) { for (int index = 0; index < consumers.Num(); ++index) { if (consumers[index] != nullptr && _stricmp(consumers[index]->consumerTag.c_str(), tag.c_str()) == 0) { return consumers[index]; } } return nullptr; } bool idMQChannel::RemoveConsumer(const idStr& tag) { for (int index = 0; index < consumers.Num(); ++index) { if (consumers[index] != nullptr && _stricmp(consumers[index]->consumerTag.c_str(), tag.c_str()) == 0) { return consumers.RemoveIndex(index); } } return false; } MQErrors_t idMQChannel::HandleCommand(idMQCommand& command) { if (command.method == nullptr) return MQERROR_UNEXPECTED_METHOD; const int classId = command.method->GetClassId(); const int methodId = command.method->GetMethodId(); if (classId == AMQP_CLASS_CONNECTION) { switch (methodId) { case 10: deferredConnectionStart.SetValue( *static_cast(command.method)); break; case 30: deferredConnectionTune.SetValue( *static_cast(command.method)); break; case 41: deferredConnectionOpenOk.SetValue( *static_cast(command.method)); break; case 60: { AMQPConnectionCloseOk response; SendMethod(session, response); return MQERROR_SERVER_CLOSED; } case 61: break; default: return MQERROR_UNEXPECTED_METHOD; } return MQERROR_NONE; } if (classId == AMQP_CLASS_CHANNEL) { switch (methodId) { case 11: deferredChannelOpenOk.SetValue( *static_cast(command.method)); break; case 20: { AMQPChannelFlow& flow = *static_cast(command.method); deferredChannelFlow.SetValue(flow); AMQPChannelFlowOk response; response.active = flow.active; SendMethod(session, response); break; } case 21: deferredChannelFlowOk.SetValue( *static_cast(command.method)); break; case 40: { AMQPChannelCloseOk response; SendMethod(session, response); return MQERROR_SERVER_CLOSED; } case 41: break; default: return MQERROR_UNEXPECTED_METHOD; } return MQERROR_NONE; } if (classId == AMQP_CLASS_EXCHANGE) { if (methodId == 11) deferredExchangeDeclareOk.SetValue( *static_cast(command.method)); else if (methodId == 21) deferredExchangeDeleteOk.SetValue( *static_cast(command.method)); else return MQERROR_UNEXPECTED_METHOD; return MQERROR_NONE; } if (classId == AMQP_CLASS_QUEUE) { switch (methodId) { case 11: deferredQueueDeclareOk.SetValue( *static_cast(command.method)); break; case 21: deferredQueueBindOk.SetValue( *static_cast(command.method)); break; case 31: deferredQueuePurgeOk.SetValue( *static_cast(command.method)); break; case 41: deferredQueueDeleteOk.SetValue( *static_cast(command.method)); break; default: return MQERROR_UNEXPECTED_METHOD; } return MQERROR_NONE; } if (classId == AMQP_CLASS_BASIC) { switch (methodId) { case 21: { AMQPBasicConsumeOk& value = *static_cast(command.method); deferredBasicConsumeOk.SetValue(value); idMQConsumer* consumer = LookupConsumer(value.consumerTag); if (consumer != nullptr) consumer->ProcessBasicConsumeOk( value.consumerTag); break; } case 30: { AMQPBasicCancel& value = *static_cast(command.method); idMQConsumer* consumer = LookupConsumer(value.consumerTag); if (consumer != nullptr) consumer->ProcessBasicCancel( value.consumerTag); RemoveConsumer(value.consumerTag); break; } case 31: deferredBasicCancelOk.SetValue( *static_cast(command.method)); break; case 60: { AMQPBasicDeliver& value = *static_cast(command.method); idMQConsumer* consumer = LookupConsumer(value.consumerTag); if (consumer != nullptr) consumer->ProcessBasicDeliver( value.consumerTag, value.deliveryTag, value.redelivered, value.exchange, value.routingKey, static_cast(command.contentHeader), command.contentBody); break; } case 71: { AMQPBasicGetOk& value = *static_cast(command.method); amqpBasicGetResultOk_t result; result.deliveryTag = value.deliveryTag; result.redelivered = value.redelivered; result.exchange = value.exchange; result.routingKey = value.routingKey; result.messageCount = value.messageCount; result.header = command.contentHeader == nullptr ? nullptr : new AMQPBasicProperties( *static_cast(command.contentHeader)); result.body = command.contentBody; deferredBasicGetResultOk.SetValue(result); break; } case 111: deferredAMQPBasicRecoverOk.SetValue( *static_cast(command.method)); break; case 50: case 72: case 80: case 90: break; default: return MQERROR_UNEXPECTED_METHOD; } return MQERROR_NONE; } return MQERROR_UNEXPECTED_CLASS; } void idMQChannel::ConnectionStartOk(AMQPConnectionTune& tune, const idStr& mechanism, const idMQList& response, const idStr& locale) { idScopedCriticalSection lock(protocolWait); AMQPConnectionStartOk request; request.mechanism = mechanism; request.response = response; request.locale = locale; SendMethod(session, request); deferredConnectionTune.GetValue(tune); } void idMQChannel::ConnectionTuneOk(const unsigned short channelMax, const unsigned int frameMax, const unsigned short heartbeat) { AMQPConnectionTuneOk request; request.channelMax = channelMax; request.frameMax = frameMax; request.heartbeat = heartbeat; SendMethod(session, request); } void idMQChannel::ConnectionOpen(idStr& knownHosts, const idStr& vhost, const idStr& capabilities, const bool insist) { idScopedCriticalSection lock(protocolWait); AMQPConnectionOpen request; request.virtualHost = vhost; request.capabilities = capabilities; request.insist = insist; SendMethod(session, request); AMQPConnectionOpenOk result; if (deferredConnectionOpenOk.GetValue(result)) knownHosts = result.knownHosts; } void idMQChannel::ConnectionClose(const unsigned short replyCode, const idStr& replyText, const unsigned short classId, const unsigned short methodId) { AMQPConnectionClose request; request.replyCode = replyCode; request.replyText = replyText; request.cId = classId; request.mId = methodId; SendMethod(session, request); } void idMQChannel::ChannelOpen(const idStr& outOfBand) { idScopedCriticalSection lock(protocolWait); AMQPChannelOpen request; request.oob = outOfBand; SendMethod(session, request); deferredChannelOpenOk.GetValue(); } void idMQChannel::ExchangeDeclareInternal(const idStr& exchange, const idStr& type, const bool passive, const bool durable, const bool autoDelete, const bool internal, const bool noWait) { idScopedCriticalSection lock(protocolWait); AMQPExchangeDeclare request; request.exchange = exchange; request.type = type; request.passive = passive; request.durable = durable; request.autoDelete = autoDelete; request.isInternal = internal; request.noWait = noWait; SendMethod(session, request); if (!noWait) deferredExchangeDeclareOk.GetValue(); } void idMQChannel::ExchangeDeclare(const idStr& exchange, const idStr& type, const bool durable) { ExchangeDeclareInternal(exchange, type, false, durable, false, false, false); } void idMQChannel::ExchangeDeclare(const idStr& exchange, const idStr& type, const bool durable, const bool autoDelete) { ExchangeDeclareInternal(exchange, type, false, durable, autoDelete, false, false); } void idMQChannel::BasicPublishInternal(const idStr& exchange, const idStr& routingKey, const bool mandatory, const bool immediate, const idMQList& body) { AMQPBasicPublish method; method.exchange = exchange; method.routingKey = routingKey; method.mandatory = mandatory; method.immediate = immediate; AMQPBasicProperties properties; properties.bodyLength = body.Num(); idMQCommand command; command.method = &method; command.contentHeader = &properties; command.contentBody = body; session->SendCommand(command); } void idMQChannel::BasicPublish(const idStr& exchange, const idStr& routingKey, const idMQList& body) { BasicPublishInternal(exchange, routingKey, false, false, body); } void idMQChannel::BasicPublish(const idStr& exchange, const idStr& routingKey, const bool mandatory, const bool immediate, const void* data, const unsigned int size) { idMQList body(16); const unsigned char* bytes = static_cast(data); for (unsigned int index = 0; index < size; ++index) body.Append(bytes[index]); BasicPublishInternal(exchange, routingKey, mandatory, immediate, body); } void idMQChannel::Close() { if (session != nullptr) session->Close(); }