Merge with upstream
This commit is contained in:
commit
bfd91b6ab5
|
|
@ -13,3 +13,8 @@
|
|||
*.la
|
||||
*.a
|
||||
*.a.*
|
||||
/build
|
||||
/.vscode
|
||||
.atom-build.cson
|
||||
.atom-dbg.cson
|
||||
/bin
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
################
|
||||
# Config
|
||||
################
|
||||
|
||||
# C++ project
|
||||
language: cpp
|
||||
|
||||
dist: trusty
|
||||
sudo: required
|
||||
group: edge
|
||||
|
||||
|
||||
################
|
||||
# Services
|
||||
################
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
################
|
||||
# Build matrix
|
||||
################
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
||||
################
|
||||
# Linux / GCC
|
||||
################
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER_PACKAGE=g++-5
|
||||
- C_COMPILER=gcc-5
|
||||
- CXX_COMPILER=g++-5
|
||||
- CXXFLAGS=-std=c++11
|
||||
|
||||
- os: linux
|
||||
env:
|
||||
- COMPILER_PACKAGE=g++-6
|
||||
- C_COMPILER=gcc-6
|
||||
- CXX_COMPILER=g++-6
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER_PACKAGE=g++-7
|
||||
- C_COMPILER=gcc-7
|
||||
- CXX_COMPILER=g++-7
|
||||
|
||||
################
|
||||
# Linux / Clang
|
||||
################
|
||||
|
||||
- os: linux
|
||||
env:
|
||||
- COMPILER_PACKAGE=clang-4.0
|
||||
- C_COMPILER=clang-4.0
|
||||
- CXX_COMPILER=clang++-4.0
|
||||
|
||||
- os: linux
|
||||
env:
|
||||
- COMPILER_PACKAGE=clang-5.0
|
||||
- C_COMPILER=clang-5.0
|
||||
- CXX_COMPILER=clang++-5.0
|
||||
|
||||
before_install:
|
||||
|
||||
# Show OS/compiler version (this may not be the same OS as the Docker container)
|
||||
- uname -a
|
||||
|
||||
# Use an artful container - gives us access to latest compilers.
|
||||
- docker run -d --name ubuntu-test-container -v $(pwd):/travis ubuntu:artful tail -f /dev/null
|
||||
- docker ps
|
||||
|
||||
|
||||
install:
|
||||
|
||||
# Create our container
|
||||
- docker exec -t ubuntu-test-container bash -c "apt-get update -y &&
|
||||
apt-get --no-install-recommends install -y software-properties-common cmake
|
||||
ninja-build libboost-all-dev libev-dev libuv1-dev ninja-build $COMPILER_PACKAGE &&
|
||||
apt-get -y clean && rm -rf /var/lib/apt/lists/*"
|
||||
|
||||
################
|
||||
# Build / Test
|
||||
################
|
||||
|
||||
script:
|
||||
|
||||
# Run the container that we created and build the code
|
||||
- docker exec -t ubuntu-test-container bash -c "cd /travis &&
|
||||
export CC=/usr/bin/$C_COMPILER &&
|
||||
export CXX=/usr/bin/$CXX_COMPILER &&
|
||||
mkdir build.release && cd build.release &&
|
||||
cmake ${CMAKE_OPTIONS} -DAMQP-CPP_BUILD_EXAMPLES=ON -DAMQP-CPP_LINUX_TCP=ON -GNinja .. &&
|
||||
cmake --config Release --build . &&
|
||||
cd .."
|
||||
113
CMakeLists.txt
113
CMakeLists.txt
|
|
@ -1,11 +1,36 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
# Builds AMQP-CPP
|
||||
#
|
||||
# Options:
|
||||
#
|
||||
# - AMQP-CPP_BUILD_SHARED (default OFF)
|
||||
# ON: Build shared lib
|
||||
# OFF: Build static lib
|
||||
#
|
||||
# - AMQP-CPP_LINUX_TCP (default OFF)
|
||||
# ON: Build posix handler implementation
|
||||
# OFF: Don't build posix handler implementation
|
||||
|
||||
project(amqp-cpp)
|
||||
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
|
||||
|
||||
# project name
|
||||
project(amqpcpp)
|
||||
|
||||
# build options
|
||||
option(AMQP-CPP_BUILD_SHARED "Build shared library. If off, build will be static." OFF)
|
||||
option(AMQP-CPP_LINUX_TCP "Build linux sockets implementation." OFF)
|
||||
option(AMQP-CPP_BUILD_EXAMPLES "Build amqpcpp examples" OFF)
|
||||
|
||||
# ensure c++11 on all compilers
|
||||
include(set_cxx_norm.cmake)
|
||||
set_cxx_norm (${CXX_NORM_CXX11})
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
|
||||
# add source files
|
||||
# ------------------------------------------------------------------------------------------------------
|
||||
|
||||
# set include/ as include directory
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# macro that adds a list of provided source files to a list called SRCS.
|
||||
# if variable SRCS does not yet exist, it is created.
|
||||
macro (add_sources)
|
||||
file (RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
foreach (_src ${ARGN})
|
||||
|
|
@ -21,36 +46,62 @@ macro (add_sources)
|
|||
endif()
|
||||
endmacro()
|
||||
|
||||
# add source files
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(include)
|
||||
|
||||
option(BUILD_SHARED "build shared library" OFF)
|
||||
|
||||
if(BUILD_SHARED)
|
||||
add_library(amqp-cpp SHARED ${SRCS})
|
||||
set_target_properties(amqp-cpp PROPERTIES SOVERSION 2.7)
|
||||
install(TARGETS amqp-cpp
|
||||
LIBRARY DESTINATION lib
|
||||
)
|
||||
else()
|
||||
add_library(amqp-cpp STATIC ${SRCS})
|
||||
install(TARGETS amqp-cpp
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
endif()
|
||||
Include_directories(${PROJECT_SOURCE_DIR})
|
||||
install(DIRECTORY include/ DESTINATION include/amqpcpp
|
||||
FILES_MATCHING PATTERN "*.h")
|
||||
install(FILES amqpcpp.h DESTINATION include)
|
||||
|
||||
option(BUILD_TUTORIALS "build rabbitmq tutorials" OFF)
|
||||
if(BUILD_TUTORIALS)
|
||||
# add_subdirectory(examples/rabbitmq_tutorials)
|
||||
if(AMQP-CPP_LINUX_TCP)
|
||||
add_subdirectory(src/linux_tcp)
|
||||
endif()
|
||||
|
||||
set(AMQP-CPP_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(AMQP-CPP_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
|
||||
# potentially build the examples
|
||||
if(AMQP-CPP_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
# settings for specific compilers
|
||||
# ------------------------------------------------------------------------------------------------------
|
||||
|
||||
# we have to prevent windows from defining the max macro.
|
||||
if (WIN32)
|
||||
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
|
||||
# build targets
|
||||
# ------------------------------------------------------------------------------------------------------
|
||||
|
||||
# set output directory
|
||||
set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/bin)
|
||||
|
||||
if(AMQP-CPP_BUILD_SHARED)
|
||||
# create shared lib
|
||||
add_library(${PROJECT_NAME} SHARED ${SRCS})
|
||||
# set shared lib version
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION 3.0)
|
||||
else()
|
||||
# create static lib
|
||||
add_library(${PROJECT_NAME} STATIC ${SRCS})
|
||||
endif()
|
||||
|
||||
# install rules
|
||||
# ------------------------------------------------------------------------------------------------------
|
||||
|
||||
if(AMQP-CPP_BUILD_SHARED)
|
||||
# copy shared lib and its static counter part
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION lib
|
||||
)
|
||||
else()
|
||||
# copy static lib
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
endif()
|
||||
|
||||
# copy header files
|
||||
install(DIRECTORY include/amqpcpp/ DESTINATION include/amqpcpp
|
||||
FILES_MATCHING PATTERN "*.h")
|
||||
install(FILES include/amqpcpp.h DESTINATION include)
|
||||
|
||||
install(EXPORT ${PROJECT_NAME}Config DESTINATION cmake)
|
||||
export(TARGETS ${PROJECT_NAME} FILE ${PROJECT_NAME}Config.cmake)
|
||||
|
|
|
|||
10
Makefile
10
Makefile
|
|
@ -2,8 +2,8 @@ PREFIX ?= /usr
|
|||
INCLUDE_DIR = ${PREFIX}/include
|
||||
LIBRARY_DIR = ${PREFIX}/lib
|
||||
export LIBRARY_NAME = amqpcpp
|
||||
export SONAME = 2.7
|
||||
export VERSION = 2.7.1
|
||||
export SONAME = 3.0
|
||||
export VERSION = 3.0.1
|
||||
|
||||
all:
|
||||
$(MAKE) -C src all
|
||||
|
|
@ -25,9 +25,11 @@ clean:
|
|||
|
||||
install:
|
||||
mkdir -p ${INCLUDE_DIR}/$(LIBRARY_NAME)
|
||||
mkdir -p ${INCLUDE_DIR}/$(LIBRARY_NAME)/linux_tcp
|
||||
mkdir -p ${LIBRARY_DIR}
|
||||
cp -f $(LIBRARY_NAME).h ${INCLUDE_DIR}
|
||||
cp -f include/*.h ${INCLUDE_DIR}/$(LIBRARY_NAME)
|
||||
cp -f include/$(LIBRARY_NAME).h ${INCLUDE_DIR}
|
||||
cp -f include/amqpcpp/*.h ${INCLUDE_DIR}/$(LIBRARY_NAME)
|
||||
cp -f include/amqpcpp/linux_tcp/*.h ${INCLUDE_DIR}/$(LIBRARY_NAME)/linux_tcp
|
||||
-cp -f src/lib$(LIBRARY_NAME).so.$(VERSION) ${LIBRARY_DIR}
|
||||
-cp -f src/lib$(LIBRARY_NAME).a.$(VERSION) ${LIBRARY_DIR}
|
||||
ln -r -s -f $(LIBRARY_DIR)/lib$(LIBRARY_NAME).so.$(VERSION) $(LIBRARY_DIR)/lib$(LIBRARY_NAME).so.$(SONAME)
|
||||
|
|
|
|||
407
README.md
407
README.md
|
|
@ -1,53 +1,55 @@
|
|||
AMQP-CPP
|
||||
========
|
||||
|
||||
[](https://travis-ci.org/CopernicaMarketingSoftware/AMQP-CPP)
|
||||
|
||||
AMQP-CPP is a C++ library for communicating with a RabbitMQ message broker. The
|
||||
library can be used to parse incoming data from a RabbitMQ server, and to
|
||||
generate frames that can be sent to a RabbitMQ server.
|
||||
|
||||
This library has a layered architecture, and allows you - if you like - to
|
||||
completely take care of the network layer. If you want to set up and manage the
|
||||
This library has a layered architecture, and allows you - if you like - to
|
||||
completely take care of the network layer. If you want to set up and manage the
|
||||
network connections yourself, the AMQP-CPP library will not make a connection to
|
||||
RabbitMQ by itself, nor will it create sockets and/or perform IO operations. As
|
||||
a user of this library, you create the socket connection and implement a certain
|
||||
interface that you pass to the AMQP-CPP library and that the library will use
|
||||
RabbitMQ by itself, nor will it create sockets and/or perform IO operations. As
|
||||
a user of this library, you create the socket connection and implement a certain
|
||||
interface that you pass to the AMQP-CPP library and that the library will use
|
||||
for IO operations.
|
||||
|
||||
Intercepting this network layer is however optional, the AMQP-CPP library also
|
||||
comes with a predefined Tcp module that can be used if you trust the AMQP library
|
||||
Intercepting this network layer is however optional, the AMQP-CPP library also
|
||||
comes with a predefined Tcp module that can be used if you trust the AMQP library
|
||||
to take care of the network handling. In that case, the AMQP-CPP library does
|
||||
all the system calls to set up network connections and send and receive the data.
|
||||
|
||||
This layered architecture makes the library extremely flexible and portable: it
|
||||
does not necessarily rely on operating system specific IO calls, and can be
|
||||
This layered architecture makes the library extremely flexible and portable: it
|
||||
does not necessarily rely on operating system specific IO calls, and can be
|
||||
easily integrated into any kind of event loop. If you want to implement the AMQP
|
||||
protocol on top of some [unusual other communication layer](https://tools.ietf.org/html/rfc1149),
|
||||
this library can be used for that - but if you want to use it with regular TCP
|
||||
connections, setting it up is just as easy.
|
||||
protocol on top of some [unusual other communication layer](https://tools.ietf.org/html/rfc1149),
|
||||
this library can be used for that - but if you want to use it with regular TCP
|
||||
connections, setting it up is just as easy.
|
||||
|
||||
AMQP-CPP is fully asynchronous and does not do any blocking (system) calls, so
|
||||
AMQP-CPP is fully asynchronous and does not do any blocking (system) calls, so
|
||||
it can be used in high performance applications without the need for threads.
|
||||
|
||||
The AMQP-CPP library uses C++11 features, so if you intend use it, please make
|
||||
The AMQP-CPP library uses C++11 features, so if you intend use it, please make
|
||||
sure that your compiler is up-to-date and supports C++11.
|
||||
|
||||
**Note for the reader:** This readme file has a peculiar structure. We start
|
||||
**Note for the reader:** This readme file has a peculiar structure. We start
|
||||
explaining the pure and hard core low level interface in which you have to
|
||||
take care of opening socket connections yourself. In reality, you probably want
|
||||
to use the simpler TCP interface that is being described later on.
|
||||
to use the simpler TCP interface that is being described [later on](#tcp-connections).
|
||||
|
||||
|
||||
ABOUT
|
||||
=====
|
||||
|
||||
This library is created and maintained by Copernica (www.copernica.com), and is
|
||||
used inside the MailerQ (www.mailerq.com), Yothalot (www.yothalot.com) and
|
||||
AMQPipe (www.amqpipe.com) applications. MailerQ is a tool for sending large
|
||||
used inside the MailerQ (www.mailerq.com), Yothalot (www.yothalot.com) and
|
||||
AMQPipe (www.amqpipe.com) applications. MailerQ is a tool for sending large
|
||||
volumes of email, using AMQP message queues, Yothalot is a big data processing
|
||||
map/reduce framework and AMQPipe is a tool for high-speed processing messages
|
||||
map/reduce framework and AMQPipe is a tool for high-speed processing messages
|
||||
between AMQP pipes
|
||||
|
||||
Do you appreciate our work and are you looking for high quality email solutions?
|
||||
Do you appreciate our work and are you looking for high quality email solutions?
|
||||
Then check out our other commercial and open source solutions:
|
||||
|
||||
* Copernica Marketing Suite (www.copernica.com)
|
||||
|
|
@ -63,35 +65,68 @@ Then check out our other commercial and open source solutions:
|
|||
INSTALLING
|
||||
==========
|
||||
|
||||
If you are on some kind of Linux environment, installing the library is as easy
|
||||
as running `make` and `make install`. This will install the full version of
|
||||
the AMQP-CPP, including the system specific TCP module. If you do not need the
|
||||
additional TCP module (because you take care of handling the network stuff
|
||||
yourself), you can also compile a pure form of the library. Use `make pure`
|
||||
and `make install` for that.
|
||||
AMQP-CPP comes with an optional Linux-only TCP module that takes care of the
|
||||
network part required for the AMQP-CPP core library. If you use this module, you
|
||||
are required to link with `pthread` and `dl`.
|
||||
|
||||
For users on a non-Linux environment: this library is known to work on your
|
||||
environment too (after all, it does not do any operating specific system calls),
|
||||
but it might take some extra effort to get your compiler to compile it. Please
|
||||
send in your pull requests once you have it running, so that others can benefit
|
||||
from your experiences.
|
||||
There are two methods to compile AMQP-CPP: CMake and Make. CMake is platform portable
|
||||
and works on all systems, while the Makefile only works on Linux. Building of a shared
|
||||
library is currently not supported on Windows.
|
||||
|
||||
After building there are two relevant files to include when using the library.
|
||||
|
||||
File | Include when?
|
||||
---------------------|--------------------------------------------------------
|
||||
amqpcpp.h | Always
|
||||
amqpcpp/linux_tcp.h | If using the Linux-only TCP module
|
||||
|
||||
On Windows you are required to define `NOMINMAX` when compiling code that includes public AMQP-CPP header files.
|
||||
|
||||
## Using cmake
|
||||
|
||||
The CMake file supports both building and installing. You can choose not to use
|
||||
the install functionality, and instead manually use the build output at `build/bin/`. Keep
|
||||
in mind that the TCP module is only supported for Linux. An example install method
|
||||
would be:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. [-DAMQP-CPP_AMQBUILD_SHARED] [-DAMQP-CPP_LINUX_TCP]
|
||||
cmake --build .. --target install
|
||||
```
|
||||
|
||||
Option | Default | Meaning
|
||||
-------------------------|---------|-----------------------------------------------------------------------
|
||||
AMQP-CPP_BUILD_SHARED | OFF | Static lib(ON) or shared lib(OFF)? Shared is not supported on Windows.
|
||||
AMQP-CPP_LINUX_TCP | OFF | Should the Linux-only TCP module be built?
|
||||
|
||||
## Using make
|
||||
|
||||
Compiling and installing AMQP-CPP with make is as easy as running `make` and
|
||||
then `make install`. This will install the full version of AMQP-CPP, including
|
||||
the system specific TCP module. If you do not need the additional TCP module
|
||||
(because you take care of handling the network stuff yourself), you can also
|
||||
compile a pure form of the library. Use `make pure` and `make install` for that.
|
||||
|
||||
When you compile an application that uses the AMQP-CPP library, do not
|
||||
forget to link with the library. For gcc and clang the linker flag is -lamqpcpp.
|
||||
If you use the fullblown version of AMQP-CPP (with the TCP module), you also
|
||||
need to pass a -lpthread linker flag, because the TCP module uses a thread
|
||||
for running an asynchronous and non-blocking DNS hostname lookup.
|
||||
If you use the fullblown version of AMQP-CPP (with the TCP module), you also
|
||||
need to pass the -lpthread and -ldl linker flags, because the TCP module uses a
|
||||
thread for running an asynchronous and non-blocking DNS hostname lookup, and it
|
||||
may dynamically look up functions from the openssl library if a secure connection
|
||||
to RabbitMQ has to be set up.
|
||||
|
||||
|
||||
HOW TO USE AMQP-CPP
|
||||
===================
|
||||
|
||||
As we mentioned above, the library can be used in a network-agnostic fashion.
|
||||
It then does not do any IO by itself, and you need to pass an object to the
|
||||
It then does not do any IO by itself, and you need to pass an object to the
|
||||
library that the library can use for IO. So, before you start using the
|
||||
library, you first you need to create a class that extends from the
|
||||
ConnectionHandler base class. This is a class with a number of methods that are
|
||||
called by the library every time it wants to send out data, or when it needs to
|
||||
library, you first you need to create a class that extends from the
|
||||
ConnectionHandler base class. This is a class with a number of methods that are
|
||||
called by the library every time it wants to send out data, or when it needs to
|
||||
inform you that an error occured.
|
||||
|
||||
````c++
|
||||
|
|
@ -204,16 +239,16 @@ example call the "send()" or "write()" system call to send out the data to
|
|||
the RabbitMQ server. But what about data in the other direction? How does the
|
||||
library receive data back from RabbitMQ?
|
||||
|
||||
In this raw setup, the AMQP-CPP library does not do any IO by itself and it is
|
||||
therefore also not possible for the library to receive data from a
|
||||
socket. It is again up to you to do this. If, for example, you notice in your
|
||||
event loop that the socket that is connected with the RabbitMQ server becomes
|
||||
readable, you should read out that socket (for example by using the recv() system
|
||||
call), and pass the received bytes to the AMQP-CPP library. This is done by
|
||||
In this raw setup, the AMQP-CPP library does not do any IO by itself and it is
|
||||
therefore also not possible for the library to receive data from a
|
||||
socket. It is again up to you to do this. If, for example, you notice in your
|
||||
event loop that the socket that is connected with the RabbitMQ server becomes
|
||||
readable, you should read out that socket (for example by using the recv() system
|
||||
call), and pass the received bytes to the AMQP-CPP library. This is done by
|
||||
calling the parse() method in the Connection object.
|
||||
|
||||
The Connection::parse() method gets two parameters, a pointer to a buffer of
|
||||
data that you just read from the socket, and a parameter that holds the size of
|
||||
data that you just read from the socket, and a parameter that holds the size of
|
||||
this buffer. The code snippet below comes from the Connection.h C++ header file.
|
||||
|
||||
````c++
|
||||
|
|
@ -249,9 +284,9 @@ both the old data, and the new data.
|
|||
To optimize your calls to the parse() method, you _could_ use the Connection::expected()
|
||||
and Connection::maxFrame() methods. The expected() method returns the number of bytes
|
||||
that the library prefers to receive next. It is pointless to call the parse() method
|
||||
with a smaller buffer, and it is best to call the method with a buffer of exactly this
|
||||
size. The maxFrame() returns the max frame size for AMQP messages. If you read your
|
||||
messages into a reusable buffer, you could allocate this buffer up to this size, so that
|
||||
with a smaller buffer, and it is best to call the method with a buffer of exactly this
|
||||
size. The maxFrame() returns the max frame size for AMQP messages. If you read your
|
||||
messages into a reusable buffer, you could allocate this buffer up to this size, so that
|
||||
you never will have to reallocate.
|
||||
|
||||
|
||||
|
|
@ -259,21 +294,22 @@ TCP CONNECTIONS
|
|||
===============
|
||||
|
||||
Although the AMQP-CPP library gives you extreme flexibility by letting you setup
|
||||
your own network connections, the reality is that most if not all AMQP connections
|
||||
use the TCP protocol. To help you out, the library therefore also comes with a
|
||||
your own network connections, the reality is that most if not all AMQP connections
|
||||
use the TCP protocol. To help you out, the library therefore also comes with a
|
||||
TCP module that takes care of setting up the network connections, and sending
|
||||
and receiving the data.
|
||||
and receiving the data.
|
||||
|
||||
If you want to use this TCP module, you should not use the AMQP::Connection
|
||||
and AMQP::Channel classes that you saw above, but the alternative AMQP::TcpConnection
|
||||
and AMQP::TcpChannel classes instead. You also do not have to create your own class
|
||||
that implements the "AMQP::ConnectionHandler" interface - but a class that inherits
|
||||
from "AMQP::TcpHandler" instead. You especially need to implement the "monitor()"
|
||||
method in that class, as that is needed by the AMQP-CPP library to interact with
|
||||
that implements the "AMQP::ConnectionHandler" interface - but a class that inherits
|
||||
from "AMQP::TcpHandler" instead. You especially need to implement the "monitor()"
|
||||
method in that class, as that is needed by the AMQP-CPP library to interact with
|
||||
the main event loop:
|
||||
|
||||
````c++
|
||||
#include <amqpcpp.h>
|
||||
#include <amqpcpp/linux_tcp.h>
|
||||
|
||||
class MyTcpHandler : public AMQP::TcpHandler
|
||||
{
|
||||
|
|
@ -334,7 +370,7 @@ class MyTcpHandler : public AMQP::TcpHandler
|
|||
// descriptor to the main application event loop (like the select() or
|
||||
// poll() loop). When the event loop reports that the descriptor becomes
|
||||
// readable and/or writable, it is up to you to inform the AMQP-CPP
|
||||
// library that the filedescriptor is active by calling the
|
||||
// library that the filedescriptor is active by calling the
|
||||
// connection->process(fd, flags) method.
|
||||
}
|
||||
};
|
||||
|
|
@ -344,8 +380,8 @@ The "monitor()" method can be used to integrate the AMQP filedescriptors in your
|
|||
application's event loop. For some popular event loops (libev, libevent), we have
|
||||
already added example handler objects (see the next section for that).
|
||||
|
||||
Using the TCP module of the AMQP-CPP library is easier than using the
|
||||
raw AMQP::Connection and AMQP::Channel objects, because you do not have to
|
||||
Using the TCP module of the AMQP-CPP library is easier than using the
|
||||
raw AMQP::Connection and AMQP::Channel objects, because you do not have to
|
||||
create the sockets and connections yourself, and you also do not have to take
|
||||
care of buffering network data.
|
||||
|
||||
|
|
@ -371,14 +407,97 @@ channel.declareQueue("my-queue");
|
|||
channel.bindQueue("my-exchange", "my-queue", "my-routing-key");
|
||||
````
|
||||
|
||||
SECURE CONNECTIONS
|
||||
==================
|
||||
|
||||
The TCP module of AMQP-CPP also supports setting up secure connections. If your
|
||||
RabbitMQ server accepts SSL connections, you can specify the address to your
|
||||
server using the amqps:// protocol:
|
||||
|
||||
````c++
|
||||
// init the SSL library (this works for openssl 1.1, for openssl 1.0 use SSL_library_init())
|
||||
OPENSSL_init_ssl(0, NULL);
|
||||
|
||||
// address of the server (secure!)
|
||||
AMQP::Address address("amqps://guest:guest@localhost/vhost");
|
||||
|
||||
// create a AMQP connection object
|
||||
AMQP::TcpConnection connection(&myHandler, address);
|
||||
````
|
||||
|
||||
There are two things to take care of if you want to create a secure connection:
|
||||
(1) you must link your application with the -lssl flag (or use dlopen()), and (2)
|
||||
you must initialize the openssl library by calling OPENSSL_init_ssl(). This
|
||||
initializating must take place before you let you application connect to RabbitMQ.
|
||||
This is necessary because AMQP-CPP needs access to the openssl library to set up
|
||||
secure connections. It can only access this library if you have linked your
|
||||
application with this library, or if you have loaded this library at runtime
|
||||
using dlopen()).
|
||||
|
||||
Linking openssl is the normal thing to do. You just have to add the `-lssl` flag
|
||||
to your linker. If you however do not want to link your application with openssl,
|
||||
you can also load the openssl library at runtime, and pass in the pointer to the
|
||||
handle to AMQP-CPP:
|
||||
|
||||
````c++
|
||||
// dynamically open the openssl library
|
||||
void *handle = dlopen("/path/to/openssl.so", RTLD_LAZY);
|
||||
|
||||
// tell AMQP-CPP library where the handle to openssl can be found
|
||||
AMQP::openssl(handle);
|
||||
|
||||
// @todo call functions to initialize openssl, and create the AMQP connection
|
||||
// (see exampe above)
|
||||
````
|
||||
|
||||
By itself, AMQP-CPP does not check if the created TLS connection is sufficient
|
||||
secure. Whether the certificate is expired, self-signed, missing or invalid: for
|
||||
AMQP-CPP it all doesn't matter and the connection is simply permitted. If you
|
||||
want to be more strict (for example: if you want to verify the server's certificate),
|
||||
you must do this yourself by implementing the "onSecured()" method in your handler
|
||||
object:
|
||||
|
||||
````c++
|
||||
#include <amqpcpp.h>
|
||||
#include <amqpcpp/linux_tcp.h>
|
||||
|
||||
class MyTcpHandler : public AMQP::TcpHandler
|
||||
{
|
||||
/**
|
||||
* Method that is called right after the TLS connection has been created.
|
||||
* In this method you can check the connection properties (like the certificate)
|
||||
* and return false if you find it not secure enough
|
||||
* @param connection the connection that has just completed the tls handshake
|
||||
* @param ssl SSL structure from the openssl library
|
||||
* @return bool true if connection is secure enough to start the AMQP protocol
|
||||
*/
|
||||
virtual bool onSecure(AMQP::TcpConnection *connection, const SSL *ssl) override
|
||||
{
|
||||
// @todo call functions from the openssl library to check the certificate,
|
||||
// like SSL_get_peer_certificate() or SSL_get_verify_result().
|
||||
// For now we always allow the connection to proceed
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* All other methods (like onConnected(), onError(), etc) are left out of this
|
||||
* example, but would be here if this was an actual user space handler class.
|
||||
*/
|
||||
};
|
||||
````
|
||||
|
||||
The SSL pointer that is passed to the onSecured() method refers to the "SSL"
|
||||
structure from the openssl library.
|
||||
|
||||
|
||||
EXISTING EVENT LOOPS
|
||||
====================
|
||||
|
||||
Both the pure AMQP::Connection as well as the easier AMQP::TcpConnection class
|
||||
allow you to integrate AMQP-CPP in your own event loop. Whether you take care
|
||||
of running the event loop yourself (for example by using the select() system
|
||||
call), or if you use an existing library for it (like libevent, libev or libuv),
|
||||
you can implement the "monitor()" method to watch the file descriptors and
|
||||
of running the event loop yourself (for example by using the select() system
|
||||
call), or if you use an existing library for it (like libevent, libev or libuv),
|
||||
you can implement the "monitor()" method to watch the file descriptors and
|
||||
hand over control back to AMQP-CPP when one of the sockets become active.
|
||||
|
||||
For libev and libevent users, we have even implemented an example implementation,
|
||||
|
|
@ -394,26 +513,26 @@ int main()
|
|||
{
|
||||
// access to the event loop
|
||||
auto *loop = EV_DEFAULT;
|
||||
|
||||
|
||||
// handler for libev (so we don't have to implement AMQP::TcpHandler!)
|
||||
AMQP::LibEvHandler handler(loop);
|
||||
|
||||
|
||||
// make a connection
|
||||
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
|
||||
|
||||
|
||||
// we need a channel too
|
||||
AMQP::TcpChannel channel(&connection);
|
||||
|
||||
|
||||
// create a temporary queue
|
||||
channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) {
|
||||
|
||||
|
||||
// report the name of the temporary queue
|
||||
std::cout << "declared queue " << name << std::endl;
|
||||
|
||||
|
||||
// now we can close the connection
|
||||
connection.close();
|
||||
});
|
||||
|
||||
|
||||
// run the loop
|
||||
ev_run(loop, 0);
|
||||
|
||||
|
|
@ -426,18 +545,84 @@ The AMQP::LibEvHandler and AMQP::LibEventHandler classes are extended AMQP::TcpH
|
|||
classes, with an implementation of the monitor() method that simply adds the
|
||||
filedescriptor to the event loop. If you use this class however, it is recommended not to
|
||||
instantiate it directly (like we did in the example), but to create your own
|
||||
"MyHandler" class that extends from it, and in which you also implement the
|
||||
"MyHandler" class that extends from it, and in which you also implement the
|
||||
onError() method to report possible connection errors to your end users.
|
||||
|
||||
Currently, we have only added such an example TcpHandler implementation for libev and
|
||||
libevent. For other event loops (like libuv and boost asio) we do not yet have
|
||||
Currently, we have example TcpHandler implementations for libev,
|
||||
libevent, and Boost's asio. For other event loops (like libuv) we do not yet have
|
||||
such examples.
|
||||
|
||||
| TCP Handler Impl | Header File Location | Sample File Location |
|
||||
| ----------------------- | ---------------------- | --------------------- |
|
||||
| Boost asio (io_service) | include/libboostasio.h | tests/libboostasio.cpp |
|
||||
| libev | include/libev.h | tests/libev.cpp |
|
||||
| libevent | include/libevent.h | tests/libevent.cpp |
|
||||
| libuv | include/libuv.h | (Not available) |
|
||||
|
||||
HEARTBEATS
|
||||
==========
|
||||
|
||||
The AMQP protocol supports *heartbeats*. If this heartbeat feature is enabled, the
|
||||
client and the server negotiate a heartbeat interval during connection setup, and
|
||||
they agree to send at least *some kind of data* over the connection during every
|
||||
iteration of that interval. The normal data that is sent over the connection (like
|
||||
publishing or consuming messages) is normally sufficient to keep the connection alive,
|
||||
but if the client or server was idle during the negotiated interval time, a dummy
|
||||
heartbeat message must be sent instead.
|
||||
|
||||
The default behavior of the AMQP-CPP library is to disable heartbeats. The
|
||||
proposed heartbeat interval of the server during connection setup (the server
|
||||
normally suggests an interval of 60 seconds) is vetoed by the AMQP-CPP library so
|
||||
no heartbeats are ever needed to be sent over the connection. This means that you
|
||||
can safely keep your AMQP connection idle for as long as you like, and/or run long
|
||||
lasting algorithms after you've consumed a message from RabbitMQ, without having
|
||||
to worry about the connection being idle for too long.
|
||||
|
||||
You can however choose to enable these heartbeats. If you want to enable heartbeats,
|
||||
simple implement the onNegotiate() method inside your ConnectionHandler or
|
||||
TcpHandler class and have it return the interval that you find appropriate.
|
||||
|
||||
````c++
|
||||
#include <amqpcpp.h>
|
||||
|
||||
class MyTcpHandler : public AMQP::TcpHandler
|
||||
{
|
||||
/**
|
||||
* Method that is called when the server tries to negotiate a heartbeat
|
||||
* interval, and that is overridden to get rid of the default implementation
|
||||
* (which vetoes the suggested heartbeat interval), and accept the interval
|
||||
* instead.
|
||||
* @param connection The connection on which the error occured
|
||||
* @param interval The suggested interval in seconds
|
||||
*/
|
||||
virtual void onNegotiate(AMQP::TcpConnection *connection, uint16_t interval)
|
||||
{
|
||||
// we accept the suggestion from the server, but if the interval is smaller
|
||||
// that one minute, we will use a one minute interval instead
|
||||
if (interval < 60) interval = 60;
|
||||
|
||||
// @todo
|
||||
// set a timer in your event loop, and make sure that you call
|
||||
// connection->heartbeat() every _interval_ seconds.
|
||||
|
||||
// return the interval that we want to use
|
||||
return interval;
|
||||
}
|
||||
};
|
||||
````
|
||||
|
||||
If you have enabled heartbeats, it is your own responsibility to ensure that the
|
||||
```connection->heartbeat()``` method is called at least once during this period,
|
||||
or that you call one of the other channel or connection methods to send data
|
||||
over the connection.
|
||||
|
||||
In the libev event loop implementation the heartbeats are enabled by default.
|
||||
|
||||
|
||||
CHANNELS
|
||||
========
|
||||
|
||||
In the above example we created a channel object. A channel is a sort of virtual
|
||||
In the above example we created a channel object. A channel is a sort of virtual
|
||||
connection, and it is possible to create many channels that all use
|
||||
the same connection.
|
||||
|
||||
|
|
@ -451,8 +636,8 @@ documented.
|
|||
|
||||
All operations that you can perform on a channel are non-blocking. This means
|
||||
that it is not possible for a method (like Channel::declareExchange()) to
|
||||
immediately return 'true' or 'false'. Instead, almost every method of the Channel
|
||||
class returns an instance of the 'Deferred' class. This 'Deferred' object can be
|
||||
immediately return 'true' or 'false'. Instead, almost every method of the Channel
|
||||
class returns an instance of the 'Deferred' class. This 'Deferred' object can be
|
||||
used to install handlers that will be called in case of success or failure.
|
||||
|
||||
For example, if you call the channel.declareExchange() method, the AMQP-CPP library
|
||||
|
|
@ -544,7 +729,7 @@ channel object right after it was constructed.
|
|||
CHANNEL ERRORS
|
||||
==============
|
||||
|
||||
It is important to realize that any error that occurs on a channel,
|
||||
It is important to realize that any error that occurs on a channel,
|
||||
invalidates the entire channel, including all subsequent instructions that
|
||||
were already sent over it. This means that if you call multiple methods in a row,
|
||||
and the first method fails, all subsequent methods will not be executed either:
|
||||
|
|
@ -576,7 +761,7 @@ requires and extra instruction to be sent to the RabbitMQ server, so some extra
|
|||
bytes are sent over the network, and some additional resources in both the client
|
||||
application and the RabbitMQ server are used (although this is all very limited).
|
||||
|
||||
If possible, it is best to make use of this feature. For example, if you have an important AMQP
|
||||
If possible, it is best to make use of this feature. For example, if you have an important AMQP
|
||||
connection that you use for consuming messages, and at the same time you want
|
||||
to send another instruction to RabbitMQ (like declaring a temporary queue), it is
|
||||
best to set up a new channel for this 'declare' instruction. If the declare fails,
|
||||
|
|
@ -591,7 +776,7 @@ void myDeclareMethod(AMQP::Connection *connection)
|
|||
{
|
||||
// create temporary channel to declare a queue
|
||||
AMQP::Channel channel(connection);
|
||||
|
||||
|
||||
// declare the queue (the channel object is destructed before the
|
||||
// instruction reaches the server, but the AMQP-CPP library can deal
|
||||
// with this)
|
||||
|
|
@ -710,44 +895,54 @@ in almost any form:
|
|||
````c++
|
||||
/**
|
||||
* Publish a message to an exchange
|
||||
*
|
||||
* The following flags can be used
|
||||
*
|
||||
* - mandatory if set, an unroutable message will be sent back to
|
||||
* the client (currently not supported)
|
||||
*
|
||||
* - immediate if set, a message that could not immediately be consumed
|
||||
* is returned to the client (currently not supported)
|
||||
*
|
||||
* If either of the two flags is set, and the message could not immediately
|
||||
* be published, the message is returned by the server to the client. However,
|
||||
* at this moment in time, the AMQP-CPP library does not support catching
|
||||
* such returned messages.
|
||||
*
|
||||
*
|
||||
* You have to supply the name of an exchange and a routing key. RabbitMQ will
|
||||
* then try to send the message to one or more queues. With the optional flags
|
||||
* parameter you can specify what should happen if the message could not be routed
|
||||
* to a queue. By default, unroutable message are silently discarded.
|
||||
*
|
||||
* This method returns a reference to a DeferredPublisher object. You can use
|
||||
* this returned object to install callbacks that are called when an undeliverable
|
||||
* message is returned, or to set the callback that is called when the server
|
||||
* confirms that the message was received.
|
||||
*
|
||||
* To enable handling returned messages, or to enable publisher-confirms, you must
|
||||
* not only set the callback, but also pass in appropriate flags to enable this
|
||||
* feature. If you do not pass in these flags, your callbacks will not be called.
|
||||
* If you are not at all interested in returned messages or publish-confirms, you
|
||||
* can ignore the flag and the returned object.
|
||||
*
|
||||
* Watch out: the channel returns _the same_ DeferredPublisher object for all
|
||||
* calls to the publish() method. This means that the callbacks that you install
|
||||
* for the first published message are also used for subsequent messages _and_
|
||||
* it means that if you install a different callback for a later publish
|
||||
* operation, it overwrites your earlier callbacks
|
||||
*
|
||||
* The following flags can be supplied:
|
||||
*
|
||||
* - mandatory If set, server returns messages that are not sent to a queue
|
||||
* - immediate If set, server returns messages that can not immediately be forwarded to a consumer.
|
||||
*
|
||||
* @param exchange the exchange to publish to
|
||||
* @param routingkey the routing key
|
||||
* @param flags optional flags (see above)
|
||||
* @param envelope the full envelope to send
|
||||
* @param message the message to send
|
||||
* @param size size of the message
|
||||
* @param flags optional flags
|
||||
*/
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, int flags, const AMQP::Envelope &envelope);
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const AMQP::Envelope &envelope);
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, int flags, const std::string &message);
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const std::string &message);
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, int flags, const char *message, size_t size);
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const char *message, size_t size);
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope, int flags = 0) { return _implementation->publish(exchange, routingKey, envelope, flags); }
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const std::string &message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message.data(), message.size()), flags); }
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const char *message, size_t size, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, size), flags); }
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const char *message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, strlen(message)), flags); }
|
||||
````
|
||||
|
||||
Published messages are normally not confirmed by the server, and the RabbitMQ
|
||||
will not send a report back to inform you whether the message was succesfully
|
||||
published or not. Therefore the publish method does not return a Deferred
|
||||
object.
|
||||
published or not. But with the flags you can instruct RabbitMQ to send back
|
||||
the message if it was undeliverable.
|
||||
|
||||
As long as no error is reported via the Channel::onError() method, you can safely
|
||||
assume that your messages were delivered.
|
||||
|
||||
This can of course be a problem when you are publishing many messages. If you get
|
||||
You can also use transactions to ensure that your messages get delivered.
|
||||
Let's say that you are publishing many messages in a row. If you get
|
||||
an error halfway through there is no way to know for sure how many messages made
|
||||
it to the broker and how many should be republished. If this is important, you can
|
||||
wrap the publish commands inside a transaction. In this case, if an error occurs,
|
||||
|
|
@ -774,8 +969,8 @@ channel.commitTransaction()
|
|||
});
|
||||
````
|
||||
|
||||
Note that AMQP transactions are not as powerful as transactions that are
|
||||
knows in the database world. It is not possible to wrap all sort of
|
||||
Note that AMQP transactions are not as powerful as transactions that are
|
||||
knows in the database world. It is not possible to wrap all sort of
|
||||
operations in a transaction, they are only meaningful for publishing
|
||||
and consuming.
|
||||
|
||||
|
|
@ -951,7 +1146,6 @@ need additional attention:
|
|||
|
||||
- ability to set up secure connections (or is this fully done on the IO level)
|
||||
- login with other protocols than login/password
|
||||
- returned messages
|
||||
|
||||
We also need to add more safety checks so that strange or invalid data from
|
||||
RabbitMQ does not break the library (although in reality RabbitMQ only sends
|
||||
|
|
@ -964,4 +1158,3 @@ class that can be directly plugged into libev, libevent and libuv event loops.
|
|||
|
||||
For performance reasons, we need to investigate if we can limit the number of times
|
||||
an incoming or outgoing messages is copied.
|
||||
|
||||
|
|
|
|||
77
amqpcpp.h
77
amqpcpp.h
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
* AMQP.h
|
||||
*
|
||||
* Starting point for all includes of the Copernica AMQP library
|
||||
*
|
||||
* @documentation public
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// base C++ include files
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <limits>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
// base C include files
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
// forward declarations
|
||||
#include <amqpcpp/classes.h>
|
||||
|
||||
// utility classes
|
||||
#include <amqpcpp/endian.h>
|
||||
#include <amqpcpp/buffer.h>
|
||||
#include <amqpcpp/bytebuffer.h>
|
||||
#include <amqpcpp/receivedframe.h>
|
||||
#include <amqpcpp/outbuffer.h>
|
||||
#include <amqpcpp/watchable.h>
|
||||
#include <amqpcpp/monitor.h>
|
||||
|
||||
// amqp types
|
||||
#include <amqpcpp/field.h>
|
||||
#include <amqpcpp/numericfield.h>
|
||||
#include <amqpcpp/decimalfield.h>
|
||||
#include <amqpcpp/stringfield.h>
|
||||
#include <amqpcpp/booleanset.h>
|
||||
#include <amqpcpp/fieldproxy.h>
|
||||
#include <amqpcpp/table.h>
|
||||
#include <amqpcpp/array.h>
|
||||
|
||||
// envelope for publishing and consuming
|
||||
#include <amqpcpp/metadata.h>
|
||||
#include <amqpcpp/envelope.h>
|
||||
#include <amqpcpp/message.h>
|
||||
|
||||
// mid level includes
|
||||
#include <amqpcpp/exchangetype.h>
|
||||
#include <amqpcpp/flags.h>
|
||||
#include <amqpcpp/callbacks.h>
|
||||
#include <amqpcpp/deferred.h>
|
||||
#include <amqpcpp/deferredconsumer.h>
|
||||
#include <amqpcpp/deferredqueue.h>
|
||||
#include <amqpcpp/deferreddelete.h>
|
||||
#include <amqpcpp/deferredcancel.h>
|
||||
#include <amqpcpp/deferredget.h>
|
||||
#include <amqpcpp/channelimpl.h>
|
||||
#include <amqpcpp/channel.h>
|
||||
#include <amqpcpp/login.h>
|
||||
#include <amqpcpp/address.h>
|
||||
#include <amqpcpp/connectionhandler.h>
|
||||
#include <amqpcpp/connectionimpl.h>
|
||||
#include <amqpcpp/connection.h>
|
||||
#include <amqpcpp/tcphandler.h>
|
||||
#include <amqpcpp/tcpconnection.h>
|
||||
#include <amqpcpp/tcpchannel.h>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
version: '1.0.{build}'
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
- Debug
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
|
||||
before_build:
|
||||
- cmake -G "Visual Studio 15 2017 Win64" .
|
||||
|
||||
build:
|
||||
project: $(APPVEYOR_BUILD_FOLDER)\amqpcpp.sln
|
||||
|
|
@ -0,0 +1 @@
|
|||
a.out
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
|
||||
###################################
|
||||
# Boost
|
||||
###################################
|
||||
|
||||
add_executable(amqpcpp_boost_example libboostasio.cpp)
|
||||
|
||||
add_dependencies(amqpcpp_boost_example amqpcpp)
|
||||
|
||||
target_link_libraries(amqpcpp_boost_example amqpcpp boost_system pthread dl ssl)
|
||||
|
||||
###################################
|
||||
# Libev
|
||||
###################################
|
||||
|
||||
add_executable(amqpcpp_libev_example libev.cpp)
|
||||
|
||||
add_dependencies(amqpcpp_libev_example amqpcpp)
|
||||
|
||||
target_link_libraries(amqpcpp_libev_example amqpcpp ev pthread dl ssl)
|
||||
|
||||
|
||||
###################################
|
||||
# Libuv
|
||||
###################################
|
||||
|
||||
add_executable(amqpcpp_libuv_example libuv.cpp)
|
||||
|
||||
add_dependencies(amqpcpp_libuv_example amqpcpp)
|
||||
|
||||
target_link_libraries(amqpcpp_libuv_example amqpcpp uv pthread dl ssl)
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* LibBoostAsio.cpp
|
||||
*
|
||||
* Test program to check AMQP functionality based on Boost's asio io_service.
|
||||
*
|
||||
* @author Gavin Smith <gavin.smith@coralbay.tv>
|
||||
*
|
||||
* Compile with g++ -std=c++14 libboostasio.cpp -o boost_test -lpthread -lboost_system -lamqpcpp
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
|
||||
|
||||
#include <amqpcpp.h>
|
||||
#include <amqpcpp/libboostasio.h>
|
||||
|
||||
/**
|
||||
* Main program
|
||||
* @return int
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
|
||||
// access to the boost asio handler
|
||||
// note: we suggest use of 2 threads - normally one is fin (we are simply demonstrating thread safety).
|
||||
boost::asio::io_service service(4);
|
||||
|
||||
// handler for libev
|
||||
AMQP::LibBoostAsioHandler handler(service);
|
||||
|
||||
// make a connection
|
||||
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://guest:guest@localhost/"));
|
||||
|
||||
// we need a channel too
|
||||
AMQP::TcpChannel channel(&connection);
|
||||
|
||||
// create a temporary queue
|
||||
channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) {
|
||||
|
||||
// report the name of the temporary queue
|
||||
std::cout << "declared queue " << name << std::endl;
|
||||
|
||||
// now we can close the connection
|
||||
connection.close();
|
||||
});
|
||||
|
||||
// run the handler
|
||||
// a t the moment, one will need SIGINT to stop. In time, should add signal handling through boost API.
|
||||
return service.run();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* LibEV.cpp
|
||||
*
|
||||
* Test program to check AMQP functionality based on LibEV
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <ev.h>
|
||||
#include <amqpcpp.h>
|
||||
#include <amqpcpp/libev.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
/**
|
||||
* Custom handler
|
||||
*/
|
||||
class MyHandler : public AMQP::LibEvHandler
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Method that is called when a connection error occurs
|
||||
* @param connection
|
||||
* @param message
|
||||
*/
|
||||
virtual void onError(AMQP::TcpConnection *connection, const char *message) override
|
||||
{
|
||||
std::cout << "error: " << message << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that is called when the TCP connection ends up in a connected state
|
||||
* @param connection The TCP connection
|
||||
*/
|
||||
virtual void onConnected(AMQP::TcpConnection *connection) override
|
||||
{
|
||||
std::cout << "connected" << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param ev_loop
|
||||
*/
|
||||
MyHandler(struct ev_loop *loop) : AMQP::LibEvHandler(loop) {}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~MyHandler() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main program
|
||||
* @return int
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
// access to the event loop
|
||||
auto *loop = EV_DEFAULT;
|
||||
|
||||
// handler for libev
|
||||
MyHandler handler(loop);
|
||||
|
||||
// init the SSL library
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
SSL_library_init();
|
||||
#else
|
||||
OPENSSL_init_ssl(0, NULL);
|
||||
#endif
|
||||
|
||||
// make a connection
|
||||
AMQP::Address address("amqp://guest:guest@localhost/");
|
||||
// AMQP::Address address("amqps://guest:guest@localhost/");
|
||||
AMQP::TcpConnection connection(&handler, address);
|
||||
|
||||
// we need a channel too
|
||||
AMQP::TcpChannel channel(&connection);
|
||||
|
||||
// create a temporary queue
|
||||
channel.declareQueue(AMQP::exclusive).onSuccess([&connection, &channel](const std::string &name, uint32_t messagecount, uint32_t consumercount) {
|
||||
|
||||
// report the name of the temporary queue
|
||||
std::cout << "declared queue " << name << std::endl;
|
||||
|
||||
// close the channel
|
||||
channel.close().onSuccess([&connection, &channel]() {
|
||||
|
||||
// report that channel was closed
|
||||
std::cout << "channel closed" << std::endl;
|
||||
|
||||
// close the connection
|
||||
connection.close();
|
||||
});
|
||||
});
|
||||
|
||||
// run the loop
|
||||
ev_run(loop, 0);
|
||||
|
||||
// done
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* LibUV.cpp
|
||||
*
|
||||
* Test program to check AMQP functionality based on LibUV
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 - 2017 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <uv.h>
|
||||
#include <amqpcpp.h>
|
||||
#include <amqpcpp/libuv.h>
|
||||
|
||||
/**
|
||||
* Custom handler
|
||||
*/
|
||||
class MyHandler : public AMQP::LibUvHandler
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Method that is called when a connection error occurs
|
||||
* @param connection
|
||||
* @param message
|
||||
*/
|
||||
virtual void onError(AMQP::TcpConnection *connection, const char *message) override
|
||||
{
|
||||
std::cout << "error: " << message << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that is called when the TCP connection ends up in a connected state
|
||||
* @param connection The TCP connection
|
||||
*/
|
||||
virtual void onConnected(AMQP::TcpConnection *connection) override
|
||||
{
|
||||
std::cout << "connected" << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param uv_loop
|
||||
*/
|
||||
MyHandler(uv_loop_t *loop) : AMQP::LibUvHandler(loop) {}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~MyHandler() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main program
|
||||
* @return int
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
// access to the event loop
|
||||
auto *loop = uv_default_loop();
|
||||
|
||||
// handler for libev
|
||||
MyHandler handler(loop);
|
||||
|
||||
// make a connection
|
||||
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://guest:guest@localhost/"));
|
||||
|
||||
// we need a channel too
|
||||
AMQP::TcpChannel channel(&connection);
|
||||
|
||||
// create a temporary queue
|
||||
channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) {
|
||||
|
||||
// report the name of the temporary queue
|
||||
std::cout << "declared queue " << name << std::endl;
|
||||
});
|
||||
|
||||
// run the loop
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
|
||||
// done
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
add_sources(
|
||||
address.h
|
||||
addresses.h
|
||||
array.h
|
||||
booleanset.h
|
||||
buffer.h
|
||||
bytebuffer.h
|
||||
callbacks.h
|
||||
channel.h
|
||||
channelimpl.h
|
||||
classes.h
|
||||
connection.h
|
||||
connectionhandler.h
|
||||
connectionimpl.h
|
||||
copiedbuffer.h
|
||||
decimalfield.h
|
||||
deferred.h
|
||||
deferredcancel.h
|
||||
deferredconsumer.h
|
||||
deferredconsumerbase.h
|
||||
deferreddelete.h
|
||||
deferredget.h
|
||||
deferredqueue.h
|
||||
endian.h
|
||||
entityimpl.h
|
||||
envelope.h
|
||||
exception.h
|
||||
exchangetype.h
|
||||
field.h
|
||||
fieldproxy.h
|
||||
flags.h
|
||||
frame.h
|
||||
libev.h
|
||||
libevent.h
|
||||
libuv.h
|
||||
login.h
|
||||
message.h
|
||||
metadata.h
|
||||
monitor.h
|
||||
numericfield.h
|
||||
outbuffer.h
|
||||
protocolexception.h
|
||||
receivedframe.h
|
||||
stack_ptr.h
|
||||
stringfield.h
|
||||
table.h
|
||||
tcpchannel.h
|
||||
tcpconnection.h
|
||||
tcpdefines.h
|
||||
tcphandler.h
|
||||
watchable.h
|
||||
|
||||
)
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* AMQP.h
|
||||
*
|
||||
* Starting point for all includes of the Copernica AMQP library
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// base C++ include files
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <limits>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
// base C include files
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
// fix strcasecmp on non linux platforms
|
||||
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)) && !defined(__CYGWIN__)
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
|
||||
// forward declarations
|
||||
#include "amqpcpp/classes.h"
|
||||
|
||||
// utility classes
|
||||
#include "amqpcpp/endian.h"
|
||||
#include "amqpcpp/buffer.h"
|
||||
#include "amqpcpp/bytebuffer.h"
|
||||
#include "amqpcpp/receivedframe.h"
|
||||
#include "amqpcpp/outbuffer.h"
|
||||
#include "amqpcpp/watchable.h"
|
||||
#include "amqpcpp/monitor.h"
|
||||
|
||||
// amqp types
|
||||
#include "amqpcpp/field.h"
|
||||
#include "amqpcpp/numericfield.h"
|
||||
#include "amqpcpp/decimalfield.h"
|
||||
#include "amqpcpp/stringfield.h"
|
||||
#include "amqpcpp/booleanset.h"
|
||||
#include "amqpcpp/fieldproxy.h"
|
||||
#include "amqpcpp/table.h"
|
||||
#include "amqpcpp/array.h"
|
||||
|
||||
// envelope for publishing and consuming
|
||||
#include "amqpcpp/metadata.h"
|
||||
#include "amqpcpp/envelope.h"
|
||||
#include "amqpcpp/message.h"
|
||||
|
||||
// mid level includes
|
||||
#include "amqpcpp/exchangetype.h"
|
||||
#include "amqpcpp/flags.h"
|
||||
#include "amqpcpp/callbacks.h"
|
||||
#include "amqpcpp/deferred.h"
|
||||
#include "amqpcpp/deferredconsumer.h"
|
||||
#include "amqpcpp/deferredqueue.h"
|
||||
#include "amqpcpp/deferreddelete.h"
|
||||
#include "amqpcpp/deferredcancel.h"
|
||||
#include "amqpcpp/deferredget.h"
|
||||
#include "amqpcpp/deferredpublisher.h"
|
||||
#include "amqpcpp/channelimpl.h"
|
||||
#include "amqpcpp/channel.h"
|
||||
#include "amqpcpp/login.h"
|
||||
#include "amqpcpp/address.h"
|
||||
#include "amqpcpp/connectionhandler.h"
|
||||
#include "amqpcpp/connectionimpl.h"
|
||||
#include "amqpcpp/connection.h"
|
||||
#include "amqpcpp/openssl.h"
|
||||
|
|
@ -4,9 +4,9 @@
|
|||
* An AMQP address in the "amqp://user:password@hostname:port/vhost" notation
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 Copernica BV
|
||||
* @copyright 2015 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Include guard
|
||||
*/
|
||||
|
|
@ -23,6 +23,12 @@ namespace AMQP {
|
|||
class Address
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* The auth method
|
||||
* @var bool
|
||||
*/
|
||||
bool _secure = false;
|
||||
|
||||
/**
|
||||
* Login data (username + password)
|
||||
* @var Login
|
||||
|
|
@ -46,6 +52,16 @@ private:
|
|||
* @var std::string
|
||||
*/
|
||||
std::string _vhost;
|
||||
|
||||
|
||||
/**
|
||||
* The default port
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t defaultport() const
|
||||
{
|
||||
return _secure ? 5671 : 5672;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
@ -60,11 +76,17 @@ public:
|
|||
// position of the last byte
|
||||
const char *last = data + size;
|
||||
|
||||
// must start with amqp://
|
||||
if (size < 7 || strncmp(data, "amqp://", 7) != 0) throw std::runtime_error("AMQP address should start with \"amqp://\"");
|
||||
// must start with ampqs:// to have a secure connection (and we also assign a different default port)
|
||||
if (strncmp(data, "amqps://", 8) == 0) _secure = true;
|
||||
|
||||
// otherwise protocol must be amqp://
|
||||
else if (strncmp(data, "amqp://", 7) != 0) throw std::runtime_error("AMQP address should start with \"amqp://\" or \"amqps://\"");
|
||||
|
||||
// assign default port (we may overwrite it later)
|
||||
_port = defaultport();
|
||||
|
||||
// begin of the string was parsed
|
||||
data += 7;
|
||||
data += _secure ? 8 : 7;
|
||||
|
||||
// do we have a '@' to split user-data and hostname?
|
||||
const char *at = (const char *)memchr(data, '@', last - data);
|
||||
|
|
@ -115,7 +137,7 @@ public:
|
|||
|
||||
/**
|
||||
* Constructor to parse an address string
|
||||
* The address should start with "amqp://
|
||||
* The address should start with amqp:// or amqps://
|
||||
* @param data
|
||||
* @throws std::runtime_error
|
||||
*/
|
||||
|
|
@ -133,8 +155,10 @@ public:
|
|||
* @param port
|
||||
* @param login
|
||||
* @param vhost
|
||||
* @param secure
|
||||
*/
|
||||
Address(std::string host, uint16_t port, Login login, std::string vhost) :
|
||||
Address(std::string host, uint16_t port, Login login, std::string vhost, bool secure = false) :
|
||||
_secure(secure),
|
||||
_login(std::move(login)),
|
||||
_hostname(std::move(host)),
|
||||
_port(port),
|
||||
|
|
@ -145,6 +169,15 @@ public:
|
|||
*/
|
||||
virtual ~Address() = default;
|
||||
|
||||
/**
|
||||
* Should we open a secure connection?
|
||||
* @return bool
|
||||
*/
|
||||
bool secure() const
|
||||
{
|
||||
return _secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the login data
|
||||
* @return Login
|
||||
|
|
@ -183,12 +216,12 @@ public:
|
|||
|
||||
/**
|
||||
* Cast to a string
|
||||
* @return std:string
|
||||
* @return std::string
|
||||
*/
|
||||
operator std::string () const
|
||||
{
|
||||
// result object
|
||||
std::string str("amqp://");
|
||||
std::string str(_secure ? "amqps://" : "amqp://");
|
||||
|
||||
// append login
|
||||
str.append(_login.user()).append(":").append(_login.password()).append("@").append(_hostname);
|
||||
|
|
@ -213,10 +246,13 @@ public:
|
|||
*/
|
||||
bool operator==(const Address &that) const
|
||||
{
|
||||
// security setting should match
|
||||
if (_secure != that._secure) return false;
|
||||
|
||||
// logins must match
|
||||
if (_login != that._login) return false;
|
||||
|
||||
// hostname must match, but are nt case sensitive
|
||||
// hostname must match, but are not case sensitive
|
||||
if (strcasecmp(_hostname.data(), that._hostname.data()) != 0) return false;
|
||||
|
||||
// portnumber must match
|
||||
|
|
@ -236,6 +272,62 @@ public:
|
|||
// the opposite of operator==
|
||||
return !operator==(that);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison operator that is useful if addresses have to be ordered
|
||||
* @param that
|
||||
* @return bool
|
||||
*/
|
||||
bool operator<(const Address &that) const
|
||||
{
|
||||
// compare auth methods (amqp comes before amqps)
|
||||
if (_secure != that._secure) return !_secure;
|
||||
|
||||
// compare logins
|
||||
if (_login != that._login) return _login < that._login;
|
||||
|
||||
// hostname must match, but are not case sensitive
|
||||
int result = strcasecmp(_hostname.data(), that._hostname.data());
|
||||
|
||||
// if hostnames are not equal, we know the result
|
||||
if (result != 0) return result < 0;
|
||||
|
||||
// portnumber must match
|
||||
if (_port != that._port) return _port < that._port;
|
||||
|
||||
// and finally compare the vhosts
|
||||
return _vhost < that._vhost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Friend function to allow writing the address to a stream
|
||||
* @param stream
|
||||
* @param address
|
||||
* @return std::ostream
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Address &address)
|
||||
{
|
||||
// start with the protocol and login
|
||||
stream << (address._secure ? "amqps://" : "amqp://");
|
||||
|
||||
// do we have a login?
|
||||
if (address._login) stream << address._login << "@";
|
||||
|
||||
// write hostname
|
||||
stream << address._hostname;
|
||||
|
||||
// do we need a special portnumber?
|
||||
if (address._port != address.defaultport()) stream << ":" << address._port;
|
||||
|
||||
// append default vhost
|
||||
stream << "/";
|
||||
|
||||
// do we have a special vhost?
|
||||
if (address._vhost != "/") stream << address._vhost;
|
||||
|
||||
// done
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Callbacks.h
|
||||
*
|
||||
* Class storing deferred callbacks of different type.
|
||||
*
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include guard
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Forward declarations
|
||||
*/
|
||||
class Message;
|
||||
class MetaData;
|
||||
|
||||
/**
|
||||
* Generic callbacks that are used by many deferred objects
|
||||
*/
|
||||
using SuccessCallback = std::function<void()>;
|
||||
using ErrorCallback = std::function<void(const char *message)>;
|
||||
using FinalizeCallback = std::function<void()>;
|
||||
|
||||
/**
|
||||
* Declaring and deleting a queue
|
||||
*/
|
||||
using QueueCallback = std::function<void(const std::string &name, uint32_t messagecount, uint32_t consumercount)>;
|
||||
using DeleteCallback = std::function<void(uint32_t deletedmessages)>;
|
||||
|
||||
/**
|
||||
* When retrieving the size of a queue in some way
|
||||
*/
|
||||
using EmptyCallback = std::function<void()>;
|
||||
using SizeCallback = std::function<void(uint32_t messagecount)>;
|
||||
|
||||
/**
|
||||
* Starting and stopping a consumer
|
||||
*/
|
||||
using ConsumeCallback = std::function<void(const std::string &consumer)>;
|
||||
using CancelCallback = std::function<void(const std::string &consumer)>;
|
||||
|
||||
/**
|
||||
* Receiving messages, either via consume(), get() or as returned messages
|
||||
* The following methods receive the returned message in multiple parts
|
||||
*/
|
||||
using StartCallback = std::function<void(const std::string &exchange, const std::string &routingkey)>;
|
||||
using HeaderCallback = std::function<void(const MetaData &metaData)>;
|
||||
using DataCallback = std::function<void(const char *data, size_t size)>;
|
||||
using DeliveredCallback = std::function<void(uint64_t deliveryTag, bool redelivered)>;
|
||||
|
||||
/**
|
||||
* For returned messages amqp-cpp first calls a return-callback before the start,
|
||||
* header and data callbacks are called. Instead of the deliver-callback, a
|
||||
* returned-callback is called.
|
||||
*/
|
||||
using ReturnCallback = std::function<void(int16_t code, const std::string &message)>;
|
||||
using ReturnedCallback = std::function<void()>;
|
||||
|
||||
/**
|
||||
* If you do not want to merge all data into a single string, you can als
|
||||
* implement callbacks that return the collected message.
|
||||
*/
|
||||
using MessageCallback = std::function<void(const Message &message, uint64_t deliveryTag, bool redelivered)>;
|
||||
using BounceCallback = std::function<void(const Message &message, int16_t code, const std::string &description)>;
|
||||
|
||||
/**
|
||||
* When using publisher confirms, AckCallback is called when server confirms that message is received
|
||||
* and processed. NackCallback is called otherwise.
|
||||
*/
|
||||
using AckCallback = std::function<void(uint64_t deliveryTag, bool multiple)>;
|
||||
using NackCallback = std::function<void(uint64_t deliveryTag, bool multiple, bool requeue)>;
|
||||
|
||||
/**
|
||||
* End namespace
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Class describing a (mid-level) AMQP channel implementation
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -29,6 +29,10 @@ private:
|
|||
public:
|
||||
/**
|
||||
* Construct a channel object
|
||||
*
|
||||
* The passed in connection pointer must remain valid for the
|
||||
* lifetime of the channel.
|
||||
*
|
||||
* @param connection
|
||||
*/
|
||||
Channel(Connection *connection) : _implementation(new ChannelImpl())
|
||||
|
|
@ -218,6 +222,7 @@ public:
|
|||
* - durable exchange survives a broker restart
|
||||
* - autodelete exchange is automatically removed when all connected queues are removed
|
||||
* - passive only check if the exchange exist
|
||||
* - internal create an internal exchange
|
||||
*
|
||||
* @param name name of the exchange
|
||||
* @param type exchange type
|
||||
|
|
@ -388,17 +393,42 @@ public:
|
|||
|
||||
/**
|
||||
* Publish a message to an exchange
|
||||
*
|
||||
*
|
||||
* You have to supply the name of an exchange and a routing key. RabbitMQ will then try
|
||||
* to send the message to one or more queues. With the optional flags parameter you can
|
||||
* specify what should happen if the message could not be routed to a queue. By default,
|
||||
* unroutable message are silently discarded.
|
||||
*
|
||||
* This method returns a reference to a DeferredPublisher object. You can use this returned
|
||||
* object to install callbacks that are called when an undeliverable message is returned, or
|
||||
* to set the callback that is called when the server confirms that the message was received.
|
||||
*
|
||||
* To enable handling returned messages, or to enable publisher-confirms, you must not only
|
||||
* set the callback, but also pass in appropriate flags to enable this feature. If you do not
|
||||
* pass in these flags, your callbacks will not be called. If you are not at all interested
|
||||
* in returned messages or publish-confirms, you can ignore the flag and the returned object.
|
||||
*
|
||||
* Watch out: the channel returns _the same_ DeferredPublisher object for all calls to the
|
||||
* publish() method. This means that the callbacks that you install for the first published
|
||||
* message are also used for subsequent messages _and_ it means that if you install a different
|
||||
* callback for a later publish operation, it overwrites your earlier callbacks
|
||||
*
|
||||
* The following flags can be supplied:
|
||||
*
|
||||
* - mandatory If set, server returns messages that are not sent to a queue
|
||||
* - immediate If set, server returns messages that can not immediately be forwarded to a consumer.
|
||||
*
|
||||
* @param exchange the exchange to publish to
|
||||
* @param routingkey the routing key
|
||||
* @param envelope the full envelope to send
|
||||
* @param message the message to send
|
||||
* @param size size of the message
|
||||
* @param flags optional flags
|
||||
*/
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope) { return _implementation->publish(exchange, routingKey, envelope); }
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const std::string &message) { return _implementation->publish(exchange, routingKey, Envelope(message.data(), message.size())); }
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const char *message, size_t size) { return _implementation->publish(exchange, routingKey, Envelope(message, size)); }
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const char *message) { return _implementation->publish(exchange, routingKey, Envelope(message, strlen(message))); }
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope, int flags = 0) { return _implementation->publish(exchange, routingKey, envelope, flags); }
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const std::string &message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message.data(), message.size()), flags); }
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const char *message, size_t size, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, size), flags); }
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const char *message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, strlen(message)), flags); }
|
||||
|
||||
/**
|
||||
* Set the Quality of Service (QOS) for this channel
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* that has a private constructor so that it can not be used from outside
|
||||
* the AMQP library
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -34,7 +34,7 @@ namespace AMQP {
|
|||
/**
|
||||
* Forward declarations
|
||||
*/
|
||||
class DeferredConsumerBase;
|
||||
class DeferredReceiver;
|
||||
class BasicDeliverFrame;
|
||||
class DeferredConsumer;
|
||||
class BasicGetOKFrame;
|
||||
|
|
@ -44,6 +44,7 @@ class DeferredDelete;
|
|||
class DeferredCancel;
|
||||
class DeferredQueue;
|
||||
class DeferredGet;
|
||||
class DeferredPublisher;
|
||||
class Connection;
|
||||
class Envelope;
|
||||
class Table;
|
||||
|
|
@ -74,10 +75,16 @@ private:
|
|||
ErrorCallback _errorCallback;
|
||||
|
||||
/**
|
||||
* Handlers for all consumers that are active
|
||||
* @var std::map<std::string,std::shared_ptr<DeferredConsumerBase>
|
||||
* Handler that deals with incoming messages as a result of publish operations
|
||||
* @var std::shared_ptr<DeferredPublisher>
|
||||
*/
|
||||
std::map<std::string,std::shared_ptr<DeferredConsumerBase>> _consumers;
|
||||
std::shared_ptr<DeferredPublisher> _publisher;
|
||||
|
||||
/**
|
||||
* Handlers for all consumers that are active
|
||||
* @var std::map<std::string,std::shared_ptr<DeferredConsumer>
|
||||
*/
|
||||
std::map<std::string,std::shared_ptr<DeferredConsumer>> _consumers;
|
||||
|
||||
/**
|
||||
* Pointer to the oldest deferred result (the first one that is going
|
||||
|
|
@ -122,16 +129,17 @@ private:
|
|||
std::queue<std::pair<bool, CopiedBuffer>> _queue;
|
||||
|
||||
/**
|
||||
* Are we currently operating in synchronous mode?
|
||||
* Are we currently operating in synchronous mode? Meaning: do we first have
|
||||
* to wait for the answer to previous instructions before we send a new instruction?
|
||||
* @var bool
|
||||
*/
|
||||
bool _synchronous = false;
|
||||
|
||||
/**
|
||||
* The current consumer receiving a message
|
||||
* @var std::shared_ptr<DeferredConsumerBase>
|
||||
* The current object that is busy receiving a message
|
||||
* @var std::shared_ptr<DeferredReceiver>
|
||||
*/
|
||||
std::shared_ptr<DeferredConsumerBase> _consumer;
|
||||
std::shared_ptr<DeferredReceiver> _receiver;
|
||||
|
||||
/**
|
||||
* Number of messages sent. Used in confirm mode
|
||||
|
|
@ -446,16 +454,17 @@ public:
|
|||
* Publish a message to an exchange
|
||||
*
|
||||
* If the mandatory or immediate flag is set, and the message could not immediately
|
||||
* be published, the message will be returned to the client. However, the AMQP-CPP
|
||||
* library does not yet report such returned messages.
|
||||
* be published, the message will be returned to the client.
|
||||
*
|
||||
* @param exchange the exchange to publish to
|
||||
* @param routingkey the routing key
|
||||
* @param envelope the full envelope to send
|
||||
* @param message the message to send
|
||||
* @param size size of the message
|
||||
* @param flags optional flags
|
||||
* @return DeferredPublisher
|
||||
*/
|
||||
bool publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope);
|
||||
DeferredPublisher &publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope, int flags);
|
||||
|
||||
/**
|
||||
* Set the Quality of Service (QOS) of the entire connection
|
||||
|
|
@ -559,7 +568,7 @@ public:
|
|||
* @return bool
|
||||
*/
|
||||
bool reject(uint64_t deliveryTag, int flags);
|
||||
|
||||
|
||||
/**
|
||||
* Recover messages that were not yet ack'ed
|
||||
* @param flags optional flags
|
||||
|
|
@ -634,6 +643,9 @@ public:
|
|||
|
||||
// if we are still in connected state we are now ready
|
||||
if (_state == state_connected) _state = state_ready;
|
||||
|
||||
// the last (possibly synchronous) operation was received, so we're no longer in synchronous mode
|
||||
if (_synchronous && _queue.empty()) _synchronous = false;
|
||||
|
||||
// inform handler
|
||||
if (_readyCallback) _readyCallback();
|
||||
|
|
@ -653,7 +665,6 @@ public:
|
|||
{
|
||||
// change state
|
||||
_state = state_closed;
|
||||
_synchronous = false;
|
||||
|
||||
// create a monitor, because the callbacks could destruct the current object
|
||||
Monitor monitor(this);
|
||||
|
|
@ -687,6 +698,9 @@ public:
|
|||
{
|
||||
// skip if there is no oldest callback
|
||||
if (!_oldestCallback) return true;
|
||||
|
||||
// the last (possibly synchronous) operation was received, so we're no longer in synchronous mode
|
||||
if (_synchronous && _queue.empty()) _synchronous = false;
|
||||
|
||||
// we are going to call callbacks that could destruct the channel
|
||||
Monitor monitor(this);
|
||||
|
|
@ -720,18 +734,23 @@ public:
|
|||
|
||||
/**
|
||||
* Install a consumer
|
||||
*
|
||||
* @param consumertag The consumer tag
|
||||
* @param consumer The consumer handler
|
||||
* @param active Is this the new active consumer
|
||||
* @param consumer The consumer object
|
||||
*/
|
||||
void install(std::string consumertag, const std::shared_ptr<DeferredConsumerBase> &consumer, bool active = false)
|
||||
void install(const std::string &consumertag, const std::shared_ptr<DeferredConsumer> &consumer)
|
||||
{
|
||||
// install the consumer handler
|
||||
_consumers[consumertag] = consumer;
|
||||
}
|
||||
|
||||
// should we become the current consumer?
|
||||
if (active) _consumer = consumer;
|
||||
/**
|
||||
* Install the current consumer
|
||||
* @param receiver The receiver object
|
||||
*/
|
||||
void install(const std::shared_ptr<DeferredReceiver> &receiver)
|
||||
{
|
||||
// store object as current receiver
|
||||
_receiver = receiver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -745,23 +764,23 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Process incoming delivery
|
||||
*
|
||||
* @param frame The frame to process
|
||||
* Fetch the receiver for a specific consumer tag
|
||||
* @param consumertag the consumer tag
|
||||
* @return the receiver object
|
||||
*/
|
||||
void process(BasicDeliverFrame &frame);
|
||||
DeferredConsumer *consumer(const std::string &consumertag) const;
|
||||
|
||||
/**
|
||||
* Retrieve the current consumer handler
|
||||
*
|
||||
* Retrieve the current object that is receiving a message
|
||||
* @return The handler responsible for the current message
|
||||
*/
|
||||
DeferredConsumerBase *consumer();
|
||||
|
||||
DeferredReceiver *receiver() const { return _receiver.get(); }
|
||||
|
||||
/**
|
||||
* Mark the current consumer as done
|
||||
* Retrieve the deferred publisher that handles returned messages
|
||||
* @return The deferred publisher object
|
||||
*/
|
||||
void complete();
|
||||
DeferredPublisher *publisher() const { return _publisher.get(); }
|
||||
|
||||
/**
|
||||
* Reset message counter
|
||||
|
|
@ -94,6 +94,15 @@ public:
|
|||
return _implementation.vhost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a ping/heartbeat to the channel to keep it alive
|
||||
* @return bool
|
||||
*/
|
||||
bool heartbeat()
|
||||
{
|
||||
return _implementation.heartbeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse data that was recevied from RabbitMQ
|
||||
*
|
||||
|
|
@ -195,15 +204,6 @@ public:
|
|||
return _implementation.waiting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the heartbeat delay used by this connection
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t heartbeat() const
|
||||
{
|
||||
return _implementation.heartbeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Some classes have access to private properties
|
||||
*/
|
||||
|
|
@ -51,6 +51,10 @@ public:
|
|||
* alternative heartbeat interval, you can override this method
|
||||
* to use an other interval. You should return 0 if you want to
|
||||
* disable heartbeats.
|
||||
*
|
||||
* If heartbeats are enabled, you yourself are responsible to send
|
||||
* out a heartbeat every *interval* number of seconds by calling
|
||||
* the Connection::heartbeat() method.
|
||||
*
|
||||
* @param connection The connection that suggested a heartbeat interval
|
||||
* @param interval The suggested interval from the server
|
||||
|
|
@ -58,8 +62,8 @@ public:
|
|||
*/
|
||||
virtual uint16_t onNegotiate(Connection *connection, uint16_t interval)
|
||||
{
|
||||
// default implementation, suggested heartbeat is ok
|
||||
return interval;
|
||||
// default implementation, disable heartbeats
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -123,12 +123,12 @@ protected:
|
|||
* @var queue
|
||||
*/
|
||||
std::queue<CopiedBuffer> _queue;
|
||||
|
||||
|
||||
/**
|
||||
* Heartbeat delay
|
||||
* @var uint16_t
|
||||
* Is the connection idle (meaning: a heartbeat is necessary)
|
||||
* @var bool
|
||||
*/
|
||||
uint16_t _heartbeat = 0;
|
||||
bool _idle = true;
|
||||
|
||||
/**
|
||||
* Helper method to send the close frame
|
||||
|
|
@ -413,15 +413,6 @@ public:
|
|||
return _channels.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Heartbeat delay
|
||||
* @return uint16_t
|
||||
*/
|
||||
uint16_t heartbeat() const
|
||||
{
|
||||
return _heartbeat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the heartbeat delay
|
||||
* @param heartbeat suggested heartbeat by server
|
||||
|
|
@ -430,7 +421,7 @@ public:
|
|||
uint16_t setHeartbeat(uint16_t heartbeat)
|
||||
{
|
||||
// pass to the handler
|
||||
return _heartbeat = _handler->onNegotiate(_parent, heartbeat);
|
||||
return _handler->onNegotiate(_parent, heartbeat);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -441,6 +432,14 @@ public:
|
|||
// pass to handler
|
||||
_handler->onHeartbeat(_parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a heartbeat to keep the connection alive
|
||||
* By default, this function does nothing if the connection is not in an idle state
|
||||
* @param force always send the heartbeat, even if the connection is not idle
|
||||
* @return bool
|
||||
*/
|
||||
bool heartbeat(bool force=false);
|
||||
|
||||
/**
|
||||
* The actual connection is a friend and can construct this class
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Deferred callback for consumers
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -14,17 +14,22 @@
|
|||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include "deferredconsumerbase.h"
|
||||
#include "deferredextreceiver.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Forward declararions
|
||||
*/
|
||||
class BasicDeliverFrame;
|
||||
|
||||
/**
|
||||
* We extend from the default deferred and add extra functionality
|
||||
*/
|
||||
class DeferredConsumer : public DeferredConsumerBase
|
||||
class DeferredConsumer : public DeferredExtReceiver, public std::enable_shared_from_this<DeferredConsumer>
|
||||
{
|
||||
private:
|
||||
/**
|
||||
|
|
@ -33,6 +38,13 @@ private:
|
|||
*/
|
||||
ConsumeCallback _consumeCallback;
|
||||
|
||||
/**
|
||||
* Process a delivery frame
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void process(BasicDeliverFrame &frame);
|
||||
|
||||
/**
|
||||
* Report success for frames that report start consumer operations
|
||||
* @param name Consumer tag that is started
|
||||
|
|
@ -41,12 +53,10 @@ private:
|
|||
virtual const std::shared_ptr<Deferred> &reportSuccess(const std::string &name) override;
|
||||
|
||||
/**
|
||||
* Announce that a message has been received
|
||||
* @param message The message to announce
|
||||
* @param deliveryTag The delivery tag (for ack()ing)
|
||||
* @param redelivered Is this a redelivered message
|
||||
* Get reference to self to prevent that object falls out of scope
|
||||
* @return std::shared_ptr
|
||||
*/
|
||||
virtual void announce(const Message &message, uint64_t deliveryTag, bool redelivered) const override;
|
||||
virtual std::shared_ptr<DeferredReceiver> lock() override { return shared_from_this(); }
|
||||
|
||||
/**
|
||||
* The channel implementation may call our
|
||||
|
|
@ -54,11 +64,11 @@ private:
|
|||
*/
|
||||
friend class ChannelImpl;
|
||||
friend class ConsumedMessage;
|
||||
friend class BasicDeliverFrame;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Protected constructor that can only be called
|
||||
* from within the channel implementation
|
||||
* Constructor that should only be called from within the channel implementation
|
||||
*
|
||||
* Note: this constructor _should_ be protected, but because make_shared
|
||||
* will then not work, we have decided to make it public after all,
|
||||
|
|
@ -68,26 +78,136 @@ public:
|
|||
* @param failed are we already failed?
|
||||
*/
|
||||
DeferredConsumer(ChannelImpl *channel, bool failed = false) :
|
||||
DeferredConsumerBase(failed, channel) {}
|
||||
DeferredExtReceiver(failed, channel) {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Register the function to be called when a new message is expected
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
* Register a callback function that gets called when the consumer is
|
||||
* started. In the callback you will for receive the consumer-tag
|
||||
* that you need to later stop the consumer
|
||||
* @param callback
|
||||
*/
|
||||
DeferredConsumer &onBegin(const BeginCallback &callback)
|
||||
DeferredConsumer &onSuccess(const ConsumeCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_beginCallback = callback;
|
||||
// store the callback
|
||||
_consumeCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when message headers come in
|
||||
* Register the function that is called when the consumer starts.
|
||||
* It is recommended to use the onSuccess() method mentioned above
|
||||
* since that will also pass the consumer-tag as parameter.
|
||||
* @param callback
|
||||
*/
|
||||
DeferredConsumer &onSuccess(const SuccessCallback &callback)
|
||||
{
|
||||
// call base
|
||||
Deferred::onSuccess(callback);
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when a full message is received
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredConsumer &onReceived(const MessageCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_messageCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for onReceived() (see above)
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredConsumer &onMessage(const MessageCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_messageCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* RabbitMQ sends a message in multiple frames to its consumers.
|
||||
* The AMQP-CPP library collects these frames and merges them into a
|
||||
* single AMQP::Message object that is passed to the callback that
|
||||
* you can set with the onReceived() or onMessage() methods (see above).
|
||||
*
|
||||
* However, you can also write your own algorithm to merge the frames.
|
||||
* In that case you can install callbacks to handle the frames. Every
|
||||
* message is sent in a number of frames:
|
||||
*
|
||||
* - a begin frame that marks the start of the message
|
||||
* - an optional header if the message was sent with an envelope
|
||||
* - zero or more data frames (usually 1, but more for large messages)
|
||||
* - an end frame to mark the end of the message.
|
||||
*
|
||||
* To install handlers for these frames, you can use the onBegin(),
|
||||
* onHeaders(), onData() and onComplete() methods.
|
||||
*
|
||||
* If you just rely on the onReceived() or onMessage() callbacks, you
|
||||
* do not need any of the methods below this line.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register the function that is called when the start frame of a new
|
||||
* consumed message is received
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredConsumer &onBegin(const StartCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_startCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function that is called when the start frame of a new
|
||||
* consumed message is received
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredConsumer &onStart(const StartCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_startCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function that is called when the message size is known
|
||||
*
|
||||
* @param callback The callback to invoke for message headers
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredConsumer &onSize(const SizeCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_sizeCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function that is called when message headers come in
|
||||
*
|
||||
* @param callback The callback to invoke for message headers
|
||||
* @return Same object for chaining
|
||||
|
|
@ -124,54 +244,15 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Register the function that is called when the consumer starts
|
||||
* @param callback
|
||||
* Register a funtion to be called when a message was completely received
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredConsumer &onSuccess(const ConsumeCallback &callback)
|
||||
{
|
||||
// store the callback
|
||||
_consumeCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function that is called when the consumer starts
|
||||
* @param callback
|
||||
*/
|
||||
DeferredConsumer &onSuccess(const SuccessCallback &callback)
|
||||
{
|
||||
// call base
|
||||
Deferred::onSuccess(callback);
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when a message arrives
|
||||
* This fuction is also available as onMessage() because I always forget which name I gave to it
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredConsumer &onReceived(const MessageCallback &callback)
|
||||
DeferredConsumer &onComplete(const DeliveredCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_messageCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when a message arrives
|
||||
* This fuction is also available as onReceived() because I always forget which name I gave to it
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredConsumer &onMessage(const MessageCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_messageCallback = callback;
|
||||
_deliveredCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
|
|
@ -183,10 +264,10 @@ public:
|
|||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredConsumer &onComplete(const CompleteCallback &callback)
|
||||
DeferredConsumer &onDelivered(const DeliveredCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_completeCallback = callback;
|
||||
_deliveredCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* DeferredExtReceiver.h
|
||||
*
|
||||
* Extended receiver that _wants_ to receive message (because it is
|
||||
* consuming or get'ting messages. This is the base class for both
|
||||
* the DeferredConsumer as well as the DeferredGet classes, but not
|
||||
* the base of the DeferredPublisher (which can also receive returned
|
||||
* messages, but not as a result of an explicit request)
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include guard
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include "deferredreceiver.h"
|
||||
|
||||
/**
|
||||
* Begin of namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
class DeferredExtReceiver : public DeferredReceiver
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* The delivery tag for the current message
|
||||
* @var uint64_t
|
||||
*/
|
||||
uint64_t _deliveryTag = 0;
|
||||
|
||||
/**
|
||||
* Is this a redelivered message
|
||||
* @var bool
|
||||
*/
|
||||
bool _redelivered = false;
|
||||
|
||||
/**
|
||||
* Callback for incoming messages
|
||||
* @var MessageCallback
|
||||
*/
|
||||
MessageCallback _messageCallback;
|
||||
|
||||
/**
|
||||
* Callback for when a message was complete finished
|
||||
* @var DeliveredCallback
|
||||
*/
|
||||
DeliveredCallback _deliveredCallback;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the object to send out a message
|
||||
* @param exchange the exchange to which the message was published
|
||||
* @param routingkey the routing key that was used to publish the message
|
||||
*/
|
||||
virtual void initialize(const std::string &exchange, const std::string &routingkey) override;
|
||||
|
||||
/**
|
||||
* Indicate that a message was done
|
||||
*/
|
||||
virtual void complete() override;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param failed Have we already failed?
|
||||
* @param channel The channel we are consuming on
|
||||
*/
|
||||
DeferredExtReceiver(bool failed, ChannelImpl *channel) :
|
||||
DeferredReceiver(failed, channel) {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~DeferredExtReceiver() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* End of namespace
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* DeferredGet.h
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include "deferredconsumerbase.h"
|
||||
#include "deferredextreceiver.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
|
|
@ -27,7 +27,7 @@ namespace AMQP {
|
|||
* it grabs a self-pointer when the callback is running, otherwise the onFinalize()
|
||||
* is called before the actual message is consumed.
|
||||
*/
|
||||
class DeferredGet : public DeferredConsumerBase
|
||||
class DeferredGet : public DeferredExtReceiver, public std::enable_shared_from_this<DeferredGet>
|
||||
{
|
||||
private:
|
||||
/**
|
||||
|
|
@ -40,7 +40,7 @@ private:
|
|||
* Callback with the number of messages still in the queue
|
||||
* @var SizeCallback
|
||||
*/
|
||||
SizeCallback _sizeCallback;
|
||||
SizeCallback _countCallback;
|
||||
|
||||
/**
|
||||
* Report success for a get operation
|
||||
|
|
@ -57,12 +57,15 @@ private:
|
|||
virtual const std::shared_ptr<Deferred> &reportSuccess() const override;
|
||||
|
||||
/**
|
||||
* Announce that a message has been received
|
||||
* @param message The message to announce
|
||||
* @param deliveryTag The delivery tag (for ack()ing)
|
||||
* @param redelivered Is this a redelivered message
|
||||
* Get reference to self to prevent that object falls out of scope
|
||||
* @return std::shared_ptr
|
||||
*/
|
||||
virtual void announce(const Message &message, uint64_t deliveryTag, bool redelivered) const override;
|
||||
virtual std::shared_ptr<DeferredReceiver> lock() override { return shared_from_this(); }
|
||||
|
||||
/**
|
||||
* Extended implementation of the complete method that is called when a message was fully received
|
||||
*/
|
||||
virtual void complete() override;
|
||||
|
||||
/**
|
||||
* The channel implementation may call our
|
||||
|
|
@ -84,61 +87,9 @@ public:
|
|||
* @param failed are we already failed?
|
||||
*/
|
||||
DeferredGet(ChannelImpl *channel, bool failed = false) :
|
||||
DeferredConsumerBase(failed, channel) {}
|
||||
DeferredExtReceiver(failed, channel) {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Register the function to be called when a new message is expected
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onBegin(const BeginCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_beginCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when message headers come in
|
||||
*
|
||||
* @param callback The callback to invoke for message headers
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onHeaders(const HeaderCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_headerCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when a chunk of data comes in
|
||||
*
|
||||
* Note that this function may be called zero, one or multiple times
|
||||
* for each incoming message depending on the size of the message data.
|
||||
*
|
||||
* If you install this callback you very likely also want to install
|
||||
* the onComplete callback so you know when the last data part was
|
||||
* received.
|
||||
*
|
||||
* @param callback The callback to invoke for chunks of message data
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onData(const DataCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_dataCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when a message arrives
|
||||
* This fuction is also available as onReceived() and onMessage() because I always forget which name I gave to it
|
||||
|
|
@ -195,13 +146,95 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when size information is known
|
||||
* Register a function to be called when queue size information is known
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredGet &onCount(const SizeCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_countCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when a new message is expected
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onBegin(const StartCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_startCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when a new message is expected
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onStart(const StartCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_startCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function that is called when the message size is known
|
||||
*
|
||||
* @param callback The callback to invoke for message headers
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onSize(const SizeCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_sizeCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when message headers come in
|
||||
*
|
||||
* @param callback The callback to invoke for message headers
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onHeaders(const HeaderCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_headerCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when a chunk of data comes in
|
||||
*
|
||||
* Note that this function may be called zero, one or multiple times
|
||||
* for each incoming message depending on the size of the message data.
|
||||
*
|
||||
* If you install this callback you very likely also want to install
|
||||
* the onComplete callback so you know when the last data part was
|
||||
* received.
|
||||
*
|
||||
* @param callback The callback to invoke for chunks of message data
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onData(const DataCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_dataCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
|
|
@ -213,10 +246,25 @@ public:
|
|||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onComplete(const CompleteCallback &callback)
|
||||
DeferredGet &onComplete(const DeliveredCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_completeCallback = callback;
|
||||
_deliveredCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a funtion to be called when a message was completely received
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredGet &onDelivered(const DeliveredCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_deliveredCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
/**
|
||||
* DeferredPublisher.h
|
||||
*
|
||||
* Class that is returned when channel::publish() is called, and that
|
||||
* can be used to install callback methods that define how returned
|
||||
* messages should be handled.
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include guard
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Begin of namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Forward declarations
|
||||
*/
|
||||
class ChannelImpl;
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
class DeferredPublisher : public DeferredReceiver, public std::enable_shared_from_this<DeferredPublisher>
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* The error code
|
||||
* @var int16_t
|
||||
*/
|
||||
int16_t _code = 0;
|
||||
|
||||
/**
|
||||
* The error message
|
||||
* @var std::string
|
||||
*/
|
||||
std::string _description;
|
||||
|
||||
/**
|
||||
* Callback that is called when a message is returned
|
||||
* @var BounceCallback
|
||||
*/
|
||||
BounceCallback _bounceCallback;
|
||||
|
||||
/**
|
||||
* Begin of a bounced message
|
||||
* @var ReturnCallback
|
||||
*/
|
||||
ReturnCallback _beginCallback;
|
||||
|
||||
/**
|
||||
* End of a bounced message
|
||||
* @var ReturnedCallback
|
||||
*/
|
||||
ReturnedCallback _completeCallback;
|
||||
|
||||
/**
|
||||
* Process a return frame
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void process(BasicReturnFrame &frame);
|
||||
|
||||
/**
|
||||
* Get reference to self to prevent that object falls out of scope
|
||||
* @return std::shared_ptr
|
||||
*/
|
||||
virtual std::shared_ptr<DeferredReceiver> lock() override { return shared_from_this(); }
|
||||
|
||||
/**
|
||||
* Extended implementation of the complete method that is called when a message was fully received
|
||||
*/
|
||||
virtual void complete() override;
|
||||
|
||||
/**
|
||||
* Classes that can access private members
|
||||
*/
|
||||
friend class BasicReturnFrame;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor that should only be called from within the channel implementation
|
||||
*
|
||||
* Note: this constructor _should_ be protected, but because make_shared
|
||||
* will then not work, we have decided to make it public after all,
|
||||
* because the work-around would result in not-so-easy-to-read code.
|
||||
*
|
||||
* @param channel the channel implementation
|
||||
* @param failed are we already failed?
|
||||
*/
|
||||
DeferredPublisher(ChannelImpl *channel, bool failed = false) :
|
||||
DeferredReceiver(failed, channel) {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Register a function to be called when a full message is returned
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredPublisher &onReceived(const BounceCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_bounceCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for onReceived() (see above)
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredPublisher &onMessage(const BounceCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_bounceCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for onReceived() (see above)
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredPublisher &onReturned(const BounceCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_bounceCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for onReceived() (see above)
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
DeferredPublisher &onBounced(const BounceCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_bounceCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function that is called when the start frame of a new
|
||||
* consumed message is received
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredPublisher &onBegin(const ReturnCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_beginCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function that is called when the message size is known
|
||||
*
|
||||
* @param callback The callback to invoke for message headers
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredPublisher &onSize(const SizeCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_sizeCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function that is called when message headers come in
|
||||
*
|
||||
* @param callback The callback to invoke for message headers
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredPublisher &onHeaders(const HeaderCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_headerCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the function to be called when a chunk of data comes in
|
||||
*
|
||||
* Note that this function may be called zero, one or multiple times
|
||||
* for each incoming message depending on the size of the message data.
|
||||
*
|
||||
* If you install this callback you very likely also want to install
|
||||
* the onComplete callback so you know when the last data part was
|
||||
* received.
|
||||
*
|
||||
* @param callback The callback to invoke for chunks of message data
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredPublisher &onData(const DataCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_dataCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a funtion to be called when a message was completely received
|
||||
*
|
||||
* @param callback The callback to invoke
|
||||
* @return Same object for chaining
|
||||
*/
|
||||
DeferredPublisher &onComplete(const ReturnedCallback &callback)
|
||||
{
|
||||
// store callback
|
||||
_completeCallback = callback;
|
||||
|
||||
// allow chaining
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* End of namespace
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
/**
|
||||
* deferredconsumerbase.h
|
||||
* DeferredReceiver.h
|
||||
*
|
||||
* Base class for the deferred consumer and the
|
||||
* deferred get.
|
||||
* Base class for the deferred consumer, the deferred get and the
|
||||
* deferred publisher (that may receive returned messages)
|
||||
*
|
||||
* @copyright 2016 - 2017 Copernica B.V.
|
||||
* @copyright 2016 - 2018 Copernica B.V.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -35,9 +35,7 @@ class BodyFrame;
|
|||
/**
|
||||
* Base class for deferred consumers
|
||||
*/
|
||||
class DeferredConsumerBase :
|
||||
public Deferred,
|
||||
public std::enable_shared_from_this<DeferredConsumerBase>
|
||||
class DeferredReceiver : public Deferred
|
||||
{
|
||||
private:
|
||||
/**
|
||||
|
|
@ -46,20 +44,27 @@ private:
|
|||
*/
|
||||
uint64_t _bodySize = 0;
|
||||
|
||||
/**
|
||||
* Process a delivery frame
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void process(BasicDeliverFrame &frame);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Process a delivery frame from a get request
|
||||
*
|
||||
* @param frame The frame to process
|
||||
* Initialize the object to send out a message
|
||||
* @param exchange the exchange to which the message was published
|
||||
* @param routingkey the routing key that was used to publish the message
|
||||
*/
|
||||
void process(BasicGetOKFrame &frame);
|
||||
virtual void initialize(const std::string &exchange, const std::string &routingkey);
|
||||
|
||||
/**
|
||||
* Get reference to self to prevent that object falls out of scope
|
||||
* @return std::shared_ptr
|
||||
*/
|
||||
virtual std::shared_ptr<DeferredReceiver> lock() = 0;
|
||||
|
||||
/**
|
||||
* Indicate that a message was done
|
||||
*/
|
||||
virtual void complete() = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Process the message headers
|
||||
*
|
||||
|
|
@ -74,40 +79,15 @@ private:
|
|||
*/
|
||||
void process(BodyFrame &frame);
|
||||
|
||||
/**
|
||||
* Indicate that a message was done
|
||||
*/
|
||||
void complete();
|
||||
|
||||
/**
|
||||
* Announce that a message has been received
|
||||
* @param message The message to announce
|
||||
* @param deliveryTag The delivery tag (for ack()ing)
|
||||
* @param redelivered Is this a redelivered message
|
||||
*/
|
||||
virtual void announce(const Message &message, uint64_t deliveryTag, bool redelivered) const = 0;
|
||||
|
||||
/**
|
||||
* Frames may be processed
|
||||
*/
|
||||
friend class ChannelImpl;
|
||||
friend class BasicDeliverFrame;
|
||||
friend class BasicGetOKFrame;
|
||||
friend class BasicHeaderFrame;
|
||||
friend class BodyFrame;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The delivery tag for the current message
|
||||
* @var uint64_t
|
||||
*/
|
||||
uint64_t _deliveryTag = 0;
|
||||
|
||||
/**
|
||||
* Is this a redelivered message
|
||||
* @var bool
|
||||
*/
|
||||
bool _redelivered = false;
|
||||
|
||||
/**
|
||||
* The channel to which the consumer is linked
|
||||
* @var ChannelImpl
|
||||
|
|
@ -116,9 +96,15 @@ protected:
|
|||
|
||||
/**
|
||||
* Callback for new message
|
||||
* @var BeginCallback
|
||||
* @var StartCallback
|
||||
*/
|
||||
BeginCallback _beginCallback;
|
||||
StartCallback _startCallback;
|
||||
|
||||
/**
|
||||
* Callback that is called when size of the message is known
|
||||
* @var SizeCallback
|
||||
*/
|
||||
SizeCallback _sizeCallback;
|
||||
|
||||
/**
|
||||
* Callback for incoming headers
|
||||
|
|
@ -132,18 +118,6 @@ protected:
|
|||
*/
|
||||
DataCallback _dataCallback;
|
||||
|
||||
/**
|
||||
* Callback for incoming messages
|
||||
* @var MessageCallback
|
||||
*/
|
||||
MessageCallback _messageCallback;
|
||||
|
||||
/**
|
||||
* Callback for when a message was complete finished
|
||||
* @var CompleteCallback
|
||||
*/
|
||||
CompleteCallback _completeCallback;
|
||||
|
||||
/**
|
||||
* The message that we are currently receiving
|
||||
* @var stack_ptr<Message>
|
||||
|
|
@ -152,12 +126,17 @@ protected:
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param failed Have we already failed?
|
||||
* @param channel The channel we are consuming on
|
||||
*/
|
||||
DeferredConsumerBase(bool failed, ChannelImpl *channel) : Deferred(failed), _channel(channel) {}
|
||||
DeferredReceiver(bool failed, ChannelImpl *channel) :
|
||||
Deferred(failed), _channel(channel) {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~DeferredReceiver() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
#include <winsock2.h>
|
||||
#pragma comment(lib,"Ws2_32.lib")
|
||||
//# include <sys/param.h>
|
||||
//# include <sys/param.h>
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
#define be32toh(x) ntohl(x)
|
||||
#define le32toh(x) (x)
|
||||
|
||||
#define htobe64(x) htonll(x)
|
||||
#define htobe64(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
|
||||
#define htole64(x) (x)
|
||||
#define be64toh(x) ntohll(x)
|
||||
#define le64toh(x) (x)
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* The various flags that are supported
|
||||
*
|
||||
* @copyright 2014 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -38,6 +38,7 @@ extern const int multiple;
|
|||
extern const int requeue;
|
||||
extern const int readable;
|
||||
extern const int writable;
|
||||
extern const int internal;
|
||||
|
||||
/**
|
||||
* End of namespace
|
||||
|
|
@ -0,0 +1,602 @@
|
|||
/**
|
||||
* LibBoostAsio.h
|
||||
*
|
||||
* Implementation for the AMQP::TcpHandler for boost::asio. You can use this class
|
||||
* instead of a AMQP::TcpHandler class, just pass the boost asio service to the
|
||||
* constructor and you're all set. See tests/libboostasio.cpp for example.
|
||||
*
|
||||
* Watch out: this class was not implemented or reviewed by the original author of
|
||||
* AMQP-CPP. However, we do get a lot of questions and issues from users of this class,
|
||||
* so we cannot guarantee its quality. If you run into such issues too, it might be
|
||||
* better to implement your own handler that interact with boost.
|
||||
*
|
||||
*
|
||||
* @author Gavin Smith <gavin.smith@coralbay.tv>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Include guard
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <memory>
|
||||
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
#include <boost/asio/posix/stream_descriptor.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "amqpcpp/linux_tcp.h"
|
||||
|
||||
// C++17 has 'weak_from_this()' support.
|
||||
#if __cplusplus >= 201701L
|
||||
#define PTR_FROM_THIS(T) weak_from_this()
|
||||
#else
|
||||
#define PTR_FROM_THIS(T) std::weak_ptr<T>(shared_from_this())
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
* @note Because of a limitation on Windows, this will only work on POSIX based systems - see https://github.com/chriskohlhoff/asio/issues/70
|
||||
*/
|
||||
class LibBoostAsioHandler : public virtual TcpHandler
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Helper class that wraps a boost io_service socket monitor.
|
||||
*/
|
||||
class Watcher : public virtual std::enable_shared_from_this<Watcher>
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* The boost asio io_service which is responsible for detecting events.
|
||||
* @var class boost::asio::io_service&
|
||||
*/
|
||||
boost::asio::io_service & _ioservice;
|
||||
|
||||
using strand_weak_ptr = std::weak_ptr<boost::asio::io_service::strand>;
|
||||
|
||||
/**
|
||||
* The boost asio io_service::strand managed pointer.
|
||||
* @var class std::shared_ptr<boost::asio::io_service>
|
||||
*/
|
||||
strand_weak_ptr _wpstrand;
|
||||
|
||||
/**
|
||||
* The boost tcp socket.
|
||||
* @var class boost::asio::ip::tcp::socket
|
||||
* @note https://stackoverflow.com/questions/38906711/destroying-boost-asio-socket-without-closing-native-handler
|
||||
*/
|
||||
boost::asio::posix::stream_descriptor _socket;
|
||||
|
||||
/**
|
||||
* A boolean that indicates if the watcher is monitoring for read events.
|
||||
* @var _read True if reads are being monitored else false.
|
||||
*/
|
||||
bool _read{false};
|
||||
|
||||
/**
|
||||
* A boolean that indicates if the watcher has a pending read event.
|
||||
* @var _read True if read is pending else false.
|
||||
*/
|
||||
bool _read_pending{false};
|
||||
|
||||
/**
|
||||
* A boolean that indicates if the watcher is monitoring for write events.
|
||||
* @var _read True if writes are being monitored else false.
|
||||
*/
|
||||
bool _write{false};
|
||||
|
||||
/**
|
||||
* A boolean that indicates if the watcher has a pending write event.
|
||||
* @var _read True if read is pending else false.
|
||||
*/
|
||||
bool _write_pending{false};
|
||||
|
||||
using handler_cb = boost::function<void(boost::system::error_code,std::size_t)>;
|
||||
using io_handler = boost::function<void(const boost::system::error_code&, const std::size_t)>;
|
||||
|
||||
/**
|
||||
* Builds a io handler callback that executes the io callback in a strand.
|
||||
* @param io_handler The handler callback to dispatch
|
||||
* @return handler_cb A function wrapping the execution of the handler function in a io_service::strand.
|
||||
*/
|
||||
handler_cb get_dispatch_wrapper(io_handler fn)
|
||||
{
|
||||
const strand_weak_ptr wpstrand = _wpstrand;
|
||||
|
||||
return [fn, wpstrand](const boost::system::error_code &ec, const std::size_t bytes_transferred)
|
||||
{
|
||||
const strand_shared_ptr strand = wpstrand.lock();
|
||||
if (!strand)
|
||||
{
|
||||
fn(boost::system::errc::make_error_code(boost::system::errc::operation_canceled), std::size_t{0});
|
||||
return;
|
||||
}
|
||||
strand->dispatch(boost::bind(fn, ec, bytes_transferred));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds and returns a read handler for the io operation.
|
||||
* @param connection The connection being watched.
|
||||
* @param fd The file descripter being watched.
|
||||
* @return handler callback
|
||||
*/
|
||||
handler_cb get_read_handler(TcpConnection *const connection, const int fd)
|
||||
{
|
||||
auto fn = boost::bind(&Watcher::read_handler,
|
||||
this,
|
||||
_1,
|
||||
_2,
|
||||
PTR_FROM_THIS(Watcher),
|
||||
connection,
|
||||
fd);
|
||||
return get_dispatch_wrapper(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds and returns a read handler for the io operation.
|
||||
* @param connection The connection being watched.
|
||||
* @param fd The file descripter being watched.
|
||||
* @return handler callback
|
||||
*/
|
||||
handler_cb get_write_handler(TcpConnection *const connection, const int fd)
|
||||
{
|
||||
auto fn = boost::bind(&Watcher::write_handler,
|
||||
this,
|
||||
_1,
|
||||
_2,
|
||||
PTR_FROM_THIS(Watcher),
|
||||
connection,
|
||||
fd);
|
||||
return get_dispatch_wrapper(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler method that is called by boost's io_service when the socket pumps a read event.
|
||||
* @param ec The status of the callback.
|
||||
* @param bytes_transferred The number of bytes transferred.
|
||||
* @param awpWatcher A weak pointer to this object.
|
||||
* @param connection The connection being watched.
|
||||
* @param fd The file descriptor being watched.
|
||||
* @note The handler will get called if a read is cancelled.
|
||||
*/
|
||||
void read_handler(const boost::system::error_code &ec,
|
||||
const std::size_t bytes_transferred,
|
||||
const std::weak_ptr<Watcher> awpWatcher,
|
||||
TcpConnection *const connection,
|
||||
const int fd)
|
||||
{
|
||||
// Resolve any potential problems with dangling pointers
|
||||
// (remember we are using async).
|
||||
const std::shared_ptr<Watcher> apWatcher = awpWatcher.lock();
|
||||
if (!apWatcher) { return; }
|
||||
|
||||
_read_pending = false;
|
||||
|
||||
if ((!ec || ec == boost::asio::error::would_block) && _read)
|
||||
{
|
||||
connection->process(fd, AMQP::readable);
|
||||
|
||||
_read_pending = true;
|
||||
|
||||
_socket.async_read_some(
|
||||
boost::asio::null_buffers(),
|
||||
get_read_handler(connection, fd));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler method that is called by boost's io_service when the socket pumps a write event.
|
||||
* @param ec The status of the callback.
|
||||
* @param bytes_transferred The number of bytes transferred.
|
||||
* @param awpWatcher A weak pointer to this object.
|
||||
* @param connection The connection being watched.
|
||||
* @param fd The file descriptor being watched.
|
||||
* @note The handler will get called if a write is cancelled.
|
||||
*/
|
||||
void write_handler(const boost::system::error_code ec,
|
||||
const std::size_t bytes_transferred,
|
||||
const std::weak_ptr<Watcher> awpWatcher,
|
||||
TcpConnection *const connection,
|
||||
const int fd)
|
||||
{
|
||||
// Resolve any potential problems with dangling pointers
|
||||
// (remember we are using async).
|
||||
const std::shared_ptr<Watcher> apWatcher = awpWatcher.lock();
|
||||
if (!apWatcher) { return; }
|
||||
|
||||
_write_pending = false;
|
||||
|
||||
if ((!ec || ec == boost::asio::error::would_block) && _write)
|
||||
{
|
||||
connection->process(fd, AMQP::writable);
|
||||
|
||||
_write_pending = true;
|
||||
|
||||
_socket.async_write_some(
|
||||
boost::asio::null_buffers(),
|
||||
get_write_handler(connection, fd));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor- initialises the watcher and assigns the filedescriptor to
|
||||
* a boost socket for monitoring.
|
||||
* @param io_service The boost io_service
|
||||
* @param wpstrand A weak pointer to a io_service::strand instance.
|
||||
* @param fd The filedescriptor being watched
|
||||
*/
|
||||
Watcher(boost::asio::io_service &io_service,
|
||||
const strand_weak_ptr wpstrand,
|
||||
const int fd) :
|
||||
_ioservice(io_service),
|
||||
_wpstrand(wpstrand),
|
||||
_socket(_ioservice)
|
||||
{
|
||||
_socket.assign(fd);
|
||||
|
||||
_socket.non_blocking(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watchers cannot be copied or moved
|
||||
*
|
||||
* @param that The object to not move or copy
|
||||
*/
|
||||
Watcher(Watcher &&that) = delete;
|
||||
Watcher(const Watcher &that) = delete;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Watcher()
|
||||
{
|
||||
_read = false;
|
||||
_write = false;
|
||||
_socket.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the events for which the filedescriptor is monitored
|
||||
* @param events
|
||||
*/
|
||||
void events(TcpConnection *connection, int fd, int events)
|
||||
{
|
||||
// 1. Handle reads?
|
||||
_read = ((events & AMQP::readable) != 0);
|
||||
|
||||
// Read requsted but no read pending?
|
||||
if (_read && !_read_pending)
|
||||
{
|
||||
_read_pending = true;
|
||||
|
||||
_socket.async_read_some(
|
||||
boost::asio::null_buffers(),
|
||||
get_read_handler(connection, fd));
|
||||
}
|
||||
|
||||
// 2. Handle writes?
|
||||
_write = ((events & AMQP::writable) != 0);
|
||||
|
||||
// Write requested but no write pending?
|
||||
if (_write && !_write_pending)
|
||||
{
|
||||
_write_pending = true;
|
||||
|
||||
_socket.async_write_some(
|
||||
boost::asio::null_buffers(),
|
||||
get_write_handler(connection, fd));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Timer class to periodically fire a heartbeat
|
||||
*/
|
||||
class Timer : public std::enable_shared_from_this<Timer>
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* The boost asio io_service which is responsible for detecting events.
|
||||
* @var class boost::asio::io_service&
|
||||
*/
|
||||
boost::asio::io_service & _ioservice;
|
||||
|
||||
using strand_weak_ptr = std::weak_ptr<boost::asio::io_service::strand>;
|
||||
|
||||
/**
|
||||
* The boost asio io_service::strand managed pointer.
|
||||
* @var class std::shared_ptr<boost::asio::io_service>
|
||||
*/
|
||||
strand_weak_ptr _wpstrand;
|
||||
|
||||
/**
|
||||
* The boost asynchronous deadline timer.
|
||||
* @var class boost::asio::deadline_timer
|
||||
*/
|
||||
boost::asio::deadline_timer _timer;
|
||||
|
||||
using handler_fn = boost::function<void(boost::system::error_code)>;
|
||||
|
||||
/**
|
||||
* Binds and returns a lamba function handler for the io operation.
|
||||
* @param connection The connection being watched.
|
||||
* @param timeout The file descripter being watched.
|
||||
* @return handler callback
|
||||
*/
|
||||
handler_fn get_handler(TcpConnection *const connection, const uint16_t timeout)
|
||||
{
|
||||
const auto fn = boost::bind(&Timer::timeout,
|
||||
this,
|
||||
_1,
|
||||
PTR_FROM_THIS(Timer),
|
||||
connection,
|
||||
timeout);
|
||||
|
||||
const strand_weak_ptr wpstrand = _wpstrand;
|
||||
|
||||
return [fn, wpstrand](const boost::system::error_code &ec)
|
||||
{
|
||||
const strand_shared_ptr strand = wpstrand.lock();
|
||||
if (!strand)
|
||||
{
|
||||
fn(boost::system::errc::make_error_code(boost::system::errc::operation_canceled));
|
||||
return;
|
||||
}
|
||||
strand->dispatch(boost::bind(fn, ec));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method that is called by libev when the timer expires
|
||||
* @param ec error code returned from loop
|
||||
* @param loop The loop in which the event was triggered
|
||||
* @param connection
|
||||
* @param timeout
|
||||
*/
|
||||
void timeout(const boost::system::error_code &ec,
|
||||
std::weak_ptr<Timer> awpThis,
|
||||
TcpConnection *const connection,
|
||||
const uint16_t timeout)
|
||||
{
|
||||
// Resolve any potential problems with dangling pointers
|
||||
// (remember we are using async).
|
||||
const std::shared_ptr<Timer> apTimer = awpThis.lock();
|
||||
if (!apTimer) { return; }
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
if (connection)
|
||||
{
|
||||
// send the heartbeat
|
||||
connection->heartbeat();
|
||||
}
|
||||
|
||||
// Reschedule the timer for the future:
|
||||
_timer.expires_at(_timer.expires_at() + boost::posix_time::seconds(timeout));
|
||||
|
||||
// Posts the timer event
|
||||
_timer.async_wait(get_handler(connection, timeout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the timer
|
||||
*/
|
||||
void stop()
|
||||
{
|
||||
// do nothing if it was never set
|
||||
_timer.cancel();
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param io_service The boost asio io_service.
|
||||
* @param wpstrand A weak pointer to a io_service::strand instance.
|
||||
*/
|
||||
Timer(boost::asio::io_service &io_service,
|
||||
const strand_weak_ptr wpstrand) :
|
||||
_ioservice(io_service),
|
||||
_wpstrand(wpstrand),
|
||||
_timer(_ioservice)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Timers cannot be copied or moved
|
||||
*
|
||||
* @param that The object to not move or copy
|
||||
*/
|
||||
Timer(Timer &&that) = delete;
|
||||
Timer(const Timer &that) = delete;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Timer()
|
||||
{
|
||||
// stop the timer
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the expire time
|
||||
* @param connection
|
||||
* @param timeout
|
||||
*/
|
||||
void set(TcpConnection *connection, uint16_t timeout)
|
||||
{
|
||||
// stop timer in case it was already set
|
||||
stop();
|
||||
|
||||
// Reschedule the timer for the future:
|
||||
_timer.expires_from_now(boost::posix_time::seconds(timeout));
|
||||
|
||||
// Posts the timer event
|
||||
_timer.async_wait(get_handler(connection, timeout));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The boost asio io_service.
|
||||
* @var class boost::asio::io_service&
|
||||
*/
|
||||
boost::asio::io_service & _ioservice;
|
||||
|
||||
using strand_shared_ptr = std::shared_ptr<boost::asio::io_service::strand>;
|
||||
|
||||
/**
|
||||
* The boost asio io_service::strand managed pointer.
|
||||
* @var class std::shared_ptr<boost::asio::io_service>
|
||||
*/
|
||||
strand_shared_ptr _strand;
|
||||
|
||||
/**
|
||||
* All I/O watchers that are active, indexed by their filedescriptor
|
||||
* @var std::map<int,Watcher>
|
||||
*/
|
||||
std::map<int, std::shared_ptr<Watcher> > _watchers;
|
||||
|
||||
/**
|
||||
* The boost asio io_service::deadline_timer managed pointer.
|
||||
* @var class std::shared_ptr<Timer>
|
||||
*/
|
||||
std::shared_ptr<Timer> _timer;
|
||||
|
||||
/**
|
||||
* Method that is called by AMQP-CPP to register a filedescriptor for readability or writability
|
||||
* @param connection The TCP connection object that is reporting
|
||||
* @param fd The filedescriptor to be monitored
|
||||
* @param flags Should the object be monitored for readability or writability?
|
||||
*/
|
||||
void monitor(TcpConnection *const connection,
|
||||
const int fd,
|
||||
const int flags) override
|
||||
{
|
||||
// do we already have this filedescriptor
|
||||
auto iter = _watchers.find(fd);
|
||||
|
||||
// was it found?
|
||||
if (iter == _watchers.end())
|
||||
{
|
||||
// we did not yet have this watcher - but that is ok if no filedescriptor was registered
|
||||
if (flags == 0){ return; }
|
||||
|
||||
// construct a new pair (watcher/timer), and put it in the map
|
||||
const std::shared_ptr<Watcher> apWatcher =
|
||||
std::make_shared<Watcher>(_ioservice, _strand, fd);
|
||||
|
||||
_watchers[fd] = apWatcher;
|
||||
|
||||
// explicitly set the events to monitor
|
||||
apWatcher->events(connection, fd, flags);
|
||||
}
|
||||
else if (flags == 0)
|
||||
{
|
||||
// the watcher does already exist, but we no longer have to watch this watcher
|
||||
_watchers.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Change the events on which to act.
|
||||
iter->second->events(connection,fd,flags);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Method that is called when the heartbeat frequency is negotiated between the server and the client.
|
||||
* @param connection The connection that suggested a heartbeat interval
|
||||
* @param interval The suggested interval from the server
|
||||
* @return uint16_t The interval to use
|
||||
*/
|
||||
virtual uint16_t onNegotiate(TcpConnection *connection, uint16_t interval) override
|
||||
{
|
||||
// skip if no heartbeats are needed
|
||||
if (interval == 0) return 0;
|
||||
|
||||
// set the timer
|
||||
_timer->set(connection, interval);
|
||||
|
||||
// we agree with the interval
|
||||
return interval;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Handler cannot be default constructed.
|
||||
*
|
||||
* @param that The object to not move or copy
|
||||
*/
|
||||
LibBoostAsioHandler() = delete;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param io_service The boost io_service to wrap
|
||||
*/
|
||||
explicit LibBoostAsioHandler(boost::asio::io_service &io_service) :
|
||||
_ioservice(io_service),
|
||||
_strand(std::make_shared<boost::asio::io_service::strand>(_ioservice)),
|
||||
_timer(std::make_shared<Timer>(_ioservice,_strand))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler cannot be copied or moved
|
||||
*
|
||||
* @param that The object to not move or copy
|
||||
*/
|
||||
LibBoostAsioHandler(LibBoostAsioHandler &&that) = delete;
|
||||
LibBoostAsioHandler(const LibBoostAsioHandler &that) = delete;
|
||||
|
||||
/**
|
||||
* Returns a reference to the boost io_service object that is being used.
|
||||
* @return The boost io_service object.
|
||||
*/
|
||||
boost::asio::io_service &service()
|
||||
{
|
||||
return _ioservice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~LibBoostAsioHandler() override = default;
|
||||
|
||||
/**
|
||||
* Make sure to stop the heartbeat timer after the connection is closed.
|
||||
* Otherwise it will keep the service running forever.
|
||||
*/
|
||||
void onClosed(TcpConnection* connection) override
|
||||
{
|
||||
(void)connection;
|
||||
_timer.reset();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* End of namespace
|
||||
*/
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* Compile with: "g++ -std=c++11 libev.cpp -lamqpcpp -lev -lpthread"
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 Copernica BV
|
||||
* @copyright 2015 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -21,6 +21,8 @@
|
|||
*/
|
||||
#include <ev.h>
|
||||
|
||||
#include "amqpcpp/linux_tcp.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
|
|
@ -119,6 +121,111 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Timer class to periodically fire a heartbeat
|
||||
*/
|
||||
class Timer
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* The event loop to which it is attached
|
||||
* @var struct ev_loop
|
||||
*/
|
||||
struct ev_loop *_loop;
|
||||
|
||||
/**
|
||||
* The actual watcher structure
|
||||
* @var struct ev_io
|
||||
*/
|
||||
struct ev_timer _timer;
|
||||
|
||||
/**
|
||||
* Callback method that is called by libev when the timer expires
|
||||
* @param loop The loop in which the event was triggered
|
||||
* @param timer Internal timer object
|
||||
* @param revents The events that triggered this call
|
||||
*/
|
||||
static void callback(struct ev_loop *loop, struct ev_timer *timer, int revents)
|
||||
{
|
||||
// retrieve the connection
|
||||
TcpConnection *connection = static_cast<TcpConnection*>(timer->data);
|
||||
|
||||
// send the heartbeat
|
||||
connection->heartbeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the timer
|
||||
*/
|
||||
void stop()
|
||||
{
|
||||
// do nothing if it was never set
|
||||
if (_timer.data == nullptr) return;
|
||||
|
||||
// restore loop refcount
|
||||
ev_ref(_loop);
|
||||
|
||||
// stop the timer
|
||||
ev_timer_stop(_loop, &_timer);
|
||||
|
||||
// restore data nullptr to indicate that timer is not set
|
||||
_timer.data = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param loop The current event loop
|
||||
*/
|
||||
Timer(struct ev_loop *loop) : _loop(loop)
|
||||
{
|
||||
// there is no data yet
|
||||
_timer.data = nullptr;
|
||||
|
||||
// initialize the libev structure
|
||||
ev_timer_init(&_timer, callback, 60.0, 60.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watchers cannot be copied or moved
|
||||
*
|
||||
* @param that The object to not move or copy
|
||||
*/
|
||||
Timer(Watcher &&that) = delete;
|
||||
Timer(const Watcher &that) = delete;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Timer()
|
||||
{
|
||||
// stop the timer
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the expire time
|
||||
* @param connection
|
||||
* @param timeout
|
||||
*/
|
||||
void set(TcpConnection *connection, uint16_t timeout)
|
||||
{
|
||||
// stop timer in case it was already set
|
||||
stop();
|
||||
|
||||
// store the connection in the data "void*"
|
||||
_timer.data = connection;
|
||||
|
||||
// set the timer
|
||||
ev_timer_set(&_timer, timeout, timeout);
|
||||
|
||||
// and start it
|
||||
ev_timer_start(_loop, &_timer);
|
||||
|
||||
// the timer should not keep the event loop active
|
||||
ev_unref(_loop);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The event loop
|
||||
|
|
@ -131,6 +238,12 @@ private:
|
|||
* @var std::map<int,Watcher>
|
||||
*/
|
||||
std::map<int,std::unique_ptr<Watcher>> _watchers;
|
||||
|
||||
/**
|
||||
* A timer to periodically send out heartbeats
|
||||
* @var Timer
|
||||
*/
|
||||
Timer _timer;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -165,12 +278,31 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Method that is called when the heartbeat frequency is negotiated between the server and the client.
|
||||
* @param connection The connection that suggested a heartbeat interval
|
||||
* @param interval The suggested interval from the server
|
||||
* @return uint16_t The interval to use
|
||||
*/
|
||||
virtual uint16_t onNegotiate(TcpConnection *connection, uint16_t interval) override
|
||||
{
|
||||
// skip if no heartbeats are needed
|
||||
if (interval == 0) return 0;
|
||||
|
||||
// set the timer
|
||||
_timer.set(connection, interval);
|
||||
|
||||
// we agree with the interval
|
||||
return interval;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param loop The event loop to wrap
|
||||
*/
|
||||
LibEvHandler(struct ev_loop *loop) : _loop(loop) {}
|
||||
LibEvHandler(struct ev_loop *loop) : _loop(loop), _timer(loop) {}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
#include <event2/event.h>
|
||||
#include <amqpcpp/flags.h>
|
||||
#include <amqpcpp/linux_tcp.h>
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
|
|
@ -21,6 +21,8 @@
|
|||
*/
|
||||
#include <uv.h>
|
||||
|
||||
#include "amqpcpp/linux_tcp.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#include "linux_tcp/tcphandler.h"
|
||||
#include "linux_tcp/tcpconnection.h"
|
||||
#include "linux_tcp/tcpchannel.h"
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* Extended channel that can be constructed on top of a TCP connection
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 Copernica BV
|
||||
* @copyright 2015 - 2017 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -25,6 +25,10 @@ class TcpChannel : public Channel
|
|||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The passed in connection pointer must remain valid for the
|
||||
* lifetime of the channel.
|
||||
*
|
||||
* @param connection
|
||||
*/
|
||||
TcpChannel(TcpConnection *connection) :
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
* IO between the client application and the RabbitMQ server.
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 - 2016 Copernica BV
|
||||
* @copyright 2015 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -34,9 +34,7 @@ private:
|
|||
/**
|
||||
* The state of the TCP connection - this state objecs changes based on
|
||||
* the state of the connection (resolving, connected or closed)
|
||||
* a shared pointer is used because we use a forward declaration, which isn't
|
||||
* allowed in a unique pointer
|
||||
* @var std::shared_ptr<TcpState>
|
||||
* @var std::unique_ptr<TcpState>
|
||||
*/
|
||||
std::shared_ptr<TcpState> _state;
|
||||
|
||||
|
|
@ -100,6 +98,7 @@ private:
|
|||
/**
|
||||
* Classes that have access to private data
|
||||
*/
|
||||
friend class SslConnected;
|
||||
friend class TcpConnected;
|
||||
friend class TcpChannel;
|
||||
|
||||
|
|
@ -115,7 +114,13 @@ public:
|
|||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~TcpConnection() noexcept {}
|
||||
virtual ~TcpConnection() noexcept;
|
||||
|
||||
/**
|
||||
* The filedescriptor that is used for this connection
|
||||
* @return int
|
||||
*/
|
||||
int fileno() const;
|
||||
|
||||
/**
|
||||
* Process the TCP connection
|
||||
|
|
@ -150,7 +155,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* The max frame size
|
||||
* The max frame size. Useful if you set up a buffer to parse incoming data: it does not have to exceed this size.
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t maxFrame() const
|
||||
|
|
@ -159,7 +164,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* The number of bytes that can best be passed to the next call to the parse() method
|
||||
* The number of bytes that can best be passed to the next call to the parse() method.
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t expected() const
|
||||
|
|
@ -168,7 +173,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the amount of channels this connection has
|
||||
* Return the number of channels this connection has.
|
||||
* @return std::size_t
|
||||
*/
|
||||
std::size_t channels() const
|
||||
|
|
@ -176,6 +181,21 @@ public:
|
|||
// return the amount of channels this connection has
|
||||
return _connection.channels();
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of outgoing bytes queued on this connection.
|
||||
* @return std::size_t
|
||||
*/
|
||||
std::size_t queued() const;
|
||||
|
||||
/**
|
||||
* Send a heartbeat
|
||||
* @return bool
|
||||
*/
|
||||
bool heartbeat()
|
||||
{
|
||||
return _connection.heartbeat();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
* class.
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 Copernica BV
|
||||
* @copyright 2015 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -14,6 +14,11 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
|
|
@ -33,11 +38,35 @@ public:
|
|||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~TcpHandler() {}
|
||||
virtual ~TcpHandler() = default;
|
||||
|
||||
/**
|
||||
* Method that is called after a TCP connection has been set up and the initial
|
||||
* TLS handshake is finished too, but right before the AMQP login handshake is
|
||||
* going to take place and the first data is going to be sent over the connection.
|
||||
* This method allows you to inspect the TLS certificate and other connection
|
||||
* properties, and to break up the connection if you find it not secure enough.
|
||||
* The default implementation considers all connections to be secure, even if the
|
||||
* connection has a self-signed or even invalid certificate. To be more strict,
|
||||
* override this method, inspect the certificate and return false if you do not
|
||||
* want to use the connection. The passed in SSL pointer is a pointer to a SSL
|
||||
* structure from the openssl library. This method is only called for secure
|
||||
* connections (connection with an amqps:// address).
|
||||
* @param connection The connection for which TLS was just started
|
||||
* @param ssl Pointer to the SSL structure that can be inspected
|
||||
* @return bool True to proceed / accept the connection, false to break up
|
||||
*/
|
||||
virtual bool onSecured(TcpConnection *connection, const SSL *ssl)
|
||||
{
|
||||
// default implementation: do not inspect anything, just allow the connection
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that is called when the heartbeat frequency is negotiated
|
||||
* between the server and the client.
|
||||
* between the server and the client. Applications can override this method
|
||||
* if they want to use a different heartbeat interval (for example: return 0
|
||||
* to disable heartbeats)
|
||||
* @param connection The connection that suggested a heartbeat interval
|
||||
* @param interval The suggested interval from the server
|
||||
* @return uint16_t The interval to use
|
||||
|
|
@ -51,7 +80,9 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Method that is called when the TCP connection ends up in a connected state
|
||||
* Method that is called when the AMQP connection ends up in a connected state
|
||||
* This method is called after the TCP connection has been set up, the (optional)
|
||||
* secure TLS connection, and the AMQP login handshake has been completed.
|
||||
* @param connection The TCP connection
|
||||
*/
|
||||
virtual void onConnected(TcpConnection *connection) {}
|
||||
|
|
@ -96,8 +127,6 @@ public:
|
|||
* @param flags Should the object be monitored for readability or writability?
|
||||
*/
|
||||
virtual void monitor(TcpConnection *connection, int fd, int flags) = 0;
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* This class combines login, password and vhost
|
||||
*
|
||||
* @copyright 2014 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -65,7 +65,25 @@ public:
|
|||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Login() {}
|
||||
virtual ~Login() = default;
|
||||
|
||||
/**
|
||||
* Cast to boolean: is the login set?
|
||||
* @return bool
|
||||
*/
|
||||
operator bool () const
|
||||
{
|
||||
return !_user.empty() || !_password.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate operator: is it not set
|
||||
* @return bool
|
||||
*/
|
||||
bool operator! () const
|
||||
{
|
||||
return _user.empty() && _password.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the user name
|
||||
|
|
@ -119,6 +137,32 @@ public:
|
|||
// the opposite of operator==
|
||||
return !operator==(that);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison operator
|
||||
* @param that
|
||||
* @return bool
|
||||
*/
|
||||
bool operator<(const Login &that) const
|
||||
{
|
||||
// compare users
|
||||
if (_user != that._user) return _user < that._user;
|
||||
|
||||
// compare passwords
|
||||
return _password < that._password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Friend function to allow writing the login to a stream
|
||||
* @param stream
|
||||
* @param login
|
||||
* @return std::ostream
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Login &login)
|
||||
{
|
||||
// write username and password
|
||||
return stream << login._user << ":" << login._password;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
* Message objects can not be constructed by end users, they are only constructed
|
||||
* by the AMQP library, and passed to user callbacks.
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
#include "envelope.h"
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
|
|
@ -30,7 +31,7 @@ namespace AMQP {
|
|||
/**
|
||||
* Forward declarations
|
||||
*/
|
||||
class DeferredConsumerBase;
|
||||
class DeferredReceiver;
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
|
|
@ -60,7 +61,7 @@ protected:
|
|||
/**
|
||||
* We are an open book to the consumer handler
|
||||
*/
|
||||
friend class DeferredConsumerBase;
|
||||
friend class DeferredReceiver;
|
||||
|
||||
/**
|
||||
* Set the body size
|
||||
|
|
@ -93,10 +94,10 @@ protected:
|
|||
size = std::min(size, _bodySize - _filled);
|
||||
|
||||
// append more data
|
||||
memcpy(_body + _filled, buffer, size);
|
||||
memcpy(_body + _filled, buffer, (size_t)size);
|
||||
|
||||
// update filled data
|
||||
_filled += size;
|
||||
_filled += (size_t)size;
|
||||
}
|
||||
else if (size >= _bodySize)
|
||||
{
|
||||
|
|
@ -107,16 +108,16 @@ protected:
|
|||
else
|
||||
{
|
||||
// allocate the buffer
|
||||
_body = (char *)malloc(_bodySize);
|
||||
_body = (char *)malloc((size_t)_bodySize);
|
||||
|
||||
// remember that the buffer was allocated, so that the destructor can get rid of it
|
||||
_allocated = true;
|
||||
|
||||
// append more data
|
||||
memcpy(_body, buffer, std::min(size, _bodySize));
|
||||
memcpy(_body, buffer, std::min((size_t)size, (size_t)_bodySize));
|
||||
|
||||
// update filled data
|
||||
_filled = std::min(size, _bodySize);
|
||||
_filled = std::min((size_t)size, (size_t)_bodySize);
|
||||
}
|
||||
|
||||
// check if we're done
|
||||
|
|
@ -302,20 +302,20 @@ public:
|
|||
// the result (2 for the two boolean sets)
|
||||
uint32_t result = 2;
|
||||
|
||||
if (hasExpiration()) result += _expiration.size();
|
||||
if (hasReplyTo()) result += _replyTo.size();
|
||||
if (hasCorrelationID()) result += _correlationID.size();
|
||||
if (hasPriority()) result += _priority.size();
|
||||
if (hasDeliveryMode()) result += _deliveryMode.size();
|
||||
if (hasHeaders()) result += _headers.size();
|
||||
if (hasContentEncoding()) result += _contentEncoding.size();
|
||||
if (hasContentType()) result += _contentType.size();
|
||||
if (hasClusterID()) result += _clusterID.size();
|
||||
if (hasAppID()) result += _appID.size();
|
||||
if (hasUserID()) result += _userID.size();
|
||||
if (hasTypeName()) result += _typeName.size();
|
||||
if (hasTimestamp()) result += _timestamp.size();
|
||||
if (hasMessageID()) result += _messageID.size();
|
||||
if (hasExpiration()) result += (uint32_t)_expiration.size();
|
||||
if (hasReplyTo()) result += (uint32_t)_replyTo.size();
|
||||
if (hasCorrelationID()) result += (uint32_t)_correlationID.size();
|
||||
if (hasPriority()) result += (uint32_t)_priority.size();
|
||||
if (hasDeliveryMode()) result += (uint32_t)_deliveryMode.size();
|
||||
if (hasHeaders()) result += (uint32_t)_headers.size();
|
||||
if (hasContentEncoding()) result += (uint32_t)_contentEncoding.size();
|
||||
if (hasContentType()) result += (uint32_t)_contentType.size();
|
||||
if (hasClusterID()) result += (uint32_t)_clusterID.size();
|
||||
if (hasAppID()) result += (uint32_t)_appID.size();
|
||||
if (hasUserID()) result += (uint32_t)_userID.size();
|
||||
if (hasTypeName()) result += (uint32_t)_typeName.size();
|
||||
if (hasTimestamp()) result += (uint32_t)_timestamp.size();
|
||||
if (hasMessageID()) result += (uint32_t)_messageID.size();
|
||||
|
||||
// done
|
||||
return result;
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* 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
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -76,6 +76,24 @@ public:
|
|||
if (_watchable) _watchable->remove(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast to boolean: is object in valid state?
|
||||
* @return bool
|
||||
*/
|
||||
operator bool () const
|
||||
{
|
||||
return _watchable != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate operator: is the object in an invalid state?
|
||||
* @return bool
|
||||
*/
|
||||
bool operator! () const
|
||||
{
|
||||
return _watchable == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the object is valid
|
||||
* @return bool
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Numeric field types for AMQP
|
||||
*
|
||||
*
|
||||
* @copyright 2014 Copernica BV
|
||||
*/
|
||||
|
||||
|
|
@ -42,6 +42,8 @@ private:
|
|||
T _value;
|
||||
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
/**
|
||||
* Default constructor, assign 0
|
||||
*/
|
||||
|
|
@ -116,14 +118,14 @@ public:
|
|||
* Get the value
|
||||
* @return mixed
|
||||
*/
|
||||
operator uint8_t () const override { return _value; }
|
||||
operator uint16_t() const override { return _value; }
|
||||
operator uint32_t() const override { return _value; }
|
||||
operator uint64_t() const override { return _value; }
|
||||
operator int8_t () const override { return _value; }
|
||||
operator int16_t () const override { return _value; }
|
||||
operator int32_t () const override { return _value; }
|
||||
operator int64_t () const override { return _value; }
|
||||
operator uint8_t () const override { return (uint8_t)_value; }
|
||||
operator uint16_t() const override { return (uint16_t)_value; }
|
||||
operator uint32_t() const override { return (uint32_t)_value; }
|
||||
operator uint64_t() const override { return (uint64_t)_value; }
|
||||
operator int8_t () const override { return (int8_t)_value; }
|
||||
operator int16_t () const override { return (int16_t)_value; }
|
||||
operator int32_t () const override { return (int32_t)_value; }
|
||||
operator int64_t () const override { return (int64_t)_value; }
|
||||
|
||||
/**
|
||||
* Get the value
|
||||
|
|
@ -213,4 +215,3 @@ typedef NumericField<double, 'd'> Double;
|
|||
* end namespace
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* OpenSSL.h
|
||||
*
|
||||
* Function to set openssl features
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include guard
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Begin of namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* To make secure "amqps://" connections, AMQP-CPP relies on functions from the
|
||||
* openssl library. It your application is dynamically linked to openssl (because
|
||||
* it was compiled with the "-lssl" flag), this works flawlessly because AMQPCPP
|
||||
* can then locate the openssl symbols in its own project space. However, if the
|
||||
* openssl library was not linked, you either cannot use amqps:// connections,
|
||||
* or you have to supply a handle to the openssl library yourself, using the
|
||||
* following method.
|
||||
*
|
||||
* @param handle Handle returned by dlopen() that has access to openssl
|
||||
*/
|
||||
void openssl(void *handle);
|
||||
|
||||
/**
|
||||
* End of namespace
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ public:
|
|||
virtual size_t size() const override
|
||||
{
|
||||
// find out size of the size parameter
|
||||
T size(_data.size());
|
||||
T size((typename T::Type)_data.size());
|
||||
|
||||
// size of the uint8 or uint32 + the actual string size
|
||||
return size.size() + _data.size();
|
||||
|
|
@ -160,7 +160,7 @@ public:
|
|||
virtual void fill(OutBuffer& buffer) const override
|
||||
{
|
||||
// create size
|
||||
T size(_data.size());
|
||||
T size((typename T::Type)_data.size());
|
||||
|
||||
// first, write down the size of the string
|
||||
size.fill(buffer);
|
||||
|
|
@ -210,4 +210,3 @@ typedef StringField<ULong, 'S'> LongString;
|
|||
* end namespace
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* Callbacks.h
|
||||
*
|
||||
* Class storing deferred callbacks of different type.
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Include guard
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Forward declarations
|
||||
*/
|
||||
class Message;
|
||||
class MetaData;
|
||||
|
||||
/**
|
||||
* All the callbacks that are supported
|
||||
*
|
||||
* When someone registers a callback function for certain events, it should
|
||||
* match one of the following signatures.
|
||||
*/
|
||||
using SuccessCallback = std::function<void()>;
|
||||
using ErrorCallback = std::function<void(const char *message)>;
|
||||
using FinalizeCallback = std::function<void()>;
|
||||
using EmptyCallback = std::function<void()>;
|
||||
using BeginCallback = std::function<void()>;
|
||||
using HeaderCallback = std::function<void(const MetaData &metaData)>;
|
||||
using DataCallback = std::function<void(const char *data, size_t size)>;
|
||||
using MessageCallback = std::function<void(const Message &message, uint64_t deliveryTag, bool redelivered)>;
|
||||
using CompleteCallback = std::function<void(uint64_t deliveryTag, bool redelivered)>;
|
||||
using QueueCallback = std::function<void(const std::string &name, uint32_t messagecount, uint32_t consumercount)>;
|
||||
using DeleteCallback = std::function<void(uint32_t deletedmessages)>;
|
||||
using SizeCallback = std::function<void(uint32_t messagecount)>;
|
||||
using ConsumeCallback = std::function<void(const std::string &consumer)>;
|
||||
using CancelCallback = std::function<void(const std::string &consumer)>;
|
||||
using AckCallback = std::function<void(uint64_t deliveryTag, bool multiple)>;
|
||||
using NackCallback = std::function<void(uint64_t deliveryTag, bool multiple, bool requeue)>;
|
||||
|
||||
/**
|
||||
* End namespace
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
# Version
|
||||
cmake_minimum_required(VERSION 2.6.3)
|
||||
|
||||
set(CXX_NORM_CXX98 1) # C++98
|
||||
set(CXX_NORM_CXX03 2) # C++03
|
||||
set(CXX_NORM_CXX11 3) # C++11
|
||||
|
||||
# - Set the wanted C++ norm
|
||||
# Adds the good argument to the command line in function of the compiler
|
||||
# Currently only works with g++ and clang++
|
||||
macro(set_cxx_norm NORM)
|
||||
|
||||
# Extract c++ compiler --version output
|
||||
exec_program(
|
||||
${CMAKE_CXX_COMPILER}
|
||||
ARGS --version
|
||||
OUTPUT_VARIABLE _compiler_output
|
||||
)
|
||||
# Keep only the first line
|
||||
string(REGEX REPLACE
|
||||
"(\n.*$)"
|
||||
""
|
||||
cxx_compiler_version "${_compiler_output}"
|
||||
)
|
||||
# Extract the version number
|
||||
string(REGEX REPLACE
|
||||
"([^0-9.])|([0-9.][^0-9.])"
|
||||
""
|
||||
cxx_compiler_version "${cxx_compiler_version}"
|
||||
)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
if(${NORM} EQUAL ${CXX_NORM_CXX98})
|
||||
add_definitions("-std=c++98")
|
||||
elseif(${NORM} EQUAL ${CXX_NORM_CXX03})
|
||||
add_definitions("-std=c++03")
|
||||
elseif(${NORM} EQUAL ${CXX_NORM_CXX11})
|
||||
if(${cxx_compiler_version} VERSION_LESS "4.7.0")
|
||||
add_definitions("-std=c++0x")
|
||||
else()
|
||||
add_definitions("-std=c++11")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
|
||||
if(${NORM} EQUAL ${CXX_NORM_CXX11})
|
||||
add_definitions("-std=c++11")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
endmacro()
|
||||
|
||||
|
|
@ -1,102 +1,95 @@
|
|||
add_sources(
|
||||
addressinfo.h
|
||||
array.cpp
|
||||
basicackframe.h
|
||||
basiccancelframe.h
|
||||
basiccancelokframe.h
|
||||
basicconsumeframe.h
|
||||
basicconsumeokframe.h
|
||||
basicdeliverframe.h
|
||||
basicframe.h
|
||||
basicgetemptyframe.h
|
||||
basicgetframe.h
|
||||
basicgetokframe.h
|
||||
basicheaderframe.h
|
||||
basicnackframe.h
|
||||
basicpublishframe.h
|
||||
basicqosframe.h
|
||||
basicqosokframe.h
|
||||
basicrecoverasyncframe.h
|
||||
basicrecoverframe.h
|
||||
basicrecoverokframe.h
|
||||
basicrejectframe.h
|
||||
basicreturnframe.h
|
||||
bodyframe.h
|
||||
channelcloseframe.h
|
||||
channelcloseokframe.h
|
||||
channelflowframe.h
|
||||
channelflowokframe.h
|
||||
channelframe.h
|
||||
channelimpl.cpp
|
||||
channelopenframe.h
|
||||
channelopenokframe.h
|
||||
confirmselectframe.h
|
||||
confirmselectokframe.h
|
||||
connectioncloseframe.h
|
||||
connectioncloseokframe.h
|
||||
connectionframe.h
|
||||
connectionimpl.cpp
|
||||
connectionopenframe.h
|
||||
connectionopenokframe.h
|
||||
connectionsecureframe.h
|
||||
connectionsecureokframe.h
|
||||
connectionstartframe.h
|
||||
connectionstartokframe.h
|
||||
connectiontuneframe.h
|
||||
connectiontuneokframe.h
|
||||
consumedmessage.h
|
||||
deferredcancel.cpp
|
||||
deferredconsumer.cpp
|
||||
deferredconsumerbase.cpp
|
||||
deferredget.cpp
|
||||
exchangebindframe.h
|
||||
exchangebindokframe.h
|
||||
exchangedeclareframe.h
|
||||
exchangedeclareokframe.h
|
||||
exchangedeleteframe.h
|
||||
exchangedeleteokframe.h
|
||||
exchangeframe.h
|
||||
exchangeunbindframe.h
|
||||
exchangeunbindokframe.h
|
||||
extframe.h
|
||||
field.cpp
|
||||
flags.cpp
|
||||
framecheck.h
|
||||
headerframe.h
|
||||
heartbeatframe.h
|
||||
includes.h
|
||||
methodframe.h
|
||||
passthroughbuffer.h
|
||||
pipe.h
|
||||
protocolheaderframe.h
|
||||
queuebindframe.h
|
||||
queuebindokframe.h
|
||||
queuedeclareframe.h
|
||||
queuedeclareokframe.h
|
||||
queuedeleteframe.h
|
||||
queuedeleteokframe.h
|
||||
queueframe.h
|
||||
queuepurgeframe.h
|
||||
queuepurgeokframe.h
|
||||
queueunbindframe.h
|
||||
queueunbindokframe.h
|
||||
receivedframe.cpp
|
||||
reducedbuffer.h
|
||||
returnedmessage.h
|
||||
table.cpp
|
||||
tcpclosed.h
|
||||
tcpconnected.h
|
||||
tcpconnection.cpp
|
||||
tcpinbuffer.h
|
||||
tcpoutbuffer.h
|
||||
tcpresolver.h
|
||||
tcpstate.h
|
||||
transactioncommitframe.h
|
||||
transactioncommitokframe.h
|
||||
transactionframe.h
|
||||
transactionrollbackframe.h
|
||||
transactionrollbackokframe.h
|
||||
transactionselectframe.h
|
||||
transactionselectokframe.h
|
||||
watchable.cpp
|
||||
array.cpp
|
||||
basicackframe.h
|
||||
basiccancelframe.h
|
||||
basiccancelokframe.h
|
||||
basicconsumeframe.h
|
||||
basicconsumeokframe.h
|
||||
basicdeliverframe.h
|
||||
basicframe.h
|
||||
basicgetemptyframe.h
|
||||
basicgetframe.h
|
||||
basicgetokframe.h
|
||||
basicheaderframe.h
|
||||
basicnackframe.h
|
||||
basicpublishframe.h
|
||||
basicqosframe.h
|
||||
basicqosokframe.h
|
||||
basicrecoverasyncframe.h
|
||||
basicrecoverframe.h
|
||||
basicrecoverokframe.h
|
||||
basicrejectframe.h
|
||||
basicreturnframe.h
|
||||
bodyframe.h
|
||||
channelcloseframe.h
|
||||
channelcloseokframe.h
|
||||
channelflowframe.h
|
||||
channelflowokframe.h
|
||||
channelframe.h
|
||||
channelimpl.cpp
|
||||
channelopenframe.h
|
||||
channelopenokframe.h
|
||||
confirmselectframe.h
|
||||
confirmselectokframe.h
|
||||
connectioncloseframe.h
|
||||
connectioncloseokframe.h
|
||||
connectionframe.h
|
||||
connectionimpl.cpp
|
||||
connectionopenframe.h
|
||||
connectionopenokframe.h
|
||||
connectionsecureframe.h
|
||||
connectionsecureokframe.h
|
||||
connectionstartframe.h
|
||||
connectionstartokframe.h
|
||||
connectiontuneframe.h
|
||||
connectiontuneokframe.h
|
||||
consumedmessage.h
|
||||
deferredcancel.cpp
|
||||
deferredconsumer.cpp
|
||||
deferredreceiver.cpp
|
||||
deferredextreceiver.cpp
|
||||
deferredpublisher.cpp
|
||||
deferredget.cpp
|
||||
exchangebindframe.h
|
||||
exchangebindokframe.h
|
||||
exchangedeclareframe.h
|
||||
exchangedeclareokframe.h
|
||||
exchangedeleteframe.h
|
||||
exchangedeleteokframe.h
|
||||
exchangeframe.h
|
||||
exchangeunbindframe.h
|
||||
exchangeunbindokframe.h
|
||||
extframe.h
|
||||
field.cpp
|
||||
flags.cpp
|
||||
framecheck.h
|
||||
headerframe.h
|
||||
heartbeatframe.h
|
||||
includes.h
|
||||
methodframe.h
|
||||
passthroughbuffer.h
|
||||
protocolheaderframe.h
|
||||
queuebindframe.h
|
||||
queuebindokframe.h
|
||||
queuedeclareframe.h
|
||||
queuedeclareokframe.h
|
||||
queuedeleteframe.h
|
||||
queuedeleteokframe.h
|
||||
queueframe.h
|
||||
queuepurgeframe.h
|
||||
queuepurgeokframe.h
|
||||
queueunbindframe.h
|
||||
queueunbindokframe.h
|
||||
receivedframe.cpp
|
||||
reducedbuffer.h
|
||||
returnedmessage.h
|
||||
table.cpp
|
||||
transactioncommitframe.h
|
||||
transactioncommitokframe.h
|
||||
transactionframe.h
|
||||
transactionrollbackframe.h
|
||||
transactionrollbackokframe.h
|
||||
transactionselectframe.h
|
||||
transactionselectokframe.h
|
||||
watchable.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
CPP = g++
|
||||
RM = rm -f
|
||||
CPPFLAGS = -Wall -c -I. -std=c++11 -MD
|
||||
CPPFLAGS = -Wall -c -I../include -std=c++11 -MD
|
||||
LD = g++
|
||||
LD_FLAGS = -Wall -shared
|
||||
SHARED_LIB = lib$(LIBRARY_NAME).so.$(VERSION)
|
||||
STATIC_LIB = lib$(LIBRARY_NAME).a.$(VERSION)
|
||||
SOURCES = $(wildcard *.cpp)
|
||||
SOURCES = $(wildcard *.cpp) $(wildcard linux_tcp/*.cpp)
|
||||
SHARED_OBJECTS = $(SOURCES:%.cpp=%.o)
|
||||
STATIC_OBJECTS = $(SOURCES:%.cpp=%.s.o)
|
||||
DEPENDENCIES = $(SOURCES:%.cpp=%.d)
|
||||
|
|
@ -53,4 +53,3 @@ ${SHARED_OBJECTS}:
|
|||
|
||||
${STATIC_OBJECTS}:
|
||||
${CPP} ${CPPFLAGS} -o $@ ${@:%.s.o=%.cpp}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Array::Array(ReceivedFrame &frame)
|
|||
if (!field) continue;
|
||||
|
||||
// less bytes to read
|
||||
charsToRead -= field->size();
|
||||
charsToRead -= (uint32_t)field->size();
|
||||
|
||||
// add the additional field
|
||||
_fields.push_back(std::shared_ptr<Field>(field));
|
||||
|
|
@ -76,7 +76,7 @@ const Field &Array::get(uint8_t index) const
|
|||
*/
|
||||
uint32_t Array::count() const
|
||||
{
|
||||
return _fields.size();
|
||||
return (uint32_t)_fields.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public:
|
|||
* @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
|
||||
BasicFrame(channel, (uint32_t)(consumerTag.size() + 2)), // 1 for extra string size, 1 for bool
|
||||
_consumerTag(consumerTag),
|
||||
_noWait(noWait) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public:
|
|||
* @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
|
||||
BasicFrame(channel, (uint32_t)(consumerTag.length() + 1)), // add 1 byte for encoding the size of consumer tag
|
||||
_consumerTag(consumerTag)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public:
|
|||
* @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
|
||||
BasicFrame(channel, (uint32_t)(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),
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
* @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
|
||||
BasicFrame(channel, (uint32_t)(consumerTag.length() + 1)), // length of string + 1 for encoding of stringsize
|
||||
_consumerTag(consumerTag)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Class describing a basic deliver frame
|
||||
*
|
||||
* @copyright 2014 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -13,9 +13,10 @@
|
|||
* Dependencies
|
||||
*/
|
||||
#include "basicframe.h"
|
||||
#include "../include/stringfield.h"
|
||||
#include "../include/booleanset.h"
|
||||
#include "../include/connectionimpl.h"
|
||||
#include "amqpcpp/stringfield.h"
|
||||
#include "amqpcpp/booleanset.h"
|
||||
#include "amqpcpp/connectionimpl.h"
|
||||
#include "amqpcpp/deferredconsumer.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
|
|
@ -87,7 +88,7 @@ public:
|
|||
* @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)),
|
||||
BasicFrame(channel, (uint32_t)(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),
|
||||
|
|
@ -193,8 +194,14 @@ public:
|
|||
// channel does not exist
|
||||
if (!channel) return false;
|
||||
|
||||
// construct the message
|
||||
channel->process(*this);
|
||||
// get the appropriate consumer object
|
||||
auto consumer = channel->consumer(_consumerTag);
|
||||
|
||||
// skip if there was no consumer for this tag
|
||||
if (consumer == nullptr) return false;
|
||||
|
||||
// initialize the object, because we're about to receive a message
|
||||
consumer->process(*this);
|
||||
|
||||
// done
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public:
|
|||
* @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
|
||||
BasicFrame(channel, (uint32_t)(queue.length() + 4)), // 1 for bool, 1 for string size, 2 for deprecated field
|
||||
_queue(queue),
|
||||
_noAck(noAck)
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Class describing a basic get ok frame
|
||||
*
|
||||
* @copyright 2014 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -76,7 +76,7 @@ public:
|
|||
* @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)
|
||||
BasicFrame(channel, (uint32_t)(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),
|
||||
|
|
@ -170,14 +170,17 @@ public:
|
|||
// channel does not exist
|
||||
if (!channel) return false;
|
||||
|
||||
// report success for the get operation
|
||||
channel->reportSuccess(messageCount(), deliveryTag(), redelivered());
|
||||
// report success for the get operation (this will also update the current receiver!)
|
||||
channel->reportSuccess(messageCount(), _deliveryTag, redelivered());
|
||||
|
||||
// check if we have a valid consumer
|
||||
if (!channel->consumer()) return false;
|
||||
// get the current receiver object
|
||||
auto *receiver = channel->receiver();
|
||||
|
||||
// pass on to consumer
|
||||
channel->consumer()->process(*this);
|
||||
// check if we have a valid receiver
|
||||
if (receiver == nullptr) return false;
|
||||
|
||||
// initialize the receiver for the upcoming message
|
||||
receiver->initialize(_exchange, _routingKey);
|
||||
|
||||
// done
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Class describing an AMQP basic header frame
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -13,10 +13,10 @@
|
|||
* Dependencies
|
||||
*/
|
||||
#include "headerframe.h"
|
||||
#include "../include/metadata.h"
|
||||
#include "../include/envelope.h"
|
||||
#include "../include/connectionimpl.h"
|
||||
#include "../include/deferredconsumerbase.h"
|
||||
#include "amqpcpp/metadata.h"
|
||||
#include "amqpcpp/envelope.h"
|
||||
#include "amqpcpp/connectionimpl.h"
|
||||
#include "amqpcpp/deferredreceiver.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
|
|
@ -134,12 +134,18 @@ public:
|
|||
{
|
||||
// we need the appropriate channel
|
||||
auto channel = connection->channel(this->channel());
|
||||
|
||||
// we need a channel
|
||||
if (channel == nullptr) return false;
|
||||
|
||||
// do we have an object that is receiving this data?
|
||||
auto *receiver = channel->receiver();
|
||||
|
||||
// check if we have a valid channel and consumer
|
||||
if (!channel || !channel->consumer()) return false;
|
||||
if (receiver == nullptr) return false;
|
||||
|
||||
// the channel can process the frame
|
||||
channel->consumer()->process(*this);
|
||||
receiver->process(*this);
|
||||
|
||||
// done
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public:
|
|||
* @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
|
||||
BasicFrame(channel, (uint32_t)(exchange.length() + routingKey.length() + 5)), // 1 extra per string (for the size), 1 for bools, 2 for deprecated field
|
||||
_exchange(exchange),
|
||||
_routingKey(routingKey),
|
||||
_bools(mandatory, immediate)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Class describing a basic return frame
|
||||
*
|
||||
* @copyright 2014 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -67,7 +67,7 @@ public:
|
|||
* @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
|
||||
BasicFrame(channel, (uint32_t)(replyText.length() + exchange.length() + routingKey.length() + 5)), // 3 for each string (extra size byte), 2 for uint16_t
|
||||
_replyCode(replyCode),
|
||||
_replyText(replyText),
|
||||
_exchange(exchange),
|
||||
|
|
@ -155,8 +155,23 @@ public:
|
|||
*/
|
||||
virtual bool process(ConnectionImpl *connection) override
|
||||
{
|
||||
// we no longer support returned messages
|
||||
return false;
|
||||
// we need the appropriate channel
|
||||
auto channel = connection->channel(this->channel());
|
||||
|
||||
// channel does not exist
|
||||
if (!channel) return false;
|
||||
|
||||
// get the current publisher
|
||||
auto publisher = channel->publisher();
|
||||
|
||||
// if there is no deferred publisher, we can just as well stop
|
||||
if (publisher == nullptr) return false;
|
||||
|
||||
// initialize the object, because we're about to receive a message
|
||||
publisher->process(*this);
|
||||
|
||||
// done
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Class describing an AMQP Body Frame
|
||||
*
|
||||
* @copyright 2014 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -13,8 +13,8 @@
|
|||
* Dependencies
|
||||
*/
|
||||
#include "extframe.h"
|
||||
#include "../include/connectionimpl.h"
|
||||
#include "../include/deferredconsumerbase.h"
|
||||
#include "amqpcpp/connectionimpl.h"
|
||||
#include "amqpcpp/deferredreceiver.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
|
|
@ -105,12 +105,18 @@ public:
|
|||
{
|
||||
// we need the appropriate channel
|
||||
auto channel = connection->channel(this->channel());
|
||||
|
||||
// we must have a channel object
|
||||
if (channel == nullptr) return false;
|
||||
|
||||
// get the object that is receiving the messages
|
||||
auto *receiver = channel->receiver();
|
||||
|
||||
// check if we have a valid channel and consumer
|
||||
if (!channel || !channel->consumer()) return false;
|
||||
// check if we have a valid receiver
|
||||
if (receiver == nullptr) return false;
|
||||
|
||||
// the consumer may process the frame
|
||||
channel->consumer()->process(*this);
|
||||
receiver->process(*this);
|
||||
|
||||
// done
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public:
|
|||
* @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
|
||||
ChannelFrame(channel, (uint32_t)(text.length() + 7)), // sizeof code, failingclass, failingmethod (2byte + 2byte + 2byte) + text length + text length byte
|
||||
_code(code),
|
||||
_text(text),
|
||||
_failingClass(failingClass),
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
*
|
||||
* Implementation for a channel
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
#include "includes.h"
|
||||
#include "basicdeliverframe.h"
|
||||
#include "basicgetokframe.h"
|
||||
#include "basicreturnframe.h"
|
||||
#include "consumedmessage.h"
|
||||
|
|
@ -40,7 +39,6 @@
|
|||
#include "basicrejectframe.h"
|
||||
#include "basicgetframe.h"
|
||||
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
|
|
@ -266,7 +264,7 @@ Deferred &ChannelImpl::close()
|
|||
Deferred &ChannelImpl::declareExchange(const std::string &name, ExchangeType type, int flags, const Table &arguments)
|
||||
{
|
||||
// convert exchange type
|
||||
std::string exchangeType;
|
||||
const char *exchangeType = "";
|
||||
|
||||
// convert the exchange type into a string
|
||||
if (type == ExchangeType::fanout) exchangeType = "fanout";
|
||||
|
|
@ -275,8 +273,15 @@ Deferred &ChannelImpl::declareExchange(const std::string &name, ExchangeType typ
|
|||
else if (type == ExchangeType::headers) exchangeType = "headers";
|
||||
else if (type == ExchangeType::consistent_hash) exchangeType = "x-consistent-hash";
|
||||
|
||||
// the boolean options
|
||||
bool passive = flags & AMQP::passive;
|
||||
bool durable = flags & AMQP::durable;
|
||||
bool autodelete = flags & AMQP::autodelete;
|
||||
bool internal = flags & AMQP::internal;
|
||||
bool nowait = flags & AMQP::nowait;
|
||||
|
||||
// send declare exchange frame
|
||||
return push(ExchangeDeclareFrame(_id, name, exchangeType, (flags & passive) != 0, (flags & durable) != 0, false, arguments));
|
||||
return push(ExchangeDeclareFrame(_id, name, exchangeType, passive, durable, autodelete, internal, nowait, arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -459,26 +464,31 @@ DeferredDelete &ChannelImpl::removeQueue(const std::string &name, int flags)
|
|||
* @param envelope the full envelope to send
|
||||
* @param message the message to send
|
||||
* @param size size of the message
|
||||
* @param flags
|
||||
* @return DeferredPublisher
|
||||
*/
|
||||
bool ChannelImpl::publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope)
|
||||
DeferredPublisher &ChannelImpl::publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope, int flags)
|
||||
{
|
||||
// we are going to send out multiple frames, each one will trigger a call to the handler,
|
||||
// which in turn could destruct the channel object, we need to monitor that
|
||||
Monitor monitor(this);
|
||||
|
||||
// @todo do not copy the entire buffer to individual frames
|
||||
|
||||
// make sure we have a deferred object to return
|
||||
if (!_publisher) _publisher.reset(new DeferredPublisher(this));
|
||||
|
||||
// send the publish frame
|
||||
if (!send(BasicPublishFrame(_id, exchange, routingKey))) return false;
|
||||
if (!send(BasicPublishFrame(_id, exchange, routingKey, (flags & mandatory) != 0, (flags & immediate) != 0))) return *_publisher;
|
||||
|
||||
// channel still valid?
|
||||
if (!monitor.valid()) return false;
|
||||
if (!monitor.valid()) return *_publisher;
|
||||
|
||||
// send header
|
||||
if (!send(BasicHeaderFrame(_id, envelope))) return false;
|
||||
if (!send(BasicHeaderFrame(_id, envelope))) return *_publisher;
|
||||
|
||||
// channel and connection still valid?
|
||||
if (!monitor.valid() || !_connection) return false;
|
||||
if (!monitor.valid() || !_connection) return *_publisher;
|
||||
|
||||
// the max payload size is the max frame size minus the bytes for headers and trailer
|
||||
uint32_t maxpayload = _connection->maxPayload();
|
||||
|
|
@ -495,10 +505,10 @@ bool ChannelImpl::publish(const std::string &exchange, const std::string &routin
|
|||
uint64_t chunksize = std::min(static_cast<uint64_t>(maxpayload), bytesleft);
|
||||
|
||||
// send out a body frame
|
||||
if (!send(BodyFrame(_id, data + bytessent, chunksize))) return false;
|
||||
if (!send(BodyFrame(_id, data + bytessent, (uint32_t)chunksize))) return *_publisher;
|
||||
|
||||
// channel still valid?
|
||||
if (!monitor.valid()) return false;
|
||||
if (!monitor.valid()) return *_publisher;
|
||||
|
||||
// update counters
|
||||
bytessent += chunksize;
|
||||
|
|
@ -509,7 +519,7 @@ bool ChannelImpl::publish(const std::string &exchange, const std::string &routin
|
|||
_messageCounter++;
|
||||
|
||||
// done
|
||||
return true;
|
||||
return *_publisher;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -703,7 +713,7 @@ bool ChannelImpl::send(const Frame &frame)
|
|||
// added to the list of deferred objects. it will be notified about
|
||||
// the error when the close operation succeeds
|
||||
if (_state == state_closing) return true;
|
||||
|
||||
|
||||
// are we currently in synchronous mode or are there
|
||||
// other frames waiting for their turn to be sent?
|
||||
if (_synchronous || !_queue.empty())
|
||||
|
|
@ -826,41 +836,17 @@ void ChannelImpl::reportError(const char *message, bool notifyhandler)
|
|||
}
|
||||
|
||||
/**
|
||||
* Process incoming delivery
|
||||
*
|
||||
* @param frame The frame to process
|
||||
* Get the current receiver for a given consumer tag
|
||||
* @param consumertag the consumer frame
|
||||
* @return DeferredConsumer
|
||||
*/
|
||||
void ChannelImpl::process(BasicDeliverFrame &frame)
|
||||
DeferredConsumer *ChannelImpl::consumer(const std::string &consumertag) const
|
||||
{
|
||||
// find the consumer for this frame
|
||||
auto iter = _consumers.find(frame.consumerTag());
|
||||
if (iter == _consumers.end()) return;
|
||||
|
||||
// we are going to be receiving a message, store
|
||||
// the handler for the incoming message
|
||||
_consumer = iter->second;
|
||||
|
||||
// let the consumer process the frame
|
||||
_consumer->process(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current consumer handler
|
||||
*
|
||||
* @return The handler responsible for the current message
|
||||
*/
|
||||
DeferredConsumerBase *ChannelImpl::consumer()
|
||||
{
|
||||
return _consumer.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the current consumer as done
|
||||
*/
|
||||
void ChannelImpl::complete()
|
||||
{
|
||||
// no more consumer
|
||||
_consumer.reset();
|
||||
// look in the map
|
||||
auto iter = _consumers.find(consumertag);
|
||||
|
||||
// return the result
|
||||
return iter == _consumers.end() ? nullptr : iter->second.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public:
|
|||
* @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
|
||||
ConnectionFrame((uint32_t)(text.length() + 7)), // 1 for extra string byte, 2 for each uint16
|
||||
_code(code),
|
||||
_text(text),
|
||||
_failingClass(failingClass),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Implementation of an AMQP connection
|
||||
*
|
||||
* @copyright 2014 - 2016 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
#include "includes.h"
|
||||
#include "protocolheaderframe.h"
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include "connectioncloseframe.h"
|
||||
#include "reducedbuffer.h"
|
||||
#include "passthroughbuffer.h"
|
||||
#include "heartbeatframe.h"
|
||||
|
||||
/**
|
||||
* set namespace
|
||||
|
|
@ -124,7 +125,7 @@ uint64_t ConnectionImpl::parse(const Buffer &buffer)
|
|||
try
|
||||
{
|
||||
// try to recognize the frame
|
||||
ReducedBuffer reduced_buf(buffer, processed);
|
||||
ReducedBuffer reduced_buf(buffer, (size_t)processed);
|
||||
ReceivedFrame receivedFrame(reduced_buf, _maxFrame);
|
||||
|
||||
// do we have the full frame?
|
||||
|
|
@ -145,8 +146,8 @@ uint64_t ConnectionImpl::parse(const Buffer &buffer)
|
|||
// have the initial bytes of the header, we already know how much
|
||||
// data we need for the next frame, otherwise we need at least 7
|
||||
// bytes for processing the header of the next frame
|
||||
_expected = receivedFrame.header() ? receivedFrame.totalSize() : 7;
|
||||
|
||||
_expected = receivedFrame.header() ? (uint32_t)receivedFrame.totalSize() : 7;
|
||||
|
||||
// we're ready for now
|
||||
return processed;
|
||||
}
|
||||
|
|
@ -267,6 +268,9 @@ void ConnectionImpl::setConnected()
|
|||
{
|
||||
// get the next message
|
||||
const auto &buffer = _queue.front();
|
||||
|
||||
// data is going to be sent, thus connection is not idle
|
||||
_idle = false;
|
||||
|
||||
// send it
|
||||
_handler->onData(_parent, buffer.data(), buffer.size());
|
||||
|
|
@ -335,6 +339,9 @@ bool ConnectionImpl::send(const Frame &frame)
|
|||
// are we still setting up the connection?
|
||||
if ((_state == state_connected && _queue.empty()) || frame.partOfHandshake())
|
||||
{
|
||||
// the data will be sent, so connection is not idle
|
||||
_idle = false;
|
||||
|
||||
// we need an output buffer (this will immediately send the data)
|
||||
PassthroughBuffer buffer(_parent, _handler, frame);
|
||||
}
|
||||
|
|
@ -361,6 +368,9 @@ bool ConnectionImpl::send(CopiedBuffer &&buffer)
|
|||
// are we waiting for other frames to be sent before us?
|
||||
if (_queue.empty())
|
||||
{
|
||||
// data will be sent, thus connection is not empty
|
||||
_idle = false;
|
||||
|
||||
// send it directly
|
||||
_handler->onData(_parent, buffer.data(), buffer.size());
|
||||
}
|
||||
|
|
@ -374,6 +384,24 @@ bool ConnectionImpl::send(CopiedBuffer &&buffer)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a ping / heartbeat frame to keep the connection alive
|
||||
* @param force also send heartbeat if connection is not idle
|
||||
* @return bool
|
||||
*/
|
||||
bool ConnectionImpl::heartbeat(bool force)
|
||||
{
|
||||
// do nothing if the connection is not idle (but we do reset the idle state to ensure
|
||||
// that the next heartbeat will be sent if nothing is going to change from now on)
|
||||
if (!force && !_idle) return _idle = true;
|
||||
|
||||
// send a frame
|
||||
if (!send(HeartbeatFrame())) return false;
|
||||
|
||||
// frame has been sent, we treat the connection as idle now until some other data is sent over it
|
||||
return _idle = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of namspace
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public:
|
|||
* @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
|
||||
ConnectionFrame((uint32_t)(vhost.length() + 3)), // length of vhost + byte to encode this length + deprecated shortstring size + deprecated bool
|
||||
_vhost(vhost),
|
||||
_deprecatedCapabilities(""),
|
||||
_deprecatedInsist()
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
* @param challenge the challenge
|
||||
*/
|
||||
ConnectionSecureFrame(const std::string& challenge) :
|
||||
ConnectionFrame(challenge.length() + 4), // 4 for the length of the challenge (uint32_t)
|
||||
ConnectionFrame((uint32_t)(challenge.length() + 4)), // 4 for the length of the challenge (uint32_t)
|
||||
_challenge(challenge)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
* @param response the challenge response
|
||||
*/
|
||||
ConnectionSecureOKFrame(const std::string& response) :
|
||||
ConnectionFrame(response.length() + 4), //response length + uint32_t for encoding the length
|
||||
ConnectionFrame((uint32_t)(response.length() + 4)), //response length + uint32_t for encoding the length
|
||||
_response(response)
|
||||
{}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public:
|
|||
* @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
|
||||
ConnectionFrame((uint32_t)(properties.size() + mechanisms.length() + locales.length() + 10)), // 4 for each longstring (size-uint32), 2 major/minor
|
||||
_major(major),
|
||||
_minor(minor),
|
||||
_properties(properties),
|
||||
|
|
@ -121,7 +121,7 @@ public:
|
|||
* Major AMQP version number
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t major() const
|
||||
uint8_t majorVersion() const
|
||||
{
|
||||
return _major;
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ public:
|
|||
* Minor AMQP version number
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t minor() const
|
||||
uint8_t minorVersion() const
|
||||
{
|
||||
return _minor;
|
||||
}
|
||||
|
|
@ -196,8 +196,8 @@ public:
|
|||
properties["product"] = "Copernica AMQP library";
|
||||
properties["version"] = "Unknown";
|
||||
properties["platform"] = "Unknown";
|
||||
properties["copyright"] = "Copyright 2015 Copernica BV";
|
||||
properties["information"] = "http://www.copernica.com";
|
||||
properties["copyright"] = "Copyright 2015 - 2018 Copernica BV";
|
||||
properties["information"] = "https://www.copernica.com";
|
||||
properties["capabilities"] = capabilities;
|
||||
|
||||
// move connection to handshake mode
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public:
|
|||
* @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
|
||||
ConnectionFrame((uint32_t)(properties.size() + mechanism.length() + response.length() + locale.length() + 6)), // 1 byte extra per shortstring, 4 per longstring
|
||||
_properties(properties),
|
||||
_mechanism(mechanism),
|
||||
_response(response),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
/**
|
||||
* Base class for a message implementation
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include "basicdeliverframe.h"
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,15 +3,34 @@
|
|||
*
|
||||
* Implementation file for the DeferredConsumer class
|
||||
*
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
#include "includes.h"
|
||||
#include "basicdeliverframe.h"
|
||||
|
||||
/**
|
||||
* Namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Process a delivery frame
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void DeferredConsumer::process(BasicDeliverFrame &frame)
|
||||
{
|
||||
// this object will handle all future frames with header and body data
|
||||
_channel->install(shared_from_this());
|
||||
|
||||
// retrieve the delivery tag and whether we were redelivered
|
||||
_deliveryTag = frame.deliveryTag();
|
||||
_redelivered = frame.redelivered();
|
||||
|
||||
// initialize the object for the next message
|
||||
initialize(frame.exchange(), frame.routingKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Report success for frames that report start consumer operations
|
||||
* @param name Consumer tag that is started
|
||||
|
|
@ -32,18 +51,6 @@ const std::shared_ptr<Deferred> &DeferredConsumer::reportSuccess(const std::stri
|
|||
return _next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce that a message was received
|
||||
* @param message The message to announce
|
||||
* @param deliveryTag The delivery tag (for ack()ing)
|
||||
* @param redelivered Is this a redelivered message
|
||||
*/
|
||||
void DeferredConsumer::announce(const Message &message, uint64_t deliveryTag, bool redelivered) const
|
||||
{
|
||||
// simply execute the message callback
|
||||
_messageCallback(message, deliveryTag, redelivered);
|
||||
}
|
||||
|
||||
/**
|
||||
* End namespace
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,142 +0,0 @@
|
|||
/**
|
||||
* deferredconsumerbase.cpp
|
||||
*
|
||||
* Base class for the deferred consumer and the
|
||||
* deferred get.
|
||||
*
|
||||
* @copyright 2016 - 2017 Copernica B.V.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include "../include/deferredconsumerbase.h"
|
||||
#include "basicdeliverframe.h"
|
||||
#include "basicgetokframe.h"
|
||||
#include "basicheaderframe.h"
|
||||
#include "bodyframe.h"
|
||||
|
||||
/**
|
||||
* Start namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Process a delivery frame
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void DeferredConsumerBase::process(BasicDeliverFrame &frame)
|
||||
{
|
||||
// retrieve the delivery tag and whether we were redelivered
|
||||
_deliveryTag = frame.deliveryTag();
|
||||
_redelivered = frame.redelivered();
|
||||
|
||||
// anybody interested in the new message?
|
||||
if (_beginCallback) _beginCallback();
|
||||
|
||||
// do we have anybody interested in messages?
|
||||
if (_messageCallback) _message.construct(frame.exchange(), frame.routingKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a delivery frame from a get request
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void DeferredConsumerBase::process(BasicGetOKFrame &frame)
|
||||
{
|
||||
// retrieve the delivery tag and whether we were redelivered
|
||||
_deliveryTag = frame.deliveryTag();
|
||||
_redelivered = frame.redelivered();
|
||||
|
||||
// anybody interested in the new message?
|
||||
if (_beginCallback) _beginCallback();
|
||||
|
||||
// do we have anybody interested in messages?
|
||||
if (_messageCallback) _message.construct(frame.exchange(), frame.routingKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the message headers
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void DeferredConsumerBase::process(BasicHeaderFrame &frame)
|
||||
{
|
||||
// store the body size
|
||||
_bodySize = frame.bodySize();
|
||||
|
||||
// do we have a message?
|
||||
if (_message)
|
||||
{
|
||||
// store the body size and metadata
|
||||
_message->setBodySize(_bodySize);
|
||||
_message->set(frame.metaData());
|
||||
}
|
||||
|
||||
// anybody interested in the headers?
|
||||
if (_headerCallback) _headerCallback(frame.metaData());
|
||||
|
||||
// no body data expected? then we are now complete
|
||||
if (!_bodySize) complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the message data
|
||||
*
|
||||
* @param frame The frame to process
|
||||
*/
|
||||
void DeferredConsumerBase::process(BodyFrame &frame)
|
||||
{
|
||||
// make sure we stay in scope
|
||||
auto self = shared_from_this();
|
||||
|
||||
// update the bytes still to receive
|
||||
_bodySize -= frame.payloadSize();
|
||||
|
||||
// anybody interested in the data?
|
||||
if (_dataCallback) _dataCallback(frame.payload(), frame.payloadSize());
|
||||
|
||||
// do we have a message? then append the data
|
||||
if (_message) _message->append(frame.payload(), frame.payloadSize());
|
||||
|
||||
// if all bytes were received we are now complete
|
||||
if (!_bodySize) complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that a message was done
|
||||
*/
|
||||
void DeferredConsumerBase::complete()
|
||||
{
|
||||
// make sure we stay in scope
|
||||
auto self = shared_from_this();
|
||||
|
||||
// also monitor the channel
|
||||
Monitor monitor{ _channel };
|
||||
|
||||
// do we have a message?
|
||||
if (_message)
|
||||
{
|
||||
// announce the message
|
||||
announce(*_message, _deliveryTag, _redelivered);
|
||||
|
||||
// and destroy it
|
||||
_message.reset();
|
||||
}
|
||||
|
||||
// do we have to inform anyone about completion?
|
||||
if (_completeCallback) _completeCallback(_deliveryTag, _redelivered);
|
||||
|
||||
// do we still have a valid channel
|
||||
if (!monitor.valid()) return;
|
||||
|
||||
// we are now done executing
|
||||
_channel->complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* End namespace
|
||||
*/
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* DeferredExtReceiver.cpp
|
||||
*
|
||||
* Implementation file for the DeferredExtReceiver class
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include "amqpcpp/deferredextreceiver.h"
|
||||
#include "amqpcpp/channelimpl.h"
|
||||
|
||||
/**
|
||||
* Begin of namespace
|
||||
*/
|
||||
namespace AMQP {
|
||||
|
||||
/**
|
||||
* Initialize the object to send out a message
|
||||
* @param exchange the exchange to which the message was published
|
||||
* @param routingkey the routing key that was used to publish the message
|
||||
*/
|
||||
void DeferredExtReceiver::initialize(const std::string &exchange, const std::string &routingkey)
|
||||
{
|
||||
// call base
|
||||
DeferredReceiver::initialize(exchange, routingkey);
|
||||
|
||||
// do we have anybody interested in messages? in that case we construct the message
|
||||
if (_messageCallback) _message.construct(exchange, routingkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that a message was done
|
||||
*/
|
||||
void DeferredExtReceiver::complete()
|
||||
{
|
||||
// also monitor the channel
|
||||
Monitor monitor(_channel);
|
||||
|
||||
// do we have a message?
|
||||
if (_message) _messageCallback(*_message, _deliveryTag, _redelivered);
|
||||
|
||||
// do we have to inform anyone about completion?
|
||||
if (_deliveredCallback) _deliveredCallback(_deliveryTag, _redelivered);
|
||||
|
||||
// for the next iteration we want a new message
|
||||
_message.reset();
|
||||
|
||||
// do we still have a valid channel
|
||||
if (!monitor.valid()) return;
|
||||
|
||||
// we are now done executing, so the channel can forget the current receiving object
|
||||
_channel->install(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* End of namespace
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* Implementation of the DeferredGet call
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2014 - 2017 Copernica BV
|
||||
* @copyright 2014 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -19,23 +19,22 @@ namespace AMQP {
|
|||
|
||||
/**
|
||||
* Report success for a get operation
|
||||
*
|
||||
* @param messagecount Number of messages left in the queue
|
||||
* @param deliveryTag Delivery tag of the message coming in
|
||||
* @param redelivered Was the message redelivered?
|
||||
*/
|
||||
const std::shared_ptr<Deferred> &DeferredGet::reportSuccess(uint32_t messagecount, uint64_t deliveryTag, bool redelivered)
|
||||
{
|
||||
// install this object as the handler for the upcoming header and body frames
|
||||
_channel->install(shared_from_this());
|
||||
|
||||
// store delivery tag and redelivery status
|
||||
_deliveryTag = deliveryTag;
|
||||
_redelivered = redelivered;
|
||||
|
||||
// install ourselves in the channel
|
||||
_channel->install("", shared_from_this(), true);
|
||||
|
||||
// report the size (note that this is the size _minus_ the message that is retrieved
|
||||
// (and for which the callback will be called later), so it could be zero)
|
||||
if (_sizeCallback) _sizeCallback(messagecount);
|
||||
if (_countCallback) _countCallback(messagecount);
|
||||
|
||||
// return next handler
|
||||
return _next;
|
||||
|
|
@ -48,7 +47,7 @@ const std::shared_ptr<Deferred> &DeferredGet::reportSuccess(uint32_t messagecoun
|
|||
const std::shared_ptr<Deferred> &DeferredGet::reportSuccess() const
|
||||
{
|
||||
// report the size
|
||||
if (_sizeCallback) _sizeCallback(0);
|
||||
if (_countCallback) _countCallback(0);
|
||||
|
||||
// check if a callback was set
|
||||
if (_emptyCallback) _emptyCallback();
|
||||
|
|
@ -58,27 +57,15 @@ const std::shared_ptr<Deferred> &DeferredGet::reportSuccess() const
|
|||
}
|
||||
|
||||
/**
|
||||
* Announce that a message has been received
|
||||
* @param message The message to announce
|
||||
* @param deliveryTag The delivery tag (for ack()ing)
|
||||
* @param redelivered Is this a redelivered message
|
||||
* Extended implementation of the complete method that is called when a message was fully received
|
||||
*/
|
||||
void DeferredGet::announce(const Message &message, uint64_t deliveryTag, bool redelivered) const
|
||||
void DeferredGet::complete()
|
||||
{
|
||||
// monitor the channel
|
||||
Monitor monitor{ _channel };
|
||||
|
||||
// the channel is now synchronized
|
||||
// the channel is now synchronized, delayed frames may now be sent
|
||||
_channel->onSynchronized();
|
||||
|
||||
// simply execute the message callback
|
||||
_messageCallback(std::move(message), deliveryTag, redelivered);
|
||||
|
||||
// check if the channel is still valid
|
||||
if (!monitor.valid()) return;
|
||||
|
||||
// stop consuming now
|
||||
_channel->uninstall({});
|
||||
|
||||
// pass on to normal implementation
|
||||
DeferredExtReceiver::complete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue