first commit

This commit is contained in:
bing
2026-04-03 11:29:43 +08:00
commit 2974a3dec5
60 changed files with 10444 additions and 0 deletions

168
.clang-format Normal file
View File

@@ -0,0 +1,168 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
- Regex: '^<.*'
Priority: 2
SortPriority: 0
- Regex: '.*'
Priority: 3
SortPriority: 0
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Auto
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
...

11
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
# [Choice] Debian / Ubuntu version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04
ARG VARIANT=buster
FROM mcr.microsoft.com/vscode/devcontainers/cpp:dev-${VARIANT}
# [Optional] Uncomment this section to install additional packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends lsb-release wget software-properties-common \
&& bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" \
&& apt-get -y install --no-install-recommends clang-format-11 clang-tidy-11 ninja-build rabbitmq-server \
libssl-dev librabbitmq-dev libboost-dev libboost-chrono-dev libboost-system-dev

View File

@@ -0,0 +1,12 @@
# [Choice] Debian / Ubuntu version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04
ARG VARIANT=buster
FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT}
# Install needed packages. Use a separate RUN statement to add your own dependencies.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install build-essential cmake cppcheck valgrind clang lldb llvm gdb \
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/*
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>

View File

@@ -0,0 +1,35 @@
{
"name": "C++",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-10, debian-9, ubuntu-20.04, ubuntu-18.04
"args": { "VARIANT": "ubuntu-20.04" }
},
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
// Set *default* container specific settings.json values on container create.
"settings": {
"clangd.path": "clangd-11",
"cmake.copyCompileCommands": "${workspaceFolder}/compile_commands.json",
"workbench.colorTheme": "Solarized Dark",
"terminal.integrated.shell.linux": "/bin/bash"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
// "ms-vscode.cpptools",
"ms-vscode.cmake-tools",
"vscodevim.vim",
"llvm-vs-code-extensions.vscode-clangd",
"twxs.cmake",
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "gcc -v",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

10
.gitattributes vendored Normal file
View File

@@ -0,0 +1,10 @@
# Default for those who don't have core.autocrlf set
* text=auto
# Things that should be treated as text
*.cpp text
*.h text
CMakeLists.txt text
*.cmake text
*.md text
*.in text

72
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: ci
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
fail-fast: false
# Set up a matrix to run the following 3 configurations:
# 1. <Windows, Release, latest MSVC compiler toolchain on the default runner image, default generator>
# 2. <Linux, Release, latest GCC compiler toolchain on the default runner image, default generator>
# 3. <Linux, Release, latest Clang compiler toolchain on the default runner image, default generator>
#
# To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list.
matrix:
os: [ubuntu-latest]
build_type: [Release]
c_compiler: [gcc, clang, cl]
include:
- os: ubuntu-latest
c_compiler: gcc
cpp_compiler: g++
- os: ubuntu-latest
c_compiler: clang
cpp_compiler: clang++
exclude:
- os: ubuntu-latest
c_compiler: cl
steps:
- uses: actions/checkout@v3
- name: Set reusable strings
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
- name: Install Deps
shell: bash
run: |
sudo apt update
sudo apt install libboost-all-dev librabbitmq-dev
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
-DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-S ${{ github.workspace }}
- name: Build
# Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}
- name: Test
working-directory: ${{ steps.strings.outputs.build-output-dir }}
# Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest --build-config ${{ matrix.build_type }}

20
.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
*~
.*.sw?
.cache/
.vscode/
build/
CMakeCache.txt
cmake_install.cmake
CMakeFiles/
Debug/
*.vcxproj*
*.sln
*.suo
*.*sdf
ipch/
install_manifest.txt
.ycm_extra_conf.py*
compile_commands.json
*.orig

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest.git

55
.travis.yml Normal file
View File

@@ -0,0 +1,55 @@
# Travis-CI Build for SimpleAmqpClient
# see travis-ci.org for details
language: cpp
os: linux
dist: xenial
jobs:
include:
- compiler: gcc
env: FLAGS=""
- compiler: clang
env: FLAGS=""
- compiler: clang
env: FLAGS="-g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer"
- compiler: clang
env: FLAGS="-g -O1 -fsanitize=thread -fno-omit-frame-pointer"
addons:
apt:
sources:
- sourceline: deb http://dl.bintray.com/rabbitmq-erlang/debian xenial erlang
key_url: https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc
- sourceline: deb https://dl.bintray.com/rabbitmq/debian xenial main
key_url: https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc
packages:
- libboost-dev
- libboost-chrono-dev
- libboost-system-dev
- rabbitmq-server
- ninja-build
# install pre-reqs
install:
- mkdir -p _prereqs
- pushd _prereqs
- git clone https://github.com/alanxz/rabbitmq-c
- cd rabbitmq-c
- git checkout v0.10.0
- export RABBITMQC_DIR=`pwd`/../../_install
- cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${RABBITMQC_DIR} -DBUILD_EXAMPLES=OFF -DBUILD_TESTS=OFF -DBUILD_TOOLS=OFF .
- cmake --build . --target install
- popd
before_script:
- mkdir _build
- cd _build
- pwd
# Run the Build script
script:
- cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="${FLAGS}" -DCMAKE_INSTALL_PREFIX=../_install -DENABLE_TESTING=ON -DRabbitmqc_DIR=${RABBITMQ_C_DIR} ..
- cmake --build . --target install
- AMQP_BROKER=localhost ASAN_OPTIONS=detect_leaks=1 ctest -V .

316
CMakeLists.txt Normal file
View File

@@ -0,0 +1,316 @@
# Most widely used distributions have cmake 3.5 or greater available as of March
# 2019. A notable exception is RHEL-7 (CentOS7). You can install a current
# version of CMake by first installing Extra Packages for Enterprise Linux
# (https://fedoraproject.org/wiki/EPEL#Extra_Packages_for_Enterprise_Linux_.28EPEL.29)
# and then issuing `yum install cmake3` on the command line.
cmake_minimum_required(VERSION 3.5)
project(SimpleAmqpClient LANGUAGES CXX)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 98)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Follow all steps below in order to calculate new ABI version when updating the library
# NOTE: THIS IS UNRELATED to the actual project version
#
# 1. If the library source code has changed at all since the last update, then increment revision
# 2. If any interfaces have been added, removed, or changed since the last update, increment
# current and set revision to 0.
# 3. If any interfaces have been added since the last public release, then increment age.
# 4. If any interfaces have been removed since the last public release, then set age to 0.
set(SAC_SOVERSION_CURRENT 7)
set(SAC_SOVERSION_REVISION 1)
set(SAC_SOVERSION_AGE 0)
math(EXPR SAC_SOVERSION_MAJOR "${SAC_SOVERSION_CURRENT} - ${SAC_SOVERSION_AGE}")
math(EXPR SAC_SOVERSION_MINOR "${SAC_SOVERSION_AGE}")
math(EXPR SAC_SOVERSION_PATCH "${SAC_SOVERSION_REVISION}")
set(SAC_VERSION ${SAC_SOVERSION_MAJOR}.${SAC_SOVERSION_MINOR}.${SAC_SOVERSION_PATCH})
set(SAC_SOVERSION ${SAC_SOVERSION_MAJOR})
file(STRINGS src/SimpleAmqpClient/Version.h _API_VERSION_MAJOR REGEX "^#define SIMPLEAMQPCLIENT_VERSION_MAJOR [0-9]+$")
file(STRINGS src/SimpleAmqpClient/Version.h _API_VERSION_MINOR REGEX "^#define SIMPLEAMQPCLIENT_VERSION_MINOR [0-9]+$")
file(STRINGS src/SimpleAmqpClient/Version.h _API_VERSION_PATCH REGEX "^#define SIMPLEAMQPCLIENT_VERSION_PATCH [0-9]+$")
string(REGEX MATCH "[0-9]+" _API_VERSION_MAJOR ${_API_VERSION_MAJOR})
string(REGEX MATCH "[0-9]+" _API_VERSION_MINOR ${_API_VERSION_MINOR})
string(REGEX MATCH "[0-9]+" _API_VERSION_PATCH ${_API_VERSION_PATCH})
set(SAC_APIVERSION ${_API_VERSION_MAJOR}.${_API_VERSION_MINOR}.${_API_VERSION_PATCH})
option(BUILD_SHARED_LIBS "Build SimpleAmqpClient as a shared library" ON)
# Force the use of static boost library for static libraries
include(CMakeDependentOption)
cmake_dependent_option(
Boost_Dynamic_Linking_ENABLED
"Enable boost dynamic linking"
ON
"BUILD_SHARED_LIBS"
OFF
)
if(Boost_Dynamic_Linking_ENABLED)
set(Boost_USE_STATIC_LIBS OFF)
else()
set(Boost_USE_STATIC_LIBS ON)
endif()
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.47.0 COMPONENTS chrono REQUIRED)
if(Boost_VERSION VERSION_LESS 1.89)
find_package(Boost 1.47.0 COMPONENTS chrono system REQUIRED)
endif()
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
# Try using the CMake config modules first
find_package(rabbitmq-c CONFIG QUIET)
if (rabbitmq-c_FOUND)
if (BUILD_SHARED_LIBS)
set(Rabbitmqc_LIBRARY rabbitmq::rabbitmq)
else()
set(Rabbitmqc_LIBRARY rabbitmq::rabbitmq-static)
endif()
get_target_property(Rabbitmqc_INCLUDE_DIRS ${Rabbitmqc_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES)
else()
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Modules)
find_package(Rabbitmqc REQUIRED)
INCLUDE_DIRECTORIES(SYSTEM ${Rabbitmqc_INCLUDE_DIRS})
endif()
option(ENABLE_SSL_SUPPORT "Enable SSL support." ${Rabbitmqc_SSL_ENABLED})
if (ENABLE_SSL_SUPPORT)
add_definitions(-DSAC_SSL_SUPPORT_ENABLED)
endif()
if (CMAKE_GENERATOR MATCHES ".*(Make|Ninja).*"
AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel" FORCE)
message(STATUS "CMAKE_BUILD_TYPE not specified. Using ${CMAKE_BUILD_TYPE} build")
endif ()
if (CMAKE_CXX_FLAGS STREQUAL ""
AND NOT DEFINED SAC_CXX_FLAGS_SET)
if (CMAKE_COMPILER_IS_GNUCXX
OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
SET(CMAKE_CXX_FLAGS "-Wall -Wextra" CACHE STRING "Flags used by the compiler during all build types." FORCE)
endif ()
set(SAC_CXX_FLAGS_SET TRUE CACHE INTERNAL "Have the SAC default compiler flags been set?")
endif ()
include_directories(BEFORE src
${CMAKE_CURRENT_BINARY_DIR})
if (WIN32)
set(SOCKET_LIBRARY ws2_32)
endif ()
set(SAC_LIB_SRCS
src/SimpleAmqpClient/SimpleAmqpClient.h
src/SimpleAmqpClient/AmqpException.h
src/AmqpException.cpp
src/SimpleAmqpClient/Bytes.h
src/SimpleAmqpClient/Channel.h
src/Channel.cpp
src/SimpleAmqpClient/ChannelImpl.h
src/ChannelImpl.cpp
src/SimpleAmqpClient/BasicMessage.h
src/BasicMessage.cpp
src/SimpleAmqpClient/Util.h
src/SimpleAmqpClient/AmqpLibraryException.h
src/AmqpLibraryException.cpp
src/SimpleAmqpClient/AmqpResponseLibraryException.h
src/AmqpResponseLibraryException.cpp
src/SimpleAmqpClient/BadUriException.h
src/SimpleAmqpClient/ConnectionClosedException.h
src/SimpleAmqpClient/ConsumerTagNotFoundException.h
src/SimpleAmqpClient/MessageRejectedException.h
src/SimpleAmqpClient/Envelope.h
src/Envelope.cpp
src/SimpleAmqpClient/MessageReturnedException.h
src/MessageReturnedException.cpp
src/SimpleAmqpClient/Table.h
src/Table.cpp
src/SimpleAmqpClient/TableImpl.h
src/TableImpl.cpp
)
add_library(SimpleAmqpClient ${SAC_LIB_SRCS})
target_link_libraries(SimpleAmqpClient ${Rabbitmqc_LIBRARY} ${SOCKET_LIBRARY} ${Boost_LIBRARIES} $<$<BOOL:${Boost_Dynamic_Linking_ENABLED}>:Boost::dynamic_linking>)
if (WIN32)
if (NOT BUILD_SHARED_LIBS)
target_compile_definitions(SimpleAmqpClient PUBLIC SimpleAmqpClient_STATIC)
endif ()
set_target_properties(SimpleAmqpClient PROPERTIES VERSION ${SAC_VERSION} OUTPUT_NAME SimpleAmqpClient.${SAC_SOVERSION})
else ()
set_target_properties(SimpleAmqpClient PROPERTIES VERSION ${SAC_VERSION} SOVERSION ${SAC_SOVERSION})
endif ()
# Some smoke tests:
option(ENABLE_TESTING "Enable smoke tests" OFF)
if (ENABLE_TESTING)
enable_testing()
set(BUILD_GTEST ON CACHE BOOL "" FORCE)
set(BUILD_GMOCK OFF CACHE BOOL "" FORCE)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
# This only affects targets declared after this.
set(BUILD_SHARED_LIBS OFF)
mark_as_advanced(BUILD_GMOCK)
mark_as_advanced(BUILD_GTEST)
mark_as_advanced(INSTALL_GTEST)
mark_as_advanced(gmock_build_tests)
mark_as_advanced(gtest_build_samples)
mark_as_advanced(gtest_build_tests)
mark_as_advanced(gtest_disable_pthreads)
mark_as_advanced(gtest_force_shared_crt)
mark_as_advanced(gtest_hide_internal_symbols)
add_subdirectory(third-party/googletest)
add_subdirectory(testing)
endif (ENABLE_TESTING)
# Documentation generation
find_package(Doxygen COMPONENTS dot)
option(BUILD_API_DOCS "Build Doxygen API docs" ${DOXYGEN_FOUND})
if (BUILD_API_DOCS)
if (NOT DOXYGEN_FOUND)
message(FATAL_ERROR "Doxygen is required to build the API documentation")
endif ()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile @ONLY)
add_custom_target(docs ALL
COMMAND ${DOXYGEN_EXECUTABLE}
VERBATIM
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs
DEPENDS SimpleAmqpClient
COMMENT "Generating API documentation"
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
)
endif ()
include(GNUInstallDirs)
install(TARGETS SimpleAmqpClient
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(FILES
src/SimpleAmqpClient/AmqpException.h
src/SimpleAmqpClient/AmqpLibraryException.h
src/SimpleAmqpClient/AmqpResponseLibraryException.h
src/SimpleAmqpClient/BadUriException.h
src/SimpleAmqpClient/BasicMessage.h
src/SimpleAmqpClient/Channel.h
src/SimpleAmqpClient/ConnectionClosedException.h
src/SimpleAmqpClient/ConsumerCancelledException.h
src/SimpleAmqpClient/ConsumerTagNotFoundException.h
src/SimpleAmqpClient/Envelope.h
src/SimpleAmqpClient/MessageReturnedException.h
src/SimpleAmqpClient/MessageRejectedException.h
src/SimpleAmqpClient/SimpleAmqpClient.h
src/SimpleAmqpClient/Table.h
src/SimpleAmqpClient/Util.h
src/SimpleAmqpClient/Version.h
DESTINATION include/SimpleAmqpClient
)
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix "\${prefix}")
set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
set(includedir "\${prefix}/include")
if(WIN32)
get_target_property(SIMPLEAMQPCLIENT_LIB SimpleAmqpClient OUTPUT_NAME)
else(WIN32)
set(SIMPLEAMQPCLIENT_LIB SimpleAmqpClient)
endif(WIN32)
# Propagate package dependencies
if (BUILD_SHARED_LIBS)
set(requires_private "librabbitmq")
else (BUILD_SHARED_LIBS)
set(requires_public "librabbitmq")
endif (BUILD_SHARED_LIBS)
# Propagate interface compile definitions
set(SIMPLEAMQPCLIENT_DEFINITIONS "")
get_target_property(propagated_definitions SimpleAmqpClient INTERFACE_COMPILE_DEFINITIONS)
if (propagated_definitions)
foreach(_def ${propagated_definitions})
set(SIMPLEAMQPCLIENT_DEFINITIONS "${SIMPLEAMQPCLIENT_DEFINITIONS} -D${_def}")
endforeach()
endif(propagated_definitions)
# Propagate library dependencies
set(libs_private "")
set(libs_public "")
if (BUILD_SHARED_LIBS)
set(populate_libs "libs_private")
else (BUILD_SHARED_LIBS)
set(populate_libs "libs_public")
set(extra_win32_targets "${Rabbitmqc_LIBRARY};${SOCKET_LIBRARY}")
endif (BUILD_SHARED_LIBS)
foreach(_lib ${Boost_LIBRARIES} ${extra_win32_targets})
# Check if FindBoost.cmake provided actual library paths or targets
if(TARGET ${_lib})
get_target_property(_lib ${_lib} LOCATION)
message(WARNING "Using target ${_lib} as a library")
endif()
get_filename_component(_LIBPATH ${_lib} PATH)
if (NOT _LIBPATH STREQUAL _LASTLIBPATH AND NOT _LIBPATH STREQUAL "")
set(${populate_libs} "${${populate_libs}} -L\"${_LIBPATH}\"")
set(_LASTLIBPATH ${_LIBPATH})
endif()
get_filename_component(_LIBNAME ${_lib} NAME_WLE)
if (NOT _LIBNAME STREQUAL "debug" AND NOT _LIBNAME STREQUAL "optimized")
if (NOT WIN32)
string(REGEX REPLACE "^lib" "" _LIBNAME ${_LIBNAME})
endif()
set(_LIBNAME "-l${_LIBNAME}")
set(${populate_libs} "${${populate_libs}} ${_LIBNAME}")
endif()
endforeach()
configure_file(libSimpleAmqpClient.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libSimpleAmqpClient.pc @ONLY)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/libSimpleAmqpClient.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)

15
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,15 @@
Contributing to SimpleAmqpClient
===============================
Thanks for contributing to SimpleAmqpClient. I firmly believe that
participation help make open source software great. With that in mind there
are a few things you can do to make this interaction a bit smoother.
Please use the following guidelines when creating an issue or submitting
a pull request
Creating an issue
-----------------
Submitting a pull-request
------------------------

80
ChangeLog.md Normal file
View File

@@ -0,0 +1,80 @@
# Changes since v2.5.0 (v2.5.1)
- fix: restore GNUInstallDirs that was mistakenly removed.
- refactor of Channel constructors
# Changes since v2.4 (v2.5)
NOTE: this release requires rabbitmq-c v0.8.0 or better.
- add: support timestamp values in `Table` (1057ed4)
- enh: improve efficiency of `BasicMessage` reducing number of copies (938b102)
- doc: many clarifications in documentation (47f4c79)
- add: support for BasicPublish message rejection due to queue full (ecfbbfc)
- add: support for additional parameters in creating SSL connection (56713c0, eaa6044)
- add: `Channel::GetSocketFD` to allow access to underlying channel socket. (7c14a2e)
- add: multiple flag on `Channel::BasicAck` (6323892)
- add: support for unsigned types in `Table` (20296d2)
- enh: support for RabbitMQ auth-failure extension (0b67021)
- fix: consumer prefetch difference on RabbitMQ v3.3+ (59a1e05)
- rabbitmq-c errors are wrapped in `AmqpLibraryException` (a3a3ef7)
Changes since v2.3 (v2.4):
- Add support for consumer cancellation notification (5d35698)
- Improvements in extra-frame bookkeeping reducing memory useage under certain
conditions (e9de652, f4980bc)
- API support for waiting for multiple consumers at a time (e7e701a)
- Add version header (0fc6cab)
- Add pkg-config on install (a13c99a)
- Add DeclareQueueWithCounts API (Kai Blaschke 7fbcd96)
- Support for C++11 (Alexandre Jacquin 57a8d85)
- Add BasicReject API (Luca Marturana 0c9478e)
- Upgrade gtest to v1.7.0 (8fe82fd)
Changes since v2.2 (v2.3):
NOTE: this release uses new rabbitmq-c interfaces introduced in v0.4.0, thus
requires rabbitmq-c v0.4.0 or later.
- Add support for SSL (Ashok Anand 44b8b4e)
- Use new rabbitmq-c socket interface (Nikita Vasiliev 9f6cdac)
- Use new rabbitmq-c error-string interface (a26da26)
- Code formatting and license header updates (752ae75, 5a2f64c, c3dec10, 2b82942)
- Use new rabbitmq-c timeout interface when reading frames (d4a9f31)
- Use new rabbitmq-c interface to release memory on a per-channel basis (49b8ba8)
Changes since v2.1 (v2.2):
NOTE: this is the last version targeting rabbitmq-c v0.3, newer versions will
target rabbitmq-c v0.4
- Disable building test suite by default (4f6af4e)
- Default to building Release build when none is specified (c60d0e9)
- Add -Wall -Wextra to default C++ flags (bf813e5)
- Improve documentation (f967758, 23151d3)
- BUG: throw std::bad_alloc when a 0-length table is received (6d17950, d694d4b)
- Improve Channel::BasicGet documentation (ead3936)
- Disable tests that exercise the immediate flag in basic.publish (48636b1)
- Add Channel::BasicAck() overload allowing basic.ack without keeping the whole Envelope obj (0dea3b8, fcd094a)
- Add method to create Channel from an AMQP URI (c8cae56, 8dd62b5)
- Updated examples (fcc1176, a9d4eec, 03bb42d)
Changes since v2.0-beta (v2.1)
- Add wrapping of amqp_table_t for passing table arguments to various
AMQP RPC methods (bae7b97)
- Fix for bug in BasicConsumeMessage default timeout (6412fcf3)
- Enable travis-ci continuous integration (44089d65)
- Ship google-test framework with library (8d86d2e4)
- Implement SOVERSION-ing (b44f3b7b)
- Missing include in AmqpException.cpp (20ccca9)
- Fix for memory leak in BasicPublish when exception is thrown (56e20b2)
- Fix for memory leak in BasicMessage when new body assigned (e5bf1157)
- Missing string.h include in AmqpException.h (ecee2104)
- Compile changes to compile cleanly under -Wall -Wextra (2b5a1a23)
- Fix for crash when AmqpException thrown without a class or method id (6a4fac62)
- Fix for incorrect timeout units when BasicConsumeMessage (3cdf94d9)
- Relicensed library under MIT license (a069444b)
- Fix sending unitialized data to broker (080bd9e9)
- Fix free strings returned by amqp_error_string (c7b0cfcc)
- Fix destroy amqp_connection_state object if an exception is thrown in Channel constructor (af936d0)
- Add ability to build as static library (50b6afd)
- Fix for macro redefinition (548084)
- Correct usage of stdint.h on VS2008 and earlier (795c0fea)

289
Doxyfile.in Normal file
View File

@@ -0,0 +1,289 @@
# Doxyfile 1.8.2
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "SimpleAmqpClient"
PROJECT_NUMBER = "@SAC_VERSION@"
PROJECT_BRIEF = "A C++ wrapper around the rabbitmq-c AMQP client library"
PROJECT_LOGO =
OUTPUT_DIRECTORY = .
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 2
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = YES
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = NO
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = "@CMAKE_SOURCE_DIR@/src/SimpleAmqpClient"
INPUT += "@CMAKE_SOURCE_DIR@/README.md"
USE_MDFILE_AS_MAINPAGE = README.md
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h
RECURSIVE = YES
EXCLUDE = *Impl.h
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = *Impl.h
EXCLUDE_SYMBOLS =
#EXAMPLE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/examples"
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = NO
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = YES
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.SimpleAmqpClient
DOCSET_PUBLISHER_ID = com.github.alanxz
DOCSET_PUBLISHER_NAME = Alan Antonuk
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE =
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.SimpleAmqpClient
DISABLE_INDEX = NO
GENERATE_TREEVIEW = NONE
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
SEARCHENGINE = NO
SERVER_BASED_SEARCH = NO
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = YES
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH = @CMAKE_CURRENT_SOURCE_DIR@/src
INCLUDE_FILE_PATTERNS =
PREDEFINED = GENERATING_DOCUMENTATION
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
DOT_NUM_THREADS = 0
#DOT_FONTNAME = FreeSans
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = svg
INTERACTIVE_SVG = YES
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = YES
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

23
LICENSE-MIT Normal file
View File

@@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2010-2014 Alan Antonuk. All Rights Reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,32 @@
#Find the Rabbitmq C library
INCLUDE(LibFindMacros)
# Find the include directories
FIND_PATH(Rabbitmqc_INCLUDE_DIR
NAMES amqp.h
HINTS ${Rabbitmqc_DIR}/include
)
FIND_LIBRARY(Rabbitmqc_LIBRARY
NAMES rabbitmq
HINTS ${Rabbitmqc_DIR}/lib
)
SET(Rabbitmqc_PROCESS_INCLUDES Rabbitmqc_INCLUDE_DIR)
SET(Rabbitmqc_PROCESS_LIBS Rabbitmqc_LIBRARY)
LIBFIND_PROCESS(Rabbitmqc)
find_file(_Rabbitmqc_SSL_HEADER
NAMES amqp_ssl_socket.h
PATHS ${Rabbitmqc_INCLUDE_DIR}
NO_DEFAULT_PATH
)
string(COMPARE NOTEQUAL "${_Rabbitmqc_SSL_HEADER}"
"_Rabbitmqc_SSL_HEADER-NOTFOUND" _rmqc_ssl_enabled)
set(Rabbitmqc_SSL_ENABLED ${_rmqc_ssl_enabled} CACHE BOOL
"Rabbitmqc is SSL Enabled" FORCE)
mark_as_advanced(_Rabbitmqc_SSL_HEADER Rabbitmqc_SSL_ENABLED)

View File

@@ -0,0 +1,99 @@
# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments
# used for the current package. For this to work, the first parameter must be the
# prefix of the current package, then the prefix of the new package etc, which are
# passed to find_package.
macro (libfind_package PREFIX)
set (LIBFIND_PACKAGE_ARGS ${ARGN})
if (${PREFIX}_FIND_QUIETLY)
set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET)
endif (${PREFIX}_FIND_QUIETLY)
if (${PREFIX}_FIND_REQUIRED)
set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED)
endif (${PREFIX}_FIND_REQUIRED)
find_package(${LIBFIND_PACKAGE_ARGS})
endmacro (libfind_package)
# CMake developers made the UsePkgConfig system deprecated in the same release (2.6)
# where they added pkg_check_modules. Consequently I need to support both in my scripts
# to avoid those deprecated warnings. Here's a helper that does just that.
# Works identically to pkg_check_modules, except that no checks are needed prior to use.
macro (libfind_pkg_check_modules PREFIX PKGNAME)
if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
include(UsePkgConfig)
pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS)
else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(${PREFIX} ${PKGNAME})
endif (PKG_CONFIG_FOUND)
endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
endmacro (libfind_pkg_check_modules)
# Do the final processing once the paths have been detected.
# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
# all the variables, each of which contain one include directory.
# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
# Also handles errors in case library detection was required, etc.
macro (libfind_process PREFIX)
# Skip processing if already processed during this run
if (NOT ${PREFIX}_FOUND)
# Start with the assumption that the library was found
set (${PREFIX}_FOUND TRUE)
# Process all includes and set _FOUND to false if any are missing
foreach (i ${${PREFIX}_PROCESS_INCLUDES})
if (${i})
set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}})
mark_as_advanced(${i})
else (${i})
set (${PREFIX}_FOUND FALSE)
endif (${i})
endforeach (i)
# Process all libraries and set _FOUND to false if any are missing
foreach (i ${${PREFIX}_PROCESS_LIBS})
if (${i})
set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}})
mark_as_advanced(${i})
else (${i})
set (${PREFIX}_FOUND FALSE)
endif (${i})
endforeach (i)
# Print message and/or exit on fatal error
if (${PREFIX}_FOUND)
if (NOT ${PREFIX}_FIND_QUIETLY)
message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
endif (NOT ${PREFIX}_FIND_QUIETLY)
else (${PREFIX}_FOUND)
if (${PREFIX}_FIND_REQUIRED)
foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS})
message("${i}=${${i}}")
endforeach (i)
message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.")
endif (${PREFIX}_FIND_REQUIRED)
endif (${PREFIX}_FOUND)
endif (NOT ${PREFIX}_FOUND)
endmacro (libfind_process)
macro(libfind_library PREFIX basename)
set(TMP "")
if(MSVC80)
set(TMP -vc80)
endif(MSVC80)
if(MSVC90)
set(TMP -vc90)
endif(MSVC90)
set(${PREFIX}_LIBNAMES ${basename}${TMP})
if(${ARGC} GREATER 2)
set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2})
string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES})
set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP})
endif(${ARGC} GREATER 2)
find_library(${PREFIX}_LIBRARY
NAMES ${${PREFIX}_LIBNAMES}
PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}
)
endmacro(libfind_library)

101
README.md Normal file
View File

@@ -0,0 +1,101 @@
SimpleAmqpClient
==================
[SimpleAmqpClient](https://github.com/alanxz/SimpleAmqpClient) is an easy-to-use C++
wrapper around the [rabbitmq-c](https://github.com/alanxz/rabbitmq-c) C library.
It derives inspiration from the [puka](https://github.com/majek/puka) AMQP library
in that it abstracts away the underlying AMQP wire concept of channels and uses them
as an error/consumer scope. This should make writing simple single-threaded AMQP-enabled apps easy.
Installing
----------------
Known to work in the following environments:
- Windows 10 (MSVC 2019, Win64)
- Windows 7 (MSVC 10, Win64, Win32). Likely to work in others, but has not been tested
- Linux (RHEL 6.0, GCC-4.4.5, 32 and 64 bit). Likely to work on other configurations, but has not been tested
- Mac OS X (10.7, 10.6, gcc-4.2, 32 and 64-bit). Likely to work on older version, but has not been tested
### Pre-requisites
+ [boost-1.47.0](http://www.boost.org/) or newer (uses chrono, system internally in addition to other header based libraries such as sharedptr and noncopyable)
+ [rabbitmq-c](http://github.com/alanxz/rabbitmq-c) you'll need version 0.8.0 or better.
+ [cmake 3.5+](http://www.cmake.org/) what is needed for the build system
+ [Doxygen](http://www.stack.nl/~dimitri/doxygen/) OPTIONAL only necessary to generate API documentation
### Build procedure
This is a typical cmake project, it should work like most typical cmake projects:
In a sibling directory to where you extracted the source code:
mkdir simpleamqpclient-build
cd simpleamqpclient-build
cmake ..
Then use your the appropriate build utility to build the library (make, msbuild)
Interesting targets
+ test - will build and run the tests
+ install - will install the library and headers to whatever `CMAKE_INSTALL_PREFIX` is defined to
+ doc - will generate API documentation if you have doxygen setup
Notes:
+ The test google-test based test suite can be enabled by passing `-DENABLE_TESTING=ON` to
cmake
### Build procedure for Windows
Boost libraries are needed, so you can install them using nuget:
```
nuget install boost_chrono-vc142 -Version 1.77.0
nuget install boost_system-vc142 -Version 1.77.0
nuget install boost -Version 1.77.0
```
To build and install succesfully, [rabbitmq-c](https://github.com/alanxz/rabbitmq-c) should be built **as shared library**.
Let *boost_chrono* and *boost_system* be in same directory ```C:\boost```, [rabbitmq-c](https://github.com/alanxz/rabbitmq-c) be on ```C:\rabbitmq-c```,
SSL be OFF, and VS2019 is used, than CMake CLI is:
```
cd cmake -G "Visual Studio 16" -A x64 -DBoost_INCLUDE_DIR="C:/boost.XX.XX.X.X/lib/native/include" -DBOOST_ROOT="C:/boost.X.XX.X.X" -DBOOST_LIBRARYDIR="C:/boost" -DRabbitmqc_INCLUDE_DIR="C:/rabbitmq-c/include" -DRabbitmqc_LIBRARY="C:/rabbitmq-c/lib/rabbitmq.4.lib" -DBoost_USE_STATIC_LIBS=ON -DBUILD_STATIC_LIBS=ON -DENABLE_SSL_SUPPORT=OFF ..
```
Using the library
-----------------
#include <SimpleAmqpClient/SimpleAmqpClient.h>
Will include all the headers necessary to use the library.
The corresponding library is SimpleAmqpClient
The main interface to the library is the AmqpClient::Channel class. It represents
a connection to an AMQP broker, the connection is established on construction of an
instance of this class.
AmqpClient::Channel::ptr_t connection = AmqpClient::Channel::Create("localhost");
All classes have a typedef ptr_t which is equivalent to boost::shared_ptr<> of the
containing class. All classes also have a Create() method does the job creating a new
ptr_t using boost::make_shared<>(). It is recommended that you use these methods
to construct objects in the library.
Commands dealing with declaring/binding/unbinding/deleting exchanges and queues are
all done with the above AmqpClient::Channel object. If one of these commands
fails to complete a AmqpClient::ChannelException will be thrown, which can be caught
and the AmqpClient::Channel object is still useable. If a more severe error occurs
a AmqpClient::ConnectionException or AmqpClient::AmqpResponseLibraryException maybe
thrown, in which case the Channel object is no longer in a usable state and further
use will only generate more exceptions.
Consuming messages is done by setting up a consumer using the BasicConsume method.
This method returns a consumer tag that should be used with the BasicConsumeMessage
BasicQos, BasicRecover, and BasicCancel.
std::string consumer_tag = channel->BasicConsume("my_queue", "");
Envelope::ptr_t envelope = channel->BasicConsumeMessage(consumer_tag);
// Alternatively:
Envelope::ptr_t envelope;
channel->BasicConsumeMessage(consumer_tag, envelope, 10); // 10 ms timeout
// To ack:
channel->BasicAck(envelope);
// To cancel:
channel->BasicCancel(consumer_tag);

12
libSimpleAmqpClient.pc.in Normal file
View File

@@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: SimpleAmqpClient
Description: C++ wrapper of rabbitmq-c AMQP client library
Version: @SAC_APIVERSION@
Requires.private: librabbitmq
Libs: -L${libdir} -l@SIMPLEAMQPCLIENT_LIB@ @libs_public@
Libs.private: @libs_private@
CFlags: -I${includedir} -I"@Boost_INCLUDE_DIRS@" @SIMPLEAMQPCLIENT_DEFINITIONS@

201
src/AmqpException.cpp Normal file
View File

@@ -0,0 +1,201 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "SimpleAmqpClient/AmqpException.h"
#include <amqp.h>
#include <amqp_framing.h>
#include <assert.h>
#include <boost/lexical_cast.hpp>
namespace AmqpClient {
const boost::uint16_t ContentTooLargeException::REPLY_CODE =
AMQP_CONTENT_TOO_LARGE;
const boost::uint16_t NoRouteException::REPLY_CODE = AMQP_NO_ROUTE;
const boost::uint16_t NoConsumersException::REPLY_CODE = AMQP_NO_CONSUMERS;
const boost::uint16_t AccessRefusedException::REPLY_CODE = AMQP_ACCESS_REFUSED;
const boost::uint16_t NotFoundException::REPLY_CODE = AMQP_NOT_FOUND;
const boost::uint16_t ResourceLockedException::REPLY_CODE =
AMQP_RESOURCE_LOCKED;
const boost::uint16_t PreconditionFailedException::REPLY_CODE =
AMQP_PRECONDITION_FAILED;
const boost::uint16_t ConnectionForcedException::REPLY_CODE =
AMQP_CONNECTION_FORCED;
const boost::uint16_t InvalidPathException::REPLY_CODE = AMQP_INVALID_PATH;
const boost::uint16_t FrameErrorException::REPLY_CODE = AMQP_FRAME_ERROR;
const boost::uint16_t SyntaxErrorException::REPLY_CODE = AMQP_SYNTAX_ERROR;
const boost::uint16_t CommandInvalidException::REPLY_CODE =
AMQP_COMMAND_INVALID;
const boost::uint16_t ChannelErrorException::REPLY_CODE = AMQP_CHANNEL_ERROR;
const boost::uint16_t UnexpectedFrameException::REPLY_CODE =
AMQP_UNEXPECTED_FRAME;
const boost::uint16_t ResourceErrorException::REPLY_CODE = AMQP_RESOURCE_ERROR;
const boost::uint16_t NotAllowedException::REPLY_CODE = AMQP_NOT_ALLOWED;
const boost::uint16_t NotImplementedException::REPLY_CODE =
AMQP_NOT_IMPLEMENTED;
const boost::uint16_t InternalErrorException::REPLY_CODE = AMQP_INTERNAL_ERROR;
void AmqpException::Throw(const amqp_rpc_reply_t &reply) {
assert(reply.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION);
switch (reply.reply.id) {
case AMQP_CONNECTION_CLOSE_METHOD:
Throw(
*(reinterpret_cast<amqp_connection_close_t *>(reply.reply.decoded)));
break;
case AMQP_CHANNEL_CLOSE_METHOD:
Throw(*(reinterpret_cast<amqp_channel_close_t *>(reply.reply.decoded)));
break;
default:
throw std::logic_error(
std::string(
"Programming error: unknown server exception class/method")
.append(boost::lexical_cast<std::string>(reply.reply.id)));
}
}
void AmqpException::Throw(const amqp_channel_close_t &reply) {
std::ostringstream what;
std::string reply_text;
if (reply.reply_text.bytes != NULL) {
reply_text =
std::string((char *)reply.reply_text.bytes, reply.reply_text.len);
}
const char *method_name =
amqp_method_name(((reply.class_id << 16) | reply.method_id));
if (method_name != NULL) {
what << "channel error: " << reply.reply_code << ": " << method_name
<< " caused: " << reply_text;
} else {
what << "channel error: " << reply.reply_code << ": " << reply_text;
}
switch (reply.reply_code) {
case ContentTooLargeException::REPLY_CODE:
throw ContentTooLargeException(what.str(), reply_text, reply.class_id,
reply.method_id);
case NoRouteException::REPLY_CODE:
throw NoRouteException(what.str(), reply_text, reply.class_id,
reply.method_id);
case NoConsumersException::REPLY_CODE:
throw NoConsumersException(what.str(), reply_text, reply.class_id,
reply.method_id);
case AccessRefusedException::REPLY_CODE:
throw AccessRefusedException(what.str(), reply_text, reply.class_id,
reply.method_id);
case NotFoundException::REPLY_CODE:
throw NotFoundException(what.str(), reply_text, reply.class_id,
reply.method_id);
case ResourceLockedException::REPLY_CODE:
throw ResourceLockedException(what.str(), reply_text, reply.class_id,
reply.method_id);
case PreconditionFailedException::REPLY_CODE:
throw PreconditionFailedException(what.str(), reply_text, reply.class_id,
reply.method_id);
default:
throw std::logic_error(
std::string("Programming error: unknown channel reply code: ")
.append(boost::lexical_cast<std::string>(reply.reply_code)));
}
}
void AmqpException::Throw(const amqp_connection_close_t &reply) {
std::ostringstream what;
const char *method_name =
amqp_method_name(((reply.class_id << 16) | reply.method_id));
std::string reply_text;
if (reply.reply_text.bytes != NULL) {
reply_text =
std::string((char *)reply.reply_text.bytes, reply.reply_text.len);
}
if (method_name != NULL) {
what << "connection error: " << reply.reply_code << ": " << method_name
<< " caused: " << reply_text;
} else {
what << "connection error: " << reply.reply_code << ": " << reply_text;
}
switch (reply.reply_code) {
case ConnectionForcedException::REPLY_CODE:
throw ConnectionForcedException(what.str(), reply_text, reply.class_id,
reply.method_id);
case InvalidPathException::REPLY_CODE:
throw InvalidPathException(what.str(), reply_text, reply.class_id,
reply.method_id);
case FrameErrorException::REPLY_CODE:
throw FrameErrorException(what.str(), reply_text, reply.class_id,
reply.method_id);
case SyntaxErrorException::REPLY_CODE:
throw SyntaxErrorException(what.str(), reply_text, reply.class_id,
reply.method_id);
case CommandInvalidException::REPLY_CODE:
throw CommandInvalidException(what.str(), reply_text, reply.class_id,
reply.method_id);
case ChannelErrorException::REPLY_CODE:
throw ChannelErrorException(what.str(), reply_text, reply.class_id,
reply.method_id);
case UnexpectedFrameException::REPLY_CODE:
throw UnexpectedFrameException(what.str(), reply_text, reply.class_id,
reply.method_id);
case ResourceErrorException::REPLY_CODE:
throw ResourceErrorException(what.str(), reply_text, reply.class_id,
reply.method_id);
case NotAllowedException::REPLY_CODE:
throw NotAllowedException(what.str(), reply_text, reply.class_id,
reply.method_id);
case NotImplementedException::REPLY_CODE:
throw NotImplementedException(what.str(), reply_text, reply.class_id,
reply.method_id);
case InternalErrorException::REPLY_CODE:
throw InternalErrorException(what.str(), reply_text, reply.class_id,
reply.method_id);
case AccessRefusedException::REPLY_CODE:
throw AccessRefusedException(what.str(), reply_text, reply.class_id,
reply.method_id);
default:
throw std::logic_error(
std::string("Programming error: unknown connection reply code: ")
.append(boost::lexical_cast<std::string>(reply.reply_code)));
}
}
AmqpException::AmqpException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: std::runtime_error(what),
m_reply_text(reply_text),
m_class_id(class_id),
m_method_id(method_id) {}
} // namespace AmqpClient

View File

@@ -0,0 +1,56 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
// Put these first to avoid warnings about INT#_C macro redefinition
#include "SimpleAmqpClient/AmqpLibraryException.h"
#include <amqp.h>
#include <stdlib.h>
namespace AmqpClient {
AmqpLibraryException AmqpLibraryException::CreateException(int error_code) {
std::string message(amqp_error_string2(error_code));
return AmqpLibraryException(message, error_code);
}
AmqpLibraryException AmqpLibraryException::CreateException(
int error_code, const std::string &context) {
std::string message(context);
message.append(": ");
message.append(amqp_error_string2(error_code));
return AmqpLibraryException(message, error_code);
}
AmqpLibraryException::AmqpLibraryException(const std::string &message,
int error_code) throw()
: std::runtime_error(message), m_errorCode(error_code) {}
} // namespace AmqpClient

View File

@@ -0,0 +1,50 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
// Put these first to avoid warnings about INT#_C macro redefinition
#include "SimpleAmqpClient/AmqpResponseLibraryException.h"
#include <amqp.h>
#include <stdlib.h>
namespace AmqpClient {
AmqpResponseLibraryException AmqpResponseLibraryException::CreateException(
const amqp_rpc_reply_t_ &reply, const std::string &context) {
std::string message(context);
message.append(": ");
message.append(amqp_error_string2(reply.library_error));
return AmqpResponseLibraryException(message);
}
AmqpResponseLibraryException::AmqpResponseLibraryException(
const std::string &message) throw()
: std::runtime_error(message) {}
} // namespace AmqpClient

315
src/BasicMessage.cpp Normal file
View File

@@ -0,0 +1,315 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
// Put these first to avoid warnings about INT#_C macro redefinition
#include "SimpleAmqpClient/BasicMessage.h"
#include <amqp.h>
#include <amqp_framing.h>
#include <boost/optional/optional.hpp>
#include <cstring>
#include <string>
#include "SimpleAmqpClient/TableImpl.h"
namespace AmqpClient {
struct BasicMessage::Impl {
std::string body;
boost::optional<std::string> content_type;
boost::optional<std::string> content_encoding;
boost::optional<delivery_mode_t> delivery_mode;
boost::optional<boost::uint8_t> priority;
boost::optional<std::string> correlation_id;
boost::optional<std::string> reply_to;
boost::optional<std::string> expiration;
boost::optional<std::string> message_id;
boost::optional<boost::uint64_t> timestamp;
boost::optional<std::string> type;
boost::optional<std::string> user_id;
boost::optional<std::string> app_id;
boost::optional<std::string> cluster_id;
boost::optional<Table> header_table;
};
BasicMessage::BasicMessage() : m_impl(new Impl) {}
BasicMessage::BasicMessage(const std::string& body) : m_impl(new Impl) {
Body(body);
}
BasicMessage::~BasicMessage() {}
const std::string& BasicMessage::Body() const { return m_impl->body; }
std::string& BasicMessage::Body() { return m_impl->body; }
void BasicMessage::Body(const std::string& body) { m_impl->body = body; }
const std::string& BasicMessage::ContentType() const {
if (ContentTypeIsSet()) {
return m_impl->content_type.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::ContentType(const std::string& content_type) {
m_impl->content_type = content_type;
}
bool BasicMessage::ContentTypeIsSet() const {
return m_impl->content_type.is_initialized();
}
void BasicMessage::ContentTypeClear() { m_impl->content_type.reset(); }
const std::string& BasicMessage::ContentEncoding() const {
if (ContentEncodingIsSet()) {
return m_impl->content_encoding.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::ContentEncoding(const std::string& content_encoding) {
m_impl->content_encoding = content_encoding;
}
bool BasicMessage::ContentEncodingIsSet() const {
return m_impl->content_encoding.is_initialized();
}
void BasicMessage::ContentEncodingClear() { m_impl->content_encoding.reset(); }
BasicMessage::delivery_mode_t BasicMessage::DeliveryMode() const {
return m_impl->delivery_mode.value_or(dm_notset);
}
void BasicMessage::DeliveryMode(delivery_mode_t delivery_mode) {
m_impl->delivery_mode = delivery_mode;
}
bool BasicMessage::DeliveryModeIsSet() const {
return m_impl->delivery_mode.is_initialized();
}
void BasicMessage::DeliveryModeClear() { m_impl->delivery_mode.reset(); }
boost::uint8_t BasicMessage::Priority() const {
return m_impl->priority.value_or(0);
}
void BasicMessage::Priority(boost::uint8_t priority) {
m_impl->priority = priority;
}
bool BasicMessage::PriorityIsSet() const {
return m_impl->priority.is_initialized();
}
void BasicMessage::PriorityClear() { m_impl->priority.reset(); }
const std::string& BasicMessage::CorrelationId() const {
if (CorrelationIdIsSet()) {
return m_impl->correlation_id.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::CorrelationId(const std::string& correlation_id) {
m_impl->correlation_id = correlation_id;
}
bool BasicMessage::CorrelationIdIsSet() const {
return m_impl->correlation_id.is_initialized();
}
void BasicMessage::CorrelationIdClear() { m_impl->correlation_id.reset(); }
const std::string& BasicMessage::ReplyTo() const {
if (ReplyToIsSet()) {
return m_impl->reply_to.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::ReplyTo(const std::string& reply_to) {
m_impl->reply_to = reply_to;
}
bool BasicMessage::ReplyToIsSet() const {
return m_impl->reply_to.is_initialized();
}
void BasicMessage::ReplyToClear() { m_impl->reply_to.reset(); }
const std::string& BasicMessage::Expiration() const {
if (ExpirationIsSet()) {
return m_impl->expiration.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::Expiration(const std::string& expiration) {
m_impl->expiration = expiration;
}
bool BasicMessage::ExpirationIsSet() const {
return m_impl->expiration.is_initialized();
}
void BasicMessage::ExpirationClear() { m_impl->expiration.reset(); }
const std::string& BasicMessage::MessageId() const {
if (MessageIdIsSet()) {
return m_impl->message_id.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::MessageId(const std::string& message_id) {
m_impl->message_id = message_id;
}
bool BasicMessage::MessageIdIsSet() const {
return m_impl->message_id.is_initialized();
}
void BasicMessage::MessageIdClear() { m_impl->message_id.reset(); }
boost::uint64_t BasicMessage::Timestamp() const {
return m_impl->timestamp.value_or(0);
}
void BasicMessage::Timestamp(boost::uint64_t timestamp) {
m_impl->timestamp = timestamp;
}
bool BasicMessage::TimestampIsSet() const {
return m_impl->timestamp.is_initialized();
}
void BasicMessage::TimestampClear() { m_impl->timestamp.reset(); }
const std::string& BasicMessage::Type() const {
if (TypeIsSet()) {
return m_impl->type.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::Type(const std::string& type) { m_impl->type = type; }
bool BasicMessage::TypeIsSet() const { return m_impl->type.is_initialized(); }
void BasicMessage::TypeClear() { m_impl->type.reset(); }
const std::string& BasicMessage::UserId() const {
if (UserIdIsSet()) {
return m_impl->user_id.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::UserId(const std::string& user_id) {
m_impl->user_id = user_id;
}
bool BasicMessage::UserIdIsSet() const {
return m_impl->user_id.is_initialized();
}
void BasicMessage::UserIdClear() { m_impl->user_id.reset(); }
const std::string& BasicMessage::AppId() const {
if (AppIdIsSet()) {
return m_impl->app_id.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::AppId(const std::string& app_id) { m_impl->app_id = app_id; }
bool BasicMessage::AppIdIsSet() const {
return m_impl->app_id.is_initialized();
}
void BasicMessage::AppIdClear() { m_impl->app_id.reset(); }
const std::string& BasicMessage::ClusterId() const {
if (ClusterIdIsSet()) {
return m_impl->cluster_id.get();
}
static const std::string empty;
return empty;
}
void BasicMessage::ClusterId(const std::string& cluster_id) {
m_impl->cluster_id = cluster_id;
}
bool BasicMessage::ClusterIdIsSet() const {
return m_impl->cluster_id.is_initialized();
}
void BasicMessage::ClusterIdClear() { m_impl->cluster_id.reset(); }
Table& BasicMessage::HeaderTable() {
if (!HeaderTableIsSet()) {
m_impl->header_table = Table();
}
return m_impl->header_table.get();
}
const Table& BasicMessage::HeaderTable() const {
if (HeaderTableIsSet()) {
return m_impl->header_table.get();
}
static const Table empty;
return empty;
}
void BasicMessage::HeaderTable(const Table& header_table) {
m_impl->header_table = header_table;
}
bool BasicMessage::HeaderTableIsSet() const {
return m_impl->header_table.is_initialized();
}
void BasicMessage::HeaderTableClear() { m_impl->header_table.reset(); }
} // namespace AmqpClient

1086
src/Channel.cpp Normal file

File diff suppressed because it is too large Load Diff

571
src/ChannelImpl.cpp Normal file
View File

@@ -0,0 +1,571 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#ifdef _WIN32
#define NOMINMAX
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Winsock2.h>
#else
#include <sys/time.h>
#include <sys/types.h>
#endif
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/array.hpp>
#include "SimpleAmqpClient/AmqpException.h"
#include "SimpleAmqpClient/AmqpLibraryException.h"
#include "SimpleAmqpClient/AmqpResponseLibraryException.h"
#include "SimpleAmqpClient/ChannelImpl.h"
#include "SimpleAmqpClient/ConnectionClosedException.h"
#include "SimpleAmqpClient/ConsumerTagNotFoundException.h"
#include "SimpleAmqpClient/TableImpl.h"
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <string.h>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#define BROKER_HEARTBEAT 0
namespace AmqpClient {
namespace {
std::string BytesToString(amqp_bytes_t bytes) {
return std::string(reinterpret_cast<char *>(bytes.bytes), bytes.len);
}
void SetMessageProperties(BasicMessage &mes,
const amqp_basic_properties_t &props) {
if (0 != (props._flags & AMQP_BASIC_CONTENT_TYPE_FLAG)) {
mes.ContentType(BytesToString(props.content_type));
}
if (0 != (props._flags & AMQP_BASIC_CONTENT_ENCODING_FLAG)) {
mes.ContentEncoding(BytesToString(props.content_encoding));
}
if (0 != (props._flags & AMQP_BASIC_DELIVERY_MODE_FLAG)) {
mes.DeliveryMode(
static_cast<BasicMessage::delivery_mode_t>(props.delivery_mode));
}
if (0 != (props._flags & AMQP_BASIC_PRIORITY_FLAG)) {
mes.Priority(props.priority);
}
if (0 != (props._flags & AMQP_BASIC_CORRELATION_ID_FLAG)) {
mes.CorrelationId(BytesToString(props.correlation_id));
}
if (0 != (props._flags & AMQP_BASIC_REPLY_TO_FLAG)) {
mes.ReplyTo(BytesToString(props.reply_to));
}
if (0 != (props._flags & AMQP_BASIC_EXPIRATION_FLAG)) {
mes.Expiration(BytesToString(props.expiration));
}
if (0 != (props._flags & AMQP_BASIC_MESSAGE_ID_FLAG)) {
mes.MessageId(BytesToString(props.message_id));
}
if (0 != (props._flags & AMQP_BASIC_TIMESTAMP_FLAG)) {
mes.Timestamp(props.timestamp);
}
if (0 != (props._flags & AMQP_BASIC_TYPE_FLAG)) {
mes.Type(BytesToString(props.type));
}
if (0 != (props._flags & AMQP_BASIC_USER_ID_FLAG)) {
mes.UserId(BytesToString(props.user_id));
}
if (0 != (props._flags & AMQP_BASIC_APP_ID_FLAG)) {
mes.AppId(BytesToString(props.app_id));
}
if (0 != (props._flags & AMQP_BASIC_CLUSTER_ID_FLAG)) {
mes.ClusterId(BytesToString(props.cluster_id));
}
if (0 != (props._flags & AMQP_BASIC_HEADERS_FLAG)) {
mes.HeaderTable(Detail::TableValueImpl::CreateTable(props.headers));
}
}
} // namespace
Channel::ChannelImpl::ChannelImpl()
: m_last_used_channel(0), m_is_connected(false) {
m_channels.push_back(CS_Used);
}
Channel::ChannelImpl::~ChannelImpl() {}
void Channel::ChannelImpl::DoLogin(const std::string &username,
const std::string &password,
const std::string &vhost, int frame_max,
bool sasl_external) {
amqp_table_entry_t capabilties[1];
amqp_table_entry_t capability_entry;
amqp_table_t client_properties;
capabilties[0].key = amqp_cstring_bytes("consumer_cancel_notify");
capabilties[0].value.kind = AMQP_FIELD_KIND_BOOLEAN;
capabilties[0].value.value.boolean = 1;
capability_entry.key = amqp_cstring_bytes("capabilities");
capability_entry.value.kind = AMQP_FIELD_KIND_TABLE;
capability_entry.value.value.table.num_entries =
sizeof(capabilties) / sizeof(amqp_table_entry_t);
capability_entry.value.value.table.entries = capabilties;
client_properties.num_entries = 1;
client_properties.entries = &capability_entry;
if (sasl_external) {
CheckRpcReply(0, amqp_login_with_properties(
m_connection, vhost.c_str(), 0, frame_max,
BROKER_HEARTBEAT, &client_properties,
AMQP_SASL_METHOD_EXTERNAL, username.c_str()));
} else {
CheckRpcReply(
0, amqp_login_with_properties(m_connection, vhost.c_str(), 0, frame_max,
BROKER_HEARTBEAT, &client_properties,
AMQP_SASL_METHOD_PLAIN, username.c_str(),
password.c_str()));
}
m_brokerVersion = ComputeBrokerVersion(m_connection);
}
amqp_channel_t Channel::ChannelImpl::GetNextChannelId() {
channel_state_list_t::iterator unused_channel =
std::find(m_channels.begin(), m_channels.end(), CS_Closed);
if (m_channels.end() == unused_channel) {
int max_channels = amqp_get_channel_max(m_connection);
if (0 == max_channels) {
max_channels = std::numeric_limits<uint16_t>::max();
}
if (static_cast<size_t>(max_channels) < m_channels.size()) {
throw std::runtime_error("Too many channels open");
}
m_channels.push_back(CS_Closed);
unused_channel = m_channels.end() - 1;
}
return unused_channel - m_channels.begin();
}
amqp_channel_t Channel::ChannelImpl::CreateNewChannel() {
amqp_channel_t new_channel = GetNextChannelId();
static const boost::array<boost::uint32_t, 1> OPEN_OK = {
{AMQP_CHANNEL_OPEN_OK_METHOD}};
amqp_channel_open_t channel_open = {};
DoRpcOnChannel<boost::array<boost::uint32_t, 1> >(
new_channel, AMQP_CHANNEL_OPEN_METHOD, &channel_open, OPEN_OK);
static const boost::array<boost::uint32_t, 1> CONFIRM_OK = {
{AMQP_CONFIRM_SELECT_OK_METHOD}};
amqp_confirm_select_t confirm_select = {};
DoRpcOnChannel<boost::array<boost::uint32_t, 1> >(
new_channel, AMQP_CONFIRM_SELECT_METHOD, &confirm_select, CONFIRM_OK);
m_channels.at(new_channel) = CS_Open;
return new_channel;
}
amqp_channel_t Channel::ChannelImpl::GetChannel() {
if (CS_Open == m_channels.at(m_last_used_channel)) {
m_channels[m_last_used_channel] = CS_Used;
return m_last_used_channel;
}
channel_state_list_t::iterator it =
std::find(m_channels.begin(), m_channels.end(), CS_Open);
if (m_channels.end() == it) {
amqp_channel_t new_channel = CreateNewChannel();
m_channels.at(new_channel) = CS_Used;
return new_channel;
}
*it = CS_Used;
return it - m_channels.begin();
}
void Channel::ChannelImpl::ReturnChannel(amqp_channel_t channel) {
m_channels.at(channel) = CS_Open;
m_last_used_channel = channel;
}
bool Channel::ChannelImpl::IsChannelOpen(amqp_channel_t channel) {
return CS_Closed != m_channels.at(channel);
}
void Channel::ChannelImpl::FinishCloseChannel(amqp_channel_t channel) {
m_channels.at(channel) = CS_Closed;
amqp_channel_close_ok_t close_ok;
CheckForError(amqp_send_method(m_connection, channel,
AMQP_CHANNEL_CLOSE_OK_METHOD, &close_ok));
}
void Channel::ChannelImpl::FinishCloseConnection() {
SetIsConnected(false);
amqp_connection_close_ok_t close_ok;
amqp_send_method(m_connection, 0, AMQP_CONNECTION_CLOSE_OK_METHOD, &close_ok);
}
void Channel::ChannelImpl::CheckRpcReply(amqp_channel_t channel,
const amqp_rpc_reply_t &reply) {
switch (reply.reply_type) {
case AMQP_RESPONSE_NORMAL:
return;
break;
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
// If we're getting this likely is the socket is already closed
throw AmqpResponseLibraryException::CreateException(reply, "");
break;
case AMQP_RESPONSE_SERVER_EXCEPTION:
if (reply.reply.id == AMQP_CHANNEL_CLOSE_METHOD) {
FinishCloseChannel(channel);
} else if (reply.reply.id == AMQP_CONNECTION_CLOSE_METHOD) {
FinishCloseConnection();
}
AmqpException::Throw(reply);
break;
default:
AmqpException::Throw(reply);
}
}
void Channel::ChannelImpl::CheckForError(int ret) {
if (ret < 0) {
throw AmqpLibraryException::CreateException(ret);
}
}
MessageReturnedException Channel::ChannelImpl::CreateMessageReturnedException(
amqp_basic_return_t &return_method, amqp_channel_t channel) {
const int reply_code = return_method.reply_code;
const std::string reply_text((char *)return_method.reply_text.bytes,
return_method.reply_text.len);
const std::string exchange((char *)return_method.exchange.bytes,
return_method.exchange.len);
const std::string routing_key((char *)return_method.routing_key.bytes,
return_method.routing_key.len);
BasicMessage::ptr_t content = ReadContent(channel);
return MessageReturnedException(content, reply_code, reply_text, exchange,
routing_key);
}
BasicMessage::ptr_t Channel::ChannelImpl::ReadContent(amqp_channel_t channel) {
amqp_frame_t frame;
GetNextFrameOnChannel(channel, frame);
if (frame.frame_type != AMQP_FRAME_HEADER) {
// TODO: We should connection.close here
throw std::runtime_error(
"Channel::BasicConsumeMessage: received unexpected frame type (was "
"expected AMQP_FRAME_HEADER)");
}
// The memory for this is allocated in a pool associated with the connection
// The BasicMessage constructor does a deep copy of the properties structure
amqp_basic_properties_t *properties =
reinterpret_cast<amqp_basic_properties_t *>(
frame.payload.properties.decoded);
// size_t could possibly be 32-bit, body_size is always 64-bit
assert(frame.payload.properties.body_size <
static_cast<uint64_t>(std::numeric_limits<size_t>::max()));
size_t body_size = static_cast<size_t>(frame.payload.properties.body_size);
size_t received_size = 0;
BasicMessage::ptr_t message = BasicMessage::Create();
message->Body().reserve(body_size);
// frame #3 and up:
while (received_size < body_size) {
GetNextFrameOnChannel(channel, frame);
if (frame.frame_type != AMQP_FRAME_BODY)
// TODO: we should connection.close here
throw std::runtime_error(
"Channel::BasicConsumeMessage: received unexpected frame type (was "
"expecting AMQP_FRAME_BODY)");
message->Body().append(
reinterpret_cast<char *>(frame.payload.body_fragment.bytes),
frame.payload.body_fragment.len);
received_size += frame.payload.body_fragment.len;
}
SetMessageProperties(*message, *properties);
return message;
}
void Channel::ChannelImpl::CheckFrameForClose(amqp_frame_t &frame,
amqp_channel_t channel) {
if (frame.frame_type == AMQP_FRAME_METHOD) {
switch (frame.payload.method.id) {
case AMQP_CHANNEL_CLOSE_METHOD:
FinishCloseChannel(channel);
AmqpException::Throw(*reinterpret_cast<amqp_channel_close_t *>(
frame.payload.method.decoded));
break;
case AMQP_CONNECTION_CLOSE_METHOD:
FinishCloseConnection();
AmqpException::Throw(*reinterpret_cast<amqp_connection_close_t *>(
frame.payload.method.decoded));
break;
}
}
}
void Channel::ChannelImpl::AddConsumer(const std::string &consumer_tag,
amqp_channel_t channel) {
m_consumer_channel_map.insert(std::make_pair(consumer_tag, channel));
}
amqp_channel_t Channel::ChannelImpl::RemoveConsumer(
const std::string &consumer_tag) {
std::map<std::string, amqp_channel_t>::iterator it =
m_consumer_channel_map.find(consumer_tag);
if (it == m_consumer_channel_map.end()) {
throw ConsumerTagNotFoundException();
}
amqp_channel_t result = it->second;
m_consumer_channel_map.erase(it);
return result;
}
amqp_channel_t Channel::ChannelImpl::GetConsumerChannel(
const std::string &consumer_tag) {
std::map<std::string, amqp_channel_t>::const_iterator it =
m_consumer_channel_map.find(consumer_tag);
if (it == m_consumer_channel_map.end()) {
throw ConsumerTagNotFoundException();
}
return it->second;
}
std::vector<amqp_channel_t> Channel::ChannelImpl::GetAllConsumerChannels()
const {
std::vector<amqp_channel_t> ret;
for (consumer_map_t::const_iterator it = m_consumer_channel_map.begin();
it != m_consumer_channel_map.end(); ++it) {
ret.push_back(it->second);
}
return ret;
}
bool Channel::ChannelImpl::CheckForQueuedMessageOnChannel(
amqp_channel_t channel) const {
frame_queue_t::const_iterator it =
std::find_if(m_frame_queue.begin(), m_frame_queue.end(),
boost::bind(&Channel::ChannelImpl::is_method_on_channel, _1,
AMQP_BASIC_DELIVER_METHOD, channel));
if (it == m_frame_queue.end()) {
return false;
}
it = std::find_if(
it + 1, m_frame_queue.end(),
boost::bind(&Channel::ChannelImpl::is_on_channel, _1, channel));
if (it == m_frame_queue.end()) {
return false;
}
if (it->frame_type != AMQP_FRAME_HEADER) {
throw std::runtime_error("Protocol error");
}
uint64_t body_length = it->payload.properties.body_size;
uint64_t body_received = 0;
while (body_received < body_length) {
it = std::find_if(
it + 1, m_frame_queue.end(),
boost::bind(&Channel::ChannelImpl::is_on_channel, _1, channel));
if (it == m_frame_queue.end()) {
return false;
}
if (it->frame_type != AMQP_FRAME_BODY) {
throw std::runtime_error("Protocol error");
}
body_received += it->payload.body_fragment.len;
}
return true;
}
void Channel::ChannelImpl::AddToFrameQueue(const amqp_frame_t &frame) {
m_frame_queue.push_back(frame);
if (CheckForQueuedMessageOnChannel(frame.channel)) {
boost::array<amqp_channel_t, 1> channel = {{frame.channel}};
Envelope::ptr_t envelope;
if (!ConsumeMessageOnChannelInner(channel, envelope, -1)) {
throw std::logic_error(
"ConsumeMessageOnChannelInner returned false unexpectedly");
}
m_delivered_messages.push_back(envelope);
}
}
bool Channel::ChannelImpl::GetNextFrameFromBroker(
amqp_frame_t &frame, boost::chrono::microseconds timeout) {
struct timeval *tvp = NULL;
struct timeval tv_timeout;
memset(&tv_timeout, 0, sizeof(tv_timeout));
if (timeout != boost::chrono::microseconds::max()) {
// boost::chrono::seconds.count() returns boost::int_atleast64_t,
// long can be 32 or 64 bit depending on the platform/arch
// unless the timeout is something absurd cast to long will be ok, but
// lets guard against the case where someone does something silly
assert(
boost::chrono::duration_cast<boost::chrono::seconds>(timeout).count() <
static_cast<boost::chrono::seconds::rep>(
std::numeric_limits<long>::max()));
tv_timeout.tv_sec = static_cast<long>(
boost::chrono::duration_cast<boost::chrono::seconds>(timeout).count());
tv_timeout.tv_usec = static_cast<long>(
(timeout - boost::chrono::seconds(tv_timeout.tv_sec)).count());
tvp = &tv_timeout;
}
int ret = amqp_simple_wait_frame_noblock(m_connection, &frame, tvp);
if (AMQP_STATUS_TIMEOUT == ret) {
return false;
}
CheckForError(ret);
return true;
}
bool Channel::ChannelImpl::GetNextFrameOnChannel(
amqp_channel_t channel, amqp_frame_t &frame,
boost::chrono::microseconds timeout) {
frame_queue_t::iterator it = std::find_if(
m_frame_queue.begin(), m_frame_queue.end(),
boost::bind(&Channel::ChannelImpl::is_on_channel, _1, channel));
if (m_frame_queue.end() != it) {
frame = *it;
m_frame_queue.erase(it);
if (AMQP_FRAME_METHOD == frame.frame_type &&
AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id) {
FinishCloseChannel(channel);
AmqpException::Throw(*reinterpret_cast<amqp_channel_close_t *>(
frame.payload.method.decoded));
}
return true;
}
boost::array<amqp_channel_t, 1> channels = {{channel}};
return GetNextFrameFromBrokerOnChannel(channels, frame, timeout);
}
void Channel::ChannelImpl::MaybeReleaseBuffersOnChannel(
amqp_channel_t channel) {
if (m_frame_queue.end() ==
std::find_if(
m_frame_queue.begin(), m_frame_queue.end(),
boost::bind(&Channel::ChannelImpl::is_on_channel, _1, channel))) {
amqp_maybe_release_buffers_on_channel(m_connection, channel);
}
}
void Channel::ChannelImpl::CheckIsConnected() {
if (!m_is_connected) {
throw ConnectionClosedException();
}
}
namespace {
bool bytesEqual(amqp_bytes_t r, amqp_bytes_t l) {
if (r.len == l.len) {
if (0 == memcmp(r.bytes, l.bytes, r.len)) {
return true;
}
}
return false;
}
} // namespace
boost::uint32_t Channel::ChannelImpl::ComputeBrokerVersion(
amqp_connection_state_t state) {
const amqp_table_t *properties = amqp_get_server_properties(state);
const amqp_bytes_t version = amqp_cstring_bytes("version");
amqp_table_entry_t *version_entry = NULL;
for (int i = 0; i < properties->num_entries; ++i) {
if (bytesEqual(properties->entries[i].key, version)) {
version_entry = &properties->entries[i];
break;
}
}
if (NULL == version_entry) {
return 0;
}
std::string version_string(
static_cast<char *>(version_entry->value.value.bytes.bytes),
version_entry->value.value.bytes.len);
std::vector<std::string> version_components;
boost::split(version_components, version_string, boost::is_any_of("."));
if (version_components.size() != 3) {
return 0;
}
boost::uint32_t version_major =
boost::lexical_cast<boost::uint32_t>(version_components[0]);
boost::uint32_t version_minor =
boost::lexical_cast<boost::uint32_t>(version_components[1]);
boost::uint32_t version_patch =
boost::lexical_cast<boost::uint32_t>(version_components[2]);
return (version_major & 0xFF) << 16 | (version_minor & 0xFF) << 8 |
(version_patch & 0xFF);
}
} // namespace AmqpClient

48
src/Envelope.cpp Normal file
View File

@@ -0,0 +1,48 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "SimpleAmqpClient/Envelope.h"
namespace AmqpClient {
Envelope::Envelope(const BasicMessage::ptr_t message,
const std::string &consumer_tag,
const boost::uint64_t delivery_tag,
const std::string &exchange, bool redelivered,
const std::string &routing_key,
const boost::uint16_t delivery_channel)
: m_message(message),
m_consumerTag(consumer_tag),
m_deliveryTag(delivery_tag),
m_exchange(exchange),
m_redelivered(redelivered),
m_routingKey(routing_key),
m_deliveryChannel(delivery_channel) {}
Envelope::~Envelope() {}
} // namespace AmqpClient

View File

@@ -0,0 +1,49 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "SimpleAmqpClient/MessageReturnedException.h"
#include <boost/lexical_cast.hpp>
namespace AmqpClient {
MessageReturnedException::MessageReturnedException(
BasicMessage::ptr_t message, boost::uint32_t reply_code,
const std::string &reply_text, const std::string &exchange,
const std::string &routing_key) throw()
: std::runtime_error(
std::string("Message returned. Reply code: ")
.append(boost::lexical_cast<std::string>(reply_code))
.append(" ")
.append(reply_text)),
m_message(message),
m_reply_code(reply_code),
m_reply_text(reply_text),
m_exchange(exchange),
m_routing_key(routing_key) {}
} // namespace AmqpClient

View File

@@ -0,0 +1,572 @@
#ifndef SIMPLEAMQPCLIENT_AMQPEXCEPTION_H
#define SIMPLEAMQPCLIENT_AMQPEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <stdexcept>
#include <string>
#include "Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif
/// @file SimpleAmqpClient/AmqpException.h
/// Defines AmqpClient::AmqpException
struct amqp_rpc_reply_t_;
struct amqp_channel_close_t_;
struct amqp_connection_close_t_;
namespace AmqpClient {
/**
* Base-class for exceptions from the broker
*/
class SIMPLEAMQPCLIENT_EXPORT AmqpException : public std::runtime_error {
public:
/**
* Construct an AmqpException from an amqp_rpc_reply_t and throw it
*
* @param [in] reply the reply from the RPC call
*/
static void Throw(const amqp_rpc_reply_t_ &reply);
/**
* Construct an AmqpException from an amqp_channel_close_t and throw it
*
* @param [in] reply the channel.close AMQP method
*/
static void Throw(const amqp_channel_close_t_ &reply);
/**
* Construct an AmqpException from an amqp_connection_close_t and throw it
*
* @param [in] reply the connection.close AMQP method
*/
static void Throw(const amqp_connection_close_t_ &reply);
/**
* Constructor
*
* @param [in] what the error string to pass to the std::runtime_error
* base-class
* @param [in] reply_text the error message (if any) from the broker
* @param [in] class_id the class id of the method that caused the error
* @param [in] method_id the method id of the method that caused the error
*/
explicit AmqpException(const std::string &what, const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw();
/**
* Destructor
*/
virtual ~AmqpException() throw() {}
/**
* Query to see if the error is soft
*
* A soft error is generally recoverable, meaning the Channel
* object that it originated from can be reused. If its a hard error
* the Channel object is closed and will throw exceptions if its used
* again.
*
* @returns `true` if its a soft error, `false` otherwise
*/
virtual bool is_soft_error() const throw() = 0;
/**
* Get the error code returned from the broker
*
* @returns the error code
*/
virtual boost::uint16_t reply_code() const throw() = 0;
/**
* Get the class id of the method that caused the error
*
* @returns the class id
*/
virtual boost::uint16_t class_id() const throw() { return m_class_id; }
/**
* Get the method id of the method that caused the error
*
* @returns the method id
*/
virtual boost::uint16_t method_id() const throw() { return m_method_id; }
/**
* Get the error string returned from the broker
*
* @returns the error string from the broker
*/
virtual std::string reply_text() const throw() { return m_reply_text; }
protected:
/** @cond INTERNAL */
std::string m_reply_text;
boost::uint16_t m_class_id;
boost::uint16_t m_method_id;
/** @endcond */
};
/**
* Base class for exceptions that result in the connection being closed
*
* If the program receives this kind of exception, the Channel object that
* threw the exception is now disconnected from the broker and any further
* use of the channel will cause further errors
*/
class SIMPLEAMQPCLIENT_EXPORT ConnectionException : public AmqpException {
public:
/**
* Constructor
*
* @param [in] what
* @param [in] reply_text
* @param [in] class_id
* @param [in] method_id
*/
explicit ConnectionException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: AmqpException(what, reply_text, class_id, method_id) {}
virtual bool is_soft_error() const throw() { return false; }
};
/**
* Base class for exceptions that are soft errors
*
* If the program receives this kind of exception, the Channel object
* that threw the exception will continue to be functional.
*/
class SIMPLEAMQPCLIENT_EXPORT ChannelException : public AmqpException {
public:
/**
* Constructor
*
* @param [in] what
* @param [in] reply_text
* @param [in] class_id
* @param [in] method_id
*/
explicit ChannelException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: AmqpException(what, reply_text, class_id, method_id) {}
virtual bool is_soft_error() const throw() { return true; }
};
/**
* The connection was force closed by an operator
*/
class SIMPLEAMQPCLIENT_EXPORT ConnectionForcedException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit ConnectionForcedException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client tried to work with an invalid virtual host
*/
class SIMPLEAMQPCLIENT_EXPORT InvalidPathException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit InvalidPathException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The broker received a malformed frame.
*
* This likely indicates a bug in SimpleAmqpClient
*/
class SIMPLEAMQPCLIENT_EXPORT FrameErrorException : public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit FrameErrorException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client sent a frame with bad values (out of range)
*
* This likely indicates a bug in SimpleAmqpClient
*/
class SIMPLEAMQPCLIENT_EXPORT SyntaxErrorException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit SyntaxErrorException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client sent an invalid sequence of frames
*
* This likely indicates a bug in SimpleAmqpClient
*/
class SIMPLEAMQPCLIENT_EXPORT CommandInvalidException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit CommandInvalidException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client tried to use a Channel that isn't open
*
* This likely indicates a bug in SimpleAmqpClient
*/
class SIMPLEAMQPCLIENT_EXPORT ChannelErrorException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit ChannelErrorException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client sent a frame the broker wasn't expecting
*
* This likely indicates a bug in SimpleAmqpClient
*/
class SIMPLEAMQPCLIENT_EXPORT UnexpectedFrameException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit UnexpectedFrameException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The broker could not complete the request because of lack of resources
*
* Likely due to clients creating too many of something
*/
class SIMPLEAMQPCLIENT_EXPORT ResourceErrorException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit ResourceErrorException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client tried to work with some entity in a manner that is prohibited by
* the server.
*
* Like due to security settings or by some other criteria.
*/
class SIMPLEAMQPCLIENT_EXPORT NotAllowedException : public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit NotAllowedException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client tried to use a method that isn't implemented by the broker
*/
class SIMPLEAMQPCLIENT_EXPORT NotImplementedException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/**
* Constructor
*/
explicit NotImplementedException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* An internal error occurred on the broker
*/
class SIMPLEAMQPCLIENT_EXPORT InternalErrorException
: public ConnectionException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit InternalErrorException(const std::string &what,
const std::string &reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ConnectionException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client tried to send content that was too large
*
* Try sending the content at a later time
*/
class SIMPLEAMQPCLIENT_EXPORT ContentTooLargeException
: public ChannelException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit ContentTooLargeException(const std::string &what,
const std::string reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ChannelException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* AMQP_NO_ROUTE error, occurs when the IP stack cannot route our data to the
* broker.
*/
class SIMPLEAMQPCLIENT_EXPORT NoRouteException : public ChannelException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit NoRouteException(const std::string &what,
const std::string reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ChannelException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* AMQP_NO_CONSUMERS error. When the exchange cannot deliver to a consumer when
* the immediate flag is set. As a result of pending data on the queue or the
* absence of any consumers of the queue.
*/
class SIMPLEAMQPCLIENT_EXPORT NoConsumersException : public ChannelException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit NoConsumersException(const std::string &what,
const std::string reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ChannelException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* AMQP_ACCESS_REFUSED error. The client attempted to work with a server entity
* to which it has no access due to security settings.
*/
class SIMPLEAMQPCLIENT_EXPORT AccessRefusedException : public ChannelException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit AccessRefusedException(const std::string &what,
const std::string reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ChannelException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* AMQP_NOT_FOUND error. The client attempted to work with a server entity that
* does not exist.
*/
class SIMPLEAMQPCLIENT_EXPORT NotFoundException : public ChannelException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit NotFoundException(const std::string &what,
const std::string reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ChannelException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client attempted to work with a server entity to which it has no access
* because another client is working with it.
*/
class SIMPLEAMQPCLIENT_EXPORT ResourceLockedException
: public ChannelException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit ResourceLockedException(const std::string &what,
const std::string reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ChannelException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
/**
* The client requested a method that was not allowed because some precondition
* failed.
*/
class SIMPLEAMQPCLIENT_EXPORT PreconditionFailedException
: public ChannelException {
public:
/** reply code */
static const boost::uint16_t REPLY_CODE;
/** Constructor */
explicit PreconditionFailedException(const std::string &what,
const std::string reply_text,
boost::uint16_t class_id,
boost::uint16_t method_id) throw()
: ChannelException(what, reply_text, class_id, method_id) {}
virtual boost::uint16_t reply_code() const throw() { return REPLY_CODE; }
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_AMQPEXCEPTION_H

View File

@@ -0,0 +1,84 @@
#ifndef SIMPLEAMQPCLIENT_AMQPLIBRARYEXCEPTION_H
#define SIMPLEAMQPCLIENT_AMQPLIBRARYEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2014 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <stdexcept>
#include <string>
#include "SimpleAmqpClient/Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif
/// @file SimpleAmqpClient/AmqpLibraryException.h
/// Defines SimpleAmqpClient::AmqpLibraryException
struct amqp_rpc_reply_t_;
namespace AmqpClient {
/**
* Errors arising from incorrect usage of this library APIs
*/
class SIMPLEAMQPCLIENT_EXPORT AmqpLibraryException : public std::runtime_error {
public:
/**
* Factory-construct with an error code
*/
static AmqpLibraryException CreateException(int error_code);
/**
* Factory-construct with an error code and a string context
*/
static AmqpLibraryException CreateException(int error_code,
const std::string &context);
/**
* Error code getter
*/
int ErrorCode() const { return m_errorCode; }
protected:
/** @cond INTERNAL */
explicit AmqpLibraryException(const std::string &message,
int error_code) throw();
/** @endcond@ */
private:
int m_errorCode;
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_AMQPLIBRARYEXCEPTION_H

View File

@@ -0,0 +1,70 @@
#ifndef SIMPLEAMQPCLIENT_AMQPRESPONSELIBRARYEXCEPTION_H
#define SIMPLEAMQPCLIENT_AMQPRESPONSELIBRARYEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <stdexcept>
#include <string>
#include "SimpleAmqpClient/Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif
/// @file SimpleAmqpClient/AmqpResponseLibraryException.h
/// Defines AmqpClient::AmqpResponseLibraryException
struct amqp_rpc_reply_t_;
namespace AmqpClient {
/**
* `AMQP_RESPONSE_LIBRARY_EXCEPTION`: an RPC response returned a library error
*/
class SIMPLEAMQPCLIENT_EXPORT AmqpResponseLibraryException
: public std::runtime_error {
public:
/// Factory-construct from RPC reply and a string context
static AmqpResponseLibraryException CreateException(
const amqp_rpc_reply_t_ &reply, const std::string &context);
protected:
/// Construct an instance with exact message
explicit AmqpResponseLibraryException(const std::string &message) throw();
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_AMQPRESPONSELIBRARYEXCEPTION_H

View File

@@ -0,0 +1,46 @@
#ifndef SIMPLEAMQPCLIENT_BADURIEXCEPTION_H
#define SIMPLEAMQPCLIENT_BADURIEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <stdexcept>
#include "SimpleAmqpClient/Util.h"
/// @file SimpleAmqpClient/BadUriException.h
/// Defines AmqpClient::BadUriException
namespace AmqpClient {
/** "URI is malformed" exception */
class SIMPLEAMQPCLIENT_EXPORT BadUriException : public std::runtime_error {
public:
explicit BadUriException() : std::runtime_error("URI is malformed") {}
};
} // namespace AmqpClient
#endif // SIMPLEAMQPCLIENT_BADURIEXCEPTION_H

View File

@@ -0,0 +1,354 @@
#ifndef SIMPLEAMQPCLIENT_BASICMESSAGE_H
#define SIMPLEAMQPCLIENT_BASICMESSAGE_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <string>
#include "SimpleAmqpClient/Table.h"
#include "SimpleAmqpClient/Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4275 4251)
#endif
/// @file SimpleAmqpClient/BasicMessage.h
/// The AmqpClient::BasicMessage class is defined in this header file.
namespace AmqpClient {
/**
* An AMQP BasicMessage
*/
class SIMPLEAMQPCLIENT_EXPORT BasicMessage : boost::noncopyable {
public:
/// A shared pointer to BasicMessage
typedef boost::shared_ptr<BasicMessage> ptr_t;
/// With durable queues, messages can be requested to persist or not
enum delivery_mode_t {
dm_notset = 0,
dm_nonpersistent = 1,
dm_persistent = 2
};
/**
* Create a new empty BasicMessage object
*/
static ptr_t Create() { return boost::make_shared<BasicMessage>(); }
/**
* Create a new BasicMessage object with given body
*
* @param body the message body.
* @returns a new BasicMessage object
*/
static ptr_t Create(const std::string& body) {
return boost::make_shared<BasicMessage>(body);
}
/// Construct empty BasicMessage
BasicMessage();
/// Construct BasicMessage with given body
BasicMessage(const std::string& body);
public:
/**
* Destructor
*/
virtual ~BasicMessage();
/**
* Gets the message body as a std::string
*/
const std::string& Body() const;
std::string& Body();
/**
* Sets the message body as a std::string
*/
void Body(const std::string& body);
/**
* Gets the content type property
*/
const std::string& ContentType() const;
/**
* Sets the content type property
*/
void ContentType(const std::string& content_type);
/**
* Determines whether the content type property is set
*/
bool ContentTypeIsSet() const;
/**
* Unsets the content type property if it is set
*/
void ContentTypeClear();
/**
* Gets the content encoding property
*/
const std::string& ContentEncoding() const;
/**
* Sets the content encoding property
*/
void ContentEncoding(const std::string& content_encoding);
/**
* Determines whether the content encoding property is set
*/
bool ContentEncodingIsSet() const;
/**
* Unsets the content encoding property if it is set
*/
void ContentEncodingClear();
/**
* Gets the delivery mode property
*/
delivery_mode_t DeliveryMode() const;
/**
* Sets the delivery mode property
*/
void DeliveryMode(delivery_mode_t delivery_mode);
/**
* Determines whether the delivery mode property is set
*/
bool DeliveryModeIsSet() const;
/**
* Unsets the delivery mode property if it is set
*/
void DeliveryModeClear();
/**
* Gets the priority property
*/
boost::uint8_t Priority() const;
/**
* Sets the priority property
*/
void Priority(boost::uint8_t priority);
/**
* Determines whether the priority property is set
*/
bool PriorityIsSet() const;
/**
* Unsets the priority property if it is set
*/
void PriorityClear();
/**
* Gets the correlation id property
*/
const std::string& CorrelationId() const;
/**
* Sets the correlation id property
*/
void CorrelationId(const std::string& correlation_id);
/**
* Determines whether the correlation id property is set
*/
bool CorrelationIdIsSet() const;
/**
* Unsets the correlation id property
*/
void CorrelationIdClear();
/**
* Gets the reply to property
*/
const std::string& ReplyTo() const;
/**
* Sets the reply to property
*/
void ReplyTo(const std::string& reply_to);
/**
* Determines whether the reply to property is set
*/
bool ReplyToIsSet() const;
/**
* Unsets the reply to property
*/
void ReplyToClear();
/**
* Gets the expiration property
*/
const std::string& Expiration() const;
/**
* Sets the expiration property
*/
void Expiration(const std::string& expiration);
/**
* Determines whether the expiration property is set
*/
bool ExpirationIsSet() const;
/**
* Unsets the expiration property
*/
void ExpirationClear();
/**
* Gets the message id property
*/
const std::string& MessageId() const;
/**
* Sets the message id property
*/
void MessageId(const std::string& message_id);
/**
* Determines if the message id property is set
*/
bool MessageIdIsSet() const;
/**
* Unsets the message id property
*/
void MessageIdClear();
/**
* Gets the timestamp property
*/
boost::uint64_t Timestamp() const;
/**
* Sets the timestamp property
*/
void Timestamp(boost::uint64_t timestamp);
/**
* Determines whether the timestamp property is set
*/
bool TimestampIsSet() const;
/**
* Unsets the timestamp property
*/
void TimestampClear();
/**
* Gets the type property
*/
const std::string& Type() const;
/**
* Sets the type property
*/
void Type(const std::string& type);
/**
* Determines whether the type property is set
*/
bool TypeIsSet() const;
/**
* Unsets the type property
*/
void TypeClear();
/**
* Gets the user id property
*/
const std::string& UserId() const;
/**
* Sets the user id property
*/
void UserId(const std::string& user_id);
/**
* Determines whether the user id property is set
*/
bool UserIdIsSet() const;
/**
* Unsets the user id property
*/
void UserIdClear();
/**
* Gets the app id property
*/
const std::string& AppId() const;
/**
* Sets the app id property
*/
void AppId(const std::string& app_id);
/**
* Determines whether the app id property is set
*/
bool AppIdIsSet() const;
/**
* Unsets the app id property
*/
void AppIdClear();
/**
* Gets the cluster id property
*/
const std::string& ClusterId() const;
/**
* Sets the custer id property
*/
void ClusterId(const std::string& cluster_id);
/**
* Determines if the cluster id property is set
*/
bool ClusterIdIsSet() const;
/**
* Unsets the cluster id property
*/
void ClusterIdClear();
/**
* Gets the header table property
*/
Table& HeaderTable();
const Table& HeaderTable() const;
/**
* Sets the header table property
*/
void HeaderTable(const Table& header_table);
/**
* Is there a header table associated with the message
*/
bool HeaderTableIsSet() const;
/**
* Unsets the header table property
*/
void HeaderTableClear();
protected:
struct Impl;
/// PIMPL idiom
boost::scoped_ptr<Impl> m_impl;
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_BASICMESSAGE_H

View File

@@ -0,0 +1,26 @@
#ifndef SIMPLEAMQPCLIENT_BYTES_H
#define SIMPLEAMQPCLIENT_BYTES_H
#include <amqp.h>
#include <boost/utility/string_ref.hpp>
#include <string>
namespace AmqpClient {
amqp_bytes_t StringToBytes(const std::string& str) {
amqp_bytes_t ret;
ret.bytes = reinterpret_cast<void*>(const_cast<char*>(str.data()));
ret.len = str.length();
return ret;
}
amqp_bytes_t StringRefToBytes(boost::string_ref str) {
amqp_bytes_t ret;
ret.bytes = reinterpret_cast<void*>(const_cast<char*>(str.data()));
ret.len = str.length();
return ret;
}
} // namespace AmqpClient
#endif // SIMPLEAMQPCLIENT_BYTES_H

View File

@@ -0,0 +1,946 @@
#ifndef SIMPLEAMQPCLIENT_CHANNEL_H
#define SIMPLEAMQPCLIENT_CHANNEL_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <boost/make_shared.hpp>
#include <boost/optional.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include "SimpleAmqpClient/BasicMessage.h"
#include "SimpleAmqpClient/Envelope.h"
#include "SimpleAmqpClient/Table.h"
#include "SimpleAmqpClient/Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif
/// @file SimpleAmqpClient/Channel.h
/// The AmqpClient::Channel class is defined in this header file.
namespace AmqpClient {
/**
* A single channel multiplexed in an AMQP connection
*
* Represents a logical AMQP channel multiplexed over a connection
*/
class SIMPLEAMQPCLIENT_EXPORT Channel : boost::noncopyable {
public:
/// a `shared_ptr` to Channel
typedef boost::shared_ptr<Channel> ptr_t;
static const std::string
EXCHANGE_TYPE_DIRECT; ///< `"direct"` string constant
static const std::string
EXCHANGE_TYPE_FANOUT; ///< `"fanout"` string constant
static const std::string EXCHANGE_TYPE_TOPIC; ///< `"topic"` string constant
struct SIMPLEAMQPCLIENT_EXPORT OpenOpts {
/// Use username and password to authenticate with the broker.
struct SIMPLEAMQPCLIENT_EXPORT BasicAuth {
std::string username;
std::string password;
BasicAuth() {}
BasicAuth(const std::string &username, const std::string &password)
: username(username), password(password) {}
bool operator==(const BasicAuth &) const;
};
/// Use External SASL method to authenticate with the broker.
struct SIMPLEAMQPCLIENT_EXPORT ExternalSaslAuth {
std::string identity;
ExternalSaslAuth() {}
explicit ExternalSaslAuth(const std::string &identity)
: identity(identity) {}
bool operator==(const ExternalSaslAuth &) const;
};
/// Parameters
struct SIMPLEAMQPCLIENT_EXPORT TLSParams {
std::string client_key_path; ///< Path to client key.
std::string client_cert_path; ///< Path to client cert.
std::string ca_cert_path; ///< Path to CA cert.
bool verify_hostname; ///< Verify host matches certificate. Default: true
bool verify_peer; ///< Verify presented certificate. Default: true
TLSParams() : verify_hostname(true), verify_peer(true) {}
bool operator==(const TLSParams &) const;
};
std::string host; ///< Broker hostname. Required.
std::string vhost; ///< Virtualhost on the broker. Default '/', required.
int port; ///< Port to connect to, default is 5672.
int frame_max; ///< Max frame size in bytes. Default 128KB.
/// One of BasicAuth or ExternalSaslAuth is required.
boost::variant<BasicAuth, ExternalSaslAuth> auth;
/// Connect using TLS/SSL when set, otherwise use an unencrypted channel.
boost::optional<TLSParams> tls_params;
/**
* Create an OpenOpts struct from a URI.
*
* URIs look like amqp[s]://[username[:password]@]host[:port]/[vhost].
* Unspecified parts of the URL will take default values:
* - username: guest
* - password: guest
* - host: localhost
* - port: 5672 for 'amqp', and 5671 for 'amqps'
* - vhost: '/'
*
* NOTE: for TLS/SSL connections, additional configuration is required.
*/
static OpenOpts FromUri(const std::string &uri);
OpenOpts() : vhost("/"), port(5672), frame_max(131072) {}
bool operator==(const OpenOpts &) const;
};
/**
* Open a new channel to the broker.
*
* See documentation for \ref OpenOpts for details on what can be
* passed into this function.
*/
static ptr_t Open(const OpenOpts &opts);
/**
* Creates a new channel object
*
* Creates a new connection to an AMQP broker using the supplied parameters
* and opens a single channel for use
* @param host The hostname or IP address of the AMQP broker
* @param port The port to connect to the AMQP broker on
* @param username The username used to authenticate with the AMQP broker
* @param password The password corresponding to the username used to
* authenticate with the AMQP broker
* @param vhost The virtual host on the AMQP we should connect to
* @param frame_max Request that the server limit the maximum size of any
* frame to this value
* @return a new Channel object pointer
*/
SAC_DEPRECATED("Channel::Create is deprecated. Use Channel::Open.")
static ptr_t Create(const std::string &host = "127.0.0.1", int port = 5672,
const std::string &username = "guest",
const std::string &password = "guest",
const std::string &vhost = "/", int frame_max = 131072);
/**
* Creates a new channel object
* Creates a new connection to an AMQP broker using the supplied parameters
* and opens a single channel for use
* This channel uses the EXTERNAL SASL method for authentication to the broker
* @param host The hostname or IP address of the AMQP broker
* @param port The port to connect to the AMQP broker on
* @param identity The identity used to authenticate with the AMQP broker
* @param vhost The virtual host on the AMQP we should connect to
* @param channel_max Request that the server limit the number of channels
* for
* this connection to the specified parameter, a value of zero will use the
* broker-supplied value
* @param frame_max Request that the server limit the maximum size of any
* frame to this value
* @return a new Channel object pointer
*/
SAC_DEPRECATED(
"Channel::CreateSaslExternal is deprecated. Use Channel::Open.")
static ptr_t CreateSaslExternal(const std::string &host = "127.0.0.1",
int port = 5672,
const std::string &identity = "guest",
const std::string &vhost = "/",
int frame_max = 131072);
/**
* Creates a new channel object, using TLS
*
* Creates a new connection to an AMQP broker using the supplied parameters
* and opens a single channel for use
* @param path_to_ca_cert Path to CA certificate file
* @param host The hostname or IP address of the AMQP broker
* @param path_to_client_key Path to client key file
* @param path_to_client_cert Path to client certificate file
* @param port The port to connect to the AMQP broker on
* @param username The username used to authenticate with the AMQP broker
* @param password The password corresponding to the username used to
* authenticate with the AMQP broker
* @param vhost The virtual host on the AMQP we should connect to
* @param frame_max Request that the server limit the maximum size of any
* frame to this value
* @param verify_hostname Verify the hostname against the certificate when
* opening the SSL connection.
*
* @return a new Channel object pointer
*/
SAC_DEPRECATED("Channel::CreateSecure is deprecated. Use Channel::Open.")
static ptr_t CreateSecure(const std::string &path_to_ca_cert = "",
const std::string &host = "127.0.0.1",
const std::string &path_to_client_key = "",
const std::string &path_to_client_cert = "",
int port = 5671,
const std::string &username = "guest",
const std::string &password = "guest",
const std::string &vhost = "/",
int frame_max = 131072,
bool verify_hostname_and_peer = true);
/**
* Creates a new channel object
* Creates a new connection to an AMQP broker using the supplied parameters
* and opens a single channel for use
* @param path_to_ca_cert Path to ca certificate file
* @param host The hostname or IP address of the AMQP broker
* @param path_to_client_key Path to client key file
* @param path_to_client_cert Path to client certificate file
* @param port The port to connect to the AMQP broker on
* @param username The username used to authenticate with the AMQP broker
* @param password The password corresponding to the username used to
* authenticate with the AMQP broker
* @param vhost The virtual host on the AMQP we should connect to
* @param channel_max Request that the server limit the number of channels for
* this connection to the specified parameter, a value of zero will use the
* broker-supplied value
* @param frame_max Request that the server limit the maximum size of any
* frame to this value
* @param verify_host Verify the hostname against the certificate when
* opening the SSL connection.
* @param verify_peer Verify the certificate chain that is sent by the broker
* when opening the SSL connection.
*
* @return a new Channel object pointer
*/
SAC_DEPRECATED("Channel::CreateSecure is deprecated. Use Channel::Open.")
static ptr_t CreateSecure(const std::string &path_to_ca_cert,
const std::string &host,
const std::string &path_to_client_key,
const std::string &path_to_client_cert, int port,
const std::string &username,
const std::string &password,
const std::string &vhost, int frame_max,
bool verify_hostname, bool verify_peer);
/**
* Creates a new channel object
* Creates a new connection to an AMQP broker using the supplied parameters
* and opens a single channel for use
* This channel uses the EXTERNAL SASL method for authentication to the broker
* @param path_to_ca_cert Path to ca certificate file
* @param host The hostname or IP address of the AMQP broker
* @param path_to_client_key Path to client key file
* @param path_to_client_cert Path to client certificate file
* @param port The port to connect to the AMQP broker on
* @param identity The identity used to authenticate with the AMQP broker
* @param vhost The virtual host on the AMQP we should connect to
* @param channel_max Request that the server limit the number of channels for
* this connection to the specified parameter, a value of zero will use the
* broker-supplied value
* @param frame_max Request that the server limit the maximum size of any
* frame to this value
* @param verify_host Verify the hostname against the certificate when
* opening the SSL connection.
* @param verify_peer Verify the certificate chain that is sent by the broker
* when opening the SSL connection.
*
* @return a new Channel object pointer
*/
SAC_DEPRECATED(
"Channel::CreateSecureSaslExternal is deprecated. Use Channel::Open.")
static ptr_t CreateSecureSaslExternal(const std::string &path_to_ca_cert,
const std::string &host,
const std::string &path_to_client_key,
const std::string &path_to_client_cert,
int port, const std::string &identity,
const std::string &vhost, int frame_max,
bool verify_hostname, bool verify_peer);
/**
* Create a new Channel object from an AMQP URI
*
* @param uri a URI of the form:
* `amqp://[username:password@]{HOSTNAME}[:PORT][/VHOST]`
* @param frame_max requests that the broker limit the maximum size of
* any frame to this value
* @returns a new Channel object
*/
SAC_DEPRECATED("Channel::CreateFromUri is deprecated. Use Channel::Open.")
static ptr_t CreateFromUri(const std::string &uri, int frame_max = 131072);
/**
* Create a new Channel object from an AMQP URI, secured with SSL.
* If URI should start with amqps://
*
* @param uri a URI of the form:
* `amqp[s]://[username:password@]{HOSTNAME}[:PORT][/VHOST]`
* @param path_to_ca_cert Path to ca certificate file
* @param path_to_client_key Path to client key file
* @param path_to_client_cert Path to client certificate file
* @param verify_hostname Verify the hostname against the certificate when
* opening the SSL connection and the certificate chain that is sent by the
* broker.
* @param frame_max requests that the broker limit the maximum size of
* any frame to this value
* @returns a new Channel object
*/
SAC_DEPRECATED(
"Channel::CreateSecureFromUri is deprecated. Use Channel::Open.")
static ptr_t CreateSecureFromUri(const std::string &uri,
const std::string &path_to_ca_cert,
const std::string &path_to_client_key = "",
const std::string &path_to_client_cert = "",
bool verify_hostname_and_peer = true,
int frame_max = 131072);
private:
class ChannelImpl;
public:
explicit Channel(ChannelImpl *impl);
virtual ~Channel();
/**
* Exposes the underlying socket handle
* @returns file descriptor number associated with the connection socket
*
* @warning This function exposes an internal implementation detail
* of SimpleAmqpClient. Manipulating the socket descriptor will result in
* undefined behavior of the library. Additionally SimpleAmqpClient's use of
* the socket will change depending on what version of rabbitmq-c and
* SimpleAmqpClient are used. Test carefully before depending on any specific
* behavior.
*/
int GetSocketFD() const;
/**
* Checks to see if an exchange exists on the broker.
*
* @param exchange_name the name of the exchange to check for.
* @returns true if the exchange exists on the broker, false otherwise.
*/
bool CheckExchangeExists(boost::string_ref exchange_name);
/**
* Declares an exchange
*
* Creates an exchange on the AMQP broker if it does not already exist
* @param exchange_name the name of the exchange
* @param exchange_type the type of exchange to be declared. Defaults to
* `direct`; other types that could be used: `fanout`, `topic`
* @param passive Indicates how the broker should react if the exchange does
* not exist. If passive is `true` and the exhange does not exist the broker
* will respond with an error and not create the exchange; exchange is created
* otherwise. Defaults to `false` (exchange is created if needed)
* @param durable Indicates whether the exchange is durable - e.g., will it
* survive a broker restart.
* @param auto_delete Indicates whether the exchange will automatically be
* removed when no queues are bound to it.
*/
void DeclareExchange(
const std::string &exchange_name,
const std::string &exchange_type = Channel::EXCHANGE_TYPE_DIRECT,
bool passive = false, bool durable = false, bool auto_delete = false);
/**
* Declares an exchange
*
* Creates an exchange on the AMQP broker if it does not already exist
* @param exchange_name the name of the exchange
* @param exchange_type the type of exchange to be declared. Defaults to
* `direct`; other types that could be used: `fanout`, `topic`
* @param passive Indicates how the broker should react if the exchange does
* not exist. If passive is `true` and the exhange does not exist the broker
* will respond with an error and not create the exchange; exchange is created
* otherwise. Defaults to `false` (exchange is created if needed)
* @param durable Indicates whether the exchange is durable - e.g., will it
* survive a broker restart
* @param auto_delete Indicates whether the exchange will automatically be
* removed when no queues are bound to it.
* @param arguments A table of additional arguments used when creating the
* exchange
*/
void DeclareExchange(const std::string &exchange_name,
const std::string &exchange_type, bool passive,
bool durable, bool auto_delete, const Table &arguments);
/**
* Deletes an exchange on the AMQP broker
*
* @param exchange_name the name of the exchange to be deleted
* @param if_unused when `true`, delete the exchange only if it has no queues
* bound to it, or throw `AmqpResponseServerException`. Default `false` -
* delete regardless.
*/
void DeleteExchange(const std::string &exchange_name, bool if_unused = false);
/**
* Binds one exchange to another exchange using a given key
* @param destination the name of the exchange to route messages to
* @param source the name of the exchange to route messages from
* @param routing_key the routing key to use when binding
*/
void BindExchange(const std::string &destination, const std::string &source,
const std::string &routing_key);
/**
* Binds one exchange to another exchange using a given key
* @param destination the name of the exchange to route messages to
* @param source the name of the exchange to route messages from
* @param routing_key the routing key to use when binding
* @param arguments A table of additional arguments used when creating the
* binding
*/
void BindExchange(const std::string &destination, const std::string &source,
const std::string &routing_key, const Table &arguments);
/**
* Unbind an existing exchange-exchange binding
* @see BindExchange
* @param destination the name of the exchange to route messages to
* @param source the name of the exchange to route messages from
* @param routing_key the routing key to use when binding
*/
void UnbindExchange(const std::string &destination, const std::string &source,
const std::string &routing_key);
/**
* Unbind an existing exchange-exchange binding
* @see BindExchange
* @param destination the name of the exchange to route messages to
* @param source the name of the exchange to route messages from
* @param routing_key the routing key to use when binding
* @param arguments A table of additional arguments when unbinding the
* exchange
*/
void UnbindExchange(const std::string &destination, const std::string &source,
const std::string &routing_key, const Table &arguments);
/**
* Checks to see if a queue exists on the broker.
*
* @param queue_name the name of the exchange to check for.
* @returns true if the exchange exists on the broker, false otherwise.
*/
bool CheckQueueExists(boost::string_ref queue_name);
/**
* Declare a queue
*
* Creates a queue on the AMQP broker if it does not already exist.
* @param queue_name The desired name of the queue. If this is an empty
* string, the broker will generate a queue name that this method will return.
* @param passive Indicated how the broker should react if the queue does not
* exist. The broker will raise an error if the queue doesn't already exist
* and passive is `true`. With passive `false` (the default), the queue gets
* created automatically, if needed.
* @param durable Indicates whether the exchange is durable - e.g., will it
* survive a broker restart.
* @param exclusive Indicates that only client can use the queue. Defaults to
* true. An exclusive queue is deleted when the connection is closed.
* @param auto_delete the queue will be deleted after at least one exchange
* has been bound to it, then has been unbound
* @returns The name of the queue created on the broker. Used mostly when the
* broker is asked to create a unique queue by not providing a queue name.
*/
std::string DeclareQueue(const std::string &queue_name, bool passive = false,
bool durable = false, bool exclusive = true,
bool auto_delete = true);
/**
* Declares a queue
*
* Creates a queue on the AMQP broker if it does not already exist.
* @param queue_name The desired name of the queue. If this is an empty
* string, the broker will generate a queue name that this method will return.
* @param passive Indicated how the broker should react if the queue does not
* exist. The broker will raise an error if the queue doesn't already exist
* and passive is `true`. With passive `false` (the default), the queue gets
* created automatically, if needed.
* @param durable Indicates whether the exchange is durable - e.g., will it
* survive a broker restart.
* @param exclusive Indicates that only client can use the queue. Defaults to
* true. An exclusive queue is deleted when the connection is closed.
* @param auto_delete the queue will be deleted after at least one exchange
* has been bound to it, then has been unbound
* @param arguments A table of additional arguments
* @returns The name of the queue created on the broker. Used mostly when the
* broker is asked to create a unique queue by not providing a queue name.
*/
std::string DeclareQueue(const std::string &queue_name, bool passive,
bool durable, bool exclusive, bool auto_delete,
const Table &arguments);
/**
* Declares a queue and returns current message- and consumer counts
*
* Creates a queue on the AMQP broker if it does not already exist.
* @param queue_name The desired name of the queue. If this is an empty
* string, the broker will generate a queue name that this method will return.
* @param [out] message_count The current number of messages in the declared
* queue gets passed out through this argument.
* @param [out] consumer_count The current number of consumers of the declared
* queue gets passed out through this argument.
* @param passive Indicated how the broker should react if the queue does not
* exist. The broker will raise an error if the queue doesn't already exist
* and passive is `true`. With passive `false` (the default), the queue gets
* created automatically, if needed.
* @param durable Indicates whether the exchange is durable - e.g., will it
* survive a broker restart.
* @param exclusive Indicates that only client can use the queue. Defaults to
* true. An exclusive queue is deleted when the connection is closed.
* @param auto_delete the queue will be deleted after at least one exchange
* has been bound to it, then has been unbound
* @returns The name of the queue created on the broker. Used mostly when the
* broker is asked to create a unique queue by not providing a queue name.
*/
std::string DeclareQueueWithCounts(const std::string &queue_name,
boost::uint32_t &message_count,
boost::uint32_t &consumer_count,
bool passive = false, bool durable = false,
bool exclusive = true,
bool auto_delete = true);
/**
* Declares a queue and returns current message- and consumer counts
*
* Creates a queue on the AMQP broker if it does not already exist.
* @param queue_name The desired name of the queue. If this is an empty
* string, the broker will generate a queue name that this method will return.
* @param [out] message_count The current number of messages in the declared
* queue gets passed out through this argument.
* @param [out] consumer_count The current number of consumers of the declared
* queue gets passed out through this argument.
* @param passive Indicated how the broker should react if the queue does not
* exist. The broker will raise an error if the queue doesn't already exist
* and passive is `true`. With passive `false` (the default), the queue gets
* created automatically, if needed.
* @param durable Indicates whether the exchange is durable - e.g., will it
* survive a broker restart.
* @param exclusive Indicates that only client can use the queue. Defaults to
* true. An exclusive queue is deleted when the connection is closed.
* @param auto_delete the queue will be deleted after at least one exchange
* has been bound to it, then has been unbound
* @param arguments A table of additional arguments
* @returns The name of the queue created on the broker. Used mostly when the
* broker is asked to create a unique queue by not providing a queue name.
*/
std::string DeclareQueueWithCounts(const std::string &queue_name,
boost::uint32_t &message_count,
boost::uint32_t &consumer_count,
bool passive, bool durable, bool exclusive,
bool auto_delete, const Table &arguments);
/**
* Deletes a queue
*
* Removes a queue from the broker. There is no indication of whether the
* queue was actually deleted on the broker.
* @param queue_name The name of the queue to remove.
* @param if_unused Only deletes the queue if the queue doesn't have any
* active consumers.
* @param if_empty Only deletes the queue if the queue is empty.
*/
void DeleteQueue(const std::string &queue_name, bool if_unused = false,
bool if_empty = false);
/**
* Binds a queue to an exchange
*
* Connects a queue to an exchange on the broker.
* @param queue_name The name of the queue to bind.
* @param exchange_name The name of the exchange to bind to.
* @param routing_key Defines the routing key of the binding. Only messages
* with matching routing key will be delivered to the queue from the exchange.
* Defaults to `""` which means all messages will be delivered.
*/
void BindQueue(const std::string &queue_name,
const std::string &exchange_name,
const std::string &routing_key = "");
/**
* Binds a queue to an exchange
*
* Connects a queue to an exchange on the broker.
* @param queue_name The name of the queue to bind.
* @param exchange_name The name of the exchange to bind to.
* @param routing_key Defines the routing key of the binding. Only messages
* with matching routing key will be delivered to the queue from the exchange.
* Defaults to `""` which means all messages will be delivered.
* @param arguments A table of additional arguments used when binding the
* queue
*/
void BindQueue(const std::string &queue_name,
const std::string &exchange_name,
const std::string &routing_key, const Table &arguments);
/**
* Unbinds a queue from an exchange
*
* Disconnects a queue from an exchange.
* @param queue_name The name of the queue to unbind.
* @param exchange_name The name of the exchange to unbind.
* @param routing_key This must match the routing_key of the binding.
* @see BindQueue
*/
void UnbindQueue(const std::string &queue_name,
const std::string &exchange_name,
const std::string &routing_key = "");
/**
* Unbinds a queue from an exchange
*
* Disconnects a queue from an exchange.
* @param queue_name The name of the queue to unbind.
* @param exchange_name The name of the exchange to unbind.
* @param routing_key This must match the routing_key of the binding.
* @param arguments A table of additional arguments.
* @see BindQueue
*/
void UnbindQueue(const std::string &queue_name,
const std::string &exchange_name,
const std::string &routing_key, const Table &arguments);
/**
* Purges a queue
*
* Removes all messages in a queue on the broker. The queue will become empty.
* @param queue_name The name of the queue to purge.
*/
void PurgeQueue(const std::string &queue_name);
/**
* Acknowledges a Basic message
*
* Acknowledges a message delievered using \ref BasicGet or \ref BasicConsume.
* @param message The message that is being ack'ed.
*/
void BasicAck(const Envelope::ptr_t &message);
/**
* Acknowledges a Basic message
*
* Acknowledges a message delivered using \ref BasicGet or \ref BasicConsume.
* This overload doesn't require the \ref Envelope object to acknowledge.
*
* @param info The `delivery-tag` of the message to acknowledge.
*/
void BasicAck(const Envelope::DeliveryInfo &info);
/**
* Acknowledges a Basic message
*
* Acknowledges a message delivered using \ref BasicGet or \ref BasicConsume.
* This overload doesn't require the \ref Envelope object to acknowledge.
*
* @note Ack'ing multiple messages (using `multiple=true`) is
* scoped to messages delivered on a given AMQP channel. Since
* SimpleAmqpClient uses one channel per consumer, this means that
* multiple-ack will acknowledge messages up to and including the current
* message id *for a given consumer*.
*
* @param info The `delivery-tag` of the message to acknowledge.
* @param multiple If `true`, ack all messages in this channel up to this
* delivery tag. If `false`, ack only this delivery tag.
*/
void BasicAck(const Envelope::DeliveryInfo &info, bool multiple);
/**
* Reject (NAck) a Basic message
*
* Rejects a message delivered using \ref BasicGet or \ref BasicConsume
* @param message The message that is being nack'ed.
* @param requeue Tells the broker to requeue the message or not.
* @param multiple If `true`, reject all messages in this channel up to this.
*/
void BasicReject(const Envelope::ptr_t &message, bool requeue,
bool multiple = false);
/**
* Reject (NAck) a Basic message
*
* Rejects a message delivered using \ref BasicGet or \ref BasicConsume.
* This overload doesn't require the \ref Envelope object to Reject
* @param info The `delivery-tag` of the message to Reject.
* @param requeue Tells the broker to requeue the message or not.
* @param multiple If `true`, reject all messages in this channel up to this.
*/
void BasicReject(const Envelope::DeliveryInfo &info, bool requeue,
bool multiple = false);
/**
* Publishes a Basic message
*
* Publishes a Basic message to an exchange
* @param exchange_name The name of the exchange to publish the message to
* @param routing_key The routing key to publish with, this is used to route
* to corresponding queue(s).
* @param message The \ref BasicMessage object to publish to the queue.
* @param mandatory Requires the message to be delivered to a queue. A
* \ref MessageReturnedException is thrown if the message cannot be routed to
* a queue.
* @param immediate Requires the message to be both routed to a queue, and
* immediately delivered to a consumer. If the message is not routed, or a
* consumer cannot immediately deliver the message,
* a \ref MessageReturnedException is thrown. This has no effect when using
* RabbitMQ v3.0 and newer.
*/
void BasicPublish(const std::string &exchange_name,
const std::string &routing_key,
const BasicMessage::ptr_t message, bool mandatory = false,
bool immediate = false);
/**
* Synchronously consume a message from a queue
*
* This function will not wait for a message to arrive in a queue, it will
* return `false` if the queue is empty.
* @note This function effectively polls the broker for a message; in general,
* better performance is realized using \ref BasicConsume / \ref
* BasicConsumeMessage.
* @param [out] message A message envelope pointer that will be populated if a
* message is delivered.
* @param queue The name of the queue to try to get the message from.
* @param no_ack Can the message be un-ack'ed. Default `true` (message does
* not need to be acked).
* @returns `true` if a message was delivered, `false` if the queue was empty.
*/
bool BasicGet(Envelope::ptr_t &message, const std::string &queue,
bool no_ack = true);
/**
* Redeliver unacknowledged messages from the broker
* @param consumer The consumer to recover message from
*/
void BasicRecover(const std::string &consumer);
/**
* Starts consuming Basic messages on a queue
*
* Subscribes as a consumer to a queue, so all future messages on a queue
* will be Basic.Delivered
* @note Due to a limitation to how things are done, it is only possible to
* reliably have **a single consumer per channel**; calling this
* more than once per channel may result in undefined results.
* @param queue The name of the queue to subscribe to.
* @param consumer_tag The name of the consumer. This is used to do
* operations with a consumer.
* @param no_local Defaults to true
* @param no_ack If `true`, ack'ing the message is automatically done when the
* message is delivered. Defaults to `true` (message does not have to be
* ack'ed).
* @param exclusive Means only this consumer can access the queue.
* @param message_prefetch_count Number of unacked messages the broker will
* deliver. Setting this to more than 1 will allow the broker to deliver
* messages while a current message is being processed. A value of
* 0 means no limit. This option is ignored if `no_ack = true`.
* @returns the consumer tag
*/
std::string BasicConsume(const std::string &queue,
const std::string &consumer_tag = "",
bool no_local = true, bool no_ack = true,
bool exclusive = true,
boost::uint16_t message_prefetch_count = 1);
/**
* Starts consuming Basic messages on a queue
*
* Subscribes as a consumer to a queue, so all future messages on a queue
* will be Basic.Delivered
* @note Due to a limitation to how things are done, it is only possible to
* reliably have **a single consumer per channel**; calling this
* more than once per channel may result in undefined results.
* @param queue The name of the queue to subscribe to.
* @param consumer_tag The name of the consumer. This is used to do
* operations with a consumer.
* @param no_local Defaults to true
* @param no_ack If `true`, ack'ing the message is automatically done when the
* message is delivered. Defaults to `true` (message does not have to be
* ack'ed).
* @param exclusive Means only this consumer can access the queue.
* @param message_prefetch_count Number of unacked messages the broker will
* deliver. Setting this to more than 1 will allow the broker to deliver
* messages while a current message is being processed. A value of
* 0 means no limit. This option is ignored if `no_ack = true`.
* @param arguments A table of additional arguments when creating the consumer
* @returns the consumer tag
*/
std::string BasicConsume(const std::string &queue,
const std::string &consumer_tag, bool no_local,
bool no_ack, bool exclusive,
boost::uint16_t message_prefetch_count,
const Table &arguments);
/**
* Modify consumer's message prefetch count
*
* Sets the number of unacknowledged messages that will be delivered
* by the broker to a consumer.
*
* Has no effect for consumer with `no_ack` set.
*
* @param consumer_tag The consumer tag to adjust the prefetch for.
* @param message_prefetch_count The number of unacknowledged message the
* broker will deliver. A value of 0 means no limit.
*/
void BasicQos(const std::string &consumer_tag,
boost::uint16_t message_prefetch_count);
/**
* Cancels a previously created Consumer
*
* Unsubscribes a consumer from a queue. In other words undoes what
* \ref BasicConsume does.
* @param consumer_tag The same `consumer_tag` used when the consumer was
* created with \ref BasicConsume.
* @see BasicConsume
*/
void BasicCancel(const std::string &consumer_tag);
/**
* Consumes a single message
*
* Waits for a single Basic message to be Delivered.
*
* This function only works after `BasicConsume` has successfully been called.
*
* @param consumer_tag Consumer ID (returned from \ref BasicConsume).
* @returns The next message on the queue
*/
Envelope::ptr_t BasicConsumeMessage(const std::string &consumer_tag);
/**
* Consumes a single message from multiple consumers
*
* Waits for a single message to be delivered from a list of consumers.
*
* This function only works after \ref BasicConsume has been called.
*
* @returns The next message delivered from the broker
*/
Envelope::ptr_t BasicConsumeMessage(
const std::vector<std::string> &consumer_tags);
/**
* Consumes a single message from any open consumers
*
* Waits for a message from any consumer open on this Channel object.
*
* This function only works after \ref BasicConsume has been called.
*
* @returns The next message delivered from the broker
*/
Envelope::ptr_t BasicConsumeMessage();
/**
* Consumes a single message with a timeout (gets an envelope object)
*
* Waits for a single Basic message to be Delivered or the timeout to expire.
*
* This function only works after \ref BasicConsume as been successfully
* called.
*
* This function returns an envelope object which contains more information
* about the message delivered.
*
* @param consumer_tag Consumer ID (returned from \ref BasicConsume).
* @param [out] envelope The message object to save to. Empty pointer is ok.
* @param timeout Timeout, in ms, for the first part of the message to be
* delivered
* @throws MessageReturnedException If a `basic.return` is received while
* waiting for a message.
* @returns `false` on timeout, `true` on message delivery
*/
bool BasicConsumeMessage(const std::string &consumer_tag,
Envelope::ptr_t &envelope, int timeout = -1);
/**
* Consumes a single message with a timeout from multiple consumers
*
* Waits for a single message to be delivered to one of the listed consumer
* tags, or for the timeout to expire.
*
* This function only works after `BasicConsume` has been successfully called.
*
* @param consumer_tags A list of the consumer tags to wait from.
* @param [out] envelope The message object that is delivered.
* @param timeout The timeout in milliseconds for the message to be
* delivered. 0 works like a non-blocking read, -1 is an infinite timeout.
* @returns `true` on message delivery, `false` on timeout.
*/
bool BasicConsumeMessage(const std::vector<std::string> &consumer_tags,
Envelope::ptr_t &envelope, int timeout = -1);
/**
* Consumes a single message from any consumer on a Channel
*
* Waits for a single message to be delivered to one of the consumers opened
* on this Channel, or for the timeout to occur.
*
* This function only works after \ref BasicConsume has been successfully
* called.
*
* @param [out] envelope The message object that is delivered.
* @param timeout The timeout in milliseconds for the message to be delivered.
* 0 works like a non-blocking read, -1 is an infinite timeout.
* @returns `true` on message delivery, `false` on timeout.
*/
bool BasicConsumeMessage(Envelope::ptr_t &envelope, int timeout = -1);
private:
static ChannelImpl *OpenChannel(const std::string &host, int port,
const std::string &username,
const std::string &password,
const std::string &vhost, int frame_max,
bool sasl_external);
static ChannelImpl *OpenSecureChannel(const std::string &host, int port,
const std::string &username,
const std::string &password,
const std::string &vhost, int frame_max,
const OpenOpts::TLSParams &tls_params,
bool sasl_external);
/// PIMPL idiom
boost::scoped_ptr<ChannelImpl> m_impl;
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_CHANNEL_H

View File

@@ -0,0 +1,369 @@
#ifndef SIMPLEAMQPCLIENT_CHANNELIMPL_H
#define SIMPLEAMQPCLIENT_CHANNELIMPL_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
// Put these first to avoid warnings about INT#_C macro redefinition
#include <amqp.h>
#include <amqp_framing.h>
#include <boost/array.hpp>
#include "SimpleAmqpClient/AmqpException.h"
#include "SimpleAmqpClient/BasicMessage.h"
#include "SimpleAmqpClient/Channel.h"
#include "SimpleAmqpClient/ConsumerCancelledException.h"
#include "SimpleAmqpClient/Envelope.h"
#include "SimpleAmqpClient/MessageReturnedException.h"
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/bind.hpp>
#include <boost/chrono.hpp>
#include <boost/noncopyable.hpp>
#include <map>
#include <vector>
namespace AmqpClient {
class Channel::ChannelImpl : boost::noncopyable {
public:
ChannelImpl();
virtual ~ChannelImpl();
typedef std::vector<amqp_channel_t> channel_list_t;
typedef std::vector<amqp_frame_t> frame_queue_t;
typedef std::map<amqp_channel_t, frame_queue_t> channel_map_t;
typedef channel_map_t::iterator channel_map_iterator_t;
void DoLogin(const std::string &username, const std::string &password,
const std::string &vhost, int frame_max,
bool sasl_external = false);
amqp_channel_t GetChannel();
void ReturnChannel(amqp_channel_t channel);
bool IsChannelOpen(amqp_channel_t channel);
bool GetNextFrameFromBroker(amqp_frame_t &frame,
boost::chrono::microseconds timeout);
bool CheckForQueuedMessageOnChannel(amqp_channel_t message_on_channel) const;
void AddToFrameQueue(const amqp_frame_t &frame);
template <class ChannelListType>
bool GetNextFrameFromBrokerOnChannel(const ChannelListType channels,
amqp_frame_t &frame_out,
boost::chrono::microseconds timeout =
boost::chrono::microseconds::max()) {
boost::chrono::steady_clock::time_point end_point;
boost::chrono::microseconds timeout_left = timeout;
if (timeout != boost::chrono::microseconds::max()) {
end_point = boost::chrono::steady_clock::now() + timeout;
}
amqp_frame_t frame;
while (GetNextFrameFromBroker(frame, timeout_left)) {
if (channels.end() !=
std::find(channels.begin(), channels.end(), frame.channel)) {
frame_out = frame;
return true;
}
if (frame.channel == 0) {
// Only thing we care to handle on the channel0 is the connection.close
// method
if (AMQP_FRAME_METHOD == frame.frame_type &&
AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id) {
FinishCloseConnection();
AmqpException::Throw(*reinterpret_cast<amqp_connection_close_t *>(
frame.payload.method.decoded));
}
} else {
AddToFrameQueue(frame);
}
if (timeout != boost::chrono::microseconds::max()) {
boost::chrono::steady_clock::time_point now =
boost::chrono::steady_clock::now();
if (now >= end_point) {
return false;
}
timeout_left =
boost::chrono::duration_cast<boost::chrono::microseconds>(
end_point - now);
}
}
return false;
}
bool GetNextFrameOnChannel(
amqp_channel_t channel, amqp_frame_t &frame,
boost::chrono::microseconds timeout = boost::chrono::microseconds::max());
static bool is_on_channel(const amqp_frame_t frame, amqp_channel_t channel) {
return channel == frame.channel;
}
static bool is_frame_type_on_channel(const amqp_frame_t frame,
uint8_t frame_type,
amqp_channel_t channel) {
return frame.frame_type == frame_type && frame.channel == channel;
}
static bool is_method_on_channel(const amqp_frame_t frame,
amqp_method_number_t method,
amqp_channel_t channel) {
return frame.channel == channel && frame.frame_type == AMQP_FRAME_METHOD &&
frame.payload.method.id == method;
}
template <class ChannelListType, class ResponseListType>
static bool is_expected_method_on_channel(
const amqp_frame_t &frame, const ChannelListType channels,
const ResponseListType &expected_responses) {
return channels.end() !=
std::find(channels.begin(), channels.end(), frame.channel) &&
AMQP_FRAME_METHOD == frame.frame_type &&
expected_responses.end() != std::find(expected_responses.begin(),
expected_responses.end(),
frame.payload.method.id);
}
template <class ChannelListType, class ResponseListType>
bool GetMethodOnChannel(const ChannelListType channels, amqp_frame_t &frame,
const ResponseListType &expected_responses,
boost::chrono::microseconds timeout =
boost::chrono::microseconds::max()) {
frame_queue_t::iterator desired_frame = std::find_if(
m_frame_queue.begin(), m_frame_queue.end(),
boost::bind(
&ChannelImpl::is_expected_method_on_channel<ChannelListType,
ResponseListType>,
_1, channels, expected_responses));
if (m_frame_queue.end() != desired_frame) {
frame = *desired_frame;
m_frame_queue.erase(desired_frame);
return true;
}
boost::chrono::steady_clock::time_point end_point;
boost::chrono::microseconds timeout_left = timeout;
if (timeout != boost::chrono::microseconds::max()) {
end_point = boost::chrono::steady_clock::now() + timeout;
}
amqp_frame_t incoming_frame;
while (GetNextFrameFromBrokerOnChannel(channels, incoming_frame,
timeout_left)) {
if (is_expected_method_on_channel(incoming_frame, channels,
expected_responses)) {
frame = incoming_frame;
return true;
}
if (AMQP_FRAME_METHOD == incoming_frame.frame_type &&
AMQP_CHANNEL_CLOSE_METHOD == incoming_frame.payload.method.id) {
FinishCloseChannel(incoming_frame.channel);
try {
AmqpException::Throw(*reinterpret_cast<amqp_channel_close_t *>(
incoming_frame.payload.method.decoded));
} catch (AmqpException &) {
MaybeReleaseBuffersOnChannel(incoming_frame.channel);
throw;
}
}
m_frame_queue.push_back(incoming_frame);
if (timeout != boost::chrono::microseconds::max()) {
boost::chrono::steady_clock::time_point now =
boost::chrono::steady_clock::now();
if (now >= end_point) {
return false;
}
timeout_left =
boost::chrono::duration_cast<boost::chrono::microseconds>(
end_point - now);
}
}
return false;
}
template <class ResponseListType>
amqp_frame_t DoRpcOnChannel(amqp_channel_t channel, boost::uint32_t method_id,
void *decoded,
const ResponseListType &expected_responses) {
CheckForError(amqp_send_method(m_connection, channel, method_id, decoded));
amqp_frame_t response;
boost::array<amqp_channel_t, 1> channels = {{channel}};
GetMethodOnChannel(channels, response, expected_responses);
return response;
}
template <class ResponseListType>
amqp_frame_t DoRpc(boost::uint32_t method_id, void *decoded,
const ResponseListType &expected_responses) {
amqp_channel_t channel = GetChannel();
amqp_frame_t ret =
DoRpcOnChannel(channel, method_id, decoded, expected_responses);
ReturnChannel(channel);
return ret;
}
template <class ChannelListType>
static bool envelope_on_channel(const Envelope::ptr_t &envelope,
const ChannelListType channels) {
return channels.end() != std::find(channels.begin(), channels.end(),
envelope->DeliveryChannel());
}
template <class ChannelListType>
bool ConsumeMessageOnChannel(const ChannelListType channels,
Envelope::ptr_t &message, int timeout) {
envelope_list_t::iterator it = std::find_if(
m_delivered_messages.begin(), m_delivered_messages.end(),
boost::bind(ChannelImpl::envelope_on_channel<ChannelListType>, _1,
channels));
if (it != m_delivered_messages.end()) {
message = *it;
m_delivered_messages.erase(it);
return true;
}
return ConsumeMessageOnChannelInner(channels, message, timeout);
}
template <class ChannelListType>
bool ConsumeMessageOnChannelInner(const ChannelListType channels,
Envelope::ptr_t &message, int timeout) {
const boost::array<boost::uint32_t, 2> DELIVER_OR_CANCEL = {
{AMQP_BASIC_DELIVER_METHOD, AMQP_BASIC_CANCEL_METHOD}};
boost::chrono::microseconds real_timeout =
(timeout >= 0 ? boost::chrono::milliseconds(timeout)
: boost::chrono::microseconds::max());
amqp_frame_t deliver;
if (!GetMethodOnChannel(channels, deliver, DELIVER_OR_CANCEL,
real_timeout)) {
return false;
}
if (AMQP_BASIC_CANCEL_METHOD == deliver.payload.method.id) {
amqp_basic_cancel_t *cancel_method =
reinterpret_cast<amqp_basic_cancel_t *>(
deliver.payload.method.decoded);
std::string consumer_tag((char *)cancel_method->consumer_tag.bytes,
cancel_method->consumer_tag.len);
RemoveConsumer(consumer_tag);
ReturnChannel(deliver.channel);
MaybeReleaseBuffersOnChannel(deliver.channel);
throw ConsumerCancelledException(consumer_tag);
}
amqp_basic_deliver_t *deliver_method =
reinterpret_cast<amqp_basic_deliver_t *>(
deliver.payload.method.decoded);
const std::string exchange((char *)deliver_method->exchange.bytes,
deliver_method->exchange.len);
const std::string routing_key((char *)deliver_method->routing_key.bytes,
deliver_method->routing_key.len);
const std::string in_consumer_tag(
(char *)deliver_method->consumer_tag.bytes,
deliver_method->consumer_tag.len);
const boost::uint64_t delivery_tag = deliver_method->delivery_tag;
const bool redelivered = (deliver_method->redelivered == 0 ? false : true);
MaybeReleaseBuffersOnChannel(deliver.channel);
BasicMessage::ptr_t content = ReadContent(deliver.channel);
MaybeReleaseBuffersOnChannel(deliver.channel);
message = Envelope::Create(content, in_consumer_tag, delivery_tag, exchange,
redelivered, routing_key, deliver.channel);
return true;
}
amqp_channel_t CreateNewChannel();
amqp_channel_t GetNextChannelId();
void CheckRpcReply(amqp_channel_t channel, const amqp_rpc_reply_t &reply);
void CheckForError(int ret);
void CheckFrameForClose(amqp_frame_t &frame, amqp_channel_t channel);
void FinishCloseChannel(amqp_channel_t channel);
void FinishCloseConnection();
MessageReturnedException CreateMessageReturnedException(
amqp_basic_return_t &return_method, amqp_channel_t channel);
AmqpClient::BasicMessage::ptr_t ReadContent(amqp_channel_t channel);
void AddConsumer(const std::string &consumer_tag, amqp_channel_t channel);
amqp_channel_t RemoveConsumer(const std::string &consumer_tag);
amqp_channel_t GetConsumerChannel(const std::string &consumer_tag);
std::vector<amqp_channel_t> GetAllConsumerChannels() const;
void MaybeReleaseBuffersOnChannel(amqp_channel_t channel);
void CheckIsConnected();
void SetIsConnected(bool state) { m_is_connected = state; }
// The RabbitMQ broker changed the way that basic.qos worked as of v3.3.0.
// See: http://www.rabbitmq.com/consumer-prefetch.html
// Newer versions of RabbitMQ basic.qos.global set to false applies to new
// consumers made on the channel, and true applies to all consumers on the
// channel (not connection).
bool BrokerHasNewQosBehavior() const { return 0x030300 <= m_brokerVersion; }
amqp_connection_state_t m_connection;
private:
static boost::uint32_t ComputeBrokerVersion(
const amqp_connection_state_t state);
frame_queue_t m_frame_queue;
typedef std::vector<Envelope::ptr_t> envelope_list_t;
envelope_list_t m_delivered_messages;
typedef std::map<std::string, amqp_channel_t> consumer_map_t;
consumer_map_t m_consumer_channel_map;
enum channel_state_t { CS_Closed = 0, CS_Open, CS_Used };
typedef std::vector<channel_state_t> channel_state_list_t;
channel_state_list_t m_channels;
boost::uint32_t m_brokerVersion;
// A channel that is likely to be an CS_Open state
amqp_channel_t m_last_used_channel;
bool m_is_connected;
};
} // namespace AmqpClient
#endif // SIMPLEAMQPCLIENT_CHANNELIMPL_H

View File

@@ -0,0 +1,51 @@
#ifndef SIMPLEAMQPCLIENT_CONNECTIONCLOSEDEXCEPTION_H
#define SIMPLEAMQPCLIENT_CONNECTIONCLOSEDEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <stdexcept>
#include "SimpleAmqpClient/Util.h"
/// @file SimpleAmqpClient/ConnectionClosedException.h
/// Defines AmqpClient::ConnectionClosedException
namespace AmqpClient {
/**
* "Connection is closed" exception
*/
class SIMPLEAMQPCLIENT_EXPORT ConnectionClosedException
: public std::runtime_error {
public:
/// Constructor
explicit ConnectionClosedException()
: std::runtime_error("Connection is closed") {}
};
} // namespace AmqpClient
#endif // SIMPLEAMQPCLIENT_CONNECTIONCLOSEDEXCEPTION_H

View File

@@ -0,0 +1,67 @@
#ifndef SIMPLEAMQPCLIENT_CONSUMERCANCELLEDEXCEPTION_H
#define SIMPLEAMQPCLIENT_CONSUMERCANCELLEDEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <stdexcept>
#include <string>
#include "SimpleAmqpClient/Util.h"
/// @file SimpleAmqpClient/ConsumerCancelledException.h
/// Defines AmqpClient::ConsumerCancelledException
namespace AmqpClient {
/**
* "Consumer was cancelled" exception
*
* Happens when the server ends a consumer subscription, e.g. when the
* subscribed queue is being deleted, or when a client issues basic.cancel
* request.
*/
class SIMPLEAMQPCLIENT_EXPORT ConsumerCancelledException
: public std::runtime_error {
public:
/// Constructor
explicit ConsumerCancelledException(const std::string &consumer_tag) throw()
: std::runtime_error(
std::string("Consumer was cancelled: ").append(consumer_tag)),
m_consumer_tag(consumer_tag) {}
/// Destructor
virtual ~ConsumerCancelledException() throw() {}
/// Getter of the consumer tag
std::string GetConsumerTag() const { return m_consumer_tag; }
private:
std::string m_consumer_tag;
};
} // namespace AmqpClient
#endif // SIMPLEAMQPCLIENT_CONSUMERCANCELLEDEXCEPTION_H

View File

@@ -0,0 +1,61 @@
#ifndef SIMPLEAMQPCLIENT_CONSUMERTAGNOTFOUND_H
#define SIMPLEAMQPCLIENT_CONSUMERTAGNOTFOUND_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <stdexcept>
#include "Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif
/// @file SimpleAmqpClient/ConsumerTagNotFoundException.h
/// Defines AmqpClient::ConsumerTagNotFoundException
namespace AmqpClient {
/**
* "Consumer tag not found" exception
*
* @see BasicConsume
*/
class SIMPLEAMQPCLIENT_EXPORT ConsumerTagNotFoundException
: public std::runtime_error {
public:
/// Constructor
ConsumerTagNotFoundException() throw()
: std::runtime_error("The specified consumer tag is unknown") {}
/// Destructor
virtual ~ConsumerTagNotFoundException() throw() {}
};
} // namespace AmqpClient
#endif // SIMPLEAMQPCLIENT_CONSUMERTAGNOTFOUND_H

View File

@@ -0,0 +1,211 @@
#ifndef SIMPLEAMQPCLIENT_ENVELOPE_H
#define SIMPLEAMQPCLIENT_ENVELOPE_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <boost/make_shared.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include "SimpleAmqpClient/BasicMessage.h"
#include "SimpleAmqpClient/Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4275 4251)
#endif // _MSC_VER
/// @file SimpleAmqpClient/Envelope.h
/// The AmqpClient::Envelope class is defined in this header file.
namespace AmqpClient {
/**
* A "message envelope" object containing the message body and delivery metadata
*/
class SIMPLEAMQPCLIENT_EXPORT Envelope : boost::noncopyable {
public:
/// a `shared_ptr` pointer to Envelope
typedef boost::shared_ptr<Envelope> ptr_t;
/**
* Creates an new envelope object
* @param message the payload
* @param consumer_tag the consumer tag the message was delivered to
* @param delivery_tag the delivery tag that the broker assigned to the
* message
* @param exchange the name of the exchange that the message was published to
* @param redelivered a flag indicating whether the message consumed as a
* result of a redelivery
* @param routing_key the routing key that the message was published with
* @param delivery_channel channel ID of the delivery (see DeliveryInfo)
* @returns a boost::shared_ptr to an envelope object
*/
static ptr_t Create(const BasicMessage::ptr_t message,
const std::string &consumer_tag,
const boost::uint64_t delivery_tag,
const std::string &exchange, bool redelivered,
const std::string &routing_key,
const boost::uint16_t delivery_channel) {
return boost::make_shared<Envelope>(message, consumer_tag, delivery_tag,
exchange, redelivered, routing_key,
delivery_channel);
}
/**
* Construct a new Envelope object
* @param message the payload
* @param consumer_tag the consumer tag the message was delivered to
* @param delivery_tag the delivery tag that the broker assigned to the
* message
* @param exchange the name of the exchange that the message was published to
* @param redelivered a flag indicating whether the message consumed as a
* result of a redelivery
* @param routing_key the routing key that the message was published with
* @param delivery_channel channel ID of the delivery (see DeliveryInfo)
*/
explicit Envelope(const BasicMessage::ptr_t message,
const std::string &consumer_tag,
const boost::uint64_t delivery_tag,
const std::string &exchange, bool redelivered,
const std::string &routing_key,
const boost::uint16_t delivery_channel);
public:
/**
* destructor
*/
virtual ~Envelope();
/**
* Get the payload of the envelope
*
* @returns the message
*/
inline BasicMessage::ptr_t Message() const { return m_message; }
/**
* Get the consumer tag for the consumer that delivered the message
*
* @returns the consumer that delivered the message
*/
inline std::string ConsumerTag() const { return m_consumerTag; }
/**
* Get the delivery tag for the message.
*
* The delivery tag is a unique tag for a given message assigned by the
* broker
* This tag is used when Ack'ing a message
*
* @returns the delivery tag for a message
*/
inline boost::uint64_t DeliveryTag() const { return m_deliveryTag; }
/**
* Get the name of the exchange that the message was published to
*
* @returns the name of the exchange the message was published to
*/
inline std::string Exchange() const { return m_exchange; }
/**
* Get the flag that indicates whether the message was redelivered
*
* A flag that indicates whether the message was redelievered means
* the broker tried to deliver the message and the client did not Ack
* the message, so the message was requeued, or the client asked the broker
* to Recover which forced all non-Acked messages to be redelivered
*
* @return a boolean flag indicating whether the message was redelivered
*/
inline bool Redelivered() const { return m_redelivered; }
/**
* Get the routing key that the message was published with
*
* @returns a string containing the routing key the message was published
* with
*/
inline std::string RoutingKey() const { return m_routingKey; }
/**
* Get the delivery channel
*/
inline boost::uint16_t DeliveryChannel() const { return m_deliveryChannel; }
/**
* A POD carrier of delivery-tag
*
* This is server-assigned and channel-specific.
*
* The delivery tag is valid only within the channel from which the message
* was received. I.e. a client MUST NOT receive a message on one channel and
* then acknowledge it on another.
*
* The server MUST NOT use a zero value for delivery tags. Zero is reserved
* for client use, meaning "all messages so far received".
*/
struct DeliveryInfo {
/// A delivery tag, assigned by the broker to identify this delivery within
/// a channel
boost::uint64_t delivery_tag;
/// An ID of the delivery channel
boost::uint16_t delivery_channel;
};
/**
* Getter of the delivery-tag
*/
inline DeliveryInfo GetDeliveryInfo() const {
DeliveryInfo info;
info.delivery_tag = m_deliveryTag;
info.delivery_channel = m_deliveryChannel;
return info;
}
private:
const BasicMessage::ptr_t m_message;
const std::string m_consumerTag;
const boost::uint64_t m_deliveryTag;
const std::string m_exchange;
const bool m_redelivered;
const std::string m_routingKey;
const boost::uint16_t m_deliveryChannel;
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
#endif // SIMPLEAMQPCLIENT_ENVELOPE_H

View File

@@ -0,0 +1,70 @@
#ifndef SIMPLEAMQPCLIENT_MESSAGEREJECTEDEXCEPTION_H
#define SIMPLEAMQPCLIENT_MESSAGEREJECTEDEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <boost/lexical_cast.hpp>
#include <stdexcept>
#include "SimpleAmqpClient/BasicMessage.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif
/// @file SimpleAmqpClient/MessageReturnedException.h
/// Defines AmqpClient::MessageReturnedException
namespace AmqpClient {
/// "Message rejected" exception
class SIMPLEAMQPCLIENT_EXPORT MessageRejectedException
: public std::runtime_error {
public:
MessageRejectedException(uint64_t delivery_tag)
: std::runtime_error(
std::string("Message rejected: ")
.append(boost::lexical_cast<std::string>(delivery_tag))),
m_delivery_tag(delivery_tag) {}
/// `delivery_tag` getter
uint64_t GetDeliveryTag() { return m_delivery_tag; }
private:
uint64_t m_delivery_tag;
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_MESSAGEREJECTEDEXCEPTION_H

View File

@@ -0,0 +1,85 @@
#ifndef SIMPLEAMQPCLIENT_MESSAGERETURNEDEXCEPTION_H
#define SIMPLEAMQPCLIENT_MESSAGERETURNEDEXCEPTION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <stdexcept>
#include "SimpleAmqpClient/BasicMessage.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251 4275)
#endif
/// @file SimpleAmqpClient/MessageReturnedException.h
/// Defines AmqpClient::MessageReturnedException
namespace AmqpClient {
/// "Message returned" exception.
class SIMPLEAMQPCLIENT_EXPORT MessageReturnedException
: public std::runtime_error {
public:
/// Constructor.
explicit MessageReturnedException(BasicMessage::ptr_t message,
boost::uint32_t reply_code,
const std::string &reply_text,
const std::string &exchange,
const std::string &routing_key) throw();
virtual ~MessageReturnedException() throw() {}
/// `message` getter
BasicMessage::ptr_t message() const throw() { return m_message; }
/// `reply_code` getter
boost::uint32_t reply_code() const throw() { return m_reply_code; }
/// `reply_text` getter
std::string reply_text() const throw() { return m_reply_text; }
/// Exchange name getter
std::string exchange() const throw() { return m_exchange; }
/// Routing key getter
std::string routing_key() const throw() { return m_routing_key; }
private:
BasicMessage::ptr_t m_message;
boost::uint32_t m_reply_code;
std::string m_reply_text;
std::string m_exchange;
std::string m_routing_key;
mutable std::string m_what;
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_MESSAGERETURNEDEXCEPTION_H

View File

@@ -0,0 +1,49 @@
#ifndef SIMPLEAMQPCLIENT_SIMPLEAMQPCLIENT_H
#define SIMPLEAMQPCLIENT_SIMPLEAMQPCLIENT_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
/// @file SimpleAmqpClient/SimpleAmqpClient.h
/// This "include all" header file re-exports all of SimpleAmqpClient public API
#include "SimpleAmqpClient/AmqpException.h"
#include "SimpleAmqpClient/AmqpLibraryException.h"
#include "SimpleAmqpClient/AmqpResponseLibraryException.h"
#include "SimpleAmqpClient/BadUriException.h"
#include "SimpleAmqpClient/BasicMessage.h"
#include "SimpleAmqpClient/Channel.h"
#include "SimpleAmqpClient/ConnectionClosedException.h"
#include "SimpleAmqpClient/ConsumerCancelledException.h"
#include "SimpleAmqpClient/ConsumerTagNotFoundException.h"
#include "SimpleAmqpClient/Envelope.h"
#include "SimpleAmqpClient/MessageRejectedException.h"
#include "SimpleAmqpClient/MessageReturnedException.h"
#include "SimpleAmqpClient/Table.h"
#include "SimpleAmqpClient/Version.h"
#endif // SIMPLEAMQPCLIENT_SIMPLEAMQPCLIENT_H

View File

@@ -0,0 +1,501 @@
#ifndef SIMPLEAMQPCLIENT_TABLE_H
#define SIMPLEAMQPCLIENT_TABLE_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <boost/cstdint.hpp>
#include <boost/scoped_ptr.hpp>
#include <ctime>
#include <map>
#include <string>
#include <vector>
#include "SimpleAmqpClient/Util.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4251)
#endif
/// @file SimpleAmqpClient/Table.h
/// The AmqpClient::TableValue variant is defined in this header file
namespace AmqpClient {
namespace Detail {
class TableValueImpl;
} // namespace Detail
/**
* Table key
*
* Note this must be less than 128 bytes long
*/
typedef std::string TableKey;
class TableValue;
/**
* Array field value
*/
typedef std::vector<TableValue> Array;
/**
* Field table
*
* Is just an STL map
*/
typedef std::map<TableKey, TableValue> Table;
typedef Table::value_type TableEntry;
/**
* A variant type for the Table Value
*/
class SIMPLEAMQPCLIENT_EXPORT TableValue {
public:
friend class Detail::TableValueImpl;
/** Types enumeration */
enum ValueType {
VT_void = 0, ///< void type
VT_bool = 1, ///< boolean type
VT_int8 = 2, ///< 1-byte/char signed type
VT_int16 = 3, ///< 2-byte/short signed type
VT_int32 = 4, ///< 4-byte/int signed type
VT_int64 = 5, ///< 8-byte/long long int signed type
VT_float = 6, ///< single-precision floating point type
VT_double = 7, ///< double-precision floating point type
VT_string = 8, ///< string type
VT_array = 9, ///< array of TableValues type
VT_table = 10, ///< a table type
VT_uint8 = 11, ///< 1-byte/char unsigned type
VT_uint16 = 12, ///< 2-byte/short unsigned type
VT_uint32 = 13, ///< 4-byte/int unsigned type
VT_timestamp = 14, ///< std::time_t type
};
/**
* Construct void table value
*
* A table value that doesn't have any value associated with it
*/
TableValue();
/**
* Construct a boolean table value
*
* @param [in] value the value
*/
TableValue(bool value);
/**
* Construct a 1-byte unsigned integer value
*
* @param [in] value the value
*/
TableValue(boost::uint8_t value);
/**
* Construct a 1-byte signed integer value
*
* @param [in] value the value
*/
TableValue(boost::int8_t value);
/**
* Construct a 2-byte unsigned integer value
*
* @param [in] value the value
*/
TableValue(boost::uint16_t value);
/**
* Construct a 2-byte signed integer value
*
* @param [in] value the value
*/
TableValue(boost::int16_t value);
/**
* Construct a 4-byte unsigned integer value
*
* @param [in] value the value
*/
TableValue(boost::uint32_t value);
/**
* Construct a 4-byte signed integer value
*
* @param [in] value the value
*/
TableValue(boost::int32_t value);
private:
/**
* Private
*
* RabbitMQ does not support unsigned 64-bit values in tables,
* however, timestamps are used for this.
*/
TableValue(boost::uint64_t value);
public:
/**
* Construct an AMQP timestamp TableValue
*
* This seconds since epoch.
*
* @param [in] value the value
*/
static TableValue Timestamp(std::time_t value);
/**
* Construct a 8-byte signed integer value
*
* @param [in] value the value
*/
TableValue(boost::int64_t value);
/**
* Construct a single-precision floating point value
*
* @param [in] value the value
*/
TableValue(float value);
/**
* Construct a double-precision floating point value
*
* @param [in] value the value
*/
TableValue(double value);
/**
* Construct a character string value
*
* @param [in] value the value
*/
TableValue(const char *value);
/**
* Construct a character string value
*
* @param [in] value the value
*/
TableValue(const std::string &value);
/**
* Construct an array value
*
* @param [in] values the value
*/
TableValue(const std::vector<TableValue> &values);
/**
* Construct a Table value
*
* @param [in] value the value
*/
TableValue(const Table &value);
/**
* Copy-constructor
*/
TableValue(const TableValue &l);
/**
* Assignment operator
*/
TableValue &operator=(const TableValue &l);
/**
* Equality operator
*/
bool operator==(const TableValue &l) const;
/**
* In-equality operator
*/
bool operator!=(const TableValue &l) const;
/**
* Destructor
*/
virtual ~TableValue();
/**
* Get the type
*/
ValueType GetType() const;
/**
* Get the boolean value
*
* @returns the value if its a VT_bool type, false otherwise
*/
bool GetBool() const;
/**
* Get the uint8 value
*
* @returns the value if its a VT_uint8 type, 0 otherwise
*/
boost::uint8_t GetUint8() const;
/**
* Get the int8 value
*
* @returns the value if its a VT_int8 type, 0 otherwise
*/
boost::int8_t GetInt8() const;
/**
* Get the uint16 value
*
* @returns the value if its a VT_uint16 type, 0 otherwise
*/
boost::uint16_t GetUint16() const;
/**
* Get the int16 value
*
* @returns the value if its a VT_int16 type, 0 otherwise
*/
boost::int16_t GetInt16() const;
/**
* Get the uint32 value
*
* @returns the value if its a VT_uint32 type, 0 otherwise
*/
boost::uint32_t GetUint32() const;
/**
* Get the int32 value
*
* @returns the value if its a VT_int32 type, 0 otherwise
*/
boost::int32_t GetInt32() const;
/**
* Get the uint64 value
*
* @returns the value if its a VT_uint64 type, 0 otherwise
*/
boost::uint64_t GetUint64() const;
/**
* Get the timestamp value
*
* @returns the value if its a VT_timestamp type, 0 otherwise
*/
std::time_t GetTimestamp() const;
/**
* Get the int64 value
*
* @returns the value if its a VT_int64 type, 0 otherwise
*/
boost::int64_t GetInt64() const;
/**
* Get an integral number
*
* Works for uint64 up to std::numeric_limits<int64_t>::max(),
* will throw a std::overflow_error otherwise. If the entire range
* of uint64_t is possible, please use GetUint64()
*
* @returns an integer number if the ValueType is VT_uint8, VT_int8,
* VT_uint16, VT_int16, VT_uint32, VT_int32,or VT_int64 type, 0 otherwise.
*/
boost::int64_t GetInteger() const;
/**
* Get a float value
*
* @returns the value if its a VT_float type, 0. otherwise
*/
float GetFloat() const;
/**
* Get a double value
*
* @returns the value if its a VT_double type, 0. otherwise
*/
double GetDouble() const;
/**
* Get a floating-point value
*
* @returns the value if its a VT_float or VT_double type, 0. otherwise
*/
double GetReal() const;
/**
* Get a string value
*
* @returns the value if its a VT_string type, an empty string otherwise
*/
std::string GetString() const;
/**
* Gets an array
*
* @returns the value if its a VT_array type, an empty array otherwise
*/
std::vector<TableValue> GetArray() const;
/**
* Gets a table
*
* @returns the value if its a VT_table type, an empty table otherwise
*/
Table GetTable() const;
/**
* Sets the value as a void value
*/
void Set();
/**
* Set the value as a boolean
*
* @param [in] value the value
*/
void Set(bool value);
/**
* Set the value as a uint8_t
*
* @param [in] value the value
*/
void Set(boost::uint8_t value);
/**
* Set the value as a int8_t
*
* @param [in] value the value
*/
void Set(boost::int8_t value);
/**
* Set the value as a uint16_t
*
* @param [in] value the value
*/
void Set(boost::uint16_t value);
/**
* Set the value as a int16_t
*
* @param [in] value the value
*/
void Set(boost::int16_t value);
/**
* Set the value as a uint32_t
*
* @param [in] value the value
*/
void Set(boost::uint32_t value);
/**
* Set the value as a int32_t
*
* @param [in] value the value
*/
void Set(boost::int32_t value);
/**
* Set the value as a timestamp.
*
* @param [in] value the value
*/
void SetTimestamp(std::time_t value);
/**
* Set the value as a int64_t
*
* @param [in] value the value
*/
void Set(boost::int64_t value);
/**
* Set the value as a float
*
* @param [in] value the value
*/
void Set(float value);
/**
* Set the value as a double
*
* @param [in] value the value
*/
void Set(double value);
/**
* Set the value as a string
*
* @param [in] value the value
*/
void Set(const char *value);
/**
* Set the value as a string
*
* @param [in] value the value
*/
void Set(const std::string &value);
/**
* Set the value as an array
*
* @param [in] value the value
*/
void Set(const std::vector<TableValue> &value);
/**
* Set the value as a table
*
* @param [in] value the value
*/
void Set(const Table &value);
private:
boost::scoped_ptr<Detail::TableValueImpl> m_impl;
};
} // namespace AmqpClient
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // SIMPLEAMQPCLIENT_TABLE_H

View File

@@ -0,0 +1,113 @@
#ifndef SIMPLEAMQPCLIENT_TABLEIMPL_H
#define SIMPLEAMQPCLIENT_TABLEIMPL_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <amqp.h>
#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/variant/variant.hpp>
#include <ctime>
#include <string>
#include <vector>
#include "SimpleAmqpClient/Table.h"
namespace AmqpClient {
namespace Detail {
typedef boost::shared_ptr<amqp_pool_t> amqp_pool_ptr_t;
struct void_t {};
inline bool operator==(const void_t &, const void_t &) { return true; }
typedef std::vector<TableValue> array_t;
typedef boost::variant<void_t, bool, boost::int8_t, boost::int16_t,
boost::int32_t, boost::int64_t, float, double,
std::string, array_t, Table, boost::uint8_t,
boost::uint16_t, boost::uint32_t, boost::uint64_t>
value_t;
class TableValueImpl {
public:
explicit TableValueImpl(const value_t &v) : m_value(v) {}
virtual ~TableValueImpl() {}
value_t m_value;
static amqp_table_t CreateAmqpTable(const Table &table,
amqp_pool_ptr_t &pool);
static Table CreateTable(const amqp_table_t &table);
static amqp_table_t CopyTable(const amqp_table_t &table,
amqp_pool_ptr_t &pool);
private:
static amqp_table_t CreateAmqpTableInner(const Table &table,
amqp_pool_t &pool);
static TableValue CreateTableValue(const amqp_field_value_t &entry);
static amqp_table_t CopyTableInner(const amqp_table_t &table,
amqp_pool_t &pool);
static amqp_field_value_t CopyValue(const amqp_field_value_t value,
amqp_pool_t &pool);
public:
class generate_field_value
: public boost::static_visitor<amqp_field_value_t> {
public:
explicit generate_field_value(amqp_pool_t &p) : pool(p) {}
virtual ~generate_field_value() {}
amqp_field_value_t operator()(const void_t) const;
amqp_field_value_t operator()(const bool value) const;
amqp_field_value_t operator()(const boost::uint8_t value) const;
amqp_field_value_t operator()(const boost::int8_t value) const;
amqp_field_value_t operator()(const boost::uint16_t value) const;
amqp_field_value_t operator()(const boost::int16_t value) const;
amqp_field_value_t operator()(const boost::uint32_t value) const;
amqp_field_value_t operator()(const boost::int32_t value) const;
amqp_field_value_t operator()(const boost::uint64_t value) const;
amqp_field_value_t operator()(const boost::int64_t value) const;
amqp_field_value_t operator()(const float value) const;
amqp_field_value_t operator()(const double value) const;
amqp_field_value_t operator()(const std::string &value) const;
amqp_field_value_t operator()(const array_t &value) const;
amqp_field_value_t operator()(const Table &value) const;
private:
amqp_pool_t &pool;
};
};
} // namespace Detail
} // namespace AmqpClient
#endif // SIMPLEAMQPCLIENT_TABLEIMPL_H

View File

@@ -0,0 +1,49 @@
#ifndef SIMPLEAMQPCLIENT_UTIL_H
#define SIMPLEAMQPCLIENT_UTIL_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#if defined(WIN32) && !defined(SimpleAmqpClient_STATIC)
# ifdef SimpleAmqpClient_EXPORTS
# define SIMPLEAMQPCLIENT_EXPORT __declspec(dllexport)
# else
# define SIMPLEAMQPCLIENT_EXPORT __declspec(dllimport)
# endif
#else
# define SIMPLEAMQPCLIENT_EXPORT
#endif
#if defined(__GNUC__) || defined(__clang__)
#define SAC_DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
#define SAC_DEPRECATED(msg) __declspec(deprecated(msg))
#else
#define SAC_DEPRECATED(msg)
#endif
#endif // SIMPLEAMQPCLIENT_UTIL_H

View File

@@ -0,0 +1,35 @@
#ifndef SIMPLEAMQPCLIENT_VERSION_H
#define SIMPLEAMQPCLIENT_VERSION_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#define SIMPLEAMQPCLIENT_VERSION_MAJOR 2
#define SIMPLEAMQPCLIENT_VERSION_MINOR 6
#define SIMPLEAMQPCLIENT_VERSION_PATCH 0
#endif // SIMPLEAMQPCLIENT_VERSION_H

264
src/Table.cpp Normal file
View File

@@ -0,0 +1,264 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "SimpleAmqpClient/Table.h"
#include <algorithm>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/variant/get.hpp>
#include <ctime>
#include <iterator>
#include <limits>
#include <stdexcept>
#include "SimpleAmqpClient/TableImpl.h"
namespace AmqpClient {
TableValue::TableValue()
: m_impl(new Detail::TableValueImpl(Detail::void_t())) {}
TableValue::TableValue(bool value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(boost::uint8_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(boost::int8_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(boost::uint16_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(boost::int16_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(boost::uint32_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(boost::int32_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(boost::uint64_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue TableValue::Timestamp(std::time_t ts) {
return TableValue(static_cast<boost::uint64_t>(ts));
}
TableValue::TableValue(boost::int64_t value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(float value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(double value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(const char *value)
: m_impl(new Detail::TableValueImpl(std::string(value))) {}
TableValue::TableValue(const std::string &value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(const std::vector<TableValue> &values)
: m_impl(new Detail::TableValueImpl(values)) {}
TableValue::TableValue(const Table &value)
: m_impl(new Detail::TableValueImpl(value)) {}
TableValue::TableValue(const TableValue &l)
: m_impl(new Detail::TableValueImpl(l.m_impl->m_value)) {}
TableValue &TableValue::operator=(const TableValue &l) {
if (this != &l) {
m_impl->m_value = l.m_impl->m_value;
}
return *this;
}
bool operator==(const Array &l, const Array &r) {
if (l.size() == r.size()) {
return std::equal(l.begin(), l.end(), r.begin());
}
return false;
}
bool operator==(const Table &l, const Table &r) {
if (l.size() == r.size()) {
return std::equal(l.begin(), l.end(), r.begin());
}
return false;
}
bool TableValue::operator==(const TableValue &l) const {
if (this == &l) {
return true;
}
return m_impl->m_value == l.m_impl->m_value;
}
bool TableValue::operator!=(const TableValue &l) const {
if (this == &l) {
return false;
}
return !(m_impl->m_value == l.m_impl->m_value);
}
TableValue::~TableValue() {}
TableValue::ValueType TableValue::GetType() const {
return static_cast<ValueType>(m_impl->m_value.which());
}
bool TableValue::GetBool() const { return boost::get<bool>(m_impl->m_value); }
boost::uint8_t TableValue::GetUint8() const {
return boost::get<boost::uint8_t>(m_impl->m_value);
}
boost::int8_t TableValue::GetInt8() const {
return boost::get<boost::int8_t>(m_impl->m_value);
}
boost::uint16_t TableValue::GetUint16() const {
return boost::get<boost::uint16_t>(m_impl->m_value);
}
boost::int16_t TableValue::GetInt16() const {
return boost::get<boost::int16_t>(m_impl->m_value);
}
boost::uint32_t TableValue::GetUint32() const {
return boost::get<boost::uint32_t>(m_impl->m_value);
}
boost::int32_t TableValue::GetInt32() const {
return boost::get<boost::int32_t>(m_impl->m_value);
}
std::time_t TableValue::GetTimestamp() const {
return static_cast<std::time_t>(boost::get<boost::uint64_t>(m_impl->m_value));
}
boost::int64_t TableValue::GetInt64() const {
return boost::get<boost::int64_t>(m_impl->m_value);
}
boost::int64_t TableValue::GetInteger() const {
switch (m_impl->m_value.which()) {
case VT_uint8:
return GetUint8();
case VT_int8:
return GetInt8();
case VT_uint16:
return GetUint16();
case VT_int16:
return GetInt16();
case VT_uint32:
return GetUint32();
case VT_int32:
return GetInt32();
case VT_int64:
return GetInt64();
default:
throw boost::bad_get();
}
}
float TableValue::GetFloat() const {
return boost::get<float>(m_impl->m_value);
}
double TableValue::GetDouble() const {
return boost::get<double>(m_impl->m_value);
}
double TableValue::GetReal() const {
switch (m_impl->m_value.which()) {
case VT_float:
return GetFloat();
case VT_double:
return GetDouble();
default:
throw boost::bad_get();
}
}
std::string TableValue::GetString() const {
return boost::get<std::string>(m_impl->m_value);
}
std::vector<TableValue> TableValue::GetArray() const {
return boost::get<Detail::array_t>(m_impl->m_value);
}
Table TableValue::GetTable() const {
return boost::get<Table>(m_impl->m_value);
}
void TableValue::Set() { m_impl->m_value = Detail::void_t(); }
void TableValue::Set(bool value) { m_impl->m_value = value; }
void TableValue::Set(boost::uint8_t value) { m_impl->m_value = value; }
void TableValue::Set(boost::int8_t value) { m_impl->m_value = value; }
void TableValue::Set(boost::uint16_t value) { m_impl->m_value = value; }
void TableValue::Set(boost::int16_t value) { m_impl->m_value = value; }
void TableValue::Set(boost::uint32_t value) { m_impl->m_value = value; }
void TableValue::Set(boost::int32_t value) { m_impl->m_value = value; }
void TableValue::SetTimestamp(std::time_t value) {
m_impl->m_value = static_cast<boost::uint64_t>(value);
}
void TableValue::Set(boost::int64_t value) { m_impl->m_value = value; }
void TableValue::Set(float value) { m_impl->m_value = value; }
void TableValue::Set(double value) { m_impl->m_value = value; }
void TableValue::Set(const char *value) {
m_impl->m_value = std::string(value);
}
void TableValue::Set(const std::string &value) { m_impl->m_value = value; }
void TableValue::Set(const std::vector<TableValue> &value) {
m_impl->m_value = value;
}
void TableValue::Set(const Table &value) { m_impl->m_value = value; }
} // namespace AmqpClient

361
src/TableImpl.cpp Normal file
View File

@@ -0,0 +1,361 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif
#include "SimpleAmqpClient/TableImpl.h"
#include <amqp.h>
#include <string.h>
#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
#include <new>
#ifdef _MSC_VER
#pragma warning(disable : 4800)
#endif
namespace AmqpClient {
namespace Detail {
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const void_t) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_VOID;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const bool value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_BOOLEAN;
v.value.boolean = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::uint8_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_U8;
v.value.u8 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::int8_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_I8;
v.value.i8 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::uint16_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_U16;
v.value.u16 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::int16_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_I16;
v.value.i16 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::uint32_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_U32;
v.value.u32 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::int32_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_I32;
v.value.i32 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::uint64_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_TIMESTAMP;
v.value.u64 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const boost::int64_t value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_I64;
v.value.i64 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const float value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_F32;
v.value.f32 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const double value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_F64;
v.value.f64 = value;
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const std::string &value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_UTF8;
amqp_pool_alloc_bytes(&pool, value.size(), &v.value.bytes);
memcpy(v.value.bytes.bytes, value.data(), v.value.bytes.len);
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const array_t &value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_ARRAY;
v.value.array.num_entries = value.size();
v.value.array.entries = (amqp_field_value_t *)amqp_pool_alloc(
&pool, sizeof(amqp_field_value_t) * value.size());
if (NULL == v.value.array.entries) {
throw std::bad_alloc();
}
amqp_field_value_t *output_iterator = v.value.array.entries;
for (array_t::const_iterator it = value.begin(); it != value.end();
++it, ++output_iterator) {
*output_iterator =
boost::apply_visitor(generate_field_value(pool), it->m_impl->m_value);
}
return v;
}
amqp_field_value_t TableValueImpl::generate_field_value::operator()(
const Table &value) const {
amqp_field_value_t v;
v.kind = AMQP_FIELD_KIND_TABLE;
v.value.table = CreateAmqpTableInner(value, pool);
return v;
}
void free_pool(amqp_pool_t *pool) {
empty_amqp_pool(pool);
delete pool;
}
amqp_table_t TableValueImpl::CreateAmqpTable(const Table &table,
amqp_pool_ptr_t &pool) {
if (0 == table.size()) {
return AMQP_EMPTY_TABLE;
}
pool = boost::shared_ptr<amqp_pool_t>(new amqp_pool_t, free_pool);
init_amqp_pool(pool.get(), 1024);
return CreateAmqpTableInner(table, *pool.get());
}
amqp_table_t TableValueImpl::CreateAmqpTableInner(const Table &table,
amqp_pool_t &pool) {
amqp_table_t new_table;
new_table.num_entries = table.size();
new_table.entries = (amqp_table_entry_t *)amqp_pool_alloc(
&pool, sizeof(amqp_table_entry_t) * table.size());
if (NULL == new_table.entries) {
throw std::bad_alloc();
}
amqp_table_entry_t *output_it = new_table.entries;
for (Table::const_iterator it = table.begin(); it != table.end();
++it, ++output_it) {
amqp_pool_alloc_bytes(&pool, it->first.size(), &output_it->key);
if (NULL == output_it->key.bytes) {
throw std::bad_alloc();
}
std::copy(it->first.begin(), it->first.end(), (char *)output_it->key.bytes);
output_it->value = boost::apply_visitor(
TableValueImpl::generate_field_value(pool), it->second.m_impl->m_value);
}
return new_table;
}
Table TableValueImpl::CreateTable(const amqp_table_t &table) {
Table new_table;
for (int i = 0; i < table.num_entries; ++i) {
amqp_table_entry_t *entry = &table.entries[i];
std::string key((char *)entry->key.bytes, entry->key.len);
new_table.insert(TableEntry(key, CreateTableValue(entry->value)));
}
return new_table;
}
TableValue TableValueImpl::CreateTableValue(const amqp_field_value_t &entry) {
switch (entry.kind) {
case AMQP_FIELD_KIND_VOID:
return TableValue();
case AMQP_FIELD_KIND_BOOLEAN:
return TableValue((bool)entry.value.boolean);
case AMQP_FIELD_KIND_U8:
return TableValue(entry.value.u8);
case AMQP_FIELD_KIND_I8:
return TableValue(entry.value.i8);
case AMQP_FIELD_KIND_U16:
return TableValue(entry.value.u16);
case AMQP_FIELD_KIND_I16:
return TableValue(entry.value.i16);
case AMQP_FIELD_KIND_U32:
return TableValue(entry.value.u32);
case AMQP_FIELD_KIND_I32:
return TableValue(entry.value.i32);
case AMQP_FIELD_KIND_TIMESTAMP:
return TableValue(entry.value.u64);
case AMQP_FIELD_KIND_I64:
return TableValue(entry.value.i64);
case AMQP_FIELD_KIND_F32:
return TableValue(entry.value.f32);
case AMQP_FIELD_KIND_F64:
return TableValue(entry.value.f64);
case AMQP_FIELD_KIND_UTF8:
case AMQP_FIELD_KIND_BYTES:
return TableValue(
std::string((char *)entry.value.bytes.bytes, entry.value.bytes.len));
case AMQP_FIELD_KIND_ARRAY: {
amqp_array_t array = entry.value.array;
Detail::array_t new_array;
for (int i = 0; i < array.num_entries; ++i) {
new_array.push_back(CreateTableValue(array.entries[i]));
}
return TableValue(new_array);
}
case AMQP_FIELD_KIND_TABLE:
return TableValue(CreateTable(entry.value.table));
case AMQP_FIELD_KIND_DECIMAL:
// uint64_t is unsupported by RabbitMQ.
case AMQP_FIELD_KIND_U64:
default:
return TableValue();
}
}
amqp_table_t TableValueImpl::CopyTable(const amqp_table_t &table,
amqp_pool_ptr_t &pool) {
if (0 == table.num_entries) {
return AMQP_EMPTY_TABLE;
}
pool = boost::shared_ptr<amqp_pool_t>(new amqp_pool_t, free_pool);
init_amqp_pool(pool.get(), 1024);
return CopyTableInner(table, *pool.get());
}
amqp_table_t TableValueImpl::CopyTableInner(const amqp_table_t &table,
amqp_pool_t &pool) {
amqp_table_t new_table;
new_table.num_entries = table.num_entries;
new_table.entries = (amqp_table_entry_t *)amqp_pool_alloc(
&pool, sizeof(amqp_table_entry_t) * table.num_entries);
if (NULL == new_table.entries) {
throw std::bad_alloc();
}
for (int i = 0; i < table.num_entries; ++i) {
amqp_table_entry_t *entry = &new_table.entries[i];
amqp_pool_alloc_bytes(&pool, table.entries[i].key.len, &entry->key);
if (NULL == entry->key.bytes) {
throw std::bad_alloc();
}
memcpy(entry->key.bytes, table.entries[i].key.bytes, entry->key.len);
entry->value = CopyValue(table.entries[i].value, pool);
}
return new_table;
}
amqp_field_value_t TableValueImpl::CopyValue(const amqp_field_value_t value,
amqp_pool_t &pool) {
amqp_field_value_t new_value = value;
switch (value.kind) {
case AMQP_FIELD_KIND_UTF8:
case AMQP_FIELD_KIND_BYTES:
amqp_pool_alloc_bytes(&pool, value.value.bytes.len,
&new_value.value.bytes);
memcpy(new_value.value.bytes.bytes, value.value.bytes.bytes,
value.value.bytes.len);
return new_value;
case AMQP_FIELD_KIND_ARRAY: {
new_value.value.array.entries = (amqp_field_value_t *)amqp_pool_alloc(
&pool, sizeof(amqp_field_value_t) * value.value.array.num_entries);
for (int i = 0; i < value.value.array.num_entries; ++i) {
new_value.value.array.entries[i] =
CopyValue(value.value.array.entries[i], pool);
}
return new_value;
}
case AMQP_FIELD_KIND_TABLE:
new_value.value.table = CopyTableInner(value.value.table, pool);
return new_value;
default:
return new_value;
}
}
} // namespace Detail
} // namespace AmqpClient

19
testing/CMakeLists.txt Normal file
View File

@@ -0,0 +1,19 @@
include_directories(BEFORE SYSTEM ${gtest_SOURCE_DIR}/include)
include_directories(../src)
add_executable(test_api
connected_test.h
test_connect.cpp
test_channels.cpp
test_exchange.cpp
test_queue.cpp
test_publish.cpp
test_get.cpp
test_consume.cpp
test_message.cpp
test_table.cpp
test_ack.cpp
test_nack.cpp
)
target_link_libraries(test_api SimpleAmqpClient gtest gtest_main)
add_test(test_api test_api)

58
testing/connected_test.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef CONNECTED_TEST_H
#define CONNECTED_TEST_H
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <SimpleAmqpClient/SimpleAmqpClient.h>
#include <gtest/gtest.h>
using namespace AmqpClient;
class connected_test : public ::testing::Test {
public:
virtual void SetUp() { channel = Channel::Open(GetTestOpenOpts()); }
Channel::ptr_t channel;
static Channel::OpenOpts GetTestOpenOpts() {
Channel::OpenOpts ret;
ret.host = GetBrokerHost();
ret.auth = Channel::OpenOpts::BasicAuth("guest", "guest");
return ret;
}
static std::string GetBrokerHost() {
const char *host = getenv("AMQP_BROKER");
if (NULL != host) {
return std::string(host);
}
return std::string("");
}
};
#endif // CONNECTED_TEST_H

61
testing/test_ack.cpp Normal file
View File

@@ -0,0 +1,61 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <iostream>
#include "connected_test.h"
using namespace AmqpClient;
TEST_F(connected_test, basic_ack_envelope) {
const BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message);
std::string consumer = channel->BasicConsume(queue, "", true, false);
Envelope::ptr_t env = channel->BasicConsumeMessage(consumer);
channel->BasicAck(env);
}
TEST_F(connected_test, basic_ack_deliveryinfo) {
const BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message);
std::string consumer = channel->BasicConsume(queue, "", true, false);
Envelope::DeliveryInfo info;
{
Envelope::ptr_t env = channel->BasicConsumeMessage(consumer);
info = env->GetDeliveryInfo();
}
channel->BasicAck(info);
}

147
testing/test_channels.cpp Normal file
View File

@@ -0,0 +1,147 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "connected_test.h"
using namespace AmqpClient;
TEST_F(connected_test, first_channel) {
channel->DeclareExchange("test_channel_exchange",
Channel::EXCHANGE_TYPE_FANOUT, false, false, true);
channel->DeleteExchange("test_channel_exchange");
}
// Check to see that channels are reused properly
TEST_F(connected_test, channel_reuse) {
channel->DeclareExchange("test_channel_exchange1",
Channel::EXCHANGE_TYPE_FANOUT, false, false, true);
channel->DeclareExchange("test_channel_exchange2",
Channel::EXCHANGE_TYPE_FANOUT, false, false, true);
channel->DeleteExchange("test_channel_exchange1");
channel->DeleteExchange("test_channel_exchange2");
}
// Check to see that a new channel is created when a channel is put in an
// exception state
TEST_F(connected_test, channel_recover_from_error) {
EXPECT_THROW(channel->DeclareExchange("test_channel_exchangedoesnotexist",
Channel::EXCHANGE_TYPE_FANOUT, true,
false, true),
ChannelException);
channel->DeclareExchange("test_channel_exchange",
Channel::EXCHANGE_TYPE_FANOUT, false, false, true);
channel->DeleteExchange("test_channel_exchange");
}
TEST_F(connected_test, channel_publish_success1) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
channel->BasicPublish("", "test_channel_routingkey", message, false, false);
}
TEST_F(connected_test, channel_publish_success2) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
channel->BasicPublish("", "test_channel_routingkey", message, false, false);
channel->BasicPublish("", "test_channel_routingkey", message, false, false);
}
TEST_F(connected_test, channel_publish_returned_mandatory) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
EXPECT_THROW(
channel->BasicPublish("", "test_channel_noqueue", message, true, false),
MessageReturnedException);
}
TEST_F(connected_test, channel_publish_full_rejected) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
Table args;
args.insert(TableEntry("x-max-length", 0));
args.insert(TableEntry("x-overflow", "reject-publish"));
std::string queue = channel->DeclareQueue("", false, false, true, true, args);
EXPECT_THROW(channel->BasicPublish("", queue, message),
MessageRejectedException);
}
TEST_F(connected_test, channel_publish_bad_exchange) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
EXPECT_THROW(channel->BasicPublish("test_channel_badexchange",
"test_channel_rk", message, false, false),
ChannelException);
}
TEST_F(connected_test, channel_publish_bad_exchange_recover) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
EXPECT_THROW(channel->BasicPublish("test_channel_badexchange",
"test_channel_rk", message, false, false),
ChannelException);
channel->BasicPublish("", "test_channel_rk", message, false, false);
}
TEST_F(connected_test, channel_consume_success) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message);
std::string consumer = channel->BasicConsume(queue);
Envelope::ptr_t consumed_envelope;
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, consumed_envelope));
}
TEST_F(connected_test, channel_consume_success_timeout) {
BasicMessage::ptr_t message = BasicMessage::Create("Test message");
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue, "", true, false);
channel->BasicPublish("", queue, message);
Envelope::ptr_t consumed_envelope;
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, consumed_envelope, 5000));
}
TEST(test_channels, big_message) {
Channel::OpenOpts opts = connected_test::GetTestOpenOpts();
opts.frame_max = 4096;
Channel::ptr_t channel = Channel::Open(opts);
BasicMessage::ptr_t message = BasicMessage::Create(std::string(4099, 'a'));
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue);
channel->BasicPublish("", queue, message);
Envelope::ptr_t consumed_envelope;
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, consumed_envelope));
}

131
testing/test_connect.cpp Normal file
View File

@@ -0,0 +1,131 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <gtest/gtest.h>
#include "SimpleAmqpClient/SimpleAmqpClient.h"
#include "connected_test.h"
using namespace AmqpClient;
TEST(connecting_test, connect_default) {
Channel::ptr_t channel = Channel::Create(connected_test::GetBrokerHost());
}
TEST(connecting_test, connect_badhost) {
EXPECT_THROW(Channel::ptr_t channel = Channel::Create("HostDoesntExist"),
std::runtime_error);
}
TEST(connecting_test, open_badhost) {
Channel::OpenOpts opts = connected_test::GetTestOpenOpts();
opts.host = "HostDoesNotExist";
EXPECT_THROW(Channel::ptr_t channel = Channel::Open(opts),
std::runtime_error);
}
TEST(connecting_test, connect_badauth) {
EXPECT_THROW(Channel::ptr_t channel = Channel::Create(
connected_test::GetBrokerHost(), 5672, "baduser", "badpass"),
AccessRefusedException);
}
TEST(connecting_test, open_badauth) {
Channel::OpenOpts opts = connected_test::GetTestOpenOpts();
opts.auth = Channel::OpenOpts::BasicAuth("baduser", "badpass");
EXPECT_THROW(Channel::ptr_t channel = Channel::Open(opts),
AccessRefusedException);
}
TEST(connecting_test, connect_badframesize) {
// AMQP Spec says we have a minimum frame size of 4096
EXPECT_THROW(
Channel::ptr_t channel = Channel::Create(
connected_test::GetBrokerHost(), 5672, "guest", "guest", "/", 400),
AmqpResponseLibraryException);
}
TEST(connecting_test, open_badframesize) {
// AMQP Spec says we have a minimum frame size of 4096
Channel::OpenOpts opts = connected_test::GetTestOpenOpts();
opts.frame_max = 400;
EXPECT_THROW(Channel::ptr_t channel = Channel::Open(opts),
AmqpResponseLibraryException);
}
TEST(connecting_test, connect_badvhost) {
EXPECT_THROW(Channel::ptr_t channel =
Channel::Create(connected_test::GetBrokerHost(), 5672,
"guest", "guest", "nonexitant_vhost"),
NotAllowedException);
}
TEST(connecting_test, open_badvhost) {
Channel::OpenOpts opts = connected_test::GetTestOpenOpts();
opts.vhost = "bad_vhost";
EXPECT_THROW(Channel::ptr_t channel = Channel::Open(opts),
NotAllowedException);
}
TEST(connecting_test, connect_using_uri) {
std::string host_uri = "amqp://" + connected_test::GetBrokerHost();
Channel::ptr_t channel = Channel::CreateFromUri(host_uri);
}
TEST(connecting_test, openopts_from_uri) {
Channel::OpenOpts expected;
expected.host = "host";
expected.vhost = "vhost";
expected.port = 123;
expected.auth = Channel::OpenOpts::BasicAuth("user", "pass");
EXPECT_EQ(expected,
Channel::OpenOpts::FromUri("amqp://user:pass@host:123/vhost"));
}
TEST(connecting_test, openopts_from_uri_defaults) {
Channel::OpenOpts expected;
expected.host = "host";
expected.vhost = "/";
expected.port = 5672;
expected.auth = Channel::OpenOpts::BasicAuth("guest", "guest");
EXPECT_EQ(expected, Channel::OpenOpts::FromUri("amqp://host"));
}
TEST(connecting_test, openopts_from_amqps_uri) {
Channel::OpenOpts expected;
expected.host = "host";
expected.vhost = "vhost";
expected.port = 123;
expected.auth = Channel::OpenOpts::BasicAuth("user", "pass");
expected.tls_params = Channel::OpenOpts::TLSParams();
}
TEST(connecting_test, openopts_fromuri_bad) {
EXPECT_THROW(Channel::OpenOpts::FromUri("not-a-valid-uri"), BadUriException);
}

228
testing/test_consume.cpp Normal file
View File

@@ -0,0 +1,228 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <iostream>
#include "connected_test.h"
using namespace AmqpClient;
TEST_F(connected_test, basic_consume) {
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue);
}
TEST_F(connected_test, basic_consume_badqueue) {
EXPECT_THROW(channel->BasicConsume("test_consume_noexistqueue"),
ChannelException);
}
TEST_F(connected_test, basic_consume_duplicatetag) {
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue);
EXPECT_THROW(channel->BasicConsume(queue, consumer), ChannelException);
}
TEST_F(connected_test, basic_cancel_consumer) {
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue);
channel->BasicCancel(consumer);
}
TEST_F(connected_test, basic_cancel_bad_consumer) {
EXPECT_THROW(channel->BasicCancel("test_consume_noexistconsumer"),
ConsumerTagNotFoundException);
}
TEST_F(connected_test, basic_cancel_cancelled_consumer) {
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue);
channel->BasicCancel(consumer);
EXPECT_THROW(channel->BasicCancel(consumer), ConsumerTagNotFoundException);
}
TEST_F(connected_test, basic_consume_message) {
BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue);
channel->BasicPublish("", queue, message);
Envelope::ptr_t delivered;
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, delivered, -1));
EXPECT_EQ(consumer, delivered->ConsumerTag());
EXPECT_EQ("", delivered->Exchange());
EXPECT_EQ(queue, delivered->RoutingKey());
EXPECT_EQ(message->Body(), delivered->Message()->Body());
}
TEST_F(connected_test, basic_consume_message_bad_consumer) {
EXPECT_THROW(channel->BasicConsumeMessage("test_consume_noexistconsumer"),
ConsumerTagNotFoundException);
}
TEST_F(connected_test, basic_consume_inital_qos) {
BasicMessage::ptr_t message1 = BasicMessage::Create("Message1");
BasicMessage::ptr_t message2 = BasicMessage::Create("Message2");
BasicMessage::ptr_t message3 = BasicMessage::Create("Message3");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message1, true);
channel->BasicPublish("", queue, message2, true);
channel->BasicPublish("", queue, message3, true);
std::string consumer = channel->BasicConsume(queue, "", true, false);
Envelope::ptr_t received1, received2;
ASSERT_TRUE(channel->BasicConsumeMessage(consumer, received1, 100));
EXPECT_FALSE(channel->BasicConsumeMessage(consumer, received2, 100));
channel->BasicAck(received1);
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, received2, 100));
}
TEST_F(connected_test, basic_consume_2consumers) {
BasicMessage::ptr_t message1 = BasicMessage::Create("Message1");
BasicMessage::ptr_t message2 = BasicMessage::Create("Message2");
BasicMessage::ptr_t message3 = BasicMessage::Create("Message3");
std::string queue1 = channel->DeclareQueue("");
std::string queue2 = channel->DeclareQueue("");
std::string queue3 = channel->DeclareQueue("");
channel->BasicPublish("", queue1, message1);
channel->BasicPublish("", queue2, message2);
channel->BasicPublish("", queue3, message3);
std::string consumer1 = channel->BasicConsume(queue1, "", true, false);
std::string consumer2 = channel->BasicConsume(queue2, "", true, false);
Envelope::ptr_t envelope1;
Envelope::ptr_t envelope2;
Envelope::ptr_t envelope3;
channel->BasicConsumeMessage(consumer1, envelope1);
channel->BasicAck(envelope1);
channel->BasicConsumeMessage(consumer2, envelope2);
channel->BasicAck(envelope2);
channel->BasicGet(envelope3, queue3);
channel->BasicAck(envelope3);
}
TEST_F(connected_test, basic_consume_1000messages) {
BasicMessage::ptr_t message1 = BasicMessage::Create("Message1");
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue, "");
Envelope::ptr_t msg;
for (int i = 0; i < 1000; ++i) {
message1->Timestamp(i);
channel->BasicPublish("", queue, message1, true);
channel->BasicConsumeMessage(consumer, msg);
}
}
TEST_F(connected_test, basic_recover) {
BasicMessage::ptr_t message = BasicMessage::Create("Message1");
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue, "", true, false);
channel->BasicPublish("", queue, message);
Envelope::ptr_t message1;
Envelope::ptr_t message2;
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, message1));
channel->BasicRecover(consumer);
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, message2));
channel->DeleteQueue(queue);
}
TEST_F(connected_test, basic_recover_badconsumer) {
EXPECT_THROW(channel->BasicRecover("consumer_notexist"),
ConsumerTagNotFoundException);
}
TEST_F(connected_test, basic_qos) {
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue, "", true, false);
channel->BasicPublish("", queue, BasicMessage::Create("Message1"));
channel->BasicPublish("", queue, BasicMessage::Create("Message2"));
Envelope::ptr_t incoming;
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, incoming, 100));
EXPECT_FALSE(channel->BasicConsumeMessage(consumer, incoming, 100));
channel->BasicQos(consumer, 2);
EXPECT_TRUE(channel->BasicConsumeMessage(consumer, incoming, 100));
channel->DeleteQueue(queue);
}
TEST_F(connected_test, basic_qos_badconsumer) {
EXPECT_THROW(channel->BasicQos("consumer_notexist", 1),
ConsumerTagNotFoundException);
}
TEST_F(connected_test, consumer_cancelled) {
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue, "", true, false);
channel->DeleteQueue(queue);
EXPECT_THROW(channel->BasicConsumeMessage(consumer),
ConsumerCancelledException);
}
TEST_F(connected_test, consumer_cancelled_one_message) {
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue, "", true, false);
channel->BasicPublish("", queue, BasicMessage::Create("Message"));
channel->BasicConsumeMessage(consumer);
channel->DeleteQueue(queue);
EXPECT_THROW(channel->BasicConsumeMessage(consumer),
ConsumerCancelledException);
}
TEST_F(connected_test, consume_multiple) {
std::string queue1 = channel->DeclareQueue("");
std::string queue2 = channel->DeclareQueue("");
std::string Body = "Message 1";
channel->BasicPublish("", queue1, BasicMessage::Create(Body));
channel->BasicConsume(queue1);
channel->BasicConsume(queue2);
Envelope::ptr_t env = channel->BasicConsumeMessage();
EXPECT_EQ(Body, env->Message()->Body());
}

