Browse Source

Beginning CMake configuration for ZT

Only tested on Windows so far
Grant Limberg 6 years ago
parent
commit
0b3b5f6174
100 changed files with 19017 additions and 14 deletions
  1. 73 7
      CMakeLists.txt
  2. 33 0
      controller/CMakeLists.txt
  3. 47 0
      ext/librabbitmq/.clang-format
  4. 18 0
      ext/librabbitmq/.gitattributes
  5. 73 0
      ext/librabbitmq/.gitignore
  6. 6 0
      ext/librabbitmq/.gitmodules
  7. 92 0
      ext/librabbitmq/.travis.yml
  8. 157 0
      ext/librabbitmq/.ycm_extra_conf.py
  9. 2 0
      ext/librabbitmq/AUTHORS
  10. 343 0
      ext/librabbitmq/CMakeLists.txt
  11. 26 0
      ext/librabbitmq/CONTRIBUTING.md
  12. 221 0
      ext/librabbitmq/ChangeLog.md
  13. 28 0
      ext/librabbitmq/LICENSE-MIT
  14. 128 0
      ext/librabbitmq/README.md
  15. 8 0
      ext/librabbitmq/THANKS
  16. 9 0
      ext/librabbitmq/TODO
  17. 34 0
      ext/librabbitmq/appveyor.yml
  18. BIN
      ext/librabbitmq/centos_x64/lib/librabbitmq.a
  19. 103 0
      ext/librabbitmq/cmake/CMakePushCheckState.cmake
  20. 22 0
      ext/librabbitmq/cmake/COPYING-CMAKE-SCRIPTS
  21. 39 0
      ext/librabbitmq/cmake/FindPOPT.cmake
  22. 98 0
      ext/librabbitmq/cmake/FindXmlTo.cmake
  23. 205 0
      ext/librabbitmq/cmake/GNUInstallDirs.cmake
  24. 28 0
      ext/librabbitmq/cmake/TestCInline.cmake
  25. 14 0
      ext/librabbitmq/cmake/config.h.in
  26. 17 0
      ext/librabbitmq/coverity/model.c
  27. 317 0
      ext/librabbitmq/docs/Doxyfile.in
  28. 52 0
      ext/librabbitmq/examples/CMakeLists.txt
  29. 95 0
      ext/librabbitmq/examples/amqp_bind.c
  30. 114 0
      ext/librabbitmq/examples/amqp_connect_timeout.c
  31. 215 0
      ext/librabbitmq/examples/amqp_consumer.c
  32. 94 0
      ext/librabbitmq/examples/amqp_exchange_declare.c
  33. 143 0
      ext/librabbitmq/examples/amqp_listen.c
  34. 122 0
      ext/librabbitmq/examples/amqp_listenq.c
  35. 150 0
      ext/librabbitmq/examples/amqp_producer.c
  36. 243 0
      ext/librabbitmq/examples/amqp_rpc_sendstring_client.c
  37. 103 0
      ext/librabbitmq/examples/amqp_sendstring.c
  38. 135 0
      ext/librabbitmq/examples/amqp_ssl_connect.c
  39. 95 0
      ext/librabbitmq/examples/amqp_unbind.c
  40. 52 0
      ext/librabbitmq/examples/unix/platform_utils.c
  41. 188 0
      ext/librabbitmq/examples/utils.c
  42. 48 0
      ext/librabbitmq/examples/utils.h
  43. 47 0
      ext/librabbitmq/examples/win32/platform_utils.c
  44. 13 0
      ext/librabbitmq/librabbitmq.pc.in
  45. 186 0
      ext/librabbitmq/librabbitmq/CMakeLists.txt
  46. 4 7
      ext/librabbitmq/librabbitmq/amqp.h
  47. 394 0
      ext/librabbitmq/librabbitmq/amqp_api.c
  48. 595 0
      ext/librabbitmq/librabbitmq/amqp_connection.c
  49. 307 0
      ext/librabbitmq/librabbitmq/amqp_consumer.c
  50. 2876 0
      ext/librabbitmq/librabbitmq/amqp_framing.c
  51. 0 0
      ext/librabbitmq/librabbitmq/amqp_framing.h
  52. 195 0
      ext/librabbitmq/librabbitmq/amqp_hostcheck.c
  53. 48 0
      ext/librabbitmq/librabbitmq/amqp_hostcheck.h
  54. 242 0
      ext/librabbitmq/librabbitmq/amqp_mem.c
  55. 704 0
      ext/librabbitmq/librabbitmq/amqp_openssl.c
  56. 193 0
      ext/librabbitmq/librabbitmq/amqp_openssl_bio.c
  57. 44 0
      ext/librabbitmq/librabbitmq/amqp_openssl_bio.h
  58. 179 0
      ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.c
  59. 58 0
      ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.h
  60. 374 0
      ext/librabbitmq/librabbitmq/amqp_private.h
  61. 1492 0
      ext/librabbitmq/librabbitmq/amqp_socket.c
  62. 188 0
      ext/librabbitmq/librabbitmq/amqp_socket.h
  63. 239 0
      ext/librabbitmq/librabbitmq/amqp_ssl_socket.h
  64. 668 0
      ext/librabbitmq/librabbitmq/amqp_table.c
  65. 81 0
      ext/librabbitmq/librabbitmq/amqp_table.h
  66. 238 0
      ext/librabbitmq/librabbitmq/amqp_tcp_socket.c
  67. 0 0
      ext/librabbitmq/librabbitmq/amqp_tcp_socket.h
  68. 265 0
      ext/librabbitmq/librabbitmq/amqp_time.c
  69. 130 0
      ext/librabbitmq/librabbitmq/amqp_time.h
  70. 220 0
      ext/librabbitmq/librabbitmq/amqp_url.c
  71. 785 0
      ext/librabbitmq/librabbitmq/codegen.py
  72. 28 0
      ext/librabbitmq/librabbitmq/unix/threads.h
  73. 245 0
      ext/librabbitmq/librabbitmq/win32/msinttypes/stdint.h
  74. 56 0
      ext/librabbitmq/librabbitmq/win32/threads.c
  75. 52 0
      ext/librabbitmq/librabbitmq/win32/threads.h
  76. 47 0
      ext/librabbitmq/tests/CMakeLists.txt
  77. 207 0
      ext/librabbitmq/tests/test_basic.c
  78. 71 0
      ext/librabbitmq/tests/test_hostcheck.c
  79. 203 0
      ext/librabbitmq/tests/test_merge_capabilities.c
  80. 220 0
      ext/librabbitmq/tests/test_parse_url.c
  81. 70 0
      ext/librabbitmq/tests/test_sasl_mechanism.c
  82. 52 0
      ext/librabbitmq/tests/test_status_enum.c
  83. 466 0
      ext/librabbitmq/tests/test_tables.c
  84. 90 0
      ext/librabbitmq/tests/test_tables.expected
  85. 304 0
      ext/librabbitmq/tests/win32/msinttypes/inttypes.h
  86. 82 0
      ext/librabbitmq/tools/CMakeLists.txt
  87. 444 0
      ext/librabbitmq/tools/common.c
  88. 73 0
      ext/librabbitmq/tools/common.h
  89. 250 0
      ext/librabbitmq/tools/consume.c
  90. 79 0
      ext/librabbitmq/tools/declare_queue.c
  91. 81 0
      ext/librabbitmq/tools/delete_queue.c
  92. 223 0
      ext/librabbitmq/tools/doc/amqp-consume.xml
  93. 122 0
      ext/librabbitmq/tools/doc/amqp-declare-queue.xml
  94. 94 0
      ext/librabbitmq/tools/doc/amqp-delete-queue.xml
  95. 95 0
      ext/librabbitmq/tools/doc/amqp-get.xml
  96. 169 0
      ext/librabbitmq/tools/doc/amqp-publish.xml
  97. 90 0
      ext/librabbitmq/tools/doc/librabbitmq-tools.xml
  98. 78 0
      ext/librabbitmq/tools/get.c
  99. 180 0
      ext/librabbitmq/tools/publish.c
  100. 91 0
      ext/librabbitmq/tools/unix/process.c

+ 73 - 7
CMakeLists.txt

@@ -1,12 +1,78 @@
-# CMake build script for libzerotiercore.a
+# CMake build script for ZeroTier One
 
 cmake_minimum_required (VERSION 2.8)
-project (zerotiercore)
+project (zerotier-one)
 
-set (PROJ_DIR ${PROJECT_SOURCE_DIR})
-set (ZT_DEFS -std=c++11)
+option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF)
 
-file(GLOB core_src_glob ${PROJ_DIR}/node/*.cpp)
-add_library(zerotiercore STATIC ${core_src_glob})
+if (BUILD_CENTRAL_CONTROLLER)
+	find_package(PostgreSQL REQUIRED)
 
-target_compile_options(zerotiercore PRIVATE ${ZT_DEFS})
+	set(ENABLE_SSL_SUPPORT OFF)
+	set(BUILD_SHARED_LIBS OFF)
+	set(BUILD_EXAMPLES OFF)
+	set(BUILD_TOOLS OFF)
+	set(BUILD_TESTS OFF)
+	set(BUILD_API_DOCS OFF)
+	add_subdirectory("ext/librabbitmq")
+endif(BUILD_CENTRAL_CONTROLLER)
+
+if(CMAKE_BUILD_TYPE EQUAL "Debug")
+	add_definitions(-DZT_TRACE)
+endif(CMAKE_BUILD_TYPE EQUAL "Debug")
+
+if(WIN32)
+	add_definitions(-DNOMINMAX)
+	if(CMAKE_BUILD_TYPE EQUAL "Debug")
+		add_definitions(-DZT_WIN_RUN_IN_CONSOLE)
+	endif(CMAKE_BUILD_TYPE EQUAL "Debug")
+endif(WIN32)
+
+
+add_subdirectory(node)
+add_subdirectory(controller)
+add_subdirectory(osdep)
+add_subdirectory(service)
+
+if(WIN32)
+	add_definitions(-DNOMINMAX)
+endif(WIN32)
+
+set(libs
+	zt_controller
+	zt_core
+	zt_osdep
+	zt_service
+)
+
+set(src
+	one.cpp
+	"ext/http-parser/http_parser.c"
+)
+set(headers
+	"ext/http-parser/http_parser.h"
+)
+
+if(WIN32)
+	set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi)
+	set(src
+		${src}
+		"windows/ZeroTierOne/ServiceBase.cpp"
+		"windows/ZeroTierOne/ServiceInstaller.cpp"
+		"windows/ZeroTierOne/ZeroTierOneService.cpp"
+		"windows/ZeroTierOne/ZeroTierOne.rc"
+	)
+	set(headers
+		${headers}
+		"windows/ZeroTierOne/ServiceBase.h"
+		"windows/ZeroTierOne/ServiceInstaller.h"
+		"windows/ZeroTierOne/ZeroTierOneService.h"
+	)
+endif(WIN32)
+
+if(BUILD_CENTRAL_CONTROLLER)
+	set(libs ${libs} rabbitmq-static ${PostgreSQL_LIBRARIES})
+endif(BUILD_CENTRAL_CONTROLLER)
+
+add_executable(${PROJECT_NAME} ${src} ${headers})
+target_link_libraries(${PROJECT_NAME} ${libs})

+ 33 - 0
controller/CMakeLists.txt

@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 2.8)
+project(zt_controller)
+
+if(WIN32)
+	add_definitions(-DNOMINMAX)
+	if(CMAKE_BUILD_TYPE EQUAL "Debug")
+		
+	endif(CMAKE_BUILD_TYPE EQUAL "Debug")
+endif(WIN32)
+
+set(ctl_src
+	DB.cpp
+	EmbeddedNetworkController.cpp
+	FileDB.cpp
+	RabbitMQ.cpp
+)
+
+set(ctl_hdr
+	DB.hpp
+	EmbeddedNetworkController.hpp
+	FileDB.hpp
+	RabbitMQ.hpp
+)
+
+if(BUILD_CENTRAL_CONTROLLER)
+	add_definitions(-DZT_CONTROLLER_USE_LIBPQ)
+	include_directories("../ext/librabbitmq/librabbitmq" ${PostgreSQL_INCLUDE_DIRS})
+
+	set(ctl_src ${ctl_src} PostgreSQL.cpp)
+	set(ctl_hdr ${ctl_hdr} PostgreSQL.hpp)
+endif(BUILD_CENTRAL_CONTROLLER)
+
+add_library(${PROJECT_NAME} STATIC ${ctl_src} ${ctl_hdr})

+ 47 - 0
ext/librabbitmq/.clang-format

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

+ 18 - 0
ext/librabbitmq/.gitattributes

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

+ 73 - 0
ext/librabbitmq/.gitignore

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

+ 6 - 0
ext/librabbitmq/.gitmodules

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

+ 92 - 0
ext/librabbitmq/.travis.yml

@@ -0,0 +1,92 @@
+# Travis-CI Build for rabbitmq-c
+# see travis-ci.org for details
+
+language: c
+
+dist: trusty
+# Currently libpopt-dev is not on the list of whitelisted apt-packages.
+sudo: true
+
+env:
+  global:
+   # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
+   #   via the "travis encrypt" command using the project repo's public key
+   - secure: "gDwqo3jHj+HHGzFKnxL/nwZhbVeh2pItw0TbeaHcLtWubUZaf85ViEQRaXPyfnbG7l0OEQq+PjyhKAfvViVq2NP0lGeeu4VM5uMZJhsCLN594BJr39Y4XzOapg0O8mEMhQ0DU2u1Zo4LMgEcRz67aosVQOj6QV30tOzp9fnxn9U="
+
+services:
+  - rabbitmq
+
+matrix:
+  include:
+    # Note that the first compiler in the matrix must be gcc, so that the
+    # coverity_scan branch hack below works correctly.
+    - compiler: gcc
+      os: linux
+      env: CONFIG=cmake
+    - compiler: gcc
+      os: linux
+      env: CONFIG=format
+    - compiler: gcc
+      os: linux
+      env: CONFIG=coverage
+    - compiler: clang
+      os: linux
+      env: CONFIG=cmake
+    - compiler: clang
+      os: linux
+      env: CONFIG=asan
+    - compiler: clang
+      os: linux
+      env: CONFIG=tsan
+    - compiler: clang
+      os: linux
+      env: CONFIG=scan-build
+    - compiler: clang
+      os: osx
+      env: CONFIG=cmake
+    - compiler: gcc
+      os: linux
+      env: NAME="openssl-1.1.0" CONFIG=cmake
+      addons:
+        apt:
+          sources:
+          - sourceline: 'ppa:ondrej/nginx-mainline'
+          packages:
+          - libssl1.1
+          - openssl
+          - libssl-dev
+
+  allow_failures:
+    - compiler: clang
+      os: linux
+      env: CONFIG=tsan
+
+before_install:
+  - |
+    if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
+      wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
+      sudo apt-add-repository "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
+      sudo apt-get -q update;
+      sudo apt-get install -y clang-3.9 clang-format-3.9 libpopt-dev;
+    fi
+  # ugly hack; if running a coverity scan abort all except the 1st build
+  # see note re gcc compiler above needing to be 1st
+  # also note that branch_pattern & the TRAVIS_BRANCH check must match
+  # unfortunately COVERITY_SCAN_BRANCH isn't defined until later in the
+  # build process
+  - if ([[ "${TRAVIS_JOB_NUMBER##*.}" != "1" ]] && [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]); then false ; fi
+
+
+script:
+  # Don't bother building if this is being done in the coverity_scan branch.
+  - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ./travis.sh $CONFIG ; fi
+
+addons:
+  coverity_scan:
+    project:
+      name: "alanxz/rabbitmq-c"
+      description: "C AMQP client for RabbitMQ"
+    notification_email: [email protected]
+    build_command_prepend: mkdir build && pushd build && cmake .. && popd
+    build_command: cmake --build ./build
+    branch_pattern: coverity_scan

+ 157 - 0
ext/librabbitmq/.ycm_extra_conf.py

@@ -0,0 +1,157 @@
+# This file is NOT licensed under the GPLv3, which is the license for the rest
+# of YouCompleteMe.
+#
+# Here's the license text for this file:
+#
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+#
+# In jurisdictions that recognize copyright laws, the author or authors
+# of this software dedicate any and all copyright interest in the
+# software to the public domain. We make this dedication for the benefit
+# of the public at large and to the detriment of our heirs and
+# successors. We intend this dedication to be an overt act of
+# relinquishment in perpetuity of all present and future rights to this
+# software under copyright law.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+# For more information, please refer to <http://unlicense.org/>
+
+import os
+import ycm_core
+
+# These are the compilation flags that will be used in case there's no
+# compilation database set (by default, one is not set).
+# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
+flags = [
+'-Wall',
+'-Wextra',
+# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
+# language to use when compiling headers. So it will guess. Badly. So C++
+# headers will be compiled as C headers. You don't want that so ALWAYS specify
+# a "-std=<something>".
+# For a C project, you would set this to something like 'c99' instead of
+# 'c++11'.
+'-std=gnu90',
+# ...and the same thing goes for the magic -x option which specifies the
+# language that the files to be compiled are written in. This is mostly
+# relevant for c++ headers.
+# For a C project, you would set this to 'c' instead of 'c++'.
+'-x',
+'c',
+'-I', './librabbitmq',
+'-I', './librabbitmq/unix',
+'-D', 'HAVE_POLL',
+]
+
+
+# Set this to the absolute path to the folder (NOT the file!) containing the
+# compile_commands.json file to use that instead of 'flags'. See here for
+# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
+#
+# You can get CMake to generate this file for you by adding:
+#   set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
+# to your CMakeLists.txt file.
+#
+# Most projects will NOT need to set this to anything; you can just change the
+# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
+compilation_database_folder = ''
+
+if os.path.exists( compilation_database_folder ):
+  database = ycm_core.CompilationDatabase( compilation_database_folder )
+else:
+  database = None
+
+SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
+
+def DirectoryOfThisScript():
+  return os.path.dirname( os.path.abspath( __file__ ) )
+
+
+def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
+  if not working_directory:
+    return list( flags )
+  new_flags = []
+  make_next_absolute = False
+  path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
+  for flag in flags:
+    new_flag = flag
+
+    if make_next_absolute:
+      make_next_absolute = False
+      if not flag.startswith( '/' ):
+        new_flag = os.path.join( working_directory, flag )
+
+    for path_flag in path_flags:
+      if flag == path_flag:
+        make_next_absolute = True
+        break
+
+      if flag.startswith( path_flag ):
+        path = flag[ len( path_flag ): ]
+        new_flag = path_flag + os.path.join( working_directory, path )
+        break
+
+    if new_flag:
+      new_flags.append( new_flag )
+  return new_flags
+
+
+def IsHeaderFile( filename ):
+  extension = os.path.splitext( filename )[ 1 ]
+  return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
+
+
+def GetCompilationInfoForFile( filename ):
+  # The compilation_commands.json file generated by CMake does not have entries
+  # for header files. So we do our best by asking the db for flags for a
+  # corresponding source file, if any. If one exists, the flags for that file
+  # should be good enough.
+  if IsHeaderFile( filename ):
+    basename = os.path.splitext( filename )[ 0 ]
+    for extension in SOURCE_EXTENSIONS:
+      replacement_file = basename + extension
+      if os.path.exists( replacement_file ):
+        compilation_info = database.GetCompilationInfoForFile(
+          replacement_file )
+        if compilation_info.compiler_flags_:
+          return compilation_info
+    return None
+  return database.GetCompilationInfoForFile( filename )
+
+
+def FlagsForFile( filename, **kwargs ):
+  if database:
+    # Bear in mind that compilation_info.compiler_flags_ does NOT return a
+    # python list, but a "list-like" StringVec object
+    compilation_info = GetCompilationInfoForFile( filename )
+    if not compilation_info:
+      relative_to = DirectoryOfThisScript()
+      return {
+        'flags': MakeRelativePathsInFlagsAbsolute( flags, relative_to ),
+        'do_cache': True
+      }
+
+    final_flags = MakeRelativePathsInFlagsAbsolute(
+      compilation_info.compiler_flags_,
+      compilation_info.compiler_working_dir_ )
+
+  else:
+    relative_to = DirectoryOfThisScript()
+    final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
+
+  return {
+    'flags': final_flags,
+    'do_cache': True
+  }

+ 2 - 0
ext/librabbitmq/AUTHORS

@@ -0,0 +1,2 @@
+Tony Garnock-Jones <[email protected]>
+The RabbitMQ team <[email protected]>

+ 343 - 0
ext/librabbitmq/CMakeLists.txt

@@ -0,0 +1,343 @@
+cmake_minimum_required(VERSION 2.8.12)
+project(rabbitmq-c "C")
+
+# Enable MACOSX_RPATH by default. See: cmake --help-policy CMP0042
+if (POLICY CMP0042)
+  cmake_policy(SET CMP0042 NEW)
+endif()
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+# Follow all steps below in order to calculate new ABI version when updating the library
+# NOTE: THIS IS UNRELATED to the actual project version
+#
+# 1. If the library source code has changed at all since the last update, then increment revision
+# 2. If any interfaces have been added, removed, or changed since the last update, increment current and set revision to 0.
+# 3. If any interfaces have been added since the last public release, then increment age.
+# 4. If any interfaces have been removed since the last public release, then set age to 0.
+
+set(RMQ_SOVERSION_CURRENT   7)
+set(RMQ_SOVERSION_REVISION  0)
+set(RMQ_SOVERSION_AGE       3)
+
+math(EXPR RMQ_SOVERSION_MAJOR "${RMQ_SOVERSION_CURRENT} - ${RMQ_SOVERSION_AGE}")
+math(EXPR RMQ_SOVERSION_MINOR "${RMQ_SOVERSION_AGE}")
+math(EXPR RMQ_SOVERSION_PATCH "${RMQ_SOVERSION_REVISION}")
+
+set(RMQ_VERSION ${RMQ_SOVERSION_MAJOR}.${RMQ_SOVERSION_MINOR}.${RMQ_SOVERSION_PATCH})
+set(RMQ_SOVERSION ${RMQ_SOVERSION_MAJOR})
+
+file(STRINGS librabbitmq/amqp.h _API_VERSION_MAJOR REGEX "^#define AMQP_VERSION_MAJOR [0-9]+$")
+file(STRINGS librabbitmq/amqp.h _API_VERSION_MINOR REGEX "^#define AMQP_VERSION_MINOR [0-9]+$")
+file(STRINGS librabbitmq/amqp.h _API_VERSION_PATCH REGEX "^#define AMQP_VERSION_PATCH [0-9]+$")
+
+string(REGEX MATCH "[0-9]+" _API_VERSION_MAJOR ${_API_VERSION_MAJOR})
+string(REGEX MATCH "[0-9]+" _API_VERSION_MINOR ${_API_VERSION_MINOR})
+string(REGEX MATCH "[0-9]+" _API_VERSION_PATCH ${_API_VERSION_PATCH})
+
+# VERSION to match what is in autotools
+set(VERSION ${_API_VERSION_MAJOR}.${_API_VERSION_MINOR}.${_API_VERSION_PATCH})
+
+if (CMAKE_GENERATOR MATCHES ".*(Make|Ninja).*"
+    AND NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+  message(STATUS "CMAKE_BUILD_TYPE not specified. Creating ${CMAKE_BUILD_TYPE} build")
+endif()
+
+include(TestCInline)
+include(CheckSymbolExists)
+include(CheckLibraryExists)
+include(CMakePushCheckState)
+include(GNUInstallDirs)
+include(CheckCCompilerFlag)
+
+# Detect if we need to link against a socket library:
+cmake_push_check_state()
+if (WIN32)
+  # Always use WinSock2 on Windows
+  set(SOCKET_LIBRARIES ws2_32)
+else ()
+  # Is it in the default link?
+  check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO)
+  if (NOT (HAVE_GETADDRINFO EQUAL 1))
+    SET(CMAKE_REQUIRED_LIBRARIES "socket")
+    check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO2)
+    if (HAVE_GETADDRINFO2 EQUAL 1)
+      set(SOCKET_LIBRARIES socket)
+    else ()
+      SET(CMAKE_REQUIRED_LIBRARIES "socket;nsl")
+      check_symbol_exists(getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO3)
+      if (HAVE_GETADDRINFO3 EQUAL 1)
+        set(SOCKET_LIBRARIES socket nsl)
+      else ()
+        message(FATAL_ERROR "Cannot find name resolution library (containing symbol getaddrinfo)")
+      endif ()
+    endif ()
+  endif ()
+
+  set(CMAKE_REQUIRED_LIBRARIES ${SOCKET_LIBRARIES})
+  check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET)
+  if (NOT HAVE_SOCKET EQUAL 1)
+    set(CMAKE_REQUIRED_LIBRARIES socket ${SOCKET_LIBRARIES})
+    check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET2)
+    if (HAVE_SOCKET2 EQUAL 1)
+      set(SOCKET_LIBRARIES socket ${SOCKET_LIBRARIES})
+    else ()
+      set(CMAKE_REQUIRED_LIBRARIES socket nsl ${SOCKET_LIBRARIES})
+      check_symbol_exists(socket "sys/types.h;sys/socket.h" HAVE_SOCKET3)
+      if (HAVE_SOCKET3 EQUAL 1)
+        set(SOCKET_LIBRARIES socket nsl ${SOCKET_LIBRARIES})
+      else ()
+        message(FATAL_ERROR "Cannot find socket library (containing symbol socket)")
+      endif ()
+    endif ()
+  endif ()
+endif ()
+cmake_pop_check_state()
+
+cmake_push_check_state()
+set(CMAKE_REQUIRED_LIBRARIES ${SOCKET_LIBRARIES})
+check_symbol_exists(poll poll.h HAVE_POLL)
+if (NOT HAVE_POLL)
+  if (WIN32)
+    set(HAVE_SELECT 1)
+  else()
+    check_symbol_exists(select sys/select.h HAVE_SELECT)
+  endif()
+  if (NOT HAVE_SELECT)
+    message(FATAL_ERROR "rabbitmq-c requires poll() or select() to be available")
+  endif()
+endif()
+cmake_pop_check_state()
+
+check_library_exists(rt clock_gettime "time.h" CLOCK_GETTIME_NEEDS_LIBRT)
+check_library_exists(rt posix_spawnp "spawn.h" POSIX_SPAWNP_NEEDS_LIBRT)
+if (CLOCK_GETTIME_NEEDS_LIBRT OR POSIX_SPAWNP_NEEDS_LIBRT)
+  set(LIBRT rt)
+endif()
+
+option(ENABLE_SSL_SUPPORT "Enable SSL support" ON)
+
+if (ENABLE_SSL_SUPPORT)
+  find_package(OpenSSL 0.9.8 REQUIRED)
+
+  cmake_push_check_state()
+  set(THREADS_PREFER_PTHREAD_FLAG ON)
+  find_package(Threads REQUIRED)
+  cmake_pop_check_state()
+endif()
+
+if (MSVC)
+  set(CMAKE_C_FLAGS "/W4 /nologo ${CMAKE_C_FLAGS}")
+elseif (CMAKE_C_COMPILER_ID MATCHES ".*Clang")
+  set(CMAKE_C_FLAGS "-Wall -Wextra -Wstrict-prototypes -Wno-unused-function -fno-common -fvisibility=hidden ${CMAKE_C_FLAGS}")
+elseif (CMAKE_COMPILER_IS_GNUCC)
+  set(RMQ_C_FLAGS "-Wall -Wextra -Wstrict-prototypes -Wno-unused-function -fno-common")
+  execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
+  if (GCC_VERSION VERSION_GREATER 4.0 OR GCC_VERSION VERSION_EQUAL 4.0)
+      set(RMQ_C_FLAGS "${RMQ_C_FLAGS} -fvisibility=hidden")
+  endif()
+  set(CMAKE_C_FLAGS "${RMQ_C_FLAGS} ${CMAKE_C_FLAGS}")
+endif ()
+
+CHECK_C_COMPILER_FLAG("-std=gnu90" HAVE_GNU90)
+if (HAVE_GNU90)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90")
+else()
+  CHECK_C_COMPILER_FLAG("-std=c90" HAVE_C90)
+  if (HAVE_C90)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c90")
+  endif()
+endif()
+
+option(REGENERATE_AMQP_FRAMING "Regenerate amqp_framing.h/amqp_framing.c sources (for developer use)" OFF)
+mark_as_advanced(REGENERATE_AMQP_FRAMING)
+
+if (REGENERATE_AMQP_FRAMING)
+  find_package(PythonInterp)
+  if (NOT PYTHONINTERP_FOUND)
+    message(FATAL_ERROR "REGENERATE_AMQP_FRAMING requires Python to be available")
+  endif ()
+
+  #Determine Python Version:
+  if(PYTHON_EXECUTABLE)
+    execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c
+      "import sys; sys.stdout.write(';'.join([str(x) for x in sys.version_info[:3]]))"
+      OUTPUT_VARIABLE _VERSION
+      RESULT_VARIABLE _PYTHON_VERSION_RESULT
+      ERROR_QUIET)
+    if(NOT _PYTHON_VERSION_RESULT)
+      string(REPLACE ";" "." PYTHON_VERSION_STRING "${_VERSION}")
+      list(GET _VERSION 0 PYTHON_VERSION_MAJOR)
+      list(GET _VERSION 1 PYTHON_VERSION_MINOR)
+      list(GET _VERSION 2 PYTHON_VERSION_PATCH)
+      if(PYTHON_VERSION_PATCH EQUAL 0)
+        # it's called "Python 2.7", not "2.7.0"
+        string(REGEX REPLACE "\\.0$" "" PYTHON_VERSION_STRING "${PYTHON_VERSION_STRING}")
+      endif()
+    else()
+      # sys.version predates sys.version_info, so use that
+      execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import sys; sys.stdout.write(sys.version)"
+        OUTPUT_VARIABLE _VERSION
+        RESULT_VARIABLE _PYTHON_VERSION_RESULT
+        ERROR_QUIET)
+      if(NOT _PYTHON_VERSION_RESULT)
+        string(REGEX REPLACE " .*" "" PYTHON_VERSION_STRING "${_VERSION}")
+        string(REGEX REPLACE "^([0-9]+)\\.[0-9]+.*" "\\1" PYTHON_VERSION_MAJOR "${PYTHON_VERSION_STRING}")
+        string(REGEX REPLACE "^[0-9]+\\.([0-9])+.*" "\\1" PYTHON_VERSION_MINOR "${PYTHON_VERSION_STRING}")
+        if(PYTHON_VERSION_STRING MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+.*")
+          string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PYTHON_VERSION_PATCH "${PYTHON_VERSION_STRING}")
+        else()
+          set(PYTHON_VERSION_PATCH "0")
+        endif()
+      else()
+        # sys.version was first documented for Python 1.5, so assume
+        # this is older.
+        set(PYTHON_VERSION_STRING "1.4")
+        set(PYTHON_VERSION_MAJOR "1")
+        set(PYTHON_VERSION_MAJOR "4")
+        set(PYTHON_VERSION_MAJOR "0")
+      endif()
+    endif()
+    unset(_PYTHON_VERSION_RESULT)
+    unset(_VERSION)
+  endif(PYTHON_EXECUTABLE)
+
+  # If we're running v3.x look for a 2to3 utility
+  if (PYTHON_VERSION_MAJOR GREATER 2)
+    get_filename_component(PYTHON_EXE_DIR ${PYTHON_EXECUTABLE} PATH)
+    find_program(PYTHON_2TO3_EXECUTABLE
+      NAMES 2to3
+      HINTS ${PYTHON_EXE_DIR}
+      )
+
+    if ("PYTHON_2TO3_EXECUTABLE-NOTFOUND" STREQUAL PYTHON_2TO3_EXECUTABLE)
+      message(FATAL_ERROR "Unable to find 2to3 python utility, specify python 2.7 or specify 2to3 utility")
+    endif ()
+  endif (PYTHON_VERSION_MAJOR GREATER 2)
+
+
+  #check for json or simplejson
+  execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import json"
+    RESULT_VARIABLE CHECK_PYTHON_JSON_FAILED
+  )
+
+  if (CHECK_PYTHON_JSON_FAILED)
+    execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import simplejson"
+      RESULT_VARIABLE CHECK_PYTHON_SIMPLEJSON_FAILED
+    )
+
+    if (CHECK_PYTHON_SIMPLEJSON_FAILED)
+      message(FATAL_ERROR "REGENERATE_AMQP_FRAMING requires a python with json or simplejson modules")
+    endif (CHECK_PYTHON_SIMPLEJSON_FAILED)
+  endif (CHECK_PYTHON_JSON_FAILED)
+
+
+  find_path(AMQP_CODEGEN_DIR
+    amqp_codegen.py
+    PATHS ${CMAKE_CURRENT_SOURCE_DIR}/codegen
+          ${CMAKE_CURRENT_SOURCE_DIR}/rabbitmq-codegen
+          ${CMAKE_CURRENT_SOURCE_DIR}/../rabbitmq-codegen
+          DOC "Path to directory containing amqp_codegen.py (rabbitmq-codegen)"
+          NO_DEFAULT_PATH
+          )
+
+  if (AMQP_CODEGEN_DIR STREQUAL "AMQP_CODEGEN_DIR-NOTFOUND")
+    message(SEND_ERROR "REGENERATE_AMQP_FRAMING requires the amqp_codegen.py script. If this is a git clone you can:\n\ngit submodule init\ngit submodule update\n\n Or set AMQP_CODEGEN_DIR to directory containing amqp_codegen.py")
+  else ()
+    message(STATUS "Found amqp_codegen.py in ${AMQP_CODEGEN_DIR}")
+  endif()
+endif (REGENERATE_AMQP_FRAMING)
+
+find_package(POPT)
+find_package(XmlTo)
+find_package(Doxygen)
+
+if (POPT_FOUND AND XmlTo_FOUND)
+  set(DO_DOCS ON)
+endif()
+
+
+option(BUILD_SHARED_LIBS "Build rabbitmq-c as a shared library" ON)
+option(BUILD_STATIC_LIBS "Build rabbitmq-c as a static library" ON)
+
+option(BUILD_EXAMPLES "Build Examples" ON)
+option(BUILD_TOOLS "Build Tools (requires POPT Library)" ${POPT_FOUND})
+option(BUILD_TOOLS_DOCS "Build man pages for Tools (requires xmlto)" ${DO_DOCS})
+option(BUILD_TESTS "Build tests (run tests with make test)" ON)
+option(BUILD_API_DOCS "Build Doxygen API docs" ${DOXYGEN_FOUND})
+
+if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS)
+    message(FATAL_ERROR "One or both of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS must be set to ON to build")
+endif()
+
+add_subdirectory(librabbitmq)
+
+if (BUILD_EXAMPLES)
+  add_subdirectory(examples)
+endif ()
+
+if (BUILD_TOOLS)
+  if (POPT_FOUND)
+    add_subdirectory(tools)
+  else ()
+    message(WARNING "POpt library was not found. Tools will not be built")
+  endif ()
+endif ()
+
+if (BUILD_TESTS)
+  if (NOT BUILD_STATIC_LIBS)
+    message(FATAL_ERROR
+      "Tests can only be built against static libraries "
+      "(set BUILD_STATIC_LIBS=ON)")
+  endif ()
+  enable_testing()
+  add_subdirectory(tests)
+endif (BUILD_TESTS)
+
+if (BUILD_API_DOCS)
+  if (NOT DOXYGEN_FOUND)
+    message(FATAL_ERROR "Doxygen is required to build the API documentation")
+  endif ()
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile @ONLY)
+
+  add_custom_target(docs
+    COMMAND ${DOXYGEN_EXECUTABLE}
+    VERBATIM
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs
+    DEPENDS rabbitmq
+    COMMENT "Generating API documentation"
+    SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in
+    )
+endif ()
+
+set(libs_private ${SOCKET_LIBRARIES} ${LIBRT})
+if (ENABLE_SSL_SUPPORT)
+  set(requires_private "openssl")
+  set(libs_private ${libs_private} ${CMAKE_THREAD_LIBS_INIT})
+endif()
+
+set(prefix ${CMAKE_INSTALL_PREFIX})
+set(exec_prefix "\${prefix}")
+set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+
+configure_file(cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq/config.h)
+configure_file(librabbitmq.pc.in ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq.pc @ONLY)
+
+install(FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/librabbitmq.pc
+  DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
+  )
+
+if (BUILD_SHARED_LIBS)
+  message(STATUS "Building rabbitmq as a shared library - yes")
+else ()
+  message(STATUS "Building rabbitmq as a shared library - no")
+endif ()
+
+if (BUILD_STATIC_LIBS)
+  message(STATUS "Building rabbitmq as a static library - yes")
+else ()
+  message(STATUS "Building rabbitmq as a static library - no")
+endif ()

+ 26 - 0
ext/librabbitmq/CONTRIBUTING.md

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

+ 221 - 0
ext/librabbitmq/ChangeLog.md

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

+ 28 - 0
ext/librabbitmq/LICENSE-MIT

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

+ 128 - 0
ext/librabbitmq/README.md

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

+ 8 - 0
ext/librabbitmq/THANKS

@@ -0,0 +1,8 @@
+Thank-you to the following people for their contributions to the
+codebase:
+
+ - Scott Brooks / Epic Advertising <[email protected]>
+
+ - Frank Gönninger <[email protected]>
+
+ - Daniel Schauenberg <[email protected]>

+ 9 - 0
ext/librabbitmq/TODO

@@ -0,0 +1,9 @@
+Deal with version-mismatch-header received from the server
+
+Cope with unknown frame types better. Currently it gets horribly
+confused about frame lengths.
+
+Make client brutal by default, killing the program on any amqp
+error. Only if the user disables this behaviour will the user get to
+deal with error conditions themselves. Make use of amqp_rpc_reply
+consistent (i.e. universal), and rename it something like amqp_errno.

+ 34 - 0
ext/librabbitmq/appveyor.yml

@@ -0,0 +1,34 @@
+# appveyor configuration
+version: '{build}'
+
+# Limit history cloned. This matches what travis-CI currently does.
+clone_depth: 50
+
+environment:
+  matrix:
+    - GENERATOR: Visual Studio 12 Win64
+      BITS: 64
+    - GENERATOR: Visual Studio 12
+      BITS: 32
+
+cache:
+  - c:\deps -> appveyor.yml
+  
+# borrowed from https://github.com/FreeTDS/freetds
+install:
+  # xidel (xpath command line tool)
+  - appveyor DownloadFile "https://downloads.sourceforge.net/project/videlibri/Xidel/Xidel 0.9.6/xidel-0.9.6.win32.zip"
+  - 7z x xidel-0.9.6.win32.zip xidel.exe
+  # detect version of Windows OpenSSL binaries published by the Shining Light Productions crew
+  - xidel https://slproweb.com/products/Win32OpenSSL.html --extract "(//td/a[starts-with(@href, '/download') and starts-with(text(), 'Win32 OpenSSL') and ends-with(text(), 'Light')])[1]/translate(substring-before(substring-after(text(), 'Win32 OpenSSL v'), ' Light'), '.', '_')" > openssl_ver.txt
+  - set /P OPENSSL_VER=< openssl_ver.txt
+  # OpenSSL
+  - appveyor DownloadFile https://slproweb.com/download/Win%BITS%OpenSSL-%OPENSSL_VER%.exe	
+  - "Win%BITS%OpenSSL-%OPENSSL_VER%.exe /SP- /SILENT /SUPPRESSMSGBOXES /NORESTART"
+
+before_build:
+  - cmake -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON -DBUILD_TESTS=ON -DENABLE_SSL_SUPPORT=True -G"%GENERATOR%" .
+
+build:
+  project: ALL_BUILD.vcxproj
+  verbosity: normal

BIN
ext/librabbitmq/centos_x64/lib/librabbitmq.a


+ 103 - 0
ext/librabbitmq/cmake/CMakePushCheckState.cmake

@@ -0,0 +1,103 @@
+# This module defines two macros:
+# CMAKE_PUSH_CHECK_STATE()
+# and
+# CMAKE_POP_CHECK_STATE()
+# These two macros can be used to save and restore the state of the variables
+# CMAKE_REQUIRED_FLAGS, CMAKE_REQUIRED_DEFINITIONS, CMAKE_REQUIRED_LIBRARIES
+# and CMAKE_REQUIRED_INCLUDES used by the various Check-files coming with CMake,
+# like e.g. check_function_exists() etc.
+# The variable contents are pushed on a stack, pushing multiple times is supported.
+# This is useful e.g. when executing such tests in a Find-module, where they have to be set,
+# but after the Find-module has been executed they should have the same value
+# as they had before.
+#
+# Usage:
+#   cmake_push_check_state()
+#   set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -DSOME_MORE_DEF)
+#   check_function_exists(...)
+#   cmake_pop_check_state()
+
+#=============================================================================
+# Copyright 2006-2011 Alexander Neundorf, <[email protected]>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 
+# * Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# 
+# * Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# 
+# * Neither the names of Kitware, Inc., the Insight Software Consortium,
+#   nor the names of their contributors may be used to endorse or promote
+#   products derived from this software without specific prior written
+#   permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# ------------------------------------------------------------------------------
+# 
+# The above copyright and license notice applies to distributions of
+# CMake in source and binary form.  Some source files contain additional
+# notices of original copyright by their contributors; see each source
+# for details.  Third-party software packages supplied with CMake under
+# compatible licenses provide their own copyright notices documented in
+# corresponding subdirectories.
+# 
+# ------------------------------------------------------------------------------
+# 
+# CMake was initially developed by Kitware with the following sponsorship:
+# 
+#  * National Library of Medicine at the National Institutes of Health
+#    as part of the Insight Segmentation and Registration Toolkit (ITK).
+# 
+#  * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
+#    Visualization Initiative.
+# 
+#  * National Alliance for Medical Image Computing (NAMIC) is funded by the
+#    National Institutes of Health through the NIH Roadmap for Medical Research,
+#    Grant U54 EB005149.
+# 
+#  * Kitware, Inc.
+
+macro(CMAKE_PUSH_CHECK_STATE)
+
+   if(NOT DEFINED _CMAKE_PUSH_CHECK_STATE_COUNTER)
+      set(_CMAKE_PUSH_CHECK_STATE_COUNTER 0)
+   endif()
+
+   math(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}+1")
+
+   set(_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}    ${CMAKE_REQUIRED_INCLUDES})
+   set(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS})
+   set(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}   ${CMAKE_REQUIRED_LIBRARIES})
+   set(_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}       ${CMAKE_REQUIRED_FLAGS})
+endmacro()
+
+macro(CMAKE_POP_CHECK_STATE)
+
+# don't pop more than we pushed
+   if("${_CMAKE_PUSH_CHECK_STATE_COUNTER}" GREATER "0")
+
+      set(CMAKE_REQUIRED_INCLUDES    ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      set(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      set(CMAKE_REQUIRED_LIBRARIES   ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      set(CMAKE_REQUIRED_FLAGS       ${_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+
+      math(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}-1")
+   endif()
+
+endmacro()

+ 22 - 0
ext/librabbitmq/cmake/COPYING-CMAKE-SCRIPTS

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

+ 39 - 0
ext/librabbitmq/cmake/FindPOPT.cmake

@@ -0,0 +1,39 @@
+# - Try to find the popt options processing library
+# The module will set the following variables
+#
+#  POPT_FOUND - System has popt
+#  POPT_INCLUDE_DIR - The popt include directory
+#  POPT_LIBRARY - The libraries needed to use popt
+
+# use pkg-config to get the directories and then use these values
+# in the FIND_PATH() and FIND_LIBRARY() calls
+
+find_package(PkgConfig QUIET)
+if (PKG_CONFIG_FOUND)
+  pkg_search_module(PC_POPT QUIET popt)
+endif ()
+
+# Find the include directories
+FIND_PATH(POPT_INCLUDE_DIR
+    NAMES popt.h
+    HINTS
+          ${PC_POPT_INCLUDEDIR}
+          ${PC_POPT_INCLUDE_DIRS}
+    DOC "Path containing the popt.h include file"
+    )
+
+FIND_LIBRARY(POPT_LIBRARY
+    NAMES popt
+    HINTS
+          ${PC_POPT_LIBRARYDIR}
+          ${PC_POPT_LIBRARY_DIRS}
+    DOC "popt library path"
+    )
+
+include(FindPackageHandleStandardArgs)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(POPT
+  REQUIRED_VARS POPT_INCLUDE_DIR POPT_LIBRARY
+  VERSION_VAR PC_POPT_VERSION)
+
+MARK_AS_ADVANCED(POPT_INCLUDE_DIR POPT_LIBRARY)

+ 98 - 0
ext/librabbitmq/cmake/FindXmlTo.cmake

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

+ 205 - 0
ext/librabbitmq/cmake/GNUInstallDirs.cmake

@@ -0,0 +1,205 @@
+#.rst:
+# GNUInstallDirs
+# --------------
+#
+# Define GNU standard installation directories
+#
+# Provides install directory variables as defined for GNU software:
+#
+# ::
+#
+#   http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
+#
+# Inclusion of this module defines the following variables:
+#
+# ::
+#
+#   CMAKE_INSTALL_<dir>      - destination for files of a given type
+#   CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
+#
+# where <dir> is one of:
+#
+# ::
+#
+#   BINDIR           - user executables (bin)
+#   SBINDIR          - system admin executables (sbin)
+#   LIBEXECDIR       - program executables (libexec)
+#   SYSCONFDIR       - read-only single-machine data (etc)
+#   SHAREDSTATEDIR   - modifiable architecture-independent data (com)
+#   LOCALSTATEDIR    - modifiable single-machine data (var)
+#   LIBDIR           - object code libraries (lib or lib64 or lib/<multiarch-tuple> on Debian)
+#   INCLUDEDIR       - C header files (include)
+#   OLDINCLUDEDIR    - C header files for non-gcc (/usr/include)
+#   DATAROOTDIR      - read-only architecture-independent data root (share)
+#   DATADIR          - read-only architecture-independent data (DATAROOTDIR)
+#   INFODIR          - info documentation (DATAROOTDIR/info)
+#   LOCALEDIR        - locale-dependent data (DATAROOTDIR/locale)
+#   MANDIR           - man documentation (DATAROOTDIR/man)
+#   DOCDIR           - documentation root (DATAROOTDIR/doc/PROJECT_NAME)
+#
+# Each CMAKE_INSTALL_<dir> value may be passed to the DESTINATION
+# options of install() commands for the corresponding file type.  If the
+# includer does not define a value the above-shown default will be used
+# and the value will appear in the cache for editing by the user.  Each
+# CMAKE_INSTALL_FULL_<dir> value contains an absolute path constructed
+# from the corresponding destination by prepending (if necessary) the
+# value of CMAKE_INSTALL_PREFIX.
+
+#=============================================================================
+# Copyright 2011 Nikita Krupen'ko <[email protected]>
+# Copyright 2011 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+# Installation directories
+#
+if(NOT DEFINED CMAKE_INSTALL_BINDIR)
+  set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_SBINDIR)
+  set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR)
+  set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR)
+  set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR)
+  set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR)
+  set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
+  set(_LIBDIR_DEFAULT "lib")
+  # Override this default 'lib' with 'lib64' iff:
+  #  - we are on Linux system but NOT cross-compiling
+  #  - we are NOT on debian
+  #  - we are on a 64 bits system
+  # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf
+  # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if
+  # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu"
+  # See http://wiki.debian.org/Multiarch
+  if(CMAKE_SYSTEM_NAME MATCHES "Linux"
+      AND NOT CMAKE_CROSSCOMPILING)
+    if (EXISTS "/etc/debian_version") # is this a debian system ?
+       if(CMAKE_LIBRARY_ARCHITECTURE)
+         set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
+       endif()
+    else() # not debian, rely on CMAKE_SIZEOF_VOID_P:
+      if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
+        message(AUTHOR_WARNING
+          "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
+          "Please enable at least one language before including GNUInstallDirs.")
+      else()
+        if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
+          set(_LIBDIR_DEFAULT "lib64")
+        endif()
+      endif()
+    endif()
+  endif()
+  set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
+  set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR)
+  set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)")
+endif()
+
+if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR)
+  set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)")
+endif()
+
+#-----------------------------------------------------------------------------
+# Values whose defaults are relative to DATAROOTDIR.  Store empty values in
+# the cache and store the defaults in local variables if the cache values are
+# not set explicitly.  This auto-updates the defaults as DATAROOTDIR changes.
+
+if(NOT CMAKE_INSTALL_DATADIR)
+  set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)")
+  set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}")
+endif()
+
+if(NOT CMAKE_INSTALL_INFODIR)
+  set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)")
+  set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info")
+endif()
+
+if(NOT CMAKE_INSTALL_LOCALEDIR)
+  set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)")
+  set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale")
+endif()
+
+if(NOT CMAKE_INSTALL_MANDIR)
+  set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)")
+  set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man")
+endif()
+
+if(NOT CMAKE_INSTALL_DOCDIR)
+  set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)")
+  set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}")
+endif()
+
+#-----------------------------------------------------------------------------
+
+mark_as_advanced(
+  CMAKE_INSTALL_BINDIR
+  CMAKE_INSTALL_SBINDIR
+  CMAKE_INSTALL_LIBEXECDIR
+  CMAKE_INSTALL_SYSCONFDIR
+  CMAKE_INSTALL_SHAREDSTATEDIR
+  CMAKE_INSTALL_LOCALSTATEDIR
+  CMAKE_INSTALL_LIBDIR
+  CMAKE_INSTALL_INCLUDEDIR
+  CMAKE_INSTALL_OLDINCLUDEDIR
+  CMAKE_INSTALL_DATAROOTDIR
+  CMAKE_INSTALL_DATADIR
+  CMAKE_INSTALL_INFODIR
+  CMAKE_INSTALL_LOCALEDIR
+  CMAKE_INSTALL_MANDIR
+  CMAKE_INSTALL_DOCDIR
+  )
+
+# Result directories
+#
+foreach(dir
+    BINDIR
+    SBINDIR
+    LIBEXECDIR
+    SYSCONFDIR
+    SHAREDSTATEDIR
+    LOCALSTATEDIR
+    LIBDIR
+    INCLUDEDIR
+    OLDINCLUDEDIR
+    DATAROOTDIR
+    DATADIR
+    INFODIR
+    LOCALEDIR
+    MANDIR
+    DOCDIR
+    )
+  if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}})
+    set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}")
+  else()
+    set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}")
+  endif()
+endforeach()

+ 28 - 0
ext/librabbitmq/cmake/TestCInline.cmake

@@ -0,0 +1,28 @@
+#Inspired from http://www.cmake.org/Wiki/CMakeTestInline
+
+IF(NOT DEFINED C_INLINE_KEYWORD)
+
+  SET(INLINE_TEST_SRC "/* Inspired by autoconf's c.m4 */
+static inline int static_foo() {return 0\;}
+int main(int argc, char *argv[]){return 0\;}
+")
+
+  FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/CMakeTestCInline.c ${INLINE_TEST_SRC})
+
+  FOREACH(KEYWORD "inline" "__inline__" "__inline")
+    IF(NOT DEFINED C_INLINE)
+      TRY_COMPILE(C_HAS_${KEYWORD}
+        ${CMAKE_CURRENT_BINARY_DIR}
+        ${CMAKE_CURRENT_BINARY_DIR}/CMakeTestCInline.c
+        COMPILE_DEFINITIONS "-Dinline=${KEYWORD}"
+        )
+      IF(C_HAS_${KEYWORD})
+        SET(C_INLINE ${KEYWORD})
+      ENDIF(C_HAS_${KEYWORD})
+    ENDIF(NOT DEFINED C_INLINE)
+  ENDFOREACH(KEYWORD)
+
+  SET(C_INLINE_KEYWORD ${C_INLINE} CACHE INTERNAL "The keyword needed by the C compiler to inline a function" FORCE)
+  message(STATUS "Found C inline keyword: ${C_INLINE_KEYWORD}")
+
+ENDIF(NOT DEFINED C_INLINE_KEYWORD)

+ 14 - 0
ext/librabbitmq/cmake/config.h.in

@@ -0,0 +1,14 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#ifndef __cplusplus
+# define inline ${C_INLINE_KEYWORD}
+#endif
+
+#cmakedefine HAVE_SELECT
+
+#cmakedefine HAVE_POLL
+
+#define AMQ_PLATFORM "@CMAKE_SYSTEM@"
+
+#endif /* CONFIG_H */

+ 17 - 0
ext/librabbitmq/coverity/model.c

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

+ 317 - 0
ext/librabbitmq/docs/Doxyfile.in

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

+ 52 - 0
ext/librabbitmq/examples/CMakeLists.txt

@@ -0,0 +1,52 @@
+if (NOT BUILD_SHARED_LIBS)
+  add_definitions(-DAMQP_STATIC)
+endif()
+
+include_directories(${LIBRABBITMQ_INCLUDE_DIRS})
+
+if (WIN32)
+    set(PLATFORM_DIR win32)
+else (WIN32)
+    set(PLATFORM_DIR unix)
+endif (WIN32)
+
+set(COMMON_SRCS
+    utils.h
+    utils.c
+    ${PLATFORM_DIR}/platform_utils.c
+    )
+
+add_executable(amqp_sendstring amqp_sendstring.c ${COMMON_SRCS})
+target_link_libraries(amqp_sendstring ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_rpc_sendstring_client amqp_rpc_sendstring_client.c ${COMMON_SRCS})
+target_link_libraries(amqp_rpc_sendstring_client ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_exchange_declare amqp_exchange_declare.c ${COMMON_SRCS})
+target_link_libraries(amqp_exchange_declare ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_listen amqp_listen.c ${COMMON_SRCS})
+target_link_libraries(amqp_listen ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_producer amqp_producer.c ${COMMON_SRCS})
+target_link_libraries(amqp_producer ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_connect_timeout amqp_connect_timeout.c ${COMMON_SRCS})
+target_link_libraries(amqp_connect_timeout ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_consumer amqp_consumer.c ${COMMON_SRCS})
+target_link_libraries(amqp_consumer ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_unbind amqp_unbind.c ${COMMON_SRCS})
+target_link_libraries(amqp_unbind ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_bind amqp_bind.c ${COMMON_SRCS})
+target_link_libraries(amqp_bind ${RMQ_LIBRARY_TARGET})
+
+add_executable(amqp_listenq amqp_listenq.c ${COMMON_SRCS})
+target_link_libraries(amqp_listenq ${RMQ_LIBRARY_TARGET})
+
+if (ENABLE_SSL_SUPPORT)
+add_executable(amqp_ssl_connect amqp_ssl_connect.c ${COMMON_SRCS})
+target_link_libraries(amqp_ssl_connect ${RMQ_LIBRARY_TARGET})
+endif (ENABLE_SSL_SUPPORT)

+ 95 - 0
ext/librabbitmq/examples/amqp_bind.c

@@ -0,0 +1,95 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  char const *exchange;
+  char const *bindingkey;
+  char const *queue;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  if (argc < 6) {
+    fprintf(stderr, "Usage: amqp_bind host port exchange bindingkey queue\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  exchange = argv[3];
+  bindingkey = argv[4];
+  queue = argv[5];
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  amqp_queue_bind(conn, 1, amqp_cstring_bytes(queue),
+                  amqp_cstring_bytes(exchange), amqp_cstring_bytes(bindingkey),
+                  amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Unbinding");
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+  return 0;
+}

+ 114 - 0
ext/librabbitmq/examples/amqp_connect_timeout.c

@@ -0,0 +1,114 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by Bogdan Padalko are Copyright (c) 2013.
+ * Bogdan Padalko. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include <assert.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <Winsock2.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port;
+  amqp_socket_t *socket;
+  amqp_connection_state_t conn;
+  struct timeval tval;
+  struct timeval *tv;
+
+  if (argc < 3) {
+    fprintf(stderr,
+            "Usage: amqp_connect_timeout host port [timeout_sec "
+            "[timeout_usec=0]]\n");
+    return 1;
+  }
+
+  if (argc > 3) {
+    tv = &tval;
+
+    tv->tv_sec = atoi(argv[3]);
+
+    if (argc > 4) {
+      tv->tv_usec = atoi(argv[4]);
+    } else {
+      tv->tv_usec = 0;
+    }
+
+  } else {
+    tv = NULL;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  die_on_error(amqp_socket_open_noblock(socket, hostname, port, tv),
+               "opening TCP socket");
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+
+  printf("Done\n");
+  return 0;
+}

+ 215 - 0
ext/librabbitmq/examples/amqp_consumer.c

@@ -0,0 +1,215 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include <assert.h>
+
+#include "utils.h"
+
+#define SUMMARY_EVERY_US 1000000
+
+static void run(amqp_connection_state_t conn) {
+  uint64_t start_time = now_microseconds();
+  int received = 0;
+  int previous_received = 0;
+  uint64_t previous_report_time = start_time;
+  uint64_t next_summary_time = start_time + SUMMARY_EVERY_US;
+
+  amqp_frame_t frame;
+
+  uint64_t now;
+
+  for (;;) {
+    amqp_rpc_reply_t ret;
+    amqp_envelope_t envelope;
+
+    now = now_microseconds();
+    if (now > next_summary_time) {
+      int countOverInterval = received - previous_received;
+      double intervalRate =
+          countOverInterval / ((now - previous_report_time) / 1000000.0);
+      printf("%d ms: Received %d - %d since last report (%d Hz)\n",
+             (int)(now - start_time) / 1000, received, countOverInterval,
+             (int)intervalRate);
+
+      previous_received = received;
+      previous_report_time = now;
+      next_summary_time += SUMMARY_EVERY_US;
+    }
+
+    amqp_maybe_release_buffers(conn);
+    ret = amqp_consume_message(conn, &envelope, NULL, 0);
+
+    if (AMQP_RESPONSE_NORMAL != ret.reply_type) {
+      if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type &&
+          AMQP_STATUS_UNEXPECTED_STATE == ret.library_error) {
+        if (AMQP_STATUS_OK != amqp_simple_wait_frame(conn, &frame)) {
+          return;
+        }
+
+        if (AMQP_FRAME_METHOD == frame.frame_type) {
+          switch (frame.payload.method.id) {
+            case AMQP_BASIC_ACK_METHOD:
+              /* if we've turned publisher confirms on, and we've published a
+               * message here is a message being confirmed.
+               */
+              break;
+            case AMQP_BASIC_RETURN_METHOD:
+              /* if a published message couldn't be routed and the mandatory
+               * flag was set this is what would be returned. The message then
+               * needs to be read.
+               */
+              {
+                amqp_message_t message;
+                ret = amqp_read_message(conn, frame.channel, &message, 0);
+                if (AMQP_RESPONSE_NORMAL != ret.reply_type) {
+                  return;
+                }
+
+                amqp_destroy_message(&message);
+              }
+
+              break;
+
+            case AMQP_CHANNEL_CLOSE_METHOD:
+              /* a channel.close method happens when a channel exception occurs,
+               * this can happen by publishing to an exchange that doesn't exist
+               * for example.
+               *
+               * In this case you would need to open another channel redeclare
+               * any queues that were declared auto-delete, and restart any
+               * consumers that were attached to the previous channel.
+               */
+              return;
+
+            case AMQP_CONNECTION_CLOSE_METHOD:
+              /* a connection.close method happens when a connection exception
+               * occurs, this can happen by trying to use a channel that isn't
+               * open for example.
+               *
+               * In this case the whole connection must be restarted.
+               */
+              return;
+
+            default:
+              fprintf(stderr, "An unexpected method was received %u\n",
+                      frame.payload.method.id);
+              return;
+          }
+        }
+      }
+
+    } else {
+      amqp_destroy_envelope(&envelope);
+    }
+
+    received++;
+  }
+}
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  char const *exchange;
+  char const *bindingkey;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  amqp_bytes_t queuename;
+
+  if (argc < 3) {
+    fprintf(stderr, "Usage: amqp_consumer host port\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  exchange = "amq.direct";   /* argv[3]; */
+  bindingkey = "test queue"; /* argv[4]; */
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  {
+    amqp_queue_declare_ok_t *r = amqp_queue_declare(
+        conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table);
+    die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue");
+    queuename = amqp_bytes_malloc_dup(r->queue);
+    if (queuename.bytes == NULL) {
+      fprintf(stderr, "Out of memory while copying queue name");
+      return 1;
+    }
+  }
+
+  amqp_queue_bind(conn, 1, queuename, amqp_cstring_bytes(exchange),
+                  amqp_cstring_bytes(bindingkey), amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Binding queue");
+
+  amqp_basic_consume(conn, 1, queuename, amqp_empty_bytes, 0, 1, 0,
+                     amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
+
+  run(conn);
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+
+  return 0;
+}

+ 94 - 0
ext/librabbitmq/examples/amqp_exchange_declare.c

@@ -0,0 +1,94 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  char const *exchange;
+  char const *exchangetype;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  if (argc < 5) {
+    fprintf(stderr,
+            "Usage: amqp_exchange_declare host port exchange exchangetype\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  exchange = argv[3];
+  exchangetype = argv[4];
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  amqp_exchange_declare(conn, 1, amqp_cstring_bytes(exchange),
+                        amqp_cstring_bytes(exchangetype), 0, 0, 0, 0,
+                        amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring exchange");
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+  return 0;
+}

+ 143 - 0
ext/librabbitmq/examples/amqp_listen.c

@@ -0,0 +1,143 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include <assert.h>
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  char const *exchange;
+  char const *bindingkey;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  amqp_bytes_t queuename;
+
+  if (argc < 5) {
+    fprintf(stderr, "Usage: amqp_listen host port exchange bindingkey\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  exchange = argv[3];
+  bindingkey = argv[4];
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  {
+    amqp_queue_declare_ok_t *r = amqp_queue_declare(
+        conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table);
+    die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue");
+    queuename = amqp_bytes_malloc_dup(r->queue);
+    if (queuename.bytes == NULL) {
+      fprintf(stderr, "Out of memory while copying queue name");
+      return 1;
+    }
+  }
+
+  amqp_queue_bind(conn, 1, queuename, amqp_cstring_bytes(exchange),
+                  amqp_cstring_bytes(bindingkey), amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Binding queue");
+
+  amqp_basic_consume(conn, 1, queuename, amqp_empty_bytes, 0, 1, 0,
+                     amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
+
+  {
+    for (;;) {
+      amqp_rpc_reply_t res;
+      amqp_envelope_t envelope;
+
+      amqp_maybe_release_buffers(conn);
+
+      res = amqp_consume_message(conn, &envelope, NULL, 0);
+
+      if (AMQP_RESPONSE_NORMAL != res.reply_type) {
+        break;
+      }
+
+      printf("Delivery %u, exchange %.*s routingkey %.*s\n",
+             (unsigned)envelope.delivery_tag, (int)envelope.exchange.len,
+             (char *)envelope.exchange.bytes, (int)envelope.routing_key.len,
+             (char *)envelope.routing_key.bytes);
+
+      if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
+        printf("Content-type: %.*s\n",
+               (int)envelope.message.properties.content_type.len,
+               (char *)envelope.message.properties.content_type.bytes);
+      }
+      printf("----\n");
+
+      amqp_dump(envelope.message.body.bytes, envelope.message.body.len);
+
+      amqp_destroy_envelope(&envelope);
+    }
+  }
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+
+  return 0;
+}

+ 122 - 0
ext/librabbitmq/examples/amqp_listenq.c

@@ -0,0 +1,122 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include <assert.h>
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  char const *queuename;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  if (argc < 4) {
+    fprintf(stderr, "Usage: amqp_listenq host port queuename\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  queuename = argv[3];
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  amqp_basic_consume(conn, 1, amqp_cstring_bytes(queuename), amqp_empty_bytes,
+                     0, 0, 0, amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
+
+  for (;;) {
+    amqp_rpc_reply_t res;
+    amqp_envelope_t envelope;
+
+    amqp_maybe_release_buffers(conn);
+
+    res = amqp_consume_message(conn, &envelope, NULL, 0);
+
+    if (AMQP_RESPONSE_NORMAL != res.reply_type) {
+      break;
+    }
+
+    printf("Delivery %u, exchange %.*s routingkey %.*s\n",
+           (unsigned)envelope.delivery_tag, (int)envelope.exchange.len,
+           (char *)envelope.exchange.bytes, (int)envelope.routing_key.len,
+           (char *)envelope.routing_key.bytes);
+
+    if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
+      printf("Content-type: %.*s\n",
+             (int)envelope.message.properties.content_type.len,
+             (char *)envelope.message.properties.content_type.bytes);
+    }
+    printf("----\n");
+
+    amqp_dump(envelope.message.body.bytes, envelope.message.body.len);
+
+    amqp_destroy_envelope(&envelope);
+  }
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+
+  return 0;
+}

+ 150 - 0
ext/librabbitmq/examples/amqp_producer.c

@@ -0,0 +1,150 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include "utils.h"
+
+#define SUMMARY_EVERY_US 1000000
+
+static void send_batch(amqp_connection_state_t conn, char const *queue_name,
+                       int rate_limit, int message_count) {
+  uint64_t start_time = now_microseconds();
+  int i;
+  int sent = 0;
+  int previous_sent = 0;
+  uint64_t previous_report_time = start_time;
+  uint64_t next_summary_time = start_time + SUMMARY_EVERY_US;
+
+  char message[256];
+  amqp_bytes_t message_bytes;
+
+  for (i = 0; i < (int)sizeof(message); i++) {
+    message[i] = i & 0xff;
+  }
+
+  message_bytes.len = sizeof(message);
+  message_bytes.bytes = message;
+
+  for (i = 0; i < message_count; i++) {
+    uint64_t now = now_microseconds();
+
+    die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes("amq.direct"),
+                                    amqp_cstring_bytes(queue_name), 0, 0, NULL,
+                                    message_bytes),
+                 "Publishing");
+    sent++;
+    if (now > next_summary_time) {
+      int countOverInterval = sent - previous_sent;
+      double intervalRate =
+          countOverInterval / ((now - previous_report_time) / 1000000.0);
+      printf("%d ms: Sent %d - %d since last report (%d Hz)\n",
+             (int)(now - start_time) / 1000, sent, countOverInterval,
+             (int)intervalRate);
+
+      previous_sent = sent;
+      previous_report_time = now;
+      next_summary_time += SUMMARY_EVERY_US;
+    }
+
+    while (((i * 1000000.0) / (now - start_time)) > rate_limit) {
+      microsleep(2000);
+      now = now_microseconds();
+    }
+  }
+
+  {
+    uint64_t stop_time = now_microseconds();
+    int total_delta = (int)(stop_time - start_time);
+
+    printf("PRODUCER - Message count: %d\n", message_count);
+    printf("Total time, milliseconds: %d\n", total_delta / 1000);
+    printf("Overall messages-per-second: %g\n",
+           (message_count / (total_delta / 1000000.0)));
+  }
+}
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  int rate_limit;
+  int message_count;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  if (argc < 5) {
+    fprintf(stderr,
+            "Usage: amqp_producer host port rate_limit message_count\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  rate_limit = atoi(argv[3]);
+  message_count = atoi(argv[4]);
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  send_batch(conn, "test queue", rate_limit, message_count);
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+  return 0;
+}

+ 243 - 0
ext/librabbitmq/examples/amqp_rpc_sendstring_client.c

@@ -0,0 +1,243 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include <assert.h>
+
+#include "utils.h"
+
+int main(int argc, char *argv[]) {
+  char const *hostname;
+  int port, status;
+  char const *exchange;
+  char const *routingkey;
+  char const *messagebody;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+  amqp_bytes_t reply_to_queue;
+
+  if (argc < 6) { /* minimum number of mandatory arguments */
+    fprintf(stderr,
+            "usage:\namqp_rpc_sendstring_client host port exchange routingkey "
+            "messagebody\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  exchange = argv[3];
+  routingkey = argv[4];
+  messagebody = argv[5];
+
+  /*
+     establish a channel that is used to connect RabbitMQ server
+  */
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  /*
+     create private reply_to queue
+  */
+
+  {
+    amqp_queue_declare_ok_t *r = amqp_queue_declare(
+        conn, 1, amqp_empty_bytes, 0, 0, 0, 1, amqp_empty_table);
+    die_on_amqp_error(amqp_get_rpc_reply(conn), "Declaring queue");
+    reply_to_queue = amqp_bytes_malloc_dup(r->queue);
+    if (reply_to_queue.bytes == NULL) {
+      fprintf(stderr, "Out of memory while copying queue name");
+      return 1;
+    }
+  }
+
+  /*
+     send the message
+  */
+
+  {
+    /*
+      set properties
+    */
+    amqp_basic_properties_t props;
+    props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG |
+                   AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_REPLY_TO_FLAG |
+                   AMQP_BASIC_CORRELATION_ID_FLAG;
+    props.content_type = amqp_cstring_bytes("text/plain");
+    props.delivery_mode = 2; /* persistent delivery mode */
+    props.reply_to = amqp_bytes_malloc_dup(reply_to_queue);
+    if (props.reply_to.bytes == NULL) {
+      fprintf(stderr, "Out of memory while copying queue name");
+      return 1;
+    }
+    props.correlation_id = amqp_cstring_bytes("1");
+
+    /*
+      publish
+    */
+    die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange),
+                                    amqp_cstring_bytes(routingkey), 0, 0,
+                                    &props, amqp_cstring_bytes(messagebody)),
+                 "Publishing");
+
+    amqp_bytes_free(props.reply_to);
+  }
+
+  /*
+    wait an answer
+  */
+
+  {
+    amqp_basic_consume(conn, 1, reply_to_queue, amqp_empty_bytes, 0, 1, 0,
+                       amqp_empty_table);
+    die_on_amqp_error(amqp_get_rpc_reply(conn), "Consuming");
+    amqp_bytes_free(reply_to_queue);
+
+    {
+      amqp_frame_t frame;
+      int result;
+
+      amqp_basic_deliver_t *d;
+      amqp_basic_properties_t *p;
+      size_t body_target;
+      size_t body_received;
+
+      for (;;) {
+        amqp_maybe_release_buffers(conn);
+        result = amqp_simple_wait_frame(conn, &frame);
+        printf("Result: %d\n", result);
+        if (result < 0) {
+          break;
+        }
+
+        printf("Frame type: %u channel: %u\n", frame.frame_type, frame.channel);
+        if (frame.frame_type != AMQP_FRAME_METHOD) {
+          continue;
+        }
+
+        printf("Method: %s\n", amqp_method_name(frame.payload.method.id));
+        if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) {
+          continue;
+        }
+
+        d = (amqp_basic_deliver_t *)frame.payload.method.decoded;
+        printf("Delivery: %u exchange: %.*s routingkey: %.*s\n",
+               (unsigned)d->delivery_tag, (int)d->exchange.len,
+               (char *)d->exchange.bytes, (int)d->routing_key.len,
+               (char *)d->routing_key.bytes);
+
+        result = amqp_simple_wait_frame(conn, &frame);
+        if (result < 0) {
+          break;
+        }
+
+        if (frame.frame_type != AMQP_FRAME_HEADER) {
+          fprintf(stderr, "Expected header!");
+          abort();
+        }
+        p = (amqp_basic_properties_t *)frame.payload.properties.decoded;
+        if (p->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
+          printf("Content-type: %.*s\n", (int)p->content_type.len,
+                 (char *)p->content_type.bytes);
+        }
+        printf("----\n");
+
+        body_target = (size_t)frame.payload.properties.body_size;
+        body_received = 0;
+
+        while (body_received < body_target) {
+          result = amqp_simple_wait_frame(conn, &frame);
+          if (result < 0) {
+            break;
+          }
+
+          if (frame.frame_type != AMQP_FRAME_BODY) {
+            fprintf(stderr, "Expected body!");
+            abort();
+          }
+
+          body_received += frame.payload.body_fragment.len;
+          assert(body_received <= body_target);
+
+          amqp_dump(frame.payload.body_fragment.bytes,
+                    frame.payload.body_fragment.len);
+        }
+
+        if (body_received != body_target) {
+          /* Can only happen when amqp_simple_wait_frame returns <= 0 */
+          /* We break here to close the connection */
+          break;
+        }
+
+        /* everything was fine, we can quit now because we received the reply */
+        break;
+      }
+    }
+  }
+
+  /*
+     closing
+  */
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+
+  return 0;
+}

+ 103 - 0
ext/librabbitmq/examples/amqp_sendstring.c

@@ -0,0 +1,103 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  char const *exchange;
+  char const *routingkey;
+  char const *messagebody;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  if (argc < 6) {
+    fprintf(
+        stderr,
+        "Usage: amqp_sendstring host port exchange routingkey messagebody\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  exchange = argv[3];
+  routingkey = argv[4];
+  messagebody = argv[5];
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  {
+    amqp_basic_properties_t props;
+    props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG;
+    props.content_type = amqp_cstring_bytes("text/plain");
+    props.delivery_mode = 2; /* persistent delivery mode */
+    die_on_error(amqp_basic_publish(conn, 1, amqp_cstring_bytes(exchange),
+                                    amqp_cstring_bytes(routingkey), 0, 0,
+                                    &props, amqp_cstring_bytes(messagebody)),
+                 "Publishing");
+  }
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+  return 0;
+}

+ 135 - 0
ext/librabbitmq/examples/amqp_ssl_connect.c

@@ -0,0 +1,135 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by Mike Steinert are Copyright (c) 2012-2013
+ * Mike Steinert. All Rights Reserved.
+ *
+ * Portions created by Bogdan Padalko are Copyright (c) 2013.
+ * Bogdan Padalko. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_ssl_socket.h>
+
+#include <assert.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <Winsock2.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port;
+  int timeout;
+  amqp_socket_t *socket;
+  amqp_connection_state_t conn;
+  struct timeval tval;
+  struct timeval *tv;
+
+  if (argc < 3) {
+    fprintf(stderr,
+            "Usage: amqps_connect_timeout host port timeout_sec "
+            "[cacert.pem [verifypeer] [verifyhostname] [key.pem cert.pem]]\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+
+  timeout = atoi(argv[3]);
+  if (timeout > 0) {
+    tv = &tval;
+
+    tv->tv_sec = timeout;
+    tv->tv_usec = 0;
+  } else {
+    tv = NULL;
+  }
+
+  conn = amqp_new_connection();
+
+  socket = amqp_ssl_socket_new(conn);
+  if (!socket) {
+    die("creating SSL/TLS socket");
+  }
+
+  amqp_ssl_socket_set_verify_peer(socket, 0);
+  amqp_ssl_socket_set_verify_hostname(socket, 0);
+
+  if (argc > 5) {
+    int nextarg = 5;
+    die_on_error(amqp_ssl_socket_set_cacert(socket, argv[4]),
+                 "setting CA certificate");
+    if (argc > nextarg && !strcmp("verifypeer", argv[nextarg])) {
+      amqp_ssl_socket_set_verify_peer(socket, 1);
+      nextarg++;
+    }
+    if (argc > nextarg && !strcmp("verifyhostname", argv[nextarg])) {
+      amqp_ssl_socket_set_verify_hostname(socket, 1);
+      nextarg++;
+    }
+    if (argc > nextarg + 1) {
+      die_on_error(
+          amqp_ssl_socket_set_key(socket, argv[nextarg + 1], argv[nextarg]),
+          "setting client key");
+    }
+  }
+
+  die_on_error(amqp_socket_open_noblock(socket, hostname, port, tv),
+               "opening SSL/TLS connection");
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+  die_on_error(amqp_uninitialize_ssl_library(), "Uninitializing SSL library");
+
+  printf("Done\n");
+  return 0;
+}

+ 95 - 0
ext/librabbitmq/examples/amqp_unbind.c

@@ -0,0 +1,95 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_tcp_socket.h>
+
+#include "utils.h"
+
+int main(int argc, char const *const *argv) {
+  char const *hostname;
+  int port, status;
+  char const *exchange;
+  char const *bindingkey;
+  char const *queue;
+  amqp_socket_t *socket = NULL;
+  amqp_connection_state_t conn;
+
+  if (argc < 6) {
+    fprintf(stderr, "Usage: amqp_unbind host port exchange bindingkey queue\n");
+    return 1;
+  }
+
+  hostname = argv[1];
+  port = atoi(argv[2]);
+  exchange = argv[3];
+  bindingkey = argv[4];
+  queue = argv[5];
+
+  conn = amqp_new_connection();
+
+  socket = amqp_tcp_socket_new(conn);
+  if (!socket) {
+    die("creating TCP socket");
+  }
+
+  status = amqp_socket_open(socket, hostname, port);
+  if (status) {
+    die("opening TCP socket");
+  }
+
+  die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,
+                               "guest", "guest"),
+                    "Logging in");
+  amqp_channel_open(conn, 1);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
+
+  amqp_queue_unbind(conn, 1, amqp_cstring_bytes(queue),
+                    amqp_cstring_bytes(exchange),
+                    amqp_cstring_bytes(bindingkey), amqp_empty_table);
+  die_on_amqp_error(amqp_get_rpc_reply(conn), "Unbinding");
+
+  die_on_amqp_error(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS),
+                    "Closing channel");
+  die_on_amqp_error(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+                    "Closing connection");
+  die_on_error(amqp_destroy_connection(conn), "Ending connection");
+  return 0;
+}

+ 52 - 0
ext/librabbitmq/examples/unix/platform_utils.c

@@ -0,0 +1,52 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+uint64_t now_microseconds(void) {
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;
+}
+
+void microsleep(int usec) {
+  struct timespec req;
+  req.tv_sec = 0;
+  req.tv_nsec = 1000 * usec;
+  nanosleep(&req, NULL);
+}

+ 188 - 0
ext/librabbitmq/examples/utils.c

@@ -0,0 +1,188 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp.h>
+#include <amqp_framing.h>
+#include <stdint.h>
+
+#include "utils.h"
+
+void die(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+void die_on_error(int x, char const *context) {
+  if (x < 0) {
+    fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x));
+    exit(1);
+  }
+}
+
+void die_on_amqp_error(amqp_rpc_reply_t x, char const *context) {
+  switch (x.reply_type) {
+    case AMQP_RESPONSE_NORMAL:
+      return;
+
+    case AMQP_RESPONSE_NONE:
+      fprintf(stderr, "%s: missing RPC reply type!\n", context);
+      break;
+
+    case AMQP_RESPONSE_LIBRARY_EXCEPTION:
+      fprintf(stderr, "%s: %s\n", context, amqp_error_string2(x.library_error));
+      break;
+
+    case AMQP_RESPONSE_SERVER_EXCEPTION:
+      switch (x.reply.id) {
+        case AMQP_CONNECTION_CLOSE_METHOD: {
+          amqp_connection_close_t *m =
+              (amqp_connection_close_t *)x.reply.decoded;
+          fprintf(stderr, "%s: server connection error %uh, message: %.*s\n",
+                  context, m->reply_code, (int)m->reply_text.len,
+                  (char *)m->reply_text.bytes);
+          break;
+        }
+        case AMQP_CHANNEL_CLOSE_METHOD: {
+          amqp_channel_close_t *m = (amqp_channel_close_t *)x.reply.decoded;
+          fprintf(stderr, "%s: server channel error %uh, message: %.*s\n",
+                  context, m->reply_code, (int)m->reply_text.len,
+                  (char *)m->reply_text.bytes);
+          break;
+        }
+        default:
+          fprintf(stderr, "%s: unknown server error, method id 0x%08X\n",
+                  context, x.reply.id);
+          break;
+      }
+      break;
+  }
+
+  exit(1);
+}
+
+static void dump_row(long count, int numinrow, int *chs) {
+  int i;
+
+  printf("%08lX:", count - numinrow);
+
+  if (numinrow > 0) {
+    for (i = 0; i < numinrow; i++) {
+      if (i == 8) {
+        printf(" :");
+      }
+      printf(" %02X", chs[i]);
+    }
+    for (i = numinrow; i < 16; i++) {
+      if (i == 8) {
+        printf(" :");
+      }
+      printf("   ");
+    }
+    printf("  ");
+    for (i = 0; i < numinrow; i++) {
+      if (isprint(chs[i])) {
+        printf("%c", chs[i]);
+      } else {
+        printf(".");
+      }
+    }
+  }
+  printf("\n");
+}
+
+static int rows_eq(int *a, int *b) {
+  int i;
+
+  for (i = 0; i < 16; i++)
+    if (a[i] != b[i]) {
+      return 0;
+    }
+
+  return 1;
+}
+
+void amqp_dump(void const *buffer, size_t len) {
+  unsigned char *buf = (unsigned char *)buffer;
+  long count = 0;
+  int numinrow = 0;
+  int chs[16];
+  int oldchs[16] = {0};
+  int showed_dots = 0;
+  size_t i;
+
+  for (i = 0; i < len; i++) {
+    int ch = buf[i];
+
+    if (numinrow == 16) {
+      int j;
+
+      if (rows_eq(oldchs, chs)) {
+        if (!showed_dots) {
+          showed_dots = 1;
+          printf(
+              "          .. .. .. .. .. .. .. .. : .. .. .. .. .. .. .. ..\n");
+        }
+      } else {
+        showed_dots = 0;
+        dump_row(count, numinrow, chs);
+      }
+
+      for (j = 0; j < 16; j++) {
+        oldchs[j] = chs[j];
+      }
+
+      numinrow = 0;
+    }
+
+    count++;
+    chs[numinrow++] = ch;
+  }
+
+  dump_row(count, numinrow, chs);
+
+  if (numinrow != 0) {
+    printf("%08lX:\n", count);
+  }
+}

+ 48 - 0
ext/librabbitmq/examples/utils.h

@@ -0,0 +1,48 @@
+#ifndef librabbitmq_examples_utils_h
+#define librabbitmq_examples_utils_h
+
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+void die(const char *fmt, ...);
+extern void die_on_error(int x, char const *context);
+extern void die_on_amqp_error(amqp_rpc_reply_t x, char const *context);
+
+extern void amqp_dump(void const *buffer, size_t len);
+
+extern uint64_t now_microseconds(void);
+extern void microsleep(int usec);
+
+#endif

+ 47 - 0
ext/librabbitmq/examples/win32/platform_utils.c

@@ -0,0 +1,47 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+
+#include <windows.h>
+
+uint64_t now_microseconds(void) {
+  FILETIME ft;
+  GetSystemTimeAsFileTime(&ft);
+  return (((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime) /
+         10;
+}
+
+void microsleep(int usec) { Sleep(usec / 1000); }

+ 13 - 0
ext/librabbitmq/librabbitmq.pc.in

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

+ 186 - 0
ext/librabbitmq/librabbitmq/CMakeLists.txt

@@ -0,0 +1,186 @@
+project(librabbitmq "C")
+
+if (REGENERATE_AMQP_FRAMING)
+  set(AMQP_CODEGEN_PY "${CMAKE_CURRENT_BINARY_DIR}/amqp_codegen.py")
+  set(CODEGEN_PY "${CMAKE_CURRENT_BINARY_DIR}/codegen.py")
+  set(AMQP_SPEC_JSON_PATH "${AMQP_CODEGEN_DIR}/amqp-rabbitmq-0.9.1.json")
+  set(AMQP_FRAMING_H_PATH ${CMAKE_CURRENT_BINARY_DIR}/amqp_framing.h)
+  set(AMQP_FRAMING_C_PATH ${CMAKE_CURRENT_BINARY_DIR}/amqp_framing.c)
+
+  if (PYTHON_VERSION_MAJOR GREATER 2)
+    set(CONVERT_CODEGEN ${PYTHON_2TO3_EXECUTABLE} -w ${CODEGEN_PY} > codegen_2to3.out)
+    set(CONVERT_AMQP_CODEGEN ${PYTHON_2TO3_EXECUTABLE} -w ${AMQP_CODEGEN_PY} > amqp_codegen_2to3.out)
+  else ()
+    set(CONVERT_CODEGEN "")
+    set(CONVERT_AMQP_CODEGEN "")
+  endif ()
+
+  add_custom_command(
+    OUTPUT ${CODEGEN_PY}
+    COMMAND ${CMAKE_COMMAND} ARGS -E copy ${CMAKE_CURRENT_SOURCE_DIR}/codegen.py ${CODEGEN_PY}
+    COMMAND ${CONVERT_CODEGEN}
+    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/codegen.py
+    VERBATIM)
+
+  add_custom_command(
+    OUTPUT ${AMQP_CODEGEN_PY}
+    COMMAND ${CMAKE_COMMAND} ARGS -E copy ${AMQP_CODEGEN_DIR}/amqp_codegen.py ${AMQP_CODEGEN_PY}
+    COMMAND ${CONVERT_AMQP_CODEGEN}
+    DEPENDS ${AMQP_CODEGEN_DIR}/amqp_codegen.py ${AMQP_CODEGEN_TARGET}
+    VERBATIM)
+
+  add_custom_command(
+    OUTPUT ${AMQP_FRAMING_H_PATH}
+    COMMAND ${PYTHON_EXECUTABLE} ARGS ${CODEGEN_PY} header ${AMQP_SPEC_JSON_PATH} ${AMQP_FRAMING_H_PATH}
+    DEPENDS ${AMQP_SPEC_JSON_PATH} ${CODEGEN_PY} ${AMQP_CODEGEN_PY}
+    VERBATIM)
+
+  add_custom_command(
+    OUTPUT ${AMQP_FRAMING_C_PATH}
+    COMMAND ${PYTHON_EXECUTABLE} ARGS ${CODEGEN_PY} body ${AMQP_SPEC_JSON_PATH} ${AMQP_FRAMING_C_PATH}
+    DEPENDS ${AMQP_SPEC_JSON_PATH} ${CODEGEN_PY} ${AMQP_CODEGEN_PY}
+    VERBATIM)
+else (REGENERATE_AMQP_FRAMING)
+  set(AMQP_FRAMING_H_PATH ${CMAKE_CURRENT_SOURCE_DIR}/amqp_framing.h)
+  set(AMQP_FRAMING_C_PATH ${CMAKE_CURRENT_SOURCE_DIR}/amqp_framing.c)
+endif (REGENERATE_AMQP_FRAMING)
+
+if(WIN32)
+  set(SOCKET_IMPL "win32")
+else(WIN32)
+  set(SOCKET_IMPL "unix")
+endif(WIN32)
+
+if(MSVC)
+  if(MSVC_VERSION LESS 1600)
+    set(MSINTTYPES_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/win32/msinttypes")
+    set(STDINT_H_INSTALL_FILE "${CMAKE_CURRENT_SOURCE_DIR}/win32/msinttypes/stdint.h")
+  endif(MSVC_VERSION LESS 1600)
+endif(MSVC)
+
+# NOTE: order is important here: if we generate amqp_framing.h/.c it'll be in the
+# binary directory, and should shadow whats in the source directory
+set(LIBRABBITMQ_INCLUDE_DIRS
+  ${CMAKE_CURRENT_BINARY_DIR}
+	${CMAKE_CURRENT_SOURCE_DIR}
+	${SOCKET_IMPL}
+	${MSINTTYPES_INCLUDE}
+	)
+
+include_directories(${LIBRABBITMQ_INCLUDE_DIRS})
+
+set(LIBRABBITMQ_INCLUDE_DIRS
+	${LIBRABBITMQ_INCLUDE_DIRS}
+	PARENT_SCOPE)
+
+add_definitions(-DHAVE_CONFIG_H)
+
+if (ENABLE_SSL_SUPPORT)
+  add_definitions(-DWITH_SSL=1)
+  set(AMQP_SSL_SOCKET_H_PATH amqp_ssl_socket.h)
+
+  set(AMQP_SSL_SRCS ${AMQP_SSL_SOCKET_H_PATH}
+      amqp_openssl.c
+      amqp_openssl_hostname_validation.c
+      amqp_openssl_hostname_validation.h
+      amqp_hostcheck.c
+      amqp_hostcheck.h
+      amqp_openssl_bio.c
+      amqp_openssl_bio.h
+  )
+  include_directories(${OPENSSL_INCLUDE_DIR})
+  set(AMQP_SSL_LIBS ${OPENSSL_LIBRARIES})
+  if (APPLE)
+    # Apple has deprecated OpenSSL in 10.7+. This disables that warning.
+    set_source_files_properties(${AMQP_SSL_SRCS}
+      PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations)
+  endif()
+
+  if (WIN32)
+    set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} win32/threads.h win32/threads.c)
+  else()
+    set(AMQP_SSL_SRCS ${AMQP_SSL_SRCS} unix/threads.h)
+  endif()
+endif()
+
+set(RABBITMQ_SOURCES
+    ${AMQP_FRAMING_H_PATH}
+    ${AMQP_FRAMING_C_PATH}
+    amqp_api.c amqp.h amqp_connection.c amqp_mem.c amqp_private.h amqp_socket.c
+    amqp_table.c amqp_url.c amqp_socket.h amqp_tcp_socket.c amqp_tcp_socket.h
+    amqp_time.c amqp_time.h
+    amqp_consumer.c
+    ${AMQP_SSL_SRCS}
+)
+
+add_definitions(-DAMQP_BUILD)
+
+set(RMQ_LIBRARIES ${AMQP_SSL_LIBS} ${SOCKET_LIBRARIES} ${LIBRT} ${CMAKE_THREAD_LIBS_INIT})
+
+if (BUILD_SHARED_LIBS)
+    add_library(rabbitmq SHARED ${RABBITMQ_SOURCES})
+    if (THREADS_HAVE_PTHREAD_ARG)
+      target_compile_options(rabbitmq PUBLIC "-pthread")
+    endif()
+
+    target_link_libraries(rabbitmq ${RMQ_LIBRARIES})
+
+    if (WIN32)
+        set_target_properties(rabbitmq PROPERTIES VERSION ${RMQ_VERSION} OUTPUT_NAME rabbitmq.${RMQ_SOVERSION})
+    else (WIN32)
+        set_target_properties(rabbitmq PROPERTIES VERSION ${RMQ_VERSION} SOVERSION ${RMQ_SOVERSION})
+    endif (WIN32)
+
+    install(TARGETS rabbitmq
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+
+    set(RMQ_LIBRARY_TARGET rabbitmq)
+endif (BUILD_SHARED_LIBS)
+
+if (BUILD_STATIC_LIBS)
+    add_library(rabbitmq-static STATIC ${RABBITMQ_SOURCES})
+    if (THREADS_HAVE_PTHREAD_ARG)
+      target_compile_options(rabbitmq-static PUBLIC "-pthread")
+    endif()
+
+    target_link_libraries(rabbitmq-static ${RMQ_LIBRARIES})
+
+    set_target_properties(rabbitmq-static PROPERTIES COMPILE_DEFINITIONS AMQP_STATIC)
+    if (WIN32)
+        set_target_properties(rabbitmq-static PROPERTIES
+          VERSION ${RMQ_VERSION}
+          OUTPUT_NAME librabbitmq.${RMQ_SOVERSION})
+
+        if(MSVC)
+            set_target_properties(rabbitmq-static PROPERTIES
+            # Embed debugging info in the library itself instead of generating
+            # a .pdb file.
+            COMPILE_OPTIONS "/Z7")
+        endif(MSVC)
+
+    else (WIN32)
+        set_target_properties(rabbitmq-static PROPERTIES VERSION ${RMQ_VERSION} SOVERSION ${RMQ_SOVERSION} OUTPUT_NAME rabbitmq)
+    endif (WIN32)
+
+    install(TARGETS rabbitmq-static
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        )
+
+    if (NOT DEFINED RMQ_LIBRARY_TARGET)
+        set(RMQ_LIBRARY_TARGET rabbitmq-static)
+    endif ()
+endif (BUILD_STATIC_LIBS)
+
+install(FILES
+  amqp.h
+  ${AMQP_FRAMING_H_PATH}
+  amqp_tcp_socket.h
+  ${AMQP_SSL_SOCKET_H_PATH}
+  ${STDINT_H_INSTALL_FILE}
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+    )
+
+set(RMQ_LIBRARY_TARGET ${RMQ_LIBRARY_TARGET} PARENT_SCOPE)

+ 4 - 7
ext/librabbitmq/centos_x64/include/amqp.h → ext/librabbitmq/librabbitmq/amqp.h

@@ -219,9 +219,9 @@ AMQP_BEGIN_DECLS
  */
 
 #define AMQP_VERSION_MAJOR 0
-#define AMQP_VERSION_MINOR 10
+#define AMQP_VERSION_MINOR 9
 #define AMQP_VERSION_PATCH 0
-#define AMQP_VERSION_IS_RELEASE 0
+#define AMQP_VERSION_IS_RELEASE 1
 
 /**
  * \def AMQP_VERSION_CODE
@@ -328,16 +328,13 @@ char const *AMQP_CALL amqp_version(void);
 /**
  * \def AMQP_DEFAULT_MAX_CHANNELS
  *
- * Default maximum number of channels (2047, RabbitMQ default limit of 2048,
- * minus 1 for channel 0). RabbitMQ set a default limit of 2048 channels per
- * connection in v3.7.5 to prevent broken clients from leaking too many
- * channels.
+ * Default maximum number of channels (0, no limit)
  *
  * \sa amqp_login(), amqp_login_with_properties()
  *
  * \since v0.4.0
  */
-#define AMQP_DEFAULT_MAX_CHANNELS 2047
+#define AMQP_DEFAULT_MAX_CHANNELS 0
 
 /**
  * \def AMQP_DEFAULT_HEARTBEAT

+ 394 - 0
ext/librabbitmq/librabbitmq/amqp_api.c

@@ -0,0 +1,394 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _MSC_VER
+/* MSVC complains about sprintf being deprecated in favor of sprintf_s */
+#define _CRT_SECURE_NO_WARNINGS
+/* MSVC complains about strdup being deprecated in favor of _strdup */
+#define _CRT_NONSTDC_NO_DEPRECATE
+#endif
+
+#include "amqp_private.h"
+#include "amqp_time.h"
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ERROR_MASK (0x00FF)
+#define ERROR_CATEGORY_MASK (0xFF00)
+
+enum error_category_enum_ { EC_base = 0, EC_tcp = 1, EC_ssl = 2 };
+
+static const char *base_error_strings[] = {
+    /* AMQP_STATUS_OK 0x0 */
+    "operation completed successfully",
+    /* AMQP_STATUS_NO_MEMORY                  -0x0001 */
+    "could not allocate memory",
+    /* AMQP_STATUS_BAD_AQMP_DATA              -0x0002 */
+    "invalid AMQP data",
+    /* AMQP_STATUS_UNKNOWN_CLASS              -0x0003 */
+    "unknown AMQP class id",
+    /* AMQP_STATUS_UNKNOWN_METHOD             -0x0004 */
+    "unknown AMQP method id",
+    /* AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED -0x0005 */
+    "hostname lookup failed",
+    /* AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION  -0x0006 */
+    "incompatible AMQP version",
+    /* AMQP_STATUS_CONNECTION_CLOSED          -0x0007 */
+    "connection closed unexpectedly",
+    /* AMQP_STATUS_BAD_AMQP_URL               -0x0008 */
+    "could not parse AMQP URL",
+    /* AMQP_STATUS_SOCKET_ERROR               -0x0009 */
+    "a socket error occurred",
+    /* AMQP_STATUS_INVALID_PARAMETER          -0x000A */
+    "invalid parameter",
+    /* AMQP_STATUS_TABLE_TOO_BIG              -0x000B */
+    "table too large for buffer",
+    /* AMQP_STATUS_WRONG_METHOD               -0x000C */
+    "unexpected method received",
+    /* AMQP_STATUS_TIMEOUT                    -0x000D */
+    "request timed out",
+    /* AMQP_STATUS_TIMER_FAILED               -0x000E */
+    "system timer has failed",
+    /* AMQP_STATUS_HEARTBEAT_TIMEOUT          -0x000F */
+    "heartbeat timeout, connection closed",
+    /* AMQP_STATUS_UNEXPECTED STATE           -0x0010 */
+    "unexpected protocol state",
+    /* AMQP_STATUS_SOCKET_CLOSED              -0x0011 */
+    "socket is closed",
+    /* AMQP_STATUS_SOCKET_INUSE               -0x0012 */
+    "socket already open",
+    /* AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD -0x00013 */
+    "unsupported sasl method requested",
+    /* AMQP_STATUS_UNSUPPORTED                -0x0014 */
+    "parameter value is unsupported"};
+
+static const char *tcp_error_strings[] = {
+    /* AMQP_STATUS_TCP_ERROR                  -0x0100 */
+    "a socket error occurred",
+    /* AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR   -0x0101 */
+    "socket library initialization failed"};
+
+static const char *ssl_error_strings[] = {
+    /* AMQP_STATUS_SSL_ERRO  R                -0x0200 */
+    "a SSL error occurred",
+    /* AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED -0x0201 */
+    "SSL hostname verification failed",
+    /* AMQP_STATUS_SSL_PEER_VERIFY_FAILED     -0x0202 */
+    "SSL peer cert verification failed",
+    /* AMQP_STATUS_SSL_CONNECTION_FAILED      -0x0203 */
+    "SSL handshake failed"};
+
+static const char *unknown_error_string = "(unknown error)";
+
+const char *amqp_error_string2(int code) {
+  const char *error_string;
+  size_t category = (((-code) & ERROR_CATEGORY_MASK) >> 8);
+  size_t error = (-code) & ERROR_MASK;
+
+  switch (category) {
+    case EC_base:
+      if (error < (sizeof(base_error_strings) / sizeof(char *))) {
+        error_string = base_error_strings[error];
+      } else {
+        error_string = unknown_error_string;
+      }
+      break;
+
+    case EC_tcp:
+      if (error < (sizeof(tcp_error_strings) / sizeof(char *))) {
+        error_string = tcp_error_strings[error];
+      } else {
+        error_string = unknown_error_string;
+      }
+      break;
+
+    case EC_ssl:
+      if (error < (sizeof(ssl_error_strings) / sizeof(char *))) {
+        error_string = ssl_error_strings[error];
+      } else {
+        error_string = unknown_error_string;
+      }
+
+      break;
+
+    default:
+      error_string = unknown_error_string;
+      break;
+  }
+
+  return error_string;
+}
+
+char *amqp_error_string(int code) {
+  /* Previously sometimes clients had to flip the sign on a return value from a
+   * function to get the correct error code. Now, all error codes are negative.
+   * To keep people's legacy code running correctly, we map all error codes to
+   * negative values.
+   *
+   * This is only done with this deprecated function.
+   */
+  if (code > 0) {
+    code = -code;
+  }
+  return strdup(amqp_error_string2(code));
+}
+
+void amqp_abort(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fputc('\n', stderr);
+  abort();
+}
+
+const amqp_bytes_t amqp_empty_bytes = {0, NULL};
+const amqp_table_t amqp_empty_table = {0, NULL};
+const amqp_array_t amqp_empty_array = {0, NULL};
+
+int amqp_basic_publish(amqp_connection_state_t state, amqp_channel_t channel,
+                       amqp_bytes_t exchange, amqp_bytes_t routing_key,
+                       amqp_boolean_t mandatory, amqp_boolean_t immediate,
+                       amqp_basic_properties_t const *properties,
+                       amqp_bytes_t body) {
+  amqp_frame_t f;
+  size_t body_offset;
+  size_t usable_body_payload_size =
+      state->frame_max - (HEADER_SIZE + FOOTER_SIZE);
+  int res;
+  int flagz;
+
+  amqp_basic_publish_t m;
+  amqp_basic_properties_t default_properties;
+
+  m.exchange = exchange;
+  m.routing_key = routing_key;
+  m.mandatory = mandatory;
+  m.immediate = immediate;
+  m.ticket = 0;
+
+  /* TODO(alanxz): this heartbeat check is happening in the wrong place, it
+   * should really be done in amqp_try_send/writev */
+  res = amqp_time_has_past(state->next_recv_heartbeat);
+  if (AMQP_STATUS_TIMER_FAILURE == res) {
+    return res;
+  } else if (AMQP_STATUS_TIMEOUT == res) {
+    res = amqp_try_recv(state);
+    if (AMQP_STATUS_TIMEOUT == res) {
+      return AMQP_STATUS_HEARTBEAT_TIMEOUT;
+    } else if (AMQP_STATUS_OK != res) {
+      return res;
+    }
+  }
+
+  res = amqp_send_method_inner(state, channel, AMQP_BASIC_PUBLISH_METHOD, &m,
+                               AMQP_SF_MORE, amqp_time_infinite());
+  if (res < 0) {
+    return res;
+  }
+
+  if (properties == NULL) {
+    memset(&default_properties, 0, sizeof(default_properties));
+    properties = &default_properties;
+  }
+
+  f.frame_type = AMQP_FRAME_HEADER;
+  f.channel = channel;
+  f.payload.properties.class_id = AMQP_BASIC_CLASS;
+  f.payload.properties.body_size = body.len;
+  f.payload.properties.decoded = (void *)properties;
+
+  if (body.len > 0) {
+    flagz = AMQP_SF_MORE;
+  } else {
+    flagz = AMQP_SF_NONE;
+  }
+  res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite());
+  if (res < 0) {
+    return res;
+  }
+
+  body_offset = 0;
+  while (body_offset < body.len) {
+    size_t remaining = body.len - body_offset;
+
+    if (remaining == 0) {
+      break;
+    }
+
+    f.frame_type = AMQP_FRAME_BODY;
+    f.channel = channel;
+    f.payload.body_fragment.bytes = amqp_offset(body.bytes, body_offset);
+    if (remaining >= usable_body_payload_size) {
+      f.payload.body_fragment.len = usable_body_payload_size;
+      flagz = AMQP_SF_MORE;
+    } else {
+      f.payload.body_fragment.len = remaining;
+      flagz = AMQP_SF_NONE;
+    }
+
+    body_offset += f.payload.body_fragment.len;
+    res = amqp_send_frame_inner(state, &f, flagz, amqp_time_infinite());
+    if (res < 0) {
+      return res;
+    }
+  }
+
+  return AMQP_STATUS_OK;
+}
+
+amqp_rpc_reply_t amqp_channel_close(amqp_connection_state_t state,
+                                    amqp_channel_t channel, int code) {
+  char codestr[13];
+  amqp_method_number_t replies[2] = {AMQP_CHANNEL_CLOSE_OK_METHOD, 0};
+  amqp_channel_close_t req;
+
+  if (code < 0 || code > UINT16_MAX) {
+    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
+  }
+
+  req.reply_code = (uint16_t)code;
+  req.reply_text.bytes = codestr;
+  req.reply_text.len = sprintf(codestr, "%d", code);
+  req.class_id = 0;
+  req.method_id = 0;
+
+  return amqp_simple_rpc(state, channel, AMQP_CHANNEL_CLOSE_METHOD, replies,
+                         &req);
+}
+
+amqp_rpc_reply_t amqp_connection_close(amqp_connection_state_t state,
+                                       int code) {
+  char codestr[13];
+  amqp_method_number_t replies[2] = {AMQP_CONNECTION_CLOSE_OK_METHOD, 0};
+  amqp_channel_close_t req;
+
+  if (code < 0 || code > UINT16_MAX) {
+    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
+  }
+
+  req.reply_code = (uint16_t)code;
+  req.reply_text.bytes = codestr;
+  req.reply_text.len = sprintf(codestr, "%d", code);
+  req.class_id = 0;
+  req.method_id = 0;
+
+  return amqp_simple_rpc(state, 0, AMQP_CONNECTION_CLOSE_METHOD, replies, &req);
+}
+
+int amqp_basic_ack(amqp_connection_state_t state, amqp_channel_t channel,
+                   uint64_t delivery_tag, amqp_boolean_t multiple) {
+  amqp_basic_ack_t m;
+  m.delivery_tag = delivery_tag;
+  m.multiple = multiple;
+  return amqp_send_method(state, channel, AMQP_BASIC_ACK_METHOD, &m);
+}
+
+amqp_rpc_reply_t amqp_basic_get(amqp_connection_state_t state,
+                                amqp_channel_t channel, amqp_bytes_t queue,
+                                amqp_boolean_t no_ack) {
+  amqp_method_number_t replies[] = {AMQP_BASIC_GET_OK_METHOD,
+                                    AMQP_BASIC_GET_EMPTY_METHOD, 0};
+  amqp_basic_get_t req;
+  req.ticket = 0;
+  req.queue = queue;
+  req.no_ack = no_ack;
+
+  state->most_recent_api_result =
+      amqp_simple_rpc(state, channel, AMQP_BASIC_GET_METHOD, replies, &req);
+  return state->most_recent_api_result;
+}
+
+int amqp_basic_reject(amqp_connection_state_t state, amqp_channel_t channel,
+                      uint64_t delivery_tag, amqp_boolean_t requeue) {
+  amqp_basic_reject_t req;
+  req.delivery_tag = delivery_tag;
+  req.requeue = requeue;
+  return amqp_send_method(state, channel, AMQP_BASIC_REJECT_METHOD, &req);
+}
+
+int amqp_basic_nack(amqp_connection_state_t state, amqp_channel_t channel,
+                    uint64_t delivery_tag, amqp_boolean_t multiple,
+                    amqp_boolean_t requeue) {
+  amqp_basic_nack_t req;
+  req.delivery_tag = delivery_tag;
+  req.multiple = multiple;
+  req.requeue = requeue;
+  return amqp_send_method(state, channel, AMQP_BASIC_NACK_METHOD, &req);
+}
+
+struct timeval *amqp_get_handshake_timeout(amqp_connection_state_t state) {
+  return state->handshake_timeout;
+}
+
+int amqp_set_handshake_timeout(amqp_connection_state_t state,
+                               struct timeval *timeout) {
+  if (timeout) {
+    if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
+      return AMQP_STATUS_INVALID_PARAMETER;
+    }
+    state->internal_handshake_timeout = *timeout;
+    state->handshake_timeout = &state->internal_handshake_timeout;
+  } else {
+    state->handshake_timeout = NULL;
+  }
+
+  return AMQP_STATUS_OK;
+}
+
+struct timeval *amqp_get_rpc_timeout(amqp_connection_state_t state) {
+  return state->rpc_timeout;
+}
+
+int amqp_set_rpc_timeout(amqp_connection_state_t state,
+                         struct timeval *timeout) {
+  if (timeout) {
+    if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
+      return AMQP_STATUS_INVALID_PARAMETER;
+    }
+    state->rpc_timeout = &state->internal_rpc_timeout;
+    *state->rpc_timeout = *timeout;
+  } else {
+    state->rpc_timeout = NULL;
+  }
+  return AMQP_STATUS_OK;
+}

+ 595 - 0
ext/librabbitmq/librabbitmq/amqp_connection.c

@@ -0,0 +1,595 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2014
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "amqp_private.h"
+#include "amqp_tcp_socket.h"
+#include "amqp_time.h"
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef AMQP_INITIAL_FRAME_POOL_PAGE_SIZE
+#define AMQP_INITIAL_FRAME_POOL_PAGE_SIZE 65536
+#endif
+
+#ifndef AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE
+#define AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE 131072
+#endif
+
+#ifndef AMQP_DEFAULT_LOGIN_TIMEOUT_SEC
+#define AMQP_DEFAULT_LOGIN_TIMEOUT_SEC 12
+#endif
+
+#define ENFORCE_STATE(statevec, statenum)                                   \
+  {                                                                         \
+    amqp_connection_state_t _check_state = (statevec);                      \
+    amqp_connection_state_enum _wanted_state = (statenum);                  \
+    if (_check_state->state != _wanted_state)                               \
+      amqp_abort(                                                           \
+          "Programming error: invalid AMQP connection state: expected %d, " \
+          "got %d",                                                         \
+          _wanted_state, _check_state->state);                              \
+  }
+
+amqp_connection_state_t amqp_new_connection(void) {
+  int res;
+  amqp_connection_state_t state = (amqp_connection_state_t)calloc(
+      1, sizeof(struct amqp_connection_state_t_));
+
+  if (state == NULL) {
+    return NULL;
+  }
+
+  res = amqp_tune_connection(state, 0, AMQP_INITIAL_FRAME_POOL_PAGE_SIZE, 0);
+  if (0 != res) {
+    goto out_nomem;
+  }
+
+  state->inbound_buffer.bytes = state->header_buffer;
+  state->inbound_buffer.len = sizeof(state->header_buffer);
+
+  state->state = CONNECTION_STATE_INITIAL;
+  /* the server protocol version response is 8 bytes, which conveniently
+     is also the minimum frame size */
+  state->target_size = 8;
+
+  state->sock_inbound_buffer.len = AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE;
+  state->sock_inbound_buffer.bytes =
+      malloc(AMQP_INITIAL_INBOUND_SOCK_BUFFER_SIZE);
+  if (state->sock_inbound_buffer.bytes == NULL) {
+    goto out_nomem;
+  }
+
+  init_amqp_pool(&state->properties_pool, 512);
+
+  /* Use address of the internal_handshake_timeout object by default. */
+  state->internal_handshake_timeout.tv_sec = AMQP_DEFAULT_LOGIN_TIMEOUT_SEC;
+  state->internal_handshake_timeout.tv_usec = 0;
+  state->handshake_timeout = &state->internal_handshake_timeout;
+
+  return state;
+
+out_nomem:
+  free(state->sock_inbound_buffer.bytes);
+  free(state);
+  return NULL;
+}
+
+int amqp_get_sockfd(amqp_connection_state_t state) {
+  return state->socket ? amqp_socket_get_sockfd(state->socket) : -1;
+}
+
+void amqp_set_sockfd(amqp_connection_state_t state, int sockfd) {
+  amqp_socket_t *socket = amqp_tcp_socket_new(state);
+  if (!socket) {
+    amqp_abort("%s", strerror(errno));
+  }
+  amqp_tcp_socket_set_sockfd(socket, sockfd);
+}
+
+void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket) {
+  amqp_socket_delete(state->socket);
+  state->socket = socket;
+}
+
+amqp_socket_t *amqp_get_socket(amqp_connection_state_t state) {
+  return state->socket;
+}
+
+int amqp_tune_connection(amqp_connection_state_t state, int channel_max,
+                         int frame_max, int heartbeat) {
+  void *newbuf;
+  int res;
+
+  ENFORCE_STATE(state, CONNECTION_STATE_IDLE);
+
+  state->channel_max = channel_max;
+  state->frame_max = frame_max;
+
+  state->heartbeat = heartbeat;
+  if (0 > state->heartbeat) {
+    state->heartbeat = 0;
+  }
+
+  res = amqp_time_s_from_now(&state->next_send_heartbeat,
+                             amqp_heartbeat_send(state));
+  if (AMQP_STATUS_OK != res) {
+    return res;
+  }
+  res = amqp_time_s_from_now(&state->next_recv_heartbeat,
+                             amqp_heartbeat_recv(state));
+  if (AMQP_STATUS_OK != res) {
+    return res;
+  }
+
+  state->outbound_buffer.len = frame_max;
+  newbuf = realloc(state->outbound_buffer.bytes, frame_max);
+  if (newbuf == NULL) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+  state->outbound_buffer.bytes = newbuf;
+
+  return AMQP_STATUS_OK;
+}
+
+int amqp_get_channel_max(amqp_connection_state_t state) {
+  return state->channel_max;
+}
+
+int amqp_get_frame_max(amqp_connection_state_t state) {
+  return state->frame_max;
+}
+
+int amqp_get_heartbeat(amqp_connection_state_t state) {
+  return state->heartbeat;
+}
+
+int amqp_destroy_connection(amqp_connection_state_t state) {
+  int status = AMQP_STATUS_OK;
+  if (state) {
+    int i;
+    for (i = 0; i < POOL_TABLE_SIZE; ++i) {
+      amqp_pool_table_entry_t *entry = state->pool_table[i];
+      while (NULL != entry) {
+        amqp_pool_table_entry_t *todelete = entry;
+        empty_amqp_pool(&entry->pool);
+        entry = entry->next;
+        free(todelete);
+      }
+    }
+
+    free(state->outbound_buffer.bytes);
+    free(state->sock_inbound_buffer.bytes);
+    amqp_socket_delete(state->socket);
+    empty_amqp_pool(&state->properties_pool);
+    free(state);
+  }
+  return status;
+}
+
+static void return_to_idle(amqp_connection_state_t state) {
+  state->inbound_buffer.len = sizeof(state->header_buffer);
+  state->inbound_buffer.bytes = state->header_buffer;
+  state->inbound_offset = 0;
+  state->target_size = HEADER_SIZE;
+  state->state = CONNECTION_STATE_IDLE;
+}
+
+static size_t consume_data(amqp_connection_state_t state,
+                           amqp_bytes_t *received_data) {
+  /* how much data is available and will fit? */
+  size_t bytes_consumed = state->target_size - state->inbound_offset;
+  if (received_data->len < bytes_consumed) {
+    bytes_consumed = received_data->len;
+  }
+
+  memcpy(amqp_offset(state->inbound_buffer.bytes, state->inbound_offset),
+         received_data->bytes, bytes_consumed);
+  state->inbound_offset += bytes_consumed;
+  received_data->bytes = amqp_offset(received_data->bytes, bytes_consumed);
+  received_data->len -= bytes_consumed;
+
+  return bytes_consumed;
+}
+
+int amqp_handle_input(amqp_connection_state_t state, amqp_bytes_t received_data,
+                      amqp_frame_t *decoded_frame) {
+  size_t bytes_consumed;
+  void *raw_frame;
+
+  /* Returning frame_type of zero indicates either insufficient input,
+     or a complete, ignored frame was read. */
+  decoded_frame->frame_type = 0;
+
+  if (received_data.len == 0) {
+    return AMQP_STATUS_OK;
+  }
+
+  if (state->state == CONNECTION_STATE_IDLE) {
+    state->state = CONNECTION_STATE_HEADER;
+  }
+
+  bytes_consumed = consume_data(state, &received_data);
+
+  /* do we have target_size data yet? if not, return with the
+     expectation that more will arrive */
+  if (state->inbound_offset < state->target_size) {
+    return (int)bytes_consumed;
+  }
+
+  raw_frame = state->inbound_buffer.bytes;
+
+  switch (state->state) {
+    case CONNECTION_STATE_INITIAL:
+      /* check for a protocol header from the server */
+      if (memcmp(raw_frame, "AMQP", 4) == 0) {
+        decoded_frame->frame_type = AMQP_PSEUDOFRAME_PROTOCOL_HEADER;
+        decoded_frame->channel = 0;
+
+        decoded_frame->payload.protocol_header.transport_high =
+            amqp_d8(amqp_offset(raw_frame, 4));
+        decoded_frame->payload.protocol_header.transport_low =
+            amqp_d8(amqp_offset(raw_frame, 5));
+        decoded_frame->payload.protocol_header.protocol_version_major =
+            amqp_d8(amqp_offset(raw_frame, 6));
+        decoded_frame->payload.protocol_header.protocol_version_minor =
+            amqp_d8(amqp_offset(raw_frame, 7));
+
+        return_to_idle(state);
+        return (int)bytes_consumed;
+      }
+
+    /* it's not a protocol header; fall through to process it as a
+       regular frame header */
+
+    case CONNECTION_STATE_HEADER: {
+      amqp_channel_t channel;
+      amqp_pool_t *channel_pool;
+      /* frame length is 3 bytes in */
+      channel = amqp_d16(amqp_offset(raw_frame, 1));
+
+      state->target_size =
+          amqp_d32(amqp_offset(raw_frame, 3)) + HEADER_SIZE + FOOTER_SIZE;
+
+      if ((size_t)state->frame_max < state->target_size) {
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+
+      channel_pool = amqp_get_or_create_channel_pool(state, channel);
+      if (NULL == channel_pool) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+
+      amqp_pool_alloc_bytes(channel_pool, state->target_size,
+                            &state->inbound_buffer);
+      if (NULL == state->inbound_buffer.bytes) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      memcpy(state->inbound_buffer.bytes, state->header_buffer, HEADER_SIZE);
+      raw_frame = state->inbound_buffer.bytes;
+
+      state->state = CONNECTION_STATE_BODY;
+
+      bytes_consumed += consume_data(state, &received_data);
+
+      /* do we have target_size data yet? if not, return with the
+         expectation that more will arrive */
+      if (state->inbound_offset < state->target_size) {
+        return (int)bytes_consumed;
+      }
+    }
+    /* fall through to process body */
+
+    case CONNECTION_STATE_BODY: {
+      amqp_bytes_t encoded;
+      int res;
+      amqp_pool_t *channel_pool;
+
+      /* Check frame end marker (footer) */
+      if (amqp_d8(amqp_offset(raw_frame, state->target_size - 1)) !=
+          AMQP_FRAME_END) {
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+
+      decoded_frame->frame_type = amqp_d8(amqp_offset(raw_frame, 0));
+      decoded_frame->channel = amqp_d16(amqp_offset(raw_frame, 1));
+
+      channel_pool =
+          amqp_get_or_create_channel_pool(state, decoded_frame->channel);
+      if (NULL == channel_pool) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+
+      switch (decoded_frame->frame_type) {
+        case AMQP_FRAME_METHOD:
+          decoded_frame->payload.method.id =
+              amqp_d32(amqp_offset(raw_frame, HEADER_SIZE));
+          encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 4);
+          encoded.len = state->target_size - HEADER_SIZE - 4 - FOOTER_SIZE;
+
+          res = amqp_decode_method(decoded_frame->payload.method.id,
+                                   channel_pool, encoded,
+                                   &decoded_frame->payload.method.decoded);
+          if (res < 0) {
+            return res;
+          }
+
+          break;
+
+        case AMQP_FRAME_HEADER:
+          decoded_frame->payload.properties.class_id =
+              amqp_d16(amqp_offset(raw_frame, HEADER_SIZE));
+          /* unused 2-byte weight field goes here */
+          decoded_frame->payload.properties.body_size =
+              amqp_d64(amqp_offset(raw_frame, HEADER_SIZE + 4));
+          encoded.bytes = amqp_offset(raw_frame, HEADER_SIZE + 12);
+          encoded.len = state->target_size - HEADER_SIZE - 12 - FOOTER_SIZE;
+          decoded_frame->payload.properties.raw = encoded;
+
+          res = amqp_decode_properties(
+              decoded_frame->payload.properties.class_id, channel_pool, encoded,
+              &decoded_frame->payload.properties.decoded);
+          if (res < 0) {
+            return res;
+          }
+
+          break;
+
+        case AMQP_FRAME_BODY:
+          decoded_frame->payload.body_fragment.len =
+              state->target_size - HEADER_SIZE - FOOTER_SIZE;
+          decoded_frame->payload.body_fragment.bytes =
+              amqp_offset(raw_frame, HEADER_SIZE);
+          break;
+
+        case AMQP_FRAME_HEARTBEAT:
+          break;
+
+        default:
+          /* Ignore the frame */
+          decoded_frame->frame_type = 0;
+          break;
+      }
+
+      return_to_idle(state);
+      return (int)bytes_consumed;
+    }
+
+    default:
+      amqp_abort("Internal error: invalid amqp_connection_state_t->state %d",
+                 state->state);
+  }
+}
+
+amqp_boolean_t amqp_release_buffers_ok(amqp_connection_state_t state) {
+  return (state->state == CONNECTION_STATE_IDLE);
+}
+
+void amqp_release_buffers(amqp_connection_state_t state) {
+  int i;
+  ENFORCE_STATE(state, CONNECTION_STATE_IDLE);
+
+  for (i = 0; i < POOL_TABLE_SIZE; ++i) {
+    amqp_pool_table_entry_t *entry = state->pool_table[i];
+
+    for (; NULL != entry; entry = entry->next) {
+      amqp_maybe_release_buffers_on_channel(state, entry->channel);
+    }
+  }
+}
+
+void amqp_maybe_release_buffers(amqp_connection_state_t state) {
+  if (amqp_release_buffers_ok(state)) {
+    amqp_release_buffers(state);
+  }
+}
+
+void amqp_maybe_release_buffers_on_channel(amqp_connection_state_t state,
+                                           amqp_channel_t channel) {
+  amqp_link_t *queued_link;
+  amqp_pool_t *pool;
+  if (CONNECTION_STATE_IDLE != state->state) {
+    return;
+  }
+
+  queued_link = state->first_queued_frame;
+
+  while (NULL != queued_link) {
+    amqp_frame_t *frame = queued_link->data;
+    if (channel == frame->channel) {
+      return;
+    }
+
+    queued_link = queued_link->next;
+  }
+
+  pool = amqp_get_channel_pool(state, channel);
+
+  if (pool != NULL) {
+    recycle_amqp_pool(pool);
+  }
+}
+
+static int amqp_frame_to_bytes(const amqp_frame_t *frame, amqp_bytes_t buffer,
+                               amqp_bytes_t *encoded) {
+  void *out_frame = buffer.bytes;
+  size_t out_frame_len;
+  int res;
+
+  amqp_e8(frame->frame_type, amqp_offset(out_frame, 0));
+  amqp_e16(frame->channel, amqp_offset(out_frame, 1));
+
+  switch (frame->frame_type) {
+    case AMQP_FRAME_BODY: {
+      const amqp_bytes_t *body = &frame->payload.body_fragment;
+
+      memcpy(amqp_offset(out_frame, HEADER_SIZE), body->bytes, body->len);
+
+      out_frame_len = body->len;
+      break;
+    }
+    case AMQP_FRAME_METHOD: {
+      amqp_bytes_t method_encoded;
+
+      amqp_e32(frame->payload.method.id, amqp_offset(out_frame, HEADER_SIZE));
+
+      method_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 4);
+      method_encoded.len = buffer.len - HEADER_SIZE - 4 - FOOTER_SIZE;
+
+      res = amqp_encode_method(frame->payload.method.id,
+                               frame->payload.method.decoded, method_encoded);
+      if (res < 0) {
+        return res;
+      }
+
+      out_frame_len = res + 4;
+      break;
+    }
+
+    case AMQP_FRAME_HEADER: {
+      amqp_bytes_t properties_encoded;
+
+      amqp_e16(frame->payload.properties.class_id,
+               amqp_offset(out_frame, HEADER_SIZE));
+      amqp_e16(0, amqp_offset(out_frame, HEADER_SIZE + 2)); /* "weight" */
+      amqp_e64(frame->payload.properties.body_size,
+               amqp_offset(out_frame, HEADER_SIZE + 4));
+
+      properties_encoded.bytes = amqp_offset(out_frame, HEADER_SIZE + 12);
+      properties_encoded.len = buffer.len - HEADER_SIZE - 12 - FOOTER_SIZE;
+
+      res = amqp_encode_properties(frame->payload.properties.class_id,
+                                   frame->payload.properties.decoded,
+                                   properties_encoded);
+      if (res < 0) {
+        return res;
+      }
+
+      out_frame_len = res + 12;
+      break;
+    }
+
+    case AMQP_FRAME_HEARTBEAT:
+      out_frame_len = 0;
+      break;
+
+    default:
+      return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  amqp_e32((uint32_t)out_frame_len, amqp_offset(out_frame, 3));
+  amqp_e8(AMQP_FRAME_END, amqp_offset(out_frame, HEADER_SIZE + out_frame_len));
+
+  encoded->bytes = out_frame;
+  encoded->len = out_frame_len + HEADER_SIZE + FOOTER_SIZE;
+
+  return AMQP_STATUS_OK;
+}
+
+int amqp_send_frame(amqp_connection_state_t state, const amqp_frame_t *frame) {
+  return amqp_send_frame_inner(state, frame, AMQP_SF_NONE,
+                               amqp_time_infinite());
+}
+
+int amqp_send_frame_inner(amqp_connection_state_t state,
+                          const amqp_frame_t *frame, int flags,
+                          amqp_time_t deadline) {
+  int res;
+  ssize_t sent;
+  amqp_bytes_t encoded;
+  amqp_time_t next_timeout;
+
+  /* TODO: if the AMQP_SF_MORE socket optimization can be shown to work
+   * correctly, then this could be un-done so that body-frames are sent as 3
+   * send calls, getting rid of the copy of the body content, some testing
+   * would need to be done to see if this would actually a win for performance.
+   * */
+  res = amqp_frame_to_bytes(frame, state->outbound_buffer, &encoded);
+  if (AMQP_STATUS_OK != res) {
+    return res;
+  }
+
+start_send:
+
+  next_timeout = amqp_time_first(deadline, state->next_recv_heartbeat);
+
+  sent = amqp_try_send(state, encoded.bytes, encoded.len, next_timeout, flags);
+  if (0 > sent) {
+    return (int)sent;
+  }
+
+  /* A partial send has occurred, because of a heartbeat timeout (so try recv
+   * something) or common timeout (so return AMQP_STATUS_TIMEOUT) */
+  if ((ssize_t)encoded.len != sent) {
+    if (amqp_time_equal(next_timeout, deadline)) {
+      /* timeout of method was received, so return from method*/
+      return AMQP_STATUS_TIMEOUT;
+    }
+
+    res = amqp_try_recv(state);
+
+    if (AMQP_STATUS_TIMEOUT == res) {
+      return AMQP_STATUS_HEARTBEAT_TIMEOUT;
+    } else if (AMQP_STATUS_OK != res) {
+      return res;
+    }
+
+    encoded.bytes = (uint8_t *)encoded.bytes + sent;
+    encoded.len -= sent;
+    goto start_send;
+  }
+
+  res = amqp_time_s_from_now(&state->next_send_heartbeat,
+                             amqp_heartbeat_send(state));
+  return res;
+}
+
+amqp_table_t *amqp_get_server_properties(amqp_connection_state_t state) {
+  return &state->server_properties;
+}
+
+amqp_table_t *amqp_get_client_properties(amqp_connection_state_t state) {
+  return &state->client_properties;
+}

+ 307 - 0
ext/librabbitmq/librabbitmq/amqp_consumer.c

@@ -0,0 +1,307 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2013-2014
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+#include "amqp.h"
+#include "amqp_private.h"
+#include "amqp_socket.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+static int amqp_basic_properties_clone(amqp_basic_properties_t *original,
+                                       amqp_basic_properties_t *clone,
+                                       amqp_pool_t *pool) {
+  memset(clone, 0, sizeof(*clone));
+  clone->_flags = original->_flags;
+
+#define CLONE_BYTES_POOL(original, clone, pool)        \
+  if (0 == original.len) {                             \
+    clone = amqp_empty_bytes;                          \
+  } else {                                             \
+    amqp_pool_alloc_bytes(pool, original.len, &clone); \
+    if (NULL == clone.bytes) {                         \
+      return AMQP_STATUS_NO_MEMORY;                    \
+    }                                                  \
+    memcpy(clone.bytes, original.bytes, clone.len);    \
+  }
+
+  if (clone->_flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
+    CLONE_BYTES_POOL(original->content_type, clone->content_type, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) {
+    CLONE_BYTES_POOL(original->content_encoding, clone->content_encoding, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_HEADERS_FLAG) {
+    int res = amqp_table_clone(&original->headers, &clone->headers, pool);
+    if (AMQP_STATUS_OK != res) {
+      return res;
+    }
+  }
+
+  if (clone->_flags & AMQP_BASIC_DELIVERY_MODE_FLAG) {
+    clone->delivery_mode = original->delivery_mode;
+  }
+
+  if (clone->_flags & AMQP_BASIC_PRIORITY_FLAG) {
+    clone->priority = original->priority;
+  }
+
+  if (clone->_flags & AMQP_BASIC_CORRELATION_ID_FLAG) {
+    CLONE_BYTES_POOL(original->correlation_id, clone->correlation_id, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_REPLY_TO_FLAG) {
+    CLONE_BYTES_POOL(original->reply_to, clone->reply_to, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_EXPIRATION_FLAG) {
+    CLONE_BYTES_POOL(original->expiration, clone->expiration, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_MESSAGE_ID_FLAG) {
+    CLONE_BYTES_POOL(original->message_id, clone->message_id, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_TIMESTAMP_FLAG) {
+    clone->timestamp = original->timestamp;
+  }
+
+  if (clone->_flags & AMQP_BASIC_TYPE_FLAG) {
+    CLONE_BYTES_POOL(original->type, clone->type, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_USER_ID_FLAG) {
+    CLONE_BYTES_POOL(original->user_id, clone->user_id, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_APP_ID_FLAG) {
+    CLONE_BYTES_POOL(original->app_id, clone->app_id, pool)
+  }
+
+  if (clone->_flags & AMQP_BASIC_CLUSTER_ID_FLAG) {
+    CLONE_BYTES_POOL(original->cluster_id, clone->cluster_id, pool)
+  }
+
+  return AMQP_STATUS_OK;
+#undef CLONE_BYTES_POOL
+}
+
+void amqp_destroy_message(amqp_message_t *message) {
+  empty_amqp_pool(&message->pool);
+  amqp_bytes_free(message->body);
+}
+
+void amqp_destroy_envelope(amqp_envelope_t *envelope) {
+  amqp_destroy_message(&envelope->message);
+  amqp_bytes_free(envelope->routing_key);
+  amqp_bytes_free(envelope->exchange);
+  amqp_bytes_free(envelope->consumer_tag);
+}
+
+static int amqp_bytes_malloc_dup_failed(amqp_bytes_t bytes) {
+  if (bytes.len != 0 && bytes.bytes == NULL) {
+    return 1;
+  }
+  return 0;
+}
+
+amqp_rpc_reply_t amqp_consume_message(amqp_connection_state_t state,
+                                      amqp_envelope_t *envelope,
+                                      struct timeval *timeout,
+                                      AMQP_UNUSED int flags) {
+  int res;
+  amqp_frame_t frame;
+  amqp_basic_deliver_t *delivery_method;
+  amqp_rpc_reply_t ret;
+
+  memset(&ret, 0, sizeof(ret));
+  memset(envelope, 0, sizeof(*envelope));
+
+  res = amqp_simple_wait_frame_noblock(state, &frame, timeout);
+  if (AMQP_STATUS_OK != res) {
+    ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+    ret.library_error = res;
+    goto error_out1;
+  }
+
+  if (AMQP_FRAME_METHOD != frame.frame_type ||
+      AMQP_BASIC_DELIVER_METHOD != frame.payload.method.id) {
+    amqp_put_back_frame(state, &frame);
+    ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+    ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
+    goto error_out1;
+  }
+
+  delivery_method = frame.payload.method.decoded;
+
+  envelope->channel = frame.channel;
+  envelope->consumer_tag = amqp_bytes_malloc_dup(delivery_method->consumer_tag);
+  envelope->delivery_tag = delivery_method->delivery_tag;
+  envelope->redelivered = delivery_method->redelivered;
+  envelope->exchange = amqp_bytes_malloc_dup(delivery_method->exchange);
+  envelope->routing_key = amqp_bytes_malloc_dup(delivery_method->routing_key);
+
+  if (amqp_bytes_malloc_dup_failed(envelope->consumer_tag) ||
+      amqp_bytes_malloc_dup_failed(envelope->exchange) ||
+      amqp_bytes_malloc_dup_failed(envelope->routing_key)) {
+    ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+    ret.library_error = AMQP_STATUS_NO_MEMORY;
+    goto error_out2;
+  }
+
+  ret = amqp_read_message(state, envelope->channel, &envelope->message, 0);
+  if (AMQP_RESPONSE_NORMAL != ret.reply_type) {
+    goto error_out2;
+  }
+
+  ret.reply_type = AMQP_RESPONSE_NORMAL;
+  return ret;
+
+error_out2:
+  amqp_bytes_free(envelope->routing_key);
+  amqp_bytes_free(envelope->exchange);
+  amqp_bytes_free(envelope->consumer_tag);
+error_out1:
+  return ret;
+}
+
+amqp_rpc_reply_t amqp_read_message(amqp_connection_state_t state,
+                                   amqp_channel_t channel,
+                                   amqp_message_t *message,
+                                   AMQP_UNUSED int flags) {
+  amqp_frame_t frame;
+  amqp_rpc_reply_t ret;
+
+  size_t body_read;
+  char *body_read_ptr;
+  int res;
+
+  memset(&ret, 0, sizeof(ret));
+  memset(message, 0, sizeof(*message));
+
+  res = amqp_simple_wait_frame_on_channel(state, channel, &frame);
+  if (AMQP_STATUS_OK != res) {
+    ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+    ret.library_error = res;
+
+    goto error_out1;
+  }
+
+  if (AMQP_FRAME_HEADER != frame.frame_type) {
+    if (AMQP_FRAME_METHOD == frame.frame_type &&
+        (AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id ||
+         AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) {
+
+      ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
+      ret.reply = frame.payload.method;
+
+    } else {
+      ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+      ret.library_error = AMQP_STATUS_UNEXPECTED_STATE;
+
+      amqp_put_back_frame(state, &frame);
+    }
+    goto error_out1;
+  }
+
+  init_amqp_pool(&message->pool, 4096);
+  res = amqp_basic_properties_clone(frame.payload.properties.decoded,
+                                    &message->properties, &message->pool);
+
+  if (AMQP_STATUS_OK != res) {
+    ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+    ret.library_error = res;
+    goto error_out3;
+  }
+
+  if (0 == frame.payload.properties.body_size) {
+    message->body = amqp_empty_bytes;
+  } else {
+    if (SIZE_MAX < frame.payload.properties.body_size) {
+      ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+      ret.library_error = AMQP_STATUS_NO_MEMORY;
+      goto error_out1;
+    }
+    message->body =
+        amqp_bytes_malloc((size_t)frame.payload.properties.body_size);
+    if (NULL == message->body.bytes) {
+      ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+      ret.library_error = AMQP_STATUS_NO_MEMORY;
+      goto error_out1;
+    }
+  }
+
+  body_read = 0;
+  body_read_ptr = message->body.bytes;
+
+  while (body_read < message->body.len) {
+    res = amqp_simple_wait_frame_on_channel(state, channel, &frame);
+    if (AMQP_STATUS_OK != res) {
+      ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+      ret.library_error = res;
+      goto error_out2;
+    }
+    if (AMQP_FRAME_BODY != frame.frame_type) {
+      if (AMQP_FRAME_METHOD == frame.frame_type &&
+          (AMQP_CHANNEL_CLOSE_METHOD == frame.payload.method.id ||
+           AMQP_CONNECTION_CLOSE_METHOD == frame.payload.method.id)) {
+
+        ret.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
+        ret.reply = frame.payload.method;
+      } else {
+        ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+        ret.library_error = AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      goto error_out2;
+    }
+
+    if (body_read + frame.payload.body_fragment.len > message->body.len) {
+      ret.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+      ret.library_error = AMQP_STATUS_BAD_AMQP_DATA;
+      goto error_out2;
+    }
+
+    memcpy(body_read_ptr, frame.payload.body_fragment.bytes,
+           frame.payload.body_fragment.len);
+
+    body_read += frame.payload.body_fragment.len;
+    body_read_ptr += frame.payload.body_fragment.len;
+  }
+
+  ret.reply_type = AMQP_RESPONSE_NORMAL;
+  return ret;
+
+error_out2:
+  amqp_bytes_free(message->body);
+error_out3:
+  empty_amqp_pool(&message->pool);
+error_out1:
+  return ret;
+}

+ 2876 - 0
ext/librabbitmq/librabbitmq/amqp_framing.c

@@ -0,0 +1,2876 @@
+/* Generated code. Do not edit. Edit and re-run codegen.py instead.
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp_private.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+char const *amqp_constant_name(int constantNumber) {
+  switch (constantNumber) {
+    case AMQP_FRAME_METHOD:
+      return "AMQP_FRAME_METHOD";
+    case AMQP_FRAME_HEADER:
+      return "AMQP_FRAME_HEADER";
+    case AMQP_FRAME_BODY:
+      return "AMQP_FRAME_BODY";
+    case AMQP_FRAME_HEARTBEAT:
+      return "AMQP_FRAME_HEARTBEAT";
+    case AMQP_FRAME_MIN_SIZE:
+      return "AMQP_FRAME_MIN_SIZE";
+    case AMQP_FRAME_END:
+      return "AMQP_FRAME_END";
+    case AMQP_REPLY_SUCCESS:
+      return "AMQP_REPLY_SUCCESS";
+    case AMQP_CONTENT_TOO_LARGE:
+      return "AMQP_CONTENT_TOO_LARGE";
+    case AMQP_NO_ROUTE:
+      return "AMQP_NO_ROUTE";
+    case AMQP_NO_CONSUMERS:
+      return "AMQP_NO_CONSUMERS";
+    case AMQP_ACCESS_REFUSED:
+      return "AMQP_ACCESS_REFUSED";
+    case AMQP_NOT_FOUND:
+      return "AMQP_NOT_FOUND";
+    case AMQP_RESOURCE_LOCKED:
+      return "AMQP_RESOURCE_LOCKED";
+    case AMQP_PRECONDITION_FAILED:
+      return "AMQP_PRECONDITION_FAILED";
+    case AMQP_CONNECTION_FORCED:
+      return "AMQP_CONNECTION_FORCED";
+    case AMQP_INVALID_PATH:
+      return "AMQP_INVALID_PATH";
+    case AMQP_FRAME_ERROR:
+      return "AMQP_FRAME_ERROR";
+    case AMQP_SYNTAX_ERROR:
+      return "AMQP_SYNTAX_ERROR";
+    case AMQP_COMMAND_INVALID:
+      return "AMQP_COMMAND_INVALID";
+    case AMQP_CHANNEL_ERROR:
+      return "AMQP_CHANNEL_ERROR";
+    case AMQP_UNEXPECTED_FRAME:
+      return "AMQP_UNEXPECTED_FRAME";
+    case AMQP_RESOURCE_ERROR:
+      return "AMQP_RESOURCE_ERROR";
+    case AMQP_NOT_ALLOWED:
+      return "AMQP_NOT_ALLOWED";
+    case AMQP_NOT_IMPLEMENTED:
+      return "AMQP_NOT_IMPLEMENTED";
+    case AMQP_INTERNAL_ERROR:
+      return "AMQP_INTERNAL_ERROR";
+    default:
+      return "(unknown)";
+  }
+}
+
+amqp_boolean_t amqp_constant_is_hard_error(int constantNumber) {
+  switch (constantNumber) {
+    case AMQP_CONNECTION_FORCED:
+      return 1;
+    case AMQP_INVALID_PATH:
+      return 1;
+    case AMQP_FRAME_ERROR:
+      return 1;
+    case AMQP_SYNTAX_ERROR:
+      return 1;
+    case AMQP_COMMAND_INVALID:
+      return 1;
+    case AMQP_CHANNEL_ERROR:
+      return 1;
+    case AMQP_UNEXPECTED_FRAME:
+      return 1;
+    case AMQP_RESOURCE_ERROR:
+      return 1;
+    case AMQP_NOT_ALLOWED:
+      return 1;
+    case AMQP_NOT_IMPLEMENTED:
+      return 1;
+    case AMQP_INTERNAL_ERROR:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+char const *amqp_method_name(amqp_method_number_t methodNumber) {
+  switch (methodNumber) {
+    case AMQP_CONNECTION_START_METHOD:
+      return "AMQP_CONNECTION_START_METHOD";
+    case AMQP_CONNECTION_START_OK_METHOD:
+      return "AMQP_CONNECTION_START_OK_METHOD";
+    case AMQP_CONNECTION_SECURE_METHOD:
+      return "AMQP_CONNECTION_SECURE_METHOD";
+    case AMQP_CONNECTION_SECURE_OK_METHOD:
+      return "AMQP_CONNECTION_SECURE_OK_METHOD";
+    case AMQP_CONNECTION_TUNE_METHOD:
+      return "AMQP_CONNECTION_TUNE_METHOD";
+    case AMQP_CONNECTION_TUNE_OK_METHOD:
+      return "AMQP_CONNECTION_TUNE_OK_METHOD";
+    case AMQP_CONNECTION_OPEN_METHOD:
+      return "AMQP_CONNECTION_OPEN_METHOD";
+    case AMQP_CONNECTION_OPEN_OK_METHOD:
+      return "AMQP_CONNECTION_OPEN_OK_METHOD";
+    case AMQP_CONNECTION_CLOSE_METHOD:
+      return "AMQP_CONNECTION_CLOSE_METHOD";
+    case AMQP_CONNECTION_CLOSE_OK_METHOD:
+      return "AMQP_CONNECTION_CLOSE_OK_METHOD";
+    case AMQP_CONNECTION_BLOCKED_METHOD:
+      return "AMQP_CONNECTION_BLOCKED_METHOD";
+    case AMQP_CONNECTION_UNBLOCKED_METHOD:
+      return "AMQP_CONNECTION_UNBLOCKED_METHOD";
+    case AMQP_CHANNEL_OPEN_METHOD:
+      return "AMQP_CHANNEL_OPEN_METHOD";
+    case AMQP_CHANNEL_OPEN_OK_METHOD:
+      return "AMQP_CHANNEL_OPEN_OK_METHOD";
+    case AMQP_CHANNEL_FLOW_METHOD:
+      return "AMQP_CHANNEL_FLOW_METHOD";
+    case AMQP_CHANNEL_FLOW_OK_METHOD:
+      return "AMQP_CHANNEL_FLOW_OK_METHOD";
+    case AMQP_CHANNEL_CLOSE_METHOD:
+      return "AMQP_CHANNEL_CLOSE_METHOD";
+    case AMQP_CHANNEL_CLOSE_OK_METHOD:
+      return "AMQP_CHANNEL_CLOSE_OK_METHOD";
+    case AMQP_ACCESS_REQUEST_METHOD:
+      return "AMQP_ACCESS_REQUEST_METHOD";
+    case AMQP_ACCESS_REQUEST_OK_METHOD:
+      return "AMQP_ACCESS_REQUEST_OK_METHOD";
+    case AMQP_EXCHANGE_DECLARE_METHOD:
+      return "AMQP_EXCHANGE_DECLARE_METHOD";
+    case AMQP_EXCHANGE_DECLARE_OK_METHOD:
+      return "AMQP_EXCHANGE_DECLARE_OK_METHOD";
+    case AMQP_EXCHANGE_DELETE_METHOD:
+      return "AMQP_EXCHANGE_DELETE_METHOD";
+    case AMQP_EXCHANGE_DELETE_OK_METHOD:
+      return "AMQP_EXCHANGE_DELETE_OK_METHOD";
+    case AMQP_EXCHANGE_BIND_METHOD:
+      return "AMQP_EXCHANGE_BIND_METHOD";
+    case AMQP_EXCHANGE_BIND_OK_METHOD:
+      return "AMQP_EXCHANGE_BIND_OK_METHOD";
+    case AMQP_EXCHANGE_UNBIND_METHOD:
+      return "AMQP_EXCHANGE_UNBIND_METHOD";
+    case AMQP_EXCHANGE_UNBIND_OK_METHOD:
+      return "AMQP_EXCHANGE_UNBIND_OK_METHOD";
+    case AMQP_QUEUE_DECLARE_METHOD:
+      return "AMQP_QUEUE_DECLARE_METHOD";
+    case AMQP_QUEUE_DECLARE_OK_METHOD:
+      return "AMQP_QUEUE_DECLARE_OK_METHOD";
+    case AMQP_QUEUE_BIND_METHOD:
+      return "AMQP_QUEUE_BIND_METHOD";
+    case AMQP_QUEUE_BIND_OK_METHOD:
+      return "AMQP_QUEUE_BIND_OK_METHOD";
+    case AMQP_QUEUE_PURGE_METHOD:
+      return "AMQP_QUEUE_PURGE_METHOD";
+    case AMQP_QUEUE_PURGE_OK_METHOD:
+      return "AMQP_QUEUE_PURGE_OK_METHOD";
+    case AMQP_QUEUE_DELETE_METHOD:
+      return "AMQP_QUEUE_DELETE_METHOD";
+    case AMQP_QUEUE_DELETE_OK_METHOD:
+      return "AMQP_QUEUE_DELETE_OK_METHOD";
+    case AMQP_QUEUE_UNBIND_METHOD:
+      return "AMQP_QUEUE_UNBIND_METHOD";
+    case AMQP_QUEUE_UNBIND_OK_METHOD:
+      return "AMQP_QUEUE_UNBIND_OK_METHOD";
+    case AMQP_BASIC_QOS_METHOD:
+      return "AMQP_BASIC_QOS_METHOD";
+    case AMQP_BASIC_QOS_OK_METHOD:
+      return "AMQP_BASIC_QOS_OK_METHOD";
+    case AMQP_BASIC_CONSUME_METHOD:
+      return "AMQP_BASIC_CONSUME_METHOD";
+    case AMQP_BASIC_CONSUME_OK_METHOD:
+      return "AMQP_BASIC_CONSUME_OK_METHOD";
+    case AMQP_BASIC_CANCEL_METHOD:
+      return "AMQP_BASIC_CANCEL_METHOD";
+    case AMQP_BASIC_CANCEL_OK_METHOD:
+      return "AMQP_BASIC_CANCEL_OK_METHOD";
+    case AMQP_BASIC_PUBLISH_METHOD:
+      return "AMQP_BASIC_PUBLISH_METHOD";
+    case AMQP_BASIC_RETURN_METHOD:
+      return "AMQP_BASIC_RETURN_METHOD";
+    case AMQP_BASIC_DELIVER_METHOD:
+      return "AMQP_BASIC_DELIVER_METHOD";
+    case AMQP_BASIC_GET_METHOD:
+      return "AMQP_BASIC_GET_METHOD";
+    case AMQP_BASIC_GET_OK_METHOD:
+      return "AMQP_BASIC_GET_OK_METHOD";
+    case AMQP_BASIC_GET_EMPTY_METHOD:
+      return "AMQP_BASIC_GET_EMPTY_METHOD";
+    case AMQP_BASIC_ACK_METHOD:
+      return "AMQP_BASIC_ACK_METHOD";
+    case AMQP_BASIC_REJECT_METHOD:
+      return "AMQP_BASIC_REJECT_METHOD";
+    case AMQP_BASIC_RECOVER_ASYNC_METHOD:
+      return "AMQP_BASIC_RECOVER_ASYNC_METHOD";
+    case AMQP_BASIC_RECOVER_METHOD:
+      return "AMQP_BASIC_RECOVER_METHOD";
+    case AMQP_BASIC_RECOVER_OK_METHOD:
+      return "AMQP_BASIC_RECOVER_OK_METHOD";
+    case AMQP_BASIC_NACK_METHOD:
+      return "AMQP_BASIC_NACK_METHOD";
+    case AMQP_TX_SELECT_METHOD:
+      return "AMQP_TX_SELECT_METHOD";
+    case AMQP_TX_SELECT_OK_METHOD:
+      return "AMQP_TX_SELECT_OK_METHOD";
+    case AMQP_TX_COMMIT_METHOD:
+      return "AMQP_TX_COMMIT_METHOD";
+    case AMQP_TX_COMMIT_OK_METHOD:
+      return "AMQP_TX_COMMIT_OK_METHOD";
+    case AMQP_TX_ROLLBACK_METHOD:
+      return "AMQP_TX_ROLLBACK_METHOD";
+    case AMQP_TX_ROLLBACK_OK_METHOD:
+      return "AMQP_TX_ROLLBACK_OK_METHOD";
+    case AMQP_CONFIRM_SELECT_METHOD:
+      return "AMQP_CONFIRM_SELECT_METHOD";
+    case AMQP_CONFIRM_SELECT_OK_METHOD:
+      return "AMQP_CONFIRM_SELECT_OK_METHOD";
+    default:
+      return NULL;
+  }
+}
+
+amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber) {
+  switch (methodNumber) {
+    case AMQP_BASIC_PUBLISH_METHOD:
+      return 1;
+    case AMQP_BASIC_RETURN_METHOD:
+      return 1;
+    case AMQP_BASIC_DELIVER_METHOD:
+      return 1;
+    case AMQP_BASIC_GET_OK_METHOD:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+int amqp_decode_method(amqp_method_number_t methodNumber, amqp_pool_t *pool,
+                       amqp_bytes_t encoded, void **decoded) {
+  size_t offset = 0;
+  uint8_t bit_buffer;
+
+  switch (methodNumber) {
+    case AMQP_CONNECTION_START_METHOD: {
+      amqp_connection_start_t *m = (amqp_connection_start_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_connection_start_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_8(encoded, &offset, &m->version_major))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_8(encoded, &offset, &m->version_minor))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res =
+            amqp_decode_table(encoded, pool, &(m->server_properties), &offset);
+        if (res < 0) return res;
+      }
+      {
+        uint32_t len;
+        if (!amqp_decode_32(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->mechanisms, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint32_t len;
+        if (!amqp_decode_32(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->locales, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_START_OK_METHOD: {
+      amqp_connection_start_ok_t *m =
+          (amqp_connection_start_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_start_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        int res =
+            amqp_decode_table(encoded, pool, &(m->client_properties), &offset);
+        if (res < 0) return res;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->mechanism, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint32_t len;
+        if (!amqp_decode_32(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->response, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->locale, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_SECURE_METHOD: {
+      amqp_connection_secure_t *m = (amqp_connection_secure_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_connection_secure_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint32_t len;
+        if (!amqp_decode_32(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->challenge, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_SECURE_OK_METHOD: {
+      amqp_connection_secure_ok_t *m =
+          (amqp_connection_secure_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_secure_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint32_t len;
+        if (!amqp_decode_32(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->response, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_TUNE_METHOD: {
+      amqp_connection_tune_t *m = (amqp_connection_tune_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_connection_tune_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->channel_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_32(encoded, &offset, &m->frame_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_16(encoded, &offset, &m->heartbeat))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_TUNE_OK_METHOD: {
+      amqp_connection_tune_ok_t *m =
+          (amqp_connection_tune_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_tune_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->channel_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_32(encoded, &offset, &m->frame_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_16(encoded, &offset, &m->heartbeat))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_OPEN_METHOD: {
+      amqp_connection_open_t *m = (amqp_connection_open_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_connection_open_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->virtual_host, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->capabilities, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->insist = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_OPEN_OK_METHOD: {
+      amqp_connection_open_ok_t *m =
+          (amqp_connection_open_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_open_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->known_hosts, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_CLOSE_METHOD: {
+      amqp_connection_close_t *m = (amqp_connection_close_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_connection_close_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->reply_code))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->reply_text, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->class_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_16(encoded, &offset, &m->method_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_CLOSE_OK_METHOD: {
+      amqp_connection_close_ok_t *m =
+          (amqp_connection_close_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_close_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_BLOCKED_METHOD: {
+      amqp_connection_blocked_t *m =
+          (amqp_connection_blocked_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_blocked_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->reason, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONNECTION_UNBLOCKED_METHOD: {
+      amqp_connection_unblocked_t *m =
+          (amqp_connection_unblocked_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_unblocked_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CHANNEL_OPEN_METHOD: {
+      amqp_channel_open_t *m = (amqp_channel_open_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_channel_open_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->out_of_band, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CHANNEL_OPEN_OK_METHOD: {
+      amqp_channel_open_ok_t *m = (amqp_channel_open_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_channel_open_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint32_t len;
+        if (!amqp_decode_32(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->channel_id, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CHANNEL_FLOW_METHOD: {
+      amqp_channel_flow_t *m = (amqp_channel_flow_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_channel_flow_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->active = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CHANNEL_FLOW_OK_METHOD: {
+      amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_channel_flow_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->active = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CHANNEL_CLOSE_METHOD: {
+      amqp_channel_close_t *m = (amqp_channel_close_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_channel_close_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->reply_code))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->reply_text, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->class_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_16(encoded, &offset, &m->method_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CHANNEL_CLOSE_OK_METHOD: {
+      amqp_channel_close_ok_t *m = (amqp_channel_close_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_channel_close_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_ACCESS_REQUEST_METHOD: {
+      amqp_access_request_t *m = (amqp_access_request_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_access_request_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->realm, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->exclusive = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->passive = (bit_buffer & (1 << 1)) ? 1 : 0;
+      m->active = (bit_buffer & (1 << 2)) ? 1 : 0;
+      m->write = (bit_buffer & (1 << 3)) ? 1 : 0;
+      m->read = (bit_buffer & (1 << 4)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_ACCESS_REQUEST_OK_METHOD: {
+      amqp_access_request_ok_t *m = (amqp_access_request_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_access_request_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_DECLARE_METHOD: {
+      amqp_exchange_declare_t *m = (amqp_exchange_declare_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_exchange_declare_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->type, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->passive = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->durable = (bit_buffer & (1 << 1)) ? 1 : 0;
+      m->auto_delete = (bit_buffer & (1 << 2)) ? 1 : 0;
+      m->internal = (bit_buffer & (1 << 3)) ? 1 : 0;
+      m->nowait = (bit_buffer & (1 << 4)) ? 1 : 0;
+      {
+        int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_DECLARE_OK_METHOD: {
+      amqp_exchange_declare_ok_t *m =
+          (amqp_exchange_declare_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_exchange_declare_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_DELETE_METHOD: {
+      amqp_exchange_delete_t *m = (amqp_exchange_delete_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_exchange_delete_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->if_unused = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->nowait = (bit_buffer & (1 << 1)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_DELETE_OK_METHOD: {
+      amqp_exchange_delete_ok_t *m =
+          (amqp_exchange_delete_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_exchange_delete_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_BIND_METHOD: {
+      amqp_exchange_bind_t *m = (amqp_exchange_bind_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_exchange_bind_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->destination, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->source, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0;
+      {
+        int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_BIND_OK_METHOD: {
+      amqp_exchange_bind_ok_t *m = (amqp_exchange_bind_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_exchange_bind_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_UNBIND_METHOD: {
+      amqp_exchange_unbind_t *m = (amqp_exchange_unbind_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_exchange_unbind_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->destination, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->source, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0;
+      {
+        int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_EXCHANGE_UNBIND_OK_METHOD: {
+      amqp_exchange_unbind_ok_t *m =
+          (amqp_exchange_unbind_ok_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_exchange_unbind_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_DECLARE_METHOD: {
+      amqp_queue_declare_t *m = (amqp_queue_declare_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_declare_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->passive = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->durable = (bit_buffer & (1 << 1)) ? 1 : 0;
+      m->exclusive = (bit_buffer & (1 << 2)) ? 1 : 0;
+      m->auto_delete = (bit_buffer & (1 << 3)) ? 1 : 0;
+      m->nowait = (bit_buffer & (1 << 4)) ? 1 : 0;
+      {
+        int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_DECLARE_OK_METHOD: {
+      amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_declare_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_32(encoded, &offset, &m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_32(encoded, &offset, &m->consumer_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_BIND_METHOD: {
+      amqp_queue_bind_t *m =
+          (amqp_queue_bind_t *)amqp_pool_alloc(pool, sizeof(amqp_queue_bind_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0;
+      {
+        int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_BIND_OK_METHOD: {
+      amqp_queue_bind_ok_t *m = (amqp_queue_bind_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_bind_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_PURGE_METHOD: {
+      amqp_queue_purge_t *m = (amqp_queue_purge_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_purge_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_PURGE_OK_METHOD: {
+      amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_purge_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_32(encoded, &offset, &m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_DELETE_METHOD: {
+      amqp_queue_delete_t *m = (amqp_queue_delete_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_delete_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->if_unused = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->if_empty = (bit_buffer & (1 << 1)) ? 1 : 0;
+      m->nowait = (bit_buffer & (1 << 2)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_DELETE_OK_METHOD: {
+      amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_delete_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_32(encoded, &offset, &m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_UNBIND_METHOD: {
+      amqp_queue_unbind_t *m = (amqp_queue_unbind_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_unbind_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_QUEUE_UNBIND_OK_METHOD: {
+      amqp_queue_unbind_ok_t *m = (amqp_queue_unbind_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_unbind_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_QOS_METHOD: {
+      amqp_basic_qos_t *m =
+          (amqp_basic_qos_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_qos_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_32(encoded, &offset, &m->prefetch_size))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_16(encoded, &offset, &m->prefetch_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->global = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_QOS_OK_METHOD: {
+      amqp_basic_qos_ok_t *m = (amqp_basic_qos_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_qos_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_CONSUME_METHOD: {
+      amqp_basic_consume_t *m = (amqp_basic_consume_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_consume_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->no_local = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->no_ack = (bit_buffer & (1 << 1)) ? 1 : 0;
+      m->exclusive = (bit_buffer & (1 << 2)) ? 1 : 0;
+      m->nowait = (bit_buffer & (1 << 3)) ? 1 : 0;
+      {
+        int res = amqp_decode_table(encoded, pool, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_CONSUME_OK_METHOD: {
+      amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_consume_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_CANCEL_METHOD: {
+      amqp_basic_cancel_t *m = (amqp_basic_cancel_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_cancel_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_CANCEL_OK_METHOD: {
+      amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_cancel_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_PUBLISH_METHOD: {
+      amqp_basic_publish_t *m = (amqp_basic_publish_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_publish_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->mandatory = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->immediate = (bit_buffer & (1 << 1)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_RETURN_METHOD: {
+      amqp_basic_return_t *m = (amqp_basic_return_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_return_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->reply_code))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->reply_text, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_DELIVER_METHOD: {
+      amqp_basic_deliver_t *m = (amqp_basic_deliver_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_deliver_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->consumer_tag, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_64(encoded, &offset, &m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->redelivered = (bit_buffer & (1 << 0)) ? 1 : 0;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_GET_METHOD: {
+      amqp_basic_get_t *m =
+          (amqp_basic_get_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_get_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_16(encoded, &offset, &m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->queue, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->no_ack = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_GET_OK_METHOD: {
+      amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_get_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_64(encoded, &offset, &m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->redelivered = (bit_buffer & (1 << 0)) ? 1 : 0;
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->exchange, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->routing_key, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (!amqp_decode_32(encoded, &offset, &m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_GET_EMPTY_METHOD: {
+      amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_get_empty_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      {
+        uint8_t len;
+        if (!amqp_decode_8(encoded, &offset, &len) ||
+            !amqp_decode_bytes(encoded, &offset, &m->cluster_id, len))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_ACK_METHOD: {
+      amqp_basic_ack_t *m =
+          (amqp_basic_ack_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_ack_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_64(encoded, &offset, &m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->multiple = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_REJECT_METHOD: {
+      amqp_basic_reject_t *m = (amqp_basic_reject_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_reject_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_64(encoded, &offset, &m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_RECOVER_ASYNC_METHOD: {
+      amqp_basic_recover_async_t *m =
+          (amqp_basic_recover_async_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_basic_recover_async_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_RECOVER_METHOD: {
+      amqp_basic_recover_t *m = (amqp_basic_recover_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_recover_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->requeue = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_RECOVER_OK_METHOD: {
+      amqp_basic_recover_ok_t *m = (amqp_basic_recover_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_recover_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_BASIC_NACK_METHOD: {
+      amqp_basic_nack_t *m =
+          (amqp_basic_nack_t *)amqp_pool_alloc(pool, sizeof(amqp_basic_nack_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_64(encoded, &offset, &m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->multiple = (bit_buffer & (1 << 0)) ? 1 : 0;
+      m->requeue = (bit_buffer & (1 << 1)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_TX_SELECT_METHOD: {
+      amqp_tx_select_t *m =
+          (amqp_tx_select_t *)amqp_pool_alloc(pool, sizeof(amqp_tx_select_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_TX_SELECT_OK_METHOD: {
+      amqp_tx_select_ok_t *m = (amqp_tx_select_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_tx_select_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_TX_COMMIT_METHOD: {
+      amqp_tx_commit_t *m =
+          (amqp_tx_commit_t *)amqp_pool_alloc(pool, sizeof(amqp_tx_commit_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_TX_COMMIT_OK_METHOD: {
+      amqp_tx_commit_ok_t *m = (amqp_tx_commit_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_tx_commit_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_TX_ROLLBACK_METHOD: {
+      amqp_tx_rollback_t *m = (amqp_tx_rollback_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_tx_rollback_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_TX_ROLLBACK_OK_METHOD: {
+      amqp_tx_rollback_ok_t *m = (amqp_tx_rollback_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_tx_rollback_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONFIRM_SELECT_METHOD: {
+      amqp_confirm_select_t *m = (amqp_confirm_select_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_confirm_select_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      if (!amqp_decode_8(encoded, &offset, &bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      m->nowait = (bit_buffer & (1 << 0)) ? 1 : 0;
+      *decoded = m;
+      return 0;
+    }
+    case AMQP_CONFIRM_SELECT_OK_METHOD: {
+      amqp_confirm_select_ok_t *m = (amqp_confirm_select_ok_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_confirm_select_ok_t));
+      if (m == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      *decoded = m;
+      return 0;
+    }
+    default:
+      return AMQP_STATUS_UNKNOWN_METHOD;
+  }
+}
+
+int amqp_decode_properties(uint16_t class_id, amqp_pool_t *pool,
+                           amqp_bytes_t encoded, void **decoded) {
+  size_t offset = 0;
+
+  amqp_flags_t flags = 0;
+  int flagword_index = 0;
+  uint16_t partial_flags;
+
+  do {
+    if (!amqp_decode_16(encoded, &offset, &partial_flags))
+      return AMQP_STATUS_BAD_AMQP_DATA;
+    flags |= (partial_flags << (flagword_index * 16));
+    flagword_index++;
+  } while (partial_flags & 1);
+
+  switch (class_id) {
+    case 10: {
+      amqp_connection_properties_t *p =
+          (amqp_connection_properties_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_connection_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      *decoded = p;
+      return 0;
+    }
+    case 20: {
+      amqp_channel_properties_t *p =
+          (amqp_channel_properties_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_channel_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      *decoded = p;
+      return 0;
+    }
+    case 30: {
+      amqp_access_properties_t *p = (amqp_access_properties_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_access_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      *decoded = p;
+      return 0;
+    }
+    case 40: {
+      amqp_exchange_properties_t *p =
+          (amqp_exchange_properties_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_exchange_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      *decoded = p;
+      return 0;
+    }
+    case 50: {
+      amqp_queue_properties_t *p = (amqp_queue_properties_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_queue_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      *decoded = p;
+      return 0;
+    }
+    case 60: {
+      amqp_basic_properties_t *p = (amqp_basic_properties_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_basic_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      if (flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->content_type, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->content_encoding, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_HEADERS_FLAG) {
+        {
+          int res = amqp_decode_table(encoded, pool, &(p->headers), &offset);
+          if (res < 0) return res;
+        }
+      }
+      if (flags & AMQP_BASIC_DELIVERY_MODE_FLAG) {
+        if (!amqp_decode_8(encoded, &offset, &p->delivery_mode))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_PRIORITY_FLAG) {
+        if (!amqp_decode_8(encoded, &offset, &p->priority))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_CORRELATION_ID_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->correlation_id, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_REPLY_TO_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->reply_to, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_EXPIRATION_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->expiration, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_MESSAGE_ID_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->message_id, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_TIMESTAMP_FLAG) {
+        if (!amqp_decode_64(encoded, &offset, &p->timestamp))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_TYPE_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->type, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_USER_ID_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->user_id, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_APP_ID_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->app_id, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      if (flags & AMQP_BASIC_CLUSTER_ID_FLAG) {
+        {
+          uint8_t len;
+          if (!amqp_decode_8(encoded, &offset, &len) ||
+              !amqp_decode_bytes(encoded, &offset, &p->cluster_id, len))
+            return AMQP_STATUS_BAD_AMQP_DATA;
+        }
+      }
+      *decoded = p;
+      return 0;
+    }
+    case 90: {
+      amqp_tx_properties_t *p = (amqp_tx_properties_t *)amqp_pool_alloc(
+          pool, sizeof(amqp_tx_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      *decoded = p;
+      return 0;
+    }
+    case 85: {
+      amqp_confirm_properties_t *p =
+          (amqp_confirm_properties_t *)amqp_pool_alloc(
+              pool, sizeof(amqp_confirm_properties_t));
+      if (p == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+      p->_flags = flags;
+      *decoded = p;
+      return 0;
+    }
+    default:
+      return AMQP_STATUS_UNKNOWN_CLASS;
+  }
+}
+
+int amqp_encode_method(amqp_method_number_t methodNumber, void *decoded,
+                       amqp_bytes_t encoded) {
+  size_t offset = 0;
+  uint8_t bit_buffer;
+
+  switch (methodNumber) {
+    case AMQP_CONNECTION_START_METHOD: {
+      amqp_connection_start_t *m = (amqp_connection_start_t *)decoded;
+      if (!amqp_encode_8(encoded, &offset, m->version_major))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_8(encoded, &offset, m->version_minor))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->server_properties), &offset);
+        if (res < 0) return res;
+      }
+      if (UINT32_MAX < m->mechanisms.len ||
+          !amqp_encode_32(encoded, &offset, (uint32_t)m->mechanisms.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->mechanisms))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT32_MAX < m->locales.len ||
+          !amqp_encode_32(encoded, &offset, (uint32_t)m->locales.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->locales))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_START_OK_METHOD: {
+      amqp_connection_start_ok_t *m = (amqp_connection_start_ok_t *)decoded;
+      {
+        int res = amqp_encode_table(encoded, &(m->client_properties), &offset);
+        if (res < 0) return res;
+      }
+      if (UINT8_MAX < m->mechanism.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->mechanism.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->mechanism))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT32_MAX < m->response.len ||
+          !amqp_encode_32(encoded, &offset, (uint32_t)m->response.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->response))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->locale.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->locale.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->locale))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_SECURE_METHOD: {
+      amqp_connection_secure_t *m = (amqp_connection_secure_t *)decoded;
+      if (UINT32_MAX < m->challenge.len ||
+          !amqp_encode_32(encoded, &offset, (uint32_t)m->challenge.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->challenge))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_SECURE_OK_METHOD: {
+      amqp_connection_secure_ok_t *m = (amqp_connection_secure_ok_t *)decoded;
+      if (UINT32_MAX < m->response.len ||
+          !amqp_encode_32(encoded, &offset, (uint32_t)m->response.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->response))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_TUNE_METHOD: {
+      amqp_connection_tune_t *m = (amqp_connection_tune_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->channel_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_32(encoded, &offset, m->frame_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_16(encoded, &offset, m->heartbeat))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_TUNE_OK_METHOD: {
+      amqp_connection_tune_ok_t *m = (amqp_connection_tune_ok_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->channel_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_32(encoded, &offset, m->frame_max))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_16(encoded, &offset, m->heartbeat))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_OPEN_METHOD: {
+      amqp_connection_open_t *m = (amqp_connection_open_t *)decoded;
+      if (UINT8_MAX < m->virtual_host.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->virtual_host.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->virtual_host))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->capabilities.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->capabilities.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->capabilities))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->insist) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_OPEN_OK_METHOD: {
+      amqp_connection_open_ok_t *m = (amqp_connection_open_ok_t *)decoded;
+      if (UINT8_MAX < m->known_hosts.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->known_hosts.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->known_hosts))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_CLOSE_METHOD: {
+      amqp_connection_close_t *m = (amqp_connection_close_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->reply_code))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->reply_text.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->reply_text))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_16(encoded, &offset, m->class_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_16(encoded, &offset, m->method_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_CLOSE_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_BLOCKED_METHOD: {
+      amqp_connection_blocked_t *m = (amqp_connection_blocked_t *)decoded;
+      if (UINT8_MAX < m->reason.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->reason.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->reason))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONNECTION_UNBLOCKED_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_CHANNEL_OPEN_METHOD: {
+      amqp_channel_open_t *m = (amqp_channel_open_t *)decoded;
+      if (UINT8_MAX < m->out_of_band.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->out_of_band.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->out_of_band))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CHANNEL_OPEN_OK_METHOD: {
+      amqp_channel_open_ok_t *m = (amqp_channel_open_ok_t *)decoded;
+      if (UINT32_MAX < m->channel_id.len ||
+          !amqp_encode_32(encoded, &offset, (uint32_t)m->channel_id.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->channel_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CHANNEL_FLOW_METHOD: {
+      amqp_channel_flow_t *m = (amqp_channel_flow_t *)decoded;
+      bit_buffer = 0;
+      if (m->active) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CHANNEL_FLOW_OK_METHOD: {
+      amqp_channel_flow_ok_t *m = (amqp_channel_flow_ok_t *)decoded;
+      bit_buffer = 0;
+      if (m->active) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CHANNEL_CLOSE_METHOD: {
+      amqp_channel_close_t *m = (amqp_channel_close_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->reply_code))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->reply_text.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->reply_text))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_16(encoded, &offset, m->class_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_16(encoded, &offset, m->method_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CHANNEL_CLOSE_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_ACCESS_REQUEST_METHOD: {
+      amqp_access_request_t *m = (amqp_access_request_t *)decoded;
+      if (UINT8_MAX < m->realm.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->realm.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->realm))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->exclusive) bit_buffer |= (1 << 0);
+      if (m->passive) bit_buffer |= (1 << 1);
+      if (m->active) bit_buffer |= (1 << 2);
+      if (m->write) bit_buffer |= (1 << 3);
+      if (m->read) bit_buffer |= (1 << 4);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_ACCESS_REQUEST_OK_METHOD: {
+      amqp_access_request_ok_t *m = (amqp_access_request_ok_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_DECLARE_METHOD: {
+      amqp_exchange_declare_t *m = (amqp_exchange_declare_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->type.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->type.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->type))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->passive) bit_buffer |= (1 << 0);
+      if (m->durable) bit_buffer |= (1 << 1);
+      if (m->auto_delete) bit_buffer |= (1 << 2);
+      if (m->internal) bit_buffer |= (1 << 3);
+      if (m->nowait) bit_buffer |= (1 << 4);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_DECLARE_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_DELETE_METHOD: {
+      amqp_exchange_delete_t *m = (amqp_exchange_delete_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->if_unused) bit_buffer |= (1 << 0);
+      if (m->nowait) bit_buffer |= (1 << 1);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_DELETE_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_BIND_METHOD: {
+      amqp_exchange_bind_t *m = (amqp_exchange_bind_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->destination.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->destination.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->destination))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->source.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->source.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->source))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->nowait) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_BIND_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_UNBIND_METHOD: {
+      amqp_exchange_unbind_t *m = (amqp_exchange_unbind_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->destination.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->destination.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->destination))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->source.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->source.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->source))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->nowait) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      return (int)offset;
+    }
+    case AMQP_EXCHANGE_UNBIND_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_QUEUE_DECLARE_METHOD: {
+      amqp_queue_declare_t *m = (amqp_queue_declare_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->passive) bit_buffer |= (1 << 0);
+      if (m->durable) bit_buffer |= (1 << 1);
+      if (m->exclusive) bit_buffer |= (1 << 2);
+      if (m->auto_delete) bit_buffer |= (1 << 3);
+      if (m->nowait) bit_buffer |= (1 << 4);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      return (int)offset;
+    }
+    case AMQP_QUEUE_DECLARE_OK_METHOD: {
+      amqp_queue_declare_ok_t *m = (amqp_queue_declare_ok_t *)decoded;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_32(encoded, &offset, m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_32(encoded, &offset, m->consumer_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_QUEUE_BIND_METHOD: {
+      amqp_queue_bind_t *m = (amqp_queue_bind_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->nowait) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      return (int)offset;
+    }
+    case AMQP_QUEUE_BIND_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_QUEUE_PURGE_METHOD: {
+      amqp_queue_purge_t *m = (amqp_queue_purge_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->nowait) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_QUEUE_PURGE_OK_METHOD: {
+      amqp_queue_purge_ok_t *m = (amqp_queue_purge_ok_t *)decoded;
+      if (!amqp_encode_32(encoded, &offset, m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_QUEUE_DELETE_METHOD: {
+      amqp_queue_delete_t *m = (amqp_queue_delete_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->if_unused) bit_buffer |= (1 << 0);
+      if (m->if_empty) bit_buffer |= (1 << 1);
+      if (m->nowait) bit_buffer |= (1 << 2);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_QUEUE_DELETE_OK_METHOD: {
+      amqp_queue_delete_ok_t *m = (amqp_queue_delete_ok_t *)decoded;
+      if (!amqp_encode_32(encoded, &offset, m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_QUEUE_UNBIND_METHOD: {
+      amqp_queue_unbind_t *m = (amqp_queue_unbind_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      return (int)offset;
+    }
+    case AMQP_QUEUE_UNBIND_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_BASIC_QOS_METHOD: {
+      amqp_basic_qos_t *m = (amqp_basic_qos_t *)decoded;
+      if (!amqp_encode_32(encoded, &offset, m->prefetch_size))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_16(encoded, &offset, m->prefetch_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->global) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_QOS_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_BASIC_CONSUME_METHOD: {
+      amqp_basic_consume_t *m = (amqp_basic_consume_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->consumer_tag.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->consumer_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->no_local) bit_buffer |= (1 << 0);
+      if (m->no_ack) bit_buffer |= (1 << 1);
+      if (m->exclusive) bit_buffer |= (1 << 2);
+      if (m->nowait) bit_buffer |= (1 << 3);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      {
+        int res = amqp_encode_table(encoded, &(m->arguments), &offset);
+        if (res < 0) return res;
+      }
+      return (int)offset;
+    }
+    case AMQP_BASIC_CONSUME_OK_METHOD: {
+      amqp_basic_consume_ok_t *m = (amqp_basic_consume_ok_t *)decoded;
+      if (UINT8_MAX < m->consumer_tag.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->consumer_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_CANCEL_METHOD: {
+      amqp_basic_cancel_t *m = (amqp_basic_cancel_t *)decoded;
+      if (UINT8_MAX < m->consumer_tag.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->consumer_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->nowait) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_CANCEL_OK_METHOD: {
+      amqp_basic_cancel_ok_t *m = (amqp_basic_cancel_ok_t *)decoded;
+      if (UINT8_MAX < m->consumer_tag.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->consumer_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_PUBLISH_METHOD: {
+      amqp_basic_publish_t *m = (amqp_basic_publish_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->mandatory) bit_buffer |= (1 << 0);
+      if (m->immediate) bit_buffer |= (1 << 1);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_RETURN_METHOD: {
+      amqp_basic_return_t *m = (amqp_basic_return_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->reply_code))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->reply_text.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->reply_text.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->reply_text))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_DELIVER_METHOD: {
+      amqp_basic_deliver_t *m = (amqp_basic_deliver_t *)decoded;
+      if (UINT8_MAX < m->consumer_tag.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->consumer_tag.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->consumer_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_64(encoded, &offset, m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->redelivered) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_GET_METHOD: {
+      amqp_basic_get_t *m = (amqp_basic_get_t *)decoded;
+      if (!amqp_encode_16(encoded, &offset, m->ticket))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->queue.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->queue.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->queue))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->no_ack) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_GET_OK_METHOD: {
+      amqp_basic_get_ok_t *m = (amqp_basic_get_ok_t *)decoded;
+      if (!amqp_encode_64(encoded, &offset, m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->redelivered) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->exchange.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->exchange.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->exchange))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (UINT8_MAX < m->routing_key.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->routing_key.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->routing_key))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      if (!amqp_encode_32(encoded, &offset, m->message_count))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_GET_EMPTY_METHOD: {
+      amqp_basic_get_empty_t *m = (amqp_basic_get_empty_t *)decoded;
+      if (UINT8_MAX < m->cluster_id.len ||
+          !amqp_encode_8(encoded, &offset, (uint8_t)m->cluster_id.len) ||
+          !amqp_encode_bytes(encoded, &offset, m->cluster_id))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_ACK_METHOD: {
+      amqp_basic_ack_t *m = (amqp_basic_ack_t *)decoded;
+      if (!amqp_encode_64(encoded, &offset, m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->multiple) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_REJECT_METHOD: {
+      amqp_basic_reject_t *m = (amqp_basic_reject_t *)decoded;
+      if (!amqp_encode_64(encoded, &offset, m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->requeue) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_RECOVER_ASYNC_METHOD: {
+      amqp_basic_recover_async_t *m = (amqp_basic_recover_async_t *)decoded;
+      bit_buffer = 0;
+      if (m->requeue) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_RECOVER_METHOD: {
+      amqp_basic_recover_t *m = (amqp_basic_recover_t *)decoded;
+      bit_buffer = 0;
+      if (m->requeue) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_BASIC_RECOVER_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_BASIC_NACK_METHOD: {
+      amqp_basic_nack_t *m = (amqp_basic_nack_t *)decoded;
+      if (!amqp_encode_64(encoded, &offset, m->delivery_tag))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      bit_buffer = 0;
+      if (m->multiple) bit_buffer |= (1 << 0);
+      if (m->requeue) bit_buffer |= (1 << 1);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_TX_SELECT_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_TX_SELECT_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_TX_COMMIT_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_TX_COMMIT_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_TX_ROLLBACK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_TX_ROLLBACK_OK_METHOD: {
+      return (int)offset;
+    }
+    case AMQP_CONFIRM_SELECT_METHOD: {
+      amqp_confirm_select_t *m = (amqp_confirm_select_t *)decoded;
+      bit_buffer = 0;
+      if (m->nowait) bit_buffer |= (1 << 0);
+      if (!amqp_encode_8(encoded, &offset, bit_buffer))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      return (int)offset;
+    }
+    case AMQP_CONFIRM_SELECT_OK_METHOD: {
+      return (int)offset;
+    }
+    default:
+      return AMQP_STATUS_UNKNOWN_METHOD;
+  }
+}
+
+int amqp_encode_properties(uint16_t class_id, void *decoded,
+                           amqp_bytes_t encoded) {
+  size_t offset = 0;
+
+  /* Cheat, and get the flags out generically, relying on the
+     similarity of structure between classes */
+  amqp_flags_t flags = *(amqp_flags_t *)decoded; /* cheating! */
+
+  {
+    /* We take a copy of flags to avoid destroying it, as it is used
+       in the autogenerated code below. */
+    amqp_flags_t remaining_flags = flags;
+    do {
+      amqp_flags_t remainder = remaining_flags >> 16;
+      uint16_t partial_flags = remaining_flags & 0xFFFE;
+      if (remainder != 0) {
+        partial_flags |= 1;
+      }
+      if (!amqp_encode_16(encoded, &offset, partial_flags))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      remaining_flags = remainder;
+    } while (remaining_flags != 0);
+  }
+
+  switch (class_id) {
+    case 10: {
+      return (int)offset;
+    }
+    case 20: {
+      return (int)offset;
+    }
+    case 30: {
+      return (int)offset;
+    }
+    case 40: {
+      return (int)offset;
+    }
+    case 50: {
+      return (int)offset;
+    }
+    case 60: {
+      amqp_basic_properties_t *p = (amqp_basic_properties_t *)decoded;
+      if (flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
+        if (UINT8_MAX < p->content_type.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->content_type.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->content_type))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_CONTENT_ENCODING_FLAG) {
+        if (UINT8_MAX < p->content_encoding.len ||
+            !amqp_encode_8(encoded, &offset,
+                           (uint8_t)p->content_encoding.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->content_encoding))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_HEADERS_FLAG) {
+        {
+          int res = amqp_encode_table(encoded, &(p->headers), &offset);
+          if (res < 0) return res;
+        }
+      }
+      if (flags & AMQP_BASIC_DELIVERY_MODE_FLAG) {
+        if (!amqp_encode_8(encoded, &offset, p->delivery_mode))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_PRIORITY_FLAG) {
+        if (!amqp_encode_8(encoded, &offset, p->priority))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_CORRELATION_ID_FLAG) {
+        if (UINT8_MAX < p->correlation_id.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->correlation_id.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->correlation_id))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_REPLY_TO_FLAG) {
+        if (UINT8_MAX < p->reply_to.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->reply_to.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->reply_to))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_EXPIRATION_FLAG) {
+        if (UINT8_MAX < p->expiration.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->expiration.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->expiration))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_MESSAGE_ID_FLAG) {
+        if (UINT8_MAX < p->message_id.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->message_id.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->message_id))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_TIMESTAMP_FLAG) {
+        if (!amqp_encode_64(encoded, &offset, p->timestamp))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_TYPE_FLAG) {
+        if (UINT8_MAX < p->type.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->type.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->type))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_USER_ID_FLAG) {
+        if (UINT8_MAX < p->user_id.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->user_id.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->user_id))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_APP_ID_FLAG) {
+        if (UINT8_MAX < p->app_id.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->app_id.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->app_id))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      if (flags & AMQP_BASIC_CLUSTER_ID_FLAG) {
+        if (UINT8_MAX < p->cluster_id.len ||
+            !amqp_encode_8(encoded, &offset, (uint8_t)p->cluster_id.len) ||
+            !amqp_encode_bytes(encoded, &offset, p->cluster_id))
+          return AMQP_STATUS_BAD_AMQP_DATA;
+      }
+      return (int)offset;
+    }
+    case 90: {
+      return (int)offset;
+    }
+    case 85: {
+      return (int)offset;
+    }
+    default:
+      return AMQP_STATUS_UNKNOWN_CLASS;
+  }
+}
+
+/**
+ * amqp_channel_open
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @returns amqp_channel_open_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_channel_open_ok_t *AMQP_CALL
+    amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel) {
+  amqp_channel_open_t req;
+  req.out_of_band = amqp_empty_bytes;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_CHANNEL_OPEN_METHOD,
+                                 AMQP_CHANNEL_OPEN_OK_METHOD, &req);
+}
+
+/**
+ * amqp_channel_flow
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] active active
+ * @returns amqp_channel_flow_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_channel_flow_ok_t *AMQP_CALL
+    amqp_channel_flow(amqp_connection_state_t state, amqp_channel_t channel,
+                      amqp_boolean_t active) {
+  amqp_channel_flow_t req;
+  req.active = active;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_CHANNEL_FLOW_METHOD,
+                                 AMQP_CHANNEL_FLOW_OK_METHOD, &req);
+}
+
+/**
+ * amqp_exchange_declare
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] exchange exchange
+ * @param [in] type type
+ * @param [in] passive passive
+ * @param [in] durable durable
+ * @param [in] auto_delete auto_delete
+ * @param [in] internal internal
+ * @param [in] arguments arguments
+ * @returns amqp_exchange_declare_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_exchange_declare_ok_t *AMQP_CALL amqp_exchange_declare(
+    amqp_connection_state_t state, amqp_channel_t channel,
+    amqp_bytes_t exchange, amqp_bytes_t type, amqp_boolean_t passive,
+    amqp_boolean_t durable, amqp_boolean_t auto_delete, amqp_boolean_t internal,
+    amqp_table_t arguments) {
+  amqp_exchange_declare_t req;
+  req.ticket = 0;
+  req.exchange = exchange;
+  req.type = type;
+  req.passive = passive;
+  req.durable = durable;
+  req.auto_delete = auto_delete;
+  req.internal = internal;
+  req.nowait = 0;
+  req.arguments = arguments;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_DECLARE_METHOD,
+                                 AMQP_EXCHANGE_DECLARE_OK_METHOD, &req);
+}
+
+/**
+ * amqp_exchange_delete
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] exchange exchange
+ * @param [in] if_unused if_unused
+ * @returns amqp_exchange_delete_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_exchange_delete_ok_t *AMQP_CALL
+    amqp_exchange_delete(amqp_connection_state_t state, amqp_channel_t channel,
+                         amqp_bytes_t exchange, amqp_boolean_t if_unused) {
+  amqp_exchange_delete_t req;
+  req.ticket = 0;
+  req.exchange = exchange;
+  req.if_unused = if_unused;
+  req.nowait = 0;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_DELETE_METHOD,
+                                 AMQP_EXCHANGE_DELETE_OK_METHOD, &req);
+}
+
+/**
+ * amqp_exchange_bind
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] destination destination
+ * @param [in] source source
+ * @param [in] routing_key routing_key
+ * @param [in] arguments arguments
+ * @returns amqp_exchange_bind_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_exchange_bind_ok_t *AMQP_CALL
+    amqp_exchange_bind(amqp_connection_state_t state, amqp_channel_t channel,
+                       amqp_bytes_t destination, amqp_bytes_t source,
+                       amqp_bytes_t routing_key, amqp_table_t arguments) {
+  amqp_exchange_bind_t req;
+  req.ticket = 0;
+  req.destination = destination;
+  req.source = source;
+  req.routing_key = routing_key;
+  req.nowait = 0;
+  req.arguments = arguments;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_BIND_METHOD,
+                                 AMQP_EXCHANGE_BIND_OK_METHOD, &req);
+}
+
+/**
+ * amqp_exchange_unbind
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] destination destination
+ * @param [in] source source
+ * @param [in] routing_key routing_key
+ * @param [in] arguments arguments
+ * @returns amqp_exchange_unbind_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_exchange_unbind_ok_t *AMQP_CALL
+    amqp_exchange_unbind(amqp_connection_state_t state, amqp_channel_t channel,
+                         amqp_bytes_t destination, amqp_bytes_t source,
+                         amqp_bytes_t routing_key, amqp_table_t arguments) {
+  amqp_exchange_unbind_t req;
+  req.ticket = 0;
+  req.destination = destination;
+  req.source = source;
+  req.routing_key = routing_key;
+  req.nowait = 0;
+  req.arguments = arguments;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_EXCHANGE_UNBIND_METHOD,
+                                 AMQP_EXCHANGE_UNBIND_OK_METHOD, &req);
+}
+
+/**
+ * amqp_queue_declare
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] queue queue
+ * @param [in] passive passive
+ * @param [in] durable durable
+ * @param [in] exclusive exclusive
+ * @param [in] auto_delete auto_delete
+ * @param [in] arguments arguments
+ * @returns amqp_queue_declare_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_queue_declare_ok_t *AMQP_CALL amqp_queue_declare(
+    amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue,
+    amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive,
+    amqp_boolean_t auto_delete, amqp_table_t arguments) {
+  amqp_queue_declare_t req;
+  req.ticket = 0;
+  req.queue = queue;
+  req.passive = passive;
+  req.durable = durable;
+  req.exclusive = exclusive;
+  req.auto_delete = auto_delete;
+  req.nowait = 0;
+  req.arguments = arguments;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_DECLARE_METHOD,
+                                 AMQP_QUEUE_DECLARE_OK_METHOD, &req);
+}
+
+/**
+ * amqp_queue_bind
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] queue queue
+ * @param [in] exchange exchange
+ * @param [in] routing_key routing_key
+ * @param [in] arguments arguments
+ * @returns amqp_queue_bind_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_queue_bind_ok_t *AMQP_CALL amqp_queue_bind(
+    amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue,
+    amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments) {
+  amqp_queue_bind_t req;
+  req.ticket = 0;
+  req.queue = queue;
+  req.exchange = exchange;
+  req.routing_key = routing_key;
+  req.nowait = 0;
+  req.arguments = arguments;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_BIND_METHOD,
+                                 AMQP_QUEUE_BIND_OK_METHOD, &req);
+}
+
+/**
+ * amqp_queue_purge
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] queue queue
+ * @returns amqp_queue_purge_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_queue_purge_ok_t *AMQP_CALL amqp_queue_purge(amqp_connection_state_t state,
+                                                  amqp_channel_t channel,
+                                                  amqp_bytes_t queue) {
+  amqp_queue_purge_t req;
+  req.ticket = 0;
+  req.queue = queue;
+  req.nowait = 0;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_PURGE_METHOD,
+                                 AMQP_QUEUE_PURGE_OK_METHOD, &req);
+}
+
+/**
+ * amqp_queue_delete
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] queue queue
+ * @param [in] if_unused if_unused
+ * @param [in] if_empty if_empty
+ * @returns amqp_queue_delete_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_queue_delete_ok_t *AMQP_CALL amqp_queue_delete(
+    amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue,
+    amqp_boolean_t if_unused, amqp_boolean_t if_empty) {
+  amqp_queue_delete_t req;
+  req.ticket = 0;
+  req.queue = queue;
+  req.if_unused = if_unused;
+  req.if_empty = if_empty;
+  req.nowait = 0;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_DELETE_METHOD,
+                                 AMQP_QUEUE_DELETE_OK_METHOD, &req);
+}
+
+/**
+ * amqp_queue_unbind
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] queue queue
+ * @param [in] exchange exchange
+ * @param [in] routing_key routing_key
+ * @param [in] arguments arguments
+ * @returns amqp_queue_unbind_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_queue_unbind_ok_t *AMQP_CALL amqp_queue_unbind(
+    amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue,
+    amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments) {
+  amqp_queue_unbind_t req;
+  req.ticket = 0;
+  req.queue = queue;
+  req.exchange = exchange;
+  req.routing_key = routing_key;
+  req.arguments = arguments;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_QUEUE_UNBIND_METHOD,
+                                 AMQP_QUEUE_UNBIND_OK_METHOD, &req);
+}
+
+/**
+ * amqp_basic_qos
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] prefetch_size prefetch_size
+ * @param [in] prefetch_count prefetch_count
+ * @param [in] global global
+ * @returns amqp_basic_qos_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_basic_qos_ok_t *AMQP_CALL amqp_basic_qos(amqp_connection_state_t state,
+                                              amqp_channel_t channel,
+                                              uint32_t prefetch_size,
+                                              uint16_t prefetch_count,
+                                              amqp_boolean_t global) {
+  amqp_basic_qos_t req;
+  req.prefetch_size = prefetch_size;
+  req.prefetch_count = prefetch_count;
+  req.global = global;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_QOS_METHOD,
+                                 AMQP_BASIC_QOS_OK_METHOD, &req);
+}
+
+/**
+ * amqp_basic_consume
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] queue queue
+ * @param [in] consumer_tag consumer_tag
+ * @param [in] no_local no_local
+ * @param [in] no_ack no_ack
+ * @param [in] exclusive exclusive
+ * @param [in] arguments arguments
+ * @returns amqp_basic_consume_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_basic_consume_ok_t *AMQP_CALL amqp_basic_consume(
+    amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue,
+    amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack,
+    amqp_boolean_t exclusive, amqp_table_t arguments) {
+  amqp_basic_consume_t req;
+  req.ticket = 0;
+  req.queue = queue;
+  req.consumer_tag = consumer_tag;
+  req.no_local = no_local;
+  req.no_ack = no_ack;
+  req.exclusive = exclusive;
+  req.nowait = 0;
+  req.arguments = arguments;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_CONSUME_METHOD,
+                                 AMQP_BASIC_CONSUME_OK_METHOD, &req);
+}
+
+/**
+ * amqp_basic_cancel
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] consumer_tag consumer_tag
+ * @returns amqp_basic_cancel_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_basic_cancel_ok_t *AMQP_CALL
+    amqp_basic_cancel(amqp_connection_state_t state, amqp_channel_t channel,
+                      amqp_bytes_t consumer_tag) {
+  amqp_basic_cancel_t req;
+  req.consumer_tag = consumer_tag;
+  req.nowait = 0;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_CANCEL_METHOD,
+                                 AMQP_BASIC_CANCEL_OK_METHOD, &req);
+}
+
+/**
+ * amqp_basic_recover
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @param [in] requeue requeue
+ * @returns amqp_basic_recover_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_basic_recover_ok_t *AMQP_CALL
+    amqp_basic_recover(amqp_connection_state_t state, amqp_channel_t channel,
+                       amqp_boolean_t requeue) {
+  amqp_basic_recover_t req;
+  req.requeue = requeue;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_BASIC_RECOVER_METHOD,
+                                 AMQP_BASIC_RECOVER_OK_METHOD, &req);
+}
+
+/**
+ * amqp_tx_select
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @returns amqp_tx_select_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_tx_select_ok_t *AMQP_CALL amqp_tx_select(amqp_connection_state_t state,
+                                              amqp_channel_t channel) {
+  amqp_tx_select_t req;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_TX_SELECT_METHOD,
+                                 AMQP_TX_SELECT_OK_METHOD, &req);
+}
+
+/**
+ * amqp_tx_commit
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @returns amqp_tx_commit_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_tx_commit_ok_t *AMQP_CALL amqp_tx_commit(amqp_connection_state_t state,
+                                              amqp_channel_t channel) {
+  amqp_tx_commit_t req;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_TX_COMMIT_METHOD,
+                                 AMQP_TX_COMMIT_OK_METHOD, &req);
+}
+
+/**
+ * amqp_tx_rollback
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @returns amqp_tx_rollback_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_tx_rollback_ok_t *AMQP_CALL amqp_tx_rollback(amqp_connection_state_t state,
+                                                  amqp_channel_t channel) {
+  amqp_tx_rollback_t req;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_TX_ROLLBACK_METHOD,
+                                 AMQP_TX_ROLLBACK_OK_METHOD, &req);
+}
+
+/**
+ * amqp_confirm_select
+ *
+ * @param [in] state connection state
+ * @param [in] channel the channel to do the RPC on
+ * @returns amqp_confirm_select_ok_t
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_confirm_select_ok_t *AMQP_CALL
+    amqp_confirm_select(amqp_connection_state_t state, amqp_channel_t channel) {
+  amqp_confirm_select_t req;
+  req.nowait = 0;
+
+  return amqp_simple_rpc_decoded(state, channel, AMQP_CONFIRM_SELECT_METHOD,
+                                 AMQP_CONFIRM_SELECT_OK_METHOD, &req);
+}

+ 0 - 0
ext/librabbitmq/centos_x64/include/amqp_framing.h → ext/librabbitmq/librabbitmq/amqp_framing.h


+ 195 - 0
ext/librabbitmq/librabbitmq/amqp_hostcheck.c

@@ -0,0 +1,195 @@
+/*
+ * Copyright 1996-2014 Daniel Stenberg <[email protected]>.
+ * Copyright 2014 Michael Steinert
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization of the
+ * copyright holder.
+ */
+
+#include "amqp_hostcheck.h"
+
+#include <string.h>
+
+/* Portable, consistent toupper (remember EBCDIC). Do not use toupper()
+ * because its behavior is altered by the current locale.
+ */
+
+static char amqp_raw_toupper(char in) {
+  switch (in) {
+    case 'a':
+      return 'A';
+    case 'b':
+      return 'B';
+    case 'c':
+      return 'C';
+    case 'd':
+      return 'D';
+    case 'e':
+      return 'E';
+    case 'f':
+      return 'F';
+    case 'g':
+      return 'G';
+    case 'h':
+      return 'H';
+    case 'i':
+      return 'I';
+    case 'j':
+      return 'J';
+    case 'k':
+      return 'K';
+    case 'l':
+      return 'L';
+    case 'm':
+      return 'M';
+    case 'n':
+      return 'N';
+    case 'o':
+      return 'O';
+    case 'p':
+      return 'P';
+    case 'q':
+      return 'Q';
+    case 'r':
+      return 'R';
+    case 's':
+      return 'S';
+    case 't':
+      return 'T';
+    case 'u':
+      return 'U';
+    case 'v':
+      return 'V';
+    case 'w':
+      return 'W';
+    case 'x':
+      return 'X';
+    case 'y':
+      return 'Y';
+    case 'z':
+      return 'Z';
+  }
+  return in;
+}
+
+/*
+ * amqp_raw_equal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * some further explanation to why this function is necessary.
+ *
+ * The function is capable of comparing a-z case insensitively even for
+ * non-ascii.
+ */
+
+static int amqp_raw_equal(const char *first, const char *second) {
+  while (*first && *second) {
+    if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) {
+      /* get out of the loop as soon as they don't match */
+      break;
+    }
+    first++;
+    second++;
+  }
+  /* we do the comparison here (possibly again), just to make sure that if
+   * the loop above is skipped because one of the strings reached zero, we
+   * must not return this as a successful match
+   */
+  return (amqp_raw_toupper(*first) == amqp_raw_toupper(*second));
+}
+
+static int amqp_raw_nequal(const char *first, const char *second, size_t max) {
+  while (*first && *second && max) {
+    if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) {
+      break;
+    }
+    max--;
+    first++;
+    second++;
+  }
+  if (0 == max) {
+    return 1; /* they are equal this far */
+  }
+  return amqp_raw_toupper(*first) == amqp_raw_toupper(*second);
+}
+
+/*
+ * Match a hostname against a wildcard pattern.
+ * E.g.
+ *  "foo.host.com" matches "*.host.com".
+ *
+ * We use the matching rule described in RFC6125, section 6.4.3.
+ * http://tools.ietf.org/html/rfc6125#section-6.4.3
+ */
+
+static amqp_hostcheck_result amqp_hostmatch(const char *hostname,
+                                            const char *pattern) {
+  const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
+  int wildcard_enabled;
+  size_t prefixlen, suffixlen;
+  pattern_wildcard = strchr(pattern, '*');
+  if (pattern_wildcard == NULL) {
+    return amqp_raw_equal(pattern, hostname) ? AMQP_HCR_MATCH
+                                             : AMQP_HCR_NO_MATCH;
+  }
+  /* We require at least 2 dots in pattern to avoid too wide wildcard match. */
+  wildcard_enabled = 1;
+  pattern_label_end = strchr(pattern, '.');
+  if (pattern_label_end == NULL || strchr(pattern_label_end + 1, '.') == NULL ||
+      pattern_wildcard > pattern_label_end ||
+      amqp_raw_nequal(pattern, "xn--", 4)) {
+    wildcard_enabled = 0;
+  }
+  if (!wildcard_enabled) {
+    return amqp_raw_equal(pattern, hostname) ? AMQP_HCR_MATCH
+                                             : AMQP_HCR_NO_MATCH;
+  }
+  hostname_label_end = strchr(hostname, '.');
+  if (hostname_label_end == NULL ||
+      !amqp_raw_equal(pattern_label_end, hostname_label_end)) {
+    return AMQP_HCR_NO_MATCH;
+  }
+  /* The wildcard must match at least one character, so the left-most
+   * label of the hostname is at least as large as the left-most label
+   * of the pattern.
+   */
+  if (hostname_label_end - hostname < pattern_label_end - pattern) {
+    return AMQP_HCR_NO_MATCH;
+  }
+  prefixlen = pattern_wildcard - pattern;
+  suffixlen = pattern_label_end - (pattern_wildcard + 1);
+  return amqp_raw_nequal(pattern, hostname, prefixlen) &&
+                 amqp_raw_nequal(pattern_wildcard + 1,
+                                 hostname_label_end - suffixlen, suffixlen)
+             ? AMQP_HCR_MATCH
+             : AMQP_HCR_NO_MATCH;
+}
+
+amqp_hostcheck_result amqp_hostcheck(const char *match_pattern,
+                                     const char *hostname) {
+  /* sanity check */
+  if (!match_pattern || !*match_pattern || !hostname || !*hostname) {
+    return AMQP_HCR_NO_MATCH;
+  }
+  /* trivial case */
+  if (amqp_raw_equal(hostname, match_pattern)) {
+    return AMQP_HCR_MATCH;
+  }
+  return amqp_hostmatch(hostname, match_pattern);
+}

+ 48 - 0
ext/librabbitmq/librabbitmq/amqp_hostcheck.h

@@ -0,0 +1,48 @@
+#ifndef librabbitmq_amqp_hostcheck_h
+#define librabbitmq_amqp_hostcheck_h
+
+/*
+ * Copyright 1996-2014 Daniel Stenberg <[email protected]>.
+ * Copyright 2014 Michael Steinert
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization of the
+ * copyright holder.
+ */
+
+typedef enum {
+  AMQP_HCR_NO_MATCH = 0,
+  AMQP_HCR_MATCH = 1
+} amqp_hostcheck_result;
+
+/**
+ * Determine whether hostname matches match_pattern.
+ *
+ * match_pattern may include wildcards.
+ *
+ * Match is performed based on the rules set forth in RFC6125 section 6.4.3.
+ * http://tools.ietf.org/html/rfc6125#section-6.4.3
+ *
+ * \param match_pattern RFC6125 compliant pattern
+ * \param hostname to match against
+ * \returns AMQP_HCR_MATCH if its a match, AMQP_HCR_NO_MATCH otherwise.
+ */
+amqp_hostcheck_result amqp_hostcheck(const char *match_pattern,
+                                     const char *hostname);
+
+#endif

+ 242 - 0
ext/librabbitmq/librabbitmq/amqp_mem.c

@@ -0,0 +1,242 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp_private.h"
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+char const *amqp_version(void) { return AMQP_VERSION_STRING; }
+
+uint32_t amqp_version_number(void) { return AMQP_VERSION; }
+
+void init_amqp_pool(amqp_pool_t *pool, size_t pagesize) {
+  pool->pagesize = pagesize ? pagesize : 4096;
+
+  pool->pages.num_blocks = 0;
+  pool->pages.blocklist = NULL;
+
+  pool->large_blocks.num_blocks = 0;
+  pool->large_blocks.blocklist = NULL;
+
+  pool->next_page = 0;
+  pool->alloc_block = NULL;
+  pool->alloc_used = 0;
+}
+
+static void empty_blocklist(amqp_pool_blocklist_t *x) {
+  int i;
+
+  if (x->blocklist != NULL) {
+    for (i = 0; i < x->num_blocks; i++) {
+      free(x->blocklist[i]);
+    }
+    free(x->blocklist);
+  }
+  x->num_blocks = 0;
+  x->blocklist = NULL;
+}
+
+void recycle_amqp_pool(amqp_pool_t *pool) {
+  empty_blocklist(&pool->large_blocks);
+  pool->next_page = 0;
+  pool->alloc_block = NULL;
+  pool->alloc_used = 0;
+}
+
+void empty_amqp_pool(amqp_pool_t *pool) {
+  recycle_amqp_pool(pool);
+  empty_blocklist(&pool->pages);
+}
+
+/* Returns 1 on success, 0 on failure */
+static int record_pool_block(amqp_pool_blocklist_t *x, void *block) {
+  size_t blocklistlength = sizeof(void *) * (x->num_blocks + 1);
+
+  if (x->blocklist == NULL) {
+    x->blocklist = malloc(blocklistlength);
+    if (x->blocklist == NULL) {
+      return 0;
+    }
+  } else {
+    void *newbl = realloc(x->blocklist, blocklistlength);
+    if (newbl == NULL) {
+      return 0;
+    }
+    x->blocklist = newbl;
+  }
+
+  x->blocklist[x->num_blocks] = block;
+  x->num_blocks++;
+  return 1;
+}
+
+void *amqp_pool_alloc(amqp_pool_t *pool, size_t amount) {
+  if (amount == 0) {
+    return NULL;
+  }
+
+  amount = (amount + 7) & (~7); /* round up to nearest 8-byte boundary */
+
+  if (amount > pool->pagesize) {
+    void *result = calloc(1, amount);
+    if (result == NULL) {
+      return NULL;
+    }
+    if (!record_pool_block(&pool->large_blocks, result)) {
+      free(result);
+      return NULL;
+    }
+    return result;
+  }
+
+  if (pool->alloc_block != NULL) {
+    assert(pool->alloc_used <= pool->pagesize);
+
+    if (pool->alloc_used + amount <= pool->pagesize) {
+      void *result = pool->alloc_block + pool->alloc_used;
+      pool->alloc_used += amount;
+      return result;
+    }
+  }
+
+  if (pool->next_page >= pool->pages.num_blocks) {
+    pool->alloc_block = calloc(1, pool->pagesize);
+    if (pool->alloc_block == NULL) {
+      return NULL;
+    }
+    if (!record_pool_block(&pool->pages, pool->alloc_block)) {
+      return NULL;
+    }
+    pool->next_page = pool->pages.num_blocks;
+  } else {
+    pool->alloc_block = pool->pages.blocklist[pool->next_page];
+    pool->next_page++;
+  }
+
+  pool->alloc_used = amount;
+
+  return pool->alloc_block;
+}
+
+void amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount,
+                           amqp_bytes_t *output) {
+  output->len = amount;
+  output->bytes = amqp_pool_alloc(pool, amount);
+}
+
+amqp_bytes_t amqp_cstring_bytes(char const *cstr) {
+  amqp_bytes_t result;
+  result.len = strlen(cstr);
+  result.bytes = (void *)cstr;
+  return result;
+}
+
+amqp_bytes_t amqp_bytes_malloc_dup(amqp_bytes_t src) {
+  amqp_bytes_t result;
+  result.len = src.len;
+  result.bytes = malloc(src.len);
+  if (result.bytes != NULL) {
+    memcpy(result.bytes, src.bytes, src.len);
+  }
+  return result;
+}
+
+amqp_bytes_t amqp_bytes_malloc(size_t amount) {
+  amqp_bytes_t result;
+  result.len = amount;
+  result.bytes = malloc(amount); /* will return NULL if it fails */
+  return result;
+}
+
+void amqp_bytes_free(amqp_bytes_t bytes) { free(bytes.bytes); }
+
+amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t state,
+                                             amqp_channel_t channel) {
+  amqp_pool_table_entry_t *entry;
+  size_t index = channel % POOL_TABLE_SIZE;
+
+  entry = state->pool_table[index];
+
+  for (; NULL != entry; entry = entry->next) {
+    if (channel == entry->channel) {
+      return &entry->pool;
+    }
+  }
+
+  entry = malloc(sizeof(amqp_pool_table_entry_t));
+  if (NULL == entry) {
+    return NULL;
+  }
+
+  entry->channel = channel;
+  entry->next = state->pool_table[index];
+  state->pool_table[index] = entry;
+
+  init_amqp_pool(&entry->pool, state->frame_max);
+
+  return &entry->pool;
+}
+
+amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state,
+                                   amqp_channel_t channel) {
+  amqp_pool_table_entry_t *entry;
+  size_t index = channel % POOL_TABLE_SIZE;
+
+  entry = state->pool_table[index];
+
+  for (; NULL != entry; entry = entry->next) {
+    if (channel == entry->channel) {
+      return &entry->pool;
+    }
+  }
+
+  return NULL;
+}
+
+int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l) {
+  if (r.len == l.len &&
+      (r.bytes == l.bytes || 0 == memcmp(r.bytes, l.bytes, r.len))) {
+    return 1;
+  }
+  return 0;
+}

+ 704 - 0
ext/librabbitmq/librabbitmq/amqp_openssl.c

@@ -0,0 +1,704 @@
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2014 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Portions created by Michael Steinert are Copyright (c) 2012-2014 Michael
+ * Steinert. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp_openssl_bio.h"
+#include "amqp_openssl_hostname_validation.h"
+#include "amqp_private.h"
+#include "amqp_socket.h"
+#include "amqp_ssl_socket.h"
+#include "amqp_time.h"
+#include "threads.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <openssl/bio.h>
+#include <openssl/conf.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int initialize_ssl_and_increment_connections(void);
+static int decrement_ssl_connections(void);
+
+static unsigned long ssl_threadid_callback(void);
+static void ssl_locking_callback(int mode, int n, const char *file, int line);
+static pthread_mutex_t *amqp_openssl_lockarray = NULL;
+
+static pthread_mutex_t openssl_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static amqp_boolean_t do_initialize_openssl = 1;
+static amqp_boolean_t openssl_initialized = 0;
+static amqp_boolean_t openssl_bio_initialized = 0;
+static int openssl_connections = 0;
+
+#define CHECK_SUCCESS(condition)                                            \
+  do {                                                                      \
+    int check_success_ret = (condition);                                    \
+    if (check_success_ret) {                                                \
+      amqp_abort("Check %s failed <%d>: %s", #condition, check_success_ret, \
+                 strerror(check_success_ret));                              \
+    }                                                                       \
+  } while (0)
+
+struct amqp_ssl_socket_t {
+  const struct amqp_socket_class_t *klass;
+  SSL_CTX *ctx;
+  int sockfd;
+  SSL *ssl;
+  amqp_boolean_t verify_peer;
+  amqp_boolean_t verify_hostname;
+  int internal_error;
+};
+
+static ssize_t amqp_ssl_socket_send(void *base, const void *buf, size_t len,
+                                    AMQP_UNUSED int flags) {
+  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
+  int res;
+  if (-1 == self->sockfd) {
+    return AMQP_STATUS_SOCKET_CLOSED;
+  }
+
+  /* SSL_write takes an int for length of buffer, protect against len being
+   * larger than larger than what SSL_write can take */
+  if (len > INT_MAX) {
+    return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  ERR_clear_error();
+  self->internal_error = 0;
+
+  /* This will only return on error, or once the whole buffer has been
+   * written to the SSL stream. See SSL_MODE_ENABLE_PARTIAL_WRITE */
+  res = SSL_write(self->ssl, buf, (int)len);
+  if (0 >= res) {
+    self->internal_error = SSL_get_error(self->ssl, res);
+    /* TODO: Close connection if it isn't already? */
+    /* TODO: Possibly be more intelligent in reporting WHAT went wrong */
+    switch (self->internal_error) {
+      case SSL_ERROR_WANT_READ:
+        res = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
+        break;
+      case SSL_ERROR_WANT_WRITE:
+        res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
+        break;
+      case SSL_ERROR_ZERO_RETURN:
+        res = AMQP_STATUS_CONNECTION_CLOSED;
+        break;
+      default:
+        res = AMQP_STATUS_SSL_ERROR;
+        break;
+    }
+  } else {
+    self->internal_error = 0;
+  }
+
+  return (ssize_t)res;
+}
+
+static ssize_t amqp_ssl_socket_recv(void *base, void *buf, size_t len,
+                                    AMQP_UNUSED int flags) {
+  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
+  int received;
+  if (-1 == self->sockfd) {
+    return AMQP_STATUS_SOCKET_CLOSED;
+  }
+
+  /* SSL_read takes an int for length of buffer, protect against len being
+   * larger than larger than what SSL_read can take */
+  if (len > INT_MAX) {
+    return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  ERR_clear_error();
+  self->internal_error = 0;
+
+  received = SSL_read(self->ssl, buf, (int)len);
+  if (0 >= received) {
+    self->internal_error = SSL_get_error(self->ssl, received);
+    switch (self->internal_error) {
+      case SSL_ERROR_WANT_READ:
+        received = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
+        break;
+      case SSL_ERROR_WANT_WRITE:
+        received = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
+        break;
+      case SSL_ERROR_ZERO_RETURN:
+        received = AMQP_STATUS_CONNECTION_CLOSED;
+        break;
+      default:
+        received = AMQP_STATUS_SSL_ERROR;
+        break;
+    }
+  }
+
+  return (ssize_t)received;
+}
+
+static int amqp_ssl_socket_open(void *base, const char *host, int port,
+                                struct timeval *timeout) {
+  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
+  long result;
+  int status;
+  amqp_time_t deadline;
+  X509 *cert;
+  BIO *bio;
+  if (-1 != self->sockfd) {
+    return AMQP_STATUS_SOCKET_INUSE;
+  }
+  ERR_clear_error();
+
+  self->ssl = SSL_new(self->ctx);
+  if (!self->ssl) {
+    self->internal_error = ERR_peek_error();
+    status = AMQP_STATUS_SSL_ERROR;
+    goto exit;
+  }
+
+  status = amqp_time_from_now(&deadline, timeout);
+  if (AMQP_STATUS_OK != status) {
+    return status;
+  }
+
+  self->sockfd = amqp_open_socket_inner(host, port, deadline);
+  if (0 > self->sockfd) {
+    status = self->sockfd;
+    self->internal_error = amqp_os_socket_error();
+    self->sockfd = -1;
+    goto error_out1;
+  }
+
+  bio = BIO_new(amqp_openssl_bio());
+  if (!bio) {
+    status = AMQP_STATUS_NO_MEMORY;
+    goto error_out2;
+  }
+
+  BIO_set_fd(bio, self->sockfd, BIO_NOCLOSE);
+  SSL_set_bio(self->ssl, bio, bio);
+
+  status = SSL_set_tlsext_host_name(self->ssl, host);
+  if (!status) {
+    self->internal_error = SSL_get_error(self->ssl, status);
+    status = AMQP_STATUS_SSL_ERROR;
+    goto error_out2;
+  }
+
+start_connect:
+  status = SSL_connect(self->ssl);
+  if (status != 1) {
+    self->internal_error = SSL_get_error(self->ssl, status);
+    switch (self->internal_error) {
+      case SSL_ERROR_WANT_READ:
+        status = amqp_poll(self->sockfd, AMQP_SF_POLLIN, deadline);
+        break;
+      case SSL_ERROR_WANT_WRITE:
+        status = amqp_poll(self->sockfd, AMQP_SF_POLLOUT, deadline);
+        break;
+      default:
+        status = AMQP_STATUS_SSL_CONNECTION_FAILED;
+    }
+    if (AMQP_STATUS_OK == status) {
+      goto start_connect;
+    }
+    goto error_out2;
+  }
+
+  cert = SSL_get_peer_certificate(self->ssl);
+
+  if (self->verify_peer) {
+    if (!cert) {
+      self->internal_error = 0;
+      status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
+      goto error_out3;
+    }
+
+    result = SSL_get_verify_result(self->ssl);
+    if (X509_V_OK != result) {
+      self->internal_error = result;
+      status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
+      goto error_out4;
+    }
+  }
+  if (self->verify_hostname) {
+    if (!cert) {
+      self->internal_error = 0;
+      status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
+      goto error_out3;
+    }
+
+    if (AMQP_HVR_MATCH_FOUND != amqp_ssl_validate_hostname(host, cert)) {
+      self->internal_error = 0;
+      status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
+      goto error_out4;
+    }
+  }
+
+  X509_free(cert);
+  self->internal_error = 0;
+  status = AMQP_STATUS_OK;
+
+exit:
+  return status;
+
+error_out4:
+  X509_free(cert);
+error_out3:
+  SSL_shutdown(self->ssl);
+error_out2:
+  amqp_os_socket_close(self->sockfd);
+  self->sockfd = -1;
+error_out1:
+  SSL_free(self->ssl);
+  self->ssl = NULL;
+  goto exit;
+}
+
+static int amqp_ssl_socket_close(void *base, amqp_socket_close_enum force) {
+  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
+
+  if (-1 == self->sockfd) {
+    return AMQP_STATUS_SOCKET_CLOSED;
+  }
+
+  if (AMQP_SC_NONE == force) {
+    /* don't try too hard to shutdown the connection */
+    SSL_shutdown(self->ssl);
+  }
+
+  SSL_free(self->ssl);
+  self->ssl = NULL;
+
+  if (amqp_os_socket_close(self->sockfd)) {
+    return AMQP_STATUS_SOCKET_ERROR;
+  }
+  self->sockfd = -1;
+
+  return AMQP_STATUS_OK;
+}
+
+static int amqp_ssl_socket_get_sockfd(void *base) {
+  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
+  return self->sockfd;
+}
+
+static void amqp_ssl_socket_delete(void *base) {
+  struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
+
+  if (self) {
+    amqp_ssl_socket_close(self, AMQP_SC_NONE);
+
+    SSL_CTX_free(self->ctx);
+    free(self);
+  }
+  decrement_ssl_connections();
+}
+
+static const struct amqp_socket_class_t amqp_ssl_socket_class = {
+    amqp_ssl_socket_send,       /* send */
+    amqp_ssl_socket_recv,       /* recv */
+    amqp_ssl_socket_open,       /* open */
+    amqp_ssl_socket_close,      /* close */
+    amqp_ssl_socket_get_sockfd, /* get_sockfd */
+    amqp_ssl_socket_delete      /* delete */
+};
+
+amqp_socket_t *amqp_ssl_socket_new(amqp_connection_state_t state) {
+  struct amqp_ssl_socket_t *self = calloc(1, sizeof(*self));
+  int status;
+  if (!self) {
+    return NULL;
+  }
+
+  self->sockfd = -1;
+  self->klass = &amqp_ssl_socket_class;
+  self->verify_peer = 1;
+  self->verify_hostname = 1;
+
+  status = initialize_ssl_and_increment_connections();
+  if (status) {
+    goto error;
+  }
+
+  self->ctx = SSL_CTX_new(SSLv23_client_method());
+  if (!self->ctx) {
+    goto error;
+  }
+  /* Disable SSLv2 and SSLv3 */
+  SSL_CTX_set_options(self->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+  amqp_set_socket(state, (amqp_socket_t *)self);
+
+  return (amqp_socket_t *)self;
+error:
+  amqp_ssl_socket_delete((amqp_socket_t *)self);
+  return NULL;
+}
+
+int amqp_ssl_socket_set_cacert(amqp_socket_t *base, const char *cacert) {
+  int status;
+  struct amqp_ssl_socket_t *self;
+  if (base->klass != &amqp_ssl_socket_class) {
+    amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
+  }
+  self = (struct amqp_ssl_socket_t *)base;
+  status = SSL_CTX_load_verify_locations(self->ctx, cacert, NULL);
+  if (1 != status) {
+    return AMQP_STATUS_SSL_ERROR;
+  }
+  return AMQP_STATUS_OK;
+}
+
+int amqp_ssl_socket_set_key(amqp_socket_t *base, const char *cert,
+                            const char *key) {
+  int status;
+  struct amqp_ssl_socket_t *self;
+  if (base->klass != &amqp_ssl_socket_class) {
+    amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
+  }
+  self = (struct amqp_ssl_socket_t *)base;
+  status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
+  if (1 != status) {
+    return AMQP_STATUS_SSL_ERROR;
+  }
+  status = SSL_CTX_use_PrivateKey_file(self->ctx, key, SSL_FILETYPE_PEM);
+  if (1 != status) {
+    return AMQP_STATUS_SSL_ERROR;
+  }
+  return AMQP_STATUS_OK;
+}
+
+static int password_cb(AMQP_UNUSED char *buffer, AMQP_UNUSED int length,
+                       AMQP_UNUSED int rwflag, AMQP_UNUSED void *user_data) {
+  amqp_abort("rabbitmq-c does not support password protected keys");
+}
+
+int amqp_ssl_socket_set_key_buffer(amqp_socket_t *base, const char *cert,
+                                   const void *key, size_t n) {
+  int status = AMQP_STATUS_OK;
+  BIO *buf = NULL;
+  RSA *rsa = NULL;
+  struct amqp_ssl_socket_t *self;
+  if (base->klass != &amqp_ssl_socket_class) {
+    amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
+  }
+  if (n > INT_MAX) {
+    return AMQP_STATUS_INVALID_PARAMETER;
+  }
+  self = (struct amqp_ssl_socket_t *)base;
+  status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
+  if (1 != status) {
+    return AMQP_STATUS_SSL_ERROR;
+  }
+  buf = BIO_new_mem_buf((void *)key, (int)n);
+  if (!buf) {
+    goto error;
+  }
+  rsa = PEM_read_bio_RSAPrivateKey(buf, NULL, password_cb, NULL);
+  if (!rsa) {
+    goto error;
+  }
+  status = SSL_CTX_use_RSAPrivateKey(self->ctx, rsa);
+  if (1 != status) {
+    goto error;
+  }
+exit:
+  BIO_vfree(buf);
+  RSA_free(rsa);
+  return status;
+error:
+  status = AMQP_STATUS_SSL_ERROR;
+  goto exit;
+}
+
+int amqp_ssl_socket_set_cert(amqp_socket_t *base, const char *cert) {
+  int status;
+  struct amqp_ssl_socket_t *self;
+  if (base->klass != &amqp_ssl_socket_class) {
+    amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
+  }
+  self = (struct amqp_ssl_socket_t *)base;
+  status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
+  if (1 != status) {
+    return AMQP_STATUS_SSL_ERROR;
+  }
+  return AMQP_STATUS_OK;
+}
+
+void amqp_ssl_socket_set_verify(amqp_socket_t *base, amqp_boolean_t verify) {
+  amqp_ssl_socket_set_verify_peer(base, verify);
+  amqp_ssl_socket_set_verify_hostname(base, verify);
+}
+
+void amqp_ssl_socket_set_verify_peer(amqp_socket_t *base,
+                                     amqp_boolean_t verify) {
+  struct amqp_ssl_socket_t *self;
+  if (base->klass != &amqp_ssl_socket_class) {
+    amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
+  }
+  self = (struct amqp_ssl_socket_t *)base;
+  self->verify_peer = verify;
+}
+
+void amqp_ssl_socket_set_verify_hostname(amqp_socket_t *base,
+                                         amqp_boolean_t verify) {
+  struct amqp_ssl_socket_t *self;
+  if (base->klass != &amqp_ssl_socket_class) {
+    amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
+  }
+  self = (struct amqp_ssl_socket_t *)base;
+  self->verify_hostname = verify;
+}
+
+int amqp_ssl_socket_set_ssl_versions(amqp_socket_t *base,
+                                     amqp_tls_version_t min,
+                                     amqp_tls_version_t max) {
+  struct amqp_ssl_socket_t *self;
+  if (base->klass != &amqp_ssl_socket_class) {
+    amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
+  }
+  self = (struct amqp_ssl_socket_t *)base;
+
+  {
+    long clear_options;
+    long set_options = 0;
+#if defined(SSL_OP_NO_TLSv1_2)
+    amqp_tls_version_t max_supported = AMQP_TLSv1_2;
+    clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
+#elif defined(SSL_OP_NO_TLSv1_1)
+    amqp_tls_version_t max_supported = AMQP_TLSv1_1;
+    clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
+#elif defined(SSL_OP_NO_TLSv1)
+    amqp_tls_version_t max_supported = AMQP_TLSv1;
+    clear_options = SSL_OP_NO_TLSv1;
+#else
+#error "Need a version of OpenSSL that can support TLSv1 or greater."
+#endif
+
+    if (AMQP_TLSvLATEST == max) {
+      max = max_supported;
+    }
+    if (AMQP_TLSvLATEST == min) {
+      min = max_supported;
+    }
+
+    if (min > max) {
+      return AMQP_STATUS_INVALID_PARAMETER;
+    }
+
+    if (max > max_supported || min > max_supported) {
+      return AMQP_STATUS_UNSUPPORTED;
+    }
+
+    if (min > AMQP_TLSv1) {
+      set_options |= SSL_OP_NO_TLSv1;
+    }
+#ifdef SSL_OP_NO_TLSv1_1
+    if (min > AMQP_TLSv1_1 || max < AMQP_TLSv1_1) {
+      set_options |= SSL_OP_NO_TLSv1_1;
+    }
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+    if (max < AMQP_TLSv1_2) {
+      set_options |= SSL_OP_NO_TLSv1_2;
+    }
+#endif
+    SSL_CTX_clear_options(self->ctx, clear_options);
+    SSL_CTX_set_options(self->ctx, set_options);
+  }
+
+  return AMQP_STATUS_OK;
+}
+
+void amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize) {
+  CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+  if (openssl_connections == 0 && !openssl_initialized) {
+    do_initialize_openssl = do_initialize;
+  }
+  CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+}
+
+static unsigned long ssl_threadid_callback(void) {
+  return (unsigned long)pthread_self();
+}
+
+static void ssl_locking_callback(int mode, int n, AMQP_UNUSED const char *file,
+                                 AMQP_UNUSED int line) {
+  if (mode & CRYPTO_LOCK) {
+    CHECK_SUCCESS(pthread_mutex_lock(&amqp_openssl_lockarray[n]));
+  } else {
+    CHECK_SUCCESS(pthread_mutex_unlock(&amqp_openssl_lockarray[n]));
+  }
+}
+
+static int setup_openssl(void) {
+  int status;
+
+  int i;
+  amqp_openssl_lockarray = calloc(CRYPTO_num_locks(), sizeof(pthread_mutex_t));
+  if (!amqp_openssl_lockarray) {
+    status = AMQP_STATUS_NO_MEMORY;
+    goto out;
+  }
+  for (i = 0; i < CRYPTO_num_locks(); i++) {
+    if (pthread_mutex_init(&amqp_openssl_lockarray[i], NULL)) {
+      int j;
+      for (j = 0; j < i; j++) {
+        pthread_mutex_destroy(&amqp_openssl_lockarray[j]);
+      }
+      free(amqp_openssl_lockarray);
+      status = AMQP_STATUS_SSL_ERROR;
+      goto out;
+    }
+  }
+  CRYPTO_set_id_callback(ssl_threadid_callback);
+  CRYPTO_set_locking_callback(ssl_locking_callback);
+
+#ifdef AMQP_OPENSSL_V110
+  if (CONF_modules_load_file(NULL, "rabbitmq-c", CONF_MFLAGS_DEFAULT_SECTION) <=
+      0) {
+    status = AMQP_STATUS_SSL_ERROR;
+    goto out;
+  }
+#else
+  OPENSSL_config(NULL);
+#endif
+  SSL_library_init();
+  SSL_load_error_strings();
+
+  status = AMQP_STATUS_OK;
+out:
+  return status;
+}
+
+int amqp_initialize_ssl_library(void) {
+  int status;
+  CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+  if (!openssl_initialized) {
+    status = setup_openssl();
+    if (status) {
+      goto out;
+    }
+    openssl_initialized = 1;
+  }
+
+  status = AMQP_STATUS_OK;
+out:
+  CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+  return status;
+}
+
+static int initialize_ssl_and_increment_connections() {
+  int status;
+  CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+  if (do_initialize_openssl && !openssl_initialized) {
+    status = setup_openssl();
+    if (status) {
+      goto exit;
+    }
+    openssl_initialized = 1;
+  }
+
+  if (!openssl_bio_initialized) {
+    status = amqp_openssl_bio_init();
+    if (status) {
+      goto exit;
+    }
+    openssl_bio_initialized = 1;
+  }
+
+  openssl_connections += 1;
+  status = AMQP_STATUS_OK;
+exit:
+  CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+  return status;
+}
+
+static int decrement_ssl_connections(void) {
+  CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+  if (openssl_connections > 0) {
+    openssl_connections--;
+  }
+
+  CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+  return AMQP_STATUS_OK;
+}
+
+int amqp_uninitialize_ssl_library(void) {
+  int status;
+  CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
+
+  if (openssl_connections > 0) {
+    status = AMQP_STATUS_SOCKET_INUSE;
+    goto out;
+  }
+
+  amqp_openssl_bio_destroy();
+  openssl_bio_initialized = 0;
+
+#ifndef AMQP_OPENSSL_V110
+  ERR_remove_state(0);
+#endif
+
+#ifndef LIBRESSL_VERSION_NUMBER
+  FIPS_mode_set(0);
+#endif
+
+  CRYPTO_set_locking_callback(NULL);
+  CRYPTO_set_id_callback(NULL);
+  {
+    int i;
+    for (i = 0; i < CRYPTO_num_locks(); i++) {
+      pthread_mutex_destroy(&amqp_openssl_lockarray[i]);
+    }
+    free(amqp_openssl_lockarray);
+  }
+
+  ENGINE_cleanup();
+  CONF_modules_free();
+  EVP_cleanup();
+  CRYPTO_cleanup_all_ex_data();
+  ERR_free_strings();
+#if (OPENSSL_VERSION_NUMBER >= 0x10002003L) && !defined(LIBRESSL_VERSION_NUMBER)
+  SSL_COMP_free_compression_methods();
+#endif
+
+  openssl_initialized = 0;
+
+  status = AMQP_STATUS_OK;
+out:
+  CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
+  return status;
+}

+ 193 - 0
ext/librabbitmq/librabbitmq/amqp_openssl_bio.c

@@ -0,0 +1,193 @@
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2017 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "amqp_openssl_bio.h"
+#include "amqp_socket.h"
+
+#include <assert.h>
+#include <errno.h>
+#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <sys/types.h>
+#endif
+
+#ifdef MSG_NOSIGNAL
+#define AMQP_USE_AMQP_BIO
+#endif
+
+static int amqp_ssl_bio_initialized = 0;
+
+#ifdef AMQP_USE_AMQP_BIO
+
+static BIO_METHOD *amqp_bio_method;
+
+static int amqp_openssl_bio_should_retry(int res) {
+  if (res == -1) {
+    int err = amqp_os_socket_error();
+    if (
+#ifdef EWOULDBLOCK
+        err == EWOULDBLOCK ||
+#endif
+#ifdef WSAEWOULDBLOCK
+        err == WSAEWOULDBLOCK ||
+#endif
+#ifdef ENOTCONN
+        err == ENOTCONN ||
+#endif
+#ifdef EINTR
+        err == EINTR ||
+#endif
+#ifdef EAGAIN
+        err == EAGAIN ||
+#endif
+#ifdef EPROTO
+        err == EPROTO ||
+#endif
+#ifdef EINPROGRESS
+        err == EINPROGRESS ||
+#endif
+#ifdef EALREADY
+        err == EALREADY ||
+#endif
+        0) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static int amqp_openssl_bio_write(BIO *b, const char *in, int inl) {
+  int flags = 0;
+  int fd;
+  int res;
+
+#ifdef MSG_NOSIGNAL
+  flags |= MSG_NOSIGNAL;
+#endif
+
+  BIO_get_fd(b, &fd);
+  res = send(fd, in, inl, flags);
+
+  BIO_clear_retry_flags(b);
+  if (res <= 0 && amqp_openssl_bio_should_retry(res)) {
+    BIO_set_retry_write(b);
+  }
+
+  return res;
+}
+
+static int amqp_openssl_bio_read(BIO *b, char *out, int outl) {
+  int flags = 0;
+  int fd;
+  int res;
+
+#ifdef MSG_NOSIGNAL
+  flags |= MSG_NOSIGNAL;
+#endif
+
+  BIO_get_fd(b, &fd);
+  res = recv(fd, out, outl, flags);
+
+  BIO_clear_retry_flags(b);
+  if (res <= 0 && amqp_openssl_bio_should_retry(res)) {
+    BIO_set_retry_read(b);
+  }
+
+  return res;
+}
+
+#ifndef AMQP_OPENSSL_V110
+static int BIO_meth_set_write(BIO_METHOD *biom,
+                              int (*wfn)(BIO *, const char *, int)) {
+  biom->bwrite = wfn;
+  return 0;
+}
+
+static int BIO_meth_set_read(BIO_METHOD *biom, int (*rfn)(BIO *, char *, int)) {
+  biom->bread = rfn;
+  return 0;
+}
+#endif /* AQP_OPENSSL_V110 */
+#endif /* AMQP_USE_AMQP_BIO */
+
+int amqp_openssl_bio_init(void) {
+  assert(!amqp_ssl_bio_initialized);
+#ifdef AMQP_USE_AMQP_BIO
+#ifdef AMQP_OPENSSL_V110
+  if (!(amqp_bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "amqp_bio_method"))) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  // casting away const is necessary until
+  // https://github.com/openssl/openssl/pull/2181/, which is targeted for
+  // openssl 1.1.1
+  BIO_METHOD *meth = (BIO_METHOD *)BIO_s_socket();
+  BIO_meth_set_create(amqp_bio_method, BIO_meth_get_create(meth));
+  BIO_meth_set_destroy(amqp_bio_method, BIO_meth_get_destroy(meth));
+  BIO_meth_set_ctrl(amqp_bio_method, BIO_meth_get_ctrl(meth));
+  BIO_meth_set_callback_ctrl(amqp_bio_method, BIO_meth_get_callback_ctrl(meth));
+  BIO_meth_set_read(amqp_bio_method, BIO_meth_get_read(meth));
+  BIO_meth_set_write(amqp_bio_method, BIO_meth_get_write(meth));
+  BIO_meth_set_gets(amqp_bio_method, BIO_meth_get_gets(meth));
+  BIO_meth_set_puts(amqp_bio_method, BIO_meth_get_puts(meth));
+#else
+  if (!(amqp_bio_method = OPENSSL_malloc(sizeof(BIO_METHOD)))) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  memcpy(amqp_bio_method, BIO_s_socket(), sizeof(BIO_METHOD));
+#endif
+  BIO_meth_set_write(amqp_bio_method, amqp_openssl_bio_write);
+  BIO_meth_set_read(amqp_bio_method, amqp_openssl_bio_read);
+#endif
+
+  amqp_ssl_bio_initialized = 1;
+  return AMQP_STATUS_OK;
+}
+
+void amqp_openssl_bio_destroy(void) {
+  assert(amqp_ssl_bio_initialized);
+#ifdef AMQP_USE_AMQP_BIO
+#ifdef AMQP_OPENSSL_V110
+  BIO_meth_free(amqp_bio_method);
+#else
+  OPENSSL_free(amqp_bio_method);
+#endif
+  amqp_bio_method = NULL;
+#endif
+  amqp_ssl_bio_initialized = 0;
+}
+
+BIO_METHOD_PTR amqp_openssl_bio(void) {
+  assert(amqp_ssl_bio_initialized);
+#ifdef AMQP_USE_AMQP_BIO
+  return amqp_bio_method;
+#else
+  return BIO_s_socket();
+#endif
+}

+ 44 - 0
ext/librabbitmq/librabbitmq/amqp_openssl_bio.h

@@ -0,0 +1,44 @@
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2017 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef AMQP_OPENSSL_BIO
+#define AMQP_OPENSSL_BIO
+
+#include <openssl/bio.h>
+
+int amqp_openssl_bio_init(void);
+
+void amqp_openssl_bio_destroy(void);
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+#define AMQP_OPENSSL_V110
+#endif
+
+#ifdef AMQP_OPENSSL_V110
+typedef const BIO_METHOD *BIO_METHOD_PTR;
+#else
+typedef BIO_METHOD *BIO_METHOD_PTR;
+#endif
+
+BIO_METHOD_PTR amqp_openssl_bio(void);
+
+#endif /* ifndef AMQP_OPENSSL_BIO */

+ 179 - 0
ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.c

@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012, iSEC Partners.
+ * Copyright (C) 2015 Alan Antonuk.
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization of the
+ * copyright holder.
+ */
+
+/* Originally from:
+ * https://github.com/iSECPartners/ssl-conservatory
+ * https://wiki.openssl.org/index.php/Hostname_validation
+ */
+
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+#include "amqp_hostcheck.h"
+#include "amqp_openssl_bio.h"
+#include "amqp_openssl_hostname_validation.h"
+
+#include <string.h>
+
+#define HOSTNAME_MAX_SIZE 255
+
+/**
+ * Tries to find a match for hostname in the certificate's Common Name field.
+ *
+ * Returns AMQP_HVR_MATCH_FOUND if a match was found.
+ * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
+ * Returns AMQP_HVR_MALFORMED_CERTIFICATE if the Common Name had a NUL character
+ * embedded in it.
+ * Returns AMQP_HVR_ERROR if the Common Name could not be extracted.
+ */
+static amqp_hostname_validation_result amqp_matches_common_name(
+    const char *hostname, const X509 *server_cert) {
+  int common_name_loc = -1;
+  X509_NAME_ENTRY *common_name_entry = NULL;
+  ASN1_STRING *common_name_asn1 = NULL;
+  const char *common_name_str = NULL;
+
+  // Find the position of the CN field in the Subject field of the certificate
+  common_name_loc = X509_NAME_get_index_by_NID(
+      X509_get_subject_name((X509 *)server_cert), NID_commonName, -1);
+  if (common_name_loc < 0) {
+    return AMQP_HVR_ERROR;
+  }
+
+  // Extract the CN field
+  common_name_entry = X509_NAME_get_entry(
+      X509_get_subject_name((X509 *)server_cert), common_name_loc);
+  if (common_name_entry == NULL) {
+    return AMQP_HVR_ERROR;
+  }
+
+  // Convert the CN field to a C string
+  common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
+  if (common_name_asn1 == NULL) {
+    return AMQP_HVR_ERROR;
+  }
+
+#ifdef AMQP_OPENSSL_V110
+  common_name_str = (const char *)ASN1_STRING_get0_data(common_name_asn1);
+#else
+  common_name_str = (char *)ASN1_STRING_data(common_name_asn1);
+#endif
+
+  // Make sure there isn't an embedded NUL character in the CN
+  if ((size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) {
+    return AMQP_HVR_MALFORMED_CERTIFICATE;
+  }
+
+  // Compare expected hostname with the CN
+  if (amqp_hostcheck(common_name_str, hostname) == AMQP_HCR_MATCH) {
+    return AMQP_HVR_MATCH_FOUND;
+  } else {
+    return AMQP_HVR_MATCH_NOT_FOUND;
+  }
+}
+
+/**
+ * Tries to find a match for hostname in the certificate's Subject Alternative
+ * Name extension.
+ *
+ * Returns AMQP_HVR_MATCH_FOUND if a match was found.
+ * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
+ * Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL
+ * character embedded in it.
+ * Returns AMQP_HVR_NO_SAN_PRESENT if the SAN extension was not present in the
+ * certificate.
+ */
+static amqp_hostname_validation_result amqp_matches_subject_alternative_name(
+    const char *hostname, const X509 *server_cert) {
+  amqp_hostname_validation_result result = AMQP_HVR_MATCH_NOT_FOUND;
+  int i;
+  int san_names_nb = -1;
+  STACK_OF(GENERAL_NAME) *san_names = NULL;
+
+  // Try to extract the names within the SAN extension from the certificate
+  san_names =
+      X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL);
+  if (san_names == NULL) {
+    return AMQP_HVR_NO_SAN_PRESENT;
+  }
+  san_names_nb = sk_GENERAL_NAME_num(san_names);
+
+  // Check each name within the extension
+  for (i = 0; i < san_names_nb; i++) {
+    const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
+
+    if (current_name->type == GEN_DNS) {
+      // Current name is a DNS name, let's check it
+      const char *dns_name = (const char *)
+#ifdef AMQP_OPENSSL_V110
+          ASN1_STRING_get0_data(current_name->d.dNSName);
+#else
+          ASN1_STRING_data(current_name->d.dNSName);
+#endif
+
+      // Make sure there isn't an embedded NUL character in the DNS name
+      if ((size_t)ASN1_STRING_length(current_name->d.dNSName) !=
+          strlen(dns_name)) {
+        result = AMQP_HVR_MALFORMED_CERTIFICATE;
+        break;
+      } else {  // Compare expected hostname with the DNS name
+        if (amqp_hostcheck(dns_name, hostname) == AMQP_HCR_MATCH) {
+          result = AMQP_HVR_MATCH_FOUND;
+          break;
+        }
+      }
+    }
+  }
+  sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
+
+  return result;
+}
+
+/**
+ * Validates the server's identity by looking for the expected hostname in the
+ * server's certificate. As described in RFC 6125, it first tries to find a
+ * match in the Subject Alternative Name extension. If the extension is not
+ * present in the certificate, it checks the Common Name instead.
+ *
+ * Returns AMQP_HVR_MATCH_FOUND if a match was found.
+ * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
+ * Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL
+ * character embedded in it.
+ * Returns AMQP_HVR_ERROR if there was an error.
+ */
+amqp_hostname_validation_result amqp_ssl_validate_hostname(
+    const char *hostname, const X509 *server_cert) {
+  amqp_hostname_validation_result result;
+
+  if ((hostname == NULL) || (server_cert == NULL)) return AMQP_HVR_ERROR;
+
+  // First try the Subject Alternative Names extension
+  result = amqp_matches_subject_alternative_name(hostname, server_cert);
+  if (result == AMQP_HVR_NO_SAN_PRESENT) {
+    // Extension was not found: try the Common Name
+    result = amqp_matches_common_name(hostname, server_cert);
+  }
+
+  return result;
+}

+ 58 - 0
ext/librabbitmq/librabbitmq/amqp_openssl_hostname_validation.h

@@ -0,0 +1,58 @@
+#ifndef librabbitmq_amqp_openssl_hostname_validation_h
+#define librabbitmq_amqp_openssl_hostname_validation_h
+
+/*
+ * Copyright (C) 2012, iSEC Partners.
+ * Copyright (C) 2015 Alan Antonuk.
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization of the
+ * copyright holder.
+ */
+
+/* Originally from:
+ * https://github.com/iSECPartners/ssl-conservatory
+ * https://wiki.openssl.org/index.php/Hostname_validation
+ */
+
+#include <openssl/x509v3.h>
+
+typedef enum {
+  AMQP_HVR_MATCH_FOUND,
+  AMQP_HVR_MATCH_NOT_FOUND,
+  AMQP_HVR_NO_SAN_PRESENT,
+  AMQP_HVR_MALFORMED_CERTIFICATE,
+  AMQP_HVR_ERROR
+} amqp_hostname_validation_result;
+
+/**
+* Validates the server's identity by looking for the expected hostname in the
+* server's certificate. As described in RFC 6125, it first tries to find a match
+* in the Subject Alternative Name extension. If the extension is not present in
+* the certificate, it checks the Common Name instead.
+*
+* Returns AMQP_HVR_MATCH_FOUND if a match was found.
+* Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found.
+* Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL
+* character embedded in it.
+* Returns AMQP_HVR_ERROR if there was an error.
+*/
+amqp_hostname_validation_result amqp_ssl_validate_hostname(
+    const char *hostname, const X509 *server_cert);
+
+#endif

+ 374 - 0
ext/librabbitmq/librabbitmq/amqp_private.h

@@ -0,0 +1,374 @@
+#ifndef librabbitmq_amqp_private_h
+#define librabbitmq_amqp_private_h
+
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2014
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define AMQ_COPYRIGHT                                       \
+  "Copyright (c) 2007-2014 VMWare Inc, Tony Garnock-Jones," \
+  " and Alan Antonuk."
+
+#include "amqp.h"
+#include "amqp_framing.h"
+#include <string.h>
+
+#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
+#ifndef WINVER
+/* WINVER 0x0502 is WinXP SP2+, Windows Server 2003 SP1+
+ * See:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa383745(v=vs.85).aspx#macros_for_conditional_declarations
+ */
+#define WINVER 0x0502
+#endif
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#endif
+
+/* GCC attributes */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#define AMQP_NORETURN __attribute__((__noreturn__))
+#define AMQP_UNUSED __attribute__((__unused__))
+#elif defined(_MSC_VER)
+#define AMQP_NORETURN __declspec(noreturn)
+#define AMQP_UNUSED
+#else
+#define AMQP_NORETURN
+#define AMQP_UNUSED
+#endif
+
+#if __GNUC__ >= 4
+#define AMQP_PRIVATE __attribute__((visibility("hidden")))
+#else
+#define AMQP_PRIVATE
+#endif
+
+char *amqp_os_error_string(int err);
+
+#ifdef WITH_SSL
+char *amqp_ssl_error_string(int err);
+#endif
+
+#include "amqp_socket.h"
+#include "amqp_time.h"
+
+/*
+ * Connection states: XXX FIX THIS
+ *
+ * - CONNECTION_STATE_INITIAL: The initial state, when we cannot be
+ *   sure if the next thing we will get is the first AMQP frame, or a
+ *   protocol header from the server.
+ *
+ * - CONNECTION_STATE_IDLE: The normal state between
+ *   frames. Connections may only be reconfigured, and the
+ *   connection's pools recycled, when in this state. Whenever we're
+ *   in this state, the inbound_buffer's bytes pointer must be NULL;
+ *   any other state, and it must point to a block of memory allocated
+ *   from the frame_pool.
+ *
+ * - CONNECTION_STATE_HEADER: Some bytes of an incoming frame have
+ *   been seen, but not a complete frame header's worth.
+ *
+ * - CONNECTION_STATE_BODY: A complete frame header has been seen, but
+ *   the frame is not yet complete. When it is completed, it will be
+ *   returned, and the connection will return to IDLE state.
+ *
+ */
+typedef enum amqp_connection_state_enum_ {
+  CONNECTION_STATE_IDLE = 0,
+  CONNECTION_STATE_INITIAL,
+  CONNECTION_STATE_HEADER,
+  CONNECTION_STATE_BODY
+} amqp_connection_state_enum;
+
+typedef enum amqp_status_private_enum_ {
+  /* 0x00xx -> AMQP_STATUS_*/
+  /* 0x01xx -> AMQP_STATUS_TCP_* */
+  /* 0x02xx -> AMQP_STATUS_SSL_* */
+  AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD = -0x1301,
+  AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE = -0x1302
+} amqp_status_private_enum;
+
+/* 7 bytes up front, then payload, then 1 byte footer */
+#define HEADER_SIZE 7
+#define FOOTER_SIZE 1
+
+#define AMQP_PSEUDOFRAME_PROTOCOL_HEADER 'A'
+
+typedef struct amqp_link_t_ {
+  struct amqp_link_t_ *next;
+  void *data;
+} amqp_link_t;
+
+#define POOL_TABLE_SIZE 16
+
+typedef struct amqp_pool_table_entry_t_ {
+  struct amqp_pool_table_entry_t_ *next;
+  amqp_pool_t pool;
+  amqp_channel_t channel;
+} amqp_pool_table_entry_t;
+
+struct amqp_connection_state_t_ {
+  amqp_pool_table_entry_t *pool_table[POOL_TABLE_SIZE];
+
+  amqp_connection_state_enum state;
+
+  int channel_max;
+  int frame_max;
+
+  /* Heartbeat interval in seconds. If this is <= 0, then heartbeats are not
+   * enabled, and next_recv_heartbeat and next_send_heartbeat are set to
+   * infinite */
+  int heartbeat;
+  amqp_time_t next_recv_heartbeat;
+  amqp_time_t next_send_heartbeat;
+
+  /* buffer for holding frame headers.  Allows us to delay allocating
+   * the raw frame buffer until the type, channel, and size are all known
+   */
+  char header_buffer[HEADER_SIZE + 1];
+  amqp_bytes_t inbound_buffer;
+
+  size_t inbound_offset;
+  size_t target_size;
+
+  amqp_bytes_t outbound_buffer;
+
+  amqp_socket_t *socket;
+
+  amqp_bytes_t sock_inbound_buffer;
+  size_t sock_inbound_offset;
+  size_t sock_inbound_limit;
+
+  amqp_link_t *first_queued_frame;
+  amqp_link_t *last_queued_frame;
+
+  amqp_rpc_reply_t most_recent_api_result;
+
+  amqp_table_t server_properties;
+  amqp_table_t client_properties;
+  amqp_pool_t properties_pool;
+
+  struct timeval *handshake_timeout;
+  struct timeval internal_handshake_timeout;
+  struct timeval *rpc_timeout;
+  struct timeval internal_rpc_timeout;
+};
+
+amqp_pool_t *amqp_get_or_create_channel_pool(amqp_connection_state_t connection,
+                                             amqp_channel_t channel);
+amqp_pool_t *amqp_get_channel_pool(amqp_connection_state_t state,
+                                   amqp_channel_t channel);
+
+static inline int amqp_heartbeat_send(amqp_connection_state_t state) {
+  return state->heartbeat;
+}
+
+static inline int amqp_heartbeat_recv(amqp_connection_state_t state) {
+  return 2 * state->heartbeat;
+}
+
+int amqp_try_recv(amqp_connection_state_t state);
+
+static inline void *amqp_offset(void *data, size_t offset) {
+  return (char *)data + offset;
+}
+
+/* This macro defines the encoding and decoding functions associated with a
+   simple type. */
+
+#define DECLARE_CODEC_BASE_TYPE(bits)                                        \
+                                                                             \
+  static inline int amqp_encode_##bits(amqp_bytes_t encoded, size_t *offset, \
+                                       uint##bits##_t input) {               \
+    size_t o = *offset;                                                      \
+    if ((*offset = o + bits / 8) <= encoded.len) {                           \
+      amqp_e##bits(input, amqp_offset(encoded.bytes, o));                    \
+      return 1;                                                              \
+    }                                                                        \
+    return 0;                                                                \
+  }                                                                          \
+                                                                             \
+  static inline int amqp_decode_##bits(amqp_bytes_t encoded, size_t *offset, \
+                                       uint##bits##_t *output) {             \
+    size_t o = *offset;                                                      \
+    if ((*offset = o + bits / 8) <= encoded.len) {                           \
+      *output = amqp_d##bits(amqp_offset(encoded.bytes, o));                 \
+      return 1;                                                              \
+    }                                                                        \
+    return 0;                                                                \
+  }
+
+static inline int is_bigendian(void) {
+  union {
+    uint32_t i;
+    char c[4];
+  } bint = {0x01020304};
+  return bint.c[0] == 1;
+}
+
+static inline void amqp_e8(uint8_t val, void *data) {
+  memcpy(data, &val, sizeof(val));
+}
+
+static inline uint8_t amqp_d8(void *data) {
+  uint8_t val;
+  memcpy(&val, data, sizeof(val));
+  return val;
+}
+
+static inline void amqp_e16(uint16_t val, void *data) {
+  if (!is_bigendian()) {
+    val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u);
+  }
+  memcpy(data, &val, sizeof(val));
+}
+
+static inline uint16_t amqp_d16(void *data) {
+  uint16_t val;
+  memcpy(&val, data, sizeof(val));
+  if (!is_bigendian()) {
+    val = ((val & 0xFF00u) >> 8u) | ((val & 0x00FFu) << 8u);
+  }
+  return val;
+}
+
+static inline void amqp_e32(uint32_t val, void *data) {
+  if (!is_bigendian()) {
+    val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) |
+          ((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u);
+  }
+  memcpy(data, &val, sizeof(val));
+}
+
+static inline uint32_t amqp_d32(void *data) {
+  uint32_t val;
+  memcpy(&val, data, sizeof(val));
+  if (!is_bigendian()) {
+    val = ((val & 0xFF000000u) >> 24u) | ((val & 0x00FF0000u) >> 8u) |
+          ((val & 0x0000FF00u) << 8u) | ((val & 0x000000FFu) << 24u);
+  }
+  return val;
+}
+
+static inline void amqp_e64(uint64_t val, void *data) {
+  if (!is_bigendian()) {
+    val = ((val & 0xFF00000000000000u) >> 56u) |
+          ((val & 0x00FF000000000000u) >> 40u) |
+          ((val & 0x0000FF0000000000u) >> 24u) |
+          ((val & 0x000000FF00000000u) >> 8u) |
+          ((val & 0x00000000FF000000u) << 8u) |
+          ((val & 0x0000000000FF0000u) << 24u) |
+          ((val & 0x000000000000FF00u) << 40u) |
+          ((val & 0x00000000000000FFu) << 56u);
+  }
+  memcpy(data, &val, sizeof(val));
+}
+
+static inline uint64_t amqp_d64(void *data) {
+  uint64_t val;
+  memcpy(&val, data, sizeof(val));
+  if (!is_bigendian()) {
+    val = ((val & 0xFF00000000000000u) >> 56u) |
+          ((val & 0x00FF000000000000u) >> 40u) |
+          ((val & 0x0000FF0000000000u) >> 24u) |
+          ((val & 0x000000FF00000000u) >> 8u) |
+          ((val & 0x00000000FF000000u) << 8u) |
+          ((val & 0x0000000000FF0000u) << 24u) |
+          ((val & 0x000000000000FF00u) << 40u) |
+          ((val & 0x00000000000000FFu) << 56u);
+  }
+  return val;
+}
+
+DECLARE_CODEC_BASE_TYPE(8)
+DECLARE_CODEC_BASE_TYPE(16)
+DECLARE_CODEC_BASE_TYPE(32)
+DECLARE_CODEC_BASE_TYPE(64)
+
+static inline int amqp_encode_bytes(amqp_bytes_t encoded, size_t *offset,
+                                    amqp_bytes_t input) {
+  size_t o = *offset;
+  /* The memcpy below has undefined behavior if the input is NULL. It is valid
+   * for a 0-length amqp_bytes_t to have .bytes == NULL. Thus we should check
+   * before encoding.
+   */
+  if (input.len == 0) {
+    return 1;
+  }
+  if ((*offset = o + input.len) <= encoded.len) {
+    memcpy(amqp_offset(encoded.bytes, o), input.bytes, input.len);
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+static inline int amqp_decode_bytes(amqp_bytes_t encoded, size_t *offset,
+                                    amqp_bytes_t *output, size_t len) {
+  size_t o = *offset;
+  if ((*offset = o + len) <= encoded.len) {
+    output->bytes = amqp_offset(encoded.bytes, o);
+    output->len = len;
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+AMQP_NORETURN
+void amqp_abort(const char *fmt, ...);
+
+int amqp_bytes_equal(amqp_bytes_t r, amqp_bytes_t l);
+
+static inline amqp_rpc_reply_t amqp_rpc_reply_error(amqp_status_enum status) {
+  amqp_rpc_reply_t reply;
+  reply.reply_type = AMQP_RESPONSE_LIBRARY_EXCEPTION;
+  reply.library_error = status;
+  return reply;
+}
+
+int amqp_send_frame_inner(amqp_connection_state_t state,
+                          const amqp_frame_t *frame, int flags,
+                          amqp_time_t deadline);
+#endif

+ 1492 - 0
ext/librabbitmq/librabbitmq/amqp_socket.c

@@ -0,0 +1,1492 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2014
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "amqp_private.h"
+#include "amqp_socket.h"
+#include "amqp_table.h"
+#include "amqp_time.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+
+#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+/* On older BSD types.h must come before net includes */
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef HAVE_SELECT
+#include <sys/select.h>
+#endif
+#include <fcntl.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#ifdef HAVE_POLL
+#include <poll.h>
+#endif
+#include <unistd.h>
+#endif
+
+static int amqp_id_in_reply_list(amqp_method_number_t expected,
+                                 amqp_method_number_t *list);
+
+static int amqp_os_socket_init(void) {
+#ifdef _WIN32
+  static int called_wsastartup = 0;
+  if (!called_wsastartup) {
+    WSADATA data;
+    int res = WSAStartup(0x0202, &data);
+    if (res) {
+      return AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR;
+    }
+
+    called_wsastartup = 1;
+  }
+  return AMQP_STATUS_OK;
+
+#else
+  return AMQP_STATUS_OK;
+#endif
+}
+
+int amqp_os_socket_error(void) {
+#ifdef _WIN32
+  return WSAGetLastError();
+#else
+  return errno;
+#endif
+}
+
+int amqp_os_socket_close(int sockfd) {
+#ifdef _WIN32
+  return closesocket(sockfd);
+#else
+  return close(sockfd);
+#endif
+}
+
+ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len,
+                         int flags) {
+  assert(self);
+  assert(self->klass->send);
+  return self->klass->send(self, buf, len, flags);
+}
+
+ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len,
+                         int flags) {
+  assert(self);
+  assert(self->klass->recv);
+  return self->klass->recv(self, buf, len, flags);
+}
+
+int amqp_socket_open(amqp_socket_t *self, const char *host, int port) {
+  assert(self);
+  assert(self->klass->open);
+  return self->klass->open(self, host, port, NULL);
+}
+
+int amqp_socket_open_noblock(amqp_socket_t *self, const char *host, int port,
+                             struct timeval *timeout) {
+  assert(self);
+  assert(self->klass->open);
+  return self->klass->open(self, host, port, timeout);
+}
+
+int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force) {
+  assert(self);
+  assert(self->klass->close);
+  return self->klass->close(self, force);
+}
+
+void amqp_socket_delete(amqp_socket_t *self) {
+  if (self) {
+    assert(self->klass->delete);
+    self->klass->delete (self);
+  }
+}
+
+int amqp_socket_get_sockfd(amqp_socket_t *self) {
+  assert(self);
+  assert(self->klass->get_sockfd);
+  return self->klass->get_sockfd(self);
+}
+
+int amqp_poll(int fd, int event, amqp_time_t deadline) {
+#ifdef HAVE_POLL
+  struct pollfd pfd;
+  int res;
+  int timeout_ms;
+
+  /* Function should only ever be called with one of these two */
+  assert(event == AMQP_SF_POLLIN || event == AMQP_SF_POLLOUT);
+
+start_poll:
+  pfd.fd = fd;
+  switch (event) {
+    case AMQP_SF_POLLIN:
+      pfd.events = POLLIN;
+      break;
+    case AMQP_SF_POLLOUT:
+      pfd.events = POLLOUT;
+      break;
+  }
+
+  timeout_ms = amqp_time_ms_until(deadline);
+  if (-1 > timeout_ms) {
+    return timeout_ms;
+  }
+
+  res = poll(&pfd, 1, timeout_ms);
+
+  if (0 < res) {
+    /* TODO: optimize this a bit by returning the AMQP_STATUS_SOCKET_ERROR or
+     * equivalent when pdf.revent is POLLHUP or POLLERR, so an extra syscall
+     * doesn't need to be made. */
+    return AMQP_STATUS_OK;
+  } else if (0 == res) {
+    return AMQP_STATUS_TIMEOUT;
+  } else {
+    switch (amqp_os_socket_error()) {
+      case EINTR:
+        goto start_poll;
+      default:
+        return AMQP_STATUS_SOCKET_ERROR;
+    }
+  }
+  return AMQP_STATUS_OK;
+#elif defined(HAVE_SELECT)
+  fd_set fds;
+  fd_set exceptfds;
+  fd_set *exceptfdsp;
+  int res;
+  struct timeval tv;
+  struct timeval *tvp;
+
+  assert((0 != (event & AMQP_SF_POLLIN)) || (0 != (event & AMQP_SF_POLLOUT)));
+#ifndef _WIN32
+  /* On Win32 connect() failure is indicated through the exceptfds, it does not
+   * make any sense to allow POLLERR on any other platform or condition */
+  assert(0 == (event & AMQP_SF_POLLERR));
+#endif
+
+start_select:
+  FD_ZERO(&fds);
+  FD_SET(fd, &fds);
+
+  if (event & AMQP_SF_POLLERR) {
+    FD_ZERO(&exceptfds);
+    FD_SET(fd, &exceptfds);
+    exceptfdsp = &exceptfds;
+  } else {
+    exceptfdsp = NULL;
+  }
+
+  res = amqp_time_tv_until(deadline, &tv, &tvp);
+  if (res != AMQP_STATUS_OK) {
+    return res;
+  }
+
+  if (event & AMQP_SF_POLLIN) {
+    res = select(fd + 1, &fds, NULL, exceptfdsp, tvp);
+  } else if (event & AMQP_SF_POLLOUT) {
+    res = select(fd + 1, NULL, &fds, exceptfdsp, tvp);
+  }
+
+  if (0 < res) {
+    return AMQP_STATUS_OK;
+  } else if (0 == res) {
+    return AMQP_STATUS_TIMEOUT;
+  } else {
+    switch (amqp_os_socket_error()) {
+      case EINTR:
+        goto start_select;
+      default:
+        return AMQP_STATUS_SOCKET_ERROR;
+    }
+  }
+#else
+#error "poll() or select() is needed to compile rabbitmq-c"
+#endif
+}
+
+static ssize_t do_poll(amqp_connection_state_t state, ssize_t res,
+                       amqp_time_t deadline) {
+  int fd = amqp_get_sockfd(state);
+  if (-1 == fd) {
+    return AMQP_STATUS_SOCKET_CLOSED;
+  }
+  switch (res) {
+    case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD:
+      res = amqp_poll(fd, AMQP_SF_POLLIN, deadline);
+      break;
+    case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE:
+      res = amqp_poll(fd, AMQP_SF_POLLOUT, deadline);
+      break;
+  }
+  return res;
+}
+
+ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf,
+                      size_t len, amqp_time_t deadline, int flags) {
+  ssize_t res;
+  void *buf_left = (void *)buf;
+  /* Assume that len is not going to be larger than ssize_t can hold. */
+  ssize_t len_left = (size_t)len;
+
+start_send:
+  res = amqp_socket_send(state->socket, buf_left, len_left, flags);
+
+  if (res > 0) {
+    len_left -= res;
+    buf_left = (char *)buf_left + res;
+    if (0 == len_left) {
+      return (ssize_t)len;
+    }
+    goto start_send;
+  }
+  res = do_poll(state, res, deadline);
+  if (AMQP_STATUS_OK == res) {
+    goto start_send;
+  }
+  if (AMQP_STATUS_TIMEOUT == res) {
+    return (ssize_t)len - len_left;
+  }
+  return res;
+}
+
+int amqp_open_socket(char const *hostname, int portnumber) {
+  return amqp_open_socket_inner(hostname, portnumber, amqp_time_infinite());
+}
+
+int amqp_open_socket_noblock(char const *hostname, int portnumber,
+                             struct timeval *timeout) {
+  amqp_time_t deadline;
+  int res = amqp_time_from_now(&deadline, timeout);
+  if (AMQP_STATUS_OK != res) {
+    return res;
+  }
+  return amqp_open_socket_inner(hostname, portnumber, deadline);
+}
+
+#ifdef _WIN32
+static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) {
+  int one = 1;
+  SOCKET sockfd;
+  int last_error;
+
+  /*
+   * This cast is to squash warnings on Win64, see:
+   * http://stackoverflow.com/questions/1953639/is-it-safe-to-cast-socket-to-int-under-win64
+   */
+
+  sockfd = (int)socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+  if (INVALID_SOCKET == sockfd) {
+    return AMQP_STATUS_SOCKET_ERROR;
+  }
+
+  /* Set the socket to be non-blocking */
+  if (SOCKET_ERROR == ioctlsocket(sockfd, FIONBIO, &one)) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  /* Disable nagle */
+  if (SOCKET_ERROR == setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
+                                 (const char *)&one, sizeof(one))) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  /* Enable TCP keepalives */
+  if (SOCKET_ERROR == setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+                                 (const char *)&one, sizeof(one))) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  if (SOCKET_ERROR != connect(sockfd, addr->ai_addr, (int)addr->ai_addrlen)) {
+    return (int)sockfd;
+  }
+
+  if (WSAEWOULDBLOCK != WSAGetLastError()) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  last_error =
+      amqp_poll((int)sockfd, AMQP_SF_POLLOUT | AMQP_SF_POLLERR, deadline);
+  if (AMQP_STATUS_OK != last_error) {
+    goto err;
+  }
+
+  {
+    int result;
+    int result_len = sizeof(result);
+
+    if (SOCKET_ERROR == getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
+                                   (char *)&result, &result_len) ||
+        result != 0) {
+      last_error = AMQP_STATUS_SOCKET_ERROR;
+      goto err;
+    }
+  }
+
+  return (int)sockfd;
+
+err:
+  closesocket(sockfd);
+  return last_error;
+}
+#else
+static int connect_socket(struct addrinfo *addr, amqp_time_t deadline) {
+  int one = 1;
+  int sockfd;
+  int flags;
+  int last_error;
+
+  sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+  if (-1 == sockfd) {
+    return AMQP_STATUS_SOCKET_ERROR;
+  }
+
+  /* Enable CLOEXEC on socket */
+  flags = fcntl(sockfd, F_GETFD);
+  if (flags == -1 || fcntl(sockfd, F_SETFD, (long)(flags | FD_CLOEXEC)) == -1) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  /* Set the socket as non-blocking */
+  flags = fcntl(sockfd, F_GETFL);
+  if (flags == -1 || fcntl(sockfd, F_SETFL, (long)(flags | O_NONBLOCK)) == -1) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+#ifdef SO_NOSIGPIPE
+  /* Turn off SIGPIPE on platforms that support it, BSD, MacOSX */
+  if (0 != setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one))) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+#endif /* SO_NOSIGPIPE */
+
+  /* Disable nagle */
+  if (0 != setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one))) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  /* Enable TCP keepalives */
+  if (0 != setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  if (0 == connect(sockfd, addr->ai_addr, addr->ai_addrlen)) {
+    return sockfd;
+  }
+
+  if (EINPROGRESS != errno) {
+    last_error = AMQP_STATUS_SOCKET_ERROR;
+    goto err;
+  }
+
+  last_error = amqp_poll(sockfd, AMQP_SF_POLLOUT, deadline);
+  if (AMQP_STATUS_OK != last_error) {
+    goto err;
+  }
+
+  {
+    int result;
+    socklen_t result_len = sizeof(result);
+
+    if (-1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &result, &result_len) ||
+        result != 0) {
+      last_error = AMQP_STATUS_SOCKET_ERROR;
+      goto err;
+    }
+  }
+
+  return sockfd;
+
+err:
+  close(sockfd);
+  return last_error;
+}
+#endif
+
+int amqp_open_socket_inner(char const *hostname, int portnumber,
+                           amqp_time_t deadline) {
+  struct addrinfo hint;
+  struct addrinfo *address_list;
+  struct addrinfo *addr;
+  char portnumber_string[33];
+  int sockfd = -1;
+  int last_error;
+
+  last_error = amqp_os_socket_init();
+  if (AMQP_STATUS_OK != last_error) {
+    return last_error;
+  }
+
+  memset(&hint, 0, sizeof(hint));
+  hint.ai_family = PF_UNSPEC; /* PF_INET or PF_INET6 */
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
+
+  (void)sprintf(portnumber_string, "%d", portnumber);
+
+  last_error = getaddrinfo(hostname, portnumber_string, &hint, &address_list);
+  if (0 != last_error) {
+    return AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED;
+  }
+
+  for (addr = address_list; addr; addr = addr->ai_next) {
+    sockfd = connect_socket(addr, deadline);
+
+    if (sockfd >= 0) {
+      last_error = AMQP_STATUS_OK;
+      break;
+    } else if (sockfd == AMQP_STATUS_TIMEOUT) {
+      last_error = sockfd;
+      break;
+    }
+  }
+
+  freeaddrinfo(address_list);
+  if (last_error != AMQP_STATUS_OK || sockfd == -1) {
+    return last_error;
+  }
+  return sockfd;
+}
+
+static int send_header_inner(amqp_connection_state_t state,
+                             amqp_time_t deadline) {
+  ssize_t res;
+  static const uint8_t header[8] = {'A',
+                                    'M',
+                                    'Q',
+                                    'P',
+                                    0,
+                                    AMQP_PROTOCOL_VERSION_MAJOR,
+                                    AMQP_PROTOCOL_VERSION_MINOR,
+                                    AMQP_PROTOCOL_VERSION_REVISION};
+  res = amqp_try_send(state, header, sizeof(header), deadline, AMQP_SF_NONE);
+  if (sizeof(header) == res) {
+    return AMQP_STATUS_OK;
+  }
+  return (int)res;
+}
+
+int amqp_send_header(amqp_connection_state_t state) {
+  return send_header_inner(state, amqp_time_infinite());
+}
+
+static amqp_bytes_t sasl_method_name(amqp_sasl_method_enum method) {
+  amqp_bytes_t res;
+
+  switch (method) {
+    case AMQP_SASL_METHOD_PLAIN:
+      res = amqp_cstring_bytes("PLAIN");
+      break;
+    case AMQP_SASL_METHOD_EXTERNAL:
+      res = amqp_cstring_bytes("EXTERNAL");
+      break;
+
+    default:
+      amqp_abort("Invalid SASL method: %d", (int)method);
+  }
+
+  return res;
+}
+
+static int bytes_equal(amqp_bytes_t l, amqp_bytes_t r) {
+  if (l.len == r.len) {
+    if (l.bytes && r.bytes) {
+      if (0 == memcmp(l.bytes, r.bytes, l.len)) {
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+int sasl_mechanism_in_list(amqp_bytes_t mechanisms,
+                           amqp_sasl_method_enum method) {
+  amqp_bytes_t mechanism;
+  amqp_bytes_t supported_mechanism;
+  uint8_t *start;
+  uint8_t *end;
+  uint8_t *current;
+
+  assert(NULL != mechanisms.bytes);
+
+  mechanism = sasl_method_name(method);
+
+  start = (uint8_t *)mechanisms.bytes;
+  current = start;
+  end = start + mechanisms.len;
+
+  for (; current != end; start = current + 1) {
+    /* HACK: SASL states that we should be parsing this string as a UTF-8
+     * string, which we're plainly not doing here. At this point its not worth
+     * dragging an entire UTF-8 parser for this one case, and this should work
+     * most of the time */
+    current = memchr(start, ' ', end - start);
+    if (NULL == current) {
+      current = end;
+    }
+    supported_mechanism.bytes = start;
+    supported_mechanism.len = current - start;
+    if (bytes_equal(mechanism, supported_mechanism)) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static amqp_bytes_t sasl_response(amqp_pool_t *pool,
+                                  amqp_sasl_method_enum method, va_list args) {
+  amqp_bytes_t response;
+
+  switch (method) {
+    case AMQP_SASL_METHOD_PLAIN: {
+      char *username = va_arg(args, char *);
+      size_t username_len = strlen(username);
+      char *password = va_arg(args, char *);
+      size_t password_len = strlen(password);
+      char *response_buf;
+
+      amqp_pool_alloc_bytes(pool, strlen(username) + strlen(password) + 2,
+                            &response);
+      if (response.bytes == NULL)
+      /* We never request a zero-length block, because of the +2
+         above, so a NULL here really is ENOMEM. */
+      {
+        return response;
+      }
+
+      response_buf = response.bytes;
+      response_buf[0] = 0;
+      memcpy(response_buf + 1, username, username_len);
+      response_buf[username_len + 1] = 0;
+      memcpy(response_buf + username_len + 2, password, password_len);
+      break;
+    }
+    case AMQP_SASL_METHOD_EXTERNAL: {
+      char *identity = va_arg(args, char *);
+      size_t identity_len = strlen(identity);
+
+      amqp_pool_alloc_bytes(pool, identity_len, &response);
+      if (response.bytes == NULL) {
+        return response;
+      }
+
+      memcpy(response.bytes, identity, identity_len);
+      break;
+    }
+    default:
+      amqp_abort("Invalid SASL method: %d", (int)method);
+  }
+
+  return response;
+}
+
+amqp_boolean_t amqp_frames_enqueued(amqp_connection_state_t state) {
+  return (state->first_queued_frame != NULL);
+}
+
+/*
+ * Check to see if we have data in our buffer. If this returns 1, we
+ * will avoid an immediate blocking read in amqp_simple_wait_frame.
+ */
+amqp_boolean_t amqp_data_in_buffer(amqp_connection_state_t state) {
+  return (state->sock_inbound_offset < state->sock_inbound_limit);
+}
+
+static int consume_one_frame(amqp_connection_state_t state,
+                             amqp_frame_t *decoded_frame) {
+  int res;
+
+  amqp_bytes_t buffer;
+  buffer.len = state->sock_inbound_limit - state->sock_inbound_offset;
+  buffer.bytes =
+      ((char *)state->sock_inbound_buffer.bytes) + state->sock_inbound_offset;
+
+  res = amqp_handle_input(state, buffer, decoded_frame);
+  if (res < 0) {
+    return res;
+  }
+
+  state->sock_inbound_offset += res;
+
+  return AMQP_STATUS_OK;
+}
+
+static int recv_with_timeout(amqp_connection_state_t state,
+                             amqp_time_t timeout) {
+  ssize_t res;
+  int fd;
+
+start_recv:
+  res = amqp_socket_recv(state->socket, state->sock_inbound_buffer.bytes,
+                         state->sock_inbound_buffer.len, 0);
+
+  if (res < 0) {
+    fd = amqp_get_sockfd(state);
+    if (-1 == fd) {
+      return AMQP_STATUS_CONNECTION_CLOSED;
+    }
+    switch (res) {
+      default:
+        return (int)res;
+      case AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD:
+        res = amqp_poll(fd, AMQP_SF_POLLIN, timeout);
+        break;
+      case AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE:
+        res = amqp_poll(fd, AMQP_SF_POLLOUT, timeout);
+        break;
+    }
+    if (AMQP_STATUS_OK == res) {
+      goto start_recv;
+    }
+    return (int)res;
+  }
+
+  state->sock_inbound_limit = res;
+  state->sock_inbound_offset = 0;
+
+  res = amqp_time_s_from_now(&state->next_recv_heartbeat,
+                             amqp_heartbeat_recv(state));
+  if (AMQP_STATUS_OK != res) {
+    return (int)res;
+  }
+  return AMQP_STATUS_OK;
+}
+
+int amqp_try_recv(amqp_connection_state_t state) {
+  amqp_time_t timeout;
+
+  while (amqp_data_in_buffer(state)) {
+    amqp_frame_t frame;
+    int res = consume_one_frame(state, &frame);
+
+    if (AMQP_STATUS_OK != res) {
+      return res;
+    }
+
+    if (frame.frame_type != 0) {
+      amqp_pool_t *channel_pool;
+      amqp_frame_t *frame_copy;
+      amqp_link_t *link;
+
+      channel_pool = amqp_get_or_create_channel_pool(state, frame.channel);
+      if (NULL == channel_pool) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+
+      frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
+      link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
+
+      if (frame_copy == NULL || link == NULL) {
+        return AMQP_STATUS_NO_MEMORY;
+      }
+
+      *frame_copy = frame;
+
+      link->next = NULL;
+      link->data = frame_copy;
+
+      if (state->last_queued_frame == NULL) {
+        state->first_queued_frame = link;
+      } else {
+        state->last_queued_frame->next = link;
+      }
+      state->last_queued_frame = link;
+    }
+  }
+  timeout = amqp_time_immediate();
+
+  return recv_with_timeout(state, timeout);
+}
+
+static int wait_frame_inner(amqp_connection_state_t state,
+                            amqp_frame_t *decoded_frame,
+                            amqp_time_t timeout_deadline) {
+  amqp_time_t deadline;
+  int res;
+
+  for (;;) {
+    while (amqp_data_in_buffer(state)) {
+      res = consume_one_frame(state, decoded_frame);
+
+      if (AMQP_STATUS_OK != res) {
+        return res;
+      }
+
+      if (AMQP_FRAME_HEARTBEAT == decoded_frame->frame_type) {
+        amqp_maybe_release_buffers_on_channel(state, 0);
+        continue;
+      }
+
+      if (decoded_frame->frame_type != 0) {
+        /* Complete frame was read. Return it. */
+        return AMQP_STATUS_OK;
+      }
+    }
+
+  beginrecv:
+    res = amqp_time_has_past(state->next_send_heartbeat);
+    if (AMQP_STATUS_TIMER_FAILURE == res) {
+      return res;
+    } else if (AMQP_STATUS_TIMEOUT == res) {
+      amqp_frame_t heartbeat;
+      heartbeat.channel = 0;
+      heartbeat.frame_type = AMQP_FRAME_HEARTBEAT;
+
+      res = amqp_send_frame(state, &heartbeat);
+      if (AMQP_STATUS_OK != res) {
+        return res;
+      }
+    }
+    deadline = amqp_time_first(timeout_deadline,
+                               amqp_time_first(state->next_recv_heartbeat,
+                                               state->next_send_heartbeat));
+
+    /* TODO this needs to wait for a _frame_ and not anything written from the
+     * socket */
+    res = recv_with_timeout(state, deadline);
+
+    if (AMQP_STATUS_TIMEOUT == res) {
+      if (amqp_time_equal(deadline, state->next_recv_heartbeat)) {
+        amqp_socket_close(state->socket, AMQP_SC_FORCE);
+        return AMQP_STATUS_HEARTBEAT_TIMEOUT;
+      } else if (amqp_time_equal(deadline, timeout_deadline)) {
+        return AMQP_STATUS_TIMEOUT;
+      } else if (amqp_time_equal(deadline, state->next_send_heartbeat)) {
+        /* send heartbeat happens before we do recv_with_timeout */
+        goto beginrecv;
+      } else {
+        amqp_abort("Internal error: unable to determine timeout reason");
+      }
+    } else if (AMQP_STATUS_OK != res) {
+      return res;
+    }
+  }
+}
+
+static amqp_link_t *amqp_create_link_for_frame(amqp_connection_state_t state,
+                                               amqp_frame_t *frame) {
+  amqp_link_t *link;
+  amqp_frame_t *frame_copy;
+
+  amqp_pool_t *channel_pool =
+      amqp_get_or_create_channel_pool(state, frame->channel);
+
+  if (NULL == channel_pool) {
+    return NULL;
+  }
+
+  link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
+  frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
+
+  if (NULL == link || NULL == frame_copy) {
+    return NULL;
+  }
+
+  *frame_copy = *frame;
+  link->data = frame_copy;
+
+  return link;
+}
+
+int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
+  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
+  if (NULL == link) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  if (NULL == state->first_queued_frame) {
+    state->first_queued_frame = link;
+  } else {
+    state->last_queued_frame->next = link;
+  }
+
+  link->next = NULL;
+  state->last_queued_frame = link;
+
+  return AMQP_STATUS_OK;
+}
+
+int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame) {
+  amqp_link_t *link = amqp_create_link_for_frame(state, frame);
+  if (NULL == link) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  if (NULL == state->first_queued_frame) {
+    state->first_queued_frame = link;
+    state->last_queued_frame = link;
+    link->next = NULL;
+  } else {
+    link->next = state->first_queued_frame;
+    state->first_queued_frame = link;
+  }
+
+  return AMQP_STATUS_OK;
+}
+
+int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state,
+                                      amqp_channel_t channel,
+                                      amqp_frame_t *decoded_frame) {
+  amqp_frame_t *frame_ptr;
+  amqp_link_t *cur;
+  int res;
+
+  for (cur = state->first_queued_frame; NULL != cur; cur = cur->next) {
+    frame_ptr = cur->data;
+
+    if (channel == frame_ptr->channel) {
+      state->first_queued_frame = cur->next;
+      if (NULL == state->first_queued_frame) {
+        state->last_queued_frame = NULL;
+      }
+
+      *decoded_frame = *frame_ptr;
+
+      return AMQP_STATUS_OK;
+    }
+  }
+
+  for (;;) {
+    res = wait_frame_inner(state, decoded_frame, amqp_time_infinite());
+
+    if (AMQP_STATUS_OK != res) {
+      return res;
+    }
+
+    if (channel == decoded_frame->channel) {
+      return AMQP_STATUS_OK;
+    } else {
+      res = amqp_queue_frame(state, decoded_frame);
+      if (res != AMQP_STATUS_OK) {
+        return res;
+      }
+    }
+  }
+}
+
+int amqp_simple_wait_frame(amqp_connection_state_t state,
+                           amqp_frame_t *decoded_frame) {
+  return amqp_simple_wait_frame_noblock(state, decoded_frame, NULL);
+}
+
+int amqp_simple_wait_frame_noblock(amqp_connection_state_t state,
+                                   amqp_frame_t *decoded_frame,
+                                   struct timeval *timeout) {
+  amqp_time_t deadline;
+
+  int res = amqp_time_from_now(&deadline, timeout);
+  if (AMQP_STATUS_OK != res) {
+    return res;
+  }
+
+  if (state->first_queued_frame != NULL) {
+    amqp_frame_t *f = (amqp_frame_t *)state->first_queued_frame->data;
+    state->first_queued_frame = state->first_queued_frame->next;
+    if (state->first_queued_frame == NULL) {
+      state->last_queued_frame = NULL;
+    }
+    *decoded_frame = *f;
+    return AMQP_STATUS_OK;
+  } else {
+    return wait_frame_inner(state, decoded_frame, deadline);
+  }
+}
+
+static int amqp_simple_wait_method_list(amqp_connection_state_t state,
+                                        amqp_channel_t expected_channel,
+                                        amqp_method_number_t *expected_methods,
+                                        amqp_time_t deadline,
+                                        amqp_method_t *output) {
+  amqp_frame_t frame;
+  struct timeval tv;
+  struct timeval *tvp;
+
+  int res = amqp_time_tv_until(deadline, &tv, &tvp);
+  if (res != AMQP_STATUS_OK) {
+    return res;
+  }
+
+  res = amqp_simple_wait_frame_noblock(state, &frame, tvp);
+  if (AMQP_STATUS_OK != res) {
+    return res;
+  }
+
+  if (AMQP_FRAME_METHOD != frame.frame_type ||
+      expected_channel != frame.channel ||
+      !amqp_id_in_reply_list(frame.payload.method.id, expected_methods)) {
+    return AMQP_STATUS_WRONG_METHOD;
+  }
+  *output = frame.payload.method;
+  return AMQP_STATUS_OK;
+}
+
+static int simple_wait_method_inner(amqp_connection_state_t state,
+                                    amqp_channel_t expected_channel,
+                                    amqp_method_number_t expected_method,
+                                    amqp_time_t deadline,
+                                    amqp_method_t *output) {
+  amqp_method_number_t expected_methods[] = {expected_method, 0};
+  return amqp_simple_wait_method_list(state, expected_channel, expected_methods,
+                                      deadline, output);
+}
+
+int amqp_simple_wait_method(amqp_connection_state_t state,
+                            amqp_channel_t expected_channel,
+                            amqp_method_number_t expected_method,
+                            amqp_method_t *output) {
+  return simple_wait_method_inner(state, expected_channel, expected_method,
+                                  amqp_time_infinite(), output);
+}
+
+int amqp_send_method(amqp_connection_state_t state, amqp_channel_t channel,
+                     amqp_method_number_t id, void *decoded) {
+  return amqp_send_method_inner(state, channel, id, decoded, AMQP_SF_NONE,
+                                amqp_time_infinite());
+}
+
+int amqp_send_method_inner(amqp_connection_state_t state,
+                           amqp_channel_t channel, amqp_method_number_t id,
+                           void *decoded, int flags, amqp_time_t deadline) {
+  amqp_frame_t frame;
+
+  frame.frame_type = AMQP_FRAME_METHOD;
+  frame.channel = channel;
+  frame.payload.method.id = id;
+  frame.payload.method.decoded = decoded;
+  return amqp_send_frame_inner(state, &frame, flags, deadline);
+}
+
+static int amqp_id_in_reply_list(amqp_method_number_t expected,
+                                 amqp_method_number_t *list) {
+  while (*list != 0) {
+    if (*list == expected) {
+      return 1;
+    }
+    list++;
+  }
+  return 0;
+}
+
+static amqp_rpc_reply_t simple_rpc_inner(
+    amqp_connection_state_t state, amqp_channel_t channel,
+    amqp_method_number_t request_id, amqp_method_number_t *expected_reply_ids,
+    void *decoded_request_method, amqp_time_t deadline) {
+  int status;
+  amqp_rpc_reply_t result;
+
+  memset(&result, 0, sizeof(result));
+
+  status = amqp_send_method(state, channel, request_id, decoded_request_method);
+  if (status < 0) {
+    return amqp_rpc_reply_error(status);
+  }
+
+  {
+    amqp_frame_t frame;
+
+  retry:
+    status = wait_frame_inner(state, &frame, deadline);
+    if (status < 0) {
+      if (status == AMQP_STATUS_TIMEOUT) {
+        amqp_socket_close(state->socket, AMQP_SC_FORCE);
+      }
+      return amqp_rpc_reply_error(status);
+    }
+
+    /*
+     * We store the frame for later processing unless it's something
+     * that directly affects us here, namely a method frame that is
+     * either
+     *  - on the channel we want, and of the expected type, or
+     *  - on the channel we want, and a channel.close frame, or
+     *  - on channel zero, and a connection.close frame.
+     */
+    if (!((frame.frame_type == AMQP_FRAME_METHOD) &&
+          (((frame.channel == channel) &&
+            (amqp_id_in_reply_list(frame.payload.method.id,
+                                   expected_reply_ids) ||
+             (frame.payload.method.id == AMQP_CHANNEL_CLOSE_METHOD))) ||
+           ((frame.channel == 0) &&
+            (frame.payload.method.id == AMQP_CONNECTION_CLOSE_METHOD))))) {
+      amqp_pool_t *channel_pool;
+      amqp_frame_t *frame_copy;
+      amqp_link_t *link;
+
+      channel_pool = amqp_get_or_create_channel_pool(state, frame.channel);
+      if (NULL == channel_pool) {
+        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
+      }
+
+      frame_copy = amqp_pool_alloc(channel_pool, sizeof(amqp_frame_t));
+      link = amqp_pool_alloc(channel_pool, sizeof(amqp_link_t));
+
+      if (frame_copy == NULL || link == NULL) {
+        return amqp_rpc_reply_error(AMQP_STATUS_NO_MEMORY);
+      }
+
+      *frame_copy = frame;
+
+      link->next = NULL;
+      link->data = frame_copy;
+
+      if (state->last_queued_frame == NULL) {
+        state->first_queued_frame = link;
+      } else {
+        state->last_queued_frame->next = link;
+      }
+      state->last_queued_frame = link;
+
+      goto retry;
+    }
+
+    result.reply_type =
+        (amqp_id_in_reply_list(frame.payload.method.id, expected_reply_ids))
+            ? AMQP_RESPONSE_NORMAL
+            : AMQP_RESPONSE_SERVER_EXCEPTION;
+
+    result.reply = frame.payload.method;
+    return result;
+  }
+}
+
+amqp_rpc_reply_t amqp_simple_rpc(amqp_connection_state_t state,
+                                 amqp_channel_t channel,
+                                 amqp_method_number_t request_id,
+                                 amqp_method_number_t *expected_reply_ids,
+                                 void *decoded_request_method) {
+  amqp_time_t deadline;
+  int res;
+
+  res = amqp_time_from_now(&deadline, state->rpc_timeout);
+  if (res != AMQP_STATUS_OK) {
+    return amqp_rpc_reply_error(res);
+  }
+
+  return simple_rpc_inner(state, channel, request_id, expected_reply_ids,
+                          decoded_request_method, deadline);
+}
+
+void *amqp_simple_rpc_decoded(amqp_connection_state_t state,
+                              amqp_channel_t channel,
+                              amqp_method_number_t request_id,
+                              amqp_method_number_t reply_id,
+                              void *decoded_request_method) {
+  amqp_time_t deadline;
+  int res;
+  amqp_method_number_t replies[2];
+
+  res = amqp_time_from_now(&deadline, state->rpc_timeout);
+  if (res != AMQP_STATUS_OK) {
+    state->most_recent_api_result = amqp_rpc_reply_error(res);
+    return NULL;
+  }
+
+  replies[0] = reply_id;
+  replies[1] = 0;
+
+  state->most_recent_api_result = simple_rpc_inner(
+      state, channel, request_id, replies, decoded_request_method, deadline);
+
+  if (state->most_recent_api_result.reply_type == AMQP_RESPONSE_NORMAL) {
+    return state->most_recent_api_result.reply.decoded;
+  } else {
+    return NULL;
+  }
+}
+
+amqp_rpc_reply_t amqp_get_rpc_reply(amqp_connection_state_t state) {
+  return state->most_recent_api_result;
+}
+
+/*
+ * Merge base and add tables. If the two tables contain an entry with the same
+ * key, the entry from the add table takes precedence. For entries that are both
+ * tables with the same key, the table is recursively merged.
+ */
+int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add,
+                            amqp_table_t *result, amqp_pool_t *pool) {
+  int i;
+  int res;
+  amqp_pool_t temp_pool;
+  amqp_table_t temp_result;
+  assert(base != NULL);
+  assert(result != NULL);
+  assert(pool != NULL);
+
+  if (NULL == add) {
+    return amqp_table_clone(base, result, pool);
+  }
+
+  init_amqp_pool(&temp_pool, 4096);
+  temp_result.num_entries = 0;
+  temp_result.entries =
+      amqp_pool_alloc(&temp_pool, sizeof(amqp_table_entry_t) *
+                                      (base->num_entries + add->num_entries));
+  if (NULL == temp_result.entries) {
+    res = AMQP_STATUS_NO_MEMORY;
+    goto error_out;
+  }
+  for (i = 0; i < base->num_entries; ++i) {
+    temp_result.entries[temp_result.num_entries] = base->entries[i];
+    temp_result.num_entries++;
+  }
+  for (i = 0; i < add->num_entries; ++i) {
+    amqp_table_entry_t *e =
+        amqp_table_get_entry_by_key(&temp_result, add->entries[i].key);
+    if (NULL != e) {
+      if (AMQP_FIELD_KIND_TABLE == add->entries[i].value.kind &&
+          AMQP_FIELD_KIND_TABLE == e->value.kind) {
+        amqp_table_entry_t *be =
+            amqp_table_get_entry_by_key(base, add->entries[i].key);
+
+        res = amqp_merge_capabilities(&be->value.value.table,
+                                      &add->entries[i].value.value.table,
+                                      &e->value.value.table, &temp_pool);
+        if (AMQP_STATUS_OK != res) {
+          goto error_out;
+        }
+      } else {
+        e->value = add->entries[i].value;
+      }
+    } else {
+      temp_result.entries[temp_result.num_entries] = add->entries[i];
+      temp_result.num_entries++;
+    }
+  }
+  res = amqp_table_clone(&temp_result, result, pool);
+error_out:
+  empty_amqp_pool(&temp_pool);
+  return res;
+}
+
+static amqp_rpc_reply_t amqp_login_inner(
+    amqp_connection_state_t state, char const *vhost, int channel_max,
+    int frame_max, int heartbeat, const amqp_table_t *client_properties,
+    struct timeval *timeout, amqp_sasl_method_enum sasl_method, va_list vl) {
+  int res;
+  amqp_method_t method;
+
+  uint16_t client_channel_max;
+  uint32_t client_frame_max;
+  uint16_t client_heartbeat;
+
+  uint16_t server_channel_max;
+  uint32_t server_frame_max;
+  uint16_t server_heartbeat;
+
+  amqp_rpc_reply_t result;
+  amqp_time_t deadline;
+
+  if (channel_max < 0 || channel_max > UINT16_MAX) {
+    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
+  }
+  client_channel_max = (uint16_t)channel_max;
+
+  if (frame_max < 0) {
+    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
+  }
+  client_frame_max = (uint32_t)frame_max;
+
+  if (heartbeat < 0 || heartbeat > UINT16_MAX) {
+    return amqp_rpc_reply_error(AMQP_STATUS_INVALID_PARAMETER);
+  }
+  client_heartbeat = (uint16_t)heartbeat;
+
+  res = amqp_time_from_now(&deadline, timeout);
+  if (AMQP_STATUS_OK != res) {
+    goto error_res;
+  }
+
+  res = send_header_inner(state, deadline);
+  if (AMQP_STATUS_OK != res) {
+    goto error_res;
+  }
+
+  res = simple_wait_method_inner(state, 0, AMQP_CONNECTION_START_METHOD,
+                                 deadline, &method);
+  if (AMQP_STATUS_OK != res) {
+    goto error_res;
+  }
+
+  {
+    amqp_connection_start_t *s = (amqp_connection_start_t *)method.decoded;
+    if ((s->version_major != AMQP_PROTOCOL_VERSION_MAJOR) ||
+        (s->version_minor != AMQP_PROTOCOL_VERSION_MINOR)) {
+      res = AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION;
+      goto error_res;
+    }
+
+    res = amqp_table_clone(&s->server_properties, &state->server_properties,
+                           &state->properties_pool);
+
+    if (AMQP_STATUS_OK != res) {
+      goto error_res;
+    }
+
+    /* TODO: check that our chosen SASL mechanism is in the list of
+       acceptable mechanisms. Or even let the application choose from
+       the list! */
+    if (!sasl_mechanism_in_list(s->mechanisms, sasl_method)) {
+      res = AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD;
+      goto error_res;
+    }
+  }
+
+  {
+    amqp_table_entry_t default_properties[6];
+    amqp_table_t default_table;
+    amqp_table_entry_t client_capabilities[2];
+    amqp_table_t client_capabilities_table;
+    amqp_connection_start_ok_t s;
+    amqp_pool_t *channel_pool;
+    amqp_bytes_t response_bytes;
+
+    channel_pool = amqp_get_or_create_channel_pool(state, 0);
+    if (NULL == channel_pool) {
+      res = AMQP_STATUS_NO_MEMORY;
+      goto error_res;
+    }
+
+    response_bytes = sasl_response(channel_pool, sasl_method, vl);
+    if (response_bytes.bytes == NULL) {
+      res = AMQP_STATUS_NO_MEMORY;
+      goto error_res;
+    }
+
+    client_capabilities[0] =
+        amqp_table_construct_bool_entry("authentication_failure_close", 1);
+    client_capabilities[1] =
+        amqp_table_construct_bool_entry("exchange_exchange_bindings", 1);
+
+    client_capabilities_table.entries = client_capabilities;
+    client_capabilities_table.num_entries =
+        sizeof(client_capabilities) / sizeof(amqp_table_entry_t);
+
+    default_properties[0] =
+        amqp_table_construct_utf8_entry("product", "rabbitmq-c");
+    default_properties[1] =
+        amqp_table_construct_utf8_entry("version", AMQP_VERSION_STRING);
+    default_properties[2] =
+        amqp_table_construct_utf8_entry("platform", AMQ_PLATFORM);
+    default_properties[3] =
+        amqp_table_construct_utf8_entry("copyright", AMQ_COPYRIGHT);
+    default_properties[4] = amqp_table_construct_utf8_entry(
+        "information", "See https://github.com/alanxz/rabbitmq-c");
+    default_properties[5] = amqp_table_construct_table_entry(
+        "capabilities", &client_capabilities_table);
+
+    default_table.entries = default_properties;
+    default_table.num_entries =
+        sizeof(default_properties) / sizeof(amqp_table_entry_t);
+
+    res = amqp_merge_capabilities(&default_table, client_properties,
+                                  &state->client_properties, channel_pool);
+    if (AMQP_STATUS_OK != res) {
+      goto error_res;
+    }
+
+    s.client_properties = state->client_properties;
+    s.mechanism = sasl_method_name(sasl_method);
+    s.response = response_bytes;
+    s.locale = amqp_cstring_bytes("en_US");
+
+    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_START_OK_METHOD, &s,
+                                 AMQP_SF_NONE, deadline);
+    if (res < 0) {
+      goto error_res;
+    }
+  }
+
+  amqp_release_buffers(state);
+
+  {
+    amqp_method_number_t expected[] = {AMQP_CONNECTION_TUNE_METHOD,
+                                       AMQP_CONNECTION_CLOSE_METHOD, 0};
+
+    res = amqp_simple_wait_method_list(state, 0, expected, deadline, &method);
+    if (AMQP_STATUS_OK != res) {
+      goto error_res;
+    }
+  }
+
+  if (AMQP_CONNECTION_CLOSE_METHOD == method.id) {
+    result.reply_type = AMQP_RESPONSE_SERVER_EXCEPTION;
+    result.reply = method;
+    result.library_error = 0;
+    goto out;
+  }
+
+  {
+    amqp_connection_tune_t *s = (amqp_connection_tune_t *)method.decoded;
+    server_channel_max = s->channel_max;
+    server_frame_max = s->frame_max;
+    server_heartbeat = s->heartbeat;
+  }
+
+  if (server_channel_max != 0 &&
+      (server_channel_max < client_channel_max || client_channel_max == 0)) {
+    client_channel_max = server_channel_max;
+  } else if (server_channel_max == 0 && client_channel_max == 0) {
+    client_channel_max = UINT16_MAX;
+  }
+
+  if (server_frame_max != 0 && server_frame_max < client_frame_max) {
+    client_frame_max = server_frame_max;
+  }
+
+  if (server_heartbeat != 0 && server_heartbeat < client_heartbeat) {
+    client_heartbeat = server_heartbeat;
+  }
+
+  res = amqp_tune_connection(state, client_channel_max, client_frame_max,
+                             client_heartbeat);
+  if (res < 0) {
+    goto error_res;
+  }
+
+  {
+    amqp_connection_tune_ok_t s;
+    s.frame_max = client_frame_max;
+    s.channel_max = client_channel_max;
+    s.heartbeat = client_heartbeat;
+
+    res = amqp_send_method_inner(state, 0, AMQP_CONNECTION_TUNE_OK_METHOD, &s,
+                                 AMQP_SF_NONE, deadline);
+    if (res < 0) {
+      goto error_res;
+    }
+  }
+
+  amqp_release_buffers(state);
+
+  {
+    amqp_method_number_t replies[] = {AMQP_CONNECTION_OPEN_OK_METHOD, 0};
+    amqp_connection_open_t s;
+    s.virtual_host = amqp_cstring_bytes(vhost);
+    s.capabilities = amqp_empty_bytes;
+    s.insist = 1;
+
+    result = simple_rpc_inner(state, 0, AMQP_CONNECTION_OPEN_METHOD, replies,
+                              &s, deadline);
+    if (result.reply_type != AMQP_RESPONSE_NORMAL) {
+      goto out;
+    }
+  }
+
+  result.reply_type = AMQP_RESPONSE_NORMAL;
+  result.reply.id = 0;
+  result.reply.decoded = NULL;
+  result.library_error = 0;
+  amqp_maybe_release_buffers(state);
+
+out:
+  return result;
+
+error_res:
+  amqp_socket_close(state->socket, AMQP_SC_FORCE);
+  result = amqp_rpc_reply_error(res);
+
+  goto out;
+}
+
+amqp_rpc_reply_t amqp_login(amqp_connection_state_t state, char const *vhost,
+                            int channel_max, int frame_max, int heartbeat,
+                            int sasl_method, ...) {
+  va_list vl;
+  amqp_rpc_reply_t ret;
+
+  va_start(vl, sasl_method);
+
+  ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat,
+                         &amqp_empty_table, state->handshake_timeout,
+                         sasl_method, vl);
+
+  va_end(vl);
+
+  return ret;
+}
+
+amqp_rpc_reply_t amqp_login_with_properties(
+    amqp_connection_state_t state, char const *vhost, int channel_max,
+    int frame_max, int heartbeat, const amqp_table_t *client_properties,
+    int sasl_method, ...) {
+  va_list vl;
+  amqp_rpc_reply_t ret;
+
+  va_start(vl, sasl_method);
+
+  ret = amqp_login_inner(state, vhost, channel_max, frame_max, heartbeat,
+                         client_properties, state->handshake_timeout,
+                         sasl_method, vl);
+
+  va_end(vl);
+
+  return ret;
+}

+ 188 - 0
ext/librabbitmq/librabbitmq/amqp_socket.h

@@ -0,0 +1,188 @@
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael
+ * Steinert. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * An abstract socket interface.
+ */
+
+#ifndef AMQP_SOCKET_H
+#define AMQP_SOCKET_H
+
+#include "amqp_private.h"
+#include "amqp_time.h"
+
+AMQP_BEGIN_DECLS
+
+typedef enum {
+  AMQP_SF_NONE = 0,
+  AMQP_SF_MORE = 1,
+  AMQP_SF_POLLIN = 2,
+  AMQP_SF_POLLOUT = 4,
+  AMQP_SF_POLLERR = 8
+} amqp_socket_flag_enum;
+
+typedef enum { AMQP_SC_NONE = 0, AMQP_SC_FORCE = 1 } amqp_socket_close_enum;
+
+int amqp_os_socket_error(void);
+
+int amqp_os_socket_close(int sockfd);
+
+/* Socket callbacks. */
+typedef ssize_t (*amqp_socket_send_fn)(void *, const void *, size_t, int);
+typedef ssize_t (*amqp_socket_recv_fn)(void *, void *, size_t, int);
+typedef int (*amqp_socket_open_fn)(void *, const char *, int, struct timeval *);
+typedef int (*amqp_socket_close_fn)(void *, amqp_socket_close_enum);
+typedef int (*amqp_socket_get_sockfd_fn)(void *);
+typedef void (*amqp_socket_delete_fn)(void *);
+
+/** V-table for amqp_socket_t */
+struct amqp_socket_class_t {
+  amqp_socket_send_fn send;
+  amqp_socket_recv_fn recv;
+  amqp_socket_open_fn open;
+  amqp_socket_close_fn close;
+  amqp_socket_get_sockfd_fn get_sockfd;
+  amqp_socket_delete_fn delete;
+};
+
+/** Abstract base class for amqp_socket_t */
+struct amqp_socket_t_ {
+  const struct amqp_socket_class_t *klass;
+};
+
+/**
+ * Set set the socket object for a connection
+ *
+ * This assigns a socket object to the connection, closing and deleting any
+ * existing socket
+ *
+ * \param [in] state The connection object to add the socket to
+ * \param [in] socket The socket object to assign to the connection
+ */
+void amqp_set_socket(amqp_connection_state_t state, amqp_socket_t *socket);
+
+/**
+ * Send a message from a socket.
+ *
+ * This function wraps send(2) functionality.
+ *
+ * This function will only return on error, or when all of the bytes in buf
+ * have been sent, or when an error occurs.
+ *
+ * \param [in,out] self A socket object.
+ * \param [in] buf A buffer to read from.
+ * \param [in] len The number of bytes in \e buf.
+ * \param [in]
+ *
+ * \return AMQP_STATUS_OK on success. amqp_status_enum value otherwise
+ */
+ssize_t amqp_socket_send(amqp_socket_t *self, const void *buf, size_t len,
+                         int flags);
+
+ssize_t amqp_try_send(amqp_connection_state_t state, const void *buf,
+                      size_t len, amqp_time_t deadline, int flags);
+
+/**
+ * Receive a message from a socket.
+ *
+ * This function wraps recv(2) functionality.
+ *
+ * \param [in,out] self A socket object.
+ * \param [out] buf A buffer to write to.
+ * \param [in] len The number of bytes at \e buf.
+ * \param [in] flags Receive flags, implementation specific.
+ *
+ * \return The number of bytes received, or < 0 on error (\ref amqp_status_enum)
+ */
+ssize_t amqp_socket_recv(amqp_socket_t *self, void *buf, size_t len, int flags);
+
+/**
+ * Close a socket connection and free resources.
+ *
+ * This function closes a socket connection and releases any resources used by
+ * the object. After calling this function the specified socket should no
+ * longer be referenced.
+ *
+ * \param [in,out] self A socket object.
+ * \param [in] force, if set, just close the socket, don't attempt a TLS
+ * shutdown.
+ *
+ * \return Zero upon success, non-zero otherwise.
+ */
+int amqp_socket_close(amqp_socket_t *self, amqp_socket_close_enum force);
+
+/**
+ * Destroy a socket object
+ *
+ * \param [in] self the socket object to delete
+ */
+void amqp_socket_delete(amqp_socket_t *self);
+
+/**
+ * Open a socket connection.
+ *
+ * This function opens a socket connection returned from amqp_tcp_socket_new()
+ * or amqp_ssl_socket_new(). This function should be called after setting
+ * socket options and prior to assigning the socket to an AMQP connection with
+ * amqp_set_socket().
+ *
+ * \param [in] host Connect to this host.
+ * \param [in] port Connect on this remote port.
+ * \param [in] timeout Max allowed time to spent on opening. If NULL - run in
+ * blocking mode
+ *
+ * \return File descriptor upon success, non-zero negative error code otherwise.
+ */
+int amqp_open_socket_noblock(char const *hostname, int portnumber,
+                             struct timeval *timeout);
+
+int amqp_open_socket_inner(char const *hostname, int portnumber,
+                           amqp_time_t deadline);
+
+/* Wait up to dealline for fd to become readable or writeable depending on
+ * event (AMQP_SF_POLLIN, AMQP_SF_POLLOUT) */
+int amqp_poll(int fd, int event, amqp_time_t deadline);
+
+int amqp_send_method_inner(amqp_connection_state_t state,
+                           amqp_channel_t channel, amqp_method_number_t id,
+                           void *decoded, int flags, amqp_time_t deadline);
+
+int amqp_queue_frame(amqp_connection_state_t state, amqp_frame_t *frame);
+
+int amqp_put_back_frame(amqp_connection_state_t state, amqp_frame_t *frame);
+
+int amqp_simple_wait_frame_on_channel(amqp_connection_state_t state,
+                                      amqp_channel_t channel,
+                                      amqp_frame_t *decoded_frame);
+
+int sasl_mechanism_in_list(amqp_bytes_t mechanisms,
+                           amqp_sasl_method_enum method);
+
+int amqp_merge_capabilities(const amqp_table_t *base, const amqp_table_t *add,
+                            amqp_table_t *result, amqp_pool_t *pool);
+AMQP_END_DECLS
+
+#endif /* AMQP_SOCKET_H */

+ 239 - 0
ext/librabbitmq/librabbitmq/amqp_ssl_socket.h

@@ -0,0 +1,239 @@
+/** \file */
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael
+ * Steinert. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef AMQP_SSL_H
+#define AMQP_SSL_H
+
+#include <amqp.h>
+
+AMQP_BEGIN_DECLS
+
+/**
+ * Create a new SSL/TLS socket object.
+ *
+ * The returned socket object is owned by the \ref amqp_connection_state_t
+ * object and will be destroyed when the state object is destroyed or a new
+ * socket object is created.
+ *
+ * If the socket object creation fails, the \ref amqp_connection_state_t object
+ * will not be changed.
+ *
+ * The object returned by this function can be retrieved from the
+ * amqp_connection_state_t object later using the amqp_get_socket() function.
+ *
+ * Calling this function may result in the underlying SSL library being
+ * initialized.
+ * \sa amqp_set_initialize_ssl_library()
+ *
+ * \param [in,out] state The connection object that owns the SSL/TLS socket
+ * \return A new socket object or NULL if an error occurred.
+ *
+ * \since v0.4.0
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_socket_t *AMQP_CALL amqp_ssl_socket_new(amqp_connection_state_t state);
+
+/**
+ * Set the CA certificate.
+ *
+ * \param [in,out] self An SSL/TLS socket object.
+ * \param [in] cacert Path to the CA cert file in PEM format.
+ *
+ * \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
+ *  failure.
+ *
+ * \since v0.4.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_ssl_socket_set_cacert(amqp_socket_t *self,
+                                         const char *cacert);
+
+/**
+ * Set the client key.
+ *
+ * \param [in,out] self An SSL/TLS socket object.
+ * \param [in] cert Path to the client certificate in PEM foramt.
+ * \param [in] key Path to the client key in PEM format.
+ *
+ * \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
+ *  failure.
+ *
+ * \since v0.4.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_ssl_socket_set_key(amqp_socket_t *self, const char *cert,
+                                      const char *key);
+
+/**
+ * Set the client key from a buffer.
+ *
+ * \param [in,out] self An SSL/TLS socket object.
+ * \param [in] cert Path to the client certificate in PEM foramt.
+ * \param [in] key A buffer containing client key in PEM format.
+ * \param [in] n The length of the buffer.
+ *
+ * \return \ref AMQP_STATUS_OK on success an \ref amqp_status_enum value on
+ *  failure.
+ *
+ * \since v0.4.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_ssl_socket_set_key_buffer(amqp_socket_t *self,
+                                             const char *cert, const void *key,
+                                             size_t n);
+
+/**
+ * Enable or disable peer verification.
+ *
+ * \deprecated use \amqp_ssl_socket_set_verify_peer and
+ * \amqp_ssl_socket_set_verify_hostname instead.
+ *
+ * If peer verification is enabled then the common name in the server
+ * certificate must match the server name. Peer verification is enabled by
+ * default.
+ *
+ * \param [in,out] self An SSL/TLS socket object.
+ * \param [in] verify Enable or disable peer verification.
+ *
+ * \since v0.4.0
+ */
+AMQP_DEPRECATED(AMQP_PUBLIC_FUNCTION void AMQP_CALL amqp_ssl_socket_set_verify(
+    amqp_socket_t *self, amqp_boolean_t verify));
+
+/**
+ * Enable or disable peer verification.
+ *
+ * Peer verification validates the certificate chain that is sent by the broker.
+ * Hostname validation is controlled by \amqp_ssl_socket_set_verify_peer.
+ *
+ * \param [in,out] self An SSL/TLS socket object.
+ * \param [in] verify enable or disable peer validation
+ *
+ * \since v0.8.0
+ */
+AMQP_PUBLIC_FUNCTION
+void AMQP_CALL amqp_ssl_socket_set_verify_peer(amqp_socket_t *self,
+                                               amqp_boolean_t verify);
+
+/**
+ * Enable or disable hostname verification.
+ *
+ * Hostname verification checks the broker cert for a CN or SAN that matches the
+ * hostname that amqp_socket_open() is presented. Peer verification is
+ * controlled by \amqp_ssl_socket_set_verify_peer
+ *
+ * \since v0.8.0
+ */
+AMQP_PUBLIC_FUNCTION
+void AMQP_CALL amqp_ssl_socket_set_verify_hostname(amqp_socket_t *self,
+                                                   amqp_boolean_t verify);
+
+typedef enum {
+  AMQP_TLSv1 = 1,
+  AMQP_TLSv1_1 = 2,
+  AMQP_TLSv1_2 = 3,
+  AMQP_TLSvLATEST = 0xFFFF
+} amqp_tls_version_t;
+
+/**
+ * Set min and max TLS versions.
+ *
+ * Set the oldest and newest acceptable TLS versions that are acceptable when
+ * connecting to the broker. Set min == max to restrict to just that
+ * version.
+ *
+ * \param [in,out] self An SSL/TLS socket object.
+ * \param [in] min the minimum acceptable TLS version
+ * \param [in] max the maxmium acceptable TLS version
+ * \returns AMQP_STATUS_OK on success, AMQP_STATUS_UNSUPPORTED if OpenSSL does
+ * not support the requested TLS version, AMQP_STATUS_INVALID_PARAMETER if an
+ * invalid combination of parameters is passed.
+ *
+ * \since v0.8.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_ssl_socket_set_ssl_versions(amqp_socket_t *self,
+                                               amqp_tls_version_t min,
+                                               amqp_tls_version_t max);
+
+/**
+ * Sets whether rabbitmq-c will initialize OpenSSL.
+ *
+ * OpenSSL requires a one-time initialization across a whole program, this sets
+ * whether or not rabbitmq-c will initialize the SSL library when the first call
+ * to amqp_ssl_socket_new() is made. You should call this function with
+ * do_init = 0 if the underlying SSL library is initialized somewhere else
+ * the program.
+ *
+ * Failing to initialize or double initialization of the SSL library will
+ * result in undefined behavior
+ *
+ * By default rabbitmq-c will initialize the underlying SSL library.
+ *
+ * NOTE: calling this function after the first socket has been opened with
+ * amqp_open_socket() will not have any effect.
+ *
+ * \param [in] do_initialize If 0 rabbitmq-c will not initialize the SSL
+ *                           library, otherwise rabbitmq-c will initialize the
+ *                           SSL library
+ *
+ * \since v0.4.0
+ */
+AMQP_PUBLIC_FUNCTION
+void AMQP_CALL amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize);
+
+/**
+ * Initialize the underlying SSL/TLS library.
+ *
+ * The OpenSSL library requires a one-time initialization across the whole
+ * program.
+ *
+ * This function unconditionally initializes OpenSSL so that rabbitmq-c may
+ * use it.
+ *
+ * This function is thread-safe, and may be called more than once.
+ *
+ * \return AMQP_STATUS_OK on success.
+ *
+ * \since v0.9.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_initialize_ssl_library(void);
+
+/**
+ * Uninitialize the underlying SSL/TLS library.
+ *
+ * \return AMQP_STATUS_OK on success.
+ *
+ * \since v0.9.0
+ */
+AMQP_PUBLIC_FUNCTION
+int AMQP_CALL amqp_uninitialize_ssl_library(void);
+
+AMQP_END_DECLS
+
+#endif /* AMQP_SSL_H */

+ 668 - 0
ext/librabbitmq/librabbitmq/amqp_table.c

@@ -0,0 +1,668 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp_private.h"
+#include "amqp_table.h"
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define INITIAL_ARRAY_SIZE 16
+#define INITIAL_TABLE_SIZE 16
+
+static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
+                                   amqp_field_value_t *entry, size_t *offset);
+
+static int amqp_encode_field_value(amqp_bytes_t encoded,
+                                   amqp_field_value_t *entry, size_t *offset);
+
+/*---------------------------------------------------------------------------*/
+
+static int amqp_decode_array(amqp_bytes_t encoded, amqp_pool_t *pool,
+                             amqp_array_t *output, size_t *offset) {
+  uint32_t arraysize;
+  int num_entries = 0;
+  int allocated_entries = INITIAL_ARRAY_SIZE;
+  amqp_field_value_t *entries;
+  size_t limit;
+  int res;
+
+  if (!amqp_decode_32(encoded, offset, &arraysize)) {
+    return AMQP_STATUS_BAD_AMQP_DATA;
+  }
+
+  if (arraysize + *offset > encoded.len) {
+    return AMQP_STATUS_BAD_AMQP_DATA;
+  }
+
+  entries = malloc(allocated_entries * sizeof(amqp_field_value_t));
+  if (entries == NULL) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  limit = *offset + arraysize;
+  while (*offset < limit) {
+    if (num_entries >= allocated_entries) {
+      void *newentries;
+      allocated_entries = allocated_entries * 2;
+      newentries =
+          realloc(entries, allocated_entries * sizeof(amqp_field_value_t));
+      res = AMQP_STATUS_NO_MEMORY;
+      if (newentries == NULL) {
+        goto out;
+      }
+
+      entries = newentries;
+    }
+
+    res = amqp_decode_field_value(encoded, pool, &entries[num_entries], offset);
+    if (res < 0) {
+      goto out;
+    }
+
+    num_entries++;
+  }
+
+  output->num_entries = num_entries;
+  output->entries =
+      amqp_pool_alloc(pool, num_entries * sizeof(amqp_field_value_t));
+  /* NULL is legitimate if we requested a zero-length block. */
+  if (output->entries == NULL) {
+    if (num_entries == 0) {
+      res = AMQP_STATUS_OK;
+    } else {
+      res = AMQP_STATUS_NO_MEMORY;
+    }
+    goto out;
+  }
+
+  memcpy(output->entries, entries, num_entries * sizeof(amqp_field_value_t));
+  res = AMQP_STATUS_OK;
+
+out:
+  free(entries);
+  return res;
+}
+
+int amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool,
+                      amqp_table_t *output, size_t *offset) {
+  uint32_t tablesize;
+  int num_entries = 0;
+  amqp_table_entry_t *entries;
+  int allocated_entries = INITIAL_TABLE_SIZE;
+  size_t limit;
+  int res;
+
+  if (!amqp_decode_32(encoded, offset, &tablesize)) {
+    return AMQP_STATUS_BAD_AMQP_DATA;
+  }
+
+  if (tablesize + *offset > encoded.len) {
+    return AMQP_STATUS_BAD_AMQP_DATA;
+  }
+
+  entries = malloc(allocated_entries * sizeof(amqp_table_entry_t));
+  if (entries == NULL) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  limit = *offset + tablesize;
+  while (*offset < limit) {
+    uint8_t keylen;
+
+    res = AMQP_STATUS_BAD_AMQP_DATA;
+    if (!amqp_decode_8(encoded, offset, &keylen)) {
+      goto out;
+    }
+
+    if (num_entries >= allocated_entries) {
+      void *newentries;
+      allocated_entries = allocated_entries * 2;
+      newentries =
+          realloc(entries, allocated_entries * sizeof(amqp_table_entry_t));
+      res = AMQP_STATUS_NO_MEMORY;
+      if (newentries == NULL) {
+        goto out;
+      }
+
+      entries = newentries;
+    }
+
+    res = AMQP_STATUS_BAD_AMQP_DATA;
+    if (!amqp_decode_bytes(encoded, offset, &entries[num_entries].key,
+                           keylen)) {
+      goto out;
+    }
+
+    res = amqp_decode_field_value(encoded, pool, &entries[num_entries].value,
+                                  offset);
+    if (res < 0) {
+      goto out;
+    }
+
+    num_entries++;
+  }
+
+  output->num_entries = num_entries;
+  output->entries =
+      amqp_pool_alloc(pool, num_entries * sizeof(amqp_table_entry_t));
+  /* NULL is legitimate if we requested a zero-length block. */
+  if (output->entries == NULL) {
+    if (num_entries == 0) {
+      res = AMQP_STATUS_OK;
+    } else {
+      res = AMQP_STATUS_NO_MEMORY;
+    }
+    goto out;
+  }
+
+  memcpy(output->entries, entries, num_entries * sizeof(amqp_table_entry_t));
+  res = AMQP_STATUS_OK;
+
+out:
+  free(entries);
+  return res;
+}
+
+static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
+                                   amqp_field_value_t *entry, size_t *offset) {
+  int res = AMQP_STATUS_BAD_AMQP_DATA;
+
+  if (!amqp_decode_8(encoded, offset, &entry->kind)) {
+    goto out;
+  }
+
+#define TRIVIAL_FIELD_DECODER(bits)                                          \
+  if (!amqp_decode_##bits(encoded, offset, &entry->value.u##bits)) goto out; \
+  break
+#define SIMPLE_FIELD_DECODER(bits, dest, how)                 \
+  {                                                           \
+    uint##bits##_t val;                                       \
+    if (!amqp_decode_##bits(encoded, offset, &val)) goto out; \
+    entry->value.dest = how;                                  \
+  }                                                           \
+  break
+
+  switch (entry->kind) {
+    case AMQP_FIELD_KIND_BOOLEAN:
+      SIMPLE_FIELD_DECODER(8, boolean, val ? 1 : 0);
+
+    case AMQP_FIELD_KIND_I8:
+      SIMPLE_FIELD_DECODER(8, i8, (int8_t)val);
+    case AMQP_FIELD_KIND_U8:
+      TRIVIAL_FIELD_DECODER(8);
+
+    case AMQP_FIELD_KIND_I16:
+      SIMPLE_FIELD_DECODER(16, i16, (int16_t)val);
+    case AMQP_FIELD_KIND_U16:
+      TRIVIAL_FIELD_DECODER(16);
+
+    case AMQP_FIELD_KIND_I32:
+      SIMPLE_FIELD_DECODER(32, i32, (int32_t)val);
+    case AMQP_FIELD_KIND_U32:
+      TRIVIAL_FIELD_DECODER(32);
+
+    case AMQP_FIELD_KIND_I64:
+      SIMPLE_FIELD_DECODER(64, i64, (int64_t)val);
+    case AMQP_FIELD_KIND_U64:
+      TRIVIAL_FIELD_DECODER(64);
+
+    case AMQP_FIELD_KIND_F32:
+      TRIVIAL_FIELD_DECODER(32);
+    /* and by punning, f32 magically gets the right value...! */
+
+    case AMQP_FIELD_KIND_F64:
+      TRIVIAL_FIELD_DECODER(64);
+    /* and by punning, f64 magically gets the right value...! */
+
+    case AMQP_FIELD_KIND_DECIMAL:
+      if (!amqp_decode_8(encoded, offset, &entry->value.decimal.decimals) ||
+          !amqp_decode_32(encoded, offset, &entry->value.decimal.value)) {
+        goto out;
+      }
+      break;
+
+    case AMQP_FIELD_KIND_UTF8:
+    /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
+       same implementation, but different interpretations. */
+    /* fall through */
+    case AMQP_FIELD_KIND_BYTES: {
+      uint32_t len;
+      if (!amqp_decode_32(encoded, offset, &len) ||
+          !amqp_decode_bytes(encoded, offset, &entry->value.bytes, len)) {
+        goto out;
+      }
+      break;
+    }
+
+    case AMQP_FIELD_KIND_ARRAY:
+      res = amqp_decode_array(encoded, pool, &(entry->value.array), offset);
+      goto out;
+
+    case AMQP_FIELD_KIND_TIMESTAMP:
+      TRIVIAL_FIELD_DECODER(64);
+
+    case AMQP_FIELD_KIND_TABLE:
+      res = amqp_decode_table(encoded, pool, &(entry->value.table), offset);
+      goto out;
+
+    case AMQP_FIELD_KIND_VOID:
+      break;
+
+    default:
+      goto out;
+  }
+
+  res = AMQP_STATUS_OK;
+
+out:
+  return res;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int amqp_encode_array(amqp_bytes_t encoded, amqp_array_t *input,
+                             size_t *offset) {
+  size_t start = *offset;
+  int i, res;
+
+  *offset += 4; /* size of the array gets filled in later on */
+
+  for (i = 0; i < input->num_entries; i++) {
+    res = amqp_encode_field_value(encoded, &input->entries[i], offset);
+    if (res < 0) {
+      goto out;
+    }
+  }
+
+  if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
+    res = AMQP_STATUS_TABLE_TOO_BIG;
+    goto out;
+  }
+
+  res = AMQP_STATUS_OK;
+
+out:
+  return res;
+}
+
+int amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input,
+                      size_t *offset) {
+  size_t start = *offset;
+  int i, res;
+
+  *offset += 4; /* size of the table gets filled in later on */
+
+  for (i = 0; i < input->num_entries; i++) {
+    if (!amqp_encode_8(encoded, offset, (uint8_t)input->entries[i].key.len)) {
+      res = AMQP_STATUS_TABLE_TOO_BIG;
+      goto out;
+    }
+
+    if (!amqp_encode_bytes(encoded, offset, input->entries[i].key)) {
+      res = AMQP_STATUS_TABLE_TOO_BIG;
+      goto out;
+    }
+
+    res = amqp_encode_field_value(encoded, &input->entries[i].value, offset);
+    if (res < 0) {
+      goto out;
+    }
+  }
+
+  if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
+    res = AMQP_STATUS_TABLE_TOO_BIG;
+    goto out;
+  }
+
+  res = AMQP_STATUS_OK;
+
+out:
+  return res;
+}
+
+static int amqp_encode_field_value(amqp_bytes_t encoded,
+                                   amqp_field_value_t *entry, size_t *offset) {
+  int res = AMQP_STATUS_BAD_AMQP_DATA;
+
+  if (!amqp_encode_8(encoded, offset, entry->kind)) {
+    goto out;
+  }
+
+#define FIELD_ENCODER(bits, val)                   \
+  if (!amqp_encode_##bits(encoded, offset, val)) { \
+    res = AMQP_STATUS_TABLE_TOO_BIG;               \
+    goto out;                                      \
+  }                                                \
+  break
+
+  switch (entry->kind) {
+    case AMQP_FIELD_KIND_BOOLEAN:
+      FIELD_ENCODER(8, entry->value.boolean ? 1 : 0);
+
+    case AMQP_FIELD_KIND_I8:
+      FIELD_ENCODER(8, entry->value.i8);
+    case AMQP_FIELD_KIND_U8:
+      FIELD_ENCODER(8, entry->value.u8);
+
+    case AMQP_FIELD_KIND_I16:
+      FIELD_ENCODER(16, entry->value.i16);
+    case AMQP_FIELD_KIND_U16:
+      FIELD_ENCODER(16, entry->value.u16);
+
+    case AMQP_FIELD_KIND_I32:
+      FIELD_ENCODER(32, entry->value.i32);
+    case AMQP_FIELD_KIND_U32:
+      FIELD_ENCODER(32, entry->value.u32);
+
+    case AMQP_FIELD_KIND_I64:
+      FIELD_ENCODER(64, entry->value.i64);
+    case AMQP_FIELD_KIND_U64:
+      FIELD_ENCODER(64, entry->value.u64);
+
+    case AMQP_FIELD_KIND_F32:
+      /* by punning, u32 magically gets the right value...! */
+      FIELD_ENCODER(32, entry->value.u32);
+
+    case AMQP_FIELD_KIND_F64:
+      /* by punning, u64 magically gets the right value...! */
+      FIELD_ENCODER(64, entry->value.u64);
+
+    case AMQP_FIELD_KIND_DECIMAL:
+      if (!amqp_encode_8(encoded, offset, entry->value.decimal.decimals) ||
+          !amqp_encode_32(encoded, offset, entry->value.decimal.value)) {
+        res = AMQP_STATUS_TABLE_TOO_BIG;
+        goto out;
+      }
+      break;
+
+    case AMQP_FIELD_KIND_UTF8:
+    /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
+       same implementation, but different interpretations. */
+    /* fall through */
+    case AMQP_FIELD_KIND_BYTES:
+      if (!amqp_encode_32(encoded, offset, (uint32_t)entry->value.bytes.len) ||
+          !amqp_encode_bytes(encoded, offset, entry->value.bytes)) {
+        res = AMQP_STATUS_TABLE_TOO_BIG;
+        goto out;
+      }
+      break;
+
+    case AMQP_FIELD_KIND_ARRAY:
+      res = amqp_encode_array(encoded, &entry->value.array, offset);
+      goto out;
+
+    case AMQP_FIELD_KIND_TIMESTAMP:
+      FIELD_ENCODER(64, entry->value.u64);
+
+    case AMQP_FIELD_KIND_TABLE:
+      res = amqp_encode_table(encoded, &entry->value.table, offset);
+      goto out;
+
+    case AMQP_FIELD_KIND_VOID:
+      break;
+
+    default:
+      res = AMQP_STATUS_INVALID_PARAMETER;
+      goto out;
+  }
+
+  res = AMQP_STATUS_OK;
+
+out:
+  return res;
+}
+
+/*---------------------------------------------------------------------------*/
+
+int amqp_table_entry_cmp(void const *entry1, void const *entry2) {
+  amqp_table_entry_t const *p1 = (amqp_table_entry_t const *)entry1;
+  amqp_table_entry_t const *p2 = (amqp_table_entry_t const *)entry2;
+
+  int d;
+  size_t minlen;
+
+  minlen = p1->key.len;
+  if (p2->key.len < minlen) {
+    minlen = p2->key.len;
+  }
+
+  d = memcmp(p1->key.bytes, p2->key.bytes, minlen);
+  if (d != 0) {
+    return d;
+  }
+
+  return (int)p1->key.len - (int)p2->key.len;
+}
+
+static int amqp_field_value_clone(const amqp_field_value_t *original,
+                                  amqp_field_value_t *clone,
+                                  amqp_pool_t *pool) {
+  int i;
+  int res;
+  clone->kind = original->kind;
+
+  switch (clone->kind) {
+    case AMQP_FIELD_KIND_BOOLEAN:
+      clone->value.boolean = original->value.boolean;
+      break;
+
+    case AMQP_FIELD_KIND_I8:
+      clone->value.i8 = original->value.i8;
+      break;
+
+    case AMQP_FIELD_KIND_U8:
+      clone->value.u8 = original->value.u8;
+      break;
+
+    case AMQP_FIELD_KIND_I16:
+      clone->value.i16 = original->value.i16;
+      break;
+
+    case AMQP_FIELD_KIND_U16:
+      clone->value.u16 = original->value.u16;
+      break;
+
+    case AMQP_FIELD_KIND_I32:
+      clone->value.i32 = original->value.i32;
+      break;
+
+    case AMQP_FIELD_KIND_U32:
+      clone->value.u32 = original->value.u32;
+      break;
+
+    case AMQP_FIELD_KIND_I64:
+      clone->value.i64 = original->value.i64;
+      break;
+
+    case AMQP_FIELD_KIND_U64:
+    case AMQP_FIELD_KIND_TIMESTAMP:
+      clone->value.u64 = original->value.u64;
+      break;
+
+    case AMQP_FIELD_KIND_F32:
+      clone->value.f32 = original->value.f32;
+      break;
+
+    case AMQP_FIELD_KIND_F64:
+      clone->value.f64 = original->value.f64;
+      break;
+
+    case AMQP_FIELD_KIND_DECIMAL:
+      clone->value.decimal = original->value.decimal;
+      break;
+
+    case AMQP_FIELD_KIND_UTF8:
+    case AMQP_FIELD_KIND_BYTES:
+      if (0 == original->value.bytes.len) {
+        clone->value.bytes = amqp_empty_bytes;
+      } else {
+        amqp_pool_alloc_bytes(pool, original->value.bytes.len,
+                              &clone->value.bytes);
+        if (NULL == clone->value.bytes.bytes) {
+          return AMQP_STATUS_NO_MEMORY;
+        }
+        memcpy(clone->value.bytes.bytes, original->value.bytes.bytes,
+               clone->value.bytes.len);
+      }
+      break;
+
+    case AMQP_FIELD_KIND_ARRAY:
+      if (0 == original->value.array.entries) {
+        clone->value.array = amqp_empty_array;
+      } else {
+        clone->value.array.num_entries = original->value.array.num_entries;
+        clone->value.array.entries = amqp_pool_alloc(
+            pool, clone->value.array.num_entries * sizeof(amqp_field_value_t));
+        if (NULL == clone->value.array.entries) {
+          return AMQP_STATUS_NO_MEMORY;
+        }
+
+        for (i = 0; i < clone->value.array.num_entries; ++i) {
+          res = amqp_field_value_clone(&original->value.array.entries[i],
+                                       &clone->value.array.entries[i], pool);
+          if (AMQP_STATUS_OK != res) {
+            return res;
+          }
+        }
+      }
+      break;
+
+    case AMQP_FIELD_KIND_TABLE:
+      return amqp_table_clone(&original->value.table, &clone->value.table,
+                              pool);
+
+    case AMQP_FIELD_KIND_VOID:
+      break;
+
+    default:
+      return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  return AMQP_STATUS_OK;
+}
+
+static int amqp_table_entry_clone(const amqp_table_entry_t *original,
+                                  amqp_table_entry_t *clone,
+                                  amqp_pool_t *pool) {
+  if (0 == original->key.len) {
+    return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  amqp_pool_alloc_bytes(pool, original->key.len, &clone->key);
+  if (NULL == clone->key.bytes) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  memcpy(clone->key.bytes, original->key.bytes, clone->key.len);
+
+  return amqp_field_value_clone(&original->value, &clone->value, pool);
+}
+
+int amqp_table_clone(const amqp_table_t *original, amqp_table_t *clone,
+                     amqp_pool_t *pool) {
+  int i;
+  int res;
+  clone->num_entries = original->num_entries;
+  if (0 == clone->num_entries) {
+    *clone = amqp_empty_table;
+    return AMQP_STATUS_OK;
+  }
+
+  clone->entries =
+      amqp_pool_alloc(pool, clone->num_entries * sizeof(amqp_table_entry_t));
+
+  if (NULL == clone->entries) {
+    return AMQP_STATUS_NO_MEMORY;
+  }
+
+  for (i = 0; i < clone->num_entries; ++i) {
+    res =
+        amqp_table_entry_clone(&original->entries[i], &clone->entries[i], pool);
+    if (AMQP_STATUS_OK != res) {
+      goto error_out1;
+    }
+  }
+
+  return AMQP_STATUS_OK;
+
+error_out1:
+  return res;
+}
+
+amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key,
+                                                   const char *value) {
+  amqp_table_entry_t ret;
+  ret.key = amqp_cstring_bytes(key);
+  ret.value.kind = AMQP_FIELD_KIND_UTF8;
+  ret.value.value.bytes = amqp_cstring_bytes(value);
+  return ret;
+}
+
+amqp_table_entry_t amqp_table_construct_table_entry(const char *key,
+                                                    const amqp_table_t *value) {
+  amqp_table_entry_t ret;
+  ret.key = amqp_cstring_bytes(key);
+  ret.value.kind = AMQP_FIELD_KIND_TABLE;
+  ret.value.value.table = *value;
+  return ret;
+}
+
+amqp_table_entry_t amqp_table_construct_bool_entry(const char *key,
+                                                   const int value) {
+  amqp_table_entry_t ret;
+  ret.key = amqp_cstring_bytes(key);
+  ret.value.kind = AMQP_FIELD_KIND_BOOLEAN;
+  ret.value.value.boolean = value;
+  return ret;
+}
+
+amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table,
+                                                const amqp_bytes_t key) {
+  int i;
+  assert(table != NULL);
+  for (i = 0; i < table->num_entries; ++i) {
+    if (amqp_bytes_equal(table->entries[i].key, key)) {
+      return &table->entries[i];
+    }
+  }
+  return NULL;
+}

+ 81 - 0
ext/librabbitmq/librabbitmq/amqp_table.h

@@ -0,0 +1,81 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2014 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+#ifndef AMQP_TABLE_H
+#define AMQP_TABLE_H
+
+#include "amqp.h"
+#include "amqp_private.h"
+
+/**
+ * Initializes a table entry with utf-8 string type value.
+ *
+ * \param [in] key the table entry key. The string must remain valid for the
+ * life of the resulting amqp_table_entry_t.
+ * \param [in] value the string value. The string must remain valid for the life
+ * of the resulting amqp_table_entry_t.
+ * \returns An initialized table entry.
+ */
+amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key,
+                                                   const char *value);
+
+/**
+ * Initializes a table entry with table type value.
+ *
+ * \param [in] key the table entry key. The string must remain value for the
+ * life of the resulting amqp_table_entry_t.
+ * \param [in] value the amqp_table_t value. The table must remain valid for the
+ * life of the resulting amqp_table_entry_t.
+ * \returns An initialized table entry.
+ */
+amqp_table_entry_t amqp_table_construct_table_entry(const char *key,
+                                                    const amqp_table_t *value);
+
+/**
+ * Initializes a table entry with boolean type value.
+ *
+ * \param [in] key the table entry key. The string must remain value for the
+ * life of the resulting amqp_table_entry_t.
+ * \param [in] value the boolean value. 0 means false, any other value is true.
+ * \returns An initialized table entry.
+ */
+amqp_table_entry_t amqp_table_construct_bool_entry(const char *key,
+                                                   const int value);
+
+/**
+ * Searches a table for an entry with a matching key.
+ *
+ * \param [in] table the table to search.
+ * \param [in] key the string to search with.
+ * \returns a pointer to the table entry in the table if a matching key can be
+ * found, NULL otherwise.
+ */
+amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table,
+                                                const amqp_bytes_t key);
+
+#endif /* AMQP_TABLE_H */

+ 238 - 0
ext/librabbitmq/librabbitmq/amqp_tcp_socket.c

@@ -0,0 +1,238 @@
+/*
+ * Copyright 2012-2013 Michael Steinert
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp_private.h"
+#include "amqp_tcp_socket.h"
+
+#include <errno.h>
+#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+struct amqp_tcp_socket_t {
+  const struct amqp_socket_class_t *klass;
+  int sockfd;
+  int internal_error;
+  int state;
+};
+
+static ssize_t amqp_tcp_socket_send(void *base, const void *buf, size_t len,
+                                    int flags) {
+  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
+  ssize_t res;
+  int flagz = 0;
+
+  if (-1 == self->sockfd) {
+    return AMQP_STATUS_SOCKET_CLOSED;
+  }
+
+#ifdef MSG_NOSIGNAL
+  flagz |= MSG_NOSIGNAL;
+#endif
+
+#if defined(MSG_MORE)
+  if (flags & AMQP_SF_MORE) {
+    flagz |= MSG_MORE;
+  }
+/* Cygwin defines TCP_NOPUSH, but trying to use it will return not
+ * implemented. Disable it here. */
+#elif defined(TCP_NOPUSH) && !defined(__CYGWIN__)
+  if (flags & AMQP_SF_MORE && !(self->state & AMQP_SF_MORE)) {
+    int one = 1;
+    res = setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &one, sizeof(one));
+    if (0 != res) {
+      self->internal_error = res;
+      return AMQP_STATUS_SOCKET_ERROR;
+    }
+    self->state |= AMQP_SF_MORE;
+  } else if (!(flags & AMQP_SF_MORE) && self->state & AMQP_SF_MORE) {
+    int zero = 0;
+    res =
+        setsockopt(self->sockfd, IPPROTO_TCP, TCP_NOPUSH, &zero, sizeof(&zero));
+    if (0 != res) {
+      self->internal_error = res;
+      res = AMQP_STATUS_SOCKET_ERROR;
+    } else {
+      self->state &= ~AMQP_SF_MORE;
+    }
+  }
+#endif
+
+start:
+#ifdef _WIN32
+  res = send(self->sockfd, buf, (int)len, flagz);
+#else
+  res = send(self->sockfd, buf, len, flagz);
+#endif
+
+  if (res < 0) {
+    self->internal_error = amqp_os_socket_error();
+    switch (self->internal_error) {
+      case EINTR:
+        goto start;
+#ifdef _WIN32
+      case WSAEWOULDBLOCK:
+#else
+      case EWOULDBLOCK:
+#endif
+#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
+      case EAGAIN:
+#endif
+        res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
+        break;
+      default:
+        res = AMQP_STATUS_SOCKET_ERROR;
+    }
+  } else {
+    self->internal_error = 0;
+  }
+
+  return res;
+}
+
+static ssize_t amqp_tcp_socket_recv(void *base, void *buf, size_t len,
+                                    int flags) {
+  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
+  ssize_t ret;
+  if (-1 == self->sockfd) {
+    return AMQP_STATUS_SOCKET_CLOSED;
+  }
+
+start:
+#ifdef _WIN32
+  ret = recv(self->sockfd, buf, (int)len, flags);
+#else
+  ret = recv(self->sockfd, buf, len, flags);
+#endif
+
+  if (0 > ret) {
+    self->internal_error = amqp_os_socket_error();
+    switch (self->internal_error) {
+      case EINTR:
+        goto start;
+#ifdef _WIN32
+      case WSAEWOULDBLOCK:
+#else
+      case EWOULDBLOCK:
+#endif
+#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
+      case EAGAIN:
+#endif
+        ret = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
+        break;
+      default:
+        ret = AMQP_STATUS_SOCKET_ERROR;
+    }
+  } else if (0 == ret) {
+    ret = AMQP_STATUS_CONNECTION_CLOSED;
+  }
+
+  return ret;
+}
+
+static int amqp_tcp_socket_open(void *base, const char *host, int port,
+                                struct timeval *timeout) {
+  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
+  if (-1 != self->sockfd) {
+    return AMQP_STATUS_SOCKET_INUSE;
+  }
+  self->sockfd = amqp_open_socket_noblock(host, port, timeout);
+  if (0 > self->sockfd) {
+    int err = self->sockfd;
+    self->sockfd = -1;
+    return err;
+  }
+  return AMQP_STATUS_OK;
+}
+
+static int amqp_tcp_socket_close(void *base,
+                                 AMQP_UNUSED amqp_socket_close_enum force) {
+  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
+  if (-1 == self->sockfd) {
+    return AMQP_STATUS_SOCKET_CLOSED;
+  }
+
+  if (amqp_os_socket_close(self->sockfd)) {
+    return AMQP_STATUS_SOCKET_ERROR;
+  }
+  self->sockfd = -1;
+
+  return AMQP_STATUS_OK;
+}
+
+static int amqp_tcp_socket_get_sockfd(void *base) {
+  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
+  return self->sockfd;
+}
+
+static void amqp_tcp_socket_delete(void *base) {
+  struct amqp_tcp_socket_t *self = (struct amqp_tcp_socket_t *)base;
+
+  if (self) {
+    amqp_tcp_socket_close(self, AMQP_SC_NONE);
+    free(self);
+  }
+}
+
+static const struct amqp_socket_class_t amqp_tcp_socket_class = {
+    amqp_tcp_socket_send,       /* send */
+    amqp_tcp_socket_recv,       /* recv */
+    amqp_tcp_socket_open,       /* open */
+    amqp_tcp_socket_close,      /* close */
+    amqp_tcp_socket_get_sockfd, /* get_sockfd */
+    amqp_tcp_socket_delete      /* delete */
+};
+
+amqp_socket_t *amqp_tcp_socket_new(amqp_connection_state_t state) {
+  struct amqp_tcp_socket_t *self = calloc(1, sizeof(*self));
+  if (!self) {
+    return NULL;
+  }
+  self->klass = &amqp_tcp_socket_class;
+  self->sockfd = -1;
+
+  amqp_set_socket(state, (amqp_socket_t *)self);
+
+  return (amqp_socket_t *)self;
+}
+
+void amqp_tcp_socket_set_sockfd(amqp_socket_t *base, int sockfd) {
+  struct amqp_tcp_socket_t *self;
+  if (base->klass != &amqp_tcp_socket_class) {
+    amqp_abort("<%p> is not of type amqp_tcp_socket_t", base);
+  }
+  self = (struct amqp_tcp_socket_t *)base;
+  self->sockfd = sockfd;
+}

+ 0 - 0
ext/librabbitmq/centos_x64/include/amqp_tcp_socket.h → ext/librabbitmq/librabbitmq/amqp_tcp_socket.h


+ 265 - 0
ext/librabbitmq/librabbitmq/amqp_time.c

@@ -0,0 +1,265 @@
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "amqp_time.h"
+#include "amqp.h"
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || \
+     defined(__MINGW32__) || defined(__MINGW64__))
+#define AMQP_WIN_TIMER_API
+#elif (defined(machintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
+#define AMQP_MAC_TIMER_API
+#else
+#define AMQP_POSIX_TIMER_API
+#endif
+
+#ifdef AMQP_WIN_TIMER_API
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+uint64_t amqp_get_monotonic_timestamp(void) {
+  static double NS_PER_COUNT = 0;
+  LARGE_INTEGER perf_count;
+
+  if (0 == NS_PER_COUNT) {
+    LARGE_INTEGER perf_frequency;
+    if (!QueryPerformanceFrequency(&perf_frequency)) {
+      return 0;
+    }
+    NS_PER_COUNT = (double)AMQP_NS_PER_S / perf_frequency.QuadPart;
+  }
+
+  if (!QueryPerformanceCounter(&perf_count)) {
+    return 0;
+  }
+
+  return (uint64_t)(perf_count.QuadPart * NS_PER_COUNT);
+}
+#endif /* AMQP_WIN_TIMER_API */
+
+#ifdef AMQP_MAC_TIMER_API
+#include <mach/mach_time.h>
+
+uint64_t amqp_get_monotonic_timestamp(void) {
+  static mach_timebase_info_data_t s_timebase = {0, 0};
+  uint64_t timestamp;
+
+  timestamp = mach_absolute_time();
+
+  if (s_timebase.denom == 0) {
+    mach_timebase_info(&s_timebase);
+    if (0 == s_timebase.denom) {
+      return 0;
+    }
+  }
+
+  timestamp *= (uint64_t)s_timebase.numer;
+  timestamp /= (uint64_t)s_timebase.denom;
+
+  return timestamp;
+}
+#endif /* AMQP_MAC_TIMER_API */
+
+#ifdef AMQP_POSIX_TIMER_API
+#include <time.h>
+
+uint64_t amqp_get_monotonic_timestamp(void) {
+#ifdef __hpux
+  return (uint64_t)gethrtime();
+#else
+  struct timespec tp;
+  if (-1 == clock_gettime(CLOCK_MONOTONIC, &tp)) {
+    return 0;
+  }
+
+  return ((uint64_t)tp.tv_sec * AMQP_NS_PER_S + (uint64_t)tp.tv_nsec);
+#endif
+}
+#endif /* AMQP_POSIX_TIMER_API */
+
+int amqp_time_from_now(amqp_time_t *time, struct timeval *timeout) {
+  uint64_t now_ns;
+  uint64_t delta_ns;
+
+  assert(NULL != time);
+
+  if (NULL == timeout) {
+    *time = amqp_time_infinite();
+    return AMQP_STATUS_OK;
+  }
+  if (0 == timeout->tv_sec && 0 == timeout->tv_usec) {
+    *time = amqp_time_immediate();
+    return AMQP_STATUS_OK;
+  }
+
+  if (timeout->tv_sec < 0 || timeout->tv_usec < 0) {
+    return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  delta_ns = (uint64_t)timeout->tv_sec * AMQP_NS_PER_S +
+             (uint64_t)timeout->tv_usec * AMQP_NS_PER_US;
+
+  now_ns = amqp_get_monotonic_timestamp();
+  if (0 == now_ns) {
+    return AMQP_STATUS_TIMER_FAILURE;
+  }
+
+  time->time_point_ns = now_ns + delta_ns;
+  if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) {
+    return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  return AMQP_STATUS_OK;
+}
+
+int amqp_time_s_from_now(amqp_time_t *time, int seconds) {
+  uint64_t now_ns;
+  uint64_t delta_ns;
+  assert(NULL != time);
+
+  if (0 >= seconds) {
+    *time = amqp_time_infinite();
+    return AMQP_STATUS_OK;
+  }
+
+  now_ns = amqp_get_monotonic_timestamp();
+  if (0 == now_ns) {
+    return AMQP_STATUS_TIMER_FAILURE;
+  }
+
+  delta_ns = (uint64_t)seconds * AMQP_NS_PER_S;
+  time->time_point_ns = now_ns + delta_ns;
+  if (now_ns > time->time_point_ns || delta_ns > time->time_point_ns) {
+    return AMQP_STATUS_INVALID_PARAMETER;
+  }
+
+  return AMQP_STATUS_OK;
+}
+
+amqp_time_t amqp_time_immediate(void) {
+  amqp_time_t time;
+  time.time_point_ns = 0;
+  return time;
+}
+
+amqp_time_t amqp_time_infinite(void) {
+  amqp_time_t time;
+  time.time_point_ns = UINT64_MAX;
+  return time;
+}
+
+int amqp_time_ms_until(amqp_time_t time) {
+  uint64_t now_ns;
+  uint64_t delta_ns;
+  int left_ms;
+
+  if (UINT64_MAX == time.time_point_ns) {
+    return -1;
+  }
+  if (0 == time.time_point_ns) {
+    return 0;
+  }
+
+  now_ns = amqp_get_monotonic_timestamp();
+  if (0 == now_ns) {
+    return AMQP_STATUS_TIMER_FAILURE;
+  }
+
+  if (now_ns >= time.time_point_ns) {
+    return 0;
+  }
+
+  delta_ns = time.time_point_ns - now_ns;
+  left_ms = (int)(delta_ns / AMQP_NS_PER_MS);
+
+  return left_ms;
+}
+
+int amqp_time_tv_until(amqp_time_t time, struct timeval *in,
+                       struct timeval **out) {
+  uint64_t now_ns;
+  uint64_t delta_ns;
+
+  assert(in != NULL);
+  if (UINT64_MAX == time.time_point_ns) {
+    *out = NULL;
+    return AMQP_STATUS_OK;
+  }
+  if (0 == time.time_point_ns) {
+    in->tv_sec = 0;
+    in->tv_usec = 0;
+    *out = in;
+    return AMQP_STATUS_OK;
+  }
+
+  now_ns = amqp_get_monotonic_timestamp();
+  if (0 == now_ns) {
+    return AMQP_STATUS_TIMER_FAILURE;
+  }
+
+  if (now_ns >= time.time_point_ns) {
+    in->tv_sec = 0;
+    in->tv_usec = 0;
+    *out = in;
+    return AMQP_STATUS_OK;
+  }
+
+  delta_ns = time.time_point_ns - now_ns;
+  in->tv_sec = (int)(delta_ns / AMQP_NS_PER_S);
+  in->tv_usec = (int)((delta_ns % AMQP_NS_PER_S) / AMQP_NS_PER_US);
+  *out = in;
+
+  return AMQP_STATUS_OK;
+}
+
+int amqp_time_has_past(amqp_time_t time) {
+  uint64_t now_ns;
+  if (UINT64_MAX == time.time_point_ns) {
+    return AMQP_STATUS_OK;
+  }
+
+  now_ns = amqp_get_monotonic_timestamp();
+  if (0 == now_ns) {
+    return AMQP_STATUS_TIMER_FAILURE;
+  }
+
+  if (now_ns > time.time_point_ns) {
+    return AMQP_STATUS_TIMEOUT;
+  }
+  return AMQP_STATUS_OK;
+}
+
+amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r) {
+  if (l.time_point_ns < r.time_point_ns) {
+    return l;
+  }
+  return r;
+}
+
+int amqp_time_equal(amqp_time_t l, amqp_time_t r) {
+  return l.time_point_ns == r.time_point_ns;
+}

+ 130 - 0
ext/librabbitmq/librabbitmq/amqp_time.h

@@ -0,0 +1,130 @@
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef AMQP_TIMER_H
+#define AMQP_TIMER_H
+
+#include <stdint.h>
+
+#if ((defined(_WIN32)) || (defined(__MINGW32__)) || (defined(__MINGW64__)))
+#ifndef WINVER
+#define WINVER 0x0502
+#endif
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <winsock2.h>
+#else
+#include <sys/time.h>
+#endif
+
+#define AMQP_MS_PER_S 1000
+#define AMQP_US_PER_MS 1000
+#define AMQP_NS_PER_S 1000000000
+#define AMQP_NS_PER_MS 1000000
+#define AMQP_NS_PER_US 1000
+
+/* This represents a point in time in reference to a monotonic clock.
+ *
+ * The internal representation is ns, relative to the monotonic clock.
+ *
+ * There are two 'special' values:
+ * - 0: means 'this instant', its meant for polls with a 0-timeout, or
+ *   non-blocking option
+ * - UINT64_MAX: means 'at infinity', its mean for polls with an infinite
+ *   timeout
+ */
+typedef struct amqp_time_t_ { uint64_t time_point_ns; } amqp_time_t;
+
+/* Gets a monotonic timestamp. This will return 0 if the underlying call to the
+ * system fails.
+ */
+uint64_t amqp_get_monotonic_timestamp(void);
+
+/* Get a amqp_time_t that is timeout from now.
+ * If timeout is NULL, an amqp_time_infinite() is created.
+ * If timeout = {0, 0}, an amqp_time_immediate() is created.
+ *
+ * Returns AMQP_STATUS_OK on success.
+ * AMQP_STATUS_INVALID_PARAMETER if timeout is invalid
+ * AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp
+ * fails.
+ */
+int amqp_time_from_now(amqp_time_t *time, struct timeval *timeout);
+
+/* Get a amqp_time_t that is seconds from now.
+ * If seconds <= 0, then amqp_time_infinite() is created.
+ *
+ * Returns AMQP_STATUS_OK on success.
+ * AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current timestamp
+ * fails.
+ */
+int amqp_time_s_from_now(amqp_time_t *time, int seconds);
+
+/* Create an immediate amqp_time_t */
+amqp_time_t amqp_time_immediate(void);
+
+/* Create an infinite amqp_time_t */
+amqp_time_t amqp_time_infinite(void);
+
+/* Gets the number of ms until the amqp_time_t, suitable for the timeout
+ * parameter in poll().
+ *
+ * -1 will be returned for amqp_time_infinite values.
+ * 0 will be returned for amqp_time_immediate values.
+ * AMQP_STATUS_TIMEOUT will be returned if time was in the past.
+ * AMQP_STATUS_TIMER_FAILURE will be returned if the underlying call to get the
+ * current timestamp fails.
+ */
+int amqp_time_ms_until(amqp_time_t time);
+
+/* Gets a timeval filled in with the time until amqp_time_t. Suitable for the
+ * parameter in select().
+ *
+ * The in parameter specifies a storage location for *out.
+ * If time is an inf timeout, then *out = NULL.
+ * If time is a 0-timeout or the timer has expired, then *out = {0, 0}
+ * Otherwise *out is set to the time left on the time.
+ *
+ * AMQP_STATUS_OK will be returned if successfully filled.
+ * AMQP_STATUS_TIMER_FAILURE is returned when the underlying call to get the
+ * current timestamp fails.
+ */
+int amqp_time_tv_until(amqp_time_t time, struct timeval *in,
+                       struct timeval **out);
+
+/* Test whether current time is past the provided time.
+ *
+ * TODO: this isn't a great interface to use. Fix this.
+ *
+ * Return AMQP_STATUS_OK if time has not past
+ * Return AMQP_STATUS_TIMEOUT if time has past
+ * Return AMQP_STATUS_TIMER_FAILURE if the underlying call to get the current
+ * timestamp fails.
+ */
+int amqp_time_has_past(amqp_time_t time);
+
+/* Return the time value that happens first */
+amqp_time_t amqp_time_first(amqp_time_t l, amqp_time_t r);
+
+int amqp_time_equal(amqp_time_t l, amqp_time_t r);
+#endif /* AMQP_TIMER_H */

+ 220 - 0
ext/librabbitmq/librabbitmq/amqp_url.c

@@ -0,0 +1,220 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "amqp_private.h"
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void amqp_default_connection_info(struct amqp_connection_info *ci) {
+  /* Apply defaults */
+  ci->user = "guest";
+  ci->password = "guest";
+  ci->host = "localhost";
+  ci->port = 5672;
+  ci->vhost = "/";
+  ci->ssl = 0;
+}
+
+/* Scan for the next delimiter, handling percent-encodings on the way. */
+static char find_delim(char **pp, int colon_and_at_sign_are_delims) {
+  char *from = *pp;
+  char *to = from;
+
+  for (;;) {
+    char ch = *from++;
+
+    switch (ch) {
+      case ':':
+      case '@':
+        if (!colon_and_at_sign_are_delims) {
+          *to++ = ch;
+          break;
+        }
+
+      /* fall through */
+      case 0:
+      case '/':
+      case '?':
+      case '#':
+      case '[':
+      case ']':
+        *to = 0;
+        *pp = from;
+        return ch;
+
+      case '%': {
+        unsigned int val;
+        int chars;
+        int res = sscanf(from, "%2x%n", &val, &chars);
+
+        if (res == EOF || res < 1 || chars != 2 || val > CHAR_MAX)
+        /* Return a surprising delimiter to
+           force an error. */
+        {
+          return '%';
+        }
+
+        *to++ = (char)val;
+        from += 2;
+        break;
+      }
+
+      default:
+        *to++ = ch;
+        break;
+    }
+  }
+}
+
+/* Parse an AMQP URL into its component parts. */
+int amqp_parse_url(char *url, struct amqp_connection_info *parsed) {
+  int res = AMQP_STATUS_BAD_URL;
+  char delim;
+  char *start;
+  char *host;
+  char *port = NULL;
+
+  amqp_default_connection_info(parsed);
+
+  /* check the prefix */
+  if (!strncmp(url, "amqp://", 7)) {
+    /* do nothing */
+  } else if (!strncmp(url, "amqps://", 8)) {
+    parsed->port = 5671;
+    parsed->ssl = 1;
+  } else {
+    goto out;
+  }
+
+  host = start = url += (parsed->ssl ? 8 : 7);
+  delim = find_delim(&url, 1);
+
+  if (delim == ':') {
+    /* The colon could be introducing the port or the
+       password part of the userinfo.  We don't know yet,
+       so stash the preceding component. */
+    port = start = url;
+    delim = find_delim(&url, 1);
+  }
+
+  if (delim == '@') {
+    /* What might have been the host and port were in fact
+       the username and password */
+    parsed->user = host;
+    if (port) {
+      parsed->password = port;
+    }
+
+    port = NULL;
+    host = start = url;
+    delim = find_delim(&url, 1);
+  }
+
+  if (delim == '[') {
+    /* IPv6 address.  The bracket should be the first
+       character in the host. */
+    if (host != start || *host != 0) {
+      goto out;
+    }
+
+    start = url;
+    delim = find_delim(&url, 0);
+
+    if (delim != ']') {
+      goto out;
+    }
+
+    parsed->host = start;
+    start = url;
+    delim = find_delim(&url, 1);
+
+    /* Closing bracket should be the last character in the
+       host. */
+    if (*start != 0) {
+      goto out;
+    }
+  } else {
+    /* If we haven't seen the host yet, this is it. */
+    if (*host != 0) {
+      parsed->host = host;
+    }
+  }
+
+  if (delim == ':') {
+    port = start = url;
+    delim = find_delim(&url, 1);
+  }
+
+  if (port) {
+    char *end;
+    long portnum = strtol(port, &end, 10);
+
+    if (port == end || *end != 0 || portnum < 0 || portnum > 65535) {
+      goto out;
+    }
+
+    parsed->port = portnum;
+  }
+
+  if (delim == '/') {
+    start = url;
+    delim = find_delim(&url, 1);
+
+    if (delim != 0) {
+      goto out;
+    }
+
+    parsed->vhost = start;
+    res = AMQP_STATUS_OK;
+  } else if (delim == 0) {
+    res = AMQP_STATUS_OK;
+  }
+
+/* Any other delimiter is bad, and we will return AMQP_STATUS_BAD_AMQP_URL. */
+
+out:
+  return res;
+}

+ 785 - 0
ext/librabbitmq/librabbitmq/codegen.py

@@ -0,0 +1,785 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MIT
+#
+# Portions created by Alan Antonuk are Copyright (c) 2012-2013
+# Alan Antonuk. All Rights Reserved.
+#
+# Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+# All Rights Reserved.
+#
+# Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+# VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ***** END LICENSE BLOCK *****
+
+from __future__ import nested_scopes
+from __future__ import division
+
+from amqp_codegen import *
+import string
+import re
+
+
+class Emitter(object):
+    """An object the trivially emits generated code lines.
+
+    This largely exists to be wrapped by more sophisticated emitter
+    classes.
+    """
+
+    def __init__(self, prefix):
+        self.prefix = prefix
+
+    def emit(self, line):
+        """Emit a line of generated code."""
+        print self.prefix + line
+
+
+class BitDecoder(object):
+    """An emitter object that keeps track of the state involved in
+    decoding the AMQP bit type."""
+
+    def __init__(self, emitter):
+        self.emitter = emitter
+        self.bit = 0
+
+    def emit(self, line):
+        self.bit = 0
+        self.emitter.emit(line)
+
+    def decode_bit(self, lvalue):
+        """Generate code to decode a value of the AMQP bit type into
+        the given lvalue."""
+        if self.bit == 0:
+            self.emitter.emit("if (!amqp_decode_8(encoded, &offset, &bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;")
+
+        self.emitter.emit("%s = (bit_buffer & (1 << %d)) ? 1 : 0;"
+                                                        % (lvalue, self.bit))
+        self.bit += 1
+        if self.bit == 8:
+            self.bit = 0
+
+
+class BitEncoder(object):
+    """An emitter object that keeps track of the state involved in
+    encoding the AMQP bit type."""
+
+    def __init__(self, emitter):
+        self.emitter = emitter
+        self.bit = 0
+
+    def flush(self):
+        """Flush the state associated with AMQP bit types."""
+        if self.bit:
+            self.emitter.emit("if (!amqp_encode_8(encoded, &offset, bit_buffer)) return AMQP_STATUS_BAD_AMQP_DATA;")
+            self.bit = 0
+
+    def emit(self, line):
+        self.flush()
+        self.emitter.emit(line)
+
+    def encode_bit(self, value):
+        """Generate code to encode a value of the AMQP bit type from
+        the given value."""
+        if self.bit == 0:
+            self.emitter.emit("bit_buffer = 0;")
+
+        self.emitter.emit("if (%s) bit_buffer |= (1 << %d);"
+                                                       % (value, self.bit))
+        self.bit += 1
+        if self.bit == 8:
+            self.flush()
+
+
+class SimpleType(object):
+    """A AMQP type that corresponds to a simple scalar C value of a
+    certain width."""
+
+    def __init__(self, bits):
+        self.bits = bits
+        self.ctype = "uint%d_t" % (bits,)
+
+    def decode(self, emitter, lvalue):
+        emitter.emit("if (!amqp_decode_%d(encoded, &offset, &%s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, lvalue))
+
+    def encode(self, emitter, value):
+        emitter.emit("if (!amqp_encode_%d(encoded, &offset, %s)) return AMQP_STATUS_BAD_AMQP_DATA;" % (self.bits, value))
+
+    def literal(self, value):
+        return value
+
+class StrType(object):
+    """The AMQP shortstr or longstr types."""
+
+    def __init__(self, lenbits):
+        self.lenbits = lenbits
+        self.ctype = "amqp_bytes_t"
+
+    def decode(self, emitter, lvalue):
+        emitter.emit("{")
+        emitter.emit("  uint%d_t len;" % (self.lenbits,))
+        emitter.emit("  if (!amqp_decode_%d(encoded, &offset, &len)" % (self.lenbits,))
+        emitter.emit("      || !amqp_decode_bytes(encoded, &offset, &%s, len))" % (lvalue,))
+        emitter.emit("    return AMQP_STATUS_BAD_AMQP_DATA;")
+        emitter.emit("}")
+
+    def encode(self, emitter, value):
+        emitter.emit("if (UINT%d_MAX < %s.len" % (self.lenbits, value))
+        emitter.emit("    || !amqp_encode_%d(encoded, &offset, (uint%d_t)%s.len)" %
+                (self.lenbits, self.lenbits, value))
+        emitter.emit("    || !amqp_encode_bytes(encoded, &offset, %s))" % (value,))
+        emitter.emit("  return AMQP_STATUS_BAD_AMQP_DATA;")
+
+    def literal(self, value):
+        if value != '':
+            raise NotImplementedError()
+
+        return "amqp_empty_bytes"
+
+class BitType(object):
+    """The AMQP bit type."""
+
+    def __init__(self):
+        self.ctype = "amqp_boolean_t"
+
+    def decode(self, emitter, lvalue):
+        emitter.decode_bit(lvalue)
+
+    def encode(self, emitter, value):
+        emitter.encode_bit(value)
+
+    def literal(self, value):
+        return {True: 1, False: 0}[value]
+
+class TableType(object):
+    """The AMQP table type."""
+
+    def __init__(self):
+        self.ctype = "amqp_table_t"
+
+    def decode(self, emitter, lvalue):
+        emitter.emit("{")
+        emitter.emit("  int res = amqp_decode_table(encoded, pool, &(%s), &offset);" % (lvalue,))
+        emitter.emit("  if (res < 0) return res;")
+        emitter.emit("}")
+
+    def encode(self, emitter, value):
+        emitter.emit("{")
+        emitter.emit("  int res = amqp_encode_table(encoded, &(%s), &offset);" % (value,))
+        emitter.emit("  if (res < 0) return res;")
+        emitter.emit("}")
+
+    def literal(self, value):
+        raise NotImplementedError()
+
+types = {
+    'octet': SimpleType(8),
+    'short': SimpleType(16),
+    'long': SimpleType(32),
+    'longlong': SimpleType(64),
+    'shortstr': StrType(8),
+    'longstr': StrType(32),
+    'bit': BitType(),
+    'table': TableType(),
+    'timestamp': SimpleType(64),
+}
+
+def typeFor(spec, f):
+    """Get a representation of the AMQP type of a field."""
+    return types[spec.resolveDomain(f.domain)]
+
+def c_ize(s):
+    s = s.replace('-', '_')
+    s = s.replace(' ', '_')
+    return s
+
+# When generating API functions corresponding to synchronous methods,
+# we need some information that isn't in the protocol def: Some
+# methods should not be exposed, indicated here by a False value.
+# Some methods should be exposed but certain fields should not be
+# exposed as parameters.
+apiMethodInfo  = {
+    "amqp_connection_start": False, # application code should not use this
+    "amqp_connection_secure": False, # application code should not use this
+    "amqp_connection_tune": False, # application code should not use this
+    "amqp_connection_open": False, # application code should not use this
+    "amqp_connection_close": False, # needs special handling
+    "amqp_channel_open": ["out_of_band"],
+    "amqp_channel_close": False, # needs special handling
+    "amqp_access_request": False, # huh?
+    "amqp_basic_get": False, # get-ok has content
+}
+
+# When generating API functions corresponding to synchronous methods,
+# some fields should be suppressed everywhere.  This dict names those
+# fields, and the fixed values to use for them.
+apiMethodsSuppressArgs = {"ticket": 0, "nowait": False}
+
+AmqpMethod.defName = lambda m: cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name) + "_method")
+AmqpMethod.fullName = lambda m: "amqp_%s_%s" % (c_ize(m.klass.name), c_ize(m.name))
+AmqpMethod.structName = lambda m: m.fullName() + "_t"
+
+AmqpClass.structName = lambda c: "amqp_" + c_ize(c.name) + "_properties_t"
+
+def methodApiPrototype(m):
+    fn = m.fullName()
+    info = apiMethodInfo.get(fn, [])
+
+    docs = "/**\n * %s\n *\n" % (fn)
+    docs += " * @param [in] state connection state\n"
+    docs += " * @param [in] channel the channel to do the RPC on\n"
+
+    args = []
+    for f in m.arguments:
+        n = c_ize(f.name)
+        if n in apiMethodsSuppressArgs or n in info:
+            continue
+
+        args.append(", ")
+        args.append(typeFor(m.klass.spec, f).ctype)
+        args.append(" ")
+        args.append(n)
+        docs += " * @param [in] %s %s\n" % (n, n)
+
+    docs += " * @returns %s_ok_t\n" % (fn)
+    docs += " */\n"
+
+    return "%sAMQP_PUBLIC_FUNCTION\n%s_ok_t *\nAMQP_CALL %s(amqp_connection_state_t state, amqp_channel_t channel%s)" % (docs, fn, fn, ''.join(args))
+
+AmqpMethod.apiPrototype = methodApiPrototype
+
+def cConstantName(s):
+    return 'AMQP_' + '_'.join(re.split('[- ]', s.upper()))
+
+def cFlagName(c, f):
+    return cConstantName(c.name + '_' + f.name) + '_FLAG'
+
+def genErl(spec):
+    def fieldTempList(fields):
+        return '[' + ', '.join(['F' + str(f.index) for f in fields]) + ']'
+
+    def fieldMapList(fields):
+        return ', '.join([c_ize(f.name) + " = F" + str(f.index) for f in fields])
+
+    def genLookupMethodName(m):
+        print '    case %s: return "%s";' % (m.defName(), m.defName())
+
+    def genDecodeMethodFields(m):
+        print "    case %s: {" % (m.defName(),)
+        print "      %s *m = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \
+            (m.structName(), m.structName(), m.structName())
+        print "      if (m == NULL) { return AMQP_STATUS_NO_MEMORY; }"
+
+        emitter = BitDecoder(Emitter("      "))
+        for f in m.arguments:
+            typeFor(spec, f).decode(emitter, "m->"+c_ize(f.name))
+
+        print "      *decoded = m;"
+        print "      return 0;"
+        print "    }"
+
+    def genDecodeProperties(c):
+        print "    case %d: {" % (c.index,)
+        print "      %s *p = (%s *) amqp_pool_alloc(pool, sizeof(%s));" % \
+              (c.structName(), c.structName(), c.structName())
+        print "      if (p == NULL) { return AMQP_STATUS_NO_MEMORY; }"
+        print "      p->_flags = flags;"
+
+        emitter = Emitter("      ")
+        for f in c.fields:
+            emitter.emit("if (flags & %s) {" % (cFlagName(c, f),))
+            typeFor(spec, f).decode(emitter, "p->"+c_ize(f.name))
+            emitter.emit("}")
+
+        print "      *decoded = p;"
+        print "      return 0;"
+        print "    }"
+
+    def genEncodeMethodFields(m):
+        print "    case %s: {" % (m.defName(),)
+        if m.arguments:
+            print "      %s *m = (%s *) decoded;" % (m.structName(), m.structName())
+
+        emitter = BitEncoder(Emitter("      "))
+        for f in m.arguments:
+            typeFor(spec, f).encode(emitter, "m->"+c_ize(f.name))
+        emitter.flush()
+
+        print "      return (int)offset;"
+        print "    }"
+
+    def genEncodeProperties(c):
+        print "    case %d: {" % (c.index,)
+        if c.fields:
+            print "      %s *p = (%s *) decoded;" % (c.structName(), c.structName())
+
+        emitter = Emitter("      ")
+        for f in c.fields:
+            emitter.emit("      if (flags & %s) {" % (cFlagName(c, f),))
+            typeFor(spec, f).encode(emitter, "p->"+c_ize(f.name))
+            emitter.emit("}")
+
+        print "      return (int)offset;"
+        print "    }"
+
+    methods = spec.allMethods()
+
+    print """/* Generated code. Do not edit. Edit and re-run codegen.py instead.
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp_private.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+"""
+
+    print """
+char const *amqp_constant_name(int constantNumber) {
+  switch (constantNumber) {"""
+    for (c,v,cls) in spec.constants:
+        print "    case %s: return \"%s\";" % (cConstantName(c), cConstantName(c))
+    print """    default: return "(unknown)";
+  }
+}"""
+
+    print """
+amqp_boolean_t amqp_constant_is_hard_error(int constantNumber) {
+  switch (constantNumber) {"""
+    for (c,v,cls) in spec.constants:
+        if cls == 'hard-error':
+            print "    case %s: return 1;" % (cConstantName(c),)
+    print """    default: return 0;
+  }
+}"""
+
+    print """
+char const *amqp_method_name(amqp_method_number_t methodNumber) {
+  switch (methodNumber) {"""
+    for m in methods: genLookupMethodName(m)
+    print """    default: return NULL;
+  }
+}"""
+
+    print """
+amqp_boolean_t amqp_method_has_content(amqp_method_number_t methodNumber) {
+  switch (methodNumber) {"""
+    for m in methods:
+        if m.hasContent:
+            print '    case %s: return 1;' % (m.defName())
+    print """    default: return 0;
+  }
+}"""
+
+    print """
+int amqp_decode_method(amqp_method_number_t methodNumber,
+                       amqp_pool_t *pool,
+                       amqp_bytes_t encoded,
+                       void **decoded)
+{
+  size_t offset = 0;
+  uint8_t bit_buffer;
+
+  switch (methodNumber) {"""
+    for m in methods: genDecodeMethodFields(m)
+    print """    default: return AMQP_STATUS_UNKNOWN_METHOD;
+  }
+}"""
+
+    print """
+int amqp_decode_properties(uint16_t class_id,
+                           amqp_pool_t *pool,
+                           amqp_bytes_t encoded,
+                           void **decoded)
+{
+  size_t offset = 0;
+
+  amqp_flags_t flags = 0;
+  int flagword_index = 0;
+  uint16_t partial_flags;
+
+  do {
+    if (!amqp_decode_16(encoded, &offset, &partial_flags))
+      return AMQP_STATUS_BAD_AMQP_DATA;
+    flags |= (partial_flags << (flagword_index * 16));
+    flagword_index++;
+  } while (partial_flags & 1);
+
+  switch (class_id) {"""
+    for c in spec.allClasses(): genDecodeProperties(c)
+    print """    default: return AMQP_STATUS_UNKNOWN_CLASS;
+  }
+}"""
+
+    print """
+int amqp_encode_method(amqp_method_number_t methodNumber,
+                       void *decoded,
+                       amqp_bytes_t encoded)
+{
+  size_t offset = 0;
+  uint8_t bit_buffer;
+
+  switch (methodNumber) {"""
+    for m in methods: genEncodeMethodFields(m)
+    print """    default: return AMQP_STATUS_UNKNOWN_METHOD;
+  }
+}"""
+
+    print """
+int amqp_encode_properties(uint16_t class_id,
+                           void *decoded,
+                           amqp_bytes_t encoded)
+{
+  size_t offset = 0;
+
+  /* Cheat, and get the flags out generically, relying on the
+     similarity of structure between classes */
+  amqp_flags_t flags = * (amqp_flags_t *) decoded; /* cheating! */
+
+  {
+    /* We take a copy of flags to avoid destroying it, as it is used
+       in the autogenerated code below. */
+    amqp_flags_t remaining_flags = flags;
+    do {
+      amqp_flags_t remainder = remaining_flags >> 16;
+      uint16_t partial_flags = remaining_flags & 0xFFFE;
+      if (remainder != 0) { partial_flags |= 1; }
+      if (!amqp_encode_16(encoded, &offset, partial_flags))
+        return AMQP_STATUS_BAD_AMQP_DATA;
+      remaining_flags = remainder;
+    } while (remaining_flags != 0);
+  }
+
+  switch (class_id) {"""
+    for c in spec.allClasses(): genEncodeProperties(c)
+    print """    default: return AMQP_STATUS_UNKNOWN_CLASS;
+  }
+}"""
+
+    for m in methods:
+        if not m.isSynchronous:
+            continue
+
+        info = apiMethodInfo.get(m.fullName(), [])
+        if info is False:
+            continue
+
+        print
+        print m.apiPrototype()
+        print "{"
+        print "  %s req;" % (m.structName(),)
+
+        for f in m.arguments:
+            n = c_ize(f.name)
+
+            val = apiMethodsSuppressArgs.get(n)
+            if val is None and n in info:
+                val = f.defaultvalue
+
+            if val is None:
+                val = n
+            else:
+                val = typeFor(spec, f).literal(val)
+
+
+            print "  req.%s = %s;" % (n, val)
+
+        reply = cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name)
+                              + "_ok_method")
+        print """
+  return amqp_simple_rpc_decoded(state, channel, %s, %s, &req);
+}
+""" % (m.defName(), reply)
+
+def genHrl(spec):
+    def fieldDeclList(fields):
+        if fields:
+            return ''.join(["  %s %s; /**< %s */\n" % (typeFor(spec, f).ctype,
+                                            c_ize(f.name), f.name)
+                            for f in fields])
+        else:
+            return "  char dummy; /**< Dummy field to avoid empty struct */\n"
+
+    def propDeclList(fields):
+        return ''.join(["  %s %s;\n" % (typeFor(spec, f).ctype, c_ize(f.name))
+                        for f in fields
+                        if spec.resolveDomain(f.domain) != 'bit'])
+
+    methods = spec.allMethods()
+
+    print """/* Generated code. Do not edit. Edit and re-run codegen.py instead.
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+/** @file amqp_framing.h */
+#ifndef AMQP_FRAMING_H
+#define AMQP_FRAMING_H
+
+#include <amqp.h>
+
+AMQP_BEGIN_DECLS
+"""
+    print "#define AMQP_PROTOCOL_VERSION_MAJOR %d     /**< AMQP protocol version major */" % (spec.major)
+    print "#define AMQP_PROTOCOL_VERSION_MINOR %d     /**< AMQP protocol version minor */" % (spec.minor)
+    print "#define AMQP_PROTOCOL_VERSION_REVISION %d  /**< AMQP protocol version revision */" % (spec.revision)
+    print "#define AMQP_PROTOCOL_PORT %d              /**< Default AMQP Port */" % (spec.port)
+
+    for (c,v,cls) in spec.constants:
+        print "#define %s %s  /**< Constant: %s */" % (cConstantName(c), v, c)
+    print
+
+    print """/* Function prototypes. */
+
+/**
+ * Get constant name string from constant
+ *
+ * @param [in] constantNumber constant to get the name of
+ * @returns string describing the constant. String is managed by
+ *           the library and should not be free()'d by the program
+ */
+AMQP_PUBLIC_FUNCTION
+char const *
+AMQP_CALL amqp_constant_name(int constantNumber);
+
+/**
+ * Checks to see if a constant is a hard error
+ *
+ * A hard error occurs when something severe enough
+ * happens that the connection must be closed.
+ *
+ * @param [in] constantNumber the error constant
+ * @returns true if its a hard error, false otherwise
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_boolean_t
+AMQP_CALL amqp_constant_is_hard_error(int constantNumber);
+
+/**
+ * Get method name string from method number
+ *
+ * @param [in] methodNumber the method number
+ * @returns method name string. String is managed by the library
+ *           and should not be freed()'d by the program
+ */
+AMQP_PUBLIC_FUNCTION
+char const *
+AMQP_CALL amqp_method_name(amqp_method_number_t methodNumber);
+
+/**
+ * Check whether a method has content
+ *
+ * A method that has content will receive the method frame
+ * a properties frame, then 1 to N body frames
+ *
+ * @param [in] methodNumber the method number
+ * @returns true if method has content, false otherwise
+ */
+AMQP_PUBLIC_FUNCTION
+amqp_boolean_t
+AMQP_CALL amqp_method_has_content(amqp_method_number_t methodNumber);
+
+/**
+ * Decodes a method from AMQP wireformat
+ *
+ * @param [in] methodNumber the method number for the decoded parameter
+ * @param [in] pool the memory pool to allocate the decoded method from
+ * @param [in] encoded the encoded byte string buffer
+ * @param [out] decoded pointer to the decoded method struct
+ * @returns 0 on success, an error code otherwise
+ */
+AMQP_PUBLIC_FUNCTION
+int
+AMQP_CALL amqp_decode_method(amqp_method_number_t methodNumber,
+		   amqp_pool_t *pool,
+		   amqp_bytes_t encoded,
+		   void **decoded);
+
+/**
+ * Decodes a header frame properties structure from AMQP wireformat
+ *
+ * @param [in] class_id the class id for the decoded parameter
+ * @param [in] pool the memory pool to allocate the decoded properties from
+ * @param [in] encoded the encoded byte string buffer
+ * @param [out] decoded pointer to the decoded properties struct
+ * @returns 0 on success, an error code otherwise
+ */
+AMQP_PUBLIC_FUNCTION
+int
+AMQP_CALL amqp_decode_properties(uint16_t class_id,
+            amqp_pool_t *pool,
+            amqp_bytes_t encoded,
+            void **decoded);
+
+/**
+ * Encodes a method structure in AMQP wireformat
+ *
+ * @param [in] methodNumber the method number for the decoded parameter
+ * @param [in] decoded the method structure (e.g., amqp_connection_start_t)
+ * @param [in] encoded an allocated byte buffer for the encoded method
+ *              structure to be written to. If the buffer isn't large enough
+ *              to hold the encoded method, an error code will be returned.
+ * @returns 0 on success, an error code otherwise.
+ */
+AMQP_PUBLIC_FUNCTION
+int
+AMQP_CALL amqp_encode_method(amqp_method_number_t methodNumber,
+		   void *decoded,
+		   amqp_bytes_t encoded);
+
+/**
+ * Encodes a properties structure in AMQP wireformat
+ *
+ * @param [in] class_id the class id for the decoded parameter
+ * @param [in] decoded the properties structure (e.g., amqp_basic_properties_t)
+ * @param [in] encoded an allocated byte buffer for the encoded properties to written to.
+ *              If the buffer isn't large enough to hold the encoded method, an
+ *              an error code will be returned
+ * @returns 0 on success, an error code otherwise.
+ */
+AMQP_PUBLIC_FUNCTION
+int
+AMQP_CALL amqp_encode_properties(uint16_t class_id,
+		       void *decoded,
+		       amqp_bytes_t encoded);
+"""
+
+    print "/* Method field records. */\n"
+    for m in methods:
+        methodid = m.klass.index << 16 | m.index
+        print "#define %s ((amqp_method_number_t) 0x%.08X) /**< %s.%s method id @internal %d, %d; %d */" % \
+              (m.defName(),
+               methodid,
+               m.klass.name,
+               m.name,
+               m.klass.index,
+               m.index,
+               methodid)
+        print "/** %s.%s method fields */\ntypedef struct %s_ {\n%s} %s;\n" % \
+              (m.klass.name, m.name, m.structName(), fieldDeclList(m.arguments), m.structName())
+
+    print "/* Class property records. */"
+    for c in spec.allClasses():
+        print "#define %s (0x%.04X) /**< %s class id @internal %d */" % \
+              (cConstantName(c.name + "_class"), c.index, c.name, c.index)
+        index = 0
+        for f in c.fields:
+            if index % 16 == 15:
+                index = index + 1
+            shortnum = index // 16
+            partialindex = 15 - (index % 16)
+            bitindex = shortnum * 16 + partialindex
+            print '#define %s (1 << %d) /**< %s.%s property flag */' % (cFlagName(c, f), bitindex, c.name, f.name)
+            index = index + 1
+        print "/** %s class properties */\ntypedef struct %s_ {\n  amqp_flags_t _flags; /**< bit-mask of set fields */\n%s} %s;\n" % \
+              (c.name,
+               c.structName(),
+               fieldDeclList(c.fields),
+               c.structName())
+
+    print "/* API functions for methods */\n"
+
+    for m in methods:
+        if m.isSynchronous and apiMethodInfo.get(m.fullName()) is not False:
+            print "%s;" % (m.apiPrototype(),)
+
+    print """
+AMQP_END_DECLS
+
+#endif /* AMQP_FRAMING_H */"""
+
+def generateErl(specPath):
+    genErl(AmqpSpec(specPath))
+
+def generateHrl(specPath):
+    genHrl(AmqpSpec(specPath))
+
+if __name__ == "__main__":
+    do_main(generateHrl, generateErl)

+ 28 - 0
ext/librabbitmq/librabbitmq/unix/threads.h

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

+ 245 - 0
ext/librabbitmq/librabbitmq/win32/msinttypes/stdint.h

@@ -0,0 +1,245 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+//  Copyright (c) 2006-2008 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+//
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+//
+//   3. The name of the author may be used to endorse or promote products
+//      derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER  // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif  // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_  // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#define _W64 __w64
+#else
+#define _W64
+#endif
+#endif
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+#else
+typedef signed __int8 int8_t;
+typedef signed __int16 int16_t;
+typedef signed __int32 int32_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64  // [
+typedef signed __int64 intptr_t;
+typedef unsigned __int64 uintptr_t;
+#else   // _WIN64 ][
+typedef _W64 signed int intptr_t;
+typedef _W64 unsigned int uintptr_t;
+#endif  // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || \
+    defined(__STDC_LIMIT_MACROS)  // [   See footnote 220 at page 257 and
+                                  // footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64  // [
+#define INTPTR_MIN INT64_MIN
+#define INTPTR_MAX INT64_MAX
+#define UINTPTR_MAX UINT64_MAX
+#else  // _WIN64 ][
+#define INTPTR_MIN INT32_MIN
+#define INTPTR_MAX INT32_MAX
+#define UINTPTR_MAX UINT32_MAX
+#endif  // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64  // [
+#define PTRDIFF_MIN _I64_MIN
+#define PTRDIFF_MAX _I64_MAX
+#else  // _WIN64 ][
+#define PTRDIFF_MIN _I32_MIN
+#define PTRDIFF_MAX _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX  // [
+#ifdef _WIN64     // [
+#define SIZE_MAX _UI64_MAX
+#else  // _WIN64 ][
+#define SIZE_MAX _UI32_MAX
+#endif  // _WIN64 ]
+#endif  // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN  // [
+#define WCHAR_MIN 0
+#endif             // WCHAR_MIN ]
+#ifndef WCHAR_MAX  // [
+#define WCHAR_MAX _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif  // __STDC_LIMIT_MACROS ]
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || \
+    defined(__STDC_CONSTANT_MACROS)  // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C INT64_C
+#define UINTMAX_C UINT64_C
+
+#endif  // __STDC_CONSTANT_MACROS ]
+
+#endif  // _MSC_STDINT_H_ ]

+ 56 - 0
ext/librabbitmq/librabbitmq/win32/threads.c

@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2013 Michael Steinert
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "threads.h"
+
+#include <stdlib.h>
+
+DWORD pthread_self(void) { return GetCurrentThreadId(); }
+
+int pthread_mutex_init(pthread_mutex_t *mutex, void *attr) {
+  if (!mutex) {
+    return 1;
+  }
+  InitializeSRWLock(mutex);
+  return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mutex) {
+  if (!mutex) {
+    return 1;
+  }
+  AcquireSRWLockExclusive(mutex);
+  return 0;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex) {
+  if (!mutex) {
+    return 1;
+  }
+  ReleaseSRWLockExclusive(mutex);
+  return 0;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex) {
+  /* SRW's do not require destruction. */
+  return 0;
+}

+ 52 - 0
ext/librabbitmq/librabbitmq/win32/threads.h

@@ -0,0 +1,52 @@
+/*
+ * Portions created by Alan Antonuk are Copyright (c) 2013-2014 Alan Antonuk.
+ * All Rights Reserved.
+ *
+ * Portions created by Michael Steinert are Copyright (c) 2012-2013 Michael
+ * Steinert. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef AMQP_THREAD_H
+#define AMQP_THREAD_H
+
+#if !defined(WINVER) || defined(__MINGW32__) || defined(__MINGW64__)
+#ifdef WINVER
+#undef WINVER
+#endif
+/* Windows Vista or newer */
+#define WINVER 0x0600
+#endif
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+typedef SRWLOCK pthread_mutex_t;
+#define PTHREAD_MUTEX_INITIALIZER SRWLOCK_INIT;
+
+DWORD pthread_self(void);
+
+int pthread_mutex_init(pthread_mutex_t *, void *attr);
+int pthread_mutex_lock(pthread_mutex_t *);
+int pthread_mutex_unlock(pthread_mutex_t *);
+int pthread_mutex_destroy(pthread_mutex_t *);
+
+#endif /* AMQP_THREAD_H */

+ 47 - 0
ext/librabbitmq/tests/CMakeLists.txt

@@ -0,0 +1,47 @@
+include_directories(${LIBRABBITMQ_INCLUDE_DIRS})
+
+if (MSVC)
+  # No version of MSVC has inttypes.h, this uses the msinttypes
+  #  Note this includes stdint.h which is either in 
+  #  ../librabbitmq/win32/msinttypes or in a standard location
+  include_directories(win32/msinttypes)
+endif (MSVC)
+
+add_definitions(-DHAVE_CONFIG_H)
+add_definitions(-DAMQP_STATIC)
+
+add_executable(test_parse_url test_parse_url.c)
+target_link_libraries(test_parse_url rabbitmq-static)
+add_test(parse_url test_parse_url)
+
+add_executable(test_tables test_tables.c)
+target_link_libraries(test_tables rabbitmq-static)
+add_test(tables test_tables)
+configure_file(test_tables.expected ${CMAKE_CURRENT_BINARY_DIR}/tests/test_tables.expected COPYONLY)
+
+add_executable(test_hostcheck
+               test_hostcheck.c
+               ../librabbitmq/amqp_hostcheck.c)
+add_test(hostcheck test_hostcheck)
+
+add_executable(test_status_enum
+               test_status_enum.c)
+target_link_libraries(test_status_enum rabbitmq-static)
+add_test(status_enum test_status_enum)
+
+add_executable(test_basic
+               test_basic.c)
+target_link_libraries(test_basic rabbitmq-static)
+
+if (NOT APPLE)
+  add_test(basic test_basic)
+endif()
+
+add_executable(test_sasl_mechanism test_sasl_mechanism.c)
+target_link_libraries(test_sasl_mechanism rabbitmq-static)
+add_test(sasl_mechanism test_sasl_mechanism)
+
+add_executable(test_merge_capabilities test_merge_capabilities.c)
+target_link_libraries(test_merge_capabilities rabbitmq-static)
+add_test(merge_capabilities test_merge_capabilities)
+

+ 207 - 0
ext/librabbitmq/tests/test_basic.c

@@ -0,0 +1,207 @@
+/*
+ * Copyright 2017 Simon Giesecke
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "amqp.h"
+#include "amqp_tcp_socket.h"
+#include "amqp_time.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <WinSock2.h>
+#else
+#include <sys/time.h>
+#endif
+
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+#include <assert.h>
+
+static const int fixed_channel_id = 1;
+static const char test_queue_name[] = "test_queue";
+
+amqp_connection_state_t setup_connection_and_channel(void) {
+  amqp_connection_state_t connection_state_ = amqp_new_connection();
+
+  amqp_socket_t *socket = amqp_tcp_socket_new(connection_state_);
+  assert(socket);
+
+  int rc = amqp_socket_open(socket, "localhost", AMQP_PROTOCOL_PORT);
+  assert(rc == AMQP_STATUS_OK);
+
+  amqp_rpc_reply_t rpc_reply = amqp_login(
+      connection_state_, "/", 1, AMQP_DEFAULT_FRAME_SIZE,
+      AMQP_DEFAULT_HEARTBEAT, AMQP_SASL_METHOD_PLAIN, "guest", "guest");
+  assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
+
+  amqp_channel_open_ok_t *res =
+      amqp_channel_open(connection_state_, fixed_channel_id);
+  assert(res != NULL);
+
+  return connection_state_;
+}
+
+void close_and_destroy_connection(amqp_connection_state_t connection_state_) {
+  amqp_rpc_reply_t rpc_reply =
+      amqp_connection_close(connection_state_, AMQP_REPLY_SUCCESS);
+  assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
+
+  int rc = amqp_destroy_connection(connection_state_);
+  assert(rc == AMQP_STATUS_OK);
+}
+
+void basic_publish(amqp_connection_state_t connectionState_,
+                   const char *message_) {
+  amqp_bytes_t message_bytes = amqp_cstring_bytes(message_);
+
+  amqp_basic_properties_t properties;
+  properties._flags = 0;
+
+  properties._flags |= AMQP_BASIC_DELIVERY_MODE_FLAG;
+  properties.delivery_mode = AMQP_DELIVERY_NONPERSISTENT;
+
+  int retval = amqp_basic_publish(
+      connectionState_, fixed_channel_id, amqp_cstring_bytes(""),
+      amqp_cstring_bytes(test_queue_name),
+      /* mandatory=*/1,
+      /* immediate=*/0, /* RabbitMQ 3.x does not support the "immediate" flag
+                          according to
+                          https://www.rabbitmq.com/specification.html */
+      &properties, message_bytes);
+
+  assert(retval == 0);
+}
+
+void queue_declare(amqp_connection_state_t connection_state_,
+                   const char *queue_name_) {
+  amqp_queue_declare_ok_t *res = amqp_queue_declare(
+      connection_state_, fixed_channel_id, amqp_cstring_bytes(queue_name_),
+      /*passive*/ 0,
+      /*durable*/ 0,
+      /*exclusive*/ 0,
+      /*auto_delete*/ 1, amqp_empty_table);
+  assert(res != NULL);
+}
+
+char *basic_get(amqp_connection_state_t connection_state_,
+                const char *queue_name_, uint64_t *out_body_size_) {
+  amqp_rpc_reply_t rpc_reply;
+  amqp_time_t deadline;
+  struct timeval timeout = {5, 0};
+  int time_rc = amqp_time_from_now(&deadline, &timeout);
+  assert(time_rc == AMQP_STATUS_OK);
+
+  do {
+    rpc_reply = amqp_basic_get(connection_state_, fixed_channel_id,
+                               amqp_cstring_bytes(queue_name_), /*no_ack*/ 1);
+  } while (rpc_reply.reply_type == AMQP_RESPONSE_NORMAL &&
+           rpc_reply.reply.id == AMQP_BASIC_GET_EMPTY_METHOD &&
+           amqp_time_has_past(deadline) == AMQP_STATUS_OK);
+
+  assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
+  assert(rpc_reply.reply.id == AMQP_BASIC_GET_OK_METHOD);
+
+  amqp_message_t message;
+  rpc_reply =
+      amqp_read_message(connection_state_, fixed_channel_id, &message, 0);
+  assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
+
+  char *body = malloc(message.body.len);
+  memcpy(body, message.body.bytes, message.body.len);
+  *out_body_size_ = message.body.len;
+  amqp_destroy_message(&message);
+
+  return body;
+}
+
+void publish_and_basic_get_message(const char *msg_to_publish) {
+  amqp_connection_state_t connection_state = setup_connection_and_channel();
+
+  queue_declare(connection_state, test_queue_name);
+  basic_publish(connection_state, msg_to_publish);
+
+  uint64_t body_size;
+  char *msg = basic_get(connection_state, test_queue_name, &body_size);
+
+  assert(body_size == strlen(msg_to_publish));
+  assert(strncmp(msg_to_publish, msg, body_size) == 0);
+  free(msg);
+
+  close_and_destroy_connection(connection_state);
+}
+
+char *consume_message(amqp_connection_state_t connection_state_,
+                      const char *queue_name_, uint64_t *out_body_size_) {
+  amqp_basic_consume_ok_t *result =
+      amqp_basic_consume(connection_state_, fixed_channel_id,
+                         amqp_cstring_bytes(queue_name_), amqp_empty_bytes,
+                         /*no_local*/ 0,
+                         /*no_ack*/ 1,
+                         /*exclusive*/ 0, amqp_empty_table);
+  assert(result != NULL);
+
+  amqp_envelope_t envelope;
+  struct timeval timeout = {5, 0};
+  amqp_rpc_reply_t rpc_reply =
+      amqp_consume_message(connection_state_, &envelope, &timeout, 0);
+  assert(rpc_reply.reply_type == AMQP_RESPONSE_NORMAL);
+
+  *out_body_size_ = envelope.message.body.len;
+  char *body = malloc(*out_body_size_);
+  if (*out_body_size_) {
+    memcpy(body, envelope.message.body.bytes, *out_body_size_);
+  }
+
+  amqp_destroy_envelope(&envelope);
+  return body;
+}
+
+void publish_and_consume_message(const char *msg_to_publish) {
+  amqp_connection_state_t connection_state = setup_connection_and_channel();
+
+  queue_declare(connection_state, test_queue_name);
+  basic_publish(connection_state, msg_to_publish);
+
+  uint64_t body_size;
+  char *msg = consume_message(connection_state, test_queue_name, &body_size);
+
+  assert(body_size == strlen(msg_to_publish));
+  assert(strncmp(msg_to_publish, msg, body_size) == 0);
+  free(msg);
+
+  close_and_destroy_connection(connection_state);
+}
+
+int main(void) {
+  publish_and_basic_get_message("");
+  publish_and_basic_get_message("TEST");
+
+  publish_and_consume_message("");
+  publish_and_consume_message("TEST");
+
+  return 0;
+}

+ 71 - 0
ext/librabbitmq/tests/test_hostcheck.c

@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 Michael Steinert
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp_hostcheck.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void hostcheck_success(const char *match_pattern, const char *url) {
+  int ok;
+
+  ok = amqp_hostcheck(match_pattern, url);
+  if (!ok) {
+    fprintf(stderr, "Expected hostname check to pass, but didn't: %s (%s)\n",
+            url, match_pattern);
+    abort();
+  }
+
+  fprintf(stdout, "ok: [success] %s, %s\n", url, match_pattern);
+}
+
+static void hostcheck_fail(const char *match_pattern, const char *url) {
+  int ok;
+
+  ok = amqp_hostcheck(match_pattern, url);
+  if (ok) {
+    fprintf(stderr, "Expected hostname check to fail, but didn't: %s (%s)\n",
+            url, match_pattern);
+    abort();
+  }
+
+  fprintf(stdout, "ok: [fail] %s, %s\n", url, match_pattern);
+}
+
+int main(void) {
+  hostcheck_success("www.rabbitmq.com", "www.rabbitmq.com");
+  hostcheck_success("www.rabbitmq.com", "wWw.RaBbItMq.CoM");
+  hostcheck_success("*.rabbitmq.com", "wWw.RaBbItMq.CoM");
+  hostcheck_fail("rabbitmq.com", "www.rabbitmq.com");
+  hostcheck_success("*.rabbitmq.com", "www.rabbitmq.com");
+  hostcheck_fail("*.com", "www.rabbitmq.com");
+  hostcheck_fail("*.rabbitmq.com", "long.url.rabbitmq.com");
+  hostcheck_success("*.url.rabbitmq.com", "long.url.rabbitmq.com");
+
+  return 0;
+}

+ 203 - 0
ext/librabbitmq/tests/test_merge_capabilities.c

@@ -0,0 +1,203 @@
+/*
+ * Copyright 2015 Alan Antonuk. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "amqp_socket.h"
+#include "amqp_table.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static int compare_bytes(amqp_bytes_t l, amqp_bytes_t r);
+static int compare_amqp_table_entry(amqp_table_entry_t result,
+                                    amqp_table_entry_t expect);
+static int compare_field_value(amqp_field_value_t result,
+                               amqp_field_value_t expect);
+static int compare_amqp_table(amqp_table_t* result, amqp_table_t* expect);
+
+static int compare_bytes(amqp_bytes_t l, amqp_bytes_t r) {
+  if (l.len == r.len &&
+      (l.bytes == r.bytes || 0 == memcmp(l.bytes, r.bytes, l.len))) {
+    return 1;
+  }
+  return 0;
+}
+
+static int compare_amqp_table_entry(amqp_table_entry_t result,
+                                    amqp_table_entry_t expect) {
+  if (!compare_bytes(result.key, expect.key)) {
+    return 0;
+  }
+  return compare_field_value(result.value, expect.value);
+}
+
+static int compare_field_value(amqp_field_value_t result,
+                               amqp_field_value_t expect) {
+  if (result.kind != expect.kind) {
+    return 0;
+  }
+  switch (result.kind) {
+    case AMQP_FIELD_KIND_BOOLEAN:
+      return result.value.boolean == expect.value.boolean;
+    case AMQP_FIELD_KIND_I8:
+      return result.value.i8 == expect.value.i8;
+    case AMQP_FIELD_KIND_U8:
+      return result.value.u8 == expect.value.u8;
+    case AMQP_FIELD_KIND_I16:
+      return result.value.i16 == expect.value.i16;
+    case AMQP_FIELD_KIND_U16:
+      return result.value.u16 == expect.value.u16;
+    case AMQP_FIELD_KIND_I32:
+      return result.value.i32 == expect.value.i32;
+    case AMQP_FIELD_KIND_U32:
+      return result.value.u32 == expect.value.u32;
+    case AMQP_FIELD_KIND_I64:
+      return result.value.i64 == expect.value.i64;
+    case AMQP_FIELD_KIND_U64:
+    case AMQP_FIELD_KIND_TIMESTAMP:
+      return result.value.u64 == expect.value.u64;
+    case AMQP_FIELD_KIND_F32:
+      return result.value.f32 == expect.value.f32;
+    case AMQP_FIELD_KIND_F64:
+      return result.value.f64 == expect.value.f64;
+    case AMQP_FIELD_KIND_DECIMAL:
+      return !memcmp(&result.value.decimal, &expect.value.decimal,
+                     sizeof(expect.value.decimal));
+    case AMQP_FIELD_KIND_UTF8:
+    case AMQP_FIELD_KIND_BYTES:
+      return compare_bytes(result.value.bytes, expect.value.bytes);
+    case AMQP_FIELD_KIND_ARRAY: {
+      int i;
+      if (result.value.array.num_entries != expect.value.array.num_entries) {
+        return 0;
+      }
+      for (i = 0; i < result.value.array.num_entries; ++i) {
+        if (!compare_field_value(result.value.array.entries[i],
+                                 expect.value.array.entries[i])) {
+          return 0;
+        }
+      }
+      return 1;
+    }
+    case AMQP_FIELD_KIND_TABLE:
+      return compare_amqp_table(&result.value.table, &expect.value.table);
+    case AMQP_FIELD_KIND_VOID:
+      return 1;
+  }
+  return 1;
+}
+
+static int compare_amqp_table(amqp_table_t* result, amqp_table_t* expect) {
+  int i;
+
+  if (result->num_entries != expect->num_entries) {
+    return 0;
+  }
+
+  for (i = 0; i < expect->num_entries; ++i) {
+    if (!compare_amqp_table_entry(expect->entries[i], result->entries[i])) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+static void test_merge_capabilities(amqp_table_t* base, amqp_table_t* add,
+                                    amqp_table_t* expect) {
+  amqp_pool_t pool;
+  amqp_table_t result;
+  int res;
+  init_amqp_pool(&pool, 4096);
+
+  res = amqp_merge_capabilities(base, add, &result, &pool);
+  if (AMQP_STATUS_OK != res) {
+    fprintf(stderr, "amqp_merge_capabilities returned !ok: %d\n", res);
+    abort();
+  }
+
+  if (!compare_amqp_table(&result, expect)) {
+    fprintf(stderr, "amqp_merge_capabilities incorrect result.\n");
+    abort();
+  }
+  empty_amqp_pool(&pool);
+  return;
+}
+
+int main(void) {
+  {
+    amqp_table_t sub_base;
+    amqp_table_t sub_add;
+    amqp_table_t sub_expect;
+    amqp_table_t base;
+    amqp_table_t add;
+    amqp_table_t expect;
+
+    amqp_table_entry_t sub_base_entries[1];
+    amqp_table_entry_t sub_add_entries[2];
+    amqp_table_entry_t sub_expect_entries[2];
+
+    amqp_table_entry_t base_entries[3];
+    amqp_table_entry_t add_entries[3];
+    amqp_table_entry_t expect_entries[4];
+
+    sub_base_entries[0] = amqp_table_construct_utf8_entry("foo", "bar");
+    sub_base.num_entries =
+        sizeof(sub_base_entries) / sizeof(amqp_table_entry_t);
+    sub_base.entries = sub_base_entries;
+
+    sub_add_entries[0] = amqp_table_construct_utf8_entry("something", "else");
+    sub_add_entries[1] = amqp_table_construct_utf8_entry("foo", "baz");
+    sub_add.num_entries = sizeof(sub_add_entries) / sizeof(amqp_table_entry_t);
+    sub_add.entries = sub_add_entries;
+
+    sub_expect_entries[0] = amqp_table_construct_utf8_entry("foo", "baz");
+    sub_expect_entries[1] =
+        amqp_table_construct_utf8_entry("something", "else");
+    sub_expect.num_entries =
+        sizeof(sub_expect_entries) / sizeof(amqp_table_entry_t);
+    sub_expect.entries = sub_expect_entries;
+
+    base_entries[0] = amqp_table_construct_utf8_entry("product", "1.0");
+    base_entries[1] = amqp_table_construct_utf8_entry("nooverride", "yeah");
+    base_entries[2] = amqp_table_construct_table_entry("props", &sub_base);
+    base.num_entries = sizeof(base_entries) / sizeof(amqp_table_entry_t);
+    base.entries = base_entries;
+
+    add_entries[0] = amqp_table_construct_bool_entry("bool_entry", 1);
+    add_entries[1] = amqp_table_construct_utf8_entry("product", "2.0");
+    add_entries[2] = amqp_table_construct_table_entry("props", &sub_add);
+    add.num_entries = sizeof(add_entries) / sizeof(amqp_table_entry_t);
+    add.entries = add_entries;
+
+    expect_entries[0] = amqp_table_construct_utf8_entry("product", "2.0"),
+    expect_entries[1] = amqp_table_construct_utf8_entry("nooverride", "yeah"),
+    expect_entries[2] = amqp_table_construct_table_entry("props", &sub_expect);
+    expect_entries[3] = amqp_table_construct_bool_entry("bool_entry", 1);
+    expect.num_entries = sizeof(expect_entries) / sizeof(amqp_table_entry_t);
+    expect.entries = expect_entries;
+
+    test_merge_capabilities(&base, &add, &expect);
+  }
+  fprintf(stderr, "ok\n");
+  return 0;
+}

+ 220 - 0
ext/librabbitmq/tests/test_parse_url.c

@@ -0,0 +1,220 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include "config.h"
+
+#ifdef _MSC_VER
+/* MSVC complains about strdup being deprecated in favor of _strdup */
+#define _CRT_NONSTDC_NO_DEPRECATE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <inttypes.h>
+
+#include <amqp.h>
+
+static void match_string(const char *what, const char *expect,
+                         const char *got) {
+  if (strcmp(got, expect)) {
+    fprintf(stderr, "Expected %s '%s', got '%s'\n", what, expect, got);
+    abort();
+  }
+}
+
+static void match_int(const char *what, int expect, int got) {
+  if (got != expect) {
+    fprintf(stderr, "Expected %s '%d', got '%d'\n", what, expect, got);
+    abort();
+  }
+}
+
+static void parse_success(const char *url, const char *user,
+                          const char *password, const char *host, int port,
+                          const char *vhost) {
+  char *s = strdup(url);
+  struct amqp_connection_info ci;
+  int res;
+
+  res = amqp_parse_url(s, &ci);
+  if (res) {
+    fprintf(stderr, "Expected to successfully parse URL, but didn't: %s (%s)\n",
+            url, amqp_error_string2(res));
+    abort();
+  }
+
+  match_string("user", user, ci.user);
+  match_string("password", password, ci.password);
+  match_string("host", host, ci.host);
+  match_int("port", port, ci.port);
+  match_string("vhost", vhost, ci.vhost);
+
+  free(s);
+}
+
+static void parse_fail(const char *url) {
+  char *s = strdup(url);
+  struct amqp_connection_info ci;
+
+  amqp_default_connection_info(&ci);
+  if (amqp_parse_url(s, &ci) >= 0) {
+    fprintf(stderr, "Expected to fail parsing URL, but didn't: %s\n", url);
+    abort();
+  }
+
+  free(s);
+}
+
+int main(void) {
+  /* From the spec */
+  parse_success("amqp://user:pass@host:10000/vhost", "user", "pass", "host",
+                10000, "vhost");
+  parse_success("amqps://user:pass@host:10000/vhost", "user", "pass", "host",
+                10000, "vhost");
+
+  parse_success("amqp://user%61:%61pass@ho%61st:10000/v%2fhost", "usera",
+                "apass", "hoast", 10000, "v/host");
+  parse_success("amqps://user%61:%61pass@ho%61st:10000/v%2fhost", "usera",
+                "apass", "hoast", 10000, "v/host");
+
+  parse_success("amqp://", "guest", "guest", "localhost", 5672, "/");
+  parse_success("amqps://", "guest", "guest", "localhost", 5671, "/");
+
+  parse_success("amqp://:@/", "", "", "localhost", 5672, "");
+  parse_success("amqps://:@/", "", "", "localhost", 5671, "");
+
+  parse_success("amqp://user@", "user", "guest", "localhost", 5672, "/");
+  parse_success("amqps://user@", "user", "guest", "localhost", 5671, "/");
+
+  parse_success("amqp://user:pass@", "user", "pass", "localhost", 5672, "/");
+  parse_success("amqps://user:pass@", "user", "pass", "localhost", 5671, "/");
+
+  parse_success("amqp://host", "guest", "guest", "host", 5672, "/");
+  parse_success("amqps://host", "guest", "guest", "host", 5671, "/");
+
+  parse_success("amqp://:10000", "guest", "guest", "localhost", 10000, "/");
+  parse_success("amqps://:10000", "guest", "guest", "localhost", 10000, "/");
+
+  parse_success("amqp:///vhost", "guest", "guest", "localhost", 5672, "vhost");
+  parse_success("amqps:///vhost", "guest", "guest", "localhost", 5671, "vhost");
+
+  parse_success("amqp://host/", "guest", "guest", "host", 5672, "");
+  parse_success("amqps://host/", "guest", "guest", "host", 5671, "");
+
+  parse_success("amqp://host/%2f", "guest", "guest", "host", 5672, "/");
+  parse_success("amqps://host/%2f", "guest", "guest", "host", 5671, "/");
+
+  parse_success("amqp://[::1]", "guest", "guest", "::1", 5672, "/");
+  parse_success("amqps://[::1]", "guest", "guest", "::1", 5671, "/");
+
+  /* Various other success cases */
+  parse_success("amqp://host:100", "guest", "guest", "host", 100, "/");
+  parse_success("amqps://host:100", "guest", "guest", "host", 100, "/");
+
+  parse_success("amqp://[::1]:100", "guest", "guest", "::1", 100, "/");
+  parse_success("amqps://[::1]:100", "guest", "guest", "::1", 100, "/");
+
+  parse_success("amqp://host/blah", "guest", "guest", "host", 5672, "blah");
+  parse_success("amqps://host/blah", "guest", "guest", "host", 5671, "blah");
+
+  parse_success("amqp://host:100/blah", "guest", "guest", "host", 100, "blah");
+  parse_success("amqps://host:100/blah", "guest", "guest", "host", 100, "blah");
+
+  parse_success("amqp://:100/blah", "guest", "guest", "localhost", 100, "blah");
+  parse_success("amqps://:100/blah", "guest", "guest", "localhost", 100,
+                "blah");
+
+  parse_success("amqp://[::1]/blah", "guest", "guest", "::1", 5672, "blah");
+  parse_success("amqps://[::1]/blah", "guest", "guest", "::1", 5671, "blah");
+
+  parse_success("amqp://[::1]:100/blah", "guest", "guest", "::1", 100, "blah");
+  parse_success("amqps://[::1]:100/blah", "guest", "guest", "::1", 100, "blah");
+
+  parse_success("amqp://user:pass@host", "user", "pass", "host", 5672, "/");
+  parse_success("amqps://user:pass@host", "user", "pass", "host", 5671, "/");
+
+  parse_success("amqp://user:pass@host:100", "user", "pass", "host", 100, "/");
+  parse_success("amqps://user:pass@host:100", "user", "pass", "host", 100, "/");
+
+  parse_success("amqp://user:pass@:100", "user", "pass", "localhost", 100, "/");
+  parse_success("amqps://user:pass@:100", "user", "pass", "localhost", 100,
+                "/");
+
+  parse_success("amqp://user:pass@[::1]", "user", "pass", "::1", 5672, "/");
+  parse_success("amqps://user:pass@[::1]", "user", "pass", "::1", 5671, "/");
+
+  parse_success("amqp://user:pass@[::1]:100", "user", "pass", "::1", 100, "/");
+  parse_success("amqps://user:pass@[::1]:100", "user", "pass", "::1", 100, "/");
+
+  /* Various failure cases */
+  parse_fail("http://www.rabbitmq.com");
+
+  parse_fail("amqp://foo:bar:baz");
+  parse_fail("amqps://foo:bar:baz");
+
+  parse_fail("amqp://foo[::1]");
+  parse_fail("amqps://foo[::1]");
+
+  parse_fail("amqp://foo[::1]");
+  parse_fail("amqps://foo[::1]");
+
+  parse_fail("amqp://foo:[::1]");
+  parse_fail("amqps://foo:[::1]");
+
+  parse_fail("amqp://[::1]foo");
+  parse_fail("amqps://[::1]foo");
+
+  parse_fail("amqp://foo:1000xyz");
+  parse_fail("amqps://foo:1000xyz");
+
+  parse_fail("amqp://foo:1000000");
+  parse_fail("amqps://foo:1000000");
+
+  parse_fail("amqp://foo/bar/baz");
+  parse_fail("amqps://foo/bar/baz");
+
+  parse_fail("amqp://foo%1");
+  parse_fail("amqps://foo%1");
+
+  parse_fail("amqp://foo%1x");
+  parse_fail("amqps://foo%1x");
+
+  parse_fail("amqp://foo%xy");
+  parse_fail("amqps://foo%xy");
+
+  return 0;
+}

+ 70 - 0
ext/librabbitmq/tests/test_sasl_mechanism.c

@@ -0,0 +1,70 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <amqp_socket.h>
+
+static void parse_success(amqp_bytes_t mechanisms,
+                          amqp_sasl_method_enum method) {
+  if (!sasl_mechanism_in_list(mechanisms, method)) {
+    fprintf(stderr, "Expected to find mechanism in list, but didn't: %s\n",
+            (char *)mechanisms.bytes);
+    abort();
+  }
+}
+
+static void parse_fail(amqp_bytes_t mechanisms, amqp_sasl_method_enum method) {
+  if (sasl_mechanism_in_list(mechanisms, method)) {
+    fprintf(stderr,
+            "Expected the mechanism not on the list, but it was present: %s\n",
+            (char *)mechanisms.bytes);
+    abort();
+  }
+}
+
+int main(void) {
+  parse_success(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 LOGIN PLAIN"),
+                AMQP_SASL_METHOD_PLAIN);
+  parse_fail(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 LOGIN PLAIN"),
+             AMQP_SASL_METHOD_EXTERNAL);
+  parse_success(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 EXTERNAL"),
+                AMQP_SASL_METHOD_EXTERNAL);
+  parse_fail(amqp_cstring_bytes("DIGEST-MD5 CRAM-MD5 EXTERNAL"),
+             AMQP_SASL_METHOD_PLAIN);
+  return 0;
+}

+ 52 - 0
ext/librabbitmq/tests/test_status_enum.c

@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 Alan Antonuk
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "amqp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void check_errorstrings(amqp_status_enum start, amqp_status_enum end) {
+  int i;
+  for (i = start; i > end; --i) {
+    const char* err = amqp_error_string2(i);
+    if (0 == strcmp(err, "(unknown error)")) {
+      printf("amqp_status_enum value %s%X", i < 0 ? "-" : "", (unsigned)i);
+      abort();
+    }
+  }
+}
+
+int main(void) {
+  check_errorstrings(AMQP_STATUS_OK, _AMQP_STATUS_NEXT_VALUE);
+  check_errorstrings(AMQP_STATUS_TCP_ERROR, _AMQP_STATUS_TCP_NEXT_VALUE);
+  check_errorstrings(AMQP_STATUS_SSL_ERROR, _AMQP_STATUS_SSL_NEXT_VALUE);
+
+  return 0;
+}

+ 466 - 0
ext/librabbitmq/tests/test_tables.c

@@ -0,0 +1,466 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef _MSC_VER
+#define _USE_MATH_DEFINES
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <inttypes.h>
+
+#include <amqp.h>
+
+#include <math.h>
+
+void die(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fprintf(stderr, "\n");
+  abort();
+}
+
+static void dump_indent(int indent, FILE *out) {
+  int i;
+
+  for (i = 0; i < indent; i++) {
+    fputc(' ', out);
+  }
+}
+
+static void dump_value(int indent, amqp_field_value_t v, FILE *out) {
+  int i;
+
+  dump_indent(indent, out);
+  fputc(v.kind, out);
+
+  switch (v.kind) {
+    case AMQP_FIELD_KIND_BOOLEAN:
+      fputs(v.value.boolean ? " true\n" : " false\n", out);
+      break;
+
+    case AMQP_FIELD_KIND_I8:
+      fprintf(out, " %" PRId8 "\n", v.value.i8);
+      break;
+
+    case AMQP_FIELD_KIND_U8:
+      fprintf(out, " %" PRIu8 "\n", v.value.u8);
+      break;
+
+    case AMQP_FIELD_KIND_I16:
+      fprintf(out, " %" PRId16 "\n", v.value.i16);
+      break;
+
+    case AMQP_FIELD_KIND_U16:
+      fprintf(out, " %" PRIu16 "\n", v.value.u16);
+      break;
+
+    case AMQP_FIELD_KIND_I32:
+      fprintf(out, " %" PRId32 "\n", v.value.i32);
+      break;
+
+    case AMQP_FIELD_KIND_U32:
+      fprintf(out, " %" PRIu32 "\n", v.value.u32);
+      break;
+
+    case AMQP_FIELD_KIND_I64:
+      fprintf(out, " %" PRId64 "\n", v.value.i64);
+      break;
+
+    case AMQP_FIELD_KIND_F32:
+      fprintf(out, " %g\n", (double)v.value.f32);
+      break;
+
+    case AMQP_FIELD_KIND_F64:
+      fprintf(out, " %g\n", v.value.f64);
+      break;
+
+    case AMQP_FIELD_KIND_DECIMAL:
+      fprintf(out, " %u:::%u\n", v.value.decimal.decimals,
+              v.value.decimal.value);
+      break;
+
+    case AMQP_FIELD_KIND_UTF8:
+      fprintf(out, " %.*s\n", (int)v.value.bytes.len,
+              (char *)v.value.bytes.bytes);
+      break;
+
+    case AMQP_FIELD_KIND_BYTES:
+      fputc(' ', out);
+      for (i = 0; i < (int)v.value.bytes.len; i++) {
+        fprintf(out, "%02x", ((char *)v.value.bytes.bytes)[i]);
+      }
+
+      fputc('\n', out);
+      break;
+
+    case AMQP_FIELD_KIND_ARRAY:
+      fputc('\n', out);
+      for (i = 0; i < v.value.array.num_entries; i++) {
+        dump_value(indent + 2, v.value.array.entries[i], out);
+      }
+
+      break;
+
+    case AMQP_FIELD_KIND_TIMESTAMP:
+      fprintf(out, " %" PRIu64 "\n", v.value.u64);
+      break;
+
+    case AMQP_FIELD_KIND_TABLE:
+      fputc('\n', out);
+      for (i = 0; i < v.value.table.num_entries; i++) {
+        dump_indent(indent + 2, out);
+        fprintf(out, "%.*s ->\n", (int)v.value.table.entries[i].key.len,
+                (char *)v.value.table.entries[i].key.bytes);
+        dump_value(indent + 4, v.value.table.entries[i].value, out);
+      }
+
+      break;
+
+    case AMQP_FIELD_KIND_VOID:
+      fputc('\n', out);
+      break;
+
+    default:
+      fprintf(out, "???\n");
+      break;
+  }
+}
+
+static void test_dump_value(FILE *out) {
+  amqp_table_entry_t entries[8];
+  amqp_table_t table;
+  amqp_field_value_t val;
+
+  entries[0].key = amqp_cstring_bytes("zebra");
+  entries[0].value.kind = AMQP_FIELD_KIND_UTF8;
+  entries[0].value.value.bytes = amqp_cstring_bytes("last");
+
+  entries[1].key = amqp_cstring_bytes("aardvark");
+  entries[1].value.kind = AMQP_FIELD_KIND_UTF8;
+  entries[1].value.value.bytes = amqp_cstring_bytes("first");
+
+  entries[2].key = amqp_cstring_bytes("middle");
+  entries[2].value.kind = AMQP_FIELD_KIND_UTF8;
+  entries[2].value.value.bytes = amqp_cstring_bytes("third");
+
+  entries[3].key = amqp_cstring_bytes("number");
+  entries[3].value.kind = AMQP_FIELD_KIND_I32;
+  entries[3].value.value.i32 = 1234;
+
+  entries[4].key = amqp_cstring_bytes("decimal");
+  entries[4].value.kind = AMQP_FIELD_KIND_DECIMAL;
+  entries[4].value.value.decimal.decimals = 2;
+  entries[4].value.value.decimal.value = 1234;
+
+  entries[5].key = amqp_cstring_bytes("time");
+  entries[5].value.kind = AMQP_FIELD_KIND_TIMESTAMP;
+  entries[5].value.value.u64 = 1234123412341234;
+
+  entries[6].key = amqp_cstring_bytes("beta");
+  entries[6].value.kind = AMQP_FIELD_KIND_UTF8;
+  entries[6].value.value.bytes = amqp_cstring_bytes("second");
+
+  entries[7].key = amqp_cstring_bytes("wombat");
+  entries[7].value.kind = AMQP_FIELD_KIND_UTF8;
+  entries[7].value.value.bytes = amqp_cstring_bytes("fourth");
+
+  table.num_entries = 8;
+  table.entries = entries;
+
+  qsort(table.entries, table.num_entries, sizeof(amqp_table_entry_t),
+        &amqp_table_entry_cmp);
+
+  val.kind = AMQP_FIELD_KIND_TABLE;
+  val.value.table = table;
+
+  dump_value(0, val, out);
+}
+
+static uint8_t pre_encoded_table[] = {
+    0x00, 0x00, 0x00, 0xff, 0x07, 0x6c, 0x6f, 0x6e, 0x67, 0x73, 0x74, 0x72,
+    0x53, 0x00, 0x00, 0x00, 0x15, 0x48, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73,
+    0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69,
+    0x6e, 0x67, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x69, 0x6e, 0x74,
+    0x49, 0x00, 0x00, 0x30, 0x39, 0x07, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61,
+    0x6c, 0x44, 0x03, 0x00, 0x01, 0xe2, 0x40, 0x09, 0x74, 0x69, 0x6d, 0x65,
+    0x73, 0x74, 0x61, 0x6d, 0x70, 0x54, 0x00, 0x00, 0x63, 0xee, 0xa0, 0x53,
+    0xc1, 0x94, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x00, 0x00, 0x00,
+    0x1f, 0x03, 0x6f, 0x6e, 0x65, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x03, 0x74,
+    0x77, 0x6f, 0x53, 0x00, 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e,
+    0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x04, 0x62, 0x79, 0x74,
+    0x65, 0x62, 0xff, 0x04, 0x6c, 0x6f, 0x6e, 0x67, 0x6c, 0x00, 0x00, 0x00,
+    0x00, 0x49, 0x96, 0x02, 0xd2, 0x05, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x73,
+    0x02, 0x8f, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x74, 0x01, 0x06, 0x62, 0x69,
+    0x6e, 0x61, 0x72, 0x79, 0x78, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x20, 0x62,
+    0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+    0x04, 0x76, 0x6f, 0x69, 0x64, 0x56, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79,
+    0x41, 0x00, 0x00, 0x00, 0x17, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x53, 0x00,
+    0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74,
+    0x72, 0x69, 0x6e, 0x67, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x66, 0x40,
+    0x49, 0x0f, 0xdb, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x64, 0x40,
+    0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18};
+
+static void test_table_codec(FILE *out) {
+  amqp_pool_t pool;
+  int result;
+
+  amqp_table_entry_t inner_entries[2];
+  amqp_table_t inner_table;
+
+  amqp_field_value_t inner_values[2];
+  amqp_array_t inner_array;
+
+  amqp_table_entry_t entries[14];
+  amqp_table_t table;
+
+  inner_entries[0].key = amqp_cstring_bytes("one");
+  inner_entries[0].value.kind = AMQP_FIELD_KIND_I32;
+  inner_entries[0].value.value.i32 = 54321;
+
+  inner_entries[1].key = amqp_cstring_bytes("two");
+  inner_entries[1].value.kind = AMQP_FIELD_KIND_UTF8;
+  inner_entries[1].value.value.bytes = amqp_cstring_bytes("A long string");
+
+  inner_table.num_entries = 2;
+  inner_table.entries = inner_entries;
+
+  inner_values[0].kind = AMQP_FIELD_KIND_I32;
+  inner_values[0].value.i32 = 54321;
+
+  inner_values[1].kind = AMQP_FIELD_KIND_UTF8;
+  inner_values[1].value.bytes = amqp_cstring_bytes("A long string");
+
+  inner_array.num_entries = 2;
+  inner_array.entries = inner_values;
+
+  entries[0].key = amqp_cstring_bytes("longstr");
+  entries[0].value.kind = AMQP_FIELD_KIND_UTF8;
+  entries[0].value.value.bytes = amqp_cstring_bytes("Here is a long string");
+
+  entries[1].key = amqp_cstring_bytes("signedint");
+  entries[1].value.kind = AMQP_FIELD_KIND_I32;
+  entries[1].value.value.i32 = 12345;
+
+  entries[2].key = amqp_cstring_bytes("decimal");
+  entries[2].value.kind = AMQP_FIELD_KIND_DECIMAL;
+  entries[2].value.value.decimal.decimals = 3;
+  entries[2].value.value.decimal.value = 123456;
+
+  entries[3].key = amqp_cstring_bytes("timestamp");
+  entries[3].value.kind = AMQP_FIELD_KIND_TIMESTAMP;
+  entries[3].value.value.u64 = 109876543209876;
+
+  entries[4].key = amqp_cstring_bytes("table");
+  entries[4].value.kind = AMQP_FIELD_KIND_TABLE;
+  entries[4].value.value.table = inner_table;
+
+  entries[5].key = amqp_cstring_bytes("byte");
+  entries[5].value.kind = AMQP_FIELD_KIND_I8;
+  entries[5].value.value.i8 = (int8_t)-1;
+
+  entries[6].key = amqp_cstring_bytes("long");
+  entries[6].value.kind = AMQP_FIELD_KIND_I64;
+  entries[6].value.value.i64 = 1234567890;
+
+  entries[7].key = amqp_cstring_bytes("short");
+  entries[7].value.kind = AMQP_FIELD_KIND_I16;
+  entries[7].value.value.i16 = 655;
+
+  entries[8].key = amqp_cstring_bytes("bool");
+  entries[8].value.kind = AMQP_FIELD_KIND_BOOLEAN;
+  entries[8].value.value.boolean = 1;
+
+  entries[9].key = amqp_cstring_bytes("binary");
+  entries[9].value.kind = AMQP_FIELD_KIND_BYTES;
+  entries[9].value.value.bytes = amqp_cstring_bytes("a binary string");
+
+  entries[10].key = amqp_cstring_bytes("void");
+  entries[10].value.kind = AMQP_FIELD_KIND_VOID;
+
+  entries[11].key = amqp_cstring_bytes("array");
+  entries[11].value.kind = AMQP_FIELD_KIND_ARRAY;
+  entries[11].value.value.array = inner_array;
+
+  entries[12].key = amqp_cstring_bytes("float");
+  entries[12].value.kind = AMQP_FIELD_KIND_F32;
+  entries[12].value.value.f32 = (float)M_PI;
+
+  entries[13].key = amqp_cstring_bytes("double");
+  entries[13].value.kind = AMQP_FIELD_KIND_F64;
+  entries[13].value.value.f64 = M_PI;
+
+  table.num_entries = 14;
+  table.entries = entries;
+
+  fprintf(out, "AAAAAAAAAA\n");
+
+  {
+    amqp_field_value_t val;
+    val.kind = AMQP_FIELD_KIND_TABLE;
+    val.value.table = table;
+    dump_value(0, val, out);
+  }
+
+  init_amqp_pool(&pool, 4096);
+
+  {
+    amqp_table_t decoded;
+    size_t decoding_offset = 0;
+    amqp_bytes_t decoding_bytes;
+    decoding_bytes.len = sizeof(pre_encoded_table);
+    decoding_bytes.bytes = pre_encoded_table;
+
+    result =
+        amqp_decode_table(decoding_bytes, &pool, &decoded, &decoding_offset);
+    if (result < 0) {
+      die("Table decoding failed: %s", amqp_error_string2(result));
+    }
+
+    fprintf(out, "BBBBBBBBBB\n");
+
+    {
+      amqp_field_value_t val;
+      val.kind = AMQP_FIELD_KIND_TABLE;
+      val.value.table = decoded;
+
+      dump_value(0, val, out);
+    }
+  }
+
+  {
+    uint8_t encoding_buffer[4096];
+    amqp_bytes_t encoding_result;
+    size_t offset = 0;
+
+    memset(&encoding_buffer[0], 0, sizeof(encoding_buffer));
+    encoding_result.len = sizeof(encoding_buffer);
+    encoding_result.bytes = &encoding_buffer[0];
+
+    result = amqp_encode_table(encoding_result, &table, &offset);
+    if (result < 0) {
+      die("Table encoding failed: %s", amqp_error_string2(result));
+    }
+
+    if (offset != sizeof(pre_encoded_table))
+      die("Offset should be %ld, was %ld", (long)sizeof(pre_encoded_table),
+          (long)offset);
+
+    result = memcmp(pre_encoded_table, encoding_buffer, offset);
+    if (result != 0) {
+      die("Table encoding differed", result);
+    }
+  }
+
+  empty_amqp_pool(&pool);
+}
+
+#define CHUNK_SIZE 4096
+
+static int compare_files(FILE *f1_in, FILE *f2_in) {
+  char f1_buf[CHUNK_SIZE];
+  char f2_buf[CHUNK_SIZE];
+  int res;
+
+  rewind(f1_in);
+  rewind(f2_in);
+
+  for (;;) {
+    size_t f1_got = fread(f1_buf, 1, CHUNK_SIZE, f1_in);
+    size_t f2_got = fread(f2_buf, 1, CHUNK_SIZE, f2_in);
+    res = memcmp(f1_buf, f2_buf, f1_got < f2_got ? f1_got : f2_got);
+
+    if (res) {
+      break;
+    }
+
+    if (f1_got < CHUNK_SIZE || f2_got < CHUNK_SIZE) {
+      if (f1_got != f2_got) {
+        res = (f1_got < f2_got ? -1 : 1);
+      }
+      break;
+    }
+  }
+
+  return res;
+}
+
+const char *expected_file_name = "tests/test_tables.expected";
+
+int main(void) {
+  char *srcdir = getenv("srcdir");
+  FILE *out, *expected = NULL;
+  char *expected_path;
+
+  out = tmpfile();
+  if (out == NULL) {
+    die("failed to create temporary file: %s", strerror(errno));
+  }
+
+  test_table_codec(out);
+  fprintf(out, "----------\n");
+  test_dump_value(out);
+
+  if (srcdir == NULL) {
+    srcdir = ".";
+  }
+
+  expected_path = malloc(strlen(srcdir) + strlen(expected_file_name) + 2);
+  if (!expected_path) {
+    die("out of memory");
+  }
+  sprintf(expected_path, "%s/%s", srcdir, expected_file_name);
+  expected = fopen(expected_path, "r");
+  if (!expected) {
+    die("failed to open %s: %s", expected_path, strerror(errno));
+  }
+
+  if (compare_files(expected, out)) {
+    die("output file did not have expected contents");
+  }
+
+  fclose(expected);
+  free(expected_path);
+  fclose(out);
+
+  return 0;
+}

+ 90 - 0
ext/librabbitmq/tests/test_tables.expected

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

+ 304 - 0
ext/librabbitmq/tests/win32/msinttypes/inttypes.h

@@ -0,0 +1,304 @@
+// ISO C9x  compliant inttypes.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+//  Copyright (c) 2006 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+//
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+//
+//   3. The name of the author may be used to endorse or promote products
+//      derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER  // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif  // _MSC_VER ]
+
+#ifndef _MSC_INTTYPES_H_  // [
+#define _MSC_INTTYPES_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include "stdint.h"
+
+// 7.8 Format conversion of integer types
+
+typedef struct {
+  intmax_t quot;
+  intmax_t rem;
+} imaxdiv_t;
+
+// 7.8.1 Macros for format specifiers
+
+#if !defined(__cplusplus) || \
+    defined(__STDC_FORMAT_MACROS)  // [   See footnote 185 at page 198
+
+// The fprintf macros for signed integers are:
+#define PRId8 "d"
+#define PRIi8 "i"
+#define PRIdLEAST8 "d"
+#define PRIiLEAST8 "i"
+#define PRIdFAST8 "d"
+#define PRIiFAST8 "i"
+
+#define PRId16 "hd"
+#define PRIi16 "hi"
+#define PRIdLEAST16 "hd"
+#define PRIiLEAST16 "hi"
+#define PRIdFAST16 "hd"
+#define PRIiFAST16 "hi"
+
+#define PRId32 "I32d"
+#define PRIi32 "I32i"
+#define PRIdLEAST32 "I32d"
+#define PRIiLEAST32 "I32i"
+#define PRIdFAST32 "I32d"
+#define PRIiFAST32 "I32i"
+
+#define PRId64 "I64d"
+#define PRIi64 "I64i"
+#define PRIdLEAST64 "I64d"
+#define PRIiLEAST64 "I64i"
+#define PRIdFAST64 "I64d"
+#define PRIiFAST64 "I64i"
+
+#define PRIdMAX "I64d"
+#define PRIiMAX "I64i"
+
+#define PRIdPTR "Id"
+#define PRIiPTR "Ii"
+
+// The fprintf macros for unsigned integers are:
+#define PRIo8 "o"
+#define PRIu8 "u"
+#define PRIx8 "x"
+#define PRIX8 "X"
+#define PRIoLEAST8 "o"
+#define PRIuLEAST8 "u"
+#define PRIxLEAST8 "x"
+#define PRIXLEAST8 "X"
+#define PRIoFAST8 "o"
+#define PRIuFAST8 "u"
+#define PRIxFAST8 "x"
+#define PRIXFAST8 "X"
+
+#define PRIo16 "ho"
+#define PRIu16 "hu"
+#define PRIx16 "hx"
+#define PRIX16 "hX"
+#define PRIoLEAST16 "ho"
+#define PRIuLEAST16 "hu"
+#define PRIxLEAST16 "hx"
+#define PRIXLEAST16 "hX"
+#define PRIoFAST16 "ho"
+#define PRIuFAST16 "hu"
+#define PRIxFAST16 "hx"
+#define PRIXFAST16 "hX"
+
+#define PRIo32 "I32o"
+#define PRIu32 "I32u"
+#define PRIx32 "I32x"
+#define PRIX32 "I32X"
+#define PRIoLEAST32 "I32o"
+#define PRIuLEAST32 "I32u"
+#define PRIxLEAST32 "I32x"
+#define PRIXLEAST32 "I32X"
+#define PRIoFAST32 "I32o"
+#define PRIuFAST32 "I32u"
+#define PRIxFAST32 "I32x"
+#define PRIXFAST32 "I32X"
+
+#define PRIo64 "I64o"
+#define PRIu64 "I64u"
+#define PRIx64 "I64x"
+#define PRIX64 "I64X"
+#define PRIoLEAST64 "I64o"
+#define PRIuLEAST64 "I64u"
+#define PRIxLEAST64 "I64x"
+#define PRIXLEAST64 "I64X"
+#define PRIoFAST64 "I64o"
+#define PRIuFAST64 "I64u"
+#define PRIxFAST64 "I64x"
+#define PRIXFAST64 "I64X"
+
+#define PRIoMAX "I64o"
+#define PRIuMAX "I64u"
+#define PRIxMAX "I64x"
+#define PRIXMAX "I64X"
+
+#define PRIoPTR "Io"
+#define PRIuPTR "Iu"
+#define PRIxPTR "Ix"
+#define PRIXPTR "IX"
+
+// The fscanf macros for signed integers are:
+#define SCNd8 "d"
+#define SCNi8 "i"
+#define SCNdLEAST8 "d"
+#define SCNiLEAST8 "i"
+#define SCNdFAST8 "d"
+#define SCNiFAST8 "i"
+
+#define SCNd16 "hd"
+#define SCNi16 "hi"
+#define SCNdLEAST16 "hd"
+#define SCNiLEAST16 "hi"
+#define SCNdFAST16 "hd"
+#define SCNiFAST16 "hi"
+
+#define SCNd32 "ld"
+#define SCNi32 "li"
+#define SCNdLEAST32 "ld"
+#define SCNiLEAST32 "li"
+#define SCNdFAST32 "ld"
+#define SCNiFAST32 "li"
+
+#define SCNd64 "I64d"
+#define SCNi64 "I64i"
+#define SCNdLEAST64 "I64d"
+#define SCNiLEAST64 "I64i"
+#define SCNdFAST64 "I64d"
+#define SCNiFAST64 "I64i"
+
+#define SCNdMAX "I64d"
+#define SCNiMAX "I64i"
+
+#ifdef _WIN64  // [
+#define SCNdPTR "I64d"
+#define SCNiPTR "I64i"
+#else  // _WIN64 ][
+#define SCNdPTR "ld"
+#define SCNiPTR "li"
+#endif  // _WIN64 ]
+
+// The fscanf macros for unsigned integers are:
+#define SCNo8 "o"
+#define SCNu8 "u"
+#define SCNx8 "x"
+#define SCNX8 "X"
+#define SCNoLEAST8 "o"
+#define SCNuLEAST8 "u"
+#define SCNxLEAST8 "x"
+#define SCNXLEAST8 "X"
+#define SCNoFAST8 "o"
+#define SCNuFAST8 "u"
+#define SCNxFAST8 "x"
+#define SCNXFAST8 "X"
+
+#define SCNo16 "ho"
+#define SCNu16 "hu"
+#define SCNx16 "hx"
+#define SCNX16 "hX"
+#define SCNoLEAST16 "ho"
+#define SCNuLEAST16 "hu"
+#define SCNxLEAST16 "hx"
+#define SCNXLEAST16 "hX"
+#define SCNoFAST16 "ho"
+#define SCNuFAST16 "hu"
+#define SCNxFAST16 "hx"
+#define SCNXFAST16 "hX"
+
+#define SCNo32 "lo"
+#define SCNu32 "lu"
+#define SCNx32 "lx"
+#define SCNX32 "lX"
+#define SCNoLEAST32 "lo"
+#define SCNuLEAST32 "lu"
+#define SCNxLEAST32 "lx"
+#define SCNXLEAST32 "lX"
+#define SCNoFAST32 "lo"
+#define SCNuFAST32 "lu"
+#define SCNxFAST32 "lx"
+#define SCNXFAST32 "lX"
+
+#define SCNo64 "I64o"
+#define SCNu64 "I64u"
+#define SCNx64 "I64x"
+#define SCNX64 "I64X"
+#define SCNoLEAST64 "I64o"
+#define SCNuLEAST64 "I64u"
+#define SCNxLEAST64 "I64x"
+#define SCNXLEAST64 "I64X"
+#define SCNoFAST64 "I64o"
+#define SCNuFAST64 "I64u"
+#define SCNxFAST64 "I64x"
+#define SCNXFAST64 "I64X"
+
+#define SCNoMAX "I64o"
+#define SCNuMAX "I64u"
+#define SCNxMAX "I64x"
+#define SCNXMAX "I64X"
+
+#ifdef _WIN64  // [
+#define SCNoPTR "I64o"
+#define SCNuPTR "I64u"
+#define SCNxPTR "I64x"
+#define SCNXPTR "I64X"
+#else  // _WIN64 ][
+#define SCNoPTR "lo"
+#define SCNuPTR "lu"
+#define SCNxPTR "lx"
+#define SCNXPTR "lX"
+#endif  // _WIN64 ]
+
+#endif  // __STDC_FORMAT_MACROS ]
+
+// 7.8.2 Functions for greatest-width integer types
+
+// 7.8.2.1 The imaxabs function
+#define imaxabs _abs64
+
+// 7.8.2.2 The imaxdiv function
+
+// This is modified version of div() function from Microsoft's div.c found
+// in %MSVC.NET%\crt\src\div.c
+#ifdef STATIC_IMAXDIV  // [
+static
+#else   // STATIC_IMAXDIV ][
+_inline
+#endif  // STATIC_IMAXDIV ]
+    imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) {
+  imaxdiv_t result;
+
+  result.quot = numer / denom;
+  result.rem = numer % denom;
+
+  if (numer < 0 && result.rem > 0) {
+    // did division wrong; must fix up
+    ++result.quot;
+    result.rem -= denom;
+  }
+
+  return result;
+}
+
+// 7.8.2.3 The strtoimax and strtoumax functions
+#define strtoimax _strtoi64
+#define strtoumax _strtoui64
+
+// 7.8.2.4 The wcstoimax and wcstoumax functions
+#define wcstoimax _wcstoi64
+#define wcstoumax _wcstoui64
+
+#endif  // _MSC_INTTYPES_H_ ]

+ 82 - 0
ext/librabbitmq/tools/CMakeLists.txt

@@ -0,0 +1,82 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${LIBRABBITMQ_INCLUDE_DIRS} ${POPT_INCLUDE_DIR})
+
+if (WIN32)
+    set(PLATFORM_DIR win32)
+    set(PLATFORM_SRCS
+        win32/compat.c
+        )
+else (WIN32)
+    set(PLATFORM_DIR unix)
+endif (WIN32)
+
+include_directories(${PLATFORM_DIR})
+
+set(COMMON_SRCS
+    common.h
+    common.c
+    ${PLATFORM_SRCS}
+    )
+
+add_executable(amqp-publish publish.c ${COMMON_SRCS})
+target_link_libraries(amqp-publish ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY})
+
+add_executable(amqp-get get.c ${COMMON_SRCS})
+target_link_libraries(amqp-get ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY})
+
+add_executable(amqp-consume consume.c ${PLATFORM_DIR}/process.c ${COMMON_SRCS})
+target_link_libraries(amqp-consume ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY})
+
+add_executable(amqp-declare-queue declare_queue.c ${COMMON_SRCS})
+target_link_libraries(amqp-declare-queue ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY})
+
+add_executable(amqp-delete-queue delete_queue.c ${COMMON_SRCS})
+target_link_libraries(amqp-delete-queue ${RMQ_LIBRARY_TARGET} ${POPT_LIBRARY})
+
+if (BUILD_TOOLS_DOCS)
+  if (XMLTO_FOUND)
+    set(DOCS_SRCS
+      doc/amqp-consume.xml
+      doc/amqp-declare-queue.xml
+      doc/amqp-delete-queue.xml
+      doc/amqp-get.xml
+      doc/amqp-publish.xml
+      doc/librabbitmq-tools.xml
+      )
+
+    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc)
+    set(XMLTO_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/doc/man-date.ent)
+    add_custom_command(
+            OUTPUT ${XMLTO_DEPENDS}
+	          COMMAND date +'%Y-%m-%d' > ${XMLTO_DEPENDS}
+            VERBATIM
+            )
+
+    set(XMLTO_COMMAND_ARGS --skip-validation --searchpath "${CMAKE_CURRENT_BINARY_DIR}/doc")
+
+    XMLTO(${DOCS_SRCS}
+      MODES man
+      ALL)
+
+    foreach(file ${XMLTO_FILES_man})
+      get_filename_component(fileExt ${file} EXT)
+      string( REGEX REPLACE "^[.]" "" fileExt ${fileExt} )
+      install(
+        FILES ${file}
+        DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man${fileExt}
+        )
+    endforeach()
+
+  else(XMLTO_FOUND)
+    message(WARNING "xmlto not found, will not build tools documentation")
+  endif(XMLTO_FOUND)
+endif()
+
+if (ENABLE_SSL_SUPPORT)
+  add_definitions(-DWITH_SSL=1)
+endif()
+
+install(TARGETS amqp-publish amqp-get amqp-consume amqp-declare-queue amqp-delete-queue
+	RUNTIME DESTINATION bin
+	LIBRARY DESTINATION lib
+	ARCHIVE DESTINATION lib
+	PUBLIC_HEADER DESTINATION include)

+ 444 - 0
ext/librabbitmq/tools/common.c

@@ -0,0 +1,444 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "common.h"
+#ifdef WITH_SSL
+#include <amqp_ssl_socket.h>
+#endif
+#include <amqp_tcp_socket.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef WINDOWS
+#include "compat.h"
+#endif
+
+void die(const char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+void die_errno(int err, const char *fmt, ...) {
+  va_list ap;
+
+  if (err == 0) {
+    return;
+  }
+
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fprintf(stderr, ": %s\n", strerror(err));
+  exit(1);
+}
+
+void die_amqp_error(int err, const char *fmt, ...) {
+  va_list ap;
+
+  if (err >= 0) {
+    return;
+  }
+
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fprintf(stderr, ": %s\n", amqp_error_string2(err));
+  exit(1);
+}
+
+const char *amqp_server_exception_string(amqp_rpc_reply_t r) {
+  int res;
+  static char s[512];
+
+  switch (r.reply.id) {
+    case AMQP_CONNECTION_CLOSE_METHOD: {
+      amqp_connection_close_t *m = (amqp_connection_close_t *)r.reply.decoded;
+      res = snprintf(s, sizeof(s), "server connection error %d, message: %.*s",
+                     m->reply_code, (int)m->reply_text.len,
+                     (char *)m->reply_text.bytes);
+      break;
+    }
+
+    case AMQP_CHANNEL_CLOSE_METHOD: {
+      amqp_channel_close_t *m = (amqp_channel_close_t *)r.reply.decoded;
+      res = snprintf(s, sizeof(s), "server channel error %d, message: %.*s",
+                     m->reply_code, (int)m->reply_text.len,
+                     (char *)m->reply_text.bytes);
+      break;
+    }
+
+    default:
+      res = snprintf(s, sizeof(s), "unknown server error, method id 0x%08X",
+                     r.reply.id);
+      break;
+  }
+
+  return res >= 0 ? s : NULL;
+}
+
+const char *amqp_rpc_reply_string(amqp_rpc_reply_t r) {
+  switch (r.reply_type) {
+    case AMQP_RESPONSE_NORMAL:
+      return "normal response";
+
+    case AMQP_RESPONSE_NONE:
+      return "missing RPC reply type";
+
+    case AMQP_RESPONSE_LIBRARY_EXCEPTION:
+      return amqp_error_string2(r.library_error);
+
+    case AMQP_RESPONSE_SERVER_EXCEPTION:
+      return amqp_server_exception_string(r);
+
+    default:
+      abort();
+  }
+}
+
+void die_rpc(amqp_rpc_reply_t r, const char *fmt, ...) {
+  va_list ap;
+
+  if (r.reply_type == AMQP_RESPONSE_NORMAL) {
+    return;
+  }
+
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fprintf(stderr, ": %s\n", amqp_rpc_reply_string(r));
+  exit(1);
+}
+
+static char *amqp_url;
+static char *amqp_server;
+static int amqp_port = -1;
+static char *amqp_vhost;
+static char *amqp_username;
+static char *amqp_password;
+static int amqp_heartbeat = 0;
+#ifdef WITH_SSL
+static int amqp_ssl = 0;
+static char *amqp_cacert = "/etc/ssl/certs/cacert.pem";
+static char *amqp_key = NULL;
+static char *amqp_cert = NULL;
+#endif /* WITH_SSL */
+
+const char *connect_options_title = "Connection options";
+struct poptOption connect_options[] = {
+    {"url", 'u', POPT_ARG_STRING, &amqp_url, 0, "the AMQP URL to connect to",
+     "amqp://..."},
+    {"server", 's', POPT_ARG_STRING, &amqp_server, 0,
+     "the AMQP server to connect to", "hostname"},
+    {"port", 0, POPT_ARG_INT, &amqp_port, 0, "the port to connect on", "port"},
+    {"vhost", 0, POPT_ARG_STRING, &amqp_vhost, 0,
+     "the vhost to use when connecting", "vhost"},
+    {"username", 0, POPT_ARG_STRING, &amqp_username, 0,
+     "the username to login with", "username"},
+    {"password", 0, POPT_ARG_STRING, &amqp_password, 0,
+     "the password to login with", "password"},
+    {"heartbeat", 0, POPT_ARG_INT, &amqp_heartbeat, 0,
+     "heartbeat interval, set to 0 to disable", "heartbeat"},
+#ifdef WITH_SSL
+    {"ssl", 0, POPT_ARG_NONE, &amqp_ssl, 0, "connect over SSL/TLS", NULL},
+    {"cacert", 0, POPT_ARG_STRING, &amqp_cacert, 0,
+     "path to the CA certificate file", "cacert.pem"},
+    {"key", 0, POPT_ARG_STRING, &amqp_key, 0,
+     "path to the client private key file", "key.pem"},
+    {"cert", 0, POPT_ARG_STRING, &amqp_cert, 0,
+     "path to the client certificate file", "cert.pem"},
+#endif /* WITH_SSL */
+    {NULL, '\0', 0, NULL, 0, NULL, NULL}};
+
+static void init_connection_info(struct amqp_connection_info *ci) {
+  ci->user = NULL;
+  ci->password = NULL;
+  ci->host = NULL;
+  ci->port = -1;
+  ci->vhost = NULL;
+  ci->user = NULL;
+
+  amqp_default_connection_info(ci);
+
+  if (amqp_url)
+    die_amqp_error(amqp_parse_url(strdup(amqp_url), ci), "Parsing URL '%s'",
+                   amqp_url);
+
+  if (amqp_server) {
+    char *colon;
+    if (amqp_url) {
+      die("--server and --url options cannot be used at the same time");
+    }
+
+    /* parse the server string into a hostname and a port */
+    colon = strchr(amqp_server, ':');
+    if (colon) {
+      char *port_end;
+      size_t host_len;
+
+      /* Deprecate specifying the port number with the
+         --server option, because it is not ipv6 friendly.
+         --url now allows connection options to be
+         specified concisely. */
+      fprintf(stderr,
+              "Specifying the port number with --server is deprecated\n");
+
+      host_len = colon - amqp_server;
+      ci->host = malloc(host_len + 1);
+      memcpy(ci->host, amqp_server, host_len);
+      ci->host[host_len] = 0;
+
+      if (amqp_port >= 0) {
+        die("both --server and --port options specify server port");
+      }
+
+      ci->port = strtol(colon + 1, &port_end, 10);
+      if (ci->port < 0 || ci->port > 65535 || port_end == colon + 1 ||
+          *port_end != 0)
+        die("bad server port number in '%s'", amqp_server);
+    }
+
+#if WITH_SSL
+    if (amqp_ssl && !ci->ssl) {
+      die("the --ssl option specifies an SSL connection"
+          " but the --url option does not");
+    }
+#endif
+  }
+
+  if (amqp_port >= 0) {
+    if (amqp_url) {
+      die("--port and --url options cannot be used at the same time");
+    }
+
+    ci->port = amqp_port;
+  }
+
+  if (amqp_username) {
+    if (amqp_url) {
+      die("--username and --url options cannot be used at the same time");
+    }
+
+    ci->user = amqp_username;
+  }
+
+  if (amqp_password) {
+    if (amqp_url) {
+      die("--password and --url options cannot be used at the same time");
+    }
+
+    ci->password = amqp_password;
+  }
+
+  if (amqp_vhost) {
+    if (amqp_url) {
+      die("--vhost and --url options cannot be used at the same time");
+    }
+
+    ci->vhost = amqp_vhost;
+  }
+
+  if (amqp_heartbeat < 0) {
+    die("--heartbeat must be a positive value");
+  }
+}
+
+amqp_connection_state_t make_connection(void) {
+  int status;
+  amqp_socket_t *socket = NULL;
+  struct amqp_connection_info ci;
+  amqp_connection_state_t conn;
+
+  init_connection_info(&ci);
+  conn = amqp_new_connection();
+  if (ci.ssl) {
+#ifdef WITH_SSL
+    socket = amqp_ssl_socket_new(conn);
+    if (!socket) {
+      die("creating SSL/TLS socket");
+    }
+    if (amqp_cacert) {
+      amqp_ssl_socket_set_cacert(socket, amqp_cacert);
+    }
+    if (amqp_key) {
+      amqp_ssl_socket_set_key(socket, amqp_cert, amqp_key);
+    }
+#else
+    die("librabbitmq was not built with SSL/TLS support");
+#endif
+  } else {
+    socket = amqp_tcp_socket_new(conn);
+    if (!socket) {
+      die("creating TCP socket (out of memory)");
+    }
+  }
+  status = amqp_socket_open(socket, ci.host, ci.port);
+  if (status) {
+    die("opening socket to %s:%d", ci.host, ci.port);
+  }
+  die_rpc(amqp_login(conn, ci.vhost, 0, 131072, amqp_heartbeat,
+                     AMQP_SASL_METHOD_PLAIN, ci.user, ci.password),
+          "logging in to AMQP server");
+  if (!amqp_channel_open(conn, 1)) {
+    die_rpc(amqp_get_rpc_reply(conn), "opening channel");
+  }
+  return conn;
+}
+
+void close_connection(amqp_connection_state_t conn) {
+  int res;
+  die_rpc(amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS), "closing channel");
+  die_rpc(amqp_connection_close(conn, AMQP_REPLY_SUCCESS),
+          "closing connection");
+
+  res = amqp_destroy_connection(conn);
+  die_amqp_error(res, "closing connection");
+}
+
+amqp_bytes_t read_all(int fd) {
+  size_t space = 4096;
+  amqp_bytes_t bytes;
+
+  bytes.bytes = malloc(space);
+  bytes.len = 0;
+
+  for (;;) {
+    ssize_t res = read(fd, (char *)bytes.bytes + bytes.len, space - bytes.len);
+    if (res == 0) {
+      break;
+    }
+
+    if (res < 0) {
+      if (errno == EINTR) {
+        continue;
+      }
+
+      die_errno(errno, "reading");
+    }
+
+    bytes.len += res;
+    if (bytes.len == space) {
+      space *= 2;
+      bytes.bytes = realloc(bytes.bytes, space);
+    }
+  }
+
+  return bytes;
+}
+
+void write_all(int fd, amqp_bytes_t data) {
+  while (data.len > 0) {
+    ssize_t res = write(fd, data.bytes, data.len);
+    if (res < 0) {
+      die_errno(errno, "write");
+    }
+
+    data.len -= res;
+    data.bytes = (char *)data.bytes + res;
+  }
+}
+
+void copy_body(amqp_connection_state_t conn, int fd) {
+  size_t body_remaining;
+  amqp_frame_t frame;
+
+  int res = amqp_simple_wait_frame(conn, &frame);
+  die_amqp_error(res, "waiting for header frame");
+  if (frame.frame_type != AMQP_FRAME_HEADER) {
+    die("expected header, got frame type 0x%X", frame.frame_type);
+  }
+
+  body_remaining = frame.payload.properties.body_size;
+  while (body_remaining) {
+    res = amqp_simple_wait_frame(conn, &frame);
+    die_amqp_error(res, "waiting for body frame");
+    if (frame.frame_type != AMQP_FRAME_BODY) {
+      die("expected body, got frame type 0x%X", frame.frame_type);
+    }
+
+    write_all(fd, frame.payload.body_fragment);
+    body_remaining -= frame.payload.body_fragment.len;
+  }
+}
+
+poptContext process_options(int argc, const char **argv,
+                            struct poptOption *options, const char *help) {
+  int c;
+  poptContext opts = poptGetContext(NULL, argc, argv, options, 0);
+  poptSetOtherOptionHelp(opts, help);
+
+  while ((c = poptGetNextOpt(opts)) >= 0) {
+    /* no options require explicit handling */
+  }
+
+  if (c < -1) {
+    fprintf(stderr, "%s: %s\n", poptBadOption(opts, POPT_BADOPTION_NOALIAS),
+            poptStrerror(c));
+    poptPrintUsage(opts, stderr, 0);
+    exit(1);
+  }
+
+  return opts;
+}
+
+void process_all_options(int argc, const char **argv,
+                         struct poptOption *options) {
+  poptContext opts = process_options(argc, argv, options, "[OPTIONS]...");
+  const char *opt = poptPeekArg(opts);
+
+  if (opt) {
+    fprintf(stderr, "unexpected operand: %s\n", opt);
+    poptPrintUsage(opts, stderr, 0);
+    exit(1);
+  }
+
+  poptFreeContext(opts);
+}
+
+amqp_bytes_t cstring_bytes(const char *str) {
+  return str ? amqp_cstring_bytes(str) : amqp_empty_bytes;
+}

+ 73 - 0
ext/librabbitmq/tools/common.h

@@ -0,0 +1,73 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#include <stdint.h>
+
+#include <popt.h>
+
+#include <amqp.h>
+#include <amqp_framing.h>
+
+extern const char *amqp_server_exception_string(amqp_rpc_reply_t r);
+extern const char *amqp_rpc_reply_string(amqp_rpc_reply_t r);
+
+extern void die(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+extern void die_errno(int err, const char *fmt, ...)
+    __attribute__((format(printf, 2, 3)));
+extern void die_amqp_error(int err, const char *fmt, ...)
+    __attribute__((format(printf, 2, 3)));
+extern void die_rpc(amqp_rpc_reply_t r, const char *fmt, ...)
+    __attribute__((format(printf, 2, 3)));
+
+extern const char *connect_options_title;
+extern struct poptOption connect_options[];
+extern amqp_connection_state_t make_connection(void);
+extern void close_connection(amqp_connection_state_t conn);
+
+extern amqp_bytes_t read_all(int fd);
+extern void write_all(int fd, amqp_bytes_t data);
+
+extern void copy_body(amqp_connection_state_t conn, int fd);
+
+#define INCLUDE_OPTIONS(options) \
+  { NULL, 0, POPT_ARG_INCLUDE_TABLE, options, 0, options##_title, NULL }
+
+extern poptContext process_options(int argc, const char **argv,
+                                   struct poptOption *options,
+                                   const char *help);
+extern void process_all_options(int argc, const char **argv,
+                                struct poptOption *options);
+
+extern amqp_bytes_t cstring_bytes(const char *str);

+ 250 - 0
ext/librabbitmq/tools/consume.c

@@ -0,0 +1,250 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "process.h"
+
+#define MAX_LISTEN_KEYS 1024
+#define LISTEN_KEYS_DELIMITER ","
+
+/* Convert a amqp_bytes_t to an escaped string form for printing.  We
+   use the same escaping conventions as rabbitmqctl. */
+static char *stringify_bytes(amqp_bytes_t bytes) {
+  /* We will need up to 4 chars per byte, plus the terminating 0 */
+  char *res = malloc(bytes.len * 4 + 1);
+  uint8_t *data = bytes.bytes;
+  char *p = res;
+  size_t i;
+
+  for (i = 0; i < bytes.len; i++) {
+    if (data[i] >= 32 && data[i] != 127) {
+      *p++ = data[i];
+    } else {
+      *p++ = '\\';
+      *p++ = '0' + (data[i] >> 6);
+      *p++ = '0' + (data[i] >> 3 & 0x7);
+      *p++ = '0' + (data[i] & 0x7);
+    }
+  }
+
+  *p = 0;
+  return res;
+}
+
+static amqp_bytes_t setup_queue(amqp_connection_state_t conn, char *queue,
+                                char *exchange, char *routing_key, int declare,
+                                int exclusive) {
+  amqp_bytes_t queue_bytes = cstring_bytes(queue);
+
+  char *routing_key_rest;
+  char *routing_key_token;
+  char *routing_tmp;
+  int routing_key_count = 0;
+
+  /* if an exchange name wasn't provided, check that we don't have options that
+   * require it. */
+  if (!exchange && routing_key) {
+    fprintf(stderr,
+            "--routing-key option requires an exchange name to be provided "
+            "with --exchange\n");
+    exit(1);
+  }
+
+  if (!queue || exchange || declare || exclusive) {
+    /* Declare the queue as auto-delete.  */
+    amqp_queue_declare_ok_t *res = amqp_queue_declare(
+        conn, 1, queue_bytes, 0, 0, exclusive, 1, amqp_empty_table);
+    if (!res) {
+      die_rpc(amqp_get_rpc_reply(conn), "queue.declare");
+    }
+
+    if (!queue) {
+      /* the server should have provided a queue name */
+      char *sq;
+      queue_bytes = amqp_bytes_malloc_dup(res->queue);
+      sq = stringify_bytes(queue_bytes);
+      fprintf(stderr, "Server provided queue name: %s\n", sq);
+      free(sq);
+    }
+
+    /* Bind to an exchange if requested */
+    if (exchange) {
+      amqp_bytes_t eb = amqp_cstring_bytes(exchange);
+
+      routing_tmp = strdup(routing_key);
+      if (NULL == routing_tmp) {
+        fprintf(stderr, "could not allocate memory to parse routing key\n");
+        exit(1);
+      }
+
+      for (routing_key_token =
+               strtok_r(routing_tmp, LISTEN_KEYS_DELIMITER, &routing_key_rest);
+           NULL != routing_key_token && routing_key_count < MAX_LISTEN_KEYS - 1;
+           routing_key_token =
+               strtok_r(NULL, LISTEN_KEYS_DELIMITER, &routing_key_rest)) {
+
+        if (!amqp_queue_bind(conn, 1, queue_bytes, eb,
+                             cstring_bytes(routing_key_token),
+                             amqp_empty_table)) {
+          die_rpc(amqp_get_rpc_reply(conn), "queue.bind");
+        }
+      }
+      free(routing_tmp);
+    }
+  }
+
+  return queue_bytes;
+}
+
+#define AMQP_CONSUME_MAX_PREFETCH_COUNT 65535
+
+static void do_consume(amqp_connection_state_t conn, amqp_bytes_t queue,
+                       int no_ack, int count, int prefetch_count,
+                       const char *const *argv) {
+  int i;
+
+  /* If there is a limit, set the qos to match */
+  if (count > 0 && count <= AMQP_CONSUME_MAX_PREFETCH_COUNT &&
+      !amqp_basic_qos(conn, 1, 0, count, 0)) {
+    die_rpc(amqp_get_rpc_reply(conn), "basic.qos");
+  }
+
+  /* if there is a maximum number of messages to be received at a time, set the
+   * qos to match */
+  if (prefetch_count > 0 && prefetch_count <= AMQP_CONSUME_MAX_PREFETCH_COUNT) {
+    /* the maximum number of messages to be received at a time must be less
+     * than the global maximum number of messages. */
+    if (!(count > 0 && count <= AMQP_CONSUME_MAX_PREFETCH_COUNT &&
+          prefetch_count >= count)) {
+      if (!amqp_basic_qos(conn, 1, 0, prefetch_count, 0)) {
+        die_rpc(amqp_get_rpc_reply(conn), "basic.qos");
+      }
+    }
+  }
+
+  if (!amqp_basic_consume(conn, 1, queue, amqp_empty_bytes, 0, no_ack, 0,
+                          amqp_empty_table)) {
+    die_rpc(amqp_get_rpc_reply(conn), "basic.consume");
+  }
+
+  for (i = 0; count < 0 || i < count; i++) {
+    amqp_frame_t frame;
+    struct pipeline pl;
+    uint64_t delivery_tag;
+    amqp_basic_deliver_t *deliver;
+    int res = amqp_simple_wait_frame(conn, &frame);
+    die_amqp_error(res, "waiting for header frame");
+
+    if (frame.frame_type != AMQP_FRAME_METHOD ||
+        frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) {
+      continue;
+    }
+
+    deliver = (amqp_basic_deliver_t *)frame.payload.method.decoded;
+    delivery_tag = deliver->delivery_tag;
+
+    pipeline(argv, &pl);
+    copy_body(conn, pl.infd);
+
+    if (finish_pipeline(&pl) && !no_ack)
+      die_amqp_error(amqp_basic_ack(conn, 1, delivery_tag, 0), "basic.ack");
+
+    amqp_maybe_release_buffers(conn);
+  }
+}
+
+int main(int argc, const char **argv) {
+  poptContext opts;
+  amqp_connection_state_t conn;
+  const char *const *cmd_argv;
+  static char *queue = NULL;
+  static char *exchange = NULL;
+  static char *routing_key = NULL;
+  static int declare = 0;
+  static int exclusive = 0;
+  static int no_ack = 0;
+  static int count = -1;
+  static int prefetch_count = -1;
+  amqp_bytes_t queue_bytes;
+
+  struct poptOption options[] = {
+      INCLUDE_OPTIONS(connect_options),
+      {"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue to consume from",
+       "queue"},
+      {"exchange", 'e', POPT_ARG_STRING, &exchange, 0,
+       "bind the queue to this exchange", "exchange"},
+      {"routing-key", 'r', POPT_ARG_STRING, &routing_key, 0,
+       "the routing key to bind with", "routing key"},
+      {"declare", 'd', POPT_ARG_NONE, &declare, 0,
+       "declare an exclusive queue (deprecated, use --exclusive instead)",
+       NULL},
+      {"exclusive", 'x', POPT_ARG_NONE, &exclusive, 0,
+       "declare the queue as exclusive", NULL},
+      {"no-ack", 'A', POPT_ARG_NONE, &no_ack, 0, "consume in no-ack mode",
+       NULL},
+      {"count", 'c', POPT_ARG_INT, &count, 0,
+       "stop consuming after this many messages are consumed", "limit"},
+      {"prefetch-count", 'p', POPT_ARG_INT, &prefetch_count, 0,
+       "receive only this many message at a time from the server", "limit"},
+      POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
+
+  opts = process_options(argc, argv, options, "[OPTIONS]... <command> <args>");
+
+  cmd_argv = poptGetArgs(opts);
+  if (!cmd_argv || !cmd_argv[0]) {
+    fprintf(stderr, "consuming command not specified\n");
+    poptPrintUsage(opts, stderr, 0);
+    goto error;
+  }
+
+  conn = make_connection();
+  queue_bytes =
+      setup_queue(conn, queue, exchange, routing_key, declare, exclusive);
+  do_consume(conn, queue_bytes, no_ack, count, prefetch_count, cmd_argv);
+  close_connection(conn);
+  return 0;
+
+error:
+  poptFreeContext(opts);
+  return 1;
+}

+ 79 - 0
ext/librabbitmq/tools/declare_queue.c

@@ -0,0 +1,79 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+int main(int argc, const char **argv) {
+  amqp_connection_state_t conn;
+  static char *queue = NULL;
+  static int durable = 0;
+
+  struct poptOption options[] = {
+      INCLUDE_OPTIONS(connect_options),
+      {"queue", 'q', POPT_ARG_STRING, &queue, 0,
+       "the queue name to declare, or the empty string", "queue"},
+      {"durable", 'd', POPT_ARG_VAL, &durable, 1, "declare a durable queue",
+       NULL},
+      POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
+
+  process_all_options(argc, argv, options);
+
+  if (queue == NULL) {
+    fprintf(stderr, "queue name not specified\n");
+    return 1;
+  }
+
+  conn = make_connection();
+  {
+    amqp_queue_declare_ok_t *reply = amqp_queue_declare(
+        conn, 1, cstring_bytes(queue), 0, durable, 0, 0, amqp_empty_table);
+    if (reply == NULL) {
+      die_rpc(amqp_get_rpc_reply(conn), "queue.declare");
+    }
+
+    printf("%.*s\n", (int)reply->queue.len, (char *)reply->queue.bytes);
+  }
+  close_connection(conn);
+  return 0;
+}

+ 81 - 0
ext/librabbitmq/tools/delete_queue.c

@@ -0,0 +1,81 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+
+int main(int argc, const char **argv) {
+  amqp_connection_state_t conn;
+  static char *queue = NULL;
+  static int if_unused = 0;
+  static int if_empty = 0;
+
+  struct poptOption options[] = {
+      INCLUDE_OPTIONS(connect_options),
+      {"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue name to delete",
+       "queue"},
+      {"if-unused", 'u', POPT_ARG_VAL, &if_unused, 1,
+       "do not delete unless queue is unused", NULL},
+      {"if-empty", 'e', POPT_ARG_VAL, &if_empty, 1,
+       "do not delete unless queue is empty", NULL},
+      POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
+
+  process_all_options(argc, argv, options);
+
+  if (queue == NULL || *queue == '\0') {
+    fprintf(stderr, "queue name not specified\n");
+    return 1;
+  }
+
+  conn = make_connection();
+  {
+    amqp_queue_delete_ok_t *reply =
+        amqp_queue_delete(conn, 1, cstring_bytes(queue), if_unused, if_empty);
+    if (reply == NULL) {
+      die_rpc(amqp_get_rpc_reply(conn), "queue.delete");
+    }
+    printf("%u\n", reply->message_count);
+  }
+  close_connection(conn);
+  return 0;
+}

+ 223 - 0
ext/librabbitmq/tools/doc/amqp-consume.xml

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

+ 122 - 0
ext/librabbitmq/tools/doc/amqp-declare-queue.xml

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

+ 94 - 0
ext/librabbitmq/tools/doc/amqp-delete-queue.xml

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

+ 95 - 0
ext/librabbitmq/tools/doc/amqp-get.xml

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

+ 169 - 0
ext/librabbitmq/tools/doc/amqp-publish.xml

@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd"
+[
+<!ENTITY date SYSTEM "man-date.ent" >
+]
+>
+<refentry lang="en">
+    <refentryinfo>
+        <productname>RabbitMQ C Client</productname>
+        <authorgroup>
+            <corpauthor>The RabbitMQ Team &lt;<ulink url="mailto:[email protected]"><email>[email protected]</email></ulink>&gt;</corpauthor>
+        </authorgroup>
+        <date>&date;</date>
+    </refentryinfo>
+
+    <refmeta>
+        <refentrytitle>amqp-publish</refentrytitle>
+        <manvolnum>1</manvolnum>
+        <refmiscinfo class="manual">RabbitMQ C Client</refmiscinfo>
+    </refmeta>
+
+    <refnamediv>
+        <refname>amqp-publish</refname>
+        <refpurpose>Publish a message on an AMQP server</refpurpose>
+    </refnamediv>
+
+    <refsynopsisdiv>
+        <cmdsynopsis>
+            <command>amqp-publish</command>
+            <arg choice="opt" rep="repeat">
+                <replaceable>OPTION</replaceable>
+            </arg>
+        </cmdsynopsis>
+    </refsynopsisdiv>
+
+    <refsect1>
+        <title>Description</title>
+        <para>
+            Publishes a message to an exchange on an AMQP server.
+            Options allow the various properties of the message and
+            parameters of the AMQP <function>basic.publish</function>
+            method to be specified.
+        </para>
+        <para>
+            By default, the message body is read from standard input.
+            Alternatively, the <option>-b</option> option allows the message
+            body to be provided as part of the command.
+        </para>
+    </refsect1>
+
+    <refsect1>
+        <title>Options</title>
+        <variablelist>
+            <varlistentry>
+                <term><option>-e</option></term>
+                <term><option>--exchange</option>=<replaceable class="parameter">exchange name</replaceable></term>
+                <listitem>
+                    <para>
+                        The name of the exchange to publish to.  If
+                        omitted, the default exchange (also known as
+                        the nameless exchange) is used.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>-r</option></term>
+                <term><option>--routing-key</option>=<replaceable class="parameter">routing key</replaceable></term>
+                <listitem>
+                    <para>
+                        The routing key to publish with.  If omitted,
+                        an empty routing key is assumed.  A routing
+                        key must be specified when publishing to the
+                        default exchange; in that case, accoding to
+                        the AMQP specification, the routing key
+                        corresponds to a queue name.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>-p</option></term>
+                <term><option>--persistent</option></term>
+                <listitem>
+                    <para>
+                        Use the persistent delivery mode.  Without
+                        this option, non-persistent delivery is used.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>-C</option></term>
+                <term><option>--content-type</option>=<replaceable class="parameter">MIME type</replaceable></term>
+                <listitem>
+                    <para>
+                        Specifies the content-type property for the
+                        message.  If omitted, the content-type
+                        property is not set on the message.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>-E</option></term>
+                <term><option>--content-encoding</option>=<replaceable class="parameter">content coding</replaceable></term>
+                <listitem>
+                    <para>
+                        Specifies the content-encoding property for
+                        the message.  If omitted, the content-encoding
+                        property is not set on the message.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>-b</option></term>
+                <term><option>--body</option>=<replaceable class="parameter">message body</replaceable></term>
+                <listitem>
+                    <para>
+                        Specifies the message body.  If omitted, the
+                        message body is read from standard input.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>-H</option></term>
+                <term><option>--header</option>=<replaceable class="parameter">header</replaceable></term>
+                <listitem>
+                    <para>
+                        Specifies an optional header in the form "key: value".
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </refsect1>
+
+    <refsect1>
+        <title>Examples</title>
+        <variablelist>
+            <varlistentry>
+                <term>Send a short message, consisting of the word
+                <quote><literal>Hello</literal></quote> to the queue
+                <quote><systemitem
+                class="resource">myqueue</systemitem></quote> via the
+                default exchange:</term>
+                <listitem>
+                    <screen><prompt>$ </prompt><userinput>amqp-publish -r myqueue -b Hello</userinput></screen>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>Send some XML data from a file to the exchange
+                <quote><systemitem
+                class="resource">events</systemitem></quote>, with
+                persistent delivery mode, setting the content-type
+                property on the message to make the data format
+                explicit:</term>
+                <listitem>
+                    <screen><prompt>$ </prompt><userinput>amqp-publish -e events -p -C text/xml &lt;event.xml</userinput></screen>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </refsect1>
+
+    <refsect1>
+        <title>See also</title>
+        <para>
+            <citerefentry><refentrytitle>librabbitmq-tools</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+            describes connection-related options common to all the
+            RabbitMQ C Client tools.
+        </para>
+    </refsect1>
+</refentry>

+ 90 - 0
ext/librabbitmq/tools/doc/librabbitmq-tools.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.docbook.org/xml/4.5/docbookx.dtd"
+[
+<!ENTITY date SYSTEM "man-date.ent" >
+]
+>
+<refentry lang="en">
+    <refentryinfo>
+        <productname>RabbitMQ C Client</productname>
+        <authorgroup>
+            <corpauthor>The RabbitMQ Team &lt;<ulink url="mailto:[email protected]"><email>[email protected]</email></ulink>&gt;</corpauthor>
+        </authorgroup>
+        <date>&date;</date>
+    </refentryinfo>
+
+    <refmeta>
+        <refentrytitle>librabbitmq-tools</refentrytitle>
+        <manvolnum>7</manvolnum>
+        <refmiscinfo class="manual">RabbitMQ C Client</refmiscinfo>
+    </refmeta>
+
+    <refnamediv>
+        <refname>librabbitmq-tools</refname>
+        <refpurpose>Command line AMQP tools</refpurpose>
+    </refnamediv>
+
+    <refsect1>
+        <title>Description</title>
+        <para>
+            A set of command line AMQP tools based on <systemitem
+            class="library">librabbitmq</systemitem>.  This page
+            describes common options and conventions used by all of
+            the tools.
+        </para>
+    </refsect1>
+
+    <refsect1>
+        <title>Common Options</title>
+        <variablelist>
+            <varlistentry>
+                <term><option>-s</option></term>
+                <term><option>--server</option>=<replaceable class="parameter">hostname:port</replaceable></term>
+                <listitem>
+                    <para>
+                        The host name (or address) to connect to.
+                        Defaults to localhost.  The port number may
+                        also be specified; if omitted, it defaults to
+                        the standard AMQP port number (5672).
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>--vhost</option>=<replaceable class="parameter">vhost</replaceable></term>
+                <listitem>
+                    <para>
+                        The AMQP vhost to specify when connecting.
+                        Defaults to <literal>/</literal>.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>--username</option>=<replaceable class="parameter">username</replaceable></term>
+                <listitem>
+                    <para>
+                        The username to authenticate to the AMQP server with.  Defaults to <literal>guest</literal>.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term><option>--password</option>=<replaceable class="parameter">password</replaceable></term>
+                <listitem>
+                    <para>
+                        The password to authenticate to the AMQP server with.  Defaults to <literal>guest</literal>.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </refsect1>
+
+    <refsect1>
+        <title>See also</title>
+        <para>
+            <simplelist type='inline'>
+                <member><citerefentry><refentrytitle>amqp-publish</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+                <member><citerefentry><refentrytitle>amqp-consume</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+                <member><citerefentry><refentrytitle>amqp-get</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+            </simplelist>
+        </para>
+    </refsect1>
+</refentry>

+ 78 - 0
ext/librabbitmq/tools/get.c

@@ -0,0 +1,78 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include "common.h"
+
+static int do_get(amqp_connection_state_t conn, char *queue) {
+  amqp_rpc_reply_t r = amqp_basic_get(conn, 1, cstring_bytes(queue), 1);
+  die_rpc(r, "basic.get");
+
+  if (r.reply.id == AMQP_BASIC_GET_EMPTY_METHOD) {
+    return 0;
+  }
+
+  copy_body(conn, 1);
+  return 1;
+}
+
+int main(int argc, const char **argv) {
+  amqp_connection_state_t conn;
+  static char *queue = NULL;
+  int got_something;
+
+  struct poptOption options[] = {
+      INCLUDE_OPTIONS(connect_options),
+      {"queue", 'q', POPT_ARG_STRING, &queue, 0, "the queue to consume from",
+       "queue"},
+      POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
+
+  process_all_options(argc, argv, options);
+
+  if (!queue) {
+    fprintf(stderr, "queue not specified\n");
+    return 1;
+  }
+
+  conn = make_connection();
+  got_something = do_get(conn, queue);
+  close_connection(conn);
+  return got_something ? 0 : 2;
+}

+ 180 - 0
ext/librabbitmq/tools/publish.c

@@ -0,0 +1,180 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+
+#define MAX_LINE_LENGTH 1024 * 32
+
+static void do_publish(amqp_connection_state_t conn, char *exchange,
+                       char *routing_key, amqp_basic_properties_t *props,
+                       amqp_bytes_t body) {
+  int res = amqp_basic_publish(conn, 1, cstring_bytes(exchange),
+                               cstring_bytes(routing_key), 0, 0, props, body);
+  die_amqp_error(res, "basic.publish");
+}
+
+int main(int argc, const char **argv) {
+  amqp_connection_state_t conn;
+  static char *exchange = NULL;
+  static char *routing_key = NULL;
+  static char *content_type = NULL;
+  static char *content_encoding = NULL;
+  static char **headers = NULL;
+  static char *reply_to = NULL;
+  static char *body = NULL;
+  amqp_basic_properties_t props;
+  amqp_bytes_t body_bytes;
+  static int delivery = 1; /* non-persistent by default */
+  static int line_buffered = 0;
+  static char **pos;
+
+  struct poptOption options[] = {
+      INCLUDE_OPTIONS(connect_options),
+      {"exchange", 'e', POPT_ARG_STRING, &exchange, 0,
+       "the exchange to publish to", "exchange"},
+      {"routing-key", 'r', POPT_ARG_STRING, &routing_key, 0,
+       "the routing key to publish with", "routing key"},
+      {"persistent", 'p', POPT_ARG_VAL, &delivery, 2,
+       "use the persistent delivery mode", NULL},
+      {"content-type", 'C', POPT_ARG_STRING, &content_type, 0,
+       "the content-type for the message", "content type"},
+      {"reply-to", 't', POPT_ARG_STRING, &reply_to, 0,
+       "the replyTo to use for the message", "reply to"},
+      {"line-buffered", 'l', POPT_ARG_VAL, &line_buffered, 2,
+       "treat each line from standard in as a separate message", NULL},
+      {"content-encoding", 'E', POPT_ARG_STRING, &content_encoding, 0,
+       "the content-encoding for the message", "content encoding"},
+      {"header", 'H', POPT_ARG_ARGV, &headers, 0,
+       "set a message header (may be specified multiple times)",
+       "\"key: value\""},
+      {"body", 'b', POPT_ARG_STRING, &body, 0, "specify the message body",
+       "body"},
+      POPT_AUTOHELP{NULL, '\0', 0, NULL, 0, NULL, NULL}};
+
+  process_all_options(argc, argv, options);
+
+  if (!exchange && !routing_key) {
+    fprintf(stderr, "neither exchange nor routing key specified\n");
+    return 1;
+  }
+
+  memset(&props, 0, sizeof props);
+  props._flags = AMQP_BASIC_DELIVERY_MODE_FLAG;
+  props.delivery_mode = delivery;
+
+  if (content_type) {
+    props._flags |= AMQP_BASIC_CONTENT_TYPE_FLAG;
+    props.content_type = amqp_cstring_bytes(content_type);
+  }
+
+  if (content_encoding) {
+    props._flags |= AMQP_BASIC_CONTENT_ENCODING_FLAG;
+    props.content_encoding = amqp_cstring_bytes(content_encoding);
+  }
+
+  if (reply_to) {
+    props._flags |= AMQP_BASIC_REPLY_TO_FLAG;
+    props.reply_to = amqp_cstring_bytes(reply_to);
+  }
+
+  if (headers) {
+    int num = 0;
+    for (pos = headers; *pos; pos++) {
+      num++;
+    }
+
+    if (num > 0) {
+      amqp_table_t *table = &props.headers;
+      table->num_entries = num;
+      table->entries = calloc(num, sizeof(amqp_table_entry_t));
+      int i = 0;
+      for (pos = headers; *pos; pos++) {
+        char *colon = strchr(*pos, ':');
+        if (colon) {
+          *colon++ = '\0';
+          while (*colon == ' ') colon++;
+          table->entries[i].key = amqp_cstring_bytes(*pos);
+          table->entries[i].value.kind = AMQP_FIELD_KIND_UTF8;
+          table->entries[i].value.value.bytes = amqp_cstring_bytes(colon);
+          i++;
+        } else {
+          fprintf(stderr,
+                  "Ignored header definition missing ':' delimiter in \"%s\"\n",
+                  *pos);
+        }
+      }
+      props._flags |= AMQP_BASIC_HEADERS_FLAG;
+    }
+  }
+
+  conn = make_connection();
+
+  if (body) {
+    body_bytes = amqp_cstring_bytes(body);
+  } else {
+    if (line_buffered) {
+      body_bytes.bytes = (char *)malloc(MAX_LINE_LENGTH);
+      while (fgets(body_bytes.bytes, MAX_LINE_LENGTH, stdin)) {
+        body_bytes.len = strlen(body_bytes.bytes);
+        do_publish(conn, exchange, routing_key, &props, body_bytes);
+      }
+    } else {
+      body_bytes = read_all(0);
+    }
+  }
+
+  if (!line_buffered) {
+    do_publish(conn, exchange, routing_key, &props, body_bytes);
+  }
+
+  if (props.headers.num_entries > 0) {
+    free(props.headers.entries);
+  }
+
+  if (!body) {
+    free(body_bytes.bytes);
+  }
+
+  close_connection(conn);
+  return 0;
+}

+ 91 - 0
ext/librabbitmq/tools/unix/process.c

@@ -0,0 +1,91 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MIT
+ *
+ * Portions created by Alan Antonuk are Copyright (c) 2012-2013
+ * Alan Antonuk. All Rights Reserved.
+ *
+ * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
+ * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * ***** END LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <spawn.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "process.h"
+
+extern char **environ;
+
+void pipeline(const char *const *argv, struct pipeline *pl) {
+  posix_spawn_file_actions_t file_acts;
+
+  int pipefds[2];
+  if (pipe(pipefds)) {
+    die_errno(errno, "pipe");
+  }
+
+  die_errno(posix_spawn_file_actions_init(&file_acts),
+            "posix_spawn_file_actions_init");
+  die_errno(posix_spawn_file_actions_adddup2(&file_acts, pipefds[0], 0),
+            "posix_spawn_file_actions_adddup2");
+  die_errno(posix_spawn_file_actions_addclose(&file_acts, pipefds[0]),
+            "posix_spawn_file_actions_addclose");
+  die_errno(posix_spawn_file_actions_addclose(&file_acts, pipefds[1]),
+            "posix_spawn_file_actions_addclose");
+
+  die_errno(posix_spawnp(&pl->pid, argv[0], &file_acts, NULL,
+                         (char *const *)argv, environ),
+            "posix_spawnp: %s", argv[0]);
+
+  die_errno(posix_spawn_file_actions_destroy(&file_acts),
+            "posix_spawn_file_actions_destroy");
+
+  if (close(pipefds[0])) {
+    die_errno(errno, "close");
+  }
+
+  pl->infd = pipefds[1];
+}
+
+int finish_pipeline(struct pipeline *pl) {
+  int status;
+
+  if (close(pl->infd)) {
+    die_errno(errno, "close");
+  }
+  if (waitpid(pl->pid, &status, 0) < 0) {
+    die_errno(errno, "waitpid");
+  }
+  return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+}

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