Initial commit with the implementation of all methods apart from the publish and consume methods

This commit is contained in:
Emiel Bruijntjes 2014-01-04 03:45:04 -08:00
parent aa675084f1
commit ea4f82ac8f
137 changed files with 16305 additions and 0 deletions

18
Makefile Normal file
View File

@ -0,0 +1,18 @@
PREFIX = /usr
INCLUDE_DIR = ${PREFIX}/include
LIBRARY_DIR = ${PREFIX}/lib
COPERNICA_INCLUDE_DIR = ${INCLUDE_DIR}/copernica
all:
$(MAKE) -C src all
clean:
$(MAKE) -C src clean
install:
mkdir -p ${COPERNICA_INCLUDE_DIR}/amqp
mkdir -p ${LIBRARY_DIR}
cp -f amqp.h ${COPERNICA_INCLUDE_DIR}
cp -f include/*.h ${COPERNICA_INCLUDE_DIR}/amqp
cp -f src/libcopernica_amqp.so ${LIBRARY_DIR}
cp -f src/libcopernica_amqp.a ${LIBRARY_DIR}

1
amqp Symbolic link
View File

@ -0,0 +1 @@
include

68
amqp.h Normal file
View File

@ -0,0 +1,68 @@
/**
* AMQP.h
*
* Starting point for all includes of the Copernica AMQP library
*
* @documentation public
*/
// base C++ include files
#include <vector>
#include <cstring>
#include <sstream>
#include <string>
#include <limits>
#include <memory>
#include <type_traits>
#include <cstdlib>
#include <map>
// base C include files
#include <stdint.h>
#include <math.h>
#include <stddef.h>
#include <endian.h>
#include <string.h>
// other libraries
#include <copernica/event.h>
#include <copernica/network.h>
// forward declarations
#include <copernica/amqp/classes.h>
// utility classes
#include <copernica/amqp/receivedframe.h>
#include <copernica/amqp/outbuffer.h>
#include <copernica/amqp/watchable.h>
#include <copernica/amqp/monitor.h>
// amqp types
#include <copernica/amqp/field.h>
#include <copernica/amqp/numericfield.h>
#include <copernica/amqp/decimalfield.h>
#include <copernica/amqp/stringfield.h>
#include <copernica/amqp/booleanset.h>
#include <copernica/amqp/fieldproxy.h>
#include <copernica/amqp/table.h>
#include <copernica/amqp/array.h>
// envelope for publishing and consuming
#include <copernica/amqp/envelopefield.h>
#include <copernica/amqp/envelope.h>
// mid level includes
#include <copernica/amqp/exchangetype.h>
#include <copernica/amqp/flags.h>
#include <copernica/amqp/channelhandler.h>
#include <copernica/amqp/channelimpl.h>
#include <copernica/amqp/channel.h>
#include <copernica/amqp/login.h>
#include <copernica/amqp/connectionhandler.h>
#include <copernica/amqp/connectionimpl.h>
#include <copernica/amqp/connection.h>

128
include/array.h Normal file
View File

@ -0,0 +1,128 @@
/**
* AMQP field array
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* AMQP field array
*/
class Array : public Field
{
private:
/**
* Definition of an array as a vector
* @typedef
*/
typedef std::vector<std::shared_ptr<Field>> FieldArray;
/**
* The actual fields
* @var FieldArray
*/
FieldArray _fields;
public:
/**
* Constructor to construct an array from a received frame
*
* @param frame received frame
*/
Array(ReceivedFrame &frame);
/**
* Copy constructor
* @param array
*/
Array(const Array &array);
/**
* Constructor for an empty Array
*/
Array() {}
/**
* Destructor
*/
virtual ~Array() {}
/**
* Create a new instance of this object
* @return Field*
*/
virtual Field *clone() const override
{
return new Array(*this);
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
* @return size_t
*/
virtual size_t size() const override;
/**
* Set a field
*
* @param index field index
* @param value field value
* @return Array
*/
Array set(uint8_t index, const Field &value)
{
// copy to a new pointer and store it
_fields[index] = std::shared_ptr<Field>(value.clone());
// allow chaining
return *this;
}
/**
* Get a field
*
* If the field does not exist, an empty string is returned
*
* @param index field index
* @return Field
*/
const Field &get(uint8_t index);
/**
* Get a field
*
* @param index field index
* @return ArrayFieldProxy
*/
ArrayFieldProxy operator[](uint8_t index)
{
return ArrayFieldProxy(this, index);
}
/**
* Write encoded payload to the given buffer.
* @param buffer
*/
virtual void fill(OutBuffer& buffer) const override;
/**
* Get the type ID that is used to identify this type of
* field in a field table
* @return char
*/
virtual char typeID() const override
{
return 'A';
}
};
/**
* end namespace
*/
}

163
include/booleanset.h Normal file
View File

@ -0,0 +1,163 @@
/**
* BooleanSet.h
*
* AMQP can store eight booleans in a single byte. This class
* is a utility class for setting and getting the eight
* booleans
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class BooleanSet : public Field
{
private:
/**
* The actual byte
* @var uint8_t
*/
uint8_t _byte;
public:
/**
* Constructor
* @param first the first bool to set
* @param second the second bool to set
* @param third the third bool to set
* @param fourth the fourth bool to set
* @param fifth the fifth bool to set
* @param sixth the sixth bool to set
* @param seventh the seventh bool to set
* @param eigth the eigth bool to set
*/
BooleanSet(bool first = false, bool second = false, bool third = false, bool fourth = false, bool fifth = false, bool sixth = false, bool seventh = false, bool eigth = false)
{
_byte = 0;
set(0, first);
set(1, second);
set(2, third);
set(3, fourth);
set(4, fifth);
set(5, sixth);
set(6, seventh);
set(7, eigth);
}
/**
* Constructor based on incoming data
* @param frame
*/
BooleanSet(ReceivedFrame &frame)
{
_byte = frame.nextUint8();
}
/**
* Copy constructor
* @param that
*/
BooleanSet(const BooleanSet &that)
{
_byte = that._byte;
}
/**
* Destructor
*/
virtual ~BooleanSet() {}
/**
* Extending from field forces us to implement a clone function.
* @return shared_ptr
*/
virtual Field *clone() const override
{
return new BooleanSet(*this);
}
/**
* Get one of the booleans
* @param index from 0 to 7, where 0 is rightmost bit
* @return bool
*/
bool get(uint32_t index) const
{
// bigger than seven is not an option
if (index > 7) return false;
// magic bit manipulation...
return (bool)((1 << index) & _byte);
}
/**
* Set a boolean in the set
* @param index
* @param value
*/
void set(uint32_t index, bool value)
{
// index must be valid
if (index > 7) return;
// are we setting or unsetting
if (value)
{
// magic bit manipulation...
_byte |= (1 << index);
}
else
{
// magic bit manipulation...
_byte &= ~(1 << index);
}
}
/**
* Fill the buffer
* @param buffer
*/
virtual void fill(OutBuffer& buffer) const override
{
buffer.add(_byte);
}
/**
* Get the byte value
* @return value
*/
uint8_t value()
{
return _byte;
}
/**
* Type ID
* @return char
*/
virtual char typeID() const override
{
return 't';
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
*/
virtual size_t size() const override
{
// booleanset takes up a single byte.
return 1;
}
};
/**
* End of namespace
*/
}

274
include/channel.h Normal file
View File

@ -0,0 +1,274 @@
/**
* Class describing a (mid-level) AMQP channel implementation
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class Channel
{
private:
/**
* The implementation for the channel
* @var ChannelImpl
*/
ChannelImpl _implementation;
public:
/**
* Construct a channel object
* @param connection
* @param handler
*/
Channel(Connection *connection, ChannelHandler *handler) : _implementation(this, connection, handler) {}
/**
* Destructor
*/
virtual ~Channel() {}
/**
* Pause deliveries on a channel
*
* This will stop all incoming messages
*
* This method returns true if the request to pause has been sent to the
* broker. This does not necessarily mean that the channel is already
* paused.
*
* @return bool
*/
bool pause()
{
return _implementation.pause();
}
/**
* Resume a paused channel
*
* @return bool
*/
bool resume()
{
return _implementation.resume();
}
/**
* Is the channel connected?
* @return bool
*/
bool connected()
{
return _implementation.connected();
}
/**
* Start a transaction
* @return bool
*/
bool startTransaction()
{
return _implementation.startTransaction();
}
/**
* Commit the current transaction
* @return bool
*/
bool commitTransaction()
{
return _implementation.commitTransaction();
}
/**
* Rollback the current transaction
* @return bool
*/
bool rollbackTransaction()
{
return _implementation.rollbackTransaction();
}
/**
* Declare an exchange
*
* If an empty name is supplied, a name will be assigned by the server.
*
* The following flags can be used for the exchange:
*
* - amqp_durable exchange survives a broker restart
* - amqp_autodelete exchange is automatically removed when all connected queues are removed
* - amqp_passive only check if the exchange exist
*
* @param name name of the exchange
* @param type exchange type
* @param flags exchange flags
* @param arguments additional arguments
* @return bool
*/
bool declareExchange(const std::string &name, ExchangeType type, int flags, const Table &arguments) { return _implementation.declareExchange(name, type, flags, arguments); }
bool declareExchange(const std::string &name, ExchangeType type, const Table &arguments) { return _implementation.declareExchange(name, type, 0, arguments); }
bool declareExchange(const std::string &name, ExchangeType type = fanout, int flags = 0) { return _implementation.declareExchange(name, type, flags, Table()); }
bool declareExchange(ExchangeType type, int flags, const Table &arguments) { return _implementation.declareExchange(std::string(), type, flags, arguments); }
bool declareExchange(ExchangeType type, const Table &arguments) { return _implementation.declareExchange(std::string(), type, 0, arguments); }
bool declareExchange(ExchangeType type = fanout, int flags = 0) { return _implementation.declareExchange(std::string(), type, flags, Table()); }
/**
* Remove an exchange
*
* The following flags can be used for the exchange:
*
* - amqp_ifunused only delete if no queues are connected
* @param name name of the exchange to remove
* @param flags optional flags
* @return bool
*/
bool removeExchange(const std::string &name, int flags = 0) { return _implementation.removeExchange(name, flags); }
/**
* Bind two exchanges to each other
*
* The following flags can be used for the exchange
*
* - amqp_nowait do not wait on response
*
* @param source the source exchange
* @param target the target exchange
* @param routingkey the routing key
* @param flags optional flags
* @param arguments additional bind arguments
* @return bool
*/
bool bindExchange(const std::string &source, const std::string &target, const std::string &routingkey, int flags, const Table &arguments) { return _implementation.bindExchange(source, target, routingkey, flags, arguments); }
bool bindExchange(const std::string &source, const std::string &target, const std::string &routingkey, const Table &arguments) { return _implementation.bindExchange(source, target, routingkey, 0, arguments); }
bool bindExchange(const std::string &source, const std::string &target, const std::string &routingkey, int flags = 0) { return _implementation.bindExchange(source, target, routingkey, flags, Table()); }
/**
* Unbind two exchanges from one another
*
* The following flags can be used for the exchange
*
* - amqp_nowait do not wait on response
*
* @param target the target exchange
* @param source the source exchange
* @param routingkey the routing key
* @param flags optional flags
* @param arguments additional unbind arguments
* @return bool
*/
bool unbindExchange(const std::string &target, const std::string &source, const std::string &routingkey, int flags, const Table &arguments) { return _implementation.unbindExchange(target, source, routingkey, flags, arguments); }
bool unbindExchange(const std::string &target, const std::string &source, const std::string &routingkey, const Table &arguments) { return _implementation.unbindExchange(target, source, routingkey, 0, arguments); }
bool unbindExchange(const std::string &target, const std::string &source, const std::string &routingkey, int flags = 0) { return _implementation.unbindExchange(target, source, routingkey, flags, Table()); }
/**
* Declare a queue
*
* If you do not supply a name, a name will be assigned by the server.
*
* The flags can be a combination of the following values:
*
* - amqp_durable queue survives a broker restart
* - amqp_autodelete queue is automatically removed when all connected consumers are gone
* - amqp_passive only check if the queue exist
* - amqp_exclusive the queue only exists for this connection, and is automatically removed when connection is gone
*
* @param name name of the queue
* @param flags combination of flags
* @param arguments optional arguments
*/
bool declareQueue(const std::string &name, int flags, const Table &arguments) { return _implementation.declareQueue(name, flags, arguments); }
bool declareQueue(const std::string &name, const Table &arguments) { return _implementation.declareQueue(name, 0, arguments); }
bool declareQueue(const std::string &name, int flags = 0) { return _implementation.declareQueue(name, flags, Table()); }
bool declareQueue(int flags, const Table &arguments) { return _implementation.declareQueue(std::string(), flags, arguments); }
bool declareQueue(const Table &arguments) { return _implementation.declareQueue(std::string(), 0, arguments); }
bool declareQueue(int flags = 0) { return _implementation.declareQueue(std::string(), flags, Table()); }
/**
* Bind a queue to an exchange
*
* The following flags can be used for the exchange
*
* - amqp_noWait do not wait on response
*
* @param queue the target queue
* @param exchange the source exchange
* @param routingkey the routing key
* @param flags additional flags
* @param arguments additional bind arguments
* @return bool
*/
bool bindQueue(const std::string &queue, const std::string &exchange, const std::string &routingkey, int flags, const Table &arguments) { return _implementation.bindQueue(exchange, queue, routingkey, flags, arguments); }
bool bindQueue(const std::string &queue, const std::string &exchange, const std::string &routingkey, const Table &arguments) { return _implementation.bindQueue(exchange, queue, routingkey, 0, arguments); }
bool bindQueue(const std::string &queue, const std::string &exchange, const std::string &routingkey, int flags = 0) { return _implementation.bindQueue(exchange, queue, routingkey, flags, Table()); }
/**
* Unbind a queue from an exchange
* @param queue the target queue
* @param exchange the source exchange
* @param routingkey the routing key
* @param arguments additional bind arguments
* @return bool
*/
bool unbindQueue(const std::string &queue, const std::string &exchange, const std::string &routingkey, const Table &arguments) { return _implementation.unbindQueue(exchange, queue, routingkey, arguments); }
bool unbindQueue(const std::string &queue, const std::string &exchange, const std::string &routingkey) { return _implementation.unbindQueue(exchange, queue, routingkey, Table()); }
/**
* Purge a queue
*
* The following flags can be used for the exchange
*
* - amqp_noWait do not wait on response
*
* @param name name of the queue
* @param flags additional flags
* @return bool
*/
bool purgeQueue(const std::string &name, int flags = 0){ return _implementation.purgeQueue(name, flags); }
/**
* Remove a queue
*
* The following flags can be used for the exchange:
*
* - amqp_ifunused only delete if no consumers are connected
* - amqp_ifempty only delete if the queue is empty
*
* @param name name of the queue to remove
* @param flags optional flags
* @return bool
*/
bool removeQueue(const std::string &name, int flags = 0) { return _implementation.removeQueue(name, flags); }
/**
* Close the current channel
* @return bool
*/
bool close()
{
return _implementation.close();
}
/**
* Get the channel we're working on
* @return uint16_t
*/
const uint16_t id() const
{
return _implementation.id();
}
};
/**
* end namespace
*/
}

138
include/channelhandler.h Normal file
View File

@ -0,0 +1,138 @@
/**
* ChannelHandler.h
*
* Interface that should be implemented by a user of the AMQP library,
* and that is passed to the Connection::createChannel() method.
*
* This interface contains a number of methods that are called when
* the channel changes state.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ChannelHandler
{
public:
/**
* Method that is called when the channel was succesfully created.
* Only after the channel was created, you can use it for subsequent messages over it
* @param channel
*/
virtual void onReady(Channel *channel) {}
/**
* An error has occured on the channel
* @param channel
* @param message
*/
virtual void onChannelError(Channel *channel, const std::string &message) {}
/**
* Method that is called when the channel was paused
* @param channel
*/
virtual void onPaused(Channel *channel) {}
/**
* Method that is called when the channel was resumed
* @param channel
*/
virtual void onResumed(Channel *channel) {}
/**
* Method that is called when a channel is closed
* @param channel
*/
virtual void onClosed(Channel *channel) {}
/**
* Method that is called when a transaction was started
* @param channel
*/
virtual void onTransactionStarted(Channel *channel) {}
/**
* Method that is called when a transaction was committed
* @param channel
*/
virtual void onTransactionCommitted(Channel *channel) {}
/**
* Method that is called when a transaction was rolled back
* @param channel
*/
virtual void onTransactionRolledBack(Channel *channel) {}
/**
* Method that is called when an exchange is bound
* @param channel
*/
virtual void onExchangeBound(Channel *channel) {}
/**
* Method that is called when an exchange is unbound
* @param channel
*/
virtual void onExchangeUnbound(Channel *channel) {}
/**
* Method that is called when an exchange is deleted
* @param channel
*/
virtual void onExchangeDeleted(Channel *channel) {}
/**
* Mehod that is called when an exchange is declared
* @param channel
*/
virtual void onExchangeDeclared(Channel *channel) {}
/**
* Method that is called when a queue is declared
* @param channel
* @param name name of the queue
* @param messageCount number of messages in queue
* @param consumerCount number of active consumers
*/
virtual void onQueueDeclared(Channel *channel, const std::string &name, uint32_t messageCount, uint32_t consumerCount) {}
/**
* Method that is called when a queue is bound
* @param channel
* @param
*/
virtual void onQueueBound(Channel *channel) {}
/**
* Method that is called when a queue is deleted
* @param channel
* @param messageCount number of messages deleted along with the queue
*/
virtual void onQueueDeleted(Channel *channel, uint32_t messageCount) {}
/**
* Method that is called when a queue is unbound
* @param channel
*/
virtual void onQueueUnbound(Channel *channel) {}
/**
* Method that is called when a queue is purged
* @param messageCount number of message purged
*/
virtual void onQueuePurged(Channel *channel, uint32_t messageCount) {}
};
/**
* End of namespace
*/
}

375
include/channelimpl.h Normal file
View File

@ -0,0 +1,375 @@
/**
* ChannelImpl.h
*
* Extended channel object that is used internally by the library, but
* that has a private constructor so that it can not be used from outside
* the AMQP library
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ChannelImpl
{
private:
/**
* The actual channel object
* @var Channel
*/
Channel *_parent;
/**
* Pointer to the connection
* @var Connection
*/
Connection *_connection;
/**
* The handler that is notified about events
* @var MyChannelHandler
*/
ChannelHandler *_handler;
/**
* The channel number
* @var uint16_t
*/
uint16_t _id;
/**
* State of the channel object
* @var enum
*/
enum {
state_connected,
state_closed
} _state = state_connected;
/**
* Is a transaction now active?
* @var bool
*/
bool _transaction = false;
/**
* Construct a channel object
*
* Note that the constructor is private, and that the Channel class is
* a friend. By doing this we ensure that nobody can instantiate this
* object, and that it can thus only be used inside the library.
*
* @param parent the public channel object
* @param connection pointer to the connection
* @param handler handler that is notified on events
*/
ChannelImpl(Channel *parent, Connection *connection, ChannelHandler *handler = nullptr);
public:
/**
* Destructor
*/
virtual ~ChannelImpl();
/**
* Pause deliveries on a channel
*
* This will stop all incoming messages
*
* This method returns true if the request to pause has been sent to the
* broker. This does not necessarily mean that the channel is already
* paused.
*
* @return bool
*/
bool pause();
/**
* Resume a paused channel
*
* @return bool
*/
bool resume();
/**
* Is the channel connected?
* @return bool
*/
bool connected()
{
return _state == state_connected;
}
/**
* Start a transaction
* @return bool
*/
bool startTransaction();
/**
* Commit the current transaction
* @return bool
*/
bool commitTransaction();
/**
* Rollback the current transaction
* @return bool
*/
bool rollbackTransaction();
/**
* declare an exchange
* @param name name of the exchange to declare
* @param type type of exchange
* @param flags additional settings for the exchange
* @param arguments additional arguments
* @return bool
*/
bool declareExchange(const std::string &name, ExchangeType type, int flags, const Table &arguments);
/**
* bind two exchanges
* @param source exchange which binds to target
* @param target exchange to bind to
* @param routingKey routing key
* @param glags additional flags
* @param arguments additional arguments for binding
* @return bool
*/
bool bindExchange(const std::string &source, const std::string &target, const std::string &routingkey, int flags, const Table &arguments);
/**
* unbind two exchanges
* @param source the source exchange
* @param target the target exchange
* @param routingkey the routing key
* @param flags optional flags
* @param arguments additional unbind arguments
* @return bool
*/
bool unbindExchange(const std::string &source, const std::string &target, const std::string &routingkey, int flags, const Table &arguments);
/**
* remove an exchange
* @param name name of the exchange to remove
* @param flags additional settings for deleting the exchange
* @return bool
*/
bool removeExchange(const std::string &name, int flags);
/**
* declare a queue
* @param name queue name
* @param flags additional settings for the queue
* @param arguments additional arguments
* @return bool
*/
bool declareQueue(const std::string &name, int flags, const Table &arguments);
/**
* Bind a queue to an exchange
* @param exchangeName name of the exchange to bind to
* @param queueName name of the queue
* @param routingkey routingkey
* @param flags additional flags
* @param arguments additional arguments
* @return bool
*/
bool bindQueue(const std::string &exchangeName, const std::string &queueName, const std::string &routingkey, int flags, const Table &arguments);
/**
* Unbind a queue from an exchange
* @param exchange the source exchange
* @param queue the target queue
* @param routingkey the routing key
* @param arguments additional bind arguments
* @return bool
*/
bool unbindQueue(const std::string &exchangeName, const std::string &queueName, const std::string &routingkey, const Table &arguments);
/**
* Purge a queue
* @param queue queue to purge
* @param flags additional flags
* @return bool
*/
bool purgeQueue(const std::string &name, int flags);
/**
* Remove a queue
* @param queue queue to remove
* @param flags additional flags
* @return bool
*/
bool removeQueue(const std::string &name, int flags);
/**
* Close the current channel
* @return bool
*/
bool close();
/**
* Get the channel we're working on
* @return uint16_t
*/
const uint16_t id() const
{
return _id;
}
/**
* Send a frame over the channel
* @param frame frame to send
* @return size_t number of bytes sent
*/
size_t send(const Frame &frame);
/**
* Report to the handler that the channel is closed
*/
void reportClosed()
{
// change state
_state = state_closed;
// inform handler
if (_handler) _handler->onClosed(_parent);
}
/**
* Report to the handler that the channel is paused
*/
void reportPaused()
{
// inform handler
if (_handler) _handler->onPaused(_parent);
}
/**
* Report to the handler that the channel is resumed
*/
void reportResumed()
{
// inform handler
if (_handler) _handler->onResumed(_parent);
}
/**
* Report to the handler that the channel is opened
*/
void reportReady()
{
// inform handler
if (_handler) _handler->onReady(_parent);
}
/**
* Report an error message on a channel
* @param message
*/
void reportChannelError(const std::string &message)
{
// change state
_state = state_closed;
// inform handler
if (_handler) _handler->onChannelError(_parent, message);
}
/**
* Report that the exchange is succesfully declared
*/
void reportExchangeDeclared()
{
if(_handler) _handler->onExchangeDeclared(_parent);
}
/**
* Report that the exchange is succesfully deleted
*/
void reportExchangeDeleted()
{
if(_handler) _handler->onExchangeDeleted(_parent);
}
/**
* Report that the exchange is bound
*/
void reportExchangeBound()
{
if(_handler) _handler->onExchangeBound(_parent);
}
/**
* Report that the exchange is unbound
*/
void reportExchangeUnbound()
{
if(_handler) _handler->onExchangeUnbound(_parent);
}
/**
* Report that the queue was succesfully declared
* @param queueName name of the queue which was declared
* @param messagecount number of messages currently in the queue
* @param consumerCount number of active consumers in the queue
*/
void reportQueueDeclared(const std::string &queueName, uint32_t messageCount, uint32_t consumerCount)
{
if(_handler) _handler->onQueueDeclared(_parent, queueName, messageCount, consumerCount);
}
/**
* Report that a queue was succesfully bound
*/
void reportQueueBound()
{
if(_handler) _handler->onQueueBound(_parent);
}
/**
* Report that a queue was succesfully unbound
*/
void reportQueueUnbound()
{
if(_handler) _handler->onQueueUnbound(_parent);
}
/**
* Report that a queue was succesfully deleted
* @param messageCount number of messages left in queue, now deleted
*/
void reportQueueDeleted(uint32_t messageCount)
{
if(_handler) _handler->onQueueDeleted(_parent, messageCount);
}
/**
* Report that a queue was succesfully purged
* @param messageCount number of messages purged
*/
void reportQueuePurged(uint32_t messageCount)
{
if(_handler) _handler->onQueuePurged(_parent, messageCount);
}
/**
* The channel class is its friend, thus can it instantiate this object
*/
friend class Channel;
};
/**
* End of namespace
*/
}

33
include/classes.h Normal file
View File

@ -0,0 +1,33 @@
/**
* Classes.h
*
* List of all declared classes
*
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* All classes defined by this library
*/
class Array;
class Channel;
class Connection;
class ConnectionHandler;
class ConnectionImpl;
class Exchange;
class Frame;
class Login;
class Monitor;
class OutBuffer;
class ReceivedFrame;
class Table;
/**
* End of namespace
*/
}

99
include/connection.h Normal file
View File

@ -0,0 +1,99 @@
/**
* Class describing a mid-level Amqp connection
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class Connection
{
private:
/**
* The actual implementation
* @var ConnectionImpl
*/
ConnectionImpl _implementation;
public:
/**
* Construct an AMQP object based on full login data
*
* The first parameter is a handler object. This handler class is
* an interface that should be implemented by the caller.
*
* @param handler Connection handler
* @param login Login data
*/
Connection(ConnectionHandler *handler, const Login &login) : _implementation(this, handler, login) {}
/**
* Construct an AMQP object with default login data
* @param handler Connection handler
*/
Connection(ConnectionHandler *handler) : _implementation(this, handler) {}
/**
* Destructor
*/
virtual ~Connection() {}
/**
* Parse the buffer into a recognized frame
*
* Every time that data comes in on the connection, you should call this method to parse
* the incoming data, and let it handle by the AMQP library. This method returns the number
* of bytes that were processed.
*
* If not all bytes could be processed because it only contained a partial frame, you should
* call this same method later on when more data is available. The AMQP library does not do
* any buffering, so it is up to the caller to ensure that the old data is also passed in that
* later call.
*
* @param buffer buffer to decode
* @param size size of the buffer to decode
* @return number of bytes that were processed
*/
size_t parse(char *buffer, size_t size)
{
return _implementation.parse(buffer, size);
}
/**
* Close the connection
* This will close all channels
* @return bool
*/
bool close()
{
return _implementation.close();
}
/**
* Set the Quality of Service (QOS) of the entire connection
* @param prefetchSize maximum size (in octets) of messages to be prefetched
* @param prefetchCount maximum number of messages to prefetch
* @return bool whether the Qos frame is sent.
*/
bool setQos(uint32_t prefetchSize, uint16_t prefetchCount)
{
return _implementation.setQos(prefetchSize, prefetchCount);
}
/**
* Some classes have access to private properties
*/
friend class ChannelImpl;
};
/**
* End of namespace
*/
}

View File

@ -0,0 +1,63 @@
/**
* ConnectionHandler.h
*
* Interface that should be implemented by the caller of the library and
* that is passed to the AMQP connection. This interface contains all sorts
* of methods that are called when data needs to be sent, or when the
* AMQP connection ends up in a broken state.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ConnectionHandler
{
public:
/**
* Method that is called when data needs to be sent over the network
*
* Note that the AMQP library does no buffering by itself. This means
* that this method should always send out all data or do the buffering
* itself.
*
* @param connection The connection that created this output
* @param buffer Data to send
* @param size Size of the buffer
*/
virtual void onData(Connection *connection, const char *buffer, size_t size) = 0;
/**
* When the connection ends up in an error state this method is called.
* This happens when data comes in that does not match the AMQP protocol
*
* After this method is called, the connection no longer is in a valid
* state and can be used. In normal circumstances this method is not called.
*
* @todo do we need this method, or only in the ChannelHandler class?
*
* @param connection The connection that entered the error state
* @param message Error message
*/
virtual void onConnectionError(Connection *connection, const std::string &message) = 0;
/**
* Method that is called when the login attempt succeeded. After this method
* was called, the connection is ready to use
*
* @param connection The connection that can now be used
*/
virtual void onConnected(Connection *connection) = 0;
};
/**
* End of namespace
*/
}

289
include/connectionimpl.h Normal file
View File

@ -0,0 +1,289 @@
/**
* Connection implementation
*
* This is the implementation of the connection - a class that can only be
* constructed by the connection class itselves and that has all sorts of
* methods that are only useful inside the library
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ConnectionImpl : public Watchable
{
protected:
/**
* The parent connection object
* @var Connection
*/
Connection *_parent;
/**
* The connection handler
* @var ConnectionHandler
*/
ConnectionHandler *_handler;
/**
* State of the connection
* The current state is the last frame sent to the server
* @var enum
*/
enum {
state_invalid, // object is in an invalid state
state_protocol, // protocol headers are being passed
state_handshake, // busy with the handshake to open the connection
state_connected, // connection is set up and ready for communication
state_closed // connection is closed
} _state = state_protocol;
/**
* All channels that are active
* @var map
*/
std::map<uint16_t, ChannelImpl*> _channels;
/**
* The last unused channel ID
* @var uint16_t
*/
uint16_t _nextFreeChannel = 1;
/**
* Max number of channels (0 for unlimited)
* @var uint16_t
*/
uint16_t _maxChannels = 0;
/**
* Max frame size
* @var uint32_t
*/
uint32_t _maxFrame = 10000;
/**
* The address of the server (vhost, login, password)
* @var Address
*/
Login _login;
/**
* Initialize the connection
*/
void initialize();
private:
/**
* Construct an AMQP object based on full login data
*
* The first parameter is a handler object. This handler class is
* an interface that should be implemented by the caller.
*
* Note that the constructor is private to ensure that nobody can construct
* this class, only the real Connection class via a friend construct
*
* @param parent Parent connection object
* @param handler Connection handler
* @param login Login data
*/
ConnectionImpl(Connection *parent, ConnectionHandler *handler, const Login &login) :
_parent(parent), _handler(handler), _login(login)
{
// initialize the connection
initialize();
}
/**
* Construct an AMQP object with default login data
*
* Note that the constructor is private to ensure that nobody can construct
* this class, only the real Connection class via a friend construct
*
* @param parent Parent connection object
* @param handler Connection handler
*/
ConnectionImpl(Connection *parent, ConnectionHandler *handler) :
_parent(parent), _handler(handler)
{
// initialize the connection
initialize();
}
public:
/**
* Destructor
*/
virtual ~ConnectionImpl();
/**
* What is the state of the connection - is the protocol handshake completed?
* @return bool
*/
bool protocolOk()
{
// must be busy doing the connection handshake, or already connected
return _state == state_handshake || _state == state_connected;
}
/**
* Mark the protocol as being ok
*/
void setProtocolOk()
{
// move on to handshake state
if (_state == state_protocol) _state = state_handshake;
}
/**
* Are we fully connected?
* @return bool
*/
bool connected()
{
// state must be connected
return _state == state_connected;
}
/**
* Mark the connection as connected
*/
void setConnected()
{
// store connected state
_state = state_connected;
// inform handler
_handler->onConnected(_parent);
}
/**
* Retrieve the login data
* @return Login
*/
Login &login()
{
return _login;
}
/**
* Store the max number of channels and max number of frames
* @param channels max number of channels
* @param size max frame size
*/
void setCapacity(uint16_t channels, uint32_t size)
{
_maxChannels = channels;
_maxFrame = size;
}
/**
* Add a channel to the connection, and return the channel ID that it
* is allowed to use, or 0 when no more ID's are available
* @param channel
* @return uint16_t
*/
uint16_t add(ChannelImpl *channel);
/**
* Remove a channel
* @param channel
*/
void remove(ChannelImpl *channel);
/**
* Parse the buffer into a recognized frame
*
* Every time that data comes in on the connection, you should call this method to parse
* the incoming data, and let it handle by the AMQP library. This method returns the number
* of bytes that were processed.
*
* If not all bytes could be processed because it only contained a partial frame, you should
* call this same method later on when more data is available. The AMQP library does not do
* any buffering, so it is up to the caller to ensure that the old data is also passed in that
* later call.
*
* @param buffer buffer to decode
* @param size size of the buffer to decode
* @return number of bytes that were processed
*/
size_t parse(char *buffer, size_t size);
/**
* Close the connection
* This will close all channels
* @return bool
*/
bool close();
/**
* Send a frame over the connection
*
* This is an internal method that you normally do not have to call yourself
*
* @param frame the frame to send
* @return number of bytes sent
*/
size_t send(const Frame &frame);
/**
* Get a channel by its identifier
*
* This method only works if you had already created the channel before.
* This is an internal method that you will not need if you cache the channel
* object.
*
* @param number channel identifier
* @return channel the channel object, or nullptr if not yet created
*/
ChannelImpl *channel(int number)
{
auto iter = _channels.find(number);
return iter == _channels.end() ? nullptr : iter->second;
}
/**
* Report an error message
* @param message
*/
void reportConnectionError(const std::string &message)
{
// close everything
close();
// set connection state to closed
_state = state_closed;
// inform handler
_handler->onConnectionError(_parent, message);
// @Todo: notify all channels of closed connection
}
/**
* Set the Quality of Service (QOS) of the entire connection
* @param prefetchSize maximum size (in octets) of messages to be prefetched
* @param prefetchCount maximum number of messages to prefetch
* @return bool whether the Qos frame is sent.
*/
bool setQos(uint32_t prefetchSize, uint16_t prefetchCount);
/**
* The actual connection is a friend and can construct this class
*/
friend class Connection;
};
/**
* End of namespace
*/
}

208
include/decimalfield.h Normal file
View File

@ -0,0 +1,208 @@
/**
* Decimal field type for AMQP
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class DecimalField : public Field
{
/**
* To preserve precision the decision is made to work with the places and number.
* These values are sent in the framedata, so no precision will be lost.
* Other options, such as floats, doubles, Decimal32 etc result in loss of precision
* and this is something which is not acceptable.
*
* Only (in)equality and assignment operators are implemented since the decimalfield
* is not supposed to be altered.
* e.q. ==, != and =
*
* When requesting the value of this object there are 3 choices;
* float, double or DecimalField
* e.g. valueFloat(), valueDouble() and value()
*/
private:
/**
* The number of places, which means the number of decimals
* e.g. number = 1234, places = 2, true value is 12.34
*/
uint8_t _places;
/**
* The number without the decimals
*/
uint32_t _number;
protected:
/**
* Write encoded payload to the given buffer.
*/
virtual void fill(OutBuffer& buffer) const override
{
// encode fields
buffer.add(_places);
buffer.add(_number);
}
public:
/**
* Construct decimal field
*
* @param places the number of places
* @param number the integer number
*/
DecimalField(uint8_t places = 0, uint32_t number = 0) :
_places(places),
_number(number)
{}
/**
* Construct based on incoming data
* @param frame
*/
DecimalField(ReceivedFrame &frame)
{
_places = frame.nextUint8();
_number = frame.nextUint32();
}
/**
* Destructor
*/
virtual ~DecimalField() {}
/**
* Create a new identical instance of this object
* @return Field*
*/
virtual Field *clone() const override
{
return new DecimalField(_places, _number);
}
/**
* Assign a new value
*
* @param value new value for field
* @return DecimalField
*/
DecimalField& operator=(const DecimalField& value)
{
// if it's the same object, skip assignment and just return *this
if (this == &value) return *this;
// not the same object, copy values to this object.
_places = value._places;
_number = value._number;
// allow chaining
return *this;
}
/**
* Casts decimalfield to double
* e.g. "double x = decimalfield" will work
*
* @return double value of decimalfield in double format
*/
operator double() const
{
return _number / pow(10, _places);
}
/**
* Casts decimalfield to float
* e.g. "float x = decimalfield" will work
*
* @return float value of decimalfield in float format
*/
operator float() const
{
return _number / pow(10, _places);
}
/**
* Check for equality between this and another DecimalField
*
* @param value value to be checked for equality
* @return boolean whether values are equal
*/
bool operator==(const DecimalField& value) const
{
// check if everything is the same
// precision is taken into account, e.q. 1.0 != 1.00
// meaning number:10, places:1 is not equal to number:100, places:2
return _number == value.number() && _places == value.places();
}
/**
* Check for inequality between this and another DecimalField
*
* @param value value to be checked for inequality
* @return boolean whether values are inequal
*/
bool operator!=(const DecimalField& value) const
{
return !(*this == value);
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
*/
virtual size_t size() const override
{
// the sum of all fields
return 5;
}
/**
* Get the number of places
* @return uint8_t
*/
uint8_t places() const
{
return _places;
}
/**
* Get the number without decimals
* @return uint32_t
*/
uint32_t number() const
{
return _number;
}
/**
* Return the DecimalField
* To preserve precision DecimalField is returned, containing the number and places.
* @return return DecimalField
*/
DecimalField value() const
{
return *this;
}
/**
* Get the type ID that is used to identify this type of
* field in a field table
*/
virtual char typeID() const override
{
return 'D';
}
};
/**
* end namespace
*/
}

203
include/entityimpl.h Normal file
View File

@ -0,0 +1,203 @@
/**
* EntityImpl.h
*
* Common base class for exchanges and queues.
*
* @copyright 2014 Copernica BV
*/
/**
* Namespace
*/
namespace AMQP {
/**
* Class definition
*/
class EntityImpl
{
protected:
/**
* The channel on which we communicate
* @var Channel
*/
Channel *_channel;
/**
* Name of the queue/exchange
* @var string
*/
std::string _name;
/**
* Is this a durable queue/exchange?
* A durable queue/exchange survives a broker restart
* @var bool
*/
bool _durable = false;
/**
* Is this a passive queue/exchange?
* If set, only check if the queue/exchange exists without actually creating it
* @var bool
*/
bool _passive = false;
/**
* Is this an auto-delete queue/exchange?
* If set, the entity is removed when it is no longer used (for queues
* when all consumers are gone, for exchanges when all queues are gone)
* @var bool
*/
bool _autoDelete = false;
/**
* Additional arguments
* @var Table
*/
Table _arguments;
/**
* Constructor is protected and can only be accessed by derived classes
* @param channel
*/
EntityImpl(Channel *channel) : _channel(channel) {}
public:
/**
* Destructor
*/
virtual ~EntityImpl();
/**
* Retrieve the name
* @return string
*/
std::string &name()
{
return _name;
}
/**
* Change the name
* You must declare the entity before this has effect
* @param string
*/
void setName(const std::string &name)
{
_name = name;
}
/**
* Is the queue or exchange durable
* The entity survives a broker restart if it is durable
* @return bool
*/
bool durable()
{
return _durable;
}
/**
* Mark the object as durable
* @param bool
*/
void setDurable(bool durable)
{
_durable = durable;
}
/**
* Is the passive bit set
* If set, the declare method only checks if the queue/exchange exists without actually creating it
* @return bool
*/
bool passive()
{
return _passive;
}
/**
* Change the passive bit
* @param bool
*/
void setPassive(bool passive)
{
_passive = passive;
}
/**
* Is the auto-delete property set?
* The entity is removed when the consumers and/or queues are unlinked from it
* @return bool
*/
bool autoDelete()
{
return _autoDelete;
}
/**
* Update the auto-delete bit
* @param bool
*/
void setAutoDelete(bool autoDelete)
{
_autoDelete = autoDelete;
}
/**
* Set a custom argument
* @param name Name of the argument
* @param value Value of the argument
*/
void setArgument(const std::string &name, const std::string &value)
{
_arguments[name] = value;
}
/**
* Retrieve a argument
* @param name Name of the argument
* @return The value of the argument
*/
std::string &argument(const std::string &name)
{
return _arguments[name];
}
/**
* Bind to a different exchange
* @param exchange The exchange to bind to
* @param key The routing key
* @return bool
*/
virtual bool bind(const std::string &exchange, const std::string &key) = 0;
/**
* Unbind from an exchange
* @param exchange Exchange to unbind from
* @param key The routing key
* @return bool
*/
virtual bool unbind(const std::string &exchange, const std::string &key) = 0;
/**
* Declare the queue/exchange
* @return bool
*/
virtual bool declare() = 0;
/**
* Remove the queue/exchange
* @return bool
*/
virtual bool remove() = 0;
};
/**
* End of namespace
*/
}

245
include/envelope.h Normal file
View File

@ -0,0 +1,245 @@
/**
* Envelope.h
*
* When you send or receive a message to the rabbitMQ server, it is encapsulated
* in an envelope that contains additional meta information as well.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class Envelope
{
private:
/**
* Pointer to the body data (the memory buffer is not managed by the AMQP
* library!)
* @var const char *
*/
const char *_data;
/**
* Size of the data
* @var size_t
*/
size_t _size;
/**
* The content type
* @var EnvelopeField
*/
EnvelopeField<std::string> _contentType;
/**
* The content encoding
* @var EnvelopeField
*/
EnvelopeField<std::string> _contentEncoding;
/**
* The priority
* @var EnvelopeField
*/
EnvelopeField<uint8_t> _priority;
/**
* The delivery mode (1=non-persistent, 2=persistent)
* @var EnvelopeField
*/
EnvelopeField<uint8_t> _deliveryMode;
/**
* The correlation ID
* @var EnvelopeField
*/
EnvelopeField<std::string> _correlationID;
/**
* Reply-to field
* @var EnvelopeField
*/
EnvelopeField<std::string> _replyTo;
/**
* Expiration value
* @var EnvelopeField
*/
EnvelopeField<std::string> _expiration;
/**
* The message id
* @var EnvelopeField
*/
EnvelopeField<std::string> _messageID;
/**
* Timestamp
* @var EnvelopeField
*/
EnvelopeField<uint64_t> _timestamp;
/**
* The type name
* @var EnvelopeField
*/
EnvelopeField<std::string> _typeName;
/**
* The user ID
* @var EnvelopeField
*/
EnvelopeField<std::string> _userID;
/**
* The application ID
* @var EnvelopeField
*/
EnvelopeField<std::string> _appID;
/**
* Additional custom headers
* @var EnvelopeField
*/
EnvelopeField<Table> _headers;
public:
/**
* Constructor
*
* The data buffer that you pass to this constructor must be valid during
* the lifetime of the Envelope object.
*
* @param data
* @param size
*/
Envelope(const char *data, size_t size) : _data(data), _size(size) {}
/**
* Destructor
*/
virtual ~Envelope() {}
/**
* Access to the full message data
* @return buffer
*/
const char *body()
{
return _data;
}
/**
* Size of the body
* @return size_t
*/
size_t size()
{
return _size;
}
/**
* Check if a certain field is set
* @return bool
*/
bool hasPriority () { return _priority.valid(); }
bool hasDeliveryMode () { return _deliveryMode.valid(); }
bool hasTimestamp () { return _timestamp.valid(); }
bool hasContentType () { return _contentType.valid(); }
bool hasContentEncoding () { return _contentEncoding.valid(); }
bool hasCorrelationID () { return _correlationID.valid(); }
bool hasReplyTo () { return _replyTo.valid(); }
bool hasExpiration () { return _expiration.valid(); }
bool hasMessageID () { return _messageID.valid(); }
bool hasTypeName () { return _typeName.valid(); }
bool hasUserID () { return _userID.valid(); }
bool hasAppID () { return _appID.valid(); }
bool hasHeaders () { return _headers.valid(); }
/**
* Set the various supported fields
* @param value
*/
void setPriority (uint8_t value) { _priority = value; }
void setDeliveryMode (uint8_t value) { _deliveryMode = value; }
void setTimestamp (uint64_t value) { _timestamp = value; }
void setContentType (const std::string &value) { _contentType = value; }
void setContentEncoding (const std::string &value) { _contentEncoding = value; }
void setCorrelationID (const std::string &value) { _correlationID = value; }
void setReplyTo (const std::string &value) { _replyTo = value; }
void setExpiration (const std::string &value) { _expiration = value; }
void setMessageID (const std::string &value) { _messageID = value; }
void setTypeName (const std::string &value) { _typeName = value; }
void setUserID (const std::string &value) { _userID = value; }
void setAppID (const std::string &value) { _appID = value; }
void setHeaders (const Table &value) { _headers = value; }
/**
* Reset the various supported fields
* @param value
*/
void setPriority (nullptr_t value = nullptr) { _priority = value; }
void setDeliveryMode (nullptr_t value = nullptr) { _deliveryMode = value; }
void setTimestamp (nullptr_t value = nullptr) { _timestamp = value; }
void setContentType (nullptr_t value = nullptr) { _contentType = value; }
void setContentEncoding (nullptr_t value = nullptr) { _contentEncoding = value; }
void setCorrelationID (nullptr_t value = nullptr) { _correlationID = value; }
void setReplyTo (nullptr_t value = nullptr) { _replyTo = value; }
void setExpiration (nullptr_t value = nullptr) { _expiration = value; }
void setMessageID (nullptr_t value = nullptr) { _messageID = value; }
void setTypeName (nullptr_t value = nullptr) { _typeName = value; }
void setUserID (nullptr_t value = nullptr) { _userID = value; }
void setAppID (nullptr_t value = nullptr) { _appID = value; }
void setHeaders (nullptr_t value = nullptr) { _headers = value; }
/**
* Retrieve the fields
* @return string
*/
uint8_t priority () { return _priority; }
uint8_t deliveryMode () { return _deliveryMode; }
uint64_t timestamp () { return _timestamp; }
std::string &contentType () { return _contentType; }
std::string &contentEncoding() { return _contentEncoding; }
std::string &correlationID () { return _correlationID; }
std::string &replyTo () { return _replyTo; }
std::string &expiration () { return _expiration; }
std::string &messageID () { return _messageID; }
std::string &typeName () { return _typeName; }
std::string &userID () { return _userID; }
std::string &appID () { return _appID; }
Table &headers () { return _headers; }
/**
* Is this a message with persistent storage
* This is an alias for retrieving the delivery mode and checking if it is set to 2
* @return bool
*/
bool persistent()
{
return hasDeliveryMode() && deliveryMode() == 2;
}
/**
* Set whether storage should be persistent or not
* @param bool
*/
void setPersistent(bool value = true)
{
if (value) setDeliveryMode(2);
else setDeliveryMode(nullptr);
}
};
/**
* End of namespace
*/
}

