init
This commit is contained in:
47
.clang-format
Normal file
47
.clang-format
Normal 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
18
.devcontainer/Dockerfile
Normal 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
|
||||||
26
.devcontainer/devcontainer.json
Normal file
26
.devcontainer/devcontainer.json
Normal 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"
|
||||||
|
}
|
||||||
59
.devcontainer/reinstall-cmake.sh
Normal file
59
.devcontainer/reinstall-cmake.sh
Normal 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
18
.gitattributes
vendored
Normal 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
76
.github/workflows/ci.yml
vendored
Normal 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
32
.github/workflows/cifuzz.yml
vendored
Normal 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
50
.github/workflows/codeql-analysis.yml
vendored
Normal 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
77
.gitignore
vendored
Normal 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
3
.gitmodules
vendored
Normal 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
2
AUTHORS
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Tony Garnock-Jones <tonygarnockjones@gmail.com>
|
||||||
|
The RabbitMQ team <info@rabbitmq.com>
|
||||||
276
CMakeLists.txt
Normal file
276
CMakeLists.txt
Normal 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
26
CONTRIBUTING.md
Normal 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
318
ChangeLog.md
Normal 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
27
LICENSE
Normal 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
180
README.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# RabbitMQ C AMQP client library
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[](https://coveralls.io/github/alanxz/rabbitmq-c?branch=master)
|
||||||
|
|
||||||
|
[](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#rabbitmq-c)
|
||||||
|
|
||||||
|
[](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
8
THANKS
Normal 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>
|
||||||
22
cmake/COPYING-CMAKE-SCRIPTS
Normal file
22
cmake/COPYING-CMAKE-SCRIPTS
Normal 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
48
cmake/FindPOPT.cmake
Normal 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
98
cmake/FindXMLTO.cmake
Normal 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 )
|
||||||
20
cmake/VersionFunctions.cmake
Normal file
20
cmake/VersionFunctions.cmake
Normal 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
12
cmake/config.h.in
Normal 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 */
|
||||||
17
cmake/rabbitmq-c-config.cmake.in
Normal file
17
cmake/rabbitmq-c-config.cmake.in
Normal 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
17
coverity/model.c
Normal 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
316
docs/Doxyfile.in
Normal 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
51
examples/CMakeLists.txt
Normal 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
63
examples/amqp_bind.c
Normal 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;
|
||||||
|
}
|
||||||
188
examples/amqp_confirm_select.c
Normal file
188
examples/amqp_confirm_select.c
Normal 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;
|
||||||
|
}
|
||||||
79
examples/amqp_connect_timeout.c
Normal file
79
examples/amqp_connect_timeout.c
Normal 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
185
examples/amqp_consumer.c
Normal 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;
|
||||||
|
}
|
||||||
62
examples/amqp_exchange_declare.c
Normal file
62
examples/amqp_exchange_declare.c
Normal 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
113
examples/amqp_listen.c
Normal 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
90
examples/amqp_listenq.c
Normal 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
117
examples/amqp_producer.c
Normal 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;
|
||||||
|
}
|
||||||
211
examples/amqp_rpc_sendstring_client.c
Normal file
211
examples/amqp_rpc_sendstring_client.c
Normal 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;
|
||||||
|
}
|
||||||
71
examples/amqp_sendstring.c
Normal file
71
examples/amqp_sendstring.c
Normal 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
102
examples/amqp_ssl_connect.c
Normal 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
63
examples/amqp_unbind.c
Normal 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;
|
||||||
|
}
|
||||||
20
examples/unix/platform_utils.c
Normal file
20
examples/unix/platform_utils.c
Normal 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
156
examples/utils.c
Normal 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
16
examples/utils.h
Normal 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
|
||||||
15
examples/win32/platform_utils.c
Normal file
15
examples/win32/platform_utils.c
Normal 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
20
fuzz/CMakeLists.txt
Normal 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
27
fuzz/README.md
Normal 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
152
fuzz/fuzz_server.c
Normal 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
32
fuzz/fuzz_table.c
Normal 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
24
fuzz/fuzz_url.c
Normal 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
BIN
fuzz/input/fuzz_server.raw
Normal file
Binary file not shown.
BIN
fuzz/input/fuzz_table.raw
Normal file
BIN
fuzz/input/fuzz_table.raw
Normal file
Binary file not shown.
BIN
fuzz/input/fuzz_url.raw
Normal file
BIN
fuzz/input/fuzz_url.raw
Normal file
Binary file not shown.
15
include/amqp.h
Normal file
15
include/amqp.h
Normal 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
16
include/amqp_framing.h
Normal 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
17
include/amqp_ssl_socket.h
Normal 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
15
include/amqp_tcp_socket.h
Normal 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
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
1150
include/rabbitmq-c/framing.h
Normal file
File diff suppressed because it is too large
Load Diff
305
include/rabbitmq-c/ssl_socket.h
Normal file
305
include/rabbitmq-c/ssl_socket.h
Normal 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 */
|
||||||
47
include/rabbitmq-c/tcp_socket.h
Normal file
47
include/rabbitmq-c/tcp_socket.h
Normal 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
13
librabbitmq.pc.in
Normal 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
190
librabbitmq/CMakeLists.txt
Normal 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
421
librabbitmq/amqp_api.c
Normal 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;
|
||||||
|
}
|
||||||
571
librabbitmq/amqp_connection.c
Normal file
571
librabbitmq/amqp_connection.c
Normal 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
282
librabbitmq/amqp_consumer.c
Normal 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
2919
librabbitmq/amqp_framing.c
Normal file
File diff suppressed because it is too large
Load Diff
210
librabbitmq/amqp_mem.c
Normal file
210
librabbitmq/amqp_mem.c
Normal 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
669
librabbitmq/amqp_openssl.c
Normal 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; }
|
||||||
155
librabbitmq/amqp_openssl_bio.c
Normal file
155
librabbitmq/amqp_openssl_bio.c
Normal 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
|
||||||
|
}
|
||||||
20
librabbitmq/amqp_openssl_bio.h
Normal file
20
librabbitmq/amqp_openssl_bio.h
Normal 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
337
librabbitmq/amqp_private.h
Normal 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
1469
librabbitmq/amqp_socket.c
Normal file
File diff suppressed because it is too large
Load Diff
166
librabbitmq/amqp_socket.h
Normal file
166
librabbitmq/amqp_socket.h
Normal 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
652
librabbitmq/amqp_table.c
Normal 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
56
librabbitmq/amqp_table.h
Normal 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 */
|
||||||
219
librabbitmq/amqp_tcp_socket.c
Normal file
219
librabbitmq/amqp_tcp_socket.c
Normal 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
236
librabbitmq/amqp_time.c
Normal 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
109
librabbitmq/amqp_time.h
Normal 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
188
librabbitmq/amqp_url.c
Normal 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
692
librabbitmq/codegen.py
Normal 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)
|
||||||
9
librabbitmq/unix/threads.h
Normal file
9
librabbitmq/unix/threads.h
Normal 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 */
|
||||||
37
librabbitmq/win32/threads.c
Normal file
37
librabbitmq/win32/threads.c
Normal 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;
|
||||||
|
}
|
||||||
29
librabbitmq/win32/threads.h
Normal file
29
librabbitmq/win32/threads.h
Normal 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
20
regenerate_framing.sh
Normal 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
43
tests/CMakeLists.txt
Normal 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
194
tests/test_basic.c
Normal 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;
|
||||||
|
}
|
||||||
182
tests/test_merge_capabilities.c
Normal file
182
tests/test_merge_capabilities.c
Normal 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
188
tests/test_parse_url.c
Normal 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;
|
||||||
|
}
|
||||||
38
tests/test_sasl_mechanism.c
Normal file
38
tests/test_sasl_mechanism.c
Normal 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
31
tests/test_status_enum.c
Normal 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
434
tests/test_tables.c
Normal 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;
|
||||||
|
}
|
||||||
90
tests/test_tables.expected
Normal file
90
tests/test_tables.expected
Normal 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
79
tools/CMakeLists.txt
Normal 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
487
tools/common.c
Normal 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
41
tools/common.h
Normal 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
221
tools/consume.c
Normal 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
43
tools/declare_queue.c
Normal 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
45
tools/delete_queue.c
Normal 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
223
tools/doc/amqp-consume.xml
Normal 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 <<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>></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>
|
||||||
122
tools/doc/amqp-declare-queue.xml
Normal file
122
tools/doc/amqp-declare-queue.xml
Normal 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 <<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>></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>
|
||||||
94
tools/doc/amqp-delete-queue.xml
Normal file
94
tools/doc/amqp-delete-queue.xml
Normal 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 <<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>></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
95
tools/doc/amqp-get.xml
Normal 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 <<ulink url="mailto:info@rabbitmq.com"><email>info@rabbitmq.com</email></ulink>></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
Reference in New Issue
Block a user