164
testing/test_exchange.cpp Normal file
View File

@@ -0,0 +1,164 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <SimpleAmqpClient/SimpleAmqpClient.h>
#include <gtest/gtest.h>
#include "connected_test.h"
TEST_F(connected_test, declare_exchange_defaults) {
channel->DeclareExchange("declare_defaults");
channel->DeleteExchange("declare_defaults");
}
TEST_F(connected_test, declare_exchange_direct) {
channel->DeclareExchange("declare_direct", Channel::EXCHANGE_TYPE_DIRECT);
channel->DeleteExchange("declare_direct");
}
TEST_F(connected_test, declare_exchange_fanout) {
channel->DeclareExchange("declare_fanout", Channel::EXCHANGE_TYPE_FANOUT);
channel->DeleteExchange("declare_fanout");
}
TEST_F(connected_test, declare_exchange_topic) {
channel->DeclareExchange("declare_topic", Channel::EXCHANGE_TYPE_TOPIC);
channel->DeleteExchange("declare_topic");
}
TEST_F(connected_test, check_exchange_exists_succeeds) {
channel->DeclareExchange("declare_exists");
EXPECT_TRUE(channel->CheckExchangeExists("declare_exists"));
}
TEST_F(connected_test, declare_exchange_passive_good) {
channel->DeclareExchange("declare_passive", Channel::EXCHANGE_TYPE_DIRECT);
channel->DeclareExchange("declare_passive", Channel::EXCHANGE_TYPE_DIRECT,
true);
channel->DeleteExchange("declare_passive");
}
TEST_F(connected_test, check_exchange_exists_fails) {
EXPECT_FALSE(channel->CheckExchangeExists("declare_notexist"));
}
TEST_F(connected_test, declare_exchange_passive_notexist) {
EXPECT_THROW(channel->DeclareExchange("declare_passive_notexist",
Channel::EXCHANGE_TYPE_DIRECT, true),
ChannelException);
}
TEST_F(connected_test, declare_exchange_typemismatch) {
channel->DeclareExchange("declare_typemismatch",
Channel::EXCHANGE_TYPE_DIRECT);
EXPECT_THROW(channel->DeclareExchange("declare_typemismatch",
Channel::EXCHANGE_TYPE_FANOUT),
ChannelException);
channel->DeleteExchange("declare_typemismatch");
}
TEST_F(connected_test, declare_exchange_typemismatch2) {
channel->DeclareExchange("declare_typemismatch",
Channel::EXCHANGE_TYPE_DIRECT);
EXPECT_THROW(
channel->DeclareExchange("declare_typemismatch",
Channel::EXCHANGE_TYPE_DIRECT, false, true),
ChannelException);
channel->DeleteExchange("declare_typemismatch");
}
TEST_F(connected_test, declare_exchange_durable) {
channel->DeclareExchange("declare_durable", Channel::EXCHANGE_TYPE_DIRECT,
false, true);
channel->DeleteExchange("declare_durable");
}
TEST_F(connected_test, declare_exchange_autodelete) {
channel->DeclareExchange("declare_autodelete", Channel::EXCHANGE_TYPE_DIRECT,
false, false, true);
channel->DeleteExchange("declare_autodelete");
}
TEST_F(connected_test, delete_exchange) {
channel->DeclareExchange("delete_exchange");
channel->DeleteExchange("delete_exchange");
}
TEST_F(connected_test, delete_exhange_ifunused) {
channel->DeclareExchange("exchange_used", Channel::EXCHANGE_TYPE_DIRECT);
channel->DeleteExchange("exchange_used", true);
}
TEST_F(connected_test, delete_exhange_ifused) {
channel->DeclareExchange("exchange_used", Channel::EXCHANGE_TYPE_DIRECT);
std::string queue = channel->DeclareQueue("");
channel->BindQueue(queue, "exchange_used", "whatever");
EXPECT_THROW(channel->DeleteExchange("exchange_used", true),
ChannelException);
channel->DeleteQueue(queue);
channel->DeleteExchange("exchange_used");
}
TEST_F(connected_test, bind_exchange) {
channel->DeclareExchange("exchange_bind_dest");
channel->DeclareExchange("exchange_bind_src");
channel->BindExchange("exchange_bind_dest", "exchange_bind_src", "rk");
channel->DeleteExchange("exchange_bind_dest");
channel->DeleteExchange("exchange_bind_src");
}
TEST_F(connected_test, bind_exchange_badexchange) {
channel->DeclareExchange("exchange_bind_dest");
EXPECT_THROW(channel->BindExchange("exchange_bind_dest",
"exchange_bind_notexist", "rk"),
ChannelException);
channel->DeleteExchange("exchange_bind_dest");
}
TEST_F(connected_test, unbind_exchange) {
channel->DeclareExchange("exchange_bind_dest");
channel->DeclareExchange("exchange_bind_src");
channel->BindExchange("exchange_bind_dest", "exchange_bind_src", "rk");
channel->UnbindExchange("exchange_bind_dest", "exchange_bind_src", "rk");
channel->DeleteExchange("exchange_bind_dest");
channel->DeleteExchange("exchange_bind_src");
}