108
include/envelopefield.h Normal file
View File

@ -0,0 +1,108 @@
/**
* EnvelopeField.h
*
* An envelope field is a field that also keeps track whether it is set
* or not. Used internally by the Envelope class.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
template <typename T>
class EnvelopeField
{
private:
/**
* The actual value
* @var T
*/
T _value;
/**
* Is it set or not
* @var bool
*/
bool _isset;
public:
/**
* Empty constructor
*/
EnvelopeField() : _isset(false) {}
/**
* Constructor
* @param value
* @param isset
*/
EnvelopeField(const T &value, bool isset = true) : _value(value), _isset(isset) {}
/**
* Destructor
*/
virtual ~EnvelopeField() {}
/**
* Assign a new value
* @param value
* @return EnvelopeField
*/
EnvelopeField &operator=(const T &value)
{
_value = value;
_isset = true;
return *this;
}
/**
* Reset the value
* @param value
* @return EnvelopeField
*/
EnvelopeField &operator=(nullptr_t value)
{
_value = T();
_isset = false;
return *this;
}
/**
* Cast to the set value
* @return T
*/
operator T& ()
{
return _value;
}
/**
* Reset the value to not being set
*/
void reset()
{
_isset = false;
_value = T();
}
/**
* Is it set?
* @return bool
*/
bool valid()
{
return _isset;
}
};
/**
* End of namespace
*/
}

29
include/exchangetype.h Normal file
View File

@ -0,0 +1,29 @@
/**
* ExchangeType.h
*
* The various exchange types that are supported
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* The class
*/
enum ExchangeType
{
fanout,
direct,
topic,
headers
};
/**
* End of namespace
*/
}

70
include/field.h Normal file
View File

@ -0,0 +1,70 @@
/**
* Available field types for AMQP
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Base field class
*
* This class cannot be constructed, but serves
* as the base class for all AMQP field types
*/
class Field
{
protected:
/**
* Decode a field by fetching a type and full field from a frame
* The returned field is allocated on the heap!
* @param frame
* @return Field*
*/
static Field *decode(ReceivedFrame &frame);
public:
/**
* Destructor
*/
virtual ~Field() {}
/**
* Create a new instance on the heap of this object, identical to the object passed
* @return Field*
*/
virtual Field *clone() const = 0;
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
* @return size_t
*/
virtual size_t size() const = 0;
/**
* Write encoded payload to the given buffer.
* @param buffer
*/
virtual void fill(OutBuffer& buffer) const = 0;
/**
* Get the type ID that is used to identify this type of
* field in a field table
* @return char
*
* @todo check if all derived classes use the 'override' keyword
*/
virtual char typeID() const = 0;
};
/**
* end namespace
*/
}

402
include/fieldproxy.h Normal file
View File

@ -0,0 +1,402 @@
/**
* Field proxy. Returned by the table. Can be casted to the
* relevant native type (std::string or numeric)
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
template <typename T, typename I>
class FieldProxy
{
private:
/**
* The table or array possibly holding the requested field
*/
T *_source;
/**
* The key in the table
*/
I _index;
public:
/**
* Construct the field proxy
*
* @param table the table possibly holding the field
* @oaram index key in table map
*/
FieldProxy(T *source, I index) :
_source(source),
_index(index)
{}
/**
* Assign a boolean value
*
* @param value
*/
FieldProxy& operator=(bool value)
{
// assign value and allow chaining
_source->set(_index, BooleanSet(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(uint8_t value)
{
// assign value and allow chaining
_source->set(_index, UOctet(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(int8_t value)
{
// assign value and allow chaining
_source->set(_index, Octet(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(uint16_t value)
{
// assign value and allow chaining
_source->set(_index, UShort(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(int16_t value)
{
// assign value and allow chaining
_source->set(_index, Short(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(uint32_t value)
{
// assign value and allow chaining
_source->set(_index, ULong(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(int32_t value)
{
// assign value and allow chaining
_source->set(_index, Long(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(uint64_t value)
{
// assign value and allow chaining
_source->set(_index, ULongLong(value));
return *this;
}
/**
* Assign a numeric value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(int64_t value)
{
// assign value and allow chaining
_source->set(_index, LongLong(value));
return *this;
}
/**
* Assign a decimal value
*
* @param value
* @return FieldProxy
*/
FieldProxy& operator=(const DecimalField value)
{
// assign value and allow chaining
_source->set(_index, DecimalField(value));
return *this;
}
/**
* Assign a string value
*
* @param value
* @return FieldProxy
*/
FieldProxy &operator=(const std::string &value)
{
// in theory we should make a distinction between short and long string,
// but in practive only long strings are accepted
_source->set(_index, LongString(value));
// allow chaining
return *this;
}
/**
* Assign a string value
*
* @param value
* @return FieldProxy
*/
FieldProxy &operator=(const char *value)
{
// cast to a string
return operator=(std::string(value));
}
/**
* Get boolean value
* @return BooleanSet
*/
operator BooleanSet ()
{
// the value
BooleanSet value;
// retrieve the value
_source->get(_index, value);
// return the result
return value;
}
/**
* Get a boolean
* @return bool
*/
operator bool ()
{
// the value
BooleanSet value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return int8_t
*/
operator int8_t ()
{
// the value
Octet value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return uint8_t
*/
operator uint8_t ()
{
// the value
UOctet value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return int16_t
*/
operator int16_t ()
{
// the value
Short value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return uint16_t
*/
operator uint16_t ()
{
// the value
UShort value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return int32_t
*/
operator int32_t ()
{
// the value
Long value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return uint32_t
*/
operator uint32_t ()
{
// the value
ULong value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return int64_t
*/
operator int64_t ()
{
// the value
Long value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get numeric value
* @return uint64_t
*/
operator uint64_t ()
{
// the value
ULong value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get decimal value
* @return DecimalField
*/
operator DecimalField ()
{
// the value
DecimalField value;
// retrieve the value
_source->get(_index, value);
// return the result
return value.value();
}
/**
* Get string value
* @return string
*/
operator std::string ()
{
// it has to be either a short or a long string
ShortString shortValue;
LongString longValue;
// try to retrieve the value
if (_source->get(_index, shortValue)) return shortValue.value();
if (_source->get(_index, longValue)) return longValue.value();
// no valid string found
return std::string("");
}
};
// define types for array- and table-based field proxy
typedef FieldProxy<Table, std::string> AssociativeFieldProxy;
typedef FieldProxy<Array, uint8_t> ArrayFieldProxy;
/**
* end namespace
*/
}

39
include/flags.h Normal file
View File

@ -0,0 +1,39 @@
/**
* AmqpFlags.h
*
* The various flags that are supported
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* All bit flags
* @var int
*/
extern const int durable;
extern const int autodelete;
extern const int active;
extern const int passive;
extern const int ifunused;
extern const int ifempty;
extern const int global;
extern const int nolocal;
extern const int noack;
extern const int exclusive;
extern const int nowait;
extern const int mandatory;
extern const int immediate;
extern const int redelivered;
extern const int multiple;
extern const int requeue;
/**
* End of namespace
*/
}

94
include/login.h Normal file
View File

@ -0,0 +1,94 @@
/**
* The login information to access a server
*
* This class combines login, password and vhost
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class Login
{
private:
/**
* The vhost
* @var string
*/
std::string _vhost;
/**
* The username
* @var string
*/
std::string _user;
/**
* The password
* @var string
*/
std::string _password;
public:
/**
* Constructor
* @param vhost
* @param user
* @param password
*/
Login(const std::string &vhost, const std::string &user, const std::string &password) :
_vhost(vhost), _user(user), _password(password) {}
/**
* Constructor
* @param user
* @param password
*/
Login(const std::string &user, const std::string &password) :
_vhost("/"), _user(user), _password(password) {}
/**
* Constructor
*/
Login() :
_vhost("/"), _user("guest"), _password("guest") {}
/**
* Destructor
*/
virtual ~Login() {}
/**
* String representation in SASL PLAIN mode
* @return string
*/
std::string saslPlain()
{
// we need an initial string
std::string result("\0", 1);
// append other elements
return result.append(_user).append("\0",1).append(_password);
}
/**
* Retrieve the vhost
* @return string
*/
std::string &vhost()
{
return _vhost;
}
};
/**
* End of namespace
*/
}

82
include/monitor.h Normal file
View File

@ -0,0 +1,82 @@
/**
* Monitor.h
*
* A monitor object monitors if the connection is still valid. When the
* connection is parsing incoming data, it calls the user handler for each
* incoming frame. However, it is unknown what this handler is going to do,
* it could for example decide to destruct the connection object. In that
* case the connection object should stop further handling the data. This
* monitor class is used to check if the connection has been destructed.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class Monitor
{
private:
/**
* The object being watched
* @var Watchable
*/
Watchable *_watchable;
/**
* Invalidate the object
*/
void invalidate()
{
_watchable = nullptr;
}
public:
/**
* Constructor
* @param watchable
*/
Monitor(Watchable *watchable) : _watchable(watchable)
{
// register with the watchable
_watchable->add(this);
}
/**
* Destructor
*/
virtual ~Monitor()
{
// remove from watchable
if (_watchable) _watchable->remove(this);
}
/**
* Check if the object is valid
* @return bool
*/
bool valid()
{
return _watchable != nullptr;
}
/**
* The watchable can access private data
*/
friend class Watchable;
};
/**
* End of namespace
*/
}

183
include/numericfield.h Normal file
View File

@ -0,0 +1,183 @@
/**
* Numeric field types for AMQP
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Template for numeric field types
*/
template<
typename T,
char F,
typename = typename std::enable_if<std::is_arithmetic<T>::value, T>,
typename = typename std::enable_if<std::is_integral<T>::value, T>
>
class NumericField : public Field
{
private:
/**
* Field value
*/
T _value;
public:
/**
* Default constructor, assign 0
*/
NumericField() : _value(0) {}
/**
* Construct numeric field from
* one of numeric types
*
* @param value field value
*/
NumericField(T value) : _value(value) {}
/**
* Parse based on incoming buffer
* @param frame
*/
NumericField(ReceivedFrame &frame)
{
// copy the data from the buffer into the field
if (!std::is_floating_point<T>::value)
{
// convert value based on internal storage size
switch (sizeof(T))
{
case 1: _value = frame.nextUint8(); break;
case 2: _value = frame.nextUint16(); break;
case 4: _value = frame.nextUint32(); break;
case 8: _value = frame.nextUint64(); break;
}
}
else
{
switch (sizeof(T))
{
case 4: _value = frame.nextFloat(); break;
case 8: _value = frame.nextDouble(); break;
}
}
}
/**
* Destructor
*/
virtual ~NumericField() {}
/**
* Create a new instance of this object
* @return Field*
*/
virtual Field *clone() const override
{
// create a new copy of ourselves and return it
return new NumericField(_value);
}
/**
* Assign a new value
*
* @param value new value for field
* @return NumericField
*/
NumericField& operator=(T value)
{
_value = value;
};
/**
* Get the value
* @return mixed
*/
operator T () const
{
return _value;
}
/**
* Get the value
* @return mixed
*/
T value() const
{
// return internal value
return _value;
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
* @return size_t
*/
virtual size_t size() const override
{
// numeric types have no extra storage requirements
return sizeof(_value);
}
/**
* Get the maximum allowed value for this field
* @return mixed
*/
constexpr static T max()
{
return std::numeric_limits<T>::max();
}
/**
* Write encoded payload to the given buffer.
* @param buffer OutBuffer to write to
*/
virtual void fill(OutBuffer& buffer) const override
{
// store converted value
T value = _value;
// write to buffer
// adding a value takes care of host to network byte order
buffer.add(value);
}
/**
* Get the type ID that is used to identify this type of
* field in a field table
*/
virtual char typeID() const override
{
return F;
}
};
/**
* Concrete numeric types for AMQP
*/
typedef NumericField<int8_t, 'b'> Octet;
typedef NumericField<uint8_t, 'B'> UOctet;
typedef NumericField<int16_t, 'U'> Short;
typedef NumericField<uint16_t, 'u'> UShort;
typedef NumericField<int32_t, 'I'> Long;
typedef NumericField<uint32_t, 'i'> ULong;
typedef NumericField<int64_t, 'L'> LongLong;
typedef NumericField<uint64_t, 'l'> ULongLong;
typedef NumericField<uint64_t, 'T'> Timestamp;
/**
* Concrete floating-point types for AMQP
*/
typedef NumericField<float, 'f'> Float;
typedef NumericField<double, 'd'> Double;
/**
* end namespace
*/
}

220
include/outbuffer.h Normal file
View File

@ -0,0 +1,220 @@
/**
* OutBuffer.h
*
* This is a utility class for writing various data types to a binary
* string, and converting the values to network byte order
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class OutBuffer
{
private:
/**
* Pointer to the beginning of the buffer
* @var char*
*/
char *_buffer;
/**
* Pointer to the buffer to be filled
* @var char*
*/
char *_current;
/**
* Current size of the buffer
* @var size_t
*/
size_t _size;
public:
/**
* Constructor
* @param capacity
*/
OutBuffer(uint32_t capacity)
{
_size = 0;
_buffer = _current = new char[capacity];
}
/**
* Destructor
*/
virtual ~OutBuffer()
{
delete[] _buffer;
}
/**
* Get access to the internal buffer
* @return const char*
*/
const char *data()
{
return _buffer;
}
/**
* Current size of the output buffer
* @return size_t
*/
size_t size()
{
return _size;
}
/**
* Add a binary buffer to the buffer
* @param string char* to the string
* @param size size of string
*/
void add(const char *string, uint32_t size)
{
memcpy(_current, string, size);
_current += size;
_size += size;
}
/**
* Add a binary buffer to the buffer
* @param string char* to the string
* @param size size of string
*/
void add(const std::string &string)
{
add(string.c_str(), string.size());
}
/**
* add a uint8_t to the buffer
* @param value value to add
*/
void add(uint8_t value)
{
memcpy(_current, &value, sizeof(value));
_current += sizeof(value);
_size += sizeof(value);
}
/**
* add a uint16_t to the buffer
* @param value value to add
*/
void add(uint16_t value)
{
uint16_t v = htobe16(value);
memcpy(_current, &v, sizeof(v));
_current += sizeof(v);
_size += sizeof(v);
}
/**
* add a uint32_t to the buffer
* @param value value to add
*/
void add(uint32_t value)
{
uint32_t v = htobe32(value);
memcpy(_current, &v, sizeof(v));
_current += sizeof(v);
_size += sizeof(v);
}
/**
* add a uint64_t to the buffer
* @param value value to add
*/
void add(uint64_t value)
{
uint64_t v = htobe64(value);
memcpy(_current, &v, sizeof(v));
_current += sizeof(v);
_size += sizeof(v);
}
/**
* add a int8_t to the buffer
* @param value value to add
*/
void add(int8_t value)
{
memcpy(_current, &value, sizeof(value));
_current += sizeof(value);
_size += sizeof(value);
}
/**
* add a int16_t to the buffer
* @param value value to add
*/
void add(int16_t value)
{
int16_t v = htobe16(value);
memcpy(_current, &v, sizeof(v));
_current += sizeof(v);
_size += sizeof(v);
}
/**
* add a int32_t to the buffer
* @param value value to add
*/
void add(int32_t value)
{
int32_t v = htobe32(value);
memcpy(_current, &v, sizeof(v));
_current += sizeof(v);
_size += sizeof(v);
}
/**
* add a int64_t to the buffer
* @param value value to add
*/
void add(int64_t value)
{
int64_t v = htobe64(value);
memcpy(_current, &v, sizeof(v));
_current += sizeof(v);
_size += sizeof(v);
}
/**
* add a float to the buffer
* @param value value to add
*/
void add(float value)
{
memcpy(_current, &value, sizeof(value));
_current += sizeof(value);
_size += sizeof(value);
}
/**
* add a double to the buffer
* @param value value to add
*/
void add(double value)
{
memcpy(_current, &value, sizeof(value));
_current += sizeof(value);
_size += sizeof(value);
}
};
/**
* End of namespace
*/
}

263
include/receivedframe.h Normal file
View File

@ -0,0 +1,263 @@
/**
* ReceivedFrame.h
*
* The received frame class is a wrapper around a data buffer, it tries to
* find out if the buffer is big enough to contain an entire frame, and
* it will try to recognize the frame type in the buffer
*
* This is a class that is used internally by the AMQP library. As a used
* of this library, you normally do not have to instantiate it.
*
* @documentation public
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ReceivedFrame
{
private:
/**
* The buffer we are reading from
* @var char*
*/
const char *_buffer = nullptr;
/**
* Number of bytes left to retrieve
* @var uint32_t
*/
uint32_t _left = 0;
/**
* Type of frame
* @var uint8_t
*/
uint8_t _type = 0;
/**
* Channel identifier
* @var uint16_t
*/
uint16_t _channel = 0;
/**
* The payload size
* @var uint32_t
*/
uint32_t _payloadSize = 0;
/**
* Process a method frame
* @param connection
* @return bool
*/
bool processMethodFrame(ConnectionImpl *connection);
/**
* Process a connection frame
* @param connection
* @return bool
*/
bool processConnectionFrame(ConnectionImpl *connection);
/**
* Process a channel frame
* @param connection
* @return bool
*/
bool processChannelFrame(ConnectionImpl *connection);
/**
* Process an exchange frame
* @param connection
* @return bool
*/
bool processExchangeFrame(ConnectionImpl *connection);
/**
* Process a queue frame
* @param connection
* @return bool
*/
bool processQueueFrame(ConnectionImpl *connection);
/**
* Process a basic frame
* @param connection
* @return bool
*/
bool processBasicFrame(ConnectionImpl *connection);
/**
* Process a transaction frame
* @param connection
* @return bool
*/
bool processTransactionFrame(ConnectionImpl *connection);
/**
* Process a header frame
* @param connection
* @return bool
*/
bool processHeaderFrame(ConnectionImpl *connection);
public:
/**
* Constructor
* @param buffer Binary buffer
* @param size Size of the buffer
* @param max Max buffer size
*/
ReceivedFrame(const char *buffer, uint32_t size, uint32_t max);
/**
* Destructor
*/
virtual ~ReceivedFrame() {}
/**
* Is this a complete frame?
* @return integer
*/
bool complete() const;
/**
* Return the channel identifier
* @return uint16_t
*/
uint16_t channel() const
{
return _channel;
}
/**
* Total size of the frame (headers + payload)
* @return uint32_t
*/
uint64_t totalSize() const
{
// payload size + size of headers and end of frame byte
return _payloadSize + 8;
}
/**
* The size of the payload
* @return uint32_t
*/
uint32_t payloadSize() const
{
return _payloadSize;
}
/**
* Read the next uint8_t from the buffer
*
* @return uint8_t value read
*/
uint8_t nextUint8();
/**
* Read the next int8_t from the buffer
*
* @return int8_t value read
*/
int8_t nextInt8();
/**
* Read the next uint16_t from the buffer
*
* @return uint16_t value read
*/
uint16_t nextUint16();
/**
* Read the next int16_t from the buffer
*
* @return int16_t value read
*/
int16_t nextInt16();
/**
* Read the next uint32_t from the buffer
*
* @return uint32_t value read
*/
uint32_t nextUint32();
/**
* Read the next int32_t from the buffer
*
* @return int32_t value read
*/
int32_t nextInt32();
/**
* Read the next uint64_t from the buffer
*
* @return uint64_t value read
*/
uint64_t nextUint64();
/**
* Read the next int64_t from the buffer
*
* @return int64_t value read
*/
int64_t nextInt64();
/**
* Read a float from the buffer
*
* @return float float read from buffer.
*/
float nextFloat();
/**
* Read a double from the buffer
*
* @return double double read from buffer
*/
double nextDouble();
/**
* Get a pointer to the next binary buffer of a certain size
* @param size
* @return char*
*/
const char *nextData(uint32_t size);
/**
* Process the received frame
*
* If this method returns false, it means that the frame was not processed,
* because it was an unrecognized frame. This does not mean that the
* connection is now in an invalid state however.
*
* @param connection the connection over which the data was received
* @return bool was the frame fully processed
* @internal
*/
bool process(ConnectionImpl *connection);
/**
* The checker may access private data
*/
friend class FrameCheck;
};
/**
* End of namespace
*/
}

166
include/stringfield.h Normal file
View File

@ -0,0 +1,166 @@
/**
* String field types for amqp
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Base class for string types
*/
template <typename T, char F>
class StringField : public Field
{
private:
/**
* Pointer to string data
* @var string
*/
std::string _data;
public:
/**
* Initialize empty string
*/
StringField() {}
/**
* Construct based on a std::string
*
* @param value string value
*/
StringField(std::string value) : _data(value) {}
/**
* Construct based on received data
* @param frame
*/
StringField(ReceivedFrame &frame)
{
// get the size
T size(frame);
// read data
const char *data = frame.nextData(size.value());
// @todo what if this fails?
// allocate string
_data = std::string((char*) data, (size_t) size.value());
}
/**
* Clean up memory used
*/
virtual ~StringField() {}
/**
* Create a new instance of this object
* @return Field*
*
* @todo can this be protected?
* @todo check if all clone methods have a override keyword
*/
virtual Field *clone() const override
{
// create a new copy of ourselves and return it
return new StringField(_data);
}
/**
* Assign a new value
*
* @param value new value
*/
StringField& operator=(const std::string& value)
{
// overwrite data
_data = value;
// allow chaining
return *this;
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
*/
virtual size_t size() const override
{
// find out size of the size parameter
T size(_data.size());
// size of the uint8 or uint32 + the actual string size
return size.size() + _data.size();
}
/**
* Get the value
* @return string
*/
operator const std::string& () const
{
return _data;
}
/**
* Get the value
* @return string
*/
const std::string& value() const
{
// get data
return _data;
}
/**
* Get the maximum allowed string length for this field
* @return size_t
*/
constexpr static size_t maxLength()
{
return T::max();
}
/**
* Write encoded payload to the given buffer.
* @param buffer
*/
virtual void fill(OutBuffer& buffer) const override
{
// create size
T size(_data.size());
// first, write down the size of the string
size.fill(buffer);
// write down the string content
buffer.add(_data);
}
/**
* Get the type ID that is used to identify this type of
* field in a field table
* @return char
*/
virtual char typeID() const override
{
return F;
}
};
/**
* Concrete string types for AMQP
*/
typedef StringField<UOctet, 's'> ShortString;
typedef StringField<ULong, 'S'> LongString;
/**
* end namespace
*/
}

124
include/table.h Normal file
View File

@ -0,0 +1,124 @@
/**
* AMQP field table
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* AMQP field table
*/
class Table : public Field
{
private:
/**
* We define a custom type for storing fields
* @typedef FieldMap
*/
typedef std::map<std::string, std::shared_ptr<Field> > FieldMap;
/**
* Store the fields
* @var FieldMap
*/
FieldMap _fields;
public:
/**
* Constructor that creates an empty table
*/
Table() {}
/**
* Decode the data from a received frame into a table
*
* @param frame received frame to decode
*/
Table(ReceivedFrame &frame);
/**
* Copy constructor
* @param table
*/
Table(const Table &table);
/**
* Destructor
*/
virtual ~Table() {}
/**
* Create a new instance on the heap of this object, identical to the object passed
* @return Field*
*/
virtual Field *clone() const override
{
return new Table(*this);
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
*/
virtual size_t size() const override;
/**
* Set a field
*
* @param name field name
* @param value field value
*/
Table set(const std::string& name, const Field &value)
{
// copy to a new pointer and store it
_fields[name] = std::shared_ptr<Field>(value.clone());
// allow chaining
return *this;
}
/**
* Get a field
*
* If the field does not exist, an empty string field is returned
*
* @param name field name
* @return the field value
*/
const Field &get(const std::string &name);
/**
* Get a field
*
* @param name field name
*/
AssociativeFieldProxy operator[](const std::string& name)
{
return AssociativeFieldProxy(this, name);
}
/**
* Write encoded payload to the given buffer.
* @param buffer
*/
virtual void fill(OutBuffer& buffer) const override;
/**
* Get the type ID that is used to identify this type of
* field in a field table
*/
virtual char typeID() const override
{
return 'F';
}
};
/**
* end namespace
*/
}

60
include/watchable.h Normal file
View File

@ -0,0 +1,60 @@
/**
* Watchable.h
*
* Every class that overrides from the Watchable class can be monitored for
* destruction by a Monitor object
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class Watchable
{
private:
/**
* The monitors
* @var set
*/
std::set<Monitor*> _monitors;
/**
* Add a monitor
* @param monitor
*/
void add(Monitor *monitor)
{
_monitors.insert(monitor);
}
/**
* Remove a monitor
* @param monitor
*/
void remove(Monitor *monitor)
{
_monitors.erase(monitor);
}
public:
/**
* Destructor
*/
virtual ~Watchable();
/**
* Only a monitor has full access
*/
friend class Monitor;
};
/**
* End of namespace
*/
}

29
src/Makefile Normal file
View File

@ -0,0 +1,29 @@
CPP = g++-4.8
RM = rm -f
CPPFLAGS = -Wall -c -I. -O2 -flto -std=c++11 -g
LD = g++
LD_FLAGS = -Wall -shared -O2
RESULT = libcopernica_amqp.so
STATIC = $(RESULT:%.so=%.a)
SOURCES = $(wildcard *.cpp)
OBJECTS = $(SOURCES:%.cpp=%.o)
OBJECTS_STATIC = $(SOURCES:%.cpp=%.s.o)
all: ${OBJECTS} ${OBJECTS_STATIC} ${RESULT}
$(MAKE) STATIC_LIB
${RESULT}: ${OBJECTS}
${LD} ${LD_FLAGS} -o $@ ${OBJECTS}
clean:
${RM} *.obj *~* ${OBJECTS} ${OBJECTS_STATIC} ${RESULT} ${STATIC}
${OBJECTS}:
${CPP} ${CPPFLAGS} -fpic -o $@ ${@:%.o=%.cpp}
${OBJECTS_STATIC}:
${CPP} ${CPPFLAGS} -o $@ ${@:%.s.o=%.cpp}
STATIC_LIB:
ar rcs ${STATIC} ${OBJECTS_STATIC}

109
src/array.cpp Normal file
View File

@ -0,0 +1,109 @@
/**
* Array.cpp
*
* Implementation of an array
*
*/
#include "includes.h"
// we live in the copernica namespace
namespace AMQP {
/**
* Constructor based on incoming frame
* @param frame
*/
Array::Array(ReceivedFrame &frame)
{
// use this to see if we've read too many bytes.
uint32_t charsToRead = frame.nextUint32();
// keep going until all data is read
while (charsToRead > 0)
{
// one byte less for the field type
charsToRead -= 1;
// read the field type and construct the field
Field *field = Field::decode(frame);
if (!field) continue;
// less bytes to read
charsToRead -= field->size();
// add the additional field
_fields.push_back(std::shared_ptr<Field>(field));
}
}
/**
* Copy constructor
* @param array
*/
Array::Array(const Array &array)
{
// loop through the other array
for (auto iter = array._fields.begin(); iter != array._fields.end(); iter++)
{
// add to this vector
_fields.push_back(std::shared_ptr<Field>((*iter)->clone()));
}
}
/**
* Get a field
*
* If the field does not exist, an empty string is returned
*
* @param index field index
* @return Field
*/
const Field &Array::get(uint8_t index)
{
// used if index does not exist
static ShortString empty;
// check whether we have that many elements
if (index >= _fields.size()) return empty;
// get value
return *_fields[index];
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
*/
size_t Array::size() const
{
// store the size (four bytes for the initial size)
size_t size = 4;
// iterate over all elements
for (auto iter(_fields.begin()); iter != _fields.end(); ++iter)
{
// add the size of the field type and size of element
size += sizeof((*iter)->typeID());
size += (*iter)->size();
}
// return the result
return size;
}
/**
* Write encoded payload to the given buffer.
*/
void Array::fill(OutBuffer& buffer) const
{
// iterate over all elements
for (auto iter(_fields.begin()); iter != _fields.end(); ++iter)
{
// encode the element type and element
buffer.add((*iter)->typeID());
(*iter)->fill(buffer);
}
}
// end namespace
}

108
src/basicackframe.h Normal file
View File

@ -0,0 +1,108 @@
/**
* Class describing a basic acknowledgement frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class defintion
*/
class BasicAckFrame : public BasicFrame {
private:
/**
* server-assigned and channel specific delivery tag
* @var uint64_t
*/
uint64_t _deliveryTag;
/**
* if set, tag is treated as "up to and including", so client can acknowledge multiple messages with a single method
* if not set, refers to single message
* @var BooleanSet
*/
BooleanSet _multiple;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
* @return pointer to object to allow for chaining
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// add the delivery tag
buffer.add(_deliveryTag);
// add the booleans
_multiple.fill(buffer);
}
public:
/**
* Construct a basic acknowledgement frame
*
* @param channel Channel identifier
* @param deliveryTag server-assigned and channel specific delivery tag
* @param multiple acknowledge mutiple messages
*/
BasicAckFrame(uint16_t channel, uint64_t deliveryTag, bool multiple = false) :
BasicFrame(channel, 9),
_deliveryTag(deliveryTag),
_multiple(multiple) {}
/**
* Construct based on received frame
* @param frame
*/
BasicAckFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_deliveryTag(frame.nextUint64()),
_multiple(frame) {}
/**
* Destructor
*/
virtual ~BasicAckFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 80;
}
/**
* Return the server-assigned and channel specific delivery tag
* @return uint64_t
*/
uint64_t deliveryTag() const
{
return _deliveryTag;
}
/**
* Return whether to acknowledgement multiple messages
* @return bool
*/
bool multiple()
{
return _multiple.get(0);
}
};
/**
* end namespace
*/
}

103
src/basiccancelframe.h Normal file
View File

@ -0,0 +1,103 @@
/**
* Class describing a basic cancel frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicCancelFrame : public BasicFrame
{
private:
/**
* Holds the consumer tag specified by the client or provided by the server.
* @var ShortString
*/
ShortString _consumerTag;
/**
* whether to wait for a response
* @var BooleanSet
*/
BooleanSet _noWait;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// add consumer tag + booleans
_consumerTag.fill(buffer);
_noWait.fill(buffer);
}
public:
/**
* Construct a basic cancel frame from a received frame
*
* @param frame received frame to parse
*/
BasicCancelFrame(ReceivedFrame &frame) : BasicFrame(frame), _consumerTag(frame), _noWait(frame) {}
/**
* Construct a basic cancel frame
*
* @param channel Channel identifier
* @param consumerTag consumertag specified by client of provided by server
* @param noWait whether to wait for a response.
*/
BasicCancelFrame(uint16_t channel, const std::string& consumerTag, bool noWait = false) :
BasicFrame(channel, consumerTag.size() + 2), // 1 for extra string size, 1 for bool
_consumerTag(consumerTag),
_noWait(noWait) {}
/**
* Destructor
*/
virtual ~BasicCancelFrame() {}
/**
* Return the consumertag, which is specified by the client or provided by the server
* @return string
*/
const std::string& consumerTag() const
{
return _consumerTag;
}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 30;
}
/**
* Return whether to wait for a response
* @return boolean
*/
const bool noWait()
{
return _noWait.get(0);
}
};
/**
* end namespace
*/
}

90
src/basiccancelokframe.h Normal file
View File

@ -0,0 +1,90 @@
/**
* Class describing a basic cancel ok frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicCancelOKFrame : public BasicFrame
{
private:
/**
* Holds the consumer tag specified by the client or provided by the server.
* @var ShortString
*/
ShortString _consumerTag;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// add own information
_consumerTag.fill(buffer);
}
public:
/**
* Construct a basic cancel ok frame
*
* @param frame received frame
*/
BasicCancelOKFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_consumerTag(frame)
{}
/**
* Construct a basic cancel ok frame (client-side)
* @param channel channel identifier
* @param consumerTag holds the consumertag specified by client or server
*/
BasicCancelOKFrame(uint16_t channel, std::string& consumerTag) :
BasicFrame(channel, consumerTag.length() + 1), // add 1 byte for encoding the size of consumer tag
_consumerTag(consumerTag)
{}
/**
* Destructor
*/
virtual ~BasicCancelOKFrame() {}
/**
* Return the consumertag, which is specified by the client or provided by the server
* @return string
*/
const std::string& consumerTag() const
{
return _consumerTag;
}
/**
* Return the method ID
* @return uint16_t
*
* @todo check if all other implementations use override keyword
*/
virtual uint16_t methodID() const override
{
return 31;
}
};
/**
* End of namespace
*/
}

191
src/basicconsumeframe.h Normal file
View File

@ -0,0 +1,191 @@
/**
* Class describing a basic consume frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicConsumeFrame : public BasicFrame
{
private:
/**
* Field that is no longer used
* @var uint16_t
*/
uint16_t _deprecated;
/**
* specifies the name of the queue to consume from
* @var Table
*/
ShortString _queueName;
/**
* specifies the identifier for the consumer tag.
* This tag is local to a channel so two clients can use the same consumer tags.
* If empty, the server generates a tag.
* @var ShortString
*/
ShortString _consumerTag;
/**
* Booleans sent in frame
* 0: noLocal
* 1: noAck
* 2: exclusive
* 3: noWait
* @var BooleanSet
*/
BooleanSet _bools;
/**
* additional arguments, implementation dependent
* @var Table
*/
Table _filter;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// fill the buffer
buffer.add((uint16_t) _deprecated);
_queueName.fill(buffer);
_consumerTag.fill(buffer);
_bools.fill(buffer);
_filter.fill(buffer);
}
public:
/**
* Construct a basic consume frame
*
* @param channel channel we're working on
* @param queueName name of the queue to consume from
* @param consumerTag identifier for the consumer tag.
* @param noLocal no-local
* @param noAck no acknowledgements
* @param exclusive exclusive channel
* @param noWait don't wait for a response
* @param filter additional arguments
*/
BasicConsumeFrame(uint16_t channel, const std::string& queueName, const std::string& consumerTag, bool noLocal = false, bool noAck = false, bool exclusive = false, bool noWait = false, const Table& filter = {}) :
BasicFrame(channel, (queueName.length() + consumerTag.length() + 5 + filter.size())), // size of vars, +1 for each shortstring size, +1 for bools, +2 for deprecated value
_queueName(queueName),
_consumerTag(consumerTag),
_bools(noLocal, noAck, exclusive, noWait),
_filter(filter)
{}
/**
* Constructor based on incoming data
* @param frame
*/
BasicConsumeFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_deprecated(frame.nextUint16()), // read deprecated info
_queueName(frame),
_consumerTag(frame),
_bools(frame),
_filter(frame)
{}
/**
* Destructor
*/
virtual ~BasicConsumeFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 20;
}
/**
* Return the name of the queue to consume from
* @return string
*/
const std::string& queueName() const
{
return _queueName;
}
/**
* return the identifier for the consumertag
* @return string
*/
const std::string& consumerTag() const
{
return _consumerTag;
}
/**
* return the value of the noLocal bool
* @return bool
*/
bool noLocal() const
{
return _bools.get(0);
}
/**
* return the value of the noAck bool
* @return bool
*/
bool noAck() const
{
return _bools.get(1);
}
/**
* return whether the queue is exclusive
* @return bool
*/
bool exclusive() const
{
return _bools.get(2);
}
/**
* return whether to wait for a response
* @return bool
*/
bool noWait() const
{
return _bools.get(3);
}
/**
* return the additional filter arguments
* @return Table
*/
const Table& filter() const
{
return _filter;
}
};
/**
* End namespace
*/
}

88
src/basicconsumeokframe.h Normal file
View File

@ -0,0 +1,88 @@
/**
* Class describing a basic consume ok frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicConsumeOKFrame : public BasicFrame
{
private:
/**
* Holds the consumer tag specified by the client or provided by the server.
* @var ShortString
*/
ShortString _consumerTag;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// add payload
_consumerTag.fill(buffer);
}
public:
/**
* Construct a basic consume frame
*
* @param consumerTag consumertag specified by client of provided by server
*/
BasicConsumeOKFrame(uint16_t channel, const std::string& consumerTag) :
BasicFrame(channel, consumerTag.length() + 1), // length of string + 1 for encoding of stringsize
_consumerTag(consumerTag)
{}
/**
* Construct a basic consume ok frame from a received frame
*
* @param frame received frame
*/
BasicConsumeOKFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_consumerTag(frame)
{}
/**
* Destructor
*/
virtual ~BasicConsumeOKFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 21;
}
/**
* Return the consumertag, which is specified by the client or provided by the server
* @return std::string
*/
const std::string& consumerTag() const
{
return _consumerTag;
}
};
/**
* end namespace
*/
}

