Return-Path: X-Original-To: apmail-qpid-users-archive@www.apache.org Delivered-To: apmail-qpid-users-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 60BC4D031 for ; Wed, 10 Oct 2012 21:00:12 +0000 (UTC) Received: (qmail 43994 invoked by uid 500); 10 Oct 2012 21:00:12 -0000 Delivered-To: apmail-qpid-users-archive@qpid.apache.org Received: (qmail 43942 invoked by uid 500); 10 Oct 2012 21:00:11 -0000 Mailing-List: contact users-help@qpid.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: users@qpid.apache.org Delivered-To: mailing list users@qpid.apache.org Received: (qmail 43930 invoked by uid 99); 10 Oct 2012 21:00:11 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Oct 2012 21:00:11 +0000 X-ASF-Spam-Status: No, hits=-4.0 required=5.0 tests=FRT_ADOBE2,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,SPF_PASS,T_FILL_THIS_FORM_SHORT X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of gsim@redhat.com designates 209.132.183.28 as permitted sender) Received: from [209.132.183.28] (HELO mx1.redhat.com) (209.132.183.28) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Oct 2012 20:59:28 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q9AKx4L4001979 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 10 Oct 2012 16:59:04 -0400 Received: from [10.36.116.49] (ovpn-116-49.ams2.redhat.com [10.36.116.49]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q9AKwsff002691 for ; Wed, 10 Oct 2012 16:58:56 -0400 Message-ID: <5075E241.70606@redhat.com> Date: Wed, 10 Oct 2012 22:01:53 +0100 From: Gordon Sim Organization: Red Hat UK Ltd, Registered in England and Wales under Company Registration No. 3798903, Directors: Michael Cunningham (USA), Mark Hegarty (Ireland), Matt Parsons (USA), Charlie Peters (USA) User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20120717 Thunderbird/14.0 MIME-Version: 1.0 To: users@qpid.apache.org Subject: Re: [c++] AMQP 1.0 support: 0.20 milestones References: <505998D8.7060707@redhat.com> <506CAABF.4050109@redhat.com> In-Reply-To: <506CAABF.4050109@redhat.com> Content-Type: multipart/mixed; boundary="------------020300090309090500060705" X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 --------------020300090309090500060705 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit On 10/03/2012 10:14 PM, Gordon Sim wrote: > -------------------------------------------------------------------- > Oct 3rd: > > * cyrus sasl integration > * SSL support for client > > Oct 10th: > > * make AMQP 1.0 (broker) connection, sessions and links visible through > management The attached patch makes the connection and session visible through management, as well as outgoing links through a subscription. The schema does not have anything (as yet) to cover incoming links. The only change I've made is to add a protocol description string to the connection properties. Some of the data that is set for 0-10 connections cannot be set yet for 1.0 connections as the proton engine does not expose sufficient detail from the underlying protocol (see PROTON-61). The connection statistics for 0-10 include the number of bytes and frames received and sent on a connection. That level of detail is I think quite rightly masked behind the engine. I have hooked in the message counts but will ignore the frame counts for 1.0 (unless anyone has a compelling reason for figuring out a way to record them). I propose to reinterpret the bytes in and out as including only those bytes comprising the actual messages (again, unless there are objections to that approach). The current management schema identifies a session by a name, though commonly that is in fact a stringified UUID. There wasn't an ideal property from a 1.0 session to use here (the channel is not exposed by the engine), so I just use the address of the proton session object for now. > * durable messaging; broker acknowledges only after async store > operation completes Also included in the patch is persistence support for the 1.0 message format and hooking in of asynchronous enqueue completion to the update of the disposition of the transfer. > Oct 17th (0.20 Alpha): > > * support for address options in qpid::messaging (depends on PROTON-9) > * support for 'legacy filters' (i.e. pre 1.0 exchange matching) (also > depends on PROTON-9) > * structured content messages I think I will slightly change tack again for the next weeks work and while I wait for PROTON-9 I will focus instead in splitting out some of this work into patches to be committed to the trunk. Specifically I'll try and separate all the changes to the existing codepaths from the new additions, since the former are the more risky. I still plan to include this work in 0.20. As mentioned previously, both on broker and client, the 1.0 support is built directly into the broker and messaging lib respectively. On the broker I believe it is vital to be able to turn 1.0 off. That reduces risk/impact of bugs in the new codepaths. Given the likely lack of ACL support it also prevents 1.0 becoming a security hole. One way of achieving this optionality would be to make it a separate loadable module but it could also just be a configuration option. I'll be revisiting that also. Thoughts and opinions on all of this are welcome as always. --------------020300090309090500060705 Content-Type: text/x-patch; name="amqp-1.0-milestone3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="amqp-1.0-milestone3.patch" diff --combined qpid/cpp/configure.ac index 5ddd875,c4e55f9..0000000 --- a/qpid/cpp/configure.ac +++ b/qpid/cpp/configure.ac @@@ -296,23 -296,6 +296,23 @@@ 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], + [Base directory for proton project])]) + +PROTON_BASE=$with_proton +PROTON_INCLUDE=$PROTON_BASE/include +PROTON_LIB=$PROTON_BASE/lib +fail=0 +test -f $PROTON_INCLUDE/proton/engine.h || fail=1 +test $fail = 1 && + AC_MSG_ERROR([Please specify path to proton!]) +CXXFLAGS="$CXXFLAGS -I$PROTON_INCLUDE" +LDFLAGS="$LDFLAGS -L$PROTON_LIB -lqpid-proton" +AC_SUBST([PROTON_LIB]) + + # Setup --with-xml/--without-xml as arguments to configure use_xml=yes want_xml=check diff --combined qpid/cpp/include/qpid/framing/ProtocolVersion.h index 309e543,26d628e..0000000 --- a/qpid/cpp/include/qpid/framing/ProtocolVersion.h +++ b/qpid/cpp/include/qpid/framing/ProtocolVersion.h @@@ -36,28 -36,21 +36,28 @@@ class QPID_COMMON_CLASS_EXTERN Protocol private: uint8_t major_; uint8_t minor_; + uint8_t protocol_; public: - explicit ProtocolVersion(uint8_t _major=0, uint8_t _minor=0) - : major_(_major), minor_(_minor) {} + explicit ProtocolVersion(uint8_t _major=0, uint8_t _minor=0, uint8_t _protocol=0) + : major_(_major), minor_(_minor), protocol_(_protocol) {} QPID_COMMON_INLINE_EXTERN uint8_t getMajor() const { return major_; } QPID_COMMON_INLINE_EXTERN void setMajor(uint8_t major) { major_ = major; } QPID_COMMON_INLINE_EXTERN uint8_t getMinor() const { return minor_; } QPID_COMMON_INLINE_EXTERN void setMinor(uint8_t minor) { minor_ = minor; } + QPID_COMMON_INLINE_EXTERN uint8_t getProtocol() const { return protocol_; } + QPID_COMMON_INLINE_EXTERN void setProtocol(uint8_t protocol) { protocol_ = protocol; } QPID_COMMON_EXTERN const std::string toString() const; QPID_COMMON_EXTERN ProtocolVersion& operator=(ProtocolVersion p); QPID_COMMON_EXTERN bool operator==(ProtocolVersion p) const; QPID_COMMON_INLINE_EXTERN bool operator!=(ProtocolVersion p) const { return ! (*this == p); } + QPID_COMMON_EXTERN static uint8_t AMQP; + QPID_COMMON_EXTERN static uint8_t LEGACY_AMQP; + QPID_COMMON_EXTERN static uint8_t TLS; + QPID_COMMON_EXTERN static uint8_t SASL; }; } // namespace framing diff --combined qpid/cpp/src/CMakeLists.txt index c6f5be5,60dbd28..0000000 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@@ -1030,52 -1030,9 +1030,52 @@@ install (DIRECTORY ../include/qpi PATTERN ".svn" EXCLUDE) install_pdb (qpidclient ${QPID_COMPONENT_CLIENT}) +find_path(PROTON_INCLUDE proton/engine.h) +find_library(PROTON_LIB qpid-proton) + +set (proton_default ${proton_force}) +if (PROTON_LIB AND PROTON_INCLUDE) + set (proton_default ON) +endif (PROTON_LIB AND PROTON_INCLUDE) + +option(USE_PROTON "Build with Proton support for AMQP 1.0" ${proton_default}) +if (USE_PROTON) + if (PROTON_INCLUDE-NOTFOUND) + message(FATAL_ERROR "Proton support requested but proton/engine.h not found") + endif (PROTON_INCLUDE-NOTFOUND) + + if (PROTON_LIB-NOTFOUND) + message(FATAL_ERROR "Proton support requested but libqpid-proton not found") + endif (PROTON_LIB-NOTFOUND) + + include_directories(PROTON_INCLUDE) + link_directories(PROTON_LIB) + set(qpidmessaging_proton_source + qpid/messaging/amqp/ConnectionContext.h + qpid/messaging/amqp/ConnectionContext.cpp + qpid/messaging/amqp/ConnectionHandle.h + qpid/messaging/amqp/ConnectionHandle.cpp + qpid/messaging/amqp/Decoder.h + qpid/messaging/amqp/Decoder.cpp + qpid/messaging/amqp/ReceiverContext.h + qpid/messaging/amqp/ReceiverContext.cpp + qpid/messaging/amqp/ReceiverHandle.h + qpid/messaging/amqp/ReceiverHandle.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 + ) + set(qpidmessaging_proton_lib qpidproton) +endif (USE_PROTON) set (qpidmessaging_SOURCES - ${qpidmessaging_platform_SOURCES} + ${qpidmessaging_platform_SOURCES} + ${qpidmessaging_proton_source} qpid/messaging/Address.cpp qpid/messaging/AddressParser.h qpid/messaging/AddressParser.cpp diff --combined qpid/cpp/src/Makefile.am index b07b0d5,45c0d0f..0000000 --- a/qpid/cpp/src/Makefile.am +++ b/qpid/cpp/src/Makefile.am @@@ -359,9 -359,6 +359,9 @@@ libqpidcommon_la_SOURCES += qpid/Sasl.h \ qpid/SaslFactory.cpp \ qpid/SaslFactory.h \ + qpid/SaslServer.h \ + qpid/NullSaslServer.h \ + qpid/NullSaslServer.cpp \ qpid/Serializer.h \ qpid/SessionId.cpp \ qpid/SessionState.cpp \ @@@ -512,38 -509,7 +512,38 @@@ 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 @@@ -737,26 -703,6 +737,26 @@@ libqpidbroker_la_SOURCES = qpid/broker/MessageGroupManager.h \ qpid/broker/amqp_0_10/MessageTransfer.h \ qpid/broker/amqp_0_10/MessageTransfer.cpp \ + 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/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 \ qpid/management/ManagementAgent.cpp \ qpid/management/ManagementAgent.h \ qpid/management/ManagementDirectExchange.cpp \ @@@ -844,8 -790,6 +844,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 \ @@@ -860,30 -804,6 +860,30 @@@ qpid/messaging/ReceiverImpl.h \ qpid/messaging/SessionImpl.h \ qpid/messaging/FailoverUpdates.cpp \ + 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/EncodedMessage.h \ + qpid/messaging/amqp/EncodedMessage.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 \ qpid/client/amqp0_10/AcceptTracker.h \ qpid/client/amqp0_10/AcceptTracker.cpp \ qpid/client/amqp0_10/AddressResolution.h \ diff --combined qpid/cpp/src/qpid/Sasl.h index efda92d,4d579fa..0000000 --- a/qpid/cpp/src/qpid/Sasl.h +++ b/qpid/cpp/src/qpid/Sasl.h @@@ -34,7 -34,7 +34,7 @@@ struct SecuritySettings } /** - * Interface to SASL support. This class is implemented by platform-specific + * Interface to support for the SASL client role. This class is implemented by platform-specific * SASL providers. */ class Sasl diff --combined qpid/cpp/src/qpid/SaslFactory.cpp index 4d81a79,a8d1f94..0000000 --- a/qpid/cpp/src/qpid/SaslFactory.cpp +++ b/qpid/cpp/src/qpid/SaslFactory.cpp @@@ -7,9 -7,9 +7,9 @@@ * 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 @@@ -18,9 -18,7 +18,9 @@@ * under the License. * */ -#include "qpid//SaslFactory.h" +#include "qpid/SaslFactory.h" +#include "qpid/SaslServer.h" +#include "qpid/NullSaslServer.h" #include #include @@@ -34,7 -32,6 +34,7 @@@ namespace qpid //Null implementation + SaslFactory::SaslFactory() {} SaslFactory::~SaslFactory() {} @@@ -53,12 -50,6 +53,12 @@@ std::auto_ptr SaslFactory::create return std::auto_ptr(); } +std::auto_ptr SaslFactory::createServer(const std::string& realm, bool /*encryptionRequired*/, const qpid::sys::SecuritySettings&) +{ + std::auto_ptr server(new NullSaslServer(realm)); + return server; +} + qpid::sys::Mutex SaslFactory::lock; std::auto_ptr SaslFactory::instance; @@@ -149,22 -140,6 +149,22 @@@ typedef int CallbackProc() qpid::sys::Mutex SaslFactory::lock; std::auto_ptr SaslFactory::instance; +class CyrusSaslServer : public SaslServer +{ + public: + CyrusSaslServer(const std::string& realm, bool encryptionRequired, const qpid::sys::SecuritySettings& external); + ~CyrusSaslServer(); + Status start(const std::string& mechanism, const std::string* response, std::string& challenge); + Status step(const std::string* response, std::string& challenge); + std::string getMechanisms(); + std::string getUserid(); + std::auto_ptr getSecurityLayer(size_t); + private: + std::string realm; + std::string userid; + sasl_conn_t *sasl_conn; +}; + SaslFactory::SaslFactory() { sasl_callback_t* callbacks = 0; @@@ -194,12 -169,6 +194,12 @@@ std::auto_ptr SaslFactory::create return sasl; } +std::auto_ptr SaslFactory::createServer(const std::string& realm, bool encryptionRequired, const qpid::sys::SecuritySettings& external) +{ + std::auto_ptr server(new CyrusSaslServer(realm, encryptionRequired, external)); + return server; +} + CyrusSasl::CyrusSasl(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction) : conn(0), settings(username, password, serviceName, hostName, minSsf, maxSsf), allowInteraction(allowInteraction) { @@@ -408,180 -377,7 +408,180 @@@ std::auto_ptr CyrusSasl: std::auto_ptr securityLayer; if (ssf) { QPID_LOG(info, "Installing security layer, SSF: "<< ssf); - securityLayer = std::auto_ptr(new CyrusSecurityLayer(conn, maxFrameSize)); + securityLayer = std::auto_ptr(new CyrusSecurityLayer(conn, maxFrameSize, ssf)); + } + return securityLayer; +} + +CyrusSaslServer::CyrusSaslServer(const std::string& r, bool encryptionRequired, const qpid::sys::SecuritySettings& external) : realm(r), sasl_conn(0) +{ + int code = sasl_server_new(BROKER_SASL_NAME, /* Service name */ + NULL, /* Server FQDN, gethostname() */ + realm.c_str(), /* Authentication realm */ + NULL, /* Local IP, needed for some mechanism */ + NULL, /* Remote IP, needed for some mechanism */ + NULL, /* Callbacks */ + 0, /* Connection flags */ + &sasl_conn); + + if (SASL_OK != code) { + QPID_LOG(error, "SASL: Connection creation failed: [" << code << "] " << sasl_errdetail(sasl_conn)); + + // TODO: Change this to an exception signaling + // server error, when one is available + throw qpid::framing::ConnectionForcedException("Unable to perform authentication"); + } + + sasl_security_properties_t secprops; + + //TODO: should the actual SSF values be configurable here? + secprops.min_ssf = encryptionRequired ? 10: 0; + secprops.max_ssf = 256; + + // If the transport provides encryption, notify the SASL library of + // the key length and set the ssf range to prevent double encryption. + QPID_LOG(debug, "External ssf=" << external.ssf << " and auth=" << external.authid); + sasl_ssf_t external_ssf = (sasl_ssf_t) external.ssf; + if (external_ssf) { + int result = sasl_setprop(sasl_conn, SASL_SSF_EXTERNAL, &external_ssf); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external SSF: " << result)); + } + + secprops.max_ssf = secprops.min_ssf = 0; + } + + QPID_LOG(debug, "min_ssf: " << secprops.min_ssf << + ", max_ssf: " << secprops.max_ssf << + ", external_ssf: " << external_ssf ); + + if (!external.authid.empty()) { + const char* external_authid = external.authid.c_str(); + int result = sasl_setprop(sasl_conn, SASL_AUTH_EXTERNAL, external_authid); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: unable to set external auth: " << result)); + } + + QPID_LOG(debug, "external auth detected and set to " << external_authid); + } + secprops.maxbufsize = 65535; + secprops.property_names = 0; + secprops.property_values = 0; + secprops.security_flags = 0; /* or SASL_SEC_NOANONYMOUS etc as appropriate */ + /* + * The nodict flag restricts SASL authentication mechanisms + * to those that are not susceptible to dictionary attacks. + * They are: + * SRP + * PASSDSS-3DES-1 + * EXTERNAL + */ + if (external.nodict) secprops.security_flags |= SASL_SEC_NODICTIONARY; + int result = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: " << result)); + } +} + +CyrusSaslServer::~CyrusSaslServer() +{ + if (sasl_conn) { + sasl_dispose(&sasl_conn); + sasl_conn = 0; + } +} + +CyrusSaslServer::Status CyrusSaslServer::start(const std::string& mechanism, const std::string* response, std::string& chllng) +{ + const char *challenge; + unsigned int challenge_len; + + // This should be at same debug level as mech list in getMechanisms(). + QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism); + int code = sasl_server_start(sasl_conn, + mechanism.c_str(), + (response ? response->c_str() : 0), (response ? response->size() : 0), + &challenge, &challenge_len); + switch (code) { + case SASL_OK: + return SaslServer::OK; + case SASL_CONTINUE: + chllng = std::string(challenge, challenge_len); + return SaslServer::CHALLENGE; + case SASL_NOMECH: + QPID_LOG(info, "Unsupported mechanism: " << mechanism); + default: + return SaslServer::FAIL; + } +} + +CyrusSaslServer::Status CyrusSaslServer::step(const std::string* response, std::string& chllng) +{ + const char *challenge; + unsigned int challenge_len; + + int code = sasl_server_step(sasl_conn, + (response ? response->c_str() : 0), (response ? response->size() : 0), + &challenge, &challenge_len); + + switch (code) { + case SASL_OK: + return SaslServer::OK; + case SASL_CONTINUE: + chllng = std::string(challenge, challenge_len); + return SaslServer::CHALLENGE; + default: + return SaslServer::FAIL; + } + +} +std::string CyrusSaslServer::getMechanisms() +{ + const char *separator = " "; + const char *list; + unsigned int list_len; + int count; + + int code = sasl_listmech(sasl_conn, NULL, + "", separator, "", + &list, &list_len, + &count); + + if (SASL_OK != code) { + QPID_LOG(info, "SASL: Mechanism listing failed: " << sasl_errdetail(sasl_conn)); + + // TODO: Change this to an exception signaling + // server error, when one is available + throw qpid::framing::ConnectionForcedException("Mechanism listing failed"); + } else { + std::string mechanisms(list, list_len); + QPID_LOG(info, "SASL: Mechanism list: " << mechanisms); + return mechanisms; + } +} +std::string CyrusSaslServer::getUserid() +{ + const void* ptr; + int code = sasl_getprop(sasl_conn, SASL_USERNAME, &ptr); + if (SASL_OK == code) { + userid = static_cast(ptr); + } else { + QPID_LOG(warning, "Failed to retrieve sasl username"); + } + return userid; +} + +std::auto_ptr CyrusSaslServer::getSecurityLayer(size_t maxFrameSize) +{ + const void* value(0); + int result = sasl_getprop(sasl_conn, SASL_SSF, &value); + if (result != SASL_OK) { + throw framing::InternalErrorException(QPID_MSG("SASL error: " << sasl_errdetail(sasl_conn))); + } + uint ssf = *(reinterpret_cast(value)); + std::auto_ptr securityLayer; + if (ssf) { + securityLayer = std::auto_ptr(new CyrusSecurityLayer(sasl_conn, maxFrameSize, ssf)); } return securityLayer; } @@@ -632,6 -428,7 +632,6 @@@ int getPasswordFromSettings(sasl_conn_t return SASL_FAIL; } } - } // namespace qpid #endif diff --combined qpid/cpp/src/qpid/SaslFactory.h index da7e892,8554597..0000000 --- a/qpid/cpp/src/qpid/SaslFactory.h +++ b/qpid/cpp/src/qpid/SaslFactory.h @@@ -26,7 -26,7 +26,7 @@@ #include namespace qpid { - +class SaslServer; /** * Factory for instances of the Sasl interface through which Sasl * support is provided to a ConnectionHandler. @@@ -35,7 -35,6 +35,7 @@@ class SaslFactor { public: QPID_COMMON_EXTERN std::auto_ptr create(const std::string & userName, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction=true ); + QPID_COMMON_EXTERN std::auto_ptr createServer(const std::string& realm, bool encryptionRequired, const qpid::sys::SecuritySettings&); QPID_COMMON_EXTERN static SaslFactory& getInstance(); QPID_COMMON_EXTERN ~SaslFactory(); private: diff --combined qpid/cpp/src/qpid/broker/Connection.cpp index 61573a6,08a9c75..0000000 --- a/qpid/cpp/src/qpid/broker/Connection.cpp +++ b/qpid/cpp/src/qpid/broker/Connection.cpp @@@ -119,7 -119,7 +119,7 @@@ void Connection::addManagementObject() agent = broker.getManagementAgent(); if (agent != 0) { // TODO set last bool true if system connection - mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !link, false); + mgmtObject = new _qmf::Connection(agent, this, parent, mgmtId, !link, false, "AMQP 0-10"); mgmtObject->set_shadow(shadow); agent->addObject(mgmtObject, objectId); } diff --combined qpid/cpp/src/qpid/broker/ConnectionFactory.cpp index 87edf3e,d5d24ca..0000000 --- a/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp +++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp @@@ -22,7 -22,6 +22,7 @@@ #include "qpid/framing/ProtocolVersion.h" #include "qpid/amqp_0_10/Connection.h" #include "qpid/broker/Connection.h" +#include "qpid/broker/amqp/Connection.h" #include "qpid/sys/SecuritySettings.h" #include "qpid/log/Statement.h" @@@ -42,15 -41,10 +42,15 @@@ sys::ConnectionCodec ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id, const SecuritySettings& external) { if (v == ProtocolVersion(0, 10)) { + QPID_LOG(notice, "Using AMQP 0-10"); ConnectionPtr c(new amqp_0_10::Connection(out, id, false)); c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, false))); return c.release(); + } else if (v == ProtocolVersion(1, 0)) { + QPID_LOG(info, "Using AMQP 1.0 (with no SASL layer)"); + return new qpid::broker::amqp::Connection(out, id, broker, false); } + QPID_LOG(notice, "Unrecognised protocol version"); return 0; } diff --combined qpid/cpp/src/qpid/broker/DeliverableMessage.cpp index a967bdf,be4b7f0..0000000 --- a/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp +++ b/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp @@@ -37,9 -37,7 +37,9 @@@ Message& DeliverableMessage::getMessage return msg; } +/* uint64_t DeliverableMessage::contentSize() { return msg.getContentSize(); } +*/ diff --combined qpid/cpp/src/qpid/broker/DeliverableMessage.h index b277caf,d6d6bf5..0000000 --- a/qpid/cpp/src/qpid/broker/DeliverableMessage.h +++ b/qpid/cpp/src/qpid/broker/DeliverableMessage.h @@@ -36,7 -36,7 +36,7 @@@ namespace qpid QPID_BROKER_EXTERN DeliverableMessage(const Message& msg, TxBuffer* txn); QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr& queue); QPID_BROKER_EXTERN Message& getMessage(); - QPID_BROKER_EXTERN uint64_t contentSize(); + //QPID_BROKER_EXTERN uint64_t contentSize(); virtual ~DeliverableMessage(){} }; } diff --combined qpid/cpp/src/qpid/broker/Message.cpp index 431f429,c48e9bc..0000000 --- a/qpid/cpp/src/qpid/broker/Message.cpp +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@@ -131,18 -131,6 +131,18 @@@ uint64_t Message::getTtl() cons } } +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& e) { //TODO: this is still quite 0-10 specific... diff --combined qpid/cpp/src/qpid/broker/Message.h index b999a7d,599819d..0000000 --- a/qpid/cpp/src/qpid/broker/Message.h +++ b/qpid/cpp/src/qpid/broker/Message.h @@@ -61,6 -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; @@@ -91,7 -92,6 +91,7 @@@ 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 +125,7 @@@ 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 getIngressCompletion() const; QPID_BROKER_EXTERN boost::intrusive_ptr getPersistentContext() const; diff --combined qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp index f99bf6a,b04d7c3..0000000 --- a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp +++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@@ -27,12 -27,8 +27,12 @@@ #include "qpid/broker/Bridge.h" #include "qpid/broker/RecoveredEnqueue.h" #include "qpid/broker/RecoveredDequeue.h" +//AMQP 1.0: +#include "qpid/broker/amqp/Message.h" +//AMQP 0-10: #include "qpid/broker/amqp_0_10/MessageTransfer.h" #include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" using boost::dynamic_pointer_cast; using boost::intrusive_ptr; @@@ -135,19 -131,10 +135,19 @@@ RecoverableQueue::shared_ptr RecoveryMa RecoverableMessage::shared_ptr RecoveryManagerImpl::recoverMessage(framing::Buffer& buffer) { - //TODO: determine encoding/version actually used - boost::intrusive_ptr transfer(new qpid::broker::amqp_0_10::MessageTransfer()); - transfer->decodeHeader(buffer); - return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(transfer, transfer))); + framing::Buffer sniffer(buffer.getPointer(), buffer.available()); + uint32_t format = sniffer.getLong(); + if (format == 0) { + //this is a 1.0 format message + boost::intrusive_ptr m(new qpid::broker::amqp::Message(sniffer.available())); + m->decodeHeader(sniffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(m, m))); + } else { + //this is an 0-10 format message + boost::intrusive_ptr transfer(new qpid::broker::amqp_0_10::MessageTransfer()); + transfer->decodeHeader(buffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(Message(transfer, transfer))); + } } RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const std::string& xid, diff --combined qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp index fcaee2e,2d7c820..0000000 --- a/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp +++ b/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp @@@ -505,7 -505,7 +505,7 @@@ std::auto_ptr CyrusAuthe uint ssf = *(reinterpret_cast(value)); std::auto_ptr securityLayer; if (ssf) { - securityLayer = std::auto_ptr(new CyrusSecurityLayer(sasl_conn, maxFrameSize)); + securityLayer = std::auto_ptr(new CyrusSecurityLayer(sasl_conn, maxFrameSize, ssf)); } qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject(); if ( cnxMgmt ) diff --combined qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp index 64a5e47,757f6ef..0000000 --- a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp +++ b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp @@@ -23,12 -23,8 +23,12 @@@ #include "qpid/amqp_0_10/Connection.h" #include "qpid/broker/Connection.h" #include "qpid/broker/SecureConnection.h" +#include "qpid/broker/amqp/Connection.h" +#include "qpid/broker/amqp/Sasl.h" #include "qpid/sys/SecuritySettings.h" #include "qpid/log/Statement.h" +#include "qpid/SaslFactory.h" +#include "qpid/NullSaslServer.h" namespace qpid { namespace broker { @@@ -53,24 -49,6 +53,24 @@@ SecureConnectionFactory::create(Protoco c->setInputHandler(InputPtr(i.release())); sc->setCodec(std::auto_ptr(c)); return sc.release(); + } else if (v == ProtocolVersion(1, 0)) { + if (v.getProtocol() == 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 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; } diff --combined qpid/cpp/src/qpid/broker/SemanticState.cpp index b148805,6553039..0000000 --- a/qpid/cpp/src/qpid/broker/SemanticState.cpp +++ b/qpid/cpp/src/qpid/broker/SemanticState.cpp @@@ -30,7 -30,6 +30,7 @@@ #include "qpid/broker/SessionOutputException.h" #include "qpid/broker/TxAccept.h" #include "qpid/broker/amqp_0_10/MessageTransfer.h" +#include "qpid/broker/amqp/Translation.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/SequenceSet.h" @@@ -346,12 -345,11 +346,12 @@@ bool SemanticState::ConsumerImpl::deliv bool SemanticState::ConsumerImpl::deliver(const QueueCursor& cursor, const Message& msg, boost::shared_ptr consumer) { allocateCredit(msg); + qpid::broker::amqp::Translation t(msg); + const amqp_0_10::MessageTransfer* transfer = t.getTransfer(); DeliveryRecord record(cursor, msg.getSequence(), queue, getTag(), - consumer, acquire, !ackExpected, credit.isWindowMode(), amqp_0_10::MessageTransfer::getRequiredCredit(msg)); + consumer, acquire, !ackExpected, credit.isWindowMode(), transfer->getRequiredCredit()); bool sync = syncFrequency && ++deliveryCount >= syncFrequency; if (sync) deliveryCount = 0;//reset - const amqp_0_10::MessageTransfer* transfer = dynamic_cast(&msg.getEncoding()); record.setId(parent->session.deliver(*transfer, getTag(), msg.isRedelivered(), msg.getTtl(), msg.getTimestamp(), ackExpected ? message::ACCEPT_MODE_EXPLICIT : message::ACCEPT_MODE_NONE, diff --combined qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp index 1e247c3,cac4434..0000000 --- a/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp +++ b/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.cpp @@@ -118,9 -118,8 +118,9 @@@ void MessageTransfer::computeRequiredCr } uint32_t MessageTransfer::getRequiredCredit(const qpid::broker::Message& msg) { - //TODO: may need to reflect annotations and other modifications in this also - return get(msg).getRequiredCredit(); + const MessageTransfer* transfer = dynamic_cast(&msg.getEncoding()); + if (transfer) return transfer->getRequiredCredit(); + else return msg.getContentSize(); } qpid::framing::FrameSet& MessageTransfer::getFrames() @@@ -350,22 -349,6 +350,22 @@@ boost::intrusive_ptr(&message.getEncoding()); + assert(transfer); + if (!transfer) throw qpid::Exception("Translation from 1.0 to 0-10 not yet implemented!"); + return *transfer; +} +const MessageTransfer& MessageTransfer::get(const qpid::broker::Message& message) +{ + const MessageTransfer* transfer = dynamic_cast(&message.getEncoding()); + assert(transfer); + if (!transfer) throw qpid::Exception("Translation from 1.0 to 0-10 not yet implemented!"); + return *transfer; +} + } // qpid::broker namespace, TODO: move these elsewhere! void encode(const Message& in, std::string& out) diff --combined qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h index ed94093,590e389..0000000 --- a/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h +++ b/qpid/cpp/src/qpid/broker/amqp_0_10/MessageTransfer.h @@@ -110,8 -110,12 +110,8 @@@ class MessageTransfer : public qpid::br static bool isImmediateDeliveryRequired(const qpid::broker::Message& message); static uint32_t getRequiredCredit(const qpid::broker::Message&); - static MessageTransfer& get(qpid::broker::Message& message) { - return *dynamic_cast(&message.getEncoding()); - } - static const MessageTransfer& get(const qpid::broker::Message& message) { - return *dynamic_cast(&message.getEncoding()); - } + static MessageTransfer& get(qpid::broker::Message& message); + static const MessageTransfer& get(const qpid::broker::Message& message); QPID_BROKER_EXTERN static bool isQMFv2(const qpid::broker::Message& message); QPID_BROKER_EXTERN static bool isLastQMFResponse(const qpid::broker::Message& message, const std::string correlation); private: diff --combined qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp index 19cb3f0,00ddb55..0000000 --- a/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp +++ b/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp @@@ -38,17 -38,10 +38,17 @@@ void ProtocolInitiation::encode(Buffer buffer.putOctet('M'); buffer.putOctet('Q'); buffer.putOctet('P'); - buffer.putOctet(1);//class - buffer.putOctet(1);//instance - buffer.putOctet(version.getMajor()); - buffer.putOctet(version.getMinor()); + if (version.getMajor() == 1) { + buffer.putOctet(version.getProtocol()); + buffer.putOctet(version.getMajor()); + buffer.putOctet(version.getMinor()); + buffer.putOctet(0);//revision + } else { + buffer.putOctet(1);//class + buffer.putOctet(1);//instance + buffer.putOctet(version.getMajor()); + buffer.putOctet(version.getMinor()); + } } bool ProtocolInitiation::decode(Buffer& buffer){ @@@ -57,18 -50,10 +57,18 @@@ buffer.getOctet();//M buffer.getOctet();//Q buffer.getOctet();//P - buffer.getOctet();//class - buffer.getOctet();//instance - version.setMajor(buffer.getOctet()); - version.setMinor(buffer.getOctet()); + uint8_t protocolClass = buffer.getOctet();//class + version.setProtocol(protocolClass); + if (protocolClass == 1) { + //old (pre-1.0) style + buffer.getOctet();//instance + version.setMajor(buffer.getOctet()); + version.setMinor(buffer.getOctet()); + } else { + version.setMajor(buffer.getOctet()); + version.setMinor(buffer.getOctet()); + buffer.getOctet();//revision + } return true; }else{ return false; diff --combined qpid/cpp/src/qpid/framing/ProtocolVersion.cpp index 12ede2f,c63cddb..0000000 --- a/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp +++ b/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp @@@ -27,10 -27,6 +27,10 @@@ const std::string ProtocolVersion::toSt { std::stringstream ss; ss << major_ << "-" << minor_; + if (major_ == 1) { + if (protocol_ == 3) ss << " (SASL)"; + else if (protocol_ == 2) ss << " (TLS)"; + } return ss.str(); } @@@ -46,7 -42,3 +46,7 @@@ bool ProtocolVersion::operator==(Protoc return major_ == p.major_ && minor_ == p.minor_; } +uint8_t ProtocolVersion::AMQP(0); +uint8_t ProtocolVersion::LEGACY_AMQP(1); +uint8_t ProtocolVersion::TLS(2); +uint8_t ProtocolVersion::SASL(3); diff --combined qpid/cpp/src/qpid/management/ManagementAgent.cpp index 701bbfe,474c86e..0000000 --- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@@ -2140,7 -2140,7 +2140,7 @@@ bool ManagementAgent::authorizeAgentMes // authorized or not. In this case, return true (authorized) if there is no ACL in place, // otherwise return false; // - if (msg.getContentSize() > MA_BUFFER_SIZE) + if (msg.getContent().size() > MA_BUFFER_SIZE) return broker->getAcl() == 0; inBuffer.putRawData(msg.getContent()); diff --combined qpid/cpp/src/qpid/messaging/Connection.cpp index b5baaa6,bd90aa5..0000000 --- a/qpid/cpp/src/qpid/messaging/Connection.cpp +++ b/qpid/cpp/src/qpid/messaging/Connection.cpp @@@ -25,7 -25,6 +25,7 @@@ #include "qpid/messaging/SessionImpl.h" #include "qpid/messaging/PrivateImplRef.h" #include "qpid/client/amqp0_10/ConnectionImpl.h" +#include "qpid/messaging/amqp/ConnectionHandle.h" #include "qpid/log/Statement.h" namespace qpid { @@@ -41,22 -40,11 +41,22 @@@ Connection& Connection::operator=(cons Connection::~Connection() { PI::dtor(*this); } Connection::Connection(const std::string& url, const std::string& o) -{ +{ Variant::Map options; AddressParser parser(o); if (o.empty() || parser.parseMap(options)) { - PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options)); + Variant::Map::iterator i = options.find("use_amqp_1.0"); + if (i != options.end()) { + if (i->second) { + options.erase("use_amqp_1.0"); + PI::ctor(*this, new qpid::messaging::amqp::ConnectionHandle(url, options)); + } else { + options.erase("use_amqp_1.0"); + PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options)); + } + } else { + PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options)); + } } else { throw InvalidOptionString("Invalid option string: " + o); } @@@ -67,27 -55,24 +67,27 @@@ Connection::Connection(const std::strin } Connection::Connection() -{ +{ Variant::Map options; std::string url = "amqp:tcp:127.0.0.1:5672"; PI::ctor(*this, new qpid::client::amqp0_10::ConnectionImpl(url, options)); } -void Connection::open() { impl->open(); } +void Connection::open() +{ + impl->open(); +} bool Connection::isOpen() { return impl->isOpen(); } bool Connection::isOpen() const { return impl->isOpen(); } void Connection::close() { impl->close(); } Session Connection::createSession(const std::string& name) { return impl->newSession(false, name); } Session Connection::createTransactionalSession(const std::string& name) -{ +{ return impl->newSession(true, name); } Session Connection::getSession(const std::string& name) const { return impl->getSession(name); } void Connection::setOption(const std::string& name, const Variant& value) -{ +{ impl->setOption(name, value); } std::string Connection::getAuthenticatedUsername() diff --combined qpid/cpp/src/qpid/messaging/Message.cpp index 0f03bc8,ef70c10..0000000 --- a/qpid/cpp/src/qpid/messaging/Message.cpp +++ b/qpid/cpp/src/qpid/messaging/Message.cpp @@@ -46,26 -46,26 +46,26 @@@ const std::string& Message::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 --combined qpid/cpp/src/qpid/messaging/MessageImpl.cpp index fc9bc5d,0601800..0000000 --- a/qpid/cpp/src/qpid/messaging/MessageImpl.cpp +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.cpp @@@ -45,163 -45,28 +45,163 @@@ MessageImpl::MessageImpl(const char* ch 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 --combined qpid/cpp/src/qpid/messaging/MessageImpl.h index 915c790,57df6b3..0000000 --- a/qpid/cpp/src/qpid/messaging/MessageImpl.h +++ b/qpid/cpp/src/qpid/messaging/MessageImpl.h @@@ -24,77 -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 +#include 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 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 e) { encoded = e; } + boost::shared_ptr getEncoded() const { return encoded; } }; class Message; diff --combined qpid/cpp/src/qpid/messaging/ReceiverImpl.h index e450693,57059bf..0000000 --- a/qpid/cpp/src/qpid/messaging/ReceiverImpl.h +++ b/qpid/cpp/src/qpid/messaging/ReceiverImpl.h @@@ -22,12 -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 --combined qpid/cpp/src/qpid/messaging/SenderImpl.h index d978463,a1ca02c..0000000 --- a/qpid/cpp/src/qpid/messaging/SenderImpl.h +++ b/qpid/cpp/src/qpid/messaging/SenderImpl.h @@@ -22,7 -22,6 +22,7 @@@ * */ #include "qpid/RefCounted.h" +#include "qpid/sys/IntegerTypes.h" namespace qpid { namespace messaging { diff --combined qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp index e766a05,2e117a3..0000000 --- a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp +++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp @@@ -175,9 -175,6 +175,9 @@@ void AsynchIOHandler::readbuff(AsynchIO write(framing::ProtocolInitiation(framing::highestProtocolVersion)); readError = true; aio->queueWriteClose(); + } else { + //read any further data that may already have been sent + decoded += codec->decode(buff->bytes+buff->dataStart+in.getPosition(), buff->dataCount-in.getPosition()); } } catch (const std::exception& e) { QPID_LOG(error, e.what()); diff --combined qpid/cpp/src/qpid/sys/SecurityLayer.h index 317ada1,52bc40e..0000000 --- a/qpid/cpp/src/qpid/sys/SecurityLayer.h +++ b/qpid/cpp/src/qpid/sys/SecurityLayer.h @@@ -33,12 -33,8 +33,12 @@@ namespace sys class SecurityLayer : public Codec { public: + SecurityLayer(int ssf_) : ssf(ssf_) {} + int getSsf() const { return ssf; } virtual void init(Codec*) = 0; virtual ~SecurityLayer() {} + private: + int ssf; }; }} // namespace qpid::sys diff --combined qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp index 79d9d08,a4d291e..0000000 --- a/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp +++ b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp @@@ -29,8 -29,8 +29,8 @@@ namespace qpid namespace sys { namespace cyrus { -CyrusSecurityLayer::CyrusSecurityLayer(sasl_conn_t* c, uint16_t maxFrameSize) : - conn(c), decrypted(0), decryptedSize(0), encrypted(0), encryptedSize(0), codec(0), maxInputSize(0), +CyrusSecurityLayer::CyrusSecurityLayer(sasl_conn_t* c, uint16_t maxFrameSize, int ssf) : + SecurityLayer(ssf), conn(c), decrypted(0), decryptedSize(0), encrypted(0), encryptedSize(0), codec(0), maxInputSize(0), decodeBuffer(maxFrameSize), encodeBuffer(maxFrameSize), encoded(0) { const void* value(0); diff --combined qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h index ae86ba5,7f10824..0000000 --- a/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h +++ b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.h @@@ -37,7 -37,7 +37,7 @@@ namespace cyrus class CyrusSecurityLayer : public qpid::sys::SecurityLayer { public: - CyrusSecurityLayer(sasl_conn_t*, uint16_t maxFrameSize); + CyrusSecurityLayer(sasl_conn_t*, uint16_t maxFrameSize, int ssf); size_t decode(const char* buffer, size_t size); size_t encode(char* buffer, size_t size); bool canEncode(); diff --combined qpid/cpp/src/ssl.mk index 68f6c88,4dba9bb..0000000 --- a/qpid/cpp/src/ssl.mk +++ b/qpid/cpp/src/ssl.mk @@@ -50,8 -50,6 +50,8 @@@ ssl_la_LDFLAGS = $(PLUGINLDFLAGS dmoduleexec_LTLIBRARIES += ssl.la sslconnector_la_SOURCES = \ + qpid/messaging/amqp/SslTransport.h \ + qpid/messaging/amqp/SslTransport.cpp \ qpid/client/SslConnector.cpp sslconnector_la_LIBADD = \ diff --combined qpid/cpp/src/tests/qpid-receive.cpp index e623d05,7a02b87..0000000 --- a/qpid/cpp/src/tests/qpid-receive.cpp +++ b/qpid/cpp/src/tests/qpid-receive.cpp @@@ -222,7 -222,7 +222,7 @@@ int main(int argc, char ** argv if (msg.getCorrelationId().size()) std::cout << "CorrelationId: " << msg.getCorrelationId() << std::endl; if (msg.getUserId().size()) std::cout << "UserId: " << msg.getUserId() << std::endl; if (msg.getTtl().getMilliseconds()) std::cout << "TTL: " << msg.getTtl().getMilliseconds() << std::endl; - if (msg.getPriority()) std::cout << "Priority: " << msg.getPriority() << std::endl; + if (msg.getPriority()) std::cout << "Priority: " << ((uint) msg.getPriority()) << std::endl; if (msg.getDurable()) std::cout << "Durable: true" << std::endl; if (msg.getRedelivered()) std::cout << "Redelivered: true" << std::endl; std::cout << "Properties: " << msg.getProperties() << std::endl; diff --combined qpid/specs/management-schema.xml index 7236b0c,7ae1c12..0000000 --- a/qpid/specs/management-schema.xml +++ b/qpid/specs/management-schema.xml @@@ -358,7 -358,6 +358,7 @@@ + diff --git a/qpid/cpp/src/qpid/NullSaslServer.cpp b/qpid/cpp/src/qpid/NullSaslServer.cpp new file mode 100644 index 0000000..5139fec --- /dev/null +++ b/qpid/cpp/src/qpid/NullSaslServer.cpp @@ -0,0 +1,82 @@ +/* + * + * 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 "NullSaslServer.h" +#include "qpid/log/Statement.h" +#include +#include + +namespace qpid { +NullSaslServer::NullSaslServer(const std::string& r) : realm(r) {} +NullSaslServer::Status NullSaslServer::start(const std::string& mechanism, const std::string* response, std::string& /*challenge*/) +{ + if (mechanism == "PLAIN") { + if (response) { + std::string uid; + std::string::size_type i = response->find((char)0); + if (i == 0 && response->size() > 1) { + //no authorization id; use authentication id + i = response->find((char)0, 1); + if (i != std::string::npos) uid = response->substr(1, i-1); + } else if (i != std::string::npos) { + //authorization id is first null delimited field + uid = response->substr(0, i); + }//else not a valid SASL PLAIN response, throw error? + if (!uid.empty()) { + //append realm if it has not already been added + i = uid.find(realm); + if (i == std::string::npos || realm.size() + i < uid.size()) { + uid = boost::str(boost::format("%1%@%2%") % uid % realm); + } + userid = uid; + } + return OK; + } else { + QPID_LOG(error, "Invalid PLAIN request, expected response containing user credentials"); + return FAIL; + } + } else if (mechanism == "ANONYMOUS") { + userid = "anonymous"; + return OK; + } else { + return FAIL; + } +} + +NullSaslServer::Status NullSaslServer::step(const std::string* /*response*/, std::string& /*challenge*/) +{ + assert(false); + return FAIL; +} +std::string NullSaslServer::getMechanisms() +{ + return std::string("ANONYMOUS PLAIN"); +} +std::string NullSaslServer::getUserid() +{ + return userid; +} + +std::auto_ptr NullSaslServer::getSecurityLayer(size_t) +{ + return std::auto_ptr(); +} + +} // namespace qpid diff --git a/qpid/cpp/src/qpid/NullSaslServer.h b/qpid/cpp/src/qpid/NullSaslServer.h new file mode 100644 index 0000000..810defe --- /dev/null +++ b/qpid/cpp/src/qpid/NullSaslServer.h @@ -0,0 +1,49 @@ +#ifndef QPID_NULLSASLSERVER_H +#define QPID_NULLSASLSERVER_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/SaslServer.h" + +namespace qpid { + +/** + * Dummy implementation of the SASL server role. This will advertise + * ANONYMOUS and PLAIN, and parse the reponse data for those + * accordingly, but will make no attempt to actually authenticate + * users. + */ +class NullSaslServer : public SaslServer +{ + public: + NullSaslServer(const std::string& realm); + Status start(const std::string& mechanism, const std::string* response, std::string& challenge); + Status step(const std::string* response, std::string& challenge); + std::string getMechanisms(); + std::string getUserid(); + std::auto_ptr getSecurityLayer(size_t); + private: + std::string realm; + std::string userid; +}; +} // namespace qpid + +#endif /*!QPID_NULLSASLSERVER_H*/ diff --git a/qpid/cpp/src/qpid/SaslServer.h b/qpid/cpp/src/qpid/SaslServer.h new file mode 100644 index 0000000..a707a46 --- /dev/null +++ b/qpid/cpp/src/qpid/SaslServer.h @@ -0,0 +1,48 @@ +#ifndef QPID_SASLSERVER_H +#define QPID_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 +#include + +namespace qpid { +namespace sys { +class SecurityLayer; +} +/** + * + */ +class SaslServer +{ + public: + typedef enum {OK, FAIL, CHALLENGE} Status; + virtual ~SaslServer() {} + virtual Status start(const std::string& mechanism, const std::string* response, std::string& challenge) = 0; + virtual Status step(const std::string* response, std::string& challenge) = 0; + virtual std::string getMechanisms() = 0; + virtual std::string getUserid() = 0; + virtual std::auto_ptr getSecurityLayer(size_t) = 0; + private: +}; +} // namespace qpid + +#endif /*!QPID_SASLSERVER_H*/ diff --git a/qpid/cpp/src/qpid/amqp/CharSequence.cpp b/qpid/cpp/src/qpid/amqp/CharSequence.cpp new file mode 100644 index 0000000..857ec7e --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/CharSequence.h b/qpid/cpp/src/qpid/amqp/CharSequence.h new file mode 100644 index 0000000..a7f1175 --- /dev/null +++ b/qpid/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 +#include + +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/qpid/cpp/src/qpid/amqp/Codec.h b/qpid/cpp/src/qpid/amqp/Codec.h new file mode 100644 index 0000000..c91cd0a --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/Constructor.h b/qpid/cpp/src/qpid/amqp/Constructor.h new file mode 100644 index 0000000..444e455 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/Decoder.cpp b/qpid/cpp/src/qpid/amqp/Decoder.cpp new file mode 100644 index 0000000..4c14c8e --- /dev/null +++ b/qpid/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 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(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/qpid/cpp/src/qpid/amqp/Decoder.h b/qpid/cpp/src/qpid/amqp/Decoder.h new file mode 100644 index 0000000..88f5c18 --- /dev/null +++ b/qpid/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 +#include +#include + +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::map 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/qpid/cpp/src/qpid/amqp/Descriptor.cpp b/qpid/cpp/src/qpid/amqp/Descriptor.cpp new file mode 100644 index 0000000..087e87c --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/Descriptor.h b/qpid/cpp/src/qpid/amqp/Descriptor.h new file mode 100644 index 0000000..c36aa38 --- /dev/null +++ b/qpid/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 + +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/qpid/cpp/src/qpid/amqp/Encoder.cpp b/qpid/cpp/src/qpid/amqp/Encoder.cpp new file mode 100644 index 0000000..6599f70 --- /dev/null +++ b/qpid/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 +#include + +namespace qpid { +namespace amqp { + +namespace { +template size_t encode(char* data, T i); +template <> size_t encode(char* data, uint8_t i) +{ + *data = i; + return 1; +} +template <> size_t encode(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(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(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 struct Backfill +{ + T size; + T count; + char* location; +}; + +template void end(T count, void* token, char* current) +{ + Backfill b; + b.location = (char*) token; + b.size = (T) (current - b.location) - sizeof(b.size); + b.count = count; + b.location += encode(b.location, b.size); + encode(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(data+position, b ? 1u : 0u); +} +void Encoder::write(uint8_t i) +{ + check(sizeof(i)); + position += encode(data+position, i); +} +void Encoder::write(uint16_t i) +{ + check(sizeof(i)); + position += encode(data+position, i); +} +void Encoder::write(uint32_t i) +{ + check(sizeof(i)); + position += encode(data+position, i); +} +void Encoder::write(uint64_t i) +{ + check(sizeof(i)); + position += encode(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 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 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(typecodes::LIST8, d); +} + +void* Encoder::startList32(const Descriptor* d) +{ + return start(typecodes::LIST32, d); +} + +void Encoder::endList8(uint8_t count, void* token) +{ + end(count, token, data+position); +} + +void Encoder::endList32(uint32_t count, void* token) +{ + end(count, token, data+position); +} + +void* Encoder::startMap8(const Descriptor* d) +{ + return start(typecodes::MAP8, d); +} + +void* Encoder::startMap32(const Descriptor* d) +{ + return start(typecodes::MAP32, d); +} + +void Encoder::endMap8(uint8_t count, void* token) +{ + end(count, token, data+position); +} + +void Encoder::endMap32(uint32_t count, void* token) +{ + end(count, token, data+position); +} + + +void* Encoder::startArray8(const Constructor& c, const Descriptor* d) +{ + return startArray(typecodes::ARRAY8, d, c); +} + +void* Encoder::startArray32(const Constructor& c, const Descriptor* d) +{ + return startArray(typecodes::ARRAY32, d, c); +} + +void Encoder::endArray8(size_t count, void* token) +{ + end(count, token, data+position); +} + +void Encoder::endArray32(size_t count, void* token) +{ + end(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/qpid/cpp/src/qpid/amqp/Encoder.h b/qpid/cpp/src/qpid/amqp/Encoder.h new file mode 100644 index 0000000..e2938a0 --- /dev/null +++ b/qpid/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 +#include + +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 codes, const Descriptor* d); + void write(const std::string& v, std::pair codes, const Descriptor* d); + void check(size_t); + + template void write(T value, uint8_t code, const Descriptor* d) + { + if (d) writeDescriptor(*d); + writeCode(code); + write(value); + } + + template void write(T value, std::pair codes, const Descriptor* d) + { + if (value < 256) { + write((uint8_t) value, codes.first, d); + } else { + write(value, codes.second, d); + } + } + + template 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 void* startArray(uint8_t code, const Descriptor* d, const Constructor& c) + { + void* token = start(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/qpid/cpp/src/qpid/amqp/ListReader.h b/qpid/cpp/src/qpid/amqp/ListReader.h new file mode 100644 index 0000000..dce874b --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/LoggingReader.h b/qpid/cpp/src/qpid/amqp/LoggingReader.h new file mode 100644 index 0000000..ed5cab1 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/MessageEncoder.cpp b/qpid/cpp/src/qpid/amqp/MessageEncoder.cpp new file mode 100644 index 0000000..852ad29 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/MessageEncoder.h b/qpid/cpp/src/qpid/amqp/MessageEncoder.h new file mode 100644 index 0000000..3db0763 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/MessageId.cpp b/qpid/cpp/src/qpid/amqp/MessageId.cpp new file mode 100644 index 0000000..05ee778 --- /dev/null +++ b/qpid/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 + +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(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/qpid/cpp/src/qpid/amqp/MessageId.h b/qpid/cpp/src/qpid/amqp/MessageId.h new file mode 100644 index 0000000..f81dc9b --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/MessageReader.cpp b/qpid/cpp/src/qpid/amqp/MessageReader.cpp new file mode 100644 index 0000000..1550fa1 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/MessageReader.h b/qpid/cpp/src/qpid/amqp/MessageReader.h new file mode 100644 index 0000000..b2c23ba --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/Reader.h b/qpid/cpp/src/qpid/amqp/Reader.h new file mode 100644 index 0000000..64019d1 --- /dev/null +++ b/qpid/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 + +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/qpid/cpp/src/qpid/amqp/Sasl.cpp b/qpid/cpp/src/qpid/amqp/Sasl.cpp new file mode 100644 index 0000000..6d0a7cc --- /dev/null +++ b/qpid/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 + +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(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/qpid/cpp/src/qpid/amqp/Sasl.h b/qpid/cpp/src/qpid/amqp/Sasl.h new file mode 100644 index 0000000..558f607 --- /dev/null +++ b/qpid/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 +#include + +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 buffer; + Encoder encoder; + + void* startFrame(); + void endFrame(void*); +}; +}} // namespace qpid::amqp + +#endif /*!QPID_AMQP_SASL_H*/ diff --git a/qpid/cpp/src/qpid/amqp/SaslClient.cpp b/qpid/cpp/src/qpid/amqp/SaslClient.cpp new file mode 100644 index 0000000..69660e9 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/SaslClient.h b/qpid/cpp/src/qpid/amqp/SaslClient.h new file mode 100644 index 0000000..9f3eefa --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/SaslServer.cpp b/qpid/cpp/src/qpid/amqp/SaslServer.cpp new file mode 100644 index 0000000..403730a --- /dev/null +++ b/qpid/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 +#include + +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 parts = split(mechanisms, SPACE); + void* listToken = encoder.startList32(&SASL_MECHANISMS); + if (parts.size() > 1) { + void* arrayToken = encoder.startArray8(Constructor(SYMBOL8)); + for (std::vector::const_iterator i = parts.begin();i != parts.end(); ++i) { + uint8_t size = i->size() > std::numeric_limits::max() ? std::numeric_limits::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/qpid/cpp/src/qpid/amqp/SaslServer.h b/qpid/cpp/src/qpid/amqp/SaslServer.h new file mode 100644 index 0000000..43b9604 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/descriptors.h b/qpid/cpp/src/qpid/amqp/descriptors.h new file mode 100644 index 0000000..b0249f6 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/amqp/typecodes.h b/qpid/cpp/src/qpid/amqp/typecodes.h new file mode 100644 index 0000000..3c6bd17 --- /dev/null +++ b/qpid/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 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/qpid/cpp/src/qpid/broker/amqp/Connection.cpp b/qpid/cpp/src/qpid/broker/amqp/Connection.cpp new file mode 100644 index 0000000..fae0e49 --- /dev/null +++ b/qpid/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 +extern "C" { +#include +} + +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 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(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 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); + pn_link_set_source(l, pn_link_remote_source(l)); + pn_link_set_target(l, pn_link_remote_target(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/qpid/cpp/src/qpid/broker/amqp/Connection.h b/qpid/cpp/src/qpid/broker/amqp/Connection.h new file mode 100644 index 0000000..08fb732 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/amqp/Connection.h @@ -0,0 +1,72 @@ +#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" +extern "C" { +#include +} +#include +#include + +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 > 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/qpid/cpp/src/qpid/broker/amqp/Header.cpp b/qpid/cpp/src/qpid/broker/amqp/Header.cpp new file mode 100644 index 0000000..493e757 --- /dev/null +++ b/qpid/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::max()) return std::numeric_limits::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/qpid/cpp/src/qpid/broker/amqp/Header.h b/qpid/cpp/src/qpid/broker/amqp/Header.h new file mode 100644 index 0000000..6e4f763 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/broker/amqp/ManagedConnection.cpp b/qpid/cpp/src/qpid/broker/amqp/ManagedConnection.cpp new file mode 100644 index 0000000..4eb8234 --- /dev/null +++ b/qpid/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), connection(0), 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 = 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* 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/qpid/cpp/src/qpid/broker/amqp/ManagedConnection.h b/qpid/cpp/src/qpid/broker/amqp/ManagedConnection.h new file mode 100644 index 0000000..94f12c7 --- /dev/null +++ b/qpid/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* 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* connection; + qpid::management::ManagementAgent* agent; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDCONNECTION_H*/ diff --git a/qpid/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp b/qpid/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.cpp new file mode 100644 index 0000000..c6bfd18 --- /dev/null +++ b/qpid/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 = 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* 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/qpid/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h b/qpid/cpp/src/qpid/broker/amqp/ManagedOutgoingLink.h new file mode 100644 index 0000000..9e90089 --- /dev/null +++ b/qpid/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* GetManagementObject() const; + void outgoingMessageSent(); + void outgoingMessageAccepted(); + void outgoingMessageRejected(); + private: + ManagedSession& parent; + const std::string id; + qmf::org::apache::qpid::broker::Subscription* subscription; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDOUTGOINGLINK_H*/ diff --git a/qpid/cpp/src/qpid/broker/amqp/ManagedSession.cpp b/qpid/cpp/src/qpid/broker/amqp/ManagedSession.cpp new file mode 100644 index 0000000..b767056 --- /dev/null +++ b/qpid/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), session(0), unacked(0) +{ + qpid::management::ManagementAgent* agent = broker.getManagementAgent(); + if (agent != 0) { + session = 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* 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/qpid/cpp/src/qpid/broker/amqp/ManagedSession.h b/qpid/cpp/src/qpid/broker/amqp/ManagedSession.h new file mode 100644 index 0000000..ea92123 --- /dev/null +++ b/qpid/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* 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* session; + size_t unacked; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_MANAGEDSESSION_H*/ diff --git a/qpid/cpp/src/qpid/broker/amqp/Message.cpp b/qpid/cpp/src/qpid/broker/amqp/Message.cpp new file mode 100644 index 0000000..af67f2c --- /dev/null +++ b/qpid/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 + +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(&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 Message::merge(const std::map& 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 copy(new Message(data.size()+extra)); + size_t position(0); + if (deliveryAnnotations) { + ::memcpy(©->data[position], deliveryAnnotations.data, deliveryAnnotations.size); + position += deliveryAnnotations.size; + } + if (messageAnnotations) { + //TODO: actual merge required + ::memcpy(©->data[position], messageAnnotations.data, messageAnnotations.size); + position += messageAnnotations.size; + } else { + qpid::amqp::MessageEncoder encoder(©->data[position], extra); + encoder.writeMap(annotations, &qpid::amqp::message::MESSAGE_ANNOTATIONS, true); + position += extra; + } + if (bareMessage) { + ::memcpy(©->data[position], bareMessage.data, bareMessage.size); + position += bareMessage.size; + } + if (footer) { + ::memcpy(©->data[position], footer.data, footer.size); + position += footer.size; + } + copy->scan(); + return copy; +} + +}}} // namespace qpid::broker::amqp diff --git a/qpid/cpp/src/qpid/broker/amqp/Message.h b/qpid/cpp/src/qpid/broker/amqp/Message.h new file mode 100644 index 0000000..d4a97c9 --- /dev/null +++ b/qpid/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 + +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 merge(const std::map& annotations) const; + + static const Message& get(const qpid::broker::Message&); + private: + std::vector data; + + //header: + boost::optional durable; + boost::optional priority; + boost::optional ttl; + boost::optional firstAcquirer; + boost::optional 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/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp b/qpid/cpp/src/qpid/broker/amqp/Outgoing.cpp new file mode 100644 index 0000000..70c6b9e --- /dev/null +++ b/qpid/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 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(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(&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/qpid/cpp/src/qpid/broker/amqp/Outgoing.h b/qpid/cpp/src/qpid/broker/amqp/Outgoing.h new file mode 100644 index 0000000..037c5db --- /dev/null +++ b/qpid/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" +extern "C" { +#include +} +#include +#include + +namespace qpid { +namespace sys { +class OutputControl; +} +namespace broker { +class Broker; +class Queue; +namespace amqp { +class ManagedSession; +template +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, public ManagedOutgoingLink +{ + public: + Outgoing(Broker&,boost::shared_ptr 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; + CircularArray deliveries; + pn_link_t* link; + qpid::sys::OutputControl& out; + size_t current; + int outstanding; + std::vector buffer; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_OUTGOING_H*/ diff --git a/qpid/cpp/src/qpid/broker/amqp/Sasl.cpp b/qpid/cpp/src/qpid/broker/amqp/Sasl.cpp new file mode 100644 index 0000000..4b89e7b --- /dev/null +++ b/qpid/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 +#include + +namespace qpid { +namespace broker { +namespace amqp { + +Sasl::Sasl(qpid::sys::OutputControl& o, const std::string& id, qpid::broker::Broker& broker, std::auto_ptr 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/qpid/cpp/src/qpid/broker/amqp/Sasl.h b/qpid/cpp/src/qpid/broker/amqp/Sasl.h new file mode 100644 index 0000000..079128b --- /dev/null +++ b/qpid/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 +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 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 securityLayer; + std::auto_ptr 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/qpid/cpp/src/qpid/broker/amqp/Session.cpp b/qpid/cpp/src/qpid/broker/amqp/Session.cpp new file mode 100644 index 0000000..135294a --- /dev/null +++ b/qpid/cpp/src/qpid/broker/amqp/Session.cpp @@ -0,0 +1,279 @@ +/* + * + * 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 +#include +#include +#include + +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 q, pn_link_t* l) : queue(q), link(l) {} + void flow(); + void handle(qpid::broker::Message& m); + private: + boost::shared_ptr queue; + pn_link_t* link; +}; + +class Exchange : public Target +{ + public: + Exchange(boost::shared_ptr e, pn_link_t* l) : exchange(e), link(l) {} + void flow(); + void handle(qpid::broker::Message& m); + private: + boost::shared_ptr 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)) { + //i.e a subscription + if (!pn_link_remote_source(link)) { + throw qpid::Exception("No source specified!");/*invalid-field?*/ + } + std::string name = pn_link_remote_source(link); + boost::shared_ptr exchange = broker.getExchanges().find(name); + boost::shared_ptr 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 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 q(new Outgoing(broker, queue, link, *this, out, true)); + senders[link] = q; + q->init(); + } else { + pn_link_set_source(link, 0); + throw qpid::Exception("Node not found: " + name);/*not-found*/ + } + QPID_LOG(debug, "Outgoing link attached"); + } else { + if (!pn_link_remote_target(link)) { + throw qpid::Exception("No target specified!");/*invalid field?*/ + } + std::string name = pn_link_remote_target(link); + boost::shared_ptr queue = broker.getQueues().find(name); + boost::shared_ptr 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 q(new Queue(queue, link)); + targets[link] = q; + q->flow(); + } else if (exchange) { + boost::shared_ptr e(new Exchange(exchange, link)); + targets[link] = e; + e->flow(); + } else { + pn_link_set_target(link, 0); + 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 s) : delivery(d), session(s) {} + void completed(bool sync) { session->accepted(delivery, sync); } + boost::intrusive_ptr clone() + { + boost::intrusive_ptr copy(new Transfer(delivery, session)); + return copy; + } + private: + pn_delivery_t* delivery; + boost::shared_ptr 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 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 copy; + { + qpid::sys::Mutex::ScopedLock l(lock); + completed.swap(copy); + } + for (std::deque::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/qpid/cpp/src/qpid/broker/amqp/Session.h b/qpid/cpp/src/qpid/broker/amqp/Session.h new file mode 100644 index 0000000..a332c74 --- /dev/null +++ b/qpid/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" + +extern "C" { +#include +} +#include +#include +#include +#include + +namespace qpid { +namespace broker { + +class Broker; + +namespace amqp { + +class ManagedConnection; +class Outgoing; +class Target; +/** + * + */ +class Session : public ManagedSession, public boost::enable_shared_from_this +{ + 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 > Senders; + typedef std::map > Targets; + pn_session_t* session; + qpid::broker::Broker& broker; + ManagedConnection& connection; + qpid::sys::OutputControl& out; + Targets targets; + Senders senders; + std::deque completed; + bool deleted; + qpid::sys::Mutex lock; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP1_SESSION_H*/ diff --git a/qpid/cpp/src/qpid/broker/amqp/Translation.cpp b/qpid/cpp/src/qpid/broker/amqp/Translation.cpp new file mode 100644 index 0000000..b7130a4 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/amqp/Translation.cpp @@ -0,0 +1,237 @@ +/* + * + * 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 + +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()), + deliveryProperties(transfer.getProperties()) + {} + 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()->getDestination(); + } +}; +} + +Translation::Translation(const qpid::broker::Message& m) : original(m) {} + + +const qpid::broker::amqp_0_10::MessageTransfer* Translation::getTransfer() +{ + const qpid::broker::amqp_0_10::MessageTransfer* t = dynamic_cast(&original.getEncoding()); + if (t) { + return t;//no translation required + } else { + const Message* message = dynamic_cast(&original.getEncoding()); + if (message) { + //translate 1.0 message into 0-10 + transfer = boost::shared_ptr(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()->getData().assign(body.data, body.size); + transfer->getFrames().append(content); + + qpid::framing::MessageProperties* props = + transfer->getFrames().getHeaders()->get(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(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(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(&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(&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 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/qpid/cpp/src/qpid/broker/amqp/Translation.h b/qpid/cpp/src/qpid/broker/amqp/Translation.h new file mode 100644 index 0000000..458c430 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/amqp/Translation.h @@ -0,0 +1,59 @@ +#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 + +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 + */ + 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; + boost::shared_ptr transfer; +}; +}}} // namespace qpid::broker::amqp + +#endif /*!QPID_BROKER_AMQP_TRANSLATION_H*/ diff --git a/qpid/cpp/src/qpid/messaging/ConnectionOptions.cpp b/qpid/cpp/src/qpid/messaging/ConnectionOptions.cpp new file mode 100644 index 0000000..ecd5ba9 --- /dev/null +++ b/qpid/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 +#include + +namespace qpid { +namespace messaging { + +namespace { +double FOREVER(std::numeric_limits::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& 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& to) +{ + for (qpid::types::Variant::List::const_iterator i = from.begin(); i != from.end(); ++i) + merge(i->asString(), to); +} + +} + +ConnectionOptions::ConnectionOptions(const std::map& 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/qpid/cpp/src/qpid/messaging/ConnectionOptions.h b/qpid/cpp/src/qpid/messaging/ConnectionOptions.h new file mode 100644 index 0000000..6786fd4 --- /dev/null +++ b/qpid/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 +#include + +namespace qpid { +namespace types { +class Variant; +} +namespace messaging { + +struct ConnectionOptions : qpid::client::ConnectionSettings +{ + std::vector urls; + bool replaceUrls; + bool reconnect; + double timeout; + int32_t limit; + double minReconnectInterval; + double maxReconnectInterval; + int32_t retries; + bool reconnectOnLimitExceeded; + + ConnectionOptions(const std::map&); + void set(const std::string& name, const qpid::types::Variant& value); +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_CONNECTIONOPTIONS_H*/ diff --git a/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp new file mode 100644 index 0000000..af67bca --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.cpp @@ -0,0 +1,556 @@ +/* + * + * 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 +extern "C" { +#include +} + +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 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(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(); + } 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 l(lock); + return pn_connection_state(connection) & (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE); +} + +void ConnectionContext::endSession(boost::shared_ptr ssn) +{ + qpid::sys::ScopedLock l(lock); + pn_session_close(ssn->session); + //TODO: need to destroy session and remove context from map + wakeupDriver(); +} + +void ConnectionContext::close() +{ + qpid::sys::ScopedLock 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 ssn, boost::shared_ptr lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout) +{ + { + qpid::sys::ScopedLock l(lock); + if (!lnk->capacity) { + pn_link_flow(lnk->receiver, 1); + wakeupDriver(); + } + } + if (get(ssn, lnk, message, timeout)) { + qpid::sys::ScopedLock l(lock); + if (lnk->capacity) { + pn_link_flow(lnk->receiver, 1);//TODO: is this the right approach? + } + return true; + } else { + { + qpid::sys::ScopedLock 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(debug, "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 ssn, boost::shared_ptr lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout) +{ + qpid::sys::AbsTime until(convert(timeout)); + while (true) { + qpid::sys::ScopedLock 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 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 ssn, qpid::messaging::Message* message, bool cumulative) +{ + qpid::sys::ScopedLock l(lock); + if (message) { + ssn->acknowledge(MessageImplAccess::get(*message).getInternalId(), cumulative); + } else { + ssn->acknowledge(); + } + wakeupDriver(); +} + + +void ConnectionContext::attach(boost::shared_ptr ssn, boost::shared_ptr lnk) +{ + pn_link_set_target((pn_link_t*) lnk->sender, 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 ssn, boost::shared_ptr lnk) +{ + pn_link_set_source((pn_link_t*) lnk->receiver, 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 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(); + } + if (credit) pn_link_flow(link, credit); + wakeupDriver(); +} + +void ConnectionContext::send(boost::shared_ptr snd, const qpid::messaging::Message& message, bool sync) +{ + qpid::sys::ScopedLock 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 sender, uint32_t capacity) +{ + qpid::sys::ScopedLock l(lock); + sender->setCapacity(capacity); +} +uint32_t ConnectionContext::getCapacity(boost::shared_ptr sender) +{ + qpid::sys::ScopedLock l(lock); + return sender->getCapacity(); +} +uint32_t ConnectionContext::getUnsettled(boost::shared_ptr sender) +{ + qpid::sys::ScopedLock l(lock); + return sender->getUnsettled(); +} + +void ConnectionContext::setCapacity(boost::shared_ptr receiver, uint32_t capacity) +{ + qpid::sys::ScopedLock l(lock); + receiver->setCapacity(capacity); + pn_link_flow((pn_link_t*) receiver->receiver, receiver->getCapacity()); +} +uint32_t ConnectionContext::getCapacity(boost::shared_ptr receiver) +{ + qpid::sys::ScopedLock l(lock); + return receiver->getCapacity(); +} +uint32_t ConnectionContext::getAvailable(boost::shared_ptr receiver) +{ + qpid::sys::ScopedLock l(lock); + return receiver->getAvailable(); +} +uint32_t ConnectionContext::getUnsettled(boost::shared_ptr receiver) +{ + qpid::sys::ScopedLock l(lock); + return receiver->getUnsettled(); +} + +void ConnectionContext::activateOutput() +{ + qpid::sys::ScopedLock 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 ConnectionContext::newSession(bool transactional, const std::string& n) +{ + qpid::sys::ScopedLock 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 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 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 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(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 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 l(lock); + return haveOutput && state == CONNECTED; +} +void ConnectionContext::closed() +{ + qpid::sys::ScopedLock l(lock); + state = DISCONNECTED; + lock.notifyAll(); +} +void ConnectionContext::opened() +{ + qpid::sys::ScopedLock 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(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 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/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.h b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.h new file mode 100644 index 0000000..1e48d56 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/amqp/ConnectionContext.h @@ -0,0 +1,136 @@ +#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 +#include +#include +#include +#include +#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" +extern "C" { +#include +} + +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 newSession(bool transactional, const std::string& name); + boost::shared_ptr getSession(const std::string& name) const; + void endSession(boost::shared_ptr); + void attach(boost::shared_ptr, boost::shared_ptr); + void attach(boost::shared_ptr, boost::shared_ptr); + void send(boost::shared_ptr ctxt, const qpid::messaging::Message& message, bool sync); + bool fetch(boost::shared_ptr ssn, boost::shared_ptr lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout); + bool get(boost::shared_ptr ssn, boost::shared_ptr lnk, qpid::messaging::Message& message, qpid::messaging::Duration timeout); + void acknowledge(boost::shared_ptr 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, uint32_t); + uint32_t getCapacity(boost::shared_ptr); + uint32_t getUnsettled(boost::shared_ptr); + + void setCapacity(boost::shared_ptr, uint32_t); + uint32_t getCapacity(boost::shared_ptr); + uint32_t getAvailable(boost::shared_ptr); + uint32_t getUnsettled(boost::shared_ptr); + + + 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 > SessionMap; + qpid::Url url; + + boost::shared_ptr driver; + boost::shared_ptr 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; + + 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/qpid/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp b/qpid/cpp/src/qpid/messaging/amqp/ConnectionHandle.cpp new file mode 100644 index 0000000..8fc3182 --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/amqp/ConnectionHandle.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 "ConnectionHandle.h" +#include "ConnectionContext.h" +#include "SessionHandle.h" +#include "qpid/messaging/Session.h" + +namespace qpid { +namespace messaging { +namespace amqp { + +ConnectionHandle::ConnectionHandle(const std::string& url, const qpid::types::Variant::Map& options) : connection(new ConnectionContext(url, options)) {} +ConnectionHandle::ConnectionHandle(boost::shared_ptr 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/qpid/cpp/src/qpid/messaging/amqp/ConnectionHandle.h b/qpid/cpp/src/qpid/messaging/amqp/ConnectionHandle.h new file mode 100644 index 0000000..d1eb27f --- /dev/null +++ b/qpid/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 +#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); + 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 connection; +}; + +}}} // namespace qpid::messaging::amqp_1.0 + +#endif /*!QPID_MESSAGING_AMQP_CONNECTIONHANDLE_H*/ diff --git a/qpid/cpp/src/qpid/messaging/amqp/DriverImpl.cpp b/qpid/cpp/src/qpid/messaging/amqp/DriverImpl.cpp new file mode 100644 index 0000000..0c119c8 --- /dev/null +++ b/qpid/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 DriverImpl::getTransport(const std::string& protocol, TransportContext& connection) +{ + boost::shared_ptr 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::theDefault; +boost::shared_ptr DriverImpl::getDefault() +{ + qpid::sys::Mutex::ScopedLock l(defaultLock); + boost::shared_ptr p = theDefault.lock(); + if (!p) { + p = boost::shared_ptr(new DriverImpl); + theDefault = p; + } + return p; +} + +}}} // namespace qpid::messaging::amqp diff --git a/qpid/cpp/src/qpid/messaging/amqp/DriverImpl.h b/qpid/cpp/src/qpid/messaging/amqp/DriverImpl.h new file mode 100644 index 0000000..354fa1a --- /dev/null +++ b/qpid/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 +#include + +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 getTransport(const std::string& protocol, TransportContext& connection); + + static boost::shared_ptr getDefault(); + private: + boost::shared_ptr poller; + qpid::sys::Thread thread; + static qpid::sys::Mutex defaultLock; + static boost::weak_ptr theDefault; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_DRIVERIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp b/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.cpp new file mode 100644 index 0000000..39eaa3f --- /dev/null +++ b/qpid/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 +#include + +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/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.h b/qpid/cpp/src/qpid/messaging/amqp/EncodedMessage.h new file mode 100644 index 0000000..4616fcd --- /dev/null +++ b/qpid/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 + +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 durable; + boost::optional priority; + boost::optional ttl; + boost::optional firstAcquirer; + boost::optional 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 absoluteExpiryTime; + boost::optional creationTime; + qpid::amqp::CharSequence groupId; + boost::optional 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/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.cpp new file mode 100644 index 0000000..24415e6 --- /dev/null +++ b/qpid/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/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.h b/qpid/cpp/src/qpid/messaging/amqp/ReceiverContext.h new file mode 100644 index 0000000..0a6f363 --- /dev/null +++ b/qpid/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 +#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/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp b/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.cpp new file mode 100644 index 0000000..9bf64eb --- /dev/null +++ b/qpid/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 c, + boost::shared_ptr s, + boost::shared_ptr 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/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.h b/qpid/cpp/src/qpid/messaging/amqp/ReceiverHandle.h new file mode 100644 index 0000000..a1a6f26 --- /dev/null +++ b/qpid/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 +#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, + boost::shared_ptr, + boost::shared_ptr + ); + 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 connection; + boost::shared_ptr session; + boost::shared_ptr receiver; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_RECEIVERHANDLE_H*/ diff --git a/qpid/cpp/src/qpid/messaging/amqp/Sasl.cpp b/qpid/cpp/src/qpid/messaging/amqp/Sasl.cpp new file mode 100644 index 0000000..af13697 --- /dev/null +++ b/qpid/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 + +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 allowed = split(context.mechanism, " "); + std::vector supported = split(offered, " "); + std::stringstream intersection; + for (std::vector::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(securityLayer.get()); + case FAILED: throw qpid::messaging::UnauthorizedAccess("Failed to authenticate"); + case NONE: return static_cast(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/qpid/cpp/src/qpid/messaging/amqp/Sasl.h b/qpid/cpp/src/qpid/messaging/amqp/Sasl.h new file mode 100644 index 0000000..3a2f2e9 --- /dev/null +++ b/qpid/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 + +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 sasl; + std::string hostname; + bool readHeader; + bool writeHeader; + bool haveOutput; + enum { + NONE, FAILED, SUCCEEDED + } state; + std::auto_ptr 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/qpid/cpp/src/qpid/messaging/amqp/SenderContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/SenderContext.cpp new file mode 100644 index 0000000..ef12e1e --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/amqp/SenderContext.cpp @@ -0,0 +1,333 @@ +/* + * + * 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 "proton/message.h" +} +#include +#include + +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 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(&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/qpid/cpp/src/qpid/messaging/amqp/SenderContext.h b/qpid/cpp/src/qpid/messaging/amqp/SenderContext.h new file mode 100644 index 0000000..82c5e6d --- /dev/null +++ b/qpid/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 +#include +#include +#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 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/qpid/cpp/src/qpid/messaging/amqp/SenderHandle.cpp b/qpid/cpp/src/qpid/messaging/amqp/SenderHandle.cpp new file mode 100644 index 0000000..b7168e5 --- /dev/null +++ b/qpid/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 c, + boost::shared_ptr s, + boost::shared_ptr 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/qpid/cpp/src/qpid/messaging/amqp/SenderHandle.h b/qpid/cpp/src/qpid/messaging/amqp/SenderHandle.h new file mode 100644 index 0000000..3c6b666 --- /dev/null +++ b/qpid/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 +#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 connection, + boost::shared_ptr session, + boost::shared_ptr 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 connection; + boost::shared_ptr session; + boost::shared_ptr sender; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SENDERHANDLE_H*/ diff --git a/qpid/cpp/src/qpid/messaging/amqp/SessionContext.cpp b/qpid/cpp/src/qpid/messaging/amqp/SessionContext.cpp new file mode 100644 index 0000000..64c5cbf --- /dev/null +++ b/qpid/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 +#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 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 s(new SenderContext(session, name, address.str())); + senders[name] = s; + return s; +} + +boost::shared_ptr 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 r(new ReceiverContext(session, name, address.str())); + receivers[name] = r; + return r; +} + +boost::shared_ptr 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 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 SessionContext::nextReceiver(qpid::messaging::Duration /*timeout*/) +{ + return boost::shared_ptr(); +} + +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/qpid/cpp/src/qpid/messaging/amqp/SessionContext.h b/qpid/cpp/src/qpid/messaging/amqp/SessionContext.h new file mode 100644 index 0000000..fbc8731 --- /dev/null +++ b/qpid/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 +#include +#include +#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 createSender(const qpid::messaging::Address& address); + boost::shared_ptr createReceiver(const qpid::messaging::Address& address); + boost::shared_ptr getSender(const std::string& name) const; + boost::shared_ptr getReceiver(const std::string& name) const; + void closeReceiver(const std::string&); + void closeSender(const std::string&); + boost::shared_ptr nextReceiver(qpid::messaging::Duration timeout); + uint32_t getReceivable(); + uint32_t getUnsettledAcks(); + private: + friend class ConnectionContext; + typedef std::map > SenderMap; + typedef std::map > ReceiverMap; + typedef std::map 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/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.cpp b/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.cpp new file mode 100644 index 0000000..bf79771 --- /dev/null +++ b/qpid/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 c, boost::shared_ptr 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 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 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 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/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.h b/qpid/cpp/src/qpid/messaging/amqp/SessionHandle.h new file mode 100644 index 0000000..5e843aa --- /dev/null +++ b/qpid/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 +#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, boost::shared_ptr); + 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 connection; + boost::shared_ptr session; +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_SESSIONIMPL_H*/ diff --git a/qpid/cpp/src/qpid/messaging/amqp/SslTransport.cpp b/qpid/cpp/src/qpid/messaging/amqp/SslTransport.cpp new file mode 100644 index 0000000..a02b0d7 --- /dev/null +++ b/qpid/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 +#include + +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 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::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/qpid/cpp/src/qpid/messaging/amqp/SslTransport.h b/qpid/cpp/src/qpid/messaging/amqp/SslTransport.h new file mode 100644 index 0000000..e83c334 --- /dev/null +++ b/qpid/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 + +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 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 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/qpid/cpp/src/qpid/messaging/amqp/TcpTransport.cpp b/qpid/cpp/src/qpid/messaging/amqp/TcpTransport.cpp new file mode 100644 index 0000000..5105c9f --- /dev/null +++ b/qpid/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 +#include + +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 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::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/qpid/cpp/src/qpid/messaging/amqp/TcpTransport.h b/qpid/cpp/src/qpid/messaging/amqp/TcpTransport.h new file mode 100644 index 0000000..142b36b --- /dev/null +++ b/qpid/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 + +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); + + 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 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/qpid/cpp/src/qpid/messaging/amqp/Transport.cpp b/qpid/cpp/src/qpid/messaging/amqp/Transport.cpp new file mode 100644 index 0000000..4c2d212 --- /dev/null +++ b/qpid/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 + +namespace qpid { +namespace messaging { +namespace amqp { +namespace { +typedef std::map Registry; + +Registry& theRegistry() +{ + static Registry factories; + return factories; +} +} + +Transport* Transport::create(const std::string& name, TransportContext& context, boost::shared_ptr 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/qpid/cpp/src/qpid/messaging/amqp/Transport.h b/qpid/cpp/src/qpid/messaging/amqp/Transport.h new file mode 100644 index 0000000..ee021f6 --- /dev/null +++ b/qpid/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 + +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); + static Transport* create(const std::string& name, TransportContext&, boost::shared_ptr); + static void add(const std::string& name, Factory* factory); +}; +}}} // namespace qpid::messaging::amqp + +#endif /*!QPID_MESSAGING_AMQP_TRANSPORT_H*/ --------------020300090309090500060705 Content-Type: text/plain; charset=us-ascii --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscribe@qpid.apache.org For additional commands, e-mail: users-help@qpid.apache.org --------------020300090309090500060705--