81
testing/test_get.cpp Normal file
View File

@@ -0,0 +1,81 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "connected_test.h"
using namespace AmqpClient;
TEST_F(connected_test, get_ok) {
BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message, true);
Envelope::ptr_t new_message;
EXPECT_TRUE(channel->BasicGet(new_message, queue));
EXPECT_EQ(message->Body(), new_message->Message()->Body());
}
TEST_F(connected_test, get_empty) {
BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
Envelope::ptr_t new_message;
EXPECT_FALSE(channel->BasicGet(new_message, queue));
}
TEST(test_get, get_big) {
// Smallest frame size allowed by AMQP
Channel::OpenOpts opts = connected_test::GetTestOpenOpts();
opts.frame_max = 4096;
Channel::ptr_t channel = Channel::Open(opts);
// Create a message with a body larger than a single frame
BasicMessage::ptr_t message = BasicMessage::Create(std::string(4099, 'a'));
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message);
Envelope::ptr_t new_message;
EXPECT_TRUE(channel->BasicGet(new_message, queue));
EXPECT_EQ(message->Body(), new_message->Message()->Body());
}
TEST_F(connected_test, bad_queue) {
Envelope::ptr_t new_message;
EXPECT_THROW(channel->BasicGet(new_message, "test_get_nonexistantqueue"),
ChannelException);
}
TEST_F(connected_test, ack_message) {
BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message, true);
Envelope::ptr_t new_message;
EXPECT_TRUE(channel->BasicGet(new_message, queue, false));
channel->BasicAck(new_message);
EXPECT_FALSE(channel->BasicGet(new_message, queue, false));
}