164
src/basicdeliverframe.h Normal file
View File

@ -0,0 +1,164 @@
/**
* Class describing a basic deliver frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicDeliverFrame : public BasicFrame
{
private:
/**
* identifier for the consumer, valid within current channel
* @var ShortString
*/
ShortString _consumerTag;
/**
* server-assigned and channel specific delivery tag
* @var uint64_t
*/
uint64_t _deliveryTag;
/**
* indicates whether the message has been previously delivered to this (or another) client
* @var BooleanSet
*/
BooleanSet _redelivered;
/**
* the name of the exchange to publish to. An empty exchange name means the default exchange.
* @var ShortString
*/
ShortString _exchange;
/**
* Message routing key
* @var ShortString
*/
ShortString _routingKey;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
BasicFrame::fill(buffer);
_consumerTag.fill(buffer);
buffer.add(_deliveryTag);
_redelivered.fill(buffer);
_exchange.fill(buffer);
_routingKey.fill(buffer);
}
public:
/**
* Construct a basic deliver frame (client side)
*
* @param channel channel we're working on
* @param consumerTag identifier for the consumer, valid within current channel
* @param deliveryTag server-assigned and channel specific delivery tag
* @param redelivered indicates whether the message has been previously delivered to this (or another) client
* @param exchange name of exchange to publish to
* @param routingKey message routing key
*/
BasicDeliverFrame(uint16_t channel, const std::string& consumerTag, uint64_t deliveryTag, bool redelivered = false, const std::string& exchange = "", const std::string& routingKey = "") :
BasicFrame(channel, (consumerTag.length() + exchange.length() + routingKey.length() + 12)),
// length of strings + 1 byte per string for stringsize, 8 bytes for uint64_t and 1 for bools
_consumerTag(consumerTag),
_deliveryTag(deliveryTag),
_redelivered(redelivered),
_exchange(exchange),
_routingKey(routingKey)
{}
/**
* Construct a basic deliver frame from a received frame
*
* @param frame received frame
*/
BasicDeliverFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_consumerTag(frame),
_deliveryTag(frame.nextUint64()),
_redelivered(frame),
_exchange(frame),
_routingKey(frame)
{}
/**
* Destructor
*/
virtual ~BasicDeliverFrame() {}
/**
* Return the name of the exchange to publish to
* @return string
*/
const std::string& exchange() const
{
return _exchange;
}
/**
* Return the routing key
* @return string
*/
const std::string& routingKey() const
{
return _routingKey;
}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 60;
}
/**
* Return the server-assigned and channel specific delivery tag
* @return uint64_t
*/
uint64_t deliveryTag() const
{
return _deliveryTag;
}
/**
* Return the identifier for the consumer (channel specific)
* @return string
*/
const std::string& consumerTag() const
{
return _consumerTag;
}
/**
* Return whether the message has been previously delivered to (another) client
* @return bool
*/
bool redelivered() const
{
return _redelivered.get(0);
}
};
/**
* end namespace
*/
}

52
src/basicframe.h Normal file
View File

@ -0,0 +1,52 @@
/**
* Class describing an AMQP basic frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class BasicFrame : public MethodFrame
{
protected:
/**
* Constructor
* @param channel The channel ID
* @param size Payload size
*/
BasicFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {}
/**
* Constructor based on a received frame
* @param frame
*/
BasicFrame(ReceivedFrame &frame) : MethodFrame(frame) {}
public:
/**
* Destructor
*/
virtual ~BasicFrame() {}
/**
* Class id
* @return uint16_t
*/
virtual uint16_t classID() const override
{
return 60;
}
};
/**
* end namespace
*/
}

78
src/basicgetemptyframe.h Normal file
View File

@ -0,0 +1,78 @@
/**
* Class describing a basic get empty frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class BasicGetEmptyFrame : public BasicFrame
{
private:
/**
* Field that is no longer used
* @var ShortString
*/
ShortString _deprecated;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// recreate deprecated field and encode
_deprecated.fill(buffer);
}
public:
/**
* Construct a basic get empty frame
*
* @param channel channel we're working on
*/
BasicGetEmptyFrame(uint16_t channel) :
BasicFrame(channel, 1) // 1 for encoding the deprecated cluster id (shortstring)
{}
/**
* Constructor for incoming data
* @param frame received frame
*/
BasicGetEmptyFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_deprecated(frame)
{}
/**
* Destructor
*/
virtual ~BasicGetEmptyFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 72;
}
};
/**
* end namespace
*/
}

117
src/basicgetframe.h Normal file
View File

@ -0,0 +1,117 @@
/**
* Class describing a basic get frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicGetFrame : public BasicFrame
{
private:
/**
* Deprecated field
* @var uint16_t
*/
uint16_t _deprecated;
/**
* name of the queue to get a message from
* @var ShortString
*/
ShortString _queue;
/**
* if set, server does not expect acknowledgement for messages. Server dequeues message after sending
* @var BooleanSet
*/
BooleanSet _noAck;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// encode other values
buffer.add(_deprecated);
_queue.fill(buffer);
_noAck.fill(buffer);
}
public:
/**
* Construct a basic get frame
*
* @param channel channel we're working on
* @param queue name of the queue
* @param noAck whether server expects acknowledgements for messages
*/
BasicGetFrame(uint16_t channel, const std::string& queue, bool noAck = false) :
BasicFrame(channel, queue.length() + 4), // 1 for bool, 1 for string size, 2 for deprecated field
_deprecated(0),
_queue(queue),
_noAck(noAck)
{}
/**
* Constructor based on incoming frame
* @param frame
*/
BasicGetFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_deprecated(frame.nextUint16()),
_queue(frame),
_noAck(frame)
{}
/**
* Destructor
*/
virtual ~BasicGetFrame() {}
/**
* Return the name of the queue
* @return string
*/
const std::string& queue() const
{
return _queue;
}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 70;
}
/**
* Return whether the server expects acknowledgements for messages
* @return boolean
*/
bool noAck() const
{
return _noAck.get(0);
}
};
/**
* end namespace
*/
}

165
src/basicgetokframe.h Normal file
View File

