pax_global_header00006660000000000000000000000064147066307260014525gustar00rootroot0000000000000052 comment=ca49382bfc5bc165dfb7988b891bb010a939a786 AMQP-CPP-4.3.27/000077500000000000000000000000001470663072600130005ustar00rootroot00000000000000AMQP-CPP-4.3.27/.clang_complete000066400000000000000000000000531470663072600157530ustar00rootroot00000000000000-std=c++11 -Wno-pragma-once-outside-header AMQP-CPP-4.3.27/.github/000077500000000000000000000000001470663072600143405ustar00rootroot00000000000000AMQP-CPP-4.3.27/.github/ISSUE_TEMPLATE/000077500000000000000000000000001470663072600165235ustar00rootroot00000000000000AMQP-CPP-4.3.27/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000012431470663072600212150ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **Expected behavior and actual behavior** A clear and concise description of what you expected to happen. **Sample code** Provide a small piece of C++ code to demonstrate the issue. Watch out: do not use threads! AMQP-CPP objects are not thread-safe _by design_. If you construct channels or call methods from one thread, while the event loop is running in a different thread, unexpected things can and will happen. ```c++ int main() { // add your example program without using threads } ``` AMQP-CPP-4.3.27/.gitignore000066400000000000000000000002771470663072600147760ustar00rootroot00000000000000# Compiled Object files *.slo *.lo *.o # Compiled Dynamic libraries *.so *.so.* *.dylib # Compiled Static libraries *.lai *.la *.a *.a.* /build /.vscode .atom-build.cson .atom-dbg.cson /binAMQP-CPP-4.3.27/.travis.yml000066400000000000000000000046301470663072600151140ustar00rootroot00000000000000################ # Config ################ # C++ project language: cpp dist: trusty sudo: required group: edge ################ # Services ################ services: - docker ################ # Build matrix ################ matrix: include: ################ # Linux / GCC ################ - os: linux compiler: gcc env: - COMPILER_PACKAGE=g++-7 - C_COMPILER=gcc-7 - CXX_COMPILER=g++-7 - os: linux compiler: gcc env: - COMPILER_PACKAGE=g++-8 - C_COMPILER=gcc-8 - CXX_COMPILER=g++-8 - os: linux compiler: gcc env: - COMPILER_PACKAGE=g++-9 - C_COMPILER=gcc-9 - CXX_COMPILER=g++-9 - os: linux compiler: gcc env: - COMPILER_PACKAGE=g++-10 - C_COMPILER=gcc-10 - CXX_COMPILER=g++-10 ################ # Linux / Clang ################ - os: linux env: - COMPILER_PACKAGE=clang-7 - C_COMPILER=clang-7 - CXX_COMPILER=clang++-7 - os: linux env: - COMPILER_PACKAGE=clang-8 - C_COMPILER=clang-8 - CXX_COMPILER=clang++-8 - os: linux env: - COMPILER_PACKAGE=clang-9 - C_COMPILER=clang-9 - CXX_COMPILER=clang++-9 - os: linux env: - COMPILER_PACKAGE=clang-10 - C_COMPILER=clang-10 - CXX_COMPILER=clang++-10 before_install: # Show OS/compiler version (this may not be the same OS as the Docker container) - uname -a # Use an artful container - gives us access to latest compilers. - docker run -e "DEBIAN_FRONTEND=noninteractive" -d --name ubuntu-test-container -v $(pwd):/travis ubuntu:focal tail -f /dev/null - docker ps install: # Create our container - docker exec -t ubuntu-test-container bash -c "apt-get update -y && apt-get --no-install-recommends install -y software-properties-common cmake ninja-build libboost-all-dev libev-dev libuv1-dev ninja-build libssl-dev $COMPILER_PACKAGE && apt-get -y clean && rm -rf /var/lib/apt/lists/*" ################ # Build / Test ################ script: # Run the container that we created and build the code - docker exec -t ubuntu-test-container bash -c "cd /travis && export CC=/usr/bin/$C_COMPILER && export CXX=/usr/bin/$CXX_COMPILER && mkdir build.release && cd build.release && cmake ${CMAKE_OPTIONS} -DAMQP-CPP_BUILD_EXAMPLES=ON -DAMQP-CPP_LINUX_TCP=ON --config Release -GNinja .. && cmake --build . && cd .." AMQP-CPP-4.3.27/CMakeLists.txt000066400000000000000000000107531470663072600155460ustar00rootroot00000000000000# Builds AMQP-CPP # # Options: # # - AMQP-CPP_BUILD_SHARED (default OFF) # ON: Build shared lib # OFF: Build static lib # # - AMQP-CPP_LINUX_TCP (default OFF) # ON: Build posix handler implementation # OFF: Don't build posix handler implementation cmake_minimum_required(VERSION 3.4 FATAL_ERROR) # project name project(amqpcpp) set (VERSION_MAJOR 4) set (VERSION_MINOR 3) set (VERSION_PATCH 27) set (SO_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}) # build options option(AMQP-CPP_BUILD_SHARED "Build shared library. If off, build will be static." OFF) option(AMQP-CPP_LINUX_TCP "Build linux sockets implementation." OFF) option(AMQP-CPP_BUILD_EXAMPLES "Build amqpcpp examples" OFF) # pass version number to source files as macro add_compile_definitions(VERSION=${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) # ensure c++11 on all compilers set (CMAKE_CXX_STANDARD 17) # add source files # ------------------------------------------------------------------------------------------------------ # set include/ as include directory include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/include) # macro that adds a list of provided source files to a list called SRCS. # if variable SRCS does not yet exist, it is created. macro (add_sources) file (RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") foreach (_src ${ARGN}) if (_relPath) list (APPEND SRCS "${_relPath}/${_src}") else() list (APPEND SRCS "${_src}") endif() endforeach() if (_relPath) # propagate SRCS to parent directory set (SRCS ${SRCS} PARENT_SCOPE) endif() endmacro() # add source files #add_subdirectory(src) aux_source_directory(src src_MAIN) if(AMQP-CPP_LINUX_TCP) #add_subdirectory(src/linux_tcp) aux_source_directory(src/linux_tcp src_LINUX_TCP) endif() # potentially build the examples if(AMQP-CPP_BUILD_EXAMPLES) add_subdirectory(examples) endif() # settings for specific compilers # ------------------------------------------------------------------------------------------------------ # we have to prevent windows from defining the max macro. if (WIN32) add_definitions(-DNOMINMAX) endif() # build targets # ------------------------------------------------------------------------------------------------------ # set output directory set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/bin) if(AMQP-CPP_BUILD_SHARED) # create shared lib #add_library(${PROJECT_NAME} SHARED ${SRCS}) add_library(${PROJECT_NAME} SHARED ${src_MAIN} ${src_LINUX_TCP}) set_target_properties(${PROJECT_NAME} PROPERTIES # set shared lib version SOVERSION ${SO_VERSION} # export symbols for Visual Studio as a workaround WINDOWS_EXPORT_ALL_SYMBOLS ON ) else() # create static lib #add_library(${PROJECT_NAME} STATIC ${SRCS}) add_library(${PROJECT_NAME} STATIC ${src_MAIN} ${src_LINUX_TCP}) endif() if(WIN32) target_link_libraries(${PROJECT_NAME} PUBLIC ws2_32) endif() # install rules # ------------------------------------------------------------------------------------------------------ include(GNUInstallDirs) install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # copy header files install(DIRECTORY include/amqpcpp/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amqpcpp FILES_MATCHING PATTERN "*.h") install(FILES include/amqpcpp.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(EXPORT ${PROJECT_NAME}Config DESTINATION cmake) export(TARGETS ${PROJECT_NAME} FILE ${PROJECT_NAME}Config.cmake) set(DEST_DIR "${CMAKE_INSTALL_PREFIX}") set(PRIVATE_LIBS "-llibamqpcc") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/amqpcpp.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/amqpcpp.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/amqpcpp.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) # submodule support # ------------------------------------------------------------------------------------------------------ target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC $ $ ) if(AMQP-CPP_LINUX_TCP) target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS}) # Find OpenSSL and provide include dirs find_package(OpenSSL REQUIRED) target_include_directories(${PROJECT_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR}) endif() AMQP-CPP-4.3.27/LICENSE000066400000000000000000000260751470663072600140170ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. AMQP-CPP-4.3.27/Makefile000066400000000000000000000022171470663072600144420ustar00rootroot00000000000000PREFIX ?= /usr INCLUDE_DIR = ${PREFIX}/include LIBRARY_DIR = ${PREFIX}/lib export LIBRARY_NAME = amqpcpp export SONAME = 4.3 export VERSION = 4.3.27 all: $(MAKE) VERSION=${VERSION} -C src all pure: $(MAKE) VERSION=${VERSION} -C src pure release: $(MAKE) VERSION=${VERSION} -C src release static: $(MAKE) VERSION=${VERSION} -C src static shared: $(MAKE) VERSION=${VERSION} -C src shared clean: $(MAKE) -C src clean install: mkdir -p ${INCLUDE_DIR}/$(LIBRARY_NAME) mkdir -p ${INCLUDE_DIR}/$(LIBRARY_NAME)/linux_tcp mkdir -p ${LIBRARY_DIR} cp -f include/$(LIBRARY_NAME).h ${INCLUDE_DIR} cp -f include/amqpcpp/*.h ${INCLUDE_DIR}/$(LIBRARY_NAME) cp -f include/amqpcpp/linux_tcp/*.h ${INCLUDE_DIR}/$(LIBRARY_NAME)/linux_tcp -cp -f src/lib$(LIBRARY_NAME).so.$(VERSION) ${LIBRARY_DIR} -cp -f src/lib$(LIBRARY_NAME).a.$(VERSION) ${LIBRARY_DIR} ln -s -f lib$(LIBRARY_NAME).so.$(VERSION) $(LIBRARY_DIR)/lib$(LIBRARY_NAME).so.$(SONAME) ln -s -f lib$(LIBRARY_NAME).so.$(VERSION) $(LIBRARY_DIR)/lib$(LIBRARY_NAME).so ln -s -f lib$(LIBRARY_NAME).a.$(VERSION) $(LIBRARY_DIR)/lib$(LIBRARY_NAME).a AMQP-CPP-4.3.27/README.md000066400000000000000000001766701470663072600143000ustar00rootroot00000000000000AMQP-CPP ======== [![Build Status](https://travis-ci.org/CopernicaMarketingSoftware/AMQP-CPP.svg?branch=master)](https://travis-ci.org/CopernicaMarketingSoftware/AMQP-CPP) [![Build status](https://ci.appveyor.com/api/projects/status/heh4n7gjwgqcugfn/branch/master?svg=true)](https://ci.appveyor.com/project/copernica/amqp-cpp/branch/master) AMQP-CPP is a C++ library for communicating with a RabbitMQ message broker. The library can be used to parse incoming data from, and generate frames to, a RabbitMQ server. OVERVIEW ======== **Are you upgrading from AMQP-CPP 3 to AMQP-CPP 4?** [Please read the upgrade instructions](#upgrading) **Note for the reader:** This readme file has a peculiar structure. We start explaining the pure and hard core low level interface in which you have to take care of opening socket connections yourself. In reality, you probably want to use the simpler TCP interface that is being described [later on](#tcp-connections).
This library has a layered architecture, and allows you - if you like - to completely take care of the network layer. If you want to set up and manage the network connections yourself, the AMQP-CPP library will not make a connection to RabbitMQ by itself, nor will it create sockets and/or perform IO operations. As a user of this library, you create the socket connection and implement an interface defined by AMQP-CPP, passing it to the AMQP-CPP library to use for IO operations. Intercepting this network layer is optional. The AMQP-CPP library comes with a predefined TCP and TLS module that can be used if you trust the AMQP library to take care of the network (and optional TLS) handling. In that case, the AMQP-CPP library does all the system and library calls to set up network connections, and send/receive the (possibly encrypted) data. This layered architecture makes the library flexible and portable: it does not necessarily rely on operating system specific IO calls, and can be easily integrated into any kind of event loop. If you want to implement the AMQP protocol on top of some [other unusual communication layer](https://tools.ietf.org/html/rfc1149), this library can be used for that - but if you want to use it with regular TCP connections, setting it up is just as easy. AMQP-CPP is fully asynchronous and does not do any blocking (system) calls, so it can be used in high performance applications without the need for threads. The AMQP-CPP library uses C++17 features, so if you intend to use it, please make sure that your compiler is up-to-date and supports C++17. TABLE OF CONTENTS ================= * [Overview](#overview) * [About](#about) * [How to install](#how-to-install) * [How to use AMQP-CPP](#how-to-use-amqp-cpp) * [Parsing incoming data](#parsing-incoming-data) * [TCP connections](#tcp-connections) * [Secure connections](#secure-connections) * [Existing event loops](#existing-event-loops) * [Heartbeats](#heartbeats) * [Channels](#channels) * [Channel callbacks](#channel-callbacks) * [Channel errors](#channel-errors) * [Flags and Tables](#flags-and-tables) * [Publishing messages](#publishing-messages) * [Publisher confirms](#publishing-confirms) * [Consuming messages](#consuming-messages) * [Upgrading](#upgrading) * [Work in progress](#work-in-progress) ABOUT ===== [Back to Table of Contents](#table-of-contents) This library is created and maintained by Copernica (www.copernica.com), and is used inside the MailerQ (www.mailerq.com) and Yothalot (www.yothalot.com) applications. MailerQ is a tool for sending large volumes of email, using AMQP message queues, and Yothalot is a big data processing map/reduce framework. Do you appreciate our work and are you looking for high quality email solutions? Then check out our other commercial and open source solutions: * Copernica Marketing Suite (www.copernica.com) * MailerQ on-premise MTA (www.mailerq.com) * Responsive Email web service (www.responsiveemail.com) * SMTPeter cloud based SMTP server (www.smtpeter.com) * PHP-CPP bridge between PHP and C++ (www.php-cpp.com) * PHP-JS bridge between PHP and Javascript (www.php-js.com) * Yothalot big data processor (www.yothalot.com) HOW TO INSTALL ============== [Back to Table of Contents](#table-of-contents) Start by cloning the repository and navigating to the `AMQP-CPP` directory. ```bash git clone https://github.com/CopernicaMarketingSoftware/AMQP-CPP.git cd AMQP-CPP ``` There are two methods to compile AMQP-CPP: CMake and Make. CMake is platform portable and works on all systems, while the Makefile only works on Linux. The two methods create both a shared and a static version of the AMQP-CPP library. Building of a shared library is currently not supported on Windows. AMQP-CPP comes with an optional Linux-only TCP module that takes care of the network part required for the AMQP-CPP core library. If you use this module, you are required to link with `pthread` and `dl`. After building there are two relevant files to `#include` when you use the library. File | Include when? ---------------------|-------------------------------------------------------- amqpcpp.h | Always needed for the core features amqpcpp/linux_tcp.h | If using the Linux-only TCP module On Windows you are required to define `NOMINMAX` when compiling code that includes public AMQP-CPP header files. ## Using cmake The CMake file supports both building and installing. You can choose not to use the install functionality, and instead manually use the build output at `build/bin/`. Keep in mind that the TCP module is only supported for Linux. An example install method would be: ```bash mkdir build cd build cmake .. [-DAMQP-CPP_BUILD_SHARED=ON] [-DAMQP-CPP_LINUX_TCP=ON] cmake --build . --target install ``` Option | Default | Meaning -------------------------|---------|----------------------------------------------------------------------- AMQP-CPP_BUILD_SHARED | OFF | OFF for static lib, ON for shared lib. Shared is not supported on Windows. AMQP-CPP_LINUX_TCP | OFF | ON to build TCP module. TCP module is supported for Linux only. ## Using make Compiling and installing AMQP-CPP with make is as easy as running: ```bash make make install ``` This will install the full version of AMQP-CPP, including the system specific TCP module. To install without the TCP module (so that you can handle network connection yourself), run: ```bash make pure make install ``` ## Compiling a program When you compile an application that uses the AMQP-CPP library, remember to link with the library. For gcc and clang the linker flag is `-lamqpcpp`. If you use the TCP module, you also need to pass the `-lpthread` and `-ldl` linker flags. The TCP module uses a thread for running an asynchronous and non-blocking DNS hostname lookup, and it must be linked with the `dl` library to allow dynamic lookups for functions from the openssl library if a secure connection to RabbitMQ has to be set up. An example compilation command for an application using the TCP module: ```bash g++ -g -Wall -lamqcpp -lpthread -ldl my-amqp-cpp.c -o my-amqp-cpp ``` HOW TO USE AMQP-CPP =================== [Back to Table of Contents](#table-of-contents) AMQP-CPP operates in a network-agnostic fashion. It does not do IO by itself. An object must be provided that defines the IO operations. We have provided the `ConnectionHandler` base class for you to extend from and create your own object. This class defines a number of methods called by the library any time it wants to send data, or if it wants to notify you an error has occurred. ````c++ #include // You'll need to extend the ConnectionHandler class and make your own, like this class MyConnectionHandler : public AMQP::ConnectionHandler { /** * Method that is called by the AMQP library every time it has data * available that should be sent to RabbitMQ. * @param connection pointer to the main connection object * @param data memory buffer with the data that should be sent to RabbitMQ * @param size size of the buffer */ virtual void onData(AMQP::Connection *connection, const char *data, size_t size) { // @todo // Add your own implementation, for example by doing a call to the // send() system call. But be aware that the send() call may not // send all data at once, so you also need to take care of buffering // the bytes that could not immediately be sent, and try to send // them again when the socket becomes writable again } /** * Method that is called by the AMQP library when the login attempt * succeeded. After this method has been called, the connection is ready * to use. * @param connection The connection that can now be used */ virtual void onReady(AMQP::Connection *connection) { // @todo // add your own implementation, for example by creating a channel // instance, and start publishing or consuming } /** * Method that is called by the AMQP library when a fatal error occurs * on the connection, for example because data received from RabbitMQ * could not be recognized. * @param connection The connection on which the error occurred * @param message A human readable error message */ virtual void onError(AMQP::Connection *connection, const char *message) { // @todo // add your own implementation, for example by reporting the error // to the user of your program, log the error, and destruct the // connection object because it is no longer in a usable state } /** * Method that is called when the connection was closed. This is the * counter part of a call to Connection::close() and it confirms that the * AMQP connection was correctly closed. * * @param connection The connection that was closed and that is now unusable */ virtual void onClosed(AMQP::Connection *connection) { // @todo // add your own implementation, for example by closing down the // underlying TCP connection too } }; ```` Even though `ConnectionHandler` methods are not marked `noexcept` explicitly, you are not expected to throw from them, and the behaviour is undefined if you do so. After you've implemented the `ConnectionHandler` class the way you like, you can start using the library by creating a `Connection` object, and one or more `Channel` objects: ````c++ // create an instance of your own connection handler MyConnectionHandler myHandler; // create a AMQP connection object AMQP::Connection connection(&myHandler, AMQP::Login("guest","guest"), "/"); // and create a channel AMQP::Channel channel(&connection); // use the channel object to call the AMQP method you like channel.declareExchange("my-exchange", AMQP::fanout); channel.declareQueue("my-queue"); channel.bindQueue("my-exchange", "my-queue", "my-routing-key"); ```` A number of remarks about the example above. You may notice that we've created all objects on the stack. You are also free to create them on the heap with the C++ operator `new`. That works just as well, and in a real project you will likely want to keep your `Handler`, `Connection`, and `Channel` objects around for a longer time. But more importantly, you can see in the example above that we instantiated the `Channel` object directly after we made the `Connection` object, and we also started declaring exchanges and queues right away. However, under the hood, a handshake protocol is executed between the server and the client when the `Connection` object is first created. During this handshake procedure other operations are not permitted (like opening a channel or declaring a queue). It would be better practice to wait for the connection to be ready (implementing the `MyConnectionHandler::OnReady()` method) and creating the `Channel` object only then. However, this is not strictly necessary. Methods called during a handshake are cached by the AMQP library, and will be executed the moment the handshake is completed and the connection becomes ready for use. PARSING INCOMING DATA ===================== [Back to Table of Contents](#table-of-contents) The `ConnectionHandler` class has a method `onData()` that is called by the library every time that it wants to send data. The `onData` method is implemented by you, the user. For example, you might make system calls to `send()` or `write()` to send data to the RabbitMQ server. But what about data in the other direction? How does the library receive data from RabbitMQ? In this raw setup, the AMQP-CPP library does not do IO by itself, and so does not receive any data from a socket. You will have to create a socket that connects to the RabbitMQ server yourself. Inside your event loop, after checking the socket is readable, you should read out that socket (for example by using the `recv()` system call), and pass the received bytes to the AMQP-CPP library. This is done by calling the `parse()` method in the `Connection` object. The `Connection::parse()` method gets two parameters, a pointer to a buffer of data that you just read from the socket, and a parameter that holds the size of this buffer. The code snippet below comes from the Connection.h C++ header file. ````c++ /** * Parse data that was received from RabbitMQ * * Every time that data comes in from RabbitMQ, you should call this method to parse * the incoming data, and let it handle by the AMQP-CPP library. This method returns * the number of bytes that were processed. * * If not all bytes could be processed because it only contained a partial frame, * you should call this same method later on when more data is available. The * AMQP-CPP library does not do any buffering, so it is up to the caller to ensure * that the old data is also passed in that later call. * * @param buffer buffer to decode * @param size size of the buffer to decode * @return number of bytes that were processed */ size_t parse(char *buffer, size_t size) { return _implementation.parse(buffer, size); } ```` You should do all the book keeping for the buffer yourselves. If you for example call the `Connection::parse()` method with a buffer of 100 bytes, and the method returns that only 60 bytes were processed, you should later call the method again, with a buffer filled with the remaining 40 bytes. If the method returns 0, you should make a new call to `parse()` when more data is available, with a buffer that contains both the old data, and the new data. To optimize your calls to the `parse()` method, you _could_ use the `Connection::expected()` and `Connection::maxFrame()` methods. The `expected()` method returns the number of bytes that the library prefers to receive next. It is pointless to call the `parse()` method with a smaller buffer, and it is best to call the method with a buffer of exactly this size. The `maxFrame()` returns the max frame size for AMQP messages. If you read your messages into a reusable buffer, you could allocate this buffer up to this size, so that you never will have to reallocate. TCP CONNECTIONS =============== [Back to Table of Contents](#table-of-contents) Although the AMQP-CPP library gives you extreme flexibility by letting you setup your own network connections, the reality is that virtually all AMQP connections use the TCP protocol. To help you out, the library also comes with a TCP module that takes care of setting up the network connections, and sending and receiving the data. With the TCP module, you should not use the `AMQP::Connection` and `AMQP::Channel` classes that you saw above, but the alternative `AMQP::TcpConnection` and `AMQP::TcpChannel` classes instead. The `AMQP::ConnectionHandler` goes unused here as well; In a TCP connection, you create a class that extends from `AMQP::TcpHandler` instead. This `AMQP::TcpHandler` class contains a set of methods that you can override to intercept all sort of events that occur during the TCP and AMQP connection lifetime. Overriding these methods is mostly optional, because almost all have a default implementation. What does need to be implemented by the user is the `monitor()` method, as that is needed by the AMQP-CPP library to interact with the main event loop. ````c++ #include #include class MyTcpHandler : public AMQP::TcpHandler { /** * Method that is called by the AMQP library when a new connection * is associated with the handler. This is the first call to your handler * @param connection The connection that is attached to the handler */ virtual void onAttached(AMQP::TcpConnection *connection) override { // @todo // add your own implementation, for example initialize things // to handle the connection. } /** * Method that is called by the AMQP library when the TCP connection * has been established. After this method has been called, the library * still has take care of setting up the optional TLS layer and of * setting up the AMQP connection on top of the TCP layer., This method * is always paired with a later call to onLost(). * @param connection The connection that can now be used */ virtual void onConnected(AMQP::TcpConnection *connection) override { // @todo // add your own implementation (probably not needed) } /** * Method that is called when the secure TLS connection has been established. * This is only called for amqps:// connections. It allows you to inspect * whether the connection is secure enough for your liking (you can * for example check the server certificate). The AMQP protocol still has * to be started. * @param connection The connection that has been secured * @param ssl SSL structure from openssl library * @return bool True if connection can be used */ virtual bool onSecured(AMQP::TcpConnection *connection, const SSL *ssl) override { // @todo // add your own implementation, for example by reading out the // certificate and check if it is indeed yours return true; } /** * Method that is called by the AMQP library when the login attempt * succeeded. After this the connection is ready to use. * @param connection The connection that can now be used */ virtual void onReady(AMQP::TcpConnection *connection) override { // @todo // add your own implementation, for example by creating a channel // instance, and start publishing or consuming } /** * Method that is called by the AMQP library when a fatal error occurs * on the connection, for example because data received from RabbitMQ * could not be recognized, or the underlying connection is lost. This * call is normally followed by a call to onLost() (if the error occurred * after the TCP connection was established) and onDetached(). * @param connection The connection on which the error occurred * @param message A human readable error message */ virtual void onError(AMQP::TcpConnection *connection, const char *message) override { // @todo // add your own implementation, for example by reporting the error // to the user of your program and logging the error } /** * Method that is called when the AMQP protocol is ended. This is the * counter-part of a call to connection.close() to graceful shutdown * the connection. Note that the TCP connection is at this time still * active, and you will also receive calls to onLost() and onDetached() * @param connection The connection over which the AMQP protocol ended */ virtual void onClosed(AMQP::TcpConnection *connection) override { // @todo // add your own implementation (probably not necessary, but it could // be useful if you want to do some something immediately after the // amqp connection is over, but do not want to wait for the tcp // connection to shut down } /** * Method that is called when the TCP connection was closed or lost. * This method is always called if there was also a call to onConnected() * @param connection The connection that was closed and that is now unusable */ virtual void onLost(AMQP::TcpConnection *connection) override { // @todo // add your own implementation (probably not necessary) } /** * Final method that is called. This signals that no further calls to your * handler will be made about the connection. * @param connection The connection that can be destructed */ virtual void onDetached(AMQP::TcpConnection *connection) override { // @todo // add your own implementation, like cleanup resources or exit the application } /** * Method that is called by the AMQP-CPP library when it wants to interact * with the main event loop. The AMQP-CPP library is completely non-blocking, * and only make "write()" or "read()" system calls when it knows in advance * that these calls will not block. To register a filedescriptor in the * event loop, it calls this "monitor()" method with a filedescriptor and * flags telling whether the filedescriptor should be checked for readability * or writability. * * @param connection The connection that wants to interact with the event loop * @param fd The filedescriptor that should be checked * @param flags Bitwise or of AMQP::readable and/or AMQP::writable */ virtual void monitor(AMQP::TcpConnection *connection, int fd, int flags) override { // @todo // add your own implementation, for example by adding the file // descriptor to the main application event loop (like the select() or // poll() loop). When the event loop reports that the descriptor becomes // readable and/or writable, it is up to you to inform the AMQP-CPP // library that the filedescriptor is active by calling the // connection->process(fd, flags) method. } }; ```` You see that there are many methods in `TcpHandler` that you can implement. The most important one is `monitor()`. This method is used to integrate the AMQP filedescriptors in your application's event loop. For some popular event loops (libev, libuv, libevent), we have already added example handler objects (see the next section for that). All the other methods are optional to override. It often is a good idea to override the `onError()` method to log or report errors and `onDetached()` for cleaning up stuff. AMQP-CPP has its own buffers if you send instructions prematurely, but if you intend to send a lot of data over the connection, it also is a good idea to implement the `onReady()` method and delay your calls until the AMQP connection has been fully set up. Using the TCP module of the AMQP-CPP library is easier than using the raw `AMQP::Connection` and `AMQP::Channel` objects. You do not have to create the sockets and connections yourself, nor do you handle buffering the data. The example that we gave above looks slightly different if you make use of the TCP module: ````c++ // create an instance of your own tcp handler MyTcpHandler myHandler; // address of the server AMQP::Address address("amqp://guest:guest@localhost/vhost"); // create a AMQP connection object AMQP::TcpConnection connection(&myHandler, address); // and create a channel AMQP::TcpChannel channel(&connection); // use the channel object to call the AMQP method you like channel.declareExchange("my-exchange", AMQP::fanout); channel.declareQueue("my-queue"); channel.bindQueue("my-exchange", "my-queue", "my-routing-key"); ```` SECURE CONNECTIONS ================== [Back to Table of Contents](#table-of-contents) The TCP module of AMQP-CPP also supports setting up secure connections. If your RabbitMQ server accepts SSL connections, you can specify the address to your server using the amqps:// protocol: ````c++ // init the SSL library (this works for openssl 1.1, for openssl 1.0 use SSL_library_init()) OPENSSL_init_ssl(0, NULL); // address of the server (secure!) AMQP::Address address("amqps://guest:guest@localhost/vhost"); // create a AMQP connection object AMQP::TcpConnection connection(&myHandler, address); ```` There are two things to take care of if you want to create a secure connection: (1) you must link your application with the `-lssl` flag (or use `dlopen()`), and (2) you must initialize the openssl library by calling `OPENSSL_init_ssl()`. This initialization must take place before you connect to RabbitMQ. This is necessary because AMQP-CPP needs access to the openssl library to set up secure connections. It can only access this library if you have linked it to your application, or if you have loaded this library at runtime using `dlopen()`. Linking openssl is the normal thing to do. You just have to add the `-lssl` flag to your linker. If you however do not want to link your application with openssl, you can also load the openssl library at runtime, and pass in the pointer to the handle to AMQP-CPP: ````c++ // dynamically open the openssl library void *handle = dlopen("/path/to/openssl.so", RTLD_LAZY); // tell AMQP-CPP library where the handle to openssl can be found AMQP::openssl(handle); // @todo call functions to initialize openssl, and create the AMQP connection // (see exampe above) ```` By itself, AMQP-CPP does not check if the created TLS connection is sufficient secure. Whether the certificate is expired, self-signed, missing, or invalid: AMQP-CPP will simply permit the connection. If you want to be more strict (for example: if you want to verify the server's certificate), you must do this yourself by implementing the `onSecured()` method in your handler object: ````c++ #include #include class MyTcpHandler : public AMQP::TcpHandler { /** * Method that is called right after the TLS connection has been created. * In this method you can check the connection properties (like the certificate) * and return false if you find it not secure enough * @param connection the connection that has just completed the tls handshake * @param ssl SSL structure from the openssl library * @return bool true if connection is secure enough to start the AMQP protocol */ virtual bool onSecured(AMQP::TcpConnection *connection, const SSL *ssl) override { // @todo call functions from the openssl library to check the certificate, // like SSL_get_peer_certificate() or SSL_get_verify_result(). // For now we always allow the connection to proceed return true; } /** * All other methods (like onConnected(), onError(), etc) are left out of this * example, but would be here if this was an actual user space handler class. */ }; ```` The SSL pointer that is passed to the `onSecured()` method refers to the "SSL" structure from the openssl library. EXISTING EVENT LOOPS ==================== [Back to Table of Contents](#table-of-contents) Both the pure `AMQP::Connection` as well as the easier `AMQP::TcpConnection` class allow you to integrate AMQP-CPP in your own event loop. Whether you take care of running the event loop yourself (for example by using the `select()` system call), or if you use an existing library for it (like libevent, libev or libuv), you can implement the `monitor()` method to watch the file descriptors and hand over control back to AMQP-CPP when one of the sockets become active. For libev, libuv and libevent users, we have even implemented an example implementation, so that you do not even have to do this. Instead of implementing the `monitor()` method yourself, you can use the `AMQP::LibEvHandler`, `AMQP::LibUvHandler` or `AMQP:LibEventHandler` classes instead: ````c++ #include #include #include int main() { // access to the event loop auto *loop = EV_DEFAULT; // handler for libev (so we don't have to implement AMQP::TcpHandler!) AMQP::LibEvHandler handler(loop); // make a connection AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/")); // we need a channel too AMQP::TcpChannel channel(&connection); // create a temporary queue channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) { // report the name of the temporary queue std::cout << "declared queue " << name << std::endl; // now we can close the connection connection.close(); }); // run the loop ev_run(loop, 0); // done return 0; } ```` The `AMQP::LibEvHandler` and `AMQP::LibEventHandler` classes are extended `AMQP::TcpHandler` classes, with an implementation of the `monitor()` method that simply adds the filedescriptor to the event loop. If you use this class, it is recommended not to instantiate it directly (like we did in the example), but to create your own "MyHandler" class that extends from it, and in which you also implement the `onError()` method to report possible connection errors to your end users. Currently, we have example `TcpHandler` implementations for libev, libuv, libevent, and Boost's asio. For other event loops we do not yet have such examples. The quality of the libboostasio is however debatable: it was not developed and is not maintained by the original AMQP-CPP developers, and it has a couple of open issues. | TCP Handler Impl | Header File Location | Sample File Location | | ----------------------- | ---------------------- | ------------------------- | | Boost asio (io_service) | include/libboostasio.h | examples/libboostasio.cpp | | libev | include/libev.h | examples/libev.cpp | | libevent | include/libevent.h | examples/libevent.cpp | | libuv | include/libuv.h | examples/libuv.cpp | HEARTBEATS ========== [Back to Table of Contents](#table-of-contents) The AMQP protocol supports *heartbeats*. If this heartbeat feature is enabled, the client and the server negotiate a heartbeat interval during connection setup, and they agree to send at least *some kind of data* over the connection during every iteration of that interval. The normal data that is sent over the connection (like publishing or consuming messages) is normally sufficient to keep the connection alive, but if the client or server was idle during the negotiated interval time, a dummy heartbeat message must be sent instead. The default behavior of the AMQP-CPP library is to disable heartbeats. The proposed heartbeat interval of the server during connection setup (the server normally suggests an interval of 60 seconds) is vetoed by the AMQP-CPP library so no heartbeats are ever needed to be sent over the connection. This means that you can safely keep your AMQP connection idle for as long as you like, and/or run long lasting algorithms after you've consumed a message from RabbitMQ, without having to worry about the connection being idle for too long. You can however choose to enable these heartbeats. If you want to enable heartbeats, you should implement the `onNegotiate()` method inside your `ConnectionHandler` or `TcpHandler` class and have it return the interval that you find appropriate. ````c++ #include class MyTcpHandler : public AMQP::TcpHandler { /** * Method that is called when the server tries to negotiate a heartbeat * interval, and that is overridden to get rid of the default implementation * (which vetoes the suggested heartbeat interval), and accept the interval * instead. * @param connection The connection on which the error occurred * @param interval The suggested interval in seconds */ virtual uint16_t onNegotiate(AMQP::TcpConnection *connection, uint16_t interval) { // we accept the suggestion from the server, but if the interval is smaller // that one minute, we will use a one minute interval instead if (interval < 60) interval = 60; // @todo // set a timer in your event loop, and make sure that you call // connection->heartbeat() every _interval_ seconds if no other // instruction was sent in that period. // return the interval that we want to use return interval; } }; ```` If you enable heartbeats, it is your own responsibility to ensure that the ```connection->heartbeat()``` method is called at least once during this period, or that you call one of the other channel or connection methods to send data over the connection. Heartbeats are sent by the server too, RabbitMQ also ensures that _some data_ is sent over the connection from the server to the client during the heartbeat interval. It is also your responnsibility to shutdown the connection if you find out that the server stops sending data during this period. If you use the `AMQP::LibEvHandler` event loop implementation, heartbeats are enabled by default, and all these checks are automatically performed. CHANNELS ======== [Back to Table of Contents](#table-of-contents) In the above example we created a channel object. A channel is a sort of virtual connection, and it is possible to create many channels that all use the same connection. AMQP instructions are always sent over a channel, so before you can send the first command to the RabbitMQ server, you first need a channel object. The channel object has many methods to send instructions to the RabbitMQ server. It for example has methods to declare queues and exchanges, to bind and unbind them, and to publish and consume messages. You can best take a look at the channel.h C++ header file for a list of all available methods. Every method in it is well documented. All operations that you can perform on a channel are non-blocking. This means that it is not possible for a method (like `Channel::declareExchange()`) to immediately return `true` or `false`. Instead, almost every method of the `Channel` class returns an instance of the `Deferred` class. This `Deferred` object can be used to install handlers that will be called in case of success or failure. For example, if you call the `channel.declareExchange()` method, the AMQP-CPP library will send a message to the RabbitMQ message broker to ask it to declare the queue. However, because all operations in the library are asynchronous, the `declareExchange()` method can not return `true` or `false` to inform you whether the operation was successful or not. Only after a while, after the instruction has reached the RabbitMQ server, and the confirmation from the server has been sent back to the client, the library can report the result of the `declareExchange()` call. To prevent any blocking calls, the `channel.declareExchange()` method returns a `Deferred` result object, on which you can set callback functions that will be called when the operation succeeds or fails. ````c++ // create a channel (or use TcpChannel if you're using the Tcp module) Channel myChannel(&connection); // declare an exchange, and install callbacks for success and failure myChannel.declareExchange("my-exchange") .onSuccess([]() { // by now the exchange is created }) .onError([](const char *message) { // something went wrong creating the exchange }); ```` As you can see in the above example, we call the `declareExchange()` method, and treat its return value as an object, on which we immediately install a lambda callback function to handle success, and to handle failure. Installing the callback methods is optional. If you're not interested in the result of an operation, you do not have to install a callback for it. Next to the `onSuccess()` and `onError()` callbacks that can be installed, you can also install a `onFinalize()` method that gets called directly after the `onSuccess()` and `onError()` methods, and that can be used to set a callback that should run in either case: when the operation succeeds or when it fails. The signature for the `onError()` method is always the same: it gets one parameter with a human readable error message. The `onSuccess()` function has a different signature depending on the method that you call. Most `onSuccess()` functions (like the one we showed for the `declareExchange()` method) do not get any parameters at all. Some specific `onSuccess()` callbacks receive extra parameters with additional information. CHANNEL CALLBACKS ================= [Back to Table of Contents](#table-of-contents) As explained, most channel methods return a `Deferred` object on which you can install callbacks using the `Deferred::onError()` and `Deferred::onSuccess()` methods. The callbacks that you install on a `Deferred` object, only apply to one specific operation. If you want to install a generic error callback for the entire channel, you can do so by using the `Channel::onError()` method. Next to the `Channel::onError()` method, you can also install a callback to be notified when the channel is ready for sending the first instruction to RabbitMQ. ````c++ // create a channel (or use TcpChannel if you use the Tcp module) Channel myChannel(&connection); // install a generic channel-error handler that will be called for every // error that occurs on the channel myChannel.onError([](const char *message) { // report error std::cout << "channel error: " << message << std::endl; }); // install a generic callback that will be called when the channel is ready // for sending the first instruction myChannel.onReady([]() { // send the first instructions (like publishing messages) }); ```` In theory, you should wait for the `onReady()` callback to be called before you send any other instructions over the channel. In practice however, the AMQP library caches all instructions that were sent too early, so that you can use the channel object right after it was constructed. CHANNEL ERRORS ============== [Back to Table of Contents](#table-of-contents) If a channel ever sees an error, the entire channel is invalidated, including subsequent instructions that were already sent. This means that if you call multiple methods in a row, and the first method fails, all subsequent methods will not be executed: ````c++ Channel myChannel(&connection); myChannel.declareQueue("my-queue"); // If this method fails... myChannel.declareExchange("my-exchange"); // ...this method will not execute. ```` If the first `declareQueue()` call fails in the example above, the second `myChannel.declareExchange()` method will not be executed, even when this second instruction was already sent to the server. The second instruction will be ignored by the RabbitMQ server because the channel became invalid at the first instruction. You can overcome this by using multiple channels: ````c++ Channel channel1(&connection); Channel channel2(&connection); channel1.declareQueue("my-queue"); channel2.declareExchange("my-exchange"); ```` Now, if an error occurs with declaring the queue, it will not have consequences for the other call. But this comes at a small price: setting up the extra channel requires and extra instruction to be sent to the RabbitMQ server, so some extra bytes are sent over the network, and some additional resources in both the client application and the RabbitMQ server are used (although this is all very limited). If possible, it is best to make use of this feature. For example, if you have an important AMQP connection that you use for consuming messages, and at the same time you want to send another instruction to RabbitMQ (like declaring a temporary queue), it is best to set up a new channel for this 'declare' instruction. If the declare fails, it will not stop the consumer, because it was sent over a different channel. The AMQP-CPP library allows you to create channels on the stack. It is not a problem if a channel object gets destructed before the instruction was received by the RabbitMQ server: ````c++ void myDeclareMethod(AMQP::Connection *connection) { // create temporary channel to declare a queue AMQP::Channel channel(connection); // declare the queue (the channel object is destructed before the // instruction reaches the server, but the AMQP-CPP library can deal // with this) channel.declareQueue("my-new-queue"); } ```` FLAGS AND TABLES ================ [Back to Table of Contents](#table-of-contents) Let's take a closer look at one method in the `Channel` object to explain two other concepts of this AMQP-CPP library: flags and tables. The method that we will be looking at is the `Channel::declareQueue()` method - but we could've picked a different method too because flags and tables are used by many methods. ````c++ /** * Declare a queue * * If you do not supply a name, a name will be assigned by the server. * * The flags can be a combination of the following values: * * - durable queue survives a broker restart * - autodelete queue is automatically removed when all connected consumers are gone * - passive only check if the queue exist * - exclusive the queue only exists for this connection, and is automatically removed when connection is gone * * @param name name of the queue * @param flags combination of flags * @param arguments optional arguments */ DeferredQueue &declareQueue(const std::string &name, int flags, const Table &arguments); // This version is the most extensive DeferredQueue &declareQueue(const std::string &name, const Table &arguments); DeferredQueue &declareQueue(const std::string &name, int flags = 0); DeferredQueue &declareQueue(int flags, const Table &arguments); DeferredQueue &declareQueue(const Table &arguments); DeferredQueue &declareQueue(int flags = 0); ```` As you can see, the method comes in many forms, and it is up to you to choose the one that is most appropriate. We now take a look at the most complete one, the method with three parameters. All above methods returns a `DeferredQueue` object. The `DeferredQueue` class extends from the `AMQP::Deferred` class and allows you to install a more powerful `onSuccess()` callback function. The `onSuccess` method for the `declareQueue()` function gets three arguments: ````c++ // create a custom callback auto callback = [](const std::string &name, int msgcount, int consumercount) { // @todo add your own implementation }; // declare the queue, and install the callback that is called on success channel.declareQueue("myQueue").onSuccess(std::move(callback)); ```` Just like many others methods in the `Channel` class, the `declareQueue()` method accepts an integer parameter named `flags`. This is a variable in which you can set method-specific options, by summing up all the options that are described in the documentation above the method. If you for example want to create a durable, auto-deleted queue, you can pass in the value `AMQP::durable + AMQP::autodelete`. The `declareQueue()` method also accepts a parameter named `arguments`, which is of type `Table`. This `Table` object can be used as an associative array to send additional options to RabbitMQ, that are often custom RabbitMQ extensions to the AMQP standard. For a list of all supported arguments, take a look at the documentation on the RabbitMQ website. With every new RabbitMQ release more features, and supported arguments are added. The `Table` class is a very powerful class that enables you to build complicated, deeply nested structures full of strings, arrays and even other tables. In reality, you only need strings and integers. ````c++ // custom options that are passed to the declareQueue call AMQP::Table arguments; arguments["x-dead-letter-exchange"] = "some-exchange"; arguments["x-message-ttl"] = 3600 * 1000; arguments["x-expires"] = 7200 * 1000; // declare the queue channel.declareQueue("my-queue-name", AMQP::durable + AMQP::autodelete, arguments); ```` PUBLISHING MESSAGES =================== [Back to Table of Contents](#table-of-contents) Publishing messages is easy, and the `Channel` class has a list of methods that can all be used for it. The most simple one takes three arguments: the name of the exchange to publish to, the routing key to use, and the actual message that you're publishing - all these parameters are standard C++ strings. More extended versions of the `publish()` method exist that accept additional arguments, and that enable you to publish entire `Envelope` objects. An `Envelope` is an object that contains the message plus a list of optional meta properties like the content-type, content-encoding, priority, expire time and more. None of these meta fields are interpreted by this library, and RabbitMQ ignores most of them, but the AMQP protocol defines them and are free for you to use. For an extensive list of the fields that are supported, take a look at the MetaData.h header file (`MetaData` is the base class for `Envelope`). You should also check the RabbitMQ documentation to find out if an envelope header is interpreted by the RabbitMQ server (at the time of this writing, only the expire time is being used). The following snippet is copied from the Channel.h header file and lists all available `publish()` methods. As you can see, you can call the `publish()` method in almost any form: ````c++ /** * Publish a message to an exchange * * You have to supply the name of an exchange and a routing key. RabbitMQ will then try * to send the message to one or more queues. With the optional flags parameter you can * specify what should happen if the message could not be routed to a queue. By default, * unroutable message are silently discarded. * * If you set the 'mandatory' and/or 'immediate' flag, messages that could not be handled * are returned to the application. Make sure that you have called the recall()-method and * have set up all appropriate handlers to process these returned messages before you start * publishing. * * The following flags can be supplied: * * - mandatory If set, server returns messages that are not sent to a queue * - immediate If set, server returns messages that can not immediately be forwarded to a consumer. * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags optional flags */ bool publish(const std::string &exchange, const std::string &routingKey, const Envelope &envelope, int flags = 0) { return _implementation->publish(exchange, routingKey, envelope, flags); } bool publish(const std::string &exchange, const std::string &routingKey, const std::string &message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message.data(), message.size()), flags); } bool publish(const std::string &exchange, const std::string &routingKey, const char *message, size_t size, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, size), flags); } bool publish(const std::string &exchange, const std::string &routingKey, const char *message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, strlen(message)), flags); } ```` Published messages are normally not confirmed by the server, and the RabbitMQ will not send a report back to inform you whether the message was successfully published or not. But with the flags you can instruct RabbitMQ to send back the message if it was undeliverable. In you use these flags you must also install callbacks that will process these bounced messages. You can also use transactions to ensure that your messages get delivered. Let's say that you are publishing many messages in a row. If you get an error halfway through there is no way to know for sure how many messages made it to the broker and how many should be republished. If this is important, you can wrap the publish commands inside a transaction. In this case, if an error occurs, the transaction is automatically rolled back by RabbitMQ and none of the messages are actually published. ````c++ // start a transaction channel.startTransaction(); // publish a number of messages channel.publish("my-exchange", "my-key", "my first message"); channel.publish("my-exchange", "my-key", "another message"); // commit the transactions, and set up callbacks that are called when // the transaction was successful or not channel.commitTransaction() .onSuccess([]() { // all messages were successfully published }) .onError([](const char *message) { // none of the messages were published // now we have to do it all over again }); ```` Note that AMQP transactions are not as powerful as transactions that are knows in the database world. It is not possible to wrap all sort of operations in a transaction, they are only meaningful for publishing and consuming. PUBLISHER CONFIRMS =================== [Back to Table of Contents](#table-of-contents) RabbitMQ supports a lightweight method of confirming that broker received and processed a message. When you enable this, RabbitMQ sends back an 'ack' or 'nack' for each publish-operation. For this to work, the channel needs to be put in _confirm mode_. This is done using the `confirmSelect()` method. When the channel is successfully put in confirm mode, the server starts counting the received messages (starting from 1) and sends acknowledgments for every message it processed (it can also acknowledge multiple message at once). If server is unable to process a message, it will send send negative acknowledgments. Both positive and negative acknowledgments handling are passed to callbacks that you can install on the object that is returned by the `confirmSelect()` method: ````c++ // setup confirm mode and ack/nack callbacks (from this moment onwards // ack/nack confirmations are coming in) channel.confirmSelect().onSuccess([&]() { // publish the first message (this will be acked/nacked with deliveryTag=1) channel.publish("my-exchange", "my-key", "my first message"); // publish the second message (this will be acked/nacked with deliveryTag=2) channel.publish("my-exchange", "my-key", "my second message"); }).onAck([&](uint64_t deliveryTag, bool multiple) { // deliveryTag is message number // multiple is set to true, if all messages UP TO deliveryTag have been processed }).onNack([&](uint64_t deliveryTag, bool multiple, bool requeue) { // deliveryTag is message number // multiple is set to true, if all messages UP TO deliveryTag have not been processed // requeue is to be ignored }); ```` If you use this feature, you will have to implement your own bookkeeping to track which messages have already been acked/nacked, and which messages are still being handled. For your convenience, the AMQP-CPP library comes with a number of helper classes that can take over this responsibility. The `AMQP::Reliable` class is an optional wrapper around channels. When you use it, your underlying channel is automatically put it _confirm method_, and all publish operations are individually acknowledged: ````c++ // create a channel AMQP::TcpChannel mychannel(connection); // wrap the channel into a reliable-object so that publish-opertions are // individually confirmed (after wrapping the channel, it is recommended // to no longer make direct calls to the channel) AMQP::Reliable reliable(mychannel); // publish a message via the reliable-channel reliable.publish("my-exchange", "my-key", "my first message").onAck([]() { // the message has been acknowledged by RabbitMQ (in your application // code you can now safely discard the message as it has been picked up) }).onNack([]() { // the message has _explicitly_ been nack'ed by RabbitMQ (in your application // code you probably want to log or handle this to avoid data-loss) }).onLost([]() { // because the implementation for onNack() and onError() will be the same // in many applications, you can also choose to install a onLost() handler, // which is called when the message has either been nack'ed, or lost. }).onError([](const char *message) { // a channel-error occurred before any ack or nack was received, and the // message is probably lost (which you might want to handle) }); ```` In the above example we have implemented four callback methods. In a real life application, implementing the `onAck()` and `onLost()` is normally sufficient. Publisher-confirms are often useful in situations where you need reliability. If you want to have certainty about whether your message was handled by RabbitMQ or not, you can enable this feature. Call `channel.confirmSelect()` if you want to do your own bookkeeping, or using `AMQP::Reliable` for a simpler API. But it also is useful for flood prevention. RabbitMQ is not great at handling big loads of publish-operations. If you publish messages faster than RabbitMQ can handle, a server-side buffer builds up, and RabbitMQ gets slow (which causes the buffer to build up even further, etc). With publish-confirms you can keep the messages in your own application, and only proceed with publishing them when your previous messages have been handled. With this approach you prevent that RabbitMQ gets overloaded. We call it throttling. You can build your own throttling mechanism using the `confirmSelect()` approach or the `AMQP::Reliable` class. Or you use `AMQP::Throttle`: ````c++ // create a channel AMQP::TcpChannel mychannel(connection); // create a throttle (do not publish more than 20 messages at once) (after // wrapping the channel in a throttle you should no longer call any of the // channel-methods directly) AMQP::Throttle throttle(connection, 20); // publish way more messages than RabbitMQ can handle (the Throttle class // will make sure that messages are buffered inside your application if // there are more than 20 unacked messages) for (size_t i = 0; i < 100000; ++i) { // publish a message throttle.publish("my-exchange", "my-key", "my first message"); } ```` The `AMQP::Reliable` and `AMQP::Throttle` classes both wrap around a channel. But what if you want to use both? You want to throttle messages, but also like to install your own callbacks for `onAck` and `onLost`? This is possible too: ````c++ // create a channel AMQP::TcpChannel mychannel(connection); // create a throttle that allows reliable-publishing AMQP::Throttle throttle(connection, 20); // publish way more messages than RabbitMQ can handle (the Throttle class // will make sure that messages are buffered inside your application if // there are more than 20 unacked messages) for (size_t i = 0; i < 100000; ++i) { // publish a message throttle.publish("my-exchange", "my-key", "my first message").onAck([]() { // @todo add your own code }).onLost([]() { // @todo add your own code }); } ```` For more information, see http://www.rabbitmq.com/confirms.html. CONSUMING MESSAGES ================== [Back to Table of Contents](#table-of-contents) Fetching messages from RabbitMQ is called consuming, and can be started by calling the method `Channel::consume()`. After you've called this method, RabbitMQ starts delivering messages to you. Just like the `publish()` method that we just described, the `consume()` method also comes in many forms. The first parameter is always the name of the queue you like to consume from. The subsequent parameters are an optional consumer tag, flags and a table with custom arguments. The first additional parameter, the consumer tag, is nothing more than a string identifier that you can use when you want to stop consuming. The full documentation from the C++ Channel.h headerfile looks like this: ````c++ /** * Tell the RabbitMQ server that we're ready to consume messages * * After this method is called, RabbitMQ starts delivering messages to the client * application. The consume tag is a string identifier that will be passed to * each received message, so that you can associate incoming messages with a * consumer. If you do not specify a consumer tag, the server will assign one * for you. * * The following flags are supported: * * - nolocal if set, messages published on this channel are * not also consumed * * - noack if set, consumed messages do not have to be acked, * this happens automatically * * - exclusive request exclusive access, only this consumer can * access the queue * * The callback registered with DeferredConsumer::onSuccess() will be called when the * consumer has started. * * @param queue the queue from which you want to consume * @param tag a consumer tag that will be associated with this consume operation * @param flags additional flags * @param arguments additional arguments * @return bool */ DeferredConsumer &consume(const std::string &queue, const std::string &tag, int flags, const AMQP::Table &arguments); DeferredConsumer &consume(const std::string &queue, const std::string &tag, int flags = 0); DeferredConsumer &consume(const std::string &queue, const std::string &tag, const AMQP::Table &arguments); DeferredConsumer &consume(const std::string &queue, int flags, const AMQP::Table &arguments); DeferredConsumer &consume(const std::string &queue, int flags = 0); DeferredConsumer &consume(const std::string &queue, const AMQP::Table &arguments); ```` As you can see, the consume method returns a `DeferredConsumer`. This object is a regular `Deferred`, with additions. The `onSuccess()` method of a `DeferredConsumer` is slightly different than the `onSuccess()` method of a regular `Deferred` object: one extra parameter will be supplied to your callback function with the consumer tag. The `onSuccess()` callback will be called when the consume operation _has started_, but not when messages are actually consumed. For this you will have to install a different callback, using the `onReceived()` method. ````c++ // callback function that is called when the consume operation starts auto startCb = [](const std::string &consumertag) { std::cout << "consume operation started" << std::endl; }; // callback function that is called when the consume operation failed auto errorCb = [](const char *message) { std::cout << "consume operation failed" << std::endl; }; // callback operation when a message was received auto messageCb = [&channel](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) { std::cout << "message received" << std::endl; // acknowledge the message channel.ack(deliveryTag); }; // callback that is called when the consumer is cancelled by RabbitMQ (this only happens in // rare situations, for example when someone removes the queue that you are consuming from) auto cancelledCb = [](const std::string &consumertag) { std::cout << "consume operation cancelled by the RabbitMQ server" << std::endl; }; // start consuming from the queue, and install the callbacks channel.consume("my-queue") .onReceived(messageCb) .onSuccess(startCb) .onCancelled(cancelledCb) .onError(errorCb); ```` The `Message` object holds all information of the delivered message: the actual content, all meta information from the envelope (in fact, the `Message` class is derived from the `Envelope` class), and even the name of the exchange and the routing key that were used when the message was originally published. For a full list of all information in the `Message` class, you best have a look at the message.h, envelope.h and metadata.h header files. Another important parameter to the `onReceived()` method is the `deliveryTag` parameter. This is a unique identifier that you need to acknowledge an incoming message. RabbitMQ only removes the message after it has been acknowledged, so that if your application crashes while it was busy processing the message, the message does not get lost but remains in the queue. But this means that after you've processed the message, you must inform RabbitMQ about it by calling the `Channel:ack()` method. This method is very simple and takes in its simplest form only one parameter: the `deliveryTag` of the message. Consuming messages is a continuous process. RabbitMQ keeps sending messages, until you stop the consumer, which can be done by calling the `Channel::cancel()` method. If you close the channel, or the entire TCP connection, the consumer also stops. In some (rare) situations, the consume operation can also be cancelled by the RabbitMQ server. This for example happens when a queue is removed or becomes unavailable. To handle this scenario you can install a "onCancelled" callback. RabbitMQ throttles the number of messages that are delivered to you, to prevent that your application is flooded with messages from the queue, and to spread out the messages over multiple consumers. This is done with a setting called quality-of-service (QOS). The QOS setting is a numeric value which holds the number of unacknowledged messages that you are allowed to have. RabbitMQ stops sending additional messages when the number of unacknowledges messages has reached this limit, and only sends additional messages when an earlier message gets acknowledged. To change the QOS, you can simple call `Channel::setQos()`. UPGRADING ========= [Back to Table of Contents](#table-of-contents) AMQP-CPP 4.* is not always compatible with previous versions. Especially some virtual methods in the `ConnectionHandler` and `TcpHandler` classes have been renamed or are called during a different stage in the connection lifetime. Check out this README file and the comments inside the connectionhandler.h and tcphandler.h files to find out if your application has to be changed. You should especially check the following: - `ConnectionHandler::onConnected` has been renamed to `ConnectionHandler::onReady` - `TcpHandler::onConnected` is now called sooner: when the TCP connection is established, instead of when the AMQP connection is ready for instructions. - The new method `TcpHandler::onReady` is called when the AMQP connection is ready to be used (this is the old behavior of `TcpHandler::onConnected`) - `TcpHandler::onError` is no longer the last method that is called (`TcpHandler::onLost` could be called and `TcpHandler::onDetached` will be called after the error too) - `TcpHandler::onClosed` is now called to indicate the graceful end of the AMQP protocol, and not the end of TCP connection. - `TcpHandler::onLost` is called when the TCP connection is lost or closed. - The new method `TcpHandler::onDetached` is a better alternative for cleanup code instead of `TcpHandler::onClosed` and/or `TcpHandler::onError`. WORK IN PROGRESS ================ [Back to Table of Contents](#table-of-contents) Almost all AMQP features have been implemented. But the following things might need additional attention: - ability to set up secure connections (or is this fully done on the IO level) - login with other protocols than login/password We also need to add more safety checks so that strange or invalid data from RabbitMQ does not break the library (although in reality RabbitMQ only sends valid data). Also, when we now receive an answer from RabbitMQ that does not match the request that we sent before, we do not report an error (this is also an issue that only occurs in theory). It would be nice to have sample implementations for the `ConnectionHandler` class that can be directly plugged into libev, libevent and libuv event loops. For performance reasons, we need to investigate if we can limit the number of times an incoming or outgoing messages is copied. AMQP-CPP-4.3.27/amqpcpp.pc.in000066400000000000000000000004311470663072600153700ustar00rootroot00000000000000prefix=@DEST_DIR@ libdir=${prefix}/lib includedir=${prefix}/include Name: amqpcpp Description: AMQP-CPP is a C++ library for communicating with a RabbitMQ message broker Version: @VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@ Libs: -L${libdir} -lamqpcpp Cflags: -I${includedir} AMQP-CPP-4.3.27/appveyor.yml000066400000000000000000000004311470663072600153660ustar00rootroot00000000000000version: '1.0.{build}' image: Visual Studio 2017 platform: - x64 configuration: - Release - Debug install: - git submodule update --init --recursive before_build: - cmake -G "Visual Studio 15 2017 Win64" . build: project: $(APPVEYOR_BUILD_FOLDER)\amqpcpp.slnAMQP-CPP-4.3.27/examples/000077500000000000000000000000001470663072600146165ustar00rootroot00000000000000AMQP-CPP-4.3.27/examples/.gitignore000066400000000000000000000000061470663072600166020ustar00rootroot00000000000000a.out AMQP-CPP-4.3.27/examples/CMakeLists.txt000066400000000000000000000014041470663072600173550ustar00rootroot00000000000000 ################################### # Boost ################################### add_executable(amqpcpp_boost_example libboostasio.cpp) add_dependencies(amqpcpp_boost_example amqpcpp) target_link_libraries(amqpcpp_boost_example amqpcpp boost_system pthread dl ssl) ################################### # Libev ################################### add_executable(amqpcpp_libev_example libev.cpp) add_dependencies(amqpcpp_libev_example amqpcpp) target_link_libraries(amqpcpp_libev_example amqpcpp ev pthread dl ssl) ################################### # Libuv ################################### add_executable(amqpcpp_libuv_example libuv.cpp) add_dependencies(amqpcpp_libuv_example amqpcpp) target_link_libraries(amqpcpp_libuv_example amqpcpp uv pthread dl ssl) AMQP-CPP-4.3.27/examples/libboostasio.cpp000066400000000000000000000027731470663072600200240ustar00rootroot00000000000000/** * LibBoostAsio.cpp * * Test program to check AMQP functionality based on Boost's asio io_service. * * @author Gavin Smith * * Compile with g++ -std=c++14 libboostasio.cpp -o boost_test -lpthread -lboost_system -lamqpcpp */ /** * Dependencies */ #include #include #include #include #include /** * Main program * @return int */ int main() { // access to the boost asio handler // note: we suggest use of 2 threads - normally one is fin (we are simply demonstrating thread safety). boost::asio::io_service service(4); // handler for libev AMQP::LibBoostAsioHandler handler(service); // make a connection AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://guest:guest@localhost/")); // we need a channel too AMQP::TcpChannel channel(&connection); // create a temporary queue channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) { // report the name of the temporary queue std::cout << "declared queue " << name << std::endl; // now we can close the connection connection.close(); }); // run the handler // a t the moment, one will need SIGINT to stop. In time, should add signal handling through boost API. return service.run(); } AMQP-CPP-4.3.27/examples/libev.cpp000066400000000000000000000136421470663072600164310ustar00rootroot00000000000000/** * LibEV.cpp * * Test program to check AMQP functionality based on LibEV * * @author Emiel Bruijntjes * @copyright 2015 - 2022 Copernica BV */ /** * Dependencies */ #include #include #include #include #include /** * Custom handler */ class MyHandler : public AMQP::LibEvHandler { private: /** * Method that is called when a connection error occurs * @param connection * @param message */ virtual void onError(AMQP::TcpConnection *connection, const char *message) override { std::cout << "error: " << message << std::endl; } /** * Method that is called when the TCP connection ends up in a connected state * @param connection The TCP connection */ virtual void onConnected(AMQP::TcpConnection *connection) override { std::cout << "connected" << std::endl; } /** * Method that is called when the TCP connection ends up in a ready * @param connection The TCP connection */ virtual void onReady(AMQP::TcpConnection *connection) override { std::cout << "ready" << std::endl; } /** * Method that is called when the TCP connection is closed * @param connection The TCP connection */ virtual void onClosed(AMQP::TcpConnection *connection) override { std::cout << "closed" << std::endl; } /** * Method that is called when the TCP connection is detached * @param connection The TCP connection */ virtual void onDetached(AMQP::TcpConnection *connection) override { std::cout << "detached" << std::endl; } public: /** * Constructor * @param ev_loop */ MyHandler(struct ev_loop *loop) : AMQP::LibEvHandler(loop) {} /** * Destructor */ virtual ~MyHandler() = default; }; /** * Class that runs a timer */ class MyTimer { private: /** * The actual watcher structure * @var struct ev_io */ struct ev_timer _timer; /** * Pointer towards the AMQP channel * @var AMQP::TcpChannel */ AMQP::TcpChannel *_channel; /** * Name of the queue * @var std::string */ std::string _queue; /** * Callback method that is called by libev when the timer expires * @param loop The loop in which the event was triggered * @param timer Internal timer object * @param revents The events that triggered this call */ static void callback(struct ev_loop *loop, struct ev_timer *timer, int revents) { // retrieve the this pointer MyTimer *self = static_cast(timer->data); // publish a message self->_channel->publish("", self->_queue, "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"); } public: /** * Constructor * @param loop * @param channel * @param queue */ MyTimer(struct ev_loop *loop, AMQP::TcpChannel *channel, std::string queue) : _channel(channel), _queue(std::move(queue)) { // initialize the libev structure ev_timer_init(&_timer, callback, 0.005, 1.005); // this object is the data _timer.data = this; // and start it ev_timer_start(loop, &_timer); } /** * Destructor */ virtual ~MyTimer() { // @todo to be implemented } }; /** * Main program * @return int */ int main() { // access to the event loop auto *loop = EV_DEFAULT; // handler for libev MyHandler handler(loop); // init the SSL library #if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); #else OPENSSL_init_ssl(0, NULL); #endif // make a connection AMQP::Address address("amqp://guest:guest@localhost/"); // AMQP::Address address("amqps://guest:guest@localhost/"); AMQP::TcpConnection connection(&handler, address); // we need a channel too AMQP::TcpChannel channel(&connection); // create a temporary queue channel.declareQueue(AMQP::exclusive).onSuccess([&connection, &channel, loop](const std::string &queuename, uint32_t messagecount, uint32_t consumercount) { // report the name of the temporary queue std::cout << "declared queue " << queuename << std::endl; // close the channel //channel.close().onSuccess([&connection, &channel]() { // // // report that channel was closed // std::cout << "channel closed" << std::endl; // // // close the connection // connection.close(); //}); // construct a timer that is going to publish stuff auto *timer = new MyTimer(loop, &channel, queuename); // start a consumer channel.consume(queuename).onSuccess([](const std::string &tag) { // the consumer is ready std::cout << "started consuming with tag " << tag << std::endl; }).onCancelled([](const std::string &tag) { // the consumer was cancelled by the server std::cout << "consumer " << tag << " was cancelled" << std::endl; }).onReceived([&channel, queuename](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) { std::cout << "received " << deliveryTag << std::endl; // we remove the queue -- to see if this indeed causes the onCancelled method to be called if (deliveryTag > 3) channel.removeQueue(queuename); // ack the message channel.ack(deliveryTag); }); //connection.close(); }); // run the loop ev_run(loop, 0); // done return 0; } AMQP-CPP-4.3.27/examples/libevent.cpp000066400000000000000000000021161470663072600171320ustar00rootroot00000000000000/** * Libevent.cpp * * Test program to check AMQP functionality based on Libevent * * @author Brent Dimmig */ /** * Dependencies */ #include #include #include /** * Main program * @return int */ int main() { // access to the event loop auto evbase = event_base_new(); // handler for libevent AMQP::LibEventHandler handler(evbase); // make a connection AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/")); // we need a channel too AMQP::TcpChannel channel(&connection); // create a temporary queue channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) { // report the name of the temporary queue std::cout << "declared queue " << name << std::endl; // now we can close the connection connection.close(); }); // run the loop event_base_dispatch(evbase); event_base_free(evbase); // done return 0; } AMQP-CPP-4.3.27/examples/libuv.cpp000066400000000000000000000035711470663072600164510ustar00rootroot00000000000000/** * LibUV.cpp * * Test program to check AMQP functionality based on LibUV * * @author Emiel Bruijntjes * @copyright 2015 - 2017 Copernica BV */ /** * Dependencies */ #include #include #include /** * Custom handler */ class MyHandler : public AMQP::LibUvHandler { private: /** * Method that is called when a connection error occurs * @param connection * @param message */ virtual void onError(AMQP::TcpConnection *connection, const char *message) override { std::cout << "error: " << message << std::endl; } /** * Method that is called when the TCP connection ends up in a connected state * @param connection The TCP connection */ virtual void onConnected(AMQP::TcpConnection *connection) override { std::cout << "connected" << std::endl; } public: /** * Constructor * @param uv_loop */ MyHandler(uv_loop_t *loop) : AMQP::LibUvHandler(loop) {} /** * Destructor */ virtual ~MyHandler() = default; }; /** * Main program * @return int */ int main() { // access to the event loop auto *loop = uv_default_loop(); // handler for libev MyHandler handler(loop); // make a connection AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://guest:guest@localhost/")); // we need a channel too AMQP::TcpChannel channel(&connection); // create a temporary queue channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) { // report the name of the temporary queue std::cout << "declared queue " << name << std::endl; }); // run the loop uv_run(loop, UV_RUN_DEFAULT); // done return 0; } AMQP-CPP-4.3.27/include/000077500000000000000000000000001470663072600144235ustar00rootroot00000000000000AMQP-CPP-4.3.27/include/amqpcpp.h000066400000000000000000000042311470663072600162350ustar00rootroot00000000000000/** * AMQP.h * * Starting point for all includes of the Copernica AMQP library * * @author Emiel Bruijntjes * @copyright 2015 - 2018 Copernica BV */ #pragma once // base C++ include files #include #include #include #include #include #include #include #include #include #include #include #include #include #include // base C include files #include #include // fix strcasecmp on non linux platforms #if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)) && !defined(__CYGWIN__) #define strcasecmp _stricmp #endif // forward declarations #include "amqpcpp/classes.h" // utility classes #include "amqpcpp/endian.h" #include "amqpcpp/buffer.h" #include "amqpcpp/bytebuffer.h" #include "amqpcpp/inbuffer.h" #include "amqpcpp/outbuffer.h" #include "amqpcpp/watchable.h" #include "amqpcpp/monitor.h" // amqp types #include "amqpcpp/field.h" #include "amqpcpp/numericfield.h" #include "amqpcpp/decimalfield.h" #include "amqpcpp/stringfield.h" #include "amqpcpp/booleanset.h" #include "amqpcpp/fieldproxy.h" #include "amqpcpp/table.h" #include "amqpcpp/array.h" #include "amqpcpp/voidfield.h" // envelope for publishing and consuming #include "amqpcpp/metadata.h" #include "amqpcpp/envelope.h" #include "amqpcpp/message.h" // mid level includes #include "amqpcpp/exchangetype.h" #include "amqpcpp/flags.h" #include "amqpcpp/callbacks.h" #include "amqpcpp/deferred.h" #include "amqpcpp/deferredconsumer.h" #include "amqpcpp/deferredqueue.h" #include "amqpcpp/deferreddelete.h" #include "amqpcpp/deferredcancel.h" #include "amqpcpp/deferredconfirm.h" #include "amqpcpp/deferredget.h" #include "amqpcpp/deferredrecall.h" #include "amqpcpp/channelimpl.h" #include "amqpcpp/channel.h" #include "amqpcpp/tagger.h" #include "amqpcpp/throttle.h" #include "amqpcpp/reliable.h" #include "amqpcpp/login.h" #include "amqpcpp/address.h" #include "amqpcpp/connectionhandler.h" #include "amqpcpp/connectionimpl.h" #include "amqpcpp/connection.h" #include "amqpcpp/openssl.h" AMQP-CPP-4.3.27/include/amqpcpp/000077500000000000000000000000001470663072600160645ustar00rootroot00000000000000AMQP-CPP-4.3.27/include/amqpcpp/address.h000066400000000000000000000304131470663072600176630ustar00rootroot00000000000000/** * Address.h * * An AMQP address in the "amqp://user:password@hostname:port/vhost" notation * * @author Emiel Bruijntjes * @copyright 2015 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Includes */ #include /** * Set up namespace */ namespace AMQP { /** * Class definition */ class Address { private: /** * Helper class to do case insensitive comparison */ struct icasecmp { /** * Comparison operator <. Should exhibit SWO. * @param lhs * @param rhs * @return bool lhs < rhs */ bool operator() (const std::string& lhs, const std::string& rhs) const { return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; } }; private: /** * The auth method * @var bool */ bool _secure = false; /** * Login data (username + password) * @var Login */ Login _login; /** * The hostname * @var std::string */ std::string _hostname; /** * Port number * @var uint16_t */ uint16_t _port = 5672; /** * The vhost * @var std::string */ std::string _vhost; /** * Extra provided options after the question mark /vhost?option=value * @var std::map */ std::map _options; /** * The default port * @return uint16_t */ uint16_t defaultport() const { return _secure ? 5671 : 5672; } public: /** * Constructor to parse an address string * The address should start with "amqp:// * @param data * @param size * @throws std::runtime_error */ Address(const char *data, size_t size) : _vhost("/") { // position of the last byte const char *last = data + size; // must start with ampqs:// to have a secure connection (and we also assign a different default port) if (strncmp(data, "amqps://", 8) == 0) _secure = true; // otherwise protocol must be amqp:// else if (strncmp(data, "amqp://", 7) != 0) throw std::runtime_error("AMQP address should start with \"amqp://\" or \"amqps://\""); // assign default port (we may overwrite it later) _port = defaultport(); // begin of the string was parsed data += _secure ? 8 : 7; // do we have a '@' to split user-data and hostname? const char *at = (const char *)memchr(data, '@', last - data); // do we have one? if (at != nullptr) { // size of the user:password size_t loginsize = at - data; // colon could split username and password const char *colon = (const char *)memchr(data, ':', loginsize); // assign the login _login = Login( std::string(data, colon ? colon - data : loginsize), std::string(colon ? colon + 1 : "", colon ? at - colon - 1 : 0) ); // set data to the start of the hostname data = at + 1; } // find out where the vhost is set (starts with a slash) const char *slash = (const char *)memchr(data, '/', last - data); // where to start looking for the question mark, we also want to support urls where the // hostname does not have a slash. const char *start = slash ? slash : data; // we search for the ? for extra options const char *qm = static_cast(memchr(start, '?', last - start)); // if there is a questionmark, we need to parse all options if (qm != nullptr && last - qm > 1) { // we start at question mark now start = qm; do { // find the next equals sign and start of the next parameter const char *equals = (const char *)memchr(start + 1, '=', last - start - 1); const char *next = (const char *)memchr(start + 1, '&', last - start - 1); // assign it to the options if we found an equals sign if (equals) _options[std::string(start + 1, equals - start - 1)] = std::string(equals + 1, (next ? next - equals : last - equals) - 1); // we now have a new start, the next '&...' start = next; // keep iterating as long as there are more vars } while (start); } // was a vhost set? if (slash != nullptr && last - slash > 1) _vhost.assign(slash + 1, (qm ? qm - slash : last - slash) - 1); // the hostname is everything until the slash, check is portnumber was set const char *colon = (const char *)memchr(data, ':', last - data); // was a portnumber specified (colon must appear before the slash of the vhost) if (colon && (!slash || colon < slash)) { // a portnumber was set to _hostname.assign(data, colon - data); // calculate the port _port = atoi(std::string(colon + 1, slash ? slash - colon - 1 : last - colon - 1).data()); } else { // no portnumber was set _hostname.assign(data, slash ? slash - data : last - data); } } /** * Constructor to parse an address string * The address should start with amqp:// or amqps:// * @param data * @throws std::runtime_error */ Address(const char *data) : Address(data, strlen(data)) {} /** * Constructor based on std::string * @param address */ Address(const std::string &address) : Address(address.data(), address.size()) {} /** * Constructor based on already known properties * @param host * @param port * @param login * @param vhost * @param secure */ Address(std::string host, uint16_t port, Login login, std::string vhost, bool secure = false) : _secure(secure), _login(std::move(login)), _hostname(std::move(host)), _port(port), _vhost(std::move(vhost)) {} /** * Destructor */ virtual ~Address() = default; /** * Should we open a secure connection? * @return bool */ bool secure() const { return _secure; } /** * Expose the login data * @return Login */ const Login &login() const { return _login; } /** * Host name * @return std::string */ const std::string &hostname() const { return _hostname; } /** * Port number * @return uint16_t */ uint16_t port() const { return _port; } /** * The vhost to connect to * @return std::string */ const std::string &vhost() const { return _vhost; } /** * Get access to the options * @return std::map */ const decltype(_options) &options() const { return _options; } /** * Cast to a string * @return std::string */ operator std::string () const { // result object std::string str(_secure ? "amqps://" : "amqp://"); // append login str.append(_login.user()).append(":").append(_login.password()).append("@").append(_hostname); // do we need a special portnumber? if (_port != 5672) str.append(":").append(std::to_string(_port)); // append default vhost str.append("/"); // do we have a special vhost? if (_vhost != "/") str.append(_vhost); // iterate over all options, appending them if (!_options.empty()) { // first append a question mark str.push_back('?'); // iterate over all the options for (const auto &kv : _options) str.append(kv.first).append("=").append(kv.second).append("&"); // remove the extra & str.erase(str.size() - 1); } // done return str; } /** * Comparison operator * @param that * @return bool */ bool operator==(const Address &that) const { // security setting should match if (_secure != that._secure) return false; // logins must match if (_login != that._login) return false; // hostname must match, but are not case sensitive if (strcasecmp(_hostname.data(), that._hostname.data()) != 0) return false; // portnumber must match if (_port != that._port) return false; // and the vhosts, they must match too if (_vhost != that._vhost) return false; // and the options as well return _options == that._options; } /** * Comparison operator * @param that * @return bool */ bool operator!=(const Address &that) const { // the opposite of operator== return !operator==(that); } /** * Comparison operator that is useful if addresses have to be ordered * @param that * @return bool */ bool operator<(const Address &that) const { // compare auth methods (amqp comes before amqps) if (_secure != that._secure) return !_secure; // compare logins if (_login != that._login) return _login < that._login; // hostname must match, but are not case sensitive int result = strcasecmp(_hostname.data(), that._hostname.data()); // if hostnames are not equal, we know the result if (result != 0) return result < 0; // portnumber must match if (_port != that._port) return _port < that._port; // and finally compare the vhosts if (_vhost < that._vhost) return _vhost < that._vhost; // and finally lexicographically compare the options return _options < that._options; } /** * Friend function to allow writing the address to a stream * @param stream * @param address * @return std::ostream */ friend std::ostream &operator<<(std::ostream &stream, const Address &address) { // start with the protocol and login stream << (address._secure ? "amqps://" : "amqp://"); // do we have a login? if (address._login) stream << address._login << "@"; // write hostname stream << address._hostname; // do we need a special portnumber? if (address._port != address.defaultport()) stream << ":" << address._port; // append default vhost stream << "/"; // do we have a special vhost or options? if (address._vhost != "/") stream << address._vhost; // iterate over all options, appending them if (!address._options.empty()) { // first append a question mark stream << '?'; // is this the first option? bool first = true; // iterate over all the options for (const auto &kv : address._options) { // write the pair to the stream stream << (first ? "" : "&") << kv.first << "=" << kv.second; // no longer on first option first = false; } } // done return stream; } /** * Get an integer option * @param name * @param fallback * @return T */ template ::value>::type* = nullptr> T option(const char *name, T fallback) const { // find the const char* version of the option const char *value = option(name); // if there is a value, convert it to integral, otherwise return the fallback return value ? static_cast(atoll(value)) : fallback; } /** * Get a const char * option, returns nullptr if it does not exist. * @return const char * */ const char *option(const char *name) const { // find the option auto iter = _options.find(name); // if not found, we return the default if (iter == _options.end()) return nullptr; // return the value in the map return iter->second.c_str(); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/addresses.h000066400000000000000000000045361470663072600202220ustar00rootroot00000000000000/** * Addresses.h * * Class that contains multiple addresses * * @author Emiel Bruijntjes * @copyright 2017 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class Addresses { private: /** * The actual addresses * @var std::vector
*/ std::vector
_addresses; public: /** * Constructor for a comma separated list * @param buffer * @param size * @throws std::runtime_error */ Addresses(const char *buffer, size_t size) { // keep looping while (true) { // look for the comma const char *comma = memchr(buffer, ',', size); // stop if there is no comma if (comma == nullptr) break; // size of the address size_t addresssize = comma - buffer - 1; // add address _addresses.emplace_back(buffer, addresssize); // update for next iteration buffer += addresssize + 1; size -= addresssize + 1; } // do we have more? if (size > 0) _addresses.emplace_back(buffer, size); // was anything found? if (_addresses.size() > 0) return; // no addresses found throw std::runtime_error("no addresses"); } /** * Constructor for a comma separated list * @param buffer * @throws std::runtime_error */ Addresses(const char *buffer) : Addresses(buffer, strlen(buffer)) {} /** * Constructor for a comma separated list * @param buffer * @throws std::runtime_error */ Addresses(const std::string &buffer) : Addresses(buffer.data(), buffer.size()) {} /** * Destructed */ virtual ~Addresses() = default; /** * Number of addresses * @return size_t */ size_t size() const { return _addresses.size(); } /** * Expose an address by index * @param index * @return Address */ const Address &operator[](size_t index) const { return _addresses.at(index); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/array.h000066400000000000000000000120531470663072600173540ustar00rootroot00000000000000/** * AMQP field array * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "field.h" #include "fieldproxy.h" #include #include /** * Set up namespace */ namespace AMQP { /** * AMQP field array */ class Array : public Field { private: /** * Definition of an array as a vector * @typedef */ typedef std::vector> FieldArray; /** * The actual fields * @var FieldArray */ FieldArray _fields; public: /** * Constructor to construct an array from a received frame * * @param frame received frame */ Array(InBuffer &frame); /** * Copy constructor * @param array */ Array(const Array &array); /** * Move constructor * @param array */ Array(Array &&array) : _fields(std::move(array._fields)) {} /** * Constructor for an empty Array */ Array() {} /** * Destructor */ virtual ~Array() {} /** * Create a new instance of this object * @return Field* */ virtual std::unique_ptr clone() const override { return std::unique_ptr(new Array(*this)); } /** * Get the size this field will take when * encoded in the AMQP wire-frame format * @return size_t */ virtual size_t size() const override; /** * Set a field * * @param index field index * @param value field value * @return Array */ Array set(uint8_t index, const Field &value) { // make a copy auto ptr = value.clone(); // should we overwrite an existing record? if (index >= _fields.size()) { // append index _fields.push_back(std::move(ptr)); } else { // overwrite pointer _fields[index] = std::move(ptr); } // allow chaining return *this; } /** * Get a field * * If the field does not exist, an empty string is returned * * @param index field index * @return Field */ const Field &get(uint8_t index) const; /** * Get number of elements on this array * * @return array size */ uint32_t count() const; /** * Remove last element from array */ void pop_back(); /** * Add field to end of array * * @param value */ void push_back(const Field &value); /** * Get a field * * @param index field index * @return ArrayFieldProxy */ ArrayFieldProxy operator[](uint8_t index) { return ArrayFieldProxy(this, index); } /** * Get a const field * @param index field index * @return Field */ const Field &operator[](uint8_t index) const { return get(index); } /** * Write encoded payload to the given buffer. * @param buffer */ virtual void fill(OutBuffer& buffer) const override; /** * Get the type ID that is used to identify this type of * field in a field table * @return char */ virtual char typeID() const override { return 'A'; } /** * We are an array field * * @return true, because we are an array */ bool isArray() const override { return true; } /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const override { // prefix stream << "array("; // is this the first iteration bool first = true; // loop through all members for (auto &iter : _fields) { // split with comma if (!first) stream << ","; // show output stream << *iter; // no longer first iter first = false; } // postfix stream << ")"; } /** * Cast to array. * * @note: This function may look silly and unnecessary. We are, after all, already * an array. The whole reason we still have this function is that it is virtual * and if we do not declare a cast to array on a pointer to base (i.e. Field) * will return an empty field instead of the expected array. * * Yes, clang gets this wrong and gives incorrect warnings here. See * https://llvm.org/bugs/show_bug.cgi?id=28263 for more information * * @return Ourselves */ virtual operator const Array& () const override { // this already is an array, so no cast is necessary return *this; } }; /** * Custom output stream operator * @param stream * @param field * @return ostream */ inline std::ostream &operator<<(std::ostream &stream, const ArrayFieldProxy &field) { // get underlying field, and output that return stream << field.get(); } /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/booleanset.h000066400000000000000000000076451470663072600204040ustar00rootroot00000000000000/** * BooleanSet.h * * AMQP can store eight booleans in a single byte. This class * is a utility class for setting and getting the eight * booleans * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include "field.h" #include "outbuffer.h" #include "inbuffer.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class BooleanSet : public Field { private: /** * The actual byte * @var uint8_t */ uint8_t _byte; public: /** * Constructor * @param first the first bool to set * @param second the second bool to set * @param third the third bool to set * @param fourth the fourth bool to set * @param fifth the fifth bool to set * @param sixth the sixth bool to set * @param seventh the seventh bool to set * @param eigth the eigth bool to set */ BooleanSet(bool first = false, bool second = false, bool third = false, bool fourth = false, bool fifth = false, bool sixth = false, bool seventh = false, bool eigth = false) { _byte = 0; set(0, first); set(1, second); set(2, third); set(3, fourth); set(4, fifth); set(5, sixth); set(6, seventh); set(7, eigth); } /** * Constructor based on incoming data * @param frame */ BooleanSet(InBuffer &frame) { _byte = frame.nextUint8(); } /** * Copy constructor * @param that */ BooleanSet(const BooleanSet &that) { _byte = that._byte; } /** * Destructor */ virtual ~BooleanSet() {} /** * Extending from field forces us to implement a clone function. * @return unique_ptr */ virtual std::unique_ptr clone() const override { return std::unique_ptr(new BooleanSet(*this)); } /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const override { // prefix stream << "booleanset("; // the members for (int i=0; i<8; i++) stream << (i == 0 ? "" : ",") << (get(i) ? 1 : 0); // postfix stream << ")"; } /** * Get one of the booleans * @param index from 0 to 7, where 0 is rightmost bit * @return bool */ bool get(uint32_t index) const { // bigger than seven is not an option if (index > 7) return false; // magic bit manipulation... return 0 != ((1 << index) & _byte); } /** * Set a boolean in the set * @param index * @param value */ void set(uint32_t index, bool value) { // index must be valid if (index > 7) return; // are we setting or unsetting if (value) { // magic bit manipulation... _byte |= (1 << index); } else { // magic bit manipulation... _byte &= ~(1 << index); } } /** * Fill the buffer * @param buffer */ virtual void fill(OutBuffer& buffer) const override { buffer.add(_byte); } /** * Get the byte value * @return value */ uint8_t value() const { return _byte; } /** * Type ID * @return char */ virtual char typeID() const override { return 't'; } /** * We are a boolean field * * @return true, because we are a boolean */ bool isBoolean() const override { return true; } /** * Get the size this field will take when * encoded in the AMQP wire-frame format */ virtual size_t size() const override { // booleanset takes up a single byte. return 1; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/buffer.h000066400000000000000000000037321470663072600175130ustar00rootroot00000000000000/** * Buffer.h * * Interface that can be implemented by client applications and that * is passed to the Connection::parse() method. * * Normally, the Connection::parse() method is fed with a byte * array. However, if you're receiving big frames, it may be inconvenient * to copy these big frames into continguous byte arrays, and you * prefer using objects that internally use linked lists or other * ways to store the bytes. In such sitations, you can implement this * interface and pass that to the connection. * * @author Emiel Bruijntjes * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Namespace */ namespace AMQP { /** * Class definition */ class Buffer { public: /** * Destructor */ virtual ~Buffer() {} /** * Total size of the buffer * @return size_t */ virtual size_t size() const = 0; /** * Get access to a single byte * * No safety checks are necessary: this method will only be called * for bytes that actually exist * * @param pos position in the buffer * @return char value of the byte in the buffer */ virtual char byte(size_t pos) const = 0; /** * Get access to the raw data * @param pos position in the buffer * @param size number of continuous bytes * @return char* */ virtual const char *data(size_t pos, size_t size) const = 0; /** * Copy bytes to a buffer * * No safety checks are necessary: this method will only be called * for bytes that actually exist * * @param pos position in the buffer * @param size number of bytes to copy * @param buffer buffer to copy into * @return void* pointer to buffer */ virtual void *copy(size_t pos, size_t size, void *buffer) const = 0; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/bytebuffer.h000066400000000000000000000054021470663072600203730ustar00rootroot00000000000000/** * ByteByffer.h * * Very simple implementation of the buffer class that simply wraps * around a buffer of bytes * * @author Emiel Bruijntjes * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Open namespace */ namespace AMQP { /** * Class definition */ class ByteBuffer : public Buffer { protected: /** * The actual byte buffer * @var const char * */ const char *_data; /** * Size of the buffer * @var size_t */ size_t _size; public: /** * Constructor * @param data * @param size */ ByteBuffer(const char *data, size_t size) : _data(data), _size(size) {} /** * No copy'ing * @param that */ ByteBuffer(const ByteBuffer &that) = delete; /** * Move constructor * @param that */ ByteBuffer(ByteBuffer &&that) : _data(that._data), _size(that._size) { // reset other object that._data = nullptr; that._size = 0; } /** * Destructor */ virtual ~ByteBuffer() {} /** * Move assignment operator * @param that */ ByteBuffer &operator=(ByteBuffer &&that) { // skip self-assignment if (this == &that) return *this; // copy members _data = that._data; _size = that._size; // reset other object that._data = nullptr; that._size = 0; // done return *this; } /** * Total size of the buffer * @return size_t */ virtual size_t size() const override { return _size; } /** * Get access to a single byte * @param pos position in the buffer * @return char value of the byte in the buffer */ virtual char byte(size_t pos) const override { return _data[pos]; } /** * Get access to the raw data * @param pos position in the buffer * @param size number of continuous bytes * @return char* */ virtual const char *data(size_t pos, size_t size) const override { // make sure compilers dont complain about unused parameters (void) size; // expose the data return _data + pos; } /** * Copy bytes to a buffer * @param pos position in the buffer * @param size number of bytes to copy * @param buffer buffer to copy into * @return size_t pointer to buffer */ virtual void *copy(size_t pos, size_t size, void *buffer) const override { return memcpy(buffer, _data + pos, size); } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/callbacks.h000066400000000000000000000063111470663072600201550ustar00rootroot00000000000000/** * Callbacks.h * * Class storing deferred callbacks of different type. * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class Message; class MetaData; /** * Generic callbacks that are used by many deferred objects */ using SuccessCallback = std::function; using ErrorCallback = std::function; using FinalizeCallback = std::function; /** * Declaring and deleting a queue */ using QueueCallback = std::function; using DeleteCallback = std::function; /** * When retrieving the size of a queue in some way */ using EmptyCallback = std::function; using CountCallback = std::function; using SizeCallback = std::function; /** * Starting and stopping a consumer */ using ConsumeCallback = std::function; using CancelCallback = std::function; /** * Receiving messages, either via consume(), get() or as returned messages * The following methods receive the returned message in multiple parts */ using StartCallback = std::function; using HeaderCallback = std::function; using DataCallback = std::function; using DeliveredCallback = std::function; /** * For returned messages amqp-cpp first calls a return-callback before the start, * header and data callbacks are called. Instead of the deliver-callback, a * returned-callback is called. */ using ReturnCallback = std::function; using ReturnedCallback = std::function; /** * If you do not want to merge all data into a single string, you can als * implement callbacks that return the collected message. */ using MessageCallback = std::function; using BounceCallback = std::function; /** * When using publisher confirms, AckCallback is called when server confirms that message is received * and processed. NackCallback is called otherwise. */ using AckCallback = std::function; using NackCallback = std::function; /** * When using a confirm wrapped channel, these callbacks are called when a message is acknowledged/nacked. */ using PublishAckCallback = std::function; using PublishNackCallback = std::function; using PublishLostCallback = std::function; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/channel.h000066400000000000000000000634201470663072600176520ustar00rootroot00000000000000/** * Class describing a (mid-level) AMQP channel implementation * * @copyright 2014 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * Class definition */ class Channel { private: /** * The implementation for the channel * @var std::shared_ptr */ std::shared_ptr _implementation; public: /** * Construct a channel object * * The passed in connection pointer must remain valid for the * lifetime of the channel. Watch out: this method throws an error * if the channel could not be constructed (for example because the * max number of AMQP channels has been reached) * * @param connection * @throws std::runtime_error */ Channel(Connection *connection); /** * Copy'ing of channel objects is not supported * @param channel */ Channel(const Channel &channel) = delete; /** * But movement _is_ allowed * @param channel */ Channel(Channel &&channel) : _implementation(std::move(channel._implementation)) {} /** * Destructor */ virtual ~Channel() { // close the channel (this will eventually destruct the channel) // note that the channel may be in an invalid state in case it was moved, hence the "if" if (_implementation) _implementation->close(); } /** * No assignments of other channels * @param channel * @return Channel */ Channel &operator=(const Channel &channel) = delete; /** * Callback that is called when the channel was succesfully created. * * Only one callback can be registered. Calling this function multiple * times will remove the old callback. * * @param callback the callback to execute */ inline void onReady(const SuccessCallback& callback) { return onReady(SuccessCallback(callback)); } void onReady(SuccessCallback&& callback) { _implementation->onReady(std::move(callback)); } /** * Callback that is called when an error occurs. * * Only one error callback can be registered. Calling this function * multiple times will remove the old callback. * * @param callback the callback to execute */ inline void onError(const ErrorCallback& callback) { return onError(ErrorCallback(callback)); } void onError(ErrorCallback&& callback) { _implementation->onError(std::move(callback)); } /** * Pause deliveries on a channel * * This will stop all incoming messages * * Note that this function does *not* work using RabbitMQ. For more info * @see https://www.rabbitmq.com/specification.html#method-status-channel.flow * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &pause() { return _implementation->pause(); } /** * Resume a paused channel * * This will resume incoming messages * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &resume() { return _implementation->resume(); } /** * Is the channel ready / has it passed the initial handshake? * @return bool */ bool ready() const { return _implementation->ready(); } /** * Is the channel usable / not yet closed? * @return bool */ bool usable() const { return _implementation->usable(); } /** * Is the channel connected? * This method is deprecated: use Channel::usable() * @return bool * @deprecated */ bool connected() const { return usable(); } /** * Put channel in a confirm mode (RabbitMQ specific) * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ DeferredConfirm &confirmSelect() { return _implementation->confirmSelect(); } /** * Start a transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &startTransaction() { return _implementation->startTransaction(); } /** * Commit the current transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &commitTransaction() { return _implementation->commitTransaction(); } /** * Rollback the current transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &rollbackTransaction() { return _implementation->rollbackTransaction(); } /** * Declare an exchange * * If an empty name is supplied, a name will be assigned by the server. * * The following flags can be used for the exchange: * * - durable exchange survives a broker restart * - autodelete exchange is automatically removed when all connected queues are removed * - passive only check if the exchange exist * - internal create an internal exchange * * @param name name of the exchange * @param type exchange type * @param flags exchange flags * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &declareExchange(const std::string_view &name, ExchangeType type, int flags, const Table &arguments) { return _implementation->declareExchange(name, type, flags, arguments); } Deferred &declareExchange(const std::string_view &name, ExchangeType type, const Table &arguments) { return _implementation->declareExchange(name, type, 0, arguments); } Deferred &declareExchange(const std::string_view &name, ExchangeType type = fanout, int flags = 0) { return _implementation->declareExchange(name, type, flags, Table()); } Deferred &declareExchange(ExchangeType type, int flags, const Table &arguments) { return _implementation->declareExchange(std::string_view(), type, flags, arguments); } Deferred &declareExchange(ExchangeType type, const Table &arguments) { return _implementation->declareExchange(std::string_view(), type, 0, arguments); } Deferred &declareExchange(ExchangeType type = fanout, int flags = 0) { return _implementation->declareExchange(std::string_view(), type, flags, Table()); } /** * Remove an exchange * * The following flags can be used for the exchange: * * - ifunused only delete if no queues are connected * @param name name of the exchange to remove * @param flags optional flags * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &removeExchange(const std::string_view &name, int flags = 0) { return _implementation->removeExchange(name, flags); } /** * Bind two exchanges to each other * * @param source the source exchange * @param target the target exchange * @param routingkey the routing key * @param arguments additional bind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &bindExchange(const std::string_view &source, const std::string_view &target, const std::string_view &routingkey, const Table &arguments) { return _implementation->bindExchange(source, target, routingkey, arguments); } Deferred &bindExchange(const std::string_view &source, const std::string_view &target, const std::string_view &routingkey) { return _implementation->bindExchange(source, target, routingkey, Table()); } /** * Unbind two exchanges from one another * * @param target the target exchange * @param source the source exchange * @param routingkey the routing key * @param arguments additional unbind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &unbindExchange(const std::string_view &target, const std::string_view &source, const std::string_view &routingkey, const Table &arguments) { return _implementation->unbindExchange(target, source, routingkey, arguments); } Deferred &unbindExchange(const std::string_view &target, const std::string_view &source, const std::string_view &routingkey) { return _implementation->unbindExchange(target, source, routingkey, Table()); } /** * Declare a queue * * If you do not supply a name, a name will be assigned by the server. * * The flags can be a combination of the following values: * * - durable queue survives a broker restart * - autodelete queue is automatically removed when all connected consumers are gone * - passive only check if the queue exist * - exclusive the queue only exists for this connection, and is automatically removed when connection is gone * * @param name name of the queue * @param flags combination of flags * @param arguments optional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(const std::string &name, uint32_t messageCount, uint32_t consumerCount); * * For example: channel.declareQueue("myqueue").onSuccess([](const std::string &name, uint32_t messageCount, uint32_t consumerCount) { * * std::cout << "Queue '" << name << "' has been declared with " << messageCount << " messages and " << consumerCount << " consumers" << std::endl; * * }); */ DeferredQueue &declareQueue(const std::string_view &name, int flags, const Table &arguments) { return _implementation->declareQueue(name, flags, arguments); } DeferredQueue &declareQueue(const std::string_view &name, const Table &arguments) { return _implementation->declareQueue(name, 0, arguments); } DeferredQueue &declareQueue(const std::string_view &name, int flags = 0) { return _implementation->declareQueue(name, flags, Table()); } DeferredQueue &declareQueue(int flags, const Table &arguments) { return _implementation->declareQueue(std::string_view(), flags, arguments); } DeferredQueue &declareQueue(const Table &arguments) { return _implementation->declareQueue(std::string_view(), 0, arguments); } DeferredQueue &declareQueue(int flags = 0) { return _implementation->declareQueue(std::string_view(), flags, Table()); } /** * Bind a queue to an exchange * * @param exchange the source exchange * @param queue the target queue * @param routingkey the routing key * @param arguments additional bind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &bindQueue(const std::string_view &exchange, const std::string_view &queue, const std::string_view &routingkey, const Table &arguments) { return _implementation->bindQueue(exchange, queue, routingkey, arguments); } Deferred &bindQueue(const std::string_view &exchange, const std::string_view &queue, const std::string_view &routingkey) { return _implementation->bindQueue(exchange, queue, routingkey, Table()); } /** * Unbind a queue from an exchange * @param exchange the source exchange * @param queue the target queue * @param routingkey the routing key * @param arguments additional bind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &unbindQueue(const std::string_view &exchange, const std::string_view &queue, const std::string_view &routingkey, const Table &arguments) { return _implementation->unbindQueue(exchange, queue, routingkey, arguments); } Deferred &unbindQueue(const std::string_view &exchange, const std::string_view &queue, const std::string_view &routingkey) { return _implementation->unbindQueue(exchange, queue, routingkey, Table()); } /** * Purge a queue * * @param name name of the queue * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(uint32_t messageCount); * * For example: channel.purgeQueue("myqueue").onSuccess([](uint32_t messageCount) { * * std::cout << "Queue purged, all " << messageCount << " messages removed" << std::endl; * * }); */ DeferredDelete &purgeQueue(const std::string_view &name){ return _implementation->purgeQueue(name); } /** * Remove a queue * * The following flags can be used for the exchange: * * - ifunused only delete if no consumers are connected * - ifempty only delete if the queue is empty * * @param name name of the queue to remove * @param flags optional flags * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(uint32_t messageCount); * * For example: channel.removeQueue("myqueue").onSuccess([](uint32_t messageCount) { * * std::cout << "Queue deleted, along with " << messageCount << " messages" << std::endl; * * }); */ DeferredDelete &removeQueue(const std::string_view &name, int flags = 0) { return _implementation->removeQueue(name, flags); } /** * Publish a message to an exchange * * You have to supply the name of an exchange and a routing key. RabbitMQ will then try * to send the message to one or more queues. With the optional flags parameter you can * specify what should happen if the message could not be routed to a queue. By default, * unroutable message are silently discarded. * * If you set the 'mandatory' and/or 'immediate' flag, messages that could not be handled * are returned to the application. Make sure that you have called the recall()-method and * have set up all appropriate handlers to process these returned messages before you start * publishing. * * The following flags can be supplied: * * - mandatory If set, server returns messages that are not sent to a queue * - immediate If set, server returns messages that can not immediately be forwarded to a consumer. * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags optional flags */ bool publish(const std::string_view &exchange, const std::string_view &routingKey, const Envelope &envelope, int flags = 0) { return _implementation->publish(exchange, routingKey, envelope, flags); } bool publish(const std::string_view &exchange, const std::string_view &routingKey, const std::string &message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message.data(), message.size()), flags); } bool publish(const std::string_view &exchange, const std::string_view &routingKey, const char *message, size_t size, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, size), flags); } bool publish(const std::string_view &exchange, const std::string_view &routingKey, const char *message, int flags = 0) { return _implementation->publish(exchange, routingKey, Envelope(message, strlen(message)), flags); } /** * Set the Quality of Service (QOS) for this channel * * When you consume messages, every single message needs to be ack'ed to inform * the RabbitMQ server that is has been received. The Qos setting specifies the * number of unacked messages that may exist in the client application. The server * stops delivering more messages if the number of unack'ed messages has reached * the prefetchCount * * @param prefetchCount maximum number of messages to prefetch * @param global share counter between all consumers on the same channel * @return bool whether the Qos frame is sent. */ Deferred &setQos(uint16_t prefetchCount, bool global = false) { return _implementation->setQos(prefetchCount, global); } /** * Tell the RabbitMQ server that we're ready to consume messages * * After this method is called, RabbitMQ starts delivering messages to the client * application. The consume tag is a string identifier that you can use to identify * the consumer if you later want to stop it with with a channel::cancel() call. * If you do not specify a consumer tag, the server will assign one for you. * * The following flags are supported: * * - nolocal if set, messages published on this channel are not also consumed * - noack if set, consumed messages do not have to be acked, this happens automatically * - exclusive request exclusive access, only this consumer can access the queue * * @param queue the queue from which you want to consume * @param tag a consumer tag that will be associated with this consume operation * @param flags additional flags * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(const std::string_view& tag); * * For example: channel.consume("myqueue").onSuccess([](const std::string_view& tag) { * * std::cout << "Started consuming under tag " << tag << std::endl; * * }); */ DeferredConsumer &consume(const std::string_view &queue, const std::string_view &tag, int flags, const Table &arguments) { return _implementation->consume(queue, tag, flags, arguments); } DeferredConsumer &consume(const std::string_view &queue, const std::string_view &tag, int flags = 0) { return _implementation->consume(queue, tag, flags, Table()); } DeferredConsumer &consume(const std::string_view &queue, const std::string_view &tag, const Table &arguments) { return _implementation->consume(queue, tag, 0, arguments); } DeferredConsumer &consume(const std::string_view &queue, int flags, const Table &arguments) { return _implementation->consume(queue, std::string_view(), flags, arguments); } DeferredConsumer &consume(const std::string_view &queue, int flags = 0) { return _implementation->consume(queue, std::string_view(), flags, Table()); } DeferredConsumer &consume(const std::string_view &queue, const Table &arguments) { return _implementation->consume(queue, std::string_view(), 0, arguments); } /** * Tell the messages that you are ready to recall/take back messages that messages thar are unroutable. * * When you use the publish() method in combination with the 'immediate' or 'mandatory' flag, rabbitmq * sends back unroutable messages. With this recall() method you can install a sort of pseudo-consumer * that defines how such returned-messages are processed. * * Watch out: when you call this method more than once, you always get access to the same object. You * can thus not install multiple callbacks for the same event. */ DeferredRecall &recall() { return _implementation->recall(); } /** * Cancel a running consume call * * If you want to stop a running consumer, you can use this method with the consumer tag * * @param tag the consumer tag * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(const std::string& tag); * * For example: channel.cancel("myqueue").onSuccess([](const std::string& tag) { * * std::cout << "Stopped consuming under tag " << tag << std::endl; * * }); */ DeferredCancel &cancel(const std::string_view &tag) { return _implementation->cancel(tag); } /** * Retrieve a single message from RabbitMQ * * When you call this method, you can get one single message from the queue (or none * at all if the queue is empty). The deferred object that is returned, should be used * to install a onEmpty() and onSuccess() callback function that will be called * when the message is consumed and/or when the message could not be consumed. * * The following flags are supported: * * - noack if set, consumed messages do not have to be acked, this happens automatically * * @param queue name of the queue to consume from * @param flags optional flags * * The object returns a deferred handler. Callbacks can be installed * using onSuccess(), onEmpty(), onError() and onFinalize() methods. * * The onSuccess() callback has the following signature: * * void myCallback(const Message &message, uint64_t deliveryTag, bool redelivered); * * For example: channel.get("myqueue").onSuccess([](const Message &message, uint64_t deliveryTag, bool redelivered) { * * std::cout << "Message fetched" << std::endl; * * }).onEmpty([]() { * * std::cout << "Queue is empty" << std::endl; * * }); */ DeferredGet &get(const std::string_view &queue, int flags = 0) { return _implementation->get(queue, flags); } /** * Acknoldge a received message * * When a message is received in the DeferredConsumer::onReceived() method, * you must acknowledge it so that RabbitMQ removes it from the queue (unless * you are consuming with the noack option). This method can be used for * this acknowledging. * * The following flags are supported: * * - multiple acknowledge multiple messages: all un-acked messages that were earlier delivered are acknowledged too * * @param deliveryTag the unique delivery tag of the message * @param flags optional flags * @return bool */ bool ack(uint64_t deliveryTag, int flags=0) { return _implementation->ack(deliveryTag, flags); } /** * Reject or nack a message * * When a message was received in the DeferredConsumer::onReceived() method, * and you don't want to acknowledge it, you can also choose to reject it by * calling this reject method. * * The following flags are supported: * * - multiple reject multiple messages: all un-acked messages that were earlier delivered are unacked too * - requeue if set, the message is put back in the queue, otherwise it is dead-lettered/removed * * @param deliveryTag the unique delivery tag of the message * @param flags optional flags * @return bool */ bool reject(uint64_t deliveryTag, int flags=0) { return _implementation->reject(deliveryTag, flags); } /** * Recover all messages that were not yet acked * * This method asks the server to redeliver all unacknowledged messages on a specified * channel. Zero or more messages may be redelivered. * * The following flags are supported: * * - requeue if set, the server will requeue the messages, so the could also end up with at different consumer * * @param flags * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &recover(int flags = 0) { return _implementation->recover(flags); } /** * Close the current channel * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &close() { return _implementation->close(); } /** * Get the channel we're working on * @return uint16_t */ uint16_t id() const { return _implementation->id(); } /** * Some internal classes may touch our implementation */ friend class Tagger; }; /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/channelimpl.h000066400000000000000000000604461470663072600205410ustar00rootroot00000000000000/** * ChannelImpl.h * * Extended channel object that is used internally by the library, but * that has a private constructor so that it can not be used from outside * the AMQP library * * @copyright 2014 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "exchangetype.h" #include "watchable.h" #include "callbacks.h" #include "copiedbuffer.h" #include "deferred.h" #include "monitor.h" #include #include #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class DeferredReceiver; class BasicDeliverFrame; class DeferredConsumer; class BasicGetOKFrame; class ConsumedMessage; class ConnectionImpl; class DeferredDelete; class DeferredCancel; class DeferredConfirm; class DeferredQueue; class DeferredGet; class DeferredRecall; class Connection; class Envelope; class Table; class Frame; /** * Class definition */ class ChannelImpl : public Watchable, public std::enable_shared_from_this { private: /** * Pointer to the connection * @var ConnectionImpl */ ConnectionImpl *_connection = nullptr; /** * Callback when the channel is ready * @var SuccessCallback */ SuccessCallback _readyCallback; /** * Callback when the channel errors out * @var ErrorCallback */ ErrorCallback _errorCallback; /** * Handler that deals with incoming messages as a result of publish operations * @var DeferredRecall */ std::shared_ptr _recall; /** * Handler that deals with publisher confirms frames * @var std::shared_ptr */ std::shared_ptr _confirm; /** * Handlers for all consumers that are active * @var std::map */ std::map> _consumers; /** * Pointer to the oldest deferred result (the first one that is going * to be executed) * * @var Deferred */ std::shared_ptr _oldestCallback; /** * Pointer to the newest deferred result (the last one to be added). * * @var Deferred */ std::shared_ptr _newestCallback; /** * The channel number * @var uint16_t */ uint16_t _id = 0; /** * State of the channel object * @var enum */ enum { state_connected, state_ready, state_closing, state_closed } _state = state_closed; /** * The frames that still need to be send out * * We store the data as well as whether they * should be handled synchronously. * * @var std::queue */ std::queue _queue; /** * Are we currently operating in synchronous mode? Meaning: do we first have * to wait for the answer to previous instructions before we send a new instruction? * @var bool */ bool _synchronous = false; /** * The current object that is busy receiving a message * @var std::shared_ptr */ std::shared_ptr _receiver; /** * Attach the connection * @param connection * @return bool */ bool attach(Connection *connection); /** * Push a deferred result * @param result The deferred result * @return Deferred The object just pushed */ Deferred &push(const std::shared_ptr &deferred); /** * Send a framen and push a deferred result * @param frame The frame to send * @return Deferred The object just pushed */ Deferred &push(const Frame &frame); protected: /** * Construct a channel object * * Note that the constructor is private, and that the Channel class is * a friend. By doing this we ensure that nobody can instantiate this * object, and that it can thus only be used inside the library. */ ChannelImpl(); public: /** * Copy'ing of channel objects is not supported * @param channel */ ChannelImpl(const ChannelImpl &channel) = delete; /** * Destructor */ virtual ~ChannelImpl(); /** * No assignments of other channels * @param channel * @return Channel */ ChannelImpl &operator=(const ChannelImpl &channel) = delete; /** * Invalidate the channel * This method is called when the connection is destructed */ void detach() { // connection is gone _connection = nullptr; } /** * Expose the currently installed callbacks * @return ErrorCallback */ const ErrorCallback &onError() const { return _errorCallback; } const SuccessCallback &onReady() const { return _readyCallback; } /** * Callback that is called when the channel was succesfully created. * @param callback the callback to execute */ inline void onReady(const SuccessCallback& callback) { return onReady(SuccessCallback(callback)); } void onReady(SuccessCallback&& callback) { // store callback _readyCallback = std::move(callback); // direct call if channel is already ready if (_state == state_ready && _readyCallback) _readyCallback(); } /** * Callback that is called when an error occurs. * * Only one error callback can be registered. Calling this function * multiple times will remove the old callback. * * @param callback the callback to execute */ inline void onError(const ErrorCallback& callback) { return onError(ErrorCallback(callback)); } void onError(ErrorCallback&& callback); /** * Pause deliveries on a channel * * This will stop all incoming messages * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &pause(); /** * Resume a paused channel * * This will resume incoming messages * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &resume(); /** * Is the channel usable / not yet closed? * @return bool */ bool usable() const { return _state == state_connected || _state == state_ready; } /** * Is the channel ready / has it passed the initial handshake? * @return bool */ bool ready() const { return _state == state_ready; } /** * Put channel in a confirm mode (RabbitMQ specific) */ DeferredConfirm &confirmSelect(); /** * Start a transaction */ Deferred &startTransaction(); /** * Commit the current transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &commitTransaction(); /** * Rollback the current transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &rollbackTransaction(); /** * declare an exchange * * @param name name of the exchange to declare * @param type type of exchange * @param flags additional settings for the exchange * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &declareExchange(const std::string_view &name, ExchangeType type, int flags, const Table &arguments); /** * bind two exchanges * @param source exchange which binds to target * @param target exchange to bind to * @param routingKey routing key * @param arguments additional arguments for binding * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &bindExchange(const std::string_view &source, const std::string_view &target, const std::string_view &routingkey, const Table &arguments); /** * unbind two exchanges * @param source the source exchange * @param target the target exchange * @param routingkey the routing key * @param arguments additional unbind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &unbindExchange(const std::string_view &source, const std::string_view &target, const std::string_view &routingkey, const Table &arguments); /** * remove an exchange * * @param name name of the exchange to remove * @param flags additional settings for deleting the exchange * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &removeExchange(const std::string_view &name, int flags); /** * declare a queue * @param name queue name * @param flags additional settings for the queue * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ DeferredQueue &declareQueue(const std::string_view &name, int flags, const Table &arguments); /** * Bind a queue to an exchange * * @param exchangeName name of the exchange to bind to * @param queueName name of the queue * @param routingkey routingkey * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &bindQueue(const std::string_view &exchangeName, const std::string_view &queueName, const std::string_view &routingkey, const Table &arguments); /** * Unbind a queue from an exchange * * @param exchange the source exchange * @param queue the target queue * @param routingkey the routing key * @param arguments additional bind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &unbindQueue(const std::string_view &exchangeName, const std::string_view &queueName, const std::string_view &routingkey, const Table &arguments); /** * Purge a queue * @param queue queue to purge * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(AMQP::Channel *channel, uint32_t messageCount); * * For example: channel.purgeQueue("myqueue").onSuccess([](AMQP::Channel *channel, uint32_t messageCount) { * * std::cout << "Queue purged, all " << messageCount << " messages removed" << std::endl; * * }); */ DeferredDelete &purgeQueue(const std::string_view &name); /** * Remove a queue * @param queue queue to remove * @param flags additional flags * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(AMQP::Channel *channel, uint32_t messageCount); * * For example: channel.declareQueue("myqueue").onSuccess([](AMQP::Channel *channel, uint32_t messageCount) { * * std::cout << "Queue deleted, along with " << messageCount << " messages" << std::endl; * * }); */ DeferredDelete &removeQueue(const std::string_view &name, int flags); /** * Publish a message to an exchange * * If the mandatory or immediate flag is set, and the message could not immediately * be published, the message will be returned to the client. * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags optional flags * @return bool */ bool publish(const std::string_view &exchange, const std::string_view &routingKey, const Envelope &envelope, int flags); /** * Set the Quality of Service (QOS) of the entire connection * @param prefetchCount maximum number of messages to prefetch * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * @param count number of messages to pre-fetch * @param global share count between all consumers on the same channel */ Deferred &setQos(uint16_t prefetchCount, bool global = false); /** * Tell the RabbitMQ server that we're ready to consume messages * @param queue the queue from which you want to consume * @param tag a consumer tag that will be associated with this consume operation * @param flags additional flags * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(AMQP::Channel *channel, const std::string& tag); * * For example: channel.declareQueue("myqueue").onSuccess([](AMQP::Channel *channel, const std::string& tag) { * * std::cout << "Started consuming under tag " << tag << std::endl; * * }); */ DeferredConsumer& consume(const std::string_view &queue, const std::string_view &tag, int flags, const Table &arguments); /** * Tell that you are prepared to recall/take back messages that could not be * published. This is only meaningful if you pass the 'immediate' or 'mandatory' * flag to publish() operations. * * THis function returns a deferred handler more or less similar to the object * return by the consume() method and that can be used to install callbacks that * handle the recalled messages. */ DeferredRecall &recall(); /** * Cancel a running consumer * @param tag the consumer tag * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(const std::string& tag); * * For example: channel.declareQueue("myqueue").onSuccess([](const std::string& tag) { * * std::cout << "Started consuming under tag " << tag << std::endl; * * }); */ DeferredCancel &cancel(const std::string_view &tag); /** * Retrieve a single message from RabbitMQ * * When you call this method, you can get one single message from the queue (or none * at all if the queue is empty). The deferred object that is returned, should be used * to install a onEmpty() and onSuccess() callback function that will be called * when the message is consumed and/or when the message could not be consumed. * * The following flags are supported: * * - noack if set, consumed messages do not have to be acked, this happens automatically * * @param queue name of the queue to consume from * @param flags optional flags * * The object returns a deferred handler. Callbacks can be installed * using onSuccess(), onEmpty(), onError() and onFinalize() methods. * * The onSuccess() callback has the following signature: * * void myCallback(const Message &message, uint64_t deliveryTag, bool redelivered); * * For example: channel.get("myqueue").onSuccess([](const Message &message, uint64_t deliveryTag, bool redelivered) { * * std::cout << "Message fetched" << std::endl; * * }).onEmpty([]() { * * std::cout << "Queue is empty" << std::endl; * * }); */ DeferredGet &get(const std::string_view &queue, int flags = 0); /** * Acknowledge a message * @param deliveryTag the delivery tag * @param flags optional flags * @return bool */ bool ack(uint64_t deliveryTag, int flags); /** * Reject a message * @param deliveryTag the delivery tag * @param flags optional flags * @return bool */ bool reject(uint64_t deliveryTag, int flags); /** * Recover messages that were not yet ack'ed * @param flags optional flags * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &recover(int flags); /** * Close the current channel * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &close(); /** * Get the channel we're working on * @return uint16_t */ uint16_t id() const { return _id; } /** * Send a frame over the channel * @param frame frame to send * @return bool was frame succesfully sent? */ bool send(CopiedBuffer &&frame); /** * Send a frame over the channel * @param frame frame to send * @return bool was frame succesfully sent? */ bool send(const Frame &frame); /** * Is this channel waiting for an answer before it can send furher instructions * @return bool */ bool waiting() const { return _synchronous || !_queue.empty(); } /** * The max payload size for frames * @return uint32_t */ uint32_t maxPayload() const; /** * Signal the channel that a synchronous operation was completed, and that any * queued frames can be sent out. * @return false if an error on the connection level occurred, true if not */ bool flush(); /** * Report to the handler that the channel is opened */ void reportReady() { // if we are still in connected state we are now ready if (_state == state_connected) _state = state_ready; // send out more instructions if there is a queue flush(); // inform handler if (_readyCallback) _readyCallback(); } /** * Report to the handler that the channel is closed * * Returns whether the channel object is still valid * * @return bool */ bool reportClosed() { // change state _state = state_closed; // create a monitor, because the callbacks could destruct the current object Monitor monitor(this); // and pass on to the reportSuccess() method which will call the // appropriate deferred object to report the successful operation bool result = reportSuccess(); // leap out if object no longer exists if (!monitor.valid()) return result; // all later deferred objects should report an error, because it // was not possible to complete the instruction as the channel is // now closed (but the channel onError does not have to run) reportError("Channel has been closed", false); // done return result; } /** * Report success * * Returns whether the channel object is still valid * * @param mixed * @return bool */ template bool reportSuccess(Arguments ...parameters) { // skip if there is no oldest callback if (!_oldestCallback) return true; // we are going to call callbacks that could destruct the channel Monitor monitor(this); // flush the queue, which will send the next operation if the current operation was synchronous flush(); // the call to flush may have resulted in a call to reportError if (!monitor.valid()) return false; // copy the callback (so that it will not be destructed during // the "reportSuccess" call, if the channel is destructed during the call) auto cb = _oldestCallback; // the call to flush might have caused the callback to have been invoked; check once more if (!cb) return true; // call the callback auto next = cb->reportSuccess(std::forward(parameters)...); // leap out if channel no longer exist if (!monitor.valid()) return false; // in case the callback-shared-pointer is still kept in scope (for example because it // is stored in the list of consumers), we do want to ensure that it no longer maintains // a chain of queued deferred objects cb->unchain(); // set the oldest callback _oldestCallback = next; // if there was no next callback, the newest callback was just used if (!next) _newestCallback = nullptr; // we are still valid return true; } /** * Report that a consumer was cancelled by the server (for example because the * queue was removed or the node on which the queue was stored was terminated) * @param tag the consumer tag */ void reportCancelled(const std::string &tag); /** * Report an error message on a channel * @param message the error message * @param notifyhandler should the channel-wide handler also be called? */ void reportError(const char *message, bool notifyhandler = true); /** * Install a consumer * @param consumertag The consumer tag * @param consumer The consumer object */ void install(const std::string &consumertag, const std::shared_ptr &consumer) { // install the consumer handler _consumers[consumertag] = consumer; } /** * Install the current consumer * @param receiver The receiver object */ void install(const std::shared_ptr &receiver) { // store object as current receiver _receiver = receiver; } /** * Uninstall a consumer callback * @param consumertag The consumer tag */ void uninstall(const std::string &consumertag) { // erase the callback _consumers.erase(consumertag); } /** * Fetch the receiver for a specific consumer tag * @param consumertag the consumer tag * @return the receiver object */ DeferredConsumer *consumer(const std::string &consumertag) const; /** * Retrieve the current object that is receiving a message * @return The handler responsible for the current message */ DeferredReceiver *receiver() const { return _receiver.get(); } /** * Retrieve the recalls-object that handles bounces * @return The deferred recall object */ DeferredRecall *recalls() const { return _recall.get(); } /** * Retrieve the deferred confirm that handles publisher confirms * @return The deferred confirm object */ DeferredConfirm *confirm() const { return _confirm.get(); } /** * The channel class is its friend, thus can it instantiate this object */ friend class Channel; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/classes.h000066400000000000000000000012021470663072600176650ustar00rootroot00000000000000/** * Classes.h * * List of all declared classes * * @copyright 2014 - 2017 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * All classes defined by this library */ class Array; class BasicDeliverFrame; class BasicGetOKFrame; class BasicHeaderFrame; class BasicReturnFrame; class BasicAckFrame; class BasicNackFrame; class BodyFrame; class Channel; class Connection; class ConnectionHandler; class ConnectionImpl; class CopiedBuffer; class Exchange; class Frame; class Login; class Monitor; class OutBuffer; class InBuffer; class Table; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/connection.h000066400000000000000000000171511470663072600204010ustar00rootroot00000000000000/** * Class describing a mid-level Amqp connection * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * Class definition */ class Connection { private: /** * The actual implementation * @var ConnectionImpl */ ConnectionImpl _implementation; public: /** * Construct an AMQP object based on full login data * * The first parameter is a handler object. This handler class is * an interface that should be implemented by the caller. * * @param handler Connection handler * @param login Login data * @param vhost Vhost to use */ Connection(ConnectionHandler *handler, const Login &login, const std::string &vhost) : _implementation(this, handler, login, vhost) {} /** * Construct with default vhost * @param handler Connection handler * @param login Login data */ Connection(ConnectionHandler *handler, const Login &login) : _implementation(this, handler, login, "/") {} /** * Construct an AMQP object with default login data and default vhost * @param handler Connection handler */ Connection(ConnectionHandler *handler, const std::string &vhost) : _implementation(this, handler, Login(), vhost) {} /** * Construct an AMQP object with default login data and default vhost * @param handler Connection handler */ Connection(ConnectionHandler *handler) : _implementation(this, handler, Login(), "/") {} /** * No copy'ing, we do not support having two identical connection objects * @param connection */ Connection(const Connection &connection) = delete; /** * Destructor */ virtual ~Connection() {} /** * No assignments of other connections * @param connection * @return Connection */ Connection &operator=(const Connection &connection) = delete; /** * Retrieve the login data * @return Login */ const Login &login() const { return _implementation.login(); } /** * Retrieve the vhost * @return string */ const std::string &vhost() const { return _implementation.vhost(); } /** * Send a ping/heartbeat to the channel to keep it alive * @return bool */ bool heartbeat() { return _implementation.heartbeat(); } /** * Parse data that was recevied from RabbitMQ * * Every time that data comes in from RabbitMQ, you should call this method to parse * the incoming data, and let it handle by the AMQP library. This method returns the number * of bytes that were processed. * * If not all bytes could be processed because it only contained a partial frame, you should * call this same method later on when more data is available. The AMQP library does not do * any buffering, so it is up to the caller to ensure that the old data is also passed in that * later call. * * @param buffer buffer to decode * @param size size of the buffer to decode * @return number of bytes that were processed */ uint64_t parse(const char *buffer, size_t size) { return _implementation.parse(ByteBuffer(buffer, size)); } /** * Parse data that was recevied from RabbitMQ * * Every time that data comes in from RabbitMQ, you should call this method to parse * the incoming data, and let it handle by the AMQP library. This method returns the number * of bytes that were processed. * * If not all bytes could be processed because it only contained a partial frame, you should * call this same method later on when more data is available. The AMQP library does not do * any buffering, so it is up to the caller to ensure that the old data is also passed in that * later call. * * This method accepts a buffer object. This is an interface that is defined by the AMQP * library, that can be implemented by you to allow faster access to a buffer. * * @param buffer buffer to decode * @return number of bytes that were processed */ uint64_t parse(const Buffer &buffer) { return _implementation.parse(buffer); } /** * Report that the connection was lost in the middle of an operation * * The AMQP protocol normally has a nice closing handshake, and a connection * is elegantly closed via calls to the close() and parse() methods. The parse() * methods recognizes the close-confirmation and will report this to the handler. * However, if you notice yourself that the connection is lost in the middle of * an operation (for example due to a crashing RabbitMQ server), you should * explicitly tell the connection object about it, so that it can cancel all * pending operations. For all pending operations the error and finalize callbacks * will be called. The ConnectionHandler::onError() method will however _not_ be * called. * * @param message the message that has to be passed to all error handlers * @return bool false if the connection already was failed */ bool fail(const char *message) { return _implementation.fail(message); } /** * Max frame size * * If you allocate memory to receive data that you are going to pass to the parse() method, * it might be useful to have an insight in the max frame size. The parse() method process * one frame at a time, so you must at least be able to read in buffers of this specific * frame size. * * @return size_t */ uint32_t maxFrame() const { return _implementation.maxFrame(); } /** * Expected number of bytes for the next parse() call. * * This method returns the number of bytes that the next call to parse() at least expects to * do something meaningful with it. * * @return size_t */ uint32_t expected() const { return _implementation.expected(); } /** * Is the connection ready to accept instructions / has passed the login handshake and not closed? * @return bool */ bool ready() const { return _implementation.ready(); } /** * Is (or was) the connection initialized * @return bool */ bool initialized() const { return _implementation.initialized(); } /** * Is the connection in a usable state, or is it already closed or * in the process of being closed? * @return bool */ bool usable() const { return _implementation.usable(); } /** * Close the connection * This will close all channels * @return bool */ bool close() { return _implementation.close(); } /** * Retrieve the number of channels that are active for this connection * @return std::size_t */ std::size_t channels() const { return _implementation.channels(); } /** * Is the connection busy waiting for an answer from the server? (in the * meantime you can already send more instructions over it) * @return bool */ bool waiting() const { return _implementation.waiting(); } /** * Some classes have access to private properties */ friend class ChannelImpl; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/connectionhandler.h000066400000000000000000000215501470663072600217350ustar00rootroot00000000000000/** * ConnectionHandler.h * * Interface that should be implemented by the caller of the library and * that is passed to the AMQP connection. This interface contains all sorts * of methods that are called when data needs to be sent, or when the * AMQP connection ends up in a broken state. * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class Connection; /** * Class definition */ class ConnectionHandler { public: /** * Destructor */ virtual ~ConnectionHandler() = default; /** * When the connection is being set up, the client and server exchange * some information. This includes for example their name and version, * copyright statement and the operating system name. Nothing in this * exchange of information is very relevant for the actual AMQP protocol, * but by overriding this method you can read out the information that * was sent by the server, and you can decide which information you * want to send back that describe the client. In RabbitMQ's management * console the client-properties are visible on the "connections" tab, * which could be helpful in certain scenarios, like debugging. * * The read-only "server" parameter contains the information sent by * the server, while the "client" table may be filled with information * about your application. The AMQP protocol says that this table should * at least be filled with data for the "product", "version", "platform", * "copyright" and "information" keys. However, you do not have to * override this method, and even when you do, you do not have to ensure * that these properties are indeed set, because the AMQP-CPP library * takes care of filling in properties that were not explicitly set. * * @param connection The connection about which information is exchanged * @param server Properties sent by the server * @param client Properties that are to be sent back */ virtual void onProperties(Connection *connection, const Table &server, Table &client) { // make sure compilers dont complaint about unused parameters (void) connection; (void) server; (void) client; } /** * Method that is called when the heartbeat frequency is negotiated * between the server and the client durion connection setup. You * normally do not have to override this method, because in the default * implementation the suggested heartbeat is simply rejected by the client. * * However, if you want to enable heartbeats you can override this * method. You should "return interval" if you want to accept the * heartbeat interval that was suggested by the server, or you can * return an alternative value if you want a shorter or longer interval. * Return 0 if you want to disable heartbeats. * * If heartbeats are enabled, you yourself are responsible to send * out a heartbeat every *interval / 2* number of seconds by calling * the Connection::heartbeat() method. * * @param connection The connection that suggested a heartbeat interval * @param interval The suggested interval from the server * @return uint16_t The interval to use */ virtual uint16_t onNegotiate(Connection *connection, uint16_t interval) { // make sure compilers dont complain about unused parameters (void) connection; (void) interval; // default implementation, disable heartbeats return 0; } /** * Method that is called by AMQP-CPP when data has to be sent over the * network. You must implement this method and send the data over a * socket that is connected with RabbitMQ. * * Note that the AMQP library does no buffering by itself. This means * that this method should always send out all data or do the buffering * itself. * * @param connection The connection that created this output * @param buffer Data to send * @param size Size of the buffer */ virtual void onData(Connection *connection, const char *buffer, size_t size) = 0; /** * Method that is called when the AMQP-CPP library received a heartbeat * frame that was sent by the server to the client. * * You do not have to do anything here, the client sends back a heartbeat * frame automatically, but if you like, you can implement/override this * method if you want to be notified of such heartbeats * * @param connection The connection over which the heartbeat was received */ virtual void onHeartbeat(Connection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * When the connection ends up in an error state this method is called. * This happens when data comes in that does not match the AMQP protocol, * or when an error message was sent by the server to the client. * * After this method is called, the connection no longer is in a valid * state and can no longer be used. * * This method has an empty default implementation, although you are very * much advised to implement it. When an error occurs, the connection * is no longer usable, so you probably want to know. * * @param connection The connection that entered the error state * @param message Error message */ virtual void onError(Connection *connection, const char *message) { // make sure compilers dont complain about unused parameters (void) connection; (void) message; } /** * Method that is called when the login attempt succeeded. After this method * is called, the connection is ready to use, and the RabbitMQ server is * ready to receive instructions. * * According to the AMQP protocol, you must wait for the connection to become * ready (and this onConnected method to be called) before you can start * sending instructions to RabbitMQ. However, if you prematurely do send * instructions, this AMQP-CPP library caches all methods that you call * before the connection is ready and flushes them the moment the connection * has been set up, so technically there is no real reason to wait for this * method to be called before you send the first instructions. * * @param connection The connection that can now be used */ virtual void onReady(Connection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the AMQP connection was closed. * * This is the counter part of a call to Connection::close() and it confirms * that the connection was _correctly_ closed. Note that this only applies * to the AMQP connection, the underlying TCP connection is not managed by * AMQP-CPP and is still active. * * @param connection The connection that was closed and that is now unusable */ virtual void onClosed(Connection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the AMQP connection was blocked. * * This method is called, when the server connection gets blocked for the first * time due to the broker running low on a resource (memory or disk). For * example, when a RabbitMQ node detects that it is low on RAM, it sends a * notification to all connected publishing clients supporting this feature. * If before the connections are unblocked the node also starts running low on * disk space, another notification will not be sent. * * @param connection The connection that was blocked * @param reason Why was the connection blocked */ virtual void onBlocked(Connection *connection, const char *reason) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the AMQP connection is no longer blocked. * * This method is called when all resource alarms have cleared and the * connection is fully unblocked. * * @param connection The connection that is no longer blocked */ virtual void onUnblocked(Connection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/connectionimpl.h000066400000000000000000000301251470663072600212570ustar00rootroot00000000000000/** * Connection implementation * * This is the implementation of the connection - a class that can only be * constructed by the connection class itselves and that has all sorts of * methods that are only useful inside the library * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "watchable.h" #include "connectionhandler.h" #include "channelimpl.h" #include "copiedbuffer.h" #include "monitor.h" #include "login.h" #include #include #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class Connection; class Buffer; class Frame; /** * Class definition */ class ConnectionImpl : public Watchable { protected: /** * The parent connection object * @var Connection */ Connection *_parent; /** * The connection handler * @var ConnectionHandler */ ConnectionHandler *_handler; /** * State of the connection * The current state is the last frame sent to the server * @var enum */ enum { state_protocol, // protocol headers are being passed state_handshake, // busy with the handshake to open the connection state_connected, // connection is set up and ready for communication state_closing, // connection is busy closing (we have sent the close frame) state_closed, // connection is closed } _state = state_protocol; /** * Has the close() method been called? If this is true, we automatically * send a close-frame after all pending operations are finsihed. * @var bool */ bool _closed = false; /** * All channels that are active * @var std::unordered_map> */ std::unordered_map> _channels; /** * The last unused channel ID * @var uint16_t */ uint16_t _nextFreeChannel = 1; /** * Max number of channels (0 for unlimited) * @var uint16_t */ uint16_t _maxChannels = 0; /** * Max frame size * @var uint32_t */ uint32_t _maxFrame = 4096; /** * Number of expected bytes that will hold the next incoming frame * We start with seven because that is the header of a frame * @var uint32_t */ uint32_t _expected = 7; /** * The login for the server (login, password) * @var Login */ Login _login; /** * Vhost to connect to * @var string */ std::string _vhost; /** * Queued messages that should be sent after the connection has been established * @var queue */ std::queue _queue; /** * Helper method to send the close frame * Return value tells if the connection is still valid * @return bool */ bool sendClose(); /** * Is any channel waiting for an answer on a synchronous call? * @return bool */ bool waitingChannels() const; /** * Is the channel waiting for a response from the peer (server) * @return bool */ bool waiting() const; /** * Helper method for the fail() method * @param monitor * @param message * @return bool */ bool fail(const Monitor &monitor, const char *message); private: /** * Construct an AMQP object based on full login data * * The first parameter is a handler object. This handler class is * an interface that should be implemented by the caller. * * Note that the constructor is private to ensure that nobody can construct * this class, only the real Connection class via a friend construct * * @param parent Parent connection object * @param handler Connection handler * @param login Login data */ ConnectionImpl(Connection *parent, ConnectionHandler *handler, const Login &login, const std::string &vhost); public: /** * Copy'ing connections is impossible * @param connection */ ConnectionImpl(const ConnectionImpl &connection) = delete; /** * Destructor */ virtual ~ConnectionImpl(); /** * No assignments of other connections * @param connection * @return ConnectionImpl */ ConnectionImpl &operator=(const ConnectionImpl &connection) = delete; /** * What is the state of the connection - is the protocol handshake completed? * @return bool */ bool protocolOk() const { // must be busy doing the connection handshake, or already connected return _state == state_handshake || _state == state_connected; } /** * Mark the protocol as being ok * @param server properties sent by the server * @param client properties to be send back */ void setProtocolOk(const Table &server, Table &client) { // if object is destructed Monitor monitor(this); // check if user-space wants to set these properties _handler->onProperties(_parent, server, client); // leap out if userspace destructed the object if (!monitor.valid()) return; // move on to handshake state if (_state == state_protocol) _state = state_handshake; } /** * Are we fully connected and ready for instructions? This is true after the initial * protocol and login handshake were completed and the connection is not closed. * @return bool */ bool ready() const { // state must be connected return _state == state_connected; } /** * Is (or was) the connection initialized * @return bool */ bool initialized() const { // We are initalized if we have passed the initialized state. So we are in // a connected, closing, or closed state. return _state == state_connected || _state == state_closing || _state == state_closed; } /** * Are we closing down? * @return bool */ bool closing() const { // state must be connected return _state == state_closing; } /** * Are we closed? * @return bool */ bool closed() const { // state must be connected return _state == state_closed; } /** * Is the connection in a usable state / not yet closed? * @return bool */ bool usable() const { return (_state == state_protocol || _state == state_handshake || _state == state_connected) && !_closed; } /** * Mark the connection as ready */ void setReady(); /** * Retrieve the login data * @return Login */ const Login &login() const { return _login; } /** * Retrieve the vhost * @return string */ const std::string &vhost() const { return _vhost; } /** * Store the max number of channels and max number of frames * @param channels max number of channels * @param size max frame size */ void setCapacity(uint16_t channels, uint32_t size) { _maxChannels = channels; _maxFrame = size; } /** * The max frame size * @return uint32_t */ uint32_t maxFrame() const { return _maxFrame; } /** * The max payload size for body frames * @return uint32_t */ uint32_t maxPayload() const { // 8 bytes for header and end-of-frame byte return _maxFrame - 8; } /** * The number of bytes that can best be passed to the next call to the parse() method * @return uint32_t */ uint32_t expected() const { return _expected; } /** * Add a channel to the connection, and return the channel ID that it * is allowed to use, or 0 when no more ID's are available * @param channel * @return uint16_t */ uint16_t add(const std::shared_ptr &channel); /** * Remove a channel * @param channel */ void remove(const ChannelImpl *channel); /** * Parse the buffer into a recognized frame * * Every time that data comes in on the connection, you should call this method to parse * the incoming data, and let it handle by the AMQP library. This method returns the number * of bytes that were processed. * * If not all bytes could be processed because it only contained a partial frame, you should * call this same method later on when more data is available. The AMQP library does not do * any buffering, so it is up to the caller to ensure that the old data is also passed in that * later call. * * @param buffer buffer to decode * @return number of bytes that were processed */ uint64_t parse(const Buffer &buffer); /** * Fail all pending - this can be called by user-space when it is recognized that the * underlying connection is lost. All error-handlers for all operations and open * channels will be called. This will _not_ call ConnectionHandler::onError() method. * * @return bool */ bool fail(const char *message); /** * Close the connection * This will also close all channels * @return bool */ bool close(); /** * Send a frame over the connection * * This is an internal method that you normally do not have to call yourself * * @param frame the frame to send * @return bool */ bool send(const Frame &frame); /** * Send buffered data over the connection * * @param buffer the buffer with data to send */ bool send(CopiedBuffer &&buffer); /** * Get a channel by its identifier * * This method only works if you had already created the channel before. * This is an internal method that you will not need if you cache the channel * object. * * @param number channel identifier * @return channel the channel object, or nullptr if not yet created */ std::shared_ptr channel(int number) { auto iter = _channels.find(number); return iter == _channels.end() ? nullptr : iter->second; } /** * Report an error message * @param message */ void reportError(const char *message); /** * Report that the connection is closed */ void reportClosed() { // change state _state = state_closed; // inform the handler _handler->onClosed(_parent); } /** * Report that the connection is blocked * @param reason */ void reportBlocked(const char *reason) { // inform the handler _handler->onBlocked(_parent, reason); } /** * Report that the connection is unblocked */ void reportUnblocked() { // inform the handler _handler->onUnblocked(_parent); } /** * Retrieve the amount of channels this connection has * @return std::size_t */ std::size_t channels() const { return _channels.size(); } /** * Set the heartbeat timeout * @param heartbeat suggested heartbeat timeout by server * @return uint16_t accepted heartbeat timeout from client */ uint16_t setHeartbeat(uint16_t heartbeat) { // pass to the handler return _handler->onNegotiate(_parent, heartbeat); } /** * Report a heartbeat to the connection handler */ void reportHeartbeat() { // pass to handler _handler->onHeartbeat(_parent); } /** * Send a heartbeat to keep the connection alive * @return bool */ bool heartbeat(); /** * The actual connection is a friend and can construct this class */ friend class Connection; friend class ChannelImpl; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/copiedbuffer.h000066400000000000000000000055071470663072600207010ustar00rootroot00000000000000/** * CopiedBuffer.h * * If an output buffer (frame) cannot immediately be sent, we copy it to * memory using this CopiedBuffer class * * @copyright 2017 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "endian.h" #include "frame.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class CopiedBuffer : public OutBuffer { private: /** * The total capacity of the out buffer * @var size_t */ size_t _capacity; /** * Pointer to the beginning of the buffer * @var const char * */ char *_buffer; /** * Current size of the buffer * @var size_t */ size_t _size = 0; /** * Whether the frame is synchronous * @var bool */ bool _synchronous = false; protected: /** * The method that adds the actual data * @param data * @param size */ virtual void append(const void *data, size_t size) override { // copy into the buffer memcpy(_buffer + _size, data, size); // update the size _size += size; } public: /** * Constructor * @param frame */ CopiedBuffer(const Frame &frame) : _capacity(frame.totalSize()), _buffer((char *)malloc(_capacity)), _synchronous(frame.synchronous()) { // tell the frame to fill this buffer frame.fill(*this); // append an end of frame byte (but not when still negotiating the protocol) if (frame.needsSeparator()) add((uint8_t)206); } /** * Disabled copy constructor to prevent expensive copy operations * @param that */ CopiedBuffer(const CopiedBuffer &that) = delete; /** * Move constructor * @param that */ CopiedBuffer(CopiedBuffer &&that) : _capacity(that._capacity), _buffer(that._buffer), _size(that._size), _synchronous(that._synchronous) { // reset the other object that._buffer = nullptr; that._size = 0; that._capacity = 0; } /** * Destructor */ virtual ~CopiedBuffer() { // deallocate the buffer free(_buffer); } /** * Get access to the internal buffer * @return const char* */ const char *data() const { // expose member return _buffer; } /** * Current size of the output buffer * @return size_t */ size_t size() const { // expose member return _size; } /** * Whether the frame is to be sent synchronously * @return bool */ bool synchronous() const noexcept { // expose member return _synchronous; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/decimalfield.h000066400000000000000000000125641470663072600206470ustar00rootroot00000000000000/** * Decimal field type for AMQP * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "field.h" #include "outbuffer.h" #include "inbuffer.h" /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class DecimalField : public Field { /** * To preserve precision the decision is made to work with the places and number. * These values are sent in the framedata, so no precision will be lost. * Other options, such as floats, doubles, Decimal32 etc result in loss of precision * and this is something which is not acceptable. * * Only (in)equality and assignment operators are implemented since the decimalfield * is not supposed to be altered. * e.q. ==, != and = * * When requesting the value of this object there are 3 choices; * float, double or DecimalField * e.g. valueFloat(), valueDouble() and value() */ private: /** * The number of places, which means the number of decimals * e.g. number = 1234, places = 2, true value is 12.34 */ uint8_t _places; /** * The number without the decimals */ uint32_t _number; protected: /** * Write encoded payload to the given buffer. */ virtual void fill(OutBuffer& buffer) const override { // encode fields buffer.add(_places); buffer.add(_number); } public: /** * Construct decimal field * * @param places the number of places * @param number the integer number */ DecimalField(uint8_t places = 0, uint32_t number = 0) : _places(places), _number(number) {} /** * Construct based on incoming data * @param frame */ DecimalField(InBuffer &frame) { _places = frame.nextUint8(); _number = frame.nextUint32(); } /** * Destructor */ virtual ~DecimalField() {} /** * Create a new identical instance of this object * @return unique_ptr */ virtual std::unique_ptr clone() const override { return std::unique_ptr(new DecimalField(_places, _number)); } /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const override { // output floating point value stream << "decimal(" << _number / pow(10.0f, _places) << ")"; } /** * Assign a new value * * @param value new value for field * @return DecimalField */ DecimalField& operator=(const DecimalField& value) { // if it's the same object, skip assignment and just return *this if (this == &value) return *this; // not the same object, copy values to this object. _places = value._places; _number = value._number; // allow chaining return *this; } /** * Casts decimalfield to double * e.g. "double x = decimalfield" will work * * @return double value of decimalfield in double format */ virtual operator double() const override { return _number / pow(10.0f, _places); } /** * Casts decimalfield to float * e.g. "float x = decimalfield" will work * * @return float value of decimalfield in float format */ virtual operator float() const override { return static_cast(_number / pow(10.0f, _places)); } /** * Check for equality between this and another DecimalField * * @param value value to be checked for equality * @return boolean whether values are equal */ bool operator==(const DecimalField& value) const { // check if everything is the same // precision is taken into account, e.q. 1.0 != 1.00 // meaning number:10, places:1 is not equal to number:100, places:2 return _number == value.number() && _places == value.places(); } /** * Check for inequality between this and another DecimalField * * @param value value to be checked for inequality * @return boolean whether values are inequal */ bool operator!=(const DecimalField& value) const { return !(*this == value); } /** * Get the size this field will take when * encoded in the AMQP wire-frame format */ virtual size_t size() const override { // the sum of all fields return 5; } /** * Get the number of places * @return uint8_t */ uint8_t places() const { return _places; } /** * Get the number without decimals * @return uint32_t */ uint32_t number() const { return _number; } /** * Return the DecimalField * To preserve precision DecimalField is returned, containing the number and places. * @return return DecimalField */ DecimalField value() const { return *this; } /** * We are a decimal field * * @return true, because we are a decimal field */ bool isDecimal() const override { return true; } /** * Get the type ID that is used to identify this type of * field in a field table */ virtual char typeID() const override { return 'D'; } }; /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferred.h000066400000000000000000000176531470663072600200310ustar00rootroot00000000000000/** * Deferred.h * * Class describing a set of actions that could * possibly happen in the future that can be * caught. * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "callbacks.h" /** * Set up namespace */ namespace AMQP { // forward declaration class ChannelImpl; /** * Class definition */ class Deferred { private: /** * Callback to execute either way * @var FinalizeCallback */ FinalizeCallback _finalizeCallback; protected: /** * Callback to execute on success * @var SuccessCallback */ SuccessCallback _successCallback; /** * Callback to execute on failure * @var ErrorCallback */ ErrorCallback _errorCallback; /** * Pointer to the next deferred object * @var Deferred */ std::shared_ptr _next; /** * Do we already know we failed? * @var bool */ bool _failed; /** * The next deferred object * @return Deferred */ const std::shared_ptr &next() const { return _next; } /** * Indicate success * @return Deferred Next deferred result */ virtual const std::shared_ptr &reportSuccess() const { // execute callbacks if registered if (_successCallback) _successCallback(); // return the next deferred result return _next; } /** * Report success for queue declared messages * @param name Name of the new queue * @param messagecount Number of messages in the queue * @param consumercount Number of consumers linked to the queue * @return Deferred Next deferred result */ virtual const std::shared_ptr &reportSuccess(const std::string &name, uint32_t messagecount, uint32_t consumercount) const { // make sure compilers dont complain about unused parameters (void) name; (void) messagecount; (void) consumercount; // this is the same as a regular success message return reportSuccess(); } /** * Report success for frames that report delete operations * @param messagecount Number of messages that were deleted * @return Deferred */ virtual const std::shared_ptr &reportSuccess(uint32_t messagecount) const { // make sure compilers dont complain about unused parameters (void) messagecount; // this is the same as a regular success message return reportSuccess(); } /** * Report success for a get operation * * @param messagecount Number of messages left in the queue * @param deliveryTag Delivery tag of the message coming in * @param redelivered Was the message redelivered? */ virtual const std::shared_ptr &reportSuccess(uint32_t messagecount, uint64_t deliveryTag, bool redelivered) { // make sure compilers dont complain about unused parameters (void) messagecount; (void) deliveryTag; (void) redelivered; // this is the same as a regular success message return reportSuccess(); } /** * Report success for frames that report cancel operations * @param name Consumer tag that is cancelled * @return Deferred */ virtual const std::shared_ptr &reportSuccess(const std::string &name) { // make sure compilers dont complain about unused parameters (void) name; // this is the same as a regular success message return reportSuccess(); } /** * Indicate failure * @param error Description of the error that occured * @return Deferred Next deferred result */ const std::shared_ptr &reportError(const char *error) { // from this moment on the object should be listed as failed _failed = true; // execute callbacks if registered if (_errorCallback) _errorCallback(error); // return the next deferred result return _next; } /** * Add a pointer to the next deferred result * @param deferred */ void add(const std::shared_ptr &deferred) { // store pointer _next = deferred; } /** * Remove this object from the chain of deferreds */ void unchain() { // we no longer need the next pointer _next = nullptr; } /** * The channel implementation may call our * private members and construct us */ friend class ChannelImpl; friend class Tagger; public: /** * Protected constructor that can only be called * from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param failed are we already failed? */ Deferred(bool failed = false) : _failed(failed) {} public: /** * Deleted copy and move constructors */ Deferred(const Deferred &that) = delete; Deferred(Deferred &&that) = delete; /** * Destructor */ virtual ~Deferred() { // report to the finalize callback if (_finalizeCallback) _finalizeCallback(); } /** * Cast to a boolean */ operator bool () const { return !_failed; } /** * Register a function to be called * if and when the operation succesfully * completes. * * Only one callback can be registered at a time. * Successive calls to this function will clear * callbacks registered before. * * @param callback the callback to execute */ inline Deferred &onSuccess(const SuccessCallback& callback) { return onSuccess(SuccessCallback(callback)); } Deferred &onSuccess(SuccessCallback&& callback) { // store callback _successCallback = std::move(callback); // allow chaining return *this; } /** * Register a function to be called * if and when the operation fails. * * Only one callback can be registered at a time. * Successive calls to this function will clear * callbacks registered before. * * @param callback the callback to execute */ inline Deferred &onError(const ErrorCallback& callback) { return onError(ErrorCallback(callback)); } Deferred &onError(ErrorCallback&& callback) { // store callback _errorCallback = std::move(callback); // if the object is already in a failed state, we call the callback right away if (_failed) _errorCallback("Frame could not be sent"); // allow chaining return *this; } /** * Register a function to be called * if and when the operation completes * or fails. This function will be called * either way. * * In the case of success, the provided * error parameter will be an empty string. * * Only one callback can be registered at at time. * Successive calls to this function will clear * callbacks registered before. * * @param callback the callback to execute */ inline Deferred &onFinalize(const FinalizeCallback& callback) { return onFinalize(FinalizeCallback(callback)); } Deferred &onFinalize(FinalizeCallback&& callback) { // if the object is already in a failed state, we call the callback right away if (_failed) callback(); // otherwise we store callback until it's time for the call else _finalizeCallback = std::move(callback); // allow chaining return *this; } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredcancel.h000066400000000000000000000054061470663072600211700ustar00rootroot00000000000000/** * DeferredCancel.h * * Deferred callback for instructions that cancel a running consumer. This * deferred object allows one to register a callback that also gets the * consumer tag as one of its parameters. * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * We extend from the default deferred and add extra functionality */ class DeferredCancel : public Deferred { private: /** * Pointer to the channel * @var ChannelImpl */ ChannelImpl *_channel; /** * Callback to execute when the instruction is completed * @var CancelCallback */ CancelCallback _cancelCallback; /** * Report success for frames that report cancel operations * @param name Consumer tag that is cancelled * @return Deferred */ virtual const std::shared_ptr &reportSuccess(const std::string &name) override; /** * The channel implementation may call our * private members and construct us */ friend class ChannelImpl; friend class ConsumedMessage; public: /** * Protected constructor that can only be called * from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param channel Pointer to the channel * @param failed Are we already failed? */ DeferredCancel(ChannelImpl *channel, bool failed = false) : Deferred(failed), _channel(channel) {} public: /** * Register a function to be called when the cancel operation succeeded * * Only one callback can be registered. Successive calls * to this function will clear callbacks registered before. * * @param callback the callback to execute */ inline DeferredCancel &onSuccess(const CancelCallback& callback) { return onSuccess(CancelCallback(callback)); } DeferredCancel &onSuccess(CancelCallback&& callback) { // store callback _cancelCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when the cancel operation succeeded * @param callback */ inline DeferredCancel &onSuccess(const SuccessCallback& callback) { return onSuccess(SuccessCallback(callback)); } DeferredCancel &onSuccess(SuccessCallback&& callback) { // call base Deferred::onSuccess(std::move(callback)); // allow chaining return *this; } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredconfirm.h000066400000000000000000000057171470663072600214050ustar00rootroot00000000000000/** * DeferredConfirm.h * * Deferred callback for RabbitMQ-specific publisher confirms mechanism. * * @author Marcin Gibula * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * We extend from the default deferred and add extra functionality */ class DeferredConfirm : public Deferred { private: /** * Callback to execute when server confirms that message is processed * @var AckCallback */ AckCallback _ackCallback; /** * Callback to execute when server sends negative acknowledgement * @var NackCallback */ NackCallback _nackCallback; /** * Process an ACK frame * * @param frame The frame to process */ void process(BasicAckFrame &frame); /** * Process an ACK frame * * @param frame The frame to process */ void process(BasicNackFrame &frame); /** * The channel implementation may call our * private members and construct us */ friend class ChannelImpl; friend class BasicAckFrame; friend class BasicNackFrame; public: /** * Protected constructor that can only be called * from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param boolean are we already failed? */ DeferredConfirm(bool failed = false) : Deferred(failed) {} public: /** * Register the function that is called when channel is put in publisher * confirmed mode * @param callback */ inline DeferredConfirm &onSuccess(const SuccessCallback& callback) { return onSuccess(SuccessCallback(callback)); } DeferredConfirm &onSuccess(SuccessCallback&& callback) { // call base Deferred::onSuccess(std::move(callback)); // allow chaining return *this; } /** * Callback that is called when the broker confirmed message publication * @param callback the callback to execute */ inline DeferredConfirm &onAck(const AckCallback& callback) { return onAck(AckCallback(callback)); } DeferredConfirm &onAck(AckCallback&& callback) { // store callback _ackCallback = std::move(callback); // allow chaining return *this; } /** * Callback that is called when the broker denied message publication * @param callback the callback to execute */ inline DeferredConfirm &onNack(const NackCallback& callback) { return onNack(NackCallback(callback)); } DeferredConfirm &onNack(NackCallback&& callback) { // store callback _nackCallback = std::move(callback); // allow chaining return *this; } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredconsumer.h000066400000000000000000000236131470663072600215760ustar00rootroot00000000000000/** * DeferredConsumer.h * * Deferred callback for consumers * * @copyright 2014 - 2022 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "deferredextreceiver.h" /** * Set up namespace */ namespace AMQP { /** * Forward declararions */ class BasicDeliverFrame; /** * We extend from the default deferred and add extra functionality */ class DeferredConsumer : public DeferredExtReceiver, public std::enable_shared_from_this { private: /** * Callback to execute when consumption has started * @var ConsumeCallback */ ConsumeCallback _consumeCallback; /** * Callback to excute when the server has cancelled the consumer * @var CancelCallback */ CancelCallback _cancelCallback; /** * Process a delivery frame * * @param frame The frame to process */ void process(BasicDeliverFrame &frame); /** * Report success for frames that report start consumer operations * @param name Consumer tag that is started * @return Deferred */ virtual const std::shared_ptr &reportSuccess(const std::string &name) override; /** * Report that the server has cancelled this consumer * @param namae The consumer tag */ void reportCancelled(const std::string &name) { // report if (_cancelCallback) _cancelCallback(name); } /** * Get reference to self to prevent that object falls out of scope * @return std::shared_ptr */ virtual std::shared_ptr lock() override { return shared_from_this(); } /** * The channel implementation may call our * private members and construct us */ friend class ChannelImpl; friend class ConsumedMessage; friend class BasicDeliverFrame; public: /** * Constructor that should only be called from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param channel the channel implementation * @param failed are we already failed? */ DeferredConsumer(ChannelImpl *channel, bool failed = false) : DeferredExtReceiver(failed, channel) {} public: /** * Register a callback function that gets called when the consumer is * started. In the callback you will for receive the consumer-tag * that you need to later stop the consumer * @param callback */ inline DeferredConsumer &onSuccess(const ConsumeCallback& callback) { return onSuccess(ConsumeCallback(callback)); } DeferredConsumer &onSuccess(ConsumeCallback&& callback) { // store the callback _consumeCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when the consumer starts. * It is recommended to use the onSuccess() method mentioned above * since that will also pass the consumer-tag as parameter. * @param callback */ inline DeferredConsumer &onSuccess(const SuccessCallback& callback) { return onSuccess(SuccessCallback(callback)); } DeferredConsumer &onSuccess(SuccessCallback&& callback) { // call base Deferred::onSuccess(std::move(std::move(callback))); // allow chaining return *this; } /** * Register a function to be called when a full message is received * @param callback the callback to execute */ inline DeferredConsumer &onReceived(const MessageCallback& callback) { return onReceived(MessageCallback(callback)); } DeferredConsumer &onReceived(MessageCallback&& callback) { // store callback _messageCallback = std::move(callback); // allow chaining return *this; } /** * Alias for onReceived() (see above) * @param callback the callback to execute */ inline DeferredConsumer &onMessage(const MessageCallback& callback) { return onMessage(MessageCallback(callback)); } DeferredConsumer &onMessage(MessageCallback&& callback) { // store callback _messageCallback = std::move(callback); // allow chaining return *this; } /** * RabbitMQ sends a message in multiple frames to its consumers. * The AMQP-CPP library collects these frames and merges them into a * single AMQP::Message object that is passed to the callback that * you can set with the onReceived() or onMessage() methods (see above). * * However, you can also write your own algorithm to merge the frames. * In that case you can install callbacks to handle the frames. Every * message is sent in a number of frames: * * - a begin frame that marks the start of the message * - an optional header if the message was sent with an envelope * - zero or more data frames (usually 1, but more for large messages) * - an end frame to mark the end of the message. * * To install handlers for these frames, you can use the onBegin(), * onHeaders(), onData() and onComplete() methods. * * If you just rely on the onReceived() or onMessage() callbacks, you * do not need any of the methods below this line. */ /** * Register the function that is called when the start frame of a new * consumed message is received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredConsumer &onBegin(const StartCallback& callback) { return onBegin(StartCallback(callback)); } DeferredConsumer &onBegin(StartCallback&& callback) { // store callback _startCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when the start frame of a new * consumed message is received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredConsumer &onStart(const StartCallback& callback) { return onStart(StartCallback(callback)); } DeferredConsumer &onStart(StartCallback&& callback) { // store callback _startCallback = std::move(callback); // allow chaining return *this; } /** * Register a function that is called when the message size is known * * @param callback The callback to invoke for message headers * @return Same object for chaining */ inline DeferredConsumer &onSize(const SizeCallback& callback) { return onSize(SizeCallback(callback)); } DeferredConsumer &onSize(SizeCallback&& callback) { // store callback _sizeCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when message headers come in * * @param callback The callback to invoke for message headers * @return Same object for chaining */ inline DeferredConsumer &onHeaders(const HeaderCallback& callback) { return onHeaders(HeaderCallback(callback)); } DeferredConsumer &onHeaders(HeaderCallback&& callback) { // store callback _headerCallback = std::move(callback); // allow chaining return *this; } /** * Register the function to be called when a chunk of data comes in * * Note that this function may be called zero, one or multiple times * for each incoming message depending on the size of the message data. * * If you install this callback you very likely also want to install * the onComplete callback so you know when the last data part was * received. * * @param callback The callback to invoke for chunks of message data * @return Same object for chaining */ inline DeferredConsumer &onData(const DataCallback& callback) { return onData(DataCallback(callback)); } DeferredConsumer &onData(DataCallback&& callback) { // store callback _dataCallback = std::move(callback); // allow chaining return *this; } /** * Register a funtion to be called when a message was completely received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredConsumer &onComplete(const DeliveredCallback& callback) { return onComplete(DeliveredCallback(callback)); } DeferredConsumer &onComplete(DeliveredCallback&& callback) { // store callback _deliveredCallback = std::move(callback); // allow chaining return *this; } /** * Register a funtion to be called when a message was completely received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredConsumer &onDelivered(const DeliveredCallback& callback) { return onDelivered(DeliveredCallback(callback)); } DeferredConsumer &onDelivered(DeliveredCallback&& callback) { // store callback _deliveredCallback = std::move(callback); // allow chaining return *this; } /** * Register a funtion to be called when the server cancelled the consumer * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredConsumer &onCancelled(const CancelCallback& callback) { return onCancelled(CancelCallback(callback)); } DeferredConsumer &onCancelled(CancelCallback&& callback) { // store callback _cancelCallback = std::move(callback); // allow chaining return *this; } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferreddelete.h000066400000000000000000000054541470663072600212100ustar00rootroot00000000000000/** * DeferredDelete.h * * Deferred callback for instructions that delete or purge queues, and that * want to report the number of deleted messages. * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * We extend from the default deferred and add extra functionality */ class DeferredDelete : public Deferred { private: /** * Callback to execute when the instruction is completed * @var DeleteCallback */ DeleteCallback _deleteCallback; /** * Report success for queue delete and queue purge messages * @param messagecount Number of messages that were deleted * @return Deferred Next deferred result */ virtual const std::shared_ptr &reportSuccess(uint32_t messagecount) const override { // skip if no special callback was installed if (!_deleteCallback) return Deferred::reportSuccess(); // call the callback _deleteCallback(messagecount); // return next object return _next; } /** * The channel implementation may call our * private members and construct us */ friend class ChannelImpl; friend class ConsumedMessage; public: /** * Protected constructor that can only be called * from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param boolean are we already failed? */ DeferredDelete(bool failed = false) : Deferred(failed) {} public: /** * Register a function to be called when the queue is deleted or purged * * Only one callback can be registered. Successive calls * to this function will clear callbacks registered before. * * @param callback the callback to execute */ inline DeferredDelete &onSuccess(const DeleteCallback& callback) { return onSuccess(DeleteCallback(callback)); } DeferredDelete &onSuccess(DeleteCallback&& callback) { // store callback _deleteCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when the queue is deleted or purged * @param callback */ inline DeferredDelete &onSuccess(const SuccessCallback& callback) { return onSuccess(SuccessCallback(callback)); } DeferredDelete &onSuccess(SuccessCallback&& callback) { // call base Deferred::onSuccess(std::move(callback)); // allow chaining return *this; } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredextreceiver.h000066400000000000000000000037701470663072600222720ustar00rootroot00000000000000/** * DeferredExtReceiver.h * * Extended receiver that _wants_ to receive message (because it is * consuming or get'ting messages. This is the base class for both * the DeferredConsumer as well as the DeferredGet classes, but not * the base of the DeferredRecall (which can also receive returned * messages, but not as a result of an explicit request) * * @author Emiel Bruijntjes * @copyright 2018 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "deferredreceiver.h" /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class DeferredExtReceiver : public DeferredReceiver { protected: /** * The delivery tag for the current message * @var uint64_t */ uint64_t _deliveryTag = 0; /** * Is this a redelivered message * @var bool */ bool _redelivered = false; /** * Callback for incoming messages * @var MessageCallback */ MessageCallback _messageCallback; /** * Callback for when a message was complete finished * @var DeliveredCallback */ DeliveredCallback _deliveredCallback; /** * Initialize the object to send out a message * @param exchange the exchange to which the message was published * @param routingkey the routing key that was used to publish the message */ virtual void initialize(const std::string &exchange, const std::string &routingkey) override; /** * Indicate that a message was done */ virtual void complete() override; /** * Constructor * @param failed Have we already failed? * @param channel The channel we are consuming on */ DeferredExtReceiver(bool failed, ChannelImpl *channel) : DeferredReceiver(failed, channel) {} public: /** * Destructor */ virtual ~DeferredExtReceiver() = default; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredget.h000066400000000000000000000223451470663072600205230ustar00rootroot00000000000000/** * DeferredGet.h * * @author Emiel Bruijntjes * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "deferredextreceiver.h" /** * Set up namespace */ namespace AMQP { /** * Class definition * * This class implements the 'shared_from_this' functionality, because * it grabs a self-pointer when the callback is running, otherwise the onFinalize() * is called before the actual message is consumed. */ class DeferredGet : public DeferredExtReceiver, public std::enable_shared_from_this { private: /** * Callback in case the queue is empty * @var EmptyCallback */ EmptyCallback _emptyCallback; /** * Callback with the number of messages still in the queue * @var CountCallback */ CountCallback _countCallback; /** * Report success for a get operation * @param messagecount Number of messages left in the queue * @param deliveryTag Delivery tag of the message coming in * @param redelivered Was the message redelivered? */ virtual const std::shared_ptr &reportSuccess(uint32_t messagecount, uint64_t deliveryTag, bool redelivered) override; /** * Report success when queue was empty * @return Deferred */ virtual const std::shared_ptr &reportSuccess() const override; /** * Get reference to self to prevent that object falls out of scope * @return std::shared_ptr */ virtual std::shared_ptr lock() override { return shared_from_this(); } /** * Extended implementation of the complete method that is called when a message was fully received */ virtual void complete() override; /** * The channel implementation may call our * private members and construct us */ friend class ChannelImpl; friend class ConsumedMessage; public: /** * Protected constructor that can only be called * from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param channel the channel implementation * @param failed are we already failed? */ DeferredGet(ChannelImpl *channel, bool failed = false) : DeferredExtReceiver(failed, channel) {} public: /** * Register a function to be called when a message arrives * This fuction is also available as onReceived() and onMessage() because I always forget which name I gave to it * @param callback */ inline DeferredGet &onSuccess(const MessageCallback& callback) { return onSuccess(MessageCallback(callback)); } DeferredGet &onSuccess(MessageCallback&& callback) { // store the callback _messageCallback = std::move(callback); // allow chaining return *this; } /** * Register a function to be called when an error occurs. This should be defined, otherwise the base methods are used. * @param callback */ inline DeferredGet &onError(const ErrorCallback& callback) { return onError(ErrorCallback(callback)); } DeferredGet &onError(ErrorCallback&& callback) { // store the callback _errorCallback = std::move(callback); // allow chaining return *this; } /** * Register a function to be called when a message arrives * This fuction is also available as onSuccess() and onMessage() because I always forget which name I gave to it * @param callback the callback to execute */ inline DeferredGet &onReceived(const MessageCallback& callback) { return onReceived(MessageCallback(callback)); } DeferredGet &onReceived(MessageCallback&& callback) { // store callback _messageCallback = std::move(callback); // allow chaining return *this; } /** * Register a function to be called when a message arrives * This fuction is also available as onSuccess() and onReceived() because I always forget which name I gave to it * @param callback the callback to execute */ inline DeferredGet &onMessage(const MessageCallback& callback) { return onMessage(MessageCallback(callback)); } DeferredGet &onMessage(MessageCallback&& callback) { // store callback _messageCallback = std::move(callback); // allow chaining return *this; } /** * Register a function to be called if no message could be fetched * @param callback the callback to execute */ inline DeferredGet &onEmpty(const EmptyCallback& callback) { return onEmpty(EmptyCallback(callback)); } DeferredGet &onEmpty(EmptyCallback&& callback) { // store callback _emptyCallback = std::move(callback); // allow chaining return *this; } /** * Register a function to be called when queue size information is known * @param callback the callback to execute */ inline DeferredGet &onCount(const CountCallback& callback) { return onCount(CountCallback(callback)); } DeferredGet &onCount(CountCallback&& callback) { // store callback _countCallback = std::move(callback); // allow chaining return *this; } /** * Register the function to be called when a new message is expected * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredGet &onBegin(const StartCallback& callback) { return onBegin(StartCallback(callback)); } DeferredGet &onBegin(StartCallback&& callback) { // store callback _startCallback = std::move(callback); // allow chaining return *this; } /** * Register the function to be called when a new message is expected * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredGet &onStart(const StartCallback& callback) { return onStart(StartCallback(callback)); } DeferredGet &onStart(StartCallback&& callback) { // store callback _startCallback = std::move(callback); // allow chaining return *this; } /** * Register a function that is called when the message size is known * * @param callback The callback to invoke for message headers * @return Same object for chaining */ inline DeferredGet &onSize(const SizeCallback& callback) { return onSize(SizeCallback(callback)); } DeferredGet &onSize(SizeCallback&& callback) { // store callback _sizeCallback = std::move(callback); // allow chaining return *this; } /** * Register the function to be called when message headers come in * * @param callback The callback to invoke for message headers * @return Same object for chaining */ inline DeferredGet &onHeaders(const HeaderCallback& callback) { return onHeaders(HeaderCallback(callback)); } DeferredGet &onHeaders(HeaderCallback&& callback) { // store callback _headerCallback = std::move(callback); // allow chaining return *this; } /** * Register the function to be called when a chunk of data comes in * * Note that this function may be called zero, one or multiple times * for each incoming message depending on the size of the message data. * * If you install this callback you very likely also want to install * the onComplete callback so you know when the last data part was * received. * * @param callback The callback to invoke for chunks of message data * @return Same object for chaining */ inline DeferredGet &onData(const DataCallback& callback) { return onData(DataCallback(callback)); } DeferredGet &onData(DataCallback&& callback) { // store callback _dataCallback = std::move(callback); // allow chaining return *this; } /** * Register a funtion to be called when a message was completely received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredGet &onComplete(const DeliveredCallback& callback) { return onComplete(DeliveredCallback(callback)); } DeferredGet &onComplete(DeliveredCallback&& callback) { // store callback _deliveredCallback = std::move(callback); // allow chaining return *this; } /** * Register a funtion to be called when a message was completely received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredGet &onDelivered(const DeliveredCallback& callback) { return onDelivered(DeliveredCallback(callback)); } DeferredGet &onDelivered(DeliveredCallback&& callback) { // store callback _deliveredCallback = std::move(callback); // allow chaining return *this; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredpublish.h000066400000000000000000000074531470663072600214150ustar00rootroot00000000000000/** * DeferredPublish.h * * Deferred callback for RabbitMQ-specific publisher confirms mechanism per-message. * * @author Michael van der Werve * @copyright 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * We extend from the default deferred and add extra functionality */ class DeferredPublish : public Deferred { private: /** * Callback to execute when server confirms that message is processed * @var AckCallback */ PublishAckCallback _ackCallback; /** * Callback to execute when server sends negative acknowledgement * @var NackCallback */ PublishNackCallback _nackCallback; /** * Callback to execute when message is lost (nack / error) * @var LostCallback */ PublishLostCallback _lostCallback; /** * Report an ack, calls the callback. */ void reportAck() { // check if the callback is set if (_ackCallback) _ackCallback(); } /** * Report an nack, calls the callback if set. */ void reportNack() { // check if the callback is set if (_nackCallback) _nackCallback(); // message is 'lost' if (_lostCallback) _lostCallback(); } /** * Indicate failure * @param error Description of the error that occured */ void reportError(const char *error) { // from this moment on the object should be listed as failed _failed = true; // message is lost if (_lostCallback) _lostCallback(); // execute callbacks if registered if (_errorCallback) _errorCallback(error); } /** * The wrapped confirmed channel implementation may call our * private members and construct us */ template friend class Reliable; public: /** * Protected constructor that can only be called * from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param boolean are we already failed? */ DeferredPublish(bool failed = false) : Deferred(failed) {} public: /** * Callback that is called when the broker confirmed message publication * @param callback the callback to execute */ inline DeferredPublish &onAck(const PublishAckCallback& callback) { return onAck(PublishAckCallback(callback)); } DeferredPublish &onAck(PublishAckCallback&& callback) { // store callback _ackCallback = std::move(callback); // allow chaining return *this; } /** * Callback that is called when the broker denied message publication * @param callback the callback to execute */ inline DeferredPublish &onNack(const PublishNackCallback& callback) { return onNack(PublishNackCallback(callback)); } DeferredPublish &onNack(PublishNackCallback&& callback) { // store callback _nackCallback = std::move(callback); // allow chaining return *this; } /** * Callback that is called when a message is lost, either through RabbitMQ * rejecting it or because of a channel error * @param callback the callback to execute */ inline DeferredPublish &onLost(const PublishLostCallback& callback) { return onLost(PublishLostCallback(callback)); } DeferredPublish &onLost(PublishLostCallback&& callback) { // store callback _lostCallback = std::move(callback); // allow chaining return *this; } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredqueue.h000066400000000000000000000055611470663072600210710ustar00rootroot00000000000000/** * DeferredQueue.h * * Deferred callback for "declare-queue" instructions. * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * We extend from the default deferred and add extra functionality */ class DeferredQueue : public Deferred { private: /** * Callback to execute when the queue is declared * @var QueueCallback */ QueueCallback _queueCallback; /** * Report success for queue declared messages * @param name Name of the new queue * @param messagecount Number of messages in the queue * @param consumercount Number of consumers linked to the queue * @return Deferred Next deferred result */ virtual const std::shared_ptr &reportSuccess(const std::string &name, uint32_t messagecount, uint32_t consumercount) const override { // skip if no special callback was installed if (!_queueCallback) return Deferred::reportSuccess(); // call the queue callback _queueCallback(name, messagecount, consumercount); // return next object return _next; } /** * The channel implementation may call our * private members and construct us */ friend class ChannelImpl; friend class ConsumedMessage; public: /** * Protected constructor that can only be called * from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param bool are we already failed? */ DeferredQueue(bool failed = false) : Deferred(failed) {} public: /** * Register a function to be called when the queue is declared * * Only one callback can be registered. Successive calls * to this function will clear callbacks registered before. * * @param callback the callback to execute */ inline DeferredQueue &onSuccess(const QueueCallback& callback) { return onSuccess(QueueCallback(callback)); } DeferredQueue &onSuccess(QueueCallback&& callback) { // store callback _queueCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when the queue is declared * @param callback */ inline DeferredQueue &onSuccess(const SuccessCallback& callback) { return onSuccess(SuccessCallback(callback)); } DeferredQueue &onSuccess(SuccessCallback&& callback) { // call base Deferred::onSuccess(std::move(callback)); // allow chaining return *this; } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredrecall.h000066400000000000000000000154671470663072600212150ustar00rootroot00000000000000/** * DeferredRecall.h * * Class that an be used to install callback methods that define how * returned messages should be handled. * * @author Emiel Bruijntjes * @copyright 2018 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Begin of namespace */ namespace AMQP { /** * Forward declarations */ class ChannelImpl; /** * Class definition */ class DeferredRecall : public DeferredReceiver, public std::enable_shared_from_this { private: /** * The error code * @var int16_t */ int16_t _code = 0; /** * The error message * @var std::string */ std::string _description; /** * Callback that is called when a message is returned * @var BounceCallback */ BounceCallback _bounceCallback; /** * Begin of a bounced message * @var ReturnCallback */ ReturnCallback _beginCallback; /** * End of a bounced message * @var ReturnedCallback */ ReturnedCallback _completeCallback; /** * Process a return frame * * @param frame The frame to process */ void process(BasicReturnFrame &frame); /** * Get reference to self to prevent that object falls out of scope * @return std::shared_ptr */ virtual std::shared_ptr lock() override { return shared_from_this(); } /** * Extended implementation of the complete method that is called when a message was fully received */ virtual void complete() override; /** * Classes that can access private members */ friend class BasicReturnFrame; public: /** * Constructor that should only be called from within the channel implementation * * Note: this constructor _should_ be protected, but because make_shared * will then not work, we have decided to make it public after all, * because the work-around would result in not-so-easy-to-read code. * * @param channel the channel implementation * @param failed are we already failed? */ DeferredRecall(ChannelImpl *channel, bool failed = false) : DeferredReceiver(failed, channel) {} public: /** * Register a function to be called when a full message is returned * @param callback the callback to execute */ inline DeferredRecall &onReceived(const BounceCallback& callback) { return onReceived(BounceCallback(callback)); } DeferredRecall &onReceived(BounceCallback&& callback) { // store callback _bounceCallback = std::move(callback); // allow chaining return *this; } /** * Alias for onReceived() (see above) * @param callback the callback to execute */ inline DeferredRecall &onMessage(const BounceCallback& callback) { return onMessage(BounceCallback(callback)); } DeferredRecall &onMessage(BounceCallback&& callback) { // store callback _bounceCallback = std::move(callback); // allow chaining return *this; } /** * Alias for onReceived() (see above) * @param callback the callback to execute */ inline DeferredRecall &onReturned(const BounceCallback& callback) { return onReturned(BounceCallback(callback)); } DeferredRecall &onReturned(BounceCallback&& callback) { // store callback _bounceCallback = std::move(callback); // allow chaining return *this; } /** * Alias for onReceived() (see above) * @param callback the callback to execute */ inline DeferredRecall &onBounced(const BounceCallback& callback) { return onBounced(BounceCallback(callback)); } DeferredRecall &onBounced(BounceCallback&& callback) { // store callback _bounceCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when the start frame of a new * consumed message is received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredRecall &onBegin(const ReturnCallback& callback) { return onBegin(ReturnCallback(callback)); } DeferredRecall &onBegin(ReturnCallback&& callback) { // store callback _beginCallback = std::move(callback); // allow chaining return *this; } /** * Register a function that is called when the message size is known * * @param callback The callback to invoke for message headers * @return Same object for chaining */ inline DeferredRecall &onSize(const SizeCallback& callback) { return onSize(SizeCallback(callback)); } DeferredRecall &onSize(SizeCallback&& callback) { // store callback _sizeCallback = std::move(callback); // allow chaining return *this; } /** * Register the function that is called when message headers come in * * @param callback The callback to invoke for message headers * @return Same object for chaining */ inline DeferredRecall &onHeaders(const HeaderCallback& callback) { return onHeaders(HeaderCallback(callback)); } DeferredRecall &onHeaders(HeaderCallback&& callback) { // store callback _headerCallback = std::move(callback); // allow chaining return *this; } /** * Register the function to be called when a chunk of data comes in * * Note that this function may be called zero, one or multiple times * for each incoming message depending on the size of the message data. * * If you install this callback you very likely also want to install * the onComplete callback so you know when the last data part was * received. * * @param callback The callback to invoke for chunks of message data * @return Same object for chaining */ inline DeferredRecall &onData(const DataCallback& callback) { return onData(DataCallback(callback)); } DeferredRecall &onData(DataCallback&& callback) { // store callback _dataCallback = std::move(callback); // allow chaining return *this; } /** * Register a funtion to be called when a message was completely received * * @param callback The callback to invoke * @return Same object for chaining */ inline DeferredRecall &onComplete(const ReturnedCallback& callback) { return onComplete(ReturnedCallback(callback)); } DeferredRecall &onComplete(ReturnedCallback&& callback) { // store callback _completeCallback = std::move(callback); // allow chaining return *this; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/deferredreceiver.h000066400000000000000000000056221470663072600215470ustar00rootroot00000000000000/** * DeferredReceiver.h * * Base class for the deferred consumer, the deferred get and the * deferred publisher (that may receive returned messages) * * @copyright 2016 - 2018 Copernica B.V. */ /** * Include guard */ #pragma once /** * Dependencies */ #include "deferred.h" #include "stack_ptr.h" #include "message.h" /** * Start namespace */ namespace AMQP { /** * Forward declarations */ class BasicDeliverFrame; class BasicGetOKFrame; class BasicHeaderFrame; class BodyFrame; /** * Base class for deferred consumers */ class DeferredReceiver : public Deferred { private: /** * Size of the body of the current message * @var uint64_t */ uint64_t _bodySize = 0; protected: /** * Initialize the object to send out a message * @param exchange the exchange to which the message was published * @param routingkey the routing key that was used to publish the message */ virtual void initialize(const std::string &exchange, const std::string &routingkey); /** * Get reference to self to prevent that object falls out of scope * @return std::shared_ptr */ virtual std::shared_ptr lock() = 0; /** * Indicate that a message was done */ virtual void complete() = 0; private: /** * Process the message headers * * @param frame The frame to process */ void process(BasicHeaderFrame &frame); /** * Process the message data * * @param frame The frame to process */ void process(BodyFrame &frame); /** * Frames may be processed */ friend class ChannelImpl; friend class BasicGetOKFrame; friend class BasicHeaderFrame; friend class BodyFrame; protected: /** * The channel to which the consumer is linked * @var ChannelImpl */ ChannelImpl *_channel; /** * Callback for new message * @var StartCallback */ StartCallback _startCallback; /** * Callback that is called when size of the message is known * @var SizeCallback */ SizeCallback _sizeCallback; /** * Callback for incoming headers * @var HeaderCallback */ HeaderCallback _headerCallback; /** * Callback for when a chunk of data comes in * @var DataCallback */ DataCallback _dataCallback; /** * The message that we are currently receiving * @var stack_ptr */ stack_ptr _message; /** * Constructor * @param failed Have we already failed? * @param channel The channel we are consuming on */ DeferredReceiver(bool failed, ChannelImpl *channel) : Deferred(failed), _channel(channel) {} public: /** * Destructor */ virtual ~DeferredReceiver() = default; }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/endian.h000066400000000000000000000072561470663072600175050ustar00rootroot00000000000000/** * Endian.h * * On Apple systems, there are no macro's to convert between little * and big endian byte orders. This header file adds the missing macros * * @author madmongo1 * * And we have also copied code from the "portable_endian.h" file by * Mathias Panzenböck. His license: * * "License": Public Domain * I, Mathias Panzenböck, place this file hereby into the public * domain. Use it at your own risk for whatever you like. In case * there are jurisdictions that don't support putting things in the * public domain you can also consider it to be "dual licensed" * under the BSD, MIT and Apache licenses, if you want to. This * code is trivial anyway. Consider it an example on how to get the * endian conversion functions on different platforms. */ /** * Include guard */ #pragma once /** * The contents of the file are only relevant for Apple */ #if defined(__APPLE__) // dependencies #include #include // define 16 bit macros #define htobe16(x) OSSwapHostToBigInt16(x) #define htole16(x) OSSwapHostToLittleInt16(x) #define be16toh(x) OSSwapBigToHostInt16(x) #define le16toh(x) OSSwapLittleToHostInt16(x) // define 32 bit macros #define htobe32(x) OSSwapHostToBigInt32(x) #define htole32(x) OSSwapHostToLittleInt32(x) #define be32toh(x) OSSwapBigToHostInt32(x) #define le32toh(x) OSSwapLittleToHostInt32(x) // define 64 but macros #define htobe64(x) OSSwapHostToBigInt64(x) #define htole64(x) OSSwapHostToLittleInt64(x) #define be64toh(x) OSSwapBigToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x) /** * And on Windows systems weird things are going on as well */ #elif (defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)) && !defined(__CYGWIN__) #include #pragma comment(lib,"Ws2_32.lib") //# include #if BYTE_ORDER == LITTLE_ENDIAN #define htobe16(x) htons(x) #define htole16(x) (x) #define be16toh(x) ntohs(x) #define le16toh(x) (x) #define htobe32(x) htonl(x) #define htole32(x) (x) #define be32toh(x) ntohl(x) #define le32toh(x) (x) #define htobe64(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) #define htole64(x) (x) #define be64toh(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) #define le64toh(x) (x) #elif BYTE_ORDER == BIG_ENDIAN /* that would be xbox 360 */ #define htobe16(x) (x) #define htole16(x) __builtin_bswap16(x) #define be16toh(x) (x) #define le16toh(x) __builtin_bswap16(x) #define htobe32(x) (x) #define htole32(x) __builtin_bswap32(x) #define be32toh(x) (x) #define le32toh(x) __builtin_bswap32(x) #define htobe64(x) (x) #define htole64(x) __builtin_bswap64(x) #define be64toh(x) (x) #define le64toh(x) __builtin_bswap64(x) #else #error byte order not supported #endif #define __BYTE_ORDER BYTE_ORDER #define __BIG_ENDIAN BIG_ENDIAN #define __LITTLE_ENDIAN LITTLE_ENDIAN #define __PDP_ENDIAN PDP_ENDIAN /** * OpenBSD handling */ #elif defined(__OpenBSD__) #include /** * NetBSD handling */ #elif defined(__NetBSD__) || defined(__DragonFly__) #include #define be16toh(x) betoh16(x) #define le16toh(x) letoh16(x) #define be32toh(x) betoh32(x) #define le32toh(x) letoh32(x) #define be64toh(x) betoh64(x) #define le64toh(x) letoh64(x) /** * FreeBSD handling */ #elif defined(__FreeBSD__) #include /** * QNX Neutrino handling */ #elif defined(__QNXNTO__) #include /** * Not on apple, and not on windows */ #else // this is the normal linux way of doing things #include // end of "#if defined(__APPLE__)" #endif AMQP-CPP-4.3.27/include/amqpcpp/entityimpl.h000066400000000000000000000100731470663072600204340ustar00rootroot00000000000000/** * EntityImpl.h * * Common base class for exchanges and queues. * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Namespace */ namespace AMQP { /** * Class definition */ class EntityImpl { protected: /** * The channel on which we communicate * @var Channel */ Channel *_channel; /** * Name of the queue/exchange * @var string */ std::string _name; /** * Is this a durable queue/exchange? * A durable queue/exchange survives a broker restart * @var bool */ bool _durable = false; /** * Is this a passive queue/exchange? * If set, only check if the queue/exchange exists without actually creating it * @var bool */ bool _passive = false; /** * Is this an auto-delete queue/exchange? * If set, the entity is removed when it is no longer used (for queues * when all consumers are gone, for exchanges when all queues are gone) * @var bool */ bool _autoDelete = false; /** * Additional arguments * @var Table */ Table _arguments; /** * Constructor is protected and can only be accessed by derived classes * @param channel */ EntityImpl(Channel *channel) : _channel(channel) {} public: /** * Destructor */ virtual ~EntityImpl(); /** * Retrieve the name * @return string */ std::string &name() { return _name; } /** * Change the name * You must declare the entity before this has effect * @param string */ void setName(const std::string &name) { _name = name; } /** * Is the queue or exchange durable * The entity survives a broker restart if it is durable * @return bool */ bool durable() { return _durable; } /** * Mark the object as durable * @param bool */ void setDurable(bool durable) { _durable = durable; } /** * Is the passive bit set * If set, the declare method only checks if the queue/exchange exists without actually creating it * @return bool */ bool passive() { return _passive; } /** * Change the passive bit * @param bool */ void setPassive(bool passive) { _passive = passive; } /** * Is the auto-delete property set? * The entity is removed when the consumers and/or queues are unlinked from it * @return bool */ bool autoDelete() { return _autoDelete; } /** * Update the auto-delete bit * @param bool */ void setAutoDelete(bool autoDelete) { _autoDelete = autoDelete; } /** * Set a custom argument * @param name Name of the argument * @param value Value of the argument */ void setArgument(const std::string &name, const std::string &value) { _arguments[name] = value; } /** * Retrieve a argument * @param name Name of the argument * @return The value of the argument */ std::string &argument(const std::string &name) { return _arguments[name]; } /** * Bind to a different exchange * @param exchange The exchange to bind to * @param key The routing key * @return bool */ virtual bool bind(const std::string &exchange, const std::string &key) = 0; /** * Unbind from an exchange * @param exchange Exchange to unbind from * @param key The routing key * @return bool */ virtual bool unbind(const std::string &exchange, const std::string &key) = 0; /** * Declare the queue/exchange * @return bool */ virtual bool declare() = 0; /** * Remove the queue/exchange * @return bool */ virtual bool remove() = 0; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/envelope.h000066400000000000000000000074251470663072600200620ustar00rootroot00000000000000/** * Envelope.h * * When you send or receive a message to the rabbitMQ server, it is encapsulated * in an envelope that contains additional meta information as well. * * @copyright 2014 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "metadata.h" /** * Set up namespace */ namespace AMQP { /** * Class definition * The envelope extends from MetaData, although this is conceptually not entirely * correct: and envelope _holds_ meta data and a body, so it would have been more * correct to make the MetaData instance a member. But by extending we automatically * make all meta-data properties accesible. */ class Envelope : public MetaData { protected: /** * Pointer to the body data (the memory is not managed by the AMQP library!) * @var const char * */ const char *_body; /** * Size of the data * @var uint64_t */ uint64_t _bodySize; public: /** * Constructor * The data buffer that you pass to this constructor must be valid during * the lifetime of the Envelope object. * @param body * @param size */ Envelope(const char *body, uint64_t size) : MetaData(), _body(body), _bodySize(size) {} Envelope(const std::string_view &body) : Envelope(body.data(), body.size()) {} Envelope(const char *body) : Envelope(body, strlen(body)) {} /** * Constructor that preserves meta-data, but installs different body * @param metadata * @param body * @param size */ Envelope(const MetaData &metadata, const char *body, uint64_t size) : MetaData(metadata), _body(body), _bodySize(size) {} Envelope(const MetaData &metadata, const std::string_view &body) : Envelope(metadata, body.data(), body.size()) {} Envelope(const MetaData &metadata, const char *body) : Envelope(metadata, body, strlen(body)) {} /** * Read envelope frmo an input-buffer * This method is the counterpart of the Envelope::fill() method, and is not used * by the library itself, but might be useful for applications that want to store * envelopes. * @param frame */ Envelope(InBuffer &buffer) : MetaData(buffer) { // extract the properties _bodySize = buffer.nextUint64(); _body = buffer.nextData(_bodySize); } /** * Disabled copy constructor * @param envelope the envelope to copy */ Envelope(const Envelope &envelope) = delete; /** * Destructor */ virtual ~Envelope() {} /** * Access to the full message data * @return buffer */ const char *body() const { return _body; } /** * Size of the body * @return uint64_t */ uint64_t bodySize() const { return _bodySize; } /** * Size of the envelope, this is the size of the meta+data plus the number of bytes * required to store the size of the body + the actual body. This method is not used * by the AMQP-CPP library, but could be useful if you feel the need to store * @return size_t */ size_t size() const { // this is the size of the meta-data + the size of the body return MetaData::size() + _bodySize + sizeof(uint64_t); } /** * Fill an output buffer * This method is not used by this library, but could be useful if you want to store * the meta-data + message contents ( * @param buffer */ void fill(OutBuffer &buffer) const { // first we store the meta-data MetaData::fill(buffer); // now the size of the message body + the actual body buffer.add(_bodySize); buffer.add(_body, _bodySize); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/exception.h000066400000000000000000000007541470663072600202410ustar00rootroot00000000000000/** * Exception.h * * Base class for all AMQP exceptions * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Set up namespace */ namespace AMQP { /** * Base exception class */ class Exception : public std::runtime_error { protected: /** * Constructor * @param what */ explicit Exception(const std::string &what) : runtime_error(what) {} }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/exchangetype.h000066400000000000000000000005711470663072600207240ustar00rootroot00000000000000/** * ExchangeType.h * * The various exchange types that are supported * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * The class */ enum ExchangeType { fanout, direct, topic, headers, consistent_hash, message_deduplication }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/field.h000066400000000000000000000060421470663072600173220ustar00rootroot00000000000000/** * Available field types for AMQP * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class InBuffer; class OutBuffer; class Array; class Table; /** * Base field class * * This class cannot be constructed, but serves * as the base class for all AMQP field types */ class Field { protected: /** * Decode a field by fetching a type and full field from a frame * The returned field is allocated on the heap! * @param frame * @return std::unique_ptr */ static std::unique_ptr decode(InBuffer &frame); public: /** * Destructor */ virtual ~Field() {} /** * Create a new instance on the heap of this object, identical to the object passed * @return Field* */ virtual std::unique_ptr clone() const = 0; /** * Get the size this field will take when * encoded in the AMQP wire-frame format * @return size_t */ virtual size_t size() const = 0; /** * Write encoded payload to the given buffer. * @param buffer */ virtual void fill(OutBuffer& buffer) const = 0; /** * Get the type ID that is used to identify this type of * field in a field table * @return char */ virtual char typeID() const = 0; /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const = 0; /** * Casting operators * @return mixed */ virtual operator const std::string& () const; virtual operator const char * () const { return nullptr; } virtual operator uint8_t () const { return 0; } virtual operator uint16_t () const { return 0; } virtual operator uint32_t () const { return 0; } virtual operator uint64_t () const { return 0; } virtual operator int8_t () const { return 0; } virtual operator int16_t () const { return 0; } virtual operator int32_t () const { return 0; } virtual operator int64_t () const { return 0; } virtual operator float () const { return 0; } virtual operator double () const { return 0; } virtual operator const Array& () const; virtual operator const Table& () const; /** * Check the field type * * @return Is the field a specific type? */ virtual bool isInteger() const { return false; } virtual bool isDecimal() const { return false; } virtual bool isArray() const { return false; } virtual bool isTable() const { return false; } virtual bool isBoolean() const { return false; } virtual bool isString() const { return false; } virtual bool isVoid() const { return false; } }; /** * Custom output stream operator * @param stream * @param field * @return ostream */ inline std::ostream &operator<<(std::ostream &stream, const Field &field) { field.output(stream); return stream; } /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/fieldproxy.h000066400000000000000000000132501470663072600204230ustar00rootroot00000000000000/** * Field proxy. Returned by the table. Can be casted to the * relevant native type (std::string or numeric) * * @copyright 2014 - 2024 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "stringfield.h" #include "booleanset.h" #include "decimalfield.h" #include "numericfield.h" #include "voidfield.h" /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class Table; class Array; class Field; /** * Class implementation */ template class FieldProxy { private: /** * The table or array possibly holding the requested field */ T *_source; /** * The key in the table */ I _index; public: /** * Construct the field proxy * * @param table the table possibly holding the field * @oaram index key in table map */ FieldProxy(T *source, I index) : _source(source), _index(index) {} /** * Assign a boolean value * * @param value */ FieldProxy& operator=(bool value) { // assign value and allow chaining _source->set(_index, BooleanSet(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(uint8_t value) { // assign value and allow chaining _source->set(_index, UOctet(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(int8_t value) { // assign value and allow chaining _source->set(_index, Octet(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(uint16_t value) { // assign value and allow chaining _source->set(_index, UShort(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(int16_t value) { // assign value and allow chaining _source->set(_index, Short(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(uint32_t value) { // assign value and allow chaining _source->set(_index, ULong(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(int32_t value) { // assign value and allow chaining _source->set(_index, Long(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(uint64_t value) { // assign value and allow chaining _source->set(_index, ULongLong(value)); return *this; } /** * Assign a numeric value * * @param value * @return FieldProxy */ FieldProxy& operator=(int64_t value) { // assign value and allow chaining _source->set(_index, LongLong(value)); return *this; } /** * Assign a decimal value * * @param value * @return FieldProxy */ FieldProxy& operator=(const DecimalField &value) { // assign value and allow chaining _source->set(_index, DecimalField(value)); return *this; } /** * Assign a string value * * @param value * @return FieldProxy */ FieldProxy &operator=(const std::string &value) { // in theory we should make a distinction between short and long string, // but in practive only long strings are accepted _source->set(_index, LongString(value)); // allow chaining return *this; } /** * Assign a string value * * @param value * @return FieldProxy */ FieldProxy &operator=(const std::string_view &value) { // in theory we should make a distinction between short and long string, // but in practive only long strings are accepted _source->set(_index, LongString(value)); // allow chaining return *this; } /** * Assign a string value * * @param value * @return FieldProxy */ FieldProxy &operator=(const char *value) { // cast to a string return operator=(std::string(value)); } /** * Assign an array value * @param value * @return FieldProxy */ FieldProxy &operator=(const Array &value) { // assign value and allow chaining _source->set(_index, value); return *this; } /** * Assign a table value * @param value * @return FieldProxy */ FieldProxy &operator=(const Table &value) { // assign value and allow chaining _source->set(_index, value); return *this; } /** * Get the underlying field * @return Field */ const Field &get() const { return _source->get(_index); } /** * Get a boolean * @return bool */ template operator TARGET () const { // retrieve the value return _source->get(_index); } }; // define types for array- and table-based field proxy using AssociativeFieldProxy = FieldProxy; using ArrayFieldProxy = FieldProxy; /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/flags.h000066400000000000000000000014131470663072600173300ustar00rootroot00000000000000/** * AmqpFlags.h * * The various flags that are supported * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * All bit flags * @var int */ extern const int durable; extern const int autodelete; extern const int active; extern const int passive; extern const int ifunused; extern const int ifempty; extern const int global; extern const int nolocal; extern const int noack; extern const int exclusive; extern const int nowait; extern const int mandatory; extern const int immediate; extern const int redelivered; extern const int multiple; extern const int requeue; extern const int readable; extern const int writable; extern const int internal; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/frame.h000066400000000000000000000041001470663072600173220ustar00rootroot00000000000000/** * Frame.h * * Base class for frames. This base class can not be constructed from outside * the library, and is only used internally. * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "protocolexception.h" /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class ConnectionImpl; /** * Class definition */ class Frame { protected: /** * Protected constructor to ensure that no objects are created from * outside the library */ Frame() {} public: /** * Destructor */ virtual ~Frame() {} /** * return the total size of the frame * @return uint32_t */ virtual uint32_t totalSize() const = 0; /** * Fill an output buffer * @param buffer */ virtual void fill(OutBuffer &buffer) const = 0; /** * Is this a frame that is part of the connection setup? * @return bool */ virtual bool partOfHandshake() const { return false; } /** * Is this a frame that is part of the connection close operation? * @return bool */ virtual bool partOfShutdown() const { return false; } /** * Does this frame need an end-of-frame seperator? * @return bool */ virtual bool needsSeparator() const { return true; } /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ virtual bool synchronous() const { return false; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) { // make sure compilers dont complain about unused parameters (void) connection; // this is an exception throw ProtocolException("unimplemented frame"); // unreachable return false; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/inbuffer.h000066400000000000000000000052471470663072600200450ustar00rootroot00000000000000/** * InBuffer.h * * The InBuffer class is a wrapper around a data buffer and that adds * some safety checks so that the rest of the library can safely read * from it. * * This is a class that is used internally by the AMQP library. As a user * of this library, you normally do not have to instantiate it. However, * if you do want to store or safe messages yourself, it sometimes can * be useful to implement it. * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class Buffer; /** * Class definition */ class InBuffer { protected: /** * The buffer we are reading from * @var Buffer */ const Buffer &_buffer; /** * Number of bytes already processed * @var size_t */ size_t _skip = 0; public: /** * Constructor * @param buffer Binary buffer */ InBuffer(const Buffer &buffer) : _buffer(buffer) {} /** * Destructor */ virtual ~InBuffer() {} /** * Read the next uint8_t from the buffer * @return uint8_t value read */ uint8_t nextUint8(); /** * Read the next int8_t from the buffer * @return int8_t value read */ int8_t nextInt8(); /** * Read the next uint16_t from the buffer * @return uint16_t value read */ uint16_t nextUint16(); /** * Read the next int16_t from the buffer * @return int16_t value read */ int16_t nextInt16(); /** * Read the next uint32_t from the buffer * @return uint32_t value read */ uint32_t nextUint32(); /** * Read the next int32_t from the buffer * @return int32_t value read */ int32_t nextInt32(); /** * Read the next uint64_t from the buffer * @return uint64_t value read */ uint64_t nextUint64(); /** * Read the next int64_t from the buffer * @return int64_t value read */ int64_t nextInt64(); /** * Read a float from the buffer * @return float float read from buffer. */ float nextFloat(); /** * Read a double from the buffer * @return double double read from buffer */ double nextDouble(); /** * Get a pointer to the next binary buffer of a certain size * @param size * @return char* */ const char *nextData(size_t size); /** * The checker may access private data */ friend class BufferCheck; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/libboostasio.h000066400000000000000000000435751470663072600207440ustar00rootroot00000000000000/** * LibBoostAsio.h * * Implementation for the AMQP::TcpHandler for boost::asio. You can use this class * instead of a AMQP::TcpHandler class, just pass the boost asio service to the * constructor and you're all set. See tests/libboostasio.cpp for example. * * Watch out: this class was not implemented or reviewed by the original author of * AMQP-CPP. However, we do get a lot of questions and issues from users of this class, * so we cannot guarantee its quality. If you run into such issues too, it might be * better to implement your own handler that interact with boost. * * * @author Gavin Smith */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include #include #include #include #include #include #include "amqpcpp/linux_tcp.h" // C++17 has 'weak_from_this()' support. #if __cplusplus >= 201701L #define PTR_FROM_THIS(T) weak_from_this() #else #define PTR_FROM_THIS(T) std::weak_ptr(shared_from_this()) #endif /** * Set up namespace */ namespace AMQP { /** * Class definition * @note Because of a limitation on Windows, this will only work on POSIX based systems - see https://github.com/chriskohlhoff/asio/issues/70 */ class LibBoostAsioHandler : public virtual TcpHandler { protected: /** * Helper class that wraps a boost io_context socket monitor. */ class Watcher : public virtual std::enable_shared_from_this { private: /** * The boost asio io_context which is responsible for detecting events. * @var class boost::asio::io_context& */ boost::asio::io_context & _iocontext; using strand_weak_ptr = std::weak_ptr; /** * The boost asio io_context::strand managed pointer. * @var class std::shared_ptr */ strand_weak_ptr _wpstrand; /** * The boost tcp socket. * @var class boost::asio::ip::tcp::socket * @note https://stackoverflow.com/questions/38906711/destroying-boost-asio-socket-without-closing-native-handler */ boost::asio::posix::stream_descriptor _socket; /** * The boost asynchronous deadline timer. * @var class boost::asio::deadline_timer */ boost::asio::deadline_timer _timer; /** * A boolean that indicates if the watcher is monitoring for read events. * @var _read True if reads are being monitored else false. */ bool _read{false}; /** * A boolean that indicates if the watcher has a pending read event. * @var _read True if read is pending else false. */ bool _read_pending{false}; /** * A boolean that indicates if the watcher is monitoring for write events. * @var _read True if writes are being monitored else false. */ bool _write{false}; /** * A boolean that indicates if the watcher has a pending write event. * @var _read True if read is pending else false. */ bool _write_pending{false}; using handler_cb = boost::function; using io_handler = boost::function; using timer_handler = boost::function; /** * Builds a io handler callback that executes the io callback in a strand. * @param io_handler The handler callback to dispatch * @return handler_cb A function wrapping the execution of the handler function in a io_context::strand. */ handler_cb get_dispatch_wrapper(io_handler fn) { const strand_weak_ptr wpstrand = _wpstrand; return [fn, wpstrand](const boost::system::error_code &ec, const std::size_t bytes_transferred) { const strand_shared_ptr strand = wpstrand.lock(); if (!strand) { fn(boost::system::errc::make_error_code(boost::system::errc::operation_canceled), std::size_t{0}); return; } boost::asio::dispatch(strand->context().get_executor(), boost::bind(fn, ec, bytes_transferred)); }; } /** * Binds and returns a read handler for the io operation. * @param connection The connection being watched. * @param fd The file descripter being watched. * @return handler callback */ handler_cb get_read_handler(TcpConnection *const connection, const int fd) { auto fn = boost::bind(&Watcher::read_handler, this, boost::placeholders::_1, boost::placeholders::_2, PTR_FROM_THIS(Watcher), connection, fd); return get_dispatch_wrapper(fn); } /** * Binds and returns a read handler for the io operation. * @param connection The connection being watched. * @param fd The file descripter being watched. * @return handler callback */ handler_cb get_write_handler(TcpConnection *const connection, const int fd) { auto fn = boost::bind(&Watcher::write_handler, this, boost::placeholders::_1, boost::placeholders::_2, PTR_FROM_THIS(Watcher), connection, fd); return get_dispatch_wrapper(fn); } /** * Binds and returns a lamba function handler for the io operation. * @param connection The connection being watched. * @param timeout The file descripter being watched. * @return handler callback */ timer_handler get_timer_handler(TcpConnection *const connection, const uint16_t timeout) { const auto fn = boost::bind(&Watcher::timeout_handler, this, boost::placeholders::_1, PTR_FROM_THIS(Watcher), connection, timeout); const strand_weak_ptr wpstrand = _wpstrand; return [fn, wpstrand](const boost::system::error_code &ec) { const strand_shared_ptr strand = wpstrand.lock(); if (!strand) { fn(boost::system::errc::make_error_code(boost::system::errc::operation_canceled)); return; } boost::asio::dispatch(strand->context().get_executor(), boost::bind(fn, ec)); }; } /** * Handler method that is called by boost's io_context when the socket pumps a read event. * @param ec The status of the callback. * @param bytes_transferred The number of bytes transferred. * @param awpWatcher A weak pointer to this object. * @param connection The connection being watched. * @param fd The file descriptor being watched. * @note The handler will get called if a read is cancelled. */ void read_handler(const boost::system::error_code &ec, const std::size_t bytes_transferred, const std::weak_ptr awpWatcher, TcpConnection *const connection, const int fd) { // Resolve any potential problems with dangling pointers // (remember we are using async). const std::shared_ptr apWatcher = awpWatcher.lock(); if (!apWatcher) { return; } _read_pending = false; if ((!ec || ec == boost::asio::error::would_block) && _read) { connection->process(fd, AMQP::readable); _read_pending = true; _socket.async_read_some( boost::asio::null_buffers(), get_read_handler(connection, fd)); } } /** * Handler method that is called by boost's io_context when the socket pumps a write event. * @param ec The status of the callback. * @param bytes_transferred The number of bytes transferred. * @param awpWatcher A weak pointer to this object. * @param connection The connection being watched. * @param fd The file descriptor being watched. * @note The handler will get called if a write is cancelled. */ void write_handler(const boost::system::error_code ec, const std::size_t bytes_transferred, const std::weak_ptr awpWatcher, TcpConnection *const connection, const int fd) { // Resolve any potential problems with dangling pointers // (remember we are using async). const std::shared_ptr apWatcher = awpWatcher.lock(); if (!apWatcher) { return; } _write_pending = false; if ((!ec || ec == boost::asio::error::would_block) && _write) { connection->process(fd, AMQP::writable); _write_pending = true; _socket.async_write_some( boost::asio::null_buffers(), get_write_handler(connection, fd)); } } /** * Callback method that is called by libev when the timer expires * @param ec error code returned from loop * @param loop The loop in which the event was triggered * @param connection * @param timeout */ void timeout_handler(const boost::system::error_code &ec, std::weak_ptr awpThis, TcpConnection *const connection, const uint16_t timeout) { // Resolve any potential problems with dangling pointers // (remember we are using async). const std::shared_ptr apTimer = awpThis.lock(); if (!apTimer) { return; } if (!ec) { if (connection) { // send the heartbeat connection->heartbeat(); } // Reschedule the timer for the future: _timer.expires_at(_timer.expires_at() + boost::posix_time::seconds(timeout)); // Posts the timer event _timer.async_wait(get_timer_handler(connection, timeout)); } } public: /** * Constructor- initialises the watcher and assigns the filedescriptor to * a boost socket for monitoring. * @param io_context The boost io_context * @param wpstrand A weak pointer to a io_context::strand instance. * @param fd The filedescriptor being watched */ Watcher(boost::asio::io_context &io_context, const strand_weak_ptr wpstrand, const int fd) : _iocontext(io_context), _wpstrand(wpstrand), _socket(io_context), _timer(io_context) { _socket.assign(fd); _socket.non_blocking(true); } /** * Watchers cannot be copied or moved * * @param that The object to not move or copy */ Watcher(Watcher &&that) = delete; Watcher(const Watcher &that) = delete; /** * Destructor */ ~Watcher() { _read = false; _write = false; _socket.release(); stop_timer(); } /** * Change the events for which the filedescriptor is monitored * @param events */ void events(TcpConnection *connection, int fd, int events) { // 1. Handle reads? _read = ((events & AMQP::readable) != 0); // Read requsted but no read pending? if (_read && !_read_pending) { _read_pending = true; _socket.async_read_some( boost::asio::null_buffers(), get_read_handler(connection, fd)); } // 2. Handle writes? _write = ((events & AMQP::writable) != 0); // Write requested but no write pending? if (_write && !_write_pending) { _write_pending = true; _socket.async_write_some( boost::asio::null_buffers(), get_write_handler(connection, fd)); } } /** * Change the expire time * @param connection * @param timeout */ void set_timer(TcpConnection *connection, uint16_t timeout) { // stop timer in case it was already set stop_timer(); // Reschedule the timer for the future: _timer.expires_from_now(boost::posix_time::seconds(timeout)); // Posts the timer event _timer.async_wait(get_timer_handler(connection, timeout)); } /** * Stop the timer */ void stop_timer() { // do nothing if it was never set _timer.cancel(); } }; /** * The boost asio io_context. * @var class boost::asio::io_context& */ boost::asio::io_context & _iocontext; using strand_shared_ptr = std::shared_ptr; /** * The boost asio io_context::strand managed pointer. * @var class std::shared_ptr */ strand_shared_ptr _strand; /** * All I/O watchers that are active, indexed by their filedescriptor * @var std::map */ std::map > _watchers; /** * Method that is called by AMQP-CPP to register a filedescriptor for readability or writability * @param connection The TCP connection object that is reporting * @param fd The filedescriptor to be monitored * @param flags Should the object be monitored for readability or writability? */ void monitor(TcpConnection *const connection, const int fd, const int flags) override { // do we already have this filedescriptor auto iter = _watchers.find(fd); // was it found? if (iter == _watchers.end()) { // we did not yet have this watcher - but that is ok if no filedescriptor was registered if (flags == 0){ return; } // construct a new pair (watcher/timer), and put it in the map const std::shared_ptr apWatcher = std::make_shared(_iocontext, _strand, fd); _watchers[fd] = apWatcher; // explicitly set the events to monitor apWatcher->events(connection, fd, flags); } else if (flags == 0) { // the watcher does already exist, but we no longer have to watch this watcher _watchers.erase(iter); } else { // Change the events on which to act. iter->second->events(connection,fd,flags); } } protected: /** * Method that is called when the heartbeat frequency is negotiated between the server and the client. * @param connection The connection that suggested a heartbeat interval * @param interval The suggested interval from the server * @return uint16_t The interval to use */ virtual uint16_t onNegotiate(TcpConnection *connection, uint16_t interval) override { // skip if no heartbeats are needed if (interval == 0) return 0; const auto fd = connection->fileno(); auto iter = _watchers.find(fd); if (iter == _watchers.end()) return 0; // set the timer iter->second->set_timer(connection, interval); // we agree with the interval return interval; } public: /** * Handler cannot be default constructed. * * @param that The object to not move or copy */ LibBoostAsioHandler() = delete; /** * Constructor * @param io_context The boost io_context to wrap */ explicit LibBoostAsioHandler(boost::asio::io_context &io_context) : _iocontext(io_context), _strand(std::make_shared(_iocontext)) //_timer(std::make_shared(_iocontext,_strand)) { } /** * Handler cannot be copied or moved * * @param that The object to not move or copy */ LibBoostAsioHandler(LibBoostAsioHandler &&that) = delete; LibBoostAsioHandler(const LibBoostAsioHandler &that) = delete; /** * Returns a reference to the boost io_context object that is being used. * @return The boost io_context object. */ boost::asio::io_context &service() { return _iocontext; } /** * Destructor */ ~LibBoostAsioHandler() override = default; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/libev.h000066400000000000000000000411201470663072600173340ustar00rootroot00000000000000/** * LibEV.h * * Implementation for the AMQP::TcpHandler that is optimized for libev. You can * use this class instead of a AMQP::TcpHandler class, just pass the event loop * to the constructor and you're all set * * Compile with: "g++ -std=c++11 libev.cpp -lamqpcpp -lev -lpthread" * * @author Emiel Bruijntjes * @copyright 2015 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "amqpcpp/linux_tcp.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class LibEvHandler : public TcpHandler { private: /** * Internal interface for the object that is being watched */ class Watchable { public: /** * The method that is called when a filedescriptor becomes active * @param fd * @param events */ virtual void onActive(int fd, int events) = 0; /** * Method that is called when the timer expires */ virtual void onExpired() = 0; }; /** * Helper class that wraps a libev I/O watcher */ class Watcher { private: /** * The event loop to which it is attached * @var struct ev_loop */ struct ev_loop *_loop; /** * The actual watcher structure * @var struct ev_io */ struct ev_io _io; /** * Callback method that is called by libev when a filedescriptor becomes active * @param loop The loop in which the event was triggered * @param w Internal watcher object * @param revents Events triggered */ static void callback(struct ev_loop *loop, struct ev_io *watcher, int revents) { // retrieve the watched object Watchable *object = static_cast(watcher->data); // tell the object that its filedescriptor is active object->onActive(watcher->fd, revents); } public: /** * Constructor * @param loop The current event loop * @param object The object being watched * @param fd The filedescriptor being watched * @param events The events that should be monitored * @param priority The priority for the watcher */ Watcher(struct ev_loop *loop, Watchable *object, int fd, int events, int priority) : _loop(loop) { // initialize the libev structure ev_io_init(&_io, callback, fd, events); // install a priority ev_set_priority(&_io, priority); // store the object in the data "void*" _io.data = object; // start the watcher ev_io_start(_loop, &_io); } /** * Watchers cannot be copied or moved * * @param that The object to not move or copy */ Watcher(Watcher &&that) = delete; Watcher(const Watcher &that) = delete; /** * Destructor */ virtual ~Watcher() { // stop the watcher ev_io_stop(_loop, &_io); } /** * Check if a filedescriptor is covered by the watcher * @param fd * @return bool */ bool contains(int fd) const { return _io.fd == fd; } /** * Change the events for which the filedescriptor is monitored * @param events */ void events(int events) { // stop the watcher if it was active ev_io_stop(_loop, &_io); // set the events ev_io_set(&_io, _io.fd, events); // and restart it ev_io_start(_loop, &_io); } }; /** * Wrapper around a connection, this will monitor the filedescriptors * and run a timer to send out heartbeats */ class Wrapper : private Watchable { private: /** * The connection that is monitored * @var TcpConnection */ TcpConnection *_connection; /** * The event loop to which it is attached * @var struct ev_loop */ struct ev_loop *_loop; /** * The watcher for the timer * @var struct ev_io */ struct ev_timer _timer; /** * IO-watchers to monitor filedescriptors * @var std::list */ std::list _watchers; /** * When should we send the next heartbeat? * @var ev_tstamp */ ev_tstamp _next = 0.0; /** * When does the connection expire / was the server for a too longer period of time idle? * During connection setup, this member is used as the connect-timeout. * @var ev_tstamp */ ev_tstamp _expire; /** * Timeout after which the connection is no longer considered alive. * A heartbeat must be sent every _timeout / 2 seconds. * Value zero means heartbeats are disabled, or not yet negotiated. * @var uint16_t */ uint16_t _timeout = 0; /** * Callback method that is called by libev when the timer expires * @param loop The loop in which the event was triggered * @param timer Internal timer object * @param revents The events that triggered this call */ static void callback(struct ev_loop *loop, struct ev_timer *timer, int revents) { // retrieve the object Watchable *object = static_cast(timer->data); // tell the object that it expired object->onExpired(); } /** * Do we need timers / is this a timed monitor? * @return bool */ bool timed() const { // if neither timers are set return _expire > 0.0 || _next > 0.0; } /** * Method that is called when the timer expired */ virtual void onExpired() override { // get the current time ev_tstamp now = ev_now(_loop); // timer is no longer active, so the refcounter in the loop is restored ev_ref(_loop); // if the onNegotiate method was not yet called, and no heartbeat timeout was negotiated if (_timeout == 0) { // this can happen in three situations: 1. a connect-timeout, 2. user space has // told us that we're not interested in heartbeats, 3. rabbitmq does not want heartbeats, // in either case we're no longer going to run further timers. _next = _expire = 0.0; // if we have an initialized connection, user-space must have overridden the onNegotiate // method, so we keep using the connection if (_connection->initialized()) return; // this is a connection timeout, close the connection from our side too return (void)_connection->close(true); } else if (now >= _expire) { // the server was inactive for a too long period of time, reset state _next = _expire = 0.0; _timeout = 0; // close the connection because server was inactive (we close it with immediate effect, // because it was inactive so we cannot trust it to respect the AMQP close handshake) return (void)_connection->close(true); } else if (now >= _next) { // it's time for the next heartbeat _connection->heartbeat(); // remember when we should send out the next one, so the next one should be // sent only after _timout/2 seconds again _from now_ (no catching up) _next = now + std::max(_timeout / 2, 1); } // reset the timer to trigger again later ev_timer_set(&_timer, std::min(_next, _expire) - now, 0.0); // and start it again ev_timer_start(_loop, &_timer); // and because the timer is running again, we restore the refcounter ev_unref(_loop); } /** * Method that is called when a filedescriptor becomes active * @param fd the filedescriptor that is active * @param events the events that are active (readable/writable) */ virtual void onActive(int fd, int events) override { // if the server is readable, we have some extra time before it expires, the expire time // is set to 1.5 * _timeout to close the connection when the third heartbeat is about to be sent if (_timeout != 0 && (events & EV_READ)) _expire = ev_now(_loop) + _timeout * 1.5; // pass on to the connection _connection->process(fd, events); } public: /** * Constructor * @param loop The current event loop * @param connection The TCP connection * @param timeout Connect timeout * @param priority The priority (high priorities are invoked earlier */ Wrapper(struct ev_loop *loop, AMQP::TcpConnection *connection, uint16_t timeout, int priority) : _connection(connection), _loop(loop), _next(0.0), _expire(ev_now(loop) + timeout), _timeout(0) { // store the object in the data "void*" _timer.data = this; // initialize the libev structure, it should expire after the connection timeout ev_timer_init(&_timer, callback, timeout, 0.0); // set a priority ev_set_priority(&_timer, priority); // start the timer (this is the time that we reserve for setting up the connection) ev_timer_start(_loop, &_timer); // the timer should not keep the event loop active ev_unref(_loop); } /** * Watchers cannot be copied or moved * * @param that The object to not move or copy */ Wrapper(Wrapper &&that) = delete; Wrapper(const Wrapper &that) = delete; /** * Destructor */ virtual ~Wrapper() { // the timer was already stopped if (!timed()) return; // stop the timer ev_timer_stop(_loop, &_timer); // restore loop refcount ev_ref(_loop); } /** * Start the timer (and expose the interval) * @param interval the heartbeat interval proposed by the server * @return uint16_t the heartbeat interval that we accepted */ uint16_t start(uint16_t timeout) { // we now know for sure that the connection was set up _timeout = timeout; // if heartbeats are disabled we do not have to set it if (_timeout == 0) return 0; // calculate current time auto now = ev_now(_loop); // we also know when the next heartbeat should be sent _next = now + std::max(_timeout / 2, 1); // because the server has just sent us some data, we will update the expire time too _expire = now + _timeout * 1.5; // stop the existing timer (we have to stop it and restart it, because ev_timer_set() // on its own does not change the running timer) (note that we assume that the timer // is already running and keeps on running, so no calls to ev_ref()/en_unref() here) ev_timer_stop(_loop, &_timer); // find the earliest thing that expires ev_timer_set(&_timer, std::min(_next, _expire) - now, 0.0); // and start it again ev_timer_start(_loop, &_timer); // expose the accepted interval return _timeout; } /** * Check if the timer is associated with a certain connection * @param connection * @return bool */ bool contains(const AMQP::TcpConnection *connection) const { // compare the connections return _connection == connection; } /** * Monitor a filedescriptor * @param fd * @param events */ void monitor(int fd, int events) { // should we remove? if (events == 0) { // remove the io-watcher _watchers.remove_if([fd](const Watcher &watcher) -> bool { // compare filedescriptors return watcher.contains(fd); }); } else { // look in the array for this filedescriptor for (auto &watcher : _watchers) { // do we have a match? if (watcher.contains(fd)) return watcher.events(events); } // we need a watcher Watchable *watchable = this; // we should monitor a new filedescriptor _watchers.emplace_back(_loop, watchable, fd, events, ev_priority(&_timer)); } } }; /** * The event loop * @var struct ev_loop* */ struct ev_loop *_loop; /** * Each connection is wrapped * @var std::list */ std::list _wrappers; /** * The priority that watchers should have (higher prio means libev gives more prio to this eveht) * @var int */ int _priority; /** * Lookup a connection-wrapper, when the wrapper is not found, we construct one * @param connection * @return Wrapper */ Wrapper &lookup(TcpConnection *connection) { // look for the appropriate connection for (auto &wrapper : _wrappers) { // do we have a match? if (wrapper.contains(connection)) return wrapper; } // add to the wrappers _wrappers.emplace_back(_loop, connection, 60, _priority); // done return _wrappers.back(); } protected: /** * Method that is called by AMQP-CPP to register a filedescriptor for readability or writability * @param connection The TCP connection object that is reporting * @param fd The filedescriptor to be monitored * @param flags Should the object be monitored for readability or writability? */ virtual void monitor(TcpConnection *connection, int fd, int flags) override { // lookup the appropriate wrapper and start monitoring lookup(connection).monitor(fd, flags); } /** * Method that is called when the heartbeat timeout is negotiated between the server and the client. * @param connection The connection that suggested a heartbeat timeout * @param timeout The suggested timeout from the server * @return uint16_t The timeout to use */ virtual uint16_t onNegotiate(TcpConnection *connection, uint16_t timeout) override { // lookup the wrapper, and start the timer to check for activity and send heartbeats return lookup(connection).start(timeout); } /** * Method that is called when the TCP connection is destructed * @param connection The TCP connection */ virtual void onDetached(TcpConnection *connection) override { // remove from the array _wrappers.remove_if([connection](const Wrapper &wrapper) -> bool { return wrapper.contains(connection); }); } public: /** * Constructor * @param loop The event loop to wrap * @param priority The libev priority (higher priorities are invoked earlier) */ LibEvHandler(struct ev_loop *loop, int priority = 0) : _loop(loop), _priority(priority) {} /** * Destructor */ virtual ~LibEvHandler() = default; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/libevent.h000066400000000000000000000137231470663072600200530ustar00rootroot00000000000000/** * LibEvent.h * * Implementation for the AMQP::TcpHandler that is optimized for libevent. You can * use this class instead of a AMQP::TcpHandler class, just pass the event loop * to the constructor and you're all set * * Compile with: "g++ -std=c++11 libevent.cpp -lamqpcpp -levent -lpthread" * * @author Brent Dimmig */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include /** * Set up namespace */ namespace AMQP { /** * Class definition */ class LibEventHandler : public TcpHandler { private: /** * Helper class that wraps a libev I/O watcher */ class Watcher { private: /** * The actual event structure * @var struct event */ struct event * _event; /** * Callback method that is called by libevent when a filedescriptor becomes active * @param fd The filedescriptor with an event * @param what Events triggered * @param connection_arg void * to the connection */ static void callback(evutil_socket_t fd, short what, void *connection_arg) { // retrieve the connection TcpConnection *connection = static_cast(connection_arg); // setup amqp flags int amqp_flags = 0; if (what & EV_READ) amqp_flags |= AMQP::readable; if (what & EV_WRITE) amqp_flags |= AMQP::writable; // tell the connection that its filedescriptor is active connection->process(fd, amqp_flags); } public: /** * Constructor * @param evbase The current event loop * @param connection The connection being watched * @param fd The filedescriptor being watched * @param events The events that should be monitored */ Watcher(struct event_base *evbase, TcpConnection *connection, int fd, int events) { // setup libevent flags short event_flags = EV_PERSIST; if (events & AMQP::readable) event_flags |= EV_READ; if (events & AMQP::writable) event_flags |= EV_WRITE; // initialize the event _event = event_new(evbase, fd, event_flags, callback, connection); event_add(_event, nullptr); } /** * Destructor */ virtual ~Watcher() { // stop the event event_del(_event); // free the event event_free(_event); } /** * Change the events for which the filedescriptor is monitored * @param events */ void events(int events) { // stop the event if it was active event_del(_event); // setup libevent flags short event_flags = EV_PERSIST; if (events & AMQP::readable) event_flags |= EV_READ; if (events & AMQP::writable) event_flags |= EV_WRITE; // set the events event_assign(_event, event_get_base(_event), event_get_fd(_event), event_flags, event_get_callback(_event), event_get_callback_arg(_event)); // and restart it event_add(_event, nullptr); } }; /** * The event loop * @var struct event_base* */ struct event_base *_evbase; /** * All I/O watchers that are active, indexed by their filedescriptor * @var std::map */ std::map> _watchers; /** * Method that is called when the heartbeat frequency is negotiated * @param connection The connection that suggested a heartbeat interval * @param interval The suggested interval from the server * @return uint16_t The interval to use */ virtual uint16_t onNegotiate(TcpConnection *connection, uint16_t interval) override { // call base (in the highly theoretical case that the base class does something meaningful) auto response = TcpHandler::onNegotiate(connection, interval); // because the LibEvHandler has not yet implemented timers for ensuring that we send // some data every couple of seconds, we disabled timeouts return 0; } /** * Method that is called by AMQP-CPP to register a filedescriptor for readability or writability * @param connection The TCP connection object that is reporting * @param fd The filedescriptor to be monitored * @param flags Should the object be monitored for readability or writability? */ virtual void monitor(TcpConnection *connection, int fd, int flags) override { // do we already have this filedescriptor auto iter = _watchers.find(fd); // was it found? if (iter == _watchers.end()) { // we did not yet have this watcher - but that is ok if no filedescriptor was registered if (flags == 0) return; // construct a new watcher, and put it in the map _watchers[fd] = std::unique_ptr(new Watcher(_evbase, connection, fd, flags)); } else if (flags == 0) { // the watcher does already exist, but we no longer have to watch this watcher _watchers.erase(iter); } else { // change the events iter->second->events(flags); } } public: /** * Constructor * @param evbase The event loop to wrap */ LibEventHandler(struct event_base *evbase) : _evbase(evbase) {} /** * Destructor */ virtual ~LibEventHandler() = default; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/libuv.h000066400000000000000000000140231470663072600173560ustar00rootroot00000000000000/** * LibUV.h * * Implementation for the AMQP::TcpHandler that is optimized for libuv. You can * use this class instead of a AMQP::TcpHandler class, just pass the event loop * to the constructor and you're all set. * * Based heavily on the libev.h implementation by Emiel Bruijntjes * * @author David Nikdel * @copyright 2015 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include "amqpcpp/linux_tcp.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class LibUvHandler : public TcpHandler { private: /** * Helper class that wraps a libev I/O watcher */ class Watcher { private: /** * The event loop to which it is attached * @var uv_loop_t */ uv_loop_t *_loop; /** * The actual watcher structure * @var uv_poll_t */ uv_poll_t *_poll; /** * Callback method that is called by libuv when a filedescriptor becomes active * @param handle Internal poll handle * @param status LibUV error code UV_* * @param events Events triggered */ static void callback(uv_poll_t *handle, int status, int events) { // retrieve the connection TcpConnection *connection = static_cast(handle->data); // tell the connection that its filedescriptor is active int fd = -1; uv_fileno(reinterpret_cast(handle), &fd); connection->process(fd, uv_to_amqp_events(status, events)); } public: /** * Constructor * @param loop The current event loop * @param connection The connection being watched * @param fd The filedescriptor being watched * @param events The events that should be monitored */ Watcher(uv_loop_t *loop, TcpConnection *connection, int fd, int events) : _loop(loop) { // create a new poll _poll = new uv_poll_t(); // initialize the libev structure uv_poll_init(_loop, _poll, fd); // store the connection in the data "void*" _poll->data = connection; // start the watcher uv_poll_start(_poll, amqp_to_uv_events(events), callback); } /** * Watchers cannot be copied or moved * * @param that The object to not move or copy */ Watcher(Watcher &&that) = delete; Watcher(const Watcher &that) = delete; /** * Destructor */ virtual ~Watcher() { // stop the watcher uv_poll_stop(_poll); // close the handle uv_close(reinterpret_cast(_poll), [](uv_handle_t* handle) { // delete memory once closed delete reinterpret_cast(handle); }); } /** * Change the events for which the filedescriptor is monitored * @param events */ void events(int events) { // update the events being watched for uv_poll_start(_poll, amqp_to_uv_events(events), callback); } private: /** * Convert event flags from UV format to AMQP format */ static int uv_to_amqp_events(int status, int events) { // if the socket is closed report both so we pick up the error if (status != 0) return AMQP::readable | AMQP::writable; // map read or write int amqp_events = 0; if (events & UV_READABLE) amqp_events |= AMQP::readable; if (events & UV_WRITABLE) amqp_events |= AMQP::writable; return amqp_events; } /** * Convert event flags from AMQP format to UV format */ static int amqp_to_uv_events(int events) { int uv_events = 0; if (events & AMQP::readable) uv_events |= UV_READABLE; if (events & AMQP::writable) uv_events |= UV_WRITABLE; return uv_events; } }; /** * The event loop * @var uv_loop_t* */ uv_loop_t *_loop; /** * All I/O watchers that are active, indexed by their filedescriptor * @var std::map */ std::map> _watchers; /** * Method that is called by AMQP-CPP to register a filedescriptor for readability or writability * @param connection The TCP connection object that is reporting * @param fd The filedescriptor to be monitored * @param flags Should the object be monitored for readability or writability? */ virtual void monitor(TcpConnection *connection, int fd, int flags) override { // do we already have this filedescriptor auto iter = _watchers.find(fd); // was it found? if (iter == _watchers.end()) { // we did not yet have this watcher - but that is ok if no filedescriptor was registered if (flags == 0) return; // construct a new watcher, and put it in the map _watchers[fd] = std::unique_ptr(new Watcher(_loop, connection, fd, flags)); } else if (flags == 0) { // the watcher does already exist, but we no longer have to watch this watcher _watchers.erase(iter); } else { // change the events iter->second->events(flags); } } public: /** * Constructor * @param loop The event loop to wrap */ LibUvHandler(uv_loop_t *loop) : _loop(loop) {} /** * Destructor */ virtual ~LibUvHandler() = default; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/linux_tcp.h000066400000000000000000000002121470663072600202350ustar00rootroot00000000000000#include "linux_tcp/tcpparent.h" #include "linux_tcp/tcphandler.h" #include "linux_tcp/tcpconnection.h" #include "linux_tcp/tcpchannel.h" AMQP-CPP-4.3.27/include/amqpcpp/linux_tcp/000077500000000000000000000000001470663072600200715ustar00rootroot00000000000000AMQP-CPP-4.3.27/include/amqpcpp/linux_tcp/tcpchannel.h000066400000000000000000000022431470663072600223620ustar00rootroot00000000000000/** * TcpChannel.h * * Extended channel that can be constructed on top of a TCP connection * * @author Emiel Bruijntjes * @copyright 2015 - 2017 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * Class definition */ class TcpChannel : public Channel { public: /** * Constructor * * The passed in connection pointer must remain valid for the * lifetime of the channel. A constructor is thrown if the channel * cannot be connected (because the connection is already closed or * because max number of channels has been reached) * * @param connection * @throws std::runtime_error */ TcpChannel(TcpConnection *connection) : Channel(&connection->_connection) {} /** * Destructor */ virtual ~TcpChannel() {} /** * Copying is not allowed. * @param other */ TcpChannel(const TcpChannel &other) = delete; /** * But movement is allowed * @param other */ TcpChannel(TcpChannel &&other) = default; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/linux_tcp/tcpconnection.h000066400000000000000000000236251470663072600231200ustar00rootroot00000000000000/** * TcpConnection.h * * Extended Connection object that creates a TCP connection for the * IO between the client application and the RabbitMQ server. * * @author Emiel Bruijntjes * @copyright 2015 - 2021 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class TcpState; class TcpChannel; /** * Class definition */ class TcpConnection : private ConnectionHandler, private Watchable, private TcpParent { private: /** * User-space handler object * @var TcpHandler */ TcpHandler *_handler; /** * The state of the TCP connection - this state objecs changes based on * the state of the connection (resolving, connected or closed) * @var std::unique_ptr */ std::unique_ptr _state; /** * The underlying AMQP connection * @var Connection */ Connection _connection; /** * The channel may access out _connection * @friend */ friend TcpChannel; /** * Method that is called when the RabbitMQ server and your client application * exchange some properties that describe their identity. * @param connection The connection about which information is exchanged * @param server Properties sent by the server * @param client Properties that are to be sent back */ virtual void onProperties(Connection *connection, const Table &server, Table &client) override; /** * Method that is called when the heartbeat frequency is negotiated. * @param connection The connection that suggested a heartbeat interval * @param interval The suggested interval from the server * @return uint16_t The interval to use */ virtual uint16_t onNegotiate(Connection *connection, uint16_t interval) override; /** * Method that is called by the connection when data needs to be sent over the network * @param connection The connection that created this output * @param buffer Data to send * @param size Size of the buffer */ virtual void onData(Connection *connection, const char *buffer, size_t size) override; /** * Method that is called when the server sends a heartbeat to the client * @param connection The connection over which the heartbeat was received */ virtual void onHeartbeat(Connection *connection) override { // pass on to tcp handler if (_handler) _handler->onHeartbeat(this); } /** * Method called when the connection ends up in an error state * @param connection The connection that entered the error state * @param message Error message */ virtual void onError(Connection *connection, const char *message) override; /** * Method that is called when the AMQP connection is established * @param connection The connection that can now be used */ virtual void onReady(Connection *connection) override { // pass on to the handler if (_handler) _handler->onReady(this); } /** * Method that is called when the connection was closed. * @param connection The connection that was closed and that is now unusable */ virtual void onClosed(Connection *connection) override; /** * Method that is called when the AMQP connection was blocked. * @param connection The connection that was blocked * @param reason Why was the connection blocked */ virtual void onBlocked(Connection *connection, const char *reason) override { // pass to user space if (_handler) _handler->onBlocked(this, reason); } /** * Method that is called when the AMQP connection is no longer blocked. * @param connection The connection that is no longer blocked */ virtual void onUnblocked(Connection *connection) override { // pass to user space if (_handler) _handler->onUnblocked(this); } /** * Method that is called when the tcp connection has been established * @param state */ virtual void onConnected(TcpState *state) override { // pass on to the handler if (_handler) _handler->onConnected(this); } /** * Method that is called when right before connection is being secured * @param state * @param ssl * @return bool */ virtual bool onSecuring(TcpState *state, SSL *ssl) override { // pass on to user-space return _handler && _handler->onSecuring(this, ssl); } /** * Method that is called when the connection is secured * @param state * @param ssl * @return bool */ virtual bool onSecured(TcpState *state, const SSL *ssl) override { // pass on to user-space return _handler && _handler->onSecured(this, ssl); } /** * Method to be called when data was received * @param state * @param buffer * @return size_t */ virtual size_t onReceived(TcpState *state, const Buffer &buffer) override { // pass on to the connection return _connection.parse(buffer); } /** * Method to be called when we need to monitor a different filedescriptor * @param state * @param fd * @param events */ virtual void onIdle(TcpState *state, int socket, int events) override { // pass on to user-space if (_handler) _handler->monitor(this, socket, events); } /** * Method that is called when an error occurs (the connection is lost) * @param state * @param error * @param connected */ virtual void onError(TcpState *state, const char *message, bool connected) override; /** * Method to be called when it is detected that the connection was lost * @param state */ virtual void onLost(TcpState *state) override; /** * The expected number of bytes * @return size_t */ virtual size_t expected() override { // pass on to the connection return _connection.expected(); } public: /** * Constructor * @param handler User implemented handler object * @param hostname The address to connect to */ TcpConnection(TcpHandler *handler, const Address &address); /** * No copying * @param that */ TcpConnection(const TcpConnection &that) = delete; /** * Destructor */ virtual ~TcpConnection() noexcept; /** * The filedescriptor that is used for this connection * @return int */ int fileno() const; /** * Process the TCP connection * * This method should be called when the filedescriptor that is registered * in the event loop becomes active. You should pass in a flag holding the * flags AMQP::readable or AMQP::writable to indicate whether the descriptor * was readable or writable, or bitwise-or if it was both * * @param fd The filedescriptor that became readable or writable * @param events What sort of events occured? */ void process(int fd, int flags); /** * Close the connection in an elegant fashion. This closes all channels and the * TCP connection. Note that the connection is not immediately closed: first all * pending operations are completed, and then an AMQP closing-handshake is * performed. If you pass a parameter "immediate=true" the connection is * immediately closed, without waiting for earlier commands (and your handler's * onError() method is called about the premature close, including the onLost() and * onDetached()). * @return bool */ bool close(bool immediate = false); /** * Is the connection connected, meaning: it has passed the login handshake * and isn't closed yet? * @return bool */ bool ready() const { return _connection.ready(); } /** * Is the connection initialized, meaning: it has passed the login handshake? * It may be closing or closed * @return bool */ bool initialized() const { return _connection.initialized(); } /** * Is the connection in a usable state / not yet closed or being closed * When a connection is usable, you can send further commands over it. When it is * unusable, it may still be connected and finished queued commands. * @return bool */ bool usable() const { return _connection.usable(); } /** * Is the connection closed and full dead? The entire TCP connection has been discarded. * @return bool */ bool closed() const; /** * The max frame size. Useful if you set up a buffer to parse incoming data: it does not have to exceed this size. * @return uint32_t */ uint32_t maxFrame() const { return _connection.maxFrame(); } /** * The number of bytes that can best be passed to the next call to the parse() method. * @return uint32_t */ uint32_t expected() const { return _connection.expected(); } /** * Return the number of channels this connection has. * @return std::size_t */ std::size_t channels() const { // return the number of channels this connection has return _connection.channels(); } /** * The number of outgoing bytes queued on this connection. * @return std::size_t */ std::size_t queued() const; /** * Send a heartbeat * @return bool */ bool heartbeat() { return _connection.heartbeat(); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/linux_tcp/tcpdefines.h000066400000000000000000000010441470663072600223650ustar00rootroot00000000000000#pragma once /** * No MSG_NOSIGNAL on OS X. * Avoid SIGPIPE by using sockopt call. */ #ifdef MSG_NOSIGNAL # define AMQP_CPP_MSG_NOSIGNAL MSG_NOSIGNAL #else # define AMQP_CPP_MSG_NOSIGNAL 0 # ifdef SO_NOSIGPIPE # define AMQP_CPP_USE_SO_NOSIGPIPE # else # error "Cannot block SIGPIPE!" # endif #endif #ifdef AMQP_CPP_USE_SO_NOSIGPIPE /** * Ignore SIGPIPE when there is no MSG_NOSIGNAL. */ inline void set_sockopt_nosigpipe(int socket) { int optval = 1; setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)); } #endif AMQP-CPP-4.3.27/include/amqpcpp/linux_tcp/tcphandler.h000066400000000000000000000244601470663072600223740ustar00rootroot00000000000000/** * TcpHandler.h * * Interface to be implemented by the caller of the AMQP library in case * the "Tcp" class is being used as alternative for the ConnectionHandler * class. * * @author Emiel Bruijntjes * @copyright 2015 - 2021 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class TcpConnection; /** * Class definition */ class TcpHandler { public: /** * Destructor */ virtual ~TcpHandler() = default; /** * Method that is called immediately after a connection has been constructed. * @param connection The connection object that was just constructed */ virtual void onAttached(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the TCP connection ends up in a connected state * This method is called after the TCP connection has been set up, but before * the (optional) secure TLS connection is ready, and before the AMQP login * handshake has been completed. If this step has been set, the onLost() * method will also always be called when the connection is closed. * @param connection The TCP connection */ virtual void onConnected(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called after a TCP connection has been set up, and right before * the SSL handshake is going to be performed to secure the connection (only for * amqps:// connections). This method can be overridden in user space to load * client side certificates. * @param connection The connection for which TLS was just started * @param ssl Pointer to the SSL structure that can be modified * @return bool True to proceed / accept the connection, false to break up */ virtual bool onSecuring(TcpConnection *connection, SSL *ssl) { // make sure compilers dont complain about unused parameters (void) connection; (void) ssl; // default implementation: do not do anything, just allow the connection return true; } /** * Method that is called after a TCP connection has been set up and the initial * TLS handshake is finished too, but right before the AMQP login handshake is * going to take place and the first data is going to be sent over the connection. * This method allows you to inspect the TLS certificate and other connection * properties, and to break up the connection if you find it not secure enough. * The default implementation considers all connections to be secure, even if the * connection has a self-signed or even invalid certificate. To be more strict, * override this method, inspect the certificate and return false if you do not * want to use the connection. The passed in SSL pointer is a pointer to a SSL * structure from the openssl library. This method is only called for secure * connections (connection with an amqps:// address). * @param connection The connection for which TLS was just started * @param ssl Pointer to the SSL structure that can be inspected * @return bool True to proceed / accept the connection, false to break up */ virtual bool onSecured(TcpConnection *connection, const SSL *ssl) { // make sure compilers dont complain about unused parameters (void) connection; (void) ssl; // default implementation: do not inspect anything, just allow the connection return true; } /** * Method that is called when the RabbitMQ server and your client application * exchange some properties that describe their identity. * @param connection The connection about which information is exchanged * @param server Properties sent by the server * @param client Properties that are to be sent back */ virtual void onProperties(TcpConnection *connection, const Table &server, Table &client) { // make sure compilers dont complaint about unused parameters (void) connection; (void) server; (void) client; } /** * Method that is called when the heartbeat frequency is negotiated * between the server and the client. Applications can override this method * if they want to use a different heartbeat interval (for example: return 0 * to disable heartbeats) * @param connection The connection that suggested a heartbeat interval * @param interval The suggested interval from the server * @return uint16_t The interval to use * * @see ConnectionHandler::onNegotiate */ virtual uint16_t onNegotiate(TcpConnection *connection, uint16_t interval) { // make sure compilers dont complain about unused parameters (void) connection; (void) interval; // default implementation, suggested heartbeat is ok return interval; } /** * Method that is called after the AMQP login handshake has been completed * and the connection object is ready for sending out actual AMQP instructions * @param connection The TCP connection */ virtual void onReady(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the server sends a heartbeat to the client * @param connection The connection over which the heartbeat was received * @see ConnectionHandler::onHeartbeat */ virtual void onHeartbeat(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the connection ends up in an error state * This could either be an error at the AMQP level, but could also * be an error at the TCP of SSL level (like a broken connection). * If the connection is connected (the onConnected() method was called * before), the onLost() method is going to be called too. * @param connection The TCP connection * @param message Error message */ virtual void onError(TcpConnection *connection, const char *message) { // make sure compilers dont complain about unused parameters (void) connection; (void) message; } /** * Method that is called when the AMQP protocol was gracefully ended. * This is the counter-part of a call to connection.close(). Note that * the underlying TCP connection is still alive, and onLost() and * onDetached() (see below) are going to be called too. * @param connection The TCP connection */ virtual void onClosed(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the AMQP connection was blocked. * * This method is called, when the server connection gets blocked for the first * time due to the broker running low on a resource (memory or disk). For * example, when a RabbitMQ node detects that it is low on RAM, it sends a * notification to all connected publishing clients supporting this feature. * If before the connections are unblocked the node also starts running low on * disk space, another notification will not be sent. * * @param connection The connection that was blocked * @param reason Why was the connection blocked */ virtual void onBlocked(TcpConnection *connection, const char *reason) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the AMQP connection is no longer blocked. * * This method is called when all resource alarms have cleared and the * connection is fully unblocked. * * @param connection The connection that is no longer blocked */ virtual void onUnblocked(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the TCP connection is lost or closed. This * is always called if you have also received a call to onConnected(). * @param connection The TCP connection */ virtual void onLost(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Method that is called when the handler will no longer be notified. * This is the last call to your handler, and it is typically used * to clean up stuff. * @param connection The connection that is being destructed */ virtual void onDetached(TcpConnection *connection) { // make sure compilers dont complain about unused parameters (void) connection; } /** * Monitor a filedescriptor for readability or writability * * When a TCP connection is opened, it creates a non-blocking socket * connection. This method is called to inform you about this socket, * so that you can include it in the event loop. When the socket becomes * active, you should call the "process()" method in the Tcp class. * * The flags is AMQP::readable if the filedescriptor should be monitored * for readability, AMQP::writable if it is to be monitored for writability, * or AMQP::readable | AMQP::writable if it has to be checked for both. * If flags has value 0, the filedescriptor should be removed from the * event loop. * * @param connection The TCP connection object that is reporting * @param fd The filedescriptor to be monitored * @param flags Should the object be monitored for readability or writability? */ virtual void monitor(TcpConnection *connection, int fd, int flags) = 0; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/linux_tcp/tcpparent.h000066400000000000000000000043041470663072600222430ustar00rootroot00000000000000/** * TcpParent.h * * Interface to be implemented by the parent of a tcp-state. This is * an _internal_ interface that is not relevant for user-space applications. * * @author Emiel Bruijntjes * @copyright 2018 - 2021 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Begin of namespace */ namespace AMQP { /** * Forward declarations */ class TcpState; class Buffer; /** * Class definition */ class TcpParent { public: /** * Destructor */ virtual ~TcpParent() = default; /** * Method that is called when the TCP connection has been established * @param state */ virtual void onConnected(TcpState *state) = 0; /** * Method that is called right before a connection is secured and that allows userspac to change SSL * @param state * @param ssl * @return bool */ virtual bool onSecuring(TcpState *state, SSL *ssl) = 0; /** * Method that is called when the connection is secured * @param state * @param ssl * @return bool */ virtual bool onSecured(TcpState *state, const SSL *ssl) = 0; /** * Method to be called when data was received * @param state * @param buffer * @return size_t */ virtual size_t onReceived(TcpState *state, const Buffer &buffer) = 0; /** * Method to be called when we need to monitor a different filedescriptor * @param state * @param fd * @param events */ virtual void onIdle(TcpState *state, int socket, int events) = 0; /** * Method that is called when an error occurs (the connection is lost) * @param state * @param error * @param connected */ virtual void onError(TcpState *state, const char *message, bool connected = true) = 0; /** * Method to be called when it is detected that the connection was lost * @param state */ virtual void onLost(TcpState *state) = 0; /** * The expected number of bytes * @return size_t */ virtual size_t expected() = 0; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/login.h000066400000000000000000000061471470663072600173550ustar00rootroot00000000000000/** * The login information to access a server * * This class combines login, password and vhost * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Set up namespace */ namespace AMQP { /** * Class definition */ class Login { private: /** * The username * @var string */ std::string _user; /** * The password * @var string */ std::string _password; public: /** * Default constructor */ Login() : _user("guest"), _password("guest") {} /** * Constructor * @param user * @param password */ Login(std::string user, std::string password) : _user(std::move(user)), _password(std::move(password)) {} /** * Constructor * @param user * @param password */ Login(const char *user, const char *password) : _user(user), _password(password) {} /** * Destructor */ virtual ~Login() = default; /** * Cast to boolean: is the login set? * @return bool */ operator bool () const { return !_user.empty() || !_password.empty(); } /** * Negate operator: is it not set * @return bool */ bool operator! () const { return _user.empty() && _password.empty(); } /** * Retrieve the user name * @return string */ const std::string &user() const { return _user; } /** * Retrieve the password * @return string */ const std::string &password() const { return _password; } /** * String representation in SASL PLAIN mode * @return string */ std::string saslPlain() const { // we need an initial string std::string result("\0", 1); // append other elements return result.append(_user).append("\0",1).append(_password); } /** * Comparison operator * @param that * @return bool */ bool operator==(const Login &that) const { // username and password must match return _user == that._user && _password == that._password; } /** * Comparison operator * @param that * @return bool */ bool operator!=(const Login &that) const { // the opposite of operator== return !operator==(that); } /** * Comparison operator * @param that * @return bool */ bool operator<(const Login &that) const { // compare users if (_user != that._user) return _user < that._user; // compare passwords return _password < that._password; } /** * Friend function to allow writing the login to a stream * @param stream * @param login * @return std::ostream */ friend std::ostream &operator<<(std::ostream &stream, const Login &login) { // write username and password return stream << login._user << ":" << login._password; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/message.h000066400000000000000000000100221470663072600176540ustar00rootroot00000000000000/** * Message.h * * An incoming message has the same sort of information as an outgoing * message, plus some additional information. * * Message objects can not be constructed by end users, they are only constructed * by the AMQP library, and passed to user callbacks. * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "envelope.h" #include #include #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class DeferredReceiver; /** * Class definition */ class Message : public Envelope { private: /** * An allocated and mutable block of memory underlying _body * @var char * */ char *_mutableBody = nullptr; protected: /** * The exchange to which it was originally published * @var string */ std::string _exchange; /** * The routing key that was originally used * @var string */ std::string _routingkey; /** * Number of bytes already filled * @var size_t */ size_t _filled = 0; /** * We are an open book to the consumer handler */ friend class DeferredReceiver; /** * Set the body size * This field is set when the header is received * @param uint64_t */ void setBodySize(uint64_t size) { // safety-check: on 32-bit platforms size_t is obviously also a 32-bit dword // in which case casting the uint64_t to a size_t could result in truncation // here we check whether the given size fits inside a size_t if (std::numeric_limits::max() < size) throw std::runtime_error("message is too big for this system"); // store the new size _bodySize = size; } /** * Append data * @param buffer incoming data * @param size size of the data * @return bool true if the message is now complete */ bool append(const char *buffer, uint64_t size) { // is the body already allocated? if (_mutableBody) { // prevent overflow size = std::min(size, _bodySize - _filled); // append more data memcpy(_mutableBody + _filled, buffer, (size_t)size); // update filled data _filled += (size_t)size; } else if (size >= _bodySize) { // we do not have to combine multiple frames, so we can store // the buffer pointer in the message _body = buffer; } else { // allocate the buffer _mutableBody = (char *)malloc((size_t)_bodySize); // expose the body in its immutable form _body = _mutableBody; // store the initial data _filled = std::min((size_t)size, (size_t)_bodySize); memcpy(_mutableBody, buffer, _filled); } // check if we're done return _filled >= _bodySize; } public: /** * Constructor * * @param exchange * @param routingKey */ Message(std::string exchange, std::string routingkey) : Envelope(nullptr, 0), _exchange(std::move(exchange)), _routingkey(std::move(routingkey)) {} /** * Disabled copy constructor * @param message the message to copy */ Message(const Message &message) = delete; /** * Destructor */ virtual ~Message() { if (_mutableBody) free(_mutableBody); } /** * The exchange to which it was originally published * @var string */ const std::string &exchange() const { // expose member return _exchange; } /** * The routing key that was originally used * @var string */ const std::string &routingkey() const { // expose member return _routingkey; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/metadata.h000066400000000000000000000376121470663072600200260ustar00rootroot00000000000000/** * MetaData.h * * With every published message a set of meta data is passed to. This class * holds all that meta data. * * @copyright 2014 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "booleanset.h" #include "stringfield.h" #include "table.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class MetaData { protected: /** * First set of booleans * @var BooleanSet */ BooleanSet _bools1; /** * Second set of booleans * @var BooleanSet */ BooleanSet _bools2; /** * MIME content type * @var ShortString */ ShortString _contentType; /** * MIME content encoding * @var ShortString */ ShortString _contentEncoding; /** * message header field table * @var Table */ Table _headers; /** * Delivery mode (non-persistent (1) or persistent (2)) * @var UOctet */ UOctet _deliveryMode = 0; /** * boolean whether field was sent to us * @var UOctet */ UOctet _priority = 0; /** * application correlation identifier * @var ShortString */ ShortString _correlationID; /** * address to reply to * @var ShortString */ ShortString _replyTo; /** * message expiration identifier * @var ShortString */ ShortString _expiration; /** * application message identifier * @var ShortString */ ShortString _messageID; /** * message timestamp * @var Timestamp */ Timestamp _timestamp; /** * message type name * @var ShortString */ ShortString _typeName; /** * creating user id * @var ShortString */ ShortString _userID; /** * creating application id * @var ShortString */ ShortString _appID; /** * Deprecated cluster ID * @var ShortString */ ShortString _clusterID; public: /** * Constructor for empty meta data. Can be useful when user-space wants to preserve all meta-data */ MetaData() {} /** * Read incoming frame * @param frame */ MetaData(InBuffer &frame) : _bools1(frame), _bools2(frame) { // only copy the properties that were sent if (hasContentType()) _contentType = ShortString(frame); if (hasContentEncoding()) _contentEncoding = ShortString(frame); if (hasHeaders()) _headers = Table(frame); if (hasDeliveryMode()) _deliveryMode = UOctet(frame); if (hasPriority()) _priority = UOctet(frame); if (hasCorrelationID()) _correlationID = ShortString(frame); if (hasReplyTo()) _replyTo = ShortString(frame); if (hasExpiration()) _expiration = ShortString(frame); if (hasMessageID()) _messageID = ShortString(frame); if (hasTimestamp()) _timestamp = Timestamp(frame); if (hasTypeName()) _typeName = ShortString(frame); if (hasUserID()) _userID = ShortString(frame); if (hasAppID()) _appID = ShortString(frame); if (hasClusterID()) _clusterID = ShortString(frame); } /** * Destructor */ virtual ~MetaData() {} /** * Set all meta data * @param data */ void set(const MetaData &data) { // simply copy all fields _bools1 = data._bools1; _bools2 = data._bools2; _contentType = data._contentType; _contentEncoding = data._contentEncoding; _headers = data._headers; _deliveryMode = data._deliveryMode; _priority = data._priority; _correlationID = data._correlationID; _replyTo = data._replyTo; _expiration = data._expiration; _messageID = data._messageID; _timestamp = data._timestamp; _typeName = data._typeName; _userID = data._userID; _appID = data._appID; _clusterID = data._clusterID; } /** * Check if a certain field is set * @return bool */ bool hasExpiration () const { return _bools1.get(0); } bool hasReplyTo () const { return _bools1.get(1); } bool hasCorrelationID () const { return _bools1.get(2); } bool hasPriority () const { return _bools1.get(3); } bool hasDeliveryMode () const { return _bools1.get(4); } bool hasHeaders () const { return _bools1.get(5); } bool hasContentEncoding () const { return _bools1.get(6); } bool hasContentType () const { return _bools1.get(7); } bool hasClusterID () const { return _bools2.get(2); } bool hasAppID () const { return _bools2.get(3); } bool hasUserID () const { return _bools2.get(4); } bool hasTypeName () const { return _bools2.get(5); } bool hasTimestamp () const { return _bools2.get(6); } bool hasMessageID () const { return _bools2.get(7); } /** * Set the various supported fields * @param value */ void setExpiration (const std::string &value) { _expiration = value; _bools1.set(0,true); } void setReplyTo (const std::string &value) { _replyTo = value; _bools1.set(1,true); } void setCorrelationID (const std::string &value) { _correlationID = value; _bools1.set(2,true); } void setPriority (uint8_t value) { _priority = value; _bools1.set(3,true); } void setDeliveryMode (uint8_t value) { _deliveryMode = value; _bools1.set(4,true); } void setHeaders (const Table &value) { _headers = value; _bools1.set(5,true); } void setContentEncoding (const std::string &value) { _contentEncoding = value; _bools1.set(6,true); } void setContentType (const std::string &value) { _contentType = value; _bools1.set(7,true); } void setClusterID (const std::string &value) { _clusterID = value; _bools2.set(2,true); } void setAppID (const std::string &value) { _appID = value; _bools2.set(3,true); } void setUserID (const std::string &value) { _userID = value; _bools2.set(4,true); } void setTypeName (const std::string &value) { _typeName = value; _bools2.set(5,true); } void setTimestamp (uint64_t value) { _timestamp = value; _bools2.set(6,true); } void setMessageID (const std::string &value) { _messageID = value; _bools2.set(7,true); } /** * Set the various supported fields using r-value references * @param value moveable value */ void setExpiration (std::string &&value) { _expiration = std::move(value); _bools1.set(0,true); } void setReplyTo (std::string &&value) { _replyTo = std::move(value); _bools1.set(1,true); } void setCorrelationID (std::string &&value) { _correlationID = std::move(value); _bools1.set(2,true); } void setHeaders (Table &&value) { _headers = std::move(value); _bools1.set(5,true); } void setContentEncoding (std::string &&value) { _contentEncoding = std::move(value); _bools1.set(6,true); } void setContentType (std::string &&value) { _contentType = std::move(value); _bools1.set(7,true); } void setClusterID (std::string &&value) { _clusterID = std::move(value); _bools2.set(2,true); } void setAppID (std::string &&value) { _appID = std::move(value); _bools2.set(3,true); } void setUserID (std::string &&value) { _userID = std::move(value); _bools2.set(4,true); } void setTypeName (std::string &&value) { _typeName = std::move(value); _bools2.set(5,true); } void setMessageID (std::string &&value) { _messageID = std::move(value); _bools2.set(7,true); } /** * Set the various supported fields using data buffers * @param value data buffer * @param size size of the buffer */ void setExpiration (const char *value, size_t size) { _expiration.assign(value, size); _bools1.set(0,true); } void setReplyTo (const char *value, size_t size) { _replyTo.assign(value, size); _bools1.set(1,true); } void setCorrelationID (const char *value, size_t size) { _correlationID.assign(value, size); _bools1.set(2,true); } void setContentEncoding (const char *value, size_t size) { _contentEncoding.assign(value, size); _bools1.set(6,true); } void setContentType (const char *value, size_t size) { _contentType.assign(value, size); _bools1.set(7,true); } void setClusterID (const char *value, size_t size) { _clusterID.assign(value, size); _bools2.set(2,true); } void setAppID (const char *value, size_t size) { _appID.assign(value, size); _bools2.set(3,true); } void setUserID (const char *value, size_t size) { _userID.assign(value, size); _bools2.set(4,true); } void setTypeName (const char *value, size_t size) { _typeName.assign(value, size); _bools2.set(5,true); } void setMessageID (const char *value, size_t size) { _messageID.assign(value, size); _bools2.set(7,true); } /** * Set the various supported fields using c strings * @param value data buffer */ void setExpiration (const char *value) { _expiration.assign(value); _bools1.set(0,true); } void setReplyTo (const char *value) { _replyTo.assign(value); _bools1.set(1,true); } void setCorrelationID (const char *value) { _correlationID.assign(value); _bools1.set(2,true); } void setContentEncoding (const char *value) { _contentEncoding.assign(value); _bools1.set(6,true); } void setContentType (const char *value) { _contentType.assign(value); _bools1.set(7,true); } void setClusterID (const char *value) { _clusterID.assign(value); _bools2.set(2,true); } void setAppID (const char *value) { _appID.assign(value); _bools2.set(3,true); } void setUserID (const char *value) { _userID.assign(value); _bools2.set(4,true); } void setTypeName (const char *value) { _typeName.assign(value); _bools2.set(5,true); } void setMessageID (const char *value) { _messageID.assign(value); _bools2.set(7,true); } /** * Methods to remove properties from the header */ void removeExpiration () { _expiration .clear(); _bools1.set(0,false); } void removeReplyTo () { _replyTo .clear(); _bools1.set(1,false); } void removeCorrelationID () { _correlationID .clear(); _bools1.set(2,false); } void removePriority () { _priority .clear(); _bools1.set(3,false); } void removeDeliveryMode () { _deliveryMode .clear(); _bools1.set(4,false); } void removeHeaders () { _headers .clear(); _bools1.set(5,false); } void removeContentEncoding () { _contentEncoding.clear(); _bools1.set(6,false); } void removeContentType () { _contentType .clear(); _bools1.set(7,false); } void removeClusterID () { _clusterID .clear(); _bools2.set(2,false); } void removeAppID () { _appID .clear(); _bools2.set(3,false); } void removeUserID () { _userID .clear(); _bools2.set(4,false); } void removeTypeName () { _typeName .clear(); _bools2.set(5,false); } void removeTimestamp () { _timestamp .clear(); _bools2.set(6,false); } void removeMessageID () { _messageID .clear(); _bools2.set(7,false); } /** * Retrieve the fields * @return string */ const std::string &expiration () const { return _expiration; } const std::string &replyTo () const { return _replyTo; } const std::string &correlationID () const { return _correlationID; } uint8_t priority () const { return _priority; } uint8_t deliveryMode () const { return _deliveryMode; } const Table &headers () const { return _headers; } const std::string &contentEncoding() const { return _contentEncoding; } const std::string &contentType () const { return _contentType; } const std::string &clusterID () const { return _clusterID; } const std::string &appID () const { return _appID; } const std::string &userID () const { return _userID; } const std::string &typeName () const { return _typeName; } uint64_t timestamp () const { return _timestamp; } const std::string &messageID () const { return _messageID; } /** * Is this a message with persistent storage * This is an alias for retrieving the delivery mode and checking if it is set to 2 * @return bool */ bool persistent() const { return hasDeliveryMode() && deliveryMode() == 2; } /** * Set whether storage should be persistent or not * @param bool */ void setPersistent(bool value = true) { if (value) { // simply set the delivery mode setDeliveryMode(2); } else { // we remove the field from the header _deliveryMode = 0; _bools1.set(4,false); } } /** * Total size * @return uint32_t */ uint32_t size() const { // the result (2 for the two boolean sets) uint32_t result = 2; if (hasExpiration()) result += (uint32_t)_expiration.size(); if (hasReplyTo()) result += (uint32_t)_replyTo.size(); if (hasCorrelationID()) result += (uint32_t)_correlationID.size(); if (hasPriority()) result += (uint32_t)_priority.size(); if (hasDeliveryMode()) result += (uint32_t)_deliveryMode.size(); if (hasHeaders()) result += (uint32_t)_headers.size(); if (hasContentEncoding()) result += (uint32_t)_contentEncoding.size(); if (hasContentType()) result += (uint32_t)_contentType.size(); if (hasClusterID()) result += (uint32_t)_clusterID.size(); if (hasAppID()) result += (uint32_t)_appID.size(); if (hasUserID()) result += (uint32_t)_userID.size(); if (hasTypeName()) result += (uint32_t)_typeName.size(); if (hasTimestamp()) result += (uint32_t)_timestamp.size(); if (hasMessageID()) result += (uint32_t)_messageID.size(); // done return result; } /** * Fill an output buffer * @param buffer */ void fill(OutBuffer &buffer) const { // the two boolean sets are always present _bools1.fill(buffer); _bools2.fill(buffer); // only copy the properties that were sent if (hasContentType()) _contentType.fill(buffer); if (hasContentEncoding()) _contentEncoding.fill(buffer); if (hasHeaders()) _headers.fill(buffer); if (hasDeliveryMode()) _deliveryMode.fill(buffer); if (hasPriority()) _priority.fill(buffer); if (hasCorrelationID()) _correlationID.fill(buffer); if (hasReplyTo()) _replyTo.fill(buffer); if (hasExpiration()) _expiration.fill(buffer); if (hasMessageID()) _messageID.fill(buffer); if (hasTimestamp()) _timestamp.fill(buffer); if (hasTypeName()) _typeName.fill(buffer); if (hasUserID()) _userID.fill(buffer); if (hasAppID()) _appID.fill(buffer); if (hasClusterID()) _clusterID.fill(buffer); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/monitor.h000066400000000000000000000047501470663072600177320ustar00rootroot00000000000000/** * Monitor.h * * A monitor object monitors if the connection is still valid. When the * connection is parsing incoming data, it calls the user handler for each * incoming frame. However, it is unknown what this handler is going to do, * it could for example decide to destruct the connection object. In that * case the connection object should stop further handling the data. This * monitor class is used to check if the connection has been destructed. * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "watchable.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class Monitor { private: /** * The object being watched * @var Watchable */ Watchable *_watchable; /** * Invalidate the object */ void invalidate() { _watchable = nullptr; } public: /** * Constructor * @param watchable */ Monitor(Watchable *watchable) : _watchable(watchable) { // register with the watchable _watchable->add(this); } /** * Copy constructor * @param monitor */ Monitor(const Monitor &monitor) : _watchable(monitor._watchable) { // register with the watchable if (_watchable) _watchable->add(this); } /** * Assignment operator * @param monitor */ Monitor& operator= (const Monitor &monitor) { // remove from watchable if (_watchable) _watchable->remove(this); // replace watchable _watchable = monitor._watchable; // register with the watchable if (_watchable) _watchable->add(this); return *this; } /** * Destructor */ virtual ~Monitor() { // remove from watchable if (_watchable) _watchable->remove(this); } /** * Cast to boolean: is object in valid state? * @return bool */ operator bool () const { return _watchable != nullptr; } /** * Negate operator: is the object in an invalid state? * @return bool */ bool operator! () const { return _watchable == nullptr; } /** * Check if the object is valid * @return bool */ bool valid() const { return _watchable != nullptr; } /** * The watchable can access private data */ friend class Watchable; }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/numericfield.h000066400000000000000000000133271470663072600207110ustar00rootroot00000000000000/** * Numeric field types for AMQP * * @copyright 2014 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "inbuffer.h" #include "outbuffer.h" #include "field.h" #include /** * Set up namespace */ namespace AMQP { /** * Template for numeric field types */ template< typename T, char F, typename = typename std::enable_if::value, T> > class NumericField : public Field { private: /** * Field value */ T _value; public: using Type = T; /** * Default constructor, assign 0 */ NumericField() : _value(0) {} /** * Construct numeric field from * one of numeric types * * @param value field value */ NumericField(T value) : _value(value) {} /** * Parse based on incoming buffer * @param frame */ NumericField(InBuffer &frame) { // The Microsoft Visual Studio compiler thinks that there is an issue // with the following code, so we temporarily disable a specific warning #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4244) #endif if (std::is_same::type>::value) _value = frame.nextInt8(); else if (std::is_same::type>::value) _value = frame.nextInt16(); else if (std::is_same::type>::value) _value = frame.nextInt32(); else if (std::is_same::type>::value) _value = frame.nextInt64(); else if (std::is_same::type>::value) _value = frame.nextUint8(); else if (std::is_same::type>::value) _value = frame.nextUint16(); else if (std::is_same::type>::value) _value = frame.nextUint32(); else if (std::is_same::type>::value) _value = frame.nextUint64(); else if (std::is_same::type>::value) _value = frame.nextFloat(); else if (std::is_same::type>::value) _value = frame.nextDouble(); // re-enable the warning #if defined(_MSC_VER) #pragma warning( pop ) #endif } /** * Destructor */ virtual ~NumericField() {} /** * Create a new instance of this object * @return unique_ptr */ virtual std::unique_ptr clone() const override { // create a new copy of ourselves and return it return std::unique_ptr(new NumericField(_value)); } /** * Assign a new value * * @param value new value for field * @return NumericField */ NumericField& operator=(T value) { _value = value; return *this; }; /** * Clear the field * @return NumericField */ NumericField& clear() { _value = 0; return *this; } /** * Get the value * @return mixed */ operator uint8_t () const override { return (uint8_t)_value; } operator uint16_t() const override { return (uint16_t)_value; } operator uint32_t() const override { return (uint32_t)_value; } operator uint64_t() const override { return (uint64_t)_value; } operator int8_t () const override { return (int8_t)_value; } operator int16_t () const override { return (int16_t)_value; } operator int32_t () const override { return (int32_t)_value; } operator int64_t () const override { return (int64_t)_value; } operator float () const override { return (float)_value; } operator double () const override { return (double)_value; } /** * Get the value * @return mixed */ T value() const { // return internal value return _value; } /** * We are an integer field * * @return true, because we are an integer */ bool isInteger() const override { return std::is_integral::value; } /** * Get the size this field will take when * encoded in the AMQP wire-frame format * @return size_t */ virtual size_t size() const override { // numeric types have no extra storage requirements return sizeof(_value); } /** * Write encoded payload to the given buffer. * @param buffer OutBuffer to write to */ virtual void fill(OutBuffer& buffer) const override { // store converted value T value = _value; // write to buffer // adding a value takes care of host to network byte order buffer.add(value); } /** * Get the type ID that is used to identify this type of * field in a field table */ virtual char typeID() const override { return F; } /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const override { // show stream << "numeric(" << value() << ")"; } }; /** * Concrete numeric types for AMQP */ typedef NumericField Octet; typedef NumericField UOctet; typedef NumericField Short; typedef NumericField UShort; typedef NumericField Long; typedef NumericField ULong; typedef NumericField LongLong; typedef NumericField ULongLong; typedef NumericField Timestamp; /** * Concrete floating-point types for AMQP */ typedef NumericField Float; typedef NumericField Double; /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/openssl.h000066400000000000000000000016321470663072600177220ustar00rootroot00000000000000/** * OpenSSL.h * * Function to set openssl features * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Begin of namespace */ namespace AMQP { /** * To make secure "amqps://" connections, AMQP-CPP relies on functions from the * openssl library. It your application is dynamically linked to openssl (because * it was compiled with the "-lssl" flag), this works flawlessly because AMQPCPP * can then locate the openssl symbols in its own project space. However, if the * openssl library was not linked, you either cannot use amqps:// connections, * or you have to supply a handle to the openssl library yourself, using the * following method. * * @param handle Handle returned by dlopen() that has access to openssl */ void openssl(void *handle); /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/outbuffer.h000066400000000000000000000070651470663072600202460ustar00rootroot00000000000000/** * OutBuffer.h * * This is a utility class for writing various data types to a binary * string, and converting the values to network byte order * * @copyright 2014 - 2017 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "endian.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class OutBuffer { protected: /** * The method that adds the actual data * @param data * @param size */ virtual void append(const void *data, size_t size) = 0; public: /** * Destructor */ virtual ~OutBuffer() { } /** * Add a binary buffer to the buffer * @param string char* to the string * @param size size of string */ void add(const char *string, size_t size) { // append data append(string, size); } /** * Add a binary buffer to the buffer * @param string char* to the string * @param size size of string */ void add(const std::string &string) { // add data append(string.c_str(), string.size()); } /** * add a uint8_t to the buffer * @param value value to add */ void add(uint8_t value) { // append one byte append(&value, sizeof(value)); } /** * add a uint16_t to the buffer * @param value value to add */ void add(uint16_t value) { // convert to network byte order uint16_t v = htobe16(value); // append the data append(&v, sizeof(v)); } /** * add a uint32_t to the buffer * @param value value to add */ void add(uint32_t value) { // convert to network byte order uint32_t v = htobe32(value); // append the data append(&v, sizeof(v)); } /** * add a uint64_t to the buffer * @param value value to add */ void add(uint64_t value) { // convert to network byte order uint64_t v = htobe64(value); // append the data append(&v, sizeof(v)); } /** * add a int8_t to the buffer * @param value value to add */ void add(int8_t value) { // append the data append(&value, sizeof(value)); } /** * add a int16_t to the buffer * @param value value to add */ void add(int16_t value) { // convert to network byte order int16_t v = htobe16(value); // append the data append(&v, sizeof(v)); } /** * add a int32_t to the buffer * @param value value to add */ void add(int32_t value) { // convert into network byte order int32_t v = htobe32(value); // append the data append(&v, sizeof(v)); } /** * add a int64_t to the buffer * @param value value to add */ void add(int64_t value) { // copy into the buffer int64_t v = htobe64(value); // append the data append(&v, sizeof(v)); } /** * add a float to the buffer * @param value value to add */ void add(float value) { // append the data append(&value, sizeof(value)); } /** * add a double to the buffer * @param value value to add */ void add(double value) { // append the data append(&value, sizeof(value)); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/protocolexception.h000066400000000000000000000011411470663072600220120ustar00rootroot00000000000000/** * ProtocolException.h * * This exception is thrown internally in the library when invalid data is * received from the server. The best remedy is to close the connection * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "exception.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class ProtocolException : public Exception { public: /** * Constructor * @param what */ explicit ProtocolException(const std::string &what) : Exception(what) {} }; /** * End of namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/reliable.h000066400000000000000000000225411470663072600200200ustar00rootroot00000000000000/** * Reliable.h * * A channel wrapper based on AMQP::Tagger that allows message callbacks to be installed * on the publish-confirms, to be called when they a confirmation is received from RabbitMQ. * * You can also change the base class and use Reliable if you not only * want to be notified about the publish-confirms, but want to use it for automatic * throttling at the same time. * * @author Michael van der Werve * @copyright 2020 - 2023 Copernica BV */ /** * Header guard */ #pragma once /** * Includes */ #include "deferredpublish.h" #include "tagger.h" #include /** * Begin of namespaces */ namespace AMQP { /** * Class definition */ template class Reliable : public BASE { private: // make sure it is a proper channel static_assert(std::is_base_of::value, "base should be derived from a confirmed channel."); /** * Set of open deliverytags. We want a normal set (not unordered_set) because * removal will be cheaper for whole ranges. * @var size_t */ std::map> _handlers; /** * Called when the deliverytag(s) are acked * @param deliveryTag * @param multiple */ void onAck(uint64_t deliveryTag, bool multiple) override { // monitor the object, watching for destruction since these ack/nack handlers // could destruct the object Monitor monitor(this); // single element is simple if (!multiple) { // find the element auto iter = _handlers.find(deliveryTag); // we did not find it (this should not be possible, unless somebody explicitly called) // the base-class publish methods for some reason. if (iter == _handlers.end()) return BASE::onAck(deliveryTag, multiple); // get the handler (we store it first so that we can remove it) auto handler = iter->second; // erase it from the map (we remove it before the call, because the callback might update // the _handlers and invalidate the iterator) _handlers.erase(iter); // call the ack handler handler->reportAck(); } // do multiple at once else { // keep looping for as long as the object is in a valid state while (monitor && !_handlers.empty()) { // get the first handler auto iter = _handlers.begin(); // make sure this is the right deliverytag, if we've passed it we leap out if (iter->first > deliveryTag) break; // get the handler auto handler = iter->second; // remove it from the map (before we make a call to userspace, so that user space // can add even more handlers, without invalidating iterators) _handlers.erase(iter); // call the ack handler handler->reportAck(); } } // make sure the object is still valid if (!monitor) return; // call base handler as well BASE::onAck(deliveryTag, multiple); } /** * Called when the deliverytag(s) are nacked * @param deliveryTag * @param multiple */ void onNack(uint64_t deliveryTag, bool multiple) override { // monitor the object, watching for destruction since these ack/nack handlers // could destruct the object Monitor monitor(this); // single element is simple if (!multiple) { // find the element auto iter = _handlers.find(deliveryTag); // we did not find it (this should not be possible, unless somebody explicitly called) // the base-class publish methods for some reason. if (iter == _handlers.end()) return BASE::onNack(deliveryTag, multiple); // get the handler (we store it first so that we can remove it) auto handler = iter->second; // erase it from the map (we remove it before the call, because the callback might update // the _handlers and invalidate the iterator) _handlers.erase(iter); // call the ack handler handler->reportNack(); } // nack multiple elements else { // keep looping for as long as the object is in a valid state while (monitor && !_handlers.empty()) { // get the first handler auto iter = _handlers.begin(); // make sure this is the right deliverytag, if we've passed it we leap out if (iter->first > deliveryTag) break; // get the handler auto handler = iter->second; // remove it from the map (before we make a call to userspace, so that user space // can add even more handlers, without invalidating iterators) _handlers.erase(iter); // call the ack handler handler->reportNack(); } } // if the object is no longer valid, return if (!monitor) return; // call the base handler BASE::onNack(deliveryTag, multiple); } /** * Method that is called to report an error * @param message */ void reportError(const char *message) override { // monitor the object, watching for destruction since these ack/nack handlers // could destruct the object Monitor monitor(this); // move the handlers out auto handlers = std::move(_handlers); // iterate over all the messages // call the handlers for (const auto &iter : handlers) { // call the handler iter.second->reportError(message); // if we were destructed in the meantime, we leap out if (!monitor) return; } // if the monitor is no longer valid, leap out if (!monitor) return; // call the base handler BASE::reportError(message); } public: /** * Constructor * @param channel * @param throttle */ template Reliable(Args &&...args) : BASE(std::forward(args)...) {} /** * Deleted copy constructor, deleted move constructor * @param other */ Reliable(const Reliable &other) = delete; Reliable(Reliable &&other) = delete; /** * Deleted copy assignment, deleted move assignment * @param other */ Reliable &operator=(const Reliable &other) = delete; Reliable &operator=(Reliable &&other) = delete; /** * Virtual destructor */ virtual ~Reliable() = default; /** * Method to check how many messages are still unacked. * @return size_t */ virtual size_t unacknowledged() const override { return _handlers.size(); } /** * Publish a message to an exchange. See amqpcpp/channel.h for more details on the flags. * Delays actual publishing depending on the publisher confirms sent by RabbitMQ. * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags optional flags * @return bool */ DeferredPublish &publish(const std::string_view &exchange, const std::string_view &routingKey, const std::string_view &message, int flags = 0) { return publish(exchange, routingKey, Envelope(message.data(), message.size()), flags); } DeferredPublish &publish(const std::string_view &exchange, const std::string_view &routingKey, const char *message, size_t size, int flags = 0) { return publish(exchange, routingKey, Envelope(message, size), flags); } DeferredPublish &publish(const std::string_view &exchange, const std::string_view &routingKey, const char *message, int flags = 0) { return publish(exchange, routingKey, Envelope(message, strlen(message)), flags); } /** * Publish a message to an exchange. See amqpcpp/channel.h for more details on the flags. * Delays actual publishing depending on the publisher confirms sent by RabbitMQ. * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags optional flags */ DeferredPublish &publish(const std::string_view &exchange, const std::string_view &routingKey, const Envelope &envelope, int flags = 0) { // publish the entire thing, and remember if it failed at any point uint64_t tag = BASE::publish(exchange, routingKey, envelope, flags); // create the publish deferred object, if we got no tag we failed auto handler = std::make_shared(tag == 0); // add it to the open handlers _handlers[tag] = handler; // return the dereferenced handler return *handler; } }; /** * End of namespaces */ } AMQP-CPP-4.3.27/include/amqpcpp/stack_ptr.h000066400000000000000000000056131470663072600202340ustar00rootroot00000000000000/** * stack_ptr.h * * Implementation of an object that behaves like a * smart pointer but is actually managed on the stack * * @copyright 2016 Copernica B.V. */ /** * Dependencies */ #include #include /** * Set up namespace */ namespace AMQP { /** * Stack-based smart pointer */ template class stack_ptr { private: /** * Storage for the object * @var typename std::aligned_storage::type */ typename std::aligned_storage::type _data; /** * Is the pointer initialized? * @var boolean */ bool _initialized = false; public: /** * Constructor */ stack_ptr() = default; /** * Copy and moving is disabled * * @param that The stack_ptr we refuse to copy/move */ stack_ptr(const stack_ptr &that) = delete; stack_ptr(stack_ptr &&that) = delete; /** * Destructor */ ~stack_ptr() { // reset the pointer reset(); } /** * Reset the pointer */ void reset() { // are we initialized? if (!_initialized) return; // destroy the object reinterpret_cast(&_data)->~T(); // the object is not currently initialized _initialized = false; } /** * Construct the object * * @param ... Zero or more constructor arguments for T */ template void construct(Arguments&&... parameters) { // first reset the current object reset(); // initialize new object new (&_data) T(std::forward(parameters)...); // we are now initialized _initialized = true; } /** * Is the object initialized? * * @return Are we currently managing an object? */ operator bool() const { // are we initialized with an object? return _initialized; } /** * Retrieve a pointer to the object * * @return Pointer to the object or nullptr if no object is managed */ T *get() const { // do we have a managed object if (!_initialized) return nullptr; // return pointer to the managed object return const_cast(reinterpret_cast(&_data)); } /** * Retrieve a reference to the object * * @return Reference to the object, undefined if no object is managed */ T &operator*() const { // dereference the pointer return *operator->(); } /** * Retrieve a pointer to the object * * @return Pointer to the object, undefined if no object is managed */ T *operator->() const { // return pointer to the managed object return const_cast(reinterpret_cast(&_data)); } }; /** * End namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/stringfield.h000066400000000000000000000137341470663072600205570ustar00rootroot00000000000000/** * String field types for amqp * * @copyright 2014 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "field.h" #include "outbuffer.h" #include "numericfield.h" #include "inbuffer.h" /** * Set up namespace */ namespace AMQP { /** * Base class for string types */ template class StringField : public Field { private: /** * Pointer to string data * @var string */ std::string _data; public: /** * Initialize empty string */ StringField() {} /** * Construct based on a std::string * @param value string value */ StringField(const std::string &value) : _data(value) {} /** * Construct based on a std::string * @param value string value */ StringField(const std::string_view &value) : _data(value) {} /** * Construct based on a std::string * @param value string value */ StringField(std::string &&value) : _data(std::move(value)) {} /** * Construct based on a buffer * @param buffer buffer value * @param size size of the buffer */ StringField(const char *buffer, size_t size) : _data(buffer, size) {} /** * Construct based on a c-string * @param buffer buffer value */ StringField(const char *buffer) : _data(buffer) {} /** * Construct based on received data * @param frame */ StringField(InBuffer &frame) { // get the size T size(frame); // allocate string _data = std::string(frame.nextData(size.value()), (size_t) size.value()); } /** * Clean up memory used */ virtual ~StringField() = default; /** * Create a new instance of this object * @return std::unique_ptr */ virtual std::unique_ptr clone() const override { // create a new copy of ourselves and return it return std::unique_ptr(new StringField(_data)); } /** * Assign a new value * @param value new value */ StringField& operator=(const std::string &value) { // overwrite data _data = value; // allow chaining return *this; } /** * Assign a new value * @param value new value */ StringField& operator=(std::string &&value) { // overwrite data _data = std::move(value); // allow chaining return *this; } /** * Assign a new value * @param value new value */ StringField& operator=(const char *value) { // overwrite data _data.assign(value); // allow chaining return *this; } /** * Assign a new value * @param value * @return StringField */ StringField& assign(const std::string &value) { // overwrite data _data = value; // allow chaining return *this; } /** * Assign a new value * @param value new value * @return StringField */ StringField& assign(std::string &&value) { // overwrite data _data = std::move(value); // allow chaining return *this; } /** * Assign a new value * @param value new value * @return StringField */ StringField& assign(const char *value) { // overwrite data _data.assign(value); // allow chaining return *this; } /** * Assign a new value * @param value new value * @return StringField */ StringField& assign(const char *value, size_t size) { // overwrite data _data.assign(value, size); // allow chaining return *this; } /** * Make the field empty * @return StringField */ StringField &clear() { // clear internal dta _data.clear(); // allow chaining return *this; } /** * Get the size this field will take when * encoded in the AMQP wire-frame format * @return size_t */ virtual size_t size() const override { // find out size of the size parameter T size((typename T::Type)_data.size()); // size of the uint8 or uint32 + the actual string size return size.size() + _data.size(); } /** * Get the value * @return string */ virtual operator const std::string& () const override { return _data; } /** * Get the value * @return string */ const std::string& value() const { // get data return _data; } /** * Get the maximum allowed string length for this field * @return size_t */ constexpr static size_t maxLength() { return T::max(); } /** * Write encoded payload to the given buffer. * @param buffer */ virtual void fill(OutBuffer& buffer) const override { // create size T size((typename T::Type)_data.size()); // first, write down the size of the string size.fill(buffer); // write down the string content buffer.add(_data); } /** * Get the type ID that is used to identify this type of * field in a field table * @return char */ virtual char typeID() const override { return F; } /** * We are a string * * @return true, because we are a string */ bool isString() const override { return true; } /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const override { // show stream << "string(" << value() << ")"; } }; /** * Concrete string types for AMQP */ typedef StringField ShortString; typedef StringField LongString; /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/table.h000066400000000000000000000160371470663072600173330ustar00rootroot00000000000000/** * AMQP field table * * @copyright 2014 - 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "field.h" #include "fieldproxy.h" #include #include #include /** * Set up namespace */ namespace AMQP { /** * AMQP field table */ class Table : public Field { private: /** * We define a custom type for storing fields * @typedef FieldMap */ typedef std::map > FieldMap; /** * Store the fields * @var FieldMap */ FieldMap _fields; public: /** * Constructor that creates an empty table */ Table() {} /** * Decode the data from a received frame into a table * * @param frame received frame to decode */ Table(InBuffer &frame); /** * Copy constructor * @param table */ Table(const Table &table); /** * Move constructor * @param table */ Table(Table &&table) : _fields(std::move(table._fields)) {} /** * Destructor */ virtual ~Table() {} /** * Assignment operator * @param table * @return Table */ Table &operator=(const Table &table); /** * Move assignment operator * @param table * @return Table */ Table &operator=(Table &&table); /** * Retrieve all keys in the table * * @return Vector with all keys in the table */ std::vector keys() const; /** * Create a new instance on the heap of this object, identical to the object passed * @return Field* */ virtual std::unique_ptr clone() const override { return std::unique_ptr(new Table(*this)); } /** * Get the size this field will take when * encoded in the AMQP wire-frame format */ virtual size_t size() const override; /** * Set a field * @param name field name * @param value field value * @return Table */ Table &set(const std::string& name, const Field &value) { // copy to a new pointer and store it _fields[name] = value.clone(); // allow chaining return *this; } /** * Aliases for setting values * @param name * @param value * @return Table& */ Table &set(const std::string &name, bool value) { return set(name, BooleanSet(value)); } Table &set(const std::string &name, uint8_t value) { return set(name, UOctet(value)); } Table &set(const std::string &name, int8_t value) { return set(name, Octet(value)); } Table &set(const std::string &name, uint16_t value) { return set(name, UShort(value)); } Table &set(const std::string &name, int16_t value) { return set(name, Short(value)); } Table &set(const std::string &name, uint32_t value) { return set(name, ULong(value)); } Table &set(const std::string &name, int32_t value) { return set(name, Long(value)); } Table &set(const std::string &name, uint64_t value) { return set(name, ULongLong(value)); } Table &set(const std::string &name, int64_t value) { return set(name, LongLong(value)); } Table &set(const std::string &name, const std::string &value) { return set(name, LongString(value)); } Table &set(const std::string &name, const std::string_view &value) { return set(name, LongString(value)); } Table &set(const std::string &name, const char *value) { return set(name, LongString(std::string(value))); } Table &set(const std::string &name, std::nullptr_t) { return set(name, VoidField()); } /** * Clear the entire table * @return Table */ Table &clear() { _fields.clear(); return *this; } /** * Is a certain field set in the table * @param name * @return bool */ bool contains(const std::string &name) const { return _fields.find(name) != _fields.end(); } /** * Get a field * * If the field does not exist, an empty string field is returned * * @param name field name * @return the field value */ const Field &get(const std::string &name) const; /** * Get a field * * @param name field name */ AssociativeFieldProxy operator[](const std::string& name) { return AssociativeFieldProxy(this, name); } /** * Get a field * * @param name field name */ AssociativeFieldProxy operator[](const char *name) { return AssociativeFieldProxy(this, name); } /** * Get a const field * * @param name field name */ const Field &operator[](const std::string& name) const { return get(name); } /** * Get a const field * * @param name field name */ const Field &operator[](const char *name) const { return get(name); } /** * Write encoded payload to the given buffer. * @param buffer */ virtual void fill(OutBuffer& buffer) const override; /** * Get the type ID that is used to identify this type of * field in a field table */ virtual char typeID() const override { return 'F'; } /** * We are a table * * @return true, because we are a table */ bool isTable() const override { return true; } /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const override { // prefix stream << "table("; // is this the first iteration bool first = true; // loop through all members for (auto &iter : _fields) { // split with comma if (!first) stream << ","; // show output stream << iter.first << ":" << *iter.second; // no longer first iter first = false; } // postfix stream << ")"; } /** * Cast to table. * * @note: This function may look silly and unnecessary. We are, after all, already * a table. The whole reason we still have this function is that it is virtual * and if we do not declare a cast to table on a pointer to base (i.e. Field) * will return an empty field instead of the expected table. * * Yes, clang gets this wrong and gives incorrect warnings here. See * https://llvm.org/bugs/show_bug.cgi?id=28263 for more information * * @return Ourselves */ virtual operator const Table& () const override { // this already is a table, so no cast is necessary return *this; } }; /** * Custom output stream operator * @param stream * @param field * @return ostream */ inline std::ostream &operator<<(std::ostream &stream, const AssociativeFieldProxy &field) { // get underlying field, and output that return stream << field.get(); } /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/tagger.h000066400000000000000000000104351470663072600175110ustar00rootroot00000000000000/** * Tagger.h * * Base class that enables publisher confirms and keeps track of the sent * messages. You can wrap this class around a AMQP::Channel object and use * this object for publishing instead. This is a base class that you cannot * use directly. You should instead use: * * - Throttle: to throttle traffic to prevent flooding RabbitMQ * - Reliable: to be notified about publish-confirms via callbacks * - Reliable: to have throttle + notifications via callbacks * * @author Michael van der Werve * @copyright 2020 - 2023 Copernica BV */ /** * Header guard */ #pragma once /** * Includes */ #include "deferredpublish.h" #include /** * Begin of namespaces */ namespace AMQP { /** * Class definition */ class Tagger : public Watchable { protected: /** * The implementation for the channel * @var std::shared_ptr */ std::shared_ptr _implementation; /** * Current id, always starts at 1. * @var uint64_t */ uint64_t _current = 1; /** * Deferred to set up on the close * @var std::shared_ptr */ std::shared_ptr _close; /** * Callback to call when an error occurred * @var ErrorCallback */ ErrorCallback _errorCallback; protected: /** * Send method for a frame * @param id * @param frame */ virtual bool send(uint64_t id, const Frame &frame); /** * Method that is called to report an error. * @param message */ virtual void reportError(const char *message); /** * Method that gets called on ack/nack. If these methods are overridden, make sure * to also call the base class methods. * @param deliveryTag * @param multiple */ virtual void onAck(uint64_t deliveryTag, bool multiple); virtual void onNack(uint64_t deliveryTag, bool multiple); public: /** * Constructor * @param channel */ Tagger(AMQP::Channel &channel); /** * Deleted copy constructor, deleted move constructor * @param other */ Tagger(const Tagger &other) = delete; Tagger(Tagger &&other) = delete; /** * Deleted copy assignment, deleted move assignment * @param other */ Tagger &operator=(const Tagger &other) = delete; Tagger &operator=(Tagger &&other) = delete; /** * Virtual destructor */ virtual ~Tagger(); /** * Method to check how many messages are still unacked. * @return size_t */ virtual size_t unacknowledged() const { return 0; } /** * Publish a message to an exchange. See amqpcpp/channel.h for more details on the flags. * Delays actual publishing depending on the publisher confirms sent by RabbitMQ. * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags optional flags * @return uint64_t */ uint64_t publish(const std::string_view &exchange, const std::string_view &routingKey, const Envelope &envelope, int flags = 0); uint64_t publish(const std::string_view &exchange, const std::string_view &routingKey, const std::string_view &message, int flags = 0) { return publish(exchange, routingKey, Envelope(message.data(), message.size()), flags); } uint64_t publish(const std::string_view &exchange, const std::string_view &routingKey, const char *message, size_t size, int flags = 0) { return publish(exchange, routingKey, Envelope(message, size), flags); } uint64_t publish(const std::string_view &exchange, const std::string_view &routingKey, const char *message, int flags = 0) { return publish(exchange, routingKey, Envelope(message, strlen(message)), flags); } /** * Close underlying channel * @return Deferred& */ Deferred &close(); /** * Install an error callback * @param callback */ inline void onError(const ErrorCallback& callback) { return onError(ErrorCallback(callback)); } void onError(ErrorCallback&& callback); }; /** * End of namespaces */ } AMQP-CPP-4.3.27/include/amqpcpp/throttle.h000066400000000000000000000065571470663072600201170ustar00rootroot00000000000000/** * Throttle.h * * A channel wrapper that publishes more messages as soon as there is more capacity. * * @author Michael van der Werve * @copyright 2020 Copernica BV */ /** * Header guard */ #pragma once /** * Includes */ #include #include #include #include "copiedbuffer.h" #include "channelimpl.h" #include "tagger.h" /** * Begin of namespaces */ namespace AMQP { /** * Forward declarations */ class Channel; /** * Class definition */ class Throttle : public Tagger { protected: /** * Last sent ID * @var uint64_t */ uint64_t _last = 0; /** * Throttle * @var size_t */ size_t _throttle; /** * Messages that should still be sent out. * @var queue */ std::queue> _queue; /** * Set of open deliverytags. We want a normal set (not unordered_set) because * removal will be cheaper for whole ranges. * @var size_t */ std::set _open; protected: /** * Send method for a frame * @param id * @param frame */ virtual bool send(uint64_t id, const Frame &frame) override; /** * Method that is called to report an error * @param message */ virtual void reportError(const char *message) override; /** * Method that is called to report an ack/nack * @param deliveryTag * @param multiple */ virtual void onAck(uint64_t deliveryTag, bool multiple) override; virtual void onNack(uint64_t deliveryTag, bool multiple) override; public: /** * Constructor. Warning: this takes control of the channel, there should be no extra * handlers set on the channel (onError) and no further publishes should be done on the * raw channel either. Doing this will cause the throttle to work incorrectly, as the * counters are not properly updated. * @param channel * @param throttle */ Throttle(Channel &channel, size_t throttle); /** * Deleted copy constructor, deleted move constructor * @param other */ Throttle(const Throttle &other) = delete; Throttle(Throttle &&other) = delete; /** * Deleted copy assignment, deleted move assignment * @param other */ Throttle &operator=(const Throttle &other) = delete; Throttle &operator=(Throttle &&other) = delete; /** * Virtual destructor */ virtual ~Throttle() = default; /** * Method to check how many messages are still unacked. * @return size_t */ virtual size_t unacknowledged() const override { return _open.size() + (_current - _last - 1); } /** * Get the throttle * @return size_t */ size_t throttle() const { return _throttle; } /** * Set a new throttle. Note that this will only gradually take effect when set down, and * the update is picked up on the next acknowledgement. * @param size_t */ void throttle(size_t throttle) { _throttle = throttle; } /** * Flush the throttle. This flushes it _without_ taking the throttle into account, e.g. the messages * are sent in a burst over the channel. * @param max optional maximum, 0 is flush all */ size_t flush(size_t max = 0); }; /** * End of namespaces */ } AMQP-CPP-4.3.27/include/amqpcpp/voidfield.h000066400000000000000000000034651470663072600202120ustar00rootroot00000000000000/** * Void field type for AMQP * * @copyright */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include "outbuffer.h" #include "field.h" /** * Set up namespace */ namespace AMQP { class VoidField : public Field { public: /** * Default constructor */ VoidField() = default; /** * Construct based on incoming data * @param frame */ VoidField(InBuffer &frame) { (void)frame; } /** * Destructor */ virtual ~VoidField() = default; /** * Create a new instance of this object * @return unique_ptr */ virtual std::unique_ptr clone() const override { // create a new copy of ourselves and return it return std::unique_ptr(new VoidField); } /** * Get the size this field will take when * encoded in the AMQP wire-frame format * @return size_t */ virtual size_t size() const override { // numeric types have no extra storage requirements return 0; } /** * Write encoded payload to the given buffer. * @param buffer OutBuffer to write to */ virtual void fill(OutBuffer &buffer) const override { (void)buffer; } /** * Get the type ID that is used to identify this type of * field in a field table */ virtual char typeID() const override { return 'V'; } /** * Output the object to a stream * @param std::ostream */ virtual void output(std::ostream &stream) const override { // show stream << "void()"; } /** * We are an void field * * @return true, because we are an void */ bool isVoid() const override { return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/include/amqpcpp/watchable.h000066400000000000000000000023031470663072600201650ustar00rootroot00000000000000/** * Watchable.h * * Every class that overrides from the Watchable class can be monitored for * destruction by a Monitor object * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class Monitor; /** * Class definition */ class Watchable { private: /** * The monitors * @var std::vector */ std::vector _monitors; /** * Add a monitor * @param monitor */ void add(Monitor *monitor) { // add to the vector _monitors.push_back(monitor); } /** * Remove a monitor * @param monitor */ void remove(Monitor *monitor) { // put the monitor at the end of the vector auto iter = std::remove(_monitors.begin(), _monitors.end(), monitor); // make the vector smaller _monitors.erase(iter, _monitors.end()); } public: /** * Destructor */ virtual ~Watchable(); /** * Only a monitor has full access */ friend class Monitor; }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/000077500000000000000000000000001470663072600135675ustar00rootroot00000000000000AMQP-CPP-4.3.27/src/.gitignore000066400000000000000000000000041470663072600155510ustar00rootroot00000000000000*.d AMQP-CPP-4.3.27/src/CMakeLists.txt000066400000000000000000000042641470663072600163350ustar00rootroot00000000000000add_sources( array.cpp basicackframe.h basiccancelframe.h basiccancelokframe.h basicconsumeframe.h basicconsumeokframe.h basicdeliverframe.h basicframe.h basicgetemptyframe.h basicgetframe.h basicgetokframe.h basicheaderframe.h basicnackframe.h basicpublishframe.h basicqosframe.h basicqosokframe.h basicrecoverasyncframe.h basicrecoverframe.h basicrecoverokframe.h basicrejectframe.h basicreturnframe.h bodyframe.h channelcloseframe.h channelcloseokframe.h channelflowframe.h channelflowokframe.h channelframe.h channelimpl.cpp channelopenframe.h channelopenokframe.h confirmselectframe.h confirmselectokframe.h connectioncloseframe.h connectioncloseokframe.h connectionframe.h connectionimpl.cpp connectionopenframe.h connectionopenokframe.h connectionsecureframe.h connectionsecureokframe.h connectionstartframe.h connectionstartokframe.h connectiontuneframe.h connectiontuneokframe.h consumedmessage.h deferredcancel.cpp deferredconfirm.cpp deferredconsumer.cpp deferredreceiver.cpp deferredextreceiver.cpp deferredrecall.cpp deferredget.cpp exchangebindframe.h exchangebindokframe.h exchangedeclareframe.h exchangedeclareokframe.h exchangedeleteframe.h exchangedeleteokframe.h exchangeframe.h exchangeunbindframe.h exchangeunbindokframe.h extframe.h field.cpp flags.cpp framecheck.h headerframe.h heartbeatframe.h includes.h methodframe.h passthroughbuffer.h protocolheaderframe.h queuebindframe.h queuebindokframe.h queuedeclareframe.h queuedeclareokframe.h queuedeleteframe.h queuedeleteokframe.h queueframe.h queuepurgeframe.h queuepurgeokframe.h queueunbindframe.h queueunbindokframe.h receivedframe.cpp reducedbuffer.h returnedmessage.h table.cpp transactioncommitframe.h transactioncommitokframe.h transactionframe.h transactionrollbackframe.h transactionrollbackokframe.h transactionselectframe.h transactionselectokframe.h watchable.cpp ) AMQP-CPP-4.3.27/src/Makefile000066400000000000000000000030651470663072600152330ustar00rootroot00000000000000CPP = g++ RM = rm -f CPPFLAGS = -Wall -c -I../include -std=c++17 -MD -Wno-class-conversion -DVERSION=${VERSION} LD = g++ LD_FLAGS = -Wall -shared SHARED_LIB = lib$(LIBRARY_NAME).so.$(VERSION) STATIC_LIB = lib$(LIBRARY_NAME).a.$(VERSION) SOURCES = $(wildcard *.cpp) $(wildcard linux_tcp/*.cpp) SHARED_OBJECTS = $(SOURCES:%.cpp=%.o) STATIC_OBJECTS = $(SOURCES:%.cpp=%.s.o) DEPENDENCIES = $(SOURCES:%.cpp=%.d) PURE_SHARED_OBJECTS = $(filter-out tcpconnection.o, $(SOURCES:%.cpp=%.o)) PURE_STATIC_OBJECTS = $(filter-out tcpconnection.s.o, $(SOURCES:%.cpp=%.s.o)) ifeq ($(shell uname -s),Darwin) SONAMEPARAMETER = -install_name else SONAMEPARAMETER = -soname endif -include ${DEPENDENCIES} all: CPPFLAGS += -g all: LD_FLAGS += -g all: shared static pure: CPPFLAGS += -g pure: LD_FLAGS += -g pure: shared_pure static_pure release: CPPFLAGS += -O2 release: LD_FLAGS += -O2 release: shared static shared: ${SHARED_OBJECTS} ${SHARED_LIB} shared_pure: ${PURE_SHARED_OBJECTS} ${SHARED_LIB} static: ${STATIC_OBJECTS} ${STATIC_LIB} static_pure: ${PURE_STATIC_OBJECTS} ${STATIC_LIB} ${SHARED_LIB}: ${SHARED_OBJECTS} ${LD} ${LD_FLAGS} -Wl,${SONAMEPARAMETER},lib$(LIBRARY_NAME).so.$(SONAME) -o $@ ${SHARED_OBJECTS} ${STATIC_LIB}: ${STATIC_OBJECTS} ar rcs ${STATIC_LIB} ${STATIC_OBJECTS} clean: ${RM} *.obj *~* ${SHARED_OBJECTS} ${STATIC_OBJECTS} ${SHARED_LIB} ${STATIC_LIB} ${DEPENDENCIES} ${SHARED_OBJECTS}: ${CPP} ${CPPFLAGS} -fpic -o $@ ${@:%.o=%.cpp} ${STATIC_OBJECTS}: ${CPP} ${CPPFLAGS} -o $@ ${@:%.s.o=%.cpp} AMQP-CPP-4.3.27/src/array.cpp000066400000000000000000000052751470663072600154220ustar00rootroot00000000000000/** * Array.cpp * * Implementation of an array * */ #include "includes.h" // we live in the copernica namespace namespace AMQP { /** * Constructor based on incoming frame * @param frame */ Array::Array(InBuffer &frame) { // use this to see if we've read too many bytes. uint32_t charsToRead = frame.nextUint32(); // keep going until all data is read while (charsToRead > 0) { // one byte less for the field type charsToRead -= 1; // read the field type and construct the field auto field = Field::decode(frame); if (!field) continue; // less bytes to read charsToRead -= (uint32_t)field->size(); // add the additional field _fields.push_back(std::move(field)); } } /** * Copy constructor * @param array */ Array::Array(const Array &array) { // loop through the other array for (auto iter = array._fields.begin(); iter != array._fields.end(); iter++) { // add to this vector _fields.push_back((*iter)->clone()); } } /** * Get a field * * If the field does not exist, an empty string is returned * * @param index field index * @return Field */ const Field &Array::get(uint8_t index) const { // used if index does not exist static ShortString empty; // check whether we have that many elements if (index >= _fields.size()) return empty; // get value return *_fields[index]; } /** * Number of entries in the array * @return uint32_t */ uint32_t Array::count() const { return (uint32_t)_fields.size(); } /** * Remove a field from the array */ void Array::pop_back() { _fields.pop_back(); } /** * Add a field to the array * @param value */ void Array::push_back(const Field& value) { _fields.push_back(value.clone()); } /** * Get the size this field will take when * encoded in the AMQP wire-frame format * @return size_t */ size_t Array::size() const { // store the size (four bytes for the initial size) size_t size = 4; // iterate over all elements for (const auto &item : _fields) { // add the size of the field type and size of element size += sizeof(item->typeID()); size += item->size(); } // return the result return size; } /** * Write encoded payload to the given buffer. * @param buffer */ void Array::fill(OutBuffer& buffer) const { // store total size for all elements buffer.add(static_cast(size()-4)); // iterate over all elements for (const auto &item : _fields) { // encode the element type and element buffer.add((uint8_t)item->typeID()); item->fill(buffer); } } // end namespace } AMQP-CPP-4.3.27/src/basicackframe.h000066400000000000000000000063101470663072600165130ustar00rootroot00000000000000/** * Class describing a basic acknowledgement frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class defintion */ class BasicAckFrame : public BasicFrame { private: /** * server-assigned and channel specific delivery tag * @var uint64_t */ uint64_t _deliveryTag; /** * if set, tag is treated as "up to and including", so client can acknowledge multiple messages with a single method * if not set, refers to single message * @var BooleanSet */ BooleanSet _multiple; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to * @return pointer to object to allow for chaining */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // add the delivery tag buffer.add(_deliveryTag); // add the booleans _multiple.fill(buffer); } public: /** * Construct a basic acknowledgement frame * * @param channel Channel identifier * @param deliveryTag server-assigned and channel specific delivery tag * @param multiple acknowledge mutiple messages */ BasicAckFrame(uint16_t channel, uint64_t deliveryTag, bool multiple = false) : BasicFrame(channel, 9), _deliveryTag(deliveryTag), _multiple(multiple) {} /** * Construct based on received frame * @param frame */ BasicAckFrame(ReceivedFrame &frame) : BasicFrame(frame), _deliveryTag(frame.nextUint64()), _multiple(frame) {} /** * Destructor */ virtual ~BasicAckFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ virtual bool synchronous() const override { return false; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 80; } /** * Return the server-assigned and channel specific delivery tag * @return uint64_t */ uint64_t deliveryTag() const { return _deliveryTag; } /** * Return whether to acknowledge multiple messages * @return bool */ bool multiple() const { return _multiple.get(0); } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // get the current confirm auto confirm = channel->confirm(); // if there is no deferred confirm, we can just as well stop if (confirm == nullptr) return false; // process the frame confirm->process(*this); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basiccancelframe.h000066400000000000000000000060361470663072600172070ustar00rootroot00000000000000/** * Class describing a basic cancel frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicCancelFrame : public BasicFrame { private: /** * Holds the consumer tag specified by the client or provided by the server. * @var ShortString */ ShortString _consumerTag; /** * whether to wait for a response * @var BooleanSet */ BooleanSet _noWait; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // add consumer tag + booleans _consumerTag.fill(buffer); _noWait.fill(buffer); } public: /** * Construct a basic cancel frame from a received frame * * @param frame received frame to parse */ BasicCancelFrame(ReceivedFrame &frame) : BasicFrame(frame), _consumerTag(frame), _noWait(frame) {} /** * Construct a basic cancel frame * * @param channel Channel identifier * @param consumerTag consumertag specified by client of provided by server * @param noWait whether to wait for a response. */ BasicCancelFrame(uint16_t channel, const std::string_view &consumerTag, bool noWait = false) : BasicFrame(channel, (uint32_t)(consumerTag.size() + 2)), // 1 for extra string size, 1 for bool _consumerTag(consumerTag), _noWait(noWait) {} /** * Destructor */ virtual ~BasicCancelFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous when the nowait option is not used return !noWait(); } /** * Return the consumertag, which is specified by the client or provided by the server * @return string */ const std::string& consumerTag() const { return _consumerTag; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 30; } /** * Return whether to wait for a response * @return boolean */ bool noWait() const { return _noWait.get(0); } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report channel->reportCancelled(consumerTag()); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basiccancelokframe.h000066400000000000000000000045041470663072600175370ustar00rootroot00000000000000/** * Class describing a basic cancel ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicCancelOKFrame : public BasicFrame { private: /** * Holds the consumer tag specified by the client or provided by the server. * @var ShortString */ ShortString _consumerTag; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // add own information _consumerTag.fill(buffer); } public: /** * Construct a basic cancel ok frame * * @param frame received frame */ BasicCancelOKFrame(ReceivedFrame &frame) : BasicFrame(frame), _consumerTag(frame) {} /** * Construct a basic cancel ok frame (client-side) * @param channel channel identifier * @param consumerTag holds the consumertag specified by client or server */ BasicCancelOKFrame(uint16_t channel, std::string& consumerTag) : BasicFrame(channel, (uint32_t)(consumerTag.length() + 1)), // add 1 byte for encoding the size of consumer tag _consumerTag(consumerTag) {} /** * Destructor */ virtual ~BasicCancelOKFrame() {} /** * Return the consumertag, which is specified by the client or provided by the server * @return string */ const std::string& consumerTag() const { return _consumerTag; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 31; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report channel->reportSuccess(consumerTag()); // done return true; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/basicconsumeframe.h000066400000000000000000000106771470663072600174410ustar00rootroot00000000000000/** * Class describing a basic consume frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicConsumeFrame : public BasicFrame { private: /** * Field that is no longer used * @var uint16_t */ uint16_t _deprecated = 0; /** * specifies the name of the queue to consume from * @var Table */ ShortString _queueName; /** * specifies the identifier for the consumer tag. * This tag is local to a channel so two clients can use the same consumer tags. * If empty, the server generates a tag. * @var ShortString */ ShortString _consumerTag; /** * Booleans sent in frame * 0: noLocal * 1: noAck * 2: exclusive * 3: noWait * @var BooleanSet */ BooleanSet _bools; /** * additional arguments, implementation dependent * @var Table */ Table _filter; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // fill the buffer buffer.add((uint16_t) _deprecated); _queueName.fill(buffer); _consumerTag.fill(buffer); _bools.fill(buffer); _filter.fill(buffer); } public: /** * Construct a basic consume frame * * @param channel channel we're working on * @param queueName name of the queue to consume from * @param consumerTag identifier for the consumer tag. * @param noLocal no-local * @param noAck no acknowledgements * @param exclusive exclusive channel * @param noWait don't wait for a response * @param filter additional arguments */ BasicConsumeFrame(uint16_t channel, const std::string_view &queueName, const std::string_view &consumerTag, bool noLocal = false, bool noAck = false, bool exclusive = false, bool noWait = false, const Table& filter = {}) : BasicFrame(channel, (uint32_t)(queueName.length() + consumerTag.length() + 5 + filter.size())), // size of vars, +1 for each shortstring size, +1 for bools, +2 for deprecated value _queueName(queueName), _consumerTag(consumerTag), _bools(noLocal, noAck, exclusive, noWait), _filter(filter) {} /** * Constructor based on incoming data * @param frame */ BasicConsumeFrame(ReceivedFrame &frame) : BasicFrame(frame), _deprecated(frame.nextUint16()), // read deprecated info _queueName(frame), _consumerTag(frame), _bools(frame), _filter(frame) {} /** * Destructor */ virtual ~BasicConsumeFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous when the nowait option is not set return !noWait(); } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 20; } /** * Return the name of the queue to consume from * @return string */ const std::string& queueName() const { return _queueName; } /** * return the identifier for the consumertag * @return string */ const std::string& consumerTag() const { return _consumerTag; } /** * return the value of the noLocal bool * @return bool */ bool noLocal() const { return _bools.get(0); } /** * return the value of the noAck bool * @return bool */ bool noAck() const { return _bools.get(1); } /** * return whether the queue is exclusive * @return bool */ bool exclusive() const { return _bools.get(2); } /** * return whether to wait for a response * @return bool */ bool noWait() const { return _bools.get(3); } /** * return the additional filter arguments * @return Table */ const Table& filter() const { return _filter; } }; /** * End namespace */ } AMQP-CPP-4.3.27/src/basicconsumeokframe.h000066400000000000000000000044311470663072600177620ustar00rootroot00000000000000/** * Class describing a basic consume ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicConsumeOKFrame : public BasicFrame { private: /** * Holds the consumer tag specified by the client or provided by the server. * @var ShortString */ ShortString _consumerTag; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // add payload _consumerTag.fill(buffer); } public: /** * Construct a basic consume frame * * @param consumerTag consumertag specified by client of provided by server */ BasicConsumeOKFrame(uint16_t channel, const std::string& consumerTag) : BasicFrame(channel, (uint32_t)(consumerTag.length() + 1)), // length of string + 1 for encoding of stringsize _consumerTag(consumerTag) {} /** * Construct a basic consume ok frame from a received frame * * @param frame received frame */ BasicConsumeOKFrame(ReceivedFrame &frame) : BasicFrame(frame), _consumerTag(frame) {} /** * Destructor */ virtual ~BasicConsumeOKFrame() {} /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 21; } /** * Return the consumertag, which is specified by the client or provided by the server * @return std::string */ const std::string& consumerTag() const { return _consumerTag; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report channel->reportSuccess(consumerTag()); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicdeliverframe.h000066400000000000000000000121711470663072600174110ustar00rootroot00000000000000/** * Class describing a basic deliver frame * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "basicframe.h" #include "amqpcpp/stringfield.h" #include "amqpcpp/booleanset.h" #include "amqpcpp/connectionimpl.h" #include "amqpcpp/deferredconsumer.h" /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicDeliverFrame : public BasicFrame { private: /** * identifier for the consumer, valid within current channel * @var ShortString */ ShortString _consumerTag; /** * server-assigned and channel specific delivery tag * @var uint64_t */ uint64_t _deliveryTag; /** * indicates whether the message has been previously delivered to this (or another) client * @var BooleanSet */ BooleanSet _redelivered; /** * the name of the exchange to publish to. An empty exchange name means the default exchange. * @var ShortString */ ShortString _exchange; /** * Message routing key * @var ShortString */ ShortString _routingKey; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { BasicFrame::fill(buffer); _consumerTag.fill(buffer); buffer.add(_deliveryTag); _redelivered.fill(buffer); _exchange.fill(buffer); _routingKey.fill(buffer); } public: /** * Construct a basic deliver frame (client side) * * @param channel channel we're working on * @param consumerTag identifier for the consumer, valid within current channel * @param deliveryTag server-assigned and channel specific delivery tag * @param redelivered indicates whether the message has been previously delivered to this (or another) client * @param exchange name of exchange to publish to * @param routingKey message routing key */ BasicDeliverFrame(uint16_t channel, const std::string& consumerTag, uint64_t deliveryTag, bool redelivered = false, const std::string& exchange = "", const std::string& routingKey = "") : BasicFrame(channel, (uint32_t)(consumerTag.length() + exchange.length() + routingKey.length() + 12)), // length of strings + 1 byte per string for stringsize, 8 bytes for uint64_t and 1 for bools _consumerTag(consumerTag), _deliveryTag(deliveryTag), _redelivered(redelivered), _exchange(exchange), _routingKey(routingKey) {} /** * Construct a basic deliver frame from a received frame * * @param frame received frame */ BasicDeliverFrame(ReceivedFrame &frame) : BasicFrame(frame), _consumerTag(frame), _deliveryTag(frame.nextUint64()), _redelivered(frame), _exchange(frame), _routingKey(frame) {} /** * Destructor */ virtual ~BasicDeliverFrame() {} /** * Return the name of the exchange to publish to * @return string */ const std::string& exchange() const { return _exchange; } /** * Return the routing key * @return string */ const std::string& routingKey() const { return _routingKey; } /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ virtual bool synchronous() const override { return false; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 60; } /** * Return the server-assigned and channel specific delivery tag * @return uint64_t */ uint64_t deliveryTag() const { return _deliveryTag; } /** * Return the identifier for the consumer (channel specific) * @return string */ const std::string& consumerTag() const { return _consumerTag; } /** * Return whether the message has been previously delivered to (another) client * @return bool */ bool redelivered() const { return _redelivered.get(0); } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // get the appropriate consumer object auto consumer = channel->consumer(_consumerTag); // skip if there was no consumer for this tag if (consumer == nullptr) return false; // initialize the object, because we're about to receive a message consumer->process(*this); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicframe.h000066400000000000000000000016001470663072600160310ustar00rootroot00000000000000/** * Class describing an AMQP basic frame * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "methodframe.h" /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class BasicFrame : public MethodFrame { protected: /** * Constructor * @param channel The channel ID * @param size Payload size */ BasicFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {} /** * Constructor based on a received frame * @param frame */ BasicFrame(ReceivedFrame &frame) : MethodFrame(frame) {} public: /** * Destructor */ virtual ~BasicFrame() {} /** * Class id * @return uint16_t */ virtual uint16_t classID() const override { return 60; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicgetemptyframe.h000066400000000000000000000035671470663072600176260ustar00rootroot00000000000000/** * Class describing a basic get empty frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class BasicGetEmptyFrame : public BasicFrame { private: /** * Field that is no longer used * @var ShortString */ ShortString _deprecated; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // recreate deprecated field and encode _deprecated.fill(buffer); } public: /** * Construct a basic get empty frame * * @param channel channel we're working on */ BasicGetEmptyFrame(uint16_t channel) : BasicFrame(channel, 1) // 1 for encoding the deprecated cluster id (shortstring) {} /** * Constructor for incoming data * @param frame received frame */ BasicGetEmptyFrame(ReceivedFrame &frame) : BasicFrame(frame), _deprecated(frame) {} /** * Destructor */ virtual ~BasicGetEmptyFrame() {} /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 72; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicgetframe.h000066400000000000000000000044101470663072600165330ustar00rootroot00000000000000/** * Class describing a basic get frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicGetFrame : public BasicFrame { private: /** * Deprecated field * @var uint16_t */ uint16_t _deprecated = 0; /** * name of the queue to get a message from * @var ShortString */ ShortString _queue; /** * if set, server does not expect acknowledgement for messages. Server dequeues message after sending * @var BooleanSet */ BooleanSet _noAck; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // encode other values buffer.add(_deprecated); _queue.fill(buffer); _noAck.fill(buffer); } public: /** * Construct a basic get frame * * @param channel channel we're working on * @param queue name of the queue * @param noAck whether server expects acknowledgements for messages */ BasicGetFrame(uint16_t channel, const std::string_view &queue, bool noAck = false) : BasicFrame(channel, (uint32_t)(queue.length() + 4)), // 1 for bool, 1 for string size, 2 for deprecated field _queue(queue), _noAck(noAck) {} /** * Constructor based on incoming frame * @param frame */ BasicGetFrame(ReceivedFrame &frame) : BasicFrame(frame), _deprecated(frame.nextUint16()), _queue(frame), _noAck(frame) {} /** * Destructor */ virtual ~BasicGetFrame() {} /** * Return the name of the queue * @return string */ const std::string& queue() const { return _queue; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 70; } /** * Return whether the server expects acknowledgements for messages * @return boolean */ bool noAck() const { return _noAck.get(0); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicgetokframe.h000066400000000000000000000113061470663072600170670ustar00rootroot00000000000000/** * Class describing a basic get ok frame * * @copyright 2014 - 2018 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicGetOKFrame : public BasicFrame { private: /** * server-assigned and channel specific delivery tag * @var uint64_t */ uint64_t _deliveryTag; /** * indicates whether the message has been previously delivered to this (or another) client * @var BooleanSet */ BooleanSet _redelivered; /** * the name of the exchange to publish to. An empty exchange name means the default exchange. * @var ShortString */ ShortString _exchange; /** * Message routing key * @var ShortString */ ShortString _routingKey; /** * number of messages in the queue * @var uint32_t */ uint32_t _messageCount; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // encode rest of the fields buffer.add(_deliveryTag); _redelivered.fill(buffer); _exchange.fill(buffer); _routingKey.fill(buffer); buffer.add(_messageCount); } public: /** * Construct a basic get ok frame * * @param channel channel we're working on * @param deliveryTag server-assigned and channel specific delivery tag * @param redelivered indicates whether the message has been previously delivered to this (or another) client * @param exchange name of exchange to publish to * @param routingKey message routing key * @param messageCount number of messages in the queue */ BasicGetOKFrame(uint16_t channel, uint64_t deliveryTag, bool redelivered, const std::string& exchange, const std::string& routingKey, uint32_t messageCount) : BasicFrame(channel, (uint32_t)(exchange.length() + routingKey.length() + 15)), // string length, +1 for each shortsrting length + 8 (uint64_t) + 4 (uint32_t) + 1 (bool) _deliveryTag(deliveryTag), _redelivered(redelivered), _exchange(exchange), _routingKey(routingKey), _messageCount(messageCount) {} /** * Construct a basic get ok frame from a received frame * * @param frame received frame */ BasicGetOKFrame(ReceivedFrame &frame) : BasicFrame(frame), _deliveryTag(frame.nextUint64()), _redelivered(frame), _exchange(frame), _routingKey(frame), _messageCount(frame.nextUint32()) {} /** * Destructor */ virtual ~BasicGetOKFrame() {} /** * Return the name of the exchange to publish to * @return string */ const std::string& exchange() const { return _exchange; } /** * Return the routing key * @return string */ const std::string& routingKey() const { return _routingKey; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 71; } /** * Return the server-assigned and channel specific delivery tag * @return uint64_t */ uint64_t deliveryTag() const { return _deliveryTag; } /** * Return the number of messages in the queue * @return uint32_t */ uint32_t messageCount() const { return _messageCount; } /** * Return whether the message has been previously delivered to (another) client * @return bool */ bool redelivered() const { return _redelivered.get(0); } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report success for the get operation (this will also update the current receiver!) channel->reportSuccess(messageCount(), _deliveryTag, redelivered()); // get the current receiver object auto *receiver = channel->receiver(); // check if we have a valid receiver if (receiver == nullptr) return false; // initialize the receiver for the upcoming message receiver->initialize(_exchange, _routingKey); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicheaderframe.h000066400000000000000000000071731470663072600172150ustar00rootroot00000000000000/** * Class describing an AMQP basic header frame * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "headerframe.h" #include "amqpcpp/metadata.h" #include "amqpcpp/envelope.h" #include "amqpcpp/connectionimpl.h" #include "amqpcpp/deferredreceiver.h" /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class BasicHeaderFrame : public HeaderFrame { private: /** * Weight field, unused but must be sent, always value 0; * @var uint16_t */ uint16_t _weight = 0; /** * Body size, sum of the sizes of all body frames following the content header * @var uint64_t */ uint64_t _bodySize; /** * The meta data * @var MetaData */ MetaData _metadata; protected: /** * Encode a header frame to a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer &buffer) const override { // call base HeaderFrame::fill(buffer); // fill own fields. buffer.add(_weight); buffer.add(_bodySize); // the meta data _metadata.fill(buffer); } /** * Construct an empty basic header frame * @param channel channel we're working on * @param metadata the meta-data * @param bodysize size of the body */ BasicHeaderFrame(uint16_t channel, const MetaData &metadata, size_t bodysize) : HeaderFrame(channel, 10 + metadata.size()), // there are at least 10 bytes sent, weight (2), bodySize (8), plus the size of the meta data _bodySize(bodysize), _metadata(metadata) {} public: /** * Construct an empty basic header frame * * All options are set using setter functions. * * @param channel channel we're working on * @param envelope the envelope */ BasicHeaderFrame(uint16_t channel, const Envelope &envelope) : BasicHeaderFrame(channel, envelope, envelope.bodySize()) {} /** * Constructor to parse incoming frame * @param frame */ BasicHeaderFrame(ReceivedFrame &frame) : HeaderFrame(frame), _weight(frame.nextUint16()), _bodySize(frame.nextUint64()), _metadata(frame) {} /** * Destructor */ virtual ~BasicHeaderFrame() = default; /** * Size of the body * @return uint64_t */ uint64_t bodySize() const { return _bodySize; } /** * The metadata sent in this frame * * @return All the metadata for this message */ const MetaData &metaData() const { return _metadata; } /** * The class ID * @return uint16_t */ virtual uint16_t classID() const override { return 60; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // we need a channel if (channel == nullptr) return false; // do we have an object that is receiving this data? auto *receiver = channel->receiver(); // check if we have a valid channel and consumer if (receiver == nullptr) return false; // the channel can process the frame receiver->process(*this); // done return true; } }; /** * End namespace */ } AMQP-CPP-4.3.27/src/basicnackframe.h000066400000000000000000000060631470663072600166760ustar00rootroot00000000000000/** * Class describing a basic negative-acknowledgement frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class defintion */ class BasicNackFrame : public BasicFrame { private: /** * server-assigned and channel specific delivery tag * @var uint64_t */ uint64_t _deliveryTag; /** * The additional bits * @var BooleanSet */ BooleanSet _bits; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to * @return pointer to object to allow for chaining */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // add the delivery tag buffer.add(_deliveryTag); // add the booleans _bits.fill(buffer); } public: /** * Construct a basic negative-acknowledgement frame * * @param channel Channel identifier * @param deliveryTag server-assigned and channel specific delivery tag * @param multiple nack mutiple messages * @param requeue requeue the message */ BasicNackFrame(uint16_t channel, uint64_t deliveryTag, bool multiple = false, bool requeue = false) : BasicFrame(channel, 9), _deliveryTag(deliveryTag), _bits(multiple, requeue) {} /** * Construct based on received frame * @param frame */ BasicNackFrame(ReceivedFrame &frame) : BasicFrame(frame), _deliveryTag(frame.nextUint64()), _bits(frame) {} /** * Destructor */ virtual ~BasicNackFrame() {} /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 120; } /** * Return the server-assigned and channel specific delivery tag * @return uint64_t */ uint64_t deliveryTag() const { return _deliveryTag; } /** * Return whether to acknowledgement multiple messages * @return bool */ bool multiple() const { return _bits.get(0); } /** * Should the message be put back in the queue? * @return bool */ bool requeue() const { return _bits.get(1); } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // get the current confirm auto confirm = channel->confirm(); // if there is no deferred confirm, we can just as well stop if (confirm == nullptr) return false; // process the frame confirm->process(*this); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicpublishframe.h000066400000000000000000000072031470663072600174250ustar00rootroot00000000000000/** * Class describing a basic publish frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicPublishFrame : public BasicFrame { private: /** * Variable that no longer is in use * @var int16_t */ int16_t _deprecated = 0; /** * the name of the exchange to publish to. An empty exchange name means the default exchange. * @var ShortString */ ShortString _exchange; /** * Message routing key * @var ShortString */ ShortString _routingKey; /** * BooleanSet, contains: * 0: mandatory, indicate mandatory routing. If set, server will return unroutable message, otherwise server silently drops message * 1: immediate, Request immediate delivery, if set and cannot be routed, return unroutable message. * @var BooleanSet */ BooleanSet _bools; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // encode fields buffer.add(_deprecated); _exchange.fill(buffer); _routingKey.fill(buffer); _bools.fill(buffer); } public: /** * Construct a basic publish frame * * @param channel channel we're working on * @param exchange name of exchange to publish to @default = "" * @param routingKey message routing key @default = "" * @param mandatory indicate mandatory routing @default = false * @param immediate request immediate delivery @default = false */ BasicPublishFrame(uint16_t channel, const std::string_view &exchange = "", const std::string_view &routingKey = "", bool mandatory = false, bool immediate = false) : BasicFrame(channel, (uint32_t)(exchange.length() + routingKey.length() + 5)), // 1 extra per string (for the size), 1 for bools, 2 for deprecated field _exchange(exchange), _routingKey(routingKey), _bools(mandatory, immediate) {} /** * Construct a basic publish frame from a received frame * * @param frame received frame to parse */ BasicPublishFrame(ReceivedFrame &frame) : BasicFrame(frame), _deprecated(frame.nextInt16()), _exchange(frame), _routingKey(frame), _bools(frame) {} /** * Destructor */ virtual ~BasicPublishFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { return false; } /** * Return the name of the exchange to publish to * @return string */ const std::string& exchange() const { return _exchange; } /** * Return the routing key * @return string */ const std::string& routingKey() const { return _routingKey; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 40; } /** * Return the mandatory routing indication * @return boolean */ bool mandatory() const { return _bools.get(0); } /** * Return the request immediate delivery indication * @return boolean */ bool immediate() const { return _bools.get(1); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicqosframe.h000066400000000000000000000046711470663072600165670ustar00rootroot00000000000000/** * Class describing a basic QOS frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class BasicQosFrame : public BasicFrame { private: /** * specifies the size of the prefetch window in octets * @var int32_t */ int32_t _prefetchSize; /** * specifies a prefetch window in terms of whole messages * @var int16_t */ int16_t _prefetchCount; /** * apply QoS settings to entire connection * @var BooleanSet */ BooleanSet _global; protected: /** * Encode a frame on a string buffer * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // add fields buffer.add(_prefetchSize); buffer.add(_prefetchCount); _global.fill(buffer); } public: /** * Construct a basic qos frame * * @param channel channel we're working on * @param prefetchCount specifies a prefetch window in terms of whole messages * @param global share prefetch count with all consumers on the same channel * @default false */ BasicQosFrame(uint16_t channel, int16_t prefetchCount = 0, bool global = false) : BasicFrame(channel, 7), // 4 (int32) + 2 (int16) + 1 (bool) _prefetchSize(0), _prefetchCount(prefetchCount), _global(global) {} /** * Constructor based on incoming frame * @param frame */ BasicQosFrame(ReceivedFrame &frame) : BasicFrame(frame), _prefetchSize(frame.nextInt32()), _prefetchCount(frame.nextInt16()), _global(frame) {} /** * Destructor */ virtual ~BasicQosFrame() {} /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 10; } /** * Return the prefetch count * @return int16_t */ int16_t prefetchCount() const { return _prefetchCount; } /** * returns the value of global * @return boolean */ bool global() const { return _global.get(0); } /** * returns the prefetch size * @return int32_t */ int32_t prefetchSize() const { return _prefetchSize; } }; /** * End namespace */ } AMQP-CPP-4.3.27/src/basicqosokframe.h000066400000000000000000000030711470663072600171120ustar00rootroot00000000000000/** * Class describing a basic QOS frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class BasicQosOKFrame : public BasicFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base, then done (no other params) BasicFrame::fill(buffer); } public: /** * Construct a basic qos ok frame * @param channel channel we're working on */ BasicQosOKFrame(uint16_t channel) : BasicFrame(channel, 0) {} /** * Constructor based on incoming data * @param frame */ BasicQosOKFrame(ReceivedFrame &frame) : BasicFrame(frame) {} /** * Destructor */ virtual ~BasicQosOKFrame() {} /** * Return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 11; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report channel->reportSuccess(); // done return true; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/basicrecoverasyncframe.h000066400000000000000000000037201470663072600204620ustar00rootroot00000000000000/** * Class describing a basic recover-async frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class BasicRecoverAsyncFrame : public BasicFrame { private: /** * Server will try to requeue messages. If requeue is false or requeue attempt fails, messages are discarded or dead-lettered * @var BooleanSet */ BooleanSet _requeue; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // encode fields _requeue.fill(buffer); } public: /** * Construct a basic recover async frame from a received frame * * @param frame received frame */ BasicRecoverAsyncFrame(ReceivedFrame &frame) : BasicFrame(frame), _requeue(frame) {} /** * Construct a basic recover-async frame * * @param channel channel we're working on * @param requeue whether to attempt to requeue messages */ BasicRecoverAsyncFrame(uint16_t channel, bool requeue = false) : BasicFrame(channel, 1), //sizeof bool _requeue(requeue) {} /** * Destructor */ virtual ~BasicRecoverAsyncFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ virtual bool synchronous() const override { return false; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 100; } /** * Return whether the server will try to requeue * @return bool */ bool requeue() const { return _requeue.get(0); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicrecoverframe.h000066400000000000000000000036301470663072600174240ustar00rootroot00000000000000/** * Class describing a basic recover frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class BasicRecoverFrame : public BasicFrame { private: /** * Server will try to requeue messages. If requeue is false or requeue attempt fails, messages are discarded or dead-lettered * @var BooleanSet */ BooleanSet _requeue; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // encode fields _requeue.fill(buffer); } public: /** * Construct a basic recover frame from a received frame * * @param frame received frame */ BasicRecoverFrame(ReceivedFrame &frame) : BasicFrame(frame), _requeue(frame) {} /** * Construct a basic recover frame * * @param channel channel ID * @param requeue whether to attempt to requeue messages */ BasicRecoverFrame(uint16_t channel, bool requeue = false) : BasicFrame(channel, 1), //sizeof bool _requeue(requeue) {} /** * Destructor */ virtual ~BasicRecoverFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { return false; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 110; } /** * Return whether the server will try to requeue * @return bool */ bool requeue() const { return _requeue.get(0); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicrecoverokframe.h000066400000000000000000000032331470663072600177550ustar00rootroot00000000000000/** * Class describing a basic recover-async frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class BasicRecoverOKFrame : public BasicFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base then done, no other fields to encode BasicFrame::fill(buffer); } public: /** * Construct a basic recover ok frame from a received frame * * @param frame received frame */ BasicRecoverOKFrame(ReceivedFrame &frame) : BasicFrame(frame) {} /** * Construct a basic recover ok frame * * @param channel channel id */ BasicRecoverOKFrame(uint16_t channel) : BasicFrame(channel, 0) {} /** * Destructor */ virtual ~BasicRecoverOKFrame() {} /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 111; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report channel->reportSuccess(); // done return true; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/basicrejectframe.h000066400000000000000000000046461470663072600172430ustar00rootroot00000000000000/** * Class describing a basic reject frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class BasicRejectFrame : public BasicFrame { private: /** * server-assigned and channel specific delivery tag * @var int64_t */ int64_t _deliveryTag; /** * Server will try to requeue messages. If requeue is false or requeue attempt fails, messages are discarded or dead-lettered * @var BooleanSet */ BooleanSet _requeue; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // encode fields buffer.add(_deliveryTag); _requeue.fill(buffer); } public: /** * construct a basic reject frame from a received frame * * @param frame received frame */ BasicRejectFrame(ReceivedFrame &frame) : BasicFrame(frame), _deliveryTag(frame.nextInt64()), _requeue(frame) {} /** * Construct a basic reject frame * * @param channel channel id * @param deliveryTag server-assigned and channel specific delivery tag * @param requeue whether to attempt to requeue messages */ BasicRejectFrame(uint16_t channel, int64_t deliveryTag, bool requeue = true) : BasicFrame(channel, 9), // 8 for uint64_t, 1 for bool _deliveryTag(deliveryTag), _requeue(requeue) {} /** * Destructor */ virtual ~BasicRejectFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { return false; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 90; } /** * Return the server-assigned and channel specific delivery tag * @return uint64_t */ int64_t deliveryTag() const { return _deliveryTag; } /** * Return whether the server will try to requeue * @return bool */ bool requeue() const { return _requeue.get(0); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/basicreturnframe.h000066400000000000000000000100441470663072600172730ustar00rootroot00000000000000/** * Class describing a basic return frame * * @copyright 2014 - 2020 Copernica BV */ /** * Set up namespace */ namespace AMQP{ /** * Class implementation */ class BasicReturnFrame : public BasicFrame { private: /** * reply code * @var int16_t */ int16_t _replyCode; /** * reply text * @var ShortString */ ShortString _replyText; /** * the name of the exchange to publish to. An empty exchange name means the default exchange. * @var ShortString */ ShortString _exchange; /** * Message routing key * @var ShortString */ ShortString _routingKey; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base BasicFrame::fill(buffer); // add fields buffer.add(_replyCode); _replyText.fill(buffer); _exchange.fill(buffer); _routingKey.fill(buffer); } public: /** * Construct a basic return frame * * @param channel channel identifier * @param replyCode reply code * @param replyText reply text * @param exchange name of exchange to publish to * @param routingKey message routing key */ BasicReturnFrame(uint16_t channel, int16_t replyCode, const std::string& replyText = "", const std::string& exchange = "", const std::string& routingKey = "") : BasicFrame(channel, (uint32_t)(replyText.length() + exchange.length() + routingKey.length() + 5)), // 3 for each string (extra size byte), 2 for uint16_t _replyCode(replyCode), _replyText(replyText), _exchange(exchange), _routingKey(routingKey) {} /** * Construct a basic return frame from a received frame * * @param received frame */ BasicReturnFrame(ReceivedFrame &frame) : BasicFrame(frame), _replyCode(frame.nextInt16()), _replyText(frame), _exchange(frame), _routingKey(frame) {} /** * Destructor */ virtual ~BasicReturnFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ virtual bool synchronous() const override { return false; } /** * Return the name of the exchange to publish to * @return string */ const std::string& exchange() const { return _exchange; } /** * Return the routing key * @return string */ const std::string& routingKey() const { return _routingKey; } /** * Return the method ID * @return uint16_t */ virtual uint16_t methodID() const override { return 50; } /** * Return the reply text * @return string */ const std::string& replyText() const { return _replyText; } /** * Return the reply code * @return int16_t */ int16_t replyCode() const { return _replyCode; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // get the object that handles bounces auto recalls = channel->recalls(); // if there is no deferred publisher, we can just as well stop if (recalls == nullptr) return false; // initialize the object, because we're about to receive a message recalls->process(*this); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/bodyframe.h000066400000000000000000000051421470663072600157120ustar00rootroot00000000000000/** * Class describing an AMQP Body Frame * * @copyright 2014 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "extframe.h" #include "amqpcpp/connectionimpl.h" #include "amqpcpp/deferredreceiver.h" /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class BodyFrame : public ExtFrame { private: /** * Payload of the frame * Payload can be any number of octets * @var const char * */ const char *_payload; protected: /** * Encode a body frame to a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExtFrame::fill(buffer); // add payload to buffer buffer.add(_payload, _size); } public: /** * Construct a body frame * * @param channel channel identifier * @param payload payload of the body * @param size size of the payload */ BodyFrame(uint16_t channel, const char *payload, uint32_t size) : ExtFrame(channel, size), _payload(payload) {} /** * Constructor for incoming data * * @param frame received frame to decode * @return shared pointer to newly created frame */ BodyFrame(ReceivedFrame& frame) : ExtFrame(frame), _payload(frame.nextData(frame.payloadSize())) {} /** * Destructor */ virtual ~BodyFrame() {} /** * Return the type of frame * @return uint8_t */ virtual uint8_t type() const override { return 3; } /** * Return the payload of the body * @return const char * */ const char *payload() const { return _payload; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // we must have a channel object if (channel == nullptr) return false; // get the object that is receiving the messages auto *receiver = channel->receiver(); // check if we have a valid receiver if (receiver == nullptr) return false; // the consumer may process the frame receiver->process(*this); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/buffercheck.h000066400000000000000000000020561470663072600162120ustar00rootroot00000000000000/** * BufferCheck.h * * Class that checks incoming frames for their size * * @copyright 2014 - 2020 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Internal helper class that checks if there is enough room left in the buffer */ class BufferCheck { private: /** * The frame * @var InBuffer */ InBuffer *_frame; /** * The size that is checked * @var size_t */ size_t _size; public: /** * Constructor * @param frame * @param size */ BufferCheck(InBuffer *frame, size_t size) : _frame(frame), _size(size) { // no problem is there are still enough bytes left if (frame->_buffer.size() - frame->_skip >= size) return; // frame buffer is too small throw ProtocolException("frame out of range"); } /** * Destructor */ virtual ~BufferCheck() { // update the number of bytes to skip _frame->_skip += (uint32_t)_size; } }; /** * End namespace */ } AMQP-CPP-4.3.27/src/channel.cpp000066400000000000000000000021731470663072600157060ustar00rootroot00000000000000/** * Channel.cpp * * Implementation file for the Channel class * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Dependencies */ #include "includes.h" /** * Begin of namespace */ namespace AMQP { /** * Construct a channel object * * The passed in connection pointer must remain valid for the * lifetime of the channel. Watch out: this method throws an error * if the channel could not be constructed (for example because the * max number of AMQP channels has been reached) * * @param connection * @throws std::runtime_error */ Channel::Channel(Connection *connection) : _implementation(new ChannelImpl()) { // check if the connection is indeed usable if (!connection->usable()) throw std::runtime_error("failed to open channel: connection is not active"); // attach to the connection if (_implementation->attach(connection)) return; // this failed, max number of channels has been reached throw std::runtime_error("failed to open channel: max number of channels has been reached"); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/channelcloseframe.h000066400000000000000000000072551470663072600174220ustar00rootroot00000000000000/** * Class describing a channel close frame * * @copyright 2014 - 2018 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ChannelCloseFrame : public ChannelFrame { private: /** * The reply code * @var int16_t */ int16_t _code; /** * The reply text * @var ShortString */ ShortString _text; /** * The failing class id if applicable * @note: will be 0 if no error occured * @var int16_t */ int16_t _failingClass; /** * The failing method id if applicable * @note: will be 0 if no error occured * @var int16_t */ int16_t _failingMethod; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ChannelFrame::fill(buffer); // add fields buffer.add(_code); _text.fill(buffer); buffer.add(_failingClass); buffer.add(_failingMethod); } public: /** * Construct a channel close frame * * @param frame received frame */ ChannelCloseFrame(ReceivedFrame &frame) : ChannelFrame(frame), _code(frame.nextInt16()), _text(frame), _failingClass(frame.nextInt16()), _failingMethod(frame.nextInt16()) {} /** * Construct a channel close frame * * @param channel channel we're working on * @param code reply code * @param text reply text * @param failingClass failing class id if applicable * @param failingMethod failing method id if applicable */ ChannelCloseFrame(uint16_t channel, uint16_t code = 0, std::string text = "", uint16_t failingClass = 0, uint16_t failingMethod = 0) : ChannelFrame(channel, (uint32_t)(text.length() + 7)), // sizeof code, failingclass, failingmethod (2byte + 2byte + 2byte) + text length + text length byte _code(code), _text(std::move(text)), _failingClass(failingClass), _failingMethod(failingMethod) {} /** * Destructor */ virtual ~ChannelCloseFrame() = default; /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 40; } /** * Get the reply code * @return uint16_t */ uint16_t code() const { return _code; } /** * Get the reply text * @return string */ const std::string& text() const { return _text; } /** * Get the failing class id if applicable * @return uint16_t */ uint16_t failingClass() const { return _failingClass; } /** * Get the failing method id if applicable * @return uint16_t */ uint16_t failingMethod() const { return _failingMethod; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // send back an ok frame connection->send(ChannelCloseOKFrame(this->channel())); // what if channel doesn't exist? if (!channel) return false; // report to the handler channel->reportError(_text.value().c_str()); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/channelcloseokframe.h000066400000000000000000000031651470663072600177500ustar00rootroot00000000000000/** * Class describing a channel close acknowledgement frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ChannelCloseOKFrame : public ChannelFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ChannelFrame::fill(buffer); } public: /** * Construct a channel close ok frame * @param frame */ ChannelCloseOKFrame(ReceivedFrame &frame) : ChannelFrame(frame) {} /** * Construct a channel close ok frame * * @param channel channel we're working on */ ChannelCloseOKFrame(uint16_t channel) : ChannelFrame(channel, 0) {} /** * Destructor */ virtual ~ChannelCloseOKFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 41; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report that the channel is closed channel->reportClosed(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/channelflowframe.h000066400000000000000000000030201470663072600172460ustar00rootroot00000000000000/** * Class describing a channel flow frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ChannelFlowFrame : public ChannelFrame { private: /** * Enable or disable the channel flow * @var BooleanSet */ BooleanSet _active; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ChannelFrame::fill(buffer); // add fields _active.fill(buffer); } public: /** * Construct a channel flow frame * * @param frame received frame */ ChannelFlowFrame(ReceivedFrame &frame) : ChannelFrame(frame), _active(frame) {} /** * Construct a channel flow frame * * @param channel channel we're working on * @param active enable or disable channel flow */ ChannelFlowFrame(uint16_t channel, bool active) : ChannelFrame(channel, 1), //sizeof bool _active(active) {} /** * Destructor */ virtual ~ChannelFlowFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 20; } /** * Is channel flow active or not? * @return bool */ bool active() const { return _active.get(0); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/channelflowokframe.h000066400000000000000000000040371470663072600176110ustar00rootroot00000000000000/** * Class describing a channel flow acknowledgement frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ChannelFlowOKFrame : public ChannelFrame { private: /** * Is the channel flow currently active? * @var BooleanSet */ BooleanSet _active; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ChannelFrame::fill(buffer); // add fields _active.fill(buffer); } public: /** * Construct a channel flow frame * * @param frame received frame to decode */ ChannelFlowOKFrame(ReceivedFrame &frame) : ChannelFrame(frame), _active(frame) {} /** * Construct a channel flow frame * * @param channel channel we're working on * @param active enable or disable channel flow */ ChannelFlowOKFrame(uint16_t channel, bool active) : ChannelFrame(channel, 1), //sizeof bool _active(active) {} /** * Destructor */ virtual ~ChannelFlowOKFrame() {} /** * Method id */ virtual uint16_t methodID() const override { return 21; } /** * Is channel flow active? * @return bool */ bool active() const { return _active.get(0); } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if (!channel) return false; // report success for the call channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/channelframe.h000066400000000000000000000015711470663072600163670ustar00rootroot00000000000000/** * Class describing an AMQP channel frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ChannelFrame : public MethodFrame { protected: /** * Constructor for a channelFrame * * @param channel channel we're working on * @param size size of the frame */ ChannelFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {} /** * Constructor that parses an incoming frame * @param frame The received frame */ ChannelFrame(ReceivedFrame &frame) : MethodFrame(frame) {} public: /** * Destructor */ virtual ~ChannelFrame() {} /** * Class id * @return uint16_t */ virtual uint16_t classID() const override { return 20; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/channelimpl.cpp000066400000000000000000000732371470663072600166010ustar00rootroot00000000000000/** * Channel.cpp * * Implementation for a channel * * @copyright 2014 - 2023 Copernica BV */ #include "includes.h" #include "basicgetokframe.h" #include "basicreturnframe.h" #include "returnedmessage.h" #include "channelopenframe.h" #include "channelflowframe.h" #include "channelcloseokframe.h" #include "channelcloseframe.h" #include "confirmselectframe.h" #include "transactionselectframe.h" #include "transactioncommitframe.h" #include "transactionrollbackframe.h" #include "exchangedeclareframe.h" #include "exchangedeleteframe.h" #include "exchangebindframe.h" #include "exchangeunbindframe.h" #include "queuedeclareframe.h" #include "queuebindframe.h" #include "queueunbindframe.h" #include "queuepurgeframe.h" #include "queuedeleteframe.h" #include "basicpublishframe.h" #include "basicheaderframe.h" #include "bodyframe.h" #include "basicqosframe.h" #include "basicconsumeframe.h" #include "basiccancelframe.h" #include "basicackframe.h" #include "basicnackframe.h" #include "basicrecoverframe.h" #include "basicrejectframe.h" #include "basicgetframe.h" /** * Set up namespace */ namespace AMQP { /** * Constructor */ ChannelImpl::ChannelImpl() = default; /** * Destructor */ ChannelImpl::~ChannelImpl() { // remove this channel from the connection (but not if the connection is already destructed) if (_connection) _connection->remove(this); } /** * Callback that is called when an error occurs. * * Only one error callback can be registered. Calling this function * multiple times will remove the old callback. * * @param callback the callback to execute */ void ChannelImpl::onError(ErrorCallback&& callback) { // store callback _errorCallback = std::move(callback); // if the channel is usable, all is ok if (usable()) return; // validity check if (!_errorCallback) return; // is the channel closing down? if (_state == state_closing) return _errorCallback("Channel is closing down"); // the channel is closed, but what is the connection doing? if (_connection == nullptr) return _errorCallback("Channel is not linked to a connection"); // if the connection is valid, this is a pure channel error if (_connection->ready()) return _errorCallback("Channel is in an error state, but the connection is valid"); // the connection is closing down if (_connection->closing()) return _errorCallback("Channel is in an error state, the AMQP connection is closing down"); // the connection is already closed if (_connection->closed()) return _errorCallback("Channel is in an error state, the AMQP connection has been closed"); // direct call if channel is already in error state _errorCallback("Channel is in error state, something went wrong with the AMQP connection"); } /** * Initialize the object with an connection * @param connection * @return bool */ bool ChannelImpl::attach(Connection *connection) { // get connection impl _connection = &connection->_implementation; // retrieve an ID _id = _connection->add(shared_from_this()); // check if the id is valid if (_id == 0) { // this is invalid _state = state_closed; // failure return false; } else { // assume channel is connected _state = state_connected; // send the open frame if (send(ChannelOpenFrame(_id))) return true; // this is an error _state = state_closed; // report failure return false; } } /** * Push a deferred result * @param result The deferred object to push */ Deferred &ChannelImpl::push(const std::shared_ptr &deferred) { // do we already have an oldest? if (!_oldestCallback) _oldestCallback = deferred; // do we already have a newest? if (_newestCallback) _newestCallback->add(deferred); // store newest callback _newestCallback = deferred; // done return *deferred; } /** * Send a frame and push a deferred result * @param frame The frame to send */ Deferred &ChannelImpl::push(const Frame &frame) { // send the frame, and push the result return push(std::make_shared(!send(frame))); } /** * Pause deliveries on a channel * * This will stop all incoming messages * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::pause() { // send a channel flow frame return push(ChannelFlowFrame(_id, false)); } /** * Resume a paused channel * * This will resume incoming messages * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::resume() { // send a channel flow frame return push(ChannelFlowFrame(_id, true)); } /** * Put channel in a confirm mode * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ DeferredConfirm &ChannelImpl::confirmSelect() { // the frame to send ConfirmSelectFrame frame(_id); // send the frame, and create deferred object _confirm = std::make_shared(!send(frame)); // push to list push(_confirm); // done return *_confirm; } /** * Start a transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::startTransaction() { // send a transaction frame return push(TransactionSelectFrame(_id)); } /** * Commit the current transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::commitTransaction() { // send a transaction frame return push(TransactionCommitFrame(_id)); } /** * Rollback the current transaction * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::rollbackTransaction() { // send a transaction frame return push(TransactionRollbackFrame(_id)); } /** * Close the current channel * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::close() { // this is completely pointless if already closed if (!usable()) return push(std::make_shared(_state == state_closing)); // send a channel close frame auto &handler = push(ChannelCloseFrame(_id)); // was the frame sent and are we still alive? if (handler) _state = state_closing; // done return handler; } /** * declare an exchange * @param name name of the exchange to declare * @param type type of exchange * @param flags additional settings for the exchange * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::declareExchange(const std::string_view &name, ExchangeType type, int flags, const Table &arguments) { // convert exchange type const char *exchangeType = ""; // convert the exchange type into a string if (type == ExchangeType::fanout) exchangeType = "fanout"; else if (type == ExchangeType::direct) exchangeType = "direct"; else if (type == ExchangeType::topic) exchangeType = "topic"; else if (type == ExchangeType::headers) exchangeType = "headers"; else if (type == ExchangeType::consistent_hash) exchangeType = "x-consistent-hash"; else if (type == ExchangeType::message_deduplication) exchangeType = "x-message-deduplication"; // the boolean options bool passive = (flags & AMQP::passive) != 0; bool durable = (flags & AMQP::durable) != 0; bool autodelete = (flags & AMQP::autodelete) != 0; bool internal = (flags & AMQP::internal) != 0; bool nowait = (flags & AMQP::nowait) != 0; // send declare exchange frame return push(ExchangeDeclareFrame(_id, name, exchangeType, passive, durable, autodelete, internal, nowait, arguments)); } /** * bind an exchange * * @param source exchange which binds to target * @param target exchange to bind to * @param routingKey routing key * @param arguments additional arguments for binding * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::bindExchange(const std::string_view &source, const std::string_view &target, const std::string_view &routingkey, const Table &arguments) { // send exchange bind frame return push(ExchangeBindFrame(_id, target, source, routingkey, false, arguments)); } /** * unbind two exchanges * * @param source the source exchange * @param target the target exchange * @param routingkey the routing key * @param arguments additional unbind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::unbindExchange(const std::string_view &source, const std::string_view &target, const std::string_view &routingkey, const Table &arguments) { // send exchange unbind frame return push(ExchangeUnbindFrame(_id, target, source, routingkey, false, arguments)); } /** * remove an exchange * * @param name name of the exchange to remove * @param flags additional settings for deleting the exchange * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::removeExchange(const std::string_view &name, int flags) { // send delete exchange frame return push(ExchangeDeleteFrame(_id, name, (flags & ifunused) != 0, false)); } /** * declare a queue * @param name queue name * @param flags additional settings for the queue * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ DeferredQueue &ChannelImpl::declareQueue(const std::string_view &name, int flags, const Table &arguments) { // the frame to send QueueDeclareFrame frame(_id, name, (flags & passive) != 0, (flags & durable) != 0, (flags & exclusive) != 0, (flags & autodelete) != 0, false, arguments); // send the queuedeclareframe auto result = std::make_shared(!send(frame)); // add the deferred result push(result); // done return *result; } /** * Bind a queue to an exchange * * @param exchangeName name of the exchange to bind to * @param queueName name of the queue * @param routingkey routingkey * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::bindQueue(const std::string_view &exchangeName, const std::string_view &queueName, const std::string_view &routingkey, const Table &arguments) { // send the bind queue frame return push(QueueBindFrame(_id, queueName, exchangeName, routingkey, false, arguments)); } /** * Unbind a queue from an exchange * * @param exchange the source exchange * @param queue the target queue * @param routingkey the routing key * @param arguments additional bind arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::unbindQueue(const std::string_view &exchange, const std::string_view &queue, const std::string_view &routingkey, const Table &arguments) { // send the unbind queue frame return push(QueueUnbindFrame(_id, queue, exchange, routingkey, arguments)); } /** * Purge a queue * @param queue queue to purge * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(AMQP::Channel *channel, uint32_t messageCount); * * For example: channel.declareQueue("myqueue").onSuccess([](AMQP::Channel *channel, uint32_t messageCount) { * * std::cout << "Queue purged, all " << messageCount << " messages removed" << std::endl; * * }); */ DeferredDelete &ChannelImpl::purgeQueue(const std::string_view &name) { // the frame to send QueuePurgeFrame frame(_id, name, false); // send the frame, and create deferred object auto deferred = std::make_shared(!send(frame)); // push to list push(deferred); // done return *deferred; } /** * Remove a queue * @param queue queue to remove * @param flags additional flags * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(AMQP::Channel *channel, uint32_t messageCount); * * For example: channel.declareQueue("myqueue").onSuccess([](AMQP::Channel *channel, uint32_t messageCount) { * * std::cout << "Queue deleted, along with " << messageCount << " messages" << std::endl; * * }); */ DeferredDelete &ChannelImpl::removeQueue(const std::string_view &name, int flags) { // the frame to send QueueDeleteFrame frame(_id, name, (flags & ifunused) != 0, (flags & ifempty) != 0, false); // send the frame, and create deferred object auto deferred = std::make_shared(!send(frame)); // push to list push(deferred); // done return *deferred; } /** * Publish a message to an exchange * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags * @return bool */ bool ChannelImpl::publish(const std::string_view &exchange, const std::string_view &routingKey, const Envelope &envelope, int flags) { // we are going to send out multiple frames, each one will trigger a call to the handler, // which in turn could destruct the channel object, we need to monitor that Monitor monitor(this); // @todo do not copy the entire buffer to individual frames // send the publish frame if (!send(BasicPublishFrame(_id, exchange, routingKey, (flags & mandatory) != 0, (flags & immediate) != 0))) return false; // channel still valid? if (!monitor.valid()) return false; // send header if (!send(BasicHeaderFrame(_id, envelope))) return false; // if everything has been sent by now if (envelope.bodySize() == 0) return true; // channel and connection still valid? if (!monitor.valid() || !_connection) return false; // the max payload size is the max frame size minus the bytes for headers and trailer uint32_t maxpayload = _connection->maxPayload(); uint64_t bytessent = 0; // the buffer const char *data = envelope.body(); uint64_t bytesleft = envelope.bodySize(); // split up the body in multiple frames depending on the max frame size while (bytesleft > 0) { // size of this chunk uint64_t chunksize = std::min(static_cast(maxpayload), bytesleft); // send out a body frame if (!send(BodyFrame(_id, data + bytessent, (uint32_t)chunksize))) return false; // channel still valid? if (!monitor.valid()) return false; // update counters bytessent += chunksize; bytesleft -= chunksize; } // done return true; } /** * Set the Quality of Service (QOS) for this channel * @param prefetchCount maximum number of messages to prefetch * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * @param prefetchCount number of messages to fetch * @param global share counter between all consumers on the same channel */ Deferred &ChannelImpl::setQos(uint16_t prefetchCount, bool global) { // send a qos frame return push(BasicQosFrame(_id, prefetchCount, global)); } /** * Tell the RabbitMQ server that we're ready to consume messages * @param queue the queue from which you want to consume * @param tag a consumer tag that will be associated with this consume operation * @param flags additional flags * @param arguments additional arguments * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(AMQP::Channel *channel, const std::string& tag); * * For example: channel.declareQueue("myqueue").onSuccess([](AMQP::Channel *channel, const std::string& tag) { * * std::cout << "Started consuming under tag " << tag << std::endl; * * }); */ DeferredConsumer& ChannelImpl::consume(const std::string_view &queue, const std::string_view &tag, int flags, const Table &arguments) { // the frame to send BasicConsumeFrame frame(_id, queue, tag, (flags & nolocal) != 0, (flags & noack) != 0, (flags & exclusive) != 0, false, arguments); // send the frame, and create deferred object auto deferred = std::make_shared(this, !send(frame)); // push to list push(deferred); // done return *deferred; } /** * Tell that you are prepared to recall/take back messages that could not be * published. This is only meaningful if you pass the 'immediate' or 'mandatory' * flag to publish() operations. * * THis function returns a deferred handler more or less similar to the object * return by the consume() method and that can be used to install callbacks that * handle the recalled messages. */ DeferredRecall &ChannelImpl::recall() { // create the DeferredRecall if it does not exist if (!_recall) _recall = std::make_shared(this); // return the deferred handler return *_recall; } /** * Cancel a running consumer * @param tag the consumer tag * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. * * The onSuccess() callback that you can install should have the following signature: * * void myCallback(const std::string& tag); * * For example: channel.declareQueue("myqueue").onSuccess([](const std::string& tag) { * * std::cout << "Started consuming under tag " << tag << std::endl; * * }); */ DeferredCancel &ChannelImpl::cancel(const std::string_view &tag) { // the cancel frame to send BasicCancelFrame frame(_id, tag, false); // send the frame, and create deferred object auto deferred = std::make_shared(this, !send(frame)); // push to list push(deferred); // done return *deferred; } /** * Retrieve a single message from RabbitMQ * * When you call this method, you can get one single message from the queue (or none * at all if the queue is empty). The deferred object that is returned, should be used * to install a onEmpty() and onSuccess() callback function that will be called * when the message is consumed and/or when the message could not be consumed. * * The following flags are supported: * * - noack if set, consumed messages do not have to be acked, this happens automatically * * @param queue name of the queue to consume from * @param flags optional flags * * The object returns a deferred handler. Callbacks can be installed * using onSuccess(), onEmpty(), onError() and onFinalize() methods. * * The onSuccess() callback has the following signature: * * void myCallback(const Message &message, uint64_t deliveryTag, bool redelivered); * * For example: channel.get("myqueue").onSuccess([](const Message &message, uint64_t deliveryTag, bool redelivered) { * * std::cout << "Message fetched" << std::endl; * * }).onEmpty([]() { * * std::cout << "Queue is empty" << std::endl; * * }); */ DeferredGet &ChannelImpl::get(const std::string_view &queue, int flags) { // the get frame to send BasicGetFrame frame(_id, queue, (flags & noack) != 0); // send the frame, and create deferred object auto deferred = std::make_shared(this, !send(frame)); // push to list push(deferred); // done return *deferred; } /** * Acknowledge a message * @param deliveryTag the delivery tag * @param flags optional flags * @return bool */ bool ChannelImpl::ack(uint64_t deliveryTag, int flags) { // send an ack frame return send(BasicAckFrame(_id, deliveryTag, (flags & multiple) != 0)); } /** * Reject a message * @param deliveryTag the delivery tag * @param flags optional flags * @return bool */ bool ChannelImpl::reject(uint64_t deliveryTag, int flags) { // should we reject multiple messages? if (flags & multiple) { // send a nack frame return send(BasicNackFrame(_id, deliveryTag, true, (flags & requeue) != 0)); } else { // send a reject frame return send(BasicRejectFrame(_id, deliveryTag, (flags & requeue) != 0)); } } /** * Recover un-acked messages * @param flags optional flags * * This function returns a deferred handler. Callbacks can be installed * using onSuccess(), onError() and onFinalize() methods. */ Deferred &ChannelImpl::recover(int flags) { // send a nack frame return push(BasicRecoverFrame(_id, (flags & requeue) != 0)); } /** * Send a buffer over the channel * @param frame frame to send * @return bool was frame succesfully sent? */ bool ChannelImpl::send(CopiedBuffer &&frame) { // skip if channel is not connected if (_state == state_closed || !_connection) return false; // if we're busy closing, we failed as well if (_state == state_closing) return false; // are we currently in synchronous mode or are there // other frames waiting for their turn to be sent? if (waiting()) { // we need to wait until the synchronous frame has // been processed, so queue the frame until it was _queue.emplace(std::move(frame)); // it was of course not actually sent but we pretend // that it was, because no error occured return true; } // is this a synchronous frame? bool syncframe = frame.synchronous(); // send to tcp connection if (!_connection->send(std::move(frame))) return false; // frame was sent, if this was a synchronous frame, we now have to wait _synchronous = syncframe; // done return true; } /** * Send a frame over the channel * @param frame frame to send * @return bool was the frame sent? */ bool ChannelImpl::send(const Frame &frame) { // skip if channel is not connected if (_state == state_closed || !_connection) return false; // if we're busy closing, we pretend that the send operation was a // success. this causes the deferred object to be created, and to be // added to the list of deferred objects. it will be notified about // the error when the close operation succeeds if (_state == state_closing) return true; // are we currently in synchronous mode or are there // other frames waiting for their turn to be sent? if (waiting()) { // we need to wait until the synchronous frame has // been processed, so queue the frame until it was _queue.emplace(frame); // it was of course not actually sent but we pretend // that it was, because no error occured return true; } // send to tcp connection if (!_connection->send(frame)) return false; // frame was sent, if this was a synchronous frame, we now have to wait _synchronous = frame.synchronous(); // done return true; } /** * The max payload size for body frames * @return uint32_t */ uint32_t ChannelImpl::maxPayload() const { // forward to the connection // @todo what if _connection == nullptr? return _connection->maxPayload(); } /** * Signal the channel that a synchronous operation was completed. After * this operation, waiting frames can be sent out. */ bool ChannelImpl::flush() { // we are no longer waiting for synchronous operations _synchronous = false; // we need to monitor the channel for validity Monitor monitor(this); // send all frames while not in synchronous mode while (_connection && !_synchronous && !_queue.empty()) { // retrieve the front item auto buffer = std::move(_queue.front()); // remove from the list _queue.pop(); // is this a synchronous frame? bool syncframe = buffer.synchronous(); // send to tcp connection if (!_connection->send(std::move(buffer))) return false; // the user space handler may have destructed this channel object if (!monitor.valid()) return true; // frame was sent, if this was a synchronous frame, we now have to wait _synchronous = syncframe; } // done return true; } /** * Report an error message on a channel * @param message the error message * @param notifyhandler should the channel-wide handler also be called? */ void ChannelImpl::reportError(const char *message, bool notifyhandler) { // change state _state = state_closed; _synchronous = false; // the queue of messages that still have to sent can be emptied now // (we do this by moving the current queue into an unused variable) auto queue(std::move(_queue)); // we are going to call callbacks that could destruct the channel Monitor monitor(this); // call the oldest if (_oldestCallback) { // copy the callback (so that it can not be destructed during // the "reportError" call auto cb = _oldestCallback; // call the callback auto next = cb->reportError(message); // leap out if channel no longer exists if (!monitor.valid()) return; // in case the callback-shared-pointer is still kept in scope (for example because it // is stored in the list of consumers), we do want to ensure that it no longer maintains // a chain of queued deferred objects cb->unchain(); // set the oldest callback _oldestCallback = next; } // clean up all deferred other objects while (_oldestCallback) { // copy the callback (so that it can not be destructed during // the "reportError" call auto cb = _oldestCallback; // call the callback auto next = cb->reportError("Channel is in error state"); // leap out if channel no longer exists if (!monitor.valid()) return; // in case the callback-shared-pointer is still kept in scope (for example because it // is stored in the list of consumers), we do want to ensure that it no longer maintains // a chain of queued deferred objects cb->unchain(); // set the oldest callback _oldestCallback = next; } // all callbacks have been processed, so we also can reset the pointer to the newest _newestCallback = nullptr; // inform handler if (notifyhandler && _errorCallback) _errorCallback(message); // leap out if object no longer exists if (!monitor.valid()) return; // done when the channel was already no longer associated with a connection if (!_connection) return; // when we call _connection->remove(this), it is possible that the last reference // to the channelimpl is also dropped and that the object immediately destructs, // this is something that we want to prevent so we create an extra reference to the object here auto self = shared_from_this(); // the connection no longer has to know that this channel exists, // because the channel ID is no longer in use _connection->remove(this); // remember that we're no longer associated with a connection _connection = nullptr; } /** * Report that a consumer was cancelled by the server (for example because the * queue was removed or the node on which the queue was stored was terminated) * @param tag the consumer tag */ void ChannelImpl::reportCancelled(const std::string &tag) { // look in the map auto iter = _consumers.find(tag); // check if there is not even such a consumer if (iter == _consumers.end()) return; // the actual consumer (this is a shared pointer) auto consumer = iter->second; // remove from the map _consumers.erase(iter); // report that the consumer was cancelled consumer->reportCancelled(tag); } /** * Get the current receiver for a given consumer tag * @param consumertag the consumer frame * @return DeferredConsumer */ DeferredConsumer *ChannelImpl::consumer(const std::string &consumertag) const { // look in the map auto iter = _consumers.find(consumertag); // return the result return iter == _consumers.end() ? nullptr : iter->second.get(); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/channelopenframe.h000066400000000000000000000024431470663072600172500ustar00rootroot00000000000000/** * Class describing a channel open frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ChannelOpenFrame : public ChannelFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ChannelFrame::fill(buffer); // add deprecated data ShortString unused; // add to the buffer unused.fill(buffer); } public: /** * Constructor to create a channelOpenFrame * * @param channel channel we're working on */ ChannelOpenFrame(uint16_t channel) : ChannelFrame(channel, 1) {} // 1 for the deprecated shortstring size /** * Construct to parse a received frame * @param frame */ ChannelOpenFrame(ReceivedFrame &frame) : ChannelFrame(frame) { // deprecated argument ShortString unused(frame); } /** * Destructor */ virtual ~ChannelOpenFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 10; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/channelopenokframe.h000066400000000000000000000036121470663072600176010ustar00rootroot00000000000000/** * Class describing a channel open acknowledgement frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ChannelOpenOKFrame : public ChannelFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ChannelFrame::fill(buffer); // create and encode the silly deprecated argument LongString unused; // add to the buffer unused.fill(buffer); } public: /** * Constructor based on client information * @param channel Channel identifier */ ChannelOpenOKFrame(uint16_t channel) : ChannelFrame(channel, 4) {} // 4 for the longstring size value /** * Constructor based on incoming frame * @param frame */ ChannelOpenOKFrame(ReceivedFrame &frame) : ChannelFrame(frame) { // read in a deprecated argument LongString unused(frame); } /** * Destructor */ virtual ~ChannelOpenOKFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 11; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report that the channel is open channel->reportReady(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/confirmframe.h000066400000000000000000000015671470663072600164210ustar00rootroot00000000000000/** * Class describing an AMQP confirm frame * * @author Marcin Gibula * @copyright 2017 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConfirmFrame : public MethodFrame { protected: /** * Constructor * @param channel channel identifier * @param size frame size */ ConfirmFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {} /** * Constructor based on incoming frame * @param frame */ ConfirmFrame(ReceivedFrame &frame) : MethodFrame(frame) {} public: /** * Destructor */ virtual ~ConfirmFrame() {} /** * Class id * @return uint16_t */ virtual uint16_t classID() const override { return 85; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/confirmselectframe.h000066400000000000000000000031511470663072600176100ustar00rootroot00000000000000/** * Class describing an AMQP confirm select frame * * @author Marcin Gibula * @copyright 2017 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConfirmSelectFrame : public ConfirmFrame { private: /** * whether to wait for a response * @var BooleanSet */ BooleanSet _noWait; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConfirmFrame::fill(buffer); // add boolean _noWait.fill(buffer); } public: /** * Decode a confirm select frame from a received frame * * @param frame received frame to decode */ ConfirmSelectFrame(ReceivedFrame& frame) : ConfirmFrame(frame), _noWait(frame) {} /** * Construct a confirm select frame * * @param channel channel identifier * @return newly created confirm select frame */ ConfirmSelectFrame(uint16_t channel, bool noWait = false) : ConfirmFrame(channel, 1), //sizeof bool _noWait(noWait) {} /** * Destructor */ virtual ~ConfirmSelectFrame() {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 10; } /** * Return whether to wait for a response * @return boolean */ bool noWait() const { return _noWait.get(0); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/confirmselectokframe.h000066400000000000000000000034011470663072600201400ustar00rootroot00000000000000/** * Class describing an AMQP confirm select ok frame * * @author Marcin Gibula * @copyright 2017 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConfirmSelectOKFrame : public ConfirmFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConfirmFrame::fill(buffer); } public: /** * Constructor for an incoming frame * * @param frame received frame to decode */ ConfirmSelectOKFrame(ReceivedFrame& frame) : ConfirmFrame(frame) {} /** * Construct a confirm select ok frame * * @param channel channel identifier * @return newly created confirm select ok frame */ ConfirmSelectOKFrame(uint16_t channel) : ConfirmFrame(channel, 0) {} /** * Destructor */ virtual ~ConfirmSelectOKFrame() {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 11; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report that the channel is open channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionblockframe.h000066400000000000000000000044761470663072600201400ustar00rootroot00000000000000/** * Class describing a connection blocked frame * * This frame is sent by the server to the client, when their connection gets * blocked for the first time due to the broker running low on a resource * (memory or disk). For example, when a RabbitMQ node detects that it is low * on RAM, it sends a notification to all connected publishing clients * supporting this feature. If before the connections are unblocked the node * also starts running low on disk space, another notification will not be sent. * * @copyright 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionBlockFrame : public ConnectionFrame { private: /** * The reason for blocking * @var ShortString */ ShortString _reason; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // encode the field _reason.fill(buffer); } public: /** * Construct a connection blocked frame from a received frame * * @param frame received frame */ ConnectionBlockFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _reason(frame) {} /** * Construct a connection blocked frame * * @param reason the reason for blocking */ ConnectionBlockFrame(uint16_t code, std::string reason) : ConnectionFrame((uint32_t)(reason.length() + 1)), // 1 for extra string byte _reason(std::move(reason)) {} /** * Destructor */ virtual ~ConnectionBlockFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 60; } /** * Get the reason for blocking * @return string */ const std::string& reason() const { return _reason; } /** * Process the frame * @param connection */ virtual bool process(ConnectionImpl *connection) override { // report that it is blocked connection->reportBlocked(this->reason().c_str()); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectioncloseframe.h000066400000000000000000000075701470663072600201510ustar00rootroot00000000000000/** * Class describing connection close frame * * @copyright 2014 - 2018 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionCloseFrame : public ConnectionFrame { private: /** * The reply code * @var uint16_t */ uint16_t _code; /** * The reply text * @var ShortString */ ShortString _text; /** * Class id for failing class, if applicable * Will be 0 in absence of errors * @var uint16_t */ uint16_t _failingClass; /** * Method id for failinv class, if applicable * Will be 0 in absence of errors * @var uint16_t */ uint16_t _failingMethod; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // add fields buffer.add(_code); _text.fill(buffer); buffer.add(_failingClass); buffer.add(_failingMethod); } public: /** * Construct a connection close frame from a received frame * * @param frame received frame */ ConnectionCloseFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _code(frame.nextUint16()), _text(frame), _failingClass(frame.nextUint16()), _failingMethod(frame.nextUint16()) {} /** * Construct a connection close frame * * @param code the reply code * @param text the reply text * @param failingClass id of the failing class if applicable * @param failingMethod id of the failing method if applicable */ ConnectionCloseFrame(uint16_t code, std::string text, uint16_t failingClass = 0, uint16_t failingMethod = 0) : ConnectionFrame((uint32_t)(text.length() + 7)), // 1 for extra string byte, 2 for each uint16 _code(code), _text(std::move(text)), _failingClass(failingClass), _failingMethod(failingMethod) {} /** * Destructor */ virtual ~ConnectionCloseFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 50; } /** * Get the reply code * @return uint16_t */ uint16_t code() const { return _code; } /** * Get the reply text * @return string */ const std::string& text() const { return _text; } /** * Get the failing class id if applicable * @return uint16_t */ uint16_t failingClass() const { return _failingClass; } /** * Get the failing method id if applicable * @return uint16_t */ uint16_t failingMethod() const { return _failingMethod; } /** * This frame is part of the shutdown operation * @return bool */ virtual bool partOfShutdown() const override { return true; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the monitor because the connection could be destructed in the meantime Monitor monitor(connection); // send back the ok frame connection->send(ConnectionCloseOKFrame()); // check if connection still exists if (!monitor.valid()) return false; // no need to check for a channel, the error is connection wide // report the error on the connection connection->reportError(text().c_str()); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectioncloseokframe.h000066400000000000000000000024461470663072600205000ustar00rootroot00000000000000/** * Class describing connection close acknowledgement frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionCloseOKFrame : public ConnectionFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); } public: /** * Constructor based on a received frame * * @param frame received frame */ ConnectionCloseOKFrame(ReceivedFrame &frame) : ConnectionFrame(frame) {} /** * construct a channelcloseokframe object */ ConnectionCloseOKFrame() : ConnectionFrame(0) {} /** * Destructor */ virtual ~ConnectionCloseOKFrame() {} /** * Method id */ virtual uint16_t methodID() const override { return 51; } /** * Process the frame * @param connection */ virtual bool process(ConnectionImpl *connection) override { // report that it is closed connection->reportClosed(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionframe.h000066400000000000000000000016351470663072600171170ustar00rootroot00000000000000/** * Class describing an AMQP connection frame * * @copyright 2014 Copernica BV */ /** * Class definition */ namespace AMQP { /** * Class implementation */ class ConnectionFrame : public MethodFrame { protected: /** * Constructor for a connectionFrame * * A connection frame never has a channel identifier, so this is passed * as zero to the base constructor * * @param size size of the frame */ ConnectionFrame(uint32_t size) : MethodFrame(0, size) {} /** * Constructor based on a received frame * @param frame */ ConnectionFrame(ReceivedFrame &frame) : MethodFrame(frame) {} public: /** * Destructor */ virtual ~ConnectionFrame() {} /** * Class id * @return uint16_t */ virtual uint16_t classID() const override { return 10; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionimpl.cpp000066400000000000000000000302121470663072600173120ustar00rootroot00000000000000/** * ConnectionImpl.cpp * * Implementation of an AMQP connection * * @copyright 2014 - 2018 Copernica BV */ #include "includes.h" #include "protocolheaderframe.h" #include "connectioncloseokframe.h" #include "connectioncloseframe.h" #include "reducedbuffer.h" #include "passthroughbuffer.h" #include "heartbeatframe.h" /** * set namespace */ namespace AMQP { /** * Construct an AMQP object based on full login data * * The first parameter is a handler object. This handler class is * an interface that should be implemented by the caller. * * Note that the constructor is private to ensure that nobody can construct * this class, only the real Connection class via a friend construct * * @param parent Parent connection object * @param handler Connection handler * @param login Login data */ ConnectionImpl::ConnectionImpl(Connection *parent, ConnectionHandler *handler, const Login &login, const std::string &vhost) : _parent(parent), _handler(handler), _login(login), _vhost(vhost) { // we need to send a protocol header send(ProtocolHeaderFrame()); } /** * Destructor */ ConnectionImpl::~ConnectionImpl() { // close the connection in a nice fashion close(); // invalidate all channels, so they will no longer call methods on this channel object for (auto iter = _channels.begin(); iter != _channels.end(); iter++) iter->second->detach(); } /** * Add a channel to the connection, and return the channel ID that it * is allowed to use, or 0 when no more ID's are available * @param channel * @return uint16_t */ uint16_t ConnectionImpl::add(const std::shared_ptr &channel) { // check if we have exceeded the limit already if (_maxChannels > 0 && _channels.size() >= _maxChannels) return 0; // keep looping to find an id that is not in use while (true) { // is this id in use? if (_nextFreeChannel > 0 && _channels.find(_nextFreeChannel) == _channels.end()) break; // id is in use, move on _nextFreeChannel++; } // we have a new channel _channels[_nextFreeChannel] = channel; // done return _nextFreeChannel++; } /** * Remove a channel * @param channel */ void ConnectionImpl::remove(const ChannelImpl *channel) { // skip zero channel if (channel->id() == 0) return; // remove it _channels.erase(channel->id()); } /** * Parse the buffer into a recognized frame * * Every time that data comes in on the connection, you should call this method to parse * the incoming data, and let it handle by the AMQP library. This method returns the number * of bytes that were processed. * * If not all bytes could be processed because it only contained a partial frame, you should * call this same method later on when more data is available. The AMQP library does not do * any buffering, so it is up to the caller to ensure that the old data is also passed in that * later call. * * @param buffer buffer to decode * @return number of bytes that were processed */ uint64_t ConnectionImpl::parse(const Buffer &buffer) { // do not parse if already in an error state if (_state == state_closed) return 0; // number of bytes processed uint64_t processed = 0; // create a monitor object that checks if the connection still exists Monitor monitor(this); // keep looping until we have processed all bytes, and the monitor still // indicates that the connection is in a valid state while (processed < buffer.size() && monitor.valid()) { // prevent protocol exceptions try { // try to recognize the frame ReducedBuffer reduced_buf(buffer, (size_t)processed); ReceivedFrame receivedFrame(reduced_buf, _maxFrame); // do we have the full frame? if (receivedFrame.complete()) { // process the frame receivedFrame.process(this); // number of bytes processed uint64_t bytes = receivedFrame.totalSize(); // add bytes processed += bytes; } else { // we do not yet have the complete frame, but if we do at least // have the initial bytes of the header, we already know how much // data we need for the next frame, otherwise we need at least 7 // bytes for processing the header of the next frame _expected = receivedFrame.header() ? (uint32_t)receivedFrame.totalSize() : 7; // we're ready for now return processed; } } catch (const ProtocolException &exception) { // something terrible happened on the protocol (like data out of range) reportError(exception.what()); // done return processed; } } // leap out if the connection object no longer exists if (!monitor.valid()) return processed; // the entire buffer has been processed, the next call to parse() should at least // contain the size of the frame header to be meaningful for the amqp-cpp library _expected = 7; // if the connection is being closed, we have to do more stuff, otherwise we're ready now if (!_closed || _state != state_connected) return processed; // the close() function was called, but if the close frame was not yet sent // if there are no waiting channels, we can do that right now if (!waitingChannels()) sendClose(); // done return processed; } /** * Fail all open channels, helper method * @param monitor object to check if object still exists * @param message error message * @return bool does the object still exist? */ bool ConnectionImpl::fail(const Monitor &monitor, const char *message) { // all deferred result objects in the channels should report this error too while (!_channels.empty()) { // report the errors _channels.begin()->second->reportError(message); // leap out if no longer valid if (!monitor.valid()) return false; } // done return true; } /** * Fail the connection / report that the connection is lost * @param message * @return bool */ bool ConnectionImpl::fail(const char *message) { // if already closed if (_state == state_closed) return false; // from now on we consider the connection to be closed _state = state_closed; // monitor because every callback could invalidate the connection fail(Monitor(this), message); // done return true; } /** * Report an error to user-space * @param message the error message */ void ConnectionImpl::reportError(const char *message) { // monitor because every callback could invalidate the connection Monitor monitor(this); // fail all operations if (!fail(monitor, message)) return; // inform handler _handler->onError(_parent, message); } /** * Close the connection * This will close all channels * @return bool */ bool ConnectionImpl::close() { // leap out if already closed or closing if (_closed || _state == state_closed) return false; // mark that the object is closed _closed = true; // after the send operation the object could be dead Monitor monitor(this); // number of channels that are waiting for an answer and that have further data int waiters = 0; // loop over all channels, and close them for (auto iter = _channels.begin(); iter != _channels.end(); iter++) { // close the channel iter->second->close(); // we could be dead now if (!monitor.valid()) return true; // is this channel waiting for an answer? if (iter->second->waiting()) waiters++; } // if still busy with handshake, we delay closing for a while if (waiters > 0 || _state != state_connected) return true; // perform the close frame sendClose(); // done return true; } /** * Method to send the close frames * Returns true if object still exists * @return bool */ bool ConnectionImpl::sendClose() { // after the send operation the object could be dead Monitor monitor(this); // send the close frame send(ConnectionCloseFrame(0, "shutdown")); // leap out if object no longer is alive if (!monitor.valid()) return false; // we're in a new state _state = state_closing; // done return true; } /** * Mark the connection as connected */ void ConnectionImpl::setReady() { // store connected state _state = state_connected; // we're going to call the handler, which can destruct the connection, // so we must monitor if the queue object is still valid after calling Monitor monitor(this); // inform handler _handler->onReady(_parent); // the handler could have destructed us if (!monitor.valid()) return; // empty the queue of messages while (!_queue.empty()) { // get the next message const auto &buffer = _queue.front(); // send it _handler->onData(_parent, buffer.data(), buffer.size()); // stop if monitor is gone if (!monitor.valid()) return; // remove it from the queue _queue.pop(); } // if the close method was called before, and no channel is waiting // for an answer, we can now safely send out the close frame if (_closed && _state == state_connected && !waiting()) sendClose(); } /** * Is the connection waiting for an answer from an instruction? * @return bool */ bool ConnectionImpl::waiting() const { // some states are implicit waiting states if (_state == state_protocol) return true; if (_state == state_handshake) return true; if (_state == state_closing) return true; // check if there are waiting channels return waitingChannels(); } /** * Is any channel waiting for an answer on a synchronous call? * @return bool */ bool ConnectionImpl::waitingChannels() const { // loop through the channels for (auto &iter : _channels) { // is this a waiting channel if (iter.second->waiting()) return true; } // no waiting channel found return false; } /** * Send a frame over the connection * @param frame The frame to send * @return bool Was the frame succesfully sent */ bool ConnectionImpl::send(const Frame &frame) { // its not possible to send anything if closed or closing down if (_state == state_closing || _state == state_closed) return false; // some frames can be sent _after_ the close() function was called if (_closed && !frame.partOfShutdown() && !frame.partOfHandshake()) return false; // if the frame is bigger than we allow on the connection // it is impossible to send out this frame successfully if (_maxFrame > 0 && frame.totalSize() > _maxFrame) return false; // are we still setting up the connection? if ((_state == state_connected && _queue.empty()) || frame.partOfHandshake()) { // we need an output buffer (this will immediately send the data) PassthroughBuffer buffer(_parent, _handler, frame); } else { // the connection is still being set up, so we need to delay the message sending _queue.emplace(frame); } // done return true; } /** * Send buffered data over the connection * * @param buffer the buffer with data to send */ bool ConnectionImpl::send(CopiedBuffer &&buffer) { // this only works when we are already connected if (_state != state_connected) return false; // are we waiting for other frames to be sent before us? if (_queue.empty()) { // send it directly _handler->onData(_parent, buffer.data(), buffer.size()); } else { // add to the list of waiting buffers _queue.emplace(std::move(buffer)); } // done return true; } /** * Send a ping / heartbeat frame to keep the connection alive * @return bool */ bool ConnectionImpl::heartbeat() { // send a frame return send(HeartbeatFrame()); } /** * End of namspace */ } AMQP-CPP-4.3.27/src/connectionopenframe.h000066400000000000000000000045661470663072600200070ustar00rootroot00000000000000/** * Class describing connection vhost open frame * * This frame is sent by the client after the connection is started and the * capacity has been tuned, to open the connection to a specific vhost. * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionOpenFrame : public ConnectionFrame { private: /** * Virtual host name * @var ShortString */ ShortString _vhost; /** * deprecated values, still need to read them somehow in the constructor * @var ShortString */ ShortString _deprecatedCapabilities; /** * More deprecated values * @var BooleanSet */ BooleanSet _deprecatedInsist; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // encode fields _vhost.fill(buffer); _deprecatedCapabilities.fill(buffer); _deprecatedInsist.fill(buffer); } public: /** * Open a virtual host * * @param vhost name of virtual host to open */ ConnectionOpenFrame(const std::string &vhost) : ConnectionFrame((uint32_t)(vhost.length() + 3)), // length of vhost + byte to encode this length + deprecated shortstring size + deprecated bool _vhost(vhost), _deprecatedCapabilities(""), _deprecatedInsist() {} /** * Constructor based on a received frame * * @param frame received frame */ ConnectionOpenFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _vhost(frame), _deprecatedCapabilities(frame), _deprecatedInsist(frame) {} /** * Destructor */ virtual ~ConnectionOpenFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 40; } /** * Get the vhost name * @return string */ const std::string& vhost() const { return _vhost; } /** * Is this a frame that is part of the connection setup? * @return bool */ virtual bool partOfHandshake() const override { return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionopenokframe.h000066400000000000000000000035731470663072600203360ustar00rootroot00000000000000/** * Class describing connection vhost open acknowledgement frame * * Message sent by the server to the client to confirm that a connection to * a vhost could be established * * @copyright 2014 - 2018 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionOpenOKFrame : public ConnectionFrame { private: /** * Deprecated field we need to read * @var ShortString */ ShortString _deprecatedKnownHosts; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // add deprecaed field _deprecatedKnownHosts.fill(buffer); } public: /** * Construct a connectionopenokframe from a received frame * * @param frame received frame */ ConnectionOpenOKFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _deprecatedKnownHosts(frame) {} /** * Construct a connectionopenokframe * */ ConnectionOpenOKFrame() : ConnectionFrame(1), // for the deprecated shortstring _deprecatedKnownHosts("") {} /** * Destructor */ virtual ~ConnectionOpenOKFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 41; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // all is ok, mark the connection as ready connection->setReady(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionsecureframe.h000066400000000000000000000031361470663072600203240ustar00rootroot00000000000000/** * Class describing connection setup security challenge * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionSecureFrame : public ConnectionFrame { private: /** * The security challenge * @var LongString */ LongString _challenge; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // encode fields _challenge.fill(buffer); } public: /** * Construct a connection security challenge frame * * @param challenge the challenge */ ConnectionSecureFrame(const std::string& challenge) : ConnectionFrame((uint32_t)(challenge.length() + 4)), // 4 for the length of the challenge (uint32_t) _challenge(challenge) {} /** * Construct a connection secure frame from a received frame * * @param frame received frame */ ConnectionSecureFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _challenge(frame) {} /** * Destructor */ virtual ~ConnectionSecureFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 20; } /** * Get the challenge * @return string */ const std::string& challenge() const { return _challenge; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionsecureokframe.h000066400000000000000000000032401470663072600206520ustar00rootroot00000000000000/** * Class describing connection setup security challenge response * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionSecureOKFrame : public ConnectionFrame { private: /** * The security challenge response * @var LongString */ LongString _response; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // add fields _response.fill(buffer); } public: /** * Construct a connection security challenge response frame * * @param response the challenge response */ ConnectionSecureOKFrame(const std::string& response) : ConnectionFrame((uint32_t)(response.length() + 4)), //response length + uint32_t for encoding the length _response(response) {} /** * Construct a connection security challenge response frame from a received frame * * @param frame received frame */ ConnectionSecureOKFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _response(frame) {} /** * Destructor */ virtual ~ConnectionSecureOKFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 21; } /** * Get the challenge response * @return string */ const std::string& response() const { return _response; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionstartframe.h000066400000000000000000000150151470663072600201720ustar00rootroot00000000000000/** * Class describing initial connection setup * * This frame is sent by the server to the client, right after the connection * is opened. It contains the initial connection properties, and the protocol * number. * * @copyright 2014 - 2023 Copernica BV */ /** * Dependencies */ #include "programname.h" #if !defined(_WIN32) && !defined(_WIN64) #include "platformname.h" #endif /** * Cause we want to print out version string that is passed to compiled with -D * flag. Why 2 macros? https://www.guyrutenberg.com/2008/12/20/expanding-macros-into-string-constants-in-c/ */ #define STR_EXPAND(s) #s #define STR(s) STR_EXPAND(s) /** * The version and distro names */ #define VERSION_NAME STR(VERSION) /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionStartFrame : public ConnectionFrame { private: /** * Major AMQP version number * @var uint8_t */ uint8_t _major; /** * Minor AMQP version number * @var uint8_t */ uint8_t _minor; /** * Additional server properties * @note: exact properties are not specified * and are implementation-dependent * @var Table */ Table _properties; /** * Available security mechanisms * @var LongString */ LongString _mechanisms; /** * Available message locales * @var LongString */ LongString _locales; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // encode all fields buffer.add(_major); buffer.add(_minor); _properties.fill(buffer); _mechanisms.fill(buffer); _locales.fill(buffer); } public: /** * Client-side constructer for a connection start frame * * @param major major protocol version * @param minor minor protocol version * @param properties server properties * @param mechanisms available security mechanisms * @param locales available locales */ ConnectionStartFrame(uint8_t major, uint8_t minor, const Table& properties, const std::string& mechanisms, const std::string& locales) : ConnectionFrame((uint32_t)(properties.size() + mechanisms.length() + locales.length() + 10)), // 4 for each longstring (size-uint32), 2 major/minor _major(major), _minor(minor), _properties(properties), _mechanisms(mechanisms), _locales(locales) {} /** * Construct a connection start frame from a received frame * * @param frame received frame */ ConnectionStartFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _major(frame.nextUint8()), _minor(frame.nextUint8()), _properties(frame), _mechanisms(frame), _locales(frame) {} /** * Destructor */ virtual ~ConnectionStartFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 10; } /** * Major AMQP version number * @return uint8_t */ uint8_t majorVersion() const { return _major; } /** * Minor AMQP version number * @return uint8_t */ uint8_t minorVersion() const { return _minor; } /** * Additional server properties * @note: exact properties are not specified * and are implementation-dependent * * @return Table */ const Table& properties() const { return _properties; } /** * The capabilities of the connection * @return Table */ const Table& capabilities() const { // retrieve the capabilities return _properties.get("capabilities"); } /** * Available security mechanisms * @return string */ const std::string &mechanisms() const { return _mechanisms; } /** * Available message locales * @return string */ const std::string &locales() const { return _locales; } /** * Process the connection start frame * @param connection * @return bool * @internal */ virtual bool process(ConnectionImpl *connection) override { // the client properties Table properties; // move connection to handshake mode connection->setProtocolOk(_properties, properties); // the capabilities Table capabilities; // we want a special treatment for authentication failures capabilities["authentication_failure_close"] = true; // when the server cancels a consumer (for example because a queue is delete, or the node on which the // queue lives dies, we want to receive a notification that the consumer is no longer alive) capabilities["consumer_cancel_notify"] = true; // when the rabbitmq server reaches its max capacity, it can send a notification to us, we want them capabilities["connection.blocked"] = true; // fill the peer properties if (!properties.contains("version")) properties["version"] = "AMQP-CPP " VERSION_NAME; if (!properties.contains("copyright")) properties["copyright"] = "Copernica AMQP-CPP library :: Copyright 2015-2023 Copernica BV"; if (!properties.contains("information")) properties["information"] = "https://github.com/CopernicaMarketingSoftware/AMQP-CPP"; if (!properties.contains("capabilities")) properties["capabilities"] = capabilities; if (!properties.contains("product")) properties["product"] = ProgramName();; if (!properties.contains("connection_name")) properties["connection_name"] = ProgramName(); #if defined(_WIN32) || defined(_WIN64) // i don't know that much about win32, so let's use hardcoded string if (!properties.contains("platform")) properties["platform"] = "windows"; #else // on unix-like systems I know how to retrieve application and platform info if (!properties.contains("platform")) properties["platform"] = PlatformName(); #endif // send back a connection start ok frame connection->send(ConnectionStartOKFrame(properties, "PLAIN", connection->login().saslPlain(), "en_US")); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionstartokframe.h000066400000000000000000000063451470663072600205320ustar00rootroot00000000000000/** * Class describing initial connection setup acknowledge frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionStartOKFrame : public ConnectionFrame { private: /** * Additional client properties * @note: exact properties are not specified * and are implementation-dependent * @var Table */ Table _properties; /** * The selected security mechanism * @var ShortString */ ShortString _mechanism; /** * The security response * @var LongString */ LongString _response; /** * The selected locale * @var ShortString */ ShortString _locale; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // add fields _properties.fill(buffer); _mechanism.fill(buffer); _response.fill(buffer); _locale.fill(buffer); } public: /** * Construct a connection start ok frame from a received frame * * @param frame received frame */ ConnectionStartOKFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _properties(frame), _mechanism(frame), _response(frame), _locale(frame) {} /** * Construct a connection start ok frame * * @param properties client propertes * @param mechanism selected security mechanism * @param response security response data * @param locale selected locale. */ ConnectionStartOKFrame(const Table& properties, const std::string& mechanism, const std::string& response, const std::string& locale) : ConnectionFrame((uint32_t)(properties.size() + mechanism.length() + response.length() + locale.length() + 6)), // 1 byte extra per shortstring, 4 per longstring _properties(properties), _mechanism(mechanism), _response(response), _locale(locale) {} /** * Destructor */ virtual ~ConnectionStartOKFrame() {} /** * Method id */ virtual uint16_t methodID() const override { return 11; } /** * Additional client properties * @note: exact properties are not specified * and are implementation-dependent * @return Table */ const Table& properties() const { return _properties; } /** * The selected security mechanism * @return string */ const std::string& mechanism() const { return _mechanism; } /** * The security response * @return string */ const std::string& response() const { return _response; } /** * The selected locale * @return string */ const std::string locale() const { return _locale; } /** * Is this a frame that is part of the connection setup? * @return bool */ virtual bool partOfHandshake() const override { return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectiontuneframe.h000066400000000000000000000071731470663072600200160ustar00rootroot00000000000000/** * Class describing connection tune frame * * A connection tune frame is sent by the server after the connection is * started to negotiate the max frame size, the max number of channels * and the heartbeat interval. * * When this frame is received, we should send back a tune-ok frame to * confirm that we have received this frame. * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionTuneFrame : public ConnectionFrame { private: /** * Proposed maximum number of channels * @var uint16_t */ uint16_t _channels; /** * Proposed maximum frame size * @var uint32_t */ uint32_t _frameMax; /** * Desired heartbeat delay * @var uint16_t */ uint16_t _heartbeat; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // add fields buffer.add(_channels); buffer.add(_frameMax); buffer.add(_heartbeat); } public: /** * Construct a connection tuning frame * * @param channels proposed maximum number of channels * @param frameMax proposed maximum frame size * @param heartbeat desired heartbeat delay */ ConnectionTuneFrame(uint16_t channels, uint32_t frameMax, uint16_t heartbeat) : ConnectionFrame(8), // 2x uint16_t, 1x uint32_t _channels(channels), _frameMax(frameMax), _heartbeat(heartbeat) {} /** * Construct a connection tune frame from a received frame * * @param frame received frame */ ConnectionTuneFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _channels(frame.nextUint16()), _frameMax(frame.nextUint32()), _heartbeat(frame.nextUint16()) {} /** * Destructor */ virtual ~ConnectionTuneFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 30; } /** * Proposed maximum number of channels * @return _uint16_t */ uint16_t channelMax() const { return _channels; } /** * Proposed maximum frame size * @return _uint32_t */ uint32_t frameMax() const { return _frameMax; } /** * Desired heartbeat delay * @return uint16_t */ uint16_t heartbeat() const { return _heartbeat; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // remember this in the connection connection->setCapacity(channelMax(), frameMax()); // theoretically it is possible that the connection object gets destructed between sending the messages Monitor monitor(connection); // store the heartbeat the server wants uint16_t interval = connection->setHeartbeat(heartbeat()); // send it back connection->send(ConnectionTuneOKFrame(channelMax(), frameMax(), interval)); // check if the connection object still exists if (!monitor.valid()) return true; // and finally we start to open the frame return connection->send(ConnectionOpenFrame(connection->vhost())); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectiontuneokframe.h000066400000000000000000000051221470663072600203400ustar00rootroot00000000000000/** * Class describing connection tune acknowledgement frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionTuneOKFrame : public ConnectionFrame { private: /** * Proposed maximum number of channels * @var uint16_t */ uint16_t _channels; /** * Proposed maximum frame size * @var uint32_t */ uint32_t _frameMax; /** * Desired heartbeat delay * @var uint16_t */ uint16_t _heartbeat; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); // add fields buffer.add(_channels); buffer.add(_frameMax); buffer.add(_heartbeat); } public: /** * Construct a connection tune frame from a received frame * * @param frame received frame */ ConnectionTuneOKFrame(ReceivedFrame &frame) : ConnectionFrame(frame), _channels(frame.nextUint16()), _frameMax(frame.nextUint32()), _heartbeat(frame.nextUint16()) {} /** * Construct a connection tuning acknowledgement frame * * @param channels selected maximum number of channels * @param frame selected maximum frame size * @param heartbeat desired heartbeat delay */ ConnectionTuneOKFrame(uint16_t channels, uint32_t frameMax, uint16_t heartbeat) : ConnectionFrame(8), // 2x uint16_t, 1x uint32_t _channels(channels), _frameMax(frameMax), _heartbeat(heartbeat) {} /** * Destructor */ virtual ~ConnectionTuneOKFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 31; } /** * Proposed maximum number of channels * @return _uint16_t */ uint16_t channels() const { return _channels; } /** * Proposed maximum frame size * @return _uint32_t */ uint32_t frameMax() const { return _frameMax; } /** * Desired heartbeat delay * @return uint16_t */ uint16_t heartbeat() const { return _heartbeat; } /** * Is this a frame that is part of the connection setup? * @return bool */ virtual bool partOfHandshake() const override { return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/connectionunblockframe.h000066400000000000000000000030051470663072600204660ustar00rootroot00000000000000/** * Class describing a connection unblocked frame * * This frame is sent by the server to the client, when all resource alarms * have cleared and the connection is fully unblocked. * * @copyright 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ConnectionUnblockFrame : public ConnectionFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ConnectionFrame::fill(buffer); } public: /** * Construct a connection unblocked frame from a received frame * * @param frame received frame */ ConnectionUnblockFrame(ReceivedFrame &frame) : ConnectionFrame(frame) {} /** * Construct a connection unblocked frame */ ConnectionUnblockFrame(uint16_t code, std::string reason) : ConnectionFrame(0) {} /** * Destructor */ virtual ~ConnectionUnblockFrame() {} /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 61; } /** * Process the frame * @param connection */ virtual bool process(ConnectionImpl *connection) override { // report that it is no longer blocked connection->reportUnblocked(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/deferredcancel.cpp000066400000000000000000000013571470663072600172270ustar00rootroot00000000000000/** * DeferredCancel.cpp * * Implementation file for the DeferredCancel class * * @copyright 2014 Copernica BV */ #include "includes.h" /** * Namespace */ namespace AMQP { /** * Report success for frames that report cancel operations * @param name Consumer tag that is cancelled * @return Deferred */ const std::shared_ptr &DeferredCancel::reportSuccess(const std::string &name) { // in the channel, we should uninstall the consumer _channel->uninstall(name); // skip if no special callback was installed if (!_cancelCallback) return Deferred::reportSuccess(); // call the callback _cancelCallback(name); // return next object return _next; } /** * End namespace */ } AMQP-CPP-4.3.27/src/deferredconfirm.cpp000066400000000000000000000013651470663072600174360ustar00rootroot00000000000000/** * DeferredConfirm.cpp * * Implementation file for the DeferredConfirm class * * @author Marcin Gibula * @copyright 2018 Copernica BV */ #include "includes.h" #include "basicackframe.h" #include "basicnackframe.h" /** * Namespace */ namespace AMQP { /** * Process an ACK frame * * @param frame The frame to process */ void DeferredConfirm::process(BasicAckFrame &frame) { if (_ackCallback) _ackCallback(frame.deliveryTag(), frame.multiple()); } /** * Process a NACK frame * * @param frame The frame to process */ void DeferredConfirm::process(BasicNackFrame &frame) { if (_nackCallback) _nackCallback(frame.deliveryTag(), frame.multiple(), frame.requeue()); } /** * End namespace */ } AMQP-CPP-4.3.27/src/deferredconsumer.cpp000066400000000000000000000025011470663072600176250ustar00rootroot00000000000000/** * DeferredConsumer.cpp * * Implementation file for the DeferredConsumer class * * @copyright 2014 - 2018 Copernica BV */ #include "includes.h" #include "basicdeliverframe.h" /** * Namespace */ namespace AMQP { /** * Process a delivery frame * * @param frame The frame to process */ void DeferredConsumer::process(BasicDeliverFrame &frame) { // this object will handle all future frames with header and body data _channel->install(shared_from_this()); // retrieve the delivery tag and whether we were redelivered _deliveryTag = frame.deliveryTag(); _redelivered = frame.redelivered(); // initialize the object for the next message initialize(frame.exchange(), frame.routingKey()); } /** * Report success for frames that report start consumer operations * @param name Consumer tag that is started * @return Deferred */ const std::shared_ptr &DeferredConsumer::reportSuccess(const std::string &name) { // we now know the name, so install ourselves in the channel _channel->install(name, shared_from_this()); // skip if no special callback was installed if (!_consumeCallback) return Deferred::reportSuccess(); // call the callback _consumeCallback(name); // return next object return _next; } /** * End namespace */ } AMQP-CPP-4.3.27/src/deferredextreceiver.cpp000066400000000000000000000031061470663072600203210ustar00rootroot00000000000000/** * DeferredExtReceiver.cpp * * Implementation file for the DeferredExtReceiver class * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Dependencies */ #include "amqpcpp/deferredextreceiver.h" #include "amqpcpp/channelimpl.h" /** * Begin of namespace */ namespace AMQP { /** * Initialize the object to send out a message * @param exchange the exchange to which the message was published * @param routingkey the routing key that was used to publish the message */ void DeferredExtReceiver::initialize(const std::string &exchange, const std::string &routingkey) { // call base DeferredReceiver::initialize(exchange, routingkey); // do we have anybody interested in messages? in that case we construct the message if (_messageCallback) _message.construct(exchange, routingkey); } /** * Indicate that a message was done */ void DeferredExtReceiver::complete() { // also monitor the channel Monitor monitor(_channel); // do we have a message? if (_message) _messageCallback(*_message, _deliveryTag, _redelivered); // do we have to inform anyone about completion? if (_deliveredCallback) _deliveredCallback(_deliveryTag, _redelivered); // for the next iteration we want a new message _message.reset(); // do we still have a valid channel if (!monitor.valid()) return; // we are now done executing, so the channel can forget the current receiving object _channel->install(nullptr); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/deferredget.cpp000066400000000000000000000034661470663072600165640ustar00rootroot00000000000000/** * DeferredGet.cpp * * Implementation of the DeferredGet call * * @author Emiel Bruijntjes * @copyright 2014 - 2018 Copernica BV */ /** * Dependencies */ #include "includes.h" /** * Set up namespace */ namespace AMQP { /** * Report success for a get operation * @param messagecount Number of messages left in the queue * @param deliveryTag Delivery tag of the message coming in * @param redelivered Was the message redelivered? */ const std::shared_ptr &DeferredGet::reportSuccess(uint32_t messagecount, uint64_t deliveryTag, bool redelivered) { // install this object as the handler for the upcoming header and body frames _channel->install(shared_from_this()); // store delivery tag and redelivery status _deliveryTag = deliveryTag; _redelivered = redelivered; // report the size (note that this is the size _minus_ the message that is retrieved // (and for which the callback will be called later), so it could be zero) if (_countCallback) _countCallback(messagecount); // return next handler return _next; } /** * Report success, although no message could be gotten * @return Deferred */ const std::shared_ptr &DeferredGet::reportSuccess() const { // report the size if (_countCallback) _countCallback(0); // check if a callback was set if (_emptyCallback) _emptyCallback(); // return next object return _next; } /** * Extended implementation of the complete method that is called when a message was fully received */ void DeferredGet::complete() { // the channel is now synchronized, delayed frames may now be sent _channel->flush(); // pass on to normal implementation DeferredExtReceiver::complete(); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/deferredrecall.cpp000066400000000000000000000034331470663072600172410ustar00rootroot00000000000000/** * DeferredRecall.cpp * * Implementation file for the DeferredRecall class * * @author Emiel Bruijntjes * @copyright 2018 - 2020 Copernica BV */ #include "includes.h" #include "basicreturnframe.h" /** * Begin of namespace */ namespace AMQP { /** * Process a return frame * * @param frame The frame to process */ void DeferredRecall::process(BasicReturnFrame &frame) { // this object will handle all future frames with header and body data _channel->install(shared_from_this()); // retrieve the delivery tag and whether we were redelivered _code = frame.replyCode(); _description = frame.replyText(); // notify user space of the begin of the returned message if (_beginCallback) _beginCallback(_code, _description); // initialize the object for the next message initialize(frame.exchange(), frame.routingKey()); // do we have anybody interested in messages? in that case we construct the message if (_bounceCallback) _message.construct(frame.exchange(), frame.routingKey()); } /** * Indicate that a message was done */ void DeferredRecall::complete() { // also monitor the channel Monitor monitor(_channel); // do we have a message? if (_message) _bounceCallback(*_message, _code, _description); // do we have to inform anyone about completion? if (_completeCallback) _completeCallback(); // for the next iteration we want a new message _message.reset(); // the description can be thrown away too _description.clear(); // do we still have a valid channel if (!monitor.valid()) return; // we are now done executing, so the channel can forget the current receiving object _channel->install(nullptr); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/deferredreceiver.cpp000066400000000000000000000040531470663072600176020ustar00rootroot00000000000000/** * DeferredReceiver.cpp * * Implementation file for the DeferredReceiver class * * @copyright 2016 - 2018 Copernica B.V. */ /** * Dependencies */ #include "amqpcpp/deferredreceiver.h" #include "basicdeliverframe.h" #include "basicgetokframe.h" #include "basicheaderframe.h" #include "bodyframe.h" /** * Start namespace */ namespace AMQP { /** * Initialize the object: we are going to receive a message, next frames will be header and data * @param exchange * @param routingkey */ void DeferredReceiver::initialize(const std::string &exchange, const std::string &routingkey) { // anybody interested in the new message? if (_startCallback) _startCallback(exchange, routingkey); } /** * Process the message headers * * @param frame The frame to process */ void DeferredReceiver::process(BasicHeaderFrame &frame) { // make sure we stay in scope auto self = lock(); // store the body size _bodySize = frame.bodySize(); // is user interested in the size? if (_sizeCallback) _sizeCallback(_bodySize); // do we have a message? if (_message) { // store the body size and metadata _message->setBodySize(_bodySize); _message->set(frame.metaData()); } // anybody interested in the headers? if (_headerCallback) _headerCallback(frame.metaData()); // no body data expected? then we are now complete if (_bodySize == 0) complete(); } /** * Process the message data * * @param frame The frame to process */ void DeferredReceiver::process(BodyFrame &frame) { // make sure we stay in scope auto self = lock(); // update the bytes still to receive _bodySize -= frame.payloadSize(); // anybody interested in the data? if (_dataCallback) _dataCallback(frame.payload(), frame.payloadSize()); // do we have a message? then append the data if (_message) _message->append(frame.payload(), frame.payloadSize()); // if all bytes were received we are now complete if (_bodySize == 0) complete(); } /** * End namespace */ } AMQP-CPP-4.3.27/src/exchangebindframe.h000066400000000000000000000067431470663072600174040ustar00rootroot00000000000000/** * Exchangebindframe.h * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class ExchangeBindFrame : public ExchangeFrame { private: /** * reserved byte * @var uint16_t */ uint16_t _reserved = 0; /** * Exchange to bind to * @var ShortString */ ShortString _destination; /** * Exchange which is bound * @var ShortString */ ShortString _source; /** * Routing key * @var ShortString */ ShortString _routingKey; /** * contains: nowait do not wait on response * @var booleanset */ BooleanSet _bools; /** * Additional arguments * @var Table */ Table _arguments; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer &buffer) const override { // call base ExchangeFrame::fill(buffer); buffer.add(_reserved); _destination.fill(buffer); _source.fill(buffer); _routingKey.fill(buffer); _bools.fill(buffer); _arguments.fill(buffer); } public: /** * Constructor based on incoming data * * @param frame received frame to decode */ ExchangeBindFrame(ReceivedFrame &frame) : ExchangeFrame(frame), _reserved(frame.nextUint16()), _destination(frame), _source(frame), _routingKey(frame), _bools(frame), _arguments(frame) {} /** * Constructor for an exchangebindframe * @param destination * @param source * @param routingkey * @param noWait * @param arguments */ ExchangeBindFrame(uint16_t channel, const std::string_view &destination, const std::string_view &source, const std::string_view &routingKey, bool noWait, const Table &arguments) : ExchangeFrame(channel, (uint32_t)(destination.length() + source.length() + routingKey.length() + arguments.size() + 6)), // 1 for each string, 1 for booleanset, 2 for deprecated field _destination(destination), _source(source), _routingKey(routingKey), _bools(noWait), _arguments(arguments) {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous when the nowait option has not been set return !noWait(); } /** * Get the destination exchange * @return string */ const std::string& destination() const { return _destination; } /** * Get the source exchange * @return string */ const std::string& source() const { return _source; } /** * Get the routing key * @return string */ const std::string& routingkey() const { return _routingKey; } /** * Get the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 30; } /** * Get the additional arguments * @return Table */ const Table& arguments() const { return _arguments; } /** * Get the nowait bool * @return bool */ bool noWait() const { return _bools.get(0); } }; // end namespace } AMQP-CPP-4.3.27/src/exchangebindokframe.h000066400000000000000000000030251470663072600177240ustar00rootroot00000000000000/** * Exchangebindokframe.h * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class ExchangeBindOKFrame : public ExchangeFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExchangeFrame::fill(buffer); } public: /** * Constructor based on incoming data * * @param frame received frame to decode */ ExchangeBindOKFrame(ReceivedFrame &frame) : ExchangeFrame(frame) {} /** * Constructor for an exchangebindframe * @param destination * @param source * @param routingkey * @param noWait * @param arguments */ ExchangeBindOKFrame(uint16_t channel) : ExchangeFrame(channel, 0) {} virtual uint16_t methodID() const override { return 31; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report to handler channel->reportSuccess(); // done return true; } }; // end namespace } AMQP-CPP-4.3.27/src/exchangedeclareframe.h000066400000000000000000000107261470663072600200630ustar00rootroot00000000000000/** * Class describing an AMQP exchange declare frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ExchangeDeclareFrame : public ExchangeFrame { private: /** * Field that no longer is used * @var uint16_t */ uint16_t _deprecated = 0; /** * The exchange name * @var ShortString */ ShortString _name; /** * The exchange type * @var ShortString */ ShortString _type; /** * The boolean set contains three settings: * 0: Passive declaration, do not create exchange if it does not exist * 1: Durable exchange * 4: Do not wait for a response * @var BooleanSet */ BooleanSet _bools; /** * Additional arguments. Implementation dependent. * @var Table */ Table _arguments; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExchangeFrame::fill(buffer); // add fields buffer.add(_deprecated); _name.fill(buffer); _type.fill(buffer); _bools.fill(buffer); _arguments.fill(buffer); } public: /** * Construct a exchange declare frame (client side) * * @param channel channel we´re working on * @param name exchange name * @param type exchange type * @param passive do not create exchange if it does not exist * @param durable durable exchange * @param autodelete is this an auto-delete exchange? * @param internal is this an internal exchange * @param noWait do not wait on response * @param arguments additional arguments */ ExchangeDeclareFrame(uint16_t channel, const std::string_view& name, const char *type, bool passive, bool durable, bool autodelete, bool internal, bool nowait, const Table& arguments) : ExchangeFrame(channel, (uint32_t)(name.length() + strlen(type) + arguments.size() + 5)), // size of name, type and arguments + 1 (all booleans are stored in 1 byte) + 2 (deprecated short) + 2 (string sizes) _name(name), _type(type), _bools(passive, durable, autodelete, internal, nowait), _arguments(arguments) {} /** * Construct parsing a declare frame from a received frame * @param frame The received frame */ ExchangeDeclareFrame(ReceivedFrame &frame) : ExchangeFrame(frame), _deprecated(frame.nextUint16()), _name(frame), _type(frame), _bools(frame), _arguments(frame) {} /** * Destructor */ virtual ~ExchangeDeclareFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous without the nowait option return !noWait(); } /** * Method id * @return uint16_t */ virtual uint16_t methodID() const override { return 10; } /** * The exchange name * @return string */ const std::string& name() const { return _name; } /** * The exchange type * @return string */ const std::string &exchangeType() const { return _type; } /** * Passive declaration, do not create exchange if it does not exist * @return bool */ bool passive() const { return _bools.get(0); } /** * Durable exchange * @return bool */ bool durable() const { return _bools.get(1); } /** * Is this an autodelete exchange? * @return bool */ bool autoDelete() const { return _bools.get(2); } /** * Is this an internal exchange? * @return bool */ bool internal() const { return _bools.get(3); } /** * Do not wait for a response * @return bool */ bool noWait() const { return _bools.get(4); } /** * Additional arguments. Implementation dependent. * @return Table */ const Table& arguments() const { return _arguments; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/exchangedeclareokframe.h000066400000000000000000000032501470663072600204070ustar00rootroot00000000000000/** * Class describing an AMQP exchange declare ok frame * * @copyright 2014 Copernica BV */ /** * Class definition */ namespace AMQP{ /** * Class implementation */ class ExchangeDeclareOKFrame : public ExchangeFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExchangeFrame::fill(buffer); } public: /** * Construct an exchange declare ok frame * * @param channel channel we're working on */ ExchangeDeclareOKFrame(uint16_t channel) : ExchangeFrame(channel, 0) {} /** * Decode an exchange declare acknowledgement frame * * @param frame received frame to decode */ ExchangeDeclareOKFrame(ReceivedFrame &frame) : ExchangeFrame(frame) {} /** * Destructor */ virtual ~ExchangeDeclareOKFrame() {} /** * returns the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 11; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report exchange declare ok channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/exchangedeleteframe.h000066400000000000000000000055031470663072600177230ustar00rootroot00000000000000/** * Class describing an AMQP exchange delete frame * * @copyright 2014 - 2023 Copernica BV */ /** * we live in the copernica namespace */ namespace AMQP { /** * Class implementation */ class ExchangeDeleteFrame : public ExchangeFrame { private: /** * Field that is no longer in use * @var uint16_t */ uint16_t _deprecated = 0; /** * The exchange name * @var ShortString */ ShortString _name; /** * booleanset, contains: * 0: ifUnused * 1: noWait * @var BooleanSet */ BooleanSet _bools; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExchangeFrame::fill(buffer); // add fields buffer.add(_deprecated); _name.fill(buffer); _bools.fill(buffer); } public: /** * constructor based on incoming data * * @param frame received frame */ ExchangeDeleteFrame(ReceivedFrame &frame) : ExchangeFrame(frame), _deprecated(frame.nextUint16()), _name(frame), _bools(frame) {} /** * construct a exchangedeleteframe * * @param channel channel we're working on * @param String name Name of the exchange * @param bool ifUnused Delete only if frame is not used * @param bool noWait Do not wait for a response */ ExchangeDeleteFrame(uint16_t channel, const std::string_view &name, bool ifUnused = false, bool noWait = false) : ExchangeFrame(channel, (uint32_t)(name.length() + 4)), // length of the name, 1 byte for encoding this length, 1 for bools, 2 for deprecated short _name(name), _bools(ifUnused, noWait) {} /** * Destructor */ virtual ~ExchangeDeleteFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous without the nowait option return !noWait(); } /** * returns the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 20; } /** * returns the exchange name * @return string */ const std::string& name() const { return _name; } /** * returns whether to delete if unused * @return bool */ bool ifUnused() const { return _bools.get(0); } /** * returns whether to wait for a response * @return bool */ bool noWait() const { return _bools.get(1); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/exchangedeleteokframe.h000066400000000000000000000032031470663072600202500ustar00rootroot00000000000000/** * Class describing an AMQP exchange delete ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ExchangeDeleteOKFrame : public ExchangeFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExchangeFrame::fill(buffer); } public: /** * Construct an exchange delete ok frame * * @param frame received frame */ ExchangeDeleteOKFrame(ReceivedFrame &frame) : ExchangeFrame(frame) {} /** * Construct an exchange delete ok frame * * @param channel channel we're working on */ ExchangeDeleteOKFrame(uint16_t channel) : ExchangeFrame(channel, 0) {} /** * Destructor */ virtual ~ExchangeDeleteOKFrame() {} /** * returns the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 21; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report to handler channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/exchangeframe.h000066400000000000000000000016021470663072600165340ustar00rootroot00000000000000/** * Class describing an AMQP exchange frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class ExchangeFrame : public MethodFrame { protected: /** * Constructor based on incoming data * * @param frame received frame to decode */ ExchangeFrame(ReceivedFrame &frame) : MethodFrame(frame) {} /** * Constructor for an exchange frame * * @param channel channel we're working on * @param size size of the payload */ ExchangeFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {} public: /** * Destructor */ virtual ~ExchangeFrame() {} /** * Class id * @return uint16_t */ virtual uint16_t classID() const override { return 40; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/exchangeunbindframe.h000066400000000000000000000067361470663072600177510ustar00rootroot00000000000000/** * Exchangeunbindframe.h * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class ExchangeUnbindFrame : public ExchangeFrame { private: /** * reserved byte * @var uint16_t */ uint16_t _reserved = 0; /** * Exchange to bind to * @var ShortString */ ShortString _destination; /** * Exchange which is bound * @var ShortString */ ShortString _source; /** * Routing key * @var ShortString */ ShortString _routingKey; /** * contains: nowait do not wait on response * @var booleanset */ BooleanSet _bools; /** * Additional arguments * @var Table */ Table _arguments; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExchangeFrame::fill(buffer); buffer.add(_reserved); _destination.fill(buffer); _source.fill(buffer); _routingKey.fill(buffer); _bools.fill(buffer); _arguments.fill(buffer); } public: /** * Constructor based on incoming data * * @param frame received frame to decode */ ExchangeUnbindFrame(ReceivedFrame &frame) : ExchangeFrame(frame), _reserved(frame.nextUint16()), _destination(frame), _source(frame), _routingKey(frame), _bools(frame), _arguments(frame) {} /** * Constructor for an exchangebindframe * @param destination * @param source * @param routingkey * @param noWait * @param arguments */ ExchangeUnbindFrame(uint16_t channel, const std::string_view &destination, const std::string_view &source, const std::string_view &routingKey, bool noWait, const Table &arguments) : ExchangeFrame(channel, (uint32_t)(destination.length() + source.length() + routingKey.length() + arguments.size() + 6)), // 1 for each string, 1 for booleanset, 2 for deprecated field _destination(destination), _source(source), _routingKey(routingKey), _bools(noWait), _arguments(arguments) {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous without the nowait option return !noWait(); } /** * Get the destination exchange * @return string */ const std::string& destination() const { return _destination; } /** * Get the source exchange * @return string */ const std::string& source() const { return _source; } /** * Get the routing key * @return string */ const std::string& routingkey() const { return _routingKey; } /** * Get the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 40; } /** * Get the additional arguments * @return Table */ const Table& arguments() const { return _arguments; } /** * Get the nowait bool * @return bool */ bool noWait() const { return _bools.get(0); } }; // leave namespace } AMQP-CPP-4.3.27/src/exchangeunbindokframe.h000066400000000000000000000030361470663072600202710ustar00rootroot00000000000000/** * Exchangeunbindokframe.h * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class ExchangeUnbindOKFrame : public ExchangeFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExchangeFrame::fill(buffer); } public: /** * Constructor based on incoming data * * @param frame received frame to decode */ ExchangeUnbindOKFrame(ReceivedFrame &frame) : ExchangeFrame(frame) {} /** * Constructor for an exchangebindframe * @param destination * @param source * @param routingkey * @param noWait * @param arguments */ ExchangeUnbindOKFrame(uint16_t channel) : ExchangeFrame(channel, 0) {} virtual uint16_t methodID() const override { return 51; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report to handler channel->reportSuccess(); // done return true; } }; // end namespace } AMQP-CPP-4.3.27/src/extframe.h000066400000000000000000000071361470663072600155620ustar00rootroot00000000000000/** * ExtFrame.h * * Class describing an AMQP frame. A frame can be encoded into the AMQP * wireframe format, so that it can be sent over an open socket, or it can be * constructed from a buffer containing AMQP wireframe format. * * The ExtFrame is the base class for all other frames, apart from the * protocol-header-frame * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "amqpcpp/frame.h" #include "receivedframe.h" /** * Set up namespace */ namespace AMQP { /** * Base frame class for all frames apart from the protocol header frame */ class ExtFrame : public Frame { protected: /** * The AMQP channel * @var uint16_t */ uint16_t _channel; /** * The payload size of the frame * @var uint32_t */ uint32_t _size; /** * Constructor for an AMQP Frame * * The constructor is protected as you're not supposed * * @param channel channel we're working on * @param size size of the payload */ ExtFrame(uint16_t channel, uint32_t size) : _channel(channel), _size(size) {} /** * Constructor based on a received not-yet-recognized frame * @param frame the received frame */ ExtFrame(ReceivedFrame &frame) : _channel(frame.channel()), _size(frame.payloadSize()) {} /** * Modify the size of the frame * This method is called from derived classes when properties in it are * changed that force the frame to get a different size * @param change change in size */ void modifySize(int32_t change) { // change payload size _size += change; } /** * Virtual method that must be implemented by sub classes to fill the output buffer * @param buffer */ virtual void fill(OutBuffer &buffer) const override { // add type, channel id and size buffer.add(type()); buffer.add(_channel); buffer.add(_size); } public: /** * Destruct frame */ virtual ~ExtFrame() {} /** * The channel this message was sent on */ uint16_t channel() const { return _channel; } /** * Size of the header * @return uint32_t */ uint32_t headerSize() const { // 1 byte for type, 2 bytes for channel, 4 bytes for payload return 7; } /** * Size of the trailer * @return uint32_t */ uint32_t trailerSize() const { // 1 byte for end-of-frame return 1; } /** * return the total size of the frame * @return uint32_t */ virtual uint32_t totalSize() const override { // payload size + size of header and trailer return _size + headerSize() + trailerSize(); } /** * The size of the payload * @return uint32_t */ uint32_t payloadSize() const { return _size; } /** * Get the message type */ virtual uint8_t type() const = 0; /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // make sure compilers dont complain about unused parameters (void) connection; // this is an exception throw ProtocolException("unimplemented frame type " + std::to_string(type())); // unreachable return false; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/field.cpp000066400000000000000000000046441470663072600153660ustar00rootroot00000000000000/** * Field.cpp * * @copyright 2014 - 2020 Copernica BV */ #include "includes.h" /** * Set up namespace */ namespace AMQP { /** * Decode a field by fetching a type and full field from a frame * The returned field is allocated on the heap! * @param frame * @return std::unique_ptr */ std::unique_ptr Field::decode(InBuffer &frame) { // get the type uint8_t type = frame.nextUint8(); // create field based on type switch (type) { // @todo: use std::make_unique when switching to C++14/17/20 case 't': return std::unique_ptr(new BooleanSet(frame)); case 'b': return std::unique_ptr(new Octet(frame)); case 'B': return std::unique_ptr(new UOctet(frame)); case 'U': return std::unique_ptr(new Short(frame)); case 'u': return std::unique_ptr(new UShort(frame)); case 'I': return std::unique_ptr(new Long(frame)); case 'i': return std::unique_ptr(new ULong(frame)); case 'L': return std::unique_ptr(new LongLong(frame)); case 'l': return std::unique_ptr(new ULongLong(frame)); case 'f': return std::unique_ptr(new Float(frame)); case 'd': return std::unique_ptr(new Double(frame)); case 'D': return std::unique_ptr(new DecimalField(frame)); case 's': return std::unique_ptr(new ShortString(frame)); case 'S': return std::unique_ptr(new LongString(frame)); case 'A': return std::unique_ptr(new Array(frame)); case 'T': return std::unique_ptr(new Timestamp(frame)); case 'F': return std::unique_ptr(new Table(frame)); case 'V': return std::unique_ptr(new VoidField(frame)); default: return nullptr; } } /** * Cast to string * @return std::string */ Field::operator const std::string& () const { // static empty string static std::string empty; // return it return empty; } /** * Cast to array * @return Array */ Field::operator const Array& () const { // static empty array static Array empty; // return it return empty; } /** * Cast to object * @return Array */ Field::operator const Table& () const { // static empty table static Table empty; // return it return empty; } /** * End of namespace */ } AMQP-CPP-4.3.27/src/flags.cpp000066400000000000000000000015671470663072600154000ustar00rootroot00000000000000/** * Flags.cpp * * The various flags that are supported * * @copyright 2014 - 2018 Copernica BV */ #include "includes.h" /** * Set up namespace */ namespace AMQP { /** * All bit flags * @var int */ const int durable = 0x1; const int autodelete = 0x2; const int active = 0x4; const int passive = 0x8; const int ifunused = 0x10; const int ifempty = 0x20; const int global = 0x40; const int nolocal = 0x80; const int noack = 0x100; const int exclusive = 0x200; const int nowait = 0x400; const int mandatory = 0x800; const int immediate = 0x1000; const int redelivered = 0x2000; const int multiple = 0x4000; const int requeue = 0x8000; const int internal = 0x10000; /** * Flags for event loops */ const int readable = 0x1; const int writable = 0x2; /** * End of namespace */ } AMQP-CPP-4.3.27/src/headerframe.h000066400000000000000000000025161470663072600162070ustar00rootroot00000000000000/** * Class describing an AMQP header frame * * @copyright 2014, 2015 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "extframe.h" /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class HeaderFrame : public ExtFrame { protected: /** * Construct a header frame * @param channel Channel ID * @param size Payload size */ HeaderFrame(uint16_t channel, uint32_t size) : ExtFrame(channel, size + 2) {} // + size of classID (2bytes) /** * Construct based on incoming data * @param frame Incoming frame */ HeaderFrame(ReceivedFrame &frame) : ExtFrame(frame) {} /** * Encode a header frame to a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base ExtFrame::fill(buffer); // add type buffer.add(classID()); } public: /** * Destructor */ virtual ~HeaderFrame() {} /** * Get the message type * @return uint8_t */ virtual uint8_t type() const override { return 2; } /** * Class id * @return uint16_t */ virtual uint16_t classID() const = 0; }; /** * end namespace */ } AMQP-CPP-4.3.27/src/heartbeatframe.h000066400000000000000000000025061470663072600167150ustar00rootroot00000000000000/** * Class describing an AMQP Heartbeat Frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class HeartbeatFrame : public ExtFrame { public: /** * Construct a heartbeat frame * * @param channel channel identifier * @param payload payload of the body */ HeartbeatFrame() : ExtFrame(0, 0) {} /** * Decode a received frame to a frame * * @param frame received frame to decode * @return shared pointer to newly created frame */ HeartbeatFrame(ReceivedFrame& frame) : ExtFrame(frame) {} /** * Destructor */ virtual ~HeartbeatFrame() {} /** * Return the type of frame * @return uint8_t */ virtual uint8_t type() const override { // the documentation says 4, rabbitMQ sends 8 return 8; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // notify the connection-handler connection->reportHeartbeat(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/inbuffer.cpp000066400000000000000000000076061470663072600161040ustar00rootroot00000000000000/** * InBuffer.cpp * * Implementation of the InBuffer class * * @copyright 2014 - 2020 Copernica BV */ #include "includes.h" #include "buffercheck.h" /** * Set up namespace */ namespace AMQP { /** * Read the next uint8 from the buffer * @param char* buffer buffer to read from * @return uint8_t value read */ uint8_t InBuffer::nextUint8() { // check if there is enough size BufferCheck check(this, 1); // get a byte return _buffer.byte(_skip); } /** * Read the next int8 from the buffer * @param char* buffer buffer to read from * @return int8_t value read */ int8_t InBuffer::nextInt8() { // check if there is enough size BufferCheck check(this, 1); // get a byte return (int8_t)_buffer.byte(_skip); } /** * Read the next uint16_t from the buffer * @return uint16_t value read */ uint16_t InBuffer::nextUint16() { // check if there is enough size BufferCheck check(this, sizeof(uint16_t)); // get two bytes, and convert to host-byte-order uint16_t value; _buffer.copy(_skip, sizeof(uint16_t), &value); return be16toh(value); } /** * Read the next int16_t from the buffer * @return int16_t value read */ int16_t InBuffer::nextInt16() { // check if there is enough size BufferCheck check(this, sizeof(int16_t)); // get two bytes, and convert to host-byte-order int16_t value; _buffer.copy(_skip, sizeof(int16_t), &value); return be16toh(value); } /** * Read the next uint32_t from the buffer * @return uint32_t value read */ uint32_t InBuffer::nextUint32() { // check if there is enough size BufferCheck check(this, sizeof(uint32_t)); // get four bytes, and convert to host-byte-order uint32_t value; _buffer.copy(_skip, sizeof(uint32_t), &value); return be32toh(value); } /** * Read the next int32_t from the buffer * @return uint32_t value read */ int32_t InBuffer::nextInt32() { // check if there is enough size BufferCheck check(this, sizeof(int32_t)); // get four bytes, and convert to host-byte-order int32_t value; _buffer.copy(_skip, sizeof(int32_t), &value); return be32toh(value); } /** * Read the next uint64_t from the buffer * @return uint64_t value read */ uint64_t InBuffer::nextUint64() { // check if there is enough size BufferCheck check(this, sizeof(uint64_t)); // get eight bytes, and convert to host-byte-order uint64_t value; _buffer.copy(_skip, sizeof(uint64_t), &value); return be64toh(value); } /** * Read the next uint64_t from the buffer * @return uint64_t value read */ int64_t InBuffer::nextInt64() { // check if there is enough size BufferCheck check(this, sizeof(int64_t)); // get eight bytes, and convert to host-byte-order int64_t value; _buffer.copy(_skip, sizeof(int64_t), &value); return be64toh(value); } /** * Read a float from the buffer * @return float float read from buffer. */ float InBuffer::nextFloat() { // check if there is enough size BufferCheck check(this, sizeof(float)); // get four bytes float value; _buffer.copy(_skip, sizeof(float), &value); return value; } /** * Read a double from the buffer * @return double double read from buffer */ double InBuffer::nextDouble() { // check if there is enough size BufferCheck check(this, sizeof(double)); // get eight bytes, and convert to host-byte-order double value; _buffer.copy(_skip, sizeof(double), &value); return value; } /** * Get a pointer to the next binary buffer of a certain size * @param size * @return char* */ const char *InBuffer::nextData(size_t size) { // check if there is enough size BufferCheck check(this, size); // get the data return _buffer.data(_skip, size); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/includes.h000066400000000000000000000050751470663072600155550ustar00rootroot00000000000000/** * Includes.h * * The includes that are necessary to compile the AMQP library * This file also holds includes that may not be necessary for including the library * * @documentation private */ // c and c++ dependencies #include #include // TODO cstring #include #include #include #include #include #include #include #include #include #include #include #include // TODO is this needed #include #include // TODO make this nice #ifdef _MSC_VER //not #if defined(_WIN32) || defined(_WIN64) because we have strncasecmp in mingw #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif // forward declarations #include "amqpcpp/classes.h" // utility classes #include "amqpcpp/endian.h" #include "amqpcpp/buffer.h" #include "amqpcpp/bytebuffer.h" #include "amqpcpp/inbuffer.h" #include "amqpcpp/outbuffer.h" #include "amqpcpp/copiedbuffer.h" #include "amqpcpp/watchable.h" #include "amqpcpp/monitor.h" // amqp types #include "amqpcpp/field.h" #include "amqpcpp/numericfield.h" #include "amqpcpp/decimalfield.h" #include "amqpcpp/stringfield.h" #include "amqpcpp/booleanset.h" #include "amqpcpp/voidfield.h" #include "amqpcpp/fieldproxy.h" #include "amqpcpp/table.h" #include "amqpcpp/array.h" // envelope for publishing and consuming #include "amqpcpp/metadata.h" #include "amqpcpp/envelope.h" #include "amqpcpp/message.h" // mid level includes #include "amqpcpp/exchangetype.h" #include "amqpcpp/flags.h" #include "amqpcpp/callbacks.h" #include "amqpcpp/deferred.h" #include "amqpcpp/deferredconsumer.h" #include "amqpcpp/deferredrecall.h" #include "amqpcpp/deferredqueue.h" #include "amqpcpp/deferreddelete.h" #include "amqpcpp/deferredcancel.h" #include "amqpcpp/deferredconfirm.h" #include "amqpcpp/deferredget.h" #include "amqpcpp/channelimpl.h" #include "amqpcpp/channel.h" #include "amqpcpp/throttle.h" #include "amqpcpp/tagger.h" #include "amqpcpp/reliable.h" #include "amqpcpp/login.h" #include "amqpcpp/address.h" #include "amqpcpp/connectionhandler.h" #include "amqpcpp/connectionimpl.h" #include "amqpcpp/connection.h" // classes that are very commonly used #include "amqpcpp/exception.h" #include "amqpcpp/protocolexception.h" #include "amqpcpp/frame.h" #include "extframe.h" #include "methodframe.h" #include "headerframe.h" #include "connectionframe.h" #include "channelframe.h" #include "exchangeframe.h" #include "queueframe.h" #include "basicframe.h" #include "confirmframe.h" #include "transactionframe.h" AMQP-CPP-4.3.27/src/linux_tcp/000077500000000000000000000000001470663072600155745ustar00rootroot00000000000000AMQP-CPP-4.3.27/src/linux_tcp/CMakeLists.txt000066400000000000000000000004401470663072600203320ustar00rootroot00000000000000add_sources( addressinfo.h includes.h openssl.cpp openssl.h pipe.h sslconnected.h sslcontext.h sslhandshake.h sslwrapper.h tcpclosed.h tcpconnected.h tcpconnection.cpp tcpinbuffer.h tcpoutbuffer.h tcpresolver.h tcpstate.h ) AMQP-CPP-4.3.27/src/linux_tcp/addressinfo.h000066400000000000000000000113011470663072600202420ustar00rootroot00000000000000/** * AddressInfo.h * * Utility wrapper around "getAddressInfo()" * * @author Emiel Bruijntjes * @copyright 2015 Copernica BV */ /** * Dependencies */ #include #include "connectionorder.h" /** * Include guard */ namespace AMQP { /** * Class definition */ class AddressInfo { private: /** * The addresses * @var struct AddressInfo */ struct addrinfo *_info = nullptr; /** * Vector of addrinfo pointers * @var std::vector */ std::vector _v; /** * Helper function to order the vector of addrinfo based on the ordering received * This may be useful since getaddrinfo is sorting the addresses on proximity * (e.g. https://lists.debian.org/debian-glibc/2007/09/msg00347.html), * @param order */ void reorder(const ConnectionOrder &order) { // witch on order switch (order.order()) { case ConnectionOrder::Order::random: shuffle(); break; case ConnectionOrder::Order::ascending: sort(); break; case ConnectionOrder::Order::descending: sort(); reverse(); break; case ConnectionOrder::Order::reverse: reverse(); break; default: break; } } public: /** * Constructor * @param hostname * @param port * @param order */ AddressInfo(const char *hostname, uint16_t port, const ConnectionOrder &order) { // store portnumber in buffer auto portnumber = std::to_string(port); // info about the lookup struct addrinfo hints; // set everything to zero memset(&hints, 0, sizeof(struct addrinfo)); // set hints hints.ai_family = AF_UNSPEC; // allow IPv4 or IPv6 hints.ai_socktype = SOCK_STREAM; // datagram socket/ // get address of the server auto code = getaddrinfo(hostname, portnumber.data(), &hints, &_info); // was there an error if (code != 0) throw std::runtime_error(gai_strerror(code)); // keep looping for (auto *current = _info; current; current = current->ai_next) { // store in vector _v.push_back(current); } // Order the vector based on the provided ordering reorder(order); } /** * Destructor */ virtual ~AddressInfo() { // free address info freeaddrinfo(_info); } /** * Shuffle the addresses */ void shuffle() { // create a random device for the seed of the random number generator std::random_device rd; // Create the generator std::mt19937 gen(rd()); // shuffle the vector. std::shuffle(_v.begin(), _v.end(), gen); } /** * Order the addresses based on IP */ void sort() { // sort the addresses std::sort(_v.begin(), _v.end(), [](const struct addrinfo *v1, const struct addrinfo *v2) -> bool { // check the IP versions (they must be identical to be comparable) if (v1->ai_family != v2->ai_family) return v1->ai_family < v2->ai_family; // should we compare ipv4 or ipv6? switch (v1->ai_family) { case AF_INET: { // ugly cast auto *a1 = (struct sockaddr_in *)(v1->ai_addr); auto *a2 = (struct sockaddr_in *)(v2->ai_addr); // do the comparison for the addresses return a1->sin_addr.s_addr < a2->sin_addr.s_addr; } case AF_INET6: { // ugly cast auto *a1 = (struct sockaddr_in6 *)(v1->ai_addr); auto *a2 = (struct sockaddr_in6 *)(v2->ai_addr); // do the comparison return memcmp(&a1->sin6_addr, &a2->sin6_addr, sizeof(in6_addr)) < 0; } default: return false; } }); } /** * Reverse the order */ void reverse() { // reverse the order std::reverse(_v.begin(), _v.end()); } /** * Size of the array * @return size_t */ size_t size() const { return _v.size(); } /** * Get reference to struct * @param index * @return struct addrinfo* */ const struct addrinfo *operator[](int index) const { // expose vector return _v[index]; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/connectionorder.h000066400000000000000000000032431470663072600211420ustar00rootroot00000000000000/** * ConnectionOrder.h * * Class that give info on how we want to sellect the connection from a list of IPs * * @author Aljar Meesters * @copyright 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Setup namespace */ namespace AMQP { /** * Class implementation */ class ConnectionOrder { public: /** * Enum class holding the orders we support * - standard: what is used by getaddrinfo, which is proximity based * - reverse: reverse the standard order * - random: random order * - ascending: try the smallest IP address first * - descending: try the largest IP address first * @var enum Order */ enum class Order { standard, reverse, random, ascending, descending}; private: /** * The order for the connection * @var Order */ Order _order; public: /** * Constructor * @var order */ ConnectionOrder(const char *order) : _order(Order::standard) { // Set the orders based on the string if (order == nullptr) {} // first check if the order is not null else if (strcmp(order, "random") == 0) _order = Order::random; else if (strcmp(order, "ascending") == 0 || strcmp(order, "asc") == 0) _order = Order::ascending; else if (strcmp(order, "descending") == 0 || strcmp(order, "desc") == 0 ) _order = Order::descending; } /** * Destructor */ virtual ~ConnectionOrder() = default; /** * Get the order * @return Order */ const Order &order() const { return _order; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/function.h000066400000000000000000000100141470663072600175660ustar00rootroot00000000000000/** * Function.h * * When you want to call a function only if it is already linked into * the program space. you would normally use the dlsym() function for * that. Th Function object in this file is a little more convenient: * * // get the function object * Function func func("example_function"); * * // call the function * int result = func(123); * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include /** * Namespace */ namespace AMQP { /** * Make the Function class a templated class */ template class Function {}; /** * So that we can write a partial specialisation * using a function prototype, indicating the * prototype usable for the given callback */ template class Function { private: /** * Store pointer to the actual dynamically loaded * function. This is done in a union because the * result from a dlsym call is a void pointer which * cannot be legally cast into a function pointer. * * We therefore always set the first type (which is * void* making it legal) and, because all members * in a union share the same address, we can then * read the second type and actually call it. * * @var Callable */ union Callable { /** * Property for getting and setting the return * value from dlsym. This is always a void* * @var void */ void *ptr; /** * Property for executing the mapped function * * @param mixed,... function parameters * @return mixed * * @var function */ T (*func)(Arguments...); /** * Constructor */ Callable() : ptr(nullptr) {} /** * We may be moved * * @param callable the callable we are moving */ Callable(Callable&& callable) : ptr(callable.ptr) { // the other callable no longer has a handle callable.ptr = nullptr; } /** * Copy construtor * @param callable the callable we are moving */ Callable(const Callable &callable) : ptr(callable.ptr) {} /** * Constructor * * @param function the mapped function */ Callable(void *function) : ptr(function) {} } _method; public: /** * Constructor * @param handle Handle to access openssl * @param name Name of the function */ Function(void *handle, const char *name) : _method(dlsym(handle, name)) {} /** * Destructor */ virtual ~Function() {} /** * Is this a valid function or not? * @return bool */ bool valid() const { return _method.ptr != nullptr; } /** * The library object can also be used as in a boolean context, * for that there is an implementation of a casting operator, and * the negate operator * @return bool */ operator bool () const { return valid(); } bool operator ! () const { return !valid(); } /** * Test whether we are a valid object * @param nullptr test if we are null */ bool operator==(std::nullptr_t /* nullptr */) const { return !valid(); } bool operator!=(std::nullptr_t /* nullptr */) const { return valid(); } /** * Invoke the function * * @param mixed,... */ T operator()(Arguments... parameters) const { // check whether we have a valid function if (!valid()) throw std::bad_function_call(); // execute the method given all the parameters return _method.func(std::forward(parameters)...); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/includes.h000066400000000000000000000013031470663072600175500ustar00rootroot00000000000000/** * Includes.h * * The includes that are necessary to compile the optional TCP part of the AMQP library * This file also holds includes that may not be necessary for including the library * * @documentation private */ // include files from main library #include "../includes.h" // c and c++ dependencies #include #include #include #include #include // utility classes #include "amqpcpp/linux_tcp/tcpdefines.h" // mid level includes #include "amqpcpp/linux_tcp/tcpparent.h" #include "amqpcpp/linux_tcp/tcphandler.h" #include "amqpcpp/linux_tcp/tcpconnection.h" // classes that are very commonly used #include "addressinfo.h" AMQP-CPP-4.3.27/src/linux_tcp/openssl.cpp000066400000000000000000000210771470663072600177720ustar00rootroot00000000000000/** * OpenSSL.cpp * * Implementation file for the openssl.h header file * * @copyright 2018 Copernica BV */ /** * Dependencies */ #include "openssl.h" #include "function.h" #include "amqpcpp/openssl.h" /** * The handle to access openssl (the result of dlopen("openssl.so")) * By default we set this to RTLD_DEFAULT, so that AMQP-CPP checks the internal process space */ static void *handle = RTLD_DEFAULT; /** * Just the AMQP namespace */ namespace AMQP { /** * Function to set the openssl handle * @param ptr */ void openssl(void *ptr) { // store handle handle = ptr; } /** * Begin of AMQP::OpenSSL namespace */ namespace OpenSSL { /** * Is the openssl library loaded? * @return bool */ bool valid() { // create a function static Function func(handle, "SSL_CTX_new"); // we need a library return func; } /** * Get the SSL_METHOD for outgoing connections * @return SSL_METHOD * */ const SSL_METHOD *TLS_client_method() { // create a function that loads the method static Function func(handle, "TLS_client_method"); // call the openssl function if (func) return func(); // older openssl libraries do not have this function, so we try to load an other function static Function old(handle, "SSLv23_client_method"); // call the old one return old(); } /** * Create new SSL context * @param method SSL_METHOD can be of the following types: TLS_method(), TLS_server_method(), TLS_client_method() * @return pointer to object */ SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) { // create a function static Function func(handle, "SSL_CTX_new"); // call the openssl function return func(method); } /** * Read data from an ssl socket * @param ssl ssl structure * @param buf buffer to read into * @param num size of buffer * @return int number of bytes read */ int SSL_read(SSL *ssl, void *buf, int num) { // create a function static Function func(handle, "SSL_read"); // call the openssl function return func(ssl, buf, num); } /** * Write data to an ssl socket * @param ssl ssl structure * @param buf buffer to write * @param num size of buffer * @return int number of bytes written */ int SSL_write(SSL *ssl, const void *buf, int num) { // create a function static Function func(handle, "SSL_write"); // call the openssl function return func(ssl, buf, num); } /** * Connect the SSL object with a file descriptor * @param ssl SSL object * @param fd file descriptor * @return int wether the operation succeeded or not */ int SSL_set_fd(SSL *ssl, int fd) { // create a function static Function func(handle, "SSL_set_fd"); // call the openssl function return func(ssl, fd); } /** * The number of bytes availabe in the ssl struct that have been read * from the socket, but that have not been returned the SSL_read() * @param ssl SSL object * @return int number of bytes */ int SSL_pending(const SSL *ssl) { // create a function static Function func(handle, "SSL_pending"); // call the openssl function return func(ssl); } /** * Free an allocated ssl context * @param ctx */ void SSL_CTX_free(SSL_CTX *ctx) { // create a function static Function func(handle, "SSL_CTX_free"); // call the openssl function return func(ctx); } /** * Free an allocated SSL structure * @param ssl SSL object to be freed * @return int wether the operation succeeded or not */ void SSL_free(SSL *ssl) { // create a function static Function func(handle, "SSL_free"); // call the openssl function return func(ssl); } /** * Create a new SSL structure for a connection * @param ctx SSL context object * @return SSL the created SSL oject based on th context */ SSL *SSL_new(SSL_CTX *ctx) { // create a function static Function func(handle, "SSL_new"); // call the openssl function return func(ctx); } /** * Increment refcount for a ssl structure * @param ctx SSL structure * @return int 1 for success, 0 for failure */ int SSL_up_ref(SSL *ssl) { // create a function static Function func(handle, "SSL_up_ref"); // call the openssl function if it exists if (func) return func(ssl); // @todo use our own implementation return 0; } /** * Shut down a TLS/SSL shut down * @param ssl SSL object to terminate * @return int returns diagnostic values */ int SSL_shutdown(SSL *ssl) { // create a function static Function func(handle, "SSL_shutdown"); // call the openssl function return func(ssl); } /** * Prepare SSL object to work in client or server mode * @param ssl SSL object to set connect state on */ void SSL_set_connect_state(SSL *ssl) { // create a function static Function func(handle, "SSL_set_connect_state"); // call the openssl function func(ssl); } /** * Perform a TLS/SSL handshake * @param ssl SSL object * @return int returns diagnostic values */ int SSL_do_handshake(SSL *ssl) { // create a function static Function func(handle, "SSL_do_handshake"); // call the openssl function return func(ssl); } /** * Obtain shutdown statue for TLS/SSL I/O operation * @param ssl SSL object * @return int returns error values */ int SSL_get_shutdown(const SSL *ssl) { // create a function static Function func(handle, "SSL_get_shutdown"); // call the openssl function return func(ssl); } /** * Obtain result code for TLS/SSL I/O operation * @param ssl SSL object * @param ret the returned diagnostic value of SSL calls * @return int returns error values */ int SSL_get_error(const SSL *ssl, int ret) { // create a function static Function func(handle, "SSL_get_error"); // call the openssl function return func(ssl, ret); } /** * Internal handling function for a ssl context * @param ssl ssl context * @param cmd command * @param larg first arg * @param parg second arg * @return long */ long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg) { // create a function static Function func(handle, "SSL_ctrl"); // call the openssl function return func(ssl, cmd, larg, parg); } /** * Set the certificate file to be used by the connection * @param ssl ssl structure * @param file filename * @param type type of file * @return int */ int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { // create a function static Function func(handle, "SSL_use_certificate_file"); // call the openssl function return func(ssl, file, type); } /** * Control the SSL context * @param ctx * @param cmd * @param larg * @param parg * @return long */ long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) { // create a function static Function func(handle, "SSL_CTX_ctrl"); // call the openssl function return func(ctx, cmd, larg, parg); } /** * Specify that the default location from which CA certificates are loaded * should be used. * @param ctx */ int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) { // the actual function static Function func(handle, "SSL_CTX_set_default_verify_paths"); // call actual function return func(ctx); } /** * Clear the SSL error queue * @return void */ void ERR_clear_error() { // create a function static Function func(handle, "ERR_clear_error"); // call the openssl function return func(); } /** * Print errors via a callback * @param cb * @param u */ void ERR_print_errors_cb(int (*cb)(const char *str, size_t len, void *u), void *u) { // the actual function static Function func(handle, "ERR_print_errors_cb"); // call the openssl function func(cb, u); } /** * End of namespace */ }} AMQP-CPP-4.3.27/src/linux_tcp/openssl.h000066400000000000000000000032211470663072600174260ustar00rootroot00000000000000/** * OpenSSL.h * * Header file in which we list all openssl functions in our own namespace * that we call instead of the actual openssl functions. This allows us to * intercept the calls and forward them to a dynamically loaded namespace * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include /** * Begin of namespace */ namespace AMQP { namespace OpenSSL { /** * Function to check if openssl is loaded * @return bool */ bool valid(); /** * List of all wrapper methods that are in use inside AMQP-CPP */ const SSL_METHOD *TLS_client_method(); SSL_CTX *SSL_CTX_new(const SSL_METHOD *method); SSL *SSL_new(SSL_CTX *ctx); int SSL_do_handshake(SSL *ssl); int SSL_read(SSL *ssl, void *buf, int num); int SSL_write(SSL *ssl, const void *buf, int num); int SSL_shutdown(SSL *ssl); int SSL_pending(const SSL *ssl); int SSL_set_fd(SSL *ssl, int fd); int SSL_get_shutdown(const SSL *ssl); int SSL_get_error(const SSL *ssl, int ret); int SSL_use_certificate_file(SSL *ssl, const char *file, int type); void SSL_set_connect_state(SSL *ssl); void SSL_CTX_free(SSL_CTX *ctx); void SSL_free(SSL *ssl); long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg); long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg); int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); void ERR_clear_error(void); void ERR_print_errors_cb(int (*cb)(const char *str, size_t len, void *u), void *u); /** * End of namespace */ }} AMQP-CPP-4.3.27/src/linux_tcp/pipe.h000066400000000000000000000031701470663072600167030ustar00rootroot00000000000000/** * Pipe.h * * Pipe of two filedescriptors, used to communicate between master and child thread * * @author Emiel Bruijntjes * @copyright 2015 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include /** * Set up namespace */ namespace AMQP { /** * Class definition */ class Pipe { private: /** * The two filedescriptors that make up the pipe * @var int[] */ int _fds[2]; public: /** * Constructor */ Pipe() { // construct the pipe #ifdef _GNU_SOURCE if (pipe2(_fds, O_CLOEXEC) == 0) return; #else if ( pipe(_fds) == 0 && fcntl(_fds[0], F_SETFD, FD_CLOEXEC) == 0 && fcntl(_fds[1], F_SETFD, FD_CLOEXEC) == 0 ) return; #endif // something went wrong throw std::runtime_error(strerror(errno)); } /** * Destructor */ virtual ~Pipe() { // close the two filedescriptors close(_fds[0]); close(_fds[1]); } /** * Expose the filedescriptors * @return int */ int in() const { return _fds[0]; } int out() const { return _fds[1]; } /** * Notify the pipe, so that the other thread wakes up * @return bool success/failure (errno is set on failure) */ bool notify() { // one byte to send char byte = 0; // send one byte over the pipe - this will wake up the other thread return write(_fds[1], &byte, 1) == 1; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/poll.h000066400000000000000000000034071470663072600167170ustar00rootroot00000000000000/** * Poll.h * * Class to check or wait for a socket to become readable and/or writable * * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Includes */ #include /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class Poll { private: /** * Set with just one filedescriptor * @var fd_set */ pollfd _fd; /** * The socket filedescriptor * @var int */ int _socket; public: /** * Constructor * @param fd the filedescriptor that we're waiting on */ Poll(int fd) { // set the fd _fd.fd = fd; } /** * No copying * @param that */ Poll(const Poll &that) = delete; /** * Destructor */ virtual ~Poll() = default; /** * Check if a file descriptor is readable. * @return bool */ bool readable() { // check for readable _fd.events = POLLIN; _fd.revents = 0; // poll the fd with no timeout return poll(&_fd, 1, 0) > 0; } /** * Wait until the filedescriptor becomes writable * @return bool */ bool writable() { // check for readable _fd.events = POLLOUT; _fd.revents = 0; // poll the fd with no timeout return poll(&_fd, 1, 0) > 0; } /** * Wait until a filedescriptor becomes active (readable or writable) * @return bool */ bool active() { // check for readable _fd.events = POLLIN | POLLOUT; _fd.revents = 0; // poll the fd with no timeout return poll(&_fd, 1, 0) > 0; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/sslconnected.h000066400000000000000000000326671470663072600204470ustar00rootroot00000000000000/** * SslConnected.h * * The actual tcp connection over SSL * * @copyright 2018 copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "tcpoutbuffer.h" #include "tcpinbuffer.h" #include "poll.h" #include "sslwrapper.h" #include "sslshutdown.h" #include "sslerrorprinter.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class SslConnected : public TcpExtState { private: /** * The SSL structure * @var SslWrapper */ SslWrapper _ssl; /** * The outgoing buffer * @var TcpBuffer */ TcpOutBuffer _out; /** * The incoming buffer * @var TcpInBuffer */ TcpInBuffer _in; /** * Are we now busy with sending or receiving? * @var enum */ enum State { state_idle, state_sending, state_receiving, state_error } _state; /** * Should we close the connection after we've finished all operations? * @var bool */ bool _closed = false; /** * Cached reallocation instruction * @var size_t */ size_t _reallocate = 0; /** * Proceed with the next operation after the previous operation was * a success, possibly changing the filedescriptor-monitor * @return TcpState* */ TcpState *proceed() { // if we still have an outgoing buffer we want to send out data if (_out) { // let's wait until the socket becomes writable _parent->onIdle(this, _socket, readable | writable); } else if (_closed) { // start the state that closes the connection return new SslShutdown(this, std::move(_ssl)); } else { // let's wait until the socket becomes readable _parent->onIdle(this, _socket, readable); } // done return this; } /** * Method to repeat the previous call * @param monitor monitor to check if connection object still exists * @param state the state that we were in * @param result result of an earlier SSL_get_error call * @return TcpState* */ TcpState *repeat(const Monitor &monitor, enum State state, int error) { // if we are not able to repeat the call, we are in an error state and should tear down the connection if (!repeat(state, error)) return monitor.valid() ? new TcpClosed(this) : nullptr; // if the socket was closed in the meantime and we are not sending anything any more, we should initialize the shutdown sequence if (_closed && _state == state_idle) return new SslShutdown(this, std::move(_ssl)); // otherwise, we just continue as we were, since the calls should be repeated in the future else return this; } /** * Method to repeat the previous call, which has then been * @param state the state that we were in * @param result result of an earlier SSL_get_error call * @return bool */ bool repeat(enum State state, int error) { // check the error switch (error) { case SSL_ERROR_WANT_READ: // remember state _state = state; // the operation must be repeated when readable _parent->onIdle(this, _socket, readable); // allow chaining return true; case SSL_ERROR_WANT_WRITE: // remember state _state = state; // wait until socket becomes writable again _parent->onIdle(this, _socket, readable | writable); // we are done return true; // this case doesn't actually happen when repeat is called, since it will only be returned when // the result > 0 and therefore there is no error. it is here just to be sure. case SSL_ERROR_NONE: // we're ready for the next instruction from userspace _state = state_idle; // if we still have an outgoing buffer we want to send out data, otherwise we just read _parent->onIdle(this, _socket, _out ? readable | writable : readable); // nothing is wrong, we are done return true; default: // we are now in an error state _state = state_error; { // get a human-readable error string const SslErrorPrinter message{error}; // report an error to user-space _parent->onError(this, message.data()); } // ssl level error, we have to tear down the tcp connection return false; } } /** * Parse the received buffer * @param monitor object to check the existance of the connection object * @param size number of bytes available * @return TcpState */ TcpState *parse(const Monitor &monitor, size_t size) { // we need a local copy of the buffer - because it is possible that "this" // object gets destructed halfway through the call to the parse() method TcpInBuffer buffer(std::move(_in)); // parse the buffer auto processed = _parent->onReceived(this, buffer); // "this" could be removed by now, check this if (!monitor.valid()) return nullptr; // shrink buffer buffer.shrink(processed); // restore the buffer as member _in = std::move(buffer); // do we have to reallocate? if (!_reallocate) return this; // reallocate the buffer _in.reallocate(_reallocate); // we can remove the reallocate instruction _reallocate = 0; // done return this; } /** * Check if the socket is readable * @return bool */ bool isReadable() const { // object to poll a socket Poll poll(_socket); // check if socket is readable return poll.readable(); } /** * Check if the socket is writable * @return bool */ bool isWritable() const { // object to poll a socket Poll poll(_socket); // check if socket is writable return poll.writable(); } /** * Perform a write operation * @param monitor object to check the existance of the connection object * @return TcpState* */ TcpState *write(const Monitor &monitor) { // assume default state _state = state_idle; // because the output buffer contains a lot of small buffers, we can do multiple // operations till the buffer is empty (but only if the socket is not also // readable, because then we want to read that data first instead of endless writes do { // try to send more data from the outgoing buffer auto result = _out.sendto(_ssl); // we may have to repeat the operation on failure if (result > 0) continue; // check for error auto error = OpenSSL::SSL_get_error(_ssl, result); // the operation failed, we may have to repeat our call return repeat(monitor, state_sending, error); } while (_out && !isReadable()); // proceed with the read operation or the event loop return isReadable() ? receive(monitor) : proceed(); } /** * Perform a receive operation * @param monitor object to check the existance of the connection object * @return TcpState */ TcpState *receive(const Monitor &monitor) { // we are going to check for errors after the openssl operations, so we make // sure that the error queue is currently completely empty OpenSSL::ERR_clear_error(); // start a loop do { // assume default state _state = state_idle; // read data from ssl into the buffer auto result = _in.receivefrom(_ssl, _parent->expected()); // if this is a failure, we are going to repeat the operation if (result <= 0) return repeat(monitor, state_receiving, OpenSSL::SSL_get_error(_ssl, result)); // go process the received data auto *nextstate = parse(monitor, result); // leap out if we moved to a different state if (nextstate != this) return nextstate; // the call to userspace might have turned the object in sending-state, which // means that we must stop the receiving process if (_state != state_idle) return this; } while (OpenSSL::SSL_pending(_ssl) > 0); // proceed with the write operation or the event loop return _out && isWritable() ? write(monitor) : proceed(); } public: /** * Constructor * @param state The previous state * @param ssl The SSL structure * @param buffer The buffer that was already built */ SslConnected(TcpExtState *state, SslWrapper &&ssl, TcpOutBuffer &&buffer) : TcpExtState(state), _ssl(std::move(ssl)), _out(std::move(buffer)), _in(4096), _state(_out ? state_sending : state_idle) { // tell the handler to monitor the socket if there is an out _parent->onIdle(this, _socket, _state == state_sending ? readable | writable : readable); } /** * Destructor */ virtual ~SslConnected() noexcept = default; /** * Number of bytes in the outgoing buffer * @return std::size_t */ virtual std::size_t queued() const override { return _out.size(); } /** * Process the filedescriptor in the object * @param monitor Object that can be used to find out if connection object is still alive * @param fd The filedescriptor that is active * @param flags AMQP::readable and/or AMQP::writable * @return New implementation object */ virtual TcpState *process(const Monitor &monitor, int fd, int flags) override { // the socket must be the one this connection writes to if (fd != _socket) return this; // if we were busy with a write operation, we have to repeat that if (_state == state_sending) return write(monitor); // same is true for read operations, they should also be repeated if (_state == state_receiving) return receive(monitor); // if we are in an error state, we close the tcp connection if (_state == state_error) return new TcpClosed(this); // if the socket is readable, we are going to receive data if (flags & readable) return receive(monitor); // socket is not readable (so it must be writable), do we have data to write? if (_out) return write(monitor); // the only scenario in which we can end up here is the socket should be // closed, but instead of moving to the shutdown-state right, we call proceed() // because that function is a little more careful return proceed(); } /** * Send data over the connection * @param buffer buffer to send * @param size size of the buffer */ virtual void send(const char *buffer, size_t size) override { // do nothing if already busy closing if (_closed) return; // if we're not idle, we can just add bytes to the buffer and we're done if (_state != state_idle || _out) return _out.add(buffer, size); // clear ssl-level error OpenSSL::ERR_clear_error(); // get the result int result = OpenSSL::SSL_write(_ssl, buffer, size); // if the result is larger than zero, we are successful if (result > 0) return; // check for error auto error = OpenSSL::SSL_get_error(_ssl, result); // put the data in the outgoing buffer _out.add(buffer, size); // the operation failed, we may have to repeat our call. this may detect that // ssl is in an error state, however that is ok because it will set an internal // state to the error state so that on the next calls to state-changing objects, // the tcp socket will be torn down if (repeat(state_sending, error)) return; // the repeat call failed, so we are going to find out with a readable file descriptor _parent->onIdle(this, _socket, readable); } /** * Gracefully close the connection */ virtual void close() override { // remember that the object is going to be closed _closed = true; // if the previous operation is still in progress we can wait for that if (_state != state_idle) return; // let's wait until the socket becomes writable (because then we can start the shutdown) _parent->onIdle(this, _socket, readable | writable); } /** * Install max-frame size * @param heartbeat suggested heartbeat */ virtual void maxframe(size_t maxframe) override { // remember that we have to reallocate (_in member can not be accessed because it is moved away) _reallocate = maxframe; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/sslcontext.h000066400000000000000000000030701470663072600201530ustar00rootroot00000000000000/** * SslContext.h * * Class to create and maintain a tcp ssl context * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class SslContext { private: /** * The wrapped context * @var SSL_CTX */ SSL_CTX *_ctx; public: /** * Constructor * @param method the connect method to use * @throws std::runtime_error */ SslContext(const SSL_METHOD *method) : _ctx(OpenSSL::SSL_CTX_new(method)) { // report error if (_ctx == nullptr) throw std::runtime_error("failed to construct ssl context"); // set the context to accept a moving write buffer. note that SSL_CTX_set_mode is a macro // that expands to SSL_CTX_ctrl, so that is the real function that is used OpenSSL::SSL_CTX_set_mode(_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); } /** * Copy constructor is delete because the object is refcounted, * and we do not have a decent way to update the refcount in openssl 1.0 * @param that */ SslContext(SslContext &that) = delete; /** * Destructor */ virtual ~SslContext() { // free resource (this updates the refcount -1, and may destruct it) OpenSSL::SSL_CTX_free(_ctx); } /** * Cast to the actual context * @return SSL_CTX * */ operator SSL_CTX * () { return _ctx; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/sslerrorprinter.cpp000066400000000000000000000051321470663072600215600ustar00rootroot00000000000000/** * SslErrorPrinter.cpp * * Implementation of SslErrorPrinter * * @copyright 2021 copernica BV */ /** * Dependencies */ #include "sslerrorprinter.h" #include /** * Begin namespace */ namespace AMQP { /** * Callback used for ERR_print_errors_cb * @param str The string * @param len The length * @param ctx The context (this ptr) * @return always 0 to signal to OpenSSL to continue */ int sslerrorprintercallback(const char *str, size_t len, void *ctx) { // Cast to ourselves and store the error line. OpenSSL adds a newline to every error line. static_cast(ctx)->_message.append(str, len); // continue with the next message return 0; } /** * Constructor * @param retval return value of SSL_get_error (must be a proper error) */ SslErrorPrinter::SslErrorPrinter(int retval) { // check the return value of the SSL_get_error function, which has a very unfortunate name. switch (retval) { // It can be a syscall error. case SSL_ERROR_SYSCALL: // The SSL_ERROR_SYSCALL with errno value of 0 indicates unexpected // EOF from the peer. This will be properly reported as SSL_ERROR_SSL // with reason code SSL_R_UNEXPECTED_EOF_WHILE_READING in the // OpenSSL 3.0 release because it is truly a TLS protocol error to // terminate the connection without a SSL_shutdown(). if (errno == 0) _message = "SSL_R_UNEXPECTED_EOF_WHILE_READING"; // Otherwise we ask the OS for a description of the error. else _message = ::strerror(errno); // done break; // It can be an error in OpenSSL. In that case the error stack contains // more information. The documentation notes: if this error occurs then // no further I/O operations should be performed on the connection and // SSL_shutdown() must not be called. case SSL_ERROR_SSL: // collect all error lines OpenSSL::ERR_print_errors_cb(&sslerrorprintercallback, this); // remove the last newline if (!_message.empty() && _message.back() == '\n') _message.pop_back(); // done break; default: // we don't know what kind of error this is _message = "unknown ssl error"; // done break; } } /** * data ptr (guaranteed null-terminated) * @return const char * */ const char *SslErrorPrinter::data() const noexcept { return _message.data(); } /** * length of the string * @return size_t */ std::size_t SslErrorPrinter::size() const noexcept { return _message.size(); } /** * End of namespace AMQP */ } AMQP-CPP-4.3.27/src/linux_tcp/sslerrorprinter.h000066400000000000000000000025131470663072600212250ustar00rootroot00000000000000/** * SslErrorPrinter.h * * Flushes the SSL error stack to a string. * You can get at the string content via the data() and size() methods. * After constructing an instance of this class, the SSL error stack * is empty. * * @copyright 2021 copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "openssl.h" #include #include /** * Begin namespace */ namespace AMQP { /** * Class declaration */ class SslErrorPrinter final { public: /** * Constructor * @param retval return value of SSL_get_error (must be a proper error) */ SslErrorPrinter(int retval); /** * data ptr (guaranteed null-terminated) * @return const char * */ const char *data() const noexcept; /** * length of the string * @return size_t */ std::size_t size() const noexcept; private: /** * The error message * @var std::string */ std::string _message; /** * Callback used for ERR_print_errors_cb * @param str The string * @param len The length * @param ctx The context (this ptr) * @return always 0 to signal to OpenSSL to continue */ friend int sslerrorprintercallback(const char *str, size_t len, void *ctx); }; /** * End of namespace AMQP */ } AMQP-CPP-4.3.27/src/linux_tcp/sslhandshake.h000066400000000000000000000136331470663072600204230ustar00rootroot00000000000000/** * SslHandshake.h * * Implementation of the TCP state that is responsible for setting * up the STARTTLS handshake. * * @copyright 2018 - 2021 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "tcpoutbuffer.h" #include "sslconnected.h" #include "poll.h" #include "sslwrapper.h" #include "sslcontext.h" #include "sslerrorprinter.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class SslHandshake : public TcpExtState { private: /** * Ssl context * @var SslContext */ SslContext _ctx; /** * SSL structure * @var SslWrapper */ SslWrapper _ssl; /** * The outgoing buffer * @var TcpOutBuffer */ TcpOutBuffer _out; /** * Report a new state * @param monitor * @return TcpState */ TcpState *nextstate(const Monitor &monitor) { // check if the handler allows the connection bool allowed = _parent->onSecured(this, _ssl); // leap out if the user space function destructed the object if (!monitor.valid()) return nullptr; // if connection is allowed, we move to the next state if (allowed) return new SslConnected(this, std::move(_ssl), std::move(_out)); // report that the connection is broken _parent->onError(this, "TLS connection has been rejected"); // the onError method could have destructed this object if (!monitor.valid()) return nullptr; // shutdown the connection return new SslShutdown(this, std::move(_ssl)); } /** * Helper method to report an error * @param monitor * @param retval return value of SSL_get_error * @return TcpState* */ TcpState *reportError(const Monitor &monitor, int retval) { // extract a human-readable error string const SslErrorPrinter message{retval}; // we have an error - report this to the user _parent->onError(this, message.data()); // stop if connection is gone if (!monitor.valid()) return nullptr; // done, shutdown the tcp connection return new TcpClosed(this); } /** * Proceed with the handshake * @param events the events to wait for on the socket * @return TcpState */ TcpState *proceed(int events) { // tell the handler that we want to listen for certain events _parent->onIdle(this, _socket, events); // allow chaining return this; } public: /** * Constructor * @param state Earlier state * @param hostname The hostname to connect to * @param context SSL context * @param buffer The buffer that was already built * @throws std::runtime_error */ SslHandshake(TcpExtState *state, const std::string &hostname, TcpOutBuffer &&buffer) : TcpExtState(state), _ctx(OpenSSL::TLS_client_method()), _ssl(_ctx), _out(std::move(buffer)) { // use the default directories for verifying certificates OpenSSL::SSL_CTX_set_default_verify_paths(_ctx); // we will be using the ssl context as a client OpenSSL::SSL_set_connect_state(_ssl); // associate domain name with the connection OpenSSL::SSL_ctrl(_ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void *)hostname.data()); // associate the ssl context with the socket filedescriptor if (OpenSSL::SSL_set_fd(_ssl, _socket) == 0) throw std::runtime_error("failed to associate filedescriptor with ssl socket"); // we allow userspace to make changes to the SSL structure if (!_parent->onSecuring(this, _ssl)) throw std::runtime_error("failed to initialize SSL structure in user space"); // we are going to wait until the socket becomes writable before we start the handshake _parent->onIdle(this, _socket, writable); } /** * Destructor */ virtual ~SslHandshake() noexcept = default; /** * Number of bytes in the outgoing buffer * @return std::size_t */ virtual std::size_t queued() const override { return _out.size(); } /** * Process the filedescriptor in the object * @param monitor Object to check if connection still exists * @param fd Filedescriptor that is active * @param flags AMQP::readable and/or AMQP::writable * @return New state object */ virtual TcpState *process(const Monitor &monitor, int fd, int flags) override { // must be the socket if (fd != _socket) return this; // we are going to check for errors after the openssl operations, so we make // sure that the error queue is currently completely empty OpenSSL::ERR_clear_error(); // start the ssl handshake int result = OpenSSL::SSL_do_handshake(_ssl); // if the connection succeeds, we can move to the ssl-connected state if (result == 1) return nextstate(monitor); // error was returned, so we must investigate what is going on auto error = OpenSSL::SSL_get_error(_ssl, result); // check the error switch (error) { case SSL_ERROR_WANT_READ: return proceed(readable); case SSL_ERROR_WANT_WRITE: return proceed(readable | writable); default: return reportError(monitor, error); } } /** * Send data over the connection * @param buffer buffer to send * @param size size of the buffer */ virtual void send(const char *buffer, size_t size) override { // the handshake is still busy, outgoing data must be cached _out.add(buffer, size); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/sslshutdown.h000066400000000000000000000070711470663072600203470ustar00rootroot00000000000000/** * SslShutdown.h * * Class that takes care of the final handshake to close a SSL connection * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class SslShutdown : public TcpExtState { private: /** * The SSL context * @var SslWrapper */ SslWrapper _ssl; /** * Proceed with the next operation after the previous operation was * a success, possibly changing the filedescriptor-monitor * @param monitor object to check if connection still exists * @return TcpState* */ virtual TcpState *proceed(const Monitor &monitor) { // next state is to close the connection return new TcpClosed(this); } /** * Method to repeat the previous call * @param monitor object to check if connection still exists * @param result result of an earlier openssl operation * @return TcpState* */ TcpState *repeat(const Monitor &monitor, int result) { // error was returned, so we must investigate what is going on auto error = OpenSSL::SSL_get_error(_ssl, result); // check the error switch (error) { case SSL_ERROR_WANT_READ: // the operation must be repeated when readable _parent->onIdle(this, _socket, readable); return this; case SSL_ERROR_WANT_WRITE: // wait until socket becomes writable again _parent->onIdle(this, _socket, readable | writable); return this; default: // go to the final state (if not yet disconnected) return proceed(monitor); } } public: /** * Constructor * @param state Previous state * @param ssl The SSL structure */ SslShutdown(TcpExtState *state, SslWrapper &&ssl) : TcpExtState(state), _ssl(std::move(ssl)) { // wait until the socket is accessible _parent->onIdle(this, _socket, readable | writable); } /** * No copying * @param that */ SslShutdown(const SslShutdown &that) = delete; /** * Destructor */ virtual ~SslShutdown() noexcept = default; /** * Process the filedescriptor in the object * @param monitor Object to check if connection still exists * @param fd The filedescriptor that is active * @param flags AMQP::readable and/or AMQP::writable * @return New implementation object */ virtual TcpState *process(const Monitor &monitor, int fd, int flags) override { // the socket must be the one this connection writes to if (fd != _socket) return this; // we are going to check for errors after the openssl operations, so we make // sure that the error queue is currently completely empty OpenSSL::ERR_clear_error(); // close the connection auto result = OpenSSL::SSL_shutdown(_ssl); // on result==0 we need an additional call while (result == 0) result = OpenSSL::SSL_shutdown(_ssl); // if this is a success, we can proceed with the event loop if (result > 0) return proceed(monitor); // the operation failed, we may have to repeat our call else return repeat(monitor, result); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/sslwrapper.h000066400000000000000000000030261470663072600201500ustar00rootroot00000000000000/** * SslWrapper.h * * Wrapper around a SSL pointer * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class SslWrapper { private: /** * The wrapped object * @var SSL* */ SSL *_ssl; public: /** * Constructor * @param ctx * @param file */ SslWrapper(SSL_CTX *ctx) : _ssl(OpenSSL::SSL_new(ctx)) { // report error if (_ssl == nullptr) throw std::runtime_error("failed to construct ssl structure"); //OpenSSL::SSL_use_certificate_file(_ssl, "cert.pem", SSL_FILETYPE_PEM); } /** * Copy constructor is removed because openssl 1.0 has no way to up refcount * (otherwise we could be safely copying objects around) * @param that */ SslWrapper(const SslWrapper &that) = delete; /** * Move constructor * @param that */ SslWrapper(SslWrapper &&that) : _ssl(that._ssl) { // invalidate other object that._ssl = nullptr; } /** * Destructor */ virtual ~SslWrapper() { // do nothing if already moved away if (_ssl == nullptr) return; // destruct object OpenSSL::SSL_free(_ssl); } /** * Cast to the SSL* * @return SSL * */ operator SSL * () const { return _ssl; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpclosed.h000066400000000000000000000016431470663072600177310ustar00rootroot00000000000000/** * TcpClosed.h * * Class that is used when the TCP connection ends up in a closed state * * @author Emiel Bruijntjes * @copyright 2015 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * Class definition */ class TcpClosed : public TcpState { public: /** * Constructor * @param parent The parent object */ TcpClosed(TcpParent *parent) : TcpState(parent) {} /** * Constructor * @param state The to-be-copied state */ TcpClosed(const TcpState *state) : TcpState(state) {} /** * Destructor */ virtual ~TcpClosed() noexcept = default; /** * Is this a closed / dead state? * @return bool */ virtual bool closed() const override { return true; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpconnected.h000066400000000000000000000167001470663072600204220ustar00rootroot00000000000000/** * TcpConnected.h * * The actual tcp connection - this is the "_impl" of a tcp-connection after * the hostname was resolved into an IP address * * @author Emiel Bruijntjes * @copyright 2015 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "tcpoutbuffer.h" #include "tcpinbuffer.h" #include "tcpextstate.h" #include "poll.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class TcpConnected : public TcpExtState { private: /** * The outgoing buffer * @var TcpOutBuffer */ TcpOutBuffer _out; /** * An incoming buffer * @var TcpInBuffer */ TcpInBuffer _in; /** * Cached reallocation instruction * @var size_t */ size_t _reallocate = 0; /** * Did the user ask to elegantly close the connection? * @var bool */ bool _closed = false; /** * Helper method to report an error * This method also assumes that result <= 0 is an error, unless the operation can be retried. * @param result Result of the previous call to read() or write() * @return bool Was an error reported? */ bool reportError(ssize_t result) { // positive return values are no errors if (result > 0) return false; // some errors are ok and do not (necessarily) mean that we're disconnected if (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) return false; // tell the parent that it failed (but not if the connection was elegantly closed) if (!_closed) _parent->onError(this, "connection lost"); // done return true; } /** * Construct the final state * @param monitor Object that monitors whether connection still exists * @return TcpState* */ TcpState *finalState(const Monitor &monitor) { // if the object is still in a valid state, we can treat the connection // as closed otherwise there is no point in moving to a next state return monitor.valid() ? new TcpClosed(this) : nullptr; } public: /** * Constructor * @param state The previous state * @param buffer The buffer that was already built */ TcpConnected(TcpExtState *state, TcpOutBuffer &&buffer) : TcpExtState(state), _out(std::move(buffer)), _in(4096) { // if there is already an output buffer, we have to send out that first if (_out) _out.sendto(_socket); // tell the handler to monitor the socket, if there is an out _parent->onIdle(this, _socket, _out ? readable | writable : readable); } /** * Destructor */ virtual ~TcpConnected() noexcept = default; /** * Number of bytes in the outgoing buffer * @return std::size_t */ virtual std::size_t queued() const override { return _out.size(); } /** * Process the filedescriptor in the object * @param monitor Monitor to check if the object is still alive * @param fd Filedescriptor that is active * @param flags AMQP::readable and/or AMQP::writable * @return New state object */ virtual TcpState *process(const Monitor &monitor, int fd, int flags) override { // must be the socket if (fd != _socket) return this; // can we write more data to the socket? if (flags & writable) { // send out the buffered data auto result = _out.sendto(_socket); // are we in an error state? (0 bytes sent is weird, but not necessarily an error) if (result < 0 && reportError(result)) return finalState(monitor); // if we still have a buffer, we keep on monitoring if (_out) return this; // if we do not expect to send more data, we can close the connection for writing if (_closed) shutdown(_socket, SHUT_WR); // check for readability (to find more data, or to be notified that connection is gone) _parent->onIdle(this, _socket, readable); } // should we check for readability too? if (flags & readable) { // read data from buffer ssize_t result = _in.receivefrom(_socket, _parent->expected()); // did we encounter end-of-file or are we in an error state? if (reportError(result)) return finalState(monitor); // we need a local copy of the buffer - because it is possible that "this" // object gets destructed halfway through the call to the parse() method TcpInBuffer buffer(std::move(_in)); // parse the buffer auto processed = _parent->onReceived(this, buffer); // "this" could be removed by now, check this if (!monitor.valid()) return nullptr; // shrink buffer buffer.shrink(processed); // restore the buffer as member _in = std::move(buffer); // do we have to reallocate? if (_reallocate) _in.reallocate(_reallocate); // we can remove the reallocate instruction _reallocate = 0; } // keep same object return this; } /** * Send data over the connection * @param buffer buffer to send * @param size size of the buffer */ virtual void send(const char *buffer, size_t size) override { // we stop sending when connection is closed if (_closed) return; // is there already a buffer of data that can not be sent? if (_out) return _out.add(buffer, size); // there is no buffer, send the data right away auto result = ::send(_socket, buffer, size, AMQP_CPP_MSG_NOSIGNAL); // number of bytes sent size_t bytes = result < 0 ? 0 : result; // ok if all data was sent if (bytes >= size) return; // add the data to the buffer _out.add(buffer + bytes, size - bytes); // start monitoring the socket to find out when it is writable _parent->onIdle(this, _socket, readable | writable); } /** * Gracefully close the connection */ virtual void close() override { // do nothing if already closed if (_closed) return; // remember that the connection is closed _closed = true; // wait until the outgoing buffer is all gone if (_out) return; // we will shutdown the socket in a very elegant way, we notify the peer // that we will not be sending out more write operations shutdown(_socket, SHUT_WR); // we still monitor the socket for readability to see if our close call was // confirmed by the peer _parent->onIdle(this, _socket, readable); } /** * Install max-frame size * @param heartbeat suggested heartbeat */ virtual void maxframe(size_t maxframe) override { // remember that we have to reallocate (_in member can not be accessed because it is moved away) _reallocate = maxframe; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpconnection.cpp000066400000000000000000000220131470663072600211440ustar00rootroot00000000000000/** * TcpConnection.cpp * * Implementation file for the TCP connection * * @author Emiel Bruijntjes * @copyright 2015 - 2020 Copernica BV */ /** * Dependencies */ #include "includes.h" #include "tcpresolver.h" #include "tcpstate.h" /** * Set up namespace */ namespace AMQP { /** * Constructor * @param handler User implemented handler object * @param hostname The address to connect to */ TcpConnection::TcpConnection(TcpHandler *handler, const Address &address) : _handler(handler), _state(new TcpResolver(this, address.hostname(), address.port(), address.secure(), address.option("connectTimeout", 5), ConnectionOrder(address.option("connectionOrder")))), _connection(this, address.login(), address.vhost()) { // tell the handler _handler->onAttached(this); } /** * Destructor */ TcpConnection::~TcpConnection() noexcept { // if there is still a file descriptor open, we must explicitly unwatch it, otherwise it could // remain open (tcp connection is forcefully closed). since we assume the handler is in scope for // the complete lifetime of the tcpconnection, we make one last call to notify it to unmonitor // the file descriptor. if (fileno() >= 0) _handler->monitor(this, fileno(), 0); // When the object is destructed, the _state-pointer will also destruct, resulting // in some final calls back to us to inform that the connection has indeed been closed. // This normally results in calls back to user-space (via the _handler pointer) but // since user-space is apparently no longer interested in the TcpConnection (why would // it otherwise prematurely destruct the object?) we reset the handler-pointer to // prevent that such calls back to userspace take place _handler = nullptr; } /** * The filedescriptor that is used for this connection * @return int */ int TcpConnection::fileno() const { // pass on to the state object return _state->fileno(); } /** * The number of outgoing bytes queued on this connection. * @return std::size_t */ std::size_t TcpConnection::queued() const { return _state->queued(); } /** * Is the connection closed and full dead? The entire TCP connection has been discarded. * @return bool */ bool TcpConnection::closed() const { return _state->closed(); } /** * Process the TCP connection * This method should be called when the filedescriptor that is registered * in the event loop becomes active. You should pass in a flag holding the * flags AMQP::readable or AMQP::writable to indicate whether the descriptor * was readable or writable, or bitwise-or if it was both * @param fd The filedescriptor that became readable or writable * @param events What sort of events occured? */ void TcpConnection::process(int fd, int flags) { // monitor the object for destruction, because you never know what the user Monitor monitor(this); // remember the old state auto *oldstate = _state.get(); // pass on the the state, that returns a new impl auto *newstate = _state->process(monitor, fd, flags); // if the state did not change, we do not have to update a member, // when the newstate is nullptr, the object is (being) destructed // and we do not have to do anything else either if (newstate == nullptr || newstate == oldstate) return; // wrap the new state in a unique-ptr so that so that the old state // is not destructed before the new one is assigned std::unique_ptr ptr(newstate); // swap the two pointers (this ensures that the last operation of this // method is to destruct the old state, which possible results in calls // to user-space and the destruction of "this" _state.swap(ptr); } /** * Close the connection. * Warning: this potentially directly calls several handlers (onError, onLost, onDetached) * @return bool */ bool TcpConnection::close(bool immediate) { // if no immediate disconnect is needed, we can simply start the closing handshake if (!immediate) return _connection.close(); // failing the connection could destruct "this" Monitor monitor(this); // fail the connection / report the error to user-space bool failed = _connection.fail("connection prematurely closed by client"); // stop if object was destructed if (!monitor.valid()) return true; // tell the handler that the connection was closed if (failed && _handler) _handler->onError(this, "connection prematurely closed by client"); // stop if object was destructed if (!monitor.valid()) return true; // also call the lost handler, we have now lost the connection from this state (since we force-closed). // this makes sure the onLost and onDetached is properly called. onLost(_state.get()); // stop if object was destructed if (!monitor.valid()) return true; // change the state _state.reset(new TcpClosed(this)); // done, we return true because the connection is closed return true; } /** * Method that is called when the RabbitMQ server and your client application * exchange some properties that describe their identity. * @param connection The connection about which information is exchanged * @param server Properties sent by the server * @param client Properties that are to be sent back */ void TcpConnection::onProperties(Connection *connection, const Table &server, Table &client) { // tell the handler if (_handler) _handler->onProperties(this, server, client); } /** * Method that is called when the heartbeat frequency is negotiated. * @param connection The connection that suggested a heartbeat interval * @param interval The suggested interval from the server * @return uint16_t The interval to use */ uint16_t TcpConnection::onNegotiate(Connection *connection, uint16_t interval) { // tell the max-frame size _state->maxframe(connection->maxFrame()); // tell the handler return _handler ? _handler->onNegotiate(this, interval) : interval; } /** * Method that is called by the connection when data needs to be sent over the network * @param connection The connection that created this output * @param buffer Data to send * @param size Size of the buffer */ void TcpConnection::onData(Connection *connection, const char *buffer, size_t size) { // send the data over the connection _state->send(buffer, size); } /** * Method called when the AMQP connection ends up in an error state * @param connection The connection that entered the error state * @param message Error message */ void TcpConnection::onError(Connection *connection, const char *message) { // monitor to check if "this" is destructed Monitor monitor(this); // tell this to the user if (_handler) _handler->onError(this, message); // object could be destructed by user-space if (!monitor.valid()) return; // tell the state that the connection should be closed asap _state->close(); } /** * Method that is called when the connection was closed. * @param connection The connection that was closed and that is now unusable */ void TcpConnection::onClosed(Connection *connection) { // tell the state that the connection should be closed asap _state->close(); // report to the handler _handler->onClosed(this); } /** * Method that is called when an error occurs (the connection is lost) * @param state * @param error * @param connected */ void TcpConnection::onError(TcpState *state, const char *message, bool connected) { // if user-space is no longer interested in this object, the rest of the code is pointless here if (_handler == nullptr) return; // monitor to check if all operations are active Monitor monitor(this); // if there are still pending operations, they should be reported as error bool failed = _connection.fail(message); // stop if object was destructed if (!monitor.valid()) return; // tell the handler if (failed) _handler->onError(this, message); // if the object is still connected, we only have to report the error and // we wait for the subsequent call to the onLost() method if (connected || !monitor.valid()) return; // tell the handler that no further events will be fired _handler->onDetached(this); } /** * Method to be called when it is detected that the connection was lost * @param state */ void TcpConnection::onLost(TcpState *state) { // if user-space is no longer interested in this object, the rest of the code is pointless here if (_handler == nullptr) return; // monitor to check if "this" is destructed Monitor monitor(this); // tell the handler _handler->onLost(this); // leap out if object was destructed if (!monitor.valid()) return; // tell the handler that no further events will be fired _handler->onDetached(this); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpextstate.h000066400000000000000000000035111470663072600203150ustar00rootroot00000000000000/** * TcpExtState.h * * Extended state that also contains the socket filedescriptor * * @author Emiel Bruijntjes * @copyright 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class TcpExtState : public TcpState { protected: /** * The filedescriptor * @var int */ int _socket; /** * Clean-up the socket, and call the onClosed() method */ void cleanup() { // do nothing if no longer connected if (_socket < 0) return; // tell handler that the socket is idle and we're no longer interested in events _parent->onIdle(this, _socket, 0); // close the socket ::close(_socket); // forget the socket _socket = -1; // tell the handler that the connection is now lost _parent->onLost(this); } protected: /** * Constructor * @param parent */ TcpExtState(TcpParent *parent) : TcpState(parent), _socket(-1) {} /** * Constructor * @param state */ TcpExtState(TcpExtState *state) : TcpState(state), _socket(state->_socket) { // invalidate the other state state->_socket = -1; } public: /** * No copying * @param that */ TcpExtState(const TcpExtState &that) = delete; /** * Destructor */ virtual ~TcpExtState() { // cleanup the socket cleanup(); } /** * The filedescriptor of this connection * @return int */ virtual int fileno() const override { // expose the socket return _socket; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpinbuffer.h000066400000000000000000000067341470663072600202660ustar00rootroot00000000000000/** * TcpInBuffer.h * * Implementation of byte byte-buffer used for incoming frames * * @author Emiel Bruijntjes * @copyright 2016 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Beginnig of namespace */ namespace AMQP { /** * Class definition */ class TcpInBuffer : public ByteBuffer { public: /** * Constructor * Note that we pass 0 to the constructor because the buffer seems to be empty * @param size initial size to allocated */ TcpInBuffer(size_t size) : ByteBuffer((char *)malloc(size), 0) {} /** * No copy'ing * @param that object to copy */ TcpInBuffer(const TcpInBuffer &that) = delete; /** * Move constructor * @param that */ TcpInBuffer(TcpInBuffer &&that) : ByteBuffer(std::move(that)) {} /** * Destructor */ virtual ~TcpInBuffer() { // free memory if (_data) free((void *)_data); } /** * Move assignment operator * @param that */ TcpInBuffer &operator=(TcpInBuffer &&that) { // skip self-assignment if (this == &that) return *this; // call base ByteBuffer::operator=(std::move(that)); // done return *this; } /** * Reallocate date * @param size */ void reallocate(size_t size) { // update data _data = (char *)realloc((void *)_data, size); } /** * Receive data from a socket * @param socket socket to read from * @param expected number of bytes that the library expects * @return ssize_t */ ssize_t receivefrom(int socket, uint32_t expected) { // find out how many bytes are available uint32_t available = 0; // check the number of bytes that are available if (ioctl(socket, FIONREAD, &available) != 0) return -1; // if no bytes are available, it could mean that the connection was closed // by the remote client, so we do have to call read() anyway, assume a default buffer if (available == 0) available = 1; // number of bytes to read size_t bytes = std::min((uint32_t)(expected - _size), available); // read data into the buffer auto result = read(socket, (void *)(_data + _size), bytes); // update total buffer size if (result > 0) _size += result; // done return result; } /** * Receive data from a socket * @param ssl ssl wrapped socket to read from * @param expected number of bytes that the library expects * @return ssize_t */ ssize_t receivefrom(SSL *ssl, uint32_t expected) { // number of bytes to that still fit in the buffer size_t bytes = expected - _size; // read data auto result = OpenSSL::SSL_read(ssl, (void *)(_data + _size), bytes); // update total buffer size on success if (result > 0) _size += result; // done return result; } /** * Shrink the buffer (in practice this is always called with the full buffer size) * @param size */ void shrink(size_t size) { // update size _size -= size; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpoutbuffer.h000066400000000000000000000166531470663072600204700ustar00rootroot00000000000000/** * TcpOutBuffer.h * * When data could not be sent out immediately, it is buffered in a temporary * output buffer. This is the implementation of that buffer * * @author Emiel Bruijntjes * @copyright 2015 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "openssl.h" /** * FIONREAD on Solaris is defined elsewhere */ #ifdef __sun #include #endif /** * Set up namespace */ namespace AMQP { /** * Class definition */ class TcpOutBuffer { private: /** * All output buffers * @var std::deque */ mutable std::deque> _buffers; /** * Number of bytes in first buffer that is no longer in use * @var size_t */ size_t _skip = 0; /** * Total number of bytes in the buffer * @var size_t */ size_t _size = 0; public: /** * Regular constructor */ TcpOutBuffer() {} /** * No copy'ing allowed * @param that */ TcpOutBuffer(const TcpOutBuffer &that) = delete; /** * Move operator * @param that */ TcpOutBuffer(TcpOutBuffer &&that) : _buffers(std::move(that._buffers)), _skip(that._skip), _size(that._size) { // reset other object that._skip = 0; that._size = 0; } /** * Move assignment operator * @param that */ TcpOutBuffer &operator=(TcpOutBuffer &&that) { // skip self-assignment if (this == &that) return *this; // swap buffers _buffers.swap(that._buffers); // swap integers std::swap(_skip, that._skip); std::swap(_size, that._size); // done return *this; } /** * Does the buffer exist (is it non-empty) * @return bool */ operator bool () const { // there must be a size return _size > 0; } /** * Is the buffer empty * @return bool */ bool operator!() const { // size should be zero return _size == 0; } /** * Total size of the buffer * @return size_t */ size_t size() const { // this simply is a member return _size; } /** * Add data to the buffer * @param buffer * @param size */ void add(const char *buffer, size_t size) { // add element _buffers.emplace_back(buffer, buffer + size); // update total size _size += size; } /** * Shrink the buffer with a number of bytes * @param toremove */ void shrink(size_t toremove) { // are we removing everything? if (toremove >= _size) { // reset all _buffers.clear(); _skip = _size = 0; } else { // keep looping while (toremove > 0) { // access to the first buffer const auto &first = _buffers.front(); // actual used bytes in first buffer size_t bytes = first.size() - _skip; // can we remove the first buffer completely? if (toremove >= bytes) { // we're going to remove the first item, update sizes _size -= bytes; _skip = 0; // number of bytes that still have to be removed toremove -= bytes; // remove first buffer _buffers.pop_front(); } else { // we should remove the first buffer partially _skip += toremove; _size -= toremove; // done toremove = 0; } } } } /** * Clear the buffer */ void clear() { // clear all buffers _buffers.clear(); // reset members _skip = _size = 0; } /** * Fill an iovec buffer * @param buffers the buffers to be filled * @param count number of buffers available * @return size_t the number of buffers that were filled */ size_t fill(struct iovec buffers[], size_t count) const { // index counter size_t index = 0; // iterate over the buffers for (const auto &str : _buffers) { // fill buffer buffers[index].iov_base = (void *)(index == 0 ? str.data() + _skip : str.data()); buffers[index].iov_len = index == 0 ? str.size() - _skip : str.size(); // update counter for next iteration if (++index >= count) return count; } // done return index; } /** * Send the buffer to a socket * @param socket the socket to send data to * @return ssize_t number of bytes sent (or the same result as sendmsg() in case of an error) */ ssize_t sendto(int socket) { // total number of bytes written ssize_t total = 0; // keep looping while (_size > 0) { // we're going to fill a lot of buffers (64 should normally be enough) struct iovec buffer[64]; // create the message header struct msghdr header; // make sure the members of the header are empty memset(&header, 0, sizeof(header)); // save the buffers in the message header header.msg_iov = buffer; header.msg_iovlen = fill(buffer, 64); // do nothing if no buffers were filled if (header.msg_iovlen == 0) break; // send the data auto result = sendmsg(socket, &header, AMQP_CPP_MSG_NOSIGNAL); // skip on error, or when nothing was written if (result <= 0) return total > 0 ? total : result; // shrink the buffer shrink(result); // update total number of bytes written total += result; } // done return total; } /** * Send the buffer to an SSL connection * @param ssl the ssl context to send data to * @return ssize_t number of bytes sent, or the return value of ssl_write */ ssize_t sendto(SSL *ssl) { // we're going to fill a lot of buffers (for ssl only one buffer at a time can be sent) struct iovec buffer[1]; // fill the buffers, and leap out if there is no data auto buffers = fill(buffer, 1); // just to be sure we do this check if (buffers == 0) return 0; // make sure that the error queue is currently completely empty, so the error queue can be checked OpenSSL::ERR_clear_error(); // send the data auto result = OpenSSL::SSL_write(ssl, buffer[0].iov_base, buffer[0].iov_len); // on success we shrink the buffer if (result > 0) shrink(result); // done return result; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpresolver.h000066400000000000000000000232271470663072600203230ustar00rootroot00000000000000/** * TcpResolver.h * * Class that is used for the DNS lookup of the hostname of the RabbitMQ * server, and to make the initial connection * * @author Emiel Bruijntjes * @copyright 2015 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "pipe.h" #include "tcpstate.h" #include "tcpclosed.h" #include "tcpconnected.h" #include "openssl.h" #include "sslhandshake.h" #include #include #include /** * Set up namespace */ namespace AMQP { /** * Class definition */ class TcpResolver : public TcpExtState { private: /** * The hostname that we're trying to resolve * @var std::string */ std::string _hostname; /** * Should we be using a secure connection? * @var bool */ bool _secure; /** * The portnumber to connect to * @var uint16_t */ uint16_t _port; /** * Timeout for the connect call in seconds. * @var int */ int _timeout; /** * A pipe that is used to send back the socket that is connected to RabbitMQ * @var Pipe */ Pipe _pipe; /** * Possible error that occured * @var std::string */ std::string _error; /** * Data that was sent to the connection, while busy resolving the hostname * @var TcpBuffer */ TcpOutBuffer _buffer; /** * Thread in which the DNS lookup occurs * @var std::thread */ std::thread _thread; /** * How should the addresses be ordered when we want to connect * @var ConnectionOrdre */ ConnectionOrder _order; /** * Run the thread */ void run() { // prevent exceptions try { // check if we support openssl in the first place if (_secure && !OpenSSL::valid()) throw std::runtime_error("Secure connection cannot be established: libssl.so cannot be loaded"); // get address info AddressInfo addresses(_hostname.data(), _port, _order); // the pollfd structure, needed for poll() pollfd fd; // iterate over the addresses for (size_t i = 0; i < addresses.size(); ++i) { // create the socket _socket = socket(addresses[i]->ai_family, addresses[i]->ai_socktype, addresses[i]->ai_protocol); // move on on failure if (_socket < 0) continue; // turn socket into a non-blocking socket and set the close-on-exec bit fcntl(_socket, F_SETFL, O_NONBLOCK | O_CLOEXEC); // we set the 'keepalive' option so that we automatically detect if the peer is dead int keepalive = 1; // set the keepalive option setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); // try to connect non-blocking if (connect(_socket, addresses[i]->ai_addr, addresses[i]->ai_addrlen) == 0) break; // set the struct members fd.fd = _socket; fd.events = POLLOUT; fd.revents = 0; // the return code int ret = 0; // keep looping until we get a 'final' result do { // perform the poll, with a very long time to allow the event to occur ret = poll(&fd, 1, _timeout * 1000); // we want to retry if the call was interrupted by a signal } while (ret == -1 && errno == EINTR); // log the error for the time being if (ret == 0) _error = "connection timed out"; // otherwise, select might've failed else if (ret < 0) _error = strerror(errno); // otherwise the connect failed/succeeded else { // the error int err = 0; socklen_t len = 4; // get the options getsockopt(_socket, SOL_SOCKET, SO_ERROR, &err, &len); // if the error is zero, we break, socket is now valid if (err == 0) break; // set the error with the value _error = strerror(err); } // close socket because connect failed ::close(_socket); // socket no longer is valid _socket = -1; } // connection succeeded, mark socket as non-blocking if (_socket >= 0) { // we want to enable "nodelay" on sockets (otherwise all send operations are s-l-o-w int optval = 1; // set the option setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int)); #ifdef AMQP_CPP_USE_SO_NOSIGPIPE set_sockopt_nosigpipe(_socket); #endif } } catch (const std::runtime_error &error) { // address could not be resolved, we ignore this for now, but store the error _error = error.what(); } // notify the master thread by sending a byte over the pipe, store error if this fails if (!_pipe.notify()) _error = strerror(errno); } public: /** * Constructor * @param parent Parent connection object * @param hostname The hostname for the lookup * @param portnumber The portnumber for the lookup * @param secure Do we need a secure tls connection when ready? * @param timeout timeout per connection attempt * @param order How should we oreder the addresses of the host to connect to */ TcpResolver(TcpParent *parent, std::string hostname, uint16_t port, bool secure, int timeout, const ConnectionOrder &order) : TcpExtState(parent), _hostname(std::move(hostname)), _secure(secure), _port(port), _timeout(timeout), _order(order) { // tell the event loop to monitor the filedescriptor of the pipe parent->onIdle(this, _pipe.in(), readable); // we can now start the thread (must be started after filedescriptor is monitored!) std::thread thread(std::bind(&TcpResolver::run, this)); // store thread in member _thread.swap(thread); } /** * Destructor */ virtual ~TcpResolver() noexcept { // stop monitoring the pipe filedescriptor _parent->onIdle(this, _pipe.in(), 0); // wait for the thread to be ready if (_thread.joinable()) _thread.join(); } /** * Number of bytes in the outgoing buffer * @return std::size_t */ virtual std::size_t queued() const override { return _buffer.size(); } /** * Proceed to the next state * @param monitor object that checks if the connection still exists * @return TcpState * */ TcpState *proceed(const Monitor &monitor) { // prevent exceptions try { // the other thread must be ready by now, so we join it, which also guarantees us // that the memory of the two threads have been synchronized (without this call // it is possible that the memory of the threads have not been synchronized, and // _socket has not yet been set) _thread.join(); // socket should be connected by now if (_socket < 0) throw std::runtime_error(_error.data()); // report that the network-layer is connected _parent->onConnected(this); // handler callback might have destroyed connection if (!monitor.valid()) return nullptr; // if we need a secure connection, we move to the tls handshake (this could throw) if (_secure) return new SslHandshake(this, std::move(_hostname), std::move(_buffer)); // otherwise we have a valid regular tcp connection else return new TcpConnected(this, std::move(_buffer)); } catch (const std::runtime_error &error) { // report error _parent->onError(this, error.what(), false); // handler callback might have destroyed connection if (!monitor.valid()) return nullptr; // create dummy implementation return new TcpClosed(this); } } /** * Wait for the resolver to be ready * @param monitor Object to check if connection still exists * @param fd The filedescriptor that is active * @param flags Flags to indicate that fd is readable and/or writable * @return New implementation object */ virtual TcpState *process(const Monitor &monitor, int fd, int flags) override { // only works if the incoming pipe is readable if (fd != _pipe.in() || !(flags & readable)) return this; // proceed to the next state return proceed(monitor); } /** * Send data over the connection * @param buffer buffer to send * @param size size of the buffer */ virtual void send(const char *buffer, size_t size) override { // add data to buffer _buffer.add(buffer, size); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/linux_tcp/tcpstate.h000066400000000000000000000052621470663072600176010ustar00rootroot00000000000000/** * TcpState.h * * Base class / interface of the various states of the TCP connection * * @author Emiel Bruijntjes * @copyright 2015 - 2018 Copernica BV */ /** * Include guard */ #pragma once /** * Set up namespace */ namespace AMQP { /** * Class definition */ class TcpState { protected: /** * Parent object that constructed the state * @var TcpParent */ TcpParent *_parent; protected: /** * Protected constructor * @param parent The parent object * @param handler User-supplied handler class */ TcpState(TcpParent *parent) : _parent(parent) {} /** * Protected "copy" constructor * @param state Original TcpState object */ TcpState(const TcpState *state) : _parent(state->_parent) {} public: /** * Virtual destructor */ virtual ~TcpState() = default; /** * The filedescriptor of this connection * @return int */ virtual int fileno() const { return -1; } /** * The number of outgoing bytes queued on this connection. * @return size_t */ virtual std::size_t queued() const { return 0; } /** * Is this a closed / dead state? * @return bool */ virtual bool closed() const { return false; } /** * Process the filedescriptor in the object * * This method should return the handler object that will be responsible for * all future readable/writable events for the file descriptor, or nullptr * if the underlying connection object has already been destructed by the * user and it would be pointless to set up a new handler. * * @param monitor Monitor that can be used to check if the tcp connection is still alive * @param fd The filedescriptor that is active * @param flags AMQP::readable and/or AMQP::writable * @return New implementation object */ virtual TcpState *process(const Monitor &monitor, int fd, int flags) { // default implementation does nothing and preserves same implementation return this; } /** * Send data over the connection * @param buffer Buffer to send * @param size Size of the buffer */ virtual void send(const char *buffer, size_t size) { // default does nothing } /** * Gracefully start closing the connection */ virtual void close() {} /** * Install max-frame size * @param heartbeat suggested heartbeat */ virtual void maxframe(size_t maxframe) {} }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/methodframe.h000066400000000000000000000043201470663072600162320ustar00rootroot00000000000000/** * Class describing an AMQP method frame * * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include "extframe.h" /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class MethodFrame : public ExtFrame { protected: /** * Constructor for a methodFrame * * @param channel channel we're working on * @param size size of the frame. */ MethodFrame(uint16_t channel, uint32_t size) : ExtFrame(channel, size + 4) {} // size of classID and methodID /** * Load a method from from a received frame * @param frame The received frame */ MethodFrame(ReceivedFrame &frame) : ExtFrame(frame) {} /** * Fill an output buffer * @param buffer */ virtual void fill(OutBuffer &buffer) const override { // call base ExtFrame::fill(buffer); // add type buffer.add(classID()); buffer.add(methodID()); } public: /** * Destructor */ virtual ~MethodFrame() {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { return true; } /** * Get the message type * @return uint8_t */ virtual uint8_t type() const override { return 1; } /** * Class id * @return uint16_t */ virtual uint16_t classID() const = 0; /** * Method id * @return uint16_t */ virtual uint16_t methodID() const = 0; /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // make sure compilers dont complain about unused parameters (void) connection; // this is an exception throw ProtocolException("unimplemented frame type " + std::to_string(type()) + " class " + std::to_string(classID()) + " method " + std::to_string(methodID())); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/passthroughbuffer.h000066400000000000000000000052631470663072600175070ustar00rootroot00000000000000/** * PassthroughBuffer.h * * If we can immediately pass on data to the TCP layer, we use a passthrough * buffer so that we do not have to dynamically allocate memory * * @copyright 2017 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #include #include "amqpcpp/frame.h" /** * Set up namespace */ namespace AMQP { /** * Class definition */ class PassthroughBuffer : public OutBuffer { private: /** * The actual buffer * @var const char * */ char _buffer[4096]; /** * Current size of the buffer * @var size_t */ size_t _size = 0; /** * Connection object (needs to be passed to the handler) * @var Connection */ Connection *_connection; /** * Object that will send the data when the buffer is full * @var ConnectionHandler */ ConnectionHandler *_handler; /** * Flush the object */ void flush() { // notify the handler _handler->onData(_connection, _buffer, _size); // all data has been sent _size = 0; } protected: /** * The method that adds the actual data * @param data * @param size */ virtual void append(const void *data, size_t size) override { // flush existing buffers if data would not fit if (_size > 0 && _size + size > 4096) flush(); // if data would not fit anyway, we send it immediately if (size > 4096) return _handler->onData(_connection, (const char *)data, size); // copy data into the buffer memcpy(_buffer + _size, data, size); // update the size _size += size; } public: /** * Constructor * @param connection * @param handler * @param frame */ PassthroughBuffer(Connection *connection, ConnectionHandler *handler, const Frame &frame) : _connection(connection), _handler(handler) { // tell the frame to fill this buffer frame.fill(*this); // append an end of frame byte (but not when still negotiating the protocol) if (frame.needsSeparator()) add((uint8_t)206); } /** * No copying, because that would be too expensive * @param that */ PassthroughBuffer(const PassthroughBuffer &that) = delete; /** * Moving is also not necessary * @param that */ PassthroughBuffer(PassthroughBuffer &&that) = delete; /** * Destructor */ virtual ~PassthroughBuffer() { // pass data to the handler if (_size > 0) flush(); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/platformname.h000066400000000000000000000021451470663072600164270ustar00rootroot00000000000000/** * PlatformName.h * * Class to extract the platform name (operating system, etc) * * @author Emiel Bruijntjes * @copyright 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class PlatformName { private: /** * The string holding all info * @var std::string */ std::string _value; public: /** * Constructor */ PlatformName() { // all information struct utsname info; // retrieve system info if (uname(&info) != 0) return; // add all info _value.append(info.sysname).append(" ").append(info.nodename).append(" ").append(info.release).append(" ").append(info.version); } /** * Destructor */ virtual ~PlatformName() = default; /** * Cast to a const char * * @return const char * */ operator const char * () const { return _value.data(); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/programname.h000066400000000000000000000033271470663072600162550ustar00rootroot00000000000000/** * ProgramName.h * * Helper class that detects the name of the program * * @author Emiel Bruijntjes * @copyright 2023 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include #if defined(_WIN32) || defined(_WIN64) #include "Windows.h" #define PATH_MAX MAX_PATH #else #include #endif /** * Begin of namespace */ namespace AMQP { /** * Class definition */ class ProgramName { private: /** * Path of the program * @var char[] */ char _path[PATH_MAX]; /** * Is the _path valid? * @var bool */ bool _valid; public: /** * Constructor */ ProgramName() { #if defined(_WIN32) || defined(_WIN64) // the the auto size = GetModuleFileNameA(NULL, _path, PATH_MAX); // -1 is returned on error, otherwise the size _valid = size >= 0; // set trailing null byte _path[size == PATH_MAX ? PATH_MAX-1 : size] = '\0'; #else // read the link target auto size = readlink("/proc/self/exe", _path, PATH_MAX); // -1 is returned on error, otherwise the size _valid = size >= 0; // set trailing null byte _path[size == PATH_MAX ? PATH_MAX-1 : size] = '\0'; #endif } /** * Destructor */ virtual ~ProgramName() = default; /** * Cast to a const char * * @return const char * */ operator const char * () const { // empty string when not valid if (!_valid) return ""; // return path to executable return _path; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/protocolheaderframe.h000066400000000000000000000061131470663072600177660ustar00rootroot00000000000000/** * Class describing an AMQP protocol header frame * * @copyright 2014 Copernica BV */ /** * Set up header */ namespace AMQP { /** * Clas definition */ class ProtocolHeaderFrame : public Frame { private: /** * Protocol name (should be null-ended string "AMQP") * @var char* */ const char *_protocol; /** * The protocol major version, should be 0 * @var uint8_t */ uint8_t _protocolIDMajor; /** * The protocol minor version, should be 9 * @var uint8_t */ uint8_t _protocolIDMinor; /** * the protocol revision, should be 1 * @var uint8_t */ uint8_t _revision; /** * Encode a frame on a stringbuffer * * @param buffer buffer to encode frame to */ virtual void fill(OutBuffer& buffer) const override { // there is no base, add fields buffer.add(_protocol, 5); buffer.add(_protocolIDMajor); buffer.add(_protocolIDMinor); buffer.add(_revision); } public: /** * Construct based on incoming frame * * @param frame received frame to decode */ ProtocolHeaderFrame(ReceivedFrame& frame) : _protocol(frame.nextData(5)), _protocolIDMajor(frame.nextUint8()), _protocolIDMinor(frame.nextUint8()), _revision(frame.nextUint8()) {} /** * Construct a new ProtocolHeaderFrame object * * @param protocolIDMajor protocol major version, should be 0 * @param protocolIDMinor protocol minor version, should be 9 * @param revision revision of version, should be 1 */ ProtocolHeaderFrame(uint8_t protocolIDMajor = 0,uint8_t protocolIDMinor = 9,uint8_t revision = 1) : _protocol("AMQP"), _protocolIDMajor(protocolIDMajor), _protocolIDMinor(protocolIDMinor), _revision(revision) {} /** * Destructor */ virtual ~ProtocolHeaderFrame() { } /** * return the total size of the frame * @return uint32_t */ virtual uint32_t totalSize() const override { // include one byte for end of frame delimiter return 8; } /** * return the protocol major version, should be 0 * @return uint8_t */ uint8_t protocolIDMajor() const { return _protocolIDMajor; } /** * return the protocol minor version, should be 9 * @return uint8_t */ uint8_t protocolIDMinor() const { return _protocolIDMinor; } /** * return the protocol revision, should be 1 * @return uint8_t */ uint8_t revision() const { return _revision; } /** * Is this a frame that is part of the connection setup? * @return bool */ virtual bool partOfHandshake() const override { return true; } /** * Does this frame need an end-of-frame seperator? * @return bool */ virtual bool needsSeparator() const override { return false; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/queuebindframe.h000066400000000000000000000074501470663072600167420ustar00rootroot00000000000000/** * Class describing an AMQP queue bind frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class QueueBindFrame : public QueueFrame { private: /** * Deprecated field * @var uint16_t */ uint16_t _deprecated = 0; /** * Queue name * @var ShortString */ ShortString _name; /** * Exchange name * @var ShortString */ ShortString _exchange; /** * Routing key * @var ShortString */ ShortString _routingKey; /** * Do not wait on response * @var BooleanSet */ BooleanSet _noWait; /** * Additional arguments. Implementation dependent. * @var Table */ Table _arguments; protected: /** * Encode the frame into a buffer * * @params buffer buffer to encode frame in to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add fields buffer.add(_deprecated); _name.fill(buffer); _exchange.fill(buffer); _routingKey.fill(buffer); _noWait.fill(buffer); _arguments.fill(buffer); } public: /** * Destructor */ virtual ~QueueBindFrame() {} /** * Construct a queue bind frame * * @param channel channel identifier * @param String name name of the queue * @param String exchange name of the exchange * @param String routingKey the routingKey * @param Bool noWait do not wait for a response * @param Table arguments additional arguments */ QueueBindFrame(uint16_t channel, const std::string_view &name, const std::string_view &exchange, const std::string_view &routingKey = "", bool noWait = false, const Table& arguments = {}) : QueueFrame(channel, (uint32_t)(name.length() + exchange.length() + routingKey.length() + arguments.size() + 6) ), // 3 extra per string, 1 for bools, 2 for deprecated field _name(name), _exchange(exchange), _routingKey(routingKey), _noWait(noWait), _arguments(arguments) {} /** * Constructor based on incoming data * @param frame received frame */ QueueBindFrame(ReceivedFrame &frame) : QueueFrame(frame), _deprecated(frame.nextUint16()), _name(frame), _exchange(frame), _routingKey(frame), _noWait(frame), _arguments(frame) {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous without the nowait option return !noWait(); } /** * Returns the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 20; } /** * returns the queue name * @return string */ const std::string& name() const { return _name; } /** * returns the exchange Name * @return string */ const std::string& exchange() const { return _exchange; } /** * returns the routing key * @return string */ const std::string& routingKey() const { return _routingKey; } /** * returns whether to wait on a response * @return boolean */ bool noWait() const { return _noWait.get(0); } /** * returns the additional arguments. Implementation dependant. * @return Table */ const Table &arguments() const { return _arguments; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queuebindokframe.h000066400000000000000000000030251470663072600172660ustar00rootroot00000000000000/** * Class describing an AMQP queue bind ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class QueueBindOKFrame : public QueueFrame { protected: /** * Fill output buffer * @param buffer */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); } public: /** * Construct a queuebindokframe * * @param channel channel identifier */ QueueBindOKFrame(uint16_t channel) : QueueFrame(channel, 0) {} /** * Constructor based on incoming data * @param frame received frame */ QueueBindOKFrame(ReceivedFrame &frame) : QueueFrame(frame) {} /** * Destructor */ virtual ~QueueBindOKFrame() {} /** * returns the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 21; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report to handler channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queuedeclareframe.h000066400000000000000000000103571470663072600174250ustar00rootroot00000000000000/** * Class describing an AMQP queue declare frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class QueueDeclareFrame : public QueueFrame { private: /** * Field that no longer is in use * @var int16_t */ int16_t _deprecated = 0; /** * The exchange name * @var ShortString */ ShortString _name; /** * Set containing all booleans * 0: passive do not create queue if it does not exist * 1: durable durable queue * 2: exclusive request exclusive queue * 3: auto-delete delete queue if unused * 4: noWait don't wait on response * @var BooleanSet */ BooleanSet _bools; /** * Additional arguments. Implementation dependant. * @var Table */ Table _arguments; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add fields buffer.add(_deprecated); _name.fill(buffer); _bools.fill(buffer); _arguments.fill(buffer); } public: /** * Destructor */ virtual ~QueueDeclareFrame() {} /** * Construct a channel flow frame * * @param channel channel identifier * @param String name Name of the queue * @param Bool passive passive declaration, do not create queue if it does not exist * @param Bool durable whether to create a durable queue * @param Bool autoDelete automatically delete queue when it is empty * @param Bool noWait whether to wait for a return value * @param Table arguments additional arguments, implementation dependent */ QueueDeclareFrame(uint16_t channel, const std::string_view &name = "", bool passive = false, bool durable = false, bool exclusive = false, bool autoDelete = false, bool noWait = false, const Table& arguments = {}) : QueueFrame(channel, (uint32_t)(name.length() + arguments.size() + 4 ) ), // 1 extra for string size, 1 for bools, 2 for deprecated value _name(name), _bools(passive, durable, exclusive, autoDelete, noWait), _arguments(arguments) {} /** * Constructor based on incoming data * @param frame received frame */ QueueDeclareFrame(ReceivedFrame &frame) : QueueFrame(frame), _deprecated(frame.nextInt16()), _name(frame), _bools(frame), _arguments(frame) {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous without the nowait option return !noWait(); } /** * returns the method id * @return string */ virtual uint16_t methodID() const override { return 10; } /** * return the queue name * @return string */ const std::string& name() const { return _name; } /** * returns value of passive declaration, do not create queue if it does not exist * @return bool */ bool passive() const { return _bools.get(0); } /** * returns whether the queue is durable * @return bool */ bool durable() const { return _bools.get(1); } /** * returns whether the queue is exclusive * @return bool */ bool exclusive() const { return _bools.get(2); } /** * returns whether the queue is deleted if unused * @return bool */ bool autoDelete() const { return _bools.get(3); } /** * returns whether to wait for a response * @return bool */ bool noWait() const { return _bools.get(4); } /** * returns additional arguments. Implementation dependant. * @return Table */ const Table& arguments() const { return _arguments; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queuedeclareokframe.h000066400000000000000000000057161470663072600177620ustar00rootroot00000000000000/** * Class describing an AMQP queue declare ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class QueueDeclareOKFrame : public QueueFrame { private: /** * The queue name * @var ShortString */ ShortString _name; /** * Number of messages * @var int32_t */ int32_t _messageCount; /** * Number of Consumers * @var int32_t */ int32_t _consumerCount; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add fields _name.fill(buffer); buffer.add(_messageCount); buffer.add(_consumerCount); } public: /** * Construct a channel flow frame * * @param channel channel identifier * @param code reply code * @param text reply text * @param failingClass failing class id if applicable * @param failingMethod failing method id if applicable */ QueueDeclareOKFrame(uint16_t channel, const std::string& name, int32_t messageCount, int32_t consumerCount) : QueueFrame(channel, (uint32_t)(name.length() + 9)), // 4 per int, 1 for string size _name(name), _messageCount(messageCount), _consumerCount(consumerCount) {} /** * Constructor based on incoming data * @param frame received frame */ QueueDeclareOKFrame(ReceivedFrame &frame) : QueueFrame(frame), _name(frame), _messageCount(frame.nextInt32()), _consumerCount(frame.nextInt32()) {} /** * Destructor */ virtual ~QueueDeclareOKFrame() {} /** * Method id */ virtual uint16_t methodID() const override { return 11; } /** * Queue name * @return string */ const std::string& name() const { return _name; } /** * Number of messages * @return int32_t */ uint32_t messageCount() const { return _messageCount; } /** * Number of consumers * @return int32_t */ uint32_t consumerCount() const { return _consumerCount; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // what if channel doesn't exist? if (!channel) return false; // report success channel->reportSuccess(name(), messageCount(), consumerCount()); // done return true; } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/queuedeleteframe.h000066400000000000000000000057521470663072600172730ustar00rootroot00000000000000/** * Class describing an AMQP queue delete frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class QueueDeleteFrame : public QueueFrame { private: /** * Field that is no longer in use * @var int16_t */ int16_t _deprecated = 0; /** * the queue name * @var ShortString */ ShortString _name; /** * Booleanset, contains * 0: ifUnused, delete only if unused * 1: ifEmpty, delete only if empty * 2: noWait, do not wait on response * @var BooleanSet */ BooleanSet _bools; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add fields buffer.add(_deprecated); _name.fill(buffer); _bools.fill(buffer); } public: /** * Destructor */ virtual ~QueueDeleteFrame() {} /** * Construct a queuedeleteframe * * @param channel channel identifier * @param name name of the queue * @param ifUnused delete only if unused * @param ifEmpty delete only if empty * @param noWait do not wait on response */ QueueDeleteFrame(uint16_t channel, const std::string_view &name, bool ifUnused = false, bool ifEmpty = false, bool noWait = false) : QueueFrame(channel, (uint32_t)(name.length() + 4)), // 1 for string length, 1 for bools, 2 for deprecated field _name(name), _bools(ifUnused, ifEmpty, noWait) {} /** * Constructor based on received data * @param frame received frame */ QueueDeleteFrame(ReceivedFrame &frame) : QueueFrame(frame), _deprecated(frame.nextInt16()), _name(frame), _bools(frame) {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous without the nowait option return !noWait(); } /** * returns the method id * @returns uint16_t */ virtual uint16_t methodID() const override { return 40; } /** * returns the queue name * @returns string */ const std::string& name() const { return _name; } /** * returns value of ifUnused * @returns bool */ bool ifUnused() const { return _bools.get(0); } /** * returns the value of ifEmpty * @returns bool */ bool ifEmpty() const { return _bools.get(1); } /** * returns the value of noWait * @returns bool */ bool noWait() const { return _bools.get(2); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/queuedeleteokframe.h000066400000000000000000000041411470663072600176140ustar00rootroot00000000000000/** * Class describing an AMQP queue delete frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class QueueDeleteOKFrame : public QueueFrame { private: /** * The message count * @var int32_t */ int32_t _messageCount; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add field buffer.add(_messageCount); } public: /** * Construct a queuedeleteokframe * * @param channel channel identifier * @param messageCount number of messages */ QueueDeleteOKFrame(uint16_t channel, int32_t messageCount) : QueueFrame(channel, 4), // 4 for int32_t size _messageCount(messageCount) {} /** * Constructor based on received data * @param frame received frame */ QueueDeleteOKFrame(ReceivedFrame &frame) : QueueFrame(frame), _messageCount(frame.nextInt32()) {} /** * Destructor */ virtual ~QueueDeleteOKFrame() {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 41; } /** * returns the number of messages * @return uint32_t */ uint32_t messageCount() const { return _messageCount; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report queue deletion success channel->reportSuccess(this->messageCount()); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queueframe.h000066400000000000000000000015231470663072600161000ustar00rootroot00000000000000/** * Class describing an AMQP queue frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class QueueFrame : public MethodFrame { protected: /** * Construct a queueframe * @param channel channel identifier * @param size size of the frame */ QueueFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {} /** * Constructor based on incoming data * @param frame received frame */ QueueFrame(ReceivedFrame &frame) : MethodFrame(frame) {} public: /** * Destructor */ virtual ~QueueFrame() {} /** * returns the class id * @return uint16_t */ virtual uint16_t classID() const override { return 50; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queuepurgeframe.h000066400000000000000000000047161470663072600171520ustar00rootroot00000000000000/** * Class describing an AMQP queue purge frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class QueuePurgeFrame : public QueueFrame{ private: /** * Field that is no longer in use * @var int16_t */ int16_t _deprecated = 0; /** * Name of the queue * @var ShortString */ ShortString _name; /** * Do not wait on response * @var BooleanSet */ BooleanSet _noWait; protected: /** * Encode the frame into a buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add fields buffer.add(_deprecated); _name.fill(buffer); _noWait.fill(buffer); } public: /** * Destructor */ virtual ~QueuePurgeFrame() {} /** * Construct a QueuePurgeFrame * * @param channel channel identifier * @param name name of the queue * @param noWait Do not wait on response * * @return newly created Queuepurgeframe */ QueuePurgeFrame(uint16_t channel, const std::string_view &name, bool noWait = false) : QueueFrame(channel, (uint32_t)(name.length() + 4)), // 1 extra for string length, 1 for bool, 2 for deprecated field _name(name), _noWait(noWait) {} /** * Constructor based on received data * @param frame received frame */ QueuePurgeFrame(ReceivedFrame &frame) : QueueFrame(frame), _deprecated(frame.nextInt16()), _name(frame), _noWait(frame) {} /** * Is this a synchronous frame? * * After a synchronous frame no more frames may be * sent until the accompanying -ok frame arrives */ bool synchronous() const override { // we are synchronous without the nowait option return !noWait(); } /** * The method ID * @return method id */ virtual uint16_t methodID() const override { return 30; } /** * The queue name * @return the queue name */ const std::string& name() const { return _name; } /** * The nowait option * @return the value of bool noWait */ bool noWait() const { return _noWait.get(0); } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queuepurgeokframe.h000066400000000000000000000041311470663072600174730ustar00rootroot00000000000000/** * Class describing an AMQP queue purge frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class QueuePurgeOKFrame : public QueueFrame { private: /** * The message count * @var int32_t */ int32_t _messageCount; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add fields buffer.add(_messageCount); } public: /** * Construct a queuepurgeokframe * * @param channel channel identifier * @param messageCount number of messages */ QueuePurgeOKFrame(uint16_t channel, int32_t messageCount) : QueueFrame(channel, 4), // sizeof int32_t _messageCount(messageCount) {} /** * Constructor based on incoming data * @param frame received frame */ QueuePurgeOKFrame(ReceivedFrame &frame) : QueueFrame(frame), _messageCount(frame.nextInt32()) {} /** * Destructor */ virtual ~QueuePurgeOKFrame() {} /** * return the method id * @returns uint16_t */ virtual uint16_t methodID() const override { return 31; } /** * returns the number of messages * @return uint32_t */ uint32_t messageCount() const { return _messageCount; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report queue purge success channel->reportSuccess(this->messageCount()); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queueunbindframe.h000066400000000000000000000061001470663072600172740ustar00rootroot00000000000000/** * Class describing an AMQP queue bind frame * * @copyright 2014 - 2023 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class QueueUnbindFrame : public QueueFrame { private: /** * Unused field * @var int16_t */ int16_t _deprecated = 0; /** * the queue name * @var ShortString */ ShortString _name; /** * the exchange name * @var ShortString */ ShortString _exchange; /** * the routing key * @var ShortString */ ShortString _routingKey; /** * additional arguments, implementation dependant. * @var Table */ Table _arguments; protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); // add fields buffer.add(_deprecated); _name.fill(buffer); _exchange.fill(buffer); _routingKey.fill(buffer); _arguments.fill(buffer); } public: /** * Destructor */ virtual ~QueueUnbindFrame() {} /** * Construct a queueunbindframe * * @param channel channel identifier * @param name name of the queue * @param exchange name of the exchange * @param routingKey the routingKey * @param arguments additional arguments, implementation dependant. */ QueueUnbindFrame(uint16_t channel, const std::string_view &name, const std::string_view &exchange, const std::string_view &routingKey = "", const Table& arguments = {} ) : QueueFrame(channel, (uint32_t)(name.length() + exchange.length() + routingKey.length() + arguments.size() + 5) ), // 1 per string, 2 for deprecated field _name(name), _exchange(exchange), _routingKey(routingKey), _arguments(arguments) {} /** * Constructor based on incoming data * * @param frame received frame to decode */ QueueUnbindFrame(ReceivedFrame& frame) : QueueFrame(frame), _deprecated(frame.nextInt16()), _name(frame), _exchange(frame), _routingKey(frame), _arguments(frame) {} /** * returns the method id * @returns uint16_t */ virtual uint16_t methodID() const override { return 50; } /** * returns the queue name * @returns string */ const std::string& name() const { return _name; } /** * returns the exchange name * @returns string */ const std::string& exchange() const { return _exchange; } /** * returns the routingKey * @returns string */ const std::string& routingKey() const { return _routingKey; } /** * returns the additional arguments * @returns Table */ const Table& arguments() const { return _arguments; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/queueunbindokframe.h000066400000000000000000000032771470663072600176420ustar00rootroot00000000000000/** * Class describing an AMQP queue unbind ok frame * * @copyright 2014 Copernica BV */ /** * Class definition */ namespace AMQP { /** * Class implementation */ class QueueUnbindOKFrame : public QueueFrame { protected: /** * Encode a queueunbindokframe on a stringbuffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base QueueFrame::fill(buffer); } public: /** * Decode a queueunbindokframe from a received frame * * @param frame received frame to decode * @return shared pointer to created frame */ QueueUnbindOKFrame(ReceivedFrame& frame) : QueueFrame(frame) {} /** * construct a queueunbindokframe * * @param channel channel identifier */ QueueUnbindOKFrame(uint16_t channel) : QueueFrame(channel, 0) {} /** * Destructor */ virtual ~QueueUnbindOKFrame() {} /** * returns the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 51; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // check if we have a channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report queue unbind success channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/receivedframe.cpp000066400000000000000000000321611470663072600170770ustar00rootroot00000000000000/** * ReceivedFrame.cpp * * Implementation of the ReceivedFrame class * * @copyright 2014 - 2017 Copernica BV */ #include "includes.h" #include "heartbeatframe.h" #include "confirmselectframe.h" #include "confirmselectokframe.h" #include "connectionstartokframe.h" #include "connectionstartframe.h" #include "connectionsecureframe.h" #include "connectionsecureokframe.h" #include "connectionopenokframe.h" #include "connectionopenframe.h" #include "connectiontuneokframe.h" #include "connectiontuneframe.h" #include "connectioncloseokframe.h" #include "connectioncloseframe.h" #include "connectionblockframe.h" #include "connectionunblockframe.h" #include "channelopenframe.h" #include "channelopenokframe.h" #include "channelflowframe.h" #include "channelflowokframe.h" #include "channelcloseokframe.h" #include "channelcloseframe.h" #include "exchangedeclareframe.h" #include "exchangedeclareokframe.h" #include "exchangedeleteframe.h" #include "exchangedeleteokframe.h" #include "exchangebindframe.h" #include "exchangebindokframe.h" #include "exchangeunbindframe.h" #include "exchangeunbindokframe.h" #include "queuedeclareframe.h" #include "queuedeclareokframe.h" #include "queuebindframe.h" #include "queuebindokframe.h" #include "queuepurgeframe.h" #include "queuepurgeokframe.h" #include "queuedeleteframe.h" #include "queuedeleteokframe.h" #include "queueunbindframe.h" #include "queueunbindokframe.h" #include "basicqosframe.h" #include "basicqosokframe.h" #include "basicconsumeframe.h" #include "basicconsumeokframe.h" #include "basiccancelframe.h" #include "basiccancelokframe.h" #include "basicpublishframe.h" #include "basicreturnframe.h" #include "basicdeliverframe.h" #include "basicgetframe.h" #include "basicgetokframe.h" #include "basicgetemptyframe.h" #include "basicackframe.h" #include "basicnackframe.h" #include "basicrejectframe.h" #include "basicrecoverasyncframe.h" #include "basicrecoverframe.h" #include "basicrecoverokframe.h" #include "transactionselectframe.h" #include "transactionselectokframe.h" #include "transactioncommitframe.h" #include "transactioncommitokframe.h" #include "transactionrollbackframe.h" #include "transactionrollbackokframe.h" #include "bodyframe.h" #include "basicheaderframe.h" #define TYPE_INVALID 0 #define END_OF_FRAME 206 /** * Set up namespace */ namespace AMQP { /** * Constructor * @param buffer Binary buffer * @param max Max size for a frame */ ReceivedFrame::ReceivedFrame(const Buffer &buffer, uint32_t max) : InBuffer(buffer) { // we need enough room for type, channel, the payload size, // the the end-of-frame byte is not yet necessary if (buffer.size() < 7) return; // get the information _type = nextUint8(); _channel = nextUint16(); _payloadSize = nextUint32(); // is the frame size bigger than the max frame size? if (max > 0 && _payloadSize > max - 8) throw ProtocolException("frame size exceeded"); // check if the buffer is big enough to contain all data if (!complete()) return; // buffer is big enough, check for a valid end-of-frame marker if ((uint8_t)buffer.byte(_payloadSize+7) == END_OF_FRAME) return; // the frame is invalid because it does not end with the expected char throw ProtocolException("invalid end of frame marker"); } /** * Have we received the header of the frame * @return bool */ bool ReceivedFrame::header() const { return _buffer.size() >= 7; } /** * Is this a complete frame? * @return integer */ bool ReceivedFrame::complete() const { return _buffer.size() >= _payloadSize + 8; } /** * Process the received frame * @param connection * @return bool */ bool ReceivedFrame::process(ConnectionImpl *connection) { // check the type switch (_type) { case 1: return processMethodFrame(connection); case 2: return processHeaderFrame(connection); case 3: return BodyFrame(*this).process(connection); case 4: return HeartbeatFrame(*this).process(connection); case 8: return HeartbeatFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized frame type " + std::to_string(_type)); } /** * Process a method frame * @param connection * @return bool */ bool ReceivedFrame::processMethodFrame(ConnectionImpl *connection) { // read the class id from the method uint16_t classID = nextUint16(); // construct frame based on method id switch (classID) { case 10: return processConnectionFrame(connection); case 20: return processChannelFrame(connection); case 40: return processExchangeFrame(connection); case 50: return processQueueFrame(connection); case 60: return processBasicFrame(connection); case 85: return processConfirmFrame(connection); case 90: return processTransactionFrame(connection); } // this is a problem throw ProtocolException("unrecognized method frame class " + std::to_string(classID)); } /** * Process a connection frame * @param connection * @return bool */ bool ReceivedFrame::processConnectionFrame(ConnectionImpl *connection) { // read the method id from the method uint16_t methodID = nextUint16(); // construct frame based on method id switch (methodID) { case 10: return ConnectionStartFrame(*this).process(connection); case 11: return ConnectionStartOKFrame(*this).process(connection); case 20: return ConnectionSecureFrame(*this).process(connection); case 21: return ConnectionSecureOKFrame(*this).process(connection); case 30: return ConnectionTuneFrame(*this).process(connection); case 31: return ConnectionTuneOKFrame(*this).process(connection); case 40: return ConnectionOpenFrame(*this).process(connection); case 41: return ConnectionOpenOKFrame(*this).process(connection); case 50: return ConnectionCloseFrame(*this).process(connection); case 51: return ConnectionCloseOKFrame(*this).process(connection); case 60: return ConnectionBlockFrame(*this).process(connection); case 61: return ConnectionUnblockFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized connection frame method " + std::to_string(methodID)); } /** * Process a channel frame * @param connection * @return bool */ bool ReceivedFrame::processChannelFrame(ConnectionImpl *connection) { // read the method id from the method uint16_t methodID = nextUint16(); // construct frame based on method id switch (methodID) { case 10: return ChannelOpenFrame(*this).process(connection); case 11: return ChannelOpenOKFrame(*this).process(connection); case 20: return ChannelFlowFrame(*this).process(connection); case 21: return ChannelFlowOKFrame(*this).process(connection); case 40: return ChannelCloseFrame(*this).process(connection); case 41: return ChannelCloseOKFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized channel frame method " + std::to_string(methodID)); } /** * Process an exchange frame * @param connection * @return bool */ bool ReceivedFrame::processExchangeFrame(ConnectionImpl *connection) { // read the method id from the method uint16_t methodID = nextUint16(); // construct frame based on method id switch(methodID) { case 10: return ExchangeDeclareFrame(*this).process(connection); case 11: return ExchangeDeclareOKFrame(*this).process(connection); case 20: return ExchangeDeleteFrame(*this).process(connection); case 21: return ExchangeDeleteOKFrame(*this).process(connection); case 30: return ExchangeBindFrame(*this).process(connection); case 31: return ExchangeBindOKFrame(*this).process(connection); case 40: return ExchangeUnbindFrame(*this).process(connection); // contrary to the rule of good continuation, exchangeunbindok // has method ID 51, instead of (the expected) 41. This is tested // and it really has ID 51. case 51: return ExchangeUnbindOKFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized exchange frame method " + std::to_string(methodID)); } /** * Process a queue frame * @param connection * @return bool */ bool ReceivedFrame::processQueueFrame(ConnectionImpl *connection) { // read the method id from the method uint16_t methodID = nextUint16(); // construct frame based on method id switch (methodID) { case 10: return QueueDeclareFrame(*this).process(connection); case 11: return QueueDeclareOKFrame(*this).process(connection); case 20: return QueueBindFrame(*this).process(connection); case 21: return QueueBindOKFrame(*this).process(connection); case 30: return QueuePurgeFrame(*this).process(connection); case 31: return QueuePurgeOKFrame(*this).process(connection); case 40: return QueueDeleteFrame(*this).process(connection); case 41: return QueueDeleteOKFrame(*this).process(connection); case 50: return QueueUnbindFrame(*this).process(connection); case 51: return QueueUnbindOKFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized queue frame method " + std::to_string(methodID)); } /** * Process a basic frame * @param connection * @return bool */ bool ReceivedFrame::processBasicFrame(ConnectionImpl *connection) { // read the method id from the method uint16_t methodID = nextUint16(); // construct frame based on method id switch (methodID) { case 10: return BasicQosFrame(*this).process(connection); case 11: return BasicQosOKFrame(*this).process(connection); case 20: return BasicConsumeFrame(*this).process(connection); case 21: return BasicConsumeOKFrame(*this).process(connection); case 30: return BasicCancelFrame(*this).process(connection); case 31: return BasicCancelOKFrame(*this).process(connection); case 40: return BasicPublishFrame(*this).process(connection); case 50: return BasicReturnFrame(*this).process(connection); case 60: return BasicDeliverFrame(*this).process(connection); case 70: return BasicGetFrame(*this).process(connection); case 71: return BasicGetOKFrame(*this).process(connection); case 72: return BasicGetEmptyFrame(*this).process(connection); case 80: return BasicAckFrame(*this).process(connection); case 90: return BasicRejectFrame(*this).process(connection); case 100: return BasicRecoverAsyncFrame(*this).process(connection); case 110: return BasicRecoverFrame(*this).process(connection); case 111: return BasicRecoverOKFrame(*this).process(connection); case 120: return BasicNackFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized basic frame method " + std::to_string(methodID)); } /** * Process a confirm frame * @param connection * @return bool */ bool ReceivedFrame::processConfirmFrame(ConnectionImpl *connection) { // read the method id uint16_t methodID = nextUint16(); // construct frame based on method id switch (methodID) { case 10: return ConfirmSelectFrame(*this).process(connection); case 11: return ConfirmSelectOKFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized confirm frame method " + std::to_string(methodID)); } /** * Process a transaction frame * @param connection * @return bool */ bool ReceivedFrame::processTransactionFrame(ConnectionImpl *connection) { // read the method id uint16_t methodID = nextUint16(); // construct frame based on method id switch (methodID) { case 10: return TransactionSelectFrame(*this).process(connection); case 11: return TransactionSelectOKFrame(*this).process(connection); case 20: return TransactionCommitFrame(*this).process(connection); case 21: return TransactionCommitOKFrame(*this).process(connection); case 30: return TransactionRollbackFrame(*this).process(connection); case 31: return TransactionRollbackOKFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized transaction frame method " + std::to_string(methodID)); } /** * Process a header frame * @param connection * @return bool */ bool ReceivedFrame::processHeaderFrame(ConnectionImpl *connection) { // read the class id from the method uint16_t classID = nextUint16(); // construct a frame based on class id switch (classID) { case 60: return BasicHeaderFrame(*this).process(connection); } // this is a problem throw ProtocolException("unrecognized header frame class " + std::to_string(classID)); } /** * End of namespace */ } AMQP-CPP-4.3.27/src/receivedframe.h000066400000000000000000000075721470663072600165540ustar00rootroot00000000000000/** * ReceivedFrame.h * * The received frame class is a wrapper around a data buffer, it tries to * find out if the buffer is big enough to contain an entire frame, and * it will try to recognize the frame type in the buffer * * This is a class that is used internally by the AMQP library. As a user * of this library, you normally do not have to instantiate it. * * @copyright 2014 - 2020 Copernica BV */ /** * Include guard */ #pragma once /** * Dependencies */ #include /** * Set up namespace */ namespace AMQP { /** * Forward declarations */ class ConnectionImpl; /** * Class definition */ class ReceivedFrame : public InBuffer { private: /** * Type of frame * @var uint8_t */ uint8_t _type = 0; /** * Channel identifier * @var uint16_t */ uint16_t _channel = 0; /** * The payload size * @var uint32_t */ uint32_t _payloadSize = 0; /** * Process a method frame * @param connection * @return bool */ bool processMethodFrame(ConnectionImpl *connection); /** * Process a connection frame * @param connection * @return bool */ bool processConnectionFrame(ConnectionImpl *connection); /** * Process a channel frame * @param connection * @return bool */ bool processChannelFrame(ConnectionImpl *connection); /** * Process an exchange frame * @param connection * @return bool */ bool processExchangeFrame(ConnectionImpl *connection); /** * Process a queue frame * @param connection * @return bool */ bool processQueueFrame(ConnectionImpl *connection); /** * Process a basic frame * @param connection * @return bool */ bool processBasicFrame(ConnectionImpl *connection); /** * Process a confirm frame * @param connection * @return bool */ bool processConfirmFrame(ConnectionImpl *connection); /** * Process a transaction frame * @param connection * @return bool */ bool processTransactionFrame(ConnectionImpl *connection); /** * Process a header frame * @param connection * @return bool */ bool processHeaderFrame(ConnectionImpl *connection); public: /** * Constructor * @param buffer Binary buffer * @param max Max buffer size */ ReceivedFrame(const Buffer &buffer, uint32_t max); /** * Destructor */ virtual ~ReceivedFrame() {} /** * Have we at least received the full frame header? * The header contains the frame type, the channel ID and the payload size * @return bool */ bool header() const; /** * Is this a complete frame? * @return bool */ bool complete() const; /** * Return the channel identifier * @return uint16_t */ uint16_t channel() const { return _channel; } /** * Total size of the frame (headers + payload) * @return uint32_t */ uint64_t totalSize() const { // payload size + size of headers and end of frame byte return _payloadSize + 8; } /** * The size of the payload * @return uint32_t */ uint32_t payloadSize() const { return _payloadSize; } /** * Process the received frame * * If this method returns false, it means that the frame was not processed, * because it was an unrecognized frame. This does not mean that the * connection is now in an invalid state however. * * @param connection the connection over which the data was received * @return bool was the frame fully processed * @internal */ bool process(ConnectionImpl *connection); }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/reducedbuffer.h000066400000000000000000000040631470663072600165500ustar00rootroot00000000000000/** * ReducedBuffer.h * * Wrapper around a buffer with a number of bytes to skip * * @author Emiel Bruijntjes * @copyright 2014 Copernica BV */ /** * Include guard */ #pragma once /** * Open namespace */ namespace AMQP { /** * Class definition */ class ReducedBuffer : public Buffer { private: /** * Pointer to the original buffer * @var Buffer */ const Buffer &_buffer; /** * Number of bytes to skip * @var size_t */ size_t _skip; public: /** * Constructor * @param buffer * @param skip */ ReducedBuffer(const Buffer &buffer, size_t skip) : _buffer(buffer), _skip(skip) {} /** * Destructor */ virtual ~ReducedBuffer() {} /** * Total size of the buffer * @return size_t */ virtual size_t size() const override { return _buffer.size() - _skip; } /** * Get access to a single byte * @param pos position in the buffer * @return char value of the byte in the buffer */ virtual char byte(size_t pos) const override { return _buffer.byte(pos + _skip); } /** * Get access to the raw data * @param pos position in the buffer * @param size number of continuous bytes * @return char* */ virtual const char *data(size_t pos, size_t size) const override { return _buffer.data(pos + _skip, size); } /** * Copy bytes to a buffer * * No safety checks are necessary: this method will only be called * for bytes that actually exist * * @param pos position in the buffer * @param size number of bytes to copy * @param buffer buffer to copy into * @return void* pointer to buffer */ virtual void *copy(size_t pos, size_t size, void *buffer) const override { return _buffer.copy(pos + _skip, size, buffer); } }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/returnedmessage.h000066400000000000000000000016461470663072600171440ustar00rootroot00000000000000/** * ReturnedMessage.h * * Message that is received via a return call from the server, because it * was published with the immediate or mandatory flag, and could not be * delivered according to those rules. * * @copyright 2014 - 2017 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class definition */ class ReturnedMessage : public Message { private: /** * The reply code * @var int16_t */ int16_t _replyCode; /** * The reply message * @var string */ std::string _replyText; public: /** * Constructor * @param frame */ ReturnedMessage(const BasicReturnFrame &frame) : Message(frame.exchange(), frame.routingKey()), _replyCode(frame.replyCode()), _replyText(frame.replyText()) {} /** * Destructor */ virtual ~ReturnedMessage() = default; }; /** * End of namespace */ } AMQP-CPP-4.3.27/src/table.cpp000066400000000000000000000105421470663072600153640ustar00rootroot00000000000000#include "includes.h" #include // we live in the copernica namespace namespace AMQP { /** * Decode the data from a received frame into a table * * @param frame received frame to decode */ Table::Table(InBuffer &frame) { // table buffer begins with the number of bytes to read uint32_t bytesToRead = frame.nextUint32(); // keep going until the correct number of bytes is read. while (bytesToRead > 0) { // field name and type ShortString name(frame); // subtract number of bytes to read, plus one byte for the decoded type bytesToRead -= (uint32_t)(name.size() + 1); // get the field auto field = Field::decode(frame); if (!field) continue; // subtract size bytesToRead -= (uint32_t)field->size(); // add field _fields[name] = std::move(field); } } /** * Copy constructor * @param table */ Table::Table(const Table &table) { // loop through the table records for (auto iter = table._fields.begin(); iter != table._fields.end(); iter++) { // since a map is always ordered, we know that each element will // be inserted at the end of the new map, so we can simply use // emplace_hint and hint at insertion at the end of the map _fields.insert(_fields.end(), std::make_pair(iter->first, iter->second->clone())); } } /** * Assignment operator * @param table * @return Table */ Table &Table::operator=(const Table &table) { // skip self assignment if (this == &table) return *this; // empty current fields _fields.clear(); // loop through the table records for (auto iter = table._fields.begin(); iter != table._fields.end(); iter++) { // since a map is always ordered, we know that each element will // be inserted at the end of the new map, so we can simply use // emplace_hint and hint at insertion at the end of the map _fields.insert(_fields.end(), std::make_pair(iter->first, iter->second->clone())); } // done return *this; } /** * Move assignment operator * @param table * @return Table */ Table &Table::operator=(Table &&table) { // skip self assignment if (this == &table) return *this; // copy fields _fields = std::move(table._fields); // done return *this; } /** * Retrieve all keys in the table * * @return Vector with all keys in the table */ std::vector Table::keys() const { // the result vector std::vector result; result.reserve(_fields.size()); // insert all keys into the result vector for (auto &iter : _fields) result.push_back(iter.first); // now return the result return result; } /** * Get a field * * If the field does not exist, an empty string field is returned * * @param name field name * @return the field value */ const Field &Table::get(const std::string &name) const { // we need an empty string static ShortString empty; // locate the element first auto iter(_fields.find(name)); // check whether the field was found if (iter == _fields.end()) return empty; // done return *iter->second; } /** * Get the size this field will take when * encoded in the AMQP wire-frame format */ size_t Table::size() const { // add the size of the uint32_t indicating the size size_t size = 4; // iterate over all elements for (auto iter(_fields.begin()); iter != _fields.end(); ++iter) { // get the size of the field name ShortString name(iter->first); size += name.size(); // add the size of the field type size += sizeof(iter->second->typeID()); // add size of element to the total size += iter->second->size(); } // return the result return size; } /** * Write encoded payload to the given buffer. */ void Table::fill(OutBuffer& buffer) const { // add size buffer.add(static_cast(size()-4)); // loop through the fields for (auto iter(_fields.begin()); iter != _fields.end(); ++iter) { // encode the field name ShortString name(iter->first); name.fill(buffer); // encode the element type buffer.add((uint8_t) iter->second->typeID()); // encode element iter->second->fill(buffer); } } // end namespace } AMQP-CPP-4.3.27/src/tagger.cpp000066400000000000000000000156071470663072600155550ustar00rootroot00000000000000/** * Tagger.cpp * * Implementation for Tagger class. * * @author Michael van der Werve * @copyright 2020 - 2024 Copernica BV */ /** * Includes */ #include "includes.h" #include "basicpublishframe.h" #include "basicheaderframe.h" #include "bodyframe.h" /** * Begin of namespaces */ namespace AMQP { /** * Constructor * @param channel */ Tagger::Tagger(Channel &channel) : _implementation(channel._implementation) { // activate confirm-select mode auto &deferred = channel.confirmSelect() .onAck([this](uint64_t deliveryTag, bool multiple) { onAck(deliveryTag, multiple); }) .onNack([this](uint64_t deliveryTag, bool multiple, bool /* requeue*/) { onNack(deliveryTag, multiple); }); // we might have failed, in which case we throw if (!deferred) throw std::runtime_error("could not enable publisher confirms"); // remember the original callback _errorCallback = _implementation->onError(); // we wrap a handling error callback that calls our member function _implementation->onError([this](const char *message) { reportError(message); }); } /** * Destructor */ Tagger::~Tagger() { // restore the error-callback _implementation->onError(nullptr); // also unset the callbacks for onAck and onNack auto *deferred = _implementation->confirm(); // unlikely case that the onAck and onNack are not set if (deferred == nullptr) return; // unset the callbacks deferred->onAck(nullptr).onNack(nullptr); } /** * Send method for a frame * @param id * @param frame */ bool Tagger::send(uint64_t id, const Frame &frame) { // we're simply going to send it over the channel directly return _implementation->send(frame); } /** * Called when the deliverytag(s) are acked * @param deliveryTag * @param multiple */ void Tagger::onAck(uint64_t deliveryTag, bool multiple) { // leap out if there are still messages or we shouldn't close yet if (!_close || unacknowledged()) return; // we make a local copy to keep the object in scope even when 'this' is deleted auto close = _close; // close the channel, and forward the callbacks to the installed handler // we need to be sure the the deffered object stays alive even if the callback // decides to remove us. _implementation->close() .onSuccess([close]() { close->reportSuccess(); }) .onError([close](const char *message) { close->reportError(message); }); } /** * Called when the deliverytag(s) are nacked * @param deliveryTag * @param multiple */ void Tagger::onNack(uint64_t deliveryTag, bool multiple) { // leap out if there are still messages or we shouldn't close yet if (!_close || unacknowledged()) return; // we make a local copy to keep the object in scope even when 'this' is deleted auto close = _close; // close the channel, and forward the callbacks to the installed handler // we need to be sure the the deffered object stays alive even if the callback // decides to remove us. _implementation->close() .onSuccess([close]() { close->reportSuccess(); }) .onError([close](const char *message) { close->reportError(message); }); } /** * Method that is called to report an error * @param message */ void Tagger::reportError(const char *message) { // reset tracking, since channel is fully broken _current = 1; // if a callback is set, call the handler with the message if (_errorCallback) _errorCallback(message); } /** * Publish a message to an exchange. See amqpcpp/channel.h for more details on the flags. * Delays actual publishing depending on the publisher confirms sent by RabbitMQ. * * @param exchange the exchange to publish to * @param routingkey the routing key * @param envelope the full envelope to send * @param message the message to send * @param size size of the message * @param flags optional flags * @return uint64_t */ uint64_t Tagger::publish(const std::string_view &exchange, const std::string_view &routingKey, const Envelope &envelope, int flags) { // @todo do not copy the entire buffer to individual frames // fail if we're closing the channel, no more publishes allowed if (_close) return false; // send the publish frame if (!send(_current, BasicPublishFrame(_implementation->id(), exchange, routingKey, (flags & mandatory) != 0, (flags & immediate) != 0))) return false; // send header if (!send(_current, BasicHeaderFrame(_implementation->id(), envelope))) return false; // connection and channel still usable? if (!_implementation->usable()) return false; // the max payload size is the max frame size minus the bytes for headers and trailer uint32_t maxpayload = _implementation->maxPayload(); uint64_t bytessent = 0; // the buffer const char *data = envelope.body(); uint64_t bytesleft = envelope.bodySize(); // split up the body in multiple frames depending on the max frame size while (bytesleft > 0) { // size of this chunk uint64_t chunksize = std::min(static_cast(maxpayload), bytesleft); // send out a body frame if (!send(_current, BodyFrame(_implementation->id(), data + bytessent, (uint32_t)chunksize))) return false; // update counters bytessent += chunksize; bytesleft -= chunksize; } // we succeeded return _current++; } /** * Close the throttle channel (closes the underlying channel) * @return Deferred& */ Deferred &Tagger::close() { // if this was already set to be closed, return that if (_close) return *_close; // create the deferred (we make a local copy to keep the object in scope even when 'this is deleted) auto close = _close = std::make_shared(!_implementation->usable()); // if there are open messages or there is a queue, they will still get acked and we will then forward it if (unacknowledged()) return *_close; // there are no open messages, we can close the channel directly. // we need to be sure the the deffered object stays alive even if the callback // decides to remove us. _implementation->close() .onSuccess([close]() { close->reportSuccess(); }) .onError([close](const char *message) { close->reportError(message); }); // return the created deferred return *_close; } /** * Install an error callback * @param callback */ void Tagger::onError(ErrorCallback&& callback) { // we store the callback _errorCallback = std::move(callback); // check the callback if (!_errorCallback) return; // if the channel is no longer usable, report that if (!_implementation->usable()) return _errorCallback("Channel is no longer usable"); // specify that we're already closing if (_close) _errorCallback("Wrapped channel is closing down"); } /** * End of namespaces */ } AMQP-CPP-4.3.27/src/throttle.cpp000066400000000000000000000075171470663072600161520ustar00rootroot00000000000000/** * Throttle.cpp * * Implementation for Throttle class. * * @author Michael van der Werve * @copyright 2020 Copernica BV */ /** * Includes */ #include "includes.h" #include "basicpublishframe.h" #include "basicheaderframe.h" #include "bodyframe.h" #include /** * Begin of namespaces */ namespace AMQP { /** * Constructor * @param channel * @param throttle */ Throttle::Throttle(Channel &channel, size_t throttle) : Tagger(channel), _throttle(throttle) {} /** * Called when the deliverytag(s) are acked * @param deliveryTag * @param multiple */ void Throttle::onAck(uint64_t deliveryTag, bool multiple) { // number of messages exposed if (multiple) _open.erase(_open.begin(), _open.upper_bound(deliveryTag)); // otherwise, we remove the single element else _open.erase(deliveryTag); // if there is room, flush part of the queue if (_open.size() < _throttle) flush(_throttle - _open.size()); // call base handler Tagger::onAck(deliveryTag, multiple); } /** * Called when the deliverytag(s) are nacked * @param deliveryTag * @param multiple */ void Throttle::onNack(uint64_t deliveryTag, bool multiple) { // number of messages exposed if (multiple) _open.erase(_open.begin(), _open.upper_bound(deliveryTag)); // otherwise, we remove the single element else _open.erase(deliveryTag); // if there is room, flush part of the queue if (_open.size() < _throttle) flush(_throttle - _open.size()); // call base handler Tagger::onNack(deliveryTag, multiple); } /** * Send method for a frame * @param id * @param frame */ bool Throttle::send(uint64_t id, const Frame &frame) { // if there is already a queue, we always append it if (!_queue.empty() || (_open.size() >= _throttle && _last != id)) { // add the element to the queue _queue.emplace(id, frame); // we have successfully added the message return true; } // there is no queue and we have space, we send it directly _last = id; // we have now send this id _open.insert(id); // we can finally actually send it return Tagger::send(id, frame); } /** * Method that is called to report an error * @param message */ void Throttle::reportError(const char *message) { // pop all elements from the queue (older compilers dont support reassign) while (_queue.size()) _queue.pop(); // we can also forget all open messages, won't hear from them any more _open.clear(); // we have no last seen message any more _last = 0; // call base method Tagger::reportError(message); } /** * Flush the throttle * @param max */ size_t Throttle::flush(size_t max) { // how many have we published size_t published = 0; // keep sending more messages while there is a queue while (!_queue.empty()) { // get the front element from the queue auto &front = _queue.front(); // if the front has a different tag, we might not be allowed to continue if (front.first != _last) { // this is an extra publish, check if this puts us over the edge, in which case we // did one less (unless max = 0, which means do a full flush) if (max > 0 && published >= max) return published; // we are going to publish an extra message ++published; // we now go to publish a new element _last = front.first; // insert it into the set as well _open.insert(_last); } // send the buffer over the implementation _implementation->send(std::move(front.second)); // and remove the message _queue.pop(); } // return number of published messages. return published; } /** * End of namespaces */ } AMQP-CPP-4.3.27/src/transactioncommitframe.h000066400000000000000000000016751470663072600205220ustar00rootroot00000000000000/** * Class describing an AMQP transaction commit frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class TransactionCommitFrame : public TransactionFrame { public: /** * Destructor */ virtual ~TransactionCommitFrame() {} /** * Construct a transaction commit frame * * @param channel channel identifier * @return newly created transaction commit frame */ TransactionCommitFrame(uint16_t channel) : TransactionFrame(channel, 0) {} /** * Constructor based on incoming data * @param frame received frame */ TransactionCommitFrame(ReceivedFrame &frame) : TransactionFrame(frame) {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 20; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/transactioncommitokframe.h000066400000000000000000000033701470663072600210460ustar00rootroot00000000000000/** * Class describing an AMQP transaction commit ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class TransactionCommitOKFrame : public TransactionFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base TransactionFrame::fill(buffer); } public: /** * Construct a transaction commit ok frame * * @param channel channel identifier * @return newly created transaction commit ok frame */ TransactionCommitOKFrame(uint16_t channel) : TransactionFrame(channel, 0) {} /** * Constructor on incoming data * * @param frame received frame to decode */ TransactionCommitOKFrame(ReceivedFrame& frame) : TransactionFrame(frame) {} /** * Destructor */ virtual ~TransactionCommitOKFrame() {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 21; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report that the channel is open channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/transactionframe.h000066400000000000000000000015341470663072600173030ustar00rootroot00000000000000/** * Class describing an AMQP transaction frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class TransactionFrame : public MethodFrame { protected: /** * Constructor * @param channel channel identifier * @param size frame size */ TransactionFrame(uint16_t channel, uint32_t size) : MethodFrame(channel, size) {} /** * Constructor based on incoming frame * @param frame */ TransactionFrame(ReceivedFrame &frame) : MethodFrame(frame) {} public: /** * Destructor */ virtual ~TransactionFrame() {} /** * Class id * @return uint16_t */ virtual uint16_t classID() const override { return 90; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/transactionrollbackframe.h000066400000000000000000000017641470663072600210220ustar00rootroot00000000000000/** * Class describing an AMQP transaction rollback frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class TransactionRollbackFrame : public TransactionFrame { public: /** * Destructor */ virtual ~TransactionRollbackFrame() {} /** * Decode a transaction rollback frame from a received frame * * @param frame received frame to decode */ TransactionRollbackFrame(ReceivedFrame& frame) : TransactionFrame(frame) {} /** * Construct a transaction rollback frame * * @param channel channel identifier * @return newly created transaction rollback frame */ TransactionRollbackFrame(uint16_t channel) : TransactionFrame(channel, 0) {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 30; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/transactionrollbackokframe.h000066400000000000000000000034441470663072600213510ustar00rootroot00000000000000/** * Class describing an AMQP transaction rollback ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class TransactionRollbackOKFrame : public TransactionFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base TransactionFrame::fill(buffer); } public: /** * Decode a transaction rollback ok frame from a received frame * * @param frame received frame to decode */ TransactionRollbackOKFrame(ReceivedFrame& frame) : TransactionFrame(frame) {} /** * Construct a transaction rollback ok frame * * @param channel channel identifier * @return newly created transaction rollback ok frame */ TransactionRollbackOKFrame(uint16_t channel) : TransactionFrame(channel, 0) {} /** * Destructor */ virtual ~TransactionRollbackOKFrame() {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 31; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report that the channel is open channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/transactionselectframe.h000066400000000000000000000017461470663072600205100ustar00rootroot00000000000000/** * Class describing an AMQP transaction select frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class TransactionSelectFrame : public TransactionFrame { public: /** * Decode a transaction select frame from a received frame * * @param frame received frame to decode */ TransactionSelectFrame(ReceivedFrame& frame) : TransactionFrame(frame) {} /** * Construct a transaction select frame * * @param channel channel identifier * @return newly created transaction select frame */ TransactionSelectFrame(uint16_t channel) : TransactionFrame(channel, 0) {} /** * Destructor */ virtual ~TransactionSelectFrame() {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 10; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/transactionselectokframe.h000066400000000000000000000033761470663072600210430ustar00rootroot00000000000000/** * Class describing an AMQP transaction select ok frame * * @copyright 2014 Copernica BV */ /** * Set up namespace */ namespace AMQP { /** * Class implementation */ class TransactionSelectOKFrame : public TransactionFrame { protected: /** * Encode a frame on a string buffer * * @param buffer buffer to write frame to */ virtual void fill(OutBuffer& buffer) const override { // call base TransactionFrame::fill(buffer); } public: /** * Constructor for an incoming frame * * @param frame received frame to decode */ TransactionSelectOKFrame(ReceivedFrame& frame) : TransactionFrame(frame) {} /** * Construct a transaction select ok frame * * @param channel channel identifier * @return newly created transaction select ok frame */ TransactionSelectOKFrame(uint16_t channel) : TransactionFrame(channel, 0) {} /** * Destructor */ virtual ~TransactionSelectOKFrame() {} /** * return the method id * @return uint16_t */ virtual uint16_t methodID() const override { return 11; } /** * Process the frame * @param connection The connection over which it was received * @return bool Was it succesfully processed? */ virtual bool process(ConnectionImpl *connection) override { // we need the appropriate channel auto channel = connection->channel(this->channel()); // channel does not exist if(!channel) return false; // report that the channel is open channel->reportSuccess(); // done return true; } }; /** * end namespace */ } AMQP-CPP-4.3.27/src/watchable.cpp000066400000000000000000000006401470663072600162250ustar00rootroot00000000000000/** * Watchable.cpp * * @copyright 2014 Copernica BV */ #include "includes.h" /** * Set up namespace */ namespace AMQP { /** * Destructor */ Watchable::~Watchable() { // loop through all monitors for (auto iter = _monitors.begin(); iter != _monitors.end(); iter++) { // tell the monitor that it now is invalid (*iter)->invalidate(); } } /** * End of namespace */ } AMQP-CPP-4.3.27/tests/000077500000000000000000000000001470663072600141425ustar00rootroot00000000000000AMQP-CPP-4.3.27/tests/.gitignore000066400000000000000000000000061470663072600161260ustar00rootroot00000000000000a.out AMQP-CPP-4.3.27/tests/Makefile000066400000000000000000000006221470663072600156020ustar00rootroot00000000000000CPP = g++ CPPFLAGS = -Wall -c -I. -O2 -flto -std=c++11 -g LD = g++ LDFLAGS = -lamqpcpp -lcopernica_event -lcopernica_network -lev RESULT = a.out SOURCES = $(wildcard *.cpp) OBJECTS = $(SOURCES:%.cpp=%.o) all: ${OBJECTS} ${RESULT} ${RESULT}: ${OBJECTS} ${LD} -o $@ ${OBJECTS} ${LDFLAGS} clean: ${RM} *.obj *~* ${OBJECTS} ${RESULT} ${OBJECTS}: ${CPP} ${CPPFLAGS} -o $@ ${@:%.o=%.cpp} AMQP-CPP-4.3.27/tests/address.cpp000066400000000000000000000016051470663072600162750ustar00rootroot00000000000000/** * Address.cpp * * Test program to parse address strings * * @author Emiel Bruijntjes * @copyright 2017 Copernica BV */ /** * Dependencies */ #include /** * Main procedure * @param argc * @param argv * @return int */ /* int main(int argc, const char *argv[]) { // iterate over the arguments for (int i = 1; i < argc; ++i) { // parse address AMQP::Address address(argv[i]); // output all properties std::cout << "user: " << address.login().user() << std::endl; std::cout << "password: " << address.login().password() << std::endl; std::cout << "hostname: " << address.hostname() << std::endl; std::cout << "port: " << address.port() << std::endl; std::cout << "vhost: " << address.vhost() << std::endl; } // done return 0; } */AMQP-CPP-4.3.27/tests/table/000077500000000000000000000000001470663072600152315ustar00rootroot00000000000000AMQP-CPP-4.3.27/tests/table/table.cpp000066400000000000000000000003711470663072600170250ustar00rootroot00000000000000#include #include int main() { AMQP::Array x; x[0] = "abc"; x[1] = "xyz"; std::cout << x << std::endl; std::cout << x[0] << std::endl; std::cout << x[1] << std::endl; return 0; }