134
testing/test_message.cpp Normal file
View File

@@ -0,0 +1,134 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <amqp.h>
#include <algorithm>
#include <boost/array.hpp>
#include <iostream>
#include "connected_test.h"
using namespace AmqpClient;
TEST(basic_message, empty_message) {
BasicMessage::ptr_t empty_message = BasicMessage::Create();
EXPECT_EQ(std::string(), empty_message->Body());
// Allow the message to destruct
}
TEST(basic_message, empty_message_add_body) {
BasicMessage::ptr_t empty_message = BasicMessage::Create();
EXPECT_EQ(std::string(), empty_message->Body());
const std::string body("Message Body");
empty_message->Body(body);
EXPECT_EQ(body, empty_message->Body());
// Allow the message to destruct
}
TEST(basic_message, empty_message_add_body2) {
BasicMessage::ptr_t empty_message = BasicMessage::Create();
EXPECT_EQ(std::string(), empty_message->Body());
const std::string body("Message Body");
empty_message->Body(body);
EXPECT_EQ(body, empty_message->Body());
const std::string body2("Second body");
empty_message->Body(body2);
EXPECT_EQ(body2, empty_message->Body());
// Allow the message to destruct
}
TEST(basic_message, initial_message_replace) {
const std::string first_body("First message Body");
BasicMessage::ptr_t message = BasicMessage::Create(first_body);
EXPECT_EQ(first_body, message->Body());
const std::string second_body("Second message Body");
message->Body(second_body);
EXPECT_EQ(second_body, message->Body());
// Allow the message to destruct
}
TEST(basic_message, initial_message_replace2) {
const std::string first_body("First message body");
BasicMessage::ptr_t message = BasicMessage::Create(first_body);
EXPECT_EQ(first_body, message->Body());
const std::string second_body("second message body");
message->Body(second_body);
EXPECT_EQ(second_body, message->Body());
const std::string third_body("3rd Body");
message->Body(third_body);
EXPECT_EQ(third_body, message->Body());
}
TEST(basic_message, embedded_nulls) {
const boost::array<char, 7> message_data = {
{'a', 'b', 'c', 0, '1', '2', '3'}};
const std::string body(message_data.data(), message_data.size());
BasicMessage::ptr_t message = BasicMessage::Create(body);
EXPECT_EQ(body, message->Body());
const boost::array<char, 7> message_data2 = {
{'1', '2', '3', 0, 'a', 'b', 'c'}};
const std::string body2(message_data2.data(), message_data2.size());
message->Body(body2);
EXPECT_EQ(body2, message->Body());
}
TEST_F(connected_test, replaced_received_body) {
const std::string queue = channel->DeclareQueue("");
const std::string consumer = channel->BasicConsume(queue);
const std::string body("First Message Body");
BasicMessage::ptr_t out_message = BasicMessage::Create(body);
channel->BasicPublish("", queue, out_message);
Envelope::ptr_t envelope = channel->BasicConsumeMessage(consumer);
BasicMessage::ptr_t in_message = envelope->Message();
EXPECT_EQ(out_message->Body(), in_message->Body());
const std::string body2("Second message body");
in_message->Body(body2);
EXPECT_EQ(body2, in_message->Body());
}

