This commit is contained in:
bing
2026-04-02 23:18:28 +08:00
commit 6198e1b53c
112 changed files with 20893 additions and 0 deletions

47
.clang-format Normal file
View File

@@ -0,0 +1,47 @@
---
# BasedOnStyle: Google
AccessModifierOffset: -1
ConstructorInitializerIndentWidth: 4
AlignEscapedNewlinesLeft: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: true
AlwaysBreakBeforeMultilineStrings: true
BreakBeforeBinaryOperators: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BinPackParameters: true
ColumnLimit: 80
ConstructorInitializerAllOnOneLineOrOnePerLine: true
DerivePointerBinding: true
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceBeforeProtocolList: false
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 60
PenaltyBreakString: 1000
PenaltyBreakFirstLessLess: 120
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerBindsToType: true
SpacesBeforeTrailingComments: 2
Cpp11BracedListStyle: true
Standard: Auto
IndentWidth: 2
TabWidth: 8
UseTab: Never
BreakBeforeBraces: Attach
IndentFunctionDeclarationAfterType: true
SpacesInParentheses: false
SpacesInAngles: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterControlStatementKeyword: true
SpaceBeforeAssignmentOperators: true
ContinuationIndentWidth: 4
...

18
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12
ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="3.28.1"
# Optionally install the cmake for vcpkg
COPY ./reinstall-cmake.sh /tmp/
RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \
chmod +x /tmp/reinstall-cmake.sh && /tmp/reinstall-cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \
fi \
&& rm -f /tmp/reinstall-cmake.sh
# [Optional] Uncomment this section to install additional vcpkg ports.
# RUN su vscode -c "${VCPKG_ROOT}/vcpkg install <your-port-name-here>"
# [Optional] Uncomment this section to install additional packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends libssl-dev libpopt-dev clang-format clangd xmlto doxygen

View File

@@ -0,0 +1,26 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
{
"name": "C++",
"build": {
"dockerfile": "Dockerfile"
},
"features": {
"ghcr.io/itsmechlark/features/rabbitmq-server:1": {}
}
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// 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",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
set -e
CMAKE_VERSION=${1:-"none"}
if [ "${CMAKE_VERSION}" = "none" ]; then
echo "No CMake version specified, skipping CMake reinstallation"
exit 0
fi
# Cleanup temporary directory and associated files when exiting the script.
cleanup() {
EXIT_CODE=$?
set +e
if [[ -n "${TMP_DIR}" ]]; then
echo "Executing cleanup of tmp files"
rm -Rf "${TMP_DIR}"
fi
exit $EXIT_CODE
}
trap cleanup EXIT
echo "Installing CMake..."
apt-get -y purge --auto-remove cmake
mkdir -p /opt/cmake
architecture=$(dpkg --print-architecture)
case "${architecture}" in
arm64)
ARCH=aarch64 ;;
amd64)
ARCH=x86_64 ;;
*)
echo "Unsupported architecture ${architecture}."
exit 1
;;
esac
CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh"
CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt"
TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX)
echo "${TMP_DIR}"
cd "${TMP_DIR}"
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O
sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}"
sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest

18
.gitattributes vendored Normal file
View File

@@ -0,0 +1,18 @@
# Default for those who don't have core.autocrlf set
* text=auto
# Things that should be treated like text (lines converted on checkout):
*.c text
*.h text
*.py text
*.cmake text
*.md text
# This is for the output of table_test
*.expected text
*.xml
# Exceptions to the rule:
*.ac text eol=lf
*.am text eol=lf
*.m4 text eol=lf

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

@@ -0,0 +1,76 @@
name: CI
on:
# Run the jobs on either push or a pull_request, but not both.
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build-linux:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- compiler: gcc
config: cmake
- compiler: clang
config: cmake
- compiler: clang
config: asan
- compiler: clang
config: tsan
- compiler: clang
config: format
- compiler: clang
config: scan-build
- compiler: clang
config: framing
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install Prerequisites
shell: bash
run: |
sudo apt update
sudo apt install -y ninja-build libpopt-dev
- name: Configure Build & Test
shell: bash
env:
CC: ${{ matrix.compiler }}
run: ./travis.sh ${{ matrix.config }}
build-macox:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install Prerequisites
shell: bash
run: brew install ninja popt
- name: Configure Build & Test
shell: bash
run: ./travis.sh macos
build-win32:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Configure Build & Test
shell: bash
run: |
cmake -E make_directory build/
cmake -S . -B build/ -DBUILD_EXAMPLES=ON -DCMAKE_C_FLAGS=" /W4"
cmake --build build/ --config Debug --target INSTALL
ctest -V ./build/

32
.github/workflows/cifuzz.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: CIFuzz
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitizer: [address, undefined, memory]
steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'rabbitmq-c'
dry-run: false
language: c
sanitizer: ${{ matrix.sanitizer }}
- name: Run Fuzzers (${{ matrix.sanitizer }})
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'rabbitmq-c'
dry-run: false
language: c
fuzz-seconds: 300
sanitizer: ${{ matrix.sanitizer }}
- name: Upload Crash
uses: actions/upload-artifact@v4
if: failure() && steps.build.outcome == 'success'
with:
name: ${{ matrix.sanitizer }}-artifacts
path: ./out/artifacts

50
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '18 10 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Install Prerequisites
shell: bash
run: sudo apt install -y ninja-build libpopt-dev
- name: Build
shell: bash
run: |
mkdir $PWD/build
cmake -S . -B build -GNinja \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_EXAMPLES=ON \
-DBUILD_TOOLS=ON
cmake --build build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

77
.gitignore vendored Normal file
View File

@@ -0,0 +1,77 @@
*.la
*.lo
*.o
.deps
.dirstamp
.libs
/aclocal.m4
/autom4te.cache
/bin*
/build
/compile
/config.guess
/config.h
/config.h.in
/config.h.in~
/config.log
/config.status
/config.sub
/configure
/cscope.*
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1
/test-suite.log
INSTALL
Makefile
Makefile.in
examples/amqp_bind
examples/amqp_connect_timeout
examples/amqp_consumer
examples/amqp_exchange_declare
examples/amqp_listen
examples/amqp_listenq
examples/amqp_producer
examples/amqp_rpc_sendstring_client
examples/amqp_sendstring
examples/amqp_unbind
examples/amqps_bind
examples/amqps_connect_timeout
examples/amqps_consumer
examples/amqps_exchange_declare
examples/amqps_listen
examples/amqps_listenq
examples/amqps_producer
examples/amqps_sendstring
examples/amqps_unbind
librabbitmq.pc
test-driver
tests/*.log
tests/*.trs
tests/test_hostcheck
tests/test_parse_url
tests/test_status_enum
tests/test_tables
tools/amqp-consume
tools/amqp-declare-queue
tools/amqp-delete-queue
tools/amqp-get
tools/amqp-publish
tools/doc/*.1
tools/doc/*.7
tools/doc/man-date.ent
.ycm_extra_conf.py?
.DS_Store
# Ignore editor swap files
*~
*.sw?
.#*
\#*#
.venv/
.cache/
compile_commands.json
codegen/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "travis/run-clang-format"]
path = travis/run-clang-format
url = https://github.com/Sarcasm/run-clang-format.git

2
AUTHORS Normal file
View File

@@ -0,0 +1,2 @@
Tony Garnock-Jones <tonygarnockjones@gmail.com>
The RabbitMQ team <info@rabbitmq.com>

276
CMakeLists.txt Normal file
View File

@@ -0,0 +1,276 @@
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
# SPDX-License-Identifier: mit
cmake_minimum_required(VERSION 3.22...3.26)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# 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(RMQ_SOVERSION_CURRENT 11)
set(RMQ_SOVERSION_REVISION 0)
set(RMQ_SOVERSION_AGE 7)
include(VersionFunctions)
get_library_version(RMQ_VERSION)
compute_soversion(${RMQ_SOVERSION_CURRENT} ${RMQ_SOVERSION_REVISION} ${RMQ_SOVERSION_AGE} RMQ_SOVERSION)
project(rabbitmq-c
VERSION ${RMQ_VERSION}
DESCRIPTION "C RabbitMQ AMQP client library"
LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
set(default_build_type "Release")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
include(CheckSymbolExists)
include(CheckLibraryExists)
include(CMakeDependentOption)
include(CMakePushCheckState)
include(GNUInstallDirs)
# Detect if we need to link against a socket library:
cmake_push_check_state()
if (WIN32)
# Always use WinSock2 on Windows
set(SOCKET_LIBRARIES ws2_32)
else ()
# Is it in the default link?
check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO)
if (NOT (HAVE_GETADDRINFO EQUAL 1))
SET(CMAKE_REQUIRED_LIBRARIES "socket")
check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO2)
if (HAVE_GETADDRINFO2 EQUAL 1)
set(SOCKET_LIBRARIES socket)
else ()
SET(CMAKE_REQUIRED_LIBRARIES "socket;nsl")
check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO3)
if (HAVE_GETADDRINFO3 EQUAL 1)
set(SOCKET_LIBRARIES socket nsl)
else ()
message(FATAL_ERROR "Cannot find name resolution library (containing symbol getaddrinfo)")
endif ()
endif ()
endif ()
set(CMAKE_REQUIRED_LIBRARIES ${SOCKET_LIBRARIES})
check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET)
if (NOT HAVE_SOCKET EQUAL 1)
set(CMAKE_REQUIRED_LIBRARIES socket ${SOCKET_LIBRARIES})
check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET2)
if (HAVE_SOCKET2 EQUAL 1)
set(SOCKET_LIBRARIES socket ${SOCKET_LIBRARIES})
else ()
set(CMAKE_REQUIRED_LIBRARIES socket nsl ${SOCKET_LIBRARIES})
check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET3)
if (HAVE_SOCKET3 EQUAL 1)
set(SOCKET_LIBRARIES socket nsl ${SOCKET_LIBRARIES})
else ()
message(FATAL_ERROR "Cannot find socket library (containing symbol socket)")
endif ()
endif ()
endif ()
endif ()
cmake_pop_check_state()
cmake_push_check_state()
set(CMAKE_REQUIRED_LIBRARIES ${SOCKET_LIBRARIES})
check_symbol_exists(poll poll.h HAVE_POLL)
if (NOT HAVE_POLL)
if (WIN32)
set(HAVE_SELECT 1)
else()
check_symbol_exists(select sys/select.h HAVE_SELECT)
endif()
if (NOT HAVE_SELECT)
message(FATAL_ERROR "rabbitmq-c requires poll() or select() to be available")
endif()
endif()
cmake_pop_check_state()
check_library_exists(rt clock_gettime "time.h" CLOCK_GETTIME_NEEDS_LIBRT)
check_library_exists(rt posix_spawnp "spawn.h" POSIX_SPAWNP_NEEDS_LIBRT)
if (CLOCK_GETTIME_NEEDS_LIBRT OR POSIX_SPAWNP_NEEDS_LIBRT)
set(LIBRT rt)
endif()
option(ENABLE_SSL_SUPPORT "Enable SSL support" ON)
if (ENABLE_SSL_SUPPORT)
# Manually check OpenSSL version because BoringSSL doesn't support version checking via find_package
set(RMQ_OPENSSL_MIN_VERSION 1.1.1)
find_package(OpenSSL REQUIRED)
if(OPENSSL_VERSION) # Will be empty for BoringSSL
if(OPENSSL_VERSION VERSION_LESS RMQ_OPENSSL_MIN_VERSION)
MESSAGE(FATAL_ERROR "Found OpenSSL version ${OPENSSL_VERSION} but ${RMQ_OPENSSL_MIN_VERSION} or later is required")
endif()
endif()
cmake_push_check_state()
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
cmake_pop_check_state()
cmake_push_check_state()
set(CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL)
check_symbol_exists(ENGINE_new openssl/engine.h HAS_OPENSSL_ENGINE)
cmake_pop_check_state()
cmake_dependent_option(ENABLE_SSL_ENGINE_API "Enable support for deprecated OpenSSL ENGINE feature" ON "HAS_OPENSSL_ENGINE" OFF)
endif()
if(PROJECT_IS_TOP_LEVEL)
include(CTest)
endif()
option(BUILD_SHARED_LIBS "Build rabbitmq-c as a shared library" ON)
option(BUILD_STATIC_LIBS "Build rabbitmq-c as a static library" ON)
option(INSTALL_STATIC_LIBS "Install rabbitmq-c static library" ON)
option(BUILD_EXAMPLES "Build Examples" OFF)
option(BUILD_TOOLS "Build Tools (requires POPT Library)" OFF)
cmake_dependent_option(BUILD_TOOLS_DOCS "Build man pages for tools (requires xmlto)" OFF "BUILD_TOOLS" OFF)
option(BUILD_API_DOCS "Build Doxygen API docs" OFF)
option(RUN_SYSTEM_TESTS "Run system tests (i.e. tests requiring an accessible RabbitMQ server instance on localhost)" OFF)
option(BUILD_OSSFUZZ "Build OSSFUZZ" OFF)
if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS)
message(FATAL_ERROR "One or both of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS must be set to ON to build")
endif()
set(targets_export_name rabbitmq-targets)
add_subdirectory(librabbitmq)
if(BUILD_EXAMPLES)
if(NOT BUILD_SHARED_LIBS)
message(FATAL_ERROR "Examples require -DBUILD_SHARED_LIBS=ON")
endif()
add_subdirectory(examples)
endif()
if(BUILD_TOOLS)
find_package(POPT REQUIRED)
if(BUILD_TOOLS_DOCS)
find_package(XMLTO REQUIRED)
endif()
add_subdirectory(tools)
endif()
if(PROJECT_IS_TOP_LEVEL AND BUILD_TESTING)
if (NOT BUILD_STATIC_LIBS)
message(FATAL_ERROR
"Tests can only be built against static libraries "
"(set BUILD_STATIC_LIBS=ON)")
endif ()
add_subdirectory(tests)
endif ()
if(BUILD_OSSFUZZ)
if (NOT BUILD_STATIC_LIBS)
message(FATAL_ERROR "OSS-FUZZ can only be built against static libraries " "(set BUILD_STATIC_LIBS=ON)")
endif ()
add_subdirectory(fuzz)
endif ()
if (BUILD_API_DOCS)
find_package(Doxygen REQUIRED)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile @ONLY)
add_custom_target(docs
COMMAND ${DOXYGEN_EXECUTABLE}
VERBATIM
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs
DEPENDS rabbitmq
COMMENT "Generating API documentation"
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in
)
endif ()
foreach (lib ${SOCKET_LIBRARIES})
set(libs_private "${libs_private} -l${lib}")
endforeach(lib)
set(libs_private "${libs_private} -l${LIBRT}")
if (ENABLE_SSL_SUPPORT)
set(libs_private "${libs_private} -lssl -lcrypto ${CMAKE_THREAD_LIBS_INIT}")
endif()
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix "\${prefix}")
cmake_path(APPEND libdir "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}")
cmake_path(APPEND includedir "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
configure_file(cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq/config.h)
configure_file(librabbitmq.pc.in ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq.pc @ONLY)
include(CMakePackageConfigHelpers)
set(RMQ_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/rabbitmq-c)
set(project_config "${CMAKE_CURRENT_BINARY_DIR}/rabbitmq-c-config.cmake")
set(version_config "${CMAKE_CURRENT_BINARY_DIR}/rabbitmq-c-config-version.cmake")
write_basic_package_version_file(
"${version_config}"
VERSION ${RMQ_VERSION}
COMPATIBILITY SameMajorVersion)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/rabbitmq-c-config.cmake.in"
"${project_config}"
INSTALL_DESTINATION "${RMQ_CMAKE_DIR}")
if(BUILD_SHARED_LIBS)
list(APPEND INSTALL_TARGETS rabbitmq)
endif()
if(BUILD_STATIC_LIBS AND INSTALL_STATIC_LIBS)
list(APPEND INSTALL_TARGETS rabbitmq-static)
endif()
export(TARGETS ${INSTALL_TARGETS}
NAMESPACE rabbitmq::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
install(FILES ${project_config} ${version_config}
DESTINATION ${RMQ_CMAKE_DIR}
COMPONENT rabbitmq-c-development
)
install(EXPORT ${targets_export_name}
DESTINATION ${RMQ_CMAKE_DIR}
NAMESPACE rabbitmq::
COMPONENT rabbitmq-c-development
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/librabbitmq.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
if (BUILD_SHARED_LIBS)
message(STATUS "Building rabbitmq as a shared library - yes")
else ()
message(STATUS "Building rabbitmq as a shared library - no")
endif ()
if (BUILD_STATIC_LIBS)
message(STATUS "Building rabbitmq as a static library - yes")
else ()
message(STATUS "Building rabbitmq as a static library - no")
endif ()

26
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,26 @@
Contributing to rabbitmq-c
==========================
Thanks for contributing to rabbitmq-c. I firmly believe that participation helps
make open source software great. With that there are a few things that can be
done to make our interaction a bit smoother.
Please use the following guidelines when creating an issue or submitting a
pull request
Creating an issue
-----------------
When submitting an issue its helpful to know the following
- What version of rabbitmq-c are you using?
- What operating system and version are you running on?
- What compiler and version are you running?
-
- If its a build system issue: which build system are you using (
Submitting a pull-request
-------------------------
I love to get code contributions, a few things that can help out:
- Make sure your commits are rebased on the current master branch
- Please collapse your commits down to a couple logical commits

318
ChangeLog.md Normal file
View File

@@ -0,0 +1,318 @@
# Change Log
## v0.15.0 - 2024-11-19
## Fixed
- Warning on MacOS about incompatible function declaration
- Logic when decoding AMQP data after bad data may cause crash (#837)
- Use SSL_get1_peer_certificate when compiling against OpenSSL 3+
## Changed
- Updates to various github actions.
## Added
- Add amqp_publisher_confirm_wait function (#841)
- Add amqp_literal_bytes macro (#844)
## v0.14.0 - 2024-03-18
## Fixed
- Fix potential stackoverflow in decoding table and array
- Fix issue with Mach-O version (#758)
- Make dependency on OpenSSL in rabbitmq-c.cmake match what is built (#725)
- Fix pkg-config generation when CMAKE_INSTALL_DIR is absolute (#733)
- Fix issue with amqp_basic_publish blocking in non-blocking mode (#780)
- Fix SSL hostname check (#784)
- Fix bug in amqp-consume documentation #791
## Changed
- CMake minimum version is now 3.22
- OpenSSL minimum version is now 1.1.1
- Minimum TLS version supported is v1.2 (v1.3 is also supported).
- OpenSSL ENGINE APIs are conditionally enabled based on availability
## Added
- Add option to read username/password from file in tools (#781)
- Add amqp_ssl_socket_enable_default_verify_paths API to allow loading from default certification paths
- rabbitmq-c can be compiled against BoringSSL (#814)
## v0.13.0 - 2023-02-05
## Fixed
- Fixed missing option to not install static library (#665)
- Missing pkgconfig version in v0.12.0 output (#755, #751)
- Correct return value from amqp_ssl_socket_set_key_buffer (#723)
## Changed
- Remove OpenSSL code no longer needed when used with OpenSSL >= 1.1.0. (Fixed: #715, #737)
## Added
- Integration with OSS-Fuzz (#736)
## v0.12.0 - 2023-01-31
## Changed
- rabbitmq-c now compiles as C99
- CMake 3.12 is new minimum required version
- CMake -DBUILD_TESTS renamed to -DBUILD_TESTING
- CMake -DBUILD_EXAMPLES now defaults to OFF
- CMake -DBUILD_TOOLS now defaults to OFF
- Unix library version now matches the release version, SONAME remains the same.
- Modernized CMake scripts to better adopt modern standards
- Public headers have moved to rabbitmq-c/ directory
- Dropped support for MSVC older than VS 2010
- Dropped support for OpenSSL v1.1.0 and older
- Minimum SSL version set to TLSv1.2
- Updated to RabbitMQ framing to v3.8.19
## v0.11.0 - 2021-03-31
## Added:
- rabbitmq-c-config.cmake is now generated (#611)
- rabbitmq-c can be compiled on Win32 using pthreads (#613)
- Add amqp_set_ssl_engine API to allow setting OpenSSL engine (#618)
- Add amqp_ssl_socket_set_key_engine API to allow setting OpenSSL engine (#618)
- Add support use of password-protected SSL keys (#661)
## Changed
- Update OpenSSL library initialization to current best practices (#603)
- Updates to OpenSSL to support v3.0.0 (#654, 627)
## Fixed:
- OpenSSL symbol clash when compiling on Win32 (#583)
- Restore correct non-blocking behavior using OpenSSL v1.1.1 (#586)
- Disable harmless secure CRT compile warnings on Win32 (#588)
- Fix unused parameter warnings on Win32 (#591)
- Fix invalid format in generated pkg-config file (#599)
- Fix invalid AMQP_STATUS_HEARTBEAT_TIMEOUT (#557)
- Fix incorrect port when using --server flag in CLI tools
## v0.10.0 - 2019-12-01
## Added:
- amqp_ssl_socket_get_context can be used to get the current OpenSSL CTX*
associated with a connection.
## Changed:
- openssl: missing OpenSSL config is ignored as an OpenSSL init error (#523)
- AMQP_DEFAULT_MAX_CHANNELS is now set to 2047 to follow current default channel
limit in the RabbitMQ broker. (#513)
## Fixed:
- add additional input validation to prevent integer overflow when parsing a
frame header. This addresses CVE-2019-18609.
## v0.9.0 - 2018-05-08
### Added:
- amqp-publish: added support for specifying headers via the -H flag
- Add support for specifying timeout for amqp_login calls via
amqp_set_handshake_timeout
- Add support for specifying timeouts in RPC-style AMQP methods via
amqp_set_rpc_timeout
- Add define for `AMQP_DEFAULT_VHOST`
- Support for SSL SNI
- Support for OpenSSL v1.1.0
### Changed:
- rabbitmq-c now requires Windows Vista or better
- rabbitmq-c enables TCP keep-alive by default on platforms that support it
- dropped support for compiling rabbitmq-c without threading support
- OpenSSL is no longer un-intialized automatically by default. OpenSSL can be
explicitly initialized by calling amqp_initialize_ssl_library and
uninitialized by calling amqp_uninitialize_ssl_library.
### Fixed:
- Correct bugs in processing of --url flag in tools (#364).
- Improve documentation on AMQP_SASL_METHOD_EXTERNAL (#349)
- Improve support for compiling under mingw-w64
- Better support for handing SIGPIPE on Linux over SSL (#401)
- Improve publish performance on Linux by not specifying MSG_MORE on last part
of message.
- Fix connection logic where multiple hostnames won't be tried if connection to
doesn't fail immediately (#430)
### Removed:
- autotools build system has been removed
- many duplicate amqps_* examples, they did not add a lot of value
## v0.8.0 - 2016-04-09
### Added:
- SSL: peer certificate and hostname validation can now be controlled separately
using `amqp_ssl_socket_set_verify_peer` and
`amqp_ssl_socket_set_verify_hostname`.
- SSL: the desire SSL version range can now be specified using the
`amqp_ssl_socket_set_ssl_versions` function.
- Add flags to SSL examples on controlling hostname verification.
### Changed:
- SSL: SSLv2, and SSLv3 have been disabled by default.
- SSL: OpenSSL hostname validation has been improved.
- Win32 debug information is built with /Z7 on MSVC to embed debug info instead
of using a .pdb
### Fixed:
- Connection failure results in hang on Win32 (#297, #346)
- Rabbitmq-c may block when attempting to close an SSL socket (#313)
- amqp_parse_url does not correctly initialize default parameters (#319)
- x509 objects are leaked in verify_hostname (#323)
- TCP_NOPUSH doesn't work under cygwin (#335)
### Deprecated
- SSL: `amqp_ssl_socket_set_verify` is being replaced by
`amqp_ssl_socket_set_verify_peer` and `amqp_ssl_socket_set_verify_hostname`.
### Removed:
- OpenVMS build system and related files.
- Unmaintained PolarSSL, CyaSSL, and gnuTLS SSL backends
## Changes since v0.7.0 (a.k.a., v0.7.1)
- `41fa9df` Autoconf: add missing files in build system
- `ef73c06` Win32: Use WSAEWOULDBLOCK instead of EWOULDBLOCK on Win32
- `ceca348` CI: use travis-ci container based builds
- `393e2df` Lib: if channel_max is 0 use server's channel_max
- `ff47574` Lib: fix build on OpenBSD
- `8429496...0ac6430` CI: enable CI on Mac OS X in travis-ci
## Changes since v0.6.0 (a.k.a., v0.7.0)
- `3379812` Tools: Add support for heartbeats
- `d7029db` CI: Add continuous integration on Win32 using Appveyor
- `a5f7ffb` Tests: only link against static libraries
- `a16ad45...9cf7a3b` Lib: add support for EXTERNAL SASL method
- `038a9ed` Lib: fix incorrect parameters to WSAPoll on Win32
- `a240c69...14ae307` Lib: use non-blocking sockets internally
- `8d1d5cc`, `5498dc6` Lib: simplify timer/timeout logic
- `61fc4e1` Lib: add support for heartbeat checks in blocking send calls
- `f462c0f...3546a70` Lib: Fix warnings on Win32
- `ba9d8ba...112a54d` Lib: Add support for RabbitMQ auth failure extension
- `fb8e318` Lib: allow calling functions to override client-properties
- `3ef3f5f` examples: replace usleep() with nanosleep()
- `9027a94` Lib: add AMQP_VERSION code
- `9ee1718` Lib: fix res maybe returned uninitialized in amqp_merge_capbilities
- `22a36db` Lib: Fix SSL_connection status check
- `abbefd4` Lib: Fix issues with c89 compatiblity
- `2bc1f9b...816cbfc` Lib: perf improvements when sending small messages by
hinting to the OS message boundaries.
- `be2e6dd...784a0e9` Lib: add select()-based timeout implementation
- `91db548...8d77b4c` CI: add ubsan, asan, and tsan CI builds
## Changes since v0.5.2 (a.k.a., v0.6.0)
- `e1746f9` Tools: Enable support for SSL in tools.
- `9626dd5` Lib: ABI CHANGE: enable support for auto_delete, internal flags to
amqp_exchange_declare
- `ee54e27`, `656f833` Lib: check for double-close in SSL/TCP socket impl
- `cf2760d` Lib: allocate struct when method has no field.
- `513ad4a` Lib: add support for SANs in OpenSSL socket impl.
- `5348c69` Lib: add functions to get negotiated frame_max and heartbeat parms.
## Changes since v0.5.1 (a.k.a., v0.5.2)
- `fcdf0f8` Autoconf: check for htonll as declaration in a header file
- `5790ec7` SSL: correctly report hostname verification errors.
- `d60c28c` Build: disable OpenSSL deprecation warnings on OSX
- `072191a` Lib: include platform, version and copyright in AMQP handshake
- `8b448c6` Examples: print message body in amqp[s]_listen[q] examples
- `7188e5d` Tools: Add flag to set prefetch for amqp-consume tool
## Changes since v0.5.0 (a.k.a., v0.5.1)
### Enhancements:
- `a566929` SSL: Add support for wildcards in hostname verification (Mike
Steinert)
- `a78aa8a` Lib: Use poll(2) instead of select(2) for timeouts on sockets.
- `357bdb3` Lib: support for specifying frame and decoding pool sizes. (Mike
Stitt)
- `8956003` Lib: improve invalid frame detection code.
### Bug fixes:
- `b852f84` Lib: Add missing amqp_get_server_properties() function.
- `7001e82` Lib: Add missing ssize_t on Win32 (emazv72)
- `c2ce2cb` Lib: Correctly specify WINVER on Win32 when unspecified.
- `fe844e4` CMake: specify -DHAVE_CONFIG_H in examples.
- `932de5f` Lib: correct time computation on Win32 (jestor)
- `3e83192` HPUX: use gethrtime on HP-UX for timers.
- `cb1b44e` HPUX: correct include location of sys/uio.h
- `8ce585d` Lib: incorrect OOM condition when 0-lenth exchange name is received.
- `c7716b8` CMake: correct htonll detection code on platforms defined with a
macro.
- `4dc4eda` Lib: remove unused assignment.
- `45302cf` Lib: remove range-check of channel-ids.
## Changes since v0.4.1 (a.k.a., v0.5.0):
### Major changes:
- Add amqp_get_broker_properties() function 5c7c40adc1
- Remove distro-specific packaging a5749657ee
- Add -x flag to amqp-consume utilty 1d9c5291ff
- Add amqp_basic_nack() public API 9b168776fb
- Add delivery mode constants to amqp.h 5f291ea772
- Add support for connection.blocked/connection.unblocked methods ccbc24d270
### Bug fixes:
- `f8c6cee749` Examples: Destroy amqp_envelope_t in consumer example
- `ac88db56d3` CMake: fix generation of librabbitmq.pc
- `d5b35afa40` CMake: fix missing POPT_INCLUDE_DIRS variable in tools/
- `5ea6a0945a` build: provide independent locations for x64 libs
- `fac34656c0` Doc: documentation fixes
- `715901d675` Lib: Correct OpenSSL initialization under threaded conditions
- `ce64e57df8` Examples: Handle unexpected frames in amqp_consumer.c
- `bcda3e933d` CMake: Use GnuInstallDirs to generate install dirs
- `27245a4e15` Lib: correctly handle amqp_get_monotonic_timestamp on win32
- `693662ef5b` Tools: honor --persistent flag in publish utility
- `01d9c3ca60` Doc: improve documentation in amqp_ssl_socket functions
- `02d5c58ae4` autoconf: correct librabbitmq.pc generation
- `1f4e0cc48b` Doc: improve documentation in amqp_tcp_socket functions
## Changes since v0.4.0:
### Major changes:
- Removed distro-specific packaging d285d01
### Bug fixes:
- `a642602` FIX: destroy amqp_envelop_t object in consumer example
- `860dd71` FIX: correct generation of librabbitmq.pc under CMake
- `bdda7ab` FIX: amqp_socket_close() should not be exported from shlib
- `24f4131` FIX: Use correct buf/len vars when re-starting send()
## Changes since v0.3.0:
### New Features/Enhancements:
- `amqp_login_with_properties()` function to connect to a broker sending a
properties table to the broker 21b124e #101
- SSL support (Mike Steinert) 473c865 #17
- `amqp_simple_wait_frame_noblock()` function variant to wait for a frame
with a timeout f8cfc72 #119
- Allow memory to be released on a per-channel basis with
`amqp_maybe_release_buffers_on_channel()` 4a2d899 #5
- Support for AMQP heartbeats while blocking in `amqp_simple_wait_frame*()`
and `amqp_basic_publish()` daa0e66 aca5dc1
- `amqp_socket_open_noblock()` for a non-blocking socket connection
(Bogdan Padalko) 6ad770d
- `amqp_table_clone()` to do a deep-copy of an amqp_table_t 08af83a
- Add option to listen to multiple keys in `amqp_consume` tool (Brian Hammond) e6c256d
- Add contributed OpenVMS build system 448ab68
- Higher level APIs for consuming messages 33ebeed #8
- Doxygen-based API documentation.
- Many improvements to error-handling and reporting
### Bug Fixes:
- `24ffaf8` FIX: autotools was broken when dependency-tracking was disabled
- `38e741b` FIX: CMake XmlTo not found warning
- `906f04f` FIX: htonll redeclared on Win32 v8
- `8e41603` FIX: SIGPIPE not disabled on OS X/BSD #102
- `872ea49` FIX: Header issues with amqp.h on Mingw on Win32 (yoniyoni)
- `0f1f75b` FIX: potential memory leak in amqp_new_connection
- `c9f6312` FIX: missing va_end in `amqp_login()`/`amqp_login_with_properties()`
- `7bb64e4` FIX: include amqp_tcp_socket.h in dpkg (Tim Stewart)
- `ba9d1f5` FIX: Report out of buffer space in `amqp_table_encode()`
- `9496e10` FIX: Remove `abort()` on invalid parameter in `amqp_send_frame()`
- `f209420` FIX: Remote `abort()` in `amqp_simple_wait_method()`
- `f027518` FIX: Return error on socket lib init error
- `0ae534a` FIX: Correctly handle 0 return val from `SSL_read()`/`SSL_write()`
- `22e41b8` FIX: Improve error handling in socket functions
- `33c2353` FIX: Set state->socket to NULL after `amqp_socket_close()`
- `c83e728` FIX: Incorrect error code returned
- `1a19879` FIX: redecl of int i in `amqp_tcp_socket_writev()`
- `7477449` FIX: incorrect bit-shift in `amqp_error_string2()`
- `2e37bb3` FIX: correctly handle `amqp_get_sockfd()` in `amqp_simple_wait_frame()`
- `52a459b` FIX: Don't delete state in `amqp_tune_connection()` on error
- `01e38dd` FIX: Correctly handle `mach_timebase_info()` failure
- `34bffb7` FIX: Correctly disable `SIGPIPE` on platforms with `SO_NOSIGPIPE`
- `3866433` FIX: Use correct number of bits in timer precision on MacOSX
- `b6a1dfe` FIX: Squash OpenSSL deprecated warnings on MacOSX (Bogdan Padalko)
- `7a217d5` FIX: Incorrect `assert()` in `wait_frame_inner()`
- `7942af3` FIX: Correctly handle 0-length table in `amqp_table_clone()`
- `157788e` FIX: Correctly handle 0-length strings in `amqp_basic_properties_clone()`
- `4eaf771` FIX: Correctly handle 0-length message body in `amqp_read_message()`
- `59f943b` FIX: Double-free SSL on connection failure
- `7a451a4` FIX: `amqp_open_socket()` not defined

27
LICENSE Normal file
View File

@@ -0,0 +1,27 @@
MIT License
Copyright (c) 2012-2021 Alan Antonuk
Copyright (c) 2007-2012 VMware, Inc.
Copyright (c) 2009-2010 VMware, Inc. and Tony Garnock-Jones
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.

180
README.md Normal file
View File

@@ -0,0 +1,180 @@
# RabbitMQ C AMQP client library
![Build Status](https://github.com/alanxz/rabbitmq-c/actions/workflows/ci.yml/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/alanxz/rabbitmq-c/badge.svg?branch=master)](https://coveralls.io/github/alanxz/rabbitmq-c?branch=master)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/rabbitmq-c.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#rabbitmq-c)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7001/badge)](https://www.bestpractices.dev/projects/7001)
## Introduction
This is a C-language AMQP client library for use with v2.0+ of the
[RabbitMQ](http://www.rabbitmq.com/) broker.
- <http://github.com/alanxz/rabbitmq-c>
Announcements regarding the library are periodically made on the
rabbitmq-c-users and cross-posted to rabbitmq-users.
- <https://groups.google.com/forum/#!forum/rabbitmq-c-users>
- <https://groups.google.com/forum/#!forum/rabbitmq-users>
## Latest Stable Version
The latest stable release of rabbitmq-c can be found at:
- <https://github.com/alanxz/rabbitmq-c/releases/latest>
## Documentation
API documentation for v0.8.0+ can viewed from:
<http://alanxz.github.io/rabbitmq-c/docs/0.8.0/>
## Getting started
### Building and installing
#### Prereqs:
- [CMake v3.22 or better](http://www.cmake.org/)
- A C compiler (GCC 4.4+, clang, and MSVC are test. Other compilers may also
work)
- *Optionally* [OpenSSL](http://www.openssl.org/) v1.1.1+ to enable support for
connecting to RabbitMQ over SSL/TLS
- *Optionally* [POpt](http://freecode.com/projects/popt) to build some handy
command-line tools.
- *Optionally* [XmlTo](https://fedorahosted.org/xmlto/) to build man pages for
the handy command-line tools
- *Optionally* [Doxygen](http://www.stack.nl/~dimitri/doxygen/) to build
developer API documentation.
After downloading and extracting the source from a tarball to a directory
([see above](#latest-stable-version)), the commands to build rabbitmq-c on most
systems are:
mkdir build && cd build
cmake ..
cmake --build . [--config Release]
The --config Release flag should be used in multi-configuration generators e.g.,
Visual Studio or XCode.
It is also possible to point the CMake GUI tool at the CMakeLists.txt in the root of
the source tree and generate build projects or IDE workspace
Installing the library and optionally specifying a prefix can be done with:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
cmake --build . [--config Release] --target install
More information on CMake can be found on its FAQ (http://www.cmake.org/Wiki/CMake_FAQ)
Other interesting flags that can be passed to CMake:
* `BUILD_EXAMPLES=ON/OFF` toggles building the examples. OFF by default.
* `BUILD_SHARED_LIBS=ON/OFF` toggles building rabbitmq-c as a shared library.
ON by default.
* `BUILD_STATIC_LIBS=ON/OFF` toggles building rabbitmq-c as a static library.
ON by default.
* `BUILD_TESTING=ON/OFF` toggles building test code. ON by default.
* `BUILD_TOOLS=ON/OFF` toggles building the command line tools. By default
this is ON if the build system can find the POpt header and library.
* `BUILD_TOOLS_DOCS=ON/OFF` toggles building the man pages for the command line
tools. By default this is ON if BUILD_TOOLS is ON and the build system can
find the XmlTo utility.
* `ENABLE_SSL_SUPPORT=ON/OFF` toggles building rabbitmq-c with SSL support. By
default this is ON if the OpenSSL headers and library can be found.
* `BUILD_API_DOCS=ON/OFF` - toggles building the Doxygen API documentation, by
default this is OFF
* `RUN_SYSTEM_TESTS=ON/OFF` toggles building the system tests (i.e. tests requiring
an accessible RabbitMQ server instance on localhost), by default this is OFF
## Building RabbitMQ - Using vcpkg
You can download and install RabbitMQ using the [vcpkg](https://github.com/Microsoft/vcpkg)
dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install librabbitmq
The RabbitMQ port in vcpkg is kept up to date by Microsoft team members and
community contributors. If the version is out of date,
please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
## Running the examples
Arrange for a RabbitMQ or other AMQP server to be running on
`localhost` at TCP port number 5672.
In one terminal, run
./examples/amqp_listen localhost 5672 amq.direct test
In another terminal,
./examples/amqp_sendstring localhost 5672 amq.direct test "hello world"
You should see output similar to the following in the listener's
terminal window:
Delivery 1, exchange amq.direct routingkey test
Content-type: text/plain
----
00000000: 68 65 6C 6C 6F 20 77 6F : 72 6C 64 hello world
0000000B:
## Writing applications using `librabbitmq`
Please see the `examples` directory for short examples of the use of
the `librabbitmq` library.
### Threading
You cannot share a socket, an `amqp_connection_state_t`, or a channel
between threads using `librabbitmq`. The `librabbitmq` library is
built with event-driven, single-threaded applications in mind, and
does not yet cater to any of the requirements of `pthread`ed
applications.
Your applications instead should open an AMQP connection (and an
associated socket, of course) per thread. If your program needs to
access an AMQP connection or any of its channels from more than one
thread, it is entirely responsible for designing and implementing an
appropriate locking scheme. It will generally be much simpler to have
a connection exclusive to each thread that needs AMQP service.
### License & Copyright
Portions created by Alan Antonuk are Copyright (c) 2012-2021
Alan Antonuk. All Rights Reserved.
Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
All Rights Reserved.
Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
VMware, Inc. and Tony Garnock-Jones. 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.

8
THANKS Normal file
View File

@@ -0,0 +1,8 @@
Thank-you to the following people for their contributions to the
codebase:
- Scott Brooks / Epic Advertising <scott.brooks@epicadvertising.com>
- Frank Gönninger <frank.goenninger@consequor.de>
- Daniel Schauenberg <d@unwiredcouch.com>

View File

@@ -0,0 +1,22 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

48
cmake/FindPOPT.cmake Normal file
View File

@@ -0,0 +1,48 @@
# - Try to find the popt options processing library
# The module will set the following variables
#
# POPT_FOUND - System has popt
# POPT_INCLUDE_DIR - The popt include directory
# POPT_LIBRARY - The libraries needed to use popt
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(PC_POPT QUIET popt)
endif()
# Find the include directories
find_path(POPT_INCLUDE_DIR
NAMES popt.h
HINTS
${PC_POPT_INCLUDEDIR}
${PC_POPT_INCLUDE_DIRS}
DOC "Path containing the popt.h include file"
)
find_library(POPT_LIBRARY
NAMES popt
HINTS
${PC_POPT_LIBRARYDIR}
${PC_POPT_LIBRARY_DIRS}
DOC "popt library path"
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(POPT
REQUIRED_VARS POPT_INCLUDE_DIR POPT_LIBRARY
VERSION_VAR PC_POPT_VERSION)
mark_as_advanced(POPT_INCLUDE_DIR POPT_LIBRARY)
if(POPT_FOUND AND NOT TARGET popt::popt)
add_library(popt::popt UNKNOWN IMPORTED)
set_target_properties(popt::popt PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${POPT_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${POPT_INCLUDE_DIR}"
)
endif()

98
cmake/FindXMLTO.cmake Normal file
View File

@@ -0,0 +1,98 @@
# - Convert XML docBook files to various formats
# This will convert XML docBook files to various formats like:
# man html txt dvi ps pdf
# macro XMLTO(outfiles infiles... MODES modes...)
find_program ( XMLTO_EXECUTABLE
NAMES xmlto
DOC "path to the xmlto docbook xslt frontend"
)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(XMLTO
REQUIRED_VARS XMLTO_EXECUTABLE)
mark_as_advanced( XMLTO_EXECUTABLE )
macro ( _XMLTO_FILE outfiles mode)
#special settings
set ( XMLTO_FILEEXT_man 1 )
set ( XMLTO_MODE_html xhtml-nochunks )
if ( NOT XMLTO_MODE_${mode})
set ( XMLTO_MODE_${mode} ${mode} )
endif ( NOT XMLTO_MODE_${mode} )
if ( NOT XMLTO_FILEEXT_${mode} )
set ( XMLTO_FILEEXT_${mode} ${mode} )
endif ( NOT XMLTO_FILEEXT_${mode} )
foreach ( dbFile ${ARGN} )
#TODO: set XMLTO_FILEEXT_man to value from <manvolnum>
if ( "${mode}" STREQUAL "man" )
file ( READ "${dbFile}" _DB_FILE_CONTENTS )
string ( REGEX MATCH "<manvolnum>[^<]*" XMLTO_FILEEXT_${mode} "${_DB_FILE_CONTENTS}" )
string ( REGEX REPLACE "^<manvolnum>" "" XMLTO_FILEEXT_${mode} "${XMLTO_FILEEXT_${mode}}" )
string ( REGEX REPLACE "[[:space:]]" "" XMLTO_FILEEXT_${mode} "${XMLTO_FILEEXT_${mode}}" )
endif ( "${mode}" STREQUAL "man" )
get_filename_component ( dbFilePath ${CMAKE_CURRENT_BINARY_DIR}/${dbFile} PATH )
get_filename_component ( dbFileWE ${dbFile} NAME_WE )
get_filename_component ( dbFileAbsWE ${dbFilePath}/${dbFileWE} ABSOLUTE )
add_custom_command (
OUTPUT ${dbFileAbsWE}.${XMLTO_FILEEXT_${mode}}
COMMAND ${XMLTO_EXECUTABLE} ${XMLTO_COMMAND_ARGS} -o ${dbFilePath}
${XMLTO_MODE_${mode}} "${CMAKE_CURRENT_SOURCE_DIR}/${dbFile}"
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${dbFile}
DEPENDS ${XMLTO_DEPENDS}
VERBATIM
)
set ( ${outfiles}
${${outfiles}}
${dbFileAbsWE}.${XMLTO_FILEEXT_${mode}}
)
endforeach ( dbFile )
endmacro ( _XMLTO_FILE outfiles )
macro ( XMLTO )
set ( XMLTO_MODES )
set ( XMLTO_FILES )
set ( XMLTO_HAS_MODES false )
set ( XMLTO_ADD_DEFAULT false )
foreach ( arg ${ARGN} )
if ( ${arg} STREQUAL "MODES" )
set ( XMLTO_HAS_MODES true )
elseif ( ${arg} STREQUAL "ALL" )
set ( XMLTO_ADD_DEFAULT true )
else ( ${arg} STREQUAL "MODES" )
if ( XMLTO_HAS_MODES )
set ( XMLTO_MODES ${XMLTO_MODES} ${arg} )
else ( XMLTO_HAS_MODES )
set ( XMLTO_FILES ${XMLTO_FILES} ${arg} )
endif ( XMLTO_HAS_MODES )
endif ( ${arg} STREQUAL "MODES" )
endforeach ( arg ${ARGN} )
if ( NOT XMLTO_MODES )
set ( XMLTO_MODES html )
endif ( NOT XMLTO_MODES )
foreach ( mode ${XMLTO_MODES} )
_xmlto_file ( XMLTO_FILES_${mode} ${mode} ${XMLTO_FILES} )
if ( XMLTO_ADD_DEFAULT )
add_custom_target ( ${mode} ALL
DEPENDS ${XMLTO_FILES_${mode}}
VERBATIM
)
else ( XMLTO_ADD_DEFAULT )
add_custom_target ( ${mode}
DEPENDS ${XMLTO_FILES_${mode}}
VERBATIM
)
endif ( XMLTO_ADD_DEFAULT )
endforeach ( mode )
set ( XMLTO_MODES )
set ( XMLTO_FILES )
endmacro ( XMLTO )

View File

@@ -0,0 +1,20 @@
function(get_library_version VERSION_ARG)
file(STRINGS include/rabbitmq-c/amqp.h _API_VERSION_MAJOR REGEX "^#define AMQP_VERSION_MAJOR [0-9]+$")
file(STRINGS include/rabbitmq-c/amqp.h _API_VERSION_MINOR REGEX "^#define AMQP_VERSION_MINOR [0-9]+$")
file(STRINGS include/rabbitmq-c/amqp.h _API_VERSION_PATCH REGEX "^#define AMQP_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})
# VERSION to match what is in autotools
set(${VERSION_ARG} ${_API_VERSION_MAJOR}.${_API_VERSION_MINOR}.${_API_VERSION_PATCH} PARENT_SCOPE)
endfunction()
function(compute_soversion CURRENT REVISION AGE SOVERSION)
math(EXPR MAJOR "${CURRENT} - ${AGE}")
math(EXPR MINOR "${AGE}")
math(EXPR PATCH "${REVISION}")
set(${SOVERSION} ${MAJOR} PARENT_SCOPE)
endfunction()

12
cmake/config.h.in Normal file
View File

@@ -0,0 +1,12 @@
#ifndef CONFIG_H
#define CONFIG_H
#cmakedefine HAVE_SELECT
#cmakedefine HAVE_POLL
#define AMQ_PLATFORM "@CMAKE_SYSTEM_NAME@"
#cmakedefine ENABLE_SSL_ENGINE_API
#endif /* CONFIG_H */

View File

@@ -0,0 +1,17 @@
@PACKAGE_INIT@
set(RMQ_USES_OPENSSL @ENABLE_SSL_SUPPORT@)
include(CMakeFindDependencyMacro)
if (RMQ_USES_OPENSSL)
find_dependency(OpenSSL REQUIRED)
if(OPENSSL_VERSION)
if(OPENSSL_VERSION VERSION_LESS RMQ_OPENSSL_MIN_VERSION)
MESSAGE(FATAL_ERROR "Found OpenSSL version @OPENSSL_VERSION@ but @RMQ_OPENSSL_MIN_VERSION@ or later is required")
endif()
endif()
endif ()
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
check_required_components(rabbitmq-c)

17
coverity/model.c Normal file
View File

@@ -0,0 +1,17 @@
/* Functions to help coverity do static analysis on rabbitmq-c */
typedef struct {
} amqp_rpc_reply_t;
/* librabbitmq/amqp_private.h */
void amqp_abort(const char* fmt, ...) { __coverity_panic__(); }
/* tools/common.h */
void die(const char* fmt, ...) { __coverity_panic__(); }
void die_errno(int err, const char* fmt, ...) { __coverity_panic__(); }
void die_amqp_error(int err, const char* fmt, ...) { __coverity_panic__(); }
void die_rpc(amqp_rpc_reply_t r, const char* fmt, ...) { __coverity_panic__(); }
/* examples/utils.h */
void die_on_amqp_error(amqp_rpc_reply_t* r) { __coverity_panic__(); }
void die_on_error(int r) { __coverity_panic__(); }

316
docs/Doxyfile.in Normal file
View File

@@ -0,0 +1,316 @@
# Doxyfile 1.8.4
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = rabbitmq-c
PROJECT_NUMBER = @VERSION@
PROJECT_BRIEF = "C AMQP Client library for RabbitMQ"
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 = YES
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
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 = YES
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
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 = NO
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
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 = NO
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_CURRENT_SOURCE_DIR@/README.md \
@CMAKE_CURRENT_SOURCE_DIR@/ChangeLog.md \
@CMAKE_CURRENT_SOURCE_DIR@/librabbitmq \
@CMAKE_CURRENT_SOURCE_DIR@/docs
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h \
*.md
RECURSIVE = NO
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS = amqp_private.h \
config.h
EXCLUDE_SYMBOLS =
EXAMPLE_PATH = @CMAKE_CURRENT_SOURCE_DIR@ \
@CMAKE_CURRENT_SOURCE_DIR@/examples
EXAMPLE_PATTERNS = *.c \
*.md
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = README.md
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX = amqp_
#---------------------------------------------------------------------------
# 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.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
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 = org.doxygen.Project
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.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = NO
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = YES
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# 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 =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
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_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# 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@/librabbitmq \
@CMAKE_CURRENT_BINARY_DIR@/librabbitmq
INCLUDE_FILE_PATTERNS =
PREDEFINED = AMQP_BEGIN_DECLS= \
AMQP_END_DECLS= \
AMQP_EXPORT= \
AMQP_CALL= \
AMQP_DEPRECATED(x)=x
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
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 = png
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

51
examples/CMakeLists.txt Normal file
View File

@@ -0,0 +1,51 @@
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
# SPDX-License-Identifier: mit
add_library(examples-common OBJECT)
target_sources(examples-common PRIVATE
utils.h
utils.c)
if(WIN32)
target_sources(examples-common PRIVATE win32/platform_utils.c)
else()
target_sources(examples-common PRIVATE unix/platform_utils.c)
endif()
target_link_libraries(examples-common PRIVATE rabbitmq::rabbitmq)
add_executable(amqp_sendstring amqp_sendstring.c)
target_link_libraries(amqp_sendstring PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_rpc_sendstring_client amqp_rpc_sendstring_client.c)
target_link_libraries(amqp_rpc_sendstring_client PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_exchange_declare amqp_exchange_declare.c)
target_link_libraries(amqp_exchange_declare PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_listen amqp_listen.c)
target_link_libraries(amqp_listen PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_producer amqp_producer.c)
target_link_libraries(amqp_producer PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_confirm_select amqp_confirm_select.c)
target_link_libraries(amqp_confirm_select PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_connect_timeout amqp_connect_timeout.c)
target_link_libraries(amqp_connect_timeout PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_consumer amqp_consumer.c)
target_link_libraries(amqp_consumer PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_unbind amqp_unbind.c)
target_link_libraries(amqp_unbind PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_bind amqp_bind.c)
target_link_libraries(amqp_bind PRIVATE examples-common rabbitmq::rabbitmq)
add_executable(amqp_listenq amqp_listenq.c)
target_link_libraries(amqp_listenq PRIVATE examples-common rabbitmq::rabbitmq)
if (ENABLE_SSL_SUPPORT)
add_executable(amqp_ssl_connect amqp_ssl_connect.c)
target_link_libraries(amqp_ssl_connect PRIVATE examples-common rabbitmq::rabbitmq)
endif (ENABLE_SSL_SUPPORT)

63
examples/amqp_bind.c Normal file
View File

@@ -0,0 +1,63 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
char const *exchange;
char const *bindingkey;
char const *queue;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
if (argc < 6) {
fprintf(stderr, "Usage: amqp_bind host port exchange bindingkey queue\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
exchange = argv[3];
bindingkey = argv[4];
queue = argv[5];
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
amqp_queue_bind(conn, 1, amqp_cstring_bytes(queue),
amqp_cstring_bytes(exchange), amqp_cstring_bytes(bindingkey),
amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Unbinding");
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

View File

@@ -0,0 +1,188 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include "utils.h"
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
#ifndef WINVER
#define WINVER 0x0502
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#else
#include <sys/time.h>
#endif
#define SUMMARY_EVERY_US 5000
static void send_batch(amqp_connection_state_t conn, amqp_bytes_t queue_name,
int rate_limit, int message_count) {
uint64_t start_time = now_microseconds();
int i;
int sent = 0;
int previous_sent = 0;
uint64_t previous_report_time = start_time;
uint64_t next_summary_time = start_time + SUMMARY_EVERY_US;
char message[256];
amqp_bytes_t message_bytes;
for (i = 0; i < (int)sizeof(message); i++) {
message[i] = i & 0xff;
}
message_bytes.len = sizeof(message);
message_bytes.bytes = message;
for (i = 0; i < message_count; i++) {
uint64_t now = now_microseconds();
die_on_error(amqp_basic_publish(conn, 1, amqp_literal_bytes("amq.direct"),
queue_name, 0, 0, NULL, message_bytes),
"Publishing");
sent++;
if (now > next_summary_time) {
int countOverInterval = sent - previous_sent;
double intervalRate =
countOverInterval / ((now - previous_report_time) / 1000000.0);
printf("%d ms: Sent %d - %d since last report (%d Hz)\n",
(int)(now - start_time) / 1000, sent, countOverInterval,
(int)intervalRate);
previous_sent = sent;
previous_report_time = now;
next_summary_time += SUMMARY_EVERY_US;
}
while (((i * 1000000.0) / (now - start_time)) > rate_limit) {
microsleep(2000);
now = now_microseconds();
}
}
{
uint64_t stop_time = now_microseconds();
int total_delta = (int)(stop_time - start_time);
printf("PRODUCER - Message count: %d\n", message_count);
printf("Total time, milliseconds: %d\n", total_delta / 1000);
printf("Overall messages-per-second: %g\n",
(message_count / (total_delta / 1000000.0)));
}
}
#define CONSUME_TIMEOUT_USEC 100
#define WAITING_TIMEOUT_USEC (30 * 1000)
void wait_for_acks(amqp_connection_state_t conn) {
uint64_t start_time = now_microseconds();
struct timeval timeout = {0, CONSUME_TIMEOUT_USEC};
uint64_t now = 0;
amqp_publisher_confirm_t result = {};
for (;;) {
amqp_rpc_reply_t ret;
now = now_microseconds();
if (now > start_time + WAITING_TIMEOUT_USEC) {
return;
}
amqp_maybe_release_buffers(conn);
ret = amqp_publisher_confirm_wait(conn, &timeout, &result);
if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type) {
if (AMQP_STATUS_UNEXPECTED_STATE == ret.library_error) {
fprintf(stderr, "An unexpected method was received\n");
return;
} else if (AMQP_STATUS_TIMEOUT == ret.library_error) {
// Timeout means you're done; no publisher confirms were waiting!
return;
} else {
die_on_amqp_error(ret, "Waiting for publisher confirmation");
}
}
switch (result.method) {
case AMQP_BASIC_ACK_METHOD:
fprintf(stderr, "Got an ACK!\n");
fprintf(stderr, "Here's the ACK:\n");
fprintf(stderr, "\tdelivery_tag: «%" PRIu64 "»\n",
result.payload.ack.delivery_tag);
fprintf(stderr, "\tmultiple: «%d»\n", result.payload.ack.multiple);
break;
case AMQP_BASIC_NACK_METHOD:
fprintf(stderr, "NACK\n");
break;
case AMQP_BASIC_REJECT_METHOD:
fprintf(stderr, "REJECT\n");
break;
default:
fprintf(stderr, "Unexpected method «%s» is.\n",
amqp_method_name(result.method));
};
}
}
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
int rate_limit;
int message_count;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
if (argc < 5) {
fprintf(stderr,
"Usage: amqp_producer host port rate_limit message_count\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
rate_limit = atoi(argv[3]);
message_count = atoi(argv[4]);
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
// Enable confirm_select
amqp_confirm_select(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Enable confirm-select");
send_batch(conn, amqp_literal_bytes("test queue"), rate_limit, message_count);
wait_for_acks(conn);
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

View File

@@ -0,0 +1,79 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include <assert.h>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Winsock2.h>
#else
#include <sys/time.h>
#endif
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port;
amqp_socket_t *socket;
amqp_connection_state_t conn;
struct timeval tval;
struct timeval *tv;
if (argc < 3) {
fprintf(stderr,
"Usage: amqp_connect_timeout host port [timeout_sec "
"[timeout_usec=0]]\n");
return 1;
}
if (argc > 3) {
tv = &tval;
tv->tv_sec = atoi(argv[3]);
if (argc > 4) {
tv->tv_usec = atoi(argv[4]);
} else {
tv->tv_usec = 0;
}
} else {
tv = NULL;
}
hostname = argv[1];
port = atoi(argv[2]);
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
die_on_error(amqp_socket_open_noblock(socket, hostname, port, tv),
"opening TCP socket");
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
printf("Done\n");
return 0;
}

185
examples/amqp_consumer.c Normal file
View File

@@ -0,0 +1,185 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include <assert.h>
#include "utils.h"
#define SUMMARY_EVERY_US 1000000
static void run(amqp_connection_state_t conn) {
uint64_t start_time = now_microseconds();
int received = 0;
int previous_received = 0;
uint64_t previous_report_time = start_time;
uint64_t next_summary_time = start_time + SUMMARY_EVERY_US;
amqp_frame_t frame;
uint64_t now;
for (;;) {
amqp_rpc_reply_t ret;
amqp_envelope_t envelope;
now = now_microseconds();
if (now > next_summary_time) {
int countOverInterval = received - previous_received;
double intervalRate =
countOverInterval / ((now - previous_report_time) / 1000000.0);
printf("%d ms: Received %d - %d since last report (%d Hz)\n",
(int)(now - start_time) / 1000, received, countOverInterval,
(int)intervalRate);
previous_received = received;
previous_report_time = now;
next_summary_time += SUMMARY_EVERY_US;
}
amqp_maybe_release_buffers(conn);
ret = amqp_consume_message(conn, &envelope, NULL, 0);
if (AMQP_RESPONSE_NORMAL != ret.reply_type) {
if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type &&
AMQP_STATUS_UNEXPECTED_STATE == ret.library_error) {
if (AMQP_STATUS_OK != amqp_simple_wait_frame(conn, &frame)) {
return;
}
if (AMQP_FRAME_METHOD == frame.frame_type) {
switch (frame.payload.method.id) {
case AMQP_BASIC_ACK_METHOD:
/* if we've turned publisher confirms on, and we've published a
* message here is a message being confirmed.
*/
break;
case AMQP_BASIC_RETURN_METHOD:
/* if a published message couldn't be routed and the mandatory
* flag was set this is what would be returned. The message then
* needs to be read.
*/
{
amqp_message_t message;
ret = amqp_read_message(conn, frame.channel, &message, 0);
if (AMQP_RESPONSE_NORMAL != ret.reply_type) {
return;
}
amqp_destroy_message(&message);
}
break;
case AMQP_CHANNEL_CLOSE_METHOD:
/* a channel.close method happens when a channel exception occurs,
* this can happen by publishing to an exchange that doesn't exist
* for example.
*
* In this case you would need to open another channel redeclare
* any queues that were declared auto-delete, and restart any
* consumers that were attached to the previous channel.
*/
return;
case AMQP_CONNECTION_CLOSE_METHOD:
/* a connection.close method happens when a connection exception
* occurs, this can happen by trying to use a channel that isn't
* open for example.
*
* In this case the whole connection must be restarted.
*/
return;
default:
fprintf(stderr, "An unexpected method was received %u\n",
frame.payload.method.id);
return;
}
}
}
} else {
amqp_destroy_envelope(&envelope);
}
received++;
}
}
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
char const *exchange;
char const *bindingkey;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
amqp_bytes_t queuename;
if (argc < 3) {
fprintf(stderr, "Usage: amqp_consumer host port\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
exchange = "amq.direct"; /* argv[3]; */
bindingkey = "test queue"; /* argv[4]; */
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
{
amqp_queue_declare_ok_t *r = amqp_queue_declare(
conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue");
queuename = amqp_bytes_malloc_dup(r->queue);
if (queuename.bytes == NULL) {
fprintf(stderr, "Out of memory while copying queue name");
return 1;
}
}
amqp_queue_bind(conn, 1, queuename, amqp_cstring_bytes(exchange),
amqp_cstring_bytes(bindingkey), amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Binding queue");
amqp_basic_consume(conn, 1, queuename, amqp_empty_bytes, 0, 1, 0,
amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
run(conn);
amqp_bytes_free(queuename);
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

View File

@@ -0,0 +1,62 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
char const *exchange;
char const *exchangetype;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
if (argc < 5) {
fprintf(stderr,
"Usage: amqp_exchange_declare host port exchange exchangetype\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
exchange = argv[3];
exchangetype = argv[4];
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
amqp_exchange_declare(conn, 1, amqp_cstring_bytes(exchange),
amqp_cstring_bytes(exchangetype), 0, 0, 0, 0,
amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring exchange");
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

113
examples/amqp_listen.c Normal file
View File

@@ -0,0 +1,113 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include <assert.h>
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
char const *exchange;
char const *bindingkey;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
amqp_bytes_t queuename;
if (argc < 5) {
fprintf(stderr, "Usage: amqp_listen host port exchange bindingkey\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
exchange = argv[3];
bindingkey = argv[4];
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
{
amqp_queue_declare_ok_t *r = amqp_queue_declare(
conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue");
queuename = amqp_bytes_malloc_dup(r->queue);
if (queuename.bytes == NULL) {
fprintf(stderr, "Out of memory while copying queue name");
return 1;
}
}
amqp_queue_bind(conn, 1, queuename, amqp_cstring_bytes(exchange),
amqp_cstring_bytes(bindingkey), amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Binding queue");
amqp_basic_consume(conn, 1, queuename, amqp_empty_bytes, 0, 1, 0,
amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
{
for (;;) {
amqp_rpc_reply_t res;
amqp_envelope_t envelope;
amqp_maybe_release_buffers(conn);
res = amqp_consume_message(conn, &envelope, NULL, 0);
if (AMQP_RESPONSE_NORMAL != res.reply_type) {
break;
}
printf("Delivery %u, exchange %.*s routingkey %.*s\n",
(unsigned)envelope.delivery_tag, (int)envelope.exchange.len,
(char *)envelope.exchange.bytes, (int)envelope.routing_key.len,
(char *)envelope.routing_key.bytes);
if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
printf("Content-type: %.*s\n",
(int)envelope.message.properties.content_type.len,
(char *)envelope.message.properties.content_type.bytes);
}
printf("----\n");
amqp_dump(envelope.message.body.bytes, envelope.message.body.len);
amqp_destroy_envelope(&envelope);
}
}
amqp_bytes_free(queuename);
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

90
examples/amqp_listenq.c Normal file
View File

@@ -0,0 +1,90 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include <assert.h>
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
char const *queuename;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
if (argc < 4) {
fprintf(stderr, "Usage: amqp_listenq host port queuename\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
queuename = argv[3];
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
amqp_basic_consume(conn, 1, amqp_cstring_bytes(queuename), amqp_empty_bytes,
0, 0, 0, amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
for (;;) {
amqp_rpc_reply_t res;
amqp_envelope_t envelope;
amqp_maybe_release_buffers(conn);
res = amqp_consume_message(conn, &envelope, NULL, 0);
if (AMQP_RESPONSE_NORMAL != res.reply_type) {
break;
}
printf("Delivery %u, exchange %.*s routingkey %.*s\n",
(unsigned)envelope.delivery_tag, (int)envelope.exchange.len,
(char *)envelope.exchange.bytes, (int)envelope.routing_key.len,
(char *)envelope.routing_key.bytes);
if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
printf("Content-type: %.*s\n",
(int)envelope.message.properties.content_type.len,
(char *)envelope.message.properties.content_type.bytes);
}
printf("----\n");
amqp_dump(envelope.message.body.bytes, envelope.message.body.len);
amqp_destroy_envelope(&envelope);
}
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

117
examples/amqp_producer.c Normal file
View File

@@ -0,0 +1,117 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include "utils.h"
#define SUMMARY_EVERY_US 1000000
static void send_batch(amqp_connection_state_t conn, amqp_bytes_t queue_name,
int rate_limit, int message_count) {
uint64_t start_time = now_microseconds();
int i;
int sent = 0;
int previous_sent = 0;
uint64_t previous_report_time = start_time;
uint64_t next_summary_time = start_time + SUMMARY_EVERY_US;
char message[256];
amqp_bytes_t message_bytes;
for (i = 0; i < (int)sizeof(message); i++) {
message[i] = i & 0xff;
}
message_bytes.len = sizeof(message);
message_bytes.bytes = message;
for (i = 0; i < message_count; i++) {
uint64_t now = now_microseconds();
die_on_error(amqp_basic_publish(conn, 1, amqp_literal_bytes("amq.direct"),
queue_name, 0, 0, NULL, message_bytes),
"Publishing");
sent++;
if (now > next_summary_time) {
int countOverInterval = sent - previous_sent;
double intervalRate =
countOverInterval / ((now - previous_report_time) / 1000000.0);
printf("%d ms: Sent %d - %d since last report (%d Hz)\n",
(int)(now - start_time) / 1000, sent, countOverInterval,
(int)intervalRate);
previous_sent = sent;
previous_report_time = now;
next_summary_time += SUMMARY_EVERY_US;
}
while (((i * 1000000.0) / (now - start_time)) > rate_limit) {
microsleep(2000);
now = now_microseconds();
}
}
{
uint64_t stop_time = now_microseconds();
int total_delta = (int)(stop_time - start_time);
printf("PRODUCER - Message count: %d\n", message_count);
printf("Total time, milliseconds: %d\n", total_delta / 1000);
printf("Overall messages-per-second: %g\n",
(message_count / (total_delta / 1000000.0)));
}
}
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
int rate_limit;
int message_count;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
if (argc < 5) {
fprintf(stderr,
"Usage: amqp_producer host port rate_limit message_count\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
rate_limit = atoi(argv[3]);
message_count = atoi(argv[4]);
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
send_batch(conn, amqp_literal_bytes("test queue"), rate_limit, message_count);
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

View File

@@ -0,0 +1,211 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include <assert.h>
#include "utils.h"
int main(int argc, char *argv[]) {
char const *hostname;
int port, status;
char const *exchange;
char const *routingkey;
char const *messagebody;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
amqp_bytes_t reply_to_queue;
if (argc < 6) { /* minimum number of mandatory arguments */
fprintf(stderr,
"usage:\namqp_rpc_sendstring_client host port exchange routingkey "
"messagebody\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
exchange = argv[3];
routingkey = argv[4];
messagebody = argv[5];
/*
establish a channel that is used to connect RabbitMQ server
*/
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
/*
create private reply_to queue
*/
{
amqp_queue_declare_ok_t *r = amqp_queue_declare(
conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue");
reply_to_queue = amqp_bytes_malloc_dup(r->queue);
if (reply_to_queue.bytes == NULL) {
fprintf(stderr, "Out of memory while copying queue name");
return 1;
}
}
/*
send the message
*/
{
/*
set properties
*/
amqp_basic_properties_t props;
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG |
AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_REPLY_TO_FLAG |
AMQP_BASIC_CORRELATION_ID_FLAG;
props.content_type = amqp_literal_bytes("text/plain");
props.delivery_mode = 2; /* persistent delivery mode */
props.reply_to = amqp_bytes_malloc_dup(reply_to_queue);
if (props.reply_to.bytes == NULL) {
fprintf(stderr, "Out of memory while copying queue name");
return 1;
}
props.correlation_id = amqp_literal_bytes("1");
/*
publish
*/
die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange),
amqp_cstring_bytes(routingkey), 0, 0,
&props, amqp_cstring_bytes(messagebody)),
"Publishing");
amqp_bytes_free(props.reply_to);
}
/*
wait an answer
*/
{
amqp_basic_consume(conn, 1, reply_to_queue, amqp_empty_bytes, 0, 1, 0,
amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
amqp_bytes_free(reply_to_queue);
{
amqp_frame_t frame;
int result;
amqp_basic_deliver_t *d;
amqp_basic_properties_t *p;
size_t body_target;
size_t body_received;
for (;;) {
amqp_maybe_release_buffers(conn);
result = amqp_simple_wait_frame(conn, &frame);
printf("Result: %d\n", result);
if (result < 0) {
break;
}
printf("Frame type: %u channel: %u\n", frame.frame_type, frame.channel);
if (frame.frame_type != AMQP_FRAME_METHOD) {
continue;
}
printf("Method: %s\n", amqp_method_name(frame.payload.method.id));
if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) {
continue;
}
d = (amqp_basic_deliver_t *)frame.payload.method.decoded;
printf("Delivery: %u exchange: %.*s routingkey: %.*s\n",
(unsigned)d->delivery_tag, (int)d->exchange.len,
(char *)d->exchange.bytes, (int)d->routing_key.len,
(char *)d->routing_key.bytes);
result = amqp_simple_wait_frame(conn, &frame);
if (result < 0) {
break;
}
if (frame.frame_type != AMQP_FRAME_HEADER) {
fprintf(stderr, "Expected header!");
abort();
}
p = (amqp_basic_properties_t *)frame.payload.properties.decoded;
if (p->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
printf("Content-type: %.*s\n", (int)p->content_type.len,
(char *)p->content_type.bytes);
}
printf("----\n");
body_target = (size_t)frame.payload.properties.body_size;
body_received = 0;
while (body_received < body_target) {
result = amqp_simple_wait_frame(conn, &frame);
if (result < 0) {
break;
}
if (frame.frame_type != AMQP_FRAME_BODY) {
fprintf(stderr, "Expected body!");
abort();
}
body_received += frame.payload.body_fragment.len;
assert(body_received <= body_target);
amqp_dump(frame.payload.body_fragment.bytes,
frame.payload.body_fragment.len);
}
if (body_received != body_target) {
/* Can only happen when amqp_simple_wait_frame returns <= 0 */
/* We break here to close the connection */
break;
}
/* everything was fine, we can quit now because we received the reply */
break;
}
}
}
/*
closing
*/
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

View File

@@ -0,0 +1,71 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
char const *exchange;
char const *routingkey;
char const *messagebody;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
if (argc < 6) {
fprintf(
stderr,
"Usage: amqp_sendstring host port exchange routingkey messagebody\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
exchange = argv[3];
routingkey = argv[4];
messagebody = argv[5];
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
{
amqp_basic_properties_t props;
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG;
props.content_type = amqp_literal_bytes("text/plain");
props.delivery_mode = 2; /* persistent delivery mode */
die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange),
amqp_cstring_bytes(routingkey), 0, 0,
&props, amqp_cstring_bytes(messagebody)),
"Publishing");
}
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

102
examples/amqp_ssl_connect.c Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/ssl_socket.h>
#include <assert.h>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Winsock2.h>
#else
#include <sys/time.h>
#endif
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port;
int timeout;
amqp_socket_t *socket;
amqp_connection_state_t conn;
struct timeval tval;
struct timeval *tv;
if (argc < 3) {
fprintf(stderr,
"Usage: amqp_ssl_connect host port timeout_sec "
"[cacert.pem [engine engine_ID] [verifypeer] [verifyhostname] "
"[key.pem cert.pem]]\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
timeout = atoi(argv[3]);
if (timeout > 0) {
tv = &tval;
tv->tv_sec = timeout;
tv->tv_usec = 0;
} else {
tv = NULL;
}
conn = amqp_new_connection();
socket = amqp_ssl_socket_new(conn);
if (!socket) {
die("creating SSL/TLS socket");
}
amqp_ssl_socket_set_verify_peer(socket, 0);
amqp_ssl_socket_set_verify_hostname(socket, 0);
if (argc > 5) {
int nextarg = 5;
die_on_error(amqp_ssl_socket_set_cacert(socket, argv[4]),
"setting CA certificate");
if (argc > nextarg && !strcmp("engine", argv[nextarg])) {
amqp_set_ssl_engine(argv[++nextarg]);
nextarg++;
}
if (argc > nextarg && !strcmp("verifypeer", argv[nextarg])) {
amqp_ssl_socket_set_verify_peer(socket, 1);
nextarg++;
}
if (argc > nextarg && !strcmp("verifyhostname", argv[nextarg])) {
amqp_ssl_socket_set_verify_hostname(socket, 1);
nextarg++;
}
if (argc > nextarg + 1) {
die_on_error(
amqp_ssl_socket_set_key(socket, argv[nextarg + 1], argv[nextarg]),
"setting client key");
}
}
die_on_error(amqp_socket_open_noblock(socket, hostname, port, tv),
"opening SSL/TLS connection");
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
die_on_error(amqp_uninitialize_ssl_library(), "Uninitializing SSL library");
printf("Done\n");
return 0;
}

63
examples/amqp_unbind.c Normal file
View File

@@ -0,0 +1,63 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include "utils.h"
int main(int argc, char const *const *argv) {
char const *hostname;
int port, status;
char const *exchange;
char const *bindingkey;
char const *queue;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
if (argc < 6) {
fprintf(stderr, "Usage: amqp_unbind host port exchange bindingkey queue\n");
return 1;
}
hostname = argv[1];
port = atoi(argv[2]);
exchange = argv[3];
bindingkey = argv[4];
queue = argv[5];
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket");
}
status = amqp_socket_open(socket, hostname, port);
if (status) {
die("opening TCP socket");
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
"guest", "guest"),
"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
amqp_queue_unbind(conn, 1, amqp_cstring_bytes(queue),
amqp_cstring_bytes(exchange),
amqp_cstring_bytes(bindingkey), amqp_empty_table);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Unbinding");
die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
"Closing channel");
die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"Closing connection");
die_on_error(amqp_destroy_connection(conn), "Ending connection");
return 0;
}

View File

@@ -0,0 +1,20 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
uint64_t now_microseconds(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;
}
void microsleep(int usec) {
struct timespec req;
req.tv_sec = 0;
req.tv_nsec = 1000 * usec;
nanosleep(&req, NULL);
}

156
examples/utils.c Normal file
View File

@@ -0,0 +1,156 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/framing.h>
#include <stdint.h>
#include "utils.h"
void die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
void die_on_error(int x, char const *context) {
if (x < 0) {
fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x));
exit(1);
}
}
void die_on_amqp_error(amqp_rpc_reply_t x, char const *context) {
switch (x.reply_type) {
case AMQP_RESPONSE_NORMAL:
return;
case AMQP_RESPONSE_NONE:
fprintf(stderr, "%s: missing RPC reply type!\n", context);
break;
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x.library_error));
break;
case AMQP_RESPONSE_SERVER_EXCEPTION:
switch (x.reply.id) {
case AMQP_CONNECTION_CLOSE_METHOD: {
amqp_connection_close_t *m =
(amqp_connection_close_t *)x.reply.decoded;
fprintf(stderr, "%s: server connection error %uh, message: %.*s\n",
context, m->reply_code, (int)m->reply_text.len,
(char *)m->reply_text.bytes);
break;
}
case AMQP_CHANNEL_CLOSE_METHOD: {
amqp_channel_close_t *m = (amqp_channel_close_t *)x.reply.decoded;
fprintf(stderr, "%s: server channel error %uh, message: %.*s\n",
context, m->reply_code, (int)m->reply_text.len,
(char *)m->reply_text.bytes);
break;
}
default:
fprintf(stderr, "%s: unknown server error, method id 0x%08X\n",
context, x.reply.id);
break;
}
break;
}
exit(1);
}
static void dump_row(long count, int numinrow, int *chs) {
int i;
printf("%08lX:", count - numinrow);
if (numinrow > 0) {
for (i = 0; i < numinrow; i++) {
if (i == 8) {
printf(" :");
}
printf(" %02X", chs[i]);
}
for (i = numinrow; i < 16; i++) {
if (i == 8) {
printf(" :");
}
printf(" ");
}
printf(" ");
for (i = 0; i < numinrow; i++) {
if (isprint(chs[i])) {
printf("%c", chs[i]);
} else {
printf(".");
}
}
}
printf("\n");
}
static int rows_eq(int *a, int *b) {
int i;
for (i = 0; i < 16; i++)
if (a[i] != b[i]) {
return 0;
}
return 1;
}
void amqp_dump(void const *buffer, size_t len) {
unsigned char *buf = (unsigned char *)buffer;
long count = 0;
int numinrow = 0;
int chs[16];
int oldchs[16] = {0};
int showed_dots = 0;
size_t i;
for (i = 0; i < len; i++) {
int ch = buf[i];
if (numinrow == 16) {
int j;
if (rows_eq(oldchs, chs)) {
if (!showed_dots) {
showed_dots = 1;
printf(
" .. .. .. .. .. .. .. .. : .. .. .. .. .. .. .. ..\n");
}
} else {
showed_dots = 0;
dump_row(count, numinrow, chs);
}
for (j = 0; j < 16; j++) {
oldchs[j] = chs[j];
}
numinrow = 0;
}
count++;
chs[numinrow++] = ch;
}
dump_row(count, numinrow, chs);
if (numinrow != 0) {
printf("%08lX:\n", count);
}
}

16
examples/utils.h Normal file
View File

@@ -0,0 +1,16 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef librabbitmq_examples_utils_h
#define librabbitmq_examples_utils_h
void die(const char *fmt, ...);
extern void die_on_error(int x, char const *context);
extern void die_on_amqp_error(amqp_rpc_reply_t x, char const *context);
extern void amqp_dump(void const *buffer, size_t len);
extern uint64_t now_microseconds(void);
extern void microsleep(int usec);
#endif

View File

@@ -0,0 +1,15 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <windows.h>
uint64_t now_microseconds(void) {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return (((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime) /
10;
}
void microsleep(int usec) { Sleep(usec / 1000); }

20
fuzz/CMakeLists.txt Normal file
View File

@@ -0,0 +1,20 @@
include_directories(
${LIBRABBITMQ_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}/../librabbitmq/
${CMAKE_CURRENT_SOURCE_DIR}/../librabbitmq/)
add_definitions(-DHAVE_CONFIG_H)
add_definitions(-DAMQP_STATIC)
SET(CMAKE_EXE_LINKER_FLAGS "${LIB_FUZZING_ENGINE}")
if(BUILD_OSSFUZZ)
add_executable(fuzz_url fuzz_url.c)
target_link_libraries(fuzz_url rabbitmq-static)
add_executable(fuzz_table fuzz_table.c)
target_link_libraries(fuzz_table rabbitmq-static)
add_executable(fuzz_server fuzz_server.c)
target_link_libraries(fuzz_server rabbitmq-static)
endif ()

27
fuzz/README.md Normal file
View File

@@ -0,0 +1,27 @@
### OSS-Fuzz in House
#### Export Flags
```
export CC=clang
export CXX=clang++
export CFLAGS=-fsanitize=fuzzer-no-link,address
export LIB_FUZZING_ENGINE=-fsanitize=fuzzer
export LDFLAGS=-fsanitize=address
```
#### Build cmake Fuzzer
```
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_OSSFUZZ=ON \
-DCMAKE_C_COMPILER=$CC -DCMAKE_CXX_COMPILER=$CXX \
-DCMAKE_C_FLAGS=$CFLAGS -DCMAKE_EXE_LINKER_FLAGS=$CFLAGS \
-DLIB_FUZZING_ENGINE=$LIB_FUZZING_ENGINE \
../
```
#### Run Fuzzer
```
mkdir coverage
./fuzz/fuzz_url coverage/ ../fuzz/input/
./fuzz/fuzz_table coverage/ ../fuzz/input/
./fuzz/fuzz_server coverage/ ../fuzz/input/
```

152
fuzz/fuzz_server.c Normal file
View File

@@ -0,0 +1,152 @@
// Copyright 2007 - 2022, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
struct Fuzzer {
int socket;
uint16_t port;
pthread_t thread;
uint64_t size;
uint8_t *buffer;
};
typedef struct Fuzzer Fuzzer;
#define PORT 5672
#define kMinInputLength 8
#define kMaxInputLength 1024
void client(Fuzzer *fuzzer);
void fuzzinit(Fuzzer *fuzzer) {
struct sockaddr_in server_addr;
int res;
fuzzer->socket = socket(AF_INET, SOCK_STREAM, 0);
if (fuzzer->socket == -1) {
fprintf(stderr, "socket failed %s\n", strerror(errno));
exit(1);
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(fuzzer->port);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
res = setsockopt(fuzzer->socket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
if (res) {
fprintf(stderr, "setsockopt failed: %s\n", strerror(errno));
exit(1);
}
res = bind(fuzzer->socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (res) {
fprintf(stderr, "bind failed: %s\n", strerror(errno));
exit(1);
}
res = listen(fuzzer->socket, 1);
if (res) {
fprintf(stderr, "listen failed: %s\n", strerror(errno));
exit(1);
}
}
void *Server(void *args) {
Fuzzer *fuzzer = (Fuzzer *)args;
int client;
int res;
char clientData[10240];
client = accept(fuzzer->socket, NULL, NULL);
if (client == -1) {
fprintf(stderr, "accept failed: %s\n", strerror(errno));
exit(1);
}
res = recv(client, clientData, sizeof(clientData), 0);
if (res == -1) {
fprintf(stderr, "recv failed: %s\n", strerror(errno));
exit(1);
}
res = send(client, fuzzer->buffer, fuzzer->size, 0);
if (res == -1) {
fprintf(stderr, "send failed: %s\n", strerror(errno));
exit(1);
}
res = shutdown(client, SHUT_RDWR);
close(client);
return NULL;
}
void clean(Fuzzer *fuzzer) {
shutdown(fuzzer->socket, SHUT_RDWR);
close(fuzzer->socket);
free(fuzzer->buffer);
free(fuzzer);
}
extern int LLVMFuzzerTestOneInput(const char *data, size_t size) {
if (size < kMinInputLength || size > kMaxInputLength) {
return 0;
}
Fuzzer *fuzzer = (Fuzzer *)malloc(sizeof(Fuzzer));
fuzzer->port = PORT;
fuzzer->size = size;
fuzzer->buffer = malloc(fuzzer->size);
memcpy(fuzzer->buffer, data, size);
fuzzinit(fuzzer);
pthread_create(&fuzzer->thread, NULL, Server, fuzzer);
client(fuzzer);
pthread_join(fuzzer->thread, NULL);
clean(fuzzer);
return 0;
}
void client(Fuzzer *fuzzer) {
char const *hostname;
int status;
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
hostname = "127.0.0.1";
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
exit(1);
}
status = amqp_socket_open(socket, hostname, fuzzer->port);
if (status != AMQP_STATUS_OK) {
int sav_errno = errno;
fprintf(stderr, "amqp_socket_open failed: %s\n", amqp_error_string2(status));
fprintf(stderr, "amqp_socket_open errno: %d: %s\n", sav_errno, strerror(sav_errno));
exit(1);
}
amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, "guest", "guest");
amqp_destroy_connection(conn);
}

32
fuzz/fuzz_table.c Normal file
View File

@@ -0,0 +1,32 @@
// Copyright 2007 - 2022, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <errno.h>
#include <inttypes.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
extern int LLVMFuzzerTestOneInput(const char *data, size_t size) {
int unused_result;
amqp_pool_t pool;
init_amqp_pool(&pool, 4096);
{
amqp_table_t decoded;
size_t decoding_offset = 0;
amqp_bytes_t decoding_bytes;
decoding_bytes.len = size;
decoding_bytes.bytes = (uint8_t *)data;
unused_result =
amqp_decode_table(decoding_bytes, &pool, &decoded, &decoding_offset);
}
empty_amqp_pool(&pool);
return 0;
}

24
fuzz/fuzz_url.c Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2007 - 2022, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <rabbitmq-c/amqp.h>
extern int LLVMFuzzerTestOneInput(const char *data, size_t size) {
// amqp_parse_url expects null-terminated string that it can modify,
// LLVMFuzzer expects that data will not be modified and won't necessarily
// null terminate the string, so do that here.
char* in = malloc(size + 1);
memcpy(in, data, size);
in[size] = '\0';
struct amqp_connection_info ci;
amqp_parse_url(in, &ci);
free(in);
return 0;
}

BIN
fuzz/input/fuzz_server.raw Normal file

Binary file not shown.

BIN
fuzz/input/fuzz_table.raw Normal file

Binary file not shown.

BIN
fuzz/input/fuzz_url.raw Normal file

Binary file not shown.

15
include/amqp.h Normal file
View File

@@ -0,0 +1,15 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef AMQP_H
#define AMQP_H
#ifdef _MSC_VER
# pragma message("warning: amqp.h is deprecated, use rabbitmq-c/amqp.h instead.")
#else
# warning "amqp.h is deprecated, use rabbitmq-c/amqp.h instead."
#endif
#include <rabbitmq-c/amqp.h>
#endif /* AMQP_H */

16
include/amqp_framing.h Normal file
View File

@@ -0,0 +1,16 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
/** @file amqp_framing.h */
#ifndef AMQP_FRAMING_H
#define AMQP_FRAMING_H
#ifdef _MSC_VER
# pragma message("warning: amqp_framing.h is deprecated, use rabbitmq-c/framing.h instead.")
#else
# warning "amqp_framing.h is deprecated, use rabbitmq-c/framing.h instead."
#endif
#include <rabbitmq-c/framing.h>
#endif /* AMQP_FRAMING_H */

17
include/amqp_ssl_socket.h Normal file
View File

@@ -0,0 +1,17 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
/** \file */
#ifndef AMQP_SSL_H
#define AMQP_SSL_H
#ifdef _MSC_VER
# pragma message("warning: amqp_ssl_socket.h is deprecated, use rabbitmq-c/ssl_socket.h instead.")
#else
# warning "amqp_ssl_socket.h is deprecated, use rabbitmq-c/ssl_socket.h instead."
#endif
#include <rabbitmq-c/ssl_socket.h>
#endif /* AMQP_SSL_H */

15
include/amqp_tcp_socket.h Normal file
View File

@@ -0,0 +1,15 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef AMQP_TCP_SOCKET_H
#define AMQP_TCP_SOCKET_H
#ifdef _MSC_VER
# pragma message("warning: amqp_tcp_socket.h is deprecated, use rabbitmq-c/tcp_socket.h instead.")
#else
# warning "amqp_tcp_socket.h is deprecated, use rabbitmq-c/tcp_socket.h instead."
#endif
#include <rabbitmq-c/tcp_socket.h>
#endif /* AMQP_TCP_SOCKET_H */

2517
include/rabbitmq-c/amqp.h Normal file

File diff suppressed because it is too large Load Diff

1150
include/rabbitmq-c/framing.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,305 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
/** \file */
#ifndef RABBITMQ_C_SSL_SOCKET_H
#define RABBITMQ_C_SSL_SOCKET_H
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/export.h>
AMQP_BEGIN_DECLS
/**
* Create a new SSL/TLS socket object.
*
* The returned socket object is owned by the \ref amqp_connection_state_t
* object and will be destroyed when the state object is destroyed or a new
* socket object is created.
*
* If the socket object creation fails, the \ref amqp_connection_state_t object
* will not be changed.
*
* The object returned by this function can be retrieved from the
* amqp_connection_state_t object later using the amqp_get_socket() function.
*
* Calling this function may result in the underlying SSL library being
* initialized.
* \sa amqp_set_initialize_ssl_library()
*
* \param [in,out] state The connection object that owns the SSL/TLS socket
* \return A new socket object or NULL if an error occurred.
*
* \since v0.4.0
*/
AMQP_EXPORT
amqp_socket_t *AMQP_CALL amqp_ssl_socket_new(amqp_connection_state_t state);
/**
* Get the internal OpenSSL context. Caveat emptor.
*
* \param [in,out] self An SSL/TLS socket object.
*
* \return A pointer to the internal OpenSSL context. This should be cast to
* <tt>SSL_CTX*</tt>.
*
* \since v0.9.0
*/
AMQP_EXPORT
void *AMQP_CALL amqp_ssl_socket_get_context(amqp_socket_t *self);
/**
* Enable loading of the CA certificates from the default location.
*
* \param [in,out] self An SSL/TLS socket object.
*
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
* failure.
*
* \since v0.14.0
*/
AMQP_EXPORT
int AMQP_CALL amqp_ssl_socket_enable_default_verify_paths(amqp_socket_t *self);
/**
* Set the CA certificate.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] cacert Path to the CA cert file in PEM format.
*
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
* failure.
*
* \since v0.4.0
*/
AMQP_EXPORT
int AMQP_CALL amqp_ssl_socket_set_cacert(amqp_socket_t *self,
const char *cacert);
/**
* Set the password of key in PEM format.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] passwd The password of key in PEM format.
*
* \since v0.11.0
*/
AMQP_EXPORT
void AMQP_CALL amqp_ssl_socket_set_key_passwd(amqp_socket_t *self,
const char *passwd);
/**
* Set the client key.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] cert Path to the client certificate in PEM foramt.
* \param [in] key Path to the client key in PEM format.
*
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
* failure.
*
* \since v0.4.0
*/
AMQP_EXPORT
int AMQP_CALL amqp_ssl_socket_set_key(amqp_socket_t *self, const char *cert,
const char *key);
/**
* Set the client key use the engine.
*
* This function requires amqp_set_ssl_engine() has been called.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] cert Path to the client certificate in PEM foramt.
* \param [in] the key ID.
*
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
* failure. May return \ref AMQP_STATUS_SSL_UNIMPLEMENTED if OpenSSL does
* not support the ENGINE API.
*
* \since v0.11.0
*/
AMQP_EXPORT
int AMQP_CALL amqp_ssl_socket_set_key_engine(amqp_socket_t *self,
const char *cert, const char *key);
/**
* Set the client key from a buffer.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] cert Path to the client certificate in PEM foramt.
* \param [in] key A buffer containing client key in PEM format.
* \param [in] n The length of the buffer.
*
* \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
* failure.
*
* \since v0.4.0
*/
AMQP_EXPORT
int AMQP_CALL amqp_ssl_socket_set_key_buffer(amqp_socket_t *self,
const char *cert, const void *key,
size_t n);
/**
* Enable or disable peer verification.
*
* \deprecated use \amqp_ssl_socket_set_verify_peer and
* \amqp_ssl_socket_set_verify_hostname instead.
*
* If peer verification is enabled then the common name in the server
* certificate must match the server name. Peer verification is enabled by
* default.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] verify Enable or disable peer verification.
*
* \since v0.4.0
*/
AMQP_DEPRECATED_EXPORT void AMQP_CALL
amqp_ssl_socket_set_verify(amqp_socket_t *self, amqp_boolean_t verify);
/**
* Enable or disable peer verification.
*
* Peer verification validates the certificate chain that is sent by the broker.
* Hostname validation is controlled by \amqp_ssl_socket_set_verify_peer.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] verify enable or disable peer validation
*
* \since v0.8.0
*/
AMQP_EXPORT
void AMQP_CALL amqp_ssl_socket_set_verify_peer(amqp_socket_t *self,
amqp_boolean_t verify);
/**
* Enable or disable hostname verification.
*
* Hostname verification checks the broker cert for a CN or SAN that matches the
* hostname that amqp_socket_open() is presented. Peer verification is
* controlled by \amqp_ssl_socket_set_verify_peer
*
* \since v0.8.0
*/
AMQP_EXPORT
void AMQP_CALL amqp_ssl_socket_set_verify_hostname(amqp_socket_t *self,
amqp_boolean_t verify);
typedef enum {
AMQP_TLSv1 = 1,
AMQP_TLSv1_1 = 2,
AMQP_TLSv1_2 = 3,
AMQP_TLSv1_3 = 4,
AMQP_TLSvLATEST = 0xFFFF
} amqp_tls_version_t;
/**
* Set min and max TLS versions.
*
* Set the oldest and newest acceptable TLS versions that are acceptable when
* connecting to the broker. Set min == max to restrict to just that
* version.
*
* As of v0.14.0 the defaults are TLS v1.2 and TLS v1.3. TLS v1.1 and lower are
* no longer supported.
*
* \param [in,out] self An SSL/TLS socket object.
* \param [in] min the minimum acceptable TLS version
* \param [in] max the maxmium acceptable TLS version
* \returns AMQP_STATUS_OK on success, AMQP_STATUS_UNSUPPORTED if OpenSSL does
* not support the requested TLS version, AMQP_STATUS_INVALID_PARAMETER if an
* invalid combination of parameters is passed.
*
* \since v0.8.0
*/
AMQP_EXPORT
int AMQP_CALL amqp_ssl_socket_set_ssl_versions(amqp_socket_t *self,
amqp_tls_version_t min,
amqp_tls_version_t max);
/**
* Sets whether rabbitmq-c will initialize OpenSSL.
*
* \deprecated Since v0.13.0 this is a no-op. OpenSSL automatically manages
* library initialization and uninitialization.
*
* OpenSSL requires a one-time initialization across a whole program, this sets
* whether or not rabbitmq-c will initialize the SSL library when the first call
* to amqp_ssl_socket_new() is made. You should call this function with
* do_init = 0 if the underlying SSL library is initialized somewhere else
* the program.
*
* Failing to initialize or double initialization of the SSL library will
* result in undefined behavior
*
* By default rabbitmq-c will initialize the underlying SSL library.
*
* NOTE: calling this function after the first socket has been opened with
* amqp_open_socket() will not have any effect.
*
* \param [in] do_initialize If 0 rabbitmq-c will not initialize the SSL
* library, otherwise rabbitmq-c will initialize the
* SSL library
*
* \since v0.4.0
*/
AMQP_DEPRECATED_EXPORT
void AMQP_CALL amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize);
/**
* Initialize the underlying SSL/TLS library.
*
* \deprecated Since v0.13.0 this is a no-op. OpenSSL automatically manages
* library initialization and uninitialization.
*
* The OpenSSL library requires a one-time initialization across the whole
* program.
*
* This function unconditionally initializes OpenSSL so that rabbitmq-c may
* use it.
*
* This function is thread-safe, and may be called more than once.
*
* \return AMQP_STATUS_OK on success.
*
* \since v0.9.0
*/
AMQP_DEPRECATED_EXPORT
int AMQP_CALL amqp_initialize_ssl_library(void);
/**
* Set the engine for underlying SSL/TLS library.
*
* This function is thread-safe, and may be called more than once.
*
* This function requires amqp_initialize_ssl_library() or amqp_ssl_socket_new()
* has been called.
*
* \param [in] engine the engine ID
* \return AMQP_STATUS_OK on success. May return \ref AMQP_STATUS_SSL_UNIMPLEMENTED
* if OpenSSL does not support the ENGINE API.
*
* \since v0.11.0
*/
AMQP_EXPORT
int amqp_set_ssl_engine(const char *engine);
/**
* Uninitialize the underlying SSL/TLS library.
*
* \deprecated Since v0.13.0 this is a no-op. OpenSSL automatically manages
* library initialization and uninitialization.
*
* \return AMQP_STATUS_OK on success.
*
* \since v0.9.0
*/
AMQP_DEPRECATED_EXPORT
int AMQP_CALL amqp_uninitialize_ssl_library(void);
AMQP_END_DECLS
#endif /* RABBITMQ_C_SSL_SOCKET_H */

View File

@@ -0,0 +1,47 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
/** \file */
/**
* A TCP socket connection.
*/
#ifndef RABBITMQ_C_TCP_SOCKET_H
#define RABBITMQ_C_TCP_SOCKET_H
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/export.h>
AMQP_BEGIN_DECLS
/**
* Create a new TCP socket.
*
* Call amqp_connection_close() to release socket resources.
*
* \return A new socket object or NULL if an error occurred.
*
* \since v0.4.0
*/
AMQP_EXPORT
amqp_socket_t *AMQP_CALL amqp_tcp_socket_new(amqp_connection_state_t state);
/**
* Assign an open file descriptor to a socket object.
*
* This function must not be used in conjunction with amqp_socket_open(), i.e.
* the socket connection should already be open(2) when this function is
* called.
*
* \param [in,out] self A TCP socket object.
* \param [in] sockfd An open socket descriptor.
*
* \since v0.4.0
*/
AMQP_EXPORT
void AMQP_CALL amqp_tcp_socket_set_sockfd(amqp_socket_t *self, int sockfd);
AMQP_END_DECLS
#endif /* RABBITMQ_C_TCP_SOCKET_H */

13
librabbitmq.pc.in Normal file
View File

@@ -0,0 +1,13 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: rabbitmq-c
Description: An AMQP 0-9-1 client library
Version: @RMQ_VERSION@
URL: https://github.com/alanxz/rabbitmq-c
Requires.private: @requires_private@
Libs: -L${libdir} -lrabbitmq
Libs.private: @libs_private@
Cflags: -I${includedir}

190
librabbitmq/CMakeLists.txt Normal file
View File

@@ -0,0 +1,190 @@
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
# SPDX-License-Identifier: mit
if (ENABLE_SSL_SUPPORT)
SET(AMQP_SSL_SOCKET_SHIM_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include/amqp_ssl_socket.h)
set(AMQP_SSL_SOCKET_H_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include/rabbitmq-c/ssl_socket.h)
set(AMQP_SSL_SRCS
amqp_openssl.c
amqp_openssl_bio.c
amqp_openssl_bio.h
)
set(AMQP_SSL_LIBS OpenSSL::SSL)
if (APPLE)
# Apple has deprecated OpenSSL in 10.7+. This disables that warning.
set_source_files_properties(${AMQP_SSL_SRCS}
PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations)
endif()
if (WIN32 AND NOT CMAKE_USE_PTHREADS_INIT)
set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} win32/threads.h win32/threads.c)
set(SSL_INCLUDE_DIRS win32)
else()
set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} unix/threads.h)
set(SSL_INCLUDE_DIRS unix)
endif()
endif()
set(PUBLIC_INCLUDE_DIRS
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
set(PRIVATE_INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${SSL_INCLUDE_DIRS}
)
set(RMQ_SOURCES
../include/amqp.h
../include/amqp_framing.h
${AMQP_SSL_SOCKET_SHIM_PATH}
../include/amqp_tcp_socket.h
../include/rabbitmq-c/amqp.h
../include/rabbitmq-c/framing.h
${AMQP_SSL_SOCKET_H_PATH}
../include/rabbitmq-c/tcp_socket.h
amqp_api.c
amqp_connection.c
amqp_consumer.c
amqp_framing.c
amqp_mem.c
${AMQP_SSL_SRCS}
amqp_private.h
amqp_socket.c
amqp_socket.h
amqp_table.c
amqp_table.h
amqp_tcp_socket.c
amqp_time.c
amqp_time.h
amqp_url.c
)
set(RMQ_LIBRARIES ${AMQP_SSL_LIBS} ${SOCKET_LIBRARIES} ${LIBRT} ${CMAKE_THREAD_LIBS_INIT})
if(BUILD_SHARED_LIBS)
if (NOT APPLE)
set(CMAKE_INSTALL_RPATH $ORIGIN)
endif()
add_library(rabbitmq SHARED)
set(RMQ_GEN_EXPORT_TARGET rabbitmq)
target_sources(rabbitmq PRIVATE ${RMQ_SOURCES})
target_include_directories(rabbitmq
PUBLIC ${PUBLIC_INCLUDE_DIRS}
PRIVATE ${PRIVATE_INCLUDE_DIRS}
)
target_compile_definitions(rabbitmq PRIVATE -DHAVE_CONFIG_H)
target_link_libraries(rabbitmq PRIVATE ${RMQ_LIBRARIES})
set_target_properties(rabbitmq PROPERTIES
VERSION ${RMQ_VERSION}
SOVERSION ${RMQ_SOVERSION}
)
if (APPLE)
set_target_properties(rabbitmq PROPERTIES
MACHO_CURRENT_VERSION ${RMQ_SOVERSION}.${RMQ_SOVERSION_AGE}.${RMQ_SOVERSION_REVISION}
MACHO_COMPATIBILITY_VERSION ${RMQ_SOVERSION}
)
endif()
if (WIN32)
set_target_properties(rabbitmq PROPERTIES OUTPUT_NAME rabbitmq.${RMQ_SOVERSION})
endif()
install(TARGETS rabbitmq EXPORT "${targets_export_name}"
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT rabbitmq-c-runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT rabbitmq-c-runtime
NAMELINK_COMPONENT rabbitmq-c-development
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT rabbitmq-c-development
)
add_library(rabbitmq::rabbitmq ALIAS rabbitmq)
endif()
if(BUILD_STATIC_LIBS)
add_library(rabbitmq-static STATIC)
target_sources(rabbitmq-static PRIVATE ${RMQ_SOURCES})
if (NOT BUILD_SHARED_LIBS)
set(RMQ_GEN_EXPORT_TARGET rabbitmq-static)
endif()
target_include_directories(rabbitmq-static
PUBLIC ${PUBLIC_INCLUDE_DIRS}
PRIVATE ${PRIVATE_INCLUDE_DIRS}
)
target_compile_definitions(rabbitmq-static
PUBLIC -DAMQP_STATIC
PRIVATE -DHAVE_CONFIG_H
)
target_link_libraries(rabbitmq-static PRIVATE ${RMQ_LIBRARIES})
set_target_properties(rabbitmq-static PROPERTIES
VERSION ${RMQ_VERSION}
SOVERSION ${RMQ_SOVERSION}
)
if (APPLE)
set_target_properties(rabbitmq-static PROPERTIES
MACHO_CURRENT_VERSION ${RMQ_SOVERSION}.${RMQ_SOVERSION_AGE}.${RMQ_SOVERSION_REVISION}
MACHO_COMPATIBILITY_VERSION ${RMQ_SOVERSION}
)
endif()
if (WIN32)
set_target_properties(rabbitmq-static PROPERTIES OUTPUT_NAME librabbitmq.${RMQ_SOVERSION})
else()
set_target_properties(rabbitmq-static PROPERTIES OUTPUT_NAME rabbitmq)
endif()
if(INSTALL_STATIC_LIBS)
install(TARGETS rabbitmq-static EXPORT "${targets_export_name}"
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT rabbitmq-c-development
)
endif()
add_library(rabbitmq::rabbitmq-static ALIAS rabbitmq-static)
endif()
include(GenerateExportHeader)
generate_export_header(${RMQ_GEN_EXPORT_TARGET}
BASE_NAME AMQP
EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/../include/rabbitmq-c/export.h
STATIC_DEFINE AMQP_STATIC
INCLUDE_GUARD_NAME RABBITMQ_C_EXPORT_H
)
install(FILES
../include/amqp.h
../include/amqp_framing.h
../include/amqp_tcp_socket.h
${AMQP_SSL_SOCKET_SHIM_PATH}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT rabbitmq-c-development
)
install(FILES
../include/rabbitmq-c/amqp.h
../include/rabbitmq-c/framing.h
../include/rabbitmq-c/tcp_socket.h
${AMQP_SSL_SOCKET_H_PATH}
${CMAKE_CURRENT_BINARY_DIR}/../include/rabbitmq-c/export.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rabbitmq-c
COMPONENT rabbitmq-c-development
)

421
librabbitmq/amqp_api.c Normal file
View File

@@ -0,0 +1,421 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef _MSC_VER
/* MSVC complains about sprintf being deprecated in favor of sprintf_s */
#define _CRT_SECURE_NO_WARNINGS
/* MSVC complains about strdup being deprecated in favor of _strdup */
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#include "amqp_private.h"
#include "amqp_time.h"
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ERROR_MASK (0x00FF)
#define ERROR_CATEGORY_MASK (0xFF00)
enum error_category_enum_ { EC_base = 0, EC_tcp = 1, EC_ssl = 2 };
static const char *base_error_strings[] = {
/* AMQP_STATUS_OK 0x0 */
"operation completed successfully",
/* AMQP_STATUS_NO_MEMORY -0x0001 */
"could not allocate memory",
/* AMQP_STATUS_BAD_AQMP_DATA -0x0002 */
"invalid AMQP data",
/* AMQP_STATUS_UNKNOWN_CLASS -0x0003 */
"unknown AMQP class id",
/* AMQP_STATUS_UNKNOWN_METHOD -0x0004 */
"unknown AMQP method id",
/* AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED -0x0005 */
"hostname lookup failed",
/* AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION -0x0006 */
"incompatible AMQP version",
/* AMQP_STATUS_CONNECTION_CLOSED -0x0007 */
"connection closed unexpectedly",
/* AMQP_STATUS_BAD_AMQP_URL -0x0008 */
"could not parse AMQP URL",
/* AMQP_STATUS_SOCKET_ERROR -0x0009 */
"a socket error occurred",
/* AMQP_STATUS_INVALID_PARAMETER -0x000A */
"invalid parameter",
/* AMQP_STATUS_TABLE_TOO_BIG -0x000B */
"table too large for buffer",
/* AMQP_STATUS_WRONG_METHOD -0x000C */
"unexpected method received",
/* AMQP_STATUS_TIMEOUT -0x000D */
"request timed out",
/* AMQP_STATUS_TIMER_FAILED -0x000E */
"system timer has failed",
/* AMQP_STATUS_HEARTBEAT_TIMEOUT -0x000F */
"heartbeat timeout, connection closed",
/* AMQP_STATUS_UNEXPECTED STATE -0x0010 */
"unexpected protocol state",
/* AMQP_STATUS_SOCKET_CLOSED -0x0011 */
"socket is closed",
/* AMQP_STATUS_SOCKET_INUSE -0x0012 */
"socket already open",
/* AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD -0x00013 */
"unsupported sasl method requested",
/* AMQP_STATUS_UNSUPPORTED -0x0014 */
"parameter value is unsupported"};
static const char *tcp_error_strings[] = {
/* AMQP_STATUS_TCP_ERROR -0x0100 */
"a socket error occurred",
/* AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR -0x0101 */
"socket library initialization failed"};
static const char *ssl_error_strings[] = {
/* AMQP_STATUS_SSL_ERROR -0x0200 */
"a SSL error occurred",
/* AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED -0x0201 */
"SSL hostname verification failed",
/* AMQP_STATUS_SSL_PEER_VERIFY_FAILED -0x0202 */
"SSL peer cert verification failed",
/* AMQP_STATUS_SSL_CONNECTION_FAILED -0x0203 */
"SSL handshake failed",
/* AMQP_STATUS_SSL_SET_ENGINE_FAILED -0x0204 */
"SSL setting engine failed",
/* AMQP_STATUS_SSL_UNIMPLEMENTED -0x0204 */
"SSL API is not implemented"};
static const char *unknown_error_string = "(unknown error)";
const char *amqp_error_string2(int code) {
const char *error_string;
size_t category = (((-code) & ERROR_CATEGORY_MASK) >> 8);
size_t error = (-code) & ERROR_MASK;
switch (category) {
case EC_base:
if (error < (sizeof(base_error_strings) / sizeof(char *))) {
error_string = base_error_strings[error];
} else {
error_string = unknown_error_string;
}
break;
case EC_tcp:
if (error < (sizeof(tcp_error_strings) / sizeof(char *))) {
error_string = tcp_error_strings[error];
} else {
error_string = unknown_error_string;
}
break;
case EC_ssl:
if (error < (sizeof(ssl_error_strings) / sizeof(char *))) {
error_string = ssl_error_strings[error];
} else {
error_string = unknown_error_string;
}
break;
default:
error_string = unknown_error_string;
break;
}
return error_string;
}
char *amqp_error_string(int code) {
/* Previously sometimes clients had to flip the sign on a return value from a
* function to get the correct error code. Now, all error codes are negative.
* To keep people's legacy code running correctly, we map all error codes to
* negative values.
*
* This is only done with this deprecated function.
*/
if (code > 0) {
code = -code;
}
return strdup(amqp_error_string2(code));
}
void amqp_abort(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
abort();
}
const amqp_bytes_t amqp_empty_bytes = {0, NULL};
const amqp_table_t amqp_empty_table = {0, NULL};
const amqp_array_t amqp_empty_array = {0, NULL};
int amqp_basic_publish(amqp_connection_state_t state, amqp_channel_t channel,
amqp_bytes_t exchange, amqp_bytes_t routing_key,
amqp_boolean_t mandatory, amqp_boolean_t immediate,
amqp_basic_properties_t const *properties,
amqp_bytes_t body) {
amqp_frame_t f;
size_t body_offset;
size_t usable_body_payload_size =
state->frame_max - (HEADER_SIZE + FOOTER_SIZE);
int res;
int flagz;
amqp_basic_publish_t m;
amqp_basic_properties_t default_properties;
m.exchange = exchange;
m.routing_key = routing_key;
m.mandatory = mandatory;
m.immediate = immediate;
m.ticket = 0;
/* TODO(alanxz): this heartbeat check is happening in the wrong place, it
* should really be done in amqp_try_send/writev */
res = amqp_time_has_past(state->next_recv_heartbeat);
if (AMQP_STATUS_TIMER_FAILURE == res) {
return res;
} else if (AMQP_STATUS_TIMEOUT == res) {
res = amqp_try_recv(state);
if (AMQP_STATUS_TIMEOUT == res) {
return AMQP_STATUS_HEARTBEAT_TIMEOUT;
} else if (AMQP_STATUS_OK != res) {
return res;
}
}
res = amqp_send_method_inner(state, channel, AMQP_BASIC_PUBLISH_METHOD, &m,
AMQP_SF_MORE, amqp_time_infinite());
if (res < 0) {
return res;
}
if (properties == NULL) {
memset(&default_properties, 0, sizeof(default_properties));
properties = &default_properties;
}
f.frame_type = AMQP_FRAME_HEADER;
f.channel = channel;
f.payload.properties.class_id = AMQP_BASIC_CLASS;
f.payload.properties.body_size = body.len;
f.payload.properties.decoded = (void *)properties;
if (body.len > 0) {
flagz = AMQP_SF_MORE;
} else {
flagz = AMQP_SF_NONE;
}
res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite());
if (res < 0) {
return res;
}
body_offset = 0;
while (body_offset < body.len) {
size_t remaining = body.len - body_offset;
if (remaining == 0) {
break;
}
f.frame_type = AMQP_FRAME_BODY;
f.channel = channel;
f.payload.body_fragment.bytes = amqp_offset(body.bytes, body_offset);
if (remaining >= usable_body_payload_size) {
f.payload.body_fragment.len = usable_body_payload_size;
flagz = AMQP_SF_MORE;
} else {
f.payload.body_fragment.len = remaining;
flagz = AMQP_SF_NONE;
}
body_offset += f.payload.body_fragment.len;
res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite());
if (res < 0) {
return res;
}
}
return AMQP_STATUS_OK;
}
amqp_rpc_reply_t amqp_channel_close(amqp_connection_state_t state,
amqp_channel_t channel, int code) {
char codestr[13];
amqp_method_number_t replies[2] = {AMQP_CHANNEL_CLOSE_OK_METHOD, 0};
amqp_channel_close_t req;
if (code < 0 || code > UINT16_MAX) {
return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
}
req.reply_code = (uint16_t)code;
req.reply_text.bytes = codestr;
req.reply_text.len = sprintf(codestr, "%d", code);
req.class_id = 0;
req.method_id = 0;
return amqp_simple_rpc(state, channel, AMQP_CHANNEL_CLOSE_METHOD, replies,
&req);
}
amqp_rpc_reply_t amqp_connection_close(amqp_connection_state_t state,
int code) {
char codestr[13];
amqp_method_number_t replies[2] = {AMQP_CONNECTION_CLOSE_OK_METHOD, 0};
amqp_channel_close_t req;
if (code < 0 || code > UINT16_MAX) {
return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
}
req.reply_code = (uint16_t)code;
req.reply_text.bytes = codestr;
req.reply_text.len = sprintf(codestr, "%d", code);
req.class_id = 0;
req.method_id = 0;
return amqp_simple_rpc(state, 0, AMQP_CONNECTION_CLOSE_METHOD, replies, &req);
}
int amqp_basic_ack(amqp_connection_state_t state, amqp_channel_t channel,
uint64_t delivery_tag, amqp_boolean_t multiple) {
amqp_basic_ack_t m;
m.delivery_tag = delivery_tag;
m.multiple = multiple;
return amqp_send_method(state, channel, AMQP_BASIC_ACK_METHOD, &m);
}
amqp_rpc_reply_t amqp_basic_get(amqp_connection_state_t state,
amqp_channel_t channel, amqp_bytes_t queue,
amqp_boolean_t no_ack) {
amqp_method_number_t replies[] = {AMQP_BASIC_GET_OK_METHOD,
AMQP_BASIC_GET_EMPTY_METHOD, 0};
amqp_basic_get_t req;
req.ticket = 0;
req.queue = queue;
req.no_ack = no_ack;
state->most_recent_api_result =
amqp_simple_rpc(state, channel, AMQP_BASIC_GET_METHOD, replies, &req);
return state->most_recent_api_result;
}
int amqp_basic_reject(amqp_connection_state_t state, amqp_channel_t channel,
uint64_t delivery_tag, amqp_boolean_t requeue) {
amqp_basic_reject_t req;
req.delivery_tag = delivery_tag;
req.requeue = requeue;
return amqp_send_method(state, channel, AMQP_BASIC_REJECT_METHOD, &req);
}
int amqp_basic_nack(amqp_connection_state_t state, amqp_channel_t channel,
uint64_t delivery_tag, amqp_boolean_t multiple,
amqp_boolean_t requeue) {
amqp_basic_nack_t req;
req.delivery_tag = delivery_tag;
req.multiple = multiple;
req.requeue = requeue;
return amqp_send_method(state, channel, AMQP_BASIC_NACK_METHOD, &req);
}
struct timeval *amqp_get_handshake_timeout(amqp_connection_state_t state) {
return state->handshake_timeout;
}
int amqp_set_handshake_timeout(amqp_connection_state_t state,
const struct timeval *timeout) {
if (timeout) {
if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
return AMQP_STATUS_INVALID_PARAMETER;
}
state->internal_handshake_timeout = *timeout;
state->handshake_timeout = &state->internal_handshake_timeout;
} else {
state->handshake_timeout = NULL;
}
return AMQP_STATUS_OK;
}
struct timeval *amqp_get_rpc_timeout(amqp_connection_state_t state) {
return state->rpc_timeout;
}
int amqp_set_rpc_timeout(amqp_connection_state_t state,
const struct timeval *timeout) {
if (timeout) {
if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
return AMQP_STATUS_INVALID_PARAMETER;
}
state->rpc_timeout = &state->internal_rpc_timeout;
*state->rpc_timeout = *timeout;
} else {
state->rpc_timeout = NULL;
}
return AMQP_STATUS_OK;
}
amqp_rpc_reply_t amqp_publisher_confirm_wait(amqp_connection_state_t state,
const struct timeval *timeout,
amqp_publisher_confirm_t *result) {
int res;
amqp_frame_t frame;
amqp_rpc_reply_t ret;
memset(&ret, 0x0, sizeof(ret));
memset(result, 0x0, sizeof(amqp_publisher_confirm_t));
res = amqp_simple_wait_frame_noblock(state, &frame, timeout);
if (AMQP_STATUS_OK != res) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = res;
return ret;
} else if (AMQP_FRAME_METHOD != frame.frame_type ||
(AMQP_BASIC_ACK_METHOD != frame.payload.method.id &&
AMQP_BASIC_NACK_METHOD != frame.payload.method.id &&
AMQP_BASIC_REJECT_METHOD != frame.payload.method.id)) {
amqp_put_back_frame(state, &frame);
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
return ret;
}
switch (frame.payload.method.id) {
case AMQP_BASIC_ACK_METHOD:
memcpy(&(result->payload.ack), frame.payload.method.decoded,
sizeof(amqp_basic_ack_t));
break;
case AMQP_BASIC_NACK_METHOD:
memcpy(&(result->payload.nack), frame.payload.method.decoded,
sizeof(amqp_basic_nack_t));
break;
case AMQP_BASIC_REJECT_METHOD:
memcpy(&(result->payload.reject), frame.payload.method.decoded,
sizeof(amqp_basic_reject_t));
break;
default:
amqp_put_back_frame(state, &frame);
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_UNSUPPORTED;
return ret;
}
result->method = frame.payload.method.id;
result->channel = frame.channel;
ret.reply_type = AMQP_RESPONSE_NORMAL;
return ret;
}

View File

@@ -0,0 +1,571 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "amqp_private.h"
#include "amqp_time.h"
#include "rabbitmq-c/tcp_socket.h"
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef AMQP_INITIAL_FRAME_POOL_PAGE_SIZE
#define AMQP_INITIAL_FRAME_POOL_PAGE_SIZE 65536
#endif
#ifndef AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE
#define AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE 131072
#endif
#ifndef AMQP_DEFAULT_LOGIN_TIMEOUT_SEC
#define AMQP_DEFAULT_LOGIN_TIMEOUT_SEC 12
#endif
#define ENFORCE_STATE(statevec, statenum) \
{ \
amqp_connection_state_t _check_state = (statevec); \
amqp_connection_state_enum _wanted_state = (statenum); \
if (_check_state->state != _wanted_state) \
amqp_abort( \
"Programming error: invalid AMQP connection state: expected %d, " \
"got %d", \
_wanted_state, _check_state->state); \
}
amqp_connection_state_t amqp_new_connection(void) {
int res;
amqp_connection_state_t state = (amqp_connection_state_t)calloc(
1, sizeof(struct amqp_connection_state_t_));
if (state == NULL) {
return NULL;
}
res = amqp_tune_connection(state, 0, AMQP_INITIAL_FRAME_POOL_PAGE_SIZE, 0);
if (0 != res) {
goto out_nomem;
}
state->inbound_buffer.bytes = state->header_buffer;
state->inbound_buffer.len = sizeof(state->header_buffer);
state->state = CONNECTION_STATE_INITIAL;
/* the server protocol version response is 8 bytes, which conveniently
is also the minimum frame size */
state->target_size = 8;
state->sock_inbound_buffer.len = AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE;
state->sock_inbound_buffer.bytes =
malloc(AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE);
if (state->sock_inbound_buffer.bytes == NULL) {
goto out_nomem;
}
init_amqp_pool(&state->properties_pool, 512);
/* Use address of the internal_handshake_timeout object by default. */
state->internal_handshake_timeout.tv_sec = AMQP_DEFAULT_LOGIN_TIMEOUT_SEC;
state->internal_handshake_timeout.tv_usec = 0;
state->handshake_timeout = &state->internal_handshake_timeout;
return state;
out_nomem:
free(state->sock_inbound_buffer.bytes);
free(state);
return NULL;
}
int amqp_get_sockfd(amqp_connection_state_t state) {
return state->socket ? amqp_socket_get_sockfd(state->socket) : -1;
}
void amqp_set_sockfd(amqp_connection_state_t state, int sockfd) {
amqp_socket_t *socket = amqp_tcp_socket_new(state);
if (!socket) {
amqp_abort("%s", strerror(errno));
}
amqp_tcp_socket_set_sockfd(socket, sockfd);
}
void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket) {
amqp_socket_delete(state->socket);
state->socket = socket;
}
amqp_socket_t *amqp_get_socket(amqp_connection_state_t state) {
return state->socket;
}
int amqp_tune_connection(amqp_connection_state_t state, int channel_max,
int frame_max, int heartbeat) {
void *newbuf;
int res;
ENFORCE_STATE(state, CONNECTION_STATE_IDLE);
state->channel_max = channel_max;
state->frame_max = frame_max;
state->heartbeat = heartbeat;
if (0 > state->heartbeat) {
state->heartbeat = 0;
}
res = amqp_time_s_from_now(&state->next_send_heartbeat,
amqp_heartbeat_send(state));
if (AMQP_STATUS_OK != res) {
return res;
}
res = amqp_time_s_from_now(&state->next_recv_heartbeat,
amqp_heartbeat_recv(state));
if (AMQP_STATUS_OK != res) {
return res;
}
state->outbound_buffer.len = frame_max;
newbuf = realloc(state->outbound_buffer.bytes, frame_max);
if (newbuf == NULL) {
return AMQP_STATUS_NO_MEMORY;
}
state->outbound_buffer.bytes = newbuf;
return AMQP_STATUS_OK;
}
int amqp_get_channel_max(amqp_connection_state_t state) {
return state->channel_max;
}
int amqp_get_frame_max(amqp_connection_state_t state) {
return state->frame_max;
}
int amqp_get_heartbeat(amqp_connection_state_t state) {
return state->heartbeat;
}
int amqp_destroy_connection(amqp_connection_state_t state) {
int status = AMQP_STATUS_OK;
if (state) {
int i;
for (i = 0; i < POOL_TABLE_SIZE; ++i) {
amqp_pool_table_entry_t *entry = state->pool_table[i];
while (NULL != entry) {
amqp_pool_table_entry_t *todelete = entry;
empty_amqp_pool(&entry->pool);
entry = entry->next;
free(todelete);
}
}
free(state->outbound_buffer.bytes);
free(state->sock_inbound_buffer.bytes);
amqp_socket_delete(state->socket);
empty_amqp_pool(&state->properties_pool);
free(state);
}
return status;
}
static void return_to_idle(amqp_connection_state_t state) {
state->inbound_buffer.len = sizeof(state->header_buffer);
state->inbound_buffer.bytes = state->header_buffer;
state->inbound_offset = 0;
state->target_size = HEADER_SIZE;
state->state = CONNECTION_STATE_IDLE;
}
static size_t consume_data(amqp_connection_state_t state,
amqp_bytes_t *received_data) {
/* how much data is available and will fit? */
size_t bytes_consumed = state->target_size - state->inbound_offset;
if (received_data->len < bytes_consumed) {
bytes_consumed = received_data->len;
}
memcpy(amqp_offset(state->inbound_buffer.bytes, state->inbound_offset),
received_data->bytes, bytes_consumed);
state->inbound_offset += bytes_consumed;
received_data->bytes = amqp_offset(received_data->bytes, bytes_consumed);
received_data->len -= bytes_consumed;
return bytes_consumed;
}
int amqp_handle_input(amqp_connection_state_t state, amqp_bytes_t received_data,
amqp_frame_t *decoded_frame) {
size_t bytes_consumed;
void *raw_frame;
/* Returning frame_type of zero indicates either insufficient input,
or a complete, ignored frame was read. */
decoded_frame->frame_type = 0;
if (received_data.len == 0) {
return AMQP_STATUS_OK;
}
if (state->state == CONNECTION_STATE_IDLE) {
state->state = CONNECTION_STATE_HEADER;
}
bytes_consumed = consume_data(state, &received_data);
/* do we have target_size data yet? if not, return with the
expectation that more will arrive */
if (state->inbound_offset < state->target_size) {
return (int)bytes_consumed;
}
raw_frame = state->inbound_buffer.bytes;
switch (state->state) {
case CONNECTION_STATE_INITIAL:
/* check for a protocol header from the server */
if (memcmp(raw_frame, "AMQP", 4) == 0) {
decoded_frame->frame_type = AMQP_PSEUDOFRAME_PROTOCOL_HEADER;
decoded_frame->channel = 0;
decoded_frame->payload.protocol_header.transport_high =
amqp_d8(amqp_offset(raw_frame, 4));
decoded_frame->payload.protocol_header.transport_low =
amqp_d8(amqp_offset(raw_frame, 5));
decoded_frame->payload.protocol_header.protocol_version_major =
amqp_d8(amqp_offset(raw_frame, 6));
decoded_frame->payload.protocol_header.protocol_version_minor =
amqp_d8(amqp_offset(raw_frame, 7));
return_to_idle(state);
return (int)bytes_consumed;
}
/* it's not a protocol header; fall through to process it as a
regular frame header */
case CONNECTION_STATE_HEADER: {
amqp_channel_t channel;
amqp_pool_t *channel_pool;
uint32_t frame_size;
channel = amqp_d16(amqp_offset(raw_frame, 1));
/* frame length is 3 bytes in */
frame_size = amqp_d32(amqp_offset(raw_frame, 3));
/* To prevent the target_size calculation below from overflowing, check
* that the stated frame_size is smaller than a signed 32-bit. Given
* the library only allows configuring frame_max as an int32_t, and
* frame_size is uint32_t, the math below is safe from overflow. */
if (frame_size >= INT32_MAX) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
frame_size = frame_size + HEADER_SIZE + FOOTER_SIZE;
if ((size_t)state->frame_max < frame_size) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
channel_pool = amqp_get_or_create_channel_pool(state, channel);
if (NULL == channel_pool) {
return AMQP_STATUS_NO_MEMORY;
}
amqp_pool_alloc_bytes(channel_pool, frame_size, &state->inbound_buffer);
if (NULL == state->inbound_buffer.bytes) {
return AMQP_STATUS_NO_MEMORY;
}
memcpy(state->inbound_buffer.bytes, state->header_buffer, HEADER_SIZE);
raw_frame = state->inbound_buffer.bytes;
state->state = CONNECTION_STATE_BODY;
state->target_size = frame_size;
bytes_consumed += consume_data(state, &received_data);
/* do we have target_size data yet? if not, return with the
expectation that more will arrive */
if (state->inbound_offset < state->target_size) {
return (int)bytes_consumed;
}
}
/* fall through to process body */
case CONNECTION_STATE_BODY: {
amqp_bytes_t encoded;
int res;
amqp_pool_t *channel_pool;
/* Check frame end marker (footer) */
if (amqp_d8(amqp_offset(raw_frame, state->target_size - 1)) !=
AMQP_FRAME_END) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
decoded_frame->frame_type = amqp_d8(amqp_offset(raw_frame, 0));
decoded_frame->channel = amqp_d16(amqp_offset(raw_frame, 1));
channel_pool =
amqp_get_or_create_channel_pool(state, decoded_frame->channel);
if (NULL == channel_pool) {
return AMQP_STATUS_NO_MEMORY;
}
switch (decoded_frame->frame_type) {
case AMQP_FRAME_METHOD:
decoded_frame->payload.method.id =
amqp_d32(amqp_offset(raw_frame, HEADER_SIZE));
encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 4);
encoded.len = state->target_size - HEADER_SIZE - 4 - FOOTER_SIZE;
res = amqp_decode_method(decoded_frame->payload.method.id,
channel_pool, encoded,
&decoded_frame->payload.method.decoded);
if (res < 0) {
return res;
}
break;
case AMQP_FRAME_HEADER:
decoded_frame->payload.properties.class_id =
amqp_d16(amqp_offset(raw_frame, HEADER_SIZE));
/* unused 2-byte weight field goes here */
decoded_frame->payload.properties.body_size =
amqp_d64(amqp_offset(raw_frame, HEADER_SIZE + 4));
encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 12);
encoded.len = state->target_size - HEADER_SIZE - 12 - FOOTER_SIZE;
decoded_frame->payload.properties.raw = encoded;
res = amqp_decode_properties(
decoded_frame->payload.properties.class_id, channel_pool, encoded,
&decoded_frame->payload.properties.decoded);
if (res < 0) {
return res;
}
break;
case AMQP_FRAME_BODY:
decoded_frame->payload.body_fragment.len =
state->target_size - HEADER_SIZE - FOOTER_SIZE;
decoded_frame->payload.body_fragment.bytes =
amqp_offset(raw_frame, HEADER_SIZE);
break;
case AMQP_FRAME_HEARTBEAT:
break;
default:
/* Ignore the frame */
decoded_frame->frame_type = 0;
break;
}
return_to_idle(state);
return (int)bytes_consumed;
}
default:
amqp_abort("Internal error: invalid amqp_connection_state_t->state %d",
state->state);
}
}
amqp_boolean_t amqp_release_buffers_ok(amqp_connection_state_t state) {
return (state->state == CONNECTION_STATE_IDLE);
}
void amqp_release_buffers(amqp_connection_state_t state) {
int i;
ENFORCE_STATE(state, CONNECTION_STATE_IDLE);
for (i = 0; i < POOL_TABLE_SIZE; ++i) {
amqp_pool_table_entry_t *entry = state->pool_table[i];
for (; NULL != entry; entry = entry->next) {
amqp_maybe_release_buffers_on_channel(state, entry->channel);
}
}
}
void amqp_maybe_release_buffers(amqp_connection_state_t state) {
if (amqp_release_buffers_ok(state)) {
amqp_release_buffers(state);
}
}
void amqp_maybe_release_buffers_on_channel(amqp_connection_state_t state,
amqp_channel_t channel) {
amqp_link_t *queued_link;
amqp_pool_t *pool;
if (CONNECTION_STATE_IDLE != state->state) {
return;
}
queued_link = state->first_queued_frame;
while (NULL != queued_link) {
amqp_frame_t *frame = queued_link->data;
if (channel == frame->channel) {
return;
}
queued_link = queued_link->next;
}
pool = amqp_get_channel_pool(state, channel);
if (pool != NULL) {
recycle_amqp_pool(pool);
}
}
static int amqp_frame_to_bytes(const amqp_frame_t *frame, amqp_bytes_t buffer,
amqp_bytes_t *encoded) {
void *out_frame = buffer.bytes;
size_t out_frame_len;
int res;
amqp_e8(frame->frame_type, amqp_offset(out_frame, 0));
amqp_e16(frame->channel, amqp_offset(out_frame, 1));
switch (frame->frame_type) {
case AMQP_FRAME_BODY: {
const amqp_bytes_t *body = &frame->payload.body_fragment;
memcpy(amqp_offset(out_frame, HEADER_SIZE), body->bytes, body->len);
out_frame_len = body->len;
break;
}
case AMQP_FRAME_METHOD: {
amqp_bytes_t method_encoded;
amqp_e32(frame->payload.method.id, amqp_offset(out_frame, HEADER_SIZE));
method_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 4);
method_encoded.len = buffer.len - HEADER_SIZE - 4 - FOOTER_SIZE;
res = amqp_encode_method(frame->payload.method.id,
frame->payload.method.decoded, method_encoded);
if (res < 0) {
return res;
}
out_frame_len = res + 4;
break;
}
case AMQP_FRAME_HEADER: {
amqp_bytes_t properties_encoded;
amqp_e16(frame->payload.properties.class_id,
amqp_offset(out_frame, HEADER_SIZE));
amqp_e16(0, amqp_offset(out_frame, HEADER_SIZE + 2)); /* "weight" */
amqp_e64(frame->payload.properties.body_size,
amqp_offset(out_frame, HEADER_SIZE + 4));
properties_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 12);
properties_encoded.len = buffer.len - HEADER_SIZE - 12 - FOOTER_SIZE;
res = amqp_encode_properties(frame->payload.properties.class_id,
frame->payload.properties.decoded,
properties_encoded);
if (res < 0) {
return res;
}
out_frame_len = res + 12;
break;
}
case AMQP_FRAME_HEARTBEAT:
out_frame_len = 0;
break;
default:
return AMQP_STATUS_INVALID_PARAMETER;
}
amqp_e32((uint32_t)out_frame_len, amqp_offset(out_frame, 3));
amqp_e8(AMQP_FRAME_END, amqp_offset(out_frame, HEADER_SIZE + out_frame_len));
encoded->bytes = out_frame;
encoded->len = out_frame_len + HEADER_SIZE + FOOTER_SIZE;
return AMQP_STATUS_OK;
}
int amqp_send_frame(amqp_connection_state_t state, const amqp_frame_t *frame) {
return amqp_send_frame_inner(state, frame, AMQP_SF_NONE,
amqp_time_infinite());
}
int amqp_send_frame_inner(amqp_connection_state_t state,
const amqp_frame_t *frame, int flags,
amqp_time_t deadline) {
int res;
ssize_t sent;
amqp_bytes_t encoded;
amqp_time_t next_timeout;
/* TODO: if the AMQP_SF_MORE socket optimization can be shown to work
* correctly, then this could be un-done so that body-frames are sent as 3
* send calls, getting rid of the copy of the body content, some testing
* would need to be done to see if this would actually a win for performance.
* */
res = amqp_frame_to_bytes(frame, state->outbound_buffer, &encoded);
if (AMQP_STATUS_OK != res) {
return res;
}
start_send:
next_timeout = amqp_time_first(deadline, state->next_recv_heartbeat);
sent = amqp_try_send(state, encoded.bytes, encoded.len, next_timeout, flags);
if (0 > sent) {
return (int)sent;
}
/* A partial send has occurred, because of a heartbeat timeout (so try recv
* something) or common timeout (so return AMQP_STATUS_TIMEOUT) */
if ((ssize_t)encoded.len != sent) {
if (amqp_time_equal(next_timeout, deadline)) {
/* timeout of method was received, so return from method*/
return AMQP_STATUS_TIMEOUT;
}
res = amqp_try_recv(state);
if (AMQP_STATUS_TIMEOUT == res) {
return AMQP_STATUS_HEARTBEAT_TIMEOUT;
} else if (AMQP_STATUS_OK != res) {
return res;
}
encoded.bytes = (uint8_t *)encoded.bytes + sent;
encoded.len -= sent;
goto start_send;
}
res = amqp_time_s_from_now(&state->next_send_heartbeat,
amqp_heartbeat_send(state));
return res;
}
amqp_table_t *amqp_get_server_properties(amqp_connection_state_t state) {
return &state->server_properties;
}
amqp_table_t *amqp_get_client_properties(amqp_connection_state_t state) {
return &state->client_properties;
}

282
librabbitmq/amqp_consumer.c Normal file
View File

@@ -0,0 +1,282 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "amqp_private.h"
#include "amqp_socket.h"
#include "rabbitmq-c/amqp.h"
#include <stdlib.h>
#include <string.h>
static int amqp_basic_properties_clone(amqp_basic_properties_t *original,
amqp_basic_properties_t *clone,
amqp_pool_t *pool) {
memset(clone, 0, sizeof(*clone));
clone->_flags = original->_flags;
#define CLONE_BYTES_POOL(original, clone, pool) \
if (0 == original.len) { \
clone = amqp_empty_bytes; \
} else { \
amqp_pool_alloc_bytes(pool, original.len, &clone); \
if (NULL == clone.bytes) { \
return AMQP_STATUS_NO_MEMORY; \
} \
memcpy(clone.bytes, original.bytes, clone.len); \
}
if (clone->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
CLONE_BYTES_POOL(original->content_type, clone->content_type, pool)
}
if (clone->_flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) {
CLONE_BYTES_POOL(original->content_encoding, clone->content_encoding, pool)
}
if (clone->_flags & AMQP_BASIC_HEADERS_FLAG) {
int res = amqp_table_clone(&original->headers, &clone->headers, pool);
if (AMQP_STATUS_OK != res) {
return res;
}
}
if (clone->_flags & AMQP_BASIC_DELIVERY_MODE_FLAG) {
clone->delivery_mode = original->delivery_mode;
}
if (clone->_flags & AMQP_BASIC_PRIORITY_FLAG) {
clone->priority = original->priority;
}
if (clone->_flags & AMQP_BASIC_CORRELATION_ID_FLAG) {
CLONE_BYTES_POOL(original->correlation_id, clone->correlation_id, pool)
}
if (clone->_flags & AMQP_BASIC_REPLY_TO_FLAG) {
CLONE_BYTES_POOL(original->reply_to, clone->reply_to, pool)
}
if (clone->_flags & AMQP_BASIC_EXPIRATION_FLAG) {
CLONE_BYTES_POOL(original->expiration, clone->expiration, pool)
}
if (clone->_flags & AMQP_BASIC_MESSAGE_ID_FLAG) {
CLONE_BYTES_POOL(original->message_id, clone->message_id, pool)
}
if (clone->_flags & AMQP_BASIC_TIMESTAMP_FLAG) {
clone->timestamp = original->timestamp;
}
if (clone->_flags & AMQP_BASIC_TYPE_FLAG) {
CLONE_BYTES_POOL(original->type, clone->type, pool)
}
if (clone->_flags & AMQP_BASIC_USER_ID_FLAG) {
CLONE_BYTES_POOL(original->user_id, clone->user_id, pool)
}
if (clone->_flags & AMQP_BASIC_APP_ID_FLAG) {
CLONE_BYTES_POOL(original->app_id, clone->app_id, pool)
}
if (clone->_flags & AMQP_BASIC_CLUSTER_ID_FLAG) {
CLONE_BYTES_POOL(original->cluster_id, clone->cluster_id, pool)
}
return AMQP_STATUS_OK;
#undef CLONE_BYTES_POOL
}
void amqp_destroy_message(amqp_message_t *message) {
empty_amqp_pool(&message->pool);
amqp_bytes_free(message->body);
}
void amqp_destroy_envelope(amqp_envelope_t *envelope) {
amqp_destroy_message(&envelope->message);
amqp_bytes_free(envelope->routing_key);
amqp_bytes_free(envelope->exchange);
amqp_bytes_free(envelope->consumer_tag);
}
static int amqp_bytes_malloc_dup_failed(amqp_bytes_t bytes) {
if (bytes.len != 0 && bytes.bytes == NULL) {
return 1;
}
return 0;
}
amqp_rpc_reply_t amqp_consume_message(amqp_connection_state_t state,
amqp_envelope_t *envelope,
const struct timeval *timeout,
AMQP_UNUSED int flags) {
int res;
amqp_frame_t frame;
amqp_basic_deliver_t *delivery_method;
amqp_rpc_reply_t ret;
memset(&ret, 0, sizeof(ret));
memset(envelope, 0, sizeof(*envelope));
res = amqp_simple_wait_frame_noblock(state, &frame, timeout);
if (AMQP_STATUS_OK != res) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = res;
goto error_out1;
}
if (AMQP_FRAME_METHOD != frame.frame_type ||
AMQP_BASIC_DELIVER_METHOD != frame.payload.method.id) {
amqp_put_back_frame(state, &frame);
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
goto error_out1;
}
delivery_method = frame.payload.method.decoded;
envelope->channel = frame.channel;
envelope->consumer_tag = amqp_bytes_malloc_dup(delivery_method->consumer_tag);
envelope->delivery_tag = delivery_method->delivery_tag;
envelope->redelivered = delivery_method->redelivered;
envelope->exchange = amqp_bytes_malloc_dup(delivery_method->exchange);
envelope->routing_key = amqp_bytes_malloc_dup(delivery_method->routing_key);
if (amqp_bytes_malloc_dup_failed(envelope->consumer_tag) ||
amqp_bytes_malloc_dup_failed(envelope->exchange) ||
amqp_bytes_malloc_dup_failed(envelope->routing_key)) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_NO_MEMORY;
goto error_out2;
}
ret = amqp_read_message(state, envelope->channel, &envelope->message, 0);
if (AMQP_RESPONSE_NORMAL != ret.reply_type) {
goto error_out2;
}
ret.reply_type = AMQP_RESPONSE_NORMAL;
return ret;
error_out2:
amqp_bytes_free(envelope->routing_key);
amqp_bytes_free(envelope->exchange);
amqp_bytes_free(envelope->consumer_tag);
error_out1:
return ret;
}
amqp_rpc_reply_t amqp_read_message(amqp_connection_state_t state,
amqp_channel_t channel,
amqp_message_t *message,
AMQP_UNUSED int flags) {
amqp_frame_t frame;
amqp_rpc_reply_t ret;
size_t body_read;
char *body_read_ptr;
int res;
memset(&ret, 0, sizeof(ret));
memset(message, 0, sizeof(*message));
res = amqp_simple_wait_frame_on_channel(state, channel, &frame);
if (AMQP_STATUS_OK != res) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = res;
goto error_out1;
}
if (AMQP_FRAME_HEADER != frame.frame_type) {
if (AMQP_FRAME_METHOD == frame.frame_type &&
(AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id ||
AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) {
ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
ret.reply = frame.payload.method;
} else {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
amqp_put_back_frame(state, &frame);
}
goto error_out1;
}
init_amqp_pool(&message->pool, 4096);
res = amqp_basic_properties_clone(frame.payload.properties.decoded,
&message->properties, &message->pool);
if (AMQP_STATUS_OK != res) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = res;
goto error_out3;
}
if (0 == frame.payload.properties.body_size) {
message->body = amqp_empty_bytes;
} else {
if (SIZE_MAX < frame.payload.properties.body_size) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_NO_MEMORY;
goto error_out1;
}
message->body =
amqp_bytes_malloc((size_t)frame.payload.properties.body_size);
if (NULL == message->body.bytes) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_NO_MEMORY;
goto error_out1;
}
}
body_read = 0;
body_read_ptr = message->body.bytes;
while (body_read < message->body.len) {
res = amqp_simple_wait_frame_on_channel(state, channel, &frame);
if (AMQP_STATUS_OK != res) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = res;
goto error_out2;
}
if (AMQP_FRAME_BODY != frame.frame_type) {
if (AMQP_FRAME_METHOD == frame.frame_type &&
(AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id ||
AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) {
ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
ret.reply = frame.payload.method;
} else {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_BAD_AMQP_DATA;
}
goto error_out2;
}
if (body_read + frame.payload.body_fragment.len > message->body.len) {
ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
ret.library_error = AMQP_STATUS_BAD_AMQP_DATA;
goto error_out2;
}
memcpy(body_read_ptr, frame.payload.body_fragment.bytes,
frame.payload.body_fragment.len);
body_read += frame.payload.body_fragment.len;
body_read_ptr += frame.payload.body_fragment.len;
}
ret.reply_type = AMQP_RESPONSE_NORMAL;
return ret;
error_out2:
amqp_bytes_free(message->body);
error_out3:
empty_amqp_pool(&message->pool);
error_out1:
return ret;
}

2919
librabbitmq/amqp_framing.c Normal file

File diff suppressed because it is too large Load Diff

210
librabbitmq/amqp_mem.c Normal file
View File

@@ -0,0 +1,210 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amqp_private.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
char const *amqp_version(void) { return AMQP_VERSION_STRING; }
uint32_t amqp_version_number(void) { return AMQP_VERSION; }
void init_amqp_pool(amqp_pool_t *pool, size_t pagesize) {
pool->pagesize = pagesize ? pagesize : 4096;
pool->pages.num_blocks = 0;
pool->pages.blocklist = NULL;
pool->large_blocks.num_blocks = 0;
pool->large_blocks.blocklist = NULL;
pool->next_page = 0;
pool->alloc_block = NULL;
pool->alloc_used = 0;
}
static void empty_blocklist(amqp_pool_blocklist_t *x) {
int i;
if (x->blocklist != NULL) {
for (i = 0; i < x->num_blocks; i++) {
free(x->blocklist[i]);
}
free(x->blocklist);
}
x->num_blocks = 0;
x->blocklist = NULL;
}
void recycle_amqp_pool(amqp_pool_t *pool) {
empty_blocklist(&pool->large_blocks);
pool->next_page = 0;
pool->alloc_block = NULL;
pool->alloc_used = 0;
}
void empty_amqp_pool(amqp_pool_t *pool) {
recycle_amqp_pool(pool);
empty_blocklist(&pool->pages);
}
/* Returns 1 on success, 0 on failure */
static int record_pool_block(amqp_pool_blocklist_t *x, void *block) {
size_t blocklistlength = sizeof(void *) * (x->num_blocks + 1);
if (x->blocklist == NULL) {
x->blocklist = malloc(blocklistlength);
if (x->blocklist == NULL) {
return 0;
}
} else {
void *newbl = realloc(x->blocklist, blocklistlength);
if (newbl == NULL) {
return 0;
}
x->blocklist = newbl;
}
x->blocklist[x->num_blocks] = block;
x->num_blocks++;
return 1;
}
void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) {
if (amount == 0) {
return NULL;
}
amount = (amount + 7) & (~7); /* round up to nearest 8-byte boundary */
if (amount > pool->pagesize) {
void *result = calloc(1, amount);
if (result == NULL) {
return NULL;
}
if (!record_pool_block(&pool->large_blocks, result)) {
free(result);
return NULL;
}
return result;
}
if (pool->alloc_block != NULL) {
assert(pool->alloc_used <= pool->pagesize);
if (pool->alloc_used + amount <= pool->pagesize) {
void *result = pool->alloc_block + pool->alloc_used;
pool->alloc_used += amount;
return result;
}
}
if (pool->next_page >= pool->pages.num_blocks) {
pool->alloc_block = calloc(1, pool->pagesize);
if (pool->alloc_block == NULL) {
return NULL;
}
if (!record_pool_block(&pool->pages, pool->alloc_block)) {
return NULL;
}
pool->next_page = pool->pages.num_blocks;
} else {
pool->alloc_block = pool->pages.blocklist[pool->next_page];
pool->next_page++;
}
pool->alloc_used = amount;
return pool->alloc_block;
}
void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount,
amqp_bytes_t *output) {
output->len = amount;
output->bytes = amqp_pool_alloc(pool, amount);
}
amqp_bytes_t amqp_cstring_bytes(char const *cstr) {
amqp_bytes_t result;
result.len = strlen(cstr);
result.bytes = (void *)cstr;
return result;
}
amqp_bytes_t amqp_bytes_malloc_dup(amqp_bytes_t src) {
amqp_bytes_t result;
result.len = src.len;
result.bytes = malloc(src.len);
if (result.bytes != NULL) {
memcpy(result.bytes, src.bytes, src.len);
}
return result;
}
amqp_bytes_t amqp_bytes_malloc(size_t amount) {
amqp_bytes_t result;
result.len = amount;
result.bytes = malloc(amount); /* will return NULL if it fails */
return result;
}
void amqp_bytes_free(amqp_bytes_t bytes) { free(bytes.bytes); }
amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t state,
amqp_channel_t channel) {
amqp_pool_table_entry_t *entry;
size_t index = channel % POOL_TABLE_SIZE;
entry = state->pool_table[index];
for (; NULL != entry; entry = entry->next) {
if (channel == entry->channel) {
return &entry->pool;
}
}
entry = malloc(sizeof(amqp_pool_table_entry_t));
if (NULL == entry) {
return NULL;
}
entry->channel = channel;
entry->next = state->pool_table[index];
state->pool_table[index] = entry;
init_amqp_pool(&entry->pool, state->frame_max);
return &entry->pool;
}
amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state,
amqp_channel_t channel) {
amqp_pool_table_entry_t *entry;
size_t index = channel % POOL_TABLE_SIZE;
entry = state->pool_table[index];
for (; NULL != entry; entry = entry->next) {
if (channel == entry->channel) {
return &entry->pool;
}
}
return NULL;
}
int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l) {
if (r.len == l.len &&
(r.bytes == l.bytes || 0 == memcmp(r.bytes, l.bytes, r.len))) {
return 1;
}
return 0;
}

669
librabbitmq/amqp_openssl.c Normal file
View File

@@ -0,0 +1,669 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
// Use OpenSSL v1.1.1 API.
#define OPENSSL_API_COMPAT 10101
#include "amqp_openssl_bio.h"
#include "amqp_private.h"
#include "amqp_socket.h"
#include "amqp_time.h"
#include "rabbitmq-c/ssl_socket.h"
#include "threads.h"
#include <ctype.h>
#include <limits.h>
#include <openssl/bio.h>
#include <openssl/conf.h>
#ifdef ENABLE_SSL_ENGINE_API
#include <openssl/engine.h>
#endif
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <stdlib.h>
#include <string.h>
static int initialize_ssl_and_increment_connections(void);
static int decrement_ssl_connections(void);
static pthread_mutex_t openssl_init_mutex = PTHREAD_MUTEX_INITIALIZER;
static amqp_boolean_t openssl_bio_initialized = 0;
static int openssl_connections = 0;
#ifdef ENABLE_SSL_ENGINE_API
static ENGINE *openssl_engine = NULL;
#endif
#define CHECK_SUCCESS(condition) \
do { \
int check_success_ret = (condition); \
if (check_success_ret) { \
amqp_abort("Check %s failed <%d>: %s", #condition, check_success_ret, \
strerror(check_success_ret)); \
} \
} while (0)
struct amqp_ssl_socket_t {
const struct amqp_socket_class_t *klass;
SSL_CTX *ctx;
int sockfd;
SSL *ssl;
amqp_boolean_t verify_peer;
amqp_boolean_t verify_hostname;
int internal_error;
};
static ssize_t amqp_ssl_socket_send(void *base, const void *buf, size_t len,
AMQP_UNUSED int flags) {
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
int res;
if (-1 == self->sockfd) {
return AMQP_STATUS_SOCKET_CLOSED;
}
/* SSL_write takes an int for length of buffer, protect against len being
* larger than larger than what SSL_write can take */
if (len > INT_MAX) {
return AMQP_STATUS_INVALID_PARAMETER;
}
ERR_clear_error();
self->internal_error = 0;
/* This will only return on error, or once the whole buffer has been
* written to the SSL stream. See SSL_MODE_ENABLE_PARTIAL_WRITE */
res = SSL_write(self->ssl, buf, (int)len);
if (0 >= res) {
self->internal_error = SSL_get_error(self->ssl, res);
/* TODO: Close connection if it isn't already? */
/* TODO: Possibly be more intelligent in reporting WHAT went wrong */
switch (self->internal_error) {
case SSL_ERROR_WANT_READ:
res = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
break;
case SSL_ERROR_WANT_WRITE:
res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
break;
case SSL_ERROR_ZERO_RETURN:
res = AMQP_STATUS_CONNECTION_CLOSED;
break;
default:
res = AMQP_STATUS_SSL_ERROR;
break;
}
} else {
self->internal_error = 0;
}
return (ssize_t)res;
}
static ssize_t amqp_ssl_socket_recv(void *base, void *buf, size_t len,
AMQP_UNUSED int flags) {
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
int received;
if (-1 == self->sockfd) {
return AMQP_STATUS_SOCKET_CLOSED;
}
/* SSL_read takes an int for length of buffer, protect against len being
* larger than larger than what SSL_read can take */
if (len > INT_MAX) {
return AMQP_STATUS_INVALID_PARAMETER;
}
ERR_clear_error();
self->internal_error = 0;
received = SSL_read(self->ssl, buf, (int)len);
if (0 >= received) {
self->internal_error = SSL_get_error(self->ssl, received);
switch (self->internal_error) {
case SSL_ERROR_WANT_READ:
received = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
break;
case SSL_ERROR_WANT_WRITE:
received = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
break;
case SSL_ERROR_ZERO_RETURN:
received = AMQP_STATUS_CONNECTION_CLOSED;
break;
default:
received = AMQP_STATUS_SSL_ERROR;
break;
}
}
return (ssize_t)received;
}
static int amqp_ssl_socket_open(void *base, const char *host, int port,
const struct timeval *timeout) {
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
long result;
int status;
amqp_time_t deadline;
X509 *cert;
BIO *bio;
if (-1 != self->sockfd) {
return AMQP_STATUS_SOCKET_INUSE;
}
ERR_clear_error();
self->ssl = SSL_new(self->ctx);
if (!self->ssl) {
self->internal_error = ERR_peek_error();
status = AMQP_STATUS_SSL_ERROR;
goto exit;
}
status = amqp_time_from_now(&deadline, timeout);
if (AMQP_STATUS_OK != status) {
return status;
}
self->sockfd = amqp_open_socket_inner(host, port, deadline);
if (0 > self->sockfd) {
status = self->sockfd;
self->internal_error = amqp_os_socket_error();
self->sockfd = -1;
goto error_out1;
}
bio = BIO_new(amqp_openssl_bio());
if (!bio) {
status = AMQP_STATUS_NO_MEMORY;
goto error_out2;
}
BIO_set_fd(bio, self->sockfd, BIO_NOCLOSE);
SSL_set_bio(self->ssl, bio, bio);
status = SSL_set_tlsext_host_name(self->ssl, host);
if (!status) {
self->internal_error = SSL_get_error(self->ssl, status);
status = AMQP_STATUS_SSL_ERROR;
goto error_out2;
}
start_connect:
status = SSL_connect(self->ssl);
if (status != 1) {
self->internal_error = SSL_get_error(self->ssl, status);
switch (self->internal_error) {
case SSL_ERROR_WANT_READ:
status = amqp_poll(self->sockfd, AMQP_SF_POLLIN, deadline);
break;
case SSL_ERROR_WANT_WRITE:
status = amqp_poll(self->sockfd, AMQP_SF_POLLOUT, deadline);
break;
default:
status = AMQP_STATUS_SSL_CONNECTION_FAILED;
}
if (AMQP_STATUS_OK == status) {
goto start_connect;
}
goto error_out2;
}
#if OPENSSL_VERSION_NUMBER < 0x30000000L
cert = SSL_get_peer_certificate(self->ssl);
#else
cert = SSL_get1_peer_certificate(self->ssl);
#endif
if (self->verify_peer) {
if (!cert) {
self->internal_error = 0;
status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
goto error_out3;
}
result = SSL_get_verify_result(self->ssl);
if (X509_V_OK != result) {
self->internal_error = result;
status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
goto error_out4;
}
}
if (self->verify_hostname) {
if (!cert) {
self->internal_error = 0;
status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
goto error_out3;
}
if (1 != X509_check_host(cert, host, strlen(host),
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, NULL)) {
self->internal_error = 0;
status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
goto error_out4;
}
}
X509_free(cert);
self->internal_error = 0;
status = AMQP_STATUS_OK;
exit:
return status;
error_out4:
X509_free(cert);
error_out3:
SSL_shutdown(self->ssl);
error_out2:
amqp_os_socket_close(self->sockfd);
self->sockfd = -1;
error_out1:
SSL_free(self->ssl);
self->ssl = NULL;
goto exit;
}
static int amqp_ssl_socket_close(void *base, amqp_socket_close_enum force) {
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
if (-1 == self->sockfd) {
return AMQP_STATUS_SOCKET_CLOSED;
}
if (AMQP_SC_NONE == force) {
/* don't try too hard to shutdown the connection */
SSL_shutdown(self->ssl);
}
SSL_free(self->ssl);
self->ssl = NULL;
if (amqp_os_socket_close(self->sockfd)) {
return AMQP_STATUS_SOCKET_ERROR;
}
self->sockfd = -1;
return AMQP_STATUS_OK;
}
static int amqp_ssl_socket_get_sockfd(void *base) {
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
return self->sockfd;
}
static void amqp_ssl_socket_delete(void *base) {
struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
if (self) {
amqp_ssl_socket_close(self, AMQP_SC_NONE);
SSL_CTX_free(self->ctx);
free(self);
}
decrement_ssl_connections();
}
static const struct amqp_socket_class_t amqp_ssl_socket_class = {
amqp_ssl_socket_send, /* send */
amqp_ssl_socket_recv, /* recv */
amqp_ssl_socket_open, /* open */
amqp_ssl_socket_close, /* close */
amqp_ssl_socket_get_sockfd, /* get_sockfd */
amqp_ssl_socket_delete /* delete */
};
amqp_socket_t *amqp_ssl_socket_new(amqp_connection_state_t state) {
struct amqp_ssl_socket_t *self = calloc(1, sizeof(*self));
int status;
if (!self) {
return NULL;
}
self->sockfd = -1;
self->klass = &amqp_ssl_socket_class;
self->verify_peer = 1;
self->verify_hostname = 1;
status = initialize_ssl_and_increment_connections();
if (status) {
goto error;
}
self->ctx = SSL_CTX_new(TLS_client_method());
if (!self->ctx) {
goto error;
}
status = amqp_ssl_socket_set_ssl_versions((amqp_socket_t *)self, AMQP_TLSv1_2,
AMQP_TLSvLATEST);
if (status != AMQP_STATUS_OK) {
goto error;
}
SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
/* OpenSSL v1.1.1 turns this on by default, which makes the non-blocking
* logic not behave as expected, so turn this back off */
SSL_CTX_clear_mode(self->ctx, SSL_MODE_AUTO_RETRY);
amqp_set_socket(state, (amqp_socket_t *)self);
return (amqp_socket_t *)self;
error:
amqp_ssl_socket_delete((amqp_socket_t *)self);
return NULL;
}
void *amqp_ssl_socket_get_context(amqp_socket_t *base) {
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
return ((struct amqp_ssl_socket_t *)base)->ctx;
}
int amqp_ssl_socket_enable_default_verify_paths(amqp_socket_t *base) {
int status;
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
status = SSL_CTX_set_default_verify_paths(self->ctx);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
return AMQP_STATUS_OK;
}
int amqp_ssl_socket_set_cacert(amqp_socket_t *base, const char *cacert) {
int status;
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
status = SSL_CTX_load_verify_locations(self->ctx, cacert, NULL);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
return AMQP_STATUS_OK;
}
int amqp_ssl_socket_set_key(amqp_socket_t *base, const char *cert,
const char *key) {
int status;
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
status = SSL_CTX_use_PrivateKey_file(self->ctx, key, SSL_FILETYPE_PEM);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
return AMQP_STATUS_OK;
}
int amqp_ssl_socket_set_key_engine(amqp_socket_t *base, const char *cert,
const char *key) {
#ifdef ENABLE_SSL_ENGINE_API
int status;
struct amqp_ssl_socket_t *self;
EVP_PKEY *pkey = NULL;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
pkey = ENGINE_load_private_key(openssl_engine, key, NULL, NULL);
if (pkey == NULL) {
return AMQP_STATUS_SSL_ERROR;
}
status = SSL_CTX_use_PrivateKey(self->ctx, pkey);
EVP_PKEY_free(pkey);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
return AMQP_STATUS_OK;
#else
return AMQP_STATUS_SSL_UNIMPLEMENTED;
#endif
}
static int password_cb(AMQP_UNUSED char *buffer, AMQP_UNUSED int length,
AMQP_UNUSED int rwflag, AMQP_UNUSED void *user_data) {
amqp_abort("rabbitmq-c does not support password protected keys");
}
int amqp_ssl_socket_set_key_buffer(amqp_socket_t *base, const char *cert,
const void *key, size_t n) {
int status = AMQP_STATUS_OK;
BIO *buf = NULL;
RSA *rsa = NULL;
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
if (n > INT_MAX) {
return AMQP_STATUS_INVALID_PARAMETER;
}
self = (struct amqp_ssl_socket_t *)base;
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
buf = BIO_new_mem_buf((void *)key, (int)n);
if (!buf) {
goto error;
}
rsa = PEM_read_bio_RSAPrivateKey(buf, NULL, password_cb, NULL);
if (!rsa) {
goto error;
}
status = SSL_CTX_use_RSAPrivateKey(self->ctx, rsa);
if (1 != status) {
goto error;
}
status = AMQP_STATUS_OK;
exit:
BIO_vfree(buf);
RSA_free(rsa);
return status;
error:
status = AMQP_STATUS_SSL_ERROR;
goto exit;
}
int amqp_ssl_socket_set_cert(amqp_socket_t *base, const char *cert) {
int status;
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
if (1 != status) {
return AMQP_STATUS_SSL_ERROR;
}
return AMQP_STATUS_OK;
}
void amqp_ssl_socket_set_key_passwd(amqp_socket_t *base, const char *passwd) {
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
SSL_CTX_set_default_passwd_cb_userdata(self->ctx, (void *)passwd);
}
void amqp_ssl_socket_set_verify(amqp_socket_t *base, amqp_boolean_t verify) {
amqp_ssl_socket_set_verify_peer(base, verify);
amqp_ssl_socket_set_verify_hostname(base, verify);
}
void amqp_ssl_socket_set_verify_peer(amqp_socket_t *base,
amqp_boolean_t verify) {
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
self->verify_peer = verify;
}
void amqp_ssl_socket_set_verify_hostname(amqp_socket_t *base,
amqp_boolean_t verify) {
struct amqp_ssl_socket_t *self;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
self->verify_hostname = verify;
}
static int get_tls_version(amqp_tls_version_t ver, int *tls_version) {
switch (ver) {
case AMQP_TLSv1_2:
*tls_version = TLS1_2_VERSION;
break;
case AMQP_TLSv1_3:
case AMQP_TLSvLATEST:
*tls_version = TLS1_3_VERSION;
break;
default:
return AMQP_STATUS_UNSUPPORTED;
}
return AMQP_STATUS_OK;
}
int amqp_ssl_socket_set_ssl_versions(amqp_socket_t *base,
amqp_tls_version_t min,
amqp_tls_version_t max) {
struct amqp_ssl_socket_t *self;
int min_ver;
int max_ver;
int status;
if (base->klass != &amqp_ssl_socket_class) {
amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
}
self = (struct amqp_ssl_socket_t *)base;
if (max < min) {
return AMQP_STATUS_INVALID_PARAMETER;
}
status = get_tls_version(min, &min_ver);
if (status != AMQP_STATUS_OK) {
return status;
}
status = get_tls_version(max, &max_ver);
if (status != AMQP_STATUS_OK) {
return status;
}
if (!SSL_CTX_set_min_proto_version(self->ctx, min_ver)) {
return AMQP_STATUS_INVALID_PARAMETER;
}
if (!SSL_CTX_set_max_proto_version(self->ctx, max_ver)) {
return AMQP_STATUS_INVALID_PARAMETER;
}
return AMQP_STATUS_OK;
}
void amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize) {
(void)do_initialize;
return;
}
int amqp_initialize_ssl_library(void) { return AMQP_STATUS_OK; }
int amqp_set_ssl_engine(const char *engine) {
#ifdef ENABLE_SSL_ENGINE_API
int status = AMQP_STATUS_OK;
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
if (openssl_engine != NULL) {
ENGINE_free(openssl_engine);
openssl_engine = NULL;
}
if (engine == NULL) {
goto out;
}
ENGINE_load_builtin_engines();
openssl_engine = ENGINE_by_id(engine);
if (openssl_engine == NULL) {
status = AMQP_STATUS_SSL_SET_ENGINE_FAILED;
goto out;
}
if (ENGINE_set_default(openssl_engine, ENGINE_METHOD_ALL) == 0) {
ENGINE_free(openssl_engine);
openssl_engine = NULL;
status = AMQP_STATUS_SSL_SET_ENGINE_FAILED;
goto out;
}
out:
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
return status;
#else
return AMQP_STATUS_SSL_UNIMPLEMENTED;
#endif
}
static int initialize_ssl_and_increment_connections(void) {
int status;
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
if (!openssl_bio_initialized) {
status = amqp_openssl_bio_init();
if (status) {
goto exit;
}
openssl_bio_initialized = 1;
}
openssl_connections += 1;
status = AMQP_STATUS_OK;
exit:
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
return status;
}
static int decrement_ssl_connections(void) {
CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
if (openssl_connections > 0) {
openssl_connections--;
}
if (openssl_connections == 0) {
amqp_openssl_bio_destroy();
openssl_bio_initialized = 0;
}
CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
return AMQP_STATUS_OK;
}
int amqp_uninitialize_ssl_library(void) { return AMQP_STATUS_OK; }

View File

@@ -0,0 +1,155 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "amqp_openssl_bio.h"
#include "amqp_socket.h"
#include <assert.h>
#include <errno.h>
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <sys/types.h>
#endif
#ifdef MSG_NOSIGNAL
#define AMQP_USE_AMQP_BIO
#endif
static int amqp_ssl_bio_initialized = 0;
#ifdef AMQP_USE_AMQP_BIO
static BIO_METHOD *amqp_bio_method;
static int amqp_openssl_bio_should_retry(int res) {
if (res == -1) {
int err = amqp_os_socket_error();
if (
#ifdef EWOULDBLOCK
err == EWOULDBLOCK ||
#endif
#ifdef WSAEWOULDBLOCK
err == WSAEWOULDBLOCK ||
#endif
#ifdef ENOTCONN
err == ENOTCONN ||
#endif
#ifdef EINTR
err == EINTR ||
#endif
#ifdef EAGAIN
err == EAGAIN ||
#endif
#ifdef EPROTO
err == EPROTO ||
#endif
#ifdef EINPROGRESS
err == EINPROGRESS ||
#endif
#ifdef EALREADY
err == EALREADY ||
#endif
0) {
return 1;
}
}
return 0;
}
static int amqp_openssl_bio_write(BIO *b, const char *in, int inl) {
int flags = 0;
int fd;
int res;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
BIO_get_fd(b, &fd);
res = send(fd, in, inl, flags);
BIO_clear_retry_flags(b);
if (res <= 0 && amqp_openssl_bio_should_retry(res)) {
BIO_set_retry_write(b);
}
return res;
}
static int amqp_openssl_bio_read(BIO *b, char *out, int outl) {
int flags = 0;
int fd;
int res;
#ifdef MSG_NOSIGNAL
flags |= MSG_NOSIGNAL;
#endif
BIO_get_fd(b, &fd);
res = recv(fd, out, outl, flags);
BIO_clear_retry_flags(b);
if (res <= 0 && amqp_openssl_bio_should_retry(res)) {
BIO_set_retry_read(b);
}
return res;
}
#endif /* AMQP_USE_AMQP_BIO */
int amqp_openssl_bio_init(void) {
assert(!amqp_ssl_bio_initialized);
#ifdef AMQP_USE_AMQP_BIO
if (!(amqp_bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "amqp_bio_method"))) {
return AMQP_STATUS_NO_MEMORY;
}
#ifdef OPENSSL_IS_BORINGSSL
BIO_meth_set_create(amqp_bio_method, BIO_s_socket()->create);
BIO_meth_set_destroy(amqp_bio_method, BIO_s_socket()->destroy);
BIO_meth_set_ctrl(amqp_bio_method, BIO_s_socket()->ctrl);
BIO_meth_set_read(amqp_bio_method, BIO_s_socket()->bread);
BIO_meth_set_write(amqp_bio_method, BIO_s_socket()->bwrite);
BIO_meth_set_gets(amqp_bio_method, BIO_s_socket()->bgets);
BIO_meth_set_puts(amqp_bio_method, BIO_s_socket()->bputs);
#else
BIO_meth_set_create(amqp_bio_method, BIO_meth_get_create(BIO_s_socket()));
BIO_meth_set_destroy(amqp_bio_method, BIO_meth_get_destroy(BIO_s_socket()));
BIO_meth_set_ctrl(amqp_bio_method, BIO_meth_get_ctrl(BIO_s_socket()));
BIO_meth_set_callback_ctrl(amqp_bio_method,
BIO_meth_get_callback_ctrl(BIO_s_socket()));
BIO_meth_set_read(amqp_bio_method, BIO_meth_get_read(BIO_s_socket()));
BIO_meth_set_write(amqp_bio_method, BIO_meth_get_write(BIO_s_socket()));
BIO_meth_set_gets(amqp_bio_method, BIO_meth_get_gets(BIO_s_socket()));
BIO_meth_set_puts(amqp_bio_method, BIO_meth_get_puts(BIO_s_socket()));
#endif
BIO_meth_set_write(amqp_bio_method, amqp_openssl_bio_write);
BIO_meth_set_read(amqp_bio_method, amqp_openssl_bio_read);
#endif
amqp_ssl_bio_initialized = 1;
return AMQP_STATUS_OK;
}
void amqp_openssl_bio_destroy(void) {
assert(amqp_ssl_bio_initialized);
#ifdef AMQP_USE_AMQP_BIO
BIO_meth_free(amqp_bio_method);
amqp_bio_method = NULL;
#endif
amqp_ssl_bio_initialized = 0;
}
BIO_METHOD_PTR amqp_openssl_bio(void) {
assert(amqp_ssl_bio_initialized);
#ifdef AMQP_USE_AMQP_BIO
return amqp_bio_method;
#else
return BIO_s_socket();
#endif
}

View File

@@ -0,0 +1,20 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef AMQP_OPENSSL_BIO
#define AMQP_OPENSSL_BIO
// Use OpenSSL v1.1.1 API.
#define OPENSSL_API_COMPAT 10101
#include <openssl/bio.h>
int amqp_openssl_bio_init(void);
void amqp_openssl_bio_destroy(void);
typedef const BIO_METHOD *BIO_METHOD_PTR;
BIO_METHOD_PTR amqp_openssl_bio(void);
#endif /* ifndef AMQP_OPENSSL_BIO */

337
librabbitmq/amqp_private.h Normal file
View File

@@ -0,0 +1,337 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef librabbitmq_amqp_private_h
#define librabbitmq_amqp_private_h
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define AMQ_COPYRIGHT \
"Copyright (c) 2007-2014 VMWare Inc, Tony Garnock-Jones," \
" and Alan Antonuk."
#include "rabbitmq-c/amqp.h"
#include "rabbitmq-c/framing.h"
#include <string.h>
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
#ifndef WINVER
/* WINVER 0x0502 is WinXP SP2+, Windows Server 2003 SP1+
* See:
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383745(v=vs.85).aspx#macros_for_conditional_declarations
*/
#define WINVER 0x0502
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <sys/uio.h>
#endif
/* GCC attributes */
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
#define AMQP_NORETURN __attribute__((__noreturn__))
#define AMQP_UNUSED __attribute__((__unused__))
#elif defined(_MSC_VER)
#define AMQP_NORETURN __declspec(noreturn)
#define AMQP_UNUSED __pragma(warning(suppress : 4100))
#else
#define AMQP_NORETURN
#define AMQP_UNUSED
#endif
#if (defined(_MSC_VER) && (_MSC_VER <= 1800)) || \
(defined(__BORLANDC__) && (__BORLANDC__ <= 0x0564))
#define inline __inline
#endif
char *amqp_os_error_string(int err);
#include "amqp_socket.h"
#include "amqp_time.h"
/*
* Connection states: XXX FIX THIS
*
* - CONNECTION_STATE_INITIAL: The initial state, when we cannot be
* sure if the next thing we will get is the first AMQP frame, or a
* protocol header from the server.
*
* - CONNECTION_STATE_IDLE: The normal state between
* frames. Connections may only be reconfigured, and the
* connection's pools recycled, when in this state. Whenever we're
* in this state, the inbound_buffer's bytes pointer must be NULL;
* any other state, and it must point to a block of memory allocated
* from the frame_pool.
*
* - CONNECTION_STATE_HEADER: Some bytes of an incoming frame have
* been seen, but not a complete frame header's worth.
*
* - CONNECTION_STATE_BODY: A complete frame header has been seen, but
* the frame is not yet complete. When it is completed, it will be
* returned, and the connection will return to IDLE state.
*
*/
typedef enum amqp_connection_state_enum_ {
CONNECTION_STATE_IDLE = 0,
CONNECTION_STATE_INITIAL,
CONNECTION_STATE_HEADER,
CONNECTION_STATE_BODY
} amqp_connection_state_enum;
typedef enum amqp_status_private_enum_ {
/* 0x00xx -> AMQP_STATUS_*/
/* 0x01xx -> AMQP_STATUS_TCP_* */
/* 0x02xx -> AMQP_STATUS_SSL_* */
AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD = -0x1301,
AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE = -0x1302
} amqp_status_private_enum;
/* 7 bytes up front, then payload, then 1 byte footer */
#define HEADER_SIZE 7
#define FOOTER_SIZE 1
#define AMQP_PSEUDOFRAME_PROTOCOL_HEADER 'A'
typedef struct amqp_link_t_ {
struct amqp_link_t_ *next;
void *data;
} amqp_link_t;
#define POOL_TABLE_SIZE 16
typedef struct amqp_pool_table_entry_t_ {
struct amqp_pool_table_entry_t_ *next;
amqp_pool_t pool;
amqp_channel_t channel;
} amqp_pool_table_entry_t;
struct amqp_connection_state_t_ {
amqp_pool_table_entry_t *pool_table[POOL_TABLE_SIZE];
amqp_connection_state_enum state;
int channel_max;
int frame_max;
/* Heartbeat interval in seconds. If this is <= 0, then heartbeats are not
* enabled, and next_recv_heartbeat and next_send_heartbeat are set to
* infinite */
int heartbeat;
amqp_time_t next_recv_heartbeat;
amqp_time_t next_send_heartbeat;
/* buffer for holding frame headers. Allows us to delay allocating
* the raw frame buffer until the type, channel, and size are all known
*/
char header_buffer[HEADER_SIZE + 1];
amqp_bytes_t inbound_buffer;
size_t inbound_offset;
size_t target_size;
amqp_bytes_t outbound_buffer;
amqp_socket_t *socket;
amqp_bytes_t sock_inbound_buffer;
size_t sock_inbound_offset;
size_t sock_inbound_limit;
amqp_link_t *first_queued_frame;
amqp_link_t *last_queued_frame;
amqp_rpc_reply_t most_recent_api_result;
amqp_table_t server_properties;
amqp_table_t client_properties;
amqp_pool_t properties_pool;
struct timeval *handshake_timeout;
struct timeval internal_handshake_timeout;
struct timeval *rpc_timeout;
struct timeval internal_rpc_timeout;
};
amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t connection,
amqp_channel_t channel);
amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state,
amqp_channel_t channel);
static inline int amqp_heartbeat_send(amqp_connection_state_t state) {
return state->heartbeat;
}
static inline int amqp_heartbeat_recv(amqp_connection_state_t state) {
return 2 * state->heartbeat;
}
int amqp_try_recv(amqp_connection_state_t state);
static inline void *amqp_offset(void *data, size_t offset) {
return (char *)data + offset;
}
/* This macro defines the encoding and decoding functions associated with a
simple type. */
#define DECLARE_CODEC_BASE_TYPE(bits) \
\
static inline int amqp_encode_##bits(amqp_bytes_t encoded, size_t *offset, \
uint##bits##_t input) { \
size_t o = *offset; \
if ((*offset = o + bits / 8) <= encoded.len) { \
amqp_e##bits(input, amqp_offset(encoded.bytes, o)); \
return 1; \
} \
return 0; \
} \
\
static inline int amqp_decode_##bits(amqp_bytes_t encoded, size_t *offset, \
uint##bits##_t *output) { \
size_t o = *offset; \
if ((*offset = o + bits / 8) <= encoded.len) { \
*output = amqp_d##bits(amqp_offset(encoded.bytes, o)); \
return 1; \
} \
return 0; \
}
static inline int is_bigendian(void) {
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
static inline void amqp_e8(uint8_t val, void *data) {
memcpy(data, &val, sizeof(val));
}
static inline uint8_t amqp_d8(void *data) {
uint8_t val;
memcpy(&val, data, sizeof(val));
return val;
}
static inline void amqp_e16(uint16_t val, void *data) {
if (!is_bigendian()) {
val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u);
}
memcpy(data, &val, sizeof(val));
}
static inline uint16_t amqp_d16(void *data) {
uint16_t val;
memcpy(&val, data, sizeof(val));
if (!is_bigendian()) {
val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u);
}
return val;
}
static inline void amqp_e32(uint32_t val, void *data) {
if (!is_bigendian()) {
val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) |
((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u);
}
memcpy(data, &val, sizeof(val));
}
static inline uint32_t amqp_d32(void *data) {
uint32_t val;
memcpy(&val, data, sizeof(val));
if (!is_bigendian()) {
val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) |
((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u);
}
return val;
}
static inline void amqp_e64(uint64_t val, void *data) {
if (!is_bigendian()) {
val = ((val & 0xFF00000000000000u) >> 56u) |
((val & 0x00FF000000000000u) >> 40u) |
((val & 0x0000FF0000000000u) >> 24u) |
((val & 0x000000FF00000000u) >> 8u) |
((val & 0x00000000FF000000u) << 8u) |
((val & 0x0000000000FF0000u) << 24u) |
((val & 0x000000000000FF00u) << 40u) |
((val & 0x00000000000000FFu) << 56u);
}
memcpy(data, &val, sizeof(val));
}
static inline uint64_t amqp_d64(void *data) {
uint64_t val;
memcpy(&val, data, sizeof(val));
if (!is_bigendian()) {
val = ((val & 0xFF00000000000000u) >> 56u) |
((val & 0x00FF000000000000u) >> 40u) |
((val & 0x0000FF0000000000u) >> 24u) |
((val & 0x000000FF00000000u) >> 8u) |
((val & 0x00000000FF000000u) << 8u) |
((val & 0x0000000000FF0000u) << 24u) |
((val & 0x000000000000FF00u) << 40u) |
((val & 0x00000000000000FFu) << 56u);
}
return val;
}
DECLARE_CODEC_BASE_TYPE(8)
DECLARE_CODEC_BASE_TYPE(16)
DECLARE_CODEC_BASE_TYPE(32)
DECLARE_CODEC_BASE_TYPE(64)
static inline int amqp_encode_bytes(amqp_bytes_t encoded, size_t *offset,
amqp_bytes_t input) {
size_t o = *offset;
/* The memcpy below has undefined behavior if the input is NULL. It is valid
* for a 0-length amqp_bytes_t to have .bytes == NULL. Thus we should check
* before encoding.
*/
if (input.len == 0) {
return 1;
}
if ((*offset = o + input.len) <= encoded.len) {
memcpy(amqp_offset(encoded.bytes, o), input.bytes, input.len);
return 1;
} else {
return 0;
}
}
static inline int amqp_decode_bytes(amqp_bytes_t encoded, size_t *offset,
amqp_bytes_t *output, size_t len) {
size_t o = *offset;
if ((*offset = o + len) <= encoded.len) {
output->bytes = amqp_offset(encoded.bytes, o);
output->len = len;
return 1;
} else {
return 0;
}
}
AMQP_NORETURN
void amqp_abort(const char *fmt, ...);
int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l);
static inline amqp_rpc_reply_t amqp_rpc_reply_error(amqp_status_enum status) {
amqp_rpc_reply_t reply;
reply.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
reply.library_error = status;
return reply;
}
int amqp_send_frame_inner(amqp_connection_state_t state,
const amqp_frame_t *frame, int flags,
amqp_time_t deadline);
#endif

1469
librabbitmq/amqp_socket.c Normal file

File diff suppressed because it is too large Load Diff

166
librabbitmq/amqp_socket.h Normal file
View File

@@ -0,0 +1,166 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
/**
* An abstract socket interface.
*/
#ifndef AMQP_SOCKET_H
#define AMQP_SOCKET_H
#include "amqp_private.h"
#include "amqp_time.h"
AMQP_BEGIN_DECLS
typedef enum {
AMQP_SF_NONE = 0,
AMQP_SF_MORE = 1,
AMQP_SF_POLLIN = 2,
AMQP_SF_POLLOUT = 4,
AMQP_SF_POLLERR = 8
} amqp_socket_flag_enum;
typedef enum { AMQP_SC_NONE = 0, AMQP_SC_FORCE = 1 } amqp_socket_close_enum;
int amqp_os_socket_error(void);
int amqp_os_socket_close(int sockfd);
/* Socket callbacks. */
typedef ssize_t (*amqp_socket_send_fn)(void *, const void *, size_t, int);
typedef ssize_t (*amqp_socket_recv_fn)(void *, void *, size_t, int);
typedef int (*amqp_socket_open_fn)(void *, const char *, int,
const struct timeval *);
typedef int (*amqp_socket_close_fn)(void *, amqp_socket_close_enum);
typedef int (*amqp_socket_get_sockfd_fn)(void *);
typedef void (*amqp_socket_delete_fn)(void *);
/** V-table for amqp_socket_t */
struct amqp_socket_class_t {
amqp_socket_send_fn send;
amqp_socket_recv_fn recv;
amqp_socket_open_fn open;
amqp_socket_close_fn close;
amqp_socket_get_sockfd_fn get_sockfd;
amqp_socket_delete_fn delete;
};
/** Abstract base class for amqp_socket_t */
struct amqp_socket_t_ {
const struct amqp_socket_class_t *klass;
};
/**
* Set set the socket object for a connection
*
* This assigns a socket object to the connection, closing and deleting any
* existing socket
*
* \param [in] state The connection object to add the socket to
* \param [in] socket The socket object to assign to the connection
*/
void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket);
/**
* Send a message from a socket.
*
* This function wraps send(2) functionality.
*
* This function will only return on error, or when all of the bytes in buf
* have been sent, or when an error occurs.
*
* \param [in,out] self A socket object.
* \param [in] buf A buffer to read from.
* \param [in] len The number of bytes in \e buf.
* \param [in]
*
* \return AMQP_STATUS_OK on success. amqp_status_enum value otherwise
*/
ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len,
int flags);
ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf,
size_t len, amqp_time_t deadline, int flags);
/**
* Receive a message from a socket.
*
* This function wraps recv(2) functionality.
*
* \param [in,out] self A socket object.
* \param [out] buf A buffer to write to.
* \param [in] len The number of bytes at \e buf.
* \param [in] flags Receive flags, implementation specific.
*
* \return The number of bytes received, or < 0 on error (\ref amqp_status_enum)
*/
ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len, int flags);
/**
* Close a socket connection and free resources.
*
* This function closes a socket connection and releases any resources used by
* the object. After calling this function the specified socket should no
* longer be referenced.
*
* \param [in,out] self A socket object.
* \param [in] force, if set, just close the socket, don't attempt a TLS
* shutdown.
*
* \return Zero upon success, non-zero otherwise.
*/
int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force);
/**
* Destroy a socket object
*
* \param [in] self the socket object to delete
*/
void amqp_socket_delete(amqp_socket_t *self);
/**
* Open a socket connection.
*
* This function opens a socket connection returned from amqp_tcp_socket_new()
* or amqp_ssl_socket_new(). This function should be called after setting
* socket options and prior to assigning the socket to an AMQP connection with
* amqp_set_socket().
*
* \param [in] host Connect to this host.
* \param [in] port Connect on this remote port.
* \param [in] timeout Max allowed time to spent on opening. If NULL - run in
* blocking mode
*
* \return File descriptor upon success, non-zero negative error code otherwise.
*/
int amqp_open_socket_noblock(char const *hostname, int portnumber,
const struct timeval *timeout);
int amqp_open_socket_inner(char const *hostname, int portnumber,
amqp_time_t deadline);
/* Wait up to dealline for fd to become readable or writeable depending on
* event (AMQP_SF_POLLIN, AMQP_SF_POLLOUT) */
int amqp_poll(int fd, int event, amqp_time_t deadline);
int amqp_send_method_inner(amqp_connection_state_t state,
amqp_channel_t channel, amqp_method_number_t id,
void *decoded, int flags, amqp_time_t deadline);
int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame);
int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame);
int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state,
amqp_channel_t channel,
amqp_frame_t *decoded_frame);
int sasl_mechanism_in_list(amqp_bytes_t mechanisms,
amqp_sasl_method_enum method);
int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add,
amqp_table_t *result, amqp_pool_t *pool);
AMQP_END_DECLS
#endif /* AMQP_SOCKET_H */

652
librabbitmq/amqp_table.c Normal file
View File

@@ -0,0 +1,652 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amqp_private.h"
#include "amqp_table.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITIAL_ARRAY_SIZE 16
#define INITIAL_TABLE_SIZE 16
#define TABLE_DEPTH_LIMIT 100
static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
amqp_field_value_t *entry, size_t *offset,
int depth);
static int amqp_encode_field_value(amqp_bytes_t encoded,
amqp_field_value_t *entry, size_t *offset);
/*---------------------------------------------------------------------------*/
static int amqp_decode_array(amqp_bytes_t encoded, amqp_pool_t *pool,
amqp_array_t *output, size_t *offset, int depth) {
uint32_t arraysize;
int num_entries = 0;
int allocated_entries = INITIAL_ARRAY_SIZE;
amqp_field_value_t *entries;
size_t limit;
int res;
if (!amqp_decode_32(encoded, offset, &arraysize)) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
if (arraysize + *offset > encoded.len) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
entries = malloc(allocated_entries * sizeof(amqp_field_value_t));
if (entries == NULL) {
return AMQP_STATUS_NO_MEMORY;
}
limit = *offset + arraysize;
while (*offset < limit) {
if (num_entries >= allocated_entries) {
void *newentries;
allocated_entries = allocated_entries * 2;
newentries =
realloc(entries, allocated_entries * sizeof(amqp_field_value_t));
res = AMQP_STATUS_NO_MEMORY;
if (newentries == NULL) {
goto out;
}
entries = newentries;
}
res = amqp_decode_field_value(encoded, pool, &entries[num_entries], offset,
depth);
if (res < 0) {
goto out;
}
num_entries++;
}
output->num_entries = num_entries;
output->entries =
amqp_pool_alloc(pool, num_entries * sizeof(amqp_field_value_t));
/* NULL is legitimate if we requested a zero-length block. */
if (output->entries == NULL) {
if (num_entries == 0) {
res = AMQP_STATUS_OK;
} else {
res = AMQP_STATUS_NO_MEMORY;
}
goto out;
}
memcpy(output->entries, entries, num_entries * sizeof(amqp_field_value_t));
res = AMQP_STATUS_OK;
out:
free(entries);
return res;
}
static int amqp_decode_table_internal(amqp_bytes_t encoded, amqp_pool_t *pool,
amqp_table_t *output, size_t *offset,
int depth) {
uint32_t tablesize;
int num_entries = 0;
amqp_table_entry_t *entries;
int allocated_entries = INITIAL_TABLE_SIZE;
size_t limit;
int res;
if (!amqp_decode_32(encoded, offset, &tablesize)) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
if (tablesize + *offset > encoded.len) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
entries = malloc(allocated_entries * sizeof(amqp_table_entry_t));
if (entries == NULL) {
return AMQP_STATUS_NO_MEMORY;
}
limit = *offset + tablesize;
while (*offset < limit) {
uint8_t keylen;
res = AMQP_STATUS_BAD_AMQP_DATA;
if (!amqp_decode_8(encoded, offset, &keylen)) {
goto out;
}
if (num_entries >= allocated_entries) {
void *newentries;
allocated_entries = allocated_entries * 2;
newentries =
realloc(entries, allocated_entries * sizeof(amqp_table_entry_t));
res = AMQP_STATUS_NO_MEMORY;
if (newentries == NULL) {
goto out;
}
entries = newentries;
}
res = AMQP_STATUS_BAD_AMQP_DATA;
if (!amqp_decode_bytes(encoded, offset, &entries[num_entries].key,
keylen)) {
goto out;
}
res = amqp_decode_field_value(encoded, pool, &entries[num_entries].value,
offset, depth);
if (res < 0) {
goto out;
}
num_entries++;
}
output->num_entries = num_entries;
output->entries =
amqp_pool_alloc(pool, num_entries * sizeof(amqp_table_entry_t));
/* NULL is legitimate if we requested a zero-length block. */
if (output->entries == NULL) {
if (num_entries == 0) {
res = AMQP_STATUS_OK;
} else {
res = AMQP_STATUS_NO_MEMORY;
}
goto out;
}
memcpy(output->entries, entries, num_entries * sizeof(amqp_table_entry_t));
res = AMQP_STATUS_OK;
out:
free(entries);
return res;
}
int amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool,
amqp_table_t *output, size_t *offset) {
return amqp_decode_table_internal(encoded, pool, output, offset, 0);
}
static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
amqp_field_value_t *entry, size_t *offset,
int depth) {
int res = AMQP_STATUS_BAD_AMQP_DATA;
if (depth > TABLE_DEPTH_LIMIT) {
return AMQP_STATUS_BAD_AMQP_DATA;
}
if (!amqp_decode_8(encoded, offset, &entry->kind)) {
goto out;
}
#define TRIVIAL_FIELD_DECODER(bits) \
if (!amqp_decode_##bits(encoded, offset, &entry->value.u##bits)) goto out; \
break
#define SIMPLE_FIELD_DECODER(bits, dest, how) \
{ \
uint##bits##_t val; \
if (!amqp_decode_##bits(encoded, offset, &val)) goto out; \
entry->value.dest = how; \
} \
break
switch (entry->kind) {
case AMQP_FIELD_KIND_BOOLEAN:
SIMPLE_FIELD_DECODER(8, boolean, val ? 1 : 0);
case AMQP_FIELD_KIND_I8:
SIMPLE_FIELD_DECODER(8, i8, (int8_t)val);
case AMQP_FIELD_KIND_U8:
TRIVIAL_FIELD_DECODER(8);
case AMQP_FIELD_KIND_I16:
SIMPLE_FIELD_DECODER(16, i16, (int16_t)val);
case AMQP_FIELD_KIND_U16:
TRIVIAL_FIELD_DECODER(16);
case AMQP_FIELD_KIND_I32:
SIMPLE_FIELD_DECODER(32, i32, (int32_t)val);
case AMQP_FIELD_KIND_U32:
TRIVIAL_FIELD_DECODER(32);
case AMQP_FIELD_KIND_I64:
SIMPLE_FIELD_DECODER(64, i64, (int64_t)val);
case AMQP_FIELD_KIND_U64:
TRIVIAL_FIELD_DECODER(64);
case AMQP_FIELD_KIND_F32:
TRIVIAL_FIELD_DECODER(32);
/* and by punning, f32 magically gets the right value...! */
case AMQP_FIELD_KIND_F64:
TRIVIAL_FIELD_DECODER(64);
/* and by punning, f64 magically gets the right value...! */
case AMQP_FIELD_KIND_DECIMAL:
if (!amqp_decode_8(encoded, offset, &entry->value.decimal.decimals) ||
!amqp_decode_32(encoded, offset, &entry->value.decimal.value)) {
goto out;
}
break;
case AMQP_FIELD_KIND_UTF8:
/* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
same implementation, but different interpretations. */
/* fall through */
case AMQP_FIELD_KIND_BYTES: {
uint32_t len;
if (!amqp_decode_32(encoded, offset, &len) ||
!amqp_decode_bytes(encoded, offset, &entry->value.bytes, len)) {
goto out;
}
break;
}
case AMQP_FIELD_KIND_ARRAY:
res = amqp_decode_array(encoded, pool, &(entry->value.array), offset,
depth + 1);
goto out;
case AMQP_FIELD_KIND_TIMESTAMP:
TRIVIAL_FIELD_DECODER(64);
case AMQP_FIELD_KIND_TABLE:
res = amqp_decode_table_internal(encoded, pool, &(entry->value.table),
offset, depth + 1);
goto out;
case AMQP_FIELD_KIND_VOID:
break;
default:
goto out;
}
res = AMQP_STATUS_OK;
out:
return res;
}
/*---------------------------------------------------------------------------*/
static int amqp_encode_array(amqp_bytes_t encoded, amqp_array_t *input,
size_t *offset) {
size_t start = *offset;
int i, res;
*offset += 4; /* size of the array gets filled in later on */
for (i = 0; i < input->num_entries; i++) {
res = amqp_encode_field_value(encoded, &input->entries[i], offset);
if (res < 0) {
goto out;
}
}
if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
res = AMQP_STATUS_TABLE_TOO_BIG;
goto out;
}
res = AMQP_STATUS_OK;
out:
return res;
}
int amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input,
size_t *offset) {
size_t start = *offset;
int i, res;
*offset += 4; /* size of the table gets filled in later on */
for (i = 0; i < input->num_entries; i++) {
if (!amqp_encode_8(encoded, offset, (uint8_t)input->entries[i].key.len)) {
res = AMQP_STATUS_TABLE_TOO_BIG;
goto out;
}
if (!amqp_encode_bytes(encoded, offset, input->entries[i].key)) {
res = AMQP_STATUS_TABLE_TOO_BIG;
goto out;
}
res = amqp_encode_field_value(encoded, &input->entries[i].value, offset);
if (res < 0) {
goto out;
}
}
if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
res = AMQP_STATUS_TABLE_TOO_BIG;
goto out;
}
res = AMQP_STATUS_OK;
out:
return res;
}
static int amqp_encode_field_value(amqp_bytes_t encoded,
amqp_field_value_t *entry, size_t *offset) {
int res = AMQP_STATUS_BAD_AMQP_DATA;
if (!amqp_encode_8(encoded, offset, entry->kind)) {
goto out;
}
#define FIELD_ENCODER(bits, val) \
if (!amqp_encode_##bits(encoded, offset, val)) { \
res = AMQP_STATUS_TABLE_TOO_BIG; \
goto out; \
} \
break
switch (entry->kind) {
case AMQP_FIELD_KIND_BOOLEAN:
FIELD_ENCODER(8, entry->value.boolean ? 1 : 0);
case AMQP_FIELD_KIND_I8:
FIELD_ENCODER(8, entry->value.i8);
case AMQP_FIELD_KIND_U8:
FIELD_ENCODER(8, entry->value.u8);
case AMQP_FIELD_KIND_I16:
FIELD_ENCODER(16, entry->value.i16);
case AMQP_FIELD_KIND_U16:
FIELD_ENCODER(16, entry->value.u16);
case AMQP_FIELD_KIND_I32:
FIELD_ENCODER(32, entry->value.i32);
case AMQP_FIELD_KIND_U32:
FIELD_ENCODER(32, entry->value.u32);
case AMQP_FIELD_KIND_I64:
FIELD_ENCODER(64, entry->value.i64);
case AMQP_FIELD_KIND_U64:
FIELD_ENCODER(64, entry->value.u64);
case AMQP_FIELD_KIND_F32:
/* by punning, u32 magically gets the right value...! */
FIELD_ENCODER(32, entry->value.u32);
case AMQP_FIELD_KIND_F64:
/* by punning, u64 magically gets the right value...! */
FIELD_ENCODER(64, entry->value.u64);
case AMQP_FIELD_KIND_DECIMAL:
if (!amqp_encode_8(encoded, offset, entry->value.decimal.decimals) ||
!amqp_encode_32(encoded, offset, entry->value.decimal.value)) {
res = AMQP_STATUS_TABLE_TOO_BIG;
goto out;
}
break;
case AMQP_FIELD_KIND_UTF8:
/* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
same implementation, but different interpretations. */
/* fall through */
case AMQP_FIELD_KIND_BYTES:
if (!amqp_encode_32(encoded, offset, (uint32_t)entry->value.bytes.len) ||
!amqp_encode_bytes(encoded, offset, entry->value.bytes)) {
res = AMQP_STATUS_TABLE_TOO_BIG;
goto out;
}
break;
case AMQP_FIELD_KIND_ARRAY:
res = amqp_encode_array(encoded, &entry->value.array, offset);
goto out;
case AMQP_FIELD_KIND_TIMESTAMP:
FIELD_ENCODER(64, entry->value.u64);
case AMQP_FIELD_KIND_TABLE:
res = amqp_encode_table(encoded, &entry->value.table, offset);
goto out;
case AMQP_FIELD_KIND_VOID:
break;
default:
res = AMQP_STATUS_INVALID_PARAMETER;
goto out;
}
res = AMQP_STATUS_OK;
out:
return res;
}
/*---------------------------------------------------------------------------*/
int amqp_table_entry_cmp(void const *entry1, void const *entry2) {
amqp_table_entry_t const *p1 = (amqp_table_entry_t const *)entry1;
amqp_table_entry_t const *p2 = (amqp_table_entry_t const *)entry2;
int d;
size_t minlen;
minlen = p1->key.len;
if (p2->key.len < minlen) {
minlen = p2->key.len;
}
d = memcmp(p1->key.bytes, p2->key.bytes, minlen);
if (d != 0) {
return d;
}
return (int)p1->key.len - (int)p2->key.len;
}
static int amqp_field_value_clone(const amqp_field_value_t *original,
amqp_field_value_t *clone,
amqp_pool_t *pool) {
int i;
int res;
clone->kind = original->kind;
switch (clone->kind) {
case AMQP_FIELD_KIND_BOOLEAN:
clone->value.boolean = original->value.boolean;
break;
case AMQP_FIELD_KIND_I8:
clone->value.i8 = original->value.i8;
break;
case AMQP_FIELD_KIND_U8:
clone->value.u8 = original->value.u8;
break;
case AMQP_FIELD_KIND_I16:
clone->value.i16 = original->value.i16;
break;
case AMQP_FIELD_KIND_U16:
clone->value.u16 = original->value.u16;
break;
case AMQP_FIELD_KIND_I32:
clone->value.i32 = original->value.i32;
break;
case AMQP_FIELD_KIND_U32:
clone->value.u32 = original->value.u32;
break;
case AMQP_FIELD_KIND_I64:
clone->value.i64 = original->value.i64;
break;
case AMQP_FIELD_KIND_U64:
case AMQP_FIELD_KIND_TIMESTAMP:
clone->value.u64 = original->value.u64;
break;
case AMQP_FIELD_KIND_F32:
clone->value.f32 = original->value.f32;
break;
case AMQP_FIELD_KIND_F64:
clone->value.f64 = original->value.f64;
break;
case AMQP_FIELD_KIND_DECIMAL:
clone->value.decimal = original->value.decimal;
break;
case AMQP_FIELD_KIND_UTF8:
case AMQP_FIELD_KIND_BYTES:
if (0 == original->value.bytes.len) {
clone->value.bytes = amqp_empty_bytes;
} else {
amqp_pool_alloc_bytes(pool, original->value.bytes.len,
&clone->value.bytes);
if (NULL == clone->value.bytes.bytes) {
return AMQP_STATUS_NO_MEMORY;
}
memcpy(clone->value.bytes.bytes, original->value.bytes.bytes,
clone->value.bytes.len);
}
break;
case AMQP_FIELD_KIND_ARRAY:
if (0 == original->value.array.entries) {
clone->value.array = amqp_empty_array;
} else {
clone->value.array.num_entries = original->value.array.num_entries;
clone->value.array.entries = amqp_pool_alloc(
pool, clone->value.array.num_entries * sizeof(amqp_field_value_t));
if (NULL == clone->value.array.entries) {
return AMQP_STATUS_NO_MEMORY;
}
for (i = 0; i < clone->value.array.num_entries; ++i) {
res = amqp_field_value_clone(&original->value.array.entries[i],
&clone->value.array.entries[i], pool);
if (AMQP_STATUS_OK != res) {
return res;
}
}
}
break;
case AMQP_FIELD_KIND_TABLE:
return amqp_table_clone(&original->value.table, &clone->value.table,
pool);
case AMQP_FIELD_KIND_VOID:
break;
default:
return AMQP_STATUS_INVALID_PARAMETER;
}
return AMQP_STATUS_OK;
}
static int amqp_table_entry_clone(const amqp_table_entry_t *original,
amqp_table_entry_t *clone,
amqp_pool_t *pool) {
if (0 == original->key.len) {
return AMQP_STATUS_INVALID_PARAMETER;
}
amqp_pool_alloc_bytes(pool, original->key.len, &clone->key);
if (NULL == clone->key.bytes) {
return AMQP_STATUS_NO_MEMORY;
}
memcpy(clone->key.bytes, original->key.bytes, clone->key.len);
return amqp_field_value_clone(&original->value, &clone->value, pool);
}
int amqp_table_clone(const amqp_table_t *original, amqp_table_t *clone,
amqp_pool_t *pool) {
int i;
int res;
clone->num_entries = original->num_entries;
if (0 == clone->num_entries) {
*clone = amqp_empty_table;
return AMQP_STATUS_OK;
}
clone->entries =
amqp_pool_alloc(pool, clone->num_entries * sizeof(amqp_table_entry_t));
if (NULL == clone->entries) {
return AMQP_STATUS_NO_MEMORY;
}
for (i = 0; i < clone->num_entries; ++i) {
res =
amqp_table_entry_clone(&original->entries[i], &clone->entries[i], pool);
if (AMQP_STATUS_OK != res) {
goto error_out1;
}
}
return AMQP_STATUS_OK;
error_out1:
return res;
}
amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key,
const char *value) {
amqp_table_entry_t ret;
ret.key = amqp_cstring_bytes(key);
ret.value.kind = AMQP_FIELD_KIND_UTF8;
ret.value.value.bytes = amqp_cstring_bytes(value);
return ret;
}
amqp_table_entry_t amqp_table_construct_table_entry(const char *key,
const amqp_table_t *value) {
amqp_table_entry_t ret;
ret.key = amqp_cstring_bytes(key);
ret.value.kind = AMQP_FIELD_KIND_TABLE;
ret.value.value.table = *value;
return ret;
}
amqp_table_entry_t amqp_table_construct_bool_entry(const char *key,
const int value) {
amqp_table_entry_t ret;
ret.key = amqp_cstring_bytes(key);
ret.value.kind = AMQP_FIELD_KIND_BOOLEAN;
ret.value.value.boolean = value;
return ret;
}
amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table,
const amqp_bytes_t key) {
int i;
assert(table != NULL);
for (i = 0; i < table->num_entries; ++i) {
if (amqp_bytes_equal(table->entries[i].key, key)) {
return &table->entries[i];
}
}
return NULL;
}

56
librabbitmq/amqp_table.h Normal file
View File

@@ -0,0 +1,56 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef AMQP_TABLE_H
#define AMQP_TABLE_H
#include "amqp_private.h"
#include "rabbitmq-c/amqp.h"
/**
* Initializes a table entry with utf-8 string type value.
*
* \param [in] key the table entry key. The string must remain valid for the
* life of the resulting amqp_table_entry_t.
* \param [in] value the string value. The string must remain valid for the life
* of the resulting amqp_table_entry_t.
* \returns An initialized table entry.
*/
amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key,
const char *value);
/**
* Initializes a table entry with table type value.
*
* \param [in] key the table entry key. The string must remain value for the
* life of the resulting amqp_table_entry_t.
* \param [in] value the amqp_table_t value. The table must remain valid for the
* life of the resulting amqp_table_entry_t.
* \returns An initialized table entry.
*/
amqp_table_entry_t amqp_table_construct_table_entry(const char *key,
const amqp_table_t *value);
/**
* Initializes a table entry with boolean type value.
*
* \param [in] key the table entry key. The string must remain value for the
* life of the resulting amqp_table_entry_t.
* \param [in] value the boolean value. 0 means false, any other value is true.
* \returns An initialized table entry.
*/
amqp_table_entry_t amqp_table_construct_bool_entry(const char *key,
const int value);
/**
* Searches a table for an entry with a matching key.
*
* \param [in] table the table to search.
* \param [in] key the string to search with.
* \returns a pointer to the table entry in the table if a matching key can be
* found, NULL otherwise.
*/
amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table,
const amqp_bytes_t key);
#endif /* AMQP_TABLE_H */

View File

@@ -0,0 +1,219 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amqp_private.h"
#include "rabbitmq-c/tcp_socket.h"
#include <errno.h>
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#endif
#include <stdio.h>
#include <stdlib.h>
struct amqp_tcp_socket_t {
const struct amqp_socket_class_t *klass;
int sockfd;
int internal_error;
int state;
};
static ssize_t amqp_tcp_socket_send(void *base, const void *buf, size_t len,
int flags) {
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
ssize_t res;
int flagz = 0;
if (-1 == self->sockfd) {
return AMQP_STATUS_SOCKET_CLOSED;
}
#ifdef MSG_NOSIGNAL
flagz |= MSG_NOSIGNAL;
#endif
#if defined(MSG_MORE)
if (flags & AMQP_SF_MORE) {
flagz |= MSG_MORE;
}
/* Cygwin defines TCP_NOPUSH, but trying to use it will return not
* implemented. Disable it here. */
#elif defined(TCP_NOPUSH) && !defined(__CYGWIN__)
if (flags & AMQP_SF_MORE && !(self->state & AMQP_SF_MORE)) {
int one = 1;
res = setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &one, sizeof(one));
if (0 != res) {
self->internal_error = res;
return AMQP_STATUS_SOCKET_ERROR;
}
self->state |= AMQP_SF_MORE;
} else if (!(flags & AMQP_SF_MORE) && self->state & AMQP_SF_MORE) {
int zero = 0;
res =
setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &zero, sizeof(&zero));
if (0 != res) {
self->internal_error = res;
res = AMQP_STATUS_SOCKET_ERROR;
} else {
self->state &= ~AMQP_SF_MORE;
}
}
#endif
start:
#ifdef _WIN32
res = send(self->sockfd, buf, (int)len, flagz);
#else
res = send(self->sockfd, buf, len, flagz);
#endif
if (res < 0) {
self->internal_error = amqp_os_socket_error();
switch (self->internal_error) {
case EINTR:
goto start;
#ifdef _WIN32
case WSAEWOULDBLOCK:
#else
case EWOULDBLOCK:
#endif
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
case EAGAIN:
#endif
res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
break;
default:
res = AMQP_STATUS_SOCKET_ERROR;
}
} else {
self->internal_error = 0;
}
return res;
}
static ssize_t amqp_tcp_socket_recv(void *base, void *buf, size_t len,
int flags) {
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
ssize_t ret;
if (-1 == self->sockfd) {
return AMQP_STATUS_SOCKET_CLOSED;
}
start:
#ifdef _WIN32
ret = recv(self->sockfd, buf, (int)len, flags);
#else
ret = recv(self->sockfd, buf, len, flags);
#endif
if (0 > ret) {
self->internal_error = amqp_os_socket_error();
switch (self->internal_error) {
case EINTR:
goto start;
#ifdef _WIN32
case WSAEWOULDBLOCK:
#else
case EWOULDBLOCK:
#endif
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
case EAGAIN:
#endif
ret = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
break;
default:
ret = AMQP_STATUS_SOCKET_ERROR;
}
} else if (0 == ret) {
ret = AMQP_STATUS_CONNECTION_CLOSED;
}
return ret;
}
static int amqp_tcp_socket_open(void *base, const char *host, int port,
const struct timeval *timeout) {
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
if (-1 != self->sockfd) {
return AMQP_STATUS_SOCKET_INUSE;
}
self->sockfd = amqp_open_socket_noblock(host, port, timeout);
if (0 > self->sockfd) {
int err = self->sockfd;
self->sockfd = -1;
return err;
}
return AMQP_STATUS_OK;
}
static int amqp_tcp_socket_close(void *base,
AMQP_UNUSED amqp_socket_close_enum force) {
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
if (-1 == self->sockfd) {
return AMQP_STATUS_SOCKET_CLOSED;
}
if (amqp_os_socket_close(self->sockfd)) {
return AMQP_STATUS_SOCKET_ERROR;
}
self->sockfd = -1;
return AMQP_STATUS_OK;
}
static int amqp_tcp_socket_get_sockfd(void *base) {
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
return self->sockfd;
}
static void amqp_tcp_socket_delete(void *base) {
struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
if (self) {
amqp_tcp_socket_close(self, AMQP_SC_NONE);
free(self);
}
}
static const struct amqp_socket_class_t amqp_tcp_socket_class = {
amqp_tcp_socket_send, /* send */
amqp_tcp_socket_recv, /* recv */
amqp_tcp_socket_open, /* open */
amqp_tcp_socket_close, /* close */
amqp_tcp_socket_get_sockfd, /* get_sockfd */
amqp_tcp_socket_delete /* delete */
};
amqp_socket_t *amqp_tcp_socket_new(amqp_connection_state_t state) {
struct amqp_tcp_socket_t *self = calloc(1, sizeof(*self));
if (!self) {
return NULL;
}
self->klass = &amqp_tcp_socket_class;
self->sockfd = -1;
amqp_set_socket(state, (amqp_socket_t *)self);
return (amqp_socket_t *)self;
}
void amqp_tcp_socket_set_sockfd(amqp_socket_t *base, int sockfd) {
struct amqp_tcp_socket_t *self;
if (base->klass != &amqp_tcp_socket_class) {
amqp_abort("<%p> is not of type amqp_tcp_socket_t", base);
}
self = (struct amqp_tcp_socket_t *)base;
self->sockfd = sockfd;
}

236
librabbitmq/amqp_time.c Normal file
View File

@@ -0,0 +1,236 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "amqp_time.h"
#include "rabbitmq-c/amqp.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || \
defined(__MINGW32__) || defined(__MINGW64__))
#define AMQP_WIN_TIMER_API
#elif (defined(machintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
#define AMQP_MAC_TIMER_API
#else
#define AMQP_POSIX_TIMER_API
#endif
#ifdef AMQP_WIN_TIMER_API
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
uint64_t amqp_get_monotonic_timestamp(void) {
static double NS_PER_COUNT = 0;
LARGE_INTEGER perf_count;
if (0 == NS_PER_COUNT) {
LARGE_INTEGER perf_frequency;
if (!QueryPerformanceFrequency(&perf_frequency)) {
return 0;
}
NS_PER_COUNT = (double)AMQP_NS_PER_S / perf_frequency.QuadPart;
}
if (!QueryPerformanceCounter(&perf_count)) {
return 0;
}
return (uint64_t)(perf_count.QuadPart * NS_PER_COUNT);
}
#endif /* AMQP_WIN_TIMER_API */
#ifdef AMQP_MAC_TIMER_API
#include <mach/mach_time.h>
uint64_t amqp_get_monotonic_timestamp(void) {
static mach_timebase_info_data_t s_timebase = {0, 0};
uint64_t timestamp;
timestamp = mach_absolute_time();
if (s_timebase.denom == 0) {
mach_timebase_info(&s_timebase);
if (0 == s_timebase.denom) {
return 0;
}
}
timestamp *= (uint64_t)s_timebase.numer;
timestamp /= (uint64_t)s_timebase.denom;
return timestamp;
}
#endif /* AMQP_MAC_TIMER_API */
#ifdef AMQP_POSIX_TIMER_API
#include <time.h>
uint64_t amqp_get_monotonic_timestamp(void) {
#ifdef __hpux
return (uint64_t)gethrtime();
#else
struct timespec tp;
if (-1 == clock_gettime(CLOCK_MONOTONIC, &tp)) {
return 0;
}
return ((uint64_t)tp.tv_sec * AMQP_NS_PER_S + (uint64_t)tp.tv_nsec);
#endif
}
#endif /* AMQP_POSIX_TIMER_API */
int amqp_time_from_now(amqp_time_t *time, const struct timeval *timeout) {
uint64_t now_ns;
uint64_t delta_ns;
assert(NULL != time);
if (NULL == timeout) {
*time = amqp_time_infinite();
return AMQP_STATUS_OK;
}
if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
return AMQP_STATUS_INVALID_PARAMETER;
}
delta_ns = (uint64_t)timeout->tv_sec * AMQP_NS_PER_S +
(uint64_t)timeout->tv_usec * AMQP_NS_PER_US;
now_ns = amqp_get_monotonic_timestamp();
if (0 == now_ns) {
return AMQP_STATUS_TIMER_FAILURE;
}
time->time_point_ns = now_ns + delta_ns;
if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) {
return AMQP_STATUS_INVALID_PARAMETER;
}
return AMQP_STATUS_OK;
}
int amqp_time_s_from_now(amqp_time_t *time, int seconds) {
uint64_t now_ns;
uint64_t delta_ns;
assert(NULL != time);
if (0 >= seconds) {
*time = amqp_time_infinite();
return AMQP_STATUS_OK;
}
now_ns = amqp_get_monotonic_timestamp();
if (0 == now_ns) {
return AMQP_STATUS_TIMER_FAILURE;
}
delta_ns = (uint64_t)seconds * AMQP_NS_PER_S;
time->time_point_ns = now_ns + delta_ns;
if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) {
return AMQP_STATUS_INVALID_PARAMETER;
}
return AMQP_STATUS_OK;
}
amqp_time_t amqp_time_infinite(void) {
amqp_time_t time;
time.time_point_ns = UINT64_MAX;
return time;
}
int amqp_time_ms_until(amqp_time_t time) {
uint64_t now_ns;
uint64_t delta_ns;
int left_ms;
if (UINT64_MAX == time.time_point_ns) {
return -1;
}
if (0 == time.time_point_ns) {
return 0;
}
now_ns = amqp_get_monotonic_timestamp();
if (0 == now_ns) {
return AMQP_STATUS_TIMER_FAILURE;
}
if (now_ns >= time.time_point_ns) {
return 0;
}
delta_ns = time.time_point_ns - now_ns;
left_ms = (int)(delta_ns / AMQP_NS_PER_MS);
return left_ms;
}
int amqp_time_tv_until(amqp_time_t time, struct timeval *in,
struct timeval **out) {
uint64_t now_ns;
uint64_t delta_ns;
assert(in != NULL);
if (UINT64_MAX == time.time_point_ns) {
*out = NULL;
return AMQP_STATUS_OK;
}
if (0 == time.time_point_ns) {
in->tv_sec = 0;
in->tv_usec = 0;
*out = in;
return AMQP_STATUS_OK;
}
now_ns = amqp_get_monotonic_timestamp();
if (0 == now_ns) {
return AMQP_STATUS_TIMER_FAILURE;
}
if (now_ns >= time.time_point_ns) {
in->tv_sec = 0;
in->tv_usec = 0;
*out = in;
return AMQP_STATUS_OK;
}
delta_ns = time.time_point_ns - now_ns;
in->tv_sec = (int)(delta_ns / AMQP_NS_PER_S);
in->tv_usec = (int)((delta_ns % AMQP_NS_PER_S) / AMQP_NS_PER_US);
*out = in;
return AMQP_STATUS_OK;
}
int amqp_time_has_past(amqp_time_t time) {
uint64_t now_ns;
if (UINT64_MAX == time.time_point_ns) {
return AMQP_STATUS_OK;
}
now_ns = amqp_get_monotonic_timestamp();
if (0 == now_ns) {
return AMQP_STATUS_TIMER_FAILURE;
}
if (now_ns > time.time_point_ns) {
return AMQP_STATUS_TIMEOUT;
}
return AMQP_STATUS_OK;
}
amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r) {
if (l.time_point_ns < r.time_point_ns) {
return l;
}
return r;
}
int amqp_time_equal(amqp_time_t l, amqp_time_t r) {
return l.time_point_ns == r.time_point_ns;
}

109
librabbitmq/amqp_time.h Normal file
View File

@@ -0,0 +1,109 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef AMQP_TIMER_H
#define AMQP_TIMER_H
#include <stdint.h>
#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
#ifndef WINVER
#define WINVER 0x0502
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#else
#include <sys/time.h>
#endif
#define AMQP_MS_PER_S 1000
#define AMQP_US_PER_MS 1000
#define AMQP_NS_PER_S 1000000000
#define AMQP_NS_PER_MS 1000000
#define AMQP_NS_PER_US 1000
/* This represents a point in time in reference to a monotonic clock.
*
* The internal representation is ns, relative to the monotonic clock.
*
* There are two 'special' values:
* - 0: means 'this instant', its meant for polls with a 0-timeout, or
* non-blocking option
* - UINT64_MAX: means 'at infinity', its mean for polls with an infinite
* timeout
*/
typedef struct amqp_time_t_ {
uint64_t time_point_ns;
} amqp_time_t;
/* Gets a monotonic timestamp. This will return 0 if the underlying call to the
* system fails.
*/
uint64_t amqp_get_monotonic_timestamp(void);
/* Get a amqp_time_t that is timeout from now.
* If timeout is NULL, an amqp_time_infinite() is created.
*
* Returns AMQP_STATUS_OK on success.
* AMQP_STATUS_INVALID_PARAMETER if timeout is invalid
* AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp
* fails.
*/
int amqp_time_from_now(amqp_time_t *time, const struct timeval *timeout);
/* Get a amqp_time_t that is seconds from now.
* If seconds <= 0, then amqp_time_infinite() is created.
*
* Returns AMQP_STATUS_OK on success.
* AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp
* fails.
*/
int amqp_time_s_from_now(amqp_time_t *time, int seconds);
/* Create an infinite amqp_time_t */
amqp_time_t amqp_time_infinite(void);
/* Gets the number of ms until the amqp_time_t, suitable for the timeout
* parameter in poll().
*
* -1 will be returned for amqp_time_infinite values.
* 0 will be returned for amqp_time_immediate values.
* AMQP_STATUS_TIMEOUT will be returned if time was in the past.
* AMQP_STATUS_TIMER_FAILURE will be returned if the underlying call to get the
* current timestamp fails.
*/
int amqp_time_ms_until(amqp_time_t time);
/* Gets a timeval filled in with the time until amqp_time_t. Suitable for the
* parameter in select().
*
* The in parameter specifies a storage location for *out.
* If time is an inf timeout, then *out = NULL.
* If time is a 0-timeout or the timer has expired, then *out = {0, 0}
* Otherwise *out is set to the time left on the time.
*
* AMQP_STATUS_OK will be returned if successfully filled.
* AMQP_STATUS_TIMER_FAILURE is returned when the underlying call to get the
* current timestamp fails.
*/
int amqp_time_tv_until(amqp_time_t time, struct timeval *in,
struct timeval **out);
/* Test whether current time is past the provided time.
*
* TODO: this isn't a great interface to use. Fix this.
*
* Return AMQP_STATUS_OK if time has not past
* Return AMQP_STATUS_TIMEOUT if time has past
* Return AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current
* timestamp fails.
*/
int amqp_time_has_past(amqp_time_t time);
/* Return the time value that happens first */
amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r);
int amqp_time_equal(amqp_time_t l, amqp_time_t r);
#endif /* AMQP_TIMER_H */

188
librabbitmq/amqp_url.c Normal file
View File

@@ -0,0 +1,188 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "amqp_private.h"
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void amqp_default_connection_info(struct amqp_connection_info *ci) {
/* Apply defaults */
ci->user = "guest";
ci->password = "guest";
ci->host = "localhost";
ci->port = 5672;
ci->vhost = "/";
ci->ssl = 0;
}
/* Scan for the next delimiter, handling percent-encodings on the way. */
static char find_delim(char **pp, int colon_and_at_sign_are_delims) {
char *from = *pp;
char *to = from;
for (;;) {
char ch = *from++;
switch (ch) {
case ':':
case '@':
if (!colon_and_at_sign_are_delims) {
*to++ = ch;
break;
}
/* fall through */
case 0:
case '/':
case '?':
case '#':
case '[':
case ']':
*to = 0;
*pp = from;
return ch;
case '%': {
unsigned int val;
int chars;
int res = sscanf(from, "%2x%n", &val, &chars);
if (res == EOF || res < 1 || chars != 2 || val > CHAR_MAX)
/* Return a surprising delimiter to
force an error. */
{
return '%';
}
*to++ = (char)val;
from += 2;
break;
}
default:
*to++ = ch;
break;
}
}
}
/* Parse an AMQP URL into its component parts. */
int amqp_parse_url(char *url, struct amqp_connection_info *parsed) {
int res = AMQP_STATUS_BAD_URL;
char delim;
char *start;
char *host;
char *port = NULL;
amqp_default_connection_info(parsed);
/* check the prefix */
if (!strncmp(url, "amqp://", 7)) {
/* do nothing */
} else if (!strncmp(url, "amqps://", 8)) {
parsed->port = 5671;
parsed->ssl = 1;
} else {
goto out;
}
host = start = url += (parsed->ssl ? 8 : 7);
delim = find_delim(&url, 1);
if (delim == ':') {
/* The colon could be introducing the port or the
password part of the userinfo. We don't know yet,
so stash the preceding component. */
port = start = url;
delim = find_delim(&url, 1);
}
if (delim == '@') {
/* What might have been the host and port were in fact
the username and password */
parsed->user = host;
if (port) {
parsed->password = port;
}
port = NULL;
host = start = url;
delim = find_delim(&url, 1);
}
if (delim == '[') {
/* IPv6 address. The bracket should be the first
character in the host. */
if (host != start || *host != 0) {
goto out;
}
start = url;
delim = find_delim(&url, 0);
if (delim != ']') {
goto out;
}
parsed->host = start;
start = url;
delim = find_delim(&url, 1);
/* Closing bracket should be the last character in the
host. */
if (*start != 0) {
goto out;
}
} else {
/* If we haven't seen the host yet, this is it. */
if (*host != 0) {
parsed->host = host;
}
}
if (delim == ':') {
port = url;
delim = find_delim(&url, 1);
}
if (port) {
char *end;
long portnum = strtol(port, &end, 10);
if (port == end || *end != 0 || portnum < 0 || portnum > 65535) {
goto out;
}
parsed->port = portnum;
}
if (delim == '/') {
start = url;
delim = find_delim(&url, 1);
if (delim != 0) {
goto out;
}
parsed->vhost = start;
res = AMQP_STATUS_OK;
} else if (delim == 0) {
res = AMQP_STATUS_OK;
}
/* Any other delimiter is bad, and we will return AMQP_STATUS_BAD_AMQP_URL. */
out:
return res;
}

692
librabbitmq/codegen.py Normal file
View File

@@ -0,0 +1,692 @@
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
# SPDX-License-Identifier: mit
from amqp_codegen import *
import string
import re
class Emitter(object):
"""An object the trivially emits generated code lines.
This largely exists to be wrapped by more sophisticated emitter
classes.
"""
def __init__(self, prefix):
self.prefix = prefix
def emit(self, line):
"""Emit a line of generated code."""
print(self.prefix + line)
class BitDecoder(object):
"""An emitter object that keeps track of the state involved in
decoding the AMQP bit type."""
def __init__(self, emitter):
self.emitter = emitter
self.bit = 0
def emit(self, line):
self.bit = 0
self.emitter.emit(line)
def decode_bit(self, lvalue):
"""Generate code to decode a value of the AMQP bit type into
the given lvalue."""
if self.bit == 0:
self.emitter.emit("if (!amqp_decode_8(encoded, &offset, &bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;")
self.emitter.emit("%s = (bit_buffer & (1 << %d)) ? 1 : 0;"
% (lvalue, self.bit))
self.bit += 1
if self.bit == 8:
self.bit = 0
class BitEncoder(object):
"""An emitter object that keeps track of the state involved in
encoding the AMQP bit type."""
def __init__(self, emitter):
self.emitter = emitter
self.bit = 0
def flush(self):
"""Flush the state associated with AMQP bit types."""
if self.bit:
self.emitter.emit("if (!amqp_encode_8(encoded, &offset, bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;")
self.bit = 0
def emit(self, line):
self.flush()
self.emitter.emit(line)
def encode_bit(self, value):
"""Generate code to encode a value of the AMQP bit type from
the given value."""
if self.bit == 0:
self.emitter.emit("bit_buffer = 0;")
self.emitter.emit("if (%s) bit_buffer |= (1 << %d);"
% (value, self.bit))
self.bit += 1
if self.bit == 8:
self.flush()
class SimpleType(object):
"""A AMQP type that corresponds to a simple scalar C value of a
certain width."""
def __init__(self, bits):
self.bits = bits
self.ctype = "uint%d_t" % (bits,)
def decode(self, emitter, lvalue):
emitter.emit("if (!amqp_decode_%d(encoded, &offset, &%s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, lvalue))
def encode(self, emitter, value):
emitter.emit("if (!amqp_encode_%d(encoded, &offset, %s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, value))
def literal(self, value):
return value
class StrType(object):
"""The AMQP shortstr or longstr types."""
def __init__(self, lenbits):
self.lenbits = lenbits
self.ctype = "amqp_bytes_t"
def decode(self, emitter, lvalue):
emitter.emit("{")
emitter.emit(" uint%d_t len;" % (self.lenbits,))
emitter.emit(" if (!amqp_decode_%d(encoded, &offset, &len)" % (self.lenbits,))
emitter.emit(" || !amqp_decode_bytes(encoded, &offset, &%s, len))" % (lvalue,))
emitter.emit(" return AMQP_STATUS_BAD_AMQP_DATA;")
emitter.emit("}")
def encode(self, emitter, value):
emitter.emit("if (UINT%d_MAX < %s.len" % (self.lenbits, value))
emitter.emit(" || !amqp_encode_%d(encoded, &offset, (uint%d_t)%s.len)" %
(self.lenbits, self.lenbits, value))
emitter.emit(" || !amqp_encode_bytes(encoded, &offset, %s))" % (value,))
emitter.emit(" return AMQP_STATUS_BAD_AMQP_DATA;")
def literal(self, value):
if value != '':
raise NotImplementedError()
return "amqp_empty_bytes"
class BitType(object):
"""The AMQP bit type."""
def __init__(self):
self.ctype = "amqp_boolean_t"
def decode(self, emitter, lvalue):
emitter.decode_bit(lvalue)
def encode(self, emitter, value):
emitter.encode_bit(value)
def literal(self, value):
return {True: 1, False: 0}[value]
class TableType(object):
"""The AMQP table type."""
def __init__(self):
self.ctype = "amqp_table_t"
def decode(self, emitter, lvalue):
emitter.emit("{")
emitter.emit(" int res = amqp_decode_table(encoded, pool, &(%s), &offset);" % (lvalue,))
emitter.emit(" if (res < 0) return res;")
emitter.emit("}")
def encode(self, emitter, value):
emitter.emit("{")
emitter.emit(" int res = amqp_encode_table(encoded, &(%s), &offset);" % (value,))
emitter.emit(" if (res < 0) return res;")
emitter.emit("}")
def literal(self, value):
raise NotImplementedError()
types = {
'octet': SimpleType(8),
'short': SimpleType(16),
'long': SimpleType(32),
'longlong': SimpleType(64),
'shortstr': StrType(8),
'longstr': StrType(32),
'bit': BitType(),
'table': TableType(),
'timestamp': SimpleType(64),
}
def typeFor(spec, f):
"""Get a representation of the AMQP type of a field."""
return types[spec.resolveDomain(f.domain)]
def c_ize(s):
s = s.replace('-', '_')
s = s.replace(' ', '_')
return s
# When generating API functions corresponding to synchronous methods,
# we need some information that isn't in the protocol def: Some
# methods should not be exposed, indicated here by a False value.
# Some methods should be exposed but certain fields should not be
# exposed as parameters.
apiMethodInfo = {
"amqp_connection_start": False, # application code should not use this
"amqp_connection_secure": False, # application code should not use this
"amqp_connection_tune": False, # application code should not use this
"amqp_connection_open": False, # application code should not use this
"amqp_connection_close": False, # needs special handling
"amqp_channel_open": ["out_of_band"],
"amqp_channel_close": False, # needs special handling
"amqp_access_request": False, # huh?
"amqp_basic_get": False, # get-ok has content
}
# When generating API functions corresponding to synchronous methods,
# some fields should be suppressed everywhere. This dict names those
# fields, and the fixed values to use for them.
apiMethodsSuppressArgs = {"ticket": 0, "nowait": False}
AmqpMethod.defName = lambda m: cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name) + "_method")
AmqpMethod.fullName = lambda m: "amqp_%s_%s" % (c_ize(m.klass.name), c_ize(m.name))
AmqpMethod.structName = lambda m: m.fullName() + "_t"
AmqpClass.structName = lambda c: "amqp_" + c_ize(c.name) + "_properties_t"
def methodApiPrototype(m):
fn = m.fullName()
info = apiMethodInfo.get(fn, [])
docs = "/**\n * %s\n *\n" % (fn)
docs += " * @param [in] state connection state\n"
docs += " * @param [in] channel the channel to do the RPC on\n"
args = []
for f in m.arguments:
n = c_ize(f.name)
if n in apiMethodsSuppressArgs or n in info:
continue
args.append(", ")
args.append(typeFor(m.klass.spec, f).ctype)
args.append(" ")
args.append(n)
docs += " * @param [in] %s %s\n" % (n, n)
docs += " * @returns %s_ok_t\n" % (fn)
docs += " */\n"
return "%sAMQP_EXPORT\n%s_ok_t *\nAMQP_CALL %s(amqp_connection_state_t state, amqp_channel_t channel%s)" % (docs, fn, fn, ''.join(args))
AmqpMethod.apiPrototype = methodApiPrototype
def cConstantName(s):
return 'AMQP_' + '_'.join(re.split('[- ]', s.upper()))
def cFlagName(c, f):
return cConstantName(c.name + '_' + f.name) + '_FLAG'
def genErl(spec):
def fieldTempList(fields):
return '[' + ', '.join(['F' + str(f.index) for f in fields]) + ']'
def fieldMapList(fields):
return ', '.join([c_ize(f.name) + " = F" + str(f.index) for f in fields])
def genLookupMethodName(m):
print(' case %s: return "%s";' % (m.defName(), m.defName()))
def genDecodeMethodFields(m):
print(" case %s: {" % (m.defName(),))
print(" %s *m = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \
(m.structName(), m.structName(), m.structName()))
print(" if (m == NULL) { return AMQP_STATUS_NO_MEMORY; }")
emitter = BitDecoder(Emitter(" "))
for f in m.arguments:
typeFor(spec, f).decode(emitter, "m->"+c_ize(f.name))
print(" *decoded = m;")
print(" return 0;")
print(" }")
def genDecodeProperties(c):
print(" case %d: {" % (c.index,))
print(" %s *p = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \
(c.structName(), c.structName(), c.structName()))
print(" if (p == NULL) { return AMQP_STATUS_NO_MEMORY; }")
print(" p->_flags = flags;")
emitter = Emitter(" ")
for f in c.fields:
emitter.emit("if (flags & %s) {" % (cFlagName(c, f),))
typeFor(spec, f).decode(emitter, "p->"+c_ize(f.name))
emitter.emit("}")
print(" *decoded = p;")
print(" return 0;")
print(" }")
def genEncodeMethodFields(m):
print(" case %s: {" % (m.defName(),))
if m.arguments:
print(" %s *m = (%s *) decoded;" % (m.structName(), m.structName()))
emitter = BitEncoder(Emitter(" "))
for f in m.arguments:
typeFor(spec, f).encode(emitter, "m->"+c_ize(f.name))
emitter.flush()
print(" return (int)offset;")
print(" }")
def genEncodeProperties(c):
print(" case %d: {" % (c.index,))
if c.fields:
print(" %s *p = (%s *) decoded;" % (c.structName(), c.structName()))
emitter = Emitter(" ")
for f in c.fields:
emitter.emit(" if (flags & %s) {" % (cFlagName(c, f),))
typeFor(spec, f).encode(emitter, "p->"+c_ize(f.name))
emitter.emit("}")
print(" return (int)offset;")
print(" }")
methods = spec.allMethods()
print("""// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
/* Generated code. Do not edit. Edit and re-run codegen.py instead. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "amqp_private.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
""")
print("""
char const *amqp_constant_name(int constantNumber) {
switch (constantNumber) {""")
for (c,_,_) in spec.constants:
print(" case %s: return \"%s\";" % (cConstantName(c), cConstantName(c)))
print(""" default: return "(unknown)";
}
}""")
print("""
amqp_boolean_t amqp_constant_is_hard_error(int constantNumber) {
switch (constantNumber) {""")
for (c,_,cls) in spec.constants:
if cls == 'hard-error':
print(" case %s: return 1;" % (cConstantName(c),))
print(""" default: return 0;
}
}""")
print("""
char const *amqp_method_name(amqp_method_number_t methodNumber) {
switch (methodNumber) {""")
for m in methods: genLookupMethodName(m)
print(""" default: return NULL;
}
}""")
print("""
amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber) {
switch (methodNumber) {""")
for m in methods:
if m.hasContent:
print(' case %s: return 1;' % (m.defName()))
print(""" default: return 0;
}
}""")
print("""
int amqp_decode_method(amqp_method_number_t methodNumber,
amqp_pool_t *pool,
amqp_bytes_t encoded,
void **decoded)
{
size_t offset = 0;
uint8_t bit_buffer;
switch (methodNumber) {""")
for m in methods: genDecodeMethodFields(m)
print(""" default: return AMQP_STATUS_UNKNOWN_METHOD;
}
}""")
print("""
int amqp_decode_properties(uint16_t class_id,
amqp_pool_t *pool,
amqp_bytes_t encoded,
void **decoded)
{
size_t offset = 0;
amqp_flags_t flags = 0;
int flagword_index = 0;
uint16_t partial_flags;
do {
if (!amqp_decode_16(encoded, &offset, &partial_flags))
return AMQP_STATUS_BAD_AMQP_DATA;
flags |= (partial_flags << (flagword_index * 16));
flagword_index++;
} while (partial_flags & 1);
switch (class_id) {""")
for c in spec.allClasses(): genDecodeProperties(c)
print(""" default: return AMQP_STATUS_UNKNOWN_CLASS;
}
}""")
print("""
int amqp_encode_method(amqp_method_number_t methodNumber,
void *decoded,
amqp_bytes_t encoded)
{
size_t offset = 0;
uint8_t bit_buffer;
switch (methodNumber) {""")
for m in methods: genEncodeMethodFields(m)
print(""" default: return AMQP_STATUS_UNKNOWN_METHOD;
}
}""")
print("""
int amqp_encode_properties(uint16_t class_id,
void *decoded,
amqp_bytes_t encoded)
{
size_t offset = 0;
/* Cheat, and get the flags out generically, relying on the
similarity of structure between classes */
amqp_flags_t flags = * (amqp_flags_t *) decoded; /* cheating! */
{
/* We take a copy of flags to avoid destroying it, as it is used
in the autogenerated code below. */
amqp_flags_t remaining_flags = flags;
do {
amqp_flags_t remainder = remaining_flags >> 16;
uint16_t partial_flags = remaining_flags & 0xFFFE;
if (remainder != 0) { partial_flags |= 1; }
if (!amqp_encode_16(encoded, &offset, partial_flags))
return AMQP_STATUS_BAD_AMQP_DATA;
remaining_flags = remainder;
} while (remaining_flags != 0);
}
switch (class_id) {""")
for c in spec.allClasses(): genEncodeProperties(c)
print(""" default: return AMQP_STATUS_UNKNOWN_CLASS;
}
}""")
for m in methods:
if not m.isSynchronous:
continue
info = apiMethodInfo.get(m.fullName(), [])
if info is False:
continue
print("")
print(m.apiPrototype())
print("{")
print(" %s req;" % (m.structName(),))
for f in m.arguments:
n = c_ize(f.name)
val = apiMethodsSuppressArgs.get(n)
if val is None and n in info:
val = f.defaultvalue
if val is None:
val = n
else:
val = typeFor(spec, f).literal(val)
print(" req.%s = %s;" % (n, val))
reply = cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name)
+ "_ok_method")
print("""
return amqp_simple_rpc_decoded(state, channel, %s, %s, &req);
}
""" % (m.defName(), reply))
def genHrl(spec):
def fieldDeclList(fields):
if fields:
return ''.join([" %s %s; /**< %s */\n" % (typeFor(spec, f).ctype,
c_ize(f.name), f.name)
for f in fields])
else:
return " char dummy; /**< Dummy field to avoid empty struct */\n"
def propDeclList(fields):
return ''.join([" %s %s;\n" % (typeFor(spec, f).ctype, c_ize(f.name))
for f in fields
if spec.resolveDomain(f.domain) != 'bit'])
methods = spec.allMethods()
print("""// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
/* Generated code. Do not edit. Edit and re-run codegen.py instead. */
/** @file rabbitmq-c/framing.h */
#ifndef RABBITMQ_C_FRAMING_H
#define RABBITMQ_C_FRAMING_H
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/export.h>
AMQP_BEGIN_DECLS
""")
print("#define AMQP_PROTOCOL_VERSION_MAJOR %d /**< AMQP protocol version major */" % (spec.major))
print("#define AMQP_PROTOCOL_VERSION_MINOR %d /**< AMQP protocol version minor */" % (spec.minor))
print("#define AMQP_PROTOCOL_VERSION_REVISION %d /**< AMQP protocol version revision */" % (spec.revision))
print("#define AMQP_PROTOCOL_PORT %d /**< Default AMQP Port */" % (spec.port))
for (c,v,cls) in spec.constants:
print("#define %s %s /**< Constant: %s */" % (cConstantName(c), v, c))
print("")
print("""/* Function prototypes. */
/**
* Get constant name string from constant
*
* @param [in] constantNumber constant to get the name of
* @returns string describing the constant. String is managed by
* the library and should not be free()'d by the program
*/
AMQP_EXPORT
char const *
AMQP_CALL amqp_constant_name(int constantNumber);
/**
* Checks to see if a constant is a hard error
*
* A hard error occurs when something severe enough
* happens that the connection must be closed.
*
* @param [in] constantNumber the error constant
* @returns true if its a hard error, false otherwise
*/
AMQP_EXPORT
amqp_boolean_t
AMQP_CALL amqp_constant_is_hard_error(int constantNumber);
/**
* Get method name string from method number
*
* @param [in] methodNumber the method number
* @returns method name string. String is managed by the library
* and should not be freed()'d by the program
*/
AMQP_EXPORT
char const *
AMQP_CALL amqp_method_name(amqp_method_number_t methodNumber);
/**
* Check whether a method has content
*
* A method that has content will receive the method frame
* a properties frame, then 1 to N body frames
*
* @param [in] methodNumber the method number
* @returns true if method has content, false otherwise
*/
AMQP_EXPORT
amqp_boolean_t
AMQP_CALL amqp_method_has_content(amqp_method_number_t methodNumber);
/**
* Decodes a method from AMQP wireformat
*
* @param [in] methodNumber the method number for the decoded parameter
* @param [in] pool the memory pool to allocate the decoded method from
* @param [in] encoded the encoded byte string buffer
* @param [out] decoded pointer to the decoded method struct
* @returns 0 on success, an error code otherwise
*/
AMQP_EXPORT
int
AMQP_CALL amqp_decode_method(amqp_method_number_t methodNumber,
amqp_pool_t *pool,
amqp_bytes_t encoded,
void **decoded);
/**
* Decodes a header frame properties structure from AMQP wireformat
*
* @param [in] class_id the class id for the decoded parameter
* @param [in] pool the memory pool to allocate the decoded properties from
* @param [in] encoded the encoded byte string buffer
* @param [out] decoded pointer to the decoded properties struct
* @returns 0 on success, an error code otherwise
*/
AMQP_EXPORT
int
AMQP_CALL amqp_decode_properties(uint16_t class_id,
amqp_pool_t *pool,
amqp_bytes_t encoded,
void **decoded);
/**
* Encodes a method structure in AMQP wireformat
*
* @param [in] methodNumber the method number for the decoded parameter
* @param [in] decoded the method structure (e.g., amqp_connection_start_t)
* @param [in] encoded an allocated byte buffer for the encoded method
* structure to be written to. If the buffer isn't large enough
* to hold the encoded method, an error code will be returned.
* @returns 0 on success, an error code otherwise.
*/
AMQP_EXPORT
int
AMQP_CALL amqp_encode_method(amqp_method_number_t methodNumber,
void *decoded,
amqp_bytes_t encoded);
/**
* Encodes a properties structure in AMQP wireformat
*
* @param [in] class_id the class id for the decoded parameter
* @param [in] decoded the properties structure (e.g., amqp_basic_properties_t)
* @param [in] encoded an allocated byte buffer for the encoded properties to written to.
* If the buffer isn't large enough to hold the encoded method, an
* an error code will be returned
* @returns 0 on success, an error code otherwise.
*/
AMQP_EXPORT
int
AMQP_CALL amqp_encode_properties(uint16_t class_id,
void *decoded,
amqp_bytes_t encoded);
""")
print("/* Method field records. */\n")
for m in methods:
methodid = m.klass.index << 16 | m.index
print("#define %s ((amqp_method_number_t) 0x%.08X) /**< %s.%s method id @internal %d, %d; %d */" % \
(m.defName(),
methodid,
m.klass.name,
m.name,
m.klass.index,
m.index,
methodid))
print("/** %s.%s method fields */\ntypedef struct %s_ {\n%s} %s;\n" % \
(m.klass.name, m.name, m.structName(), fieldDeclList(m.arguments), m.structName()))
print("/* Class property records. */")
for c in spec.allClasses():
print("#define %s (0x%.04X) /**< %s class id @internal %d */" % \
(cConstantName(c.name + "_class"), c.index, c.name, c.index))
index = 0
for f in c.fields:
if index % 16 == 15:
index = index + 1
shortnum = index // 16
partialindex = 15 - (index % 16)
bitindex = shortnum * 16 + partialindex
print('#define %s (1 << %d) /**< %s.%s property flag */' % (cFlagName(c, f), bitindex, c.name, f.name))
index = index + 1
print("/** %s class properties */\ntypedef struct %s_ {\n amqp_flags_t _flags; /**< bit-mask of set fields */\n%s} %s;\n" % \
(c.name,
c.structName(),
fieldDeclList(c.fields),
c.structName()))
print("/* API functions for methods */\n")
for m in methods:
if m.isSynchronous and apiMethodInfo.get(m.fullName()) is not False:
print("%s;" % (m.apiPrototype(),))
print("""
AMQP_END_DECLS
#endif /* RABBITMQ_C_FRAMING_H */""")
def generateErl(specPath):
genErl(AmqpSpec(specPath))
def generateHrl(specPath):
genHrl(AmqpSpec(specPath))
if __name__ == "__main__":
do_main(generateHrl, generateErl)

View File

@@ -0,0 +1,9 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef AMQP_THREADS_H
#define AMQP_THREADS_H
#include <pthread.h>
#endif /* AMQP_THREADS_H */

View File

@@ -0,0 +1,37 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "threads.h"
#include <stdlib.h>
DWORD pthread_self(void) { return GetCurrentThreadId(); }
int pthread_mutex_init(pthread_mutex_t *mutex, void *attr) {
if (!mutex) {
return 1;
}
InitializeSRWLock(mutex);
return 0;
}
int pthread_mutex_lock(pthread_mutex_t *mutex) {
if (!mutex) {
return 1;
}
AcquireSRWLockExclusive(mutex);
return 0;
}
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
if (!mutex) {
return 1;
}
ReleaseSRWLockExclusive(mutex);
return 0;
}
int pthread_mutex_destroy(pthread_mutex_t *mutex) {
/* SRW's do not require destruction. */
return 0;
}

View File

@@ -0,0 +1,29 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifndef AMQP_THREAD_H
#define AMQP_THREAD_H
#if !defined(WINVER) || defined(__MINGW32__) || defined(__MINGW64__)
#ifdef WINVER
#undef WINVER
#endif
/* Windows Vista or newer */
#define WINVER 0x0600
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
typedef SRWLOCK pthread_mutex_t;
#define PTHREAD_MUTEX_INITIALIZER SRWLOCK_INIT;
DWORD pthread_self(void);
int pthread_mutex_init(pthread_mutex_t *, void *attr);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutex_destroy(pthread_mutex_t *);
#endif /* AMQP_THREAD_H */

20
regenerate_framing.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
# SPDX-License-Identifier: mit
set -e
RMQ_VERSION=3.8.19
DATA=${PWD}/codegen/rabbitmq-server-${RMQ_VERSION}/deps/rabbitmq_codegen/amqp-rabbitmq-0.9.1.json
export PYTHONPATH=${PWD}/codegen/rabbitmq-server-${RMQ_VERSION}/deps/rabbitmq_codegen
rm -rf codegen
mkdir codegen
wget -c https://github.com/rabbitmq/rabbitmq-server/releases/download/v${RMQ_VERSION}/rabbitmq-server-${RMQ_VERSION}.tar.xz -O - | tar -xJ -C codegen
python librabbitmq/codegen.py header ${DATA} include/rabbitmq-c/framing.h
python librabbitmq/codegen.py body ${DATA} librabbitmq/amqp_framing.c
clang-format -i include/rabbitmq-c/framing.h librabbitmq/amqp_framing.c

43
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,43 @@
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
# SPDX-License-Identifier: mit
include_directories(
${LIBRABBITMQ_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}/../librabbitmq/
${CMAKE_CURRENT_SOURCE_DIR}/../librabbitmq/)
add_definitions(-DHAVE_CONFIG_H)
add_definitions(-DAMQP_STATIC)
add_executable(test_parse_url test_parse_url.c)
target_link_libraries(test_parse_url rabbitmq-static)
add_test(parse_url test_parse_url)
add_executable(test_tables test_tables.c)
target_link_libraries(test_tables rabbitmq-static)
add_test(tables test_tables)
configure_file(test_tables.expected ${CMAKE_CURRENT_BINARY_DIR}/tests/test_tables.expected COPYONLY)
add_executable(test_status_enum
test_status_enum.c)
target_link_libraries(test_status_enum rabbitmq-static)
add_test(status_enum test_status_enum)
add_executable(test_basic
test_basic.c)
target_link_libraries(test_basic rabbitmq-static)
if (RUN_SYSTEM_TESTS)
if (NOT APPLE)
add_test(basic test_basic)
endif()
endif(RUN_SYSTEM_TESTS)
add_executable(test_sasl_mechanism test_sasl_mechanism.c)
target_link_libraries(test_sasl_mechanism rabbitmq-static)
add_test(sasl_mechanism test_sasl_mechanism)
add_executable(test_merge_capabilities test_merge_capabilities.c)
target_link_libraries(test_merge_capabilities rabbitmq-static)
add_test(merge_capabilities test_merge_capabilities)

194
tests/test_basic.c Normal file
View File

@@ -0,0 +1,194 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "amqp_time.h"
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/tcp_socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <WinSock2.h>
#else
#include <sys/time.h>
#endif
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h>
static const int fixed_channel_id = 1;
static const char test_queue_name[] = "test_queue";
amqp_connection_state_t setup_connection_and_channel(void) {
amqp_connection_state_t connection_state_ = amqp_new_connection();
amqp_socket_t *socket = amqp_tcp_socket_new(connection_state_);
assert(socket);
int rc = amqp_socket_open(socket, "localhost", AMQP_PROTOCOL_PORT);
assert(rc == AMQP_STATUS_OK);
amqp_rpc_reply_t rpc_reply = amqp_login(
connection_state_, "/", 1, AMQP_DEFAULT_FRAME_SIZE,
AMQP_DEFAULT_HEARTBEAT, AMQP_SASL_METHOD_PLAIN, "guest", "guest");
assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
amqp_channel_open_ok_t *res =
amqp_channel_open(connection_state_, fixed_channel_id);
assert(res != NULL);
return connection_state_;
}
void close_and_destroy_connection(amqp_connection_state_t connection_state_) {
amqp_rpc_reply_t rpc_reply =
amqp_connection_close(connection_state_, AMQP_REPLY_SUCCESS);
assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
int rc = amqp_destroy_connection(connection_state_);
assert(rc == AMQP_STATUS_OK);
}
void basic_publish(amqp_connection_state_t connectionState_,
const char *message_) {
amqp_bytes_t message_bytes = amqp_cstring_bytes(message_);
amqp_basic_properties_t properties;
properties._flags = 0;
properties._flags |= AMQP_BASIC_DELIVERY_MODE_FLAG;
properties.delivery_mode = AMQP_DELIVERY_NONPERSISTENT;
int retval = amqp_basic_publish(
connectionState_, fixed_channel_id, amqp_cstring_bytes(""),
amqp_cstring_bytes(test_queue_name),
/* mandatory=*/1,
/* immediate=*/0, /* RabbitMQ 3.x does not support the "immediate" flag
according to
https://www.rabbitmq.com/specification.html */
&properties, message_bytes);
assert(retval == 0);
}
void queue_declare(amqp_connection_state_t connection_state_,
const char *queue_name_) {
amqp_queue_declare_ok_t *res = amqp_queue_declare(
connection_state_, fixed_channel_id, amqp_cstring_bytes(queue_name_),
/*passive*/ 0,
/*durable*/ 0,
/*exclusive*/ 0,
/*auto_delete*/ 1, amqp_empty_table);
assert(res != NULL);
}
char *basic_get(amqp_connection_state_t connection_state_,
const char *queue_name_, uint64_t *out_body_size_) {
amqp_rpc_reply_t rpc_reply;
amqp_time_t deadline;
struct timeval timeout = {5, 0};
int time_rc = amqp_time_from_now(&deadline, &timeout);
assert(time_rc == AMQP_STATUS_OK);
do {
rpc_reply = amqp_basic_get(connection_state_, fixed_channel_id,
amqp_cstring_bytes(queue_name_), /*no_ack*/ 1);
} while (rpc_reply.reply_type == AMQP_RESPONSE_NORMAL &&
rpc_reply.reply.id == AMQP_BASIC_GET_EMPTY_METHOD &&
amqp_time_has_past(deadline) == AMQP_STATUS_OK);
assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
assert(rpc_reply.reply.id == AMQP_BASIC_GET_OK_METHOD);
amqp_message_t message;
rpc_reply =
amqp_read_message(connection_state_, fixed_channel_id, &message, 0);
assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
char *body = malloc(message.body.len);
if (body == NULL) {
return NULL;
}
memcpy(body, message.body.bytes, message.body.len);
*out_body_size_ = message.body.len;
amqp_destroy_message(&message);
return body;
}
void publish_and_basic_get_message(const char *msg_to_publish) {
amqp_connection_state_t connection_state = setup_connection_and_channel();
queue_declare(connection_state, test_queue_name);
basic_publish(connection_state, msg_to_publish);
uint64_t body_size;
char *msg = basic_get(connection_state, test_queue_name, &body_size);
assert(msg != NULL && "Test errored: memory allocation failed!");
assert(body_size == strlen(msg_to_publish));
assert(strncmp(msg_to_publish, msg, body_size) == 0);
free(msg);
close_and_destroy_connection(connection_state);
}
char *consume_message(amqp_connection_state_t connection_state_,
const char *queue_name_, uint64_t *out_body_size_) {
amqp_basic_consume_ok_t *result =
amqp_basic_consume(connection_state_, fixed_channel_id,
amqp_cstring_bytes(queue_name_), amqp_empty_bytes,
/*no_local*/ 0,
/*no_ack*/ 1,
/*exclusive*/ 0, amqp_empty_table);
assert(result != NULL);
amqp_envelope_t envelope;
struct timeval timeout = {5, 0};
amqp_rpc_reply_t rpc_reply =
amqp_consume_message(connection_state_, &envelope, &timeout, 0);
assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
*out_body_size_ = envelope.message.body.len;
char *body = malloc(*out_body_size_);
if (body == NULL) {
return NULL;
}
if (*out_body_size_) {
memcpy(body, envelope.message.body.bytes, *out_body_size_);
}
amqp_destroy_envelope(&envelope);
return body;
}
void publish_and_consume_message(const char *msg_to_publish) {
amqp_connection_state_t connection_state = setup_connection_and_channel();
queue_declare(connection_state, test_queue_name);
basic_publish(connection_state, msg_to_publish);
uint64_t body_size;
char *msg = consume_message(connection_state, test_queue_name, &body_size);
assert(msg != NULL && "Test errored: memory allocation failed!");
assert(body_size == strlen(msg_to_publish));
assert(strncmp(msg_to_publish, msg, body_size) == 0);
free(msg);
close_and_destroy_connection(connection_state);
}
int main(void) {
publish_and_basic_get_message("");
publish_and_basic_get_message("TEST");
publish_and_consume_message("");
publish_and_consume_message("TEST");
return 0;
}

View File

@@ -0,0 +1,182 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "amqp_socket.h"
#include "amqp_table.h"
#include <stdio.h>
#include <stdlib.h>
static int compare_bytes(amqp_bytes_t l, amqp_bytes_t r);
static int compare_amqp_table_entry(amqp_table_entry_t result,
amqp_table_entry_t expect);
static int compare_field_value(amqp_field_value_t result,
amqp_field_value_t expect);
static int compare_amqp_table(amqp_table_t* result, amqp_table_t* expect);
static int compare_bytes(amqp_bytes_t l, amqp_bytes_t r) {
if (l.len == r.len &&
(l.bytes == r.bytes || 0 == memcmp(l.bytes, r.bytes, l.len))) {
return 1;
}
return 0;
}
static int compare_amqp_table_entry(amqp_table_entry_t result,
amqp_table_entry_t expect) {
if (!compare_bytes(result.key, expect.key)) {
return 0;
}
return compare_field_value(result.value, expect.value);
}
static int compare_field_value(amqp_field_value_t result,
amqp_field_value_t expect) {
if (result.kind != expect.kind) {
return 0;
}
switch (result.kind) {
case AMQP_FIELD_KIND_BOOLEAN:
return result.value.boolean == expect.value.boolean;
case AMQP_FIELD_KIND_I8:
return result.value.i8 == expect.value.i8;
case AMQP_FIELD_KIND_U8:
return result.value.u8 == expect.value.u8;
case AMQP_FIELD_KIND_I16:
return result.value.i16 == expect.value.i16;
case AMQP_FIELD_KIND_U16:
return result.value.u16 == expect.value.u16;
case AMQP_FIELD_KIND_I32:
return result.value.i32 == expect.value.i32;
case AMQP_FIELD_KIND_U32:
return result.value.u32 == expect.value.u32;
case AMQP_FIELD_KIND_I64:
return result.value.i64 == expect.value.i64;
case AMQP_FIELD_KIND_U64:
case AMQP_FIELD_KIND_TIMESTAMP:
return result.value.u64 == expect.value.u64;
case AMQP_FIELD_KIND_F32:
return result.value.f32 == expect.value.f32;
case AMQP_FIELD_KIND_F64:
return result.value.f64 == expect.value.f64;
case AMQP_FIELD_KIND_DECIMAL:
return !memcmp(&result.value.decimal, &expect.value.decimal,
sizeof(expect.value.decimal));
case AMQP_FIELD_KIND_UTF8:
case AMQP_FIELD_KIND_BYTES:
return compare_bytes(result.value.bytes, expect.value.bytes);
case AMQP_FIELD_KIND_ARRAY: {
int i;
if (result.value.array.num_entries != expect.value.array.num_entries) {
return 0;
}
for (i = 0; i < result.value.array.num_entries; ++i) {
if (!compare_field_value(result.value.array.entries[i],
expect.value.array.entries[i])) {
return 0;
}
}
return 1;
}
case AMQP_FIELD_KIND_TABLE:
return compare_amqp_table(&result.value.table, &expect.value.table);
case AMQP_FIELD_KIND_VOID:
return 1;
}
return 1;
}
static int compare_amqp_table(amqp_table_t* result, amqp_table_t* expect) {
int i;
if (result->num_entries != expect->num_entries) {
return 0;
}
for (i = 0; i < expect->num_entries; ++i) {
if (!compare_amqp_table_entry(expect->entries[i], result->entries[i])) {
return 0;
}
}
return 1;
}
static void test_merge_capabilities(amqp_table_t* base, amqp_table_t* add,
amqp_table_t* expect) {
amqp_pool_t pool;
amqp_table_t result;
int res;
init_amqp_pool(&pool, 4096);
res = amqp_merge_capabilities(base, add, &result, &pool);
if (AMQP_STATUS_OK != res) {
fprintf(stderr, "amqp_merge_capabilities returned !ok: %d\n", res);
abort();
}
if (!compare_amqp_table(&result, expect)) {
fprintf(stderr, "amqp_merge_capabilities incorrect result.\n");
abort();
}
empty_amqp_pool(&pool);
return;
}
int main(void) {
{
amqp_table_t sub_base;
amqp_table_t sub_add;
amqp_table_t sub_expect;
amqp_table_t base;
amqp_table_t add;
amqp_table_t expect;
amqp_table_entry_t sub_base_entries[1];
amqp_table_entry_t sub_add_entries[2];
amqp_table_entry_t sub_expect_entries[2];
amqp_table_entry_t base_entries[3];
amqp_table_entry_t add_entries[3];
amqp_table_entry_t expect_entries[4];
sub_base_entries[0] = amqp_table_construct_utf8_entry("foo", "bar");
sub_base.num_entries =
sizeof(sub_base_entries) / sizeof(amqp_table_entry_t);
sub_base.entries = sub_base_entries;
sub_add_entries[0] = amqp_table_construct_utf8_entry("something", "else");
sub_add_entries[1] = amqp_table_construct_utf8_entry("foo", "baz");
sub_add.num_entries = sizeof(sub_add_entries) / sizeof(amqp_table_entry_t);
sub_add.entries = sub_add_entries;
sub_expect_entries[0] = amqp_table_construct_utf8_entry("foo", "baz");
sub_expect_entries[1] =
amqp_table_construct_utf8_entry("something", "else");
sub_expect.num_entries =
sizeof(sub_expect_entries) / sizeof(amqp_table_entry_t);
sub_expect.entries = sub_expect_entries;
base_entries[0] = amqp_table_construct_utf8_entry("product", "1.0");
base_entries[1] = amqp_table_construct_utf8_entry("nooverride", "yeah");
base_entries[2] = amqp_table_construct_table_entry("props", &sub_base);
base.num_entries = sizeof(base_entries) / sizeof(amqp_table_entry_t);
base.entries = base_entries;
add_entries[0] = amqp_table_construct_bool_entry("bool_entry", 1);
add_entries[1] = amqp_table_construct_utf8_entry("product", "2.0");
add_entries[2] = amqp_table_construct_table_entry("props", &sub_add);
add.num_entries = sizeof(add_entries) / sizeof(amqp_table_entry_t);
add.entries = add_entries;
expect_entries[0] = amqp_table_construct_utf8_entry("product", "2.0"),
expect_entries[1] = amqp_table_construct_utf8_entry("nooverride", "yeah"),
expect_entries[2] = amqp_table_construct_table_entry("props", &sub_expect);
expect_entries[3] = amqp_table_construct_bool_entry("bool_entry", 1);
expect.num_entries = sizeof(expect_entries) / sizeof(amqp_table_entry_t);
expect.entries = expect_entries;
test_merge_capabilities(&base, &add, &expect);
}
fprintf(stderr, "ok\n");
return 0;
}

188
tests/test_parse_url.c Normal file
View File

@@ -0,0 +1,188 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "config.h"
#ifdef _MSC_VER
/* MSVC complains about strdup being deprecated in favor of _strdup */
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <rabbitmq-c/amqp.h>
static void match_string(const char *what, const char *expect,
const char *got) {
if (strcmp(got, expect)) {
fprintf(stderr, "Expected %s '%s', got '%s'\n", what, expect, got);
abort();
}
}
static void match_int(const char *what, int expect, int got) {
if (got != expect) {
fprintf(stderr, "Expected %s '%d', got '%d'\n", what, expect, got);
abort();
}
}
static void parse_success(const char *url, const char *user,
const char *password, const char *host, int port,
const char *vhost) {
char *s = strdup(url);
struct amqp_connection_info ci;
int res;
res = amqp_parse_url(s, &ci);
if (res) {
fprintf(stderr, "Expected to successfully parse URL, but didn't: %s (%s)\n",
url, amqp_error_string2(res));
abort();
}
match_string("user", user, ci.user);
match_string("password", password, ci.password);
match_string("host", host, ci.host);
match_int("port", port, ci.port);
match_string("vhost", vhost, ci.vhost);
free(s);
}
static void parse_fail(const char *url) {
char *s = strdup(url);
struct amqp_connection_info ci;
amqp_default_connection_info(&ci);
if (amqp_parse_url(s, &ci) >= 0) {
fprintf(stderr, "Expected to fail parsing URL, but didn't: %s\n", url);
abort();
}
free(s);
}
int main(void) {
/* From the spec */
parse_success("amqp://user:pass@host:10000/vhost", "user", "pass", "host",
10000, "vhost");
parse_success("amqps://user:pass@host:10000/vhost", "user", "pass", "host",
10000, "vhost");
parse_success("amqp://user%61:%61pass@ho%61st:10000/v%2fhost", "usera",
"apass", "hoast", 10000, "v/host");
parse_success("amqps://user%61:%61pass@ho%61st:10000/v%2fhost", "usera",
"apass", "hoast", 10000, "v/host");
parse_success("amqp://", "guest", "guest", "localhost", 5672, "/");
parse_success("amqps://", "guest", "guest", "localhost", 5671, "/");
parse_success("amqp://:@/", "", "", "localhost", 5672, "");
parse_success("amqps://:@/", "", "", "localhost", 5671, "");
parse_success("amqp://user@", "user", "guest", "localhost", 5672, "/");
parse_success("amqps://user@", "user", "guest", "localhost", 5671, "/");
parse_success("amqp://user:pass@", "user", "pass", "localhost", 5672, "/");
parse_success("amqps://user:pass@", "user", "pass", "localhost", 5671, "/");
parse_success("amqp://host", "guest", "guest", "host", 5672, "/");
parse_success("amqps://host", "guest", "guest", "host", 5671, "/");
parse_success("amqp://:10000", "guest", "guest", "localhost", 10000, "/");
parse_success("amqps://:10000", "guest", "guest", "localhost", 10000, "/");
parse_success("amqp:///vhost", "guest", "guest", "localhost", 5672, "vhost");
parse_success("amqps:///vhost", "guest", "guest", "localhost", 5671, "vhost");
parse_success("amqp://host/", "guest", "guest", "host", 5672, "");
parse_success("amqps://host/", "guest", "guest", "host", 5671, "");
parse_success("amqp://host/%2f", "guest", "guest", "host", 5672, "/");
parse_success("amqps://host/%2f", "guest", "guest", "host", 5671, "/");
parse_success("amqp://[::1]", "guest", "guest", "::1", 5672, "/");
parse_success("amqps://[::1]", "guest", "guest", "::1", 5671, "/");
/* Various other success cases */
parse_success("amqp://host:100", "guest", "guest", "host", 100, "/");
parse_success("amqps://host:100", "guest", "guest", "host", 100, "/");
parse_success("amqp://[::1]:100", "guest", "guest", "::1", 100, "/");
parse_success("amqps://[::1]:100", "guest", "guest", "::1", 100, "/");
parse_success("amqp://host/blah", "guest", "guest", "host", 5672, "blah");
parse_success("amqps://host/blah", "guest", "guest", "host", 5671, "blah");
parse_success("amqp://host:100/blah", "guest", "guest", "host", 100, "blah");
parse_success("amqps://host:100/blah", "guest", "guest", "host", 100, "blah");
parse_success("amqp://:100/blah", "guest", "guest", "localhost", 100, "blah");
parse_success("amqps://:100/blah", "guest", "guest", "localhost", 100,
"blah");
parse_success("amqp://[::1]/blah", "guest", "guest", "::1", 5672, "blah");
parse_success("amqps://[::1]/blah", "guest", "guest", "::1", 5671, "blah");
parse_success("amqp://[::1]:100/blah", "guest", "guest", "::1", 100, "blah");
parse_success("amqps://[::1]:100/blah", "guest", "guest", "::1", 100, "blah");
parse_success("amqp://user:pass@host", "user", "pass", "host", 5672, "/");
parse_success("amqps://user:pass@host", "user", "pass", "host", 5671, "/");
parse_success("amqp://user:pass@host:100", "user", "pass", "host", 100, "/");
parse_success("amqps://user:pass@host:100", "user", "pass", "host", 100, "/");
parse_success("amqp://user:pass@:100", "user", "pass", "localhost", 100, "/");
parse_success("amqps://user:pass@:100", "user", "pass", "localhost", 100,
"/");
parse_success("amqp://user:pass@[::1]", "user", "pass", "::1", 5672, "/");
parse_success("amqps://user:pass@[::1]", "user", "pass", "::1", 5671, "/");
parse_success("amqp://user:pass@[::1]:100", "user", "pass", "::1", 100, "/");
parse_success("amqps://user:pass@[::1]:100", "user", "pass", "::1", 100, "/");
/* Various failure cases */
parse_fail("http://www.rabbitmq.com");
parse_fail("amqp://foo:bar:baz");
parse_fail("amqps://foo:bar:baz");
parse_fail("amqp://foo[::1]");
parse_fail("amqps://foo[::1]");
parse_fail("amqp://foo[::1]");
parse_fail("amqps://foo[::1]");
parse_fail("amqp://foo:[::1]");
parse_fail("amqps://foo:[::1]");
parse_fail("amqp://[::1]foo");
parse_fail("amqps://[::1]foo");
parse_fail("amqp://foo:1000xyz");
parse_fail("amqps://foo:1000xyz");
parse_fail("amqp://foo:1000000");
parse_fail("amqps://foo:1000000");
parse_fail("amqp://foo/bar/baz");
parse_fail("amqps://foo/bar/baz");
parse_fail("amqp://foo%1");
parse_fail("amqps://foo%1");
parse_fail("amqp://foo%1x");
parse_fail("amqps://foo%1x");
parse_fail("amqp://foo%xy");
parse_fail("amqps://foo%xy");
return 0;
}

View File

@@ -0,0 +1,38 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <amqp_socket.h>
static void parse_success(amqp_bytes_t mechanisms,
amqp_sasl_method_enum method) {
if (!sasl_mechanism_in_list(mechanisms, method)) {
fprintf(stderr, "Expected to find mechanism in list, but didn't: %s\n",
(char *)mechanisms.bytes);
abort();
}
}
static void parse_fail(amqp_bytes_t mechanisms, amqp_sasl_method_enum method) {
if (sasl_mechanism_in_list(mechanisms, method)) {
fprintf(stderr,
"Expected the mechanism not on the list, but it was present: %s\n",
(char *)mechanisms.bytes);
abort();
}
}
int main(void) {
parse_success(amqp_literal_bytes("DIGEST-MD5 CRAM-MD5 LOGIN PLAIN"),
AMQP_SASL_METHOD_PLAIN);
parse_fail(amqp_literal_bytes("DIGEST-MD5 CRAM-MD5 LOGIN PLAIN"),
AMQP_SASL_METHOD_EXTERNAL);
parse_success(amqp_literal_bytes("DIGEST-MD5 CRAM-MD5 EXTERNAL"),
AMQP_SASL_METHOD_EXTERNAL);
parse_fail(amqp_literal_bytes("DIGEST-MD5 CRAM-MD5 EXTERNAL"),
AMQP_SASL_METHOD_PLAIN);
return 0;
}

31
tests/test_status_enum.c Normal file
View File

@@ -0,0 +1,31 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <rabbitmq-c/amqp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void check_errorstrings(amqp_status_enum start, amqp_status_enum end) {
int i;
for (i = start; i > end; --i) {
const char* err = amqp_error_string2(i);
if (0 == strcmp(err, "(unknown error)")) {
printf("amqp_status_enum value %s%X", i < 0 ? "-" : "", (unsigned)i);
abort();
}
}
}
int main(void) {
check_errorstrings(AMQP_STATUS_OK, _AMQP_STATUS_NEXT_VALUE);
check_errorstrings(AMQP_STATUS_TCP_ERROR, _AMQP_STATUS_TCP_NEXT_VALUE);
check_errorstrings(AMQP_STATUS_SSL_ERROR, _AMQP_STATUS_SSL_NEXT_VALUE);
return 0;
}

434
tests/test_tables.c Normal file
View File

@@ -0,0 +1,434 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#ifdef _MSC_VER
#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <rabbitmq-c/amqp.h>
#include <math.h>
void die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
abort();
}
static void dump_indent(int indent, FILE *out) {
int i;
for (i = 0; i < indent; i++) {
fputc(' ', out);
}
}
static void dump_value(int indent, amqp_field_value_t v, FILE *out) {
int i;
dump_indent(indent, out);
fputc(v.kind, out);
switch (v.kind) {
case AMQP_FIELD_KIND_BOOLEAN:
fputs(v.value.boolean ? " true\n" : " false\n", out);
break;
case AMQP_FIELD_KIND_I8:
fprintf(out, " %" PRId8 "\n", v.value.i8);
break;
case AMQP_FIELD_KIND_U8:
fprintf(out, " %" PRIu8 "\n", v.value.u8);
break;
case AMQP_FIELD_KIND_I16:
fprintf(out, " %" PRId16 "\n", v.value.i16);
break;
case AMQP_FIELD_KIND_U16:
fprintf(out, " %" PRIu16 "\n", v.value.u16);
break;
case AMQP_FIELD_KIND_I32:
fprintf(out, " %" PRId32 "\n", v.value.i32);
break;
case AMQP_FIELD_KIND_U32:
fprintf(out, " %" PRIu32 "\n", v.value.u32);
break;
case AMQP_FIELD_KIND_I64:
fprintf(out, " %" PRId64 "\n", v.value.i64);
break;
case AMQP_FIELD_KIND_F32:
fprintf(out, " %g\n", (double)v.value.f32);
break;
case AMQP_FIELD_KIND_F64:
fprintf(out, " %g\n", v.value.f64);
break;
case AMQP_FIELD_KIND_DECIMAL:
fprintf(out, " %u:::%u\n", v.value.decimal.decimals,
v.value.decimal.value);
break;
case AMQP_FIELD_KIND_UTF8:
fprintf(out, " %.*s\n", (int)v.value.bytes.len,
(char *)v.value.bytes.bytes);
break;
case AMQP_FIELD_KIND_BYTES:
fputc(' ', out);
for (i = 0; i < (int)v.value.bytes.len; i++) {
fprintf(out, "%02x", ((char *)v.value.bytes.bytes)[i]);
}
fputc('\n', out);
break;
case AMQP_FIELD_KIND_ARRAY:
fputc('\n', out);
for (i = 0; i < v.value.array.num_entries; i++) {
dump_value(indent + 2, v.value.array.entries[i], out);
}
break;
case AMQP_FIELD_KIND_TIMESTAMP:
fprintf(out, " %" PRIu64 "\n", v.value.u64);
break;
case AMQP_FIELD_KIND_TABLE:
fputc('\n', out);
for (i = 0; i < v.value.table.num_entries; i++) {
dump_indent(indent + 2, out);
fprintf(out, "%.*s ->\n", (int)v.value.table.entries[i].key.len,
(char *)v.value.table.entries[i].key.bytes);
dump_value(indent + 4, v.value.table.entries[i].value, out);
}
break;
case AMQP_FIELD_KIND_VOID:
fputc('\n', out);
break;
default:
fprintf(out, "???\n");
break;
}
}
static void test_dump_value(FILE *out) {
amqp_table_entry_t entries[8];
amqp_table_t table;
amqp_field_value_t val;
entries[0].key = amqp_literal_bytes("zebra");
entries[0].value.kind = AMQP_FIELD_KIND_UTF8;
entries[0].value.value.bytes = amqp_literal_bytes("last");
entries[1].key = amqp_literal_bytes("aardvark");
entries[1].value.kind = AMQP_FIELD_KIND_UTF8;
entries[1].value.value.bytes = amqp_literal_bytes("first");
entries[2].key = amqp_literal_bytes("middle");
entries[2].value.kind = AMQP_FIELD_KIND_UTF8;
entries[2].value.value.bytes = amqp_literal_bytes("third");
entries[3].key = amqp_literal_bytes("number");
entries[3].value.kind = AMQP_FIELD_KIND_I32;
entries[3].value.value.i32 = 1234;
entries[4].key = amqp_literal_bytes("decimal");
entries[4].value.kind = AMQP_FIELD_KIND_DECIMAL;
entries[4].value.value.decimal.decimals = 2;
entries[4].value.value.decimal.value = 1234;
entries[5].key = amqp_literal_bytes("time");
entries[5].value.kind = AMQP_FIELD_KIND_TIMESTAMP;
entries[5].value.value.u64 = 1234123412341234;
entries[6].key = amqp_literal_bytes("beta");
entries[6].value.kind = AMQP_FIELD_KIND_UTF8;
entries[6].value.value.bytes = amqp_literal_bytes("second");
entries[7].key = amqp_literal_bytes("wombat");
entries[7].value.kind = AMQP_FIELD_KIND_UTF8;
entries[7].value.value.bytes = amqp_literal_bytes("fourth");
table.num_entries = 8;
table.entries = entries;
qsort(table.entries, table.num_entries, sizeof(amqp_table_entry_t),
&amqp_table_entry_cmp);
val.kind = AMQP_FIELD_KIND_TABLE;
val.value.table = table;
dump_value(0, val, out);
}
static uint8_t pre_encoded_table[] = {
0x00, 0x00, 0x00, 0xff, 0x07, 0x6c, 0x6f, 0x6e, 0x67, 0x73, 0x74, 0x72,
0x53, 0x00, 0x00, 0x00, 0x15, 0x48, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73,
0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69,
0x6e, 0x67, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x69, 0x6e, 0x74,
0x49, 0x00, 0x00, 0x30, 0x39, 0x07, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61,
0x6c, 0x44, 0x03, 0x00, 0x01, 0xe2, 0x40, 0x09, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x54, 0x00, 0x00, 0x63, 0xee, 0xa0, 0x53,
0xc1, 0x94, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x00, 0x00, 0x00,
0x1f, 0x03, 0x6f, 0x6e, 0x65, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x03, 0x74,
0x77, 0x6f, 0x53, 0x00, 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e,
0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x04, 0x62, 0x79, 0x74,
0x65, 0x62, 0xff, 0x04, 0x6c, 0x6f, 0x6e, 0x67, 0x6c, 0x00, 0x00, 0x00,
0x00, 0x49, 0x96, 0x02, 0xd2, 0x05, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x73,
0x02, 0x8f, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x74, 0x01, 0x06, 0x62, 0x69,
0x6e, 0x61, 0x72, 0x79, 0x78, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x20, 0x62,
0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x04, 0x76, 0x6f, 0x69, 0x64, 0x56, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79,
0x41, 0x00, 0x00, 0x00, 0x17, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x53, 0x00,
0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x66, 0x40,
0x49, 0x0f, 0xdb, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x64, 0x40,
0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18};
static void test_table_codec(FILE *out) {
amqp_pool_t pool;
int result;
amqp_table_entry_t inner_entries[2];
amqp_table_t inner_table;
amqp_field_value_t inner_values[2];
amqp_array_t inner_array;
amqp_table_entry_t entries[14];
amqp_table_t table;
inner_entries[0].key = amqp_literal_bytes("one");
inner_entries[0].value.kind = AMQP_FIELD_KIND_I32;
inner_entries[0].value.value.i32 = 54321;
inner_entries[1].key = amqp_literal_bytes("two");
inner_entries[1].value.kind = AMQP_FIELD_KIND_UTF8;
inner_entries[1].value.value.bytes = amqp_literal_bytes("A long string");
inner_table.num_entries = 2;
inner_table.entries = inner_entries;
inner_values[0].kind = AMQP_FIELD_KIND_I32;
inner_values[0].value.i32 = 54321;
inner_values[1].kind = AMQP_FIELD_KIND_UTF8;
inner_values[1].value.bytes = amqp_literal_bytes("A long string");
inner_array.num_entries = 2;
inner_array.entries = inner_values;
entries[0].key = amqp_literal_bytes("longstr");
entries[0].value.kind = AMQP_FIELD_KIND_UTF8;
entries[0].value.value.bytes = amqp_literal_bytes("Here is a long string");
entries[1].key = amqp_literal_bytes("signedint");
entries[1].value.kind = AMQP_FIELD_KIND_I32;
entries[1].value.value.i32 = 12345;
entries[2].key = amqp_literal_bytes("decimal");
entries[2].value.kind = AMQP_FIELD_KIND_DECIMAL;
entries[2].value.value.decimal.decimals = 3;
entries[2].value.value.decimal.value = 123456;
entries[3].key = amqp_literal_bytes("timestamp");
entries[3].value.kind = AMQP_FIELD_KIND_TIMESTAMP;
entries[3].value.value.u64 = 109876543209876;
entries[4].key = amqp_literal_bytes("table");
entries[4].value.kind = AMQP_FIELD_KIND_TABLE;
entries[4].value.value.table = inner_table;
entries[5].key = amqp_literal_bytes("byte");
entries[5].value.kind = AMQP_FIELD_KIND_I8;
entries[5].value.value.i8 = (int8_t)-1;
entries[6].key = amqp_literal_bytes("long");
entries[6].value.kind = AMQP_FIELD_KIND_I64;
entries[6].value.value.i64 = 1234567890;
entries[7].key = amqp_literal_bytes("short");
entries[7].value.kind = AMQP_FIELD_KIND_I16;
entries[7].value.value.i16 = 655;
entries[8].key = amqp_literal_bytes("bool");
entries[8].value.kind = AMQP_FIELD_KIND_BOOLEAN;
entries[8].value.value.boolean = 1;
entries[9].key = amqp_literal_bytes("binary");
entries[9].value.kind = AMQP_FIELD_KIND_BYTES;
entries[9].value.value.bytes = amqp_literal_bytes("a binary string");
entries[10].key = amqp_literal_bytes("void");
entries[10].value.kind = AMQP_FIELD_KIND_VOID;
entries[11].key = amqp_literal_bytes("array");
entries[11].value.kind = AMQP_FIELD_KIND_ARRAY;
entries[11].value.value.array = inner_array;
entries[12].key = amqp_literal_bytes("float");
entries[12].value.kind = AMQP_FIELD_KIND_F32;
entries[12].value.value.f32 = (float)M_PI;
entries[13].key = amqp_literal_bytes("double");
entries[13].value.kind = AMQP_FIELD_KIND_F64;
entries[13].value.value.f64 = M_PI;
table.num_entries = 14;
table.entries = entries;
fprintf(out, "AAAAAAAAAA\n");
{
amqp_field_value_t val;
val.kind = AMQP_FIELD_KIND_TABLE;
val.value.table = table;
dump_value(0, val, out);
}
init_amqp_pool(&pool, 4096);
{
amqp_table_t decoded;
size_t decoding_offset = 0;
amqp_bytes_t decoding_bytes;
decoding_bytes.len = sizeof(pre_encoded_table);
decoding_bytes.bytes = pre_encoded_table;
result =
amqp_decode_table(decoding_bytes, &pool, &decoded, &decoding_offset);
if (result < 0) {
die("Table decoding failed: %s", amqp_error_string2(result));
}
fprintf(out, "BBBBBBBBBB\n");
{
amqp_field_value_t val;
val.kind = AMQP_FIELD_KIND_TABLE;
val.value.table = decoded;
dump_value(0, val, out);
}
}
{
uint8_t encoding_buffer[4096];
amqp_bytes_t encoding_result;
size_t offset = 0;
memset(&encoding_buffer[0], 0, sizeof(encoding_buffer));
encoding_result.len = sizeof(encoding_buffer);
encoding_result.bytes = &encoding_buffer[0];
result = amqp_encode_table(encoding_result, &table, &offset);
if (result < 0) {
die("Table encoding failed: %s", amqp_error_string2(result));
}
if (offset != sizeof(pre_encoded_table))
die("Offset should be %ld, was %ld", (long)sizeof(pre_encoded_table),
(long)offset);
result = memcmp(pre_encoded_table, encoding_buffer, offset);
if (result != 0) {
die("Table encoding differed", result);
}
}
empty_amqp_pool(&pool);
}
#define CHUNK_SIZE 4096
static int compare_files(FILE *f1_in, FILE *f2_in) {
char f1_buf[CHUNK_SIZE];
char f2_buf[CHUNK_SIZE];
int res;
rewind(f1_in);
rewind(f2_in);
for (;;) {
size_t f1_got = fread(f1_buf, 1, CHUNK_SIZE, f1_in);
size_t f2_got = fread(f2_buf, 1, CHUNK_SIZE, f2_in);
res = memcmp(f1_buf, f2_buf, f1_got < f2_got ? f1_got : f2_got);
if (res) {
break;
}
if (f1_got < CHUNK_SIZE || f2_got < CHUNK_SIZE) {
if (f1_got != f2_got) {
res = (f1_got < f2_got ? -1 : 1);
}
break;
}
}
return res;
}
const char *expected_file_name = "tests/test_tables.expected";
int main(void) {
char *srcdir = getenv("srcdir");
FILE *out, *expected = NULL;
char *expected_path;
out = tmpfile();
if (out == NULL) {
die("failed to create temporary file: %s", strerror(errno));
}
test_table_codec(out);
fprintf(out, "----------\n");
test_dump_value(out);
if (srcdir == NULL) {
srcdir = ".";
}
expected_path = malloc(strlen(srcdir) + strlen(expected_file_name) + 2);
if (!expected_path) {
die("out of memory");
}
sprintf(expected_path, "%s/%s", srcdir, expected_file_name);
expected = fopen(expected_path, "r");
if (!expected) {
die("failed to open %s: %s", expected_path, strerror(errno));
}
if (compare_files(expected, out)) {
die("output file did not have expected contents");
}
fclose(expected);
free(expected_path);
fclose(out);
return 0;
}

View File

@@ -0,0 +1,90 @@
AAAAAAAAAA
F
longstr ->
S Here is a long string
signedint ->
I 12345
decimal ->
D 3:::123456
timestamp ->
T 109876543209876
table ->
F
one ->
I 54321
two ->
S A long string
byte ->
b -1
long ->
l 1234567890
short ->
s 655
bool ->
t true
binary ->
x 612062696e61727920737472696e67
void ->
V
array ->
A
I 54321
S A long string
float ->
f 3.14159
double ->
d 3.14159
BBBBBBBBBB
F
longstr ->
S Here is a long string
signedint ->
I 12345
decimal ->
D 3:::123456
timestamp ->
T 109876543209876
table ->
F
one ->
I 54321
two ->
S A long string
byte ->
b -1
long ->
l 1234567890
short ->
s 655
bool ->
t true
binary ->
x 612062696e61727920737472696e67
void ->
V
array ->
A
I 54321
S A long string
float ->
f 3.14159
double ->
d 3.14159
----------
F
aardvark ->
S first
beta ->
S second
decimal ->
D 2:::1234
middle ->
S third
number ->
I 1234
time ->
T 1234123412341234
wombat ->
S fourth
zebra ->
S last

79
tools/CMakeLists.txt Normal file
View File

@@ -0,0 +1,79 @@
# Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
# SPDX-License-Identifier: mit
if(WIN32)
set(PLATFORM_DIR win32)
else()
set(PLATFORM_DIR unix)
endif()
add_library(tools-common OBJECT)
target_sources(tools-common PRIVATE
common.h
common.c)
if(WIN32)
target_sources(tools-common PRIVATE
win32/compat.h
win32/compat.c)
endif()
if(ENABLE_SSL_SUPPORT)
target_compile_definitions(tools-common PRIVATE -DWITH_SSL=1)
endif()
target_include_directories(tools-common PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(tools-common PRIVATE rabbitmq::rabbitmq PUBLIC popt::popt)
add_executable(amqp-publish publish.c)
target_link_libraries(amqp-publish PRIVATE tools-common rabbitmq::rabbitmq)
add_executable(amqp-get get.c)
target_link_libraries(amqp-get PRIVATE tools-common rabbitmq::rabbitmq)
add_executable(amqp-consume consume.c ${PLATFORM_DIR}/process.h ${PLATFORM_DIR}/process.c)
target_include_directories(amqp-consume PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${PLATFORM_DIR})
target_link_libraries(amqp-consume PRIVATE tools-common rabbitmq::rabbitmq)
add_executable(amqp-declare-queue declare_queue.c)
target_link_libraries(amqp-declare-queue PRIVATE tools-common rabbitmq::rabbitmq)
add_executable(amqp-delete-queue delete_queue.c)
target_link_libraries(amqp-delete-queue PRIVATE tools-common rabbitmq::rabbitmq)
install(TARGETS amqp-publish amqp-get amqp-consume amqp-declare-queue amqp-delete-queue
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT rabbitmq-c-tools)
if (BUILD_TOOLS_DOCS)
set(DOCS_SRCS
doc/amqp-consume.xml
doc/amqp-declare-queue.xml
doc/amqp-delete-queue.xml
doc/amqp-get.xml
doc/amqp-publish.xml
doc/librabbitmq-tools.xml
)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc)
set(XMLTO_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/doc/man-date.ent)
STRING(TIMESTAMP BUILD_DATE "%Y-%m-%d" UTC)
add_custom_command(
OUTPUT ${XMLTO_DEPENDS}
COMMAND echo ${BUILD_DATE} > ${XMLTO_DEPENDS}
VERBATIM
)
set(XMLTO_COMMAND_ARGS --skip-validation --searchpath "${CMAKE_CURRENT_BINARY_DIR}/doc")
XMLTO(${DOCS_SRCS}
MODES man
ALL)
foreach(file ${XMLTO_FILES_man})
get_filename_component(fileExt ${file} EXT)
string( REGEX REPLACE "^[.]" "" fileExt ${fileExt} )
install(
FILES ${file}
DESTINATION ${CMAKE_INSTALL_MANDIR}/man${fileExt}
COMPONENT rabbitmq-c-tool-docs
)
endforeach()
endif()

487
tools/common.c Normal file
View File

@@ -0,0 +1,487 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include "common.h"
#ifdef WITH_SSL
#include <rabbitmq-c/ssl_socket.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <rabbitmq-c/tcp_socket.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef WINDOWS
#include "compat.h"
#endif
/* For when reading auth data from a file */
#define MAXAUTHTOKENLEN 128
#define USERNAMEPREFIX "username:"
#define PASSWORDPREFIX "password:"
void die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
void die_errno(int err, const char *fmt, ...) {
va_list ap;
if (err == 0) {
return;
}
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, ": %s\n", strerror(err));
exit(1);
}
void die_amqp_error(int err, const char *fmt, ...) {
va_list ap;
if (err >= 0) {
return;
}
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, ": %s\n", amqp_error_string2(err));
exit(1);
}
const char *amqp_server_exception_string(amqp_rpc_reply_t r) {
int res;
static char s[512];
switch (r.reply.id) {
case AMQP_CONNECTION_CLOSE_METHOD: {
amqp_connection_close_t *m = (amqp_connection_close_t *)r.reply.decoded;
res = snprintf(s, sizeof(s), "server connection error %d, message: %.*s",
m->reply_code, (int)m->reply_text.len,
(char *)m->reply_text.bytes);
break;
}
case AMQP_CHANNEL_CLOSE_METHOD: {
amqp_channel_close_t *m = (amqp_channel_close_t *)r.reply.decoded;
res = snprintf(s, sizeof(s), "server channel error %d, message: %.*s",
m->reply_code, (int)m->reply_text.len,
(char *)m->reply_text.bytes);
break;
}
default:
res = snprintf(s, sizeof(s), "unknown server error, method id 0x%08X",
r.reply.id);
break;
}
return res >= 0 ? s : NULL;
}
const char *amqp_rpc_reply_string(amqp_rpc_reply_t r) {
switch (r.reply_type) {
case AMQP_RESPONSE_NORMAL:
return "normal response";
case AMQP_RESPONSE_NONE:
return "missing RPC reply type";
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
return amqp_error_string2(r.library_error);
case AMQP_RESPONSE_SERVER_EXCEPTION:
return amqp_server_exception_string(r);
default:
abort();
}
}
void die_rpc(amqp_rpc_reply_t r, const char *fmt, ...) {
va_list ap;
if (r.reply_type == AMQP_RESPONSE_NORMAL) {
return;
}
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, ": %s\n", amqp_rpc_reply_string(r));
exit(1);
}
static char *amqp_url;
static char *amqp_server;
static int amqp_port = -1;
static char *amqp_vhost;
static char *amqp_username;
static char *amqp_password;
static int amqp_heartbeat = 0;
static char *amqp_authfile;
#ifdef WITH_SSL
static int amqp_ssl = 0;
static char *amqp_cacert = "/etc/ssl/certs/cacert.pem";
static char *amqp_key = NULL;
static char *amqp_cert = NULL;
#endif /* WITH_SSL */
const char *connect_options_title = "Connection options";
struct poptOption connect_options[] = {
{"url", 'u', POPT_ARG_STRING, &amqp_url, 0, "the AMQP URL to connect to",
"amqp://..."},
{"server", 's', POPT_ARG_STRING, &amqp_server, 0,
"the AMQP server to connect to", "hostname"},
{"port", 0, POPT_ARG_INT, &amqp_port, 0, "the port to connect on", "port"},
{"vhost", 0, POPT_ARG_STRING, &amqp_vhost, 0,
"the vhost to use when connecting", "vhost"},
{"username", 0, POPT_ARG_STRING, &amqp_username, 0,
"the username to login with", "username"},
{"password", 0, POPT_ARG_STRING, &amqp_password, 0,
"the password to login with", "password"},
{"heartbeat", 0, POPT_ARG_INT, &amqp_heartbeat, 0,
"heartbeat interval, set to 0 to disable", "heartbeat"},
{"authfile", 0, POPT_ARG_STRING, &amqp_authfile, 0,
"path to file containing username/password for authentication", "file"},
#ifdef WITH_SSL
{"ssl", 0, POPT_ARG_NONE, &amqp_ssl, 0, "connect over SSL/TLS", NULL},
{"cacert", 0, POPT_ARG_STRING, &amqp_cacert, 0,
"path to the CA certificate file", "cacert.pem"},
{"key", 0, POPT_ARG_STRING, &amqp_key, 0,
"path to the client private key file", "key.pem"},
{"cert", 0, POPT_ARG_STRING, &amqp_cert, 0,
"path to the client certificate file", "cert.pem"},
#endif /* WITH_SSL */
{NULL, '\0', 0, NULL, 0, NULL, NULL}};
void read_authfile(const char *path) {
size_t n;
FILE *fp = NULL;
char token[MAXAUTHTOKENLEN];
if ((amqp_username = malloc(MAXAUTHTOKENLEN)) == NULL ||
(amqp_password = malloc(MAXAUTHTOKENLEN)) == NULL) {
die("Out of memory");
} else if ((fp = fopen(path, "r")) == NULL) {
die("Could not read auth data file %s", path);
}
if (fgets(token, MAXAUTHTOKENLEN, fp) == NULL ||
strncmp(token, USERNAMEPREFIX, strlen(USERNAMEPREFIX))) {
die("Malformed auth file (missing username)");
}
strncpy(amqp_username, &token[strlen(USERNAMEPREFIX)], MAXAUTHTOKENLEN);
/* Missing newline means token was cut off */
n = strlen(amqp_username);
if (amqp_username[n - 1] != '\n') {
die("Username too long");
} else {
amqp_username[n - 1] = '\0';
}
if (fgets(token, MAXAUTHTOKENLEN, fp) == NULL ||
strncmp(token, PASSWORDPREFIX, strlen(PASSWORDPREFIX))) {
die("Malformed auth file (missing password)");
}
strncpy(amqp_password, &token[strlen(PASSWORDPREFIX)], MAXAUTHTOKENLEN);
/* Missing newline means token was cut off */
n = strlen(amqp_password);
if (amqp_password[n - 1] != '\n') {
die("Password too long");
} else {
amqp_password[n - 1] = '\0';
}
(void)fgetc(fp);
if (!feof(fp)) {
die("Malformed auth file (trailing data)");
}
fclose(fp);
}
static void init_connection_info(struct amqp_connection_info *ci) {
ci->user = NULL;
ci->password = NULL;
ci->host = NULL;
ci->port = -1;
ci->vhost = NULL;
ci->user = NULL;
amqp_default_connection_info(ci);
if (amqp_url)
die_amqp_error(amqp_parse_url(strdup(amqp_url), ci), "Parsing URL '%s'",
amqp_url);
if (amqp_server) {
char *colon;
if (amqp_url) {
die("--server and --url options cannot be used at the same time");
}
/* parse the server string into a hostname and a port */
colon = strchr(amqp_server, ':');
if (colon) {
char *port_end;
size_t host_len;
/* Deprecate specifying the port number with the
--server option, because it is not ipv6 friendly.
--url now allows connection options to be
specified concisely. */
fprintf(stderr,
"Specifying the port number with --server is deprecated\n");
host_len = colon - amqp_server;
ci->host = malloc(host_len + 1);
memcpy(ci->host, amqp_server, host_len);
ci->host[host_len] = 0;
if (amqp_port >= 0) {
die("both --server and --port options specify server port");
}
ci->port = strtol(colon + 1, &port_end, 10);
if (ci->port < 0 || ci->port > 65535 || port_end == colon + 1 ||
*port_end != 0)
die("bad server port number in '%s'", amqp_server);
} else {
ci->host = amqp_server;
ci->port = 5672;
#if WITH_SSL
if (amqp_ssl) {
ci->port = 5671;
}
#endif
}
}
#if WITH_SSL
if (amqp_ssl && !ci->ssl) {
if (amqp_url) {
die("the --ssl option specifies an SSL connection"
" but the --url option does not");
} else {
ci->ssl = 1;
}
}
#endif
if (amqp_port >= 0) {
if (amqp_url) {
die("--port and --url options cannot be used at the same time");
}
ci->port = amqp_port;
}
if (amqp_username) {
if (amqp_url) {
die("--username and --url options cannot be used at the same time");
} else if (amqp_authfile) {
die("--username and --authfile options cannot be used at the same time");
}
ci->user = amqp_username;
}
if (amqp_password) {
if (amqp_url) {
die("--password and --url options cannot be used at the same time");
} else if (amqp_authfile) {
die("--password and --authfile options cannot be used at the same time");
}
ci->password = amqp_password;
}
if (amqp_authfile) {
if (amqp_url) {
die("--authfile and --url options cannot be used at the same time");
}
read_authfile(amqp_authfile);
ci->user = amqp_username;
ci->password = amqp_password;
}
if (amqp_vhost) {
if (amqp_url) {
die("--vhost and --url options cannot be used at the same time");
}
ci->vhost = amqp_vhost;
}
if (amqp_heartbeat < 0) {
die("--heartbeat must be a positive value");
}
}
amqp_connection_state_t make_connection(void) {
int status;
amqp_socket_t *socket = NULL;
struct amqp_connection_info ci;
amqp_connection_state_t conn;
init_connection_info(&ci);
conn = amqp_new_connection();
if (ci.ssl) {
#ifdef WITH_SSL
socket = amqp_ssl_socket_new(conn);
if (!socket) {
die("creating SSL/TLS socket");
}
if (amqp_cacert) {
amqp_ssl_socket_set_cacert(socket, amqp_cacert);
}
if (amqp_key) {
amqp_ssl_socket_set_key(socket, amqp_cert, amqp_key);
}
#else
die("librabbitmq was not built with SSL/TLS support");
#endif
} else {
socket = amqp_tcp_socket_new(conn);
if (!socket) {
die("creating TCP socket (out of memory)");
}
}
status = amqp_socket_open(socket, ci.host, ci.port);
if (status) {
die_amqp_error(status, "opening socket to %s:%d", ci.host, ci.port);
}
die_rpc(amqp_login(conn, ci.vhost, 0, 131072, amqp_heartbeat,
AMQP_SASL_METHOD_PLAIN, ci.user, ci.password),
"logging in to AMQP server");
if (!amqp_channel_open(conn, 1)) {
die_rpc(amqp_get_rpc_reply(conn), "opening channel");
}
return conn;
}
void close_connection(amqp_connection_state_t conn) {
int res;
die_rpc(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), "closing channel");
die_rpc(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
"closing connection");
res = amqp_destroy_connection(conn);
die_amqp_error(res, "closing connection");
}
amqp_bytes_t read_all(int fd) {
size_t space = 4096;
amqp_bytes_t bytes;
bytes.bytes = malloc(space);
bytes.len = 0;
for (;;) {
ssize_t res = read(fd, (char *)bytes.bytes + bytes.len, space - bytes.len);
if (res == 0) {
break;
}
if (res < 0) {
if (errno == EINTR) {
continue;
}
die_errno(errno, "reading");
}
bytes.len += res;
if (bytes.len == space) {
space *= 2;
bytes.bytes = realloc(bytes.bytes, space);
}
}
return bytes;
}
void write_all(int fd, amqp_bytes_t data) {
while (data.len > 0) {
ssize_t res = write(fd, data.bytes, data.len);
if (res < 0) {
die_errno(errno, "write");
}
data.len -= res;
data.bytes = (char *)data.bytes + res;
}
}
void copy_body(amqp_connection_state_t conn, int fd) {
size_t body_remaining;
amqp_frame_t frame;
int res = amqp_simple_wait_frame(conn, &frame);
die_amqp_error(res, "waiting for header frame");
if (frame.frame_type != AMQP_FRAME_HEADER) {
die("expected header, got frame type 0x%X", frame.frame_type);
}
body_remaining = frame.payload.properties.body_size;
while (body_remaining) {
res = amqp_simple_wait_frame(conn, &frame);
die_amqp_error(res, "waiting for body frame");
if (frame.frame_type != AMQP_FRAME_BODY) {
die("expected body, got frame type 0x%X", frame.frame_type);
}
write_all(fd, frame.payload.body_fragment);
body_remaining -= frame.payload.body_fragment.len;
}
}
poptContext process_options(int argc, const char **argv,
struct poptOption *options, const char *help) {
int c;
poptContext opts = poptGetContext(NULL, argc, argv, options, 0);
poptSetOtherOptionHelp(opts, help);
while ((c = poptGetNextOpt(opts)) >= 0) {
/* no options require explicit handling */
}
if (c < -1) {
fprintf(stderr, "%s: %s\n", poptBadOption(opts, POPT_BADOPTION_NOALIAS),
poptStrerror(c));
poptPrintUsage(opts, stderr, 0);
exit(1);
}
return opts;
}
void process_all_options(int argc, const char **argv,
struct poptOption *options) {
poptContext opts = process_options(argc, argv, options, "[OPTIONS]...");
const char *opt = poptPeekArg(opts);
if (opt) {
fprintf(stderr, "unexpected operand: %s\n", opt);
poptPrintUsage(opts, stderr, 0);
exit(1);
}
poptFreeContext(opts);
}
amqp_bytes_t cstring_bytes(const char *str) {
return str ? amqp_cstring_bytes(str) : amqp_empty_bytes;
}

41
tools/common.h Normal file
View File

@@ -0,0 +1,41 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdint.h>
#include <popt.h>
#include <rabbitmq-c/amqp.h>
#include <rabbitmq-c/framing.h>
extern const char *amqp_server_exception_string(amqp_rpc_reply_t r);
extern const char *amqp_rpc_reply_string(amqp_rpc_reply_t r);
extern void die(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
extern void die_errno(int err, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
extern void die_amqp_error(int err, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
extern void die_rpc(amqp_rpc_reply_t r, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
extern const char *connect_options_title;
extern struct poptOption connect_options[];
extern amqp_connection_state_t make_connection(void);
extern void close_connection(amqp_connection_state_t conn);
extern amqp_bytes_t read_all(int fd);
extern void write_all(int fd, amqp_bytes_t data);
extern void copy_body(amqp_connection_state_t conn, int fd);
#define INCLUDE_OPTIONS(options) \
{ NULL, 0, POPT_ARG_INCLUDE_TABLE, options, 0, options##_title, NULL }
extern poptContext process_options(int argc, const char **argv,
struct poptOption *options,
const char *help);
extern void process_all_options(int argc, const char **argv,
struct poptOption *options);
extern amqp_bytes_t cstring_bytes(const char *str);

221
tools/consume.c Normal file
View File

@@ -0,0 +1,221 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "process.h"
#define MAX_LISTEN_KEYS 1024
#define LISTEN_KEYS_DELIMITER ","
/* Convert a amqp_bytes_t to an escaped string form for printing. We
use the same escaping conventions as rabbitmqctl. */
static char *stringify_bytes(amqp_bytes_t bytes) {
/* We will need up to 4 chars per byte, plus the terminating 0 */
char *res = malloc(bytes.len * 4 + 1);
if (res == NULL) {
return NULL;
}
uint8_t *data = bytes.bytes;
char *p = res;
size_t i;
for (i = 0; i < bytes.len; i++) {
if (data[i] >= 32 && data[i] != 127) {
*p++ = data[i];
} else {
*p++ = '\\';
*p++ = '0' + (data[i] >> 6);
*p++ = '0' + (data[i] >> 3 & 0x7);
*p++ = '0' + (data[i] & 0x7);
}
}
*p = 0;
return res;
}
static amqp_bytes_t setup_queue(amqp_connection_state_t conn, char *queue,
char *exchange, char *routing_key, int declare,
int exclusive) {
amqp_bytes_t queue_bytes = cstring_bytes(queue);
char *routing_key_rest;
char *routing_key_token;
char *routing_tmp;
int routing_key_count = 0;
/* if an exchange name wasn't provided, check that we don't have options that
* require it. */
if (!exchange && routing_key) {
fprintf(stderr,
"--routing-key option requires an exchange name to be provided "
"with --exchange\n");
exit(1);
}
if (!queue || exchange || declare || exclusive) {
/* Declare the queue as auto-delete. */
amqp_queue_declare_ok_t *res = amqp_queue_declare(
conn, 1, queue_bytes, 0, 0, exclusive, 1, amqp_empty_table);
if (!res) {
die_rpc(amqp_get_rpc_reply(conn), "queue.declare");
}
if (!queue) {
/* the server should have provided a queue name */
char *sq;
queue_bytes = amqp_bytes_malloc_dup(res->queue);
sq = stringify_bytes(queue_bytes);
if (sq == NULL) {
fprintf(stderr, "Memory allocation failed.\n");
exit(1);
}
fprintf(stderr, "Server provided queue name: %s\n", sq);
free(sq);
}
/* Bind to an exchange if requested */
if (exchange) {
amqp_bytes_t eb = amqp_cstring_bytes(exchange);
routing_tmp = strdup(routing_key);
if (NULL == routing_tmp) {
fprintf(stderr, "could not allocate memory to parse routing key\n");
exit(1);
}
for (routing_key_token =
strtok_r(routing_tmp, LISTEN_KEYS_DELIMITER, &routing_key_rest);
NULL != routing_key_token && routing_key_count < MAX_LISTEN_KEYS - 1;
routing_key_token =
strtok_r(NULL, LISTEN_KEYS_DELIMITER, &routing_key_rest)) {
if (!amqp_queue_bind(conn, 1, queue_bytes, eb,
cstring_bytes(routing_key_token),
amqp_empty_table)) {
die_rpc(amqp_get_rpc_reply(conn), "queue.bind");
}
}
free(routing_tmp);
}
}
return queue_bytes;
}
#define AMQP_CONSUME_MAX_PREFETCH_COUNT 65535
static void do_consume(amqp_connection_state_t conn, amqp_bytes_t queue,
int no_ack, int count, int prefetch_count,
const char *const *argv) {
int i;
/* If there is a limit, set the qos to match */
if (count > 0 && count <= AMQP_CONSUME_MAX_PREFETCH_COUNT &&
!amqp_basic_qos(conn, 1, 0, count, 0)) {
die_rpc(amqp_get_rpc_reply(conn), "basic.qos");
}
/* if there is a maximum number of messages to be received at a time, set the
* qos to match */
if (prefetch_count > 0 && prefetch_count <= AMQP_CONSUME_MAX_PREFETCH_COUNT) {
/* the maximum number of messages to be received at a time must be less
* than the global maximum number of messages. */
if (!(count > 0 && count <= AMQP_CONSUME_MAX_PREFETCH_COUNT &&
prefetch_count >= count)) {
if (!amqp_basic_qos(conn, 1, 0, prefetch_count, 0)) {
die_rpc(amqp_get_rpc_reply(conn), "basic.qos");
}
}
}
if (!amqp_basic_consume(conn, 1, queue, amqp_empty_bytes, 0, no_ack, 0,
amqp_empty_table)) {
die_rpc(amqp_get_rpc_reply(conn), "basic.consume");
}
for (i = 0; count < 0 || i < count; i++) {
amqp_frame_t frame;
struct pipeline pl;
uint64_t delivery_tag;
amqp_basic_deliver_t *deliver;
int res = amqp_simple_wait_frame(conn, &frame);
die_amqp_error(res, "waiting for header frame");
if (frame.frame_type != AMQP_FRAME_METHOD ||
frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) {
continue;
}
deliver = (amqp_basic_deliver_t *)frame.payload.method.decoded;
delivery_tag = deliver->delivery_tag;
pipeline(argv, &pl);
copy_body(conn, pl.infd);
if (finish_pipeline(&pl) && !no_ack)
die_amqp_error(amqp_basic_ack(conn, 1, delivery_tag, 0), "basic.ack");
amqp_maybe_release_buffers(conn);
}
}
int main(int argc, const char **argv) {
poptContext opts;
amqp_connection_state_t conn;
const char *const *cmd_argv;
static char *queue = NULL;
static char *exchange = NULL;
static char *routing_key = NULL;
static int declare = 0;
static int exclusive = 0;
static int no_ack = 0;
static int count = -1;
static int prefetch_count = -1;
amqp_bytes_t queue_bytes;
struct poptOption options[] = {
INCLUDE_OPTIONS(connect_options),
{"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue to consume from",
"queue"},
{"exchange", 'e', POPT_ARG_STRING, &exchange, 0,
"bind the queue to this exchange", "exchange"},
{"routing-key", 'r', POPT_ARG_STRING, &routing_key, 0,
"the routing key to bind with", "routing key"},
{"declare", 'd', POPT_ARG_NONE, &declare, 0,
"declare an exclusive queue (deprecated, use --exclusive instead)",
NULL},
{"exclusive", 'x', POPT_ARG_NONE, &exclusive, 0,
"declare the queue as exclusive", NULL},
{"no-ack", 'A', POPT_ARG_NONE, &no_ack, 0, "consume in no-ack mode",
NULL},
{"count", 'c', POPT_ARG_INT, &count, 0,
"stop consuming after this many messages are consumed", "limit"},
{"prefetch-count", 'p', POPT_ARG_INT, &prefetch_count, 0,
"receive only this many message at a time from the server", "limit"},
POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
opts = process_options(argc, argv, options, "[OPTIONS]... <command> <args>");
cmd_argv = poptGetArgs(opts);
if (!cmd_argv || !cmd_argv[0]) {
fprintf(stderr, "consuming command not specified\n");
poptPrintUsage(opts, stderr, 0);
goto error;
}
conn = make_connection();
queue_bytes =
setup_queue(conn, queue, exchange, routing_key, declare, exclusive);
do_consume(conn, queue_bytes, no_ack, count, prefetch_count, cmd_argv);
close_connection(conn);
return 0;
error:
poptFreeContext(opts);
return 1;
}

43
tools/declare_queue.c Normal file
View File

@@ -0,0 +1,43 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
int main(int argc, const char **argv) {
amqp_connection_state_t conn;
static char *queue = NULL;
static int durable = 0;
struct poptOption options[] = {
INCLUDE_OPTIONS(connect_options),
{"queue", 'q', POPT_ARG_STRING, &queue, 0,
"the queue name to declare, or the empty string", "queue"},
{"durable", 'd', POPT_ARG_VAL, &durable, 1, "declare a durable queue",
NULL},
POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
process_all_options(argc, argv, options);
if (queue == NULL) {
fprintf(stderr, "queue name not specified\n");
return 1;
}
conn = make_connection();
{
amqp_queue_declare_ok_t *reply = amqp_queue_declare(
conn, 1, cstring_bytes(queue), 0, durable, 0, 0, amqp_empty_table);
if (reply == NULL) {
die_rpc(amqp_get_rpc_reply(conn), "queue.declare");
}
printf("%.*s\n", (int)reply->queue.len, (char *)reply->queue.bytes);
}
close_connection(conn);
return 0;
}

45
tools/delete_queue.c Normal file
View File

@@ -0,0 +1,45 @@
// Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
// SPDX-License-Identifier: mit
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
int main(int argc, const char **argv) {
amqp_connection_state_t conn;
static char *queue = NULL;
static int if_unused = 0;
static int if_empty = 0;
struct poptOption options[] = {
INCLUDE_OPTIONS(connect_options),
{"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue name to delete",
"queue"},
{"if-unused", 'u', POPT_ARG_VAL, &if_unused, 1,
"do not delete unless queue is unused", NULL},
{"if-empty", 'e', POPT_ARG_VAL, &if_empty, 1,
"do not delete unless queue is empty", NULL},
POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
process_all_options(argc, argv, options);
if (queue == NULL || *queue == '\0') {
fprintf(stderr, "queue name not specified\n");
return 1;
}
conn = make_connection();
{
amqp_queue_delete_ok_t *reply =
amqp_queue_delete(conn, 1, cstring_bytes(queue), if_unused, if_empty);
if (reply == NULL) {
die_rpc(amqp_get_rpc_reply(conn), "queue.delete");
}
printf("%u\n", reply->message_count);
}
close_connection(conn);
return 0;
}

223
tools/doc/amqp-consume.xml Normal file
View File

@@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd"
[
<!ENTITY date SYSTEM "man-date.ent" >
]
>
<refentry lang="en">
<refentryinfo>
<productname>RabbitMQ C Client</productname>
<authorgroup>
<corpauthor>The RabbitMQ Team &lt;<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>&gt;</corpauthor>
</authorgroup>
<date>&date;</date>
</refentryinfo>
<refmeta>
<refentrytitle>amqp-consume</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">RabbitMQ C Client</refmiscinfo>
</refmeta>
<refnamediv>
<refname>amqp-consume</refname>
<refpurpose>Consume messages from a queue on an AMQP server</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>amqp-consume</command>
<arg choice="opt" rep="repeat">
<replaceable>OPTION</replaceable>
</arg>
<arg choice="req">
<replaceable>command</replaceable>
</arg>
<arg choice="opt" rep="repeat">
<replaceable>args</replaceable>
</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>amqp-consume</command> consumes messages from a
queue on an AMQP server. For each message that arrives, a
receiving command is run, with the message body supplied
to it on standard input.
</para>
<para>
<command>amqp-consume</command> can consume from an
existing queue, or it can create a new queue. It can
optionally bind the queue to an existing exchange.
</para>
<para>
By default, messages will be consumed with explicit
acknowledgements. A message will only be acknowledged if
the receiving command exits successfully (i.e. with an
exit code of zero). The AMQP <quote>no ack</quote> mode
(a.k.a. auto-ack mode) can be enable with the
<option>-A</option> option.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term><option>-q</option></term>
<term><option>--queue</option>=<replaceable class="parameter">queue name</replaceable></term>
<listitem>
<para>
The name of the queue to consume messages
from.
</para>
<para>
If the <option>--queue</option> option is
omitted, the AMQP server will assign a unique
name to the queue, and that server-assigned
name will be dixsplayed on stderr; this case
implies that an exclusive queue should be
declared.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-e</option></term>
<term><option>--exchange</option>=<replaceable class="parameter">exchange name</replaceable></term>
<listitem>
<para>
Specifies that an exclusive queue should
be declared, and bound to the given exchange.
The specified exchange should already exist
unless the <option>--exchange-type</option>
option is used to request the creation of an
exchange.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-r</option></term>
<term><option>--routing-key</option>=<replaceable class="parameter">routing key</replaceable></term>
<listitem>
<para>
The routing key for binding. If omitted, an
empty routing key is assumed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-d</option></term>
<term><option>--declare</option></term>
<listitem>
<para>
Forces an exclusive queue to be declared,
even when it otherwise would not be. That is,
when a queue name is specified with the
<option>--queue</option> option, but no
binding to an exchange is requested with the
<option>--exchange</option> option.
Note: this option is deprecated and may be
removed in a future version, use the
<option>--exclusive</option> option to
explicitly declare an exclusive queue.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-x</option></term>
<term><option>--exclusive</option></term>
<listitem>
<para>
Declared queues are non-exclusive by default,
this option forces declaration of exclusive
queues.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-A</option></term>
<term><option>--no-ack</option>=<replaceable class="parameter">routing key</replaceable></term>
<listitem>
<para>
Enable <quote>no ack</quote> mode: The AMQP
server will unconditionally acknowledge each
message that is delivered, regardless of
whether the target command exits successfully
or not.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-c</option></term>
<term><option>--count</option>=<replaceable class="parameter">limit</replaceable></term>
<listitem>
<para>
Stop consuming after the given number of
messages have been received.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-p</option></term>
<term><option>--prefetch-count</option>=<replaceable class="parameter">limit</replaceable></term>
<listitem>
<para>
Request the server to only send
<replaceable class="parameter">limit</replaceable>
messages at a time.
</para>
<para>
If any value was passed to <option>--count</option>,
the value passed to <option>--prefetch-count</option>
should be smaller than that, or otherwise it will be
ignored.
</para>
<para>
If <option>-A</option>/<option>--no-ack</option> is
passed, this option has no effect.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Examples</title>
<variablelist>
<varlistentry>
<term>Consume messages from an existing queue
<quote><systemitem
class="resource">myqueue</systemitem></quote>, and
output the message bodies on standard output via
<command>cat</command>:</term>
<listitem>
<screen><prompt>$ </prompt><userinput>amqp-consume -q myqueue cat</userinput></screen>
</listitem>
</varlistentry>
<varlistentry>
<term>Bind a new exclusive queue to an
exchange <quote><systemitem
class="resource">myexch</systemitem></quote>, and send
each message body to the script
<filename>myscript</filename>, automatically
acknowledging them on the server:</term>
<listitem>
<screen><prompt>$ </prompt><userinput>amqp-consume -A -e myexch ./myscript</userinput></screen>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See also</title>
<para>
<citerefentry><refentrytitle>librabbitmq-tools</refentrytitle><manvolnum>7</manvolnum></citerefentry>
describes connection-related options common to all the
RabbitMQ C Client tools.
</para>
</refsect1>
</refentry>

View File

@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd"
[
<!ENTITY date SYSTEM "man-date.ent" >
]
>
<refentry lang="en">
<refentryinfo>
<productname>RabbitMQ C Client</productname>
<authorgroup>
<corpauthor>The RabbitMQ Team &lt;<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>&gt;</corpauthor>
</authorgroup>
<date>&date;</date>
</refentryinfo>
<refmeta>
<refentrytitle>amqp-declare-queue</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">RabbitMQ C Client</refmiscinfo>
</refmeta>
<refnamediv>
<refname>amqp-declare-queue</refname>
<refpurpose>Declare (create or assert the existence of) a queue on an AMQP server</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>amqp-declare-queue</command>
<arg choice="opt" rep="repeat">
<replaceable>OPTION</replaceable>
</arg>
<arg choice="opt">-d</arg>
<arg choice="req">-q <replaceable>queue name</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>amqp-declare-queue</command> attempts to create a
queue on an AMQP server, and exits. If the empty-string is
supplied as the queue name, a fresh queue name is
generated by the server and returned. In all cases, if a
queue was successfully declared, the (raw binary) name of
the queue is printed to standard output, followed by a
newline.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term><option>-q</option></term>
<term><option>--queue</option>=<replaceable class="parameter">queue name</replaceable></term>
<listitem>
<para>
The name of the queue to declare. If the
empty string is supplied, a fresh queue name
is generated by the server.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-d</option></term>
<term><option>--durable</option></term>
<listitem>
<para>
Causes the queue to be declared with the
"durable" flag set. Durable queues survive
server restarts. By default, queues are declared
in "transient" mode.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Exit Status</title>
<para>
If the queue was successfully declared, the exit status is
0. If an error occurs, the exit status is 1.
</para>
</refsect1>
<refsect1>
<title>Examples</title>
<variablelist>
<varlistentry>
<term>Declare the durable queue <quote><systemitem
class="resource">myqueue</systemitem></quote>, and
display the name of the queue on standard output:</term>
<listitem>
<screen><prompt>$ </prompt><userinput>amqp-declare-queue -d -q myqueue</userinput>
myqueue</screen>
</listitem>
</varlistentry>
<varlistentry>
<term>Declare a fresh, server-named transient queue,
and display the name of the queue on standard output
(use <citerefentry><refentrytitle>amqp-delete-queue</refentrytitle>
<manvolnum>1</manvolnum></citerefentry> to delete
it from the server once you're done):</term>
<listitem>
<screen><prompt>$ </prompt><userinput>amqp-declare-queue -q ""</userinput>
amq.gen-BW/wvociA8g6LFpb1PlqOA==</screen>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See also</title>
<para>
<citerefentry><refentrytitle>librabbitmq-tools</refentrytitle><manvolnum>7</manvolnum></citerefentry>
describes connection-related options common to all the
RabbitMQ C Client tools.
</para>
</refsect1>
</refentry>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd"
[
<!ENTITY date SYSTEM "man-date.ent" >
]
>
<refentry lang="en">
<refentryinfo>
<productname>RabbitMQ C Client</productname>
<authorgroup>
<corpauthor>The RabbitMQ Team &lt;<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>&gt;</corpauthor>
</authorgroup>
<date>&date;</date>
</refentryinfo>
<refmeta>
<refentrytitle>amqp-delete-queue</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">RabbitMQ C Client</refmiscinfo>
</refmeta>
<refnamediv>
<refname>amqp-delete-queue</refname>
<refpurpose>Delete a queue from an AMQP server</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>amqp-delete-queue</command>
<arg choice="opt" rep="repeat">
<replaceable>OPTION</replaceable>
</arg>
<arg choice="req">-q <replaceable>queue name</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>amqp-delete-queue</command> deletes a queue from
an AMQP server, and exits after printing to standard
output the number of messages that were in the queue at
the time of its deletion.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term><option>-q</option></term>
<term><option>--queue</option>=<replaceable class="parameter">queue name</replaceable></term>
<listitem>
<para>
The name of the queue to delete.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Exit Status</title>
<para>
If the queue was successfully deleted, the exit status is
0. If an error occurs, the exit status is 1.
</para>
</refsect1>
<refsect1>
<title>Examples</title>
<variablelist>
<varlistentry>
<term>Delete the
queue <quote><systemitem class="resource">myqueue</systemitem></quote>
at a moment when it has 123 messages waiting on
it:</term>
<listitem>
<screen><prompt>$ </prompt><userinput>amqp-delete-queue -q myqueue</userinput>
123</screen>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See also</title>
<para>
<citerefentry><refentrytitle>librabbitmq-tools</refentrytitle><manvolnum>7</manvolnum></citerefentry>
describes connection-related options common to all the
RabbitMQ C Client tools.
</para>
</refsect1>
</refentry>

95
tools/doc/amqp-get.xml Normal file
View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd"
[
<!ENTITY date SYSTEM "man-date.ent" >
]
>
<refentry lang="en">
<refentryinfo>
<productname>RabbitMQ C Client</productname>
<authorgroup>
<corpauthor>The RabbitMQ Team &lt;<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>&gt;</corpauthor>
</authorgroup>
<date>&date;</date>
</refentryinfo>
<refmeta>
<refentrytitle>amqp-get</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">RabbitMQ C Client</refmiscinfo>
</refmeta>
<refnamediv>
<refname>amqp-get</refname>
<refpurpose>Get a message from a queue on an AMQP server</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>amqp-get</command>
<arg choice="opt" rep="repeat">
<replaceable>OPTION</replaceable>
</arg>
<arg choice="req">-q <replaceable>queue name</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>amqp-get</command> attempts to consume a single
message from a queue on an AMQP server, and exits. Unless
the queue was empty, the body of the resulting message is
sent to standard output.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term><option>-q</option></term>
<term><option>--queue</option>=<replaceable class="parameter">queue name</replaceable></term>
<listitem>
<para>
The name of the queue to consume messages
from.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Exit Status</title>
<para>
If the queue is not empty, and a message is successfully
retrieved, the exit status is 0. If an error occurs, the
exit status is 1. If the queue is found to be empty, the
exit status is 2.
</para>
</refsect1>
<refsect1>
<title>Examples</title>
<variablelist>
<varlistentry>
<term>Get a message from the queue <quote><systemitem
class="resource">myqueue</systemitem></quote>, and
display its body on standard output:</term>
<listitem>
<screen><prompt>$ </prompt><userinput>amqp-get -q myqueue</userinput></screen>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See also</title>
<para>
<citerefentry><refentrytitle>librabbitmq-tools</refentrytitle><manvolnum>7</manvolnum></citerefentry>
describes connection-related options common to all the
RabbitMQ C Client tools.
</para>
</refsect1>
</refentry>

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