@ -0,0 +1,165 @@
/**
* Class describing a basic get ok frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicGetOKFrame : public BasicFrame
{
private:
/**
* server-assigned and channel specific delivery tag
* @var uint64_t
*/
uint64_t _deliveryTag;
/**
* indicates whether the message has been previously delivered to this (or another) client
* @var BooleanSet
*/
BooleanSet _redelivered;
/**
* the name of the exchange to publish to. An empty exchange name means the default exchange.
* @var ShortString
*/
ShortString _exchange;
/**
* Message routing key
* @var ShortString
*/
ShortString _routingKey;
/**
* number of messages in the queue
* @var uint32_t
*/
uint32_t _messageCount;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// encode rest of the fields
buffer.add(_deliveryTag);
_redelivered.fill(buffer);
_exchange.fill(buffer);
_routingKey.fill(buffer);
buffer.add(_messageCount);
}
public:
/**
* Construct a basic get ok frame
*
* @param channel channel we're working on
* @param deliveryTag server-assigned and channel specific delivery tag
* @param redelivered indicates whether the message has been previously delivered to this (or another) client
* @param exchange name of exchange to publish to
* @param routingKey message routing key
* @param messageCount number of messages in the queue
*/
BasicGetOKFrame(uint16_t channel, uint64_t deliveryTag, bool redelivered, const std::string& exchange, const std::string& routingKey, uint32_t messageCount) :
BasicFrame(channel, (exchange.length() + routingKey.length() + 15)), // string length, +1 for each shortsrting length + 8 (uint64_t) + 4 (uint32_t) + 1 (bool)
_deliveryTag(deliveryTag),
_redelivered(redelivered),
_exchange(exchange),
_routingKey(routingKey),
_messageCount(messageCount)
{}
/**
* Construct a basic get ok frame from a received frame
*
* @param frame received frame
*/
BasicGetOKFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_deliveryTag(frame.nextUint64()),
_redelivered(frame),
_exchange(frame),
_routingKey(frame),
_messageCount(frame.nextUint32())
{}
/**
* Destructor
*/
virtual ~BasicGetOKFrame() {}
/**
* Return the name of the exchange to publish to
* @return string
*/
const std::string& exchange() const
{
return _exchange;
}
/**
* Return the routing key
* @return string
*/
const std::string& routingKey() const
{
return _routingKey;
}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 71;
}
/**
* Return the server-assigned and channel specific delivery tag
* @return uint64_t
*/
uint64_t deliveryTag() const
{
return _deliveryTag;
}
/**
* Return the number of messages in the queue
* @return uint32_t
*/
uint32_t messageCount() const
{
return _messageCount;
}
/**
* Return whether the message has been previously delivered to (another) client
* @return bool
*/
bool redelivered() const
{
return _redelivered.get(0);
}
};
/**
* end namespace
*/
}

716
src/basicheaderframe.h Normal file
View File

@ -0,0 +1,716 @@
/**
* Class describing an AMQP basic header frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class BasicHeaderFrame : public HeaderFrame
{
private:
/**
* Weight field, unused but must be sent, always value 0;
* @var uint16_t
*/
uint16_t _weight;
/**
* Body size, sum of the sizes of all body frames following the content header
* @var uint64_t
*/
uint64_t _bodySize;
/**
* First set of booleans
* @var BooleanSet
*/
BooleanSet _bools1;
/**
* Second set of booleans
* @var BooleanSet
*/
BooleanSet _bools2;
/**
* MIME content type
* @var ShortString
*/
ShortString _contentType;
/**
* MIME content encoding
* @var ShortString
*/
ShortString _contentEncoding;
/**
* message header field table
* @var Table
*/
Table _headers;
/**
* Delivery mode (non-persistent (1) or persistent (2))
* @var uint8_t
*/
uint8_t _deliveryMode;
/**
* boolean whether field was sent to us
* @var uint8_t
*/
uint8_t _priority;
/**
* application correlation identifier
* @var ShortString
*/
ShortString _correlationID;
/**
* address to reply to
* @var ShortString
*/
ShortString _replyTo;
/**
* message expiration identifier
* @var ShortString
*/
ShortString _expiration;
/**
* application message identifier
* @var ShortString
*/
ShortString _messageID;
/**
* message timestamp
* @var Timestamp
*/
Timestamp _timestamp;
/**
* message type name
* @var ShortString
*/
ShortString _typeName;
/**
* creating user id
* @var ShortString
*/
ShortString _userID;
/**
* creating application id
* @var ShortString
*/
ShortString _appID;
/**
* Deprecated cluster ID
* @var ShortString
*/
ShortString _clusterID;
protected:
/**
* Encode a header frame to a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
HeaderFrame::fill(buffer);
// fill own fields.
buffer.add(_weight);
buffer.add(_bodySize);
_bools1.fill(buffer);
_bools2.fill(buffer);
if (contentTypeSent() ) { _contentType.fill(buffer); }
if (contentEncodingSent() ) { _contentEncoding.fill(buffer); }
if (headersSent() ) { _headers.fill(buffer); }
if (deliveryModeSent() ) { buffer.add(_deliveryMode); }
if (prioritySent() ) { buffer.add(_priority); }
if (correlationIDSent() ) { _correlationID.fill(buffer); }
if (replyToSent() ) { _replyTo.fill(buffer); }
if (expirationSent() ) { _expiration.fill(buffer); }
if (messageIDSent() ) { _messageID.fill(buffer); }
if (timestampSent() ) { _timestamp.fill(buffer); }
if (typeNameSent() ) { _typeName.fill(buffer); }
if (userIDSent() ) { _userID.fill(buffer); }
if (appIDSent() ) { _appID.fill(buffer); }
}
public:
/**
* Construct an empty basic header frame
*
* All options are set using setter functions.
*
* @param channel channel we're working on
*/
BasicHeaderFrame(uint16_t channel, uint64_t bodySize) :
HeaderFrame(channel, 12), // there are at least 12 bytes sent, weight (2), bodySize (8), property flags (2)
_weight(0),
_bodySize(bodySize),
_deliveryMode(0),
_priority(0)
{}
/**
* Constructor to parse incoming frame
* @param frame
*/
BasicHeaderFrame(ReceivedFrame &frame) :
HeaderFrame(frame),
_weight(frame.nextUint16()),
_bodySize(frame.nextUint64()),
_bools1(frame),
_bools2(frame),
_deliveryMode(0),
_priority(0)
{
if (contentTypeSent()) _contentType = ShortString(frame);
if (contentEncodingSent()) _contentEncoding = ShortString(frame);
if (headersSent()) _headers = Table(frame);
if (deliveryModeSent()) _deliveryMode = frame.nextUint8();
if (prioritySent()) _priority = frame.nextUint8();
if (correlationIDSent()) _correlationID = ShortString(frame);
if (replyToSent()) _replyTo = ShortString(frame);
if (expirationSent()) _expiration = ShortString(frame);
if (messageIDSent()) _messageID = ShortString(frame);
if (timestampSent()) _timestamp = Timestamp(frame);
if (typeNameSent()) _typeName = ShortString(frame);
if (userIDSent()) _userID = ShortString(frame);
if (appIDSent()) _appID = ShortString(frame);
if (clusterIDSent()) _clusterID = ShortString(frame);
}
/**
* Destructor
*/
virtual ~BasicHeaderFrame() {}
/**
* Size of the body
* @return uint64_t
*/
uint64_t bodySize() const
{
return _bodySize;
}
/**
* The class ID
* @return uint16_t
*/
virtual uint16_t classID() const override
{
return 60;
}
/**
* Set the body size
* @param uint64_t sum of all body-sizes sent after this headerframe
*/
void setBodySize(uint64_t size)
{
_bodySize = size;
}
/**
* return the MIME content type
* @return string
*/
const std::string& contentType() const
{
return _contentType;
}
/**
* Set the content type
* @param string
*/
void setContentType(std::string& string)
{
// was there already a content type
if (contentTypeSent()) modifySize(-_contentType.size());
// set the new content type
setContentTypeSent(string.size() > 0);
_contentType = ShortString(string);
// modify the size to include the new content type
if (contentTypeSent()) modifySize(_contentType.size());
}
/**
* Set the bool for content type sent
* @param bool
*/
void setContentTypeSent(bool b)
{
_bools1.set(7, b);
}
/**
* return the MIME content encoding
* @return string
*/
const std::string& contentEncoding() const
{
return _contentEncoding;
}
/**
* Set content encoding
* @param string
*/
void setContentEncoding(std::string& string)
{
// was there already a content encoding?
if(contentEncodingSent()) modifySize(-_contentEncoding.size());
// set new content encoding
setContentEncodingSent(string.size() > 0);
_contentEncoding = ShortString(string);
// modify size to include the new content type
modifySize(_contentEncoding.size());
}
/**
* set contentencoding sent
* @param bool
*/
void setContentEncodingSent(bool b)
{
_bools1.set(6, b);
}
/**
* return the message header field table
* @return Table
*/
const Table& headers() const
{
return _headers;
}
/**
* Set headers
* @param Table
*/
void setHeaders(Table& t)
{
// were the headers already set
if(headersSent()) modifySize(-_headers.size());
// set new headers
setHeadersSent(true);
_headers = t;
// modify size to include the new headers
modifySize(_headers.size());
}
/**
* Set headers sent
* @param bool
*/
void setHeadersSent(bool b)
{
_bools1.set(5, b);
}
/**
* return whether non-persistent (1) or persistent (2)
* @return uint8_t
*/
uint8_t deliveryMode() const
{
return _deliveryMode;
}
/**
* Set deliverymode
* @param uint8_t
*/
void setDeliveryMode(uint8_t val)
{
// was the delivery mode already set
if(deliveryModeSent()) modifySize(-1);
// set delivery mode
setDeliverModeSent(true);
_deliveryMode = Octet(val);
// add new size
modifySize(1);
}
/**
* set delivermode sent
* @param bool
*/
void setDeliverModeSent(bool b)
{
_bools1.set(4, b);
}
/**
* return the message priority (0-9)
* @return uint8_t
*/
uint8_t priority() const
{
return _priority;
}
/**
* Set priority
* @param uint8_t
*/
void setPriority(uint8_t val)
{
// was the priority already sent
if(prioritySent()) modifySize(-1);
// set priority
setPrioritySent(true);
_priority = Octet(val);
// add new size
modifySize(1);
}
/**
* Set priority sent
* @param bool
*/
void setPrioritySent(bool b)
{
_bools1.set(3, b);
}
/**
* return the application correlation identifier
* @return string
*/
const std::string& correlationID() const
{
return _correlationID;
}
/**
* set correlation ID
* @param string
*/
void setCorrelationID(std::string &s)
{
// was the correlation ID sent
if(correlationIDSent()) modifySize(-_correlationID.size());
// set new correlation ID
setCorrelationIDSent(true);
_correlationID = ShortString(s);
// add new size
modifySize(_correlationID.size());
}
/**
* Set correlationIDSent
* @param bool
*/
void setCorrelationIDSent(bool b)
{
_bools1.set(2, b);
}
/**
* return the address to reply to
* @return string
*/
const std::string& replyTo() const
{
return _replyTo;
}
/**
* Set reply to
* @param string
*/
void setReplyTo(std::string &s)
{
// was replyTo set?
if(replyToSent()) modifySize(-_replyTo.size());
// add new replyTo
setReplyToSent(true);
_replyTo = ShortString(s);
modifySize(_replyTo.size());
}
/**
* set reply to sent
* @param bool
*/
void setReplyToSent(bool b)
{
_bools1.set(1, b);
}
/**
* return the message expiration identifier
* @return string
*/
const std::string& expiration() const
{
return _expiration;
}
/**
* Set expiration
* @param string
*/
void setExpiration(std::string &s)
{
// was expiration set?
if(expirationSent()) modifySize(-_expiration.size());
// set expiration
setExpirationSent(true);
_expiration = ShortString(s);
// add new size
modifySize(_expiration.size());
}
/**
* set expiration sent
* @param bool
*/
void setExpirationSent(bool b)
{
_bools1.set(0, b);
}
/**
* return the application message identifier
* @return string
*/
const std::string& messageID() const
{
return _messageID;
}
/**
* set message ID
* @param string
*/
void setMessageID(std::string &s)
{
// was message ID sent?
if(messageIDSent()) modifySize(-_messageID.size());
// set messageID
setMessageIDSent(true);
_messageID = ShortString(s);
// add size
modifySize(_messageID.size());
}
/**
* set messageID sent
* @param bool
*/
void setMessageIDSent(bool b)
{
_bools2.set(7, b);
}
/**
* return the message timestamp
* @return uint64_t
*/
Timestamp timestamp() const
{
return _timestamp;
}
/**
* set timestamp
* @param uint64_t
*/
void setTimestamp(uint64_t val)
{
// was timestamp sent?
if(timestampSent()) modifySize(-_timestamp.size());
// set timestamp
setTimestampSent(true);
_timestamp = Timestamp(val);
// add new size
modifySize(_timestamp.size());
}
/**
* set timestamp sent
* @param bool
*/
void setTimestampSent(bool b)
{
_bools2.set(6, b);
}
/**
* return the message type name
* @return string
*/
const std::string& typeName() const
{
return _typeName;
}
/**
* set typename
* @param string
*/
void setTypeName(std::string &s)
{
// was typename sent?
if(typeNameSent()) modifySize(-_typeName.size());
// add typename
setTypeNameSent(true);
_typeName = ShortString(s);
// add new size
modifySize(_typeName.size());
}
/**
* set typename sent
* @param bool
*/
void setTypeNameSent(bool b)
{
_bools2.set(5, b);
}
/**
* return the creating user id
* @return string
*/
const std::string& userID() const
{
return _userID;
}
/**
* set User ID
* @param string
*/
void setUserID(std::string &s)
{
// was user id sent?
if(userIDSent()) modifySize(-_userID.size());
// set new userID
setUserIDSent(true);
_userID = ShortString(s);
// add size
modifySize(_userID.size());
}
/**
* set user id sent
* @param bool
*/
void setUserIDSent(bool b)
{
_bools2.set(4, b);
}
/**
* return the application id
* @return string
*/
const std::string& appID() const
{
return _appID;
}
/**
* set appID
* @param string
*/
void setAppID(std::string &s)
{
// was app id sent?
if(appIDSent()) modifySize(-_appID.size());
// add new app id
setAppIDSent(true);
_appID = ShortString(s);
// add size
modifySize(_appID.size());
}
/**
* set app id sent
* @param bool
*/
void setAppIDSent(bool b)
{
_bools2.set(3, b);
}
/**
* Return whether a field was sent
* @return bool
*/
bool expirationSent() const { return _bools1.get(0); }
bool replyToSent() const { return _bools1.get(1); }
bool correlationIDSent() const { return _bools1.get(2); }
bool prioritySent() const { return _bools1.get(3); }
bool deliveryModeSent() const { return _bools1.get(4); }
bool headersSent() const { return _bools1.get(5); }
bool contentEncodingSent() const { return _bools1.get(6); }
bool contentTypeSent() const { return _bools1.get(7); }
bool clusterIDSent() const { return _bools2.get(2); }
bool appIDSent() const { return _bools2.get(3); }
bool userIDSent() const { return _bools2.get(4); }
bool typeNameSent() const { return _bools2.get(5); }
bool timestampSent() const { return _bools2.get(6); }
bool messageIDSent() const { return _bools2.get(7); }
};
/**
* End namespace
*/
}

148
src/basicpublishframe.h Normal file
View File

@ -0,0 +1,148 @@
/**
* Class describing a basic publish frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicPublishFrame : public BasicFrame
{
private:
/**
* Variable that no longer is in use
* @var int16_t
*/
int16_t _deprecated;
/**
* the name of the exchange to publish to. An empty exchange name means the default exchange.
* @var ShortString
*/
ShortString _exchange;
/**
* Message routing key
* @var ShortString
*/
ShortString _routingKey;
/**
* BooleanSet, contains:
* 0: mandatory, indicate mandatory routing. If set, server will return unroutable message, otherwise server silently drops message
* 1: immediate, Request immediate delivery, if set and cannot be routed, return unroutable message.
* @var BooleanSet
*/
BooleanSet _bools;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// encode fields
buffer.add(_deprecated);
_exchange.fill(buffer);
_routingKey.fill(buffer);
_bools.fill(buffer);
}
public:
/**
* Construct a basic publish frame
*
* @param channel channel we're working on
* @param exchange name of exchange to publish to @default = ""
* @param routingKey message routing key @default = ""
* @param mandatory indicate mandatory routing @default = false
* @param immediate request immediate delivery @default = false
*/
BasicPublishFrame(uint16_t channel, const std::string& exchange = "", const std::string& routingKey = "", bool mandatory = false, bool immediate = false) :
BasicFrame(channel, exchange.length() + routingKey.length() + 5), // 1 extra per string (for the size), 1 for bools, 2 for deprecated field
_deprecated(0),
_exchange(exchange),
_routingKey(routingKey),
_bools(mandatory, immediate)
{}
/**
* Construct a basic publish frame from a received frame
*
* @param frame received frame to parse
*/
BasicPublishFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_deprecated(frame.nextInt16()),
_exchange(frame),
_routingKey(frame),
_bools(frame)
{}
/**
* Destructor
*/
virtual ~BasicPublishFrame() {}
/**
* Return the name of the exchange to publish to
* @return string
*/
const std::string& exchange() const
{
return _exchange;
}
/**
* Return the routing key
* @return string
*/
const std::string& routingKey() const
{
return _routingKey;
}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 40;
}
/**
* Return the mandatory routing indication
* @return boolean
*/
bool mandatory() const
{
return _bools.get(0);
}
/**
* Return the request immediate delivery indication
* @return boolean
*/
bool immediate() const
{
return _bools.get(1);
}
};
/**
* end namespace
*/
}

122
src/basicqosframe.h Normal file
View File

@ -0,0 +1,122 @@
/**
* Class describing a basic QOS frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class BasicQosFrame : public BasicFrame
{
private:
/**
* specifies the size of the prefetch window in octets
* @var int32_t
*/
int32_t _prefetchSize;
/**
* specifies a prefetch window in terms of whole messages
* @var int16_t
*/
int16_t _prefetchCount;
/**
* apply QoS settings to entire connection
* @var BooleanSet
*/
BooleanSet _global;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// add fields
buffer.add(_prefetchSize);
buffer.add(_prefetchCount);
_global.fill(buffer);
}
public:
/**
* Destructor
*/
virtual ~BasicQosFrame() {}
/**
* Construct a basic qos frame
*
* @param channel channel we're working on
* @param prefetchSize specifies the size of the prefetch window in octets
* @param prefetchCount specifies a prefetch window in terms of whole messages
* @param global apply QoS settings to entire connection
* @default false
*/
BasicQosFrame(uint16_t channel, int32_t prefetchSize = 0, int16_t prefetchCount = 0, bool global = false) :
BasicFrame(channel, 7), // 4 (int32) + 2 (int16) + 1 (bool)
_prefetchSize(prefetchSize),
_prefetchCount(prefetchCount),
_global(global)
{}
BasicQosFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_prefetchSize(frame.nextInt32()),
_prefetchCount(frame.nextInt16()),
_global(frame)
{}
/**
* Return the method ID
* @return uint16_t
*/
uint16_t methodID() const
{
return 10;
}
/**
* Return the prefetch count
* @return int16_t
*/
int16_t prefetchCount() const
{
return _prefetchCount;
}
/**
* returns the value of global
* @return boolean
*/
bool global() const
{
return _global.get(0);
}
/**
* returns the prefetch size
* @return int32_t
*/
int32_t prefetchSize() const
{
return _prefetchSize;
}
};
/**
* End namespace
*/
}

61
src/basicqosokframe.h Normal file
View File

@ -0,0 +1,61 @@
/**
* Class describing a basic QOS frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class BasicQosOKFrame : public BasicFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base, then done (no other params)
BasicFrame::fill(buffer);
}
public:
/**
* Construct a basic qos ok frame
* @param channel channel we're working on
*/
BasicQosOKFrame(uint16_t channel) : BasicFrame(channel, 0) {}
/**
* Constructor based on incoming data
* @param frame
*/
BasicQosOKFrame(ReceivedFrame &frame) : BasicFrame(frame) {}
/**
* Destructor
*/
virtual ~BasicQosOKFrame() {}
/**
* Return the method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 11;
}
};
/**
* End of namespace
*/
}

View File

@ -0,0 +1,88 @@
/**
* Class describing a basic recover-async frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class BasicRecoverAsyncFrame : public BasicFrame {
private:
/**
* Server will try to requeue messages. If requeue is false or requeue attempt fails, messages are discarded or dead-lettered
* @var BooleanSet
*/
BooleanSet _requeue;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// encode fields
_requeue.fill(buffer);
}
public:
/**
* Construct a basic recover async frame from a received frame
*
* @param frame received frame
*/
BasicRecoverAsyncFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_requeue(frame)
{}
/**
* Construct a basic recover-async frame
*
* @param channel channel we're working on
* @param requeue whether to attempt to requeue messages
*/
BasicRecoverAsyncFrame(uint16_t channel, bool requeue = false) :
BasicFrame(channel, 1), //sizeof bool
_requeue(requeue)
{}
/**
* Destructor
*/
virtual ~BasicRecoverAsyncFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 100;
}
/**
* Return whether the server will try to requeue
* @return bool
*/
bool requeue() const
{
return _requeue.get(0);
}
};
/**
* end namespace
*/
}

89
src/basicrecoverframe.h Normal file
View File

@ -0,0 +1,89 @@
/**
* Class describing a basic recover frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class BasicRecoverFrame : public BasicFrame {
private:
/**
* Server will try to requeue messages. If requeue is false or requeue attempt fails, messages are discarded or dead-lettered
* @var BooleanSet
*/
BooleanSet _requeue;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// encode fields
_requeue.fill(buffer);
}
public:
/**
* Construct a basic recover frame from a received frame
*
* @param frame received frame
*/
BasicRecoverFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_requeue(frame)
{}
/**
* Construct a basic recover frame
*
* @param channel channel ID
* @param requeue whether to attempt to requeue messages
*/
BasicRecoverFrame(uint16_t channel, bool requeue = false) :
BasicFrame(channel, 1), //sizeof bool
_requeue(requeue)
{}
/**
* Destructor
*/
virtual ~BasicRecoverFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 110;
}
/**
* Return whether the server will try to requeue
* @return bool
*/
bool requeue() const
{
return _requeue.get(0);
}
};
/**
* end namespace
*/
}

66
src/basicrecoverokframe.h Normal file
View File

@ -0,0 +1,66 @@
/**
* Class describing a basic recover-async frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class BasicRecoverOKFrame : public BasicFrame {
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base then done, no other fields to encode
BasicFrame::fill(buffer);
}
public:
/**
* Construct a basic recover ok frame from a received frame
*
* @param frame received frame
*/
BasicRecoverOKFrame(ReceivedFrame &frame) :
BasicFrame(frame)
{}
/**
* Construct a basic recover ok frame
*
* @param channel channel id
*/
BasicRecoverOKFrame(uint16_t channel) :
BasicFrame(channel, 0)
{}
/**
* Destructor
*/
virtual ~BasicRecoverOKFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 111;
}
};
/**
* End of namespace
*/
}

108
src/basicrejectframe.h Normal file
View File