77
testing/test_nack.cpp Normal file
View File

@@ -0,0 +1,77 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <iostream>
#include "connected_test.h"
using namespace AmqpClient;
TEST_F(connected_test, basic_nack_envelope) {
const BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message);
std::string consumer = channel->BasicConsume(queue, "", true, false);
Envelope::ptr_t env = channel->BasicConsumeMessage(consumer);
channel->BasicReject(env, false);
}
TEST_F(connected_test, basic_nack_deliveryinfo) {
const BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message);
std::string consumer = channel->BasicConsume(queue, "", true, false);
Envelope::DeliveryInfo info;
{
Envelope::ptr_t env = channel->BasicConsumeMessage(consumer);
info = env->GetDeliveryInfo();
}
channel->BasicReject(info, false);
}
TEST_F(connected_test, basic_nack_envelope_with_requeue) {
const BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message);
std::string consumer = channel->BasicConsume(queue, "", true, false);
Envelope::ptr_t env = channel->BasicConsumeMessage(consumer);
channel->BasicReject(env, true);
Envelope::ptr_t env2 = channel->BasicConsumeMessage(consumer);
channel->BasicReject(env2, false);
}

88
testing/test_publish.cpp Normal file
View File

@@ -0,0 +1,88 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "connected_test.h"
using namespace AmqpClient;
TEST_F(connected_test, publish_success) {
BasicMessage::ptr_t message = BasicMessage::Create("message body");
channel->BasicPublish("", "test_publish_rk", message);
}
TEST(test_publish, publish_large_message) {
// Smallest frame size allowed by AMQP
Channel::OpenOpts opts = connected_test::GetTestOpenOpts();
opts.frame_max = 4096;
Channel::ptr_t channel = Channel::Open(opts);
// Create a message with a body larger than a single frame
BasicMessage::ptr_t message = BasicMessage::Create(std::string(4099, 'a'));
channel->BasicPublish("", "test_publish_rk", message);
}
TEST_F(connected_test, publish_badexchange) {
BasicMessage::ptr_t message = BasicMessage::Create("message body");
EXPECT_THROW(channel->BasicPublish("test_publish_notexist", "test_publish_rk",
message),
ChannelException);
}
TEST_F(connected_test, publish_recover_from_error) {
BasicMessage::ptr_t message = BasicMessage::Create("message body");
EXPECT_THROW(channel->BasicPublish("test_publish_notexist", "test_publish_rk",
message),
ChannelException);
channel->BasicPublish("", "test_publish_rk", message);
}
TEST_F(connected_test, publish_mandatory_fail) {
BasicMessage::ptr_t message = BasicMessage::Create("message body");
EXPECT_THROW(
channel->BasicPublish("", "test_publish_notexist", message, true),
MessageReturnedException);
}
TEST_F(connected_test, publish_mandatory_success) {
BasicMessage::ptr_t message = BasicMessage::Create("message body");
std::string queue = channel->DeclareQueue("");
channel->BasicPublish("", queue, message, true);
}
TEST_F(connected_test, publish_immediate_success) {
BasicMessage::ptr_t message = BasicMessage::Create("message body");
std::string queue = channel->DeclareQueue("");
std::string consumer = channel->BasicConsume(queue, "");
channel->BasicPublish("", queue, message, true);
}

