diff options
author | Gordon Sim <gsim@apache.org> | 2012-10-19 17:16:07 +0000 |
---|---|---|
committer | Gordon Sim <gsim@apache.org> | 2012-10-19 17:16:07 +0000 |
commit | d9cf555efdc694433837187f8b57d7dfa8236ae1 (patch) | |
tree | 4e8e5a066db948b2f123256bcc8bb5d933f31d9a | |
parent | e6ec89af34dca9959403b6a2a3ccf9154c77d12e (diff) | |
download | qpid-python-d9cf555efdc694433837187f8b57d7dfa8236ae1.tar.gz |
QPID-4368: Pluggable AMQP 1.0 implementation for broker and client
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk/qpid@1400178 13f79535-47bb-0310-9956-ffa450edef68
89 files changed, 10862 insertions, 44 deletions
diff --git a/cpp/configure.ac b/cpp/configure.ac index c4e55f90de..872a8853e3 100644 --- a/cpp/configure.ac +++ b/cpp/configure.ac @@ -296,6 +296,15 @@ AS_IF([test "x$WANT_SASL" != xno], AM_CONDITIONAL([HAVE_SASL], [test "x$have_sasl" = xyes]) AC_SUBST([SASL_PASSWD]) +# Allow integration against external AMQP 1.0 protocol engine +AC_ARG_WITH([proton], AS_HELP_STRING([--with-proton], [Build with the proton toolkit for AMQP 1.0 support])) + +AS_IF([test "x$with_proton" = "xyes"], [ + PKG_CHECK_MODULES([PROTON], [libqpid-proton]) +]) +AM_CONDITIONAL([HAVE_PROTON], [test "x$have_proton" = xyes]) + + # Setup --with-xml/--without-xml as arguments to configure use_xml=yes want_xml=check diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 10005f06e1..a1b1741dc8 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -681,6 +681,9 @@ include (rdma.cmake) # Check for optional SSL support requirements include (ssl.cmake) +# Check for optional AMQP 1.0 support requirements +include (amqp.cmake) + # Check for syslog capabilities not present on all systems check_symbol_exists (LOG_AUTHPRIV "sys/syslog.h" HAVE_LOG_AUTHPRIV) check_symbol_exists (LOG_FTP "sys/syslog.h" HAVE_LOG_FTP) @@ -947,6 +950,30 @@ set (qpidcommon_SOURCES qpid/sys/Timer.cpp qpid/sys/TimerWarnings.cpp qpid/amqp_0_10/Codecs.cpp + qpid/amqp/CharSequence.h + qpid/amqp/CharSequence.cpp + qpid/amqp/Decoder.h + qpid/amqp/Decoder.cpp + qpid/amqp/Descriptor.h + qpid/amqp/Descriptor.cpp + qpid/amqp/Encoder.h + qpid/amqp/Encoder.cpp + qpid/amqp/MessageEncoder.h + qpid/amqp/MessageEncoder.cpp + qpid/amqp/MessageId.h + qpid/amqp/MessageId.cpp + qpid/amqp/MessageReader.h + qpid/amqp/MessageReader.cpp + qpid/amqp/Reader.h + qpid/amqp/Sasl.h + qpid/amqp/Sasl.cpp + qpid/amqp/SaslClient.h + qpid/amqp/SaslClient.cpp + qpid/amqp/SaslServer.h + qpid/amqp/SaslServer.cpp + qpid/messaging/amqp/Transport.h + qpid/messaging/amqp/Transport.cpp + qpid/messaging/amqp/TransportContext.h ${qpid_memstat_module} ) add_msvc_version (qpidcommon library dll) @@ -1039,6 +1066,8 @@ set (qpidmessaging_SOURCES qpid/messaging/AddressParser.cpp qpid/messaging/Connection.cpp qpid/messaging/ConnectionImpl.h + qpid/messaging/ConnectionOptions.h + qpid/messaging/ConnectionOptions.cpp qpid/messaging/Duration.cpp qpid/messaging/exceptions.cpp qpid/messaging/Message.cpp @@ -1052,6 +1081,8 @@ set (qpidmessaging_SOURCES qpid/messaging/Sender.cpp qpid/messaging/SenderImpl.h qpid/messaging/FailoverUpdates.cpp + qpid/messaging/amqp/EncodedMessage.h + qpid/messaging/amqp/EncodedMessage.cpp qpid/client/amqp0_10/AcceptTracker.h qpid/client/amqp0_10/AcceptTracker.cpp qpid/client/amqp0_10/AddressResolution.h diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index f2dcec5c99..e121bbb30c 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -210,6 +210,7 @@ posix_broker_src = \ lib_LTLIBRARIES = libqpidtypes.la libqpidcommon.la libqpidbroker.la libqpidclient.la libqpidmessaging.la + # Definitions for client and daemon plugins PLUGINLDFLAGS=-no-undefined -module -avoid-version confdir=$(sysconfdir)/qpid @@ -512,7 +513,38 @@ libqpidcommon_la_SOURCES += \ qpid/sys/alloca.h \ qpid/sys/uuid.h \ qpid/sys/unordered_map.h \ - qpid/amqp_0_10/Codecs.cpp + qpid/amqp_0_10/Codecs.cpp \ + qpid/amqp/CharSequence.h \ + qpid/amqp/CharSequence.cpp \ + qpid/amqp/Decoder.h \ + qpid/amqp/Decoder.cpp \ + qpid/amqp/Descriptor.h \ + qpid/amqp/Descriptor.cpp \ + qpid/amqp/Encoder.h \ + qpid/amqp/Encoder.cpp \ + qpid/amqp/MessageEncoder.h \ + qpid/amqp/MessageEncoder.cpp \ + qpid/amqp/MessageId.h \ + qpid/amqp/MessageId.cpp \ + qpid/amqp/MessageReader.h \ + qpid/amqp/MessageReader.cpp \ + qpid/amqp/Reader.h \ + qpid/amqp/Sasl.h \ + qpid/amqp/Sasl.cpp \ + qpid/amqp/SaslClient.h \ + qpid/amqp/SaslClient.cpp \ + qpid/amqp/SaslServer.h \ + qpid/amqp/SaslServer.cpp + +#libqpidcommon is not really the 'right' place for the Transport +#interface, which is only used in 1.0 impl of messaging API, but this +#lets the 1.0 SSL support be included in the existing sslconnector lib +#which in turn addresses common ssl needs in qpidclient and +#qpidmessaging: +libqpidcommon_la_SOURCES += \ + qpid/messaging/amqp/Transport.h \ + qpid/messaging/amqp/Transport.cpp \ + qpid/messaging/amqp/TransportContext.h if HAVE_SASL libqpidcommon_la_SOURCES += qpid/sys/cyrus/CyrusSecurityLayer.h @@ -718,6 +750,65 @@ libqpidbroker_la_SOURCES = \ QPIDBROKER_VERSION_INFO = 2:0:0 libqpidbroker_la_LDFLAGS = -version-info $(QPIDBROKER_VERSION_INFO) +if HAVE_PROTON + +dmoduleexec_LTLIBRARIES += amqp.la +amqp_la_LIBADD = libqpidcommon.la +amqp_la_SOURCES = \ + qpid/broker/amqp/Connection.h \ + qpid/broker/amqp/Connection.cpp \ + qpid/broker/amqp/Header.h \ + qpid/broker/amqp/Header.cpp \ + qpid/broker/amqp/ManagedConnection.h \ + qpid/broker/amqp/ManagedConnection.cpp \ + qpid/broker/amqp/ManagedSession.h \ + qpid/broker/amqp/ManagedSession.cpp \ + qpid/broker/amqp/ManagedOutgoingLink.h \ + qpid/broker/amqp/ManagedOutgoingLink.cpp \ + qpid/broker/amqp/Message.h \ + qpid/broker/amqp/Message.cpp \ + qpid/broker/amqp/Outgoing.h \ + qpid/broker/amqp/Outgoing.cpp \ + qpid/broker/amqp/ProtocolPlugin.cpp \ + qpid/broker/amqp/Sasl.h \ + qpid/broker/amqp/Sasl.cpp \ + qpid/broker/amqp/Session.h \ + qpid/broker/amqp/Session.cpp \ + qpid/broker/amqp/Translation.h \ + qpid/broker/amqp/Translation.cpp + +amqp_la_LDFLAGS = $(PLUGINLDFLAGS) + +cmoduleexec_LTLIBRARIES += amqpc.la +amqpc_la_LIBADD = libqpidcommon.la +amqpc_la_SOURCES = \ + qpid/messaging/amqp/ConnectionContext.h \ + qpid/messaging/amqp/ConnectionContext.cpp \ + qpid/messaging/amqp/ConnectionHandle.h \ + qpid/messaging/amqp/ConnectionHandle.cpp \ + qpid/messaging/amqp/DriverImpl.h \ + qpid/messaging/amqp/DriverImpl.cpp \ + qpid/messaging/amqp/ReceiverContext.h \ + qpid/messaging/amqp/ReceiverContext.cpp \ + qpid/messaging/amqp/ReceiverHandle.h \ + qpid/messaging/amqp/ReceiverHandle.cpp \ + qpid/messaging/amqp/Sasl.h \ + qpid/messaging/amqp/Sasl.cpp \ + qpid/messaging/amqp/SenderContext.h \ + qpid/messaging/amqp/SenderContext.cpp \ + qpid/messaging/amqp/SenderHandle.h \ + qpid/messaging/amqp/SenderHandle.cpp \ + qpid/messaging/amqp/SessionContext.h \ + qpid/messaging/amqp/SessionContext.cpp \ + qpid/messaging/amqp/SessionHandle.h \ + qpid/messaging/amqp/SessionHandle.cpp \ + qpid/messaging/amqp/TcpTransport.h \ + qpid/messaging/amqp/TcpTransport.cpp + +amqpc_la_LDFLAGS = $(PLUGINLDFLAGS) + +endif #HAVE_PROTON + libqpidclient_la_LIBADD = libqpidcommon.la -luuid libqpidclient_la_SOURCES = \ @@ -794,6 +885,8 @@ libqpidmessaging_la_SOURCES = \ qpid/messaging/AddressParser.h \ qpid/messaging/AddressParser.cpp \ qpid/messaging/Connection.cpp \ + qpid/messaging/ConnectionOptions.h \ + qpid/messaging/ConnectionOptions.cpp \ qpid/messaging/Duration.cpp \ qpid/messaging/exceptions.cpp \ qpid/messaging/Message.cpp \ @@ -810,6 +903,8 @@ libqpidmessaging_la_SOURCES = \ qpid/messaging/ReceiverImpl.h \ qpid/messaging/SessionImpl.h \ qpid/messaging/FailoverUpdates.cpp \ + qpid/messaging/amqp/EncodedMessage.h \ + qpid/messaging/amqp/EncodedMessage.cpp \ qpid/client/amqp0_10/AcceptTracker.h \ qpid/client/amqp0_10/AcceptTracker.cpp \ qpid/client/amqp0_10/AddressResolution.h \ diff --git a/cpp/src/qpid/amqp/CharSequence.cpp b/cpp/src/qpid/amqp/CharSequence.cpp new file mode 100644 index 0000000000..857ec7e587 --- /dev/null +++ b/cpp/src/qpid/amqp/CharSequence.cpp @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "CharSequence.h" + +namespace qpid { +namespace amqp { + +void CharSequence::init() +{ + data = 0; + size = 0; +} + +CharSequence::operator bool() const +{ + return data && size; +} +std::string CharSequence::str() const +{ + return std::string(data, size); +} + +CharSequence CharSequence::create(const char* data, size_t size) +{ + CharSequence c = {data, size}; + return c; +} + +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/CharSequence.h b/cpp/src/qpid/amqp/CharSequence.h new file mode 100644 index 0000000000..a7f1175e8f --- /dev/null +++ b/cpp/src/qpid/amqp/CharSequence.h @@ -0,0 +1,48 @@ +#ifndef QPID_AMQP_CHARSEQUENCE_H +#define QPID_AMQP_CHARSEQUENCE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <stddef.h> +#include <string> + +namespace qpid { +namespace amqp { + +/** + * Simple record of a particular sequence of chars/bytes. The memroy + * referenced is assumed to be owned by some other entity, this is + * merely a pointer into a (segment of) it. + */ +struct CharSequence +{ + const char* data; + size_t size; + + operator bool() const; + std::string str() const; + void init(); + + static CharSequence create(const char* data, size_t size); +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_CHARSEQUENCE_H*/ diff --git a/cpp/src/qpid/amqp/Codec.h b/cpp/src/qpid/amqp/Codec.h new file mode 100644 index 0000000000..c91cd0a96b --- /dev/null +++ b/cpp/src/qpid/amqp/Codec.h @@ -0,0 +1,83 @@ +#ifndef QPID_AMQP_CODEC_H +#define QPID_AMQP_CODEC_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +namespace qpid { +namespace amqp { + +/** + * + */ +class Codec +{ + public: + + + + private: + + struct Constructor + { + uint8_t code; + Descriptor descriptor; + bool isDescribed; + }; + + Constructor readConstructor(Decoder decoder, Reader reader) + { + Constructor result; + result.code = decoder.readCode(); + if (code == DESCRIPTOR) { + result.isDescribed = true; + result.descriptor = decoder.readDescriptor(); + result.code = decoder.readCode(); + } else { + result.isDescribed = false; + } + return result; + } +}; + +Codec::Descriptor Codec::Decoder::readDescriptor() +{ + uint8_t code = decoder.readCode(); + switch(code) { + case SYMBOL8: + return Descriptor(readSequence8()); + case SYMBOL32: + return Descriptor(readSequence32()); + case ULONG: + return Descriptor(readULong()); + case ULONG_SMALL: + return Descriptor((uint64_t) readUByte()); + case ULONG_ZERO: + return Descriptor((uint64_t) 0); + default: + throw qpid::Exception("Expected descriptor of type ulong or symbol; found " << code); + } +} + +Codec::Descriptor::Descriptor(uint64_t id) : value.id(id), type(NUMERIC) {} +Codec::Descriptor::Descriptor(const CharSequence& symbol) : value.symbol(symbol), type(SYMBOLIC) {} +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_CODEC_H*/ diff --git a/cpp/src/qpid/amqp/Constructor.h b/cpp/src/qpid/amqp/Constructor.h new file mode 100644 index 0000000000..444e455670 --- /dev/null +++ b/cpp/src/qpid/amqp/Constructor.h @@ -0,0 +1,42 @@ +#ifndef QPID_AMQP_CONSTRUCTOR_H +#define QPID_AMQP_CONSTRUCTOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Descriptor.h" +namespace qpid { +namespace amqp { + +/** + * Representation of an AMQP 1.0 type 'constructor' (i.e. a type code + * with an optional descriptor) + */ +struct Constructor +{ + uint8_t code; + Descriptor descriptor; + bool isDescribed; + + Constructor(uint8_t c) : code(c), descriptor(0), isDescribed(false) {} +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_CONSTRUCTOR_H*/ diff --git a/cpp/src/qpid/amqp/Decoder.cpp b/cpp/src/qpid/amqp/Decoder.cpp new file mode 100644 index 0000000000..4c14c8e4d9 --- /dev/null +++ b/cpp/src/qpid/amqp/Decoder.cpp @@ -0,0 +1,544 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/CharSequence.h" +#include "qpid/amqp/Constructor.h" +#include "qpid/amqp/Descriptor.h" +#include "qpid/amqp/Reader.h" +#include "qpid/amqp/typecodes.h" +#include "qpid/types/Uuid.h" +#include "qpid/types/Variant.h" +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" + +namespace qpid { +namespace amqp { + +using namespace qpid::amqp::typecodes; + +Decoder::Decoder(const char* d, size_t s) : start(d), size(s), position(0) {} + +namespace { +class MapBuilder : public Reader +{ + public: + void onNull(const Descriptor*) + { + qpid::types::Variant v; + handle(v, NULL_NAME); + } + void onBoolean(bool v, const Descriptor*) + { + handle(v, BOOLEAN_NAME); + } + void onUByte(uint8_t v, const Descriptor*) + { + handle(v, UBYTE_NAME); + } + void onUShort(uint16_t v, const Descriptor*) + { + handle(v, USHORT_NAME); + } + void onUInt(uint32_t v, const Descriptor*) + { + handle(v, UINT_NAME); + } + void onULong(uint64_t v, const Descriptor*) + { + handle(v, ULONG_NAME); + } + void onByte(int8_t v, const Descriptor*) + { + handle(v, BYTE_NAME); + } + void onShort(int16_t v, const Descriptor*) + { + handle(v, SHORT_NAME); + } + void onInt(int32_t v, const Descriptor*) + { + handle(v, INT_NAME); + } + void onLong(int64_t v, const Descriptor*) + { + handle(v, LONG_NAME); + } + void onFloat(float v, const Descriptor*) + { + handle(v, FLOAT_NAME); + } + void onDouble(double v, const Descriptor*) + { + handle(v, DOUBLE_NAME); + } + void onUuid(const CharSequence& v, const Descriptor*) + { + handle(v, UUID_NAME); + } + void onTimestamp(int64_t v, const Descriptor*) + { + handle(v, TIMESTAMP_NAME); + } + void onBinary(const CharSequence& v, const Descriptor*) + { + handle(v); + } + void onString(const CharSequence& v, const Descriptor*) + { + handle(v); + } + void onSymbol(const CharSequence& v, const Descriptor*) + { + handle(v); + } + MapBuilder(qpid::types::Variant::Map& m) : map(m), state(KEY) {} + private: + qpid::types::Variant::Map& map; + enum {KEY, SKIP, VALUE} state; + std::string key; + + template <typename T> void handle(T value, const std::string& name) + { + switch (state) { + case KEY: + QPID_LOG(warning, "Ignoring key of type " << name); + state = SKIP; + break; + case VALUE: + map[key] = value; + case SKIP: + state = KEY; + break; + } + } + void handle(const CharSequence& value) + { + switch (state) { + case KEY: + key = value.str(); + state = VALUE; + break; + case VALUE: + map[key] = value.str(); + case SKIP: + state = KEY; + break; + } + } +}; +} +void Decoder::readMap(qpid::types::Variant::Map& map) +{ + MapBuilder builder(map); + read(builder); +} + +qpid::types::Variant::Map Decoder::readMap() +{ + qpid::types::Variant::Map map; + readMap(map); + return map; +} + +void Decoder::read(Reader& reader) +{ + while (available() && reader.proceed()) { + readOne(reader); + } +} + +void Decoder::readOne(Reader& reader) +{ + const char* temp = start + position; + Constructor c = readConstructor(); + if (c.isDescribed) reader.onDescriptor(c.descriptor, temp); + readValue(reader, c.code, c.isDescribed ? &c.descriptor : 0); +} + +void Decoder::readValue(Reader& reader, uint8_t code, const Descriptor* descriptor) +{ + switch(code) { + case NULL_VALUE: + reader.onNull(descriptor); + break; + case BOOLEAN: + reader.onBoolean(readBoolean(), descriptor); + break; + case BOOLEAN_TRUE: + reader.onBoolean(true, descriptor); + break; + case BOOLEAN_FALSE: + reader.onBoolean(false, descriptor); + break; + case UBYTE: + reader.onUByte(readUByte(), descriptor); + break; + case USHORT: + reader.onUShort(readUShort(), descriptor); + break; + case UINT: + reader.onUInt(readUInt(), descriptor); + break; + case UINT_SMALL: + reader.onUInt(readUByte(), descriptor); + break; + case UINT_ZERO: + reader.onUInt(0, descriptor); + break; + case ULONG: + reader.onULong(readULong(), descriptor); + break; + case ULONG_SMALL: + reader.onULong(readUByte(), descriptor); + break; + case ULONG_ZERO: + reader.onULong(0, descriptor); + break; + case BYTE: + reader.onByte(readByte(), descriptor); + break; + case SHORT: + reader.onShort(readShort(), descriptor); + break; + case INT: + reader.onInt(readInt(), descriptor); + break; + case INT_SMALL: + reader.onInt(readByte(), descriptor); + break; + case LONG: + reader.onLong(readLong(), descriptor); + break; + case LONG_SMALL: + reader.onLong(readByte(), descriptor); + break; + case FLOAT: + reader.onFloat(readFloat(), descriptor); + break; + case DOUBLE: + reader.onDouble(readDouble(), descriptor); + break; + case UUID: + reader.onUuid(readRawUuid(), descriptor); + break; + case TIMESTAMP: + reader.onTimestamp(readLong(), descriptor); + break; + + case BINARY8: + reader.onBinary(readSequence8(), descriptor); + break; + case BINARY32: + reader.onBinary(readSequence32(), descriptor); + break; + case STRING8: + reader.onString(readSequence8(), descriptor); + break; + case STRING32: + reader.onString(readSequence32(), descriptor); + break; + case SYMBOL8: + reader.onSymbol(readSequence8(), descriptor); + break; + case SYMBOL32: + reader.onSymbol(readSequence32(), descriptor); + break; + + case LIST0: + reader.onStartList(0, CharSequence::create(0, 0), descriptor); + reader.onEndList(0, descriptor); + break; + case LIST8: + readList8(reader, descriptor); + break; + case LIST32: + readList32(reader, descriptor); + break; + case MAP8: + readMap8(reader, descriptor); + break; + case MAP32: + readMap32(reader, descriptor); + break; + case ARRAY8: + readArray8(reader, descriptor); + break; + case ARRAY32: + readArray32(reader, descriptor); + break; + default: + break; + } +} + +void Decoder::readList8(Reader& reader, const Descriptor* descriptor) +{ + uint8_t size = readUByte(); + uint8_t count = readUByte(); + readList(reader, size-sizeof(size), count, descriptor); +} + +void Decoder::readList32(Reader& reader, const Descriptor* descriptor) +{ + uint32_t size = readUInt(); + uint32_t count = readUInt(); + readList(reader, size-sizeof(size), count, descriptor); +} + +void Decoder::readMap8(Reader& reader, const Descriptor* descriptor) +{ + uint8_t size = readUByte(); + uint8_t count = readUByte(); + readMap(reader, size-sizeof(size), count, descriptor); +} + +void Decoder::readMap32(Reader& reader, const Descriptor* descriptor) +{ + uint32_t size = readUInt(); + uint32_t count = readUInt(); + readMap(reader, size-sizeof(size), count, descriptor); +} + +void Decoder::readArray8(Reader& reader, const Descriptor* descriptor) +{ + uint8_t size = readUByte(); + uint8_t count = readUByte(); + readArray(reader, size-sizeof(size), count, descriptor); +} + +void Decoder::readArray32(Reader& reader, const Descriptor* descriptor) +{ + uint32_t size = readUInt(); + uint32_t count = readUInt(); + readArray(reader, size-sizeof(size), count, descriptor); +} + +void Decoder::readList(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor) +{ + if (reader.onStartList(count, CharSequence::create(data(), size), descriptor)) { + for (uint32_t i = 0; i < count; ++i) { + readOne(reader); + } + reader.onEndList(count, descriptor); + } else { + //skip + advance(size); + } +} +void Decoder::readMap(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor) +{ + if (reader.onStartMap(count, CharSequence::create(data(), size), descriptor)) { + for (uint32_t i = 0; i < count; ++i) { + readOne(reader); + } + reader.onEndMap(count, descriptor); + } else { + //skip + advance(size); + } +} + +void Decoder::readArray(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor) +{ + size_t temp = position; + Constructor constructor = readConstructor(); + CharSequence raw = CharSequence::create(data(), size-(position-temp)); + if (reader.onStartArray(count, raw, constructor, descriptor)) { + for (uint32_t i = 0; i < count; ++i) { + readValue(reader, constructor.code, constructor.isDescribed ? &constructor.descriptor : 0); + } + reader.onEndArray(count, descriptor); + } else { + //skip + advance(raw.size); + } +} + + +Constructor Decoder::readConstructor() +{ + Constructor result(readCode()); + if (result.code == DESCRIPTOR) { + result.isDescribed = true; + result.descriptor = readDescriptor(); + result.code = readCode(); + } else { + result.isDescribed = false; + } + return result; +} + +Descriptor Decoder::readDescriptor() +{ + uint8_t code = readCode(); + switch(code) { + case SYMBOL8: + return Descriptor(readSequence8()); + case SYMBOL32: + return Descriptor(readSequence32()); + case ULONG: + return Descriptor(readULong()); + case ULONG_SMALL: + return Descriptor((uint64_t) readUByte()); + case ULONG_ZERO: + return Descriptor((uint64_t) 0); + default: + throw qpid::Exception(QPID_MSG("Expected descriptor of type ulong or symbol; found " << code)); + } +} + +void Decoder::advance(size_t n) +{ + if (n > available()) throw qpid::Exception(QPID_MSG("Out of Bounds")); + position += n; +} + +const char* Decoder::data() +{ + return start + position; +} + +size_t Decoder::available() +{ + return size - position; +} + +uint8_t Decoder::readCode() +{ + return readUByte(); +} + +bool Decoder::readBoolean() +{ + return readUByte(); +} + +uint8_t Decoder::readUByte() +{ + return static_cast<uint8_t>(start[position++]); +} + +uint16_t Decoder::readUShort() +{ + uint16_t hi = (unsigned char) start[position++]; + hi = hi << 8; + hi |= (unsigned char) start[position++]; + return hi; +} + +uint32_t Decoder::readUInt() +{ + uint32_t a = (unsigned char) start[position++]; + uint32_t b = (unsigned char) start[position++]; + uint32_t c = (unsigned char) start[position++]; + uint32_t d = (unsigned char) start[position++]; + a = a << 24; + a |= b << 16; + a |= c << 8; + a |= d; + return a; +} + +uint64_t Decoder::readULong() +{ + uint64_t hi =readUInt(); + uint64_t lo = readUInt(); + hi = hi << 32; + return hi | lo; +} + +int8_t Decoder::readByte() +{ + return (int8_t) readUByte(); +} + +int16_t Decoder::readShort() +{ + return (int16_t) readUShort(); +} + +int32_t Decoder::readInt() +{ + return (int32_t) readUInt(); +} + +int64_t Decoder::readLong() +{ + return (int64_t) readULong(); +} + +float Decoder::readFloat() +{ + union { + uint32_t i; + float f; + } val; + val.i = readUInt(); + return val.f; +} + +double Decoder::readDouble() +{ + union { + uint64_t i; + double f; + } val; + val.i = readULong(); + return val.f; +} + +CharSequence Decoder::readSequence8() +{ + CharSequence s; + s.size = readUByte(); + s.data = start + position; + advance(s.size); + return s; +} + +CharSequence Decoder::readSequence32() +{ + CharSequence s; + s.size = readUInt(); + s.data = start + position; + advance(s.size); + return s; +} + +qpid::types::Uuid Decoder::readUuid() +{ + qpid::types::Uuid uuid(start + position); + advance(16); + return uuid; +} + +CharSequence Decoder::readRawUuid() +{ + CharSequence s; + s.data = start + position; + s.size = 16; + advance(s.size); + return s; +} + +size_t Decoder::getPosition() const { return position; } +void Decoder::resetSize(size_t s) { size = s; } +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/Decoder.h b/cpp/src/qpid/amqp/Decoder.h new file mode 100644 index 0000000000..88f5c18ca3 --- /dev/null +++ b/cpp/src/qpid/amqp/Decoder.h @@ -0,0 +1,97 @@ +#ifndef QPID_AMQP_DECODER_H +#define QPID_AMQP_DECODER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/IntegerTypes.h" +#include <map> +#include <string> +#include <stddef.h> + +namespace qpid { +namespace types { +class Uuid; +class Variant; +} +namespace amqp { +struct CharSequence; +struct Constructor; +struct Descriptor; +class Reader; + +/** + * Class to assist in decoding an AMQP encoded data-stream. + */ +class Decoder +{ + public: + Decoder(const char*, size_t); + + size_t available(); + uint8_t readCode(); + + bool readBoolean(); + uint8_t readUByte(); + uint16_t readUShort(); + uint32_t readUInt(); + uint64_t readULong(); + int8_t readByte(); + int16_t readShort(); + int32_t readInt(); + int64_t readLong(); + float readFloat(); + double readDouble(); + qpid::types::Uuid readUuid(); + CharSequence readSequence8(); + CharSequence readSequence32(); + Descriptor readDescriptor(); + void read(Reader& reader); + + void readMap(std::map<std::string, qpid::types::Variant>&); + std::map<std::string, qpid::types::Variant> readMap(); + void advance(size_t); + size_t getPosition() const; + void resetSize(size_t size); + + private: + const char* const start; + size_t size; + size_t position; + + void readOne(Reader& reader); + void readValue(Reader& reader, uint8_t code, const Descriptor* descriptor); + void readList(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor); + void readMap(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor); + void readArray(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor); + void readList8(Reader& reader, const Descriptor* descriptor); + void readList32(Reader& reader, const Descriptor* descriptor); + void readMap8(Reader& reader, const Descriptor* descriptor); + void readMap32(Reader& reader, const Descriptor* descriptor); + void readArray8(Reader& reader, const Descriptor* descriptor); + void readArray32(Reader& reader, const Descriptor* descriptor); + CharSequence readRawUuid(); + Constructor readConstructor(); + const char* data(); + +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_DECODER_H*/ diff --git a/cpp/src/qpid/amqp/Descriptor.cpp b/cpp/src/qpid/amqp/Descriptor.cpp new file mode 100644 index 0000000000..087e87c5e6 --- /dev/null +++ b/cpp/src/qpid/amqp/Descriptor.cpp @@ -0,0 +1,52 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Descriptor.h" + +namespace qpid { +namespace amqp { +Descriptor::Descriptor(uint64_t code) : type(NUMERIC) { value.code = code; } +Descriptor::Descriptor(const CharSequence& symbol) : type(SYMBOLIC) { value.symbol = symbol; } +bool Descriptor::match(const std::string& symbol, uint64_t code) const +{ + switch (type) { + case SYMBOLIC: + return symbol.compare(0, symbol.size(), value.symbol.data, value.symbol.size) == 0; + case NUMERIC: + return code == value.code; + } + return false; +} + + +std::ostream& operator<<(std::ostream& os, const Descriptor& d) +{ + switch (d.type) { + case Descriptor::SYMBOLIC: + if (d.value.symbol.data && d.value.symbol.size) os << std::string(d.value.symbol.data, d.value.symbol.size); + else os << "null"; + break; + case Descriptor::NUMERIC: + os << d.value.code; + break; + } + return os; +} +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/Descriptor.h b/cpp/src/qpid/amqp/Descriptor.h new file mode 100644 index 0000000000..c36aa38ee3 --- /dev/null +++ b/cpp/src/qpid/amqp/Descriptor.h @@ -0,0 +1,54 @@ +#ifndef QPID_AMQP_DESCRIPTOR_H +#define QPID_AMQP_DESCRIPTOR_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/CharSequence.h" +#include "qpid/sys/IntegerTypes.h" +#include <ostream> + +namespace qpid { +namespace amqp { + +/** + * Representation of an AMQP 1.0 type descriptor. + */ +struct Descriptor +{ + union { + CharSequence symbol; + uint64_t code; + } value; + enum { + NUMERIC, + SYMBOLIC + } type; + + Descriptor(uint64_t code); + Descriptor(const CharSequence& symbol); + bool match(const std::string&, uint64_t) const; +}; + +std::ostream& operator<<(std::ostream& os, const Descriptor& d); + +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_DESCRIPTOR_H*/ diff --git a/cpp/src/qpid/amqp/Encoder.cpp b/cpp/src/qpid/amqp/Encoder.cpp new file mode 100644 index 0000000000..6599f70811 --- /dev/null +++ b/cpp/src/qpid/amqp/Encoder.cpp @@ -0,0 +1,402 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Encoder.h" +#include "qpid/amqp/CharSequence.h" +#include "qpid/amqp/Descriptor.h" +#include "qpid/amqp/typecodes.h" +#include "qpid/types/Uuid.h" +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" +#include <assert.h> +#include <string.h> + +namespace qpid { +namespace amqp { + +namespace { +template <typename T> size_t encode(char* data, T i); +template <> size_t encode<uint8_t>(char* data, uint8_t i) +{ + *data = i; + return 1; +} +template <> size_t encode<uint16_t>(char* data, uint16_t i) +{ + uint16_t b = i; + size_t position(0); + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); + return position; +} +template <> size_t encode<uint32_t>(char* data, uint32_t i) +{ + uint32_t b = i; + size_t position(0); + data[position++] = (uint8_t) (0xFF & (b >> 24)); + data[position++] = (uint8_t) (0xFF & (b >> 16)); + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); + return position; +} +template <> size_t encode<uint64_t>(char* data, uint64_t i) +{ + uint32_t hi = i >> 32; + uint32_t lo = i; + size_t r(0); + r += encode(data, hi); + r += encode(data + r, lo); + return r; +} +template<typename T> struct Backfill +{ + T size; + T count; + char* location; +}; + +template<typename T> void end(T count, void* token, char* current) +{ + Backfill<T> b; + b.location = (char*) token; + b.size = (T) (current - b.location) - sizeof(b.size); + b.count = count; + b.location += encode<T>(b.location, b.size); + encode<T>(b.location, b.count); +} +} +char* Encoder::skip(size_t n) +{ + char* current = data + position; + check(n); + position += n; + return current; +} + +void Encoder::write(bool b) +{ + check(sizeof(b)); + position += encode<uint8_t>(data+position, b ? 1u : 0u); +} +void Encoder::write(uint8_t i) +{ + check(sizeof(i)); + position += encode<uint8_t>(data+position, i); +} +void Encoder::write(uint16_t i) +{ + check(sizeof(i)); + position += encode<uint16_t>(data+position, i); +} +void Encoder::write(uint32_t i) +{ + check(sizeof(i)); + position += encode<uint32_t>(data+position, i); +} +void Encoder::write(uint64_t i) +{ + check(sizeof(i)); + position += encode<uint64_t>(data+position, i); +} +void Encoder::write(int8_t i) +{ + check(sizeof(i)); + position += encode(data+position, (uint8_t) i); +} +void Encoder::write(int16_t i) +{ + check(sizeof(i)); + position += encode(data+position, (uint16_t) i); +} +void Encoder::write(int32_t i) +{ + check(sizeof(i)); + position += encode(data+position, (uint32_t) i); +} +void Encoder::write(int64_t i) +{ + check(sizeof(i)); + position += encode(data+position, (uint64_t) i); +} +void Encoder::write(float f) +{ + check(sizeof(f)); + union { + uint32_t i; + float f; + } val; + + val.f = f; + write(val.i); +} +void Encoder::write(double d) +{ + check(sizeof(d)); + union { + uint64_t i; + double d; + } val; + + val.d = d; + write(val.i); +} +void Encoder::write(const qpid::types::Uuid& uuid) +{ + writeBytes((const char*) uuid.data(), uuid.size()); +} + +void Encoder::writeBytes(const char* bytes, size_t count) +{ + check(count); + ::memcpy(data + position, bytes, count); + position += count; +} + +void Encoder::writeCode(uint8_t code) +{ + write(code); +} + +void Encoder::writeNull(const Descriptor* d) +{ + if (d) writeDescriptor(*d); + writeCode(typecodes::NULL_VALUE); +} +void Encoder::writeBoolean(bool b, const Descriptor* d) +{ + if (d) writeDescriptor(*d); + writeCode(b ? typecodes::BOOLEAN_TRUE : typecodes::BOOLEAN_FALSE); +} +void Encoder::writeUByte(uint8_t i, const Descriptor* d) +{ + write(i, typecodes::UBYTE, d); +} + +void Encoder::writeUShort(uint16_t i, const Descriptor* d) +{ + write(i, typecodes::USHORT, d); +} + +void Encoder::writeUInt(uint32_t i, const Descriptor* d) +{ + if (i == 0) { + if (d) writeDescriptor(*d); + writeCode(typecodes::UINT_ZERO); + } else { + if (i < 256) { + write((uint8_t) i, typecodes::UINT_SMALL, d); + } else { + write(i, typecodes::UINT, d); + } + } +} + +void Encoder::writeULong(uint64_t i, const Descriptor* d) +{ + if (i == 0) { + if (d) writeDescriptor(*d); + writeCode(typecodes::ULONG_ZERO); + } else { + if (i < 256) { + write((uint8_t) i, typecodes::ULONG_SMALL, d); + } else { + write(i, typecodes::ULONG, d); + } + } +} + +void Encoder::writeByte(int8_t i, const Descriptor* d) +{ + write((uint8_t) i, typecodes::LONG, d); +} + +void Encoder::writeShort(int16_t i, const Descriptor* d) +{ + write((uint16_t) i, typecodes::SHORT, d); +} + +void Encoder::writeInt(int32_t i, const Descriptor* d) +{ + write((uint32_t) i, typecodes::INT, d); +} + +void Encoder::writeLong(int64_t i, const Descriptor* d) +{ + write((uint64_t) i, typecodes::LONG, d); +} + +void Encoder::writeFloat(float f, const Descriptor* d) +{ + write(f, typecodes::FLOAT, d); +} + +void Encoder::writeDouble(double f, const Descriptor* d) +{ + write(f, typecodes::DOUBLE, d); +} + +void Encoder::writeUuid(const qpid::types::Uuid& uuid, const Descriptor* d) +{ + write(uuid, typecodes::UUID, d); +} + +void Encoder::write(const CharSequence& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d) +{ + if (d) writeDescriptor(*d); + if (v.size < 256) { + writeCode(codes.first); + write((uint8_t) v.size); + } else { + writeCode(codes.second); + write((uint32_t) v.size); + } + writeBytes(v.data, v.size); +} + +void Encoder::write(const std::string& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d) +{ + if (d) writeDescriptor(*d); + if (v.size() < 256) { + writeCode(codes.first); + write((uint8_t) v.size()); + } else { + writeCode(codes.second); + write((uint32_t) v.size()); + } + writeBytes(v.data(), v.size()); +} + +void Encoder::writeSymbol(const CharSequence& v, const Descriptor* d) +{ + write(v, typecodes::SYMBOL, d); +} + +void Encoder::writeSymbol(const std::string& v, const Descriptor* d) +{ + write(v, typecodes::SYMBOL, d); +} + +void Encoder::writeString(const CharSequence& v, const Descriptor* d) +{ + write(v, typecodes::STRING, d); +} + +void Encoder::writeString(const std::string& v, const Descriptor* d) +{ + write(v, typecodes::STRING, d); +} + +void Encoder::writeBinary(const CharSequence& v, const Descriptor* d) +{ + write(v, typecodes::BINARY, d); +} + +void Encoder::writeBinary(const std::string& v, const Descriptor* d) +{ + write(v, typecodes::BINARY, d); +} + +void* Encoder::startList8(const Descriptor* d) +{ + return start<uint8_t>(typecodes::LIST8, d); +} + +void* Encoder::startList32(const Descriptor* d) +{ + return start<uint32_t>(typecodes::LIST32, d); +} + +void Encoder::endList8(uint8_t count, void* token) +{ + end<uint8_t>(count, token, data+position); +} + +void Encoder::endList32(uint32_t count, void* token) +{ + end<uint32_t>(count, token, data+position); +} + +void* Encoder::startMap8(const Descriptor* d) +{ + return start<uint8_t>(typecodes::MAP8, d); +} + +void* Encoder::startMap32(const Descriptor* d) +{ + return start<uint32_t>(typecodes::MAP32, d); +} + +void Encoder::endMap8(uint8_t count, void* token) +{ + end<uint8_t>(count, token, data+position); +} + +void Encoder::endMap32(uint32_t count, void* token) +{ + end<uint32_t>(count, token, data+position); +} + + +void* Encoder::startArray8(const Constructor& c, const Descriptor* d) +{ + return startArray<uint8_t>(typecodes::ARRAY8, d, c); +} + +void* Encoder::startArray32(const Constructor& c, const Descriptor* d) +{ + return startArray<uint8_t>(typecodes::ARRAY32, d, c); +} + +void Encoder::endArray8(size_t count, void* token) +{ + end<uint8_t>(count, token, data+position); +} + +void Encoder::endArray32(size_t count, void* token) +{ + end<uint32_t>(count, token, data+position); +} + + +void Encoder::writeDescriptor(const Descriptor& d) +{ + writeCode(typecodes::DESCRIPTOR); + switch (d.type) { + case Descriptor::NUMERIC: + writeULong(d.value.code, 0); + break; + case Descriptor::SYMBOLIC: + writeSymbol(d.value.symbol, 0); + break; + } +} +void Encoder::check(size_t s) +{ + if (position + s > size) { + QPID_LOG(notice, "Buffer overflow for write of size " << s << " to buffer of size " << size << " at position " << position); + assert(false); + throw qpid::Exception("Buffer overflow in encoder!"); + } +} +Encoder::Encoder(char* d, size_t s) : data(d), size(s), position(0) {} +size_t Encoder::getPosition() { return position; } +void Encoder::resetPosition(size_t p) { assert(p <= size); position = p; } + +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/Encoder.h b/cpp/src/qpid/amqp/Encoder.h new file mode 100644 index 0000000000..e2938a002a --- /dev/null +++ b/cpp/src/qpid/amqp/Encoder.h @@ -0,0 +1,149 @@ +#ifndef QPID_AMQP_ENCODER_H +#define QPID_AMQP_ENCODER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/IntegerTypes.h" +#include "qpid/amqp/Constructor.h" +#include <stddef.h> +#include <string> + +namespace qpid { +namespace types { +class Uuid; +} +namespace amqp { +struct CharSequence; +struct Descriptor; + +/** + * Class to help create AMQP encoded data. + */ +class Encoder +{ + public: + void writeCode(uint8_t); + + void write(bool); + void write(uint8_t); + void write(uint16_t); + void write(uint32_t); + void write(uint64_t); + void write(int8_t); + void write(int16_t); + void write(int32_t); + void write(int64_t); + void write(float); + void write(double); + void write(const qpid::types::Uuid&); + + void writeNull(const Descriptor* d=0); + void writeBoolean(bool, const Descriptor* d=0); + void writeUByte(uint8_t, const Descriptor* d=0); + void writeUShort(uint16_t, const Descriptor* d=0); + void writeUInt(uint32_t, const Descriptor* d=0); + void writeULong(uint64_t, const Descriptor* d=0); + void writeByte(int8_t, const Descriptor* d=0); + void writeShort(int16_t, const Descriptor* d=0); + void writeInt(int32_t, const Descriptor* d=0); + void writeLong(int64_t, const Descriptor* d=0); + void writeFloat(float, const Descriptor* d=0); + void writeDouble(double, const Descriptor* d=0); + void writeUuid(const qpid::types::Uuid&, const Descriptor* d=0); + + void writeSymbol(const CharSequence&, const Descriptor* d=0); + void writeSymbol(const std::string&, const Descriptor* d=0); + void writeString(const CharSequence&, const Descriptor* d=0); + void writeString(const std::string&, const Descriptor* d=0); + void writeBinary(const CharSequence&, const Descriptor* d=0); + void writeBinary(const std::string&, const Descriptor* d=0); + + void* startList8(const Descriptor* d=0); + void* startList32(const Descriptor* d=0); + void endList8(uint8_t count, void*); + void endList32(uint32_t count, void*); + + void* startMap8(const Descriptor* d=0); + void* startMap32(const Descriptor* d=0); + void endMap8(uint8_t count, void*); + void endMap32(uint32_t count, void*); + + void* startArray8(const Constructor&, const Descriptor* d=0); + void* startArray32(const Constructor&, const Descriptor* d=0); + void endArray8(size_t count, void*); + void endArray32(size_t count, void*); + + void writeDescriptor(const Descriptor&); + Encoder(char* data, size_t size); + size_t getPosition(); + void resetPosition(size_t p); + char* skip(size_t); + void writeBytes(const char* bytes, size_t count); + virtual ~Encoder() {} + private: + char* data; + size_t size; + size_t position; + + void write(const CharSequence& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d); + void write(const std::string& v, std::pair<uint8_t, uint8_t> codes, const Descriptor* d); + void check(size_t); + + template<typename T> void write(T value, uint8_t code, const Descriptor* d) + { + if (d) writeDescriptor(*d); + writeCode(code); + write(value); + } + + template<typename T> void write(T value, std::pair<uint8_t, uint8_t> codes, const Descriptor* d) + { + if (value < 256) { + write((uint8_t) value, codes.first, d); + } else { + write(value, codes.second, d); + } + } + + template<typename T> void* start(uint8_t code, const Descriptor* d) + { + if (d) writeDescriptor(*d); + writeCode(code); + //skip size and count, will backfill on end + return skip(sizeof(T)/*size*/ + sizeof(T)/*count*/); + } + + template<typename T> void* startArray(uint8_t code, const Descriptor* d, const Constructor& c) + { + void* token = start<T>(code, d); + if (c.isDescribed) { + writeDescriptor(c.descriptor); + } + check(1); + writeCode(c.code); + return token; + } + +}; + +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_ENCODER_H*/ diff --git a/cpp/src/qpid/amqp/ListReader.h b/cpp/src/qpid/amqp/ListReader.h new file mode 100644 index 0000000000..dce874bf2f --- /dev/null +++ b/cpp/src/qpid/amqp/ListReader.h @@ -0,0 +1,103 @@ +#ifndef QPID_AMQP_LISTREADER_H +#define QPID_AMQP_LISTREADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Reader.h" + +namespace qpid { +namespace amqp { + +/** + * Utility to assist in reading AMQP encoded lists + */ +class ListReader : public Reader +{ + public: + ListReader() : index(0), level(0) {} + virtual ~ListReader() {} + virtual void onNull(const Descriptor* descriptor) { getReader().onNull(descriptor); } + virtual void onBoolean(bool v, const Descriptor* descriptor) { getReader().onBoolean(v, descriptor); } + virtual void onUByte(uint8_t v, const Descriptor* descriptor) { getReader().onUByte(v, descriptor); } + virtual void onUShort(uint16_t v, const Descriptor* descriptor) { getReader().onUShort(v, descriptor); } + virtual void onUInt(uint32_t v, const Descriptor* descriptor) { getReader().onUInt(v, descriptor); } + virtual void onULong(uint64_t v, const Descriptor* descriptor) { getReader().onULong(v, descriptor); } + virtual void onByte(int8_t v, const Descriptor* descriptor) { getReader().onByte(v, descriptor); } + virtual void onShort(int16_t v, const Descriptor* descriptor) { getReader().onShort(v, descriptor); } + virtual void onInt(int32_t v, const Descriptor* descriptor) { getReader().onInt(v, descriptor); } + virtual void onLong(int64_t v, const Descriptor* descriptor) { getReader().onLong(v, descriptor); } + virtual void onFloat(float v, const Descriptor* descriptor) { getReader().onFloat(v, descriptor); } + virtual void onDouble(double v, const Descriptor* descriptor) { getReader().onDouble(v, descriptor); } + virtual void onUuid(const CharSequence& v, const Descriptor* descriptor) { getReader().onUuid(v, descriptor); } + virtual void onTimestamp(int64_t v, const Descriptor* descriptor) { getReader().onTimestamp(v, descriptor); } + + virtual void onBinary(const CharSequence& v, const Descriptor* descriptor) { getReader().onBinary(v, descriptor); } + virtual void onString(const CharSequence& v, const Descriptor* descriptor) { getReader().onString(v, descriptor); } + virtual void onSymbol(const CharSequence& v, const Descriptor* descriptor) { getReader().onSymbol(v, descriptor); } + + virtual bool onStartList(uint32_t count, const CharSequence& v, const Descriptor* descriptor) + { + ++level; + getReader().onStartList(count, v, descriptor); + return false; + } + virtual void onEndList(uint32_t count, const Descriptor* descriptor) + { + --level; + getReader().onEndList(count, descriptor); + } + virtual bool onStartMap(uint32_t count, const CharSequence& v, const Descriptor* descriptor) + { + ++level; + getReader().onStartMap(count, v, descriptor); + return false; + } + virtual void onEndMap(uint32_t count, const Descriptor* descriptor) + { + --level; + getReader().onEndList(count, descriptor); + } + virtual bool onStartArray(uint32_t count, const CharSequence& v, const Constructor& c, const Descriptor* descriptor) + { + ++level; + getReader().onStartArray(count, v, c, descriptor); + return false; + } + virtual void onEndArray(uint32_t count, const Descriptor* descriptor) + { + --level; + getReader().onEndList(count, descriptor); + } + private: + size_t index; + size_t level; + Reader& getReader() + { + Reader& r = getReader(index); + if (level == 0) ++index; + return r; + } + protected: + virtual Reader& getReader(size_t i) = 0; +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_LISTREADER_H*/ diff --git a/cpp/src/qpid/amqp/LoggingReader.h b/cpp/src/qpid/amqp/LoggingReader.h new file mode 100644 index 0000000000..ed5cab1cbd --- /dev/null +++ b/cpp/src/qpid/amqp/LoggingReader.h @@ -0,0 +1,64 @@ +#ifndef QPID_AMQP_LOGGINGREADER_H +#define QPID_AMQP_LOGGINGREADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Reader.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace amqp { + +class LoggingReader : public Reader +{ + public: + virtual ~LoggingReader() {} + virtual void onNull(const Descriptor*) { if (!ignoreNull()) QPID_LOG(warning, prefix() << "null" << suffix()); } + virtual void onBoolean(bool, const Descriptor*) { QPID_LOG(warning, prefix() << "boolean" << suffix()); } + virtual void onUByte(uint8_t, const Descriptor*) { QPID_LOG(warning, prefix() << "ubyte" << suffix()); } + virtual void onUShort(uint16_t, const Descriptor*) { QPID_LOG(warning, prefix() << "ushort" << suffix()); } + virtual void onUInt(uint32_t, const Descriptor*) { QPID_LOG(warning, prefix() << "uint" << suffix()); } + virtual void onULong(uint64_t, const Descriptor*) { QPID_LOG(warning, prefix() << "ulong" << suffix()); } + virtual void onByte(int8_t, const Descriptor*) { QPID_LOG(warning, prefix() << "byte" << suffix()); } + virtual void onShort(int16_t, const Descriptor*) { QPID_LOG(warning, prefix() << "short" << suffix()); } + virtual void onInt(int32_t, const Descriptor*) { QPID_LOG(warning, prefix() << "int" << suffix()); } + virtual void onLong(int64_t, const Descriptor*) { QPID_LOG(warning, prefix() << "long" << suffix()); } + virtual void onFloat(float, const Descriptor*) { QPID_LOG(warning, prefix() << "float" << suffix()); } + virtual void onDouble(double, const Descriptor*) { QPID_LOG(warning, prefix() << "double" << suffix()); } + virtual void onUuid(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "uuid" << suffix()); } + virtual void onTimestamp(int64_t, const Descriptor*) { QPID_LOG(warning, prefix() << "timestamp" << suffix()); } + + virtual void onBinary(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "binary" << suffix()); } + virtual void onString(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "string" << suffix()); } + virtual void onSymbol(const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "symbol" << suffix()); } + + virtual bool onStartList(uint32_t, const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "list" << suffix()); return recursive; } + virtual bool onStartMap(uint32_t, const CharSequence&, const Descriptor*) { QPID_LOG(warning, prefix() << "map" << suffix()); return recursive; } + virtual bool onStartArray(uint32_t, const CharSequence&, const Constructor&, const Descriptor*) { QPID_LOG(warning, prefix() << "array" << suffix()); return recursive; } + protected: + virtual bool recursive() { return true; } + virtual bool ignoreNull() { return true; } + virtual std::string prefix() = 0; + virtual std::string suffix() = 0; +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_LOGGINGREADER_H*/ diff --git a/cpp/src/qpid/amqp/MessageEncoder.cpp b/cpp/src/qpid/amqp/MessageEncoder.cpp new file mode 100644 index 0000000000..852ad29635 --- /dev/null +++ b/cpp/src/qpid/amqp/MessageEncoder.cpp @@ -0,0 +1,313 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace amqp { + +namespace { +size_t optimisable(const MessageEncoder::Header& msg) +{ + if (msg.getDeliveryCount()) return 5; + else if (msg.isFirstAcquirer()) return 4; + else if (msg.hasTtl()) return 3; + else if (msg.getPriority() != 4) return 2; + else if (msg.isDurable()) return 1; + else return 0; +} + +size_t optimisable(const MessageEncoder::Properties& msg) +{ + if (msg.hasReplyToGroupId()) return 13; + else if (msg.hasGroupSequence()) return 12; + else if (msg.hasGroupId()) return 11; + else if (msg.hasCreationTime()) return 10; + else if (msg.hasAbsoluteExpiryTime()) return 9; + else if (msg.hasContentEncoding()) return 8; + else if (msg.hasContentType()) return 7; + else if (msg.hasCorrelationId()) return 6; + else if (msg.hasReplyTo()) return 5; + else if (msg.hasSubject()) return 4; + else if (msg.hasTo()) return 3; + else if (msg.hasUserId()) return 2; + else if (msg.hasMessageId()) return 1; + else return 0; +} +size_t encodedSize(const std::string& s) +{ + size_t total = s.size(); + if (total > 255) total += 4; + else total += 1; + return total; +} +const std::string BINARY("binary"); +} + +void MessageEncoder::writeHeader(const Header& msg) +{ + size_t fields(optimise ? optimisable(msg) : 5); + if (fields) { + void* token = startList8(&qpid::amqp::message::HEADER); + writeBoolean(msg.isDurable()); + if (fields > 1) writeUByte(msg.getPriority()); + + if (msg.getTtl()) writeUInt(msg.getTtl()); + else if (fields > 2) writeNull(); + + if (msg.isFirstAcquirer()) writeBoolean(true); + else if (fields > 3) writeNull(); + + if (msg.getDeliveryCount()) writeUInt(msg.getDeliveryCount()); + else if (fields > 4) writeNull(); + endList8(fields, token); + } +} + + +void MessageEncoder::writeProperties(const Properties& msg) +{ + size_t fields(optimise ? optimisable(msg) : 13); + if (fields) { + void* token = startList32(&qpid::amqp::message::PROPERTIES); + if (msg.hasMessageId()) writeString(msg.getMessageId()); + else writeNull(); + + if (msg.hasUserId()) writeBinary(msg.getUserId()); + else if (fields > 1) writeNull(); + + if (msg.hasTo()) writeString(msg.getTo()); + else if (fields > 2) writeNull(); + + if (msg.hasSubject()) writeString(msg.getSubject()); + else if (fields > 3) writeNull(); + + if (msg.hasReplyTo()) writeString(msg.getReplyTo()); + else if (fields > 4) writeNull(); + + if (msg.hasCorrelationId()) writeString(msg.getCorrelationId()); + else if (fields > 5) writeNull(); + + if (msg.hasContentType()) writeSymbol(msg.getContentType()); + else if (fields > 6) writeNull(); + + if (msg.hasContentEncoding()) writeSymbol(msg.getContentEncoding()); + else if (fields > 7) writeNull(); + + if (msg.hasAbsoluteExpiryTime()) writeLong(msg.getAbsoluteExpiryTime()); + else if (fields > 8) writeNull(); + + if (msg.hasCreationTime()) writeLong(msg.getCreationTime()); + else if (fields > 9) writeNull(); + + if (msg.hasGroupId()) writeString(msg.getGroupId()); + else if (fields > 10) writeNull(); + + if (msg.hasGroupSequence()) writeUInt(msg.getGroupSequence()); + else if (fields > 11) writeNull(); + + if (msg.hasReplyToGroupId()) writeString(msg.getReplyToGroupId()); + else if (fields > 12) writeNull(); + + endList32(fields, token); + } +} + +void MessageEncoder::writeApplicationProperties(const qpid::types::Variant::Map& properties) +{ + writeApplicationProperties(properties, !optimise || properties.size()*2 > 255 || getEncodedSizeForElements(properties) > 255); +} + +void MessageEncoder::writeApplicationProperties(const qpid::types::Variant::Map& properties, bool large) +{ + writeMap(properties, &qpid::amqp::message::APPLICATION_PROPERTIES, large); +} + +void MessageEncoder::writeMap(const qpid::types::Variant::Map& properties, const Descriptor* d, bool large) +{ + void* token = large ? startMap32(d) : startMap8(d); + for (qpid::types::Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) { + writeString(i->first); + switch (i->second.getType()) { + case qpid::types::VAR_MAP: + case qpid::types::VAR_LIST: + //not allowed (TODO: revise, only strictly true for application-properties) whereas this is now a more general method) + QPID_LOG(warning, "Ignoring nested map/list; not allowed in application-properties for AMQP 1.0"); + case qpid::types::VAR_VOID: + writeNull(); + break; + case qpid::types::VAR_BOOL: + writeBoolean(i->second); + break; + case qpid::types::VAR_UINT8: + writeUByte(i->second); + break; + case qpid::types::VAR_UINT16: + writeUShort(i->second); + break; + case qpid::types::VAR_UINT32: + writeUInt(i->second); + break; + case qpid::types::VAR_UINT64: + writeULong(i->second); + break; + case qpid::types::VAR_INT8: + writeByte(i->second); + break; + case qpid::types::VAR_INT16: + writeShort(i->second); + break; + case qpid::types::VAR_INT32: + writeInt(i->second); + break; + case qpid::types::VAR_INT64: + writeULong(i->second); + break; + case qpid::types::VAR_FLOAT: + writeFloat(i->second); + break; + case qpid::types::VAR_DOUBLE: + writeDouble(i->second); + break; + case qpid::types::VAR_STRING: + if (i->second.getEncoding() == BINARY) { + writeBinary(i->second); + } else { + writeString(i->second); + } + break; + case qpid::types::VAR_UUID: + writeUuid(i->second); + break; + } + } + if (large) endMap32(properties.size()*2, token); + else endMap8(properties.size()*2, token); +} + +size_t MessageEncoder::getEncodedSize(const Header& h, const Properties& p, const qpid::types::Variant::Map& ap, const std::string& d) +{ + //NOTE: this does not take optional optimisation into account, + //i.e. it is a 'worst case' estimate for required buffer space + size_t total(0); + + //header: + total += 3/*descriptor*/ + 1/*code*/ + 1/*size*/ + 1/*count*/ + 5/*codes for each field*/; + if (h.getPriority() != 4) total += 1; + if (h.getDeliveryCount()) total += 4; + if (h.hasTtl()) total += 4; + return total + getEncodedSize(p, ap, d); +} + +size_t MessageEncoder::getEncodedSize(const Properties& p, const qpid::types::Variant::Map& ap, const std::string& d) +{ + //NOTE: this does not take optional optimisation into account, + //i.e. it is a 'worst case' estimate for required buffer space + size_t total(0); + + //properties: + total += 3/*descriptor*/ + 1/*code*/ + 4/*size*/ + 4/*count*/ + 13/*codes for each field*/; + if (p.hasMessageId()) total += encodedSize(p.getMessageId()); + if (p.hasUserId()) total += encodedSize(p.getUserId()); + if (p.hasTo()) total += encodedSize(p.getTo()); + if (p.hasSubject()) total += encodedSize(p.getSubject()); + if (p.hasReplyTo()) total += encodedSize(p.getReplyTo()); + if (p.hasCorrelationId()) total += encodedSize(p.getCorrelationId()); + if (p.hasContentType()) total += encodedSize(p.getContentType()); + if (p.hasContentEncoding()) total += encodedSize(p.getContentEncoding()); + if (p.hasAbsoluteExpiryTime()) total += 8; + if (p.hasCreationTime()) total += 8; + if (p.hasGroupId()) total += encodedSize(p.getGroupId()); + if (p.hasGroupSequence()) total += 4; + if (p.hasReplyToGroupId()) total += encodedSize(p.getReplyToGroupId()); + + + //application-properties: + total += 3/*descriptor*/ + getEncodedSize(ap, true); + //body: + if (d.size()) total += 3/*descriptor*/ + 1/*code*/ + encodedSize(d); + + return total; +} + +size_t MessageEncoder::getEncodedSizeForElements(const qpid::types::Variant::Map& map) +{ + size_t total = 0; + for (qpid::types::Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) { + total += 1/*code*/ + encodedSize(i->first); + + switch (i->second.getType()) { + case qpid::types::VAR_MAP: + case qpid::types::VAR_LIST: + case qpid::types::VAR_VOID: + case qpid::types::VAR_BOOL: + total += 1; + break; + + case qpid::types::VAR_UINT8: + case qpid::types::VAR_INT8: + total += 2; + break; + + case qpid::types::VAR_UINT16: + case qpid::types::VAR_INT16: + total += 3; + break; + + case qpid::types::VAR_UINT32: + case qpid::types::VAR_INT32: + case qpid::types::VAR_FLOAT: + total += 5; + break; + + case qpid::types::VAR_UINT64: + case qpid::types::VAR_INT64: + case qpid::types::VAR_DOUBLE: + total += 9; + break; + + case qpid::types::VAR_UUID: + total += 17; + break; + + case qpid::types::VAR_STRING: + total += 1/*code*/ + encodedSize(i->second); + break; + } + } + return total; +} + + +size_t MessageEncoder::getEncodedSize(const qpid::types::Variant::Map& map, bool alwaysUseLargeMap) +{ + size_t total = getEncodedSizeForElements(map); + + //its not just the count that determines whether we can use a small map, but the aggregate size: + if (alwaysUseLargeMap || map.size()*2 > 255 || total > 255) total += 4/*size*/ + 4/*count*/; + else total += 1/*size*/ + 1/*count*/; + + total += 1 /*code for map itself*/; + + return total; +} +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/MessageEncoder.h b/cpp/src/qpid/amqp/MessageEncoder.h new file mode 100644 index 0000000000..3db0763e8a --- /dev/null +++ b/cpp/src/qpid/amqp/MessageEncoder.h @@ -0,0 +1,100 @@ +#ifndef QPID_AMQP_MESSAGEENCODER_H +#define QPID_AMQP_MESSAGEENCODER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Encoder.h" +#include "qpid/types/Variant.h" + +namespace qpid { +namespace amqp { + +/** + * + */ +class MessageEncoder : public Encoder +{ + public: + class Header + { + public: + virtual ~Header() {} + virtual bool isDurable() const = 0; + virtual uint8_t getPriority() const = 0; + virtual bool hasTtl() const = 0; + virtual uint32_t getTtl() const = 0; + virtual bool isFirstAcquirer() const = 0; + virtual uint32_t getDeliveryCount() const = 0; + }; + + class Properties + { + public: + virtual ~Properties() {} + virtual bool hasMessageId() const = 0; + virtual std::string getMessageId() const = 0; + virtual bool hasUserId() const = 0; + virtual std::string getUserId() const = 0; + virtual bool hasTo() const = 0; + virtual std::string getTo() const = 0; + virtual bool hasSubject() const = 0; + virtual std::string getSubject() const = 0; + virtual bool hasReplyTo() const = 0; + virtual std::string getReplyTo() const = 0; + virtual bool hasCorrelationId() const = 0; + virtual std::string getCorrelationId() const = 0; + virtual bool hasContentType() const = 0; + virtual std::string getContentType() const = 0; + virtual bool hasContentEncoding() const = 0; + virtual std::string getContentEncoding() const = 0; + virtual bool hasAbsoluteExpiryTime() const = 0; + virtual int64_t getAbsoluteExpiryTime() const = 0; + virtual bool hasCreationTime() const = 0; + virtual int64_t getCreationTime() const = 0; + virtual bool hasGroupId() const = 0; + virtual std::string getGroupId() const = 0; + virtual bool hasGroupSequence() const = 0; + virtual uint32_t getGroupSequence() const = 0; + virtual bool hasReplyToGroupId() const = 0; + virtual std::string getReplyToGroupId() const = 0; + }; + + MessageEncoder(char* d, size_t s, bool o=false) : Encoder(d, s), optimise(o) {} + void writeHeader(const Header&); + void writeProperties(const Properties&); + void writeApplicationProperties(const qpid::types::Variant::Map& properties); + void writeApplicationProperties(const qpid::types::Variant::Map& properties, bool useLargeMap); + + void writeMap(const qpid::types::Variant::Map& map, const Descriptor*, bool useLargeMap); + + static size_t getEncodedSize(const Header&, const Properties&, const qpid::types::Variant::Map&, const std::string&); + static size_t getEncodedSize(const Properties&, const qpid::types::Variant::Map&, const std::string&); + static size_t getEncodedSize(const qpid::types::Variant::Map&, bool useLargeMap); + static size_t getEncodedSize(const qpid::types::Variant::Map&); + + private: + bool optimise; + + static size_t getEncodedSizeForElements(const qpid::types::Variant::Map&); +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_MESSAGEENCODER_H*/ diff --git a/cpp/src/qpid/amqp/MessageId.cpp b/cpp/src/qpid/amqp/MessageId.cpp new file mode 100644 index 0000000000..05ee77874a --- /dev/null +++ b/cpp/src/qpid/amqp/MessageId.cpp @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/MessageId.h" +#include <boost/lexical_cast.hpp> + +namespace qpid { +namespace amqp { + +MessageId::MessageId() : type(BYTES) +{ + value.bytes.data = 0; + value.bytes.size = 0; +} +void MessageId::assign(std::string& s) const +{ + switch (type) { + case BYTES: + if (value.bytes) s.assign(value.bytes.data, value.bytes.size); + break; + case UUID: + s = qpid::types::Uuid(value.bytes).str(); + break; + case ULONG: + s = boost::lexical_cast<std::string>(value.ulong); + break; + } +} + +void MessageId::set(qpid::amqp::CharSequence bytes, qpid::types::VariantType t) +{ + switch (t) { + case qpid::types::VAR_STRING: + type = BYTES; + break; + case qpid::types::VAR_UUID: + type = UUID; + assert(bytes.size == 16); + break; + default: + assert(false); + } + value.bytes = bytes; +} +void MessageId::set(uint64_t ulong) +{ + type = ULONG; + value.ulong = ulong; +} + +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/MessageId.h b/cpp/src/qpid/amqp/MessageId.h new file mode 100644 index 0000000000..f81dc9be2c --- /dev/null +++ b/cpp/src/qpid/amqp/MessageId.h @@ -0,0 +1,53 @@ +#ifndef QPID_AMQP_MESSAGEID_H +#define QPID_AMQP_MESSAGEID_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/CharSequence.h" +#include "qpid/types/Variant.h" + +namespace qpid { +namespace amqp { + +struct MessageId +{ + union + { + qpid::amqp::CharSequence bytes; + uint64_t ulong; + } value; + enum + { + BYTES, + UUID, + ULONG + } type; + + MessageId(); + void assign(std::string&) const; + void set(qpid::amqp::CharSequence bytes, qpid::types::VariantType t); + void set(uint64_t ulong); + +}; + +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_MESSAGEID_H*/ diff --git a/cpp/src/qpid/amqp/MessageReader.cpp b/cpp/src/qpid/amqp/MessageReader.cpp new file mode 100644 index 0000000000..1550fa1977 --- /dev/null +++ b/cpp/src/qpid/amqp/MessageReader.cpp @@ -0,0 +1,759 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/MessageReader.h" +#include "qpid/amqp/Descriptor.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/types/Uuid.h" +#include "qpid/types/Variant.h" +#include "qpid/log/Statement.h" + +using namespace qpid::amqp::message; + +namespace qpid { +namespace amqp { +namespace { + +//header fields: +const size_t DURABLE(0); +const size_t PRIORITY(1); +const size_t TTL(2); +const size_t FIRST_ACQUIRER(3); +const size_t DELIVERY_COUNT(4); + +//properties fields: +const size_t MESSAGE_ID(0); +const size_t USER_ID(1); +const size_t TO(2); +const size_t SUBJECT(3); +const size_t REPLY_TO(4); +const size_t CORRELATION_ID(5); +const size_t CONTENT_TYPE(6); +const size_t CONTENT_ENCODING(7); +const size_t ABSOLUTE_EXPIRY_TIME(8); +const size_t CREATION_TIME(9); +const size_t GROUP_ID(10); +const size_t GROUP_SEQUENCE(11); +const size_t REPLY_TO_GROUP_ID(12); + +} + +/* +Reader& MessageReader::HeaderReader::getReader(size_t index) +{ + switch (index) { + case DURABLE: return durableReader; + case PRIORITY: return priorityReader; + case TTL: return ttlReader; + case FIRST_ACQUIRER: return firstAcquirerReader; + case DELIVERY_COUNT: return deliveryCountReader; + default: return noSuchFieldReader; + } +} + +Reader& MessageReader::PropertiesReader::getReader(size_t index) +{ + switch (index) { + case MESSAGE_ID: return messageIdReader; + case USER_ID: return userIdReader; + case TO: return toReader; + case SUBJECT: return subjectReader; + case REPLY_TO: return replyToReader; + case CORRELATION_ID: return correlationIdReader; + case CONTENT_TYPE: return contentTypeReader; + case CONTENT_ENCODING: return contentEncodingReader; + case ABSOLUTE_EXPIRY_TIME: return absoluteExpiryTimeReader; + case CREATION_TIME: return creationTimeReader; + case GROUP_ID: return groupIdReader; + case GROUP_SEQUENCE: return groupSequenceReader; + case REPLY_TO_GROUP_ID: return replyToGroupIdReader; + default: return noSuchFieldReader; + } +} +*/ + +MessageReader::HeaderReader::HeaderReader(MessageReader& p) : parent(p), index(0) {} +void MessageReader::HeaderReader::onBoolean(bool v, const Descriptor*) // durable, first-acquirer +{ + if (index == DURABLE) { + parent.onDurable(v); + } else if (index == FIRST_ACQUIRER) { + parent.onFirstAcquirer(v); + } else { + QPID_LOG(warning, "Unexpected message format, got boolean at index " << index << " of headers"); + } + ++index; +} +void MessageReader::HeaderReader::onUByte(uint8_t v, const Descriptor*) // priority +{ + if (index == PRIORITY) { + parent.onPriority(v); + } else { + QPID_LOG(warning, "Unexpected message format, got ubyte at index " << index << " of headers"); + } + ++index; +} +void MessageReader::HeaderReader::onUInt(uint32_t v, const Descriptor*) // ttl, delivery-count +{ + if (index == TTL) { + parent.onTtl(v); + } else if (index == DELIVERY_COUNT) { + parent.onDeliveryCount(v); + } else { + QPID_LOG(warning, "Unexpected message format, got uint at index " << index << " of headers"); + } + ++index; +} +void MessageReader::HeaderReader::onNull(const Descriptor*) +{ + ++index; +} + +MessageReader::PropertiesReader::PropertiesReader(MessageReader& p) : parent(p), index(0) {} +void MessageReader::PropertiesReader::onUuid(const CharSequence& v, const Descriptor*) // message-id, correlation-id +{ + if (index == MESSAGE_ID) { + parent.onMessageId(v, qpid::types::VAR_UUID); + } else if (index == CORRELATION_ID) { + parent.onCorrelationId(v); + } else { + QPID_LOG(warning, "Unexpected message format, got uuid at index " << index << " of properties"); + } + ++index; +} +void MessageReader::PropertiesReader::onULong(uint64_t v, const Descriptor*) // message-id, correlation-id +{ + if (index == MESSAGE_ID) { + parent.onMessageId(v); + } else if (index == CORRELATION_ID) { + parent.onCorrelationId(v); + } else { + QPID_LOG(warning, "Unexpected message format, got long at index " << index << " of properties"); + } + ++index; +} +void MessageReader::PropertiesReader::onBinary(const CharSequence& v, const Descriptor*) // message-id, correlation-id, user-id +{ + if (index == MESSAGE_ID) { + parent.onMessageId(v, qpid::types::VAR_STRING); + } else if (index == CORRELATION_ID) { + parent.onCorrelationId(v); + } else if (index == USER_ID) { + parent.onUserId(v); + } else { + QPID_LOG(warning, "Unexpected message format, got binary at index " << index << " of properties"); + } + ++index; +} +void MessageReader::PropertiesReader::onString(const CharSequence& v, const Descriptor*) // message-id, correlation-id, group-id, reply-to-group-id, subject, to, reply-to +{ + if (index == MESSAGE_ID) { + parent.onMessageId(v); + } else if (index == CORRELATION_ID) { + parent.onCorrelationId(v); + } else if (index == GROUP_ID) { + parent.onGroupId(v); + } else if (index == REPLY_TO_GROUP_ID) { + parent.onReplyToGroupId(v); + } else if (index == SUBJECT) { + parent.onSubject(v); + } else if (index == TO) { + parent.onTo(v); + } else if (index == REPLY_TO) { + parent.onReplyTo(v); + } else { + QPID_LOG(warning, "Unexpected message format, got string at index " << index << " of properties"); + } + ++index; +} +void MessageReader::PropertiesReader::onSymbol(const CharSequence& v, const Descriptor*) // content-type, content-encoding +{ + if (index == CONTENT_TYPE) { + parent.onContentType(v); + } else if (index == CONTENT_ENCODING) { + parent.onContentEncoding(v); + } else { + QPID_LOG(warning, "Unexpected message format, got symbol at index " << index << " of properties"); + } + ++index; +} +void MessageReader::PropertiesReader::onTimestamp(int64_t v, const Descriptor*) // absolute-expiry-time, creation-time +{ + if (index == ABSOLUTE_EXPIRY_TIME) { + parent.onAbsoluteExpiryTime(v); + } else if (index == CREATION_TIME) { + parent.onCreationTime(v); + } else { + QPID_LOG(warning, "Unexpected message format, got timestamp at index " << index << " of properties"); + } + ++index; +} +void MessageReader::PropertiesReader::onUInt(uint32_t v, const Descriptor*) // group-sequence +{ + if (index == GROUP_SEQUENCE) { + parent.onGroupSequence(v); + } else { + QPID_LOG(warning, "Unexpected message format, got uint at index " << index << " of properties"); + } + ++index; +} +void MessageReader::PropertiesReader::onNull(const Descriptor*) +{ + ++index; +} + +/* +MessageReader::DurableReader::DurableReader(MessageReader& p) : parent(p) {} +void MessageReader::DurableReader::onBoolean(bool v, const Descriptor*) +{ + parent.onDurable(v); +} +MessageReader::PriorityReader::PriorityReader(MessageReader& p) : parent(p) {} +void MessageReader::PriorityReader::onUByte(uint8_t v, const Descriptor*) +{ + parent.onPriority(v); +} +MessageReader::TtlReader::TtlReader(MessageReader& p) : parent(p) {} +void MessageReader::TtlReader::onUInt(uint32_t v, const Descriptor*) +{ + parent.onTtl(v); +} +MessageReader::FirstAcquirerReader::FirstAcquirerReader(MessageReader& p) : parent(p) {} +void MessageReader::FirstAcquirerReader::onBoolean(bool v, const Descriptor*) +{ + parent.onFirstAcquirer(v); +} +MessageReader::DeliveryCountReader::DeliveryCountReader(MessageReader& p) : parent(p) {} +void MessageReader::DeliveryCountReader::onUInt(uint32_t v, const Descriptor*) +{ + parent.onDeliveryCount(v); +} +MessageReader::MessageIdReader::MessageIdReader(MessageReader& p) : parent(p) {} +void MessageReader::MessageIdReader::onUuid(const qpid::types::Uuid& v, const Descriptor*) +{ + parent.onMessageId(v); +} +void MessageReader::MessageIdReader::onULong(uint64_t v, const Descriptor*) +{ + parent.onMessageId(v); +} +void MessageReader::MessageIdReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onMessageId(v); +} +void MessageReader::MessageIdReader::onBinary(const CharSequence& v, const Descriptor*) +{ + parent.onMessageId(v); +} +MessageReader::UserIdReader::UserIdReader(MessageReader& p) : parent(p) {} +void MessageReader::UserIdReader::onBinary(const CharSequence& v, const Descriptor*) +{ + parent.onUserId(v); +} +MessageReader::ToReader::ToReader(MessageReader& p) : parent(p) {} +void MessageReader::ToReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onTo(v); +} +MessageReader::SubjectReader::SubjectReader(MessageReader& p) : parent(p) {} +void MessageReader::SubjectReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onSubject(v); +} +MessageReader::ReplyToReader::ReplyToReader(MessageReader& p) : parent(p) {} +void MessageReader::ReplyToReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onReplyTo(v); +} +MessageReader::CorrelationIdReader::CorrelationIdReader(MessageReader& p) : parent(p) {} +void MessageReader::CorrelationIdReader::onUuid(const qpid::types::Uuid& v, const Descriptor*) +{ + parent.onCorrelationId(v); +} +void MessageReader::CorrelationIdReader::onULong(uint64_t v, const Descriptor*) +{ + parent.onCorrelationId(v); +} +void MessageReader::CorrelationIdReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onCorrelationId(v); +} +void MessageReader::CorrelationIdReader::onBinary(const CharSequence& v, const Descriptor*) +{ + parent.onCorrelationId(v); +} +MessageReader::ContentTypeReader::ContentTypeReader(MessageReader& p) : parent(p) {} +void MessageReader::ContentTypeReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onContentType(v); +} +MessageReader::ContentEncodingReader::ContentEncodingReader(MessageReader& p) : parent(p) {} +void MessageReader::ContentEncodingReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onContentEncoding(v); +} +MessageReader::AbsoluteExpiryTimeReader::AbsoluteExpiryTimeReader(MessageReader& p) : parent(p) {} +void MessageReader::AbsoluteExpiryTimeReader::onTimestamp(int64_t v, const Descriptor*) +{ + parent.onAbsoluteExpiryTime(v); +} +MessageReader::CreationTimeReader::CreationTimeReader(MessageReader& p) : parent(p) {} +void MessageReader::CreationTimeReader::onTimestamp(int64_t v, const Descriptor*) +{ + parent.onCreationTime(v); +} +MessageReader::GroupIdReader::GroupIdReader(MessageReader& p) : parent(p) {} +void MessageReader::GroupIdReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onGroupId(v); +} +MessageReader::GroupSequenceReader::GroupSequenceReader(MessageReader& p) : parent(p) {} +void MessageReader::GroupSequenceReader::onUInt(uint32_t v, const Descriptor*) +{ + parent.onGroupSequence(v); +} +MessageReader::ReplyToGroupIdReader::ReplyToGroupIdReader(MessageReader& p) : parent(p) {} +void MessageReader::ReplyToGroupIdReader::onString(const CharSequence& v, const Descriptor*) +{ + parent.onReplyToGroupId(v); +} +*/ + +//header, properties, amqp-sequence, amqp-value +bool MessageReader::onStartList(uint32_t count, const CharSequence& raw, const Descriptor* descriptor) +{ + if (delegate) { + return delegate->onStartList(count, raw, descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got no descriptor for list."); + return false; + } else if (descriptor->match(HEADER_SYMBOL, HEADER_CODE)) { + delegate = &headerReader; + return true; + } else if (descriptor->match(PROPERTIES_SYMBOL, PROPERTIES_CODE)) { + delegate = &propertiesReader; + return true; + } else if (descriptor->match(AMQP_SEQUENCE_SYMBOL, AMQP_SEQUENCE_CODE) || descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + onBody(raw, *descriptor); + return false; + } else { + QPID_LOG(warning, "Unexpected described list: " << *descriptor); + return false; + } + } +} +void MessageReader::onEndList(uint32_t count, const Descriptor* descriptor) +{ + if (delegate) { + if (descriptor && (descriptor->match(HEADER_SYMBOL, HEADER_CODE) || descriptor->match(PROPERTIES_SYMBOL, PROPERTIES_CODE))) { + delegate = 0; + } else { + delegate->onEndList(count, descriptor); + } + } +} + +//delivery-annotations, message-annotations, application-properties, amqp-value +bool MessageReader::onStartMap(uint32_t count, const CharSequence& raw, const Descriptor* descriptor) +{ + if (delegate) { + return delegate->onStartMap(count, raw, descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got no descriptor for map."); + return false; + } else if (descriptor->match(DELIVERY_ANNOTATIONS_SYMBOL, DELIVERY_ANNOTATIONS_CODE)) { + onDeliveryAnnotations(raw); + return false; + } else if (descriptor->match(MESSAGE_ANNOTATIONS_SYMBOL, MESSAGE_ANNOTATIONS_CODE)) { + onMessageAnnotations(raw); + return false; + } else if (descriptor->match(FOOTER_SYMBOL, FOOTER_CODE)) { + onFooter(raw); + return false; + } else if (descriptor->match(APPLICATION_PROPERTIES_SYMBOL, APPLICATION_PROPERTIES_CODE)) { + onApplicationProperties(raw); + return false; + } else if (descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + onBody(raw, *descriptor); + return false; + } else { + QPID_LOG(warning, "Unexpected described map: " << *descriptor); + return false; + } + } +} + +void MessageReader::onEndMap(uint32_t count, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onEndMap(count, descriptor); + } +} + +//data, amqp-value +void MessageReader::onBinary(const CharSequence& bytes, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onBinary(bytes, descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got binary value with no descriptor."); + } else if (descriptor->match(DATA_SYMBOL, DATA_CODE) || descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + onBody(bytes, *descriptor); + } else { + QPID_LOG(warning, "Unexpected binary value with descriptor: " << *descriptor); + } + } + +} + +//amqp-value +void MessageReader::onNull(const Descriptor* descriptor) +{ + if (delegate) { + delegate->onNull(descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant v; + onBody(v, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got null value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected null value with descriptor: " << *descriptor); + } + } + } +} +void MessageReader::onString(const CharSequence& v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onString(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + onBody(v, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got string value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected string value with descriptor: " << *descriptor); + } + } + } +} +void MessageReader::onSymbol(const CharSequence& v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onSymbol(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + onBody(v, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got symbol value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected symbol value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onBoolean(bool v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onBoolean(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got boolean value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected boolean value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onUByte(uint8_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onUByte(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got ubyte value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected ubyte value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onUShort(uint16_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onUShort(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got ushort value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected ushort value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onUInt(uint32_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onUInt(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got uint value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected uint value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onULong(uint64_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onULong(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got ulong value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected ulong value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onByte(int8_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onByte(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got byte value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected byte value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onShort(int16_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onShort(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got short value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected short value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onInt(int32_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onInt(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got int value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected int value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onLong(int64_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onLong(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got long value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected long value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onFloat(float v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onFloat(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got float value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected float value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onDouble(double v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onDouble(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got double value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected double value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onUuid(const CharSequence& v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onUuid(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + onBody(v, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got uuid value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected uuid value with descriptor: " << *descriptor); + } + } + } +} + +void MessageReader::onTimestamp(int64_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onTimestamp(v, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + qpid::types::Variant body = v; + onBody(body, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got timestamp value with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected timestamp value with descriptor: " << *descriptor); + } + } + } +} + +bool MessageReader::onStartArray(uint32_t count, const CharSequence& raw, const Constructor& constructor, const Descriptor* descriptor) +{ + if (delegate) { + return delegate->onStartArray(count, raw, constructor, descriptor); + } else { + if (descriptor && descriptor->match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE)) { + onBody(raw, *descriptor); + } else { + if (!descriptor) { + QPID_LOG(warning, "Expected described type but got array with no descriptor."); + } else { + QPID_LOG(warning, "Unexpected array with descriptor: " << *descriptor); + } + } + return false; + } +} + +void MessageReader::onEndArray(uint32_t v, const Descriptor* descriptor) +{ + if (delegate) { + delegate->onEndArray(v, descriptor); + } +} + +MessageReader::MessageReader() : headerReader(*this), propertiesReader(*this), delegate(0) +{ + bare.init(); +} + +void MessageReader::onDescriptor(const Descriptor& descriptor, const char* position) +{ + if (bare.data) { + if (descriptor.match(FOOTER_SYMBOL, FOOTER_CODE)) { + bare.size = position - bare.data; + } + } else { + if (descriptor.match(PROPERTIES_SYMBOL, PROPERTIES_CODE) || descriptor.match(APPLICATION_PROPERTIES_SYMBOL, APPLICATION_PROPERTIES_CODE) + || descriptor.match(AMQP_SEQUENCE_SYMBOL, AMQP_SEQUENCE_CODE) || descriptor.match(AMQP_VALUE_SYMBOL, AMQP_VALUE_CODE) || descriptor.match(DATA_SYMBOL, DATA_CODE)) { + bare.data = position; + } + } +} + +CharSequence MessageReader::getBareMessage() const { return bare; } + +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/MessageReader.h b/cpp/src/qpid/amqp/MessageReader.h new file mode 100644 index 0000000000..b2c23bae49 --- /dev/null +++ b/cpp/src/qpid/amqp/MessageReader.h @@ -0,0 +1,300 @@ +#ifndef QPID_AMQP_MESSAGEREADER_H +#define QPID_AMQP_MESSAGEREADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/amqp/CharSequence.h" +#include "qpid/amqp/Reader.h" +#include "qpid/amqp/ListReader.h" +#include "qpid/types/Variant.h" + +namespace qpid { +namespace amqp { + +/** + * Reader for an AMQP 1.0 message + */ +class MessageReader : public Reader +{ + public: + MessageReader(); + + //header, properties, amqp-sequence, amqp-value + bool onStartList(uint32_t, const CharSequence&, const Descriptor*); + void onEndList(uint32_t, const Descriptor*); + + //delivery-annotations, message-annotations, application-headers, amqp-value + bool onStartMap(uint32_t, const CharSequence&, const Descriptor*); + void onEndMap(uint32_t, const Descriptor*); + + //data, amqp-value + void onBinary(const CharSequence&, const Descriptor*); + + //amqp-value + void onNull(const Descriptor*); + void onString(const CharSequence&, const Descriptor*); + void onSymbol(const CharSequence&, const Descriptor*); + void onBoolean(bool, const Descriptor*); + void onUByte(uint8_t, const Descriptor*); + void onUShort(uint16_t, const Descriptor*); + void onUInt(uint32_t, const Descriptor*); + void onULong(uint64_t, const Descriptor*); + void onByte(int8_t, const Descriptor*); + void onShort(int16_t, const Descriptor*); + void onInt(int32_t, const Descriptor*); + void onLong(int64_t, const Descriptor*); + void onFloat(float, const Descriptor*); + void onDouble(double, const Descriptor*); + void onUuid(const CharSequence&, const Descriptor*); + void onTimestamp(int64_t, const Descriptor*); + bool onStartArray(uint32_t, const CharSequence&, const Constructor&, const Descriptor*); + void onEndArray(uint32_t, const Descriptor*); + void onDescriptor(const Descriptor&, const char*); + + //header: + virtual void onDurable(bool) = 0; + virtual void onPriority(uint8_t) = 0; + virtual void onTtl(uint32_t) = 0; + virtual void onFirstAcquirer(bool) = 0; + virtual void onDeliveryCount(uint32_t) = 0; + + //properties: + virtual void onMessageId(uint64_t) = 0; + virtual void onMessageId(const CharSequence&, qpid::types::VariantType) = 0; + virtual void onUserId(const CharSequence&) = 0; + virtual void onTo(const CharSequence&) = 0; + virtual void onSubject(const CharSequence&) = 0; + virtual void onReplyTo(const CharSequence&) = 0; + virtual void onCorrelationId(uint64_t) = 0; + virtual void onCorrelationId(const CharSequence&, qpid::types::VariantType) = 0; + virtual void onContentType(const CharSequence&) = 0; + virtual void onContentEncoding(const CharSequence&) = 0; + virtual void onAbsoluteExpiryTime(int64_t) = 0; + virtual void onCreationTime(int64_t) = 0; + virtual void onGroupId(const CharSequence&) = 0; + virtual void onGroupSequence(uint32_t) = 0; + virtual void onReplyToGroupId(const CharSequence&) = 0; + + virtual void onApplicationProperties(const CharSequence&) = 0; + virtual void onDeliveryAnnotations(const CharSequence&) = 0; + virtual void onMessageAnnotations(const CharSequence&) = 0; + virtual void onBody(const CharSequence&, const Descriptor&) = 0; + virtual void onBody(const qpid::types::Variant&, const Descriptor&) = 0; + virtual void onFooter(const CharSequence&) = 0; + + CharSequence getBareMessage() const; + + private: + /* + class DurableReader : public Reader + { + public: + DurableReader(MessageReader&); + void onBoolean(bool v, const Descriptor*); + private: + MessageReader& parent; + }; + class PriorityReader : public Reader + { + public: + PriorityReader(MessageReader&); + void onUByte(uint8_t v, const Descriptor*); + private: + MessageReader& parent; + }; + class TtlReader : public Reader + { + public: + TtlReader(MessageReader&); + void onUInt(uint32_t v, const Descriptor*); + private: + MessageReader& parent; + }; + class FirstAcquirerReader : public Reader + { + public: + FirstAcquirerReader(MessageReader&); + void onBoolean(bool v, const Descriptor*); + private: + MessageReader& parent; + }; + class DeliveryCountReader : public Reader + { + public: + DeliveryCountReader(MessageReader&); + void onUInt(uint32_t v, const Descriptor*); + private: + MessageReader& parent; + }; + + class MessageIdReader : public Reader + { + public: + MessageIdReader(MessageReader&); + void onUuid(const qpid::types::Uuid& v, const Descriptor*); + void onULong(uint64_t v, const Descriptor*); + void onString(const CharSequence& v, const Descriptor*); + void onBinary(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class UserIdReader : public Reader + { + public: + UserIdReader(MessageReader&); + void onBinary(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class ToReader : public Reader + { + public: + ToReader(MessageReader&); + void onString(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class SubjectReader : public Reader + { + public: + SubjectReader(MessageReader&); + void onString(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class ReplyToReader : public Reader + { + public: + ReplyToReader(MessageReader&); + void onString(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class CorrelationIdReader : public Reader + { + public: + CorrelationIdReader(MessageReader&); + void onUuid(const qpid::types::Uuid& v, const Descriptor*); + void onULong(uint64_t v, const Descriptor*); + void onString(const CharSequence& v, const Descriptor*); + void onBinary(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class ContentTypeReader : public Reader + { + public: + ContentTypeReader(MessageReader&); + void onString(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class ContentEncodingReader : public Reader + { + public: + ContentEncodingReader(MessageReader&); + void onString(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class AbsoluteExpiryTimeReader : public Reader + { + public: + AbsoluteExpiryTimeReader(MessageReader&); + void onTimestamp(int64_t v, const Descriptor*); + private: + MessageReader& parent; + }; + class CreationTimeReader : public Reader + { + public: + CreationTimeReader(MessageReader&); + void onTimestamp(int64_t v, const Descriptor*); + private: + MessageReader& parent; + }; + class GroupIdReader : public Reader + { + public: + GroupIdReader(MessageReader&); + void onString(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + class GroupSequenceReader : public Reader + { + public: + GroupSequenceReader(MessageReader&); + void onUInt(uint32_t v, const Descriptor*); + private: + MessageReader& parent; + }; + class ReplyToGroupIdReader : public Reader + { + public: + ReplyToGroupIdReader(MessageReader&); + void onString(const CharSequence& v, const Descriptor*); + private: + MessageReader& parent; + }; + */ + + class HeaderReader : public Reader //public ListReader + { + public: + //Reader& getReader(size_t index); + + HeaderReader(MessageReader&); + void onBoolean(bool v, const Descriptor*); // durable, first-acquirer + void onUByte(uint8_t v, const Descriptor*); // priority + void onUInt(uint32_t v, const Descriptor*); // ttl, delivery-count + void onNull(const Descriptor*); + private: + MessageReader& parent; + size_t index; + }; + class PropertiesReader : public Reader //public ListReader + { + public: + //Reader& getReader(size_t index); + + PropertiesReader(MessageReader&); + void onUuid(const CharSequence& v, const Descriptor*); // message-id, correlation-id + void onULong(uint64_t v, const Descriptor*); // message-id, correlation-id + void onBinary(const CharSequence& v, const Descriptor*); // message-id, correlation-id, user-id + void onString(const CharSequence& v, const Descriptor*); // message-id, correlation-id, group-id, reply-to-group-id, subject, to, reply-to + void onSymbol(const CharSequence& v, const Descriptor*); // content-type, content-encoding + void onTimestamp(int64_t v, const Descriptor*); // absolute-expiry-time, creation-time + void onUInt(uint32_t v, const Descriptor*); // group-sequence + void onNull(const Descriptor*); + private: + MessageReader& parent; + size_t index; + }; + HeaderReader headerReader; + PropertiesReader propertiesReader; + Reader* delegate; + CharSequence bare; +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_MESSAGEREADER_H*/ diff --git a/cpp/src/qpid/amqp/Reader.h b/cpp/src/qpid/amqp/Reader.h new file mode 100644 index 0000000000..64019d1521 --- /dev/null +++ b/cpp/src/qpid/amqp/Reader.h @@ -0,0 +1,80 @@ +#ifndef QPID_AMQP_READER_H +#define QPID_AMQP_READER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/IntegerTypes.h" +#include <stddef.h> + +namespace qpid { +namespace amqp { +struct CharSequence; +struct Constructor; +struct Descriptor; + +/** + * Allows an event-driven, callback-based approach to processing an + * AMQP encoded data stream. By sublassing and implementing the + * methods of interest, readers can be constructed for different + * contexts. + */ +class Reader +{ + public: + virtual ~Reader() {} + virtual void onNull(const Descriptor*) {} + virtual void onBoolean(bool, const Descriptor*) {} + virtual void onUByte(uint8_t, const Descriptor*) {} + virtual void onUShort(uint16_t, const Descriptor*) {} + virtual void onUInt(uint32_t, const Descriptor*) {} + virtual void onULong(uint64_t, const Descriptor*) {} + virtual void onByte(int8_t, const Descriptor*) {} + virtual void onShort(int16_t, const Descriptor*) {} + virtual void onInt(int32_t, const Descriptor*) {} + virtual void onLong(int64_t, const Descriptor*) {} + virtual void onFloat(float, const Descriptor*) {} + virtual void onDouble(double, const Descriptor*) {} + virtual void onUuid(const CharSequence&, const Descriptor*) {} + virtual void onTimestamp(int64_t, const Descriptor*) {} + + virtual void onBinary(const CharSequence&, const Descriptor*) {} + virtual void onString(const CharSequence&, const Descriptor*) {} + virtual void onSymbol(const CharSequence&, const Descriptor*) {} + + /** + * @return true to get elements of the compound value, false + * to skip over it + */ + virtual bool onStartList(uint32_t /*count*/, const CharSequence&, const Descriptor*) { return true; } + virtual bool onStartMap(uint32_t /*count*/, const CharSequence&, const Descriptor*) { return true; } + virtual bool onStartArray(uint32_t /*count*/, const CharSequence&, const Constructor&, const Descriptor*) { return true; } + virtual void onEndList(uint32_t /*count*/, const Descriptor*) {} + virtual void onEndMap(uint32_t /*count*/, const Descriptor*) {} + virtual void onEndArray(uint32_t /*count*/, const Descriptor*) {} + + virtual void onDescriptor(const Descriptor&, const char*) {} + + virtual bool proceed() { return true; } + private: +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_READER_H*/ diff --git a/cpp/src/qpid/amqp/Sasl.cpp b/cpp/src/qpid/amqp/Sasl.cpp new file mode 100644 index 0000000000..6d0a7ccb1f --- /dev/null +++ b/cpp/src/qpid/amqp/Sasl.cpp @@ -0,0 +1,130 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Sasl.h" +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/Descriptor.h" +#include "qpid/amqp/Encoder.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/ProtocolInitiation.h" +#include <string.h> + +namespace qpid { +namespace amqp { + +Sasl::Sasl(const std::string& i) : id(i), buffer(2*512/*AMQP 1.0's MAX_MIN_FRAME_SIZE - is this enough though?*/), encoder(&buffer[0], buffer.size()) {} +Sasl::~Sasl() {} + +void* Sasl::startFrame() +{ + //write sasl frame header, leaving 4 bytes for total size + char* start = encoder.skip(4); + encoder.write((uint8_t) 0x02);//data offset + encoder.write((uint8_t) 0x01);//frame type + encoder.write((uint16_t) 0x0000);//ignored + return start; +} + +void Sasl::endFrame(void* frame) +{ + //now backfill the frame size + char* start = (char*) frame; + char* current = &buffer[encoder.getPosition()]; + uint32_t frameSize = current - start; + Encoder backfill(start, 4); + backfill.write(frameSize); + QPID_LOG(trace, "Completed encoding of frame of " << frameSize << " bytes"); +} + + +std::size_t Sasl::read(const char* data, size_t available) +{ + Decoder decoder(data, available); + //read frame-header + uint32_t frameSize = decoder.readUInt(); + QPID_LOG(trace, "Reading SASL frame of size " << frameSize); + decoder.resetSize(frameSize); + uint8_t dataOffset = decoder.readUByte(); + uint8_t frameType = decoder.readUByte(); + if (frameType != 0x01) { + QPID_LOG(error, "Expected SASL frame; got type " << frameType); + } + uint16_t ignored = decoder.readUShort(); + if (ignored) { + QPID_LOG(info, "Got non null bytes at end of SASL frame header"); + } + + //body is at offset 4*dataOffset from the start + size_t skip = dataOffset*4 - 8; + if (skip) { + QPID_LOG(info, "Offset for sasl frame was not as expected"); + decoder.advance(skip); + } + decoder.read(*this); + return decoder.getPosition(); +} + +std::size_t Sasl::write(char* data, size_t size) +{ + size_t available = encoder.getPosition(); + if (available) { + size_t encoded = available > size ? size : available; + ::memcpy(data, &buffer[0], encoded); + size_t remainder = encoder.getPosition() - encoded; + if (remainder) { + //shuffle + ::memcpy(&buffer[0], &buffer[size], remainder); + } + encoder.resetPosition(remainder); + return encoded; + } else { + return 0; + } +} + +std::size_t Sasl::readProtocolHeader(const char* buffer, std::size_t size) +{ + framing::ProtocolInitiation pi(qpid::framing::ProtocolVersion(1,0,qpid::framing::ProtocolVersion::SASL)); + if (size >= pi.encodedSize()) { + qpid::framing::Buffer out(const_cast<char*>(buffer), size); + pi.decode(out); + QPID_LOG_CAT(debug, protocol, id << " read protocol header: " << pi); + return pi.encodedSize(); + } else { + return 0; + } +} +std::size_t Sasl::writeProtocolHeader(char* buffer, std::size_t size) +{ + framing::ProtocolInitiation pi(qpid::framing::ProtocolVersion(1,0,qpid::framing::ProtocolVersion::SASL)); + if (size >= pi.encodedSize()) { + QPID_LOG_CAT(debug, protocol, id << " writing protocol header: " << pi); + qpid::framing::Buffer out(buffer, size); + pi.encode(out); + return pi.encodedSize(); + } else { + QPID_LOG_CAT(warning, protocol, id << " insufficient buffer for protocol header: " << size) + return 0; + } +} + +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/Sasl.h b/cpp/src/qpid/amqp/Sasl.h new file mode 100644 index 0000000000..558f6071fc --- /dev/null +++ b/cpp/src/qpid/amqp/Sasl.h @@ -0,0 +1,54 @@ +#ifndef QPID_AMQP_SASL_H +#define QPID_AMQP_SASL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Encoder.h" +#include "qpid/amqp/Reader.h" +#include <string> +#include <vector> + +namespace qpid { +namespace amqp { + +/** + * Base for SASL client and server utilities + */ +class Sasl : protected Reader +{ + public: + Sasl(const std::string& id); + virtual ~Sasl(); + std::size_t read(const char* data, size_t available); + std::size_t write(char* data, size_t available); + std::size_t readProtocolHeader(const char* buffer, std::size_t size); + std::size_t writeProtocolHeader(char* buffer, std::size_t size); + protected: + const std::string id; + std::vector<char> buffer; + Encoder encoder; + + void* startFrame(); + void endFrame(void*); +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_SASL_H*/ diff --git a/cpp/src/qpid/amqp/SaslClient.cpp b/cpp/src/qpid/amqp/SaslClient.cpp new file mode 100644 index 0000000000..69660e9294 --- /dev/null +++ b/cpp/src/qpid/amqp/SaslClient.cpp @@ -0,0 +1,154 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/SaslClient.h" +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/Descriptor.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/amqp/Encoder.h" +#include "qpid/log/Statement.h" + +using namespace qpid::amqp::sasl; + +namespace qpid { +namespace amqp { + +SaslClient::SaslClient(const std::string& id) : Sasl(id) {} +SaslClient::~SaslClient() {} +void SaslClient::init(const std::string& mechanism, const std::string* response, const std::string* hostname) +{ + void* frame = startFrame(); + + void* token = encoder.startList32(&SASL_INIT); + encoder.writeSymbol(mechanism); + if (response) encoder.writeBinary(*response); + else encoder.writeNull(); + if (hostname) encoder.writeString(*hostname); + else encoder.writeNull(); + encoder.endList32(3, token); + + endFrame(frame); + QPID_LOG_CAT(debug, protocol, id << " Sent SASL-INIT(" << mechanism << ", " << (response ? *response : "null") << ", " << (hostname ? *hostname : "null") << ")"); +} +void SaslClient::response(const std::string* r) +{ + void* frame = startFrame(); + + void* token = encoder.startList32(&SASL_RESPONSE); + if (r) encoder.writeBinary(*r); + else encoder.writeNull(); + encoder.endList32(1, token); + + endFrame(frame); + QPID_LOG_CAT(debug, protocol, id << " Sent SASL-RESPONSE(" << (r ? *r : "null") << ")"); +} + + +namespace { +const std::string SPACE(" "); +class SaslMechanismsReader : public Reader +{ + public: + SaslMechanismsReader(SaslClient& c) : client(c), expected(0) {} + void onSymbol(const CharSequence& mechanism, const Descriptor*) + { + if (expected) { + mechanisms << mechanism.str() << SPACE; + } else { + client.mechanisms(mechanism.str()); + } + } + bool onStartArray(uint32_t count, const CharSequence&, const Constructor&, const Descriptor*) + { + expected = count; + return true; + } + void onEndArray(uint32_t, const Descriptor*) + { + client.mechanisms(mechanisms.str()); + } + private: + SaslClient& client; + uint32_t expected; + std::stringstream mechanisms; +}; +class SaslChallengeReader : public Reader +{ + public: + SaslChallengeReader(SaslClient& c) : client(c) {} + void onNull(const Descriptor*) { client.challenge(); } + void onBinary(const CharSequence& c, const Descriptor*) { client.challenge(c.str()); } + private: + SaslClient& client; +}; +class SaslOutcomeReader : public Reader +{ + public: + SaslOutcomeReader(SaslClient& c, bool e) : client(c), expectExtraData(e) {} + void onUByte(uint8_t c, const Descriptor*) + { + if (expectExtraData) code = c; + else client.outcome(c); + } + void onBinary(const CharSequence& extra, const Descriptor*) { client.outcome(code, extra.str()); } + void onNull(const Descriptor*) { client.outcome(code); } + private: + SaslClient& client; + bool expectExtraData; + uint8_t code; +}; +} + +bool SaslClient::onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor) +{ + if (!descriptor) { + QPID_LOG(error, "Expected described type in SASL negotiation but got no descriptor"); + } else if (descriptor->match(SASL_MECHANISMS_SYMBOL, SASL_MECHANISMS_CODE)) { + QPID_LOG(trace, "Reading SASL-MECHANISMS"); + Decoder decoder(arguments.data, arguments.size); + if (count != 1) QPID_LOG(error, "Invalid SASL-MECHANISMS frame; exactly one field expected, got " << count); + SaslMechanismsReader reader(*this); + decoder.read(reader); + } else if (descriptor->match(SASL_CHALLENGE_SYMBOL, SASL_CHALLENGE_CODE)) { + QPID_LOG(trace, "Reading SASL-CHALLENGE"); + Decoder decoder(arguments.data, arguments.size); + if (count != 1) QPID_LOG(error, "Invalid SASL-CHALLENGE frame; exactly one field expected, got " << count); + SaslChallengeReader reader(*this); + decoder.read(reader); + } else if (descriptor->match(SASL_OUTCOME_SYMBOL, SASL_OUTCOME_CODE)) { + QPID_LOG(trace, "Reading SASL-OUTCOME"); + Decoder decoder(arguments.data, arguments.size); + if (count == 1) { + SaslOutcomeReader reader(*this, false); + decoder.read(reader); + } else if (count == 2) { + SaslOutcomeReader reader(*this, true); + decoder.read(reader); + } else { + QPID_LOG(error, "Invalid SASL-OUTCOME frame; got " << count << " fields"); + } + } else { + QPID_LOG(error, "Unexpected descriptor in SASL negotiation: " << *descriptor); + } + return false; +} + + +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/SaslClient.h b/cpp/src/qpid/amqp/SaslClient.h new file mode 100644 index 0000000000..9f3eefadc2 --- /dev/null +++ b/cpp/src/qpid/amqp/SaslClient.h @@ -0,0 +1,54 @@ +#ifndef QPID_AMQP_SASLCLIENT_H +#define QPID_AMQP_SASLCLIENT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Sasl.h" + +namespace qpid { +namespace amqp { + +/** + * Utility for decoding and encoding SASL frames by the peer acting as + * the SASL client. + */ +class SaslClient : public Sasl +{ + public: + SaslClient(const std::string& id); + virtual ~SaslClient(); + virtual void mechanisms(const std::string&) = 0; + virtual void challenge(const std::string&) = 0; + virtual void challenge() = 0; //null != empty string + virtual void outcome(uint8_t result, const std::string&) = 0; + virtual void outcome(uint8_t result) = 0; + + void init(const std::string& mechanism, const std::string* response, const std::string* hostname); + void response(const std::string*); + + private: + bool onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor); + +}; + +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_SASLCLIENT_H*/ diff --git a/cpp/src/qpid/amqp/SaslServer.cpp b/cpp/src/qpid/amqp/SaslServer.cpp new file mode 100644 index 0000000000..403730ad69 --- /dev/null +++ b/cpp/src/qpid/amqp/SaslServer.cpp @@ -0,0 +1,183 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/SaslServer.h" +#include "qpid/amqp/Constructor.h" +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/Descriptor.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/amqp/Encoder.h" +#include "qpid/amqp/typecodes.h" +#include "qpid/log/Statement.h" +#include "qpid/StringUtils.h" +#include <limits> +#include <vector> + +using namespace qpid::amqp::sasl; +using namespace qpid::amqp::typecodes; + +namespace qpid { +namespace amqp { +namespace { +const std::string SPACE(" "); +const std::string NULL_("NULL"); +} + +SaslServer::SaslServer(const std::string& id) : Sasl(id) {} +SaslServer::~SaslServer() {} + +void SaslServer::mechanisms(const std::string& mechanisms) +{ + void* frameToken = startFrame(); + + std::vector<std::string> parts = split(mechanisms, SPACE); + void* listToken = encoder.startList32(&SASL_MECHANISMS); + if (parts.size() > 1) { + void* arrayToken = encoder.startArray8(Constructor(SYMBOL8)); + for (std::vector<std::string>::const_iterator i = parts.begin();i != parts.end(); ++i) { + uint8_t size = i->size() > std::numeric_limits<uint8_t>::max() ? std::numeric_limits<uint8_t>::max() : i->size(); + encoder.write(size); + encoder.writeBytes(i->data(), size); + } + encoder.endArray8(parts.size(), arrayToken); + } else { + encoder.writeSymbol(mechanisms); + } + encoder.endList32(1, listToken); + + endFrame(frameToken); + QPID_LOG_CAT(debug, protocol, id << " Sent SASL-MECHANISMS(" << mechanisms << ") " << encoder.getPosition()); +} +void SaslServer::challenge(const std::string* c) +{ + void* frameToken = startFrame(); + + void* listToken = encoder.startList32(&SASL_CHALLENGE); + if (c) encoder.writeBinary(*c); + else encoder.writeNull(); + encoder.endList32(1, listToken); + + endFrame(frameToken); + QPID_LOG_CAT(debug, protocol, id << " Sent SASL-CHALLENGE(" << (c ? *c : NULL_) << ") " << encoder.getPosition()); +} +void SaslServer::completed(bool succeeded) +{ + void* frameToken = startFrame(); + + void* listToken = encoder.startList8(&SASL_OUTCOME); + encoder.writeUByte(succeeded ? 0 : 1); + encoder.endList8(1, listToken); + + endFrame(frameToken); + QPID_LOG_CAT(debug, protocol, id << " Sent SASL-OUTCOME(" << (succeeded ? 0 : 1) << ") " << encoder.getPosition()); +} + +namespace { +class SaslInitReader : public Reader +{ + public: + SaslInitReader(SaslServer& s, uint32_t e) : server(s), expected(e), hasResponse(false), index(0) {} + void onNull(const Descriptor*) + { + ++index; + if (index == 2) { + if (--expected == 0) { + server.init(mechanism, 0, 0); + } + } else if (index == 3) { + server.init(mechanism, hasResponse ? &response : 0, 0); + } else { + QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got null for field " << index); + } + } + void onBinary(const CharSequence& r, const Descriptor*) + { + if (++index != 2) QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got binary for field " << index); + response = r.str(); + hasResponse = true; + if (--expected == 0) { + server.init(mechanism, &response, 0); + } + } + void onString(const CharSequence& h, const Descriptor*) + { + if (--expected || ++index != 3) { + QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got string for field " << index); + } else { + std::string hostname = h.str(); + server.init(mechanism, hasResponse ? &response : 0, &hostname); + } + } + void onSymbol(const CharSequence& m, const Descriptor*) + { + if (++index != 1) QPID_LOG(warning, "Unexpected sequence of fields for SASL-INIT: got symbol for field " << index); + if (--expected) { + mechanism = m.str(); + } else { + server.init(m.str(), 0, 0); + } + } + private: + SaslServer& server; + uint32_t expected; + std::string mechanism; + std::string response; + bool hasResponse; + uint32_t index; +}; + +class SaslResponseReader : public Reader +{ + public: + SaslResponseReader(SaslServer& s) : server(s) {} + void onNull(const Descriptor*) { server.response(0); } + void onBinary(const CharSequence& r, const Descriptor*) + { + std::string s = r.str(); + server.response(&s); + } + private: + SaslServer& server; +}; +} + +bool SaslServer::onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor) +{ + if (!descriptor) { + QPID_LOG(error, "Expected described type in SASL negotiation but got no descriptor"); + } else if (descriptor->match(SASL_INIT_SYMBOL, SASL_INIT_CODE)) { + QPID_LOG(trace, "Reading SASL-INIT"); + Decoder decoder(arguments.data, arguments.size); + if (count < 1 || count > 3) QPID_LOG(error, "Invalid SASL-INIT frame; got " << count << " fields"); + SaslInitReader reader(*this, count); + decoder.read(reader); + } else if (descriptor->match(SASL_RESPONSE_SYMBOL, SASL_RESPONSE_CODE)) { + QPID_LOG(trace, "Reading SASL-RESPONSE (" << std::string(arguments.data, arguments.size) << ") " << count << " elements"); + Decoder decoder(arguments.data, arguments.size); + if (count != 1) QPID_LOG(error, "Invalid SASL-RESPONSE frame; exactly one field expected, got " << count); + SaslResponseReader reader(*this); + decoder.read(reader); + } else { + QPID_LOG(error, "Unexpected descriptor in SASL negotiation: " << *descriptor); + } + return false; +} + +}} // namespace qpid::amqp diff --git a/cpp/src/qpid/amqp/SaslServer.h b/cpp/src/qpid/amqp/SaslServer.h new file mode 100644 index 0000000000..43b960454f --- /dev/null +++ b/cpp/src/qpid/amqp/SaslServer.h @@ -0,0 +1,50 @@ +#ifndef QPID_AMQP_SASLSERVER_H +#define QPID_AMQP_SASLSERVER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/Sasl.h" + +namespace qpid { +namespace amqp { + +/** + * Utility for decoding and encoding SASL frames by the peer acting as + * the SASL server. + */ +class SaslServer : public Sasl +{ + public: + SaslServer(const std::string& id); + virtual ~SaslServer(); + virtual void init(const std::string& mechanism, const std::string* response, const std::string* hostname) = 0; + virtual void response(const std::string*) = 0; + + void mechanisms(const std::string& mechanisms); + void challenge(const std::string*); + void completed(bool succeeded); + + private: + bool onStartList(uint32_t count, const CharSequence& arguments, const Descriptor* descriptor); +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_SASLSERVER_H*/ diff --git a/cpp/src/qpid/amqp/descriptors.h b/cpp/src/qpid/amqp/descriptors.h new file mode 100644 index 0000000000..b0249f6a9f --- /dev/null +++ b/cpp/src/qpid/amqp/descriptors.h @@ -0,0 +1,81 @@ +#ifndef QPID_AMQP_DESCRIPTORS_H +#define QPID_AMQP_DESCRIPTORS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Descriptor.h" + +namespace qpid { +namespace amqp { + +namespace message { +const std::string HEADER_SYMBOL("amqp:header:list"); +const std::string PROPERTIES_SYMBOL("amqp:properties:list"); +const std::string DELIVERY_ANNOTATIONS_SYMBOL("amqp:delivery-annotations:map"); +const std::string MESSAGE_ANNOTATIONS_SYMBOL("amqp:message-annotations:map"); +const std::string APPLICATION_PROPERTIES_SYMBOL("amqp:application-properties:map"); +const std::string AMQP_SEQUENCE_SYMBOL("amqp:amqp-sequence:list"); +const std::string AMQP_VALUE_SYMBOL("amqp:amqp-sequence:*"); +const std::string DATA_SYMBOL("amqp:data:binary"); +const std::string FOOTER_SYMBOL("amqp:footer:map"); + +const uint64_t HEADER_CODE(0x70); +const uint64_t DELIVERY_ANNOTATIONS_CODE(0x71); +const uint64_t MESSAGE_ANNOTATIONS_CODE(0x72); +const uint64_t PROPERTIES_CODE(0x73); +const uint64_t APPLICATION_PROPERTIES_CODE(0x74); +const uint64_t DATA_CODE(0x75); +const uint64_t AMQP_SEQUENCE_CODE(0x76); +const uint64_t AMQP_VALUE_CODE(0x77); +const uint64_t FOOTER_CODE(0x78); + +const Descriptor HEADER(HEADER_CODE); +const Descriptor DELIVERY_ANNOTATIONS(DELIVERY_ANNOTATIONS_CODE); +const Descriptor MESSAGE_ANNOTATIONS(MESSAGE_ANNOTATIONS_CODE); +const Descriptor PROPERTIES(PROPERTIES_CODE); +const Descriptor APPLICATION_PROPERTIES(APPLICATION_PROPERTIES_CODE); +const Descriptor DATA(DATA_CODE); +} + +namespace sasl { +const std::string SASL_MECHANISMS_SYMBOL("amqp:sasl-mechanisms:list"); +const std::string SASL_INIT_SYMBOL("amqp:sasl-init:list"); +const std::string SASL_CHALLENGE_SYMBOL("amqp:sasl-challenge:list"); +const std::string SASL_RESPONSE_SYMBOL("amqp:sasl-response:list"); +const std::string SASL_OUTCOME_SYMBOL("amqp:sasl-outcome:list"); + +const uint64_t SASL_MECHANISMS_CODE(0x40); +const uint64_t SASL_INIT_CODE(0x41); +const uint64_t SASL_CHALLENGE_CODE(0x42); +const uint64_t SASL_RESPONSE_CODE(0x43); +const uint64_t SASL_OUTCOME_CODE(0x44); + +const Descriptor SASL_MECHANISMS(SASL_MECHANISMS_CODE); +const Descriptor SASL_INIT(SASL_INIT_CODE); +const Descriptor SASL_CHALLENGE(SASL_CHALLENGE_CODE); +const Descriptor SASL_RESPONSE(SASL_RESPONSE_CODE); +const Descriptor SASL_OUTCOME(SASL_OUTCOME_CODE); + +} + +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_DESCRIPTORS_H*/ diff --git a/cpp/src/qpid/amqp/typecodes.h b/cpp/src/qpid/amqp/typecodes.h new file mode 100644 index 0000000000..3c6bd17b97 --- /dev/null +++ b/cpp/src/qpid/amqp/typecodes.h @@ -0,0 +1,115 @@ +#ifndef QPID_AMQP_TYPECODES_H +#define QPID_AMQP_TYPECODES_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +namespace qpid { +namespace amqp { + +namespace typecodes +{ +const uint8_t DESCRIPTOR(0x0); + +const uint8_t NULL_VALUE(0x40); + +const uint8_t BOOLEAN(0x56); +const uint8_t BOOLEAN_TRUE(0x41); +const uint8_t BOOLEAN_FALSE(0x42); + +const uint8_t UBYTE(0x50); +const uint8_t USHORT(0x60); +const uint8_t UINT(0x70); +const uint8_t UINT_SMALL(0x52); +const uint8_t UINT_ZERO(0x43); +const uint8_t ULONG(0x80); +const uint8_t ULONG_SMALL(0x53); +const uint8_t ULONG_ZERO(0x44); + +const uint8_t BYTE(0x51); +const uint8_t SHORT(0x61); +const uint8_t INT(0x71); +const uint8_t INT_SMALL(0x54); +const uint8_t LONG(0x81); +const uint8_t LONG_SMALL(0x55); + +const uint8_t FLOAT(0x72); +const uint8_t DOUBLE(0x82); + +const uint8_t DECIMAL32(0x74); +const uint8_t DECIMAL64(0x84); +const uint8_t DECIMAL128(0x94); + +const uint8_t CHAR_UTF32(0x73); +const uint8_t TIMESTAMP(0x83); +const uint8_t UUID(0x98); + +const uint8_t BINARY8(0xa0); +const uint8_t BINARY32(0xb0); +const uint8_t STRING8(0xa1); +const uint8_t STRING32(0xb1); +const uint8_t SYMBOL8(0xa3); +const uint8_t SYMBOL32(0xb3); + +typedef std::pair<uint8_t, uint8_t> CodePair; +const CodePair SYMBOL(SYMBOL8, SYMBOL32); +const CodePair STRING(STRING8, STRING32); +const CodePair BINARY(BINARY8, BINARY32); + +const uint8_t LIST0(0x45); +const uint8_t LIST8(0xc0); +const uint8_t LIST32(0xd0); +const uint8_t MAP8(0xc1); +const uint8_t MAP32(0xd1); +const uint8_t ARRAY8(0xe0); +const uint8_t ARRAY32(0xf0); + + +const std::string NULL_NAME("null"); +const std::string BOOLEAN_NAME("name"); + +const std::string UBYTE_NAME("ubyte"); +const std::string USHORT_NAME("ushort"); +const std::string UINT_NAME("uint"); +const std::string ULONG_NAME("ulong"); + +const std::string BYTE_NAME("byte"); +const std::string SHORT_NAME("short"); +const std::string INT_NAME("int"); +const std::string LONG_NAME("long"); + +const std::string FLOAT_NAME("float"); +const std::string DOUBLE_NAME("double"); + +const std::string TIMESTAMP_NAME("timestamp"); +const std::string UUID_NAME("uuid"); + +const std::string BINARY_NAME("binary"); +const std::string STRING_NAME("string"); +const std::string SYMBOL_NAME("symbol"); + +const std::string LIST_NAME("list"); +const std::string MAP_NAME("map"); +const std::string ARRAY_NAME("array"); +} + +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_TYPECODES_H*/ diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp index c48e9bcfa4..431f4291ff 100644 --- a/cpp/src/qpid/broker/Message.cpp +++ b/cpp/src/qpid/broker/Message.cpp @@ -131,6 +131,18 @@ uint64_t Message::getTtl() const } } +bool Message::getTtl(uint64_t ttl) const +{ + if (encoding->getTtl(ttl) && expiration < FAR_FUTURE) { + sys::Duration remaining(sys::AbsTime::now(), getExpiration()); + // convert from ns to ms; set to 0 if expired + ttl = (int64_t(remaining) >= 1000000 ? int64_t(remaining)/1000000 : 0); + return true; + } else { + return false; + } +} + void Message::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e) { //TODO: this is still quite 0-10 specific... diff --git a/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h index 599819d7b6..b999a7d5a3 100644 --- a/cpp/src/qpid/broker/Message.h +++ b/cpp/src/qpid/broker/Message.h @@ -61,7 +61,6 @@ public: virtual std::string getPropertyAsString(const std::string& key) const = 0; virtual std::string getAnnotationAsString(const std::string& key) const = 0; virtual bool getTtl(uint64_t&) const = 0; - virtual bool hasExpiration() const = 0; virtual std::string getContent() const = 0; virtual void processProperties(MapHandler&) const = 0; virtual std::string getUserId() const = 0; @@ -92,6 +91,7 @@ public: sys::AbsTime getExpiration() const { return expiration; } void setExpiration(sys::AbsTime exp) { expiration = exp; } uint64_t getTtl() const; + bool getTtl(uint64_t) const; /** set the timestamp delivery property to the current time-of-day */ QPID_BROKER_EXTERN void setTimestamp(); @@ -125,7 +125,7 @@ public: QPID_BROKER_EXTERN const qpid::types::Variant::Map& getAnnotations() const; std::string getUserId() const; - QPID_BROKER_EXTERN std::string getContent() const;//TODO: may be better to get rid of this... + QPID_BROKER_EXTERN std::string getContent() const;//Used for ha, management, when content needs to be decoded QPID_BROKER_EXTERN boost::intrusive_ptr<AsyncCompletion> getIngressCompletion() const; QPID_BROKER_EXTERN boost::intrusive_ptr<PersistableMessage> getPersistentContext() const; diff --git a/cpp/src/qpid/broker/amqp/Connection.cpp b/cpp/src/qpid/broker/amqp/Connection.cpp new file mode 100644 index 0000000000..329b4263ee --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Connection.cpp @@ -0,0 +1,246 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Connection.h" +#include "Session.h" +#include "qpid/Exception.h" +#include "qpid/broker/Broker.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/OutputControl.h" +#include <sstream> +extern "C" { +#include <proton/engine.h> +#include <proton/error.h> +} + +namespace qpid { +namespace broker { +namespace amqp { + +Connection::Connection(qpid::sys::OutputControl& o, const std::string& i, qpid::broker::Broker& b, bool saslInUse) + : ManagedConnection(b, i), + connection(pn_connection()), + transport(pn_transport()), + out(o), id(i), broker(b), haveOutput(true) +{ + if (pn_transport_bind(transport, connection)) { + //error + } + out.activateOutput(); + bool enableTrace(false); + QPID_LOG_TEST_CAT(trace, protocol, enableTrace); + if (enableTrace) pn_transport_trace(transport, PN_TRACE_FRM); + + if (!saslInUse) { + //feed in a dummy AMQP 1.0 header as engine expects one, but + //we already read it (if sasl is in use we read the sasl + //header,not the AMQP 1.0 header). + std::vector<char> protocolHeader(8); + qpid::framing::ProtocolInitiation pi(getVersion()); + qpid::framing::Buffer buffer(&protocolHeader[0], protocolHeader.size()); + pi.encode(buffer); + pn_transport_input(transport, &protocolHeader[0], protocolHeader.size()); + + //wont get a userid, so set a dummy one on the ManagedConnection to trigger event + setUserid("no authentication used"); + } +} + + +Connection::~Connection() +{ + + pn_transport_free(transport); + pn_connection_free(connection); +} + +pn_transport_t* Connection::getTransport() +{ + return transport; +} +size_t Connection::decode(const char* buffer, size_t size) +{ + QPID_LOG(trace, id << " decode(" << size << ")") + //TODO: Fix pn_engine_input() to take const buffer + ssize_t n = pn_transport_input(transport, const_cast<char*>(buffer), size); + if (n > 0 || n == PN_EOS) { + //If engine returns EOS, have no way of knowing how many bytes + //it processed, but can assume none need to be reprocessed so + //consider them all read: + if (n == PN_EOS) n = size; + QPID_LOG_CAT(debug, network, id << " decoded " << n << " bytes from " << size) + process(); + pn_transport_tick(transport, 0); + if (!haveOutput) { + haveOutput = true; + out.activateOutput(); + } + return n; + } else if (n == PN_ERR) { + throw qpid::Exception(QPID_MSG("Error on input: " << getError())); + } else { + return 0; + } +} + +size_t Connection::encode(char* buffer, size_t size) +{ + QPID_LOG(trace, "encode(" << size << ")") + ssize_t n = pn_transport_output(transport, buffer, size); + if (n > 0) { + QPID_LOG_CAT(debug, network, id << " encoded " << n << " bytes from " << size) + haveOutput = true; + return n; + } else if (n == PN_EOS) { + haveOutput = size; + return size;//Is this right? + } else if (n == PN_ERR) { + throw qpid::Exception(QPID_MSG("Error on output: " << getError())); + } else { + haveOutput = false; + return 0; + } +} +bool Connection::canEncode() +{ + for (Sessions::iterator i = sessions.begin();i != sessions.end(); ++i) { + if (i->second->dispatch()) haveOutput = true; + } + process(); + //TODO: proper handling of time in and out of tick + pn_transport_tick(transport, 0); + QPID_LOG_CAT(trace, network, id << " canEncode(): " << haveOutput) + return haveOutput; +} +void Connection::closed() +{ + //TODO: tear down sessions and associated links + for (Sessions::iterator i = sessions.begin(); i != sessions.end(); ++i) { + i->second->close(); + } +} +bool Connection::isClosed() const +{ + return pn_connection_state(connection) & PN_REMOTE_CLOSED; +} +framing::ProtocolVersion Connection::getVersion() const +{ + return qpid::framing::ProtocolVersion(1,0); +} +namespace { +pn_state_t REQUIRES_OPEN = PN_LOCAL_UNINIT | PN_REMOTE_ACTIVE; +pn_state_t REQUIRES_CLOSE = PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED; +} + +void Connection::process() +{ + QPID_LOG(trace, id << " process()"); + if ((pn_connection_state(connection) & REQUIRES_OPEN) == REQUIRES_OPEN) { + QPID_LOG_CAT(debug, model, id << " connection opened"); + pn_connection_open(connection); + } + + for (pn_session_t* s = pn_session_head(connection, REQUIRES_OPEN); s; s = pn_session_next(s, REQUIRES_OPEN)) { + QPID_LOG_CAT(debug, model, id << " session begun"); + pn_session_open(s); + boost::shared_ptr<Session> ssn(new Session(s, broker, *this, out)); + sessions[s] = ssn; + } + for (pn_link_t* l = pn_link_head(connection, REQUIRES_OPEN); l; l = pn_link_next(l, REQUIRES_OPEN)) { + pn_link_open(l); + + Sessions::iterator session = sessions.find(pn_link_session(l)); + if (session == sessions.end()) { + QPID_LOG(error, id << " Link attached on unknown session!"); + } else { + try { + session->second->attach(l); + QPID_LOG_CAT(debug, protocol, id << " link " << l << " attached on " << pn_link_session(l)); + } catch (const std::exception& e) { + QPID_LOG_CAT(error, protocol, "Error on attach: " << e.what()); + //TODO: set error details on detach when that is exposed via engine API + pn_link_close(l); + } + } + } + + //handle deliveries + for (pn_delivery_t* delivery = pn_work_head(connection); delivery; delivery = pn_work_next(delivery)) { + pn_link_t* link = pn_delivery_link(delivery); + if (pn_link_is_receiver(link)) { + Sessions::iterator i = sessions.find(pn_link_session(link)); + if (i != sessions.end()) { + i->second->incoming(link, delivery); + } else { + pn_delivery_update(delivery, PN_REJECTED); + } + } else { //i.e. SENDER + Sessions::iterator i = sessions.find(pn_link_session(link)); + if (i != sessions.end()) { + QPID_LOG(trace, id << " handling outgoing delivery for " << link << " on session " << pn_link_session(link)); + i->second->outgoing(link, delivery); + } else { + QPID_LOG(error, id << " Got delivery for non-existent session: " << pn_link_session(link) << ", link: " << link); + } + } + } + + + for (pn_link_t* l = pn_link_head(connection, REQUIRES_CLOSE); l; l = pn_link_next(l, REQUIRES_CLOSE)) { + pn_link_close(l); + Sessions::iterator session = sessions.find(pn_link_session(l)); + if (session == sessions.end()) { + QPID_LOG(error, id << " peer attempted to detach link on unknown session!"); + } else { + session->second->detach(l); + QPID_LOG_CAT(debug, model, id << " link detached"); + } + } + for (pn_session_t* s = pn_session_head(connection, REQUIRES_CLOSE); s; s = pn_session_next(s, REQUIRES_CLOSE)) { + pn_session_close(s); + Sessions::iterator i = sessions.find(s); + if (i != sessions.end()) { + i->second->close(); + sessions.erase(i); + QPID_LOG_CAT(debug, model, id << " session ended"); + } else { + QPID_LOG(error, id << " peer attempted to close unrecognised session"); + } + } + if ((pn_connection_state(connection) & REQUIRES_CLOSE) == REQUIRES_CLOSE) { + QPID_LOG_CAT(debug, model, id << " connection closed"); + pn_connection_close(connection); + } +} + +std::string Connection::getError() +{ + std::stringstream text; + pn_error_t* cerror = pn_connection_error(connection); + if (cerror) text << "connection error " << pn_error_text(cerror); + pn_error_t* terror = pn_transport_error(transport); + if (terror) text << "transport error " << pn_error_text(terror); + return text.str(); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Connection.h b/cpp/src/qpid/broker/amqp/Connection.h new file mode 100644 index 0000000000..8af209af7a --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Connection.h @@ -0,0 +1,73 @@ +#ifndef QPID_BROKER_AMQP1_CONNECTION_H +#define QPID_BROKER_AMQP1_CONNECTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/broker/amqp/ManagedConnection.h" +#include <map> +#include <boost/shared_ptr.hpp> + +struct pn_connection_t; +struct pn_session_t; +struct pn_transport_t; + +namespace qpid { +namespace broker { + +class Broker; + +namespace amqp { + +class Session; +/** + * AMQP 1.0 protocol support for broker + */ +class Connection : public sys::ConnectionCodec, public ManagedConnection +{ + public: + Connection(qpid::sys::OutputControl& out, const std::string& id, qpid::broker::Broker& broker, bool saslInUse); + ~Connection(); + size_t decode(const char* buffer, size_t size); + size_t encode(char* buffer, size_t size); + bool canEncode(); + + void closed(); + bool isClosed() const; + + framing::ProtocolVersion getVersion() const; + pn_transport_t* getTransport(); + private: + typedef std::map<pn_session_t*, boost::shared_ptr<Session> > Sessions; + pn_connection_t* connection; + pn_transport_t* transport; + qpid::sys::OutputControl& out; + const std::string id; + qpid::broker::Broker& broker; + bool haveOutput; + Sessions sessions; + + void process(); + std::string getError(); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_CONNECTION_H*/ diff --git a/cpp/src/qpid/broker/amqp/Header.cpp b/cpp/src/qpid/broker/amqp/Header.cpp new file mode 100644 index 0000000000..493e757a56 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Header.cpp @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/Header.h" +#include "qpid/broker/Message.h" + +namespace qpid { +namespace broker { +namespace amqp { + +bool Header::isDurable() const +{ + return message.isPersistent(); +} + +uint8_t Header::getPriority() const +{ + return message.getPriority(); +} + +bool Header::hasTtl() const +{ + uint64_t dummy(0); + return message.getTtl(dummy); +} + +uint32_t Header::getTtl() const +{ + uint64_t ttl(0); + message.getTtl(ttl); + if (ttl > std::numeric_limits<uint32_t>::max()) return std::numeric_limits<uint32_t>::max(); + else return (uint32_t) ttl; +} + +bool Header::isFirstAcquirer() const +{ + return false;//TODO +} + +uint32_t Header::getDeliveryCount() const +{ + return message.getDeliveryCount(); +} + +Header::Header(const qpid::broker::Message& m) : message(m) {} + + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Header.h b/cpp/src/qpid/broker/amqp/Header.h new file mode 100644 index 0000000000..6e4f763028 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Header.h @@ -0,0 +1,50 @@ +#ifndef QPID_BROKER_AMQP_HEADER_H +#define QPID_BROKER_AMQP_HEADER_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/MessageEncoder.h" + +namespace qpid { +namespace broker { +class Message; +namespace amqp { + +/** + * Adapts the broker current message abstraction to provide that + * required by the AMQP 1.0 message encoder. + */ +class Header : public qpid::amqp::MessageEncoder::Header +{ + public: + Header(const qpid::broker::Message&); + bool isDurable() const; + uint8_t getPriority() const; + bool hasTtl() const; + uint32_t getTtl() const; + bool isFirstAcquirer() const; + uint32_t getDeliveryCount() const; + private: + const qpid::broker::Message& message; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_HEADER_H*/ diff --git a/cpp/src/qpid/broker/amqp/ManagedConnection.cpp b/cpp/src/qpid/broker/amqp/ManagedConnection.cpp new file mode 100644 index 0000000000..0253ba5552 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedConnection.cpp @@ -0,0 +1,98 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/ManagedConnection.h" +#include "qpid/broker/Broker.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/log/Statement.h" +#include "qmf/org/apache/qpid/broker/EventClientConnect.h" +#include "qmf/org/apache/qpid/broker/EventClientDisconnect.h" + +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { +namespace amqp { + +ManagedConnection::ManagedConnection(Broker& broker, const std::string i) : id(i), agent(0) +{ + //management integration: + agent = broker.getManagementAgent(); + if (agent != 0) { + qpid::management::Manageable* parent = broker.GetVhostObject(); + // TODO set last bool true if system connection + connection = _qmf::Connection::shared_ptr(new _qmf::Connection(agent, this, parent, id, true, false, "AMQP 1.0")); + connection->set_shadow(false); + agent->addObject(connection); + } +} + +ManagedConnection::~ManagedConnection() +{ + if (agent && connection) { + agent->raiseEvent(_qmf::EventClientDisconnect(id, userid, connection->get_remoteProperties())); + connection->resourceDestroy(); + } + QPID_LOG_CAT(debug, model, "Delete connection. user:" << userid << " rhost:" << id); +} + +void ManagedConnection::setUserid(const std::string& uid) +{ + userid = uid; + if (agent && connection) { + connection->set_authIdentity(userid); + agent->raiseEvent(_qmf::EventClientConnect(id, userid, connection->get_remoteProperties())); + } + QPID_LOG_CAT(debug, model, "Create connection. user:" << userid << " rhost:" << id ); +} + +void ManagedConnection::setSaslMechanism(const std::string& mechanism) +{ + connection->set_saslMechanism(mechanism); +} + +void ManagedConnection::setSaslSsf(int ssf) +{ + connection->set_saslSsf(ssf); +} + +qpid::management::ManagementObject::shared_ptr ManagedConnection::GetManagementObject() const +{ + return connection; +} + +std::string ManagedConnection::getId() const { return id; } +std::string ManagedConnection::getUserid() const { return userid; } + +bool ManagedConnection::isLocal(const ConnectionToken* t) const +{ + return this == t; +} +void ManagedConnection::outgoingMessageSent() +{ + if (connection) connection->inc_msgsToClient(); +} + +void ManagedConnection::incomingMessageReceived() +{ + if (connection) connection->inc_msgsFromClient(); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/ManagedConnection.h b/cpp/src/qpid/broker/amqp/ManagedConnection.h new file mode 100644 index 0000000000..e2d0376918 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedConnection.h @@ -0,0 +1,59 @@ +#ifndef QPID_BROKER_AMQP_MANAGEDCONNECTION_H +#define QPID_BROKER_AMQP_MANAGEDCONNECTION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/management/Manageable.h" +#include "qpid/broker/ConnectionToken.h" +#include "qmf/org/apache/qpid/broker/Connection.h" + +namespace qpid { +namespace management { +class ManagementAgent; +class ManagementObject; +} +namespace broker { +class Broker; +namespace amqp { + +class ManagedConnection : public qpid::management::Manageable, public ConnectionToken +{ + public: + ManagedConnection(Broker& broker, const std::string id); + virtual ~ManagedConnection(); + void setUserid(const std::string&); + std::string getId() const; + std::string getUserid() const; + void setSaslMechanism(const std::string&); + void setSaslSsf(int); + qpid::management::ManagementObject::shared_ptr GetManagementObject() const; + bool isLocal(const ConnectionToken* t) const; + void incomingMessageReceived(); + void outgoingMessageSent(); + private: + const std::string id; + std::string userid; + qmf::org::apache::qpid::broker::Connection::shared_ptr connection; + qpid::management::ManagementAgent* agent; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDCONNECTION_H*/ diff --git a/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp new file mode 100644 index 0000000000..f36a1e8da4 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp @@ -0,0 +1,70 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ManagedOutgoingLink.h" +#include "qpid/broker/amqp/ManagedSession.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Queue.h" +#include "qpid/types/Variant.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/log/Statement.h" + +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { +namespace amqp { + +ManagedOutgoingLink::ManagedOutgoingLink(Broker& broker, Queue& q, ManagedSession& p, const std::string i, bool topic) + : parent(p), id(i) +{ + qpid::management::ManagementAgent* agent = broker.getManagementAgent(); + if (agent) { + subscription = _qmf::Subscription::shared_ptr(new _qmf::Subscription(agent, this, &p, q.GetManagementObject()->getObjectId(), id, + false/*FIXME*/, true/*FIXME*/, topic, qpid::types::Variant::Map())); + agent->addObject(subscription); + subscription->set_creditMode("n/a"); + } +} +ManagedOutgoingLink::~ManagedOutgoingLink() +{ + if (subscription != 0) subscription->resourceDestroy(); +} + +qpid::management::ManagementObject::shared_ptr ManagedOutgoingLink::GetManagementObject() const +{ + return subscription; +} + +void ManagedOutgoingLink::outgoingMessageSent() +{ + if (subscription) { subscription->inc_delivered(); } + parent.outgoingMessageSent(); +} +void ManagedOutgoingLink::outgoingMessageAccepted() +{ + parent.outgoingMessageAccepted(); +} +void ManagedOutgoingLink::outgoingMessageRejected() +{ + parent.outgoingMessageRejected(); +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h new file mode 100644 index 0000000000..20a1095db2 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h @@ -0,0 +1,53 @@ +#ifndef QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H +#define QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Subscription.h" + +namespace qpid { +namespace management { +class ManagementObject; +} +namespace broker { +class Broker; +class Queue; +namespace amqp { +class ManagedSession; + +class ManagedOutgoingLink : public qpid::management::Manageable +{ + public: + ManagedOutgoingLink(Broker& broker, Queue&, ManagedSession& parent, const std::string id, bool topic); + virtual ~ManagedOutgoingLink(); + qpid::management::ManagementObject::shared_ptr GetManagementObject() const; + void outgoingMessageSent(); + void outgoingMessageAccepted(); + void outgoingMessageRejected(); + private: + ManagedSession& parent; + const std::string id; + qmf::org::apache::qpid::broker::Subscription::shared_ptr subscription; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H*/ diff --git a/cpp/src/qpid/broker/amqp/ManagedSession.cpp b/cpp/src/qpid/broker/amqp/ManagedSession.cpp new file mode 100644 index 0000000000..9bef0e842b --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedSession.cpp @@ -0,0 +1,88 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/ManagedSession.h" +#include "qpid/broker/amqp/ManagedConnection.h" +#include "qpid/broker/Broker.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/log/Statement.h" + +namespace _qmf = qmf::org::apache::qpid::broker; + +namespace qpid { +namespace broker { +namespace amqp { + +ManagedSession::ManagedSession(Broker& broker, ManagedConnection& p, const std::string i) : parent(p), id(i), unacked(0) +{ + qpid::management::ManagementAgent* agent = broker.getManagementAgent(); + if (agent != 0) { + session = _qmf::Session::shared_ptr(new _qmf::Session(agent, this, broker.GetVhostObject(), id)); + session->set_attached(true); + session->set_detachedLifespan(0); + session->clr_expireTime(); + session->set_connectionRef(parent.GetManagementObject()->getObjectId()); + agent->addObject(session); + } +} + +ManagedSession::~ManagedSession() +{ + if (session) session->resourceDestroy(); +} + +qpid::management::ManagementObject::shared_ptr ManagedSession::GetManagementObject() const +{ + return session; +} + +bool ManagedSession::isLocal(const ConnectionToken* t) const +{ + return &parent == t; +} + +void ManagedSession::outgoingMessageSent() +{ + if (session) session->set_unackedMessages(++unacked); + parent.outgoingMessageSent(); +} +void ManagedSession::outgoingMessageAccepted() +{ + if (session) session->set_unackedMessages(--unacked); +} +void ManagedSession::outgoingMessageRejected() +{ + if (session) session->set_unackedMessages(--unacked); +} + +void ManagedSession::incomingMessageReceived() +{ + parent.incomingMessageReceived(); +} +void ManagedSession::incomingMessageAccepted() +{ + +} +void ManagedSession::incomingMessageRejected() +{ + +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/ManagedSession.h b/cpp/src/qpid/broker/amqp/ManagedSession.h new file mode 100644 index 0000000000..1f56964bb6 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ManagedSession.h @@ -0,0 +1,59 @@ +#ifndef QPID_BROKER_AMQP_MANAGEDSESSION_H +#define QPID_BROKER_AMQP_MANAGEDSESSION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/management/Manageable.h" +#include "qmf/org/apache/qpid/broker/Session.h" +#include "qpid/broker/ConnectionToken.h" +#include "qpid/broker/OwnershipToken.h" + +namespace qpid { +namespace management { +class ManagementObject; +} +namespace broker { +class Broker; +namespace amqp { +class ManagedConnection; + +class ManagedSession : public qpid::management::Manageable, public OwnershipToken +{ + public: + ManagedSession(Broker& broker, ManagedConnection& parent, const std::string id); + virtual ~ManagedSession(); + qpid::management::ManagementObject::shared_ptr GetManagementObject() const; + bool isLocal(const ConnectionToken* t) const; + void incomingMessageReceived(); + void incomingMessageAccepted(); + void incomingMessageRejected(); + void outgoingMessageSent(); + void outgoingMessageAccepted(); + void outgoingMessageRejected(); + private: + ManagedConnection& parent; + const std::string id; + qmf::org::apache::qpid::broker::Session::shared_ptr session; + size_t unacked; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDSESSION_H*/ diff --git a/cpp/src/qpid/broker/amqp/Message.cpp b/cpp/src/qpid/broker/amqp/Message.cpp new file mode 100644 index 0000000000..af67f2ce22 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Message.cpp @@ -0,0 +1,259 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Message.h" +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/Buffer.h" +#include <string.h> + +namespace qpid { +namespace broker { +namespace amqp { + +namespace { +std::string empty; +} + +std::string Message::getRoutingKey() const +{ + std::string v; + v.assign(subject.data, subject.size); + return v; +} +std::string Message::getUserId() const +{ + std::string v; + v.assign(userId.data, userId.size); + return v; +} + +bool Message::isPersistent() const +{ + return durable && durable.get(); +} +bool Message::getTtl(uint64_t& t) const +{ + if (!ttl) { + return false; + } else { + t = ttl.get(); + return true; + } +} + +uint8_t Message::getPriority() const +{ + if (!priority) return 4; + else return priority.get(); +} + +std::string Message::getPropertyAsString(const std::string& /*key*/) const { return empty; } +std::string Message::getAnnotationAsString(const std::string& /*key*/) const { return empty; } +void Message::processProperties(MapHandler&) const {} + +//getContentSize() is primarily used in stats about the number of +//bytes enqueued/dequeued etc, not sure whether this is the right name +//and whether it should indeed only be the content that is thus +//measured +uint64_t Message::getContentSize() const { return data.size(); } +//getContent() is used primarily for decoding qmf messages in management and ha +std::string Message::getContent() const { return empty; } + +Message::Message(size_t size) : data(size) +{ + deliveryAnnotations.init(); + messageAnnotations.init(); + bareMessage.init(); + + userId.init(); + to.init(); + subject.init(); + replyTo.init(); + contentType.init(); + contentEncoding.init(); + + applicationProperties.init(); + body.init(); +} +char* Message::getData() { return &data[0]; } +const char* Message::getData() const { return &data[0]; } +size_t Message::getSize() const { return data.size(); } + +qpid::amqp::MessageId Message::getMessageId() const +{ + return messageId; +} +qpid::amqp::CharSequence Message::getReplyTo() const +{ + return replyTo; +} +qpid::amqp::MessageId Message::getCorrelationId() const +{ + return correlationId; +} +qpid::amqp::CharSequence Message::getContentType() const +{ + return contentType; +} +qpid::amqp::CharSequence Message::getContentEncoding() const +{ + return contentEncoding; +} + +qpid::amqp::CharSequence Message::getDeliveryAnnotations() const +{ + return deliveryAnnotations; +} +qpid::amqp::CharSequence Message::getMessageAnnotations() const +{ + return messageAnnotations; +} +qpid::amqp::CharSequence Message::getApplicationProperties() const +{ + return applicationProperties; +} +qpid::amqp::CharSequence Message::getBareMessage() const +{ + return bareMessage; +} +qpid::amqp::CharSequence Message::getBody() const +{ + return body; +} + +void Message::scan() +{ + qpid::amqp::Decoder decoder(getData(), getSize()); + decoder.read(*this); + bareMessage = qpid::amqp::MessageReader::getBareMessage(); + if (bareMessage.data && !bareMessage.size) { + bareMessage.size = getSize() - (bareMessage.data - getData()); + } +} + +const Message& Message::get(const qpid::broker::Message& message) +{ + const Message* m = dynamic_cast<const Message*>(&message.getEncoding()); + if (!m) throw qpid::Exception("Translation not yet implemented!!"); + return *m; +} + +void Message::onDurable(bool b) { durable = b; } +void Message::onPriority(uint8_t i) { priority = i; } +void Message::onTtl(uint32_t i) { ttl = i; } +void Message::onFirstAcquirer(bool b) { firstAcquirer = b; } +void Message::onDeliveryCount(uint32_t i) { deliveryCount = i; } + +void Message::onMessageId(uint64_t v) { messageId.set(v); } +void Message::onMessageId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { messageId.set(v, t); } +void Message::onUserId(const qpid::amqp::CharSequence& v) { userId = v; } +void Message::onTo(const qpid::amqp::CharSequence& v) { to = v; } +void Message::onSubject(const qpid::amqp::CharSequence& v) { subject = v; } +void Message::onReplyTo(const qpid::amqp::CharSequence& v) { replyTo = v; } +void Message::onCorrelationId(uint64_t v) { correlationId.set(v); } +void Message::onCorrelationId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { correlationId.set(v, t);} +void Message::onContentType(const qpid::amqp::CharSequence& v) { contentType = v; } +void Message::onContentEncoding(const qpid::amqp::CharSequence& v) { contentEncoding = v; } +void Message::onAbsoluteExpiryTime(int64_t) {} +void Message::onCreationTime(int64_t) {} +void Message::onGroupId(const qpid::amqp::CharSequence&) {} +void Message::onGroupSequence(uint32_t) {} +void Message::onReplyToGroupId(const qpid::amqp::CharSequence&) {} + +void Message::onApplicationProperties(const qpid::amqp::CharSequence& v) { applicationProperties = v; } +void Message::onDeliveryAnnotations(const qpid::amqp::CharSequence& v) { deliveryAnnotations = v; } +void Message::onMessageAnnotations(const qpid::amqp::CharSequence& v) { messageAnnotations = v; } +void Message::onBody(const qpid::amqp::CharSequence& v, const qpid::amqp::Descriptor&) { body = v; } +void Message::onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&) {} +void Message::onFooter(const qpid::amqp::CharSequence& v) { footer = v; } + + +//PersistableMessage interface: +void Message::encode(framing::Buffer& buffer) const +{ + buffer.putLong(0);//4-byte format indicator + buffer.putRawData((const uint8_t*) getData(), getSize()); + QPID_LOG(debug, "Encoded 1.0 message of " << getSize() << " bytes, including " << bareMessage.size << " bytes of 'bare message'"); +} +uint32_t Message::encodedSize() const +{ + return 4/*format indicator*/ + data.size(); +} +//in 1.0 the binary header/content makes less sense and in any case +//the functionality that split originally supported (i.e. lazy-loaded +//messages) is no longer in use; for 1.0 we therefore treat the whole +//content as 'header' and load it in the first stage. +uint32_t Message::encodedHeaderSize() const +{ + return encodedSize(); +} +void Message::decodeHeader(framing::Buffer& buffer) +{ + if (buffer.available() != getSize()) { + QPID_LOG(warning, "1.0 Message buffer was " << data.size() << " bytes, but " << buffer.available() << " bytes are available. Resizing."); + data.resize(buffer.available()); + } + buffer.getRawData((uint8_t*) getData(), getSize()); + scan(); + QPID_LOG(debug, "Decoded 1.0 message of " << getSize() << " bytes, including " << bareMessage.size << " bytes of 'bare message'"); +} +void Message::decodeContent(framing::Buffer& /*buffer*/) {} + +boost::intrusive_ptr<PersistableMessage> Message::merge(const std::map<std::string, qpid::types::Variant>& annotations) const +{ + //message- or delivery- annotations? would have to determine that from the name, for now assume always message-annotations + size_t extra = 0; + if (messageAnnotations) { + //TODO: actual merge required + } else { + //add whole new section + extra = qpid::amqp::MessageEncoder::getEncodedSize(annotations, true); + } + boost::intrusive_ptr<Message> copy(new Message(data.size()+extra)); + size_t position(0); + if (deliveryAnnotations) { + ::memcpy(©->data[position], deliveryAnnotations.data, deliveryAnnotations.size); + position += deliveryAnnotations.size; + } + if (messageAnnotations) { + //TODO: actual merge required + ::memcpy(©->data[position], messageAnnotations.data, messageAnnotations.size); + position += messageAnnotations.size; + } else { + qpid::amqp::MessageEncoder encoder(©->data[position], extra); + encoder.writeMap(annotations, &qpid::amqp::message::MESSAGE_ANNOTATIONS, true); + position += extra; + } + if (bareMessage) { + ::memcpy(©->data[position], bareMessage.data, bareMessage.size); + position += bareMessage.size; + } + if (footer) { + ::memcpy(©->data[position], footer.data, footer.size); + position += footer.size; + } + copy->scan(); + return copy; +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Message.h b/cpp/src/qpid/broker/amqp/Message.h new file mode 100644 index 0000000000..d4a97c928a --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Message.h @@ -0,0 +1,147 @@ +#ifndef QPID_BROKER_AMQP_MESSAGE_H +#define QPID_BROKER_AMQP_MESSAGE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/Message.h" +#include "qpid/amqp/CharSequence.h" +#include "qpid/amqp/MessageId.h" +#include "qpid/amqp/MessageReader.h" +#include <boost/optional.hpp> + +namespace qpid { +namespace framing { +class Buffer; +} +namespace broker { +namespace amqp { + +/** + * Represents an AMQP 1.0 format message + */ +class Message : public qpid::broker::Message::Encoding, private qpid::amqp::MessageReader, public qpid::broker::PersistableMessage +{ + public: + //Encoding interface: + std::string getRoutingKey() const; + bool isPersistent() const; + uint8_t getPriority() const; + uint64_t getContentSize() const; + std::string getPropertyAsString(const std::string& key) const; + std::string getAnnotationAsString(const std::string& key) const; + bool getTtl(uint64_t&) const; + std::string getContent() const; + void processProperties(MapHandler&) const; + std::string getUserId() const; + + qpid::amqp::MessageId getMessageId() const; + qpid::amqp::CharSequence getReplyTo() const; + qpid::amqp::MessageId getCorrelationId() const; + qpid::amqp::CharSequence getContentType() const; + qpid::amqp::CharSequence getContentEncoding() const; + + qpid::amqp::CharSequence getDeliveryAnnotations() const; + qpid::amqp::CharSequence getMessageAnnotations() const; + qpid::amqp::CharSequence getApplicationProperties() const; + qpid::amqp::CharSequence getBareMessage() const; + qpid::amqp::CharSequence getBody() const; + + Message(size_t size); + char* getData(); + const char* getData() const; + size_t getSize() const; + void scan(); + + //PersistableMessage interface: + void encode(framing::Buffer& buffer) const; + uint32_t encodedSize() const; + void decodeHeader(framing::Buffer& buffer); + void decodeContent(framing::Buffer& buffer); + uint32_t encodedHeaderSize() const; + boost::intrusive_ptr<PersistableMessage> merge(const std::map<std::string, qpid::types::Variant>& annotations) const; + + static const Message& get(const qpid::broker::Message&); + private: + std::vector<char> data; + + //header: + boost::optional<bool> durable; + boost::optional<uint8_t> priority; + boost::optional<uint32_t> ttl; + boost::optional<bool> firstAcquirer; + boost::optional<uint32_t> deliveryCount; + //annotations: + qpid::amqp::CharSequence deliveryAnnotations; + qpid::amqp::CharSequence messageAnnotations; + + qpid::amqp::CharSequence bareMessage;//properties, application-properties and content + //properties: + qpid::amqp::MessageId messageId; + qpid::amqp::CharSequence userId; + qpid::amqp::CharSequence to; + qpid::amqp::CharSequence subject; + qpid::amqp::CharSequence replyTo; + qpid::amqp::MessageId correlationId; + qpid::amqp::CharSequence contentType; + qpid::amqp::CharSequence contentEncoding; + + //application-properties: + qpid::amqp::CharSequence applicationProperties; + + //body: + qpid::amqp::CharSequence body; + + //footer: + qpid::amqp::CharSequence footer; + + //header: + void onDurable(bool b); + void onPriority(uint8_t i); + void onTtl(uint32_t i); + void onFirstAcquirer(bool b); + void onDeliveryCount(uint32_t i); + //properties: + void onMessageId(uint64_t); + void onMessageId(const qpid::amqp::CharSequence&, qpid::types::VariantType); + void onUserId(const qpid::amqp::CharSequence& v); + void onTo(const qpid::amqp::CharSequence& v); + void onSubject(const qpid::amqp::CharSequence& v); + void onReplyTo(const qpid::amqp::CharSequence& v); + void onCorrelationId(uint64_t); + void onCorrelationId(const qpid::amqp::CharSequence&, qpid::types::VariantType); + void onContentType(const qpid::amqp::CharSequence& v); + void onContentEncoding(const qpid::amqp::CharSequence& v); + void onAbsoluteExpiryTime(int64_t i); + void onCreationTime(int64_t); + void onGroupId(const qpid::amqp::CharSequence&); + void onGroupSequence(uint32_t); + void onReplyToGroupId(const qpid::amqp::CharSequence&); + + void onApplicationProperties(const qpid::amqp::CharSequence&); + void onDeliveryAnnotations(const qpid::amqp::CharSequence&); + void onMessageAnnotations(const qpid::amqp::CharSequence&); + void onBody(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor&); + void onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&); + void onFooter(const qpid::amqp::CharSequence&); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MESSAGE_H*/ diff --git a/cpp/src/qpid/broker/amqp/Outgoing.cpp b/cpp/src/qpid/broker/amqp/Outgoing.cpp new file mode 100644 index 0000000000..70c6b9ebd5 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Outgoing.cpp @@ -0,0 +1,191 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/Outgoing.h" +#include "qpid/broker/amqp/Header.h" +#include "qpid/broker/amqp/Translation.h" +#include "qpid/broker/Queue.h" +#include "qpid/sys/OutputControl.h" +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { +namespace amqp { + +Outgoing::Outgoing(Broker& broker, boost::shared_ptr<Queue> q, pn_link_t* l, ManagedSession& session, qpid::sys::OutputControl& o, bool topic) + : Consumer(pn_link_name(l), /*FIXME*/CONSUMER), + ManagedOutgoingLink(broker, *q, session, pn_link_name(l), topic), + exclusive(topic), + queue(q), deliveries(5000), link(l), out(o), + current(0), outstanding(0), + buffer(1024)/*used only for header at present*/ +{ + for (size_t i = 0 ; i < deliveries.capacity(); ++i) { + deliveries[i].init(i); + } +} + +void Outgoing::init() +{ + queue->consume(shared_from_this(), exclusive);//may throw exception +} + +bool Outgoing::dispatch() +{ + QPID_LOG(trace, "Dispatching to " << getName() << ": " << pn_link_credit(link)); + if (canDeliver()) { + if (queue->dispatch(shared_from_this())) { + return true; + } else { + pn_link_drained(link); + QPID_LOG(debug, "No message available on " << queue->getName()); + } + } else { + QPID_LOG(debug, "Can't deliver to " << getName() << " from " << queue->getName() << ": " << pn_link_credit(link)); + } + return false; +} + +void Outgoing::write(const char* data, size_t size) +{ + pn_link_send(link, data, size); +} + +void Outgoing::handle(pn_delivery_t* delivery) +{ + pn_delivery_tag_t tag = pn_delivery_tag(delivery); + size_t i = *reinterpret_cast<const size_t*>(tag.bytes); + Record& r = deliveries[i]; + if (pn_delivery_writable(delivery)) { + assert(r.msg); + assert(!r.delivery); + r.delivery = delivery; + //write header + qpid::amqp::MessageEncoder encoder(&buffer[0], buffer.size()); + encoder.writeHeader(Header(r.msg)); + write(&buffer[0], encoder.getPosition()); + Translation t(r.msg); + t.write(*this); + if (pn_link_advance(link)) { + --outstanding; + outgoingMessageSent(); + QPID_LOG(debug, "Sent message " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index); + } else { + QPID_LOG(error, "Failed to send message " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index); + } + } + if (pn_delivery_updated(delivery)) { + assert(r.delivery == delivery); + r.disposition = pn_delivery_remote_state(delivery); + if (r.disposition) { + switch (r.disposition) { + case PN_ACCEPTED: + //TODO: only if consuming + queue->dequeue(0, r.cursor); + outgoingMessageAccepted(); + break; + case PN_REJECTED: + queue->reject(r.cursor); + outgoingMessageRejected(); + break; + case PN_RELEASED: + queue->release(r.cursor, false);//TODO: for PN_RELEASED, delivery count should not be incremented + outgoingMessageRejected();//TODO: not quite true... + break; + case PN_MODIFIED: + queue->release(r.cursor, true);//TODO: proper handling of modified + outgoingMessageRejected();//TODO: not quite true... + break; + default: + QPID_LOG(warning, "Unhandled disposition: " << r.disposition); + } + //TODO: ony settle once any dequeue on store has completed + pn_delivery_settle(delivery); + r.reset(); + } + } +} + +bool Outgoing::canDeliver() +{ + return deliveries[current].delivery == 0 && pn_link_credit(link) > outstanding; +} + +void Outgoing::detached() +{ + QPID_LOG(debug, "Detaching outgoing link from " << queue->getName()); + queue->cancel(shared_from_this()); + //TODO: release in a clearer order? + for (size_t i = 0 ; i < deliveries.capacity(); ++i) { + if (deliveries[i].msg) queue->release(deliveries[i].cursor, true); + } + Queue::tryAutoDelete(*queue->getBroker(), queue, "", ""); +} + +//Consumer interface: +bool Outgoing::deliver(const QueueCursor& cursor, const qpid::broker::Message& msg) +{ + Record& r = deliveries[current++]; + r.cursor = cursor; + r.msg = msg; + pn_delivery(link, r.tag); + QPID_LOG(debug, "Requested delivery of " << r.msg.getSequence() << " from " << queue->getName() << ", index=" << r.index); + ++outstanding; + return true; +} + +void Outgoing::notify() +{ + QPID_LOG(trace, "Notification received for " << queue->getName()); + out.activateOutput(); +} + +bool Outgoing::accept(const qpid::broker::Message&) +{ + return canDeliver(); +} + +void Outgoing::cancel() {} + +void Outgoing::acknowledged(const qpid::broker::DeliveryRecord&) {} + +qpid::broker::OwnershipToken* Outgoing::getSession() +{ + return 0; +} + +Outgoing::Record::Record() : delivery(0), disposition(0), index(0) {} +void Outgoing::Record::init(size_t i) +{ + index = i; + tag.bytes = reinterpret_cast<const char*>(&index); + tag.size = sizeof(index); +} +void Outgoing::Record::reset() +{ + cursor = QueueCursor(); + msg = qpid::broker::Message(); + delivery = 0; + disposition = 0; +} + + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Outgoing.h b/cpp/src/qpid/broker/amqp/Outgoing.h new file mode 100644 index 0000000000..91670bcd79 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Outgoing.h @@ -0,0 +1,105 @@ +#ifndef QPID_BROKER_AMQP1_OUTGOING_H +#define QPID_BROKER_AMQP1_OUTGOING_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/Message.h" +#include "qpid/broker/amqp/ManagedOutgoingLink.h" +#include "qpid/broker/Consumer.h" +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace sys { +class OutputControl; +} +namespace broker { +class Broker; +class Queue; +namespace amqp { +class ManagedSession; +template <class T> +class CircularArray +{ + public: + CircularArray(size_t l) : limit(l), data(new T[limit]) {} + T& operator[](size_t i) { return data[i]; } + size_t capacity() { return limit; } + ~CircularArray() { delete [] data; } + private: + const size_t limit; + T* const data; + size_t next; +}; + +/** + * + */ +class Outgoing : public qpid::broker::Consumer, public boost::enable_shared_from_this<Outgoing>, public ManagedOutgoingLink +{ + public: + Outgoing(Broker&,boost::shared_ptr<Queue> q, pn_link_t* l, ManagedSession&, qpid::sys::OutputControl& o, bool topic); + void init(); + bool dispatch(); + void write(const char* data, size_t size); + void handle(pn_delivery_t* delivery); + bool canDeliver(); + void detached(); + + //Consumer interface: + bool deliver(const QueueCursor& cursor, const qpid::broker::Message& msg); + void notify(); + bool accept(const qpid::broker::Message&); + void cancel(); + void acknowledged(const qpid::broker::DeliveryRecord&); + qpid::broker::OwnershipToken* getSession(); + + private: + + struct Record + { + QueueCursor cursor; + qpid::broker::Message msg; + pn_delivery_t* delivery; + int disposition; + size_t index; + pn_delivery_tag_t tag; + + Record(); + void init(size_t i); + void reset(); + }; + + const bool exclusive; + boost::shared_ptr<Queue> queue; + CircularArray<Record> deliveries; + pn_link_t* link; + qpid::sys::OutputControl& out; + size_t current; + int outstanding; + std::vector<char> buffer; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_OUTGOING_H*/ diff --git a/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp b/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp new file mode 100644 index 0000000000..711592257c --- /dev/null +++ b/cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp @@ -0,0 +1,117 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/Plugin.h" +#include "qpid/SaslFactory.h" +#include "qpid/NullSaslServer.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Protocol.h" +#include "qpid/broker/RecoverableMessage.h" +#include "qpid/broker/RecoverableMessageImpl.h" +#include "qpid/broker/amqp/Connection.h" +#include "qpid/broker/amqp/Message.h" +#include "qpid/broker/amqp/Sasl.h" +#include "qpid/broker/amqp/Translation.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { +namespace amqp { + +class ProtocolImpl : public Protocol +{ + public: + ProtocolImpl(Broker& b) : broker(b) {} + qpid::sys::ConnectionCodec* create(const qpid::framing::ProtocolVersion&, qpid::sys::OutputControl&, const std::string&, const qpid::sys::SecuritySettings&); + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> translate(const qpid::broker::Message&); + boost::shared_ptr<RecoverableMessage> recover(qpid::framing::Buffer&); + private: + Broker& broker; +}; + +struct ProtocolPlugin : public Plugin +{ + void earlyInitialize(Plugin::Target& target) + { + //need to register protocol before recovery from store + broker::Broker* broker = dynamic_cast<qpid::broker::Broker*>(&target); + if (broker) { + broker->getProtocolRegistry().add("AMQP 1.0", new ProtocolImpl(*broker)); + } + } + + void initialize(Plugin::Target&) {} +}; + +ProtocolPlugin instance; // Static initialization + +qpid::sys::ConnectionCodec* ProtocolImpl::create(const qpid::framing::ProtocolVersion& v, qpid::sys::OutputControl& out, const std::string& id, const qpid::sys::SecuritySettings& external) +{ + if (v == qpid::framing::ProtocolVersion(1, 0)) { + if (v.getProtocol() == qpid::framing::ProtocolVersion::SASL) { + if (broker.getOptions().auth) { + QPID_LOG(info, "Using AMQP 1.0 (with SASL layer)"); + return new qpid::broker::amqp::Sasl(out, id, broker, qpid::SaslFactory::getInstance().createServer(broker.getOptions().realm, broker.getOptions().requireEncrypted, external)); + } else { + std::auto_ptr<SaslServer> authenticator(new qpid::NullSaslServer(broker.getOptions().realm)); + QPID_LOG(info, "Using AMQP 1.0 (with dummy SASL layer)"); + return new qpid::broker::amqp::Sasl(out, id, broker, authenticator); + } + } else { + if (broker.getOptions().auth) { + throw qpid::Exception("SASL layer required!"); + } else { + QPID_LOG(info, "Using AMQP 1.0 (no SASL layer)"); + return new qpid::broker::amqp::Connection(out, id, broker, false); + } + } + } + return 0; +} + +boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> ProtocolImpl::translate(const qpid::broker::Message& m) +{ + qpid::broker::amqp::Translation t(m); + return t.getTransfer(); +} + +boost::shared_ptr<RecoverableMessage> ProtocolImpl::recover(qpid::framing::Buffer& buffer) +{ + QPID_LOG(debug, "Recovering, checking for 1.0 message format indicator..."); + uint32_t format = buffer.getLong(); + if (format == 0) { + QPID_LOG(debug, "Recovered message IS in 1.0 format"); + //this is a 1.0 format message + boost::intrusive_ptr<qpid::broker::amqp::Message> m(new qpid::broker::amqp::Message(buffer.available())); + m->decodeHeader(buffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(qpid::broker::Message(m, m))); + } else { + QPID_LOG(debug, "Recovered message is NOT in 1.0 format"); + return RecoverableMessage::shared_ptr(); + } +} + + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Sasl.cpp b/cpp/src/qpid/broker/amqp/Sasl.cpp new file mode 100644 index 0000000000..4b89e7b15d --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Sasl.cpp @@ -0,0 +1,167 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/Sasl.h" +#include "qpid/broker/Broker.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/sys/OutputControl.h" +#include "qpid/sys/SecurityLayer.h" +#include <boost/format.hpp> +#include <vector> + +namespace qpid { +namespace broker { +namespace amqp { + +Sasl::Sasl(qpid::sys::OutputControl& o, const std::string& id, qpid::broker::Broker& broker, std::auto_ptr<qpid::SaslServer> auth) + : qpid::amqp::SaslServer(id), out(o), connection(out, id, broker, true), + authenticator(auth), + state(INCOMPLETE), writeHeader(true), haveOutput(true) +{ + out.activateOutput(); + mechanisms(authenticator->getMechanisms()); +} + +Sasl::~Sasl() {} + +size_t Sasl::decode(const char* buffer, size_t size) +{ + if (state == AUTHENTICATED) { + if (securityLayer.get()) return securityLayer->decode(buffer, size); + else return connection.decode(buffer, size); + } else if (state == INCOMPLETE && size) { + size_t decoded = read(buffer, size); + QPID_LOG(trace, id << " Sasl::decode(" << size << "): " << decoded); + return decoded; + } else { + return 0; + } +} + +size_t Sasl::encode(char* buffer, size_t size) +{ + if (state == AUTHENTICATED) { + if (securityLayer.get()) return securityLayer->encode(buffer, size); + else return connection.encode(buffer, size); + } else { + size_t encoded = 0; + if (writeHeader) { + encoded += writeProtocolHeader(buffer, size); + if (!encoded) return 0; + writeHeader = false; + } + if (encoded < size) { + encoded += write(buffer + encoded, size - encoded); + } + if (state == SUCCESS_PENDING) { + state = AUTHENTICATED; + } else if (state == FAILURE_PENDING) { + state = FAILED; + } else { + haveOutput = (encoded == size); + } + QPID_LOG(trace, id << " Sasl::encode(" << size << "): " << encoded); + return encoded; + } +} + +bool Sasl::canEncode() +{ + if (state == AUTHENTICATED) { + if (securityLayer.get()) return securityLayer->canEncode(); + else return connection.canEncode(); + } else { + return haveOutput; + } +} + +void Sasl::closed() +{ + if (state == AUTHENTICATED) { + connection.closed(); + } else { + QPID_LOG(info, id << " Connection closed prior to authentication completing"); + state = FAILED; + } +} +bool Sasl::isClosed() const +{ + if (state == AUTHENTICATED) { + return connection.isClosed(); + } else { + return state == FAILED; + } +} + +framing::ProtocolVersion Sasl::getVersion() const +{ + return connection.getVersion(); +} +namespace { +const std::string EMPTY; +} + +void Sasl::init(const std::string& mechanism, const std::string* response, const std::string* /*hostname*/) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-INIT(" << mechanism << ", " << (response ? *response : EMPTY) << ")"); + //TODO: what should we do with hostname here? + std::string c; + respond(authenticator->start(mechanism, response, c), c); + connection.setSaslMechanism(mechanism); +} + +void Sasl::response(const std::string* r) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-RESPONSE(" << (r ? *r : EMPTY) << ")"); + std::string c; + respond(authenticator->step(r, c), c); +} + +void Sasl::respond(qpid::SaslServer::Status status, const std::string& chllnge) +{ + switch (status) { + case qpid::SaslServer::OK: + connection.setUserid(authenticator->getUserid()); + completed(true); + //can't set authenticated & failed until we have actually sent the outcome + state = SUCCESS_PENDING; + securityLayer = authenticator->getSecurityLayer(65535); + if (securityLayer.get()) { + QPID_LOG_CAT(info, security, id << " Security layer installed"); + securityLayer->init(&connection); + connection.setSaslSsf(securityLayer->getSsf()); + } + QPID_LOG_CAT(info, security, id << " Authenticated as " << authenticator->getUserid()); + break; + case qpid::SaslServer::FAIL: + completed(false); + state = FAILURE_PENDING; + QPID_LOG_CAT(info, security, id << " Failed to authenticate"); + break; + case qpid::SaslServer::CHALLENGE: + challenge(&chllnge); + QPID_LOG_CAT(info, security, id << " Challenge issued"); + break; + } + haveOutput = true; + out.activateOutput(); +} +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Sasl.h b/cpp/src/qpid/broker/amqp/Sasl.h new file mode 100644 index 0000000000..079128be02 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Sasl.h @@ -0,0 +1,72 @@ +#ifndef QPID_BROKER_AMQP_SASL_H +#define QPID_BROKER_AMQP_SASL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/Connection.h" +#include "qpid/SaslServer.h" +#include "qpid/amqp/SaslServer.h" +#include "qpid/sys/ConnectionCodec.h" +#include <memory> +namespace qpid { +namespace sys { +class SecurityLayer; +} +namespace broker { +namespace amqp { + +/** + * An AMQP 1.0 SASL Security Layer for authentication and optionally + * encryption. + */ +class Sasl : public sys::ConnectionCodec, qpid::amqp::SaslServer +{ + public: + Sasl(qpid::sys::OutputControl& out, const std::string& id, qpid::broker::Broker& broker, std::auto_ptr<qpid::SaslServer> authenticator); + ~Sasl(); + + size_t decode(const char* buffer, size_t size); + size_t encode(char* buffer, size_t size); + bool canEncode(); + + void closed(); + bool isClosed() const; + + framing::ProtocolVersion getVersion() const; + private: + qpid::sys::OutputControl& out; + Connection connection; + std::auto_ptr<qpid::sys::SecurityLayer> securityLayer; + std::auto_ptr<qpid::SaslServer> authenticator; + enum { + INCOMPLETE, SUCCESS_PENDING, FAILURE_PENDING, AUTHENTICATED, FAILED + } state; + + bool writeHeader; + bool haveOutput; + + void init(const std::string& mechanism, const std::string* response, const std::string* hostname); + void response(const std::string*); + void respond(qpid::SaslServer::Status status, const std::string& challenge); +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_SASL_H*/ diff --git a/cpp/src/qpid/broker/amqp/Session.cpp b/cpp/src/qpid/broker/amqp/Session.cpp new file mode 100644 index 0000000000..ae5660448c --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Session.cpp @@ -0,0 +1,293 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "Session.h" +#include "Outgoing.h" +#include "Message.h" +#include "ManagedConnection.h" +#include "qpid/broker/AsyncCompletion.h" +#include "qpid/broker/Broker.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/FanOutExchange.h" +#include "qpid/broker/Message.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include <boost/intrusive_ptr.hpp> +#include <boost/format.hpp> +#include <map> +#include <sstream> +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace broker { +namespace amqp { + +class Target +{ + public: + virtual ~Target() {} + virtual void flow() = 0; + virtual void handle(qpid::broker::Message& m) = 0;//TODO: revise this for proper message + private: +}; + +class Queue : public Target +{ + public: + Queue(boost::shared_ptr<qpid::broker::Queue> q, pn_link_t* l) : queue(q), link(l) {} + void flow(); + void handle(qpid::broker::Message& m); + private: + boost::shared_ptr<qpid::broker::Queue> queue; + pn_link_t* link; +}; + +class Exchange : public Target +{ + public: + Exchange(boost::shared_ptr<qpid::broker::Exchange> e, pn_link_t* l) : exchange(e), link(l) {} + void flow(); + void handle(qpid::broker::Message& m); + private: + boost::shared_ptr<qpid::broker::Exchange> exchange; + pn_link_t* link; +}; + +Session::Session(pn_session_t* s, qpid::broker::Broker& b, ManagedConnection& c, qpid::sys::OutputControl& o) + : ManagedSession(b, c, (boost::format("%1%") % s).str()), session(s), broker(b), connection(c), out(o), deleted(false) {} + +void Session::attach(pn_link_t* link) +{ + if (pn_link_is_sender(link)) { + pn_terminus_t* source = pn_link_remote_source(link); + //i.e a subscription + if (pn_terminus_get_type(source) == PN_UNSPECIFIED) { + throw qpid::Exception("No source specified!");/*invalid-field?*/ + } + std::string name = pn_terminus_get_address(source); + QPID_LOG(debug, "Received attach request for outgoing link from " << name); + pn_terminus_set_address(pn_link_source(link), name.c_str()); + + boost::shared_ptr<qpid::broker::Exchange> exchange = broker.getExchanges().find(name); + boost::shared_ptr<qpid::broker::Queue> queue = broker.getQueues().find(name); + if (queue) { + if (exchange) { + QPID_LOG_CAT(warning, protocol, "Ambiguous node name; " << name << " could be queue or exchange, assuming queue"); + } + boost::shared_ptr<Outgoing> q(new Outgoing(broker, queue, link, *this, out, false)); + q->init(); + senders[link] = q; + } else if (exchange) { + QueueSettings settings(false, true); + //TODO: populate settings from source details when available from engine + queue = broker.createQueue(name + qpid::types::Uuid(true).str(), settings, this, "", connection.getUserid(), connection.getId()).first; + //TODO: bind based on filter when that is exposed by engine + if (exchange->getType() == FanOutExchange::typeName) { + exchange->bind(queue, std::string(), 0); + } else if (exchange->getType() == TopicExchange::typeName) { + exchange->bind(queue, "#", 0); + } else { + throw qpid::Exception("Exchange type not yet supported over 1.0: " + exchange->getType());/*not-supported?*/ + } + boost::shared_ptr<Outgoing> q(new Outgoing(broker, queue, link, *this, out, true)); + senders[link] = q; + q->init(); + } else { + //TODO: handle dynamic creation + pn_terminus_set_type(pn_link_source(link), PN_UNSPECIFIED); + throw qpid::Exception("Node not found: " + name);/*not-found*/ + } + QPID_LOG(debug, "Outgoing link attached"); + } else { + pn_terminus_t* target = pn_link_remote_target(link); + if (pn_terminus_get_type(target) == PN_UNSPECIFIED) { + throw qpid::Exception("No target specified!");/*invalid field?*/ + } + std::string name = pn_terminus_get_address(target); + QPID_LOG(debug, "Received attach request for incoming link to " << name); + pn_terminus_set_address(pn_link_target(link), name.c_str()); + + + boost::shared_ptr<qpid::broker::Queue> queue = broker.getQueues().find(name); + boost::shared_ptr<qpid::broker::Exchange> exchange = broker.getExchanges().find(name); + if (queue) { + if (exchange) { + QPID_LOG_CAT(warning, protocol, "Ambiguous node name; " << name << " could be queue or exchange, assuming queue"); + } + boost::shared_ptr<Target> q(new Queue(queue, link)); + targets[link] = q; + q->flow(); + } else if (exchange) { + boost::shared_ptr<Target> e(new Exchange(exchange, link)); + targets[link] = e; + e->flow(); + } else { + //TODO: handle dynamic creation + pn_terminus_set_type(pn_link_target(link), PN_UNSPECIFIED); + throw qpid::Exception("Node not found: " + name);/*not-found*/ + } + QPID_LOG(debug, "Incoming link attached"); + } +} + +void Session::detach(pn_link_t* link) +{ + if (pn_link_is_sender(link)) { + Senders::iterator i = senders.find(link); + if (i != senders.end()) { + i->second->detached(); + senders.erase(i); + QPID_LOG(debug, "Outgoing link detached"); + } + } else { + targets.erase(link); + QPID_LOG(debug, "Incoming link detached"); + } +} +namespace { + class Transfer : public qpid::broker::AsyncCompletion::Callback + { + public: + Transfer(pn_delivery_t* d, boost::shared_ptr<Session> s) : delivery(d), session(s) {} + void completed(bool sync) { session->accepted(delivery, sync); } + boost::intrusive_ptr<qpid::broker::AsyncCompletion::Callback> clone() + { + boost::intrusive_ptr<qpid::broker::AsyncCompletion::Callback> copy(new Transfer(delivery, session)); + return copy; + } + private: + pn_delivery_t* delivery; + boost::shared_ptr<Session> session; + }; +} + +void Session::accepted(pn_delivery_t* delivery, bool sync) +{ + if (sync) { + //this is on IO thread + pn_delivery_update(delivery, PN_ACCEPTED); + pn_delivery_settle(delivery);//do we need to check settlement modes/orders? + incomingMessageAccepted(); + } else { + //this is not on IO thread, need to delay processing until on IO thread + qpid::sys::Mutex::ScopedLock l(lock); + if (!deleted) { + completed.push_back(delivery); + out.activateOutput(); + } + } +} + +void Session::incoming(pn_link_t* link, pn_delivery_t* delivery) +{ + pn_delivery_tag_t tag = pn_delivery_tag(delivery); + QPID_LOG(debug, "received delivery: " << std::string(tag.bytes, tag.size)); + boost::intrusive_ptr<Message> received(new Message(pn_delivery_pending(delivery))); + /*ssize_t read = */pn_link_recv(link, received->getData(), received->getSize()); + received->scan(); + pn_link_advance(link); + + qpid::broker::Message message(received, received); + + incomingMessageReceived(); + Targets::iterator target = targets.find(link); + if (target == targets.end()) { + QPID_LOG(error, "Received message on unknown link"); + pn_delivery_update(delivery, PN_REJECTED); + pn_delivery_settle(delivery);//do we need to check settlement modes/orders? + incomingMessageRejected(); + } else { + target->second->handle(message); + received->begin(); + Transfer t(delivery, shared_from_this()); + received->end(t); + target->second->flow(); + } +} +void Session::outgoing(pn_link_t* link, pn_delivery_t* delivery) +{ + Senders::iterator sender = senders.find(link); + if (sender == senders.end()) { + QPID_LOG(error, "Delivery returned for unknown link"); + } else { + sender->second->handle(delivery); + } +} + +bool Session::dispatch() +{ + bool output(false); + for (Senders::iterator s = senders.begin(); s != senders.end(); ++s) { + if (s->second->dispatch()) output = true; + } + if (completed.size()) { + output = true; + std::deque<pn_delivery_t*> copy; + { + qpid::sys::Mutex::ScopedLock l(lock); + completed.swap(copy); + } + for (std::deque<pn_delivery_t*>::iterator i = copy.begin(); i != copy.end(); ++i) { + accepted(*i, true); + } + } + + return output; +} + +void Session::close() +{ + for (Senders::iterator i = senders.begin(); i != senders.end(); ++i) { + i->second->detached(); + } + senders.clear(); + targets.clear();//at present no explicit cleanup required for targets + QPID_LOG(debug, "Session closed, all senders cancelled."); + qpid::sys::Mutex::ScopedLock l(lock); + deleted = true; +} + +void Queue::flow() +{ + pn_link_flow(link, 1);//TODO: proper flow control +} + +void Queue::handle(qpid::broker::Message& message) +{ + queue->deliver(message); +} + +void Exchange::flow() +{ + pn_link_flow(link, 1);//TODO: proper flow control +} + +void Exchange::handle(qpid::broker::Message& message) +{ + DeliverableMessage deliverable(message, 0); + exchange->route(deliverable); +} +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Session.h b/cpp/src/qpid/broker/amqp/Session.h new file mode 100644 index 0000000000..07302d062b --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Session.h @@ -0,0 +1,77 @@ +#ifndef QPID_BROKER_AMQP1_SESSION_H +#define QPID_BROKER_AMQP1_SESSION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Mutex.h" +#include "qpid/sys/OutputControl.h" +#include "qpid/broker/amqp/ManagedSession.h" +#include <deque> +#include <map> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +struct pn_delivery_t; +struct pn_link_t; +struct pn_session_t; + +namespace qpid { +namespace broker { + +class Broker; + +namespace amqp { + +class ManagedConnection; +class Outgoing; +class Target; +/** + * + */ +class Session : public ManagedSession, public boost::enable_shared_from_this<Session> +{ + public: + Session(pn_session_t*, qpid::broker::Broker&, ManagedConnection&, qpid::sys::OutputControl&); + void attach(pn_link_t*); + void detach(pn_link_t*); + void incoming(pn_link_t*, pn_delivery_t*); + void outgoing(pn_link_t*, pn_delivery_t*); + bool dispatch(); + void close(); + + //called when a transfer is completly processed (e.g.including stored on disk) + void accepted(pn_delivery_t*, bool sync); + private: + typedef std::map<pn_link_t*, boost::shared_ptr<Outgoing> > Senders; + typedef std::map<pn_link_t*, boost::shared_ptr<Target> > Targets; + pn_session_t* session; + qpid::broker::Broker& broker; + ManagedConnection& connection; + qpid::sys::OutputControl& out; + Targets targets; + Senders senders; + std::deque<pn_delivery_t*> completed; + bool deleted; + qpid::sys::Mutex lock; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_SESSION_H*/ diff --git a/cpp/src/qpid/broker/amqp/Translation.cpp b/cpp/src/qpid/broker/amqp/Translation.cpp new file mode 100644 index 0000000000..551b4182e0 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Translation.cpp @@ -0,0 +1,238 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/amqp/Translation.h" +#include "qpid/broker/amqp/Outgoing.h" +#include "qpid/broker/amqp_0_10/MessageTransfer.h" +#include "qpid/amqp/Decoder.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/amqp_0_10/Codecs.h" +#include "qpid/types/Variant.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include <boost/lexical_cast.hpp> + +namespace qpid { +namespace broker { +namespace amqp { +namespace { + +const std::string EMPTY; +const std::string FORWARD_SLASH("/"); + +std::string translate(const qpid::framing::ReplyTo r) +{ + if (r.hasExchange()) { + if (r.hasRoutingKey()) return r.getExchange() + FORWARD_SLASH + r.getRoutingKey(); + else return r.getExchange(); + } else return r.getRoutingKey(); +} +std::string translate(const qpid::amqp::CharSequence& chars) +{ + if (chars.data && chars.size) return std::string(chars.data, chars.size); + else return EMPTY; +} +bool setMessageId(qpid::framing::MessageProperties& m, const qpid::amqp::CharSequence& chars) +{ + if (chars.data && chars.size) { + if (chars.size == 16) { + m.setMessageId(qpid::framing::Uuid(chars.data)); + return true; + } else { + std::istringstream in(translate(chars)); + qpid::framing::Uuid uuid; + in >> uuid; + if (!in.fail()) { + m.setMessageId(uuid); + return true; + } + } + } + return false; +} +class Properties_0_10 : public qpid::amqp::MessageEncoder::Properties +{ + public: + bool hasMessageId() const { return messageProperties && messageProperties->hasMessageId(); } + std::string getMessageId() const { return messageProperties ? messageProperties->getMessageId().str() : EMPTY; } + bool hasUserId() const { return messageProperties && messageProperties->hasUserId(); } + std::string getUserId() const { return messageProperties ? messageProperties->getUserId() : EMPTY; } + bool hasTo() const { return getDestination().size() || hasSubject(); } + std::string getTo() const { return getDestination().size() ? getDestination() : getSubject(); } + bool hasSubject() const { return deliveryProperties && getDestination().size() && deliveryProperties->hasRoutingKey(); } + std::string getSubject() const { return deliveryProperties && getDestination().size() ? deliveryProperties->getRoutingKey() : EMPTY; } + bool hasReplyTo() const { return messageProperties && messageProperties->hasReplyTo(); } + std::string getReplyTo() const { return messageProperties ? translate(messageProperties->getReplyTo()) : EMPTY; } + bool hasCorrelationId() const { return messageProperties && messageProperties->hasCorrelationId(); } + std::string getCorrelationId() const { return messageProperties ? messageProperties->getCorrelationId() : EMPTY; } + bool hasContentType() const { return messageProperties && messageProperties->hasContentType(); } + std::string getContentType() const { return messageProperties ? messageProperties->getContentType() : EMPTY; } + bool hasContentEncoding() const { return messageProperties && messageProperties->hasContentEncoding(); } + std::string getContentEncoding() const { return messageProperties ? messageProperties->getContentEncoding() : EMPTY; } + bool hasAbsoluteExpiryTime() const { return deliveryProperties && deliveryProperties->hasExpiration(); } + int64_t getAbsoluteExpiryTime() const { return deliveryProperties ? deliveryProperties->getExpiration() : 0; } + bool hasCreationTime() const { return false; } + int64_t getCreationTime() const { return 0; } + bool hasGroupId() const {return false; } + std::string getGroupId() const { return EMPTY; } + bool hasGroupSequence() const { return false; } + uint32_t getGroupSequence() const { return 0; } + bool hasReplyToGroupId() const { return false; } + std::string getReplyToGroupId() const { return EMPTY; } + + const qpid::framing::FieldTable& getApplicationProperties() { return messageProperties->getApplicationHeaders(); } + Properties_0_10(const qpid::broker::amqp_0_10::MessageTransfer& t) : transfer(t), + messageProperties(transfer.getProperties<qpid::framing::MessageProperties>()), + deliveryProperties(transfer.getProperties<qpid::framing::DeliveryProperties>()) + {} + private: + const qpid::broker::amqp_0_10::MessageTransfer& transfer; + const qpid::framing::MessageProperties* messageProperties; + const qpid::framing::DeliveryProperties* deliveryProperties; + + std::string getDestination() const + { + return transfer.getMethod<qpid::framing::MessageTransferBody>()->getDestination(); + } +}; +} + +Translation::Translation(const qpid::broker::Message& m) : original(m) {} + + +boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> Translation::getTransfer() +{ + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> t = + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer>(dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&original.getEncoding())); + if (t) { + return t;//no translation required + } else { + const Message* message = dynamic_cast<const Message*>(&original.getEncoding()); + if (message) { + //translate 1.0 message into 0-10 + boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> transfer(new qpid::broker::amqp_0_10::MessageTransfer()); + qpid::framing::AMQFrame method((qpid::framing::MessageTransferBody(qpid::framing::ProtocolVersion(), EMPTY, 0, 0))); + qpid::framing::AMQFrame header((qpid::framing::AMQHeaderBody())); + qpid::framing::AMQFrame content((qpid::framing::AMQContentBody())); + method.setEof(false); + header.setBof(false); + header.setEof(false); + content.setBof(false); + + transfer->getFrames().append(method); + transfer->getFrames().append(header); + + qpid::amqp::CharSequence body = message->getBody(); + content.castBody<qpid::framing::AMQContentBody>()->getData().assign(body.data, body.size); + transfer->getFrames().append(content); + + qpid::framing::MessageProperties* props = + transfer->getFrames().getHeaders()->get<qpid::framing::MessageProperties>(true); + props->setContentLength(body.size); + + qpid::amqp::MessageId mid = message->getMessageId(); + qpid::framing::Uuid uuid; + switch (mid.type) { + case qpid::amqp::MessageId::UUID: + case qpid::amqp::MessageId::BYTES: + if (mid.value.bytes.size == 0) break; + if (setMessageId(*props, mid.value.bytes)) break; + case qpid::amqp::MessageId::ULONG: + QPID_LOG(info, "Skipping message id in translation from 1.0 to 0-10 as it is not a UUID"); + break; + } + + qpid::amqp::MessageId cid = message->getCorrelationId(); + switch (cid.type) { + case qpid::amqp::MessageId::UUID: + assert(cid.value.bytes.size = 16); + props->setCorrelationId(qpid::framing::Uuid(cid.value.bytes.data).str()); + break; + case qpid::amqp::MessageId::BYTES: + if (cid.value.bytes.size) { + props->setCorrelationId(translate(cid.value.bytes)); + } + break; + case qpid::amqp::MessageId::ULONG: + props->setCorrelationId(boost::lexical_cast<std::string>(cid.value.ulong)); + break; + } + // TODO: ReplyTo - there is no way to reliably determine + // the type of the node from just its name, unless we + // query the brokers registries + + if (message->getContentType()) props->setContentType(translate(message->getContentType())); + if (message->getContentEncoding()) props->setContentEncoding(translate(message->getContentEncoding())); + props->setUserId(message->getUserId()); + // TODO: FieldTable applicationHeaders; + qpid::amqp::CharSequence ap = message->getApplicationProperties(); + if (ap) { + qpid::amqp::Decoder d(ap.data, ap.size); + qpid::amqp_0_10::translate(d.readMap(), props->getApplicationHeaders()); + } + + qpid::framing::DeliveryProperties* dp = + transfer->getFrames().getHeaders()->get<qpid::framing::DeliveryProperties>(true); + dp->setPriority(message->getPriority()); + if (message->isPersistent()) dp->setDeliveryMode(2); + if (message->getRoutingKey().size()) dp->setRoutingKey(message->getRoutingKey()); + + return transfer.get(); + } else { + throw qpid::Exception("Could not write message data in AMQP 0-10 format"); + } + } +} + +void Translation::write(Outgoing& out) +{ + const Message* message = dynamic_cast<const Message*>(&original.getEncoding()); + if (message) { + //write annotations + //TODO: merge in any newly added annotations + qpid::amqp::CharSequence deliveryAnnotations = message->getDeliveryAnnotations(); + qpid::amqp::CharSequence messageAnnotations = message->getMessageAnnotations(); + if (deliveryAnnotations.size) out.write(deliveryAnnotations.data, deliveryAnnotations.size); + if (messageAnnotations.size) out.write(messageAnnotations.data, messageAnnotations.size); + //write bare message + qpid::amqp::CharSequence bareMessage = message->getBareMessage(); + if (bareMessage.size) out.write(bareMessage.data, bareMessage.size); + } else { + const qpid::broker::amqp_0_10::MessageTransfer* transfer = dynamic_cast<const qpid::broker::amqp_0_10::MessageTransfer*>(&original.getEncoding()); + if (transfer) { + Properties_0_10 properties(*transfer); + qpid::types::Variant::Map applicationProperties; + qpid::amqp_0_10::translate(properties.getApplicationProperties(), applicationProperties); + std::string content = transfer->getContent(); + size_t size = qpid::amqp::MessageEncoder::getEncodedSize(properties, applicationProperties, content); + std::vector<char> buffer(size); + qpid::amqp::MessageEncoder encoder(&buffer[0], buffer.size()); + encoder.writeProperties(properties); + encoder.writeApplicationProperties(applicationProperties); + encoder.writeBinary(content, &qpid::amqp::message::DATA); + out.write(&buffer[0], encoder.getPosition()); + } else { + QPID_LOG(error, "Could not write message data in AMQP 1.0 format"); + } + } +} + +}}} // namespace qpid::broker::amqp diff --git a/cpp/src/qpid/broker/amqp/Translation.h b/cpp/src/qpid/broker/amqp/Translation.h new file mode 100644 index 0000000000..64d96560e3 --- /dev/null +++ b/cpp/src/qpid/broker/amqp/Translation.h @@ -0,0 +1,58 @@ +#ifndef QPID_BROKER_AMQP_TRANSLATION_H +#define QPID_BROKER_AMQP_TRANSLATION_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { +class Message; +namespace amqp_0_10 { +class MessageTransfer; +} +namespace amqp { + +class Outgoing; +/** + * + */ +class Translation +{ + public: + Translation(const qpid::broker::Message& message); + + /** + * @returns a pointer to an AMQP 0-10 message transfer suitable + * for sending on an 0-10 session, translating from 1.0 as + * necessary + */ + boost::intrusive_ptr<const qpid::broker::amqp_0_10::MessageTransfer> getTransfer(); + /** + * Writes the AMQP 1.0 bare message and any annotations, translating from 0-10 if necessary + */ + void write(Outgoing&); + private: + const qpid::broker::Message& original; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_TRANSLATION_H*/ diff --git a/cpp/src/qpid/messaging/ConnectionOptions.cpp b/cpp/src/qpid/messaging/ConnectionOptions.cpp new file mode 100644 index 0000000000..ecd5ba9693 --- /dev/null +++ b/cpp/src/qpid/messaging/ConnectionOptions.cpp @@ -0,0 +1,121 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/ConnectionOptions.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/types/Variant.h" +#include "qpid/log/Statement.h" +#include <algorithm> +#include <limits> + +namespace qpid { +namespace messaging { + +namespace { +double FOREVER(std::numeric_limits<double>::max()); + +double timeValue(const qpid::types::Variant& value) { + if (types::isIntegerType(value.getType())) + return double(value.asInt64()); + return value.asDouble(); +} + +void merge(const std::string& value, std::vector<std::string>& list) { + if (std::find(list.begin(), list.end(), value) == list.end()) + list.push_back(value); +} + +void merge(const qpid::types::Variant::List& from, std::vector<std::string>& to) +{ + for (qpid::types::Variant::List::const_iterator i = from.begin(); i != from.end(); ++i) + merge(i->asString(), to); +} + +} + +ConnectionOptions::ConnectionOptions(const std::map<std::string, qpid::types::Variant>& options) + : replaceUrls(false), reconnect(false), timeout(FOREVER), limit(-1), minReconnectInterval(0.001), maxReconnectInterval(2), + retries(0), reconnectOnLimitExceeded(true) +{ + for (qpid::types::Variant::Map::const_iterator i = options.begin(); i != options.end(); ++i) { + set(i->first, i->second); + } +} + +void ConnectionOptions::set(const std::string& name, const qpid::types::Variant& value) +{ + if (name == "reconnect") { + reconnect = value; + } else if (name == "reconnect-timeout" || name == "reconnect_timeout") { + timeout = timeValue(value); + } else if (name == "reconnect-limit" || name == "reconnect_limit") { + limit = value; + } else if (name == "reconnect-interval" || name == "reconnect_interval") { + maxReconnectInterval = minReconnectInterval = timeValue(value); + } else if (name == "reconnect-interval-min" || name == "reconnect_interval_min") { + minReconnectInterval = timeValue(value); + } else if (name == "reconnect-interval-max" || name == "reconnect_interval_max") { + maxReconnectInterval = timeValue(value); + } else if (name == "reconnect-urls-replace" || name == "reconnect_urls_replace") { + replaceUrls = value.asBool(); + } else if (name == "reconnect-urls" || name == "reconnect_urls") { + if (replaceUrls) urls.clear(); + if (value.getType() == qpid::types::VAR_LIST) { + merge(value.asList(), urls); + } else { + merge(value.asString(), urls); + } + } else if (name == "username") { + username = value.asString(); + } else if (name == "password") { + password = value.asString(); + } else if (name == "sasl-mechanism" || name == "sasl_mechanism" || + name == "sasl-mechanisms" || name == "sasl_mechanisms") { + mechanism = value.asString(); + } else if (name == "sasl-service" || name == "sasl_service") { + service = value.asString(); + } else if (name == "sasl-min-ssf" || name == "sasl_min_ssf") { + minSsf = value; + } else if (name == "sasl-max-ssf" || name == "sasl_max_ssf") { + maxSsf = value; + } else if (name == "heartbeat") { + heartbeat = value; + } else if (name == "tcp-nodelay" || name == "tcp_nodelay") { + tcpNoDelay = value; + } else if (name == "locale") { + locale = value.asString(); + } else if (name == "max-channels" || name == "max_channels") { + maxChannels = value; + } else if (name == "max-frame-size" || name == "max_frame_size") { + maxFrameSize = value; + } else if (name == "bounds") { + bounds = value; + } else if (name == "transport") { + protocol = value.asString(); + } else if (name == "ssl-cert-name" || name == "ssl_cert_name") { + sslCertName = value.asString(); + } else if (name == "x-reconnect-on-limit-exceeded" || name == "x_reconnect_on_limit_exceeded") { + reconnectOnLimitExceeded = value; + } else { + throw qpid::messaging::MessagingException(QPID_MSG("Invalid option: " << name << " not recognised")); + } +} + +}} // namespace qpid::messaging diff --git a/cpp/src/qpid/messaging/ConnectionOptions.h b/cpp/src/qpid/messaging/ConnectionOptions.h new file mode 100644 index 0000000000..6786fd4a64 --- /dev/null +++ b/cpp/src/qpid/messaging/ConnectionOptions.h @@ -0,0 +1,51 @@ +#ifndef QPID_MESSAGING_CONNECTIONOPTIONS_H +#define QPID_MESSAGING_CONNECTIONOPTIONS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/client/ConnectionSettings.h" +#include <map> +#include <vector> + +namespace qpid { +namespace types { +class Variant; +} +namespace messaging { + +struct ConnectionOptions : qpid::client::ConnectionSettings +{ + std::vector<std::string> urls; + bool replaceUrls; + bool reconnect; + double timeout; + int32_t limit; + double minReconnectInterval; + double maxReconnectInterval; + int32_t retries; + bool reconnectOnLimitExceeded; + + ConnectionOptions(const std::map<std::string, qpid::types::Variant>&); + void set(const std::string& name, const qpid::types::Variant& value); +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_CONNECTIONOPTIONS_H*/ diff --git a/cpp/src/qpid/messaging/Message.cpp b/cpp/src/qpid/messaging/Message.cpp index ef70c103e9..0f03bc8ca3 100644 --- a/cpp/src/qpid/messaging/Message.cpp +++ b/cpp/src/qpid/messaging/Message.cpp @@ -46,26 +46,26 @@ const std::string& Message::getSubject() const { return impl->getSubject(); } void Message::setContentType(const std::string& s) { impl->setContentType(s); } const std::string& Message::getContentType() const { return impl->getContentType(); } -void Message::setMessageId(const std::string& id) { impl->messageId = id; } -const std::string& Message::getMessageId() const { return impl->messageId; } +void Message::setMessageId(const std::string& id) { impl->setMessageId(id); } +const std::string& Message::getMessageId() const { return impl->getMessageId(); } -void Message::setUserId(const std::string& id) { impl->userId = id; } -const std::string& Message::getUserId() const { return impl->userId; } +void Message::setUserId(const std::string& id) { impl->setUserId(id); } +const std::string& Message::getUserId() const { return impl->getUserId(); } -void Message::setCorrelationId(const std::string& id) { impl->correlationId = id; } -const std::string& Message::getCorrelationId() const { return impl->correlationId; } +void Message::setCorrelationId(const std::string& id) { impl->setCorrelationId(id); } +const std::string& Message::getCorrelationId() const { return impl->getCorrelationId(); } -uint8_t Message::getPriority() const { return impl->priority; } -void Message::setPriority(uint8_t priority) { impl->priority = priority; } +uint8_t Message::getPriority() const { return impl->getPriority(); } +void Message::setPriority(uint8_t priority) { impl->setPriority(priority); } -void Message::setTtl(Duration ttl) { impl->ttl = ttl.getMilliseconds(); } -Duration Message::getTtl() const { return Duration(impl->ttl); } +void Message::setTtl(Duration ttl) { impl->setTtl(ttl.getMilliseconds()); } +Duration Message::getTtl() const { return Duration(impl->getTtl()); } -void Message::setDurable(bool durable) { impl->durable = durable; } -bool Message::getDurable() const { return impl->durable; } +void Message::setDurable(bool durable) { impl->setDurable(durable); } +bool Message::getDurable() const { return impl->isDurable(); } -bool Message::getRedelivered() const { return impl->redelivered; } -void Message::setRedelivered(bool redelivered) { impl->redelivered = redelivered; } +bool Message::getRedelivered() const { return impl->isRedelivered(); } +void Message::setRedelivered(bool redelivered) { impl->setRedelivered(redelivered); } const Variant::Map& Message::getProperties() const { return impl->getHeaders(); } Variant::Map& Message::getProperties() { return impl->getHeaders(); } diff --git a/cpp/src/qpid/messaging/MessageImpl.cpp b/cpp/src/qpid/messaging/MessageImpl.cpp index 0601800e46..fc9bc5dfa1 100644 --- a/cpp/src/qpid/messaging/MessageImpl.cpp +++ b/cpp/src/qpid/messaging/MessageImpl.cpp @@ -45,28 +45,163 @@ MessageImpl::MessageImpl(const char* chars, size_t count) : bytes(chars, count), internalId(0) {} -void MessageImpl::setReplyTo(const Address& d) { replyTo = d; } -const Address& MessageImpl::getReplyTo() const { return replyTo; } +void MessageImpl::setReplyTo(const Address& d) +{ + replyTo = d; + updated(); +} +const Address& MessageImpl::getReplyTo() const +{ + if (!replyTo && encoded) encoded->getReplyTo(replyTo); + return replyTo; +} -void MessageImpl::setSubject(const std::string& s) { subject = s; } -const std::string& MessageImpl::getSubject() const { return subject; } +void MessageImpl::setSubject(const std::string& s) +{ + subject = s; + updated(); +} +const std::string& MessageImpl::getSubject() const +{ + if (!subject.size() && encoded) encoded->getSubject(subject); + return subject; +} -void MessageImpl::setContentType(const std::string& s) { contentType = s; } -const std::string& MessageImpl::getContentType() const { return contentType; } +void MessageImpl::setContentType(const std::string& s) +{ + contentType = s; + updated(); +} +const std::string& MessageImpl::getContentType() const +{ + if (!contentType.size() && encoded) encoded->getContentType(contentType); + return contentType; +} -const Variant::Map& MessageImpl::getHeaders() const { return headers; } -Variant::Map& MessageImpl::getHeaders() { return headers; } -void MessageImpl::setHeader(const std::string& key, const qpid::types::Variant& val) { headers[key] = val; } +void MessageImpl::setMessageId(const std::string& s) +{ + messageId = s; + updated(); +} +const std::string& MessageImpl::getMessageId() const +{ + if (!messageId.size() && encoded) encoded->getMessageId(messageId); + return messageId; +} +void MessageImpl::setUserId(const std::string& s) +{ + userId = s; + updated(); +} +const std::string& MessageImpl::getUserId() const +{ + if (!userId.size() && encoded) encoded->getUserId(userId); + return userId; +} +void MessageImpl::setCorrelationId(const std::string& s) +{ + correlationId = s; + updated(); +} +const std::string& MessageImpl::getCorrelationId() const +{ + if (!correlationId.size() && encoded) encoded->getCorrelationId(correlationId); + return correlationId; +} +void MessageImpl::setPriority(uint8_t p) +{ + priority = p; +} +uint8_t MessageImpl::getPriority() const +{ + return priority; +} +void MessageImpl::setTtl(uint64_t t) +{ + ttl = t; +} +uint64_t MessageImpl::getTtl() const +{ + return ttl; +} +void MessageImpl::setDurable(bool d) +{ + durable = d; +} +bool MessageImpl::isDurable() const +{ + return durable; +} +void MessageImpl::setRedelivered(bool b) +{ + redelivered = b; +} +bool MessageImpl::isRedelivered() const +{ + return redelivered; +} + +const Variant::Map& MessageImpl::getHeaders() const +{ + if (!headers.size() && encoded) encoded->populate(headers); + return headers; +} +Variant::Map& MessageImpl::getHeaders() { + if (!headers.size() && encoded) encoded->populate(headers); + updated(); + return headers; +} +void MessageImpl::setHeader(const std::string& key, const qpid::types::Variant& val) +{ + headers[key] = val; updated(); +} //should these methods be on MessageContent? -void MessageImpl::setBytes(const std::string& c) { bytes = c; } -void MessageImpl::setBytes(const char* chars, size_t count) { bytes.assign(chars, count); } -const std::string& MessageImpl::getBytes() const { return bytes; } -std::string& MessageImpl::getBytes() { return bytes; } +void MessageImpl::setBytes(const std::string& c) +{ + bytes = c; + updated(); +} +void MessageImpl::setBytes(const char* chars, size_t count) +{ + bytes.assign(chars, count); + updated(); +} +void MessageImpl::appendBytes(const char* chars, size_t count) +{ + bytes.append(chars, count); + updated(); +} +const std::string& MessageImpl::getBytes() const +{ + if (!bytes.size() && encoded) encoded->getBody(bytes); + return bytes; +} +std::string& MessageImpl::getBytes() +{ + if (!bytes.size() && encoded) encoded->getBody(bytes); + updated();//have to assume body may be edited, invalidating our message + return bytes; +} void MessageImpl::setInternalId(qpid::framing::SequenceNumber i) { internalId = i; } qpid::framing::SequenceNumber MessageImpl::getInternalId() { return internalId; } +void MessageImpl::updated() +{ + + if (!replyTo && encoded) encoded->getReplyTo(replyTo); + if (!subject.size() && encoded) encoded->getSubject(subject); + if (!contentType.size() && encoded) encoded->getContentType(contentType); + if (!messageId.size() && encoded) encoded->getMessageId(messageId); + if (!userId.size() && encoded) encoded->getUserId(userId); + if (!correlationId.size() && encoded) encoded->getCorrelationId(correlationId); + if (!headers.size() && encoded) encoded->populate(headers); + if (!bytes.size() && encoded) encoded->getBody(bytes); + + encoded.reset(); +} + MessageImpl& MessageImplAccess::get(Message& msg) { return *msg.impl; diff --git a/cpp/src/qpid/messaging/MessageImpl.h b/cpp/src/qpid/messaging/MessageImpl.h index 57df6b3fda..915c790153 100644 --- a/cpp/src/qpid/messaging/MessageImpl.h +++ b/cpp/src/qpid/messaging/MessageImpl.h @@ -24,52 +24,77 @@ #include "qpid/messaging/Address.h" #include "qpid/types/Variant.h" #include "qpid/framing/SequenceNumber.h" +#include "qpid/messaging/amqp/EncodedMessage.h" +#include <vector> +#include <boost/shared_ptr.hpp> namespace qpid { namespace messaging { -struct MessageImpl +class MessageImpl { - Address replyTo; - std::string subject; - std::string contentType; - std::string messageId; - std::string userId; - std::string correlationId; + private: + mutable Address replyTo; + mutable std::string subject; + mutable std::string contentType; + mutable std::string messageId; + mutable std::string userId; + mutable std::string correlationId; uint8_t priority; uint64_t ttl; bool durable; bool redelivered; - qpid::types::Variant::Map headers; + mutable qpid::types::Variant::Map headers; - std::string bytes; + mutable std::string bytes; + boost::shared_ptr<const qpid::messaging::amqp::EncodedMessage> encoded; qpid::framing::SequenceNumber internalId; + void updated(); + public: MessageImpl(const std::string& c); MessageImpl(const char* chars, size_t count); void setReplyTo(const Address& d); const Address& getReplyTo() const; - + void setSubject(const std::string& s); const std::string& getSubject() const; - + void setContentType(const std::string& s); const std::string& getContentType() const; - + + void setMessageId(const std::string&); + const std::string& getMessageId() const; + void setUserId(const std::string& ); + const std::string& getUserId() const; + void setCorrelationId(const std::string& ); + const std::string& getCorrelationId() const; + void setPriority(uint8_t); + uint8_t getPriority() const; + void setTtl(uint64_t); + uint64_t getTtl() const; + void setDurable(bool); + bool isDurable() const; + void setRedelivered(bool); + bool isRedelivered() const; + + const qpid::types::Variant::Map& getHeaders() const; qpid::types::Variant::Map& getHeaders(); void setHeader(const std::string& key, const qpid::types::Variant& val); - + void setBytes(const std::string& bytes); void setBytes(const char* chars, size_t count); + void appendBytes(const char* chars, size_t count); const std::string& getBytes() const; std::string& getBytes(); void setInternalId(qpid::framing::SequenceNumber id); qpid::framing::SequenceNumber getInternalId(); - + void setEncoded(boost::shared_ptr<const qpid::messaging::amqp::EncodedMessage> e) { encoded = e; } + boost::shared_ptr<const qpid::messaging::amqp::EncodedMessage> getEncoded() const { return encoded; } }; class Message; diff --git a/cpp/src/qpid/messaging/ReceiverImpl.h b/cpp/src/qpid/messaging/ReceiverImpl.h index 57059bfd28..e450693d2c 100644 --- a/cpp/src/qpid/messaging/ReceiverImpl.h +++ b/cpp/src/qpid/messaging/ReceiverImpl.h @@ -22,10 +22,12 @@ * */ #include "qpid/RefCounted.h" +#include "qpid/sys/IntegerTypes.h" namespace qpid { namespace messaging { +class Duration; class Message; class MessageListener; class Session; diff --git a/cpp/src/qpid/messaging/SenderImpl.h b/cpp/src/qpid/messaging/SenderImpl.h index a1ca02c72c..d978463fdb 100644 --- a/cpp/src/qpid/messaging/SenderImpl.h +++ b/cpp/src/qpid/messaging/SenderImpl.h @@ -22,6 +22,7 @@ * */ #include "qpid/RefCounted.h" +#include "qpid/sys/IntegerTypes.h" namespace qpid { namespace messaging { diff --git a/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp b/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp new file mode 100644 index 0000000000..499b1ae35d --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp @@ -0,0 +1,557 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ConnectionContext.h" +#include "DriverImpl.h" +#include "ReceiverContext.h" +#include "Sasl.h" +#include "SenderContext.h" +#include "SessionContext.h" +#include "Transport.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/messaging/Duration.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/Uuid.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/Time.h" +#include <vector> +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace messaging { +namespace amqp { + + +ConnectionContext::ConnectionContext(const std::string& u, const qpid::types::Variant::Map& o) + : qpid::messaging::ConnectionOptions(o), + url(u), + engine(pn_transport()), + connection(pn_connection()), + //note: disabled read/write of header as now handled by engine + writeHeader(false), + readHeader(false), + haveOutput(false), + state(DISCONNECTED) +{ + if (pn_transport_bind(engine, connection)) { + //error + } + pn_connection_set_container(connection, "qpid::messaging");//TODO: take this from a connection option + bool enableTrace(false); + QPID_LOG_TEST_CAT(trace, protocol, enableTrace); + if (enableTrace) pn_transport_trace(engine, PN_TRACE_FRM); +} + +ConnectionContext::~ConnectionContext() +{ + close(); + sessions.clear(); + pn_transport_free(engine); + pn_connection_free(connection); +} + +namespace { +const std::string COLON(":"); +} +void ConnectionContext::open() +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + if (state != DISCONNECTED) throw qpid::messaging::ConnectionError("Connection was already opened!"); + if (!driver) driver = DriverImpl::getDefault(); + + for (Url::const_iterator i = url.begin(); state != CONNECTED && i != url.end(); ++i) { + transport = driver->getTransport(i->protocol, *this); + std::stringstream port; + port << i->port; + id = i->host + COLON + port.str(); + if (useSasl()) { + sasl = std::auto_ptr<Sasl>(new Sasl(id, *this, i->host)); + } + state = CONNECTING; + try { + QPID_LOG(debug, id << " Connecting ..."); + transport->connect(i->host, port.str()); + } catch (const std::exception& e) { + QPID_LOG(info, id << " Error while connecting: " << e.what()); + } + while (state == CONNECTING) { + lock.wait(); + } + if (state == DISCONNECTED) { + QPID_LOG(debug, id << " Failed to connect"); + transport = boost::shared_ptr<Transport>(); + } else { + QPID_LOG(debug, id << " Connected"); + } + } + + if (state != CONNECTED) throw qpid::messaging::TransportFailure(QPID_MSG("Could not connect to " << url)); + + if (sasl.get()) { + wakeupDriver(); + while (!sasl->authenticated()) { + QPID_LOG(debug, id << " Waiting to be authenticated..."); + wait(); + } + QPID_LOG(debug, id << " Authenticated"); + } + + QPID_LOG(debug, id << " Opening..."); + pn_connection_open(connection); + wakeupDriver(); //want to write + while (pn_connection_state(connection) & PN_REMOTE_UNINIT) { + wait(); + } + if (!(pn_connection_state(connection) & PN_REMOTE_ACTIVE)) { + throw qpid::messaging::ConnectionError("Failed to open connection"); + } + QPID_LOG(debug, id << " Opened"); +} + +bool ConnectionContext::isOpen() const +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + return pn_connection_state(connection) & (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE); +} + +void ConnectionContext::endSession(boost::shared_ptr<SessionContext> ssn) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + pn_session_close(ssn->session); + //TODO: need to destroy session and remove context from map + wakeupDriver(); +} + +void ConnectionContext::close() +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + if (state != CONNECTED) return; + if (!(pn_connection_state(connection) & PN_LOCAL_CLOSED)) { + for (SessionMap::iterator i = sessions.begin(); i != sessions.end(); ++i){ + if (!(pn_session_state(i->second->session) & PN_LOCAL_CLOSED)) { + pn_session_close(i->second->session); + } + } + pn_connection_close(connection); + wakeupDriver(); + //wait for close to be confirmed by peer? + while (!(pn_connection_state(connection) & PN_REMOTE_CLOSED)) { + wait(); + } + sessions.clear(); + } + transport->close(); + while (state != DISCONNECTED) { + lock.wait(); + } +} + +bool ConnectionContext::fetch(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout) +{ + { + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + if (!lnk->capacity) { + pn_link_flow(lnk->receiver, 1); + wakeupDriver(); + } + } + if (get(ssn, lnk, message, timeout)) { + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + if (lnk->capacity) { + pn_link_flow(lnk->receiver, 1);//TODO: is this the right approach? + } + return true; + } else { + { + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + pn_link_drain(lnk->receiver, 0); + wakeupDriver(); + while (pn_link_credit((pn_link_t*) lnk->receiver) - pn_link_queued((pn_link_t*) lnk->receiver)) { + QPID_LOG(notice, "Waiting for credit to be drained: " << (pn_link_credit((pn_link_t*) lnk->receiver) - pn_link_queued((pn_link_t*) lnk->receiver))); + wait(); + } + } + return get(ssn, lnk, message, qpid::messaging::Duration::IMMEDIATE); + } +} + +qpid::sys::AbsTime convert(qpid::messaging::Duration timeout) +{ + qpid::sys::AbsTime until; + uint64_t ms = timeout.getMilliseconds(); + if (ms < (uint64_t) (qpid::sys::TIME_INFINITE/qpid::sys::TIME_MSEC)) { + return qpid::sys::AbsTime(qpid::sys::now(), ms * qpid::sys::TIME_MSEC); + } else { + return qpid::sys::FAR_FUTURE; + } +} + +bool ConnectionContext::get(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout) +{ + qpid::sys::AbsTime until(convert(timeout)); + while (true) { + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + pn_delivery_t* current = pn_link_current((pn_link_t*) lnk->receiver); + QPID_LOG(debug, "In ConnectionContext::get(), current=" << current); + if (current) { + qpid::messaging::MessageImpl& impl = MessageImplAccess::get(message); + boost::shared_ptr<EncodedMessage> encoded(new EncodedMessage(pn_delivery_pending(current))); + ssize_t read = pn_link_recv(lnk->receiver, encoded->getData(), encoded->getSize()); + if (read < 0) throw qpid::messaging::MessagingException("Failed to read message"); + encoded->trim((size_t) read); + QPID_LOG(debug, "Received message of " << encoded->getSize() << " bytes: "); + encoded->init(impl); + impl.setEncoded(encoded); + impl.setInternalId(ssn->record(current)); + pn_link_advance(lnk->receiver); + return true; + } else if (until > qpid::sys::now()) { + wait(); + } else { + return false; + } + } + return false; +} + +void ConnectionContext::acknowledge(boost::shared_ptr<SessionContext> ssn, qpid::messaging::Message* message, bool cumulative) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + if (message) { + ssn->acknowledge(MessageImplAccess::get(*message).getInternalId(), cumulative); + } else { + ssn->acknowledge(); + } + wakeupDriver(); +} + + +void ConnectionContext::attach(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<SenderContext> lnk) +{ + pn_terminus_t* target = pn_link_target((pn_link_t*) lnk->sender); + pn_terminus_set_address(target, lnk->getTarget().c_str()); + attach(ssn->session, (pn_link_t*) lnk->sender); + if (!pn_link_remote_target((pn_link_t*) lnk->sender)) { + std::string msg("No such target : "); + msg += lnk->getTarget(); + throw qpid::messaging::NotFound(msg); + } +} + +void ConnectionContext::attach(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk) +{ + pn_terminus_t* source = pn_link_source((pn_link_t*) lnk->receiver); + pn_terminus_set_address(source, lnk->getSource().c_str()); + attach(ssn->session, (pn_link_t*) lnk->receiver, lnk->capacity); + if (!pn_link_remote_source((pn_link_t*) lnk->receiver)) { + std::string msg("No such source : "); + msg += lnk->getSource(); + throw qpid::messaging::NotFound(msg); + } +} + +void ConnectionContext::attach(pn_session_t* /*session*/, pn_link_t* link, int credit) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + QPID_LOG(debug, "Attaching link " << link << ", state=" << pn_link_state(link)); + pn_link_open(link); + QPID_LOG(debug, "Link attached " << link << ", state=" << pn_link_state(link)); + if (credit) pn_link_flow(link, credit); + wakeupDriver(); + while (pn_link_state(link) & PN_REMOTE_UNINIT) { + QPID_LOG(debug, "waiting for confirmation of link attach for " << link << ", state=" << pn_link_state(link)); + wait(); + } +} + +void ConnectionContext::send(boost::shared_ptr<SenderContext> snd, const qpid::messaging::Message& message, bool sync) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + SenderContext::Delivery* delivery(0); + while (!(delivery = snd->send(message))) { + QPID_LOG(debug, "Waiting for capacity..."); + wait();//wait for capacity + } + wakeupDriver(); + if (sync) { + while (!delivery->accepted()) { + QPID_LOG(debug, "Waiting for confirmation..."); + wait();//wait until message has been confirmed + } + } +} + +void ConnectionContext::setCapacity(boost::shared_ptr<SenderContext> sender, uint32_t capacity) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + sender->setCapacity(capacity); +} +uint32_t ConnectionContext::getCapacity(boost::shared_ptr<SenderContext> sender) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + return sender->getCapacity(); +} +uint32_t ConnectionContext::getUnsettled(boost::shared_ptr<SenderContext> sender) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + return sender->getUnsettled(); +} + +void ConnectionContext::setCapacity(boost::shared_ptr<ReceiverContext> receiver, uint32_t capacity) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + receiver->setCapacity(capacity); + pn_link_flow((pn_link_t*) receiver->receiver, receiver->getCapacity()); +} +uint32_t ConnectionContext::getCapacity(boost::shared_ptr<ReceiverContext> receiver) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + return receiver->getCapacity(); +} +uint32_t ConnectionContext::getAvailable(boost::shared_ptr<ReceiverContext> receiver) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + return receiver->getAvailable(); +} +uint32_t ConnectionContext::getUnsettled(boost::shared_ptr<ReceiverContext> receiver) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + return receiver->getUnsettled(); +} + +void ConnectionContext::activateOutput() +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + wakeupDriver(); +} +/** + * Expects lock to be held by caller + */ +void ConnectionContext::wakeupDriver() +{ + switch (state) { + case CONNECTED: + haveOutput = true; + transport->activateOutput(); + QPID_LOG(debug, "wakeupDriver()"); + break; + case DISCONNECTED: + case CONNECTING: + QPID_LOG(error, "wakeupDriver() called while not connected"); + break; + } +} + +void ConnectionContext::wait() +{ + lock.wait(); + if (state == DISCONNECTED) { + throw qpid::messaging::TransportFailure("Disconnected"); + } + //check for any closed links, sessions or indeed the connection +} + +boost::shared_ptr<SessionContext> ConnectionContext::newSession(bool transactional, const std::string& n) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + if (transactional) throw qpid::messaging::MessagingException("Transactions not yet supported"); + std::string name = n.empty() ? qpid::framing::Uuid(true).str() : n; + SessionMap::const_iterator i = sessions.find(name); + if (i == sessions.end()) { + boost::shared_ptr<SessionContext> s(new SessionContext(connection)); + s->session = pn_session(connection); + pn_session_open(s->session); + sessions[name] = s; + wakeupDriver(); + while (pn_session_state(s->session) & PN_REMOTE_UNINIT) { + wait(); + } + return s; + } else { + throw qpid::messaging::KeyError(std::string("Session already exists: ") + name); + } + +} +boost::shared_ptr<SessionContext> ConnectionContext::getSession(const std::string& name) const +{ + SessionMap::const_iterator i = sessions.find(name); + if (i == sessions.end()) { + throw qpid::messaging::KeyError(std::string("No such session") + name); + } else { + return i->second; + } +} + +void ConnectionContext::setOption(const std::string& name, const qpid::types::Variant& value) +{ + set(name, value); +} + +std::string ConnectionContext::getAuthenticatedUsername() +{ + return sasl.get() ? sasl->getAuthenticatedUsername() : std::string(); +} + +std::size_t ConnectionContext::decode(const char* buffer, std::size_t size) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + QPID_LOG(trace, id << " decode(" << size << ")"); + if (readHeader) { + size_t decoded = readProtocolHeader(buffer, size); + if (decoded < size) { + decoded += decode(buffer + decoded, size - decoded); + } + return decoded; + } + + //TODO: Fix pn_engine_input() to take const buffer + ssize_t n = pn_transport_input(engine, const_cast<char*>(buffer), size); + if (n > 0 || n == PN_EOS) { + //If engine returns EOS, have no way of knowing how many bytes + //it processed, but can assume none need to be reprocessed so + //consider them all read: + if (n == PN_EOS) n = size; + QPID_LOG_CAT(debug, network, id << " decoded " << n << " bytes from " << size) + pn_transport_tick(engine, 0); + lock.notifyAll(); + return n; + } else if (n == PN_ERR) { + throw qpid::Exception(QPID_MSG("Error on input: " << getError())); + } else { + return 0; + } + +} +std::size_t ConnectionContext::encode(char* buffer, std::size_t size) +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + QPID_LOG(trace, id << " encode(" << size << ")"); + if (writeHeader) { + size_t encoded = writeProtocolHeader(buffer, size); + if (encoded < size) { + encoded += encode(buffer + encoded, size - encoded); + } + return encoded; + } + + ssize_t n = pn_transport_output(engine, buffer, size); + if (n > 0) { + QPID_LOG_CAT(debug, network, id << " encoded " << n << " bytes from " << size) + haveOutput = true; + return n; + } else if (n == PN_ERR) { + throw qpid::Exception(QPID_MSG("Error on output: " << getError())); + } else if (n == PN_EOS) { + haveOutput = false; + return 0;//Is this right? + } else { + haveOutput = false; + return 0; + } +} +bool ConnectionContext::canEncode() +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + return haveOutput && state == CONNECTED; +} +void ConnectionContext::closed() +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + state = DISCONNECTED; + lock.notifyAll(); +} +void ConnectionContext::opened() +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + state = CONNECTED; + lock.notifyAll(); +} +bool ConnectionContext::isClosed() const +{ + return !isOpen(); +} +namespace { +qpid::framing::ProtocolVersion AMQP_1_0_PLAIN(1,0,qpid::framing::ProtocolVersion::AMQP); +} + +std::string ConnectionContext::getError() +{ + std::stringstream text; + pn_error_t* cerror = pn_connection_error(connection); + if (cerror) text << "connection error " << pn_error_text(cerror); + pn_error_t* terror = pn_transport_error(engine); + if (terror) text << "transport error " << pn_error_text(terror); + return text.str(); +} + +framing::ProtocolVersion ConnectionContext::getVersion() const +{ + return AMQP_1_0_PLAIN; +} + +std::size_t ConnectionContext::readProtocolHeader(const char* buffer, std::size_t size) +{ + framing::ProtocolInitiation pi(getVersion()); + if (size >= pi.encodedSize()) { + readHeader = false; + qpid::framing::Buffer out(const_cast<char*>(buffer), size); + pi.decode(out); + QPID_LOG_CAT(debug, protocol, id << " read protocol header: " << pi); + return pi.encodedSize(); + } else { + return 0; + } +} +std::size_t ConnectionContext::writeProtocolHeader(char* buffer, std::size_t size) +{ + framing::ProtocolInitiation pi(getVersion()); + if (size >= pi.encodedSize()) { + QPID_LOG_CAT(debug, protocol, id << " writing protocol header: " << pi); + writeHeader = false; + qpid::framing::Buffer out(buffer, size); + pi.encode(out); + return pi.encodedSize(); + } else { + QPID_LOG_CAT(debug, protocol, id << " insufficient buffer for protocol header: " << size) + return 0; + } +} +bool ConnectionContext::useSasl() +{ + return !(mechanism == "none" || mechanism == "NONE" || mechanism == "None"); +} + +qpid::sys::Codec& ConnectionContext::getCodec() +{ + qpid::sys::ScopedLock<qpid::sys::Monitor> l(lock); + if (sasl.get()) { + qpid::sys::Codec* c = sasl->getCodec(); + if (c) return *c; + lock.notifyAll(); + } + return *this; +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/ConnectionContext.h b/cpp/src/qpid/messaging/amqp/ConnectionContext.h new file mode 100644 index 0000000000..d9da6551b3 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ConnectionContext.h @@ -0,0 +1,139 @@ +#ifndef QPID_MESSAGING_AMQP_CONNECTIONCONTEXT_H +#define QPID_MESSAGING_AMQP_CONNECTIONCONTEXT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <deque> +#include <map> +#include <memory> +#include <string> +#include <boost/shared_ptr.hpp> +#include "qpid/Url.h" +#include "qpid/messaging/ConnectionOptions.h" +#include "qpid/sys/AtomicValue.h" +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/Monitor.h" +#include "qpid/types/Variant.h" +#include "qpid/messaging/amqp/TransportContext.h" + +struct pn_connection_t; +struct pn_link_t; +struct pn_session_t; +struct pn_transport_t; + + +namespace qpid { +namespace framing { +class ProtocolVersion; +} +namespace messaging { +class Duration; +class Message; +namespace amqp { + +class DriverImpl; +class ReceiverContext; +class Sasl; +class SessionContext; +class SenderContext; +class Transport; + +/** + * + */ +class ConnectionContext : public qpid::sys::ConnectionCodec, public qpid::messaging::ConnectionOptions, public TransportContext +{ + public: + ConnectionContext(const std::string& url, const qpid::types::Variant::Map& options); + ~ConnectionContext(); + void open(); + bool isOpen() const; + void close(); + boost::shared_ptr<SessionContext> newSession(bool transactional, const std::string& name); + boost::shared_ptr<SessionContext> getSession(const std::string& name) const; + void endSession(boost::shared_ptr<SessionContext>); + void attach(boost::shared_ptr<SessionContext>, boost::shared_ptr<SenderContext>); + void attach(boost::shared_ptr<SessionContext>, boost::shared_ptr<ReceiverContext>); + void send(boost::shared_ptr<SenderContext> ctxt, const qpid::messaging::Message& message, bool sync); + bool fetch(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout); + bool get(boost::shared_ptr<SessionContext> ssn, boost::shared_ptr<ReceiverContext> lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout); + void acknowledge(boost::shared_ptr<SessionContext> ssn, qpid::messaging::Message* message, bool cumulative); + + void setOption(const std::string& name, const qpid::types::Variant& value); + std::string getAuthenticatedUsername(); + + void setCapacity(boost::shared_ptr<SenderContext>, uint32_t); + uint32_t getCapacity(boost::shared_ptr<SenderContext>); + uint32_t getUnsettled(boost::shared_ptr<SenderContext>); + + void setCapacity(boost::shared_ptr<ReceiverContext>, uint32_t); + uint32_t getCapacity(boost::shared_ptr<ReceiverContext>); + uint32_t getAvailable(boost::shared_ptr<ReceiverContext>); + uint32_t getUnsettled(boost::shared_ptr<ReceiverContext>); + + + void activateOutput(); + qpid::sys::Codec& getCodec(); + //ConnectionCodec interface: + std::size_t decode(const char* buffer, std::size_t size); + std::size_t encode(char* buffer, std::size_t size); + bool canEncode(); + void closed(); + bool isClosed() const; + framing::ProtocolVersion getVersion() const; + //additionally, Transport needs: + void opened();//signal successful connection + + private: + typedef std::map<std::string, boost::shared_ptr<SessionContext> > SessionMap; + qpid::Url url; + + boost::shared_ptr<DriverImpl> driver; + boost::shared_ptr<Transport> transport; + + pn_transport_t* engine; + pn_connection_t* connection; + SessionMap sessions; + mutable qpid::sys::Monitor lock; + bool writeHeader; + bool readHeader; + bool haveOutput; + std::string id; + enum { + DISCONNECTED, + CONNECTING, + CONNECTED + } state; + std::auto_ptr<Sasl> sasl; + + void wait(); + void wakeupDriver(); + void attach(pn_session_t*, pn_link_t*, int credit=0); + + std::size_t readProtocolHeader(const char* buffer, std::size_t size); + std::size_t writeProtocolHeader(char* buffer, std::size_t size); + std::string getError(); + bool useSasl(); +}; + +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_CONNECTIONCONTEXT_H*/ diff --git a/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp b/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp new file mode 100644 index 0000000000..0c4ec2bfcb --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp @@ -0,0 +1,84 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ConnectionHandle.h" +#include "ConnectionContext.h" +#include "SessionHandle.h" +#include "qpid/messaging/Session.h" +#include "qpid/messaging/ProtocolRegistry.h" + +namespace qpid { +namespace messaging { +namespace amqp { +// Static constructor which registers this implementation in the ProtocolRegistry +namespace { +ConnectionImpl* create(const std::string& u, const qpid::types::Variant::Map& o) +{ + return new ConnectionHandle(u, o); +} + +struct StaticInit +{ + StaticInit() + { + ProtocolRegistry::add("amqp1.0", &create); + }; +} init; +} + +ConnectionHandle::ConnectionHandle(const std::string& url, const qpid::types::Variant::Map& options) : connection(new ConnectionContext(url, options)) {} +ConnectionHandle::ConnectionHandle(boost::shared_ptr<ConnectionContext> c) : connection(c) {} + +void ConnectionHandle::open() +{ + connection->open(); +} + +bool ConnectionHandle::isOpen() const +{ + return connection->isOpen(); +} + +void ConnectionHandle::close() +{ + connection->close(); +} + +Session ConnectionHandle::newSession(bool transactional, const std::string& name) +{ + return qpid::messaging::Session(new SessionHandle(connection, connection->newSession(transactional, name))); +} + +Session ConnectionHandle::getSession(const std::string& name) const +{ + return qpid::messaging::Session(new SessionHandle(connection, connection->getSession(name))); +} + +void ConnectionHandle::setOption(const std::string& name, const qpid::types::Variant& value) +{ + connection->setOption(name, value); +} + +std::string ConnectionHandle::getAuthenticatedUsername() +{ + return connection->getAuthenticatedUsername(); +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/ConnectionHandle.h b/cpp/src/qpid/messaging/amqp/ConnectionHandle.h new file mode 100644 index 0000000000..d1eb27f6de --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ConnectionHandle.h @@ -0,0 +1,58 @@ +#ifndef QPID_MESSAGING_AMQP_CONNECTIONHANDLE_H +#define QPID_MESSAGING_AMQP_CONNECTIONHANDLE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/shared_ptr.hpp> +#include "qpid/messaging/ConnectionImpl.h" +#include "qpid/types/Variant.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +class ConnectionContext; +/** + * Handles are directly referenced by applications; Contexts are + * referenced by Handles. This allows a graph structure that + * remains intact as long as the application references any part + * of it, but that can be automatically reclaimed if the whole + * graph becomes unreferenced. + */ +class ConnectionHandle : public qpid::messaging::ConnectionImpl +{ + public: + ConnectionHandle(const std::string& url, const qpid::types::Variant::Map& options); + ConnectionHandle(boost::shared_ptr<ConnectionContext>); + void open(); + bool isOpen() const; + void close(); + Session newSession(bool transactional, const std::string& name); + Session getSession(const std::string& name) const; + void setOption(const std::string& name, const qpid::types::Variant& value); + std::string getAuthenticatedUsername(); + private: + boost::shared_ptr<ConnectionContext> connection; +}; + +}}} // namespace qpid::messaging::amqp_1.0 + +#endif /*!QPID_MESSAGING_AMQP_CONNECTIONHANDLE_H*/ diff --git a/cpp/src/qpid/messaging/amqp/DriverImpl.cpp b/cpp/src/qpid/messaging/amqp/DriverImpl.cpp new file mode 100644 index 0000000000..0c119c87c8 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/DriverImpl.cpp @@ -0,0 +1,74 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "DriverImpl.h" +#include "Transport.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/sys/Poller.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +DriverImpl::DriverImpl() : poller(new qpid::sys::Poller) +{ + start(); +} +DriverImpl::~DriverImpl() +{ + stop(); +} + +void DriverImpl::start() +{ + thread = qpid::sys::Thread(*poller); + QPID_LOG(debug, "Driver started"); +} + +void DriverImpl::stop() +{ + QPID_LOG(debug, "Driver stopped"); + poller->shutdown(); + thread.join(); +} + +boost::shared_ptr<Transport> DriverImpl::getTransport(const std::string& protocol, TransportContext& connection) +{ + boost::shared_ptr<Transport> t(Transport::create(protocol, connection, poller)); + if (!t) throw new qpid::messaging::ConnectionError("No such transport: " + protocol); + return t; +} + + +qpid::sys::Mutex DriverImpl::defaultLock; +boost::weak_ptr<DriverImpl> DriverImpl::theDefault; +boost::shared_ptr<DriverImpl> DriverImpl::getDefault() +{ + qpid::sys::Mutex::ScopedLock l(defaultLock); + boost::shared_ptr<DriverImpl> p = theDefault.lock(); + if (!p) { + p = boost::shared_ptr<DriverImpl>(new DriverImpl); + theDefault = p; + } + return p; +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/DriverImpl.h b/cpp/src/qpid/messaging/amqp/DriverImpl.h new file mode 100644 index 0000000000..354fa1ae35 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/DriverImpl.h @@ -0,0 +1,60 @@ +#ifndef QPID_MESSAGING_AMQP_DRIVERIMPL_H +#define QPID_MESSAGING_AMQP_DRIVERIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Thread.h" +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +namespace qpid { +namespace sys { +class Poller; +} +namespace messaging { +namespace amqp { +class TransportContext; +class Transport; +/** + * + */ +class DriverImpl +{ + public: + DriverImpl(); + ~DriverImpl(); + + void start(); + void stop(); + + boost::shared_ptr<Transport> getTransport(const std::string& protocol, TransportContext& connection); + + static boost::shared_ptr<DriverImpl> getDefault(); + private: + boost::shared_ptr<qpid::sys::Poller> poller; + qpid::sys::Thread thread; + static qpid::sys::Mutex defaultLock; + static boost::weak_ptr<DriverImpl> theDefault; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_DRIVERIMPL_H*/ diff --git a/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp b/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp new file mode 100644 index 0000000000..39eaa3ffad --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp @@ -0,0 +1,263 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/amqp/EncodedMessage.h" +#include "qpid/messaging/Address.h" +#include "qpid/messaging/MessageImpl.h" +#include "qpid/amqp/Decoder.h" +#include <boost/lexical_cast.hpp> +#include <string.h> + +namespace qpid { +namespace messaging { +namespace amqp { + +using namespace qpid::amqp; + +EncodedMessage::EncodedMessage(size_t s) : size(s), data(size ? new char[size] : 0) +{ + init(); +} + +EncodedMessage::EncodedMessage() : size(0), data(0) +{ + init(); +} + +EncodedMessage::EncodedMessage(const EncodedMessage& other) : size(other.size), data(size ? new char[size] : 0) +{ + init(); +} + +void EncodedMessage::init() +{ + //init all CharSequence members + deliveryAnnotations.init(); + messageAnnotations.init(); + userId.init(); + to.init(); + subject.init(); + replyTo.init(); + contentType.init(); + contentEncoding.init(); + groupId.init(); + replyToGroupId.init(); + applicationProperties.init(); + body.init(); + footer.init(); +} + +EncodedMessage::~EncodedMessage() +{ + delete[] data; +} + +size_t EncodedMessage::getSize() const +{ + return size; +} +void EncodedMessage::trim(size_t t) +{ + size = t; +} +void EncodedMessage::resize(size_t s) +{ + delete[] data; + size = s; + data = new char[size]; +} + +char* EncodedMessage::getData() +{ + return data; +} +const char* EncodedMessage::getData() const +{ + return data; +} + +void EncodedMessage::init(qpid::messaging::MessageImpl& impl) +{ + //initial scan of raw data + qpid::amqp::Decoder decoder(data, size); + InitialScan reader(*this, impl); + decoder.read(reader); + bareMessage = reader.getBareMessage(); + if (bareMessage.data && !bareMessage.size) { + bareMessage.size = (data + size) - bareMessage.data; + } + +} +void EncodedMessage::populate(qpid::types::Variant::Map& map) const +{ + //decode application properties + if (applicationProperties) { + qpid::amqp::Decoder decoder(applicationProperties.data, applicationProperties.size); + decoder.readMap(map); + } + //add in 'x-amqp-' prefixed values + if (!!firstAcquirer) { + map["x-amqp-first-acquirer"] = firstAcquirer.get(); + } + if (!!deliveryCount) { + map["x-amqp-delivery-count"] = deliveryCount.get(); + } + if (to) { + map["x-amqp-delivery-count"] = to.str(); + } + if (!!absoluteExpiryTime) { + map["x-amqp-absolute-expiry-time"] = absoluteExpiryTime.get(); + } + if (!!creationTime) { + map["x-amqp-creation-time"] = creationTime.get(); + } + if (groupId) { + map["x-amqp-group-id"] = groupId.str(); + } + if (!!groupSequence) { + map["x-amqp-qroup-sequence"] = groupSequence.get(); + } + if (replyToGroupId) { + map["x-amqp-reply-to-group-id"] = replyToGroupId.str(); + } + //add in any annotations + if (deliveryAnnotations) { + qpid::types::Variant::Map& annotations = map["x-amqp-delivery-annotations"].asMap(); + qpid::amqp::Decoder decoder(deliveryAnnotations.data, deliveryAnnotations.size); + decoder.readMap(annotations); + } + if (messageAnnotations) { + qpid::types::Variant::Map& annotations = map["x-amqp-message-annotations"].asMap(); + qpid::amqp::Decoder decoder(messageAnnotations.data, messageAnnotations.size); + decoder.readMap(annotations); + } +} +qpid::amqp::CharSequence EncodedMessage::getBareMessage() const +{ + return bareMessage; +} + +void EncodedMessage::getReplyTo(qpid::messaging::Address& a) const +{ + a = qpid::messaging::Address(replyTo.str()); +} +void EncodedMessage::getSubject(std::string& s) const +{ + s.assign(subject.data, subject.size); +} +void EncodedMessage::getContentType(std::string& s) const +{ + s.assign(contentType.data, contentType.size); +} +void EncodedMessage::getUserId(std::string& s) const +{ + s.assign(userId.data, userId.size); +} +void EncodedMessage::getMessageId(std::string& s) const +{ + messageId.assign(s); +} +void EncodedMessage::getCorrelationId(std::string& s) const +{ + correlationId.assign(s); +} +void EncodedMessage::getBody(std::string& s) const +{ + s.assign(body.data, body.size); +} + +qpid::amqp::CharSequence EncodedMessage::getBody() const +{ + return body; +} + +bool EncodedMessage::hasHeaderChanged(const qpid::messaging::MessageImpl& msg) const +{ + if (!durable) { + if (msg.isDurable()) return true; + } else { + if (durable != msg.isDurable()) return true; + } + + if (!priority) { + if (msg.getPriority() != 4) return true; + } else { + if (priority.get() != msg.getPriority()) return true; + } + + if (msg.getTtl() && (!ttl || msg.getTtl() != ttl.get())) { + return true; + } + + //first-acquirer can't be changed via Message interface as yet + + if (msg.isRedelivered() && (!deliveryCount || deliveryCount.get() == 0)) { + return true; + } + + return false; +} + + +EncodedMessage::InitialScan::InitialScan(EncodedMessage& e, qpid::messaging::MessageImpl& m) : em(e), mi(m) +{ + //set up defaults as needed: + mi.setPriority(4); +} +//header: +void EncodedMessage::InitialScan::onDurable(bool b) { mi.setDurable(b); em.durable = b; } +void EncodedMessage::InitialScan::onPriority(uint8_t i) { mi.setPriority(i); em.priority = i; } +void EncodedMessage::InitialScan::onTtl(uint32_t i) { mi.setTtl(i); em.ttl = i; } +void EncodedMessage::InitialScan::onFirstAcquirer(bool b) { em.firstAcquirer = b; } +void EncodedMessage::InitialScan::onDeliveryCount(uint32_t i) +{ + mi.setRedelivered(i); + em.deliveryCount = i; +} + +//properties: +void EncodedMessage::InitialScan::onMessageId(uint64_t v) { em.messageId.set(v); } +void EncodedMessage::InitialScan::onMessageId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { em.messageId.set(v, t); } +void EncodedMessage::InitialScan::onUserId(const qpid::amqp::CharSequence& v) { em.userId = v; } +void EncodedMessage::InitialScan::onTo(const qpid::amqp::CharSequence& v) { em.to = v; } +void EncodedMessage::InitialScan::onSubject(const qpid::amqp::CharSequence& v) { em.subject = v; } +void EncodedMessage::InitialScan::onReplyTo(const qpid::amqp::CharSequence& v) { em.replyTo = v;} +void EncodedMessage::InitialScan::onCorrelationId(uint64_t v) { em.correlationId.set(v); } +void EncodedMessage::InitialScan::onCorrelationId(const qpid::amqp::CharSequence& v, qpid::types::VariantType t) { em.correlationId.set(v, t); } +void EncodedMessage::InitialScan::onContentType(const qpid::amqp::CharSequence& v) { em.contentType = v; } +void EncodedMessage::InitialScan::onContentEncoding(const qpid::amqp::CharSequence& v) { em.contentEncoding = v; } +void EncodedMessage::InitialScan::onAbsoluteExpiryTime(int64_t i) { em.absoluteExpiryTime = i; } +void EncodedMessage::InitialScan::onCreationTime(int64_t i) { em.creationTime = i; } +void EncodedMessage::InitialScan::onGroupId(const qpid::amqp::CharSequence& v) { em.groupId = v; } +void EncodedMessage::InitialScan::onGroupSequence(uint32_t i) { em.groupSequence = i; } +void EncodedMessage::InitialScan::onReplyToGroupId(const qpid::amqp::CharSequence& v) { em.replyToGroupId = v; } + +void EncodedMessage::InitialScan::onApplicationProperties(const qpid::amqp::CharSequence& v) { em.applicationProperties = v; } +void EncodedMessage::InitialScan::onDeliveryAnnotations(const qpid::amqp::CharSequence& v) { em.deliveryAnnotations = v; } +void EncodedMessage::InitialScan::onMessageAnnotations(const qpid::amqp::CharSequence& v) { em.messageAnnotations = v; } +void EncodedMessage::InitialScan::onBody(const qpid::amqp::CharSequence& v, const qpid::amqp::Descriptor&) +{ + //TODO: how to communicate the type, i.e. descriptor? + em.body = v; +} +void EncodedMessage::InitialScan::onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&) {} +void EncodedMessage::InitialScan::onFooter(const qpid::amqp::CharSequence& v) { em.footer = v; } + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/EncodedMessage.h b/cpp/src/qpid/messaging/amqp/EncodedMessage.h new file mode 100644 index 0000000000..4616fcd5d6 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/EncodedMessage.h @@ -0,0 +1,177 @@ +#ifndef QPID_MESSAGING_AMQP_ENCODEDMESSAGE_H +#define QPID_MESSAGING_AMQP_ENCODEDMESSAGE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/amqp/CharSequence.h" +#include "qpid/amqp/MessageId.h" +#include "qpid/amqp/MessageReader.h" +#include "qpid/sys/IntegerTypes.h" +#include "qpid/types/Variant.h" +#include <boost/optional.hpp> + +namespace qpid { +namespace amqp { +struct Descriptor; +} +namespace messaging { +class Address; +struct MessageImpl; +namespace amqp { + +/** + * Used to 'lazy-decode' an AMQP 1.0 message. + * + * There are four categories of data item: + * + * (i) simple, fixed width primitives - priority, ttl, durability, + * delivery count - for which lazy-decoding doesn't buy much. These + * are decoded unconditionally on an initial scan of the message. + * + * (ii) standard variable length string properties - subject, + * message-id, user-id etc - which require conversion to a std::string + * for returning to the application. By delaying the conversion of + * these to a std::string we can avoid allocation & copying until it + * is actually required. The initial scan of the message merely + * records the position of these strings within the raw message data. + * + * (iii) custom, application defined headers. These form a map, and + * again, delaying the creation of that map until it is actually + * required can be advantageous. The initial scan of the message merely + * records the position of this section within the raw message data. + * + * (iv) the body content. This may be retreived as a std::string, or + * as a char*. Avoiding conversion to the string until it is required + * is advantageous. The initial scan of the message merely records the + * position of this section within the raw message data. + * + * At present the Message class only explicitly exposes some of the + * standard property and headers defined by AMQP 1.0. The remainder + * will have to be accessed through the message 'headers' map, using + * the 'x-amqp-' prefix. + */ +class EncodedMessage +{ + public: + EncodedMessage(); + EncodedMessage(size_t); + EncodedMessage(const EncodedMessage&); + ~EncodedMessage(); + + + size_t getSize() const; + char* getData(); + const char* getData() const; + void trim(size_t); + void resize(size_t); + + void getReplyTo(qpid::messaging::Address&) const; + void getSubject(std::string&) const; + void getContentType(std::string&) const; + void getMessageId(std::string&) const; + void getUserId(std::string&) const; + void getCorrelationId(std::string&) const; + + void init(qpid::messaging::MessageImpl&); + void populate(qpid::types::Variant::Map&) const; + void getBody(std::string&) const; + qpid::amqp::CharSequence getBareMessage() const; + qpid::amqp::CharSequence getBody() const; + bool hasHeaderChanged(const qpid::messaging::MessageImpl&) const; + private: + size_t size; + char* data; + + class InitialScan : public qpid::amqp::MessageReader + { + public: + InitialScan(EncodedMessage& e, qpid::messaging::MessageImpl& m); + //header: + void onDurable(bool b); + void onPriority(uint8_t i); + void onTtl(uint32_t i); + void onFirstAcquirer(bool b); + void onDeliveryCount(uint32_t i); + //properties: + void onMessageId(uint64_t); + void onMessageId(const qpid::amqp::CharSequence&, qpid::types::VariantType); + void onUserId(const qpid::amqp::CharSequence& v); + void onTo(const qpid::amqp::CharSequence& v); + void onSubject(const qpid::amqp::CharSequence& v); + void onReplyTo(const qpid::amqp::CharSequence& v); + void onCorrelationId(uint64_t); + void onCorrelationId(const qpid::amqp::CharSequence&, qpid::types::VariantType); + void onContentType(const qpid::amqp::CharSequence& v); + void onContentEncoding(const qpid::amqp::CharSequence& v); + void onAbsoluteExpiryTime(int64_t i); + void onCreationTime(int64_t); + void onGroupId(const qpid::amqp::CharSequence&); + void onGroupSequence(uint32_t); + void onReplyToGroupId(const qpid::amqp::CharSequence&); + + void onApplicationProperties(const qpid::amqp::CharSequence&); + void onDeliveryAnnotations(const qpid::amqp::CharSequence&); + void onMessageAnnotations(const qpid::amqp::CharSequence&); + void onBody(const qpid::amqp::CharSequence&, const qpid::amqp::Descriptor&); + void onBody(const qpid::types::Variant&, const qpid::amqp::Descriptor&); + void onFooter(const qpid::amqp::CharSequence&); + private: + EncodedMessage& em; + qpid::messaging::MessageImpl& mi; + }; + //header: + boost::optional<bool> durable; + boost::optional<uint8_t> priority; + boost::optional<uint32_t> ttl; + boost::optional<bool> firstAcquirer; + boost::optional<uint32_t> deliveryCount; + //annotations: + qpid::amqp::CharSequence deliveryAnnotations; + qpid::amqp::CharSequence messageAnnotations; + + qpid::amqp::CharSequence bareMessage;//properties, application-properties and content + //properties: + qpid::amqp::MessageId messageId; + qpid::amqp::CharSequence userId; + qpid::amqp::CharSequence to; + qpid::amqp::CharSequence subject; + qpid::amqp::CharSequence replyTo; + qpid::amqp::MessageId correlationId; + qpid::amqp::CharSequence contentType; + qpid::amqp::CharSequence contentEncoding; + boost::optional<int64_t> absoluteExpiryTime; + boost::optional<int64_t> creationTime; + qpid::amqp::CharSequence groupId; + boost::optional<uint32_t> groupSequence; + qpid::amqp::CharSequence replyToGroupId; + //application-properties: + qpid::amqp::CharSequence applicationProperties; + qpid::amqp::CharSequence body; + //footer: + qpid::amqp::CharSequence footer; + + void init(); + //not implemented: + EncodedMessage& operator=(const EncodedMessage&); +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_ENCODEDMESSAGE_H*/ diff --git a/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp b/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp new file mode 100644 index 0000000000..7e41e727cc --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp @@ -0,0 +1,97 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ReceiverContext.h" +#include "qpid/messaging/Duration.h" +#include "qpid/messaging/Message.h" +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace messaging { +namespace amqp { +//TODO: proper conversion to wide string for address +ReceiverContext::ReceiverContext(pn_session_t* session, const std::string& n, const std::string& s) + : name(n), + source(s), + receiver(pn_receiver(session, source.c_str())), + capacity(0) {} +ReceiverContext::~ReceiverContext() +{ + pn_link_free(receiver); +} + +void ReceiverContext::setCapacity(uint32_t c) +{ + if (c != capacity) { + //stop + capacity = c; + //reissue credit + } +} + +uint32_t ReceiverContext::getCapacity() +{ + return capacity; +} + +uint32_t ReceiverContext::getAvailable() +{ + uint32_t count(0); + for (pn_delivery_t* d = pn_unsettled_head(receiver); d; d = pn_unsettled_next(d)) { + ++count; + if (d == pn_link_current(receiver)) break; + } + return count; +} + +uint32_t ReceiverContext::getUnsettled() +{ + uint32_t count(0); + for (pn_delivery_t* d = pn_unsettled_head(receiver); d; d = pn_unsettled_next(d)) { + ++count; + } + return count; +} + +void ReceiverContext::close() +{ + +} + +const std::string& ReceiverContext::getName() const +{ + return name; +} + +const std::string& ReceiverContext::getSource() const +{ + return source; +} + +bool ReceiverContext::isClosed() const +{ + return false;//TODO +} + + + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/ReceiverContext.h b/cpp/src/qpid/messaging/amqp/ReceiverContext.h new file mode 100644 index 0000000000..0a6f363228 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ReceiverContext.h @@ -0,0 +1,63 @@ +#ifndef QPID_MESSAGING_AMQP_RECEIVERCONTEXT_H +#define QPID_MESSAGING_AMQP_RECEIVERCONTEXT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <string> +#include "qpid/sys/IntegerTypes.h" + +struct pn_link_t; +struct pn_session_t; + +namespace qpid { +namespace messaging { + +class Duration; +class Message; + +namespace amqp { + +/** + * + */ +class ReceiverContext +{ + public: + ReceiverContext(pn_session_t* session, const std::string& name, const std::string& source); + ~ReceiverContext(); + void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t getAvailable(); + uint32_t getUnsettled(); + void close(); + const std::string& getName() const; + const std::string& getSource() const; + bool isClosed() const; + private: + friend class ConnectionContext; + const std::string name; + const std::string source; + pn_link_t* receiver; + uint32_t capacity; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_RECEIVERCONTEXT_H*/ diff --git a/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp b/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp new file mode 100644 index 0000000000..9bf64ebb8d --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp @@ -0,0 +1,106 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ReceiverHandle.h" +#include "ConnectionContext.h" +#include "SessionContext.h" +#include "SessionHandle.h" +#include "ReceiverContext.h" +#include "qpid/messaging/Duration.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Session.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +ReceiverHandle::ReceiverHandle(boost::shared_ptr<ConnectionContext> c, + boost::shared_ptr<SessionContext> s, + boost::shared_ptr<ReceiverContext> r +) : connection(c), session(s), receiver(r) {} + + +bool ReceiverHandle::get(qpid::messaging::Message& message, qpid::messaging::Duration timeout) +{ + return connection->get(session, receiver, message, timeout); +} + +qpid::messaging::Message ReceiverHandle::get(qpid::messaging::Duration timeout) +{ + qpid::messaging::Message result; + if (!get(result, timeout)) throw qpid::messaging::NoMessageAvailable(); + return result; +} + +bool ReceiverHandle::fetch(qpid::messaging::Message& message, qpid::messaging::Duration timeout) +{ + return connection->fetch(session, receiver, message, timeout); +} + +qpid::messaging::Message ReceiverHandle::fetch(qpid::messaging::Duration timeout) +{ + qpid::messaging::Message result; + if (!fetch(result, timeout)) throw qpid::messaging::NoMessageAvailable(); + return result; +} + +void ReceiverHandle::setCapacity(uint32_t capacity) +{ + connection->setCapacity(receiver, capacity); +} + +uint32_t ReceiverHandle::getCapacity() +{ + return connection->getCapacity(receiver); +} + +uint32_t ReceiverHandle::getAvailable() +{ + return connection->getAvailable(receiver); +} + +uint32_t ReceiverHandle::getUnsettled() +{ + return connection->getUnsettled(receiver); +} + +void ReceiverHandle::close() +{ + session->closeReceiver(getName()); +} + +const std::string& ReceiverHandle::getName() const +{ + return receiver->getName(); +} + +qpid::messaging::Session ReceiverHandle::getSession() const +{ + //create new SessionHandle instance; i.e. create new handle that shares the same context + return qpid::messaging::Session(new SessionHandle(connection, session)); +} + +bool ReceiverHandle::isClosed() const +{ + return receiver->isClosed(); +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/ReceiverHandle.h b/cpp/src/qpid/messaging/amqp/ReceiverHandle.h new file mode 100644 index 0000000000..a1a6f26025 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/ReceiverHandle.h @@ -0,0 +1,63 @@ +#ifndef QPID_MESSAGING_AMQP_RECEIVERHANDLE_H +#define QPID_MESSAGING_AMQP_RECEIVERHANDLE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/shared_ptr.hpp> +#include "qpid/messaging/ReceiverImpl.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +class ConnectionContext; +class SessionContext; +class ReceiverContext; +/** + * + */ +class ReceiverHandle : public qpid::messaging::ReceiverImpl +{ + public: + ReceiverHandle(boost::shared_ptr<ConnectionContext>, + boost::shared_ptr<SessionContext>, + boost::shared_ptr<ReceiverContext> + ); + bool get(Message& message, qpid::messaging::Duration timeout); + qpid::messaging::Message get(qpid::messaging::Duration timeout); + bool fetch(Message& message, qpid::messaging::Duration timeout); + qpid::messaging::Message fetch(qpid::messaging::Duration timeout); + void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t getAvailable(); + uint32_t getUnsettled(); + void close(); + const std::string& getName() const; + qpid::messaging::Session getSession() const; + bool isClosed() const; + private: + boost::shared_ptr<ConnectionContext> connection; + boost::shared_ptr<SessionContext> session; + boost::shared_ptr<ReceiverContext> receiver; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_RECEIVERHANDLE_H*/ diff --git a/cpp/src/qpid/messaging/amqp/Sasl.cpp b/cpp/src/qpid/messaging/amqp/Sasl.cpp new file mode 100644 index 0000000000..af13697c20 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/Sasl.cpp @@ -0,0 +1,162 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "ConnectionContext.h" +#include "qpid/messaging/amqp/Sasl.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/sys/SecurityLayer.h" +#include "qpid/log/Statement.h" +#include "qpid/Sasl.h" +#include "qpid/SaslFactory.h" +#include "qpid/StringUtils.h" +#include <sstream> + +namespace qpid { +namespace messaging { +namespace amqp { + +Sasl::Sasl(const std::string& id, ConnectionContext& c, const std::string& hostname_) + : qpid::amqp::SaslClient(id), context(c), + sasl(qpid::SaslFactory::getInstance().create(c.username, c.password, c.service, hostname_, c.minSsf, c.maxSsf, false)), + hostname(hostname_), readHeader(true), writeHeader(true), haveOutput(false), state(NONE) {} + +std::size_t Sasl::decode(const char* buffer, std::size_t size) +{ + size_t decoded = 0; + if (readHeader) { + decoded += readProtocolHeader(buffer, size); + readHeader = !decoded; + } + if (state == NONE && decoded < size) { + decoded += read(buffer + decoded, size - decoded); + } + QPID_LOG(trace, id << " Sasl::decode(" << size << "): " << decoded); + return decoded; +} + +std::size_t Sasl::encode(char* buffer, std::size_t size) +{ + size_t encoded = 0; + if (writeHeader) { + encoded += writeProtocolHeader(buffer, size); + writeHeader = !encoded; + } + if (state == NONE && encoded < size) { + encoded += write(buffer + encoded, size - encoded); + } + haveOutput = (encoded == size); + QPID_LOG(trace, id << " Sasl::encode(" << size << "): " << encoded); + return encoded; +} + +bool Sasl::canEncode() +{ + QPID_LOG(trace, id << " Sasl::canEncode(): " << writeHeader << " || " << haveOutput); + return writeHeader || haveOutput; +} + +void Sasl::mechanisms(const std::string& offered) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-MECHANISMS(" << offered << ")"); + std::string response; + + std::string mechanisms; + if (context.mechanism.size()) { + std::vector<std::string> allowed = split(context.mechanism, " "); + std::vector<std::string> supported = split(offered, " "); + std::stringstream intersection; + for (std::vector<std::string>::const_iterator i = allowed.begin(); i != allowed.end(); ++i) { + if (std::find(supported.begin(), supported.end(), *i) != supported.end()) { + intersection << *i << " "; + } + } + mechanisms = intersection.str(); + } else { + mechanisms = offered; + } + + if (sasl->start(mechanisms, response)) { + init(sasl->getMechanism(), &response, hostname.size() ? &hostname : 0); + } else { + init(sasl->getMechanism(), 0, hostname.size() ? &hostname : 0); + } + haveOutput = true; + context.activateOutput(); +} +void Sasl::challenge(const std::string& challenge) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-CHALLENGE(" << challenge.size() << " bytes)"); + std::string r = sasl->step(challenge); + response(&r); + haveOutput = true; + context.activateOutput(); +} +namespace { +const std::string EMPTY; +} +void Sasl::challenge() +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-CHALLENGE(null)"); + std::string r = sasl->step(EMPTY); + response(&r); +} +void Sasl::outcome(uint8_t result, const std::string& extra) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-OUTCOME(" << result << ", " << extra << ")"); + outcome(result); +} +void Sasl::outcome(uint8_t result) +{ + QPID_LOG_CAT(debug, protocol, id << " Received SASL-OUTCOME(" << result << ")"); + if (result) state = FAILED; + else state = SUCCEEDED; + + securityLayer = sasl->getSecurityLayer(context.maxFrameSize); + if (securityLayer.get()) { + securityLayer->init(&context); + } + context.activateOutput(); +} + +qpid::sys::Codec* Sasl::getCodec() +{ + switch (state) { + case SUCCEEDED: return static_cast<qpid::sys::Codec*>(securityLayer.get()); + case FAILED: throw qpid::messaging::UnauthorizedAccess("Failed to authenticate"); + case NONE: return static_cast<qpid::sys::Codec*>(this); + } + return 0; +} + +bool Sasl::authenticated() +{ + switch (state) { + case SUCCEEDED: return true; + case FAILED: throw qpid::messaging::UnauthorizedAccess("Failed to authenticate"); + case NONE: default: return false; + } +} + +std::string Sasl::getAuthenticatedUsername() +{ + return sasl->getUserId(); +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/Sasl.h b/cpp/src/qpid/messaging/amqp/Sasl.h new file mode 100644 index 0000000000..3a2f2e9ffc --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/Sasl.h @@ -0,0 +1,72 @@ +#ifndef QPID_MESSAGING_AMQP_SASL_H +#define QPID_MESSAGING_AMQP_SASL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/Codec.h" +#include "qpid/amqp/SaslClient.h" +#include <memory> + +namespace qpid { +class Sasl; +namespace sys { +class SecurityLayer; +} +namespace messaging { +class ConnectionOptions; +namespace amqp { +class ConnectionContext; + +/** + * + */ +class Sasl : public qpid::sys::Codec, qpid::amqp::SaslClient +{ + public: + Sasl(const std::string& id, ConnectionContext& context, const std::string& hostname); + std::size_t decode(const char* buffer, std::size_t size); + std::size_t encode(char* buffer, std::size_t size); + bool canEncode(); + + bool authenticated(); + qpid::sys::Codec* getCodec(); + std::string getAuthenticatedUsername(); + private: + ConnectionContext& context; + std::auto_ptr<qpid::Sasl> sasl; + std::string hostname; + bool readHeader; + bool writeHeader; + bool haveOutput; + enum { + NONE, FAILED, SUCCEEDED + } state; + std::auto_ptr<qpid::sys::SecurityLayer> securityLayer; + + void mechanisms(const std::string&); + void challenge(const std::string&); + void challenge(); //null != empty string + void outcome(uint8_t result, const std::string&); + void outcome(uint8_t result); +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SASL_H*/ diff --git a/cpp/src/qpid/messaging/amqp/SenderContext.cpp b/cpp/src/qpid/messaging/amqp/SenderContext.cpp new file mode 100644 index 0000000000..1306a314be --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SenderContext.cpp @@ -0,0 +1,332 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/amqp/SenderContext.h" +#include "qpid/messaging/amqp/EncodedMessage.h" +#include "qpid/amqp/descriptors.h" +#include "qpid/amqp/MessageEncoder.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/MessageImpl.h" +#include "qpid/log/Statement.h" +extern "C" { +#include <proton/engine.h> +} +#include <boost/shared_ptr.hpp> +#include <string.h> + +namespace qpid { +namespace messaging { +namespace amqp { +//TODO: proper conversion to wide string for address +SenderContext::SenderContext(pn_session_t* session, const std::string& n, const std::string& t) + : name(n), + target(t), + sender(pn_sender(session, target.c_str())), capacity(1000) {} + +SenderContext::~SenderContext() +{ + pn_link_free(sender); +} + +void SenderContext::close() +{ + +} + +void SenderContext::setCapacity(uint32_t c) +{ + if (c < deliveries.size()) throw qpid::messaging::SenderError("Desired capacity is less than unsettled message count!"); + capacity = c; +} + +uint32_t SenderContext::getCapacity() +{ + return capacity; +} + +uint32_t SenderContext::getUnsettled() +{ + return processUnsettled(); +} + +const std::string& SenderContext::getName() const +{ + return name; +} + +const std::string& SenderContext::getTarget() const +{ + return target; +} + +SenderContext::Delivery* SenderContext::send(const qpid::messaging::Message& message) +{ + if (processUnsettled() < capacity) { + deliveries.push_back(Delivery(nextId++)); + Delivery& delivery = deliveries.back(); + delivery.encode(MessageImplAccess::get(message)); + delivery.send(sender); + return &delivery; + } else { + return 0; + } +} + +uint32_t SenderContext::processUnsettled() +{ + //remove accepted messages from front of deque + while (!deliveries.empty() && deliveries.front().accepted()) { + deliveries.pop_front(); + } + return deliveries.size(); +} +namespace { +class HeaderAdapter : public qpid::amqp::MessageEncoder::Header +{ + public: + HeaderAdapter(const qpid::messaging::MessageImpl& impl) : msg(impl) {} + virtual bool isDurable() const + { + return msg.isDurable(); + } + virtual uint8_t getPriority() const + { + return msg.getPriority(); + } + virtual bool hasTtl() const + { + return msg.getTtl(); + } + virtual uint32_t getTtl() const + { + return msg.getTtl(); + } + virtual bool isFirstAcquirer() const + { + return false; + } + virtual uint32_t getDeliveryCount() const + { + return msg.isRedelivered() ? 1 : 0; + } + private: + const qpid::messaging::MessageImpl& msg; +}; +const std::string EMPTY; + +class PropertiesAdapter : public qpid::amqp::MessageEncoder::Properties +{ + public: + PropertiesAdapter(const qpid::messaging::MessageImpl& impl) : msg(impl) {} + bool hasMessageId() const + { + return getMessageId().size(); + } + std::string getMessageId() const + { + return msg.getMessageId(); + } + + bool hasUserId() const + { + return getUserId().size(); + } + + std::string getUserId() const + { + return msg.getUserId(); + } + + bool hasTo() const + { + return false;//not yet supported + } + + std::string getTo() const + { + return EMPTY;//not yet supported + } + + bool hasSubject() const + { + return getSubject().size(); + } + + std::string getSubject() const + { + return msg.getSubject(); + } + + bool hasReplyTo() const + { + return msg.getReplyTo(); + } + + std::string getReplyTo() const + { + return msg.getReplyTo().str(); + } + + bool hasCorrelationId() const + { + return getCorrelationId().size(); + } + + std::string getCorrelationId() const + { + return msg.getCorrelationId(); + } + + bool hasContentType() const + { + return getContentType().size(); + } + + std::string getContentType() const + { + return msg.getContentType(); + } + + bool hasContentEncoding() const + { + return false;//not yet supported + } + + std::string getContentEncoding() const + { + return EMPTY;//not yet supported + } + + bool hasAbsoluteExpiryTime() const + { + return false;//not yet supported + } + + int64_t getAbsoluteExpiryTime() const + { + return 0;//not yet supported + } + + bool hasCreationTime() const + { + return false;//not yet supported + } + + int64_t getCreationTime() const + { + return 0;//not yet supported + } + + bool hasGroupId() const + { + return false;//not yet supported + } + + std::string getGroupId() const + { + return EMPTY;//not yet supported + } + + bool hasGroupSequence() const + { + return false;//not yet supported + } + + uint32_t getGroupSequence() const + { + return 0;//not yet supported + } + + bool hasReplyToGroupId() const + { + return false;//not yet supported + } + + std::string getReplyToGroupId() const + { + return EMPTY;//not yet supported + } + private: + const qpid::messaging::MessageImpl& msg; +}; +} + +SenderContext::Delivery::Delivery(int32_t i) : id(i), token(0) {} + +void SenderContext::Delivery::encode(const qpid::messaging::MessageImpl& msg) +{ + boost::shared_ptr<const EncodedMessage> original = msg.getEncoded(); + + if (original) { //still have the content as received, send at least the bare message unaltered + //do we need to alter the header? are durable, priority, ttl, first-acquirer, delivery-count different from what was received? + if (original->hasHeaderChanged(msg)) { + //since as yet have no annotations, just write the revised header then the rest of the message as received + encoded.resize(16/*max header size*/ + original->getBareMessage().size); + qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize()); + HeaderAdapter header(msg); + encoder.writeHeader(header); + ::memcpy(encoded.getData() + encoder.getPosition(), original->getBareMessage().data, original->getBareMessage().size); + } else { + //since as yet have no annotations, if the header hasn't + //changed and we still have the original bare message, can + //send the entire content as is + encoded.resize(original->getSize()); + ::memcpy(encoded.getData(), original->getData(), original->getSize()); + } + } else { + HeaderAdapter header(msg); + PropertiesAdapter properties(msg); + //compute size: + encoded.resize(qpid::amqp::MessageEncoder::getEncodedSize(header, properties, msg.getHeaders(), msg.getBytes())); + QPID_LOG(debug, "Sending message, buffer is " << encoded.getSize() << " bytes") + qpid::amqp::MessageEncoder encoder(encoded.getData(), encoded.getSize()); + //write header: + encoder.writeHeader(header); + //write delivery-annotations, write message-annotations (none yet supported) + //write properties + encoder.writeProperties(properties); + //write application-properties + encoder.writeApplicationProperties(msg.getHeaders()); + //write body + if (msg.getBytes().size()) encoder.writeBinary(msg.getBytes(), &qpid::amqp::message::DATA);//structured content not yet directly supported + if (encoder.getPosition() < encoded.getSize()) { + QPID_LOG(debug, "Trimming buffer from " << encoded.getSize() << " to " << encoder.getPosition()); + encoded.trim(encoder.getPosition()); + } + //write footer (no annotations yet supported) + } +} +void SenderContext::Delivery::send(pn_link_t* sender) +{ + pn_delivery_tag_t tag; + tag.size = sizeof(id); + tag.bytes = reinterpret_cast<const char*>(&id); + token = pn_delivery(sender, tag); + pn_link_send(sender, encoded.getData(), encoded.getSize()); + pn_link_advance(sender); +} + +bool SenderContext::Delivery::accepted() +{ + return pn_delivery_remote_state(token) == PN_ACCEPTED; +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/SenderContext.h b/cpp/src/qpid/messaging/amqp/SenderContext.h new file mode 100644 index 0000000000..82c5e6dab9 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SenderContext.h @@ -0,0 +1,84 @@ +#ifndef QPID_MESSAGING_AMQP_SENDERCONTEXT_H +#define QPID_MESSAGING_AMQP_SENDERCONTEXT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <deque> +#include <string> +#include <vector> +#include "qpid/sys/IntegerTypes.h" +#include "qpid/messaging/amqp/EncodedMessage.h" + +struct pn_delivery_t; +struct pn_link_t; +struct pn_session_t; + +namespace qpid { +namespace messaging { + +class Message; +class MessageImpl; + +namespace amqp { +/** + * + */ +class SenderContext +{ + public: + class Delivery + { + public: + Delivery(int32_t id); + void encode(const qpid::messaging::MessageImpl& message); + void send(pn_link_t*); + bool accepted(); + private: + int32_t id; + pn_delivery_t* token; + EncodedMessage encoded; + }; + + SenderContext(pn_session_t* session, const std::string& name, const std::string& target); + ~SenderContext(); + void close(); + void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t getUnsettled(); + const std::string& getName() const; + const std::string& getTarget() const; + Delivery* send(const qpid::messaging::Message& message); + private: + friend class ConnectionContext; + typedef std::deque<Delivery> Deliveries; + + const std::string name; + const std::string target; + pn_link_t* sender; + int32_t nextId; + Deliveries deliveries; + uint32_t capacity; + + uint32_t processUnsettled(); +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SENDERCONTEXT_H*/ diff --git a/cpp/src/qpid/messaging/amqp/SenderHandle.cpp b/cpp/src/qpid/messaging/amqp/SenderHandle.cpp new file mode 100644 index 0000000000..b7168e5b31 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SenderHandle.cpp @@ -0,0 +1,75 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SenderHandle.h" +#include "ConnectionContext.h" +#include "SessionContext.h" +#include "SessionHandle.h" +#include "SenderContext.h" +#include "qpid/messaging/Duration.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Session.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +SenderHandle::SenderHandle(boost::shared_ptr<ConnectionContext> c, + boost::shared_ptr<SessionContext> s, + boost::shared_ptr<SenderContext> sndr +) : connection(c), session(s), sender(sndr) {} + +void SenderHandle::send(const Message& message, bool sync) +{ + connection->send(sender, message, sync); +} + +void SenderHandle::close() +{ + session->closeSender(getName()); +} + +void SenderHandle::setCapacity(uint32_t capacity) +{ + connection->setCapacity(sender, capacity); +} + +uint32_t SenderHandle::getCapacity() +{ + return connection->getCapacity(sender); +} + +uint32_t SenderHandle::getUnsettled() +{ + return connection->getUnsettled(sender); +} + +const std::string& SenderHandle::getName() const +{ + return sender->getName(); +} + +qpid::messaging::Session SenderHandle::getSession() const +{ + return qpid::messaging::Session(new SessionHandle(connection, session)); +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/SenderHandle.h b/cpp/src/qpid/messaging/amqp/SenderHandle.h new file mode 100644 index 0000000000..3c6b666582 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SenderHandle.h @@ -0,0 +1,58 @@ +#ifndef QPID_MESSAGING_AMQP_SENDERHANDLE_H +#define QPID_MESSAGING_AMQP_SENDERHANDLE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/shared_ptr.hpp> +#include "qpid/messaging/SenderImpl.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +class ConnectionContext; +class SessionContext; +class SenderContext; +/** + * + */ +class SenderHandle : public qpid::messaging::SenderImpl +{ + public: + SenderHandle(boost::shared_ptr<ConnectionContext> connection, + boost::shared_ptr<SessionContext> session, + boost::shared_ptr<SenderContext> sender + ); + void send(const Message& message, bool sync); + void close(); + void setCapacity(uint32_t); + uint32_t getCapacity(); + uint32_t getUnsettled(); + const std::string& getName() const; + Session getSession() const; + private: + boost::shared_ptr<ConnectionContext> connection; + boost::shared_ptr<SessionContext> session; + boost::shared_ptr<SenderContext> sender; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SENDERHANDLE_H*/ diff --git a/cpp/src/qpid/messaging/amqp/SessionContext.cpp b/cpp/src/qpid/messaging/amqp/SessionContext.cpp new file mode 100644 index 0000000000..9908b16443 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SessionContext.cpp @@ -0,0 +1,147 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SessionContext.h" +#include "SenderContext.h" +#include "ReceiverContext.h" +#include <boost/format.hpp> +#include "qpid/messaging/Address.h" +#include "qpid/messaging/Duration.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/log/Statement.h" +extern "C" { +#include <proton/engine.h> +} + +namespace qpid { +namespace messaging { +namespace amqp { + +SessionContext::SessionContext(pn_connection_t* connection) : session(pn_session(connection)) {} +SessionContext::~SessionContext() +{ + senders.clear(); receivers.clear(); + pn_session_free(session); +} + +boost::shared_ptr<SenderContext> SessionContext::createSender(const qpid::messaging::Address& address) +{ + std::string name = address.getName(); + + int count = 1; + for (SenderMap::const_iterator i = senders.find(name); i != senders.end(); i = senders.find(name)) { + name = (boost::format("%1%_%2%") % address.getName() % ++count).str(); + } + boost::shared_ptr<SenderContext> s(new SenderContext(session, name, address.str())); + senders[name] = s; + return s; +} + +boost::shared_ptr<ReceiverContext> SessionContext::createReceiver(const qpid::messaging::Address& address) +{ + std::string name = address.getName(); + + int count = 1; + for (ReceiverMap::const_iterator i = receivers.find(name); i != receivers.end(); i = receivers.find(name)) { + name = (boost::format("%1%_%2%") % address.getName() % ++count).str(); + } + boost::shared_ptr<ReceiverContext> r(new ReceiverContext(session, name, address.str())); + receivers[name] = r; + return r; +} + +boost::shared_ptr<SenderContext> SessionContext::getSender(const std::string& name) const +{ + SenderMap::const_iterator i = senders.find(name); + if (i == senders.end()) { + throw qpid::messaging::KeyError(std::string("No such sender") + name); + } else { + return i->second; + } +} + +boost::shared_ptr<ReceiverContext> SessionContext::getReceiver(const std::string& name) const +{ + ReceiverMap::const_iterator i = receivers.find(name); + if (i == receivers.end()) { + throw qpid::messaging::KeyError(std::string("No such receiver") + name); + } else { + return i->second; + } +} + +void SessionContext::closeReceiver(const std::string&) +{ + +} + +void SessionContext::closeSender(const std::string&) +{ + +} + +boost::shared_ptr<ReceiverContext> SessionContext::nextReceiver(qpid::messaging::Duration /*timeout*/) +{ + return boost::shared_ptr<ReceiverContext>(); +} + +uint32_t SessionContext::getReceivable() +{ + return 0;//TODO +} + +uint32_t SessionContext::getUnsettledAcks() +{ + return 0;//TODO +} + +qpid::framing::SequenceNumber SessionContext::record(pn_delivery_t* delivery) +{ + qpid::framing::SequenceNumber id = next++; + unacked[id] = delivery; + QPID_LOG(debug, "Recorded delivery " << id << " -> " << delivery); + return id; +} + +void SessionContext::acknowledge(DeliveryMap::iterator begin, DeliveryMap::iterator end) +{ + for (DeliveryMap::iterator i = begin; i != end; ++i) { + QPID_LOG(debug, "Setting disposition for delivery " << i->first << " -> " << i->second); + pn_delivery_update(i->second, PN_ACCEPTED); + pn_delivery_settle(i->second);//TODO: different settlement modes? + } + unacked.erase(begin, end); +} + +void SessionContext::acknowledge() +{ + QPID_LOG(debug, "acknowledging all " << unacked.size() << " messages"); + acknowledge(unacked.begin(), unacked.end()); +} + +void SessionContext::acknowledge(const qpid::framing::SequenceNumber& id, bool cumulative) +{ + DeliveryMap::iterator i = unacked.find(id); + if (i != unacked.end()) { + acknowledge(cumulative ? unacked.begin() : i, ++i); + } +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/SessionContext.h b/cpp/src/qpid/messaging/amqp/SessionContext.h new file mode 100644 index 0000000000..fbc8731230 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SessionContext.h @@ -0,0 +1,80 @@ +#ifndef QPID_MESSAGING_AMQP_SESSIONCONTEXT_H +#define QPID_MESSAGING_AMQP_SESSIONCONTEXT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <map> +#include <string> +#include <boost/shared_ptr.hpp> +#include "qpid/sys/IntegerTypes.h" +#include "qpid/framing/SequenceNumber.h" + +struct pn_connection_t; +struct pn_session_t; +struct pn_delivery_t; + +namespace qpid { +namespace messaging { + +class Address; +class Duration; + +namespace amqp { + +class ConnectionContext; +class SenderContext; +class ReceiverContext; +/** + * + */ +class SessionContext +{ + public: + SessionContext(pn_connection_t*); + ~SessionContext(); + boost::shared_ptr<SenderContext> createSender(const qpid::messaging::Address& address); + boost::shared_ptr<ReceiverContext> createReceiver(const qpid::messaging::Address& address); + boost::shared_ptr<SenderContext> getSender(const std::string& name) const; + boost::shared_ptr<ReceiverContext> getReceiver(const std::string& name) const; + void closeReceiver(const std::string&); + void closeSender(const std::string&); + boost::shared_ptr<ReceiverContext> nextReceiver(qpid::messaging::Duration timeout); + uint32_t getReceivable(); + uint32_t getUnsettledAcks(); + private: + friend class ConnectionContext; + typedef std::map<std::string, boost::shared_ptr<SenderContext> > SenderMap; + typedef std::map<std::string, boost::shared_ptr<ReceiverContext> > ReceiverMap; + typedef std::map<qpid::framing::SequenceNumber, pn_delivery_t*> DeliveryMap; + pn_session_t* session; + SenderMap senders; + ReceiverMap receivers; + DeliveryMap unacked; + qpid::framing::SequenceNumber next; + + qpid::framing::SequenceNumber record(pn_delivery_t*); + void acknowledge(); + void acknowledge(const qpid::framing::SequenceNumber& id, bool cummulative); + void acknowledge(DeliveryMap::iterator begin, DeliveryMap::iterator end); +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SESSIONCONTEXT_H*/ diff --git a/cpp/src/qpid/messaging/amqp/SessionHandle.cpp b/cpp/src/qpid/messaging/amqp/SessionHandle.cpp new file mode 100644 index 0000000000..bf79771ca4 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SessionHandle.cpp @@ -0,0 +1,148 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SessionHandle.h" +#include "ConnectionContext.h" +#include "ConnectionHandle.h" +#include "ReceiverContext.h" +#include "ReceiverHandle.h" +#include "SenderContext.h" +#include "SenderHandle.h" +#include "SessionContext.h" +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/Duration.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Session.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +SessionHandle::SessionHandle(boost::shared_ptr<ConnectionContext> c, boost::shared_ptr<SessionContext> s) : connection(c), session(s) {} + +void SessionHandle::commit() +{ + +} + +void SessionHandle::rollback() +{ + +} + +void SessionHandle::acknowledge(bool /*sync*/) +{ + connection->acknowledge(session, 0, false); +} + +void SessionHandle::acknowledge(qpid::messaging::Message& msg, bool cumulative) +{ + //TODO: handle cumulative + connection->acknowledge(session, &msg, cumulative); +} + +void SessionHandle::reject(qpid::messaging::Message&) +{ + +} + +void SessionHandle::release(qpid::messaging::Message&) +{ + +} + +void SessionHandle::close() +{ + connection->endSession(session); +} + +void SessionHandle::sync(bool /*block*/) +{ + +} + +qpid::messaging::Sender SessionHandle::createSender(const qpid::messaging::Address& address) +{ + boost::shared_ptr<SenderContext> sender = session->createSender(address); + connection->attach(session, sender); + return qpid::messaging::Sender(new SenderHandle(connection, session, sender)); +} + +qpid::messaging::Receiver SessionHandle::createReceiver(const qpid::messaging::Address& address) +{ + boost::shared_ptr<ReceiverContext> receiver = session->createReceiver(address); + connection->attach(session, receiver); + return qpid::messaging::Receiver(new ReceiverHandle(connection, session, receiver)); +} + +bool SessionHandle::nextReceiver(Receiver& receiver, Duration timeout) +{ + boost::shared_ptr<ReceiverContext> r = session->nextReceiver(timeout); + if (r) { + //TODO: cache handles in this case to avoid frequent allocation + receiver = qpid::messaging::Receiver(new ReceiverHandle(connection, session, r)); + return true; + } else { + return false; + } +} + +qpid::messaging::Receiver SessionHandle::nextReceiver(Duration timeout) +{ + qpid::messaging::Receiver r; + if (nextReceiver(r, timeout)) return r; + else throw qpid::messaging::NoMessageAvailable(); +} + +uint32_t SessionHandle::getReceivable() +{ + return session->getReceivable(); +} + +uint32_t SessionHandle::getUnsettledAcks() +{ + return session->getUnsettledAcks(); +} + +Sender SessionHandle::getSender(const std::string& name) const +{ + return qpid::messaging::Sender(new SenderHandle(connection, session, session->getSender(name))); +} + +Receiver SessionHandle::getReceiver(const std::string& name) const +{ + return qpid::messaging::Receiver(new ReceiverHandle(connection, session, session->getReceiver(name))); +} + +Connection SessionHandle::getConnection() const +{ + return qpid::messaging::Connection(new ConnectionHandle(connection)); +} + +void SessionHandle::checkError() +{ + +} + + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/SessionHandle.h b/cpp/src/qpid/messaging/amqp/SessionHandle.h new file mode 100644 index 0000000000..5e843aaacc --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SessionHandle.h @@ -0,0 +1,64 @@ +#ifndef QPID_MESSAGING_AMQP_SESSIONIMPL_H +#define QPID_MESSAGING_AMQP_SESSIONIMPL_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include <boost/shared_ptr.hpp> +#include "qpid/messaging/SessionImpl.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +class ConnectionContext; +class SessionContext; +/** + * + */ +class SessionHandle : public qpid::messaging::SessionImpl +{ + public: + SessionHandle(boost::shared_ptr<ConnectionContext>, boost::shared_ptr<SessionContext>); + void commit(); + void rollback(); + void acknowledge(bool sync); + void acknowledge(Message&, bool); + void reject(Message&); + void release(Message&); + void close(); + void sync(bool block); + qpid::messaging::Sender createSender(const Address& address); + qpid::messaging::Receiver createReceiver(const Address& address); + bool nextReceiver(Receiver& receiver, Duration timeout); + qpid::messaging::Receiver nextReceiver(Duration timeout); + uint32_t getReceivable(); + uint32_t getUnsettledAcks(); + qpid::messaging::Sender getSender(const std::string& name) const; + qpid::messaging::Receiver getReceiver(const std::string& name) const; + qpid::messaging::Connection getConnection() const; + void checkError(); + private: + boost::shared_ptr<ConnectionContext> connection; + boost::shared_ptr<SessionContext> session; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SESSIONIMPL_H*/ diff --git a/cpp/src/qpid/messaging/amqp/SslTransport.cpp b/cpp/src/qpid/messaging/amqp/SslTransport.cpp new file mode 100644 index 0000000000..a02b0d7052 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SslTransport.cpp @@ -0,0 +1,158 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SslTransport.h" +#include "TransportContext.h" +#include "qpid/sys/ssl/SslIo.h" +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/Poller.h" +#include "qpid/log/Statement.h" +#include <boost/bind.hpp> +#include <boost/format.hpp> + +using namespace qpid::sys; +using namespace qpid::sys::ssl; + +namespace qpid { +namespace messaging { +namespace amqp { + +// Static constructor which registers connector here +namespace { +Transport* create(TransportContext& c, Poller::shared_ptr p) +{ + return new SslTransport(c, p); +} + +struct StaticInit +{ + StaticInit() + { + Transport::add("ssl", &create); + }; +} init; +} + + +SslTransport::SslTransport(TransportContext& c, boost::shared_ptr<Poller> p) : context(c), aio(0), poller(p) {} + +void SslTransport::connect(const std::string& host, const std::string& port) +{ + assert(!aio); + try { + socket.connect(host, port); + connected(socket); + } catch (const std::exception& e) { + failed(e.what()); + } + +} + +void SslTransport::failed(const std::string& msg) +{ + QPID_LOG(debug, "Failed to connect: " << msg); + socket.close(); + context.closed(); +} + +void SslTransport::connected(const SslSocket&) +{ + context.opened(); + aio = new SslIO(socket, + boost::bind(&SslTransport::read, this, _1, _2), + boost::bind(&SslTransport::eof, this, _1), + boost::bind(&SslTransport::disconnected, this, _1), + boost::bind(&SslTransport::socketClosed, this, _1, _2), + 0, // nobuffs + boost::bind(&SslTransport::write, this, _1)); + aio->createBuffers(std::numeric_limits<uint16_t>::max());//note: AMQP 1.0 _can_ handle large frame sizes + id = boost::str(boost::format("[%1%]") % socket.getFullAddress()); + aio->start(poller); +} + +void SslTransport::read(SslIO&, SslIO::BufferBase* buffer) +{ + int32_t decoded = context.getCodec().decode(buffer->bytes+buffer->dataStart, buffer->dataCount); + if (decoded < buffer->dataCount) { + // Adjust buffer for used bytes and then "unread them" + buffer->dataStart += decoded; + buffer->dataCount -= decoded; + aio->unread(buffer); + } else { + // Give whole buffer back to aio subsystem + aio->queueReadBuffer(buffer); + } +} + +void SslTransport::write(SslIO&) +{ + if (context.getCodec().canEncode()) { + SslIO::BufferBase* buffer = aio->getQueuedBuffer(); + if (buffer) { + size_t encoded = context.getCodec().encode(buffer->bytes, buffer->byteCount); + + buffer->dataStart = 0; + buffer->dataCount = encoded; + aio->queueWrite(buffer); + } + } + +} + +void SslTransport::close() +{ + QPID_LOG(debug, id << " SslTransport closing..."); + if (aio) + aio->queueWriteClose(); +} + +void SslTransport::eof(SslIO&) +{ + close(); +} + +void SslTransport::disconnected(SslIO&) +{ + close(); + socketClosed(*aio, socket); +} + +void SslTransport::socketClosed(SslIO&, const SslSocket&) +{ + if (aio) + aio->queueForDeletion(); + context.closed(); + QPID_LOG(debug, id << " Socket closed"); +} + +void SslTransport::abort() +{ + if (aio) { + // Established connection + aio->requestCallback(boost::bind(&SslTransport::eof, this, _1)); + } +} + +void SslTransport::activateOutput() +{ + if (aio) aio->notifyPendingWrite(); +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/SslTransport.h b/cpp/src/qpid/messaging/amqp/SslTransport.h new file mode 100644 index 0000000000..e83c3346e6 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/SslTransport.h @@ -0,0 +1,74 @@ +#ifndef QPID_MESSAGING_AMQP_SSLTRANSPORT_H +#define QPID_MESSAGING_AMQP_SSLTRANSPORT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/amqp/Transport.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/ssl/SslSocket.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { +class ConnectionCodec; +class Poller; +namespace ssl { +class SslIO; +class SslIOBufferBase; +} +} + +namespace messaging { +namespace amqp { +class TransportContext; + +class SslTransport : public Transport +{ + public: + SslTransport(TransportContext&, boost::shared_ptr<qpid::sys::Poller> p); + + void connect(const std::string& host, const std::string& port); + + void activateOutput(); + void abort(); + void close(); + void giveReadCredit(int32_t) {} + private: + qpid::sys::ssl::SslSocket socket; + TransportContext& context; + qpid::sys::ssl::SslIO* aio; + boost::shared_ptr<qpid::sys::Poller> poller; + bool closed; + std::string id; + + void connected(const qpid::sys::ssl::SslSocket&); + void failed(const std::string& msg); + void read(qpid::sys::ssl::SslIO&, qpid::sys::ssl::SslIOBufferBase*); + void write(qpid::sys::ssl::SslIO&); + void eof(qpid::sys::ssl::SslIO&); + void disconnected(qpid::sys::ssl::SslIO&); + void socketClosed(qpid::sys::ssl::SslIO&, const qpid::sys::ssl::SslSocket&); + + friend class DriverImpl; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SSLTRANSPORT_H*/ diff --git a/cpp/src/qpid/messaging/amqp/TcpTransport.cpp b/cpp/src/qpid/messaging/amqp/TcpTransport.cpp new file mode 100644 index 0000000000..5105c9f384 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/TcpTransport.cpp @@ -0,0 +1,162 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "TcpTransport.h" +#include "ConnectionContext.h" +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/Poller.h" +#include "qpid/log/Statement.h" +#include <boost/bind.hpp> +#include <boost/format.hpp> + +using namespace qpid::sys; + +namespace qpid { +namespace messaging { +namespace amqp { +// Static constructor which registers connector here +namespace { +Transport* create(TransportContext& c, Poller::shared_ptr p) +{ + return new TcpTransport(c, p); +} + +struct StaticInit +{ + StaticInit() + { + Transport::add("tcp", &create); + }; +} init; +} + +TcpTransport::TcpTransport(TransportContext& c, boost::shared_ptr<Poller> p) : context(c), connector(0), aio(0), poller(p) {} + +void TcpTransport::connect(const std::string& host, const std::string& port) +{ + assert(!connector); + assert(!aio); + connector = AsynchConnector::create( + socket, + host, port, + boost::bind(&TcpTransport::connected, this, _1), + boost::bind(&TcpTransport::failed, this, _3)); + + connector->start(poller); +} + +void TcpTransport::failed(const std::string& msg) +{ + QPID_LOG(debug, "Failed to connect: " << msg); + connector = 0; + socket.close(); + context.closed(); +} + +void TcpTransport::connected(const Socket&) +{ + context.opened(); + connector = 0; + aio = AsynchIO::create(socket, + boost::bind(&TcpTransport::read, this, _1, _2), + boost::bind(&TcpTransport::eof, this, _1), + boost::bind(&TcpTransport::disconnected, this, _1), + boost::bind(&TcpTransport::socketClosed, this, _1, _2), + 0, // nobuffs + boost::bind(&TcpTransport::write, this, _1)); + aio->createBuffers(std::numeric_limits<uint16_t>::max());//note: AMQP 1.0 _can_ handle large frame sizes + id = boost::str(boost::format("[%1%]") % socket.getFullAddress()); + aio->start(poller); +} + +void TcpTransport::read(AsynchIO&, AsynchIO::BufferBase* buffer) +{ + int32_t decoded = context.getCodec().decode(buffer->bytes+buffer->dataStart, buffer->dataCount); + if (decoded < buffer->dataCount) { + // Adjust buffer for used bytes and then "unread them" + buffer->dataStart += decoded; + buffer->dataCount -= decoded; + aio->unread(buffer); + } else { + // Give whole buffer back to aio subsystem + aio->queueReadBuffer(buffer); + } +} + +void TcpTransport::write(AsynchIO&) +{ + if (context.getCodec().canEncode()) { + AsynchIO::BufferBase* buffer = aio->getQueuedBuffer(); + if (buffer) { + size_t encoded = context.getCodec().encode(buffer->bytes, buffer->byteCount); + + buffer->dataStart = 0; + buffer->dataCount = encoded; + aio->queueWrite(buffer); + } + } + +} + +void TcpTransport::close() +{ + QPID_LOG(debug, id << " TcpTransport closing..."); + if (aio) + aio->queueWriteClose(); +} + +void TcpTransport::eof(AsynchIO&) +{ + close(); +} + +void TcpTransport::disconnected(AsynchIO&) +{ + close(); + socketClosed(*aio, socket); +} + +void TcpTransport::socketClosed(AsynchIO&, const Socket&) +{ + if (aio) + aio->queueForDeletion(); + context.closed(); + QPID_LOG(debug, id << " Socket closed"); +} + +void TcpTransport::abort() +{ + if (aio) { + // Established connection + aio->requestCallback(boost::bind(&TcpTransport::eof, this, _1)); + } else if (connector) { + // We're still connecting + connector->stop(); + failed("Connection timedout"); + } +} + +void TcpTransport::activateOutput() +{ + if (aio) aio->notifyPendingWrite(); +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/TcpTransport.h b/cpp/src/qpid/messaging/amqp/TcpTransport.h new file mode 100644 index 0000000000..142b36ba8c --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/TcpTransport.h @@ -0,0 +1,71 @@ +#ifndef QPID_MESSAGING_AMQP_TCPTRANSPORT_H +#define QPID_MESSAGING_AMQP_TCPTRANSPORT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/amqp/Transport.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Socket.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { +class ConnectionCodec; +class AsynchConnector; +class AsynchIO; +class AsynchIOBufferBase; +class Poller; +} +namespace messaging { +namespace amqp { +class TransportContext; + +class TcpTransport : public Transport +{ + public: + TcpTransport(TransportContext&, boost::shared_ptr<qpid::sys::Poller>); + + void connect(const std::string& host, const std::string& port); + + void activateOutput(); + void abort(); + void close(); + void giveReadCredit(int32_t) {} + + private: + qpid::sys::Socket socket; + TransportContext& context; + qpid::sys::AsynchConnector* connector; + qpid::sys::AsynchIO* aio; + boost::shared_ptr<qpid::sys::Poller> poller; + std::string id; + + void connected(const qpid::sys::Socket&); + void failed(const std::string& msg); + void read(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*); + void write(qpid::sys::AsynchIO&); + void eof(qpid::sys::AsynchIO&); + void disconnected(qpid::sys::AsynchIO&); + void socketClosed(qpid::sys::AsynchIO&, const qpid::sys::Socket&); +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_TCPTRANSPORT_H*/ diff --git a/cpp/src/qpid/messaging/amqp/Transport.cpp b/cpp/src/qpid/messaging/amqp/Transport.cpp new file mode 100644 index 0000000000..4c2d212689 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/Transport.cpp @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/messaging/amqp/Transport.h" +#include "qpid/messaging/amqp/TransportContext.h" +#include <map> + +namespace qpid { +namespace messaging { +namespace amqp { +namespace { +typedef std::map<std::string, Transport::Factory*> Registry; + +Registry& theRegistry() +{ + static Registry factories; + return factories; +} +} + +Transport* Transport::create(const std::string& name, TransportContext& context, boost::shared_ptr<qpid::sys::Poller> poller) +{ + Registry::const_iterator i = theRegistry().find(name); + if (i != theRegistry().end()) return (i->second)(context, poller); + else return 0; +} +void Transport::add(const std::string& name, Factory* factory) +{ + theRegistry()[name] = factory; +} + +}}} // namespace qpid::messaging::amqp diff --git a/cpp/src/qpid/messaging/amqp/Transport.h b/cpp/src/qpid/messaging/amqp/Transport.h new file mode 100644 index 0000000000..ee021f645b --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/Transport.h @@ -0,0 +1,48 @@ +#ifndef QPID_MESSAGING_AMQP_TRANSPORT_H +#define QPID_MESSAGING_AMQP_TRANSPORT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/sys/OutputControl.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { +class Poller; +} +namespace messaging { +namespace amqp { +class TransportContext; + +class Transport : public qpid::sys::OutputControl +{ + public: + virtual ~Transport() {} + virtual void connect(const std::string& host, const std::string& port) = 0; + virtual void close() = 0; + + typedef Transport* Factory(TransportContext&, boost::shared_ptr<qpid::sys::Poller>); + static Transport* create(const std::string& name, TransportContext&, boost::shared_ptr<qpid::sys::Poller>); + static void add(const std::string& name, Factory* factory); +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_TRANSPORT_H*/ diff --git a/cpp/src/qpid/messaging/amqp/TransportContext.h b/cpp/src/qpid/messaging/amqp/TransportContext.h new file mode 100644 index 0000000000..57192b5976 --- /dev/null +++ b/cpp/src/qpid/messaging/amqp/TransportContext.h @@ -0,0 +1,47 @@ +#ifndef QPID_MESSAGING_AMQP_TRANSPORTCONTEXT_H +#define QPID_MESSAGING_AMQP_TRANSPORTCONTEXT_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +namespace qpid { +namespace sys { +class Codec; +} +namespace messaging { +namespace amqp { + +/** + * Interface to be supplied by 'users' of Transport interface, in + * order to provide codec and handle callbaskc for opening and closing + * of connection. + */ +class TransportContext +{ + public: + virtual ~TransportContext() {} + virtual qpid::sys::Codec& getCodec() = 0; + virtual void closed() = 0; + virtual void opened() = 0; + private: +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_TRANSPORTCONTEXT_H*/ |