@ -0,0 +1,108 @@
/**
* Class describing a basic reject frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class BasicRejectFrame : public BasicFrame
{
private:
/**
* server-assigned and channel specific delivery tag
* @var int64_t
*/
int64_t _deliveryTag;
/**
* Server will try to requeue messages. If requeue is false or requeue attempt fails, messages are discarded or dead-lettered
* @var BooleanSet
*/
BooleanSet _requeue;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// encode fields
buffer.add(_deliveryTag);
_requeue.fill(buffer);
}
public:
/**
* construct a basic reject frame from a received frame
*
* @param frame received frame
*/
BasicRejectFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_deliveryTag(frame.nextInt64()),
_requeue(frame)
{}
/**
* Construct a basic reject frame
*
* @param channel channel id
* @param deliveryTag server-assigned and channel specific delivery tag
* @param requeue whether to attempt to requeue messages
*/
BasicRejectFrame(uint16_t channel, int64_t deliveryTag, bool requeue = true) :
BasicFrame(channel, 9), // 8 for uint64_t, 1 for bool
_deliveryTag(deliveryTag),
_requeue(requeue)
{}
/**
* Destructor
*/
virtual ~BasicRejectFrame() {}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 90;
}
/**
* Return the server-assigned and channel specific delivery tag
* @return uint64_t
*/
int64_t deliveryTag() const
{
return _deliveryTag;
}
/**
* Return whether the server will try to requeue
* @return bool
*/
bool requeue() const
{
return _requeue.get(0);
}
};
/**
* end namespace
*/
}

145
src/basicreturnframe.h Normal file
View File

@ -0,0 +1,145 @@
/**
* Class describing a basic return frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP{
/**
* Class implementation
*/
class BasicReturnFrame : public BasicFrame {
private:
/**
* reply code
* @var int16_t
*/
int16_t _replyCode;
/**
* reply text
* @var ShortString
*/
ShortString _replyText;
/**
* the name of the exchange to publish to. An empty exchange name means the default exchange.
* @var ShortString
*/
ShortString _exchange;
/**
* Message routing key
* @var ShortString
*/
ShortString _routingKey;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
BasicFrame::fill(buffer);
// add fields
buffer.add(_replyCode);
_replyText.fill(buffer);
_exchange.fill(buffer);
_routingKey.fill(buffer);
}
public:
/**
* Construct a basic return frame
*
* @param channel channel identifier
* @param replyCode reply code
* @param replyText reply text
* @param exchange name of exchange to publish to
* @param routingKey message routing key
*/
BasicReturnFrame(uint16_t channel, int16_t replyCode, const std::string& replyText = "", const std::string& exchange = "", const std::string& routingKey = "") :
BasicFrame(channel, replyText.length() + exchange.length() + routingKey.length() + 5), // 3 for each string (extra size byte), 2 for uint16_t
_replyCode(replyCode),
_replyText(replyText),
_exchange(exchange),
_routingKey(routingKey)
{}
/**
* Construct a basic return frame from a received frame
*
* @param received frame
*/
BasicReturnFrame(ReceivedFrame &frame) :
BasicFrame(frame),
_replyCode(frame.nextInt16()),
_replyText(frame),
_exchange(frame),
_routingKey(frame)
{}
/**
* Destructor
*/
virtual ~BasicReturnFrame() {}
/**
* Return the name of the exchange to publish to
* @return string
*/
const std::string& exchange() const
{
return _exchange;
}
/**
* Return the routing key
* @return string
*/
const std::string& routingKey() const
{
return _routingKey;
}
/**
* Return the method ID
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 50;
}
/**
* Return the reply text
* @return string
*/
const std::string& replyText() const
{
return _replyText;
}
/**
* Return the reply code
* @return int16_t
*/
int16_t replyCode() const
{
return _replyCode;
}
};
/**
* end namespace
*/
}

91
src/bodyframe.h Normal file
View File

@ -0,0 +1,91 @@
/**
* Class describing an AMQP Body Frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class BodyFrame : public ExtFrame
{
private:
/**
* Payload of the frame
* Payload can be any number of octets
* @var vector<uint8_t>
*/
std::string _payload;
protected:
/**
* Encode a body frame to a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExtFrame::fill(buffer);
// add payload to buffer
buffer.add(_payload);
}
public:
/**
* Construct a body frame
*
* @param channel channel identifier
* @param payload payload of the body
*/
BodyFrame(uint16_t channel, const std::string &payload) :
ExtFrame(channel, payload.size()),
_payload(payload)
{}
/**
* Constructor for incoming data
*
* @param frame received frame to decode
* @return shared pointer to newly created frame
*/
BodyFrame(ReceivedFrame& frame) :
ExtFrame(frame),
_payload(frame.nextData(frame.payloadSize()), frame.payloadSize())
{}
/**
* Destructor
*/
virtual ~BodyFrame() {}
/**
* Return the type of frame
* @return uint8_t
*/
uint8_t type() const
{
return 3;
}
/**
* Return the payload of the body
* @return vector<UOctet>
*/
const std::string& payload() const
{
return _payload;
}
};
/**
* end namespace
*/
}

42
src/channelcloseframe.cpp Normal file
View File

@ -0,0 +1,42 @@
/**
* ChannelCloseFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "channelcloseframe.h"
#include "channelcloseokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ChannelCloseFrame::process(ConnectionImpl *connection)
{
// check if we have a channel
ChannelImpl *channel = connection->channel(this->channel());
// send back an ok frame
connection->send(ChannelCloseOKFrame(this->channel()));
// what if channel doesn't exist?
if (!channel) return false;
// report to the handler
channel->reportChannelError(text());
// done
return true;
}
/**
* End of namespace
*/
}

155
src/channelcloseframe.h Normal file
View File

@ -0,0 +1,155 @@
/**
* Class describing a channel close frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ChannelCloseFrame : public ChannelFrame
{
private:
/**
* The reply code
* @var int16_t
*/
int16_t _code;
/**
* The reply text
* @var ShortString
*/
ShortString _text;
/**
* The failing class id if applicable
* @note: will be 0 if no error occured
* @var int16_t
*/
int16_t _failingClass;
/**
* The failing method id if applicable
* @note: will be 0 if no error occured
* @var int16_t
*/
int16_t _failingMethod;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ChannelFrame::fill(buffer);
// add fields
buffer.add(_code);
_text.fill(buffer);
buffer.add(_failingClass);
buffer.add(_failingMethod);
}
public:
/**
* Construct a channel close frame
*
* @param frame received frame
*/
ChannelCloseFrame(ReceivedFrame &frame) :
ChannelFrame(frame),
_code(frame.nextInt16()),
_text(frame),
_failingClass(frame.nextInt16()),
_failingMethod(frame.nextInt16())
{}
/**
* Construct a channel close frame
*
* @param channel channel we're working on
* @param code reply code
* @param text reply text
* @param failingClass failing class id if applicable
* @param failingMethod failing method id if applicable
*/
ChannelCloseFrame(uint16_t channel, uint16_t code = 0, const std::string& text = "", uint16_t failingClass = 0, uint16_t failingMethod = 0) :
ChannelFrame(channel, (text.length() + 7)), // sizeof code, failingclass, failingmethod (2byte + 2byte + 2byte) + text length + text length byte
_code(code),
_text(text),
_failingClass(failingClass),
_failingMethod(failingMethod)
{}
/**
* Destructor
*/
virtual ~ChannelCloseFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 40;
}
/**
* Get the reply code
* @return uint16_t
*/
uint16_t code() const
{
return _code;
}
/**
* Get the reply text
* @return string
*/
const std::string& text() const
{
return _text;
}
/**
* Get the failing class id if applicable
* @return uint16_t
*/
uint16_t failingClass() const
{
return _failingClass;
}
/**
* Get the failing method id if applicable
* @return uint16_t
*/
uint16_t failingMethod() const
{
return _failingMethod;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,38 @@
/**
* ChannelCloseOkFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "channelcloseokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ChannelCloseOKFrame::process(ConnectionImpl *connection)
{
// we need the appropriate channel
ChannelImpl *channel = connection->channel(this->channel());
// channel does not exist
if(!channel) return false;
// report that the channel is closed
channel->reportClosed();
// done
return true;
}
/**
* End of namespace
*/
}

72
src/channelcloseokframe.h Normal file
View File

@ -0,0 +1,72 @@
/**
* Class describing a channel close acknowledgement frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ChannelCloseOKFrame : public ChannelFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ChannelFrame::fill(buffer);
}
public:
/**
* Construct a channel close ok frame
* @param frame
*/
ChannelCloseOKFrame(ReceivedFrame &frame) :
ChannelFrame(frame)
{}
/**
* Construct a channel close ok frame
*
* @param channel channel we're working on
*/
ChannelCloseOKFrame(uint16_t channel) :
ChannelFrame(channel, 0)
{}
/**
* Destructor
*/
virtual ~ChannelCloseOKFrame() {}
/**
* Method id
*/
virtual uint16_t methodID() const override
{
return 41;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

89
src/channelflowframe.h Normal file
View File

@ -0,0 +1,89 @@
/**
* Class describing a channel flow frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ChannelFlowFrame : public ChannelFrame
{
private:
/**
* Enable or disable the channel flow
* @var BooleanSet
*/
BooleanSet _active;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ChannelFrame::fill(buffer);
// add fields
_active.fill(buffer);
}
public:
/**
* Construct a channel flow frame
*
* @param frame received frame
*/
ChannelFlowFrame(ReceivedFrame &frame) :
ChannelFrame(frame),
_active(frame)
{}
/**
* Construct a channel flow frame
*
* @param channel channel we're working on
* @param active enable or disable channel flow
*/
ChannelFlowFrame(uint16_t channel, bool active) :
ChannelFrame(channel, 1), //sizeof bool
_active(active)
{}
/**
* Destructor
*/
virtual ~ChannelFlowFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 20;
}
/**
* Is channel flow active or not?
* @return bool
*/
bool active() const
{
return _active.get(0);
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,39 @@
/**
* ChannelFlowOkFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "channelflowokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ChannelFlowOKFrame::process(ConnectionImpl *connection)
{
// we need the appropriate channel
ChannelImpl *channel = connection->channel(this->channel());
// channel does not exist
if(!channel) return false;
// is the flow active?
if (active()) channel->reportResumed();
else channel->reportPaused();
// done
return true;
}
/**
* End of namespace
*/
}

94
src/channelflowokframe.h Normal file
View File

@ -0,0 +1,94 @@
/**
* Class describing a channel flow acknowledgement frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ChannelFlowOKFrame : public ChannelFrame
{
private:
/**
* Is the channel flow currently active?
* @var BooleanSet
*/
BooleanSet _active;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ChannelFrame::fill(buffer);
// add fields
_active.fill(buffer);
}
public:
/**
* Construct a channel flow frame
*
* @param frame received frame to decode
*/
ChannelFlowOKFrame(ReceivedFrame &frame) :
ChannelFrame(frame),
_active(frame)
{}
/**
* Construct a channel flow frame
*
* @param channel channel we're working on
* @param active enable or disable channel flow
*/
ChannelFlowOKFrame(uint16_t channel, bool active) :
ChannelFrame(channel, 1), //sizeof bool
_active(active)
{}
/**
* Destructor
*/
virtual ~ChannelFlowOKFrame() {}
/**
* Method id
*/
virtual uint16_t methodID() const override
{
return 21;
}
/**
* Is channel flow active?
* @return bool
*/
bool active() const
{
return _active.get(0);
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

54
src/channelframe.h Normal file
View File

@ -0,0 +1,54 @@
/**
* Class describing an AMQP channel frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ChannelFrame : public MethodFrame
{
protected:
/**
* Constructor for a channelFrame
*
* @param channel channel we're working on
* @param size size of the frame
*/
ChannelFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {}
/**
* Constructor that parses an incoming frame
* @param frame The received frame
*/
ChannelFrame(ReceivedFrame &frame) : MethodFrame(frame) {}
public:
/**
* Destructor
*/
virtual ~ChannelFrame() {}
/**
* Class id
* @return uint16_t
*
* @todo check if override keyword is used in all places
*/
virtual uint16_t classID() const override
{
return 20;
}
};
/**
* end namespace
*/
}

385
src/channelimpl.cpp Normal file
View File

@ -0,0 +1,385 @@
/**
* Channel.cpp
*
* Implementation for a channel
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "channelopenframe.h"
#include "channelflowframe.h"
#include "channelcloseframe.h"
#include "transactionselectframe.h"
#include "transactioncommitframe.h"
#include "transactionrollbackframe.h"
#include "exchangedeclareframe.h"
#include "exchangedeleteframe.h"
#include "exchangebindframe.h"
#include "exchangeunbindframe.h"
#include "queuedeclareframe.h"
#include "queuebindframe.h"
#include "queueunbindframe.h"
#include "queuepurgeframe.h"
#include "queuedeleteframe.h"
#include <iostream>
/**
* Set up namespace
*/
namespace AMQP {
/**
* Construct a channel object
* @param parent
* @param connection
* @param handler
*/
ChannelImpl::ChannelImpl(Channel *parent, Connection *connection, ChannelHandler *handler) :
_parent(parent),
_connection(connection),
_handler(handler)
{
// add the channel to the connection
_id = connection->_implementation.add(this);
// check if the id is valid
if (_id == 0)
{
// this is invalid
_state = state_closed;
// invalid id, this channel can not exist
handler->onChannelError(_parent, "Max number of channels reached");
}
else
{
// busy connecting
_state = state_connected;
// valid id, send a channel open frame
send(ChannelOpenFrame(_id));
}
}
/**
* Destructor
*/
ChannelImpl::~ChannelImpl()
{
// remove this channel from the connection
_connection->_implementation.remove(this);
// leap out if already disconnected
if (!connected()) return;
// close the channel now
close();
}
/**
* Pause deliveries on a channel
*
* This will stop all incoming messages
*
* This method returns true if the request to pause has been sent to the
* broker. This does not necessarily mean that the channel is already
* paused.
*
* @return bool
*/
bool ChannelImpl::pause()
{
// must be connected
if (!connected()) return false;
// send a flow frame
send(ChannelFlowFrame(_id, false));
// done
return true;
}
/**
* Resume a paused channel
*
* @return bool
*/
bool ChannelImpl::resume()
{
// must be connected
if (!connected()) return false;
// send a flow frame
send(ChannelFlowFrame(_id, true));
// done
return true;
}
/**
* Start a transaction
* @return bool
*/
bool ChannelImpl::startTransaction()
{
// must be connected
if (!connected()) return false;
// send a flow frame
send(TransactionSelectFrame(_id));
// done
return true;
}
/**
* Commit the current transaction
* @return bool
*/
bool ChannelImpl::commitTransaction()
{
// must be connected
if (!connected()) return false;
// send a flow frame
send(TransactionCommitFrame(_id));
// done
return true;
}
/**
* Rollback the current transaction
* @return bool
*/
bool ChannelImpl::rollbackTransaction()
{
std::cout << "send rollback frame" << std::endl;
// must be connected
if (!connected()) return false;
// send a flow frame
send(TransactionRollbackFrame(_id));
// done
return true;
}
/**
* Close the current channel
* @return bool
*/
bool ChannelImpl::close()
{
// must be connected
if (!connected()) return false;
// send a flow frame
send(ChannelCloseFrame(_id));
// now it is closed
_state = state_closed;
// done
return true;
}
/**
* declare an exchange
* @param name name of the exchange to declare
* @param type type of exchange
* @param flags additional settings for the exchange
* @param arguments additional arguments
* @return bool
*/
bool ChannelImpl::declareExchange(const std::string &name, ExchangeType type, int flags, const Table &arguments)
{
// must be connected
if(!connected()) return false;
std::string exchangeType;
if(type == ExchangeType::fanout) exchangeType = "fanout";
if(type == ExchangeType::direct) exchangeType = "direct";
if(type == ExchangeType::topic) exchangeType = "topic";
if(type == ExchangeType::headers)exchangeType = "headers";
// send declare exchange frame
send(ExchangeDeclareFrame(_id, name, exchangeType, (flags & passive) != 0, (flags & durable) != 0, (flags & nowait) != 0, arguments));
// done
return true;
}
/**
* bind an exchange
* @param source exchange which binds to target
* @param target exchange to bind to
* @param routingKey routing key
* @param flags additional flags
* @param arguments additional arguments for binding
* @return bool
*/
bool ChannelImpl::bindExchange(const std::string &source, const std::string &target, const std::string &routingkey, int flags, const Table &arguments)
{
// must be connected
if(!connected()) return false;
// send exchange bind frame
send(ExchangeBindFrame(_id, target, source, routingkey, (flags & nowait) != 0, arguments));
//done
return true;
}
/**
* unbind two exchanges
* @param source the source exchange
* @param target the target exchange
* @param routingkey the routing key
* @param flags optional flags
* @param arguments additional unbind arguments
* @return bool
*/
bool ChannelImpl::unbindExchange(const std::string &source, const std::string &target, const std::string &routingkey, int flags, const Table &arguments)
{
// must be connected
if (!connected()) return false;
// send exchange unbind frame
send(ExchangeUnbindFrame(_id, target, source, routingkey, (flags & nowait) != 0, arguments));
// done
return true;
}
/**
* remove an exchange
* @param name name of the exchange to remove
* @param flags additional settings for deleting the exchange
* @return bool
*/
bool ChannelImpl::removeExchange(const std::string &name, int flags)
{
// must be connected
if (!connected()) return false;
// send delete exchange frame
send(ExchangeDeleteFrame(_id, name, (flags & ifunused) != 0, (flags & nowait) != 0));
// done
return true;
}
/**
* declare a queue
* @param name queue name
* @param flags additional settings for the queue
* @param arguments additional arguments
* @return bool
*/
bool ChannelImpl::declareQueue(const std::string &name, int flags, const Table &arguments)
{
// must be connected
if (!connected()) return false;
// send the queuedeclareframe
send(QueueDeclareFrame(_id, name, (flags & passive) != 0, (flags & durable) != 0, (flags & durable) != 0, (flags & autodelete) != 0, (flags & nowait) != 0, arguments));
// done
return true;
}
/**
* Bind a queue to an exchange
* @param exchangeName name of the exchange to bind to
* @param queueName name of the queue
* @param routingkey routingkey
* @param flags additional flags
* @param arguments additional arguments
* @return bool
*/
bool ChannelImpl::bindQueue(const std::string &exchangeName, const std::string &queueName, const std::string &routingkey, int flags, const Table &arguments)
{
// must be connected
if(!connected()) return false;
// send the bind queue frame
send(QueueBindFrame(_id, queueName, exchangeName, routingkey, (flags & nowait) != 0, arguments));
// done
return true;
}
/**
* Unbind a queue from an exchange
* @param exchange the source exchange
* @param queue the target queue
* @param routingkey the routing key
* @param arguments additional bind arguments
* @return bool
*/
bool ChannelImpl::unbindQueue(const std::string &exchange, const std::string &queue, const std::string &routingkey, const Table &arguments)
{
// must be connected
if(!connected()) return false;
// send the unbind queue frame
send(QueueUnbindFrame(_id, queue, exchange, routingkey, arguments));
// done
return true;
}
/**
* Purge a queue
* @param queue queue to purge
* @param flags additional flags
* @return bool
*/
bool ChannelImpl::purgeQueue(const std::string &name, int flags)
{
// must be connected
if(!connected()) return false;
// send the queue purge frame
send(QueuePurgeFrame(_id, name, (flags & nowait) != 0));
// done
return true;
}
/**
* Remove a queue
* @param queue queue to remove
* @param flags additional flags
* @return bool
*/
bool ChannelImpl::removeQueue(const std::string &name, int flags)
{
// must be connected
if(!connected()) return false;
// send the remove queue frame
send(QueueDeleteFrame(_id, name, (flags & ifunused) != 0,(flags & ifempty) != 0,(flags & nowait) != 0));
// done
return true;
}
/**
* Send a frame over the channel
* @param frame frame to send
* @return size_t number of bytes sent
*/
size_t ChannelImpl::send(const Frame &frame)
{
// send to tcp connection
return _connection->_implementation.send(frame);
}
/**
* End of namespace
*/
}

72
src/channelopenframe.h Normal file
View File

@ -0,0 +1,72 @@
/**
* Class describing a channel open frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ChannelOpenFrame : public ChannelFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const
{
// call base
ChannelFrame::fill(buffer);
// add deprecated data
ShortString unused;
// add to the buffer
unused.fill(buffer);
}
public:
/**
* Constructor to create a channelOpenFrame
*
* @param channel channel we're working on
*/
ChannelOpenFrame(uint16_t channel) : ChannelFrame(channel, 1) {} // 1 for the deprecated shortstring size
/**
* Construct to parse a received frame
* @param frame
*/
ChannelOpenFrame(ReceivedFrame &frame) : ChannelFrame(frame)
{
// deprecated argument
ShortString unused(frame);
}
/**
* Destructor
*/
virtual ~ChannelOpenFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 10;
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,38 @@
/**
* ChannelOpenOkFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "channelopenokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ChannelOpenOKFrame::process(ConnectionImpl *connection)
{
// we need the appropriate channel
ChannelImpl *channel = connection->channel(this->channel());
// channel does not exist
if(!channel) return false;
// report that the channel is open
channel->reportReady();
// done
return true;
}
/**
* End of namespace
*/
}

78
src/channelopenokframe.h Normal file
View File

@ -0,0 +1,78 @@
/**
* Class describing a channel open acknowledgement frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ChannelOpenOKFrame : public ChannelFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ChannelFrame::fill(buffer);
// create and encode the silly deprecated argument
LongString unused;
// add to the buffer
unused.fill(buffer);
}
public:
/**
* Constructor based on client information
* @param channel Channel identifier
*/
ChannelOpenOKFrame(uint16_t channel) : ChannelFrame(channel, 4) {} // 4 for the longstring size value
/**
* Constructor based on incoming frame
* @param frame
*/
ChannelOpenOKFrame(ReceivedFrame &frame) : ChannelFrame(frame)
{
// read in a deprecated argument
LongString unused(frame);
}
/**
* Destructor
*/
virtual ~ChannelOpenOKFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 11;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,37 @@
/**
* ConnectionCloseFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "connectioncloseframe.h"
#include "connectioncloseokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ConnectionCloseFrame::process(ConnectionImpl *connection)
{
// send back the ok frame
connection->send(ConnectionCloseOKFrame());
// no need to check for a channel, the error is connection wide
// report the error on the connection
connection->reportConnectionError(text());
// done
return true;
}
/**
* End of namespace
*/
}

155
src/connectioncloseframe.h Normal file
View File