247
testing/test_queue.cpp Normal file
View File

@@ -0,0 +1,247 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include "connected_test.h"
TEST_F(connected_test, queue_declare) {
std::string queue = channel->DeclareQueue("");
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_declare_named) {
std::string queue = channel->DeclareQueue("declare_queue_test");
EXPECT_EQ("declare_queue_test", queue);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, check_queue_exists_success) {
channel->DeclareQueue("declare_queue_passive");
EXPECT_TRUE(channel->CheckQueueExists("declare_queue_passive"));
}
TEST_F(connected_test, queue_declare_passive) {
std::string queue = channel->DeclareQueue("declare_queue_passive");
channel->DeclareQueue("declare_queue_passive", true);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, check_queue_exists_fail) {
EXPECT_FALSE(channel->CheckQueueExists("declare_queue_notexist"));
}
TEST_F(connected_test, queue_declare_passive_fail) {
EXPECT_THROW(channel->DeclareQueue("declare_queue_notexist", true),
ChannelException);
}
TEST_F(connected_test, queue_declare_durable) {
std::string queue =
channel->DeclareQueue("declare_queue_durable", false, true, false);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_declare_notexclusive) {
std::string queue =
channel->DeclareQueue("declare_queue_notexclusive", false, false, false);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_declare_notautodelete) {
std::string queue = channel->DeclareQueue("declare_queue_notautodelete",
false, false, false, false);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_declare_counts) {
boost::uint32_t message_count = 123;
boost::uint32_t consumer_count = 123;
std::string queue = channel->DeclareQueueWithCounts(
"queue_declare_counts", message_count, consumer_count);
EXPECT_NE("", queue);
EXPECT_EQ(0, message_count);
EXPECT_EQ(0, consumer_count);
const std::string body("Test Message");
BasicMessage::ptr_t out_message = BasicMessage::Create(body);
channel->BasicPublish("", queue, out_message);
channel->BasicPublish("", queue, out_message);
channel->BasicPublish("", queue, out_message);
std::string queue2 = channel->DeclareQueueWithCounts(
"queue_declare_counts", message_count, consumer_count);
EXPECT_NE("", queue2);
EXPECT_EQ(3, message_count);
EXPECT_EQ(0, consumer_count);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_declare_counts_table) {
boost::uint32_t message_count = 123;
boost::uint32_t consumer_count = 123;
Table qTable;
qTable.insert(TableEntry(TableKey("IsATest"), TableValue(true)));
std::string queue = channel->DeclareQueueWithCounts(
"queue_declare_counts_table", message_count, consumer_count, false, false,
true, true, qTable);
EXPECT_NE("", queue);
EXPECT_EQ(0, message_count);
EXPECT_EQ(0, consumer_count);
const std::string body("Test Message");
BasicMessage::ptr_t out_message = BasicMessage::Create(body);
channel->BasicPublish("", queue, out_message);
channel->BasicPublish("", queue, out_message);
channel->BasicPublish("", queue, out_message);
std::string queue2 = channel->DeclareQueueWithCounts(
"queue_declare_counts_table", message_count, consumer_count, false, false,
true, true, qTable);
EXPECT_NE("", queue2);
EXPECT_EQ(3, message_count);
EXPECT_EQ(0, consumer_count);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_delete) {
std::string queue = channel->DeclareQueue("delete_queue");
channel->DeleteQueue(queue);
EXPECT_THROW(channel->DeclareQueue(queue, true), ChannelException);
}
TEST_F(connected_test, queue_delete_ifunused) {
std::string queue = channel->DeclareQueue("delete_queue_ifunused");
channel->DeleteQueue(queue, true);
EXPECT_THROW(channel->DeclareQueue(queue, true), ChannelException);
}
TEST_F(connected_test, queue_delete_ifused) {
std::string queue = channel->DeclareQueue("delete_queue_ifused");
channel->BasicConsume(queue);
EXPECT_THROW(channel->DeleteQueue(queue, true), ChannelException);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_delete_ifempty) {
std::string queue = channel->DeclareQueue("delete_queue_ifempty");
channel->DeleteQueue(queue, false, true);
EXPECT_THROW(channel->DeclareQueue(queue, true), ChannelException);
}
TEST_F(connected_test, queue_delete_ifnotempty) {
std::string queue = channel->DeclareQueue("delete_queue_ifnotempty");
BasicMessage::ptr_t message = BasicMessage::Create("Message body");
channel->BasicPublish("", queue, message, true);
EXPECT_THROW(channel->DeleteQueue(queue, false, true), ChannelException);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_bind) {
channel->DeclareExchange("queue_bind_exchange");
std::string queue = channel->DeclareQueue("queue_bind_queue");
channel->BindQueue(queue, "queue_bind_exchange", "rk");
channel->DeleteExchange("queue_bind_exchange");
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_bind_badexchange) {
std::string queue = channel->DeclareQueue("queue_bind_badexchange");
EXPECT_THROW(channel->BindQueue(queue, "queue_bind_exchangenotexist", "rk"),
ChannelException);
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_bind_badqueue) {
channel->DeclareExchange("queue_bind_badqueue");
EXPECT_THROW(channel->BindQueue("queue_bind_queuenotexist",
"queue_bind_badqueue", "rk"),
ChannelException);
channel->DeleteExchange("queue_bind_badqueue");
}
TEST_F(connected_test, queue_bind_nokey) {
channel->DeclareExchange("queue_bind_exchange");
std::string queue = channel->DeclareQueue("queue_bind_queue");
channel->BindQueue(queue, "queue_bind_exchange");
channel->DeleteExchange("queue_bind_exchange");
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_unbind) {
channel->DeclareExchange("queue_unbind_exchange");
std::string queue = channel->DeclareQueue("queue_unbind_queue");
channel->BindQueue(queue, "queue_unbind_exchange", "rk");
channel->UnbindQueue(queue, "queue_unbind_exchange", "rk");
channel->DeleteExchange("queue_unbind_exchange");
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_purge) {
std::string queue = channel->DeclareQueue("queue_purge");
BasicMessage::ptr_t message = BasicMessage::Create("Message Body");
channel->BasicPublish("", queue, message, true);
channel->PurgeQueue(queue);
Envelope::ptr_t envelope;
EXPECT_FALSE(channel->BasicGet(envelope, queue));
channel->DeleteQueue(queue);
}
TEST_F(connected_test, queue_purge_badqueue) {
EXPECT_THROW(channel->PurgeQueue("purge_queue_queuenotexist"),
ChannelException);
}

