summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGordon Sim <gsim@apache.org>2012-10-19 17:16:07 +0000
committerGordon Sim <gsim@apache.org>2012-10-19 17:16:07 +0000
commitd9cf555efdc694433837187f8b57d7dfa8236ae1 (patch)
tree4e8e5a066db948b2f123256bcc8bb5d933f31d9a
parente6ec89af34dca9959403b6a2a3ccf9154c77d12e (diff)
downloadqpid-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
-rw-r--r--cpp/configure.ac9
-rw-r--r--cpp/src/CMakeLists.txt31
-rw-r--r--cpp/src/Makefile.am97
-rw-r--r--cpp/src/qpid/amqp/CharSequence.cpp47
-rw-r--r--cpp/src/qpid/amqp/CharSequence.h48
-rw-r--r--cpp/src/qpid/amqp/Codec.h83
-rw-r--r--cpp/src/qpid/amqp/Constructor.h42
-rw-r--r--cpp/src/qpid/amqp/Decoder.cpp544
-rw-r--r--cpp/src/qpid/amqp/Decoder.h97
-rw-r--r--cpp/src/qpid/amqp/Descriptor.cpp52
-rw-r--r--cpp/src/qpid/amqp/Descriptor.h54
-rw-r--r--cpp/src/qpid/amqp/Encoder.cpp402
-rw-r--r--cpp/src/qpid/amqp/Encoder.h149
-rw-r--r--cpp/src/qpid/amqp/ListReader.h103
-rw-r--r--cpp/src/qpid/amqp/LoggingReader.h64
-rw-r--r--cpp/src/qpid/amqp/MessageEncoder.cpp313
-rw-r--r--cpp/src/qpid/amqp/MessageEncoder.h100
-rw-r--r--cpp/src/qpid/amqp/MessageId.cpp68
-rw-r--r--cpp/src/qpid/amqp/MessageId.h53
-rw-r--r--cpp/src/qpid/amqp/MessageReader.cpp759
-rw-r--r--cpp/src/qpid/amqp/MessageReader.h300
-rw-r--r--cpp/src/qpid/amqp/Reader.h80
-rw-r--r--cpp/src/qpid/amqp/Sasl.cpp130
-rw-r--r--cpp/src/qpid/amqp/Sasl.h54
-rw-r--r--cpp/src/qpid/amqp/SaslClient.cpp154
-rw-r--r--cpp/src/qpid/amqp/SaslClient.h54
-rw-r--r--cpp/src/qpid/amqp/SaslServer.cpp183
-rw-r--r--cpp/src/qpid/amqp/SaslServer.h50
-rw-r--r--cpp/src/qpid/amqp/descriptors.h81
-rw-r--r--cpp/src/qpid/amqp/typecodes.h115
-rw-r--r--cpp/src/qpid/broker/Message.cpp12
-rw-r--r--cpp/src/qpid/broker/Message.h4
-rw-r--r--cpp/src/qpid/broker/amqp/Connection.cpp246
-rw-r--r--cpp/src/qpid/broker/amqp/Connection.h73
-rw-r--r--cpp/src/qpid/broker/amqp/Header.cpp65
-rw-r--r--cpp/src/qpid/broker/amqp/Header.h50
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedConnection.cpp98
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedConnection.h59
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp70
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h53
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedSession.cpp88
-rw-r--r--cpp/src/qpid/broker/amqp/ManagedSession.h59
-rw-r--r--cpp/src/qpid/broker/amqp/Message.cpp259
-rw-r--r--cpp/src/qpid/broker/amqp/Message.h147
-rw-r--r--cpp/src/qpid/broker/amqp/Outgoing.cpp191
-rw-r--r--cpp/src/qpid/broker/amqp/Outgoing.h105
-rw-r--r--cpp/src/qpid/broker/amqp/ProtocolPlugin.cpp117
-rw-r--r--cpp/src/qpid/broker/amqp/Sasl.cpp167
-rw-r--r--cpp/src/qpid/broker/amqp/Sasl.h72
-rw-r--r--cpp/src/qpid/broker/amqp/Session.cpp293
-rw-r--r--cpp/src/qpid/broker/amqp/Session.h77
-rw-r--r--cpp/src/qpid/broker/amqp/Translation.cpp238
-rw-r--r--cpp/src/qpid/broker/amqp/Translation.h58
-rw-r--r--cpp/src/qpid/messaging/ConnectionOptions.cpp121
-rw-r--r--cpp/src/qpid/messaging/ConnectionOptions.h51
-rw-r--r--cpp/src/qpid/messaging/Message.cpp28
-rw-r--r--cpp/src/qpid/messaging/MessageImpl.cpp161
-rw-r--r--cpp/src/qpid/messaging/MessageImpl.h53
-rw-r--r--cpp/src/qpid/messaging/ReceiverImpl.h2
-rw-r--r--cpp/src/qpid/messaging/SenderImpl.h1
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionContext.cpp557
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionContext.h139
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp84
-rw-r--r--cpp/src/qpid/messaging/amqp/ConnectionHandle.h58
-rw-r--r--cpp/src/qpid/messaging/amqp/DriverImpl.cpp74
-rw-r--r--cpp/src/qpid/messaging/amqp/DriverImpl.h60
-rw-r--r--cpp/src/qpid/messaging/amqp/EncodedMessage.cpp263
-rw-r--r--cpp/src/qpid/messaging/amqp/EncodedMessage.h177
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverContext.cpp97
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverContext.h63
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp106
-rw-r--r--cpp/src/qpid/messaging/amqp/ReceiverHandle.h63
-rw-r--r--cpp/src/qpid/messaging/amqp/Sasl.cpp162
-rw-r--r--cpp/src/qpid/messaging/amqp/Sasl.h72
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderContext.cpp332
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderContext.h84
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderHandle.cpp75
-rw-r--r--cpp/src/qpid/messaging/amqp/SenderHandle.h58
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionContext.cpp147
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionContext.h80
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionHandle.cpp148
-rw-r--r--cpp/src/qpid/messaging/amqp/SessionHandle.h64
-rw-r--r--cpp/src/qpid/messaging/amqp/SslTransport.cpp158
-rw-r--r--cpp/src/qpid/messaging/amqp/SslTransport.h74
-rw-r--r--cpp/src/qpid/messaging/amqp/TcpTransport.cpp162
-rw-r--r--cpp/src/qpid/messaging/amqp/TcpTransport.h71
-rw-r--r--cpp/src/qpid/messaging/amqp/Transport.cpp49
-rw-r--r--cpp/src/qpid/messaging/amqp/Transport.h48
-rw-r--r--cpp/src/qpid/messaging/amqp/TransportContext.h47
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(&copy->data[position], deliveryAnnotations.data, deliveryAnnotations.size);
+ position += deliveryAnnotations.size;
+ }
+ if (messageAnnotations) {
+ //TODO: actual merge required
+ ::memcpy(&copy->data[position], messageAnnotations.data, messageAnnotations.size);
+ position += messageAnnotations.size;
+ } else {
+ qpid::amqp::MessageEncoder encoder(&copy->data[position], extra);
+ encoder.writeMap(annotations, &qpid::amqp::message::MESSAGE_ANNOTATIONS, true);
+ position += extra;
+ }
+ if (bareMessage) {
+ ::memcpy(&copy->data[position], bareMessage.data, bareMessage.size);
+ position += bareMessage.size;
+ }
+ if (footer) {
+ ::memcpy(&copy->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*/