@ -0,0 +1,155 @@
/**
* Class describing connection close frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionCloseFrame : public ConnectionFrame
{
private:
/**
* The reply code
* @var uint16_t
*/
uint16_t _code;
/**
* The reply text
* @var ShortString
*/
ShortString _text;
/**
* Class id for failing class, if applicable
* Will be 0 in absence of errors
* @var uint16_t
*/
uint16_t _failingClass;
/**
* Method id for failinv class, if applicable
* Will be 0 in absence of errors
* @var uint16_t
*/
uint16_t _failingMethod;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// add fields
buffer.add(_code);
_text.fill(buffer);
buffer.add(_failingClass);
buffer.add(_failingMethod);
}
public:
/**
* Construct a connection close frame from a received frame
*
* @param frame received frame
*/
ConnectionCloseFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_code(frame.nextUint16()),
_text(frame),
_failingClass(frame.nextUint16()),
_failingMethod(frame.nextUint16())
{}
/**
* Construct a connection close frame
*
* @param code the reply code
* @param text the reply text
* @param failingClass id of the failing class if applicable
* @param failingMethod id of the failing method if applicable
*/
ConnectionCloseFrame(uint16_t code, const std::string text, uint16_t failingClass = 0, uint16_t failingMethod = 0) :
ConnectionFrame(text.length() + 7), // 1 for extra string byte, 2 for each uint16
_code(code),
_text(text),
_failingClass(failingClass),
_failingMethod(failingMethod)
{}
/**
* Destructor
*/
virtual ~ConnectionCloseFrame() {}
/**
* Method id
* @return uint16_t
*/
uint16_t methodID() const
{
return 50;
}
/**
* Get the reply code
* @return uint16_t
*/
uint16_t code() const
{
return _code;
}
/**
* Get the reply text
* @return string
*/
const std::string& text() const
{
return _text;
}
/**
* Get the failing class id if applicable
* @return uint16_t
*/
uint16_t failingClass() const
{
return _failingClass;
}
/**
* Get the failing method id if applicable
* @return uint16_t
*/
uint16_t failingMethod() const
{
return _failingMethod;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,63 @@
/**
* Class describing connection close acknowledgement frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionCloseOKFrame : public ConnectionFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
}
public:
/**
* Constructor based on a received frame
*
* @param frame received frame
*/
ConnectionCloseOKFrame(ReceivedFrame &frame) :
ConnectionFrame(frame)
{}
/**
* construct a channelcloseokframe object
*/
ConnectionCloseOKFrame() :
ConnectionFrame(0)
{}
/**
* Destructor
*/
virtual ~ConnectionCloseOKFrame() {}
/**
* Method id
*/
virtual uint16_t methodID() const override
{
return 51;
}
};
/**
* end namespace
*/
}

54
src/connectionframe.h Normal file
View File

@ -0,0 +1,54 @@
/**
* Class describing an AMQP connection frame
*
* @copyright 2014 Copernica BV
*/
/**
* Class definition
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionFrame : public MethodFrame
{
protected:
/**
* Constructor for a connectionFrame
*
* A connection frame never has a channel identifier, so this is passed
* as zero to the base constructor
*
* @param size size of the frame
*/
ConnectionFrame(uint32_t size) : MethodFrame(0, size) {}
/**
* Constructor based on a received frame
* @param frame
*/
ConnectionFrame(ReceivedFrame &frame) : MethodFrame(frame) {}
public:
/**
* Destructor
*/
virtual ~ConnectionFrame() {}
/**
* Class id
* @return uint16_t
*/
virtual uint16_t classID() const override
{
return 10;
}
};
/**
* end namespace
*/
}

204
src/connectionimpl.cpp Normal file
View File

@ -0,0 +1,204 @@
/**
* ConnectionImpl.cpp
*
* Implementation of an AMQP connection
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "protocolheaderframe.h"
#include "exception.h"
#include "protocolexception.h"
/**
* set namespace
*/
namespace AMQP {
/**
* Destructor
*/
ConnectionImpl::~ConnectionImpl()
{
// still connected
if (_state == state_invalid) return;
// still in a connected state - should we send the close frame?
close();
}
/**
* Initialize the connection
*/
void ConnectionImpl::initialize()
{
// we need a protocol header
ProtocolHeaderFrame header;
// send out the protocol header
send(header);
}
/**
* Add a channel to the connection, and return the channel ID that it
* is allowed to use, or 0 when no more ID's are available
* @param channel
* @return uint16_t
*/
uint16_t ConnectionImpl::add(ChannelImpl *channel)
{
// check if we have exceeded the limit already
if (_maxChannels > 0 && _channels.size() >= _maxChannels) return 0;
// keep looping to find an id that is not in use
while (true)
{
// is this id in use?
if (_nextFreeChannel > 0 && _channels.find(_nextFreeChannel) == _channels.end()) break;
// id is in use, move on
_nextFreeChannel++;
}
// we have a new channel
_channels[_nextFreeChannel] = channel;
// done
return _nextFreeChannel++;
}
/**
* Remove a channel
* @param channel
*/
void ConnectionImpl::remove(ChannelImpl *channel)
{
// skip zero channel
if (channel->id() == 0) return;
// remove it
_channels.erase(channel->id());
}
/**
* Parse the buffer into a recognized frame
*
* Every time that data comes in on the connection, you should call this method to parse
* the incoming data, and let it handle by the AMQP library. This method returns the number
* of bytes that were processed.
*
* If not all bytes could be processed because it only contained a partial frame, you should
* call this same method later on when more data is available. The AMQP library does not do
* any buffering, so it is up to the caller to ensure that the old data is also passed in that
* later call.
*
* @param buffer buffer to decode
* @param size size of the buffer to decode
* @return number of bytes that were processed
*/
size_t ConnectionImpl::parse(char *buffer, size_t size)
{
// number of bytes processed
size_t processed = 0;
// create a monitor object that checks if the connection still exists
Monitor monitor(this);
// keep looping until we have processed all bytes, and the monitor still
// indicates that the connection is in a valid state
while (size > 0 && monitor.valid())
{
// prevent protocol exceptions
try
{
// try to recognize the frame
ReceivedFrame receivedFrame(buffer, size, _maxFrame);
if (!receivedFrame.complete()) return processed;
// process the frame
receivedFrame.process(this);
// number of bytes processed
size_t bytes = receivedFrame.totalSize();
// add bytes
processed += bytes; size -= bytes; buffer += bytes;
}
catch (const ProtocolException &exception)
{
// something terrible happened on the protocol (like data out of range)
reportConnectionError(exception.what());
// done
return processed;
}
}
// done
return processed;
}
/**
* Close the connection
* This will close all channels
* @return bool
*/
bool ConnectionImpl::close()
{
// leap out if not yet connected
if (_state != state_connected) return false;
// loop over all channels
for (auto iter = _channels.begin(); iter != _channels.end(); iter++)
{
// close the channel
iter->second->close();
}
// we're in a new state
_state = state_invalid;
// done
return true;
}
/**
* Send a frame over the connection
* @param frame The frame to send
* @return size_t Number of bytes sent
*/
size_t ConnectionImpl::send(const Frame &frame)
{
std::cout << "send frame of " << frame.totalSize() << " bytes" << std::endl;
// we need an output buffer
OutBuffer buffer(frame.totalSize());
// fill the buffer
frame.fill(buffer);
// append an end of frame byte (but not when still negotiating the protocol)
if (_state != state_protocol) buffer.add((uint8_t)206);
// send the buffer
_handler->onData(_parent, buffer.data(), buffer.size());
// done
return buffer.size();
}
/**
* Set the Quality of Service (QOS) of the entire connection
* @param prefetchSize maximum size (in octets) of messages to be prefetched
* @param prefetchCount maximum number of messages to prefetch
* @return bool whether the Qos frame is sent.
*/
//bool Connection::setQOS(uint32_t prefetchSize = 0, uint16_t prefetchCount = 0)
//{
// BasicQosFrame *frame = new BasicQosFrame(0, prefetchSize, prefetchCount, true);
// if(_connection->send(frame->buffer(), frame->totalSize()) != frame->totalSize()) return false;
//
// return true;
//}
}

108
src/connectionopenframe.h Normal file
View File

@ -0,0 +1,108 @@
/**
* Class describing connection vhost open frame
*
* This frame is sent by the client after the connection is started and the
* capacity has been tuned, to open the connection to a specific vhost.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionOpenFrame : public ConnectionFrame
{
private:
/**
* Virtual host name
* @var ShortString
*/
ShortString _vhost;
/**
* deprecated values, still need to read them somehow in the constructor
* @var ShortString
*/
ShortString _deprecatedCapabilities;
/**
* More deprecated values
* @var BooleanSet
*/
BooleanSet _deprecatedInsist;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// encode fields
_vhost.fill(buffer);
_deprecatedCapabilities.fill(buffer);
_deprecatedInsist.fill(buffer);
}
public:
/**
* Open a virtual host
*
* @param vhost name of virtual host to open
*/
ConnectionOpenFrame(const std::string &vhost) :
ConnectionFrame(vhost.length() + 3), // length of vhost + byte to encode this length + deprecated shortstring size + deprecated bool
_vhost(vhost),
_deprecatedCapabilities(""),
_deprecatedInsist()
{}
/**
* Constructor based on a received frame
*
* @param frame received frame
*/
ConnectionOpenFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_vhost(frame),
_deprecatedCapabilities(frame),
_deprecatedInsist(frame)
{}
/**
* Destructor
*/
virtual ~ConnectionOpenFrame() {}
/**
* Method id
*/
uint16_t methodID() const
{
return 40;
}
/**
* Get the vhost name
* @return string
*/
const std::string& vhost() const
{
return _vhost;
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,32 @@
/**
* ConnectionOpenOKFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "connectionopenokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ConnectionOpenOKFrame::process(ConnectionImpl *connection)
{
// all is ok, mark the connection as connected
connection->setConnected();
// done
return true;
}
/**
* End of namespace
*/
}

View File

@ -0,0 +1,87 @@
/**
* Class describing connection vhost open acknowledgement frame
*
* Message sent by the server to the client to confirm that a connection to
* a vhost could be established
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionOpenOKFrame : public ConnectionFrame
{
private:
/**
* Deprecated field we need to read
* @var ShortString
*/
ShortString _deprecatedKnownHosts;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// add deprecaed field
_deprecatedKnownHosts.fill(buffer);
}
public:
/**
* Construct a connectionopenokframe from a received frame
*
* @param frame received frame
*/
ConnectionOpenOKFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_deprecatedKnownHosts(frame)
{}
/**
* Construct a connectionopenokframe
*
*/
ConnectionOpenOKFrame() :
ConnectionFrame(1), // for the deprecated shortstring
_deprecatedKnownHosts("")
{}
/**
* Destructor
*/
virtual ~ConnectionOpenOKFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 41;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,88 @@
/**
* Class describing connection setup security challenge
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionSecureFrame : public ConnectionFrame
{
private:
/**
* The security challenge
* @var LongString
*/
LongString _challenge;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// encode fields
_challenge.fill(buffer);
}
public:
/**
* Construct a connection security challenge frame
*
* @param challenge the challenge
*/
ConnectionSecureFrame(const std::string& challenge) :
ConnectionFrame(challenge.length() + 4), // 4 for the length of the challenge (uint32_t)
_challenge(challenge)
{}
/**
* Construct a connection secure frame from a received frame
*
* @param frame received frame
*/
ConnectionSecureFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_challenge(frame)
{}
/**
* Destructor
*/
virtual ~ConnectionSecureFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 20;
}
/**
* Get the challenge
* @return string
*/
const std::string& challenge() const
{
return _challenge;
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,88 @@
/**
* Class describing connection setup security challenge response
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionSecureOKFrame : public ConnectionFrame
{
private:
/**
* The security challenge response
* @var LongString
*/
LongString _response;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// add fields
_response.fill(buffer);
}
public:
/**
* Construct a connection security challenge response frame
*
* @param response the challenge response
*/
ConnectionSecureOKFrame(const std::string& response) :
ConnectionFrame(response.length() + 4), //response length + uint32_t for encoding the length
_response(response)
{}
/**
* Construct a connection security challenge response frame from a received frame
*
* @param frame received frame
*/
ConnectionSecureOKFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_response(frame)
{}
/**
* Destructor
*/
virtual ~ConnectionSecureOKFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 21;
}
/**
* Get the challenge response
* @return string
*/
const std::string& response() const
{
return _response;
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,52 @@
/**
* ConnectionStartFrame.h
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "connectionstartframe.h"
#include "connectionstartokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the connection start frame
* @param connection
* @return bool
* @internal
*/
bool ConnectionStartFrame::process(ConnectionImpl *connection)
{
// @todo we must still be in protocol handshake mode
// move connection to handshake mode
connection->setProtocolOk();
// the peer properties
Table properties;
// fill the peer properties
properties["product"] = "Copernica AMQP library";
properties["version"] = "0.1";
properties["platform"] = "Ubuntu";
properties["copyright"] = "Copyright 2014 Copernica BV";
properties["information"] = "";
// the start ok frame we'd like to send back
ConnectionStartOKFrame frame(properties, "PLAIN", connection->login().saslPlain(), "en_US");
// send back a connection start ok frame
connection->send(frame);
// done
return true;
}
/**
* End of namespace
*/
}

182
src/connectionstartframe.h Normal file
View File

@ -0,0 +1,182 @@
/**
* Class describing initial connection setup
*
* This frame is sent by the server to the client, right after the connection
* is opened. It contains the initial connection properties, and the protocol
* number.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionStartFrame : public ConnectionFrame
{
private:
/**
* Major AMQP version number
* @var uint8_t
*/
uint8_t _major;
/**
* Minor AMQP version number
* @var uint8_t
*/
uint8_t _minor;
/**
* Additional server properties
* @note: exact properties are not specified
* and are implementation-dependent
* @var Table
*/
Table _properties;
/**
* Available security mechanisms
* @var LongString
*/
LongString _mechanisms;
/**
* Available message locales
* @var LongString
*/
LongString _locales;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// encode all fields
buffer.add(_major);
buffer.add(_minor);
_properties.fill(buffer);
_mechanisms.fill(buffer);
_locales.fill(buffer);
}
public:
/**
* Client-side constructer for a connection start frame
*
* @param major major protocol version
* @param minor minor protocol version
* @param properties server properties
* @param mechanisms available security mechanisms
* @param locales available locales
*/
ConnectionStartFrame(uint8_t major, uint8_t minor, const Table& properties, const std::string& mechanisms, const std::string& locales) :
ConnectionFrame((properties.size() + mechanisms.length() + locales.length() + 10)), // 4 for each longstring (size-uint32), 2 major/minor
_major(major),
_minor(minor),
_properties(properties),
_mechanisms(mechanisms),
_locales(locales)
{}
/**
* Construct a connection start frame from a received frame
*
* @param frame received frame
*/
ConnectionStartFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_major(frame.nextUint8()),
_minor(frame.nextUint8()),
_properties(frame),
_mechanisms(frame),
_locales(frame)
{}
/**
* Destructor
*/
virtual ~ConnectionStartFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 10;
}
/**
* Major AMQP version number
* @return uint8_t
*/
uint8_t major() const
{
return _major;
}
/**
* Minor AMQP version number
* @return uint8_t
*/
uint8_t minor() const
{
return _minor;
}
/**
* Additional server properties
* @note: exact properties are not specified
* and are implementation-dependent
*
* @return Table
*/
const Table& properties() const
{
return _properties;
}
/**
* Available security mechanisms
* @return string
*/
const std::string &mechanisms() const
{
return _mechanisms;
}
/**
* Available message locales
* @return string
*/
const std::string &locales() const
{
return _locales;
}
/**
* Process the connection start frame
* @param connection
* @return bool
* @internal
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,148 @@
/**
* Class describing initial connection setup acknowledge frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionStartOKFrame : public ConnectionFrame
{
private:
/**
* Additional client properties
* @note: exact properties are not specified
* and are implementation-dependent
* @var Table
*/
Table _properties;
/**
* The selected security mechanism
* @var ShortString
*/
ShortString _mechanism;
/**
* The security response
* @var LongString
*/
LongString _response;
/**
* The selected locale
* @var ShortString
*/
ShortString _locale;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// add fields
_properties.fill(buffer);
_mechanism.fill(buffer);
_response.fill(buffer);
_locale.fill(buffer);
}
public:
/**
* Construct a connection start ok frame from a received frame
*
* @param frame received frame
*/
ConnectionStartOKFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_properties(frame),
_mechanism(frame),
_response(frame),
_locale(frame)
{}
/**
* Construct a connection start ok frame
*
* @param properties client propertes
* @param mechanism selected security mechanism
* @param response security response data
* @param locale selected locale.
*/
ConnectionStartOKFrame(const Table& properties, const std::string& mechanism, const std::string& response, const std::string& locale) :
ConnectionFrame((properties.size() + mechanism.length() + response.length() + locale.length() + 6)), // 1 byte extra per shortstring, 4 per longstring
_properties(properties),
_mechanism(mechanism),
_response(response),
_locale(locale)
{}
/**
* Destructor
*/
virtual ~ConnectionStartOKFrame() {}
/**
* Method id
*/
uint16_t methodID() const
{
return 11;
}
/**
* Additional client properties
* @note: exact properties are not specified
* and are implementation-dependent
* @return Table
*/
const Table& properties() const
{
return _properties;
}
/**
* The selected security mechanism
* @return string
*/
const std::string& mechanism() const
{
return _mechanism;
}
/**
* The security response
* @return string
*/
const std::string& response() const
{
return _response;
}
/**
* The selected locale
* @return string
*/
const std::string locale() const
{
return _locale;
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,48 @@
/**
* ConnectionTuneFrame.cpp
*
* @copyright 2014
*/
#include "includes.h"
#include "connectiontuneframe.h"
#include "connectiontuneokframe.h"
#include "connectionopenframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ConnectionTuneFrame::process(ConnectionImpl *connection)
{
// @todo this is only allowed when the connection is set up
// remember this in the connection
connection->setCapacity(channelMax(), frameMax());
// send a tune-ok frame back
ConnectionTuneOKFrame okframe(channelMax(), frameMax(), heartbeat());
// send it back
connection->send(okframe);
// and finally we start to open the frame
ConnectionOpenFrame openframe(connection->login().vhost());
// send the open frame
connection->send(openframe);
// done
return true;
}
/**
* End of namespace
*/
}

139
src/connectiontuneframe.h Normal file
View File

@ -0,0 +1,139 @@
/**
* Class describing connection tune frame
*
* A connection tune frame is sent by the server after the connection is
* started to negotiate the max frame size, the max number of channels
* and the heartbeat interval.
*
* When this frame is received, we should send back a tune-ok frame to
* confirm that we have received this frame.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionTuneFrame : public ConnectionFrame
{
private:
/**
* Proposed maximum number of channels
* @var uint16_t
*/
uint16_t _channels;
/**
* Proposed maximum frame size
* @var uint32_t
*/
uint32_t _frameMax;
/**
* Desired heartbeat delay
* @var uint16_t
*/
uint16_t _heartbeat;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// add fields
buffer.add(_channels);
buffer.add(_frameMax);
buffer.add(_heartbeat);
}
public:
/**
* Construct a connection tuning frame
*
* @param channels proposed maximum number of channels
* @param frameMax proposed maximum frame size
* @param heartbeat desired heartbeat delay
*/
ConnectionTuneFrame(uint16_t channels, uint32_t frameMax, uint16_t heartbeat) :
ConnectionFrame(8), // 2x uint16_t, 1x uint32_t
_channels(channels),
_frameMax(frameMax),
_heartbeat(heartbeat)
{}
/**
* Construct a connection tune frame from a received frame
*
* @param frame received frame
*/
ConnectionTuneFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_channels(frame.nextUint16()),
_frameMax(frame.nextUint32()),
_heartbeat(frame.nextUint16())
{}
/**
* Destructor
*/
virtual ~ConnectionTuneFrame() {}
/**
* Method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 30;
}
/**
* Proposed maximum number of channels
* @return _uint16_t
*/
uint16_t channelMax() const
{
return _channels;
}
/**
* Proposed maximum frame size
* @return _uint32_t
*/
uint32_t frameMax() const
{
return _frameMax;
}
/**
* Desired heartbeat delay
* @return uint16_t
*/
uint16_t heartbeat() const
{
return _heartbeat;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

124
src/connectiontuneokframe.h Normal file
View File

@ -0,0 +1,124 @@
/**
* Class describing connection tune acknowledgement frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ConnectionTuneOKFrame : public ConnectionFrame
{
private:
/**
* Proposed maximum number of channels
* @var uint16_t
*/
uint16_t _channels;
/**
* Proposed maximum frame size
* @var uint32_t
*/
uint32_t _frameMax;
/**
* Desired heartbeat delay
* @var uint16_t
*/
uint16_t _heartbeat;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ConnectionFrame::fill(buffer);
// add fields
buffer.add(_channels);
buffer.add(_frameMax);
buffer.add(_heartbeat);
}
public:
/**
* Construct a connection tune frame from a received frame
*
* @param frame received frame
*/
ConnectionTuneOKFrame(ReceivedFrame &frame) :
ConnectionFrame(frame),
_channels(frame.nextUint16()),
_frameMax(frame.nextUint32()),
_heartbeat(frame.nextUint16())
{}
/**
* Construct a connection tuning acknowledgement frame
*
* @param channels selected maximum number of channels
* @param frame selected maximum frame size
* @param heartbeat desired heartbeat delay
*/
ConnectionTuneOKFrame(uint16_t channels, uint32_t frameMax, uint16_t heartbeat) :
ConnectionFrame(8), // 2x uint16_t, 1x uint32_t
_channels(channels),
_frameMax(frameMax),
_heartbeat(heartbeat)
{}
/**
* Destructor
*/
virtual ~ConnectionTuneOKFrame() {}
/**
* Method id
* @return uint16_t
*/
uint16_t methodID() const
{
return 31;
}
/**
* Proposed maximum number of channels
* @return _uint16_t
*/
uint16_t channels() const
{
return _channels;
}
/**
* Proposed maximum frame size
* @return _uint32_t
*/
uint32_t frameMax() const
{
return _frameMax;
}
/**
* Desired heartbeat delay
* @return uint16_t
*/
uint16_t heartbeat() const
{
return _heartbeat;
}
};
/**
* end namespace
*/
}

1
src/copernica Symbolic link
View File

@ -0,0 +1 @@
..

36
src/exception.h Normal file
View File

@ -0,0 +1,36 @@
/**
* Exception.h
*
* Base class for all AMQP exceptions
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Base exception class
*/
class Exception : public std::runtime_error
{
protected:
/**
* Constructor
* @param what
*/
explicit Exception(const std::string &what) : runtime_error(what) {}
public:
/**
* Destructor
*/
virtual ~Exception() {}
};
/**
* End of namespace
*/
}

165
src/exchangebindframe.h Normal file
View File