896
testing/test_table.cpp Normal file
View File

@@ -0,0 +1,896 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MIT
*
* Copyright (c) 2010-2013 Alan Antonuk
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* ***** END LICENSE BLOCK *****
*/
#include <algorithm>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/variant/get.hpp>
#include <ctime>
#include "connected_test.h"
using namespace AmqpClient;
TEST(table_value, void_value) {
TableValue value;
EXPECT_EQ(TableValue::VT_void, value.GetType());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set();
EXPECT_EQ(TableValue::VT_void, value.GetType());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, bool_value) {
bool v1 = true;
bool v2 = false;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_bool, value.GetType());
EXPECT_EQ(v1, value.GetBool());
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_bool, value.GetType());
EXPECT_EQ(v2, value.GetBool());
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, uint8_value) {
boost::uint8_t v1 = 1;
boost::uint8_t v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_uint8, value.GetType());
EXPECT_EQ(v1, value.GetUint8());
EXPECT_EQ(v1, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_uint8, value.GetType());
EXPECT_EQ(v2, value.GetUint8());
EXPECT_EQ(v2, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, int8_value) {
boost::int8_t v1 = 1;
boost::int8_t v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_int8, value.GetType());
EXPECT_EQ(v1, value.GetInt8());
EXPECT_EQ(v1, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_int8, value.GetType());
EXPECT_EQ(v2, value.GetInt8());
EXPECT_EQ(v2, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, uint16_value) {
boost::uint16_t v1 = 1;
boost::uint16_t v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_uint16, value.GetType());
EXPECT_EQ(v1, value.GetUint16());
EXPECT_EQ(v1, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_uint16, value.GetType());
EXPECT_EQ(v2, value.GetUint16());
EXPECT_EQ(v2, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, int16_value) {
boost::int16_t v1 = 1;
boost::int16_t v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_int16, value.GetType());
EXPECT_EQ(v1, value.GetInt16());
EXPECT_EQ(v1, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_int16, value.GetType());
EXPECT_EQ(v2, value.GetInt16());
EXPECT_EQ(v2, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, uint32_value) {
boost::uint32_t v1 = 1;
boost::uint32_t v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_uint32, value.GetType());
EXPECT_EQ(v1, value.GetUint32());
EXPECT_EQ(v1, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_uint32, value.GetType());
EXPECT_EQ(v2, value.GetUint32());
EXPECT_EQ(v2, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, int32_value) {
boost::int32_t v1 = 1;
boost::int32_t v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_int32, value.GetType());
EXPECT_EQ(v1, value.GetInt32());
EXPECT_EQ(v1, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_int32, value.GetType());
EXPECT_EQ(v2, value.GetInt32());
EXPECT_EQ(v2, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, timestamp_value) {
std::time_t v1 = 1;
std::time_t v2 = 2;
TableValue value = TableValue::Timestamp(v1);
EXPECT_EQ(TableValue::VT_timestamp, value.GetType());
EXPECT_EQ(v1, value.GetTimestamp());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.SetTimestamp(v2);
EXPECT_EQ(TableValue::VT_timestamp, value.GetType());
EXPECT_EQ(v2, value.GetTimestamp());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, int64_value) {
boost::int64_t v1 = 1;
boost::int64_t v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_int64, value.GetType());
EXPECT_EQ(v1, value.GetInt64());
EXPECT_EQ(v1, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_int64, value.GetType());
EXPECT_EQ(v2, value.GetInt64());
EXPECT_EQ(v2, value.GetInteger());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, float_value) {
float v1 = 1.;
float v2 = 2.;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_float, value.GetType());
EXPECT_EQ(v1, value.GetFloat());
EXPECT_EQ(v1, value.GetReal());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_float, value.GetType());
EXPECT_EQ(v2, value.GetFloat());
EXPECT_EQ(v2, value.GetReal());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, double_value) {
double v1 = 1;
double v2 = 2;
TableValue value(v1);
EXPECT_EQ(TableValue::VT_double, value.GetType());
EXPECT_EQ(v1, value.GetDouble());
EXPECT_EQ(v1, value.GetReal());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_double, value.GetType());
EXPECT_EQ(v2, value.GetDouble());
EXPECT_EQ(v2, value.GetReal());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, string_value) {
std::string v1 = "1";
std::string v2 = "2";
TableValue value(v1);
EXPECT_EQ(TableValue::VT_string, value.GetType());
EXPECT_EQ(v1, value.GetString());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_string, value.GetType());
EXPECT_EQ(v2, value.GetString());
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, array_value) {
Array v1;
v1.push_back(TableValue("first"));
Array v2;
v2.push_back(TableValue((int32_t)2));
TableValue value(v1);
EXPECT_EQ(TableValue::VT_array, value.GetType());
Array v1a = value.GetArray();
EXPECT_TRUE(v1.size() == v1a.size());
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), v1a.begin()));
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_array, value.GetType());
Array v2a = value.GetArray();
EXPECT_TRUE(v2.size() == v2a.size());
EXPECT_TRUE(std::equal(v2.begin(), v2.end(), v2a.begin()));
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetTable(), boost::bad_get);
}
TEST(table_value, table_value) {
Table v1;
v1.insert(TableEntry("one", 10));
Table v2;
v2.insert(TableEntry("two", 22.2));
TableValue value(v1);
EXPECT_EQ(TableValue::VT_table, value.GetType());
Table v1a = value.GetTable();
EXPECT_TRUE(v1.size() == v1a.size());
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), v1a.begin()));
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
value.Set(v2);
EXPECT_EQ(TableValue::VT_table, value.GetType());
Table v2a = value.GetTable();
EXPECT_TRUE(v2.size() == v2a.size());
EXPECT_TRUE(std::equal(v2.begin(), v2.end(), v2a.begin()));
EXPECT_THROW(value.GetBool(), boost::bad_get);
EXPECT_THROW(value.GetUint8(), boost::bad_get);
EXPECT_THROW(value.GetInt8(), boost::bad_get);
EXPECT_THROW(value.GetUint16(), boost::bad_get);
EXPECT_THROW(value.GetInt16(), boost::bad_get);
EXPECT_THROW(value.GetUint32(), boost::bad_get);
EXPECT_THROW(value.GetInt32(), boost::bad_get);
EXPECT_THROW(value.GetTimestamp(), boost::bad_get);
EXPECT_THROW(value.GetInt64(), boost::bad_get);
EXPECT_THROW(value.GetInteger(), boost::bad_get);
EXPECT_THROW(value.GetFloat(), boost::bad_get);
EXPECT_THROW(value.GetDouble(), boost::bad_get);
EXPECT_THROW(value.GetReal(), boost::bad_get);
EXPECT_THROW(value.GetString(), boost::bad_get);
EXPECT_THROW(value.GetArray(), boost::bad_get);
}
TEST(table_value, equality) {
TableValue void_val1;
TableValue void_val2;
EXPECT_EQ(void_val1, void_val1);
EXPECT_EQ(void_val1, void_val2);
TableValue bool_val1(true);
TableValue bool_val2(bool_val1);
TableValue bool_val3(false);
TableValue bool_val4;
bool_val4.Set(bool_val3.GetBool());
EXPECT_EQ(bool_val1, bool_val1);
EXPECT_EQ(bool_val1, bool_val2);
EXPECT_NE(bool_val1, bool_val3);
EXPECT_NE(void_val1, bool_val1);
EXPECT_EQ(bool_val3, bool_val4);
TableValue int8_val1(int8_t(8));
TableValue int8_val2(int8_val1);
TableValue int8_val3(int8_t(9));
EXPECT_EQ(int8_val1, int8_val1);
EXPECT_EQ(int8_val1, int8_val2);
EXPECT_NE(int8_val1, int8_val3);
TableValue string_val1("one");
TableValue string_val2(std::string("one"));
std::string empty;
TableValue string_val3(empty);
EXPECT_EQ(string_val1, string_val1);
EXPECT_EQ(string_val1, string_val2);
EXPECT_NE(string_val1, string_val3);
std::vector<TableValue> vec_val1;
vec_val1.push_back(void_val1);
vec_val1.push_back(int8_val1);
std::vector<TableValue> vec_val2;
vec_val2.push_back(bool_val1);
vec_val2.push_back(void_val1);
TableValue array_val1(vec_val1);
TableValue array_val2(array_val1);
TableValue array_val3(vec_val2);
EXPECT_EQ(array_val1, array_val1);
EXPECT_EQ(array_val1, array_val2);
EXPECT_NE(array_val1, array_val3);
Table tbl1;
tbl1.insert(TableEntry("key1", int8_val1));
tbl1.insert(TableEntry("key2", "string"));
Table tbl2;
tbl2.insert(TableEntry("key1", void_val1));
tbl2.insert(TableEntry("array", array_val1));
TableValue table_val1(tbl1);
TableValue table_val2(tbl1);
TableValue table_val3(tbl2);
EXPECT_EQ(table_val1, table_val1);
EXPECT_EQ(table_val1, table_val2);
EXPECT_NE(table_val1, table_val3);
}
TEST(table, convert_to_rabbitmq) {
Table table_in;
table_in.insert(TableEntry("void_key", TableValue()));
table_in.insert(TableEntry("bool_key", true));
table_in.insert(TableEntry("uint8_key", uint8_t(8)));
table_in.insert(TableEntry("int8_key", int8_t(8)));
table_in.insert(TableEntry("uint16_key", uint16_t(16)));
table_in.insert(TableEntry("int16_key", int16_t(16)));
table_in.insert(TableEntry("uint32_key", uint32_t(32)));
table_in.insert(TableEntry("int32_key", int32_t(32)));
table_in.insert(TableEntry("timestamp_key", TableValue::Timestamp(64)));
table_in.insert(TableEntry("int64_key", int64_t(64)));
table_in.insert(TableEntry("float_key", float(1.5)));
table_in.insert(TableEntry("double_key", double(2.25)));
table_in.insert(TableEntry("string_key", "A string!"));
std::vector<TableValue> array_in;
array_in.push_back(TableValue(false));
array_in.push_back(TableValue(int32_t(10)));
array_in.push_back(TableValue(std::string("Another string")));
table_in.insert(TableEntry("array_key", array_in));
Table table_inner;
table_inner.insert(TableEntry("inner_string", "An inner table"));
table_inner.insert(TableEntry("inner array", array_in));
table_in.insert(TableEntry("table_key", table_inner));
BasicMessage::ptr_t message = BasicMessage::Create();
message->HeaderTable(table_in);
EXPECT_TRUE(message->HeaderTableIsSet());
Table table_out = message->HeaderTable();
EXPECT_EQ(table_in.size(), table_out.size());
EXPECT_TRUE(std::equal(table_in.begin(), table_in.end(), table_out.begin()));
}
TEST(table, convert_to_rabbitmq_empty) {
Table table_in;
BasicMessage::ptr_t message = BasicMessage::Create();
message->HeaderTable(table_in);
Table table_out = message->HeaderTable();
EXPECT_EQ(0, table_out.size());
}
TEST_F(connected_test, basic_message_header_roundtrip) {
Table table_in;
table_in.insert(TableEntry("void_key", TableValue()));
table_in.insert(TableEntry("bool_key", true));
table_in.insert(TableEntry("uint8_key", uint8_t(8)));
table_in.insert(TableEntry("int8_key", int8_t(8)));
table_in.insert(TableEntry("uint16_key", uint16_t(16)));
table_in.insert(TableEntry("int16_key", int16_t(16)));
table_in.insert(TableEntry("uint32_key", uint32_t(32)));
table_in.insert(TableEntry("int32_key", int32_t(32)));
table_in.insert(TableEntry("timestamp_key", TableValue::Timestamp(64)));
table_in.insert(TableEntry("int64_key", int64_t(64)));
table_in.insert(TableEntry("float_key", float(1.5)));
table_in.insert(TableEntry("double_key", double(2.25)));
table_in.insert(TableEntry("string_key", "A string!"));
std::vector<TableValue> array_in;
array_in.push_back(TableValue(false));
array_in.push_back(TableValue(int32_t(10)));
array_in.push_back(TableValue(std::string("Another string")));
table_in.insert(TableEntry("array_key", array_in));
Table table_inner;
table_inner.insert(TableEntry("inner_string", "An inner table"));
table_inner.insert(TableEntry("inner array", array_in));
table_in.insert(TableEntry("table_key", table_inner));
std::string queue = channel->DeclareQueue("");
std::string tag = channel->BasicConsume(queue, "");
BasicMessage::ptr_t message_in = BasicMessage::Create("Body");
message_in->HeaderTable(table_in);
channel->BasicPublish("", queue, message_in);
Envelope::ptr_t envelope = channel->BasicConsumeMessage(tag);
BasicMessage::ptr_t message_out = envelope->Message();
Table table_out = message_out->HeaderTable();
EXPECT_EQ(table_in.size(), table_out.size());
EXPECT_TRUE(std::equal(table_in.begin(), table_in.end(), table_out.begin()));
}
TEST_F(connected_test, basic_message_empty_table_roundtrip) {
std::string queue = channel->DeclareQueue("");
std::string tag = channel->BasicConsume(queue, "");
Table table_in;
BasicMessage::ptr_t message_in = BasicMessage::Create("Body");
message_in->HeaderTable(table_in);
channel->BasicPublish("", queue, message_in);
Envelope::ptr_t envelope = channel->BasicConsumeMessage(tag);
BasicMessage::ptr_t message_out = envelope->Message();
Table table_out = message_out->HeaderTable();
EXPECT_EQ(table_in.size(), table_out.size());
EXPECT_TRUE(std::equal(table_in.begin(), table_in.end(), table_out.begin()));
}