@ -0,0 +1,165 @@
/**
* Exchangebindframe.h
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ExchangeBindFrame : public ExchangeFrame
{
private:
/**
* reserved byte
* @var uint16_t
*/
uint16_t _reserved;
/**
* Exchange to bind to
* @var ShortString
*/
ShortString _destination;
/**
* Exchange which is bound
* @var ShortString
*/
ShortString _source;
/**
* Routing key
* @var ShortString
*/
ShortString _routingKey;
/**
* contains: nowait do not wait on response
* @var booleanset
*/
BooleanSet _bools;
/**
* Additional arguments
* @var Table
*/
Table _arguments;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer &buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
buffer.add(_reserved);
_destination.fill(buffer);
_source.fill(buffer);
_routingKey.fill(buffer);
_bools.fill(buffer);
_arguments.fill(buffer);
}
public:
/**
* Constructor based on incoming data
*
* @param frame received frame to decode
*/
ExchangeBindFrame(ReceivedFrame &frame) :
ExchangeFrame(frame),
_reserved(frame.nextUint16()),
_destination(frame),
_source(frame),
_routingKey(frame),
_bools(frame),
_arguments(frame)
{}
/**
* Constructor for an exchangebindframe
* @param destination
* @param source
* @param routingkey
* @param noWait
* @param arguments
*/
ExchangeBindFrame(uint16_t channel, const std::string &destination, const std::string &source, const std::string &routingKey, bool noWait, const Table &arguments) :
ExchangeFrame(channel, (destination.length() + source.length() + routingKey.length() + arguments.size() + 6)), // 1 for each string, 1 for booleanset, 2 for deprecated field
_reserved(0),
_destination(destination),
_source(source),
_routingKey(routingKey),
_bools(noWait),
_arguments(arguments)
{}
/**
* Get the destination exchange
* @return string
*/
const std::string& destination() const
{
return _destination;
}
/**
* Get the source exchange
* @return string
*/
const std::string& source() const
{
return _source;
}
/**
* Get the routing key
* @return string
*/
const std::string& routingkey() const
{
return _routingKey;
}
/**
* Get the method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 30;
}
/**
* Get the additional arguments
* @return Table
*/
const Table& arguments() const
{
return _arguments;
}
/**
* Get the nowait bool
* @return bool
*/
bool noWait()
{
return _bools.get(0);
}
};
// end namespace
}

View File

@ -0,0 +1,32 @@
/**
* Exchangebindokframe.cpp
*/
#include "includes.h"
#include "exchangebindokframe.h"
// setup namespace
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ExchangeBindOKFrame::process(ConnectionImpl *connection)
{
// check if we have a channel
ChannelImpl *channel = connection->channel(this->channel());
// channel does not exist
if(!channel) return false;
// report to handler
channel->reportExchangeBound();
// done
return true;
}
// end namespace
}

66
src/exchangebindokframe.h Normal file
View File

@ -0,0 +1,66 @@
/**
* Exchangebindokframe.h
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ExchangeBindOKFrame : public ExchangeFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
}
public:
/**
* Constructor based on incoming data
*
* @param frame received frame to decode
*/
ExchangeBindOKFrame(ReceivedFrame &frame) :
ExchangeFrame(frame)
{}
/**
* Constructor for an exchangebindframe
* @param destination
* @param source
* @param routingkey
* @param noWait
* @param arguments
*/
ExchangeBindOKFrame(uint16_t channel) :
ExchangeFrame(channel, 0)
{}
virtual uint16_t methodID() const override
{
return 31;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
// end namespace
}

178
src/exchangedeclareframe.h Normal file
View File

@ -0,0 +1,178 @@
/**
* Class describing an AMQP exchange declare frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ExchangeDeclareFrame : public ExchangeFrame
{
private:
/**
* Field that no longer is used
* @var uint16_t
*/
uint16_t _deprecated;
/**
* The exchange name
* @var ShortString
*/
ShortString _name;
/**
* The exchange type
* @var ShortString
*/
ShortString _type;
/**
* The boolean set contains three settings:
* 0: Passive declaration, do not create exchange if it does not exist
* 1: Durable exchange
* 4: Do not wait for a response
* @var BooleanSet
*/
BooleanSet _bools;
/**
* Additional arguments. Implementation dependent.
* @var Table
*/
Table _arguments;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
// add fields
buffer.add(_deprecated);
_name.fill(buffer);
_type.fill(buffer);
_bools.fill(buffer);
_arguments.fill(buffer);
}
public:
/**
* Construct a exchange declare frame (client side)
*
* @param channel channel we´re working on
* @param name exchange name
* @param type exchange type
* @param passive do not create exchange if it does not exist
* @param durable durable exchange
* @param noWait do not wait on response
* @param arguments additional arguments
*/
ExchangeDeclareFrame(uint16_t channel, const std::string& name, const std::string& type, bool passive, bool durable, bool noWait, const Table& arguments) :
ExchangeFrame(channel, (name.length() + type.length() + arguments.size() + 5)), // size of name, type and arguments + 1 (all booleans are stored in 1 byte) + 2 (deprecated short) + 2 (string sizes)
_deprecated(0),
_name(name),
_type(type),
_bools(passive, durable, false, false, noWait),
_arguments(arguments)
{}
/**
* Construct parsing a declare frame from a received frame
* @param frame The received frame
*/
ExchangeDeclareFrame(ReceivedFrame &frame) :
ExchangeFrame(frame),
_deprecated(frame.nextUint16()),
_name(frame),
_type(frame),
_bools(frame),
_arguments(frame)
{}
/**
* Destructor
*/
virtual ~ExchangeDeclareFrame() {}
/**
* Method id
* @return uint16_t
*/
uint16_t methodID() const
{
return 10;
}
/**
* The exchange name
* @return string
*/
const std::string& name()
{
return _name;
}
/**
* The exchange type
* @return string
*/
const std::string& type()
{
return _type;
}
/**
* Passive declaration, do not create exchange if it does not exist
* @return bool
*/
bool passive()
{
return _bools.get(0);
}
/**
* Durable exchange
* @return bool
*/
bool durable()
{
return _bools.get(1);
}
/**
* Do not wait for a response
* @return bool
*/
bool noWait()
{
return _bools.get(4);
}
/**
* Additional arguments. Implementation dependent.
* @return Table
*/
const Table& arguments()
{
return _arguments;
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,34 @@
/**
* ExchangeDeclareOKFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "exchangedeclareokframe.h"
// setup namespace
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ExchangeDeclareOKFrame::process(ConnectionImpl *connection)
{
// we need the appropriate channel
ChannelImpl *channel = connection->channel(this->channel());
// channel does not exist
if(!channel) return false;
// report exchange declare ok
channel->reportExchangeDeclared();
// done
return true;
}
// end namespace
}

View File

@ -0,0 +1,71 @@
/**
* Class describing an AMQP exchange declare ok frame
*
* @copyright 2014 Copernica BV
*/
/**
* Class definition
*/
namespace AMQP{
/**
* Class implementation
*/
class ExchangeDeclareOKFrame : public ExchangeFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
}
public:
/**
* Construct an exchange declare ok frame
*
* @param channel channel we're working on
*/
ExchangeDeclareOKFrame(uint16_t channel) : ExchangeFrame(channel, 0) {}
/**
* Decode an exchange declare acknowledgement frame
*
* @param frame received frame to decode
*/
ExchangeDeclareOKFrame(ReceivedFrame &frame) :
ExchangeFrame(frame)
{}
/**
* Destructor
*/
virtual ~ExchangeDeclareOKFrame() {}
/**
* returns the method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 11;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

128
src/exchangedeleteframe.h Normal file
View File

@ -0,0 +1,128 @@
/**
* Class describing an AMQP exchange delete frame
*
* @copyright 2014 Copernica BV
*/
/**
* we live in the copernica namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ExchangeDeleteFrame : public ExchangeFrame
{
private:
/**
* Field that is no longer in use
* @var uint16_t
*/
uint16_t _deprecated;
/**
* The exchange name
* @var ShortString
*/
ShortString _name;
/**
* booleanset, contains:
* 0: ifUnused
* 1: noWait
* @var BooleanSet
*/
BooleanSet _bools;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
// add fields
buffer.add(_deprecated);
_name.fill(buffer);
_bools.fill(buffer);
}
public:
/**
* constructor based on incoming data
*
* @param frame received frame
*/
ExchangeDeleteFrame(ReceivedFrame &frame) :
ExchangeFrame(frame),
_deprecated(frame.nextUint16()),
_name(frame),
_bools(frame)
{}
/**
* construct a exchangedeleteframe
*
* @param channel channel we're working on
* @param String name Name of the exchange
* @param bool ifUnused Delete only if frame is not used
* @param bool noWait Do not wait for a response
*/
ExchangeDeleteFrame(uint16_t channel, const std::string& name, bool ifUnused = false, bool noWait = false) :
ExchangeFrame(channel, name.length() + 4), // length of the name, 1 byte for encoding this length, 1 for bools, 2 for deprecated short
_deprecated(0),
_name(name),
_bools(ifUnused, noWait)
{}
/**
* Destructor
*/
virtual ~ExchangeDeleteFrame() {}
/**
* returns the method id
* @return uint16_t
*/
uint16_t methodID() const
{
return 20;
}
/**
* returns the exchange name
* @return string
*/
const std::string& name()
{
return _name;
}
/**
* returns whether to delete if unused
* @return bool
*/
bool ifUnused()
{
return _bools.get(0);
}
/**
* returns whether to wait for a response
* @return bool
*/
bool noWait()
{
return _bools.get(1);
}
};
/**
* end namespace
*/
}

View File

@ -0,0 +1,38 @@
/**
* ExchangeDeleteOKFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "exchangedeleteokframe.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ExchangeDeleteOKFrame::process(ConnectionImpl *connection)
{
// check if we have a channel
ChannelImpl *channel = connection->channel(this->channel());
// channel does not exist
if(!channel) return false;
// report to handler
channel->reportExchangeDeleted();
// done
return true;
}
/**
* End of namespace
*/
}

View File

@ -0,0 +1,72 @@
/**
* Class describing an AMQP exchange delete ok frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ExchangeDeleteOKFrame : public ExchangeFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
}
public:
/**
* Construct an exchange delete ok frame
*
* @param frame received frame
*/
ExchangeDeleteOKFrame(ReceivedFrame &frame) :
ExchangeFrame(frame)
{}
/**
* Construct an exchange delete ok frame
*
* @param channel channel we're working on
*/
ExchangeDeleteOKFrame(uint16_t channel) : ExchangeFrame(channel, 0) {}
/**
* Destructor
*/
virtual ~ExchangeDeleteOKFrame() {}
/**
* returns the method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 21;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
/**
* end namespace
*/
}

53
src/exchangeframe.h Normal file
View File

@ -0,0 +1,53 @@
/**
* Class describing an AMQP exchange frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class ExchangeFrame : public MethodFrame
{
protected:
/**
* Constructor based on incoming data
*
* @param frame received frame to decode
*/
ExchangeFrame(ReceivedFrame &frame) : MethodFrame(frame) {}
/**
* Constructor for an exchange frame
*
* @param channel channel we're working on
* @param size size of the payload
*/
ExchangeFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {}
public:
/**
* Destructor
*/
virtual ~ExchangeFrame() {}
/**
* Class id
* @return uint16_t
*/
virtual uint16_t classID() const override
{
return 40;
}
};
/**
* end namespace
*/
}

164
src/exchangeunbindframe.h Normal file
View File

@ -0,0 +1,164 @@
/**
* Exchangeunbindframe.h
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ExchangeUnbindFrame : public ExchangeFrame
{
private:
/**
* reserved byte
* @var uint16_t
*/
uint16_t _reserved;
/**
* Exchange to bind to
* @var ShortString
*/
ShortString _destination;
/**
* Exchange which is bound
* @var ShortString
*/
ShortString _source;
/**
* Routing key
* @var ShortString
*/
ShortString _routingKey;
/**
* contains: nowait do not wait on response
* @var booleanset
*/
BooleanSet _bools;
/**
* Additional arguments
* @var Table
*/
Table _arguments;
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
buffer.add(_reserved);
_destination.fill(buffer);
_source.fill(buffer);
_routingKey.fill(buffer);
_bools.fill(buffer);
_arguments.fill(buffer);
}
public:
/**
* Constructor based on incoming data
*
* @param frame received frame to decode
*/
ExchangeUnbindFrame(ReceivedFrame &frame) :
ExchangeFrame(frame),
_reserved(frame.nextUint16()),
_destination(frame),
_source(frame),
_routingKey(frame),
_bools(frame),
_arguments(frame)
{}
/**
* Constructor for an exchangebindframe
* @param destination
* @param source
* @param routingkey
* @param noWait
* @param arguments
*/
ExchangeUnbindFrame(uint16_t channel, const std::string &destination, const std::string &source, const std::string &routingKey, bool noWait, const Table &arguments) :
ExchangeFrame(channel, (destination.length() + source.length() + routingKey.length() + arguments.size() + 6)), // 1 for each string, 1 for booleanset, 2 for deprecated field
_reserved(0),
_destination(destination),
_source(source),
_routingKey(routingKey),
_bools(noWait),
_arguments(arguments)
{}
/**
* Get the destination exchange
* @return string
*/
const std::string& destination() const
{
return _destination;
}
/**
* Get the source exchange
* @return string
*/
const std::string& source() const
{
return _source;
}
/**
* Get the routing key
* @return string
*/
const std::string& routingkey() const
{
return _routingKey;
}
/**
* Get the method id
* @return uint16_t
*/
virtual uint16_t methodID() const override
{
return 40;
}
/**
* Get the additional arguments
* @return Table
*/
const Table& arguments() const
{
return _arguments;
}
/**
* Get the nowait bool
* @return bool
*/
bool noWait()
{
return _bools.get(0);
}
};
// leave namespace
}

View File

@ -0,0 +1,32 @@
/**
* QueuePurgeOKFrame.cpp
*/
#include "includes.h"
#include "exchangeunbindokframe.h"
// setup namespace
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool ExchangeUnbindOKFrame::process(ConnectionImpl *connection)
{
// check if we have a channel
ChannelImpl *channel = connection->channel(this->channel());
// channel does not exist
if(!channel) return false;
// report to handler
channel->reportExchangeUnbound();
// done
return true;
}
// end namespace
}

View File

@ -0,0 +1,66 @@
/**
* Exchangeunbindokframe.h
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class ExchangeUnbindOKFrame : public ExchangeFrame
{
protected:
/**
* Encode a frame on a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const override
{
// call base
ExchangeFrame::fill(buffer);
}
public:
/**
* Constructor based on incoming data
*
* @param frame received frame to decode
*/
ExchangeUnbindOKFrame(ReceivedFrame &frame) :
ExchangeFrame(frame)
{}
/**
* Constructor for an exchangebindframe
* @param destination
* @param source
* @param routingkey
* @param noWait
* @param arguments
*/
ExchangeUnbindOKFrame(uint16_t channel) :
ExchangeFrame(channel, 0)
{}
virtual uint16_t methodID() const override
{
return 51;
}
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection) override;
};
// end namespace
}

140
src/extframe.h Normal file
View File

@ -0,0 +1,140 @@
/**
* ExtFrame.h
*
* Class describing an AMQP frame. A frame can be encoded into the AMQP
* wireframe format, so that it can be sent over an open socket, or it can be
* constructed from a buffer containing AMQP wireframe format.
*
* The ExtFrame is the base class for all other frames, apart from the
* protocol-header-frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Base frame class for all frames apart from the protocol header frame
*/
class ExtFrame : public Frame
{
protected:
/**
* The AMQP channel
* @var uint16_t
*/
uint16_t _channel;
/**
* The payload size of the frame
* @var uint32_t
*/
uint32_t _size;
/**
* Constructor for an AMQP Frame
*
* The constructor is protected as you're not supposed
*
* @param channel channel we're working on
* @param size size of the payload
*/
ExtFrame(uint16_t channel, uint32_t size) : _channel(channel), _size(size) {}
/**
* Constructor based on a received not-yet-recognized frame
* @param frame the received frame
*/
ExtFrame(ReceivedFrame &frame) : _channel(frame.channel()), _size(frame.payloadSize()) {}
/**
* Modify the size of the frame
* This method is called from derived classes when properties in it are
* changed that force the frame to get a different size
* @param change change in size
*/
void modifySize(int32_t change)
{
// change payload size
_size += change;
}
/**
* Virtual method that must be implemented by sub classes to fill the output buffer
* @param buffer
*/
virtual void fill(OutBuffer &buffer) const override
{
// add type, channel id and size
buffer.add(type());
buffer.add(_channel);
buffer.add(_size);
}
public:
/**
* Destruct frame
*/
virtual ~ExtFrame() {}
/**
* The channel this message was sent on
*/
uint16_t channel() const
{
return _channel;
}
/**
* Size of the header
* @return uint32_t
*/
uint32_t headerSize() const
{
// 1 byte for type, 2 bytes for channel, 4 bytes for payload
return 7;
}
/**
* Size of the trailer
* @return uint32_t
*/
uint32_t trailerSize() const
{
// 1 byte for end-of-frame
return 1;
}
/**
* return the total size of the frame
* @return uint32_t
*/
virtual uint32_t totalSize() const override
{
// payload size + size of header and trailer
return _size + headerSize() + trailerSize();
}
/**
* The size of the payload
* @return uint32_t
*/
uint32_t payloadSize() const
{
return _size;
}
/**
* Get the message type
*/
virtual uint8_t type() const = 0;
};
/**
* End of namespace
*/
}

52
src/field.cpp Normal file
View File

@ -0,0 +1,52 @@
/**
* Field.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Decode a field by fetching a type and full field from a frame
* The returned field is allocated on the heap!
* @param frame
* @return Field*
*/
Field *Field::decode(ReceivedFrame &frame)
{
// get the type
uint8_t type = frame.nextUint8();
// create field based on type
switch (type)
{
case 't': return new BooleanSet(frame);
case 'b': return new Octet(frame);
case 'B': return new UOctet(frame);
case 'U': return new Short(frame);
case 'u': return new UShort(frame);
case 'I': return new Long(frame);
case 'i': return new ULong(frame);
case 'L': return new LongLong(frame);
case 'l': return new ULongLong(frame);
case 'f': return new Float(frame);
case 'd': return new Double(frame);
case 'D': return new DecimalField(frame);
case 's': return new ShortString(frame);
case 'S': return new LongString(frame);
case 'A': return new Array(frame);
case 'T': return new Timestamp(frame);
case 'F': return new Table(frame);
default: return nullptr;
}
}
/**
* End of namespace
*/
}

40
src/flags.cpp Normal file
View File

@ -0,0 +1,40 @@
/**
* Flags.cpp
*
* The various flags that are supported
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* All bit flags
* @var int
*/
const int durable = 0x1;
const int autodelete = 0x2;
const int active = 0x4;
const int passive = 0x8;
const int ifunused = 0x10;
const int ifempty = 0x20;
const int global = 0x40;
const int nolocal = 0x80;
const int noack = 0x100;
const int exclusive = 0x200;
const int nowait = 0x400;
const int mandatory = 0x800;
const int immediate = 0x1000;
const int redelivered = 0x2000;
const int multiple = 0x4000;
const int requeue = 0x8000;
/**
* End of namespace
*/
}

61
src/frame.h Normal file
View File

@ -0,0 +1,61 @@
/**
* Frame.h
*
* Base class for frames. This base class can not be constructed from outside
* the library, and is only used internally.
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class definition
*/
class Frame
{
protected:
/**
* Protected constructor to ensure that no objects are created from
* outside the library
*/
Frame() {}
public:
/**
* Destructor
*/
virtual ~Frame() {}
/**
* return the total size of the frame
* @return uint32_t
*/
virtual uint32_t totalSize() const = 0;
/**
* Fill an output buffer
* @param buffer
*/
virtual void fill(OutBuffer &buffer) const = 0;
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
virtual bool process(ConnectionImpl *connection)
{
// no process was implemented
return false;
}
};
/**
* End of namespace
*/
}

62
src/framecheck.h Normal file
View File

@ -0,0 +1,62 @@
/**
* FrameCheck.h
*
* Class that checks incoming frames for their size
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Internal helper class that checks if there is enough room left in the frame
*/
class FrameCheck
{
private:
/**
* The frame
* @var ReceivedFrame
*/
ReceivedFrame *_frame;
/**
* The size that is checked
* @var size_t
*/
size_t _size;
public:
/**
* Constructor
* @param frame
* @param size
*/
FrameCheck(ReceivedFrame *frame, size_t size) : _frame(frame), _size(size)
{
// no problem is there are still enough bytes left
if (frame->_left >= size) return;
// frame buffer is too small
throw ProtocolException("frame out of range");
}
/**
* Destructor
*/
virtual ~FrameCheck()
{
// update the buffer and the number of bytes left
_frame->_buffer += _size;
_frame->_left -= _size;
}
};
/**
* End namespace
*/
}

70
src/headerframe.h Normal file
View File

@ -0,0 +1,70 @@
/**
* Class describing an AMQP header frame
*
* @copyright 2014 Copernica BV
*/
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class HeaderFrame : public ExtFrame
{
protected:
/**
* Construct a header frame
* @param channel Channel ID
* @param size Payload size
*/
HeaderFrame(uint16_t channel, uint32_t size) : ExtFrame(channel, size + 2) {} // + size of classID (2bytes)
/**
* Construct based on incoming data
* @param frame Incoming frame
*/
HeaderFrame(ReceivedFrame &frame) : ExtFrame(frame) {}
/**
* Encode a header frame to a string buffer
*
* @param buffer buffer to write frame to
*/
virtual void fill(OutBuffer& buffer) const
{
ExtFrame::fill(buffer);
// add type
buffer.add(classID());
}
public:
/**
* Destructor
*/
virtual ~HeaderFrame() {}
/**
* Get the message type
* @return uint8_t
*/
virtual uint8_t type() const override
{
return 2;
}
/**
* Class id
* @return uint16_t
*/
virtual uint16_t classID() const = 0;
};
/**
* end namespace
*/
}

32
src/heartbeatframe.cpp Normal file
View File

@ -0,0 +1,32 @@
/**
* HeartbeatFrame.cpp
*
* @copyright 2014 Copernica BV
*/
#include "includes.h"
#include "heartbeatframe.h"
/**
* Namespace
*/
namespace AMQP {
/**
* Process the frame
* @param connection The connection over which it was received
* @return bool Was it succesfully processed?
*/
bool HeartbeatFrame::process(ConnectionImpl *connection)
{
// send back the same frame
connection->send(*this);
// done
return true;
}
/**
* End namespace
*/
}

Some files were not shown because too many files have changed in this diff Show More