├── .rspec ├── .ruby-version ├── examples ├── tls_certificates │ ├── testca │ │ ├── serial │ │ ├── serial.old │ │ ├── index.txt.attr │ │ ├── index.txt.attr.old │ │ ├── index.txt.old │ │ ├── cacert.cer │ │ ├── index.txt │ │ ├── cacert.pem │ │ ├── certs │ │ │ ├── 01.pem │ │ │ └── 02.pem │ │ ├── private │ │ │ └── cakey.pem │ │ └── openssl.cnf │ ├── client │ │ ├── keycert.p12 │ │ ├── req.pem │ │ ├── cert.pem │ │ └── key.pem │ └── server │ │ ├── keycert.p12 │ │ ├── req.pem │ │ ├── cert.pem │ │ └── key.pem ├── eventmachine_adapter │ ├── server_capabilities.rb │ ├── channel_close.rb │ ├── tx_select.rb │ ├── tx_commit.rb │ ├── authentication │ │ ├── plain_password_with_default_role_credentials.rb │ │ ├── plain_password_with_incorrect_credentials_handled_with_a_rescue_block.rb │ │ ├── plain_password_with_custom_role_credentials.rb │ │ ├── plain_password_with_incorrect_credentials_handled_with_a_callback.rb │ │ └── connection_open_failure_due_to_invalid_vhost.rb │ ├── tx_rollback.rb │ ├── basic_qos.rb │ ├── basic_recover.rb │ ├── extensions │ │ └── rabbitmq │ │ │ ├── handling_confirm_select_ok.rb │ │ │ ├── publisher_confirmations_with_unroutable_message.rb │ │ │ └── publisher_confirmations_with_transient_messages.rb │ ├── error_handling │ │ ├── connection_failure_callback.rb │ │ ├── connection_failure_exception.rb │ │ ├── handling_a_channel_level_exception.rb │ │ ├── connection_loss_handler_that_fails_over.rb │ │ ├── connection_loss_handler_with_manual_recovery.rb │ │ └── connection_loss_handler_with_automatic_recovery.rb │ ├── queue_bind.rb │ ├── queue_purge.rb │ ├── basic_publish.rb │ ├── queue_unbind.rb │ ├── channel_flow.rb │ ├── basic_return.rb │ ├── queue_declare.rb │ ├── example_helper.rb │ ├── channel_level_exception_handling.rb │ ├── basic_consume_with_rejections.rb │ ├── basic_consume_with_acknowledgements.rb │ ├── basic_cancel.rb │ ├── tls │ │ └── tls_without_peer_verification.rb │ ├── basic_get_with_empty_queue.rb │ ├── kitchen_sink1.rb │ ├── basic_consume.rb │ ├── exchange_declare.rb │ └── basic_get.rb └── coolio_adapter │ ├── channel_close.rb │ ├── exchange_declare.rb │ ├── queue_bind.rb │ ├── queue_purge.rb │ ├── basic_publish.rb │ ├── queue_unbind.rb │ ├── example_helper.rb │ ├── basic_consume_with_acknowledgements.rb │ ├── basic_consume_with_rejections.rb │ ├── kitchen_sink1.rb │ └── basic_consume.rb ├── .yardopts ├── lib └── amq │ ├── client │ ├── async │ │ ├── adapters │ │ │ └── eventmachine.rb │ │ ├── extensions │ │ │ └── rabbitmq │ │ │ │ ├── confirm.rb │ │ │ │ ├── basic.rb │ │ │ │ └── cancel.rb │ │ ├── auth_mechanism_adapter │ │ │ ├── plain.rb │ │ │ └── external.rb │ │ ├── entity.rb │ │ ├── callbacks.rb │ │ └── auth_mechanism_adapter.rb │ ├── version.rb │ ├── queue.rb │ ├── channel.rb │ ├── exchange.rb │ ├── extensions │ │ ├── rabbitmq.rb │ │ └── rabbitmq │ │ │ ├── confirm.rb │ │ │ ├── basic.rb │ │ │ └── cancel.rb │ ├── callbacks.rb │ ├── adapters │ │ ├── coolio.rb │ │ └── event_machine.rb │ ├── entity.rb │ ├── adapter.rb │ ├── server_named_entity.rb │ ├── handlers_registry.rb │ ├── consumer_tag_generator.rb │ ├── framing │ │ ├── io │ │ │ └── frame.rb │ │ └── string │ │ │ └── frame.rb │ ├── openable.rb │ ├── logging.rb │ ├── exceptions.rb │ └── settings.rb │ ├── protocol │ └── get_response.rb │ └── client.rb ├── .gitignore ├── tasks.rb ├── .gitmodules ├── README.textile ├── .travis.yml ├── spec ├── integration │ ├── coolio │ │ ├── connection_close_spec.rb │ │ ├── channel_close_spec.rb │ │ ├── spec_helper.rb │ │ ├── tx_commit_spec.rb │ │ ├── tx_rollback_spec.rb │ │ ├── channel_flow_spec.rb │ │ ├── basic_ack_spec.rb │ │ ├── basic_return_spec.rb │ │ ├── connection_start_spec.rb │ │ ├── basic_cancel_spec.rb │ │ ├── basic_get_spec.rb │ │ ├── exchange_declare_spec.rb │ │ └── basic_consume_spec.rb │ └── eventmachine │ │ ├── connection_close_spec.rb │ │ ├── spec_helper.rb │ │ ├── regressions │ │ └── amqp_gem_issue66_spec.rb │ │ ├── channel_close_spec.rb │ │ ├── queue_declare_spec.rb │ │ ├── channel_flow_spec.rb │ │ ├── tx_rollback_spec.rb │ │ ├── basic_ack_spec.rb │ │ ├── tx_commit_spec.rb │ │ ├── basic_return_spec.rb │ │ ├── basic_cancel_spec.rb │ │ ├── basic_get_spec.rb │ │ ├── consumer_cancellation_notification_spec.rb │ │ ├── connection_start_spec.rb │ │ ├── exchange_declare_spec.rb │ │ └── basic_consume_spec.rb ├── spec_helper.rb ├── unit │ ├── client │ │ ├── adapter_spec.rb │ │ ├── logging_spec.rb │ │ ├── entity_spec.rb │ │ ├── mixins │ │ │ └── status_spec.rb │ │ └── settings_spec.rb │ └── client_spec.rb ├── client │ ├── framing │ │ ├── io_frame_spec.rb │ │ └── string_frame_spec.rb │ └── protocol │ │ └── get_response_spec.rb ├── regression │ └── bad_frame_slicing_in_adapters_spec.rb └── benchmarks │ └── adapters.rb ├── bin ├── set_test_suite_realms_up.sh └── ci │ └── before_build.sh ├── LICENSE ├── amq-client.gemspec ├── Gemfile ├── gemfiles └── eventmachine-pre └── irb.rb /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.0.0@amqp -------------------------------------------------------------------------------- /examples/tls_certificates/testca/serial: -------------------------------------------------------------------------------- 1 | 03 2 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --no-private --protected lib/amq/**/*.rb -------------------------------------------------------------------------------- /examples/tls_certificates/testca/serial.old: -------------------------------------------------------------------------------- 1 | 02 2 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/index.txt.attr: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/index.txt.attr.old: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/index.txt.old: -------------------------------------------------------------------------------- 1 | V 120416201727Z 01 unknown /CN=giove.local/O=server 2 | -------------------------------------------------------------------------------- /lib/amq/client/async/adapters/eventmachine.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/adapters/event_machine" 4 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/cacert.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-amqp/amq-client/HEAD/examples/tls_certificates/testca/cacert.cer -------------------------------------------------------------------------------- /lib/amq/client/version.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | VERSION = "1.1.0.pre2wip" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /examples/tls_certificates/client/keycert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-amqp/amq-client/HEAD/examples/tls_certificates/client/keycert.p12 -------------------------------------------------------------------------------- /examples/tls_certificates/server/keycert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-amqp/amq-client/HEAD/examples/tls_certificates/server/keycert.p12 -------------------------------------------------------------------------------- /examples/tls_certificates/testca/index.txt: -------------------------------------------------------------------------------- 1 | V 120416201727Z 01 unknown /CN=giove.local/O=server 2 | V 120416201833Z 02 unknown /CN=giove.local/O=client 3 | -------------------------------------------------------------------------------- /lib/amq/client/queue.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/queue" 4 | 5 | module AMQ 6 | module Client 7 | Queue = Async::Queue 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | .bundle 3 | doc/* 4 | /*.gem 5 | # see http://bit.ly/h2WJPm for reasoning 6 | Gemfile.lock 7 | .rbenv-gemsets 8 | .rbenv-version 9 | .rbx/* 10 | .rvmrc 11 | vendor 12 | vendor/bundle 13 | .yardoc/* 14 | -------------------------------------------------------------------------------- /lib/amq/client/channel.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/channel" 4 | 5 | module AMQ 6 | module Client 7 | # backwards compatibility 8 | # @private 9 | Channel = Async::Channel 10 | end # Client 11 | end # AMQ 12 | -------------------------------------------------------------------------------- /lib/amq/client/exchange.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/exchange" 4 | 5 | module AMQ 6 | module Client 7 | # backwards compatibility 8 | # @private 9 | Exchange = Async::Exchange 10 | end # Client 11 | end # AMQ 12 | -------------------------------------------------------------------------------- /lib/amq/client/extensions/rabbitmq.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # http://www.rabbitmq.com/extensions.html 4 | require "amq/client/extensions/rabbitmq/basic" 5 | require "amq/client/extensions/rabbitmq/cancel" 6 | require "amq/client/extensions/rabbitmq/confirm" 7 | -------------------------------------------------------------------------------- /lib/amq/client/callbacks.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/callbacks" 4 | 5 | module AMQ 6 | module Client 7 | # backwards compatibility 8 | # @private 9 | Callbacks = Async::Callbacks 10 | end # Client 11 | end # AMQ 12 | 13 | -------------------------------------------------------------------------------- /tasks.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nake 2 | # encoding: utf-8 3 | 4 | if RUBY_VERSION =~ /^1.9/ 5 | Encoding.default_internal = Encoding::UTF_8 6 | Encoding.default_external = Encoding::UTF_8 7 | end 8 | 9 | 10 | require "contributors" 11 | load "contributors.nake" 12 | -------------------------------------------------------------------------------- /lib/amq/client/adapters/coolio.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/adapters/coolio" 4 | 5 | module AMQ 6 | module Client 7 | # backwards compatibility 8 | # @private 9 | CoolioClient = Async::CoolioClient 10 | end # Client 11 | end # AMQ 12 | -------------------------------------------------------------------------------- /lib/amq/client/adapters/event_machine.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/adapters/eventmachine" 4 | 5 | module AMQ 6 | module Client 7 | # backwards compatibility 8 | # @private 9 | EventMachineClient = Async::EventMachineClient 10 | end # Client 11 | end # AMQ 12 | -------------------------------------------------------------------------------- /lib/amq/client/extensions/rabbitmq/confirm.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/extensions/rabbitmq/confirm" 4 | 5 | module AMQ 6 | module Client 7 | # backwards compatibility 8 | # @private 9 | Extensions = Async::Extensions unless defined?(Extensions) 10 | end # Client 11 | end # AMQ 12 | -------------------------------------------------------------------------------- /lib/amq/client/extensions/rabbitmq/basic.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/extensions/rabbitmq/basic" 4 | 5 | # Basic.Nack 6 | module AMQ 7 | module Client 8 | # backwards compatibility 9 | # @private 10 | Extensions = Async::Extensions unless defined?(Extensions) 11 | end # Client 12 | end # AMQ 13 | -------------------------------------------------------------------------------- /lib/amq/client/async/extensions/rabbitmq/confirm.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | module Async 6 | module Extensions 7 | module RabbitMQ 8 | module Confirm 9 | end # Confirm 10 | end # RabbitMQ 11 | end # Extensions 12 | end # Async 13 | end # Client 14 | end # AMQ 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/amq-protocol"] 2 | path = vendor/amq-protocol 3 | url = https://github.com/ruby-amqp/amq-protocol.git 4 | [submodule "vendor/cool.io"] 5 | path = vendor/cool.io 6 | url = https://github.com/tarcieri/cool.io.git 7 | [submodule "vendor/evented-spec"] 8 | path = vendor/evented-spec 9 | url = git://github.com/markiz/evented-spec.git 10 | -------------------------------------------------------------------------------- /lib/amq/client/entity.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/entity" 4 | 5 | module AMQ 6 | module Client 7 | # backwards compatibility 8 | # @private 9 | RegisterEntityMixin = Async::RegisterEntityMixin 10 | ProtocolMethodHandlers = Async::ProtocolMethodHandlers 11 | Entity = Async::Entity 12 | end # Client 13 | end # AMQ 14 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/server_capabilities.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Inspecting server information & capabilities" do |client| 8 | puts client.server_capabilities.inspect 9 | puts client.server_properties.inspect 10 | 11 | client.disconnect { EventMachine.stop } 12 | end 13 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h2. About amq-client 2 | 3 | This project is **no longer actively developed**. 4 | 5 | It used to back "amqp gem":http://rubyamqp.info All the relevant functionality has 6 | now been merged into amqp gem. This library should only be used by projects that 7 | have really good reasons to do so. 8 | 9 | If you are looking for a good Ruby RabbitMQ client, consider "Bunny":http://rubybunny.info 10 | or "HotBunnies":http://hotbunnies.info. 11 | 12 | -------------------------------------------------------------------------------- /lib/amq/client/async/extensions/rabbitmq/basic.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/channel" 4 | 5 | # Basic.Nack 6 | module AMQ 7 | module Client 8 | module Async 9 | module Extensions 10 | module RabbitMQ 11 | module Basic 12 | module ChannelMixin 13 | end # ChannelMixin 14 | end # Basic 15 | end # RabbitMQ 16 | end # Extensions 17 | end # Async 18 | end # Client 19 | end # AMQ 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | bundler_args: --without development 3 | before_script: ./bin/ci/before_build.sh 4 | script: "bundle exec rspec spec" 5 | rvm: 6 | - jruby-head 7 | - rbx-19mode 8 | - 1.9.3 9 | - 1.9.2 10 | - 1.8.7 11 | gemfile: 12 | - Gemfile 13 | - gemfiles/eventmachine-pre 14 | notifications: 15 | recipients: 16 | - michael@novemberain.com 17 | branches: 18 | only: 19 | - master 20 | - 0.9.x-stable 21 | - 0.8.x-stable 22 | 23 | services: 24 | - rabbitmq 25 | -------------------------------------------------------------------------------- /lib/amq/client/extensions/rabbitmq/cancel.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/extensions/rabbitmq/cancel" 4 | 5 | # Basic.Cancel 6 | module AMQ 7 | module Client 8 | # backwards compatibility 9 | # @private 10 | Extensions = Async::Extensions unless defined?(Extensions) 11 | 12 | module Settings 13 | CLIENT_PROPERTIES[:capabilities] ||= {} 14 | CLIENT_PROPERTIES[:capabilities][:consumer_cancel_notify] = true 15 | end 16 | end # Client 17 | end # AMQ 18 | -------------------------------------------------------------------------------- /lib/amq/client/adapter.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/logging" 4 | require "amq/client/settings" 5 | 6 | require "amq/client/async/queue" 7 | require "amq/client/async/exchange" 8 | require "amq/client/async/channel" 9 | require "amq/client/async/adapter" 10 | 11 | module AMQ 12 | # For overview of AMQP client adapters API, see {AMQ::Client::Adapter} 13 | module Client 14 | 15 | # backwards compatibility 16 | # @private 17 | Adapter = Async::Adapter 18 | end # Client 19 | end # AMQ 20 | -------------------------------------------------------------------------------- /lib/amq/client/async/extensions/rabbitmq/cancel.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/async/channel" 4 | 5 | # Basic.Cancel 6 | module AMQ 7 | module Client 8 | module Async 9 | module Extensions 10 | module RabbitMQ 11 | module Basic 12 | module ConsumerMixin 13 | end # ConsumerMixin 14 | 15 | module QueueMixin 16 | end 17 | 18 | end # Basic 19 | end # RabbitMQ 20 | end # Extensions 21 | end # Async 22 | end # Client 23 | end # AMQ 24 | -------------------------------------------------------------------------------- /lib/amq/client/server_named_entity.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | # Common behavior of AMQ entities that can be either client or server-named, for example, exchanges and queues. 6 | module ServerNamedEntity 7 | 8 | # @return [Boolean] true if this entity is anonymous (server-named) 9 | def server_named? 10 | @server_named || @name.nil? || @name.empty? 11 | end 12 | # backwards compabitility. MK. 13 | alias anonymous? server_named? 14 | end # ServerNamedEntity 15 | end # Client 16 | end # AMQ 17 | -------------------------------------------------------------------------------- /lib/amq/client/handlers_registry.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | class HandlersRegistry 6 | 7 | @@handlers ||= Hash.new 8 | 9 | 10 | # 11 | # API 12 | # 13 | 14 | 15 | def self.register(klass, &block) 16 | @@handlers[klass] = block 17 | end 18 | class << self 19 | alias handle register 20 | end 21 | 22 | def self.find(klass) 23 | @@handlers[klass] 24 | end 25 | 26 | def self.handlers 27 | @@handlers 28 | end 29 | 30 | end # HandlersRegistry 31 | end # Client 32 | end # AMQ -------------------------------------------------------------------------------- /spec/integration/coolio/connection_close_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Connection.Close", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 0.5 9 | 10 | it "should issue a callback and close connection" do 11 | coolio do 12 | AMQ::Client::CoolioClient.connect do |connection| 13 | @connection = connection 14 | connection.should be_opened 15 | connection.disconnect do 16 | done 17 | end 18 | end 19 | end 20 | @connection.should be_closed 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/connection_close_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Connection.Close" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 0.5 9 | 10 | it "should issue a callback and close connection" do 11 | em do 12 | AMQ::Client::EventMachineClient.connect do |connection| 13 | @connection = connection 14 | connection.should be_opened 15 | connection.disconnect do 16 | done 17 | end 18 | end 19 | end 20 | @connection.should be_closed 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/adapters/event_machine" 4 | require "amq/client/queue" 5 | require "amq/client/exchange" 6 | require "evented-spec" 7 | 8 | case RUBY_VERSION 9 | when "1.8.7" then 10 | class Array 11 | alias sample choice 12 | end 13 | when /^1.9/ then 14 | Encoding.default_internal = Encoding::UTF_8 15 | Encoding.default_external = Encoding::UTF_8 16 | end 17 | 18 | def em_amqp_connect(&block) 19 | em do 20 | AMQ::Client::EventMachineClient.connect(:port => 5672, :vhost => "amq_client_testbed", :frame_max => 65536, :heartbeat_interval => 1) do |client| 21 | yield client 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/regressions/amqp_gem_issue66_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "handling immediate disconnection" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 4 9 | 10 | after :all do 11 | done 12 | end 13 | 14 | it "successfully disconnects" do 15 | em_amqp_connect do 16 | EventMachine.run do 17 | c = described_class.connect 18 | 19 | c.disconnect do 20 | puts "Disconnection callback has fired!" 21 | done 22 | end 23 | end 24 | end # em_amqp_connect 25 | end # it 26 | end # describe -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "bundler" 4 | 5 | Bundler.setup 6 | Bundler.require(:default, :test) 7 | 8 | require 'amq/client' 9 | 10 | # 11 | # Ruby version-specific 12 | # 13 | 14 | require "effin_utf8" 15 | 16 | case RUBY_VERSION 17 | when "1.8.7" then 18 | class Array 19 | alias sample choice 20 | end 21 | when /^1.9/ then 22 | Encoding.default_internal = Encoding::UTF_8 23 | Encoding.default_external = Encoding::UTF_8 24 | end 25 | 26 | RSpec.configure do |c| 27 | c.filter_run_excluding :nojruby => true if RUBY_PLATFORM =~ /java/ 28 | end 29 | 30 | 31 | module PlatformDetection 32 | def mri? 33 | !defined?(RUBY_ENGINE) || (defined?(RUBY_ENGINE) && ("ruby" == RUBY_ENGINE)) 34 | end 35 | end -------------------------------------------------------------------------------- /lib/amq/client/consumer_tag_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | class ConsumerTagGenerator 6 | 7 | # 8 | # API 9 | # 10 | 11 | # @return [String] Generated consumer tag 12 | def generate 13 | "#{Kernel.rand}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}" 14 | end # generate 15 | 16 | # @return [String] Generated consumer tag 17 | def generate_for(queue) 18 | raise ArgumentError, "argument must respond to :name" unless queue.respond_to?(:name) 19 | 20 | "#{queue.name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}" 21 | end # generate_for(queue) 22 | end # ConsumerTagGenerator 23 | end # Client 24 | end # AMQ 25 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/channel_close.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Open and then close AMQ channel" do |connection| 8 | puts "AMQP connection is open: #{connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(connection, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | puts "Lets close it." 14 | channel.close do 15 | puts "Closed channel ##{channel.id}" 16 | puts 17 | connection.disconnect do 18 | puts 19 | puts "AMQP connection is now properly closed" 20 | EM.stop 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /examples/coolio_adapter/channel_close.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Open and then close AMQ channel" do |connection| 8 | puts "AMQP connection is open: #{connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(connection, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | puts "Lets close it." 14 | channel.close do 15 | puts "Closed channel ##{channel.id}" 16 | puts 17 | connection.disconnect do 18 | puts 19 | puts "AMQP connection is now properly closed" 20 | Coolio::Loop.default.stop 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/tx_select.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Choose to use acknowledgement transactions on a channel using tx.select" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | channel.tx_select do 11 | puts "Channel #{channel.id} is now using ack transactions" 12 | end 13 | 14 | show_stopper = Proc.new { 15 | client.disconnect do 16 | puts 17 | puts "AMQP connection is now properly closed" 18 | EM.stop 19 | end 20 | } 21 | 22 | Signal.trap "INT", show_stopper 23 | Signal.trap "TERM", show_stopper 24 | 25 | EM.add_timer(1, show_stopper) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/integration/coolio/channel_close_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Channel.Close", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1 9 | 10 | it "should close the channel" do 11 | @events = [] 12 | coolio_amqp_connect do |client| 13 | @events << :connect 14 | channel = AMQ::Client::Channel.new(client, 1) 15 | channel.open do 16 | @events << :open 17 | channel.close do 18 | @events << :close 19 | client.disconnect do 20 | @events << :disconnect 21 | done 22 | end 23 | end 24 | end 25 | end 26 | @events.should == [:connect, :open, :close, :disconnect] 27 | end 28 | end -------------------------------------------------------------------------------- /spec/integration/eventmachine/channel_close_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Channel.Close" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1 9 | 10 | it "should close the channel" do 11 | @events = [] 12 | em_amqp_connect do |connection| 13 | @events << :connect 14 | channel = AMQ::Client::Channel.new(connection, 1) 15 | channel.open do 16 | @events << :open 17 | channel.close do 18 | @events << :close 19 | connection.disconnect do 20 | @events << :disconnect 21 | done 22 | end 23 | end 24 | end 25 | end 26 | @events.should == [:connect, :open, :close, :disconnect] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/tx_commit.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Commit acknowledgement transaction using tx.commit" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | channel.tx_select do |_| 11 | channel.tx_commit do |_| 12 | puts "Transaction on channel #{channel.id} is now committed" 13 | end 14 | end 15 | 16 | show_stopper = Proc.new { 17 | client.disconnect do 18 | puts 19 | puts "AMQP connection is now properly closed" 20 | EM.stop 21 | end 22 | } 23 | 24 | Signal.trap "INT", show_stopper 25 | Signal.trap "TERM", show_stopper 26 | 27 | EM.add_timer(1, show_stopper) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/authentication/plain_password_with_default_role_credentials.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__)), "..") 5 | require File.join(__dir, "example_helper") 6 | 7 | EM.run do 8 | AMQ::Client::EventMachineClient.connect(:port => 5672, :vhost => "/amq_client_testbed", :user => "guest", :password => "guest") do |client| 9 | puts "Connected, authenticated" 10 | 11 | puts "client.authenticating? => #{client.authenticating?}" 12 | 13 | 14 | show_stopper = Proc.new { 15 | client.disconnect do 16 | puts 17 | puts "AMQP connection is now properly closed" 18 | EM.stop 19 | end 20 | } 21 | 22 | Signal.trap "INT", show_stopper 23 | Signal.trap "TERM", show_stopper 24 | 25 | EM.add_timer(1, show_stopper) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/tx_rollback.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Rollback acknowledgement transaction using tx.rollback" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | channel.tx_select do |_| 11 | channel.tx_rollback do |_| 12 | puts "Transaction on channel #{channel.id} is now rolled back" 13 | end 14 | end 15 | 16 | show_stopper = Proc.new { 17 | client.disconnect do 18 | puts 19 | puts "AMQP connection is now properly closed" 20 | EM.stop 21 | end 22 | } 23 | 24 | Signal.trap "INT", show_stopper 25 | Signal.trap "TERM", show_stopper 26 | 27 | EM.add_timer(1, show_stopper) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_qos.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Alter prefetching settings using basic.qos" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) do |q, _, _, _| 11 | channel.qos do |_| 12 | puts "basic.qos callback has fired" 13 | end 14 | end 15 | 16 | show_stopper = Proc.new { 17 | client.disconnect do 18 | puts 19 | puts "AMQP connection is now properly closed" 20 | EM.stop 21 | end 22 | } 23 | 24 | Signal.trap "INT", show_stopper 25 | Signal.trap "TERM", show_stopper 26 | 27 | EM.add_timer(1, show_stopper) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/amq/client/async/auth_mechanism_adapter/plain.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ::Client::Async 4 | 5 | # Manages the encoding of credentials for the PLAIN authentication 6 | # mechanism. 7 | class AuthMechanismAdapter::Plain < AuthMechanismAdapter 8 | 9 | auth_mechanism "PLAIN" 10 | 11 | # Encodes credentials for the given username and password. This 12 | # involves sending the password across the wire in plaintext, so 13 | # PLAIN authentication should only be used over a secure transport 14 | # layer. 15 | # 16 | # @param [String] username The username to encode. 17 | # @param [String] password The password to encode. 18 | # @return [String] The username and password, encoded for the PLAIN 19 | # mechanism. 20 | def encode_credentials(username, password) 21 | "\0#{username}\0#{password}" 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/queue_declare_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "queue.declare" do 7 | 8 | # 9 | # Environment 10 | # 11 | 12 | include EventedSpec::SpecHelper 13 | default_timeout 1 14 | 15 | 16 | 17 | # 18 | # Examples 19 | # 20 | 21 | context "when queue name is nil" do 22 | it "raises ArgumentError" do 23 | em_amqp_connect do |connection| 24 | ch = AMQ::Client::Channel.new(connection, 1) 25 | ch.open do |ch| 26 | begin 27 | AMQ::Client::Queue.new(connection, ch, nil) 28 | rescue ArgumentError => ae 29 | ae.message.should =~ /queue name must not be nil/ 30 | 31 | done 32 | end 33 | end 34 | end 35 | end 36 | end # context 37 | end 38 | -------------------------------------------------------------------------------- /examples/coolio_adapter/exchange_declare.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Declare a new fanout exchange" do |client| 8 | puts "AMQP connection is open: #{client.connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(client, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | end 14 | 15 | exchange = AMQ::Client::Exchange.new(client, channel, "amqclient.adapters.em.exchange1", :fanout) 16 | exchange.declare 17 | 18 | show_stopper = Proc.new { 19 | client.disconnect do 20 | puts 21 | puts "AMQP connection is now properly closed" 22 | Coolio::Loop.default.stop 23 | end 24 | } 25 | 26 | Signal.trap "INT", show_stopper 27 | Signal.trap "TERM", show_stopper 28 | end 29 | -------------------------------------------------------------------------------- /spec/integration/coolio/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | begin 4 | require "amq/client/adapters/coolio" 5 | rescue LoadError => e 6 | if RUBY_PLATFORM =~ /java/ 7 | puts "WARNING: Cool.io specs will not run on jruby" 8 | else 9 | # reraise, cause unknown 10 | raise e 11 | end 12 | end 13 | require "amq/client/queue" 14 | require "amq/client/exchange" 15 | require "evented-spec" 16 | 17 | case RUBY_VERSION 18 | when "1.8.7" then 19 | class Array 20 | alias sample choice 21 | end 22 | when /^1.9/ then 23 | Encoding.default_internal = Encoding::UTF_8 24 | Encoding.default_external = Encoding::UTF_8 25 | end 26 | 27 | def coolio_amqp_connect(&block) 28 | coolio do 29 | AMQ::Client::CoolioClient.connect(:port => 5672, :vhost => "amq_client_testbed", :frame_max => 2**16-1, :heartbeat_interval => 1) do |client| 30 | yield client 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_recover.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Notify broker about consumer recovery using basic.recover" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) do |q, _, _, _| 11 | channel.recover do |_| 12 | puts "basic.recover callback has fired" 13 | end 14 | end 15 | 16 | show_stopper = Proc.new { 17 | client.disconnect do 18 | puts 19 | puts "AMQP connection is now properly closed" 20 | EM.stop 21 | end 22 | } 23 | 24 | Signal.trap "INT", show_stopper 25 | Signal.trap "TERM", show_stopper 26 | 27 | EM.add_timer(1, show_stopper) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/amq/client/framing/io/frame.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/exceptions" 4 | 5 | module AMQ 6 | module Client 7 | module Framing 8 | module IO 9 | 10 | class Frame < AMQ::Protocol::Frame 11 | def self.decode(io) 12 | header = io.read(7) 13 | type, channel, size = self.decode_header(header) 14 | data = io.read(size + 1) 15 | payload, frame_end = data[0..-2], data[-1, 1] 16 | # TODO: this will hang if the size is bigger than expected or it'll leave there some chars -> make it more error-proof: 17 | # BTW: socket#eof? 18 | raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET 19 | self.new(type, payload, channel) 20 | end # self.from 21 | 22 | end # Frame 23 | end # IO 24 | end # Framing 25 | end # Client 26 | end # AMQ 27 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/authentication/plain_password_with_incorrect_credentials_handled_with_a_rescue_block.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__)), "..") 5 | require File.join(__dir, "example_helper") 6 | 7 | begin 8 | EventMachine.run do 9 | AMQ::Client::EventMachineClient.connect(:port => 5672, 10 | :vhost => "/amq_client_testbed", 11 | :user => "amq_client_gem", 12 | :password => "a password that is incorrect #{Time.now.to_i}") do |client| 13 | raise "Should not really be executed" 14 | end 15 | end 16 | rescue AMQ::Client::PossibleAuthenticationFailureError => afe 17 | puts "Authentication failed, as expected. Caught #{afe.inspect}" 18 | EventMachine.stop if EventMachine.reactor_running? 19 | end 20 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "..", "..", "example_helper") 6 | 7 | require "amq/client/extensions/rabbitmq/confirm" 8 | 9 | amq_client_example "confirm.select (a RabbitMQ extension)" do |client| 10 | channel = AMQ::Client::Channel.new(client, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open" 13 | 14 | channel.confirm_select do |select_ok| 15 | puts "Broker replied with confirm.select_ok" 16 | end 17 | 18 | show_stopper = Proc.new { 19 | client.disconnect do 20 | puts 21 | puts "AMQP connection is now properly closed" 22 | EM.stop 23 | end 24 | } 25 | 26 | Signal.trap "INT", show_stopper 27 | Signal.trap "TERM", show_stopper 28 | 29 | EM.add_timer(1, show_stopper) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /bin/set_test_suite_realms_up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # guest:guest has full access to / 4 | 5 | rabbitmqctl add_vhost / 6 | rabbitmqctl add_user guest guest 7 | rabbitmqctl set_permissions -p / guest ".*" ".*" ".*" 8 | 9 | 10 | # guest:guest has full access to amq_client_testbed 11 | # amq_client_gem:amq_client_gem has full access to /amq_client_testbed 12 | 13 | rabbitmqctl delete_vhost "amq_client_testbed" 14 | rabbitmqctl add_vhost "amq_client_testbed" 15 | rabbitmqctl delete_user amq_client_gem 16 | rabbitmqctl add_user amq_client_gem amq_client_gem_password 17 | rabbitmqctl set_permissions -p amq_client_testbed guest ".*" ".*" ".*" 18 | rabbitmqctl set_permissions -p amq_client_testbed amq_client_gem ".*" ".*" ".*" 19 | 20 | 21 | # amqp_gem_reader:reader_password has read access to amq_client_testbed 22 | 23 | rabbitmqctl add_user amq_client_gem_reader reader_password 24 | rabbitmqctl set_permissions -p amq_client_testbed amq_client_gem_reader "^---$" "^---$" ".*" -------------------------------------------------------------------------------- /examples/eventmachine_adapter/authentication/plain_password_with_custom_role_credentials.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__)), "..") 5 | require File.join(__dir, "example_helper") 6 | 7 | EM.run do 8 | AMQ::Client::EventMachineClient.connect(:port => 5672, 9 | :vhost => "/amq_client_testbed", 10 | :user => "amq_client_gem", 11 | :password => "amq_client_gem_password") do |client| 12 | puts "Connected, authenticated" 13 | 14 | 15 | show_stopper = Proc.new { 16 | client.disconnect do 17 | puts 18 | puts "AMQP connection is now properly closed" 19 | EM.stop 20 | end 21 | } 22 | 23 | Signal.trap "INT", show_stopper 24 | Signal.trap "TERM", show_stopper 25 | 26 | EM.add_timer(1, show_stopper) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /examples/coolio_adapter/queue_bind.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Bind a new queue to amq.fanout" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel, "amqclient.queue2") 14 | queue.declare do 15 | puts "Queue #{queue.name.inspect} is now declared!" 16 | end 17 | 18 | queue.bind("amq.fanout") do 19 | puts "Queue #{queue.name} is now bound to amq.fanout" 20 | puts 21 | puts "Deleting queue #{queue.name}" 22 | queue.delete do |message_count| 23 | puts "Deleted." 24 | puts 25 | client.disconnect do 26 | puts 27 | puts "AMQP connection is now properly closed" 28 | Coolio::Loop.default.stop 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/authentication/plain_password_with_incorrect_credentials_handled_with_a_callback.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__)), "..") 5 | require File.join(__dir, "example_helper") 6 | 7 | EM.run do 8 | AMQ::Client::EventMachineClient.connect(:port => 5672, 9 | :vhost => "/amq_client_testbed", 10 | :user => "amq_client_gem", 11 | :password => "a password that is incorrect #{Time.now.to_i}", :on_possible_authentication_failure => Proc.new { |settings| 12 | puts "Authentication failed, as expected, settings are: #{settings.inspect}" 13 | 14 | EventMachine.stop 15 | }) do |client| 16 | raise "Should not really be executed" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /examples/tls_certificates/client/req.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICbDCCAVQCAQAwJzEUMBIGA1UEAxMLZ2lvdmUubG9jYWwxDzANBgNVBAoTBmNs 3 | aWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL/gJKoGOmY1egO3 4 | nFTQPg40bdtBcFXhsmJ3+ebMi9Xrulu/9WmorEz5SqhX13T278f5/+MAq7Sh/V7V 5 | 37WjT0m2MmyFfixhESHb+fS6YQ0fXELK9ac/R4VOK2GP2qZVwrlQ6+hkpIphPiC0 6 | IiliFFRBmQSE9gqG/F4/O2NEh5SCGQpBz1CLT94BGbx3dl2jQ5Gec00g/4bsGN5y 7 | Ym9MsI/88EMgYbqvV7Js166U1Z12AuBxSrQAIV6nEIlbpF0ZWsxeFaiZJy8qywhQ 8 | wB0N/Az19+rrgxgTZksbU4EE9580jjvDPyH1qTXlPZmkoVT1uJcuv9KcaF2E1HfV 9 | 3XqkC0sCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4IBAQAOpwkzMe1xYgYz8GOp5xF7 10 | xJtfeh5z9JTt8ogvjS34mri2vNUPQd35H3y6Dhh9ZjLqC03mMV/KOtRCf7oJXpgZ 11 | S8C/NyJWNXB7/QNf9/EWEyOxoWYuvhTxqc0MhLPWrJRjgBKvvCGszBj2kWxDn7OO 12 | //TRq4ao2wQWHIlaP5htsr+rD4IJZqZC8lgptv0u9O/O8vruPUg3k6dnuiKdUyTL 13 | 2CDsiKSc6vZKJKL0TguBHSQj/sybAw1lyxIlpxiDcTCbM5FsER6jFydLly+ov3+x 14 | sqMOTieWU3qnaqOwK2ENqQfRMAk3FXEuDzvW4t7oxHB2gJfUJXfEVUWv7Kol25VT 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /examples/tls_certificates/server/req.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICbDCCAVQCAQAwJzEUMBIGA1UEAxMLZ2lvdmUubG9jYWwxDzANBgNVBAoTBnNl 3 | cnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN0ESmeX4FFwjNng 4 | zqkHrj1xP9hTF5tN6RSrQeEVcBNtrjheRLHzU2fl0xWcqAmAlVGEHFTRmkG3j/Ci 5 | Apppgjj84ASBtRwfTc6cRcc0sHnzp4kZ1oUeRJA5+dRsEjOlAqVCMYt+OafqZeQt 6 | bmUs/k6s36em4Qjhar+OBshBCn23srCD49v1V0kbDR2YyVV88/wVUJGkYH2UlzUD 7 | LDyAoeDiaL+7IooV0tX0hZ/4NeW9FDa+WOZASFKrhu/GcK2binkHsAUd59OeV1KA 8 | ZFRm7Ijn/i8UzKkHd15X6WS73nnXi40XSCDrp4MB+g2MSZxrT0/4YHLwt8my6IF4 9 | VXiKu2MCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4IBAQC2aRbyq5pJ34M8jsaEjcks 10 | iov5KP17jfnHnz517/rKxKYRaoxftXVvUbtDLKMQNMAA9K65jetg6/6zqi3QTJwd 11 | 52Pn6OYTpzyFov42KRh1OSiRRa5CNXzDlHhosuVnVEOo2rdQggWZTY1clAbjEJmU 12 | N6bn6NSL4B/Vn8GAVxXhRGGoHj26LupdoI6GS2S3xuExw0xiS3usq6xYthpxHQ/L 13 | pQI2Ijk+IJZPnJR2RZji/7P3nWHWQX+HrCagCu4oyY4o7inWPVJpxD+1LmagRhv5 14 | RyMdQKoY+6x3/r+tWATTNOVqbGzfuKj6TQSkCYvOZOejlRBsMhyYlpldVMCRmaoh 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /bin/ci/before_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ${RABBITMQCTL:="sudo rabbitmqctl"} 4 | 5 | # guest:guest has full access to / 6 | 7 | $RABBITMQCTL add_vhost / 8 | $RABBITMQCTL add_user guest guest 9 | $RABBITMQCTL set_permissions -p / guest ".*" ".*" ".*" 10 | 11 | 12 | # guest:guest has full access to amq_client_testbed 13 | # amq_client_gem:amq_client_gem has full access to /amq_client_testbed 14 | 15 | $RABBITMQCTL delete_vhost "amq_client_testbed" 16 | $RABBITMQCTL add_vhost "amq_client_testbed" 17 | $RABBITMQCTL delete_user amq_client_gem 18 | $RABBITMQCTL add_user amq_client_gem amq_client_gem_password 19 | $RABBITMQCTL set_permissions -p amq_client_testbed guest ".*" ".*" ".*" 20 | $RABBITMQCTL set_permissions -p amq_client_testbed amq_client_gem ".*" ".*" ".*" 21 | 22 | 23 | # amqp_gem_reader:reader_password has read access to amq_client_testbed 24 | 25 | $RABBITMQCTL add_user amq_client_gem_reader reader_password 26 | $RABBITMQCTL set_permissions -p amq_client_testbed amq_client_gem_reader "^---$" "^---$" ".*" 27 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/error_handling/connection_failure_callback.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__))) 5 | require File.join(__dir, "..", "example_helper") 6 | 7 | EM.run do 8 | 9 | show_stopper = Proc.new { EventMachine.stop } 10 | Signal.trap "TERM", show_stopper 11 | EM.add_timer(4, show_stopper) 12 | 13 | 14 | AMQ::Client::EventMachineClient.connect(:port => 9689, 15 | :vhost => "amq_client_testbed", 16 | :user => "amq_client_gem", 17 | :password => "amq_client_gem_password", 18 | :timeout => 0.3, 19 | :on_tcp_connection_failure => Proc.new { |settings| puts "Failed to connect, as expected"; EM.stop }) do |client| 20 | raise "Connected, authenticated. This is not what this example is supposed to do!" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/queue_bind.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Bind a new queue to amq.fanout" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | AMQ::Client::Queue.new(client, channel, "amqclient.queue2").declare do |queue, declare_ok| 14 | puts queue.class.name 15 | puts "Queue #{queue.name.inspect} is now declared!" 16 | 17 | queue.bind("amq.fanout") do 18 | puts "Queue #{queue.name} is now bound to amq.fanout." 19 | puts 20 | puts "Deleting queue #{queue.name}" 21 | queue.delete do |message_count| 22 | puts "Deleted." 23 | puts 24 | client.disconnect do 25 | puts 26 | puts "AMQP connection is now properly closed" 27 | EM.stop 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/queue_purge.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Purge a queue and announce how many messages it had" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel, "amqclient.queue2") 14 | queue.declare(false, false, false, true) do 15 | puts "Queue #{queue.name.inspect} is now declared!" 16 | end 17 | 18 | queue.purge do |message_count| 19 | puts "Queue #{queue.name} is now purged. It had #{message_count} messages." 20 | puts 21 | puts "Deleting queue #{queue.name}" 22 | queue.delete do |message_count| 23 | puts "Deleted." 24 | puts 25 | client.disconnect do 26 | puts 27 | puts "AMQP connection is now properly closed" 28 | EM.stop 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /examples/coolio_adapter/queue_purge.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Purge a queue and announce how many messages it had" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel, "amqclient.queue2") 14 | queue.declare(false, false, false, true) do 15 | puts "Queue #{queue.name.inspect} is now declared!" 16 | end 17 | 18 | queue.purge do |message_count| 19 | puts "Queue #{queue.name} is now purged. It had #{message_count} messages." 20 | puts 21 | puts "Deleting queue #{queue.name}" 22 | queue.delete do |message_count| 23 | puts "Deleted." 24 | puts 25 | client.disconnect do 26 | puts 27 | puts "AMQP connection is now properly closed" 28 | Coolio::Loop.default.stop 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /examples/coolio_adapter/basic_publish.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Publish 100 messages using basic.publish" do |client| 8 | puts "AMQP connection is open: #{client.connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(client, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | end 14 | 15 | exchange = AMQ::Client::Exchange.new(client, channel, "amqclient.adapters.em.exchange1", :fanout) 16 | exchange.declare do 17 | 100.times do 18 | # exchange.publish("à bientôt!") 19 | exchange.publish("See you soon!") 20 | end 21 | 22 | $stdout.flush 23 | end 24 | 25 | show_stopper = Proc.new { 26 | client.disconnect do 27 | puts 28 | puts "AMQP connection is now properly closed" 29 | Coolio::Loop.default.stop 30 | end 31 | } 32 | 33 | Signal.trap "INT", show_stopper 34 | Signal.trap "TERM", show_stopper 35 | end 36 | -------------------------------------------------------------------------------- /lib/amq/client/async/auth_mechanism_adapter/external.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ::Client::Async 4 | 5 | # Manages the encoding of credentials for the EXTERNAL authentication 6 | # mechanism. 7 | class AuthMechanismAdapter::External < AuthMechanismAdapter 8 | 9 | auth_mechanism "EXTERNAL" 10 | 11 | # Encodes a username and password for the EXTERNAL mechanism. Since 12 | # authentication is handled by an encapsulating protocol like SSL or 13 | # UNIX domain sockets, EXTERNAL doesn't pass along any username or 14 | # password information at all and this method always returns the 15 | # empty string. 16 | # 17 | # @param [String] username The username to encode. This parameter is 18 | # ignored. 19 | # @param [String] password The password to encode. This parameter is 20 | # ignored. 21 | # @return [String] The username and password, encoded for the 22 | # EXTERNAL mechanism. This is always the empty string. 23 | def encode_credentials(username, password) 24 | "" 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/channel_flow_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Channel#flow" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 2 9 | 10 | it "controls channel flow state" do 11 | em_amqp_connect do |client| 12 | flow_states = [] 13 | 14 | channel = AMQ::Client::Channel.new(client, 1) 15 | channel.open do 16 | channel.flow_is_active?.should be_true 17 | 18 | channel.flow(false) do |flow_status1| 19 | channel.flow_is_active?.should be_false 20 | flow_states << channel.flow_is_active? 21 | 22 | channel.flow(true) do |flow_status2| 23 | channel.flow_is_active?.should be_true 24 | flow_states << channel.flow_is_active? 25 | end # channel.flow 26 | end # channel.flow 27 | end # channel.open 28 | 29 | done(1.0) { flow_states.should == [false, true] } 30 | end # em_amqp_connect 31 | end # it 32 | end # describe -------------------------------------------------------------------------------- /examples/tls_certificates/testca/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICxjCCAa6gAwIBAgIJAOF/re9e0JhgMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV 3 | BAMTCE15VGVzdENBMB4XDTExMDQxNzIwMDcxN1oXDTEyMDQxNjIwMDcxN1owEzER 4 | MA8GA1UEAxMITXlUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB 5 | AQDK8ZdVo1llXXrS7ed5Luel1VRq0DmJG1TZ8bAgovK9fx6WsgzeJ17zYwAEheUz 6 | B8PogkNqU3ZIa4T51VCtamoiG6bbKYpco4lutKM7aNGNJNcUfDhwyt/NYOxXM3xf 7 | ahMWjrbH0e4qKJgEjnJLpoeEa+YquDG2NXzocZY5+upy0pX6Reh1EQbju69j9f5Q 8 | Z6u4cvraScyN5IYuq3lKTmc2TxVyINVkpK9DlGJZUBAOBLSbFxGZDRoY0D3b1aeS 9 | /XMfVvcrJi48Ns2ZvT+CAy0KVeKBeQ9+6kD/YAizcxDGLRvqOZXOHYaE2L51DabL 10 | QTxk9NFPgIxrYtGS7RtQ7dUhAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0P 11 | BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAzFtKwsrCBaIAf1/VCJZh4+gzs/UW+ 12 | UXeC17GRzazb0O0drm9aMOtwE48NGVTwBjqmnPzFg56FYyll3oVZHz/fgkQZ+6iu 13 | qAbwlmY2H+L9RyC9Mv3B+Ew/o3MNCQQ3rgxbA7hb0k8mwDplxS5GBRROwZl/zHks 14 | gb4yPR6G83+zrgn6JIybdEGUySc1XDx+p6fVXvQG/1fsmExN9/ZuC6ulgBF7e+R5 15 | d7l1AluGL4kS7GMDRZnU9QcXkhnlUyPXIDr/Jd1HFKtwgrXtVl5YIWTaRdWOXGwX 16 | Q8BpM3Vk/NQFoTHO4Na3y8JY6iJzYTIXWHjI6RJdUffsEPtBoysHFHYv 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/error_handling/connection_failure_exception.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__))) 5 | require File.join(__dir, "..", "example_helper") 6 | 7 | begin 8 | EventMachine.run do 9 | 10 | show_stopper = Proc.new { EventMachine.stop } 11 | Signal.trap "TERM", show_stopper 12 | EventMachine.add_timer(4, show_stopper) 13 | 14 | 15 | AMQ::Client::EventMachineClient.connect(:port => 9689, 16 | :vhost => "amq_client_testbed", 17 | :user => "amq_client_gem", 18 | :password => "amq_client_gem_password", 19 | :timeout => 0.3) do |client| 20 | raise "Connected, authenticated. This is not what this example is supposed to do!" 21 | end 22 | end 23 | rescue AMQ::Client::TCPConnectionFailed => e 24 | puts "TCP connection has failed, as expected. Shutting down…" 25 | EventMachine.stop if EventMachine.reactor_running? 26 | end 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Jakub Šťastný aka Botanicus 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_publish.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Publish 100 messages using basic.publish" do |connection| 8 | puts "AMQP connection is open: #{connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(connection, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | end 14 | 15 | exchange = AMQ::Client::Exchange.new(connection, channel, "amqclient.adapters.em.exchange1", :fanout) 16 | exchange.declare do 17 | 100.times do 18 | # exchange.publish("à bientôt!") 19 | exchange.publish("See you soon!") 20 | print "." 21 | end 22 | 23 | $stdout.flush 24 | end 25 | 26 | show_stopper = Proc.new { 27 | connection.disconnect do 28 | puts 29 | puts "AMQP connection is now properly closed" 30 | EM.stop 31 | end 32 | } 33 | 34 | EM.add_periodic_timer(1, show_stopper) 35 | 36 | Signal.trap "INT", show_stopper 37 | Signal.trap "TERM", show_stopper 38 | end 39 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/queue_unbind.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Bind and then unbind a queue to amq.fanout" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel, "amqclient.queue2") 14 | queue.declare do 15 | puts "Queue #{queue.name.inspect} is now declared!" 16 | end 17 | 18 | queue.bind("amq.fanout") do 19 | puts "Queue #{queue.name} is now bound to amq.fanout" 20 | 21 | queue.unbind("amq.fanout") do 22 | puts "Queue #{queue.name} is now unbound from amq.fanout" 23 | 24 | puts 25 | puts "Deleting queue #{queue.name}" 26 | queue.delete do |message_count| 27 | puts "Deleted." 28 | puts 29 | client.disconnect do 30 | puts 31 | puts "AMQP connection is now properly closed" 32 | EM.stop 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /examples/coolio_adapter/queue_unbind.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Bind and then unbind a queue to amq.fanout" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel, "amqclient.queue2") 14 | queue.declare do 15 | puts "Queue #{queue.name.inspect} is now declared!" 16 | end 17 | 18 | queue.bind("amq.fanout") do 19 | puts "Queue #{queue.name} is now bound to amq.fanout" 20 | 21 | queue.unbind("amq.fanout") do 22 | puts "Queue #{queue.name} is now unbound from amq.fanout" 23 | 24 | puts 25 | puts "Deleting queue #{queue.name}" 26 | queue.delete do |message_count| 27 | puts "Deleted." 28 | puts 29 | client.disconnect do 30 | puts 31 | puts "AMQP connection is now properly closed" 32 | Coolio::Loop.default.stop 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/channel_flow.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Activate or deactivate channel delivery using channel.flow" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) do |q, _, _, _| 11 | puts "flow is now #{channel.flow_is_active? ? 'on' : 'off'}" 12 | 13 | channel.flow(false) do |method| 14 | puts "flow is now #{method.active ? 'on' : 'off'}" 15 | end 16 | 17 | sleep 0.1 18 | channel.flow(true) do |method| 19 | puts "flow is now #{method.active ? 'on' : 'off'}" 20 | end 21 | end 22 | 23 | show_stopper = Proc.new { 24 | client.disconnect do 25 | puts 26 | puts "AMQP connection is now properly closed" 27 | EM.stop 28 | end 29 | } 30 | 31 | Signal.trap "INT", show_stopper 32 | Signal.trap "TERM", show_stopper 33 | 34 | EM.add_timer(1, show_stopper) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /examples/tls_certificates/client/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC5DCCAcygAwIBAgIBAjANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl 3 | c3RDQTAeFw0xMTA0MTcyMDE4MzNaFw0xMjA0MTYyMDE4MzNaMCcxFDASBgNVBAMT 4 | C2dpb3ZlLmxvY2FsMQ8wDQYDVQQKEwZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUA 5 | A4IBDwAwggEKAoIBAQC/4CSqBjpmNXoDt5xU0D4ONG3bQXBV4bJid/nmzIvV67pb 6 | v/VpqKxM+UqoV9d09u/H+f/jAKu0of1e1d+1o09JtjJshX4sYREh2/n0umENH1xC 7 | yvWnP0eFTithj9qmVcK5UOvoZKSKYT4gtCIpYhRUQZkEhPYKhvxePztjRIeUghkK 8 | Qc9Qi0/eARm8d3Zdo0ORnnNNIP+G7BjecmJvTLCP/PBDIGG6r1eybNeulNWddgLg 9 | cUq0ACFepxCJW6RdGVrMXhWomScvKssIUMAdDfwM9ffq64MYE2ZLG1OBBPefNI47 10 | wz8h9ak15T2ZpKFU9biXLr/SnGhdhNR31d16pAtLAgMBAAGjLzAtMAkGA1UdEwQC 11 | MAAwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEB 12 | BQUAA4IBAQCVBpz3gZRr1s48SVF4+C9YLzrSaWsvzKZNDKH7RJ4f1VR//ZY5zsYi 13 | RqSlzSfLM76Y6Y/Eq0iFshtKmuXHKyA4r/Gp+iiCw4U9Htk91rg98wAPc8wOBgn1 14 | OmomH65JpLwxYvUwyt91opGppcqZHWhruhI0fFTFtPIlGKK3KOmJLPpaSvY0YTJ+ 15 | vaI3D6yQEMQoZ/mcXk928ofJJvOpUEmvjTW4Orz+T8NmiffLb64P50h86bdV+8tw 16 | FJx6ix6vLF41LU2iPEYHuuXkA7+M5e+POGscJJCb1p6JKxzI6D/UVDnrbhOlqBa5 17 | U45f0oXQ/ndOYUrBRu3BPFdNAjvpa0ld 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /examples/tls_certificates/server/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC5DCCAcygAwIBAgIBATANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl 3 | c3RDQTAeFw0xMTA0MTcyMDE3MjdaFw0xMjA0MTYyMDE3MjdaMCcxFDASBgNVBAMT 4 | C2dpb3ZlLmxvY2FsMQ8wDQYDVQQKEwZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUA 5 | A4IBDwAwggEKAoIBAQDdBEpnl+BRcIzZ4M6pB649cT/YUxebTekUq0HhFXATba44 6 | XkSx81Nn5dMVnKgJgJVRhBxU0ZpBt4/wogKaaYI4/OAEgbUcH03OnEXHNLB586eJ 7 | GdaFHkSQOfnUbBIzpQKlQjGLfjmn6mXkLW5lLP5OrN+npuEI4Wq/jgbIQQp9t7Kw 8 | g+Pb9VdJGw0dmMlVfPP8FVCRpGB9lJc1Ayw8gKHg4mi/uyKKFdLV9IWf+DXlvRQ2 9 | vljmQEhSq4bvxnCtm4p5B7AFHefTnldSgGRUZuyI5/4vFMypB3deV+lku95514uN 10 | F0gg66eDAfoNjEmca09P+GBy8LfJsuiBeFV4irtjAgMBAAGjLzAtMAkGA1UdEwQC 11 | MAAwCwYDVR0PBAQDAgUgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEB 12 | BQUAA4IBAQCCmvsAQ1GaPfoKMqCWxyrQ2BNVPWm9J65thZKxZQF01gLox9cpv6+X 13 | QERcD71HStxbzh2B8Hebbk9OplGuyrdgfvzUxcn88kObj6udipDx4YxTJvtff/9w 14 | xeD5OWDVgef0GkB1Rjj3C3W/sfmTZBYfdKuWuwxMG/NxISkQP4aFHwJnPrzNx9ON 15 | bHoKVNrQ2iKAiMysjnFeA/4QuhBQRf41h9SBWwJEW3Ts91TzbgcjCL46Dq29QB9A 16 | 4v8t6K/nibP6n53zHbVzdxIEJ2hFKm+vuqaHRW3048Xgww1xkdxrVbyGp9si92i1 17 | KJ5SDIOR8bQ7OXvdvpiyy6p9fIG0Z6w0 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/certs/01.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC5DCCAcygAwIBAgIBATANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl 3 | c3RDQTAeFw0xMTA0MTcyMDE3MjdaFw0xMjA0MTYyMDE3MjdaMCcxFDASBgNVBAMT 4 | C2dpb3ZlLmxvY2FsMQ8wDQYDVQQKEwZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUA 5 | A4IBDwAwggEKAoIBAQDdBEpnl+BRcIzZ4M6pB649cT/YUxebTekUq0HhFXATba44 6 | XkSx81Nn5dMVnKgJgJVRhBxU0ZpBt4/wogKaaYI4/OAEgbUcH03OnEXHNLB586eJ 7 | GdaFHkSQOfnUbBIzpQKlQjGLfjmn6mXkLW5lLP5OrN+npuEI4Wq/jgbIQQp9t7Kw 8 | g+Pb9VdJGw0dmMlVfPP8FVCRpGB9lJc1Ayw8gKHg4mi/uyKKFdLV9IWf+DXlvRQ2 9 | vljmQEhSq4bvxnCtm4p5B7AFHefTnldSgGRUZuyI5/4vFMypB3deV+lku95514uN 10 | F0gg66eDAfoNjEmca09P+GBy8LfJsuiBeFV4irtjAgMBAAGjLzAtMAkGA1UdEwQC 11 | MAAwCwYDVR0PBAQDAgUgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEB 12 | BQUAA4IBAQCCmvsAQ1GaPfoKMqCWxyrQ2BNVPWm9J65thZKxZQF01gLox9cpv6+X 13 | QERcD71HStxbzh2B8Hebbk9OplGuyrdgfvzUxcn88kObj6udipDx4YxTJvtff/9w 14 | xeD5OWDVgef0GkB1Rjj3C3W/sfmTZBYfdKuWuwxMG/NxISkQP4aFHwJnPrzNx9ON 15 | bHoKVNrQ2iKAiMysjnFeA/4QuhBQRf41h9SBWwJEW3Ts91TzbgcjCL46Dq29QB9A 16 | 4v8t6K/nibP6n53zHbVzdxIEJ2hFKm+vuqaHRW3048Xgww1xkdxrVbyGp9si92i1 17 | KJ5SDIOR8bQ7OXvdvpiyy6p9fIG0Z6w0 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/certs/02.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC5DCCAcygAwIBAgIBAjANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl 3 | c3RDQTAeFw0xMTA0MTcyMDE4MzNaFw0xMjA0MTYyMDE4MzNaMCcxFDASBgNVBAMT 4 | C2dpb3ZlLmxvY2FsMQ8wDQYDVQQKEwZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUA 5 | A4IBDwAwggEKAoIBAQC/4CSqBjpmNXoDt5xU0D4ONG3bQXBV4bJid/nmzIvV67pb 6 | v/VpqKxM+UqoV9d09u/H+f/jAKu0of1e1d+1o09JtjJshX4sYREh2/n0umENH1xC 7 | yvWnP0eFTithj9qmVcK5UOvoZKSKYT4gtCIpYhRUQZkEhPYKhvxePztjRIeUghkK 8 | Qc9Qi0/eARm8d3Zdo0ORnnNNIP+G7BjecmJvTLCP/PBDIGG6r1eybNeulNWddgLg 9 | cUq0ACFepxCJW6RdGVrMXhWomScvKssIUMAdDfwM9ffq64MYE2ZLG1OBBPefNI47 10 | wz8h9ak15T2ZpKFU9biXLr/SnGhdhNR31d16pAtLAgMBAAGjLzAtMAkGA1UdEwQC 11 | MAAwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEB 12 | BQUAA4IBAQCVBpz3gZRr1s48SVF4+C9YLzrSaWsvzKZNDKH7RJ4f1VR//ZY5zsYi 13 | RqSlzSfLM76Y6Y/Eq0iFshtKmuXHKyA4r/Gp+iiCw4U9Htk91rg98wAPc8wOBgn1 14 | OmomH65JpLwxYvUwyt91opGppcqZHWhruhI0fFTFtPIlGKK3KOmJLPpaSvY0YTJ+ 15 | vaI3D6yQEMQoZ/mcXk928ofJJvOpUEmvjTW4Orz+T8NmiffLb64P50h86bdV+8tw 16 | FJx6ix6vLF41LU2iPEYHuuXkA7+M5e+POGscJJCb1p6JKxzI6D/UVDnrbhOlqBa5 17 | U45f0oXQ/ndOYUrBRu3BPFdNAjvpa0ld 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/authentication/connection_open_failure_due_to_invalid_vhost.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__)), "..") 5 | require File.join(__dir, "example_helper") 6 | 7 | begin 8 | EventMachine.run do 9 | 10 | show_stopper = Proc.new { 11 | EM.stop 12 | } 13 | 14 | Signal.trap "INT", show_stopper 15 | Signal.trap "TERM", show_stopper 16 | 17 | EventMachine.add_timer(4, show_stopper) 18 | 19 | AMQ::Client::EventMachineClient.connect(:port => 9689, 20 | :vhost => "/a/b/c/#{rand}/d/#{Time.now.to_i}", 21 | :user => "amq_client_gem", 22 | :password => "amq_client_gem_password", 23 | :timeout => 0.3) do |client| 24 | raise "Connected, authenticated. This is not what this example is supposed to do!" 25 | end 26 | end 27 | rescue AMQ::Client::TCPConnectionFailed => e 28 | puts "TCP connection has failed, as expected. Shutting down…" 29 | EventMachine.stop if EventMachine.reactor_running? 30 | end -------------------------------------------------------------------------------- /examples/coolio_adapter/example_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "bundler" 4 | 5 | Bundler.setup 6 | Bundler.require(:default) 7 | 8 | $LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__)) 9 | 10 | require "amq/client/adapters/coolio" 11 | require "amq/client/queue" 12 | require "amq/client/exchange" 13 | 14 | 15 | if RUBY_VERSION.to_s =~ /^1.9/ 16 | Encoding.default_internal = Encoding::UTF_8 17 | Encoding.default_external = Encoding::UTF_8 18 | end 19 | 20 | 21 | def amq_client_example(description = "", &block) 22 | AMQ::Client::CoolioClient.connect(:port => 5672, :vhost => "amq_client_testbed") do |client| 23 | begin 24 | puts 25 | puts 26 | puts "=============> #{description}" 27 | 28 | block.call(client) 29 | rescue Interrupt 30 | warn "Manually interrupted, terminating ..." 31 | rescue Exception => exception 32 | STDERR.puts "\n\e[1;31m[#{exception.class}] #{exception.message}\e[0m" 33 | exception.backtrace.each do |line| 34 | line = "\e[0;36m#{line}\e[0m" if line.match(Regexp::quote(File.basename(__FILE__))) 35 | STDERR.puts " - " + line 36 | end 37 | end 38 | end 39 | 40 | cool.io.run 41 | end 42 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_return.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "basic.return example" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 11 | 12 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 13 | exchange.on_return do |method, header, body| 14 | puts "Handling a returned message: exchange = #{method.exchange}, reply_code = #{method.reply_code}, reply_text = #{method.reply_text}" 15 | puts "Body of the returned message: #{body}" 16 | end 17 | 18 | 10.times do |i| 19 | exchange.publish("Message ##{i}", AMQ::Protocol::EMPTY_STRING, {}, false, true) 20 | end 21 | 22 | show_stopper = Proc.new { 23 | client.disconnect do 24 | puts 25 | puts "AMQP connection is now properly closed" 26 | EM.stop 27 | end 28 | } 29 | 30 | Signal.trap "INT", show_stopper 31 | Signal.trap "TERM", show_stopper 32 | 33 | EM.add_timer(1, show_stopper) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/integration/coolio/tx_commit_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Tx.Commit", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 4 9 | let(:message) { "Hello, world!" } 10 | 11 | 12 | # 13 | # Examples 14 | # 15 | 16 | it "should confirm transaction completeness" do 17 | received_messages = [] 18 | coolio_amqp_connect do |client| 19 | channel = AMQ::Client::Channel.new(client, 1) 20 | channel.open do 21 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 22 | queue = AMQ::Client::Queue.new(client, channel) 23 | 24 | queue.declare(false, false, false, true) do 25 | queue.bind(exchange) 26 | end 27 | 28 | channel.tx_select do 29 | queue.consume(true) do |header, payload, delivery_tag, redelivered, exchange, routing_key, message_count| 30 | received_messages << message 31 | done 32 | end 33 | 34 | exchange.publish(message) 35 | channel.tx_commit 36 | end 37 | end 38 | end 39 | received_messages.should == [message] 40 | end 41 | end -------------------------------------------------------------------------------- /examples/eventmachine_adapter/queue_declare.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Purge a queue and announce how many messages it had" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | 12 | 4.times do 13 | q = AMQ::Client::Queue.new(client, channel, AMQ::Protocol::EMPTY_STRING) 14 | q.declare(false, false, false, true) 15 | end 16 | 17 | begin 18 | q = AMQ::Client::Queue.new(client, channel, AMQ::Protocol::EMPTY_STRING) 19 | q.declare(false, false, false, true, true) 20 | rescue ArgumentError => e 21 | puts "Non-sensical declaration of a server-named queue with nowait did not slip through, great" 22 | end 23 | 24 | 25 | queue = AMQ::Client::Queue.new(client, channel, AMQ::Protocol::EMPTY_STRING) 26 | queue.declare(false, false, false, true) do 27 | puts "Queue #{queue.name.inspect} is now declared!" 28 | 29 | puts "Channel is aware of the following queues: #{channel.queues.map { |q| q.name }.join(', ')}" 30 | 31 | client.disconnect { EM.stop } 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/tx_rollback_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'integration/eventmachine/spec_helper' 4 | 5 | describe AMQ::Client::EventMachineClient, "Tx.Rollback" do 6 | include EventedSpec::SpecHelper 7 | default_timeout 4 8 | let(:message) { "Hello, world!" } 9 | it "should cancel all the changes done during transaction" do 10 | pending("Need to figure out details with AMQP protocol on that matter") 11 | received_messages = [] 12 | em_amqp_connect do |client| 13 | channel = AMQ::Client::Channel.new(client, 1) 14 | channel.open do 15 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 16 | queue = AMQ::Client::Queue.new(client, channel) 17 | 18 | queue.declare(false, false, false, true) do 19 | queue.bind(exchange) 20 | end 21 | 22 | channel.tx_select do 23 | done(0.1) 24 | queue.consume(true) do |header, payload, delivery_tag, redelivered, exchange, routing_key, message_count| 25 | received_messages << message 26 | end 27 | 28 | exchange.publish(message) 29 | channel.tx_rollback 30 | end 31 | end 32 | end 33 | received_messages.should == [] 34 | end 35 | end -------------------------------------------------------------------------------- /examples/eventmachine_adapter/example_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "bundler" 4 | 5 | Bundler.setup 6 | Bundler.require(:default) 7 | 8 | $LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__)) 9 | 10 | require "amq/client/adapters/event_machine" 11 | require "amq/client/queue" 12 | require "amq/client/exchange" 13 | 14 | 15 | if RUBY_VERSION.to_s =~ /^1.9/ 16 | Encoding.default_internal = Encoding::UTF_8 17 | Encoding.default_external = Encoding::UTF_8 18 | end 19 | 20 | 21 | def amq_client_example(description = "", &block) 22 | EM.run do 23 | AMQ::Client::EventMachineClient.connect(:port => 5672, :vhost => "amq_client_testbed", :frame_max => 65536, :heartbeat_interval => 1) do |client| 24 | begin 25 | puts 26 | puts 27 | puts "=============> #{description}" 28 | 29 | block.call(client) 30 | rescue Interrupt 31 | warn "Manually interrupted, terminating ..." 32 | rescue Exception => exception 33 | STDERR.puts "\n\e[1;31m[#{exception.class}] #{exception.message}\e[0m" 34 | exception.backtrace.each do |line| 35 | line = "\e[0;36m#{line}\e[0m" if line.match(Regexp::quote(File.basename(__FILE__))) 36 | STDERR.puts " - " + line 37 | end 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /examples/coolio_adapter/basic_consume_with_acknowledgements.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Set a queue up for message delivery" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel) 14 | queue.declare 15 | 16 | queue.bind("amq.fanout") do 17 | puts "Queue #{queue.name} is now bound to amq.fanout" 18 | end 19 | 20 | queue.consume do |consumer_tag| 21 | queue.on_delivery do |method, header, payload| 22 | puts "Got a delivery: #{payload} (delivery tag: #{method.delivery_tag}), ack-ing..." 23 | 24 | queue.acknowledge(method.delivery_tag) 25 | end 26 | 27 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 28 | 10.times do |i| 29 | exchange.publish("Message ##{i}") 30 | end 31 | end 32 | 33 | show_stopper = Proc.new { 34 | client.disconnect do 35 | puts 36 | puts "AMQP connection is now properly closed" 37 | Coolio::Loop.default.stop 38 | end 39 | } 40 | 41 | Signal.trap "INT", show_stopper 42 | Signal.trap "TERM", show_stopper 43 | end 44 | -------------------------------------------------------------------------------- /spec/integration/coolio/tx_rollback_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | require 'integration/coolio/spec_helper' 4 | 5 | describe "AMQ::Client::CoolioClient", "Tx.Rollback", :nojruby => true do 6 | include EventedSpec::SpecHelper 7 | default_timeout 4 8 | 9 | let(:message) { "Hello, world!" } 10 | 11 | 12 | 13 | # 14 | # Examples 15 | # 16 | 17 | 18 | it "should cancel all the changes done during transaction" do 19 | pending("Need to figure out details with AMQP protocol on that matter") 20 | received_messages = [] 21 | coolio_amqp_connect do |client| 22 | channel = AMQ::Client::Channel.new(client, 1) 23 | channel.open do 24 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 25 | queue = AMQ::Client::Queue.new(client, channel) 26 | 27 | queue.declare(false, false, false, true) do 28 | queue.bind(exchange) 29 | end 30 | 31 | channel.tx_select do 32 | done(0.1) 33 | queue.consume(true) do |method, header, message| 34 | received_messages << message unless message.nil? 35 | end 36 | 37 | exchange.publish(message) 38 | channel.tx_rollback 39 | end 40 | end 41 | end 42 | 43 | received_messages.should == [] 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/channel_level_exception_handling.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Channel-level exception handling" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open" 11 | 12 | show_stopper = Proc.new { 13 | client.disconnect do 14 | puts 15 | puts "AMQP connection is now properly closed" 16 | EM.stop 17 | end 18 | } 19 | 20 | 21 | 22 | channel.on_error do |ch, close| 23 | puts "Oops, there is a channel-level exception: #{close.inspect}" 24 | 25 | EM.add_timer(1.2) { show_stopper.call } 26 | end 27 | 28 | # amq.* names are reserevd so this causes a channel-level exception. MK. 29 | x = AMQ::Client::Exchange.new(client, channel, "amq.client.examples.extensions.x1", :direct) 30 | x.declare(false, false, false, true, false) 31 | 32 | 33 | EM.add_timer(1) do 34 | if channel.closed? 35 | puts "1 second later channel #{channel.id} is closed" 36 | end 37 | end 38 | 39 | Signal.trap "INT", show_stopper 40 | Signal.trap "TERM", show_stopper 41 | 42 | EM.add_timer(2, show_stopper) 43 | end 44 | end -------------------------------------------------------------------------------- /spec/integration/eventmachine/basic_ack_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Basic.Ack" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 4 9 | 10 | context "sending 100 messages" do 11 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 12 | 13 | it "should receive all the messages" do 14 | @received_messages = [] 15 | em_amqp_connect do |client| 16 | channel = AMQ::Client::Channel.new(client, 1) 17 | channel.open do 18 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 19 | queue.bind("amq.fanout") 20 | 21 | queue.consume do |amq_method| 22 | queue.on_delivery do |method, header, payload| 23 | queue.acknowledge(method.delivery_tag) 24 | @received_messages << payload 25 | end 26 | 27 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 28 | messages.each do |message| 29 | exchange.publish(message) 30 | end 31 | end 32 | 33 | done(3.5) { 34 | @received_messages =~ messages 35 | } 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/tx_commit_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Tx.Commit" do 7 | 8 | # 9 | # Environment 10 | # 11 | 12 | include EventedSpec::SpecHelper 13 | default_timeout 4 14 | 15 | let(:message) { "Hello, world!" } 16 | 17 | 18 | # 19 | # Examples 20 | # 21 | 22 | it "should confirm transaction completeness" do 23 | received_messages = [] 24 | em_amqp_connect do |client| 25 | channel = AMQ::Client::Channel.new(client, 1) 26 | channel.open do 27 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 28 | queue = AMQ::Client::Queue.new(client, channel) 29 | 30 | queue.declare(false, false, false, true) do 31 | queue.bind(exchange) 32 | end 33 | 34 | channel.tx_select do 35 | queue.consume(true) do |header, payload, delivery_tag, redelivered, exchange, routing_key, message_count| 36 | received_messages << message 37 | done 38 | end 39 | 40 | exchange.publish(message) 41 | channel.tx_commit 42 | end 43 | end # em_amqp_connect 44 | 45 | end 46 | received_messages.should == [message] 47 | end # it 48 | end # describe -------------------------------------------------------------------------------- /amq-client.gemspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env gem build 2 | # encoding: utf-8 3 | 4 | require "base64" 5 | require File.expand_path("../lib/amq/client/version", __FILE__) 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "amq-client" 9 | s.version = AMQ::Client::VERSION.dup 10 | s.authors = ["Jakub Stastny", "Michael S. Klishin", "Theo Hultberg", "Mark Abramov"] 11 | s.email = [Base64.decode64("c3Rhc3RueUAxMDFpZGVhcy5jeg==\n"), "michael@novemberain.com"] 12 | s.homepage = "http://github.com/ruby-amqp/amq-client" 13 | s.summary = "amq-client is a fully-featured, low-level AMQP 0.9.1 client" 14 | s.description = "amq-client is a fully-featured, low-level AMQP 0.9.1 client with pluggable networking I/O adapters (EventMachine, cool.io, Eventpanda and so on) and supposed to back more opinionated AMQP clients (such as amqp gem) or be used directly in cases when access to more advanced AMQP 0.9.1 features is more important that convenient APIs" 15 | 16 | # files 17 | s.files = `git ls-files`.split("\n").reject { |file| file =~ /^vendor\// || file =~ /^gemfiles\// } 18 | s.require_paths = ["lib"] 19 | s.extra_rdoc_files = ["README.textile"] + Dir.glob("doc/*") 20 | 21 | # Dependencies 22 | s.add_dependency "eventmachine" 23 | s.add_dependency "amq-protocol", ">= 1.2.0" 24 | 25 | 26 | # RubyForge 27 | s.rubyforge_project = "amq-client" 28 | end 29 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_consume_with_rejections.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Reject a message using basic.reject" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel) 14 | queue.declare 15 | 16 | queue.bind("amq.fanout") do 17 | puts "Queue #{queue.name} is now bound to amq.fanout" 18 | end 19 | 20 | queue.consume do |consumer_tag| 21 | queue.on_delivery do |method, header, payload| 22 | puts "Got a delivery: #{payload} (delivery tag: #{method.delivery_tag}), rejecting..." 23 | 24 | queue.reject(method.delivery_tag, false) 25 | end 26 | 27 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 28 | 10.times do |i| 29 | exchange.publish("Message ##{i}") 30 | end 31 | end 32 | 33 | show_stopper = Proc.new { 34 | client.disconnect do 35 | puts 36 | puts "AMQP connection is now properly closed" 37 | EM.stop 38 | end 39 | } 40 | 41 | Signal.trap "INT", show_stopper 42 | Signal.trap "TERM", show_stopper 43 | 44 | EM.add_timer(1, show_stopper) 45 | end 46 | -------------------------------------------------------------------------------- /lib/amq/protocol/get_response.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # Purpose of this class is to simplify work with GetOk and GetEmpty. 4 | module AMQ 5 | module Protocol 6 | class GetResponse 7 | attr_reader :method 8 | def initialize(method) 9 | @method = method 10 | end 11 | 12 | def empty? 13 | @method.is_a?(::AMQ::Protocol::Basic::GetEmpty) 14 | end 15 | 16 | # GetOk attributes 17 | def delivery_tag 18 | if @method.respond_to?(:delivery_tag) 19 | @method.delivery_tag 20 | end 21 | end 22 | 23 | def redelivered 24 | if @method.respond_to?(:redelivered) 25 | @method.redelivered 26 | end 27 | end 28 | 29 | def exchange 30 | if @method.respond_to?(:exchange) 31 | @method.exchange 32 | end 33 | end 34 | 35 | def routing_key 36 | if @method.respond_to?(:routing_key) 37 | @method.routing_key 38 | end 39 | end 40 | 41 | def message_count 42 | if @method.respond_to?(:message_count) 43 | @method.message_count 44 | end 45 | end 46 | 47 | # GetEmpty attributes 48 | def cluster_id 49 | if @method.respond_to?(:cluster_id) 50 | @method.cluster_id 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/amq/client/openable.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | module Openable 6 | VALUES = [:opened, :closed, :opening, :closing].freeze 7 | 8 | class ImproperStatusError < ArgumentError 9 | def initialize(value) 10 | super("Value #{value.inspect} isn't permitted. Choose one of: #{AMQ::Client::Openable::VALUES.inspect}") 11 | end 12 | end 13 | 14 | attr_reader :status 15 | def status=(value) 16 | if VALUES.include?(value) 17 | @status = value 18 | else 19 | raise ImproperStatusError.new(value) 20 | end 21 | end 22 | 23 | def opened? 24 | @status == :opened 25 | end 26 | alias open? opened? 27 | 28 | def closed? 29 | @status == :closed 30 | end 31 | 32 | 33 | 34 | def opening? 35 | @status == :opening 36 | end 37 | 38 | def closing? 39 | @status == :closing 40 | end 41 | 42 | 43 | def opened! 44 | @status = :opened 45 | end # opened! 46 | 47 | def closed! 48 | @status = :closed 49 | end # closed! 50 | 51 | 52 | 53 | def opening! 54 | @status = :opening 55 | end # opening! 56 | 57 | def closing! 58 | @status = :closing 59 | end # closing! 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /examples/coolio_adapter/basic_consume_with_rejections.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Set a queue up for message delivery" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel) 14 | queue.declare 15 | 16 | queue.bind("amq.fanout") do 17 | puts "Queue #{queue.name} is now bound to amq.fanout" 18 | end 19 | 20 | queue.consume do |consumer_tag| 21 | queue.on_delivery do |header, payload, consumer_tag, delivery_tag, redelivered, exchange, routing_key| 22 | puts "Got a delivery: #{payload} (delivery tag: #{delivery_tag}), rejecting..." 23 | 24 | queue.reject(delivery_tag, false) 25 | end 26 | 27 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 28 | 10.times do |i| 29 | exchange.publish("Message ##{i}") 30 | end 31 | end 32 | 33 | show_stopper = Proc.new { 34 | client.disconnect do 35 | puts 36 | puts "AMQP connection is now properly closed" 37 | Coolio::Loop.default.stop 38 | end 39 | } 40 | 41 | Signal.trap "INT", show_stopper 42 | Signal.trap "TERM", show_stopper 43 | end 44 | -------------------------------------------------------------------------------- /spec/integration/coolio/channel_flow_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Channel.Flow", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1 9 | 10 | it "should control the flow of channel" do 11 | coolio_amqp_connect do |client| 12 | channel = AMQ::Client::Channel.new(client, 1) 13 | channel.open do 14 | channel.flow_is_active?.should be_true 15 | channel.flow(false) do |_, flow_active| 16 | flow_active.should be_false 17 | channel.flow(true) do |_, flow_active| 18 | flow_active.should be_true 19 | end 20 | end 21 | done 22 | end 23 | end 24 | end 25 | 26 | 27 | it "should not raise errors when no state change occurs" do 28 | coolio_amqp_connect do |client| 29 | channel = AMQ::Client::Channel.new(client, 1) 30 | expect { 31 | channel.open do 32 | channel.flow_is_active?.should be_true 33 | channel.flow(false) do |_, flow_active| 34 | flow_active.should be_false 35 | channel.flow(false) do |_, flow_active| 36 | flow_active.should be_false 37 | end 38 | end 39 | done 40 | end 41 | }.to_not raise_error 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_consume_with_acknowledgements.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Acknowledge a message using basic.ack" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel) 14 | queue.declare 15 | 16 | queue.bind("amq.fanout") do 17 | puts "Queue #{queue.name} is now bound to amq.fanout" 18 | end 19 | 20 | queue.consume do |consumer_tag| 21 | queue.on_delivery do |basic_deliver, header, payload| 22 | puts "Got a delivery: #{payload} (delivery tag: #{basic_deliver.delivery_tag}), ack-ing..." 23 | 24 | queue.acknowledge(basic_deliver.delivery_tag) 25 | end 26 | 27 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 28 | 10.times do |i| 29 | exchange.publish("Message ##{i}") 30 | end 31 | end 32 | 33 | show_stopper = Proc.new { 34 | client.disconnect do 35 | puts 36 | puts "AMQP connection is now properly closed" 37 | EventMachine.stop 38 | end 39 | } 40 | 41 | Signal.trap "INT", show_stopper 42 | Signal.trap "TERM", show_stopper 43 | 44 | EM.add_timer(1, show_stopper) 45 | end 46 | -------------------------------------------------------------------------------- /spec/integration/coolio/basic_ack_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Basic.Ack", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1 9 | 10 | context "sending 100 messages" do 11 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 12 | 13 | it "should receive & acknowledge all the messages" do 14 | @received_messages = [] 15 | coolio_amqp_connect do |client| 16 | channel = AMQ::Client::Channel.new(client, 1) 17 | channel.open do 18 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 19 | queue.bind("amq.fanout") 20 | 21 | queue.consume do |_, consumer_tag| 22 | queue.on_delivery do |method, header, payload| 23 | queue.acknowledge(method.delivery_tag) 24 | @received_messages << payload 25 | end 26 | 27 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 28 | messages.each do |message| 29 | exchange.publish(message) 30 | end 31 | end # consume 32 | end # open 33 | 34 | done(0.8) { 35 | @received_messages.should =~ messages 36 | } 37 | end # coolio_amqp_connect 38 | end # it 39 | end # context 40 | end # describe 41 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_cancel.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Set a queue up for message delivery" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | queue = AMQ::Client::Queue.new(client, channel) 11 | queue.declare(false, false, false, true) 12 | 13 | queue.bind("amq.fanout") do 14 | puts "Queue #{queue.name} is now bound to amq.fanout" 15 | end 16 | 17 | queue.consume(true) do |consumer_tag| 18 | queue.on_delivery do |method, header, payload| 19 | puts "Received #{payload}" 20 | end 21 | 22 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 23 | 100.times do |i| 24 | exchange.publish("Message ##{i}") 25 | end 26 | 27 | 28 | queue.cancel do 29 | 100.times do |i| 30 | exchange.publish("Message ##{i} that MUST NOT have been routed to #{queue.name}") 31 | end 32 | end 33 | end 34 | end 35 | 36 | 37 | show_stopper = Proc.new { 38 | client.disconnect do 39 | puts 40 | puts "AMQP connection is now properly closed" 41 | EM.stop 42 | end 43 | } 44 | 45 | Signal.trap "INT", show_stopper 46 | Signal.trap "TERM", show_stopper 47 | 48 | EM.add_timer(1, show_stopper) 49 | end 50 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | source "https://rubygems.org" 4 | 5 | # Use local clones if possible. 6 | # If you want to use your local copy, just symlink it to vendor. 7 | # See http://blog.101ideas.cz/posts/custom-gems-in-gemfile.html 8 | extend Module.new { 9 | def gem(name, *args) 10 | options = args.last.is_a?(Hash) ? args.last : Hash.new 11 | 12 | local_path = File.expand_path("../vendor/#{name}", __FILE__) 13 | if File.exist?(local_path) 14 | puts "Using #{name} from #{local_path}..." 15 | super name, options.merge(:path => local_path). 16 | delete_if { |key, _| [:git, :branch].include?(key) } 17 | else 18 | super name, *args 19 | end 20 | end 21 | } 22 | 23 | gem "eventmachine", ">= 1.0.0" 24 | # cool.io uses iobuffer that won't compile on JRuby 25 | # (and, probably, Windows) 26 | gem "cool.io", :platform => :ruby 27 | gem "amq-protocol", :git => "git://github.com/ruby-amqp/amq-protocol.git", :branch => "master" 28 | 29 | group :development do 30 | gem "yard" 31 | # yard tags this buddy along 32 | gem "RedCloth", :platform => :mri 33 | end 34 | 35 | group :test do 36 | gem "rake", ">= 10.0.3" 37 | gem "rspec", ">= 2.13.0" 38 | gem "evented-spec", :git => "git://github.com/ruby-amqp/evented-spec.git", :branch => "master" 39 | gem "effin_utf8" 40 | 41 | gem "multi_json" 42 | 43 | gem "json", :platform => :jruby 44 | gem "yajl-ruby", :platform => :ruby_18 45 | end 46 | -------------------------------------------------------------------------------- /spec/integration/coolio/basic_return_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Basic.Return", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1.0 9 | 10 | context "when messages are sent to a direct exchange not bound to a queue" do 11 | let(:messages) { (0..9).map {|i| "Message #{i}" } } 12 | 13 | it "should return all the messages" do 14 | @returned_messages = [] 15 | coolio_amqp_connect do |client| 16 | channel = AMQ::Client::Channel.new(client, 1) 17 | channel.open do 18 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, false) 19 | 20 | # need to delete the queue manually because we don't start consumption, 21 | # hence, no auto-delete 22 | delayed(0.4) { queue.delete } 23 | 24 | exchange = AMQ::Client::Exchange.new(client, channel, "direct-exchange", :direct).declare.on_return do |method, header, body| 25 | @returned_messages << method.reply_text 26 | end 27 | 28 | messages.each do |message| 29 | exchange.publish(message, AMQ::Protocol::EMPTY_STRING, {}, true, false) 30 | end 31 | end 32 | 33 | done(0.6) { @returned_messages.size == messages.size } 34 | end 35 | 36 | @returned_messages.should == ["NO_ROUTE"] * messages.size 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/basic_return_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Basic.Return" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1.0 9 | 10 | context "when messages are sent to a direct exchange not bound to a queue" do 11 | let(:messages) { (0..9).map {|i| "Message #{i}" } } 12 | 13 | it "should return all the messages" do 14 | @returned_messages = [] 15 | em_amqp_connect do |client| 16 | channel = AMQ::Client::Channel.new(client, 1) 17 | channel.open do 18 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 19 | # need to delete the queue manually because we don't start consumption, 20 | # hence, no auto-delete 21 | delayed(0.4) { queue.delete } 22 | 23 | exchange = AMQ::Client::Exchange.new(client, channel, "direct-exchange", :direct).declare.on_return do |method, header, body| 24 | @returned_messages << method.reply_text 25 | end 26 | 27 | messages.each do |message| 28 | exchange.publish(message, AMQ::Protocol::EMPTY_STRING, {}, true, false) 29 | end 30 | end 31 | 32 | done(0.6) { 33 | @returned_messages.size == messages.size 34 | } 35 | end 36 | 37 | @returned_messages.should == ["NO_ROUTE"] * messages.size 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /gemfiles/eventmachine-pre: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | source :rubygems 4 | 5 | # Use local clones if possible. 6 | # If you want to use your local copy, just symlink it to vendor. 7 | # See http://blog.101ideas.cz/posts/custom-gems-in-gemfile.html 8 | extend Module.new { 9 | def gem(name, *args) 10 | options = args.last.is_a?(Hash) ? args.last : Hash.new 11 | 12 | local_path = File.expand_path("../vendor/#{name}", __FILE__) 13 | if File.exist?(local_path) 14 | super name, options.merge(:path => local_path). 15 | delete_if { |key, _| [:git, :branch].include?(key) } 16 | else 17 | super name, *args 18 | end 19 | end 20 | } 21 | 22 | gem "eventmachine", "~> 1.0.0.beta.3" 23 | # cool.io uses iobuffer that won't compile on JRuby 24 | # (and, probably, Windows) 25 | gem "cool.io", :platform => :ruby 26 | gem "amq-protocol", :git => "git://github.com/ruby-amqp/amq-protocol.git", :branch => "master" 27 | 28 | group :development do 29 | gem "yard" 30 | # yard tags this buddy along 31 | gem "RedCloth", :platform => :mri 32 | 33 | gem "nake", :platform => :ruby_19 34 | 35 | # excludes Windows and JRuby 36 | gem "perftools.rb", :platform => :mri 37 | end 38 | 39 | group :test do 40 | gem "rspec", ">= 2.6.0" 41 | gem "evented-spec", :git => "git://github.com/ruby-amqp/evented-spec.git", :branch => "master" 42 | gem "effin_utf8" 43 | 44 | gem "multi_json" 45 | 46 | gem "json", :platform => :jruby 47 | gem "yajl-ruby", :platform => :ruby_18 48 | end 49 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/error_handling/handling_a_channel_level_exception.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "..", "example_helper") 6 | 7 | amq_client_example "Handling a channel-level exception" do |connection| 8 | channel = AMQ::Client::Channel.new(connection, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | 12 | channel.on_error do |ch, close| 13 | puts "Handling a channel-level exception: #{close.reply_text}" 14 | end 15 | 16 | EventMachine.add_timer(0.4) do 17 | # these two definitions result in a race condition. For sake of this example, 18 | # however, it does not matter. Whatever definition succeeds first, 2nd one will 19 | # cause a channel-level exception (because attributes are not identical) 20 | AMQ::Client::Queue.new(connection, channel, "amqpgem.examples.channel_exception").declare(false, false, false, true) do |queue| 21 | puts "#{queue.name} is ready to go" 22 | end 23 | 24 | AMQ::Client::Queue.new(connection, channel, "amqpgem.examples.channel_exception").declare(false, true, false, false) do |queue| 25 | puts "#{queue.name} is ready to go" 26 | end 27 | end 28 | end 29 | 30 | 31 | show_stopper = Proc.new do 32 | $stdout.puts "Stopping..." 33 | connection.close { EventMachine.stop } 34 | end 35 | 36 | Signal.trap "INT", show_stopper 37 | EM.add_timer(2, show_stopper) 38 | end 39 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/error_handling/connection_loss_handler_that_fails_over.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__))) 5 | require File.join(__dir, "..", "example_helper") 6 | 7 | 8 | EM.run do 9 | AMQ::Client::EventMachineClient.connect(:port => 5672, 10 | :vhost => "amq_client_testbed", 11 | :user => "amq_client_gem", 12 | :password => "amq_client_gem_password", 13 | :timeout => 0.3, 14 | :on_tcp_connection_failure => Proc.new { |settings| puts "Failed to connect, this was NOT expected"; EM.stop }) do |connection| 15 | 16 | connection.on_tcp_connection_loss do |conn, settings| 17 | puts "Trying to reconnect..." 18 | conn.reconnect_to(:host => "dev.rabbitmq.com") 19 | end 20 | 21 | connection.on_recovery do |conn, settings| 22 | puts "Connection recovered" 23 | end 24 | 25 | show_stopper = Proc.new { 26 | connection.disconnect { puts "Disconnected. Exiting…"; EventMachine.stop } 27 | } 28 | 29 | Signal.trap "TERM", show_stopper 30 | Signal.trap "INT", show_stopper 31 | EM.add_timer(30, show_stopper) 32 | 33 | 34 | puts "Connected, authenticated. To really exercise this example, shut AMQP broker down for a few seconds. If you don't it will exit gracefully in 30 seconds." 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/unit/client/adapter_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "ostruct" 4 | require "spec_helper" 5 | require "amq/client/adapter" 6 | 7 | class SampleAdapter 8 | include AMQ::Client::Adapter 9 | end 10 | 11 | describe SampleAdapter do 12 | before(:all) do 13 | load "amq/client.rb" 14 | end 15 | 16 | describe ".settings" do 17 | it "should provide some default values" do 18 | described_class.settings.should_not be_nil 19 | described_class.settings[:host].should_not be_nil 20 | end 21 | end 22 | 23 | describe ".logger" do 24 | it "should provide a default logger" do 25 | described_class.logger.should respond_to(:debug) 26 | described_class.logger.should respond_to(:info) 27 | described_class.logger.should respond_to(:error) 28 | described_class.logger.should respond_to(:fatal) 29 | end 30 | end 31 | 32 | describe ".logger=(logger)" do 33 | context "when new logger doesn't respond to all the necessary methods" do 34 | it "should raise an exception" do 35 | lambda { 36 | described_class.logger = Object.new 37 | }.should raise_error(AMQ::Client::Logging::IncompatibleLoggerError) 38 | end 39 | 40 | it "should pass if the object provides all the necessary methods" do 41 | described_class.logging = true 42 | 43 | mock = OpenStruct.new(:debug => nil, :info => nil, :error => nil, :fatal => nil) 44 | described_class.logger = mock 45 | described_class.logger.should eql(mock) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/integration/coolio/connection_start_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | # 7 | # We assume that default connection settings (amqp://guest:guest@localhost:5672/) should work 8 | # 9 | describe "AMQ::Client::CoolioClient", "Connection.Start", :nojruby => true do 10 | include EventedSpec::SpecHelper 11 | default_timeout 0.5 12 | 13 | context "with valid credentials" do 14 | it "should trigger the callback" do 15 | coolio do 16 | AMQ::Client::CoolioClient.connect do |client| 17 | client.should be_opened 18 | done 19 | end 20 | end 21 | end 22 | end 23 | 24 | context "with invalid credentials" do 25 | context "when given an errback" do 26 | it "should trigger the errback" do 27 | coolio do 28 | AMQ::Client::CoolioClient.connect(:port => 12938, :on_tcp_connection_failure => proc { done }) do |client| 29 | raise "This callback should never happen" 30 | end 31 | end 32 | end 33 | end 34 | 35 | context "when given no errback" do 36 | it "should raise an error" do 37 | expect { 38 | coolio do 39 | begin 40 | AMQ::Client::CoolioClient.connect(:port => 12938) { } 41 | done 42 | rescue Exception => e 43 | done(0.5) 44 | end 45 | end 46 | }.to raise_error(AMQ::Client::TCPConnectionFailed) 47 | end 48 | end # context 49 | end # context 50 | end # describe 51 | -------------------------------------------------------------------------------- /examples/coolio_adapter/kitchen_sink1.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "An example that combines several AMQ operations" do |client| 8 | puts "AMQP connection is open: #{client.connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(client, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | end 14 | 15 | queue = AMQ::Client::Queue.new(client, channel, "amqclient.queue2") 16 | queue.declare(false, false, false, true) do 17 | puts "Queue #{queue.name.inspect} is now declared!" 18 | end 19 | 20 | exchange = AMQ::Client::Exchange.new(client, channel, "amqclient.adapters.em.exchange1", :fanout) 21 | exchange.declare { puts "Exchange #{exchange.name.inspect} is now declared!" } 22 | 23 | queue.consume do |msg| 24 | puts msg 25 | end 26 | 27 | 28 | show_stopper = Proc.new { 29 | puts 30 | puts "Deleting queue #{queue.name}" 31 | queue.delete do |message_count| 32 | puts 33 | puts "Deleted #{queue.name}. It had #{message_count} messages in it." 34 | puts 35 | puts "Deleting exchange #{exchange.name}" 36 | exchange.delete do 37 | client.disconnect do 38 | puts 39 | puts "AMQP connection is now properly closed" 40 | Coolio::Loop.default.stop 41 | end 42 | end 43 | end 44 | } 45 | 46 | Signal.trap "INT", show_stopper 47 | Signal.trap "TERM", show_stopper 48 | end 49 | -------------------------------------------------------------------------------- /spec/integration/coolio/basic_cancel_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Basic.Cancel", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 4 9 | 10 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 11 | 12 | it "should stop receiving messages after receiving cancel-ok" do 13 | @received_messages = [] 14 | coolio_amqp_connect do |client| 15 | channel = AMQ::Client::Channel.new(client, 1) 16 | channel.open do 17 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 18 | queue.bind("amq.fanout") 19 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 20 | 21 | queue.consume(true) do |amq_method| 22 | queue.on_delivery do |method, header, payload| 23 | @received_messages << payload 24 | end 25 | 26 | messages.each do |message| 27 | exchange.publish(message) 28 | end 29 | end 30 | 31 | delayed(1.5) { 32 | @received_messages.should =~ messages 33 | queue.cancel do 34 | exchange.publish("Extra message, should not be received") 35 | end 36 | } 37 | 38 | done(2.5) { 39 | @received_messages.should =~ messages 40 | } 41 | end 42 | end 43 | 44 | end # it "should stop receiving messages after receiving cancel-ok" 45 | end # describe AMQ::Client::CoolioClient, "Basic.Consume" 46 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "..", "..", "example_helper") 6 | 7 | require "amq/client/extensions/rabbitmq/confirm" 8 | 9 | amq_client_example "Publisher confirmations using RabbitMQ extension: unroutable message scenario" do |client| 10 | channel = AMQ::Client::Channel.new(client, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open" 13 | 14 | channel.confirm_select 15 | channel.on_error do 16 | puts "Oops, there is a channel-levle exceptions!" 17 | end 18 | 19 | 20 | channel.on_ack do |basic_ack| 21 | puts "Received basic_ack: multiple = #{basic_ack.multiple}, delivery_tag = #{basic_ack.delivery_tag}" 22 | end 23 | 24 | x = AMQ::Client::Exchange.new(client, channel, AMQ::Protocol::EMPTY_STRING, :direct) 25 | x.on_return { |basic_return, metadata, payload| 26 | puts "Received basic.return: reply_text = #{basic_return.reply_text}, reply_code = #{basic_return.reply_code}" 27 | } 28 | 29 | 10.times { x.publish("A message", AMQ::Protocol::EMPTY_STRING, {}, true) } 30 | 31 | 32 | 33 | show_stopper = Proc.new { 34 | client.disconnect do 35 | puts 36 | puts "AMQP connection is now properly closed" 37 | EM.stop 38 | end 39 | } 40 | 41 | Signal.trap "INT", show_stopper 42 | Signal.trap "TERM", show_stopper 43 | 44 | EM.add_timer(3, show_stopper) 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/tls/tls_without_peer_verification.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | examples_dir = File.join(File.dirname(File.expand_path(__FILE__)), "..", "..") 5 | __dir = File.join(File.dirname(File.expand_path(__FILE__)), "..") 6 | require File.join(__dir, "example_helper") 7 | 8 | certificate_chain_file_path = File.join(examples_dir, "tls_certificates", "client", "cert.pem") 9 | client_private_key_file_path = File.join(examples_dir, "tls_certificates", "client", "key.pem") 10 | 11 | EM.run do 12 | AMQ::Client::EventMachineClient.connect(:port => 5671, 13 | :vhost => "amq_client_testbed", 14 | :user => "amq_client_gem", 15 | :password => "amq_client_gem_password", 16 | :ssl => { 17 | :cert_chain_file => certificate_chain_file_path, 18 | :private_key_file => client_private_key_file_path 19 | }) do |client| 20 | puts "Connected, authenticated. TLS seems to work." 21 | 22 | client.disconnect { puts "Now closing the connection…"; EM.stop } 23 | end 24 | 25 | 26 | show_stopper = Proc.new { 27 | EM.stop 28 | } 29 | 30 | Signal.trap "INT", show_stopper 31 | Signal.trap "TERM", show_stopper 32 | 33 | # TLS connections take forever and a day 34 | # (compared to non-TLS connections) to estabilish. 35 | EM.add_timer(8, show_stopper) 36 | end 37 | -------------------------------------------------------------------------------- /spec/unit/client/logging_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "spec_helper" 4 | 5 | class TestLogger 6 | def log(message) 7 | message 8 | end 9 | 10 | alias_method :debug, :log 11 | alias_method :info, :log 12 | alias_method :error, :log 13 | alias_method :fatal, :log 14 | end 15 | 16 | class LoggingTestClass 17 | attr_accessor :logging 18 | 19 | def client 20 | OpenStruct.new(:logger => TestLogger.new) 21 | end 22 | 23 | include AMQ::Client::Logging 24 | end 25 | 26 | describe AMQ::Client::Logging do 27 | # We have to use Kernel#load so extensions to the 28 | # Logging module from client.rb will be overridden. 29 | before(:all) do 30 | load "amq/client/logging.rb" 31 | 32 | AMQ::Client::Logging.logging = true 33 | end 34 | 35 | after(:all) do 36 | AMQ::Client::Logging.logging = false 37 | end 38 | 39 | context "including to an incompatible class" do 40 | it "should raise an NotImplementedError if the class doesn't define method client" do 41 | lambda { 42 | Class.new { include AMQ::Client::Logging } 43 | }.should raise_error(NotImplementedError) 44 | end 45 | end 46 | 47 | context "including to a compatible class" do 48 | subject { LoggingTestClass.new } 49 | 50 | it "should be able to log via #client#logger of given class" do 51 | subject.logging = true 52 | subject.debug("message").should eql("message") 53 | end 54 | 55 | it "should not log anything if subject#logging is false" do 56 | subject.logging = false 57 | subject.debug("message").should be_nil 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/unit/client/entity_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "spec_helper" 4 | require "amq/client/entity" 5 | 6 | describe AMQ::Client::Entity do 7 | let(:klazz) do 8 | Class.new do 9 | include AMQ::Client::Entity 10 | end 11 | end 12 | 13 | 14 | subject do 15 | klazz.new(Object.new) 16 | end 17 | 18 | it "should maintain an associative array of callbacks" do 19 | subject.callbacks.should be_kind_of(Hash) 20 | end 21 | 22 | describe "#has_callback?" do 23 | it "should return true if entity has at least one callback with given name" do 24 | subject.define_callback(:init, proc {}) 25 | subject.has_callback?(:init).should be_true 26 | end 27 | 28 | it "should return false if entity doesn't have callbacks with given name" do 29 | subject.has_callback?(:init).should be_false 30 | end 31 | end 32 | 33 | describe "#exec_callback" do 34 | it "executes callback for given event" do 35 | proc = Proc.new { |*args, &block| 36 | @called = true 37 | } 38 | 39 | expect { 40 | subject.define_callback(:init, proc) 41 | subject.define_callback :init do 42 | @called2 = true 43 | end 44 | }.to change(subject.callbacks, :size).from(0).to(1) 45 | 46 | subject.callbacks[:init].size.should == 2 47 | 48 | subject.exec_callback(:init) 49 | 50 | @called.should be_true 51 | @called2.should be_true 52 | end 53 | 54 | 55 | it "should pass arguments to the callback" do 56 | f = Proc.new { |*args| args.first } 57 | subject.define_callback :init, f 58 | 59 | subject.exec_callback(:init, 1).should eql([f]) 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_get_with_empty_queue.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Set a queue up for message delivery" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel) 14 | queue.declare(false, false, false, true) do 15 | puts "Server-named, auto-deletable Queue #{queue.name.inspect} is ready" 16 | end 17 | 18 | queue.bind("amq.fanout") do 19 | puts "Queue #{queue.name} is now bound to amq.fanout" 20 | end 21 | sleep 0.1 22 | 23 | 10.times do |i| 24 | queue.get(true) do |method, header, payload| 25 | puts "basic.get callback has fired" 26 | puts 27 | puts "Payload is #{payload}" 28 | if header 29 | puts "header is #{header.decode_payload.inspect}" 30 | else 31 | puts "header is nil" 32 | end 33 | puts "delivery_tag is #{method.delivery_tag.inspect}" 34 | puts "redelivered is #{method.redelivered.inspect}" 35 | puts "exchange is #{method.exchange.inspect}" 36 | puts "routing_key is #{method.routing_key.inspect}" 37 | puts "message_count is #{method.message_count.inspect}" 38 | end 39 | end 40 | 41 | show_stopper = Proc.new { 42 | client.disconnect do 43 | puts 44 | puts "AMQP connection is now properly closed" 45 | EM.stop 46 | end 47 | } 48 | 49 | Signal.trap "INT", show_stopper 50 | Signal.trap "TERM", show_stopper 51 | 52 | EM.add_timer(1, show_stopper) 53 | end 54 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/kitchen_sink1.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "An example that combines several AMQ operations" do |connection| 8 | puts "AMQP connection is open: #{connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(connection, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | end 14 | 15 | queue = AMQ::Client::Queue.new(connection, channel, "amqclient.queue2") 16 | queue.declare(false, false, false, true) do 17 | puts "Queue #{queue.name.inspect} is now declared!" 18 | end 19 | 20 | exchange = AMQ::Client::Exchange.new(connection, channel, "amqclient.adapters.em.exchange1", :fanout) 21 | exchange.declare { puts "Exchange #{exchange.name.inspect} is now declared!" } 22 | 23 | queue.consume do |consume_ok| 24 | puts "basic.consume_ok callback has fired for #{queue.name}, consumer_tag = #{consume_ok.consumer_tag}" 25 | end 26 | 27 | 28 | show_stopper = Proc.new { 29 | puts 30 | puts "Deleting queue #{queue.name}" 31 | queue.delete do |delete_ok| 32 | puts 33 | puts "Deleted #{queue.name}. It had #{delete_ok.message_count} messages in it." 34 | puts 35 | puts "Deleting exchange #{exchange.name}" 36 | exchange.delete do 37 | connection.disconnect do 38 | puts 39 | puts "AMQP connection is now properly closed" 40 | EM.stop 41 | end 42 | end 43 | end 44 | } 45 | 46 | Signal.trap "INT", show_stopper 47 | Signal.trap "TERM", show_stopper 48 | 49 | EM.add_timer(1, show_stopper) 50 | end 51 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/basic_cancel_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Basic.Cancel" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 4 9 | 10 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 11 | 12 | it "should stop receiving messages after receiving cancel-ok" do 13 | @received_messages = [] 14 | @received_basic_cancel_ok = false 15 | em_amqp_connect do |client| 16 | channel = AMQ::Client::Channel.new(client, 1) 17 | channel.open do 18 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 19 | queue.bind("amq.fanout") 20 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 21 | 22 | queue.consume(true) do |amq_method| 23 | queue.on_delivery do |method, header, payload| 24 | @received_messages << payload 25 | end 26 | 27 | messages.each do |message| 28 | exchange.publish(message) 29 | end 30 | end 31 | 32 | delayed(1.5) { 33 | @received_messages.should =~ messages 34 | queue.cancel do 35 | @received_basic_cancel_ok = true 36 | exchange.publish("Extra message, should not be received") 37 | end 38 | } 39 | 40 | done(2.5) { 41 | @received_messages.should =~ messages 42 | @received_basic_cancel_ok.should be_true 43 | } 44 | end 45 | end 46 | 47 | end # it "should stop receiving messages after receiving cancel-ok" 48 | end # describe AMQ::Client::EventMachineClient, "Basic.Consume" 49 | -------------------------------------------------------------------------------- /examples/coolio_adapter/basic_consume.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Set a queue up for message delivery" do |connection| 8 | channel = AMQ::Client::Channel.new(connection, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(connection, channel) 14 | queue.declare(false, false, false, true) do 15 | puts "Server-named, auto-deletable Queue #{queue.name.inspect} is ready" 16 | end 17 | 18 | queue.bind("amq.fanout") do 19 | puts "Queue #{queue.name} is now bound to amq.fanout" 20 | end 21 | 22 | show_stopper = Proc.new { 23 | connection.disconnect do 24 | puts 25 | puts "AMQP connection is now properly closed" 26 | Coolio::Loop.default.stop 27 | end 28 | } 29 | 30 | 31 | queue.consume(true) do |consume_ok| 32 | puts "Subscribed for messages routed to #{queue.name}, consumer tag is #{consume_ok.consumer_tag}, using no-ack mode" 33 | puts 34 | 35 | queue.on_delivery do |basic_deliver, header, payload| 36 | puts "Got a delivery:" 37 | puts " Delivery tag: #{basic_deliver.delivery_tag}" 38 | puts " Header: #{header.inspect}" 39 | puts " Payload: #{payload.inspect}" 40 | 41 | show_stopper.call if basic_deliver.delivery_tag == 100 42 | end 43 | 44 | exchange = AMQ::Client::Exchange.new(connection, channel, "amq.fanout", :fanout) 45 | 100.times do |i| 46 | exchange.publish("Message ##{i}") 47 | end 48 | end 49 | 50 | 51 | Signal.trap "INT", show_stopper 52 | Signal.trap "TERM", show_stopper 53 | end 54 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_consume.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Set a queue up for message delivery" do |connection| 8 | channel = AMQ::Client::Channel.new(connection, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(connection, channel) 14 | queue.declare(false, false, false, true) do 15 | puts "Server-named, auto-deletable Queue #{queue.name.inspect} is ready" 16 | end 17 | 18 | queue.bind("amq.fanout") do 19 | puts "Queue #{queue.name} is now bound to amq.fanout" 20 | end 21 | 22 | show_stopper = Proc.new { 23 | connection.disconnect do 24 | puts 25 | puts "AMQP connection is now properly closed" 26 | EM.stop 27 | end 28 | } 29 | 30 | queue.consume(true) do |consume_ok| 31 | puts "Subscribed for messages routed to #{queue.name}, consumer tag is #{consume_ok.consumer_tag}, using no-ack mode" 32 | puts 33 | 34 | queue.on_delivery do |basic_deliver, header, payload| 35 | puts "Got a delivery:" 36 | puts " Delivery tag: #{basic_deliver.delivery_tag}" 37 | puts " Header: #{header.inspect}" 38 | puts " Payload: #{payload.inspect}" 39 | 40 | show_stopper.call if basic_deliver.delivery_tag == 100 41 | end 42 | 43 | exchange = AMQ::Client::Exchange.new(connection, channel, "amq.fanout", :fanout) 44 | 100.times do |i| 45 | exchange.publish("Message ##{i}") 46 | end 47 | end 48 | 49 | Signal.trap "INT", show_stopper 50 | Signal.trap "TERM", show_stopper 51 | 52 | EM.add_timer(1, show_stopper) 53 | end 54 | -------------------------------------------------------------------------------- /examples/tls_certificates/client/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAv+AkqgY6ZjV6A7ecVNA+DjRt20FwVeGyYnf55syL1eu6W7/1 3 | aaisTPlKqFfXdPbvx/n/4wCrtKH9XtXftaNPSbYybIV+LGERIdv59LphDR9cQsr1 4 | pz9HhU4rYY/aplXCuVDr6GSkimE+ILQiKWIUVEGZBIT2Cob8Xj87Y0SHlIIZCkHP 5 | UItP3gEZvHd2XaNDkZ5zTSD/huwY3nJib0ywj/zwQyBhuq9XsmzXrpTVnXYC4HFK 6 | tAAhXqcQiVukXRlazF4VqJknLyrLCFDAHQ38DPX36uuDGBNmSxtTgQT3nzSOO8M/ 7 | IfWpNeU9maShVPW4ly6/0pxoXYTUd9XdeqQLSwIDAQABAoIBAQC1hScqcpns98pm 8 | md2bnyYV6iPDuTS9Crfnz/jrD7d6uXekNQXG33uhlnW6rN94F+TgGvKpnojSai14 9 | +nyZKdZotaUeZWvoMCw1DBZ8H5aRA4oU5k1f282dn0YLFjyT/64oAxYq1IuVHknY 10 | RpHK9K6mxygrzl21t94oi0lzr/FT29t8mSlcCQiHxAZWyGE55HvqSS4625P5pvh1 11 | Bh4bZGT285XvlA1qbKbEn8M1IsCZmhPU87U+k6lCvEpBG32cdg9Ykt+BA33OCJj0 12 | OrC6vKjLsIKEfbq8xoTjm7+fofv78B2/zobK7dFMVTAdnju8Tpwu/IDcHnLuKtt2 13 | Vd8GOwGBAoGBAPBO/EeHSvlT46F3jT8ygIhB2rGQ8LBZwQmiejOdfhJ5EPs8KC7U 14 | i9Wfo2Mktn7gr+X9nsY5ZHZoTFaFV1YSoIrsaWFd0RmuQKBcka+A9WPWEgRL0KrG 15 | Km9pHjlYK6WAu+XzKnGlHzasxxLW48mvuMQApE5U5nVbZWonGBA2psRNAoGBAMxn 16 | jHWH51SN5R/ldPvmNPEG8McR1XmzTM9M40snrj6UV8mLMH+KRHgoj664nhm1dnMa 17 | rolKzoFQtS5unwjCGX+Lx9AnwUDDak2Q6LsqedO0PhIwUEtcaLnt8Zf3KWi4v4Es 18 | eAHnYXZ1I7sqjCZ7pAwNBlCfE4Ai1XMH7syv5bn3AoGAYhbrlUrdSJ58eQkyp4w2 19 | uOegnABcrq9EQ2mF1hHAR0wRqWtpv4vUtFMgPzTaJToTsp5us9zza/2ww1RUTPb3 20 | fx12+l5wHrpo+MwwT2IlSCY2XniP6VVQNv/CbYfW8Qx2Jiu2tILBFfE6pS7/9fB7 21 | S84zQyIWm+q9n7HreZrFQbUCgYB4BnnY7eOtp+CaGS/XVrdRrOSn0FYNL3bz5080 22 | a2mKv6rI4x2oVrWb5R1x2GCl8gNK1akVX0LhdlXdnQFvsCIu3hKHrUByWg1K3MN8 23 | XBXLdXRZU9tiVTH9s7YAqRa8sndpT4Zsf8SgPWqs1fAUDNgoZ2GE59QYktvJvye/ 24 | M65uwwKBgDnuu0PZ6tn7dKZWYhfBeQT21DsaQglvY3kQ4RHHlHiQnyALKnZqbLu9 25 | wlAZIvRIltWeeQ+oT/Bxq9cD7GrdX8VmZ2xo/gYkJQoduT3AtyLrau8aZFWpiREH 26 | 2pDcznE7+DWcKKwr/Z2eQEOoE2KikeuAbMd2SG4vVTLG9w+N2e5I 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /examples/tls_certificates/server/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA3QRKZ5fgUXCM2eDOqQeuPXE/2FMXm03pFKtB4RVwE22uOF5E 3 | sfNTZ+XTFZyoCYCVUYQcVNGaQbeP8KICmmmCOPzgBIG1HB9NzpxFxzSwefOniRnW 4 | hR5EkDn51GwSM6UCpUIxi345p+pl5C1uZSz+Tqzfp6bhCOFqv44GyEEKfbeysIPj 5 | 2/VXSRsNHZjJVXzz/BVQkaRgfZSXNQMsPICh4OJov7siihXS1fSFn/g15b0UNr5Y 6 | 5kBIUquG78ZwrZuKeQewBR3n055XUoBkVGbsiOf+LxTMqQd3XlfpZLveedeLjRdI 7 | IOungwH6DYxJnGtPT/hgcvC3ybLogXhVeIq7YwIDAQABAoIBAEu0+YussZET/Ztw 8 | bznlQKEZVuZR6Ccxs+J5m1JvlnmBO4lheSR/lhVj2z9u7vx6SCupFk9TkQRrzWl/ 9 | BWdBNvMwY8gHajNc4bkhPKG1AbJ0bPIAoMPuj0vcICDMeBuqrVJQb0o6DaPgHdDg 10 | Yw1TMTVf8CiseC8jj/5XtykHZoGTNTKzusvTjL8hTXFaWQfHQaN3WC1qwrFU2+x6 11 | AJeoSz5F1Q/lykqVAdl2B1L39kiSCAkbVE1GI2qjftCff3ycufYV/fxXeyZwZx9B 12 | NGWUJFyZte8EcrAUoo9B/gvALGDbJsSUsbri+HsRsdOQT3K/giafUipX2FB8Bnxm 13 | nasEfskCgYEA74PrKYo13mTUWRJ2FQTBRHLsMR53PK83RpRRs8361/uCJrjpqfdD 14 | 2fUt8kH0rmm2vaWYsllucJoYdYSC9CQdECLzpOZS907PJHXCOsFsBjw74fvjZ+gY 15 | 9EXIENZSOSR1PQCWZm+5lL4xi/T+beWBfz0YksErj2GM7dyJeQjkIz8CgYEA7Dpx 16 | ANgrgO9WTu51NIlKt3P7+t6q6MHQcWuzkWMBmVU4zxeyggux+ucMj+l3KLyw7fLT 17 | jRz03eGpqjQT8Yl676uYONhTDC9VOBbLgjShuVOX7lleMLxVFJWqL1FphNghxysF 18 | HVCq1WH0Qu4keirPEotBxkNZRaRYHwRYlVPKMt0CgYEApIHSAj0IlNBiPS995R/X 19 | 8sCQU4heU1LxP0vd9gZy1OfNU/VLoE7RzqEkxrDgcu7u8cEMaOsd/L8KL6UtIKyx 20 | PYUUHV2I/I2nnp43Io35OSsj4ipU3eg/Q3+uU0oxPUg6MgT2SDNSnsQnWb6TBj5N 21 | PGxlNV7yIU/aMQF5dqVRtJcCgYEArC4Mn6jwTJImPnHgS+Kl6wFG8JvLxss9uu3d 22 | fGLFj5VmSsvi+Ja9qzstFNf+WlruOwF64KfycqdAmyZKQwsJ6BcSZJyIK6F0Y+V5 23 | f/YMyp/7ZWcOGEetW8uat9KHLqS6OglJOQzK96zl9MLPI5yAQevujSwZrYEUGcd5 24 | KZ5hCqECgYBExYSDJOuSMvKFw66jrLiBedLPuzh1iznwd6e4idpqIUkdhbEuXhHG 25 | +35HeN5cGwjbjXtIjxJsAddTbjvSqaPukmBKLm1ABqF3r0irbNaPcK1xfG9a5L28 26 | /enwipaSWjGovfDXWqmmCvWC8M7iGiqFChI87WlzbvaAapOW0D576Q== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/private/cakey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAyvGXVaNZZV160u3neS7npdVUatA5iRtU2fGwIKLyvX8elrIM 3 | 3ide82MABIXlMwfD6IJDalN2SGuE+dVQrWpqIhum2ymKXKOJbrSjO2jRjSTXFHw4 4 | cMrfzWDsVzN8X2oTFo62x9HuKiiYBI5yS6aHhGvmKrgxtjV86HGWOfrqctKV+kXo 5 | dREG47uvY/X+UGeruHL62knMjeSGLqt5Sk5nNk8VciDVZKSvQ5RiWVAQDgS0mxcR 6 | mQ0aGNA929Wnkv1zH1b3KyYuPDbNmb0/ggMtClXigXkPfupA/2AIs3MQxi0b6jmV 7 | zh2GhNi+dQ2my0E8ZPTRT4CMa2LRku0bUO3VIQIDAQABAoIBAQDJoXiDHFVgUZ0L 8 | XlUBYKnEaIyDxzey0hXep7Me6eaUgW0JugLw4VsEI9NLqyBKMCfjpTCHvj6huzmV 9 | 4utSMI0cMC76Rm5ylgSgmhYnm3+/ZN/QOY71+YqcCfUmuj+SqNgoLEjLhPbEqipH 10 | NKO4J88ysOUwgmrZppDgfKIOHw66Xlx0WtyFksozLve5pqxzs2gNZDmT5YdlGt+w 11 | X2zaUr7GholPGUVzhZSlpBpPkloSNYyGPX7O25bc63Ev92m3vnroJZYFiaUaKcFE 12 | M2RVVd3m8J47uoSwxl2ppnIgwodTWe20swc3d4cXK30U4USLsvWesthnfUc65QWa 13 | KfeanOrBAoGBAOvxGM9ufV1EFyV3D4kDB2UGp2ccv2t+rJ+urpl7JLCw1Aetpc3v 14 | Qg/QsbBfOhfI650zOZgzE5bG6B6wNYVLAC/UusrYgeLkyqKVbQgDTP+djD61jYjy 15 | IG5RP7EumN07Gvja59B4Kw5zN6TB3MKGz9Qv3a2MOvyg/3TRnssHOkQ5AoGBANwy 16 | V822s149G46tIbESw5ICcY8HorP1kD2syMeBQWKJcEgiSXL8Q3TIKdqaim8Q1Bqu 17 | fSYwISUVsFDaTyvRl25hLfkQ0t+qsul7EcjNFHf9L7dJx7z/dj8lVTQWGYse5iVQ 18 | aplx7fYQHgXdC7NjtpIrxUZkJ7bAl+0cpavdcCgpAoGAetCbO46mDyBcdBIPsiAz 19 | fzEBfrkGIyxjKxPAqv/gz2CcXgrT3eiHGLhnZgmLscnSa5e4iTM9JSUQuri6g1HR 20 | HRS8zs34fmTd3deuU5d0QzJ9SD81F24B16rPXqmExNP5bER2mpuSvgjXlBmdklye 21 | XjM0TxxJsCsWDnb3E3QFrnECgYBSpXqbNZXBK0JqnMTmh1psNQqWWpFQ5jxLScza 22 | RMNbzqYcDPJwfAp9jJtY92Q6J6DUmuVSLgJivu88iZPpqHMj9MmikBP160XXqF+W 23 | dJLYLml4a/LSFzg0nziJojnYI7LSEorQKRjdoFMEdGDt5eEin9cdgn39c/ASCQyN 24 | o0FzcQKBgHifRoEEyyHzepXiv2osPsOV9cEoWROLRaooE7boPISimJ1PCFGON0XT 25 | 20PmWJL3j3/f2Eqk47x4WpzLOp/OwUqqpaDpImtQX7GD7C+/PbAdbT1Q1Kc9kxc9 26 | a5FJg85oJqDPB1yEmYG5nWIlqB3LOX/IdOfYSOUFvRiomwJJoxys 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/exchange_declare.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Declare a new fanout exchange" do |connection| 8 | puts "AMQP connection is open: #{connection.server_properties.inspect}" 9 | 10 | channel = AMQ::Client::Channel.new(connection, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open!" 13 | 14 | exchange_name = "amqclient.adapters.em.exchange" 15 | 16 | 10.times do 17 | exchange = AMQ::Client::Exchange.new(connection, channel, exchange_name, :fanout) 18 | exchange.declare 19 | end 20 | 21 | exchange2 = AMQ::Client::Exchange.new(connection, channel, exchange_name + "-2", :fanout) 22 | exchange2.declare 23 | 24 | AMQ::Client::Exchange.new(connection, channel, exchange_name, :fanout).declare do |exchange, declare_ok| 25 | puts "Channel is aware of the following exchanges: #{channel.exchanges.map { |e| e.name }.join(', ')}" 26 | 27 | exchange.delete do 28 | puts "Exchange #{exchange.name} was successfully deleted" 29 | exchange2.delete do 30 | puts "Exchange #{exchange2.name} was successfully deleted" 31 | 32 | connection.disconnect do 33 | puts 34 | puts "AMQP connection is now properly closed" 35 | EM.stop 36 | end 37 | end 38 | end 39 | end 40 | 41 | show_stopper = Proc.new { 42 | connection.disconnect do 43 | puts 44 | puts "AMQP connection is now properly closed" 45 | EM.stop 46 | end 47 | } 48 | 49 | Signal.trap "INT", show_stopper 50 | Signal.trap "TERM", show_stopper 51 | 52 | EM.add_timer(2, show_stopper) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/basic_get.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "example_helper") 6 | 7 | amq_client_example "Set a queue up for message delivery" do |client| 8 | channel = AMQ::Client::Channel.new(client, 1) 9 | channel.open do 10 | puts "Channel #{channel.id} is now open!" 11 | end 12 | 13 | queue = AMQ::Client::Queue.new(client, channel) 14 | queue.declare(false, false, false, true) do 15 | puts "Server-named, auto-deletable Queue #{queue.name.inspect} is ready" 16 | end 17 | 18 | queue.bind("amq.fanout") do 19 | puts "Queue #{queue.name} is now bound to amq.fanout" 20 | end 21 | 22 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 23 | 30.times do |i| 24 | puts "Publishing message ##{i}" 25 | exchange.publish("Message ##{i}") 26 | end 27 | sleep 0.1 28 | 29 | 30.times do |i| 30 | queue.get(true) do |method, header, payload| 31 | puts "basic.get callback has fired" 32 | puts 33 | puts " => Payload is #{payload}" 34 | puts " => header is #{header.decode_payload.inspect}" 35 | puts " => delivery_tag is #{method.delivery_tag}" 36 | puts " => redelivered is #{method.redelivered}" 37 | puts " => exchange is #{method.exchange}" 38 | puts " => routing_key is #{method.routing_key}" 39 | puts " => message_count is #{method.message_count}" 40 | puts 41 | puts 42 | end 43 | end 44 | 45 | show_stopper = Proc.new { 46 | client.disconnect do 47 | puts 48 | puts "AMQP connection is now properly closed" 49 | EM.stop 50 | end 51 | } 52 | 53 | Signal.trap "INT", show_stopper 54 | Signal.trap "TERM", show_stopper 55 | 56 | EM.add_timer(1, show_stopper) 57 | end 58 | -------------------------------------------------------------------------------- /lib/amq/client/logging.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # You can use arbitrary logger which responds to #debug, #info, #error and #fatal methods, so for example the logger from standard library will work fine: 4 | # 5 | # AMQ::Client.logging = true 6 | # AMQ::Client.logger = MyLogger.new(STDERR) 7 | # 8 | # AMQ::Client.logger defaults to a new instance of Ruby stdlib logger. 9 | # 10 | # If you want to be able to log messages just from specified classes or even instances, just make the instance respond to #logging and set it to desired value. So for example Queue.class_eval { def logging; true; end } will turn on logging for the whole Queue class whereas queue = Queue.new; def queue.logging; false; end will disable logging for given Queue instance. 11 | 12 | module AMQ 13 | module Client 14 | module Logging 15 | def self.included(klass) 16 | unless klass.method_defined?(:client) 17 | raise NotImplementedError.new("Class #{klass} has to provide #client method!") 18 | end 19 | end 20 | 21 | def self.logging 22 | @logging ||= false 23 | end 24 | 25 | def self.logging=(boolean) 26 | @logging = boolean 27 | end 28 | 29 | REQUIRED_METHODS = [:debug, :info, :error, :fatal].freeze 30 | 31 | def debug(message) 32 | log(:debug, message) 33 | end 34 | 35 | def info(message) 36 | log(:info, message) 37 | end 38 | 39 | def error(message) 40 | log(:error, message) 41 | end 42 | 43 | def fatal(message) 44 | log(:fatal, message) 45 | end 46 | 47 | protected 48 | def log(method, message) 49 | if self.respond_to?(:logging) ? self.logging : AMQ::Client::Logging.logging 50 | self.client.logger.__send__(method, message) 51 | message 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.dirname(File.expand_path(__FILE__)) 5 | require File.join(__dir, "..", "..", "example_helper") 6 | 7 | require "amq/client/extensions/rabbitmq/confirm" 8 | 9 | amq_client_example "Publisher confirmations using RabbitMQ extension: routable message scenario" do |client| 10 | channel = AMQ::Client::Channel.new(client, 1) 11 | channel.open do 12 | puts "Channel #{channel.id} is now open" 13 | 14 | channel.confirm_select 15 | channel.on_error do 16 | puts "Oops, there is a channel-levle exceptions!" 17 | end 18 | 19 | 20 | channel.on_ack do |basic_ack| 21 | puts "Received basic_ack: multiple = #{basic_ack.multiple}, delivery_tag = #{basic_ack.delivery_tag}" 22 | end 23 | 24 | x = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 25 | 26 | q = AMQ::Client::Queue.new(client, channel, AMQ::Protocol::EMPTY_STRING) 27 | q.declare(false, false, true, true) do |_, declare_ok| 28 | puts "Defined a new server-named queue: #{q.name}" 29 | 30 | q.bind("amq.fanout").consume(false, true, true) { |consume_ok| 31 | puts "Received basic.consume-ok" 32 | }.on_delivery do |method, header, payload| 33 | puts "Received #{payload}" 34 | end 35 | end 36 | 37 | EM.add_timer(0.5) do 38 | 10.times { |i| x.publish("Message ##{i}", AMQ::Protocol::EMPTY_STRING, { :delivery_mode => 2 }, true) } 39 | end 40 | 41 | 42 | 43 | show_stopper = Proc.new { 44 | client.disconnect do 45 | puts 46 | puts "AMQP connection is now properly closed" 47 | EM.stop 48 | end 49 | } 50 | 51 | Signal.trap "INT", show_stopper 52 | Signal.trap "TERM", show_stopper 53 | 54 | EM.add_timer(3, show_stopper) 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/client/framing/io_frame_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: binary 2 | 3 | require "spec_helper" 4 | require "stringio" 5 | 6 | # We need to require AMQ-Protocol manually. 7 | # In the library this is required in the file 8 | # amq/client.rb, but this is a unit test and 9 | # we don't want to mess around with unecessary 10 | # dependencies. 11 | require "amq/protocol/client" 12 | require "amq/protocol/frame" 13 | 14 | # We have to use Kernel#load so extensions to the 15 | # Logging module from client.rb will be overridden. 16 | load "amq/client/framing/io/frame.rb" 17 | 18 | describe AMQ::Client::Framing::IO do 19 | subject do 20 | AMQ::Client::Framing::IO::Frame 21 | end 22 | 23 | # Created by: 24 | # frame = AMQ::Protocol::Queue::Declare.encode(1, "tasks", false, false, false, false, {}) 25 | # frame.encode 26 | # frame.payload 27 | before do 28 | data = ["\x01\x00\x00\x00\x00\x00\b"] 29 | data << "\x00\n\x00(\x01/\x00\x00" 30 | data << "\xCE" 31 | @io = StringIO.new(data.join) 32 | 33 | subject.stub(:decode_header).with(data.first).and_return([1, 0, data[1].bytesize]) 34 | end 35 | 36 | it "should be able to decode frame type" do 37 | subject.decode(@io).should be_kind_of(AMQ::Protocol::MethodFrame) 38 | end 39 | 40 | it "should be able to decode channel" do 41 | subject.decode(@io).channel.should eql(0) 42 | end 43 | 44 | it "should be able to decode payload" do 45 | subject.decode(@io).payload.should eql("\x00\n\x00(\x01/\x00\x00") 46 | end 47 | 48 | context "if the frame length is miscalculated" do 49 | it "should raise an error" 50 | end 51 | 52 | 53 | context "if frame doesn't end with FINAL_OCTET" do 54 | it "should raise an error" do 55 | data = @io.read[0..-2] + "too long" + "\xCE" 56 | io = StringIO.new(data) 57 | lambda { subject.decode(io) }.should raise_error(AMQ::Client::NoFinalOctetError) 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/unit/client_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "ostruct" 4 | require "spec_helper" 5 | require "amq/client" 6 | 7 | describe AMQ::Client do 8 | if RUBY_PLATFORM =~ /java/ 9 | ADAPTERS = { 10 | :event_machine => "Async::EventMachineClient" 11 | } 12 | else 13 | ADAPTERS = { 14 | :event_machine => "Async::EventMachineClient", 15 | :coolio => "Async::CoolioClient" 16 | } 17 | end 18 | 19 | it "should have VERSION" do 20 | AMQ::Client::const_defined?(:VERSION).should be_true 21 | end 22 | 23 | ADAPTERS.each do |adapter_name, adapter_const_name| 24 | describe ".adapters" do 25 | before(:all) do 26 | @meta = AMQ::Client.adapters[adapter_name] 27 | end 28 | 29 | it "should provide info about path to the adapter" do 30 | require @meta[:path] 31 | end 32 | 33 | it "should provide info about const_name" do 34 | @meta[:const_name].should eql(adapter_const_name) 35 | end 36 | end 37 | end 38 | 39 | describe ".connect(settings = nil, &block)" do 40 | include EventedSpec::SpecHelper 41 | default_timeout 1 42 | 43 | context "with specified adapter" do 44 | it "should connect using event_machine adapter" do 45 | em do 46 | AMQ::Client.connect(:adapter => :event_machine) do |client| 47 | client.class.name.should eql("AMQ::Client::Async::EventMachineClient") 48 | done 49 | end 50 | end 51 | end # it 52 | 53 | it "should connect using coolio adapter", :nojruby => true do 54 | coolio do 55 | AMQ::Client.connect(:adapter => :coolio) do |client| 56 | client.class.name.should eql("AMQ::Client::Async::CoolioClient") 57 | done 58 | end 59 | end 60 | end # it 61 | end # context "with specified adapter" 62 | end # describe .connect 63 | end # describe AMQ::Client 64 | -------------------------------------------------------------------------------- /lib/amq/client/async/entity.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/openable" 4 | require "amq/client/async/callbacks" 5 | 6 | module AMQ 7 | module Client 8 | module Async 9 | module RegisterEntityMixin 10 | # @example Registering Channel implementation 11 | # Adapter.register_entity(:channel, Channel) 12 | # # ... so then I can do: 13 | # channel = client.channel(1) 14 | # # instead of: 15 | # channel = Channel.new(client, 1) 16 | def register_entity(name, klass) 17 | define_method(name) do |*args, &block| 18 | klass.new(self, *args, &block) 19 | end # define_method 20 | end # register_entity 21 | end # RegisterEntityMixin 22 | 23 | module ProtocolMethodHandlers 24 | def handle(klass, &block) 25 | AMQ::Client::HandlersRegistry.register(klass, &block) 26 | end 27 | 28 | def handlers 29 | AMQ::Client::HandlersRegistry.handlers 30 | end 31 | end # ProtocolMethodHandlers 32 | 33 | 34 | # AMQ entities, as implemented by AMQ::Client, have callbacks and can run them 35 | # when necessary. 36 | # 37 | # @note Exchanges and queues implementation is based on this class. 38 | # 39 | # @abstract 40 | module Entity 41 | 42 | # 43 | # Behaviors 44 | # 45 | 46 | include Openable 47 | include Async::Callbacks 48 | 49 | # 50 | # API 51 | # 52 | 53 | # @return [Array<#call>] 54 | attr_reader :callbacks 55 | 56 | 57 | def initialize(connection) 58 | @connection = connection 59 | # Be careful with default values for #ruby hashes: h = Hash.new(Array.new); h[:key] ||= 1 60 | # won't assign anything to :key. MK. 61 | @callbacks = Hash.new 62 | end # initialize 63 | end # Entity 64 | end # Async 65 | end # Client 66 | end # AMQ 67 | -------------------------------------------------------------------------------- /spec/client/framing/string_frame_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "spec_helper" 4 | 5 | # We need to require AMQ-Protocol manually. 6 | # In the library this is required in the file 7 | # amq/client.rb, but this is a unit test and 8 | # we don't want to mess around with unecessary 9 | # dependencies. 10 | require "amq/protocol/client" 11 | require "amq/protocol/frame" 12 | 13 | # We have to use Kernel#load so extensions to the 14 | # Logging module from client.rb will be overridden. 15 | load "amq/client/framing/string/frame.rb" 16 | 17 | describe AMQ::Client::Framing::String do 18 | subject do 19 | AMQ::Client::Framing::String::Frame 20 | end 21 | 22 | # Created by: 23 | # frame = AMQ::Protocol::Queue::Declare.encode(1, "tasks", false, false, false, false, {}) 24 | # frame.encode 25 | # frame.payload 26 | before do 27 | data = ["\x01\x00\x00\x00\x00\x00\b"] 28 | data << "\x00\n\x00(\x01/\x00\x00" 29 | data << "\xCE" 30 | @string = data.join 31 | 32 | subject.stub(:decode_header).with(data.first).and_return([1, 0, data[1].bytesize]) 33 | end 34 | 35 | it "should be able to decode frame type" do 36 | subject.decode(@string).should be_kind_of(AMQ::Protocol::MethodFrame) 37 | end 38 | 39 | it "should be able to decode channel" do 40 | subject.decode(@string).channel.should eql(0) 41 | end 42 | 43 | it "should be able to decode payload" do 44 | subject.decode(@string).payload.should eql("\x00\n\x00(\x01/\x00\x00") 45 | end 46 | 47 | it "should raise an error if the frame length is miscalculated" do 48 | data = @string[0..-2] + "too long" + "\xCE" 49 | string = String.new(data) 50 | lambda { subject.decode(string) }.should raise_error(AMQ::Client::BadLengthError) 51 | end 52 | 53 | it "should raise an error if the frame doesn't end with FINAL_OCTET" do 54 | string = @string[0..-2] + "x" 55 | lambda { subject.decode(string) }.should raise_error(AMQ::Client::NoFinalOctetError) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /irb.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | # This file is supposed to make inspecting AMQ client easier. 5 | 6 | # How does it work: 7 | # 1) This file is executed. 8 | # 2) We load irb, redefine where IRB looks for .irbrc and start IRB. 9 | # 3) IRB loads .irbrc, which we redefined, so it loads this file again. 10 | # However now the second branch of "if __FILE__ == $0" gets executed, 11 | # so it runs our custom code which loads the original .irbrc and then 12 | # it redefines some IRB settings. In this case it add IRB hook which 13 | # is executed after IRB is started. 14 | 15 | # Although it looks unnecessarily complicated, I can't see any easier 16 | # solution to this problem in case that you need to patch original settings. 17 | # Obviously in case you don't have the need, you'll be happy with simple: 18 | 19 | # require "irb" 20 | # 21 | # require_relative "lib/amq/protocol/client.rb" 22 | # include AMQ::Protocol 23 | # 24 | # IRB.start(__FILE__) 25 | 26 | require "irb" 27 | require "bundler" 28 | 29 | Bundler.setup 30 | Bundler.require(:default) 31 | 32 | $LOAD_PATH.unshift(File.expand_path("../lib", __FILE__)) 33 | 34 | if __FILE__ == $0 35 | puts "~ Using #{__FILE__} as an executable ..." 36 | 37 | def IRB.rc_file_generators 38 | yield Proc.new { |_| __FILE__ } 39 | end 40 | 41 | IRB.start(__FILE__) 42 | else 43 | begin 44 | irbrc = File.join(ENV["HOME"], ".irbrc") 45 | puts "~ Using #{__FILE__} as a custom .irbrc .." 46 | 47 | require "amq/client.rb" 48 | include AMQ::Client 49 | 50 | require "amq/protocol/client" 51 | include AMQ 52 | 53 | require "stringio" 54 | 55 | def fd(data) 56 | Frame.decode(StringIO.new(data)) 57 | end 58 | 59 | puts "~ Loading original #{irbrc} ..." 60 | load irbrc 61 | 62 | puts "Loading finished." 63 | rescue Exception => exception # it just discards all the exceptions! 64 | abort exception.message + "\n - " + exception.backtrace.join("\n - ") 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/regression/bad_frame_slicing_in_adapters_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: binary 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | require 'integration/eventmachine/spec_helper' 6 | 7 | describe "AMQ::Client::CoolioClient", :nojruby => true do 8 | include EventedSpec::SpecHelper 9 | default_timeout 1 10 | 11 | let(:message) { "Message with xCE \xCE" } 12 | 13 | it "should receive the message with xCE byte in it without errors" do 14 | coolio_amqp_connect do |client| 15 | channel = AMQ::Client::Channel.new(client, 1) 16 | channel.open do end 17 | queue = AMQ::Client::Queue.new(client, channel) 18 | queue.declare(false, false, false, true) 19 | queue.bind("amq.fanout") 20 | queue.consume(true) do |_, consumer_tag| 21 | queue.on_delivery do |method, header, payload| 22 | @received_message = payload 23 | done 24 | end 25 | 26 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 27 | exchange.publish(message) 28 | end 29 | end 30 | 31 | @received_message.should == message 32 | end 33 | end 34 | 35 | describe AMQ::Client::EventMachineClient do 36 | include EventedSpec::SpecHelper 37 | default_timeout 1 38 | 39 | let(:message) { "Message with xCE \xCE" } 40 | 41 | it "should receive the message with xCE byte in it without errors" do 42 | em_amqp_connect do |client| 43 | channel = AMQ::Client::Channel.new(client, 1) 44 | channel.open do end 45 | queue = AMQ::Client::Queue.new(client, channel) 46 | queue.declare(false, false, false, true) 47 | queue.bind("amq.fanout") 48 | queue.consume(true) do |_, consumer_tag| 49 | queue.on_delivery do |method, header, payload| 50 | @received_message = payload 51 | done 52 | end 53 | 54 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 55 | exchange.publish(message) 56 | end 57 | end 58 | 59 | @received_message.should == message 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/unit/client/mixins/status_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "spec_helper" 4 | require "amq/client/openable" 5 | 6 | describe AMQ::Client::Openable do 7 | subject do 8 | Class.new { include AMQ::Client::Openable }.new 9 | end 10 | 11 | describe "#status=" do 12 | context "if it is in the permitted values" do 13 | it "should be able to store status" do 14 | lambda { subject.status = :opened }.should_not raise_error 15 | end 16 | end 17 | 18 | context "when given value isn't in the permitted values" do 19 | it "should raise ImproperStatusError" do 20 | lambda { subject.status = :sleepy }.should raise_error(AMQ::Client::Openable::ImproperStatusError) 21 | end 22 | end 23 | end 24 | 25 | describe "#opened?" do 26 | it "should be true if the status is :opened" do 27 | subject.status = :opened 28 | subject.should be_opened 29 | end 30 | 31 | it "should be false if the status isn't :opened" do 32 | subject.status = :opening 33 | subject.should_not be_opened 34 | end 35 | end 36 | 37 | describe "#closed?" do 38 | it "should be true if the status is :closed" do 39 | subject.status = :closed 40 | subject.should be_closed 41 | end 42 | 43 | it "should be false if the status isn't :closed" do 44 | subject.status = :closing 45 | subject.should_not be_closed 46 | end 47 | end 48 | 49 | describe "#opening?" do 50 | it "should be true if the status is :opening" do 51 | subject.status = :opening 52 | subject.should be_opening 53 | end 54 | 55 | it "should be false if the status isn't :opening" do 56 | subject.status = :opened 57 | subject.should_not be_opening 58 | end 59 | end 60 | 61 | describe "#closing?" do 62 | it "should be true if the status is :closing" do 63 | subject.status = :closing 64 | subject.should be_closing 65 | end 66 | 67 | it "should be false if the status isn't :closing" do 68 | subject.status = :opening 69 | subject.should_not be_closing 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/amq/client/async/callbacks.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | module Async 6 | module Callbacks 7 | 8 | def redefine_callback(event, callable = nil, &block) 9 | f = (callable || block) 10 | # yes, re-assign! 11 | @callbacks[event] = [f] 12 | 13 | self 14 | end 15 | 16 | def define_callback(event, callable = nil, &block) 17 | f = (callable || block) 18 | 19 | @callbacks[event] ||= [] 20 | @callbacks[event] << f if f 21 | 22 | self 23 | end # define_callback(event, &block) 24 | alias append_callback define_callback 25 | 26 | def prepend_callback(event, &block) 27 | @callbacks[event] ||= [] 28 | @callbacks[event].unshift(block) 29 | 30 | self 31 | end # prepend_callback(event, &block) 32 | 33 | def clear_callbacks(event) 34 | @callbacks[event].clear if @callbacks[event] 35 | end # clear_callbacks(event) 36 | 37 | 38 | def exec_callback(name, *args, &block) 39 | list = Array(@callbacks[name]) 40 | if list.any? 41 | list.each { |c| c.call(*args, &block) } 42 | end 43 | end 44 | 45 | def exec_callback_once(name, *args, &block) 46 | list = (@callbacks.delete(name) || Array.new) 47 | if list.any? 48 | list.each { |c| c.call(*args, &block) } 49 | end 50 | end 51 | 52 | def exec_callback_yielding_self(name, *args, &block) 53 | list = Array(@callbacks[name]) 54 | if list.any? 55 | list.each { |c| c.call(self, *args, &block) } 56 | end 57 | end 58 | 59 | def exec_callback_once_yielding_self(name, *args, &block) 60 | list = (@callbacks.delete(name) || Array.new) 61 | 62 | if list.any? 63 | list.each { |c| c.call(self, *args, &block) } 64 | end 65 | end 66 | 67 | def has_callback?(name) 68 | @callbacks[name] && !@callbacks[name].empty? 69 | end # has_callback? 70 | end # Callbacks 71 | end # Async 72 | end # Client 73 | end # AMQ 74 | -------------------------------------------------------------------------------- /spec/client/protocol/get_response_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "spec_helper" 4 | 5 | require "amq/protocol/client" 6 | require "amq/protocol/get_response" 7 | # require "amq/protocol/frame" 8 | # 9 | # # We have to use Kernel#load so extensions to the 10 | # # Logging module from client.rb will be overridden. 11 | # load "amq/client/framing/string/frame.rb" 12 | 13 | describe AMQ::Protocol::GetResponse do 14 | describe "when method is GetOk" do 15 | before { @method = AMQ::Protocol::Basic::GetOk.new("dtag", true, "tasks", "foo", 1) } 16 | subject { AMQ::Protocol::GetResponse.new(@method) } 17 | 18 | it "should NOT be #empty?" do 19 | should_not be_empty 20 | end 21 | 22 | it "should have #delivery_tag" do 23 | subject.delivery_tag.should eql("dtag") 24 | end 25 | 26 | it "should have #redelivered" do 27 | subject.redelivered.should be_true 28 | end 29 | 30 | it "should have #exchange" do 31 | subject.exchange.should eql("tasks") 32 | end 33 | 34 | it "should have #routing_key" do 35 | subject.routing_key.should eql("foo") 36 | end 37 | 38 | it "should have #message_count" do 39 | subject.message_count.should eql(1) 40 | end 41 | 42 | it "should NOT have #cluster_id" do 43 | subject.cluster_id.should be_nil 44 | end 45 | end 46 | 47 | describe "when method is GetEmpty" do 48 | before { @method = AMQ::Protocol::Basic::GetEmpty.new("ID") } 49 | subject { AMQ::Protocol::GetResponse.new(@method) } 50 | 51 | it "should be #empty?" do 52 | should be_empty 53 | end 54 | 55 | it "should NOT have #delivery_tag" do 56 | subject.delivery_tag.should be_nil 57 | end 58 | 59 | it "should NOT have #redelivered" do 60 | subject.redelivered.should be_nil 61 | end 62 | 63 | it "should NOT have #exchange" do 64 | subject.exchange.should be_nil 65 | end 66 | 67 | it "should NOT have #routing_key" do 68 | subject.routing_key.should be_nil 69 | end 70 | 71 | it "should NOT have #message_count" do 72 | subject.message_count.should be_nil 73 | end 74 | 75 | it "should have #cluster_id" do 76 | subject.cluster_id.should eql("ID") 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/amq/client/framing/string/frame.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # This will be probably used by all the async libraries like EventMachine. 4 | # It expects the whole frame as one string, so if library of your choice 5 | # gives you input chunk-by-chunk, you'll need to have something like this: 6 | # 7 | # class Client 8 | # include EventMachine::Deferrable 9 | # 10 | # def receive_data(chunk) 11 | # if @payload.nil? 12 | # self.decode_from_string(chunk[0..6]) 13 | # @payload = "" 14 | # elsif @payload && chunk[-1] != FINAL_OCTET 15 | # @payload += chunk 16 | # @size += chunk.bytesize 17 | # else 18 | # check_size(@size, @payload.bytesize) 19 | # Frame.decode(@payload) # we need the whole payload 20 | # @size, @payload = nil 21 | # end 22 | # end 23 | # 24 | # NOTE: the client should also implement waiting for another frames, in case that some header/body frames are expected. 25 | # end 26 | 27 | require "amq/client/exceptions" 28 | 29 | module AMQ 30 | module Client 31 | module Framing 32 | module String 33 | class Frame < AMQ::Protocol::Frame 34 | ENCODINGS_SUPPORTED = defined? Encoding 35 | HEADER_SLICE = (0..6).freeze 36 | DATA_SLICE = (7..-1).freeze 37 | PAYLOAD_SLICE = (0..-2).freeze 38 | 39 | def self.decode(string) 40 | header = string[HEADER_SLICE] 41 | type, channel, size = self.decode_header(header) 42 | data = string[DATA_SLICE] 43 | payload = data[PAYLOAD_SLICE] 44 | frame_end = data[-1, 1] 45 | 46 | frame_end.force_encoding(AMQ::Protocol::Frame::FINAL_OCTET.encoding) if ENCODINGS_SUPPORTED 47 | 48 | # 1) the size is miscalculated 49 | if payload.bytesize != size 50 | raise BadLengthError.new(size, payload.bytesize) 51 | end 52 | 53 | # 2) the size is OK, but the string doesn't end with FINAL_OCTET 54 | raise NoFinalOctetError.new if frame_end != AMQ::Protocol::Frame::FINAL_OCTET 55 | 56 | self.new(type, payload, channel) 57 | end # self.from 58 | end # Frame 59 | end # String 60 | end # Framing 61 | end # Client 62 | end # AMQ 63 | -------------------------------------------------------------------------------- /lib/amq/client/async/auth_mechanism_adapter.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ::Client::Async 4 | # Provides a flexible method for encoding AMQP credentials. PLAIN and 5 | # EXTERNAL are provided by this gem. In order to implement a new 6 | # authentication mechanism, create a subclass like so: 7 | # 8 | # class MyAuthMechanism < AMQ::Client::Async::AuthMechanismAdapter 9 | # auth_mechanism "X-MYAUTH" 10 | # 11 | # def encode_credentials(username, password) 12 | # # ... 13 | # end 14 | # end 15 | class AuthMechanismAdapter 16 | 17 | # Find and instantiate an AuthMechanismAdapter. 18 | # 19 | # @param [Adapter] adapter The Adapter for which to encode credentials. 20 | # @return [AuthMechanismAdapter] An AuthMechanismAdapter that can encode 21 | # credentials for the Adapter. 22 | # @raise [NotImplementedError] The Adapter's mechanism does not 23 | # correspond to any known AuthMechanismAdapter. 24 | def self.for_adapter(adapter) 25 | registry[adapter.mechanism].new adapter 26 | end 27 | 28 | protected 29 | 30 | # Used by subclasses to declare the mechanisms that an 31 | # AuthMechanismAdapter understands. 32 | # 33 | # @param [Array] mechanisms One or more mechanisms that can be 34 | # handled by the subclass. 35 | def self.auth_mechanism(*mechanisms) 36 | mechanisms.each {|mechanism| registry[mechanism] = self} 37 | end 38 | 39 | private 40 | 41 | # Accesses the registry of AuthMechanismAdapter subclasses. Keys in 42 | # this hash are the names of the authentication mechanisms; values are 43 | # the classes that handle them. Referencing an unknown mechanism from 44 | # this Hash will raise NotImplementedError. 45 | def self.registry 46 | @@registry ||= Hash.new {raise NotImplementedError} 47 | end 48 | 49 | public 50 | 51 | # The Adapter that this AuthMechanismAdapter operates on behalf of. 52 | attr_reader :adapter 53 | 54 | private 55 | 56 | # Create a new AuthMechanismAdapter. Users of this class should instead 57 | # retrieve an instance through for_adapter. 58 | def initialize(adapter) 59 | @adapter = adapter 60 | end 61 | end 62 | end 63 | 64 | # pre-require builtin auth mechanisms 65 | Dir[File.join(File.dirname(__FILE__), 66 | File.basename(__FILE__, '.rb'), 67 | '*')].each do |f| 68 | require f 69 | end 70 | -------------------------------------------------------------------------------- /spec/integration/coolio/basic_get_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Basic.Get", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1 9 | 10 | context "when set two messages beforehand" do 11 | let(:messages) { ["message 1", "message 2"] } 12 | 13 | it "synchronously fetches all the messages" do 14 | @received_messages = [] 15 | coolio_amqp_connect do |client| 16 | channel = AMQ::Client::Channel.new(client, 1) 17 | 18 | channel.open do 19 | queue = AMQ::Client::Queue.new(client, channel) 20 | queue.declare(false, false, false, true) do 21 | queue.bind("amq.fanout") 22 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 23 | 24 | messages.each do |message| 25 | exchange.publish(message) do 26 | puts "Published a message: #{message}" 27 | end 28 | end 29 | 30 | queue.get(true) do |method, header, payload| 31 | puts "Got #{payload}" 32 | @received_messages << payload 33 | end 34 | queue.get(true) do |method, header, payload| 35 | puts "Got #{payload}" 36 | @received_messages << payload 37 | end 38 | 39 | delayed(0.5) { 40 | # need to delete the queue manually because #get doesn't count as consumption, 41 | # hence, no auto-delete 42 | queue.delete 43 | } 44 | done(0.75) { 45 | @received_messages.should =~ messages 46 | } 47 | end 48 | end 49 | end 50 | end 51 | end 52 | 53 | 54 | context "when sent no messages beforehand" do 55 | it "should receive nils" do 56 | coolio_amqp_connect do |client| 57 | channel = AMQ::Client::Channel.new(client, 1) 58 | channel.open do 59 | queue = AMQ::Client::Queue.new(client, channel) 60 | queue.declare(false, false, false, true) 61 | queue.bind("amq.fanout") 62 | 63 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 64 | 65 | queue.get(true) do |method, header, payload| 66 | header.should be_nil 67 | payload.should be_nil 68 | 69 | queue.delete 70 | done(0.5) 71 | end # get 72 | end 73 | end # em_amqp_connect 74 | end # it 75 | 76 | end # context 77 | end -------------------------------------------------------------------------------- /spec/integration/eventmachine/basic_get_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Basic.Get" do 7 | include EventedSpec::SpecHelper 8 | default_timeout 1 9 | 10 | context "when set two messages beforehand" do 11 | let(:messages) { ["message 1", "message 2"] } 12 | 13 | it "synchronously fetches all the messages" do 14 | @received_messages = [] 15 | em_amqp_connect do |client| 16 | channel = AMQ::Client::Channel.new(client, 1) 17 | 18 | channel.open do 19 | queue = AMQ::Client::Queue.new(client, channel) 20 | queue.declare(false, false, false, true) do 21 | queue.bind("amq.fanout") 22 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 23 | 24 | messages.each do |message| 25 | exchange.publish(message) do 26 | puts "Published a message: #{message}" 27 | end 28 | end 29 | 30 | queue.get(true) do |method, header, payload| 31 | puts "Got #{payload}" 32 | @received_messages << payload 33 | end 34 | queue.get(true) do |method, header, payload| 35 | puts "Got #{payload}" 36 | @received_messages << payload 37 | end 38 | 39 | delayed(0.5) { 40 | # need to delete the queue manually because #get doesn't count as consumption, 41 | # hence, no auto-delete 42 | queue.delete 43 | } 44 | 45 | done(0.75) { 46 | @received_messages.should =~ messages 47 | } 48 | end 49 | end 50 | end 51 | end 52 | end 53 | 54 | 55 | context "when sent no messages beforehand" do 56 | it "should receive nils" do 57 | em_amqp_connect do |client| 58 | channel = AMQ::Client::Channel.new(client, 1) 59 | channel.open do 60 | queue = AMQ::Client::Queue.new(client, channel) 61 | queue.declare(false, false, false, true) 62 | queue.bind("amq.fanout") 63 | 64 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 65 | 66 | queue.get(true) do |method, header, payload| 67 | header.should be_nil 68 | payload.should be_nil 69 | 70 | queue.delete 71 | done(0.5) 72 | end # get 73 | end 74 | end # em_amqp_connect 75 | end # it 76 | 77 | end # context 78 | end # describe 79 | -------------------------------------------------------------------------------- /spec/benchmarks/adapters.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | Bundler.setup 4 | Bundler.require(:default) 5 | $LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__)) 6 | 7 | require "amq/client/adapters/coolio" 8 | require "amq/client/adapters/event_machine" 9 | require "amq/client/queue" 10 | require "amq/client/exchange" 11 | 12 | TOTAL_MESSAGES = 10000 13 | # Short messages 14 | # Cool.io 15 | coolio_start = Time.now 16 | AMQ::Client::CoolioClient.connect(:port => 5672, :vhost => "/amq_client_testbed") do |client| 17 | received_messages = 0 18 | channel = AMQ::Client::Channel.new(client, 1) 19 | channel.open { } 20 | queue = AMQ::Client::Queue.new(client, channel) 21 | queue.declare(false, false, false, true) { } 22 | 23 | queue.bind("amq.fanout") { } 24 | 25 | queue.consume(true) do |_, consumer_tag| 26 | 27 | queue.on_delivery do |_, header, payload, consumer_tag, delivery_tag, redelivered, exchange, routing_key| 28 | received_messages += 1 29 | if received_messages == TOTAL_MESSAGES 30 | client.disconnect do 31 | Coolio::Loop.default.stop 32 | end 33 | end 34 | end 35 | 36 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 37 | TOTAL_MESSAGES.times do |i| 38 | exchange.publish("Message ##{i}") 39 | end 40 | end 41 | end 42 | cool.io.run 43 | coolio_finish = Time.now 44 | 45 | # Eventmachine 46 | em_start = Time.now 47 | EM.run do 48 | AMQ::Client::EventMachineClient.connect(:port => 5672, :vhost => "/amq_client_testbed") do |client| 49 | received_messages = 0 50 | channel = AMQ::Client::Channel.new(client, 1) 51 | channel.open { } 52 | queue = AMQ::Client::Queue.new(client, channel) 53 | queue.declare(false, false, false, true) { } 54 | 55 | queue.bind("amq.fanout") { } 56 | 57 | queue.consume(true) do |_, consumer_tag| 58 | 59 | queue.on_delivery do |_, header, payload, consumer_tag, delivery_tag, redelivered, exchange, routing_key| 60 | received_messages += 1 61 | if received_messages == TOTAL_MESSAGES 62 | client.disconnect do 63 | EM.stop 64 | end 65 | end 66 | end 67 | 68 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 69 | TOTAL_MESSAGES.times do |i| 70 | exchange.publish("Message ##{i}") 71 | end 72 | end 73 | end 74 | end 75 | em_finish = Time.now 76 | 77 | puts "Results for #{TOTAL_MESSAGES} messages:" 78 | puts "\tcool.io adapter: #{sprintf("%.3f", coolio_finish - coolio_start)}s" 79 | puts "\teventmachine adapter: #{sprintf("%.3f", em_finish - em_start)}s" -------------------------------------------------------------------------------- /spec/integration/eventmachine/consumer_cancellation_notification_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | require "amq/client/extensions/rabbitmq" 7 | 8 | describe AMQ::Client::EventMachineClient, "basic.cancel notification" do 9 | include EventedSpec::SpecHelper 10 | default_timeout 4 11 | 12 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 13 | 14 | it "works for default consumer" do 15 | @received_basic_cancel_ok = false 16 | em_amqp_connect do |client| 17 | channel = AMQ::Client::Channel.new(client, 1) 18 | channel.open do 19 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 20 | queue.bind("amq.fanout") 21 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 22 | 23 | queue.consume(true) do |amq_method| 24 | messages.each do |message| 25 | exchange.publish(message) 26 | end 27 | end 28 | 29 | queue.default_consumer.on_cancel do |basic_cancel| 30 | @received_basic_cancel_ok = true 31 | end 32 | 33 | delayed(1.0) { queue.delete } 34 | 35 | done(2.5) { 36 | @received_basic_cancel_ok.should be_true 37 | } 38 | end 39 | end 40 | end # it 41 | 42 | it "works for other consumers" do 43 | @cancellation_notifications = [] 44 | 45 | em_amqp_connect do |client| 46 | channel = AMQ::Client::Channel.new(client, 1) 47 | channel.open do 48 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 49 | queue.bind("amq.fanout") 50 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 51 | 52 | consumer1 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{Time.now}-#{rand}") 53 | consumer2 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{Time.now}-#{rand}") 54 | consumer1.consume do |_| 55 | messages.each do |message| 56 | exchange.publish(message) 57 | end 58 | end 59 | consumer2.consume 60 | 61 | consumer1.on_cancel do |basic_cancel| 62 | @cancellation_notifications << 1 63 | end 64 | consumer2.on_cancel do |basic_cancel| 65 | @cancellation_notifications << 2 66 | end 67 | 68 | delayed(1.0) { queue.delete } 69 | 70 | done(2.5) { 71 | @cancellation_notifications.sort.should == [1, 2] 72 | } 73 | end 74 | end 75 | end # it 76 | end # describe AMQ::Client::EventMachineClient, "Basic.Consume" 77 | -------------------------------------------------------------------------------- /lib/amq/client.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/client/version" 4 | require "amq/client/exceptions" 5 | require "amq/client/handlers_registry" 6 | require "amq/client/adapter" 7 | require "amq/client/channel" 8 | require "amq/client/exchange" 9 | require "amq/client/queue" 10 | 11 | begin 12 | require "amq/protocol/client" 13 | rescue LoadError => exception 14 | if exception.message.match("amq/protocol") 15 | raise LoadError.new("amq-client could not load amq-protocol.") 16 | else 17 | raise exception 18 | end 19 | end 20 | 21 | module AMQ 22 | module Client 23 | # List available adapters as a hash of { :adapter_name => metadata }, 24 | # where metadata are hash with :path and :const_name keys. 25 | # 26 | # @return [Hash] 27 | # @api plugin 28 | def self.adapters 29 | @adapters ||= (self.async_adapters) 30 | end 31 | 32 | # List available asynchronous adapters. 33 | # 34 | # @return [Hash] 35 | # @api plugin 36 | # @see AMQ::Client.adapters 37 | def self.async_adapters 38 | @async_adapters ||= { 39 | :eventmachine => { 40 | :path => "amq/client/async/adapters/eventmachine", 41 | :const_name => "Async::EventMachineClient" 42 | }, 43 | :event_machine => { 44 | :path => "amq/client/async/adapters/eventmachine", 45 | :const_name => "Async::EventMachineClient" 46 | }, 47 | :coolio => { 48 | :path => "amq/client/async/adapters/coolio", 49 | :const_name => "Async::CoolioClient" 50 | } 51 | } 52 | end 53 | 54 | 55 | # Establishes connection to AMQ broker using given adapter 56 | # (defaults to the socket adapter) and returns it. The new 57 | # connection object is yielded to the block if it is given. 58 | # 59 | # @example 60 | # AMQ::Client.connect(adapter: "socket") do |client| 61 | # # Use the client. 62 | # end 63 | # @param [Hash] Connection parameters, including :adapter to use. 64 | # @api public 65 | def self.connect(settings = nil, &block) 66 | adapter = (settings && settings.delete(:adapter)) 67 | adapter = load_adapter(adapter) 68 | adapter.connect(settings, &block) 69 | end 70 | 71 | # Loads adapter given its name as a Symbol. 72 | # 73 | # @raise [InvalidAdapterNameError] When loading attempt failed (LoadError was raised). 74 | def self.load_adapter(adapter) 75 | meta = self.adapters[adapter.to_sym] 76 | 77 | require meta[:path] 78 | eval(meta[:const_name]) 79 | rescue LoadError 80 | raise InvalidAdapterNameError.new(adapter) 81 | end 82 | end # Client 83 | end # AMQ 84 | -------------------------------------------------------------------------------- /examples/tls_certificates/testca/openssl.cnf: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = testca 3 | 4 | [ testca ] 5 | dir = . 6 | certificate = $dir/cacert.pem 7 | database = $dir/index.txt 8 | new_certs_dir = $dir/certs 9 | private_key = $dir/private/cakey.pem 10 | serial = $dir/serial 11 | 12 | default_crl_days = 7 13 | default_days = 365 14 | default_md = sha1 15 | 16 | policy = testca_policy 17 | x509_extensions = certificate_extensions 18 | 19 | [ testca_policy ] 20 | commonName = supplied 21 | stateOrProvinceName = optional 22 | countryName = optional 23 | emailAddress = optional 24 | organizationName = optional 25 | organizationalUnitName = optional 26 | 27 | [ certificate_extensions ] 28 | basicConstraints = CA:false 29 | 30 | [ req ] 31 | default_bits = 2048 32 | default_keyfile = ./private/cakey.pem 33 | default_md = sha1 34 | prompt = yes 35 | distinguished_name = root_ca_distinguished_name 36 | x509_extensions = root_ca_extensions 37 | 38 | [ root_ca_distinguished_name ] 39 | commonName = hostname 40 | 41 | [ root_ca_extensions ] 42 | basicConstraints = CA:true 43 | keyUsage = keyCertSign, cRLSign 44 | 45 | [ client_ca_extensions ] 46 | basicConstraints = CA:false 47 | keyUsage = digitalSignature 48 | extendedKeyUsage = 1.3.6.1.5.5.7.3.2 49 | 50 | [ server_ca_extensions ] 51 | basicConstraints = CA:false 52 | keyUsage = keyEncipherment 53 | extendedKeyUsage = 1.3.6.1.5.5.7.3.1 -------------------------------------------------------------------------------- /examples/eventmachine_adapter/error_handling/connection_loss_handler_with_manual_recovery.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__))) 5 | require File.join(__dir, "..", "example_helper") 6 | 7 | 8 | EM.run do 9 | AMQ::Client::EventMachineClient.connect(:port => 5672, 10 | :vhost => "amq_client_testbed", 11 | :user => "amq_client_gem", 12 | :password => "amq_client_gem_password", 13 | :timeout => 0.3, 14 | :on_tcp_connection_failure => Proc.new { |settings| puts "Failed to connect, this was NOT expected"; EM.stop }) do |connection| 15 | 16 | if connection.auto_recovering? 17 | puts "Connection is auto-recovering..." 18 | end 19 | 20 | ch1 = AMQ::Client::Channel.new(connection, 1) 21 | ch1.on_error do |ch, channel_close| 22 | raise channel_close.reply_text 23 | end 24 | ch1.open do 25 | puts "Channel 1 open now" 26 | end 27 | if ch1.auto_recovering? 28 | puts "Channel 1 is auto-recovering" 29 | end 30 | ch1.on_recovery do |c| 31 | puts "Channel #{c.id} has recovered" 32 | end 33 | 34 | ch2 = AMQ::Client::Channel.new(connection, 2) 35 | ch2.on_error do |ch, channel_close| 36 | raise channel_close.reply_text 37 | end 38 | ch2.open do 39 | puts "Channel 2 open now" 40 | end 41 | if ch2.auto_recovering? 42 | puts "Channel 2 is auto-recovering" 43 | end 44 | 45 | 46 | queues = Array.new(4) do 47 | q = AMQ::Client::Queue.new(connection, ch1, AMQ::Protocol::EMPTY_STRING) 48 | q.declare(false, false, false, true) do 49 | q.consume { puts "Added a consumer on #{q.name}"; q.on_delivery { |*args| puts(args.inspect) } } 50 | end 51 | 52 | q.on_recovery { |_| puts "Queue #{q.name} has recovered" } 53 | 54 | q 55 | end 56 | 57 | x = AMQ::Client::Exchange.new(connection, ch1, "amqclient.examples.exchanges.fanout", :fanout) 58 | x2 = AMQ::Client::Exchange.new(connection, ch1, "amq.fanout", :fanout) 59 | x.declare(false, false, true) 60 | x.after_connection_interruption { |x| puts "Exchange #{x.name} has reacted to connection interruption" } 61 | x.after_recovery { |x| puts "Exchange #{x.name} has recovered" } 62 | queues.each { |q| q.bind(x) } 63 | 64 | 65 | connection.on_tcp_connection_loss do |conn, settings| 66 | puts "Trying to reconnect..." 67 | conn.reconnect(false, 2) 68 | end 69 | 70 | connection.on_recovery do |conn, settings| 71 | puts "Connection recovered" 72 | end 73 | 74 | show_stopper = Proc.new { 75 | connection.disconnect { puts "Disconnected. Exiting…"; EventMachine.stop } 76 | } 77 | 78 | Signal.trap "TERM", show_stopper 79 | Signal.trap "INT", show_stopper 80 | EM.add_timer(30, show_stopper) 81 | 82 | 83 | puts "Connected, authenticated. To really exercise this example, shut AMQP broker down for a few seconds. If you don't it will exit gracefully in 30 seconds." 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /examples/eventmachine_adapter/error_handling/connection_loss_handler_with_automatic_recovery.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # encoding: utf-8 3 | 4 | __dir = File.join(File.dirname(File.expand_path(__FILE__))) 5 | require File.join(__dir, "..", "example_helper") 6 | 7 | 8 | EM.run do 9 | AMQ::Client::EventMachineClient.connect(:port => 5672, 10 | :vhost => "amq_client_testbed", 11 | :user => "amq_client_gem", 12 | :password => "amq_client_gem_password", 13 | :timeout => 0.3, 14 | :on_tcp_connection_failure => Proc.new { |settings| puts "Failed to connect, this was NOT expected"; EM.stop }) do |connection| 15 | 16 | if connection.auto_recovering? 17 | puts "Connection is auto-recovering..." 18 | end 19 | 20 | ch1 = AMQ::Client::Channel.new(connection, 1, :auto_recovery => true) 21 | ch1.on_error do |ch, channel_close| 22 | raise channel_close.reply_text 23 | end 24 | ch1.open do 25 | puts "Channel 1 open now" 26 | end 27 | if ch1.auto_recovering? 28 | puts "Channel 1 is auto-recovering" 29 | end 30 | ch1.on_recovery do |c| 31 | puts "Channel #{c.id} has recovered" 32 | end 33 | 34 | ch2 = AMQ::Client::Channel.new(connection, 2, :auto_recovery => true) 35 | ch2.on_error do |ch, channel_close| 36 | raise channel_close.reply_text 37 | end 38 | ch2.open do 39 | puts "Channel 2 open now" 40 | end 41 | if ch2.auto_recovering? 42 | puts "Channel 2 is auto-recovering" 43 | end 44 | 45 | 46 | queues = Array.new(4) do 47 | q = AMQ::Client::Queue.new(connection, ch1, AMQ::Protocol::EMPTY_STRING) 48 | q.declare(false, false, false, true) do 49 | q.consume { puts "Added a consumer on #{q.name}"; q.on_delivery { |*args| puts(args.inspect) } } 50 | end 51 | 52 | q.on_recovery { |_| puts "Queue #{q.name} has recovered" } 53 | 54 | q 55 | end 56 | 57 | x = AMQ::Client::Exchange.new(connection, ch1, "amqclient.examples.exchanges.fanout", :fanout) 58 | x2 = AMQ::Client::Exchange.new(connection, ch1, "amq.fanout", :fanout) 59 | x.declare(false, false, true) 60 | x.after_connection_interruption { |x| puts "Exchange #{x.name} has reacted to connection interruption" } 61 | x.after_recovery { |x| puts "Exchange #{x.name} has recovered" } 62 | queues.each { |q| q.bind(x) } 63 | 64 | 65 | connection.on_tcp_connection_loss do |conn, settings| 66 | puts "Trying to reconnect..." 67 | conn.reconnect(false, 2) 68 | end 69 | 70 | connection.on_recovery do |conn, settings| 71 | puts "Connection recovered" 72 | end 73 | 74 | show_stopper = Proc.new { 75 | connection.disconnect { puts "Disconnected. Exiting…"; EventMachine.stop } 76 | } 77 | 78 | Signal.trap "TERM", show_stopper 79 | Signal.trap "INT", show_stopper 80 | EM.add_timer(30, show_stopper) 81 | 82 | 83 | puts "Connected, authenticated. To really exercise this example, shut AMQP broker down for a few seconds. If you don't it will exit gracefully in 30 seconds." 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/connection_start_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | # 7 | # We assume that default connection settings (amqp://guest:guest@localhost:5672/) should work 8 | # 9 | describe AMQ::Client::EventMachineClient, "Connection.Start" do 10 | include EventedSpec::SpecHelper 11 | default_timeout 0.5 12 | 13 | it "uses PLAIN authentication by default" do 14 | em do 15 | AMQ::Client::EventMachineClient.new(0).mechanism.should eq "PLAIN" 16 | done 17 | end 18 | end 19 | 20 | it "uses PLAIN authentication when explicitly set" do 21 | em do 22 | AMQ::Client::EventMachineClient.new(0, :auth_mechanism => "PLAIN").mechanism.should eq "PLAIN" 23 | done 24 | end 25 | end 26 | 27 | it "properly encodes username and password for PLAIN authentication" do 28 | em do 29 | client = AMQ::Client::EventMachineClient.new 0, :auth_mechanism => "PLAIN" 30 | client.encode_credentials("user", "pass").should eq "\0user\0pass" 31 | done 32 | end 33 | end 34 | 35 | it "uses EXTERNAL authentication when explicitly set" do 36 | em do 37 | AMQ::Client::EventMachineClient.new(0, :auth_mechanism => "EXTERNAL").mechanism.should eq "EXTERNAL" 38 | done 39 | end 40 | end 41 | 42 | it "skips encoding username and password for EXTERNAL authentication" do 43 | em do 44 | client = AMQ::Client::EventMachineClient.new 0, :auth_mechanism => "EXTERNAL" 45 | client.encode_credentials("user", "pass").should eq "" 46 | done 47 | end 48 | end 49 | 50 | it "allows user-defined authentication mechanisms" do 51 | Class.new AMQ::Client::Async::AuthMechanismAdapter do 52 | auth_mechanism "FOO" 53 | 54 | def encode_credentials(username, password) 55 | "foo" 56 | end 57 | end 58 | 59 | em do 60 | client = AMQ::Client::EventMachineClient.new 0, :auth_mechanism => "FOO" 61 | client.encode_credentials("user", "pass").should eq "foo" 62 | done 63 | end 64 | end 65 | 66 | it "fails for unimplemented authentication mechanisms" do 67 | em do 68 | client = AMQ::Client::EventMachineClient.new 0, :auth_mechanism => "BAR" 69 | expect do 70 | client.encode_credentials("user", "pass") 71 | end.to raise_error(NotImplementedError) 72 | done 73 | end 74 | end 75 | 76 | context "with valid credentials" do 77 | it "should trigger the callback" do 78 | em do 79 | AMQ::Client::EventMachineClient.connect do |client| 80 | done 81 | end 82 | end 83 | end 84 | end 85 | 86 | context "with invalid credentials" do 87 | context "when given an errback" do 88 | it "should trigger the errback" do 89 | em do 90 | AMQ::Client::EventMachineClient.connect(:port => 12938, :on_tcp_connection_failure => proc { done }) do |client| 91 | raise "This callback should never happen" 92 | end 93 | end 94 | end 95 | end 96 | 97 | context "when given no errback" do 98 | it "should raise an error" do 99 | expect { 100 | em do 101 | AMQ::Client::EventMachineClient.connect(:port => 12938) { } 102 | done(0.5) 103 | end 104 | }.to raise_error(AMQ::Client::TCPConnectionFailed) 105 | end 106 | end 107 | end 108 | 109 | end 110 | -------------------------------------------------------------------------------- /lib/amq/client/exceptions.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module AMQ 4 | module Client 5 | 6 | # 7 | # Adapters 8 | # 9 | 10 | class TCPConnectionFailed < StandardError 11 | 12 | # 13 | # API 14 | # 15 | 16 | attr_reader :settings 17 | 18 | def initialize(settings) 19 | @settings = settings 20 | 21 | super("Could not establish TCP connection to #{@settings[:host]}:#{@settings[:port]}") 22 | end 23 | end 24 | 25 | # Base exception class for data consistency and framing errors. 26 | class InconsistentDataError < StandardError 27 | end 28 | 29 | # Raised by adapters when frame does not end with {final octet AMQ::Protocol::Frame::FINAL_OCTET}. 30 | # This suggest that there is a bug in adapter or AMQ broker implementation. 31 | # 32 | # @see http://bit.ly/amqp091spec AMQP 0.9.1 specification (Section 2.3) 33 | class NoFinalOctetError < InconsistentDataError 34 | def initialize 35 | super("Frame doesn't end with #{AMQ::Protocol::Frame::FINAL_OCTET} as it must, which means the size is miscalculated.") 36 | end 37 | end 38 | 39 | # Raised by adapters when actual frame payload size in bytes is not equal 40 | # to the size specified in that frame's header. 41 | # This suggest that there is a bug in adapter or AMQ broker implementation. 42 | # 43 | # @see http://bit.ly/amqp091spec AMQP 0.9.1 specification (Section 2.3) 44 | class BadLengthError < InconsistentDataError 45 | def initialize(expected_length, actual_length) 46 | super("Frame payload should be #{expected_length} long, but it's #{actual_length} long.") 47 | end 48 | end 49 | 50 | # 51 | # Client 52 | # 53 | 54 | class MissingHandlerError < StandardError 55 | def initialize(frame) 56 | super("No callback registered for #{frame.method_class}") 57 | end 58 | end 59 | 60 | class ConnectionClosedError < StandardError 61 | def initialize(frame) 62 | if frame.respond_to?(:method_class) 63 | super("Trying to send frame through a closed connection. Frame is #{frame.inspect}, method class is #{frame.method_class}") 64 | else 65 | super("Trying to send frame through a closed connection. Frame is #{frame.inspect}") 66 | end 67 | end # initialize 68 | end # class ConnectionClosedError 69 | 70 | module Logging 71 | # Raised when logger object passed to {AMQ::Client::Adapter.logger=} does not 72 | # provide API it supposed to provide. 73 | # 74 | # @see AMQ::Client::Adapter.logger= 75 | class IncompatibleLoggerError < StandardError 76 | def initialize(required_methods) 77 | super("Logger has to respond to the following methods: #{required_methods.inspect}") 78 | end 79 | end 80 | end # Logging 81 | 82 | 83 | class PossibleAuthenticationFailureError < StandardError 84 | 85 | # 86 | # API 87 | # 88 | 89 | def initialize(settings) 90 | super("AMQP broker closed TCP connection before authentication succeeded: this usually means authentication failure due to misconfiguration or that RabbitMQ version does not support AMQP 0.9.1. Please see http://bit.ly/amqp-gem-080-and-rabbitmq-versions and check your configuration. Settings are #{settings.inspect}.") 91 | end # initialize(settings) 92 | end # PossibleAuthenticationFailureError 93 | 94 | 95 | 96 | class UnknownExchangeTypeError < StandardError 97 | BUILTIN_TYPES = [:fanout, :direct, :topic, :headers].freeze 98 | 99 | def initialize(types, given) 100 | super("#{given.inspect} exchange type is unknown. Standard types are #{BUILTIN_TYPES.inspect}, custom exchange types must begin with x-, for example: x-recent-history") 101 | end 102 | end 103 | 104 | end # Client 105 | end # AMQ 106 | -------------------------------------------------------------------------------- /spec/unit/client/settings_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "spec_helper" 4 | require "amq/client/settings" 5 | 6 | describe AMQ::Client::Settings do 7 | describe ".default" do 8 | it "should provide some default values" do 9 | AMQ::Client::Settings.default.should_not be_nil 10 | AMQ::Client::Settings.default[:host].should_not be_nil 11 | end 12 | end 13 | 14 | describe ".configure(&block)" do 15 | it "should merge custom settings with default settings" do 16 | settings = AMQ::Client::Settings.configure(:host => "tagadab") 17 | settings[:host].should eql("tagadab") 18 | end 19 | 20 | it "should merge custom settings from AMQP URL with default settings" do 21 | settings = AMQ::Client::Settings.configure("amqp://tagadab") 22 | settings[:host].should eql("tagadab") 23 | end 24 | end 25 | 26 | describe ".parse_amqp_url(connection_string)" do 27 | context "when schema is not one of [amqp, amqps]" do 28 | it "raises ArgumentError" do 29 | expect { 30 | described_class.parse_amqp_url("http://dev.rabbitmq.com") 31 | }.to raise_error(ArgumentError, /amqp or amqps schema/) 32 | end 33 | end 34 | 35 | 36 | it "handles amqp:// URIs w/o path part" do 37 | val = described_class.parse_amqp_url("amqp://dev.rabbitmq.com") 38 | 39 | val[:vhost].should be_nil # in this case, default / will be used 40 | val[:host].should == "dev.rabbitmq.com" 41 | val[:port].should == 5672 42 | val[:scheme].should == "amqp" 43 | val[:ssl].should be_false 44 | end 45 | 46 | it "handles amqps:// URIs w/o path part" do 47 | val = described_class.parse_amqp_url("amqps://dev.rabbitmq.com") 48 | 49 | val[:vhost].should be_nil 50 | val[:host].should == "dev.rabbitmq.com" 51 | val[:port].should == 5671 52 | val[:scheme].should == "amqps" 53 | val[:ssl].should be_true 54 | end 55 | 56 | 57 | context "when URI ends in a slash" do 58 | it "parses vhost as an empty string" do 59 | val = described_class.parse_amqp_url("amqp://dev.rabbitmq.com/") 60 | 61 | val[:host].should == "dev.rabbitmq.com" 62 | val[:port].should == 5672 63 | val[:scheme].should == "amqp" 64 | val[:ssl].should be_false 65 | val[:vhost].should == "" 66 | end 67 | end 68 | 69 | 70 | context "when URI ends in /%2Fvault" do 71 | it "parses vhost as /vault" do 72 | val = described_class.parse_amqp_url("amqp://dev.rabbitmq.com/%2Fvault") 73 | 74 | val[:host].should == "dev.rabbitmq.com" 75 | val[:port].should == 5672 76 | val[:scheme].should == "amqp" 77 | val[:ssl].should be_false 78 | val[:vhost].should == "/vault" 79 | end 80 | end 81 | 82 | 83 | context "when URI is amqp://dev.rabbitmq.com/a.path.without.slashes" do 84 | it "parses vhost as a.path.without.slashes" do 85 | val = described_class.parse_amqp_url("amqp://dev.rabbitmq.com/a.path.without.slashes") 86 | 87 | val[:host].should == "dev.rabbitmq.com" 88 | val[:port].should == 5672 89 | val[:scheme].should == "amqp" 90 | val[:ssl].should be_false 91 | val[:vhost].should == "a.path.without.slashes" 92 | end 93 | end 94 | 95 | context "when URI is amqp://dev.rabbitmq.com/a/path/with/slashes" do 96 | it "raises an ArgumentError" do 97 | lambda { described_class.parse_amqp_url("amqp://dev.rabbitmq.com/a/path/with/slashes") }.should raise_error(ArgumentError) 98 | end 99 | end 100 | 101 | 102 | context "when URI has username:password, for instance, amqp://hedgehog:t0ps3kr3t@hub.megacorp.internal" do 103 | it "parses them out" do 104 | val = described_class.parse_amqp_url("amqp://hedgehog:t0ps3kr3t@hub.megacorp.internal") 105 | 106 | val[:host].should == "hub.megacorp.internal" 107 | val[:port].should == 5672 108 | val[:scheme].should == "amqp" 109 | val[:ssl].should be_false 110 | val[:user].should == "hedgehog" 111 | val[:pass].should == "t0ps3kr3t" 112 | val[:vhost].should be_nil # in this case, default / will be used 113 | end 114 | end 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /spec/integration/coolio/exchange_declare_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do 7 | include EventedSpec::SpecHelper 8 | default_timeout 2 9 | let(:exchange_name) { "amq-client.testexchange.#{Time.now.to_i}" } 10 | it "should create an exchange and trigger a callback" do 11 | coolio_amqp_connect do |client| 12 | channel = AMQ::Client::Channel.new(client, 1) 13 | channel.open do 14 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 15 | exchange.declare do 16 | exchange.delete 17 | done(0.5) 18 | end 19 | end 20 | end 21 | end # it "should create an exchange and trigger a callback" 22 | 23 | context "options" do 24 | context "passive" do 25 | context "when set" do 26 | it "should trigger channel level error if given exchange doesn't exist" do 27 | coolio_amqp_connect do |client| 28 | channel = AMQ::Client::Channel.new(client, 1) 29 | channel.open do 30 | channel.on_error do 31 | @error_fired = true 32 | end 33 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 34 | exchange.declare(true, false, false, false) do 35 | @callback_fired = true 36 | end 37 | done(0.5) 38 | end 39 | end 40 | 41 | @callback_fired.should be_false 42 | @error_fired.should be_true 43 | end # it "should trigger channel level error if given exchange doesn't exist" 44 | 45 | it "should raise no error and fire the callback if given exchange exists" do 46 | coolio_amqp_connect do |client| 47 | channel = AMQ::Client::Channel.new(client, 1) 48 | channel.open do 49 | channel.on_error do 50 | @error_fired = true 51 | end 52 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 53 | exchange.declare(false, false, false, false) do # initial declaration 54 | AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout).declare(true) do 55 | @callback_fired = true 56 | end 57 | end 58 | delayed(0.1) { exchange.delete } 59 | done(0.5) 60 | end 61 | end 62 | 63 | @callback_fired.should be_true 64 | @error_fired.should be_false 65 | end # it "should raise no error and fire the callback if given exchange exists" 66 | end # context "when set" 67 | 68 | context "when unset" do 69 | it "should raise no error and fire the callback if given exchange doesn't exist" do 70 | coolio_amqp_connect do |client| 71 | channel = AMQ::Client::Channel.new(client, 1) 72 | channel.open do 73 | channel.on_error do 74 | @error_fired = true 75 | end 76 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 77 | exchange.declare(false, false, false, false) do 78 | @callback_fired = true 79 | end 80 | delayed(0.1) { exchange.delete } 81 | done(0.5) 82 | end 83 | end 84 | 85 | @callback_fired.should be_true 86 | @error_fired.should be_false 87 | end # it "should raise no error and fire the callback if given exchange doesn't exist" 88 | 89 | it "should raise no error and fire the callback if given exchange exists" do 90 | coolio_amqp_connect do |client| 91 | channel = AMQ::Client::Channel.new(client, 1) 92 | channel.open do 93 | channel.on_error do 94 | @error_fired = true 95 | end 96 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 97 | exchange.declare(false, false, false, false) do # initial declaration 98 | AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout).declare(false) do 99 | @callback_fired = true 100 | end 101 | end 102 | delayed(0.1) { exchange.delete } 103 | done(0.5) 104 | end 105 | end 106 | 107 | @callback_fired.should be_true 108 | @error_fired.should be_false 109 | end # it "should raise no error and fire the callback if given exchange exists" 110 | end # context "when unset" 111 | end # context "passive" 112 | 113 | # Other options make little sense to test 114 | 115 | end # context "options" 116 | end # describe 117 | -------------------------------------------------------------------------------- /spec/integration/coolio/basic_consume_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/coolio/spec_helper' 5 | 6 | # 7 | # Note that this spec doesn't test acknowledgements. 8 | # See basic_ack_spec for example with acks 9 | # 10 | 11 | describe "AMQ::Client::CoolioClient", "Basic.Consume", :nojruby => true do 12 | include EventedSpec::SpecHelper 13 | default_timeout 4 14 | 15 | context "sending 100 messages" do 16 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 17 | 18 | it "should receive all the messages" do 19 | @received_messages = [] 20 | coolio_amqp_connect do |client| 21 | channel = AMQ::Client::Channel.new(client, 1) 22 | channel.open do 23 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 24 | queue.bind("amq.fanout") 25 | 26 | queue.consume(true) do |amq_method| 27 | queue.on_delivery do |method, header, payload| 28 | @received_messages << payload 29 | end 30 | 31 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 32 | messages.each do |message| 33 | exchange.publish(message) 34 | end 35 | end 36 | 37 | done(1.5) { 38 | @received_messages.should =~ messages 39 | } 40 | end 41 | end 42 | end # it "should receive all the messages" 43 | 44 | it "should not leave messages in the queues with noack=true" do 45 | @received_messages = [] 46 | @rereceived_messages = [] 47 | coolio_amqp_connect do |client| 48 | channel = AMQ::Client::Channel.new(client, 1) 49 | channel.open do 50 | channel.qos(0, 1) # Maximum prefetch size = 1 51 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 52 | queue.bind("amq.fanout") 53 | 54 | # Change noack to false and watch spec fail 55 | queue.consume(true) do |amq_method| 56 | queue.on_delivery do |method, header, payload| 57 | @received_messages << payload 58 | end 59 | 60 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 61 | messages.each do |message| 62 | exchange.publish(message) 63 | end 64 | end 65 | 66 | # We're opening another channel and starting consuming the same queue, 67 | # assuming 1.5 secs was enough to receive all the messages 68 | delayed(1.5) do 69 | other_channel = AMQ::Client::Channel.new(client, 2) 70 | other_channel.open do 71 | other_channel.qos(0, 1) # Maximum prefetch size = 1 72 | other_queue = AMQ::Client::Queue.new(client, other_channel, queue.name) 73 | other_queue.consume(true) do |amq_method| 74 | other_queue.on_delivery do |method, header, payload| 75 | @rereceived_messages << payload 76 | end 77 | end 78 | end 79 | end 80 | 81 | end 82 | 83 | 84 | done(2.5) { 85 | @rereceived_messages.should == [] 86 | @received_messages.should =~ messages 87 | } 88 | end 89 | end # it "should not leave messages in the queues with noack=true" 90 | end # context "sending 100 messages" 91 | end # describe AMQ::Client::CoolioClient, "Basic.Consume" 92 | 93 | 94 | describe "Multiple", AMQ::Client::Async::Consumer, :nojruby => true do 95 | include EventedSpec::SpecHelper 96 | default_timeout 4 97 | 98 | context "sharing the same queue with equal prefetch levels" do 99 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 100 | 101 | it "have messages distributed to them in the round-robin manner" do 102 | @consumer1_mailbox = [] 103 | @consumer2_mailbox = [] 104 | @consumer3_mailbox = [] 105 | 106 | coolio_amqp_connect do |client| 107 | channel = AMQ::Client::Channel.new(client, 1) 108 | channel.open do 109 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 110 | queue.bind("amq.fanout") 111 | 112 | consumer1 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{Time.now}") 113 | consumer2 = AMQ::Client::Async::Consumer.new(channel, queue) 114 | consumer3 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{rand}-#{Time.now}", false, true) 115 | 116 | 117 | consumer1.consume.on_delivery do |method, header, payload| 118 | @consumer1_mailbox << payload 119 | end 120 | 121 | consumer2.consume(true).on_delivery do |method, header, payload| 122 | @consumer2_mailbox << payload 123 | end 124 | 125 | consumer3.consume(false) do 126 | puts "Consumer 3 is ready" 127 | end 128 | consumer3.on_delivery do |method, header, payload| 129 | @consumer3_mailbox << payload 130 | end 131 | 132 | 133 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 134 | messages.each do |message| 135 | exchange.publish(message) 136 | end 137 | end 138 | 139 | done(1.5) { 140 | @consumer1_mailbox.size.should == 34 141 | @consumer2_mailbox.size.should == 33 142 | @consumer3_mailbox.size.should == 33 143 | } 144 | end 145 | end # it 146 | end # context 147 | end # describe 148 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/exchange_declare_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | describe AMQ::Client::EventMachineClient, "Exchange.Declare" do 7 | 8 | # 9 | # Environment 10 | # 11 | 12 | include EventedSpec::SpecHelper 13 | default_timeout 2 14 | let(:exchange_name) { "amq-client.testexchange.#{Time.now.to_i}" } 15 | 16 | 17 | 18 | # 19 | # Examples 20 | # 21 | 22 | context "when exchange type is non-standard" do 23 | context "and DOES NOT begin with x-" do 24 | it "raises an exception" do 25 | em_amqp_connect do |client| 26 | channel = AMQ::Client::Channel.new(client, 1) 27 | channel.open do 28 | begin 29 | AMQ::Client::Exchange.new(client, channel, exchange_name, "my_shiny_metal_exchange_type") 30 | rescue AMQ::Client::UnknownExchangeTypeError => e 31 | done 32 | end 33 | end # channel.open 34 | end # em_amqp_connect 35 | end # it 36 | end # context 37 | end # context 38 | 39 | 40 | 41 | it "should create an exchange and trigger a callback" do 42 | em_amqp_connect do |client| 43 | channel = AMQ::Client::Channel.new(client, 1) 44 | channel.open do 45 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, "fanout") 46 | exchange.declare do 47 | exchange.delete 48 | done(0.2) 49 | end 50 | end 51 | end 52 | end # it "should create an exchange and trigger a callback" 53 | 54 | context "options" do 55 | context "passive" do 56 | context "when set" do 57 | it "should trigger channel level error if given exchange doesn't exist" do 58 | em_amqp_connect do |client| 59 | channel = AMQ::Client::Channel.new(client, 1) 60 | channel.open do 61 | channel.on_error do 62 | @error_fired = true 63 | end 64 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 65 | exchange.declare(true, false, false, false) do 66 | @callback_fired = true 67 | end 68 | done(0.3) 69 | end 70 | end 71 | 72 | @callback_fired.should be_false 73 | @error_fired.should be_true 74 | end # it "should trigger channel level error if given exchange doesn't exist" 75 | 76 | it "should raise no error and fire the callback if given exchange exists" do 77 | em_amqp_connect do |client| 78 | channel = AMQ::Client::Channel.new(client, 1) 79 | channel.open do 80 | channel.on_error do 81 | @error_fired = true 82 | end 83 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 84 | exchange.declare(false, false, false, false) do # initial declaration 85 | AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout).declare(true) do 86 | @callback_fired = true 87 | end 88 | end 89 | delayed(0.1) { exchange.delete } 90 | done(0.3) 91 | end 92 | end 93 | 94 | @callback_fired.should be_true 95 | @error_fired.should be_false 96 | end # it "should raise no error and fire the callback if given exchange exists" 97 | end # context "when set" 98 | 99 | context "when unset" do 100 | it "should raise no error and fire the callback if given exchange doesn't exist" do 101 | em_amqp_connect do |client| 102 | channel = AMQ::Client::Channel.new(client, 1) 103 | channel.open do 104 | channel.on_error do 105 | @error_fired = true 106 | end 107 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 108 | exchange.declare(false, false, false, false) do 109 | @callback_fired = true 110 | end 111 | delayed(0.1) { exchange.delete } 112 | done(0.3) 113 | end 114 | end 115 | 116 | @callback_fired.should be_true 117 | @error_fired.should be_false 118 | end # it "should raise no error and fire the callback if given exchange doesn't exist" 119 | 120 | it "should raise no error and fire the callback if given exchange exists" do 121 | em_amqp_connect do |client| 122 | channel = AMQ::Client::Channel.new(client, 1) 123 | channel.open do 124 | channel.on_error do 125 | @error_fired = true 126 | end 127 | exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout) 128 | exchange.declare(false, false, false, false) do # initial declaration 129 | AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout).declare(false) do 130 | @callback_fired = true 131 | end 132 | end 133 | delayed(0.1) { exchange.delete } 134 | done(0.3) 135 | end 136 | end 137 | 138 | @callback_fired.should be_true 139 | @error_fired.should be_false 140 | end # it "should raise no error and fire the callback if given exchange exists" 141 | end # context "when unset" 142 | end # context "passive" 143 | 144 | # Other options make little sense to test 145 | 146 | end # context "options" 147 | end # describe 148 | -------------------------------------------------------------------------------- /spec/integration/eventmachine/basic_consume_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'spec_helper' 4 | require 'integration/eventmachine/spec_helper' 5 | 6 | # 7 | # Note that this spec doesn't test acknowledgements. 8 | # See basic_ack_spec for example with acks 9 | # 10 | 11 | describe AMQ::Client::EventMachineClient, "AMQP consumer" do 12 | include EventedSpec::SpecHelper 13 | default_timeout 5 14 | 15 | context "sending 100 messages" do 16 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 17 | 18 | it "should receive all the messages" do 19 | @received_messages = [] 20 | em_amqp_connect do |client| 21 | channel = AMQ::Client::Channel.new(client, 1) 22 | channel.open do 23 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 24 | queue.bind("amq.fanout") 25 | 26 | queue.consume(true) do |amq_method| 27 | queue.on_delivery do |method, header, payload| 28 | @received_messages << payload 29 | end 30 | 31 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 32 | messages.each do |message| 33 | exchange.publish(message) 34 | end 35 | end 36 | 37 | done(3.5) { 38 | @received_messages.should =~ messages 39 | } 40 | end 41 | end 42 | end # it "should receive all the messages" 43 | 44 | it "should not leave messages in the queues with noack=true" do 45 | @received_messages = [] 46 | @rereceived_messages = [] 47 | em_amqp_connect do |client| 48 | channel = AMQ::Client::Channel.new(client, 1) 49 | channel.open do 50 | channel.qos(0, 1) # Maximum prefetch size = 1 51 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 52 | queue.bind("amq.fanout") 53 | 54 | # Change noack to false and watch spec fail 55 | queue.consume(true) do |amq_method| 56 | queue.on_delivery do |method, header, payload| 57 | @received_messages << payload 58 | end 59 | 60 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 61 | messages.each do |message| 62 | exchange.publish(message) 63 | end 64 | end 65 | 66 | # We're opening another channel and starting consuming the same queue, 67 | # assuming 2.0 secs was enough to receive all the messages 68 | delayed(2.0) do 69 | other_channel = AMQ::Client::Channel.new(client, 2) 70 | other_channel.open do 71 | other_channel.qos(0, 1) # Maximum prefetch size = 1 72 | other_queue = AMQ::Client::Queue.new(client, other_channel, queue.name) 73 | other_queue.consume(true) do |amq_method| 74 | other_queue.on_delivery do |method, header, payload| 75 | @rereceived_messages << payload 76 | end 77 | end 78 | end 79 | end 80 | 81 | end 82 | 83 | 84 | done(4.5) { 85 | @rereceived_messages.should == [] 86 | @received_messages.should =~ messages 87 | } 88 | end 89 | end # it "should not leave messages in the queues with noack=true" 90 | end # context "sending 100 messages" 91 | end # describe AMQ::Client::EventMachineClient, "Basic.Consume" 92 | 93 | 94 | 95 | 96 | describe "Multiple", AMQ::Client::Async::Consumer do 97 | include EventedSpec::SpecHelper 98 | default_timeout 4 99 | 100 | context "sharing the same queue with equal prefetch levels" do 101 | let(:messages) { (0..99).map {|i| "Message #{i}" } } 102 | 103 | it "have messages distributed to them in the round-robin manner" do 104 | @consumer1_mailbox = [] 105 | @consumer2_mailbox = [] 106 | @consumer3_mailbox = [] 107 | 108 | em_amqp_connect do |client| 109 | channel = AMQ::Client::Channel.new(client, 1) 110 | channel.open do 111 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 112 | queue.bind("amq.fanout") 113 | 114 | consumer1 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{Time.now}") 115 | consumer2 = AMQ::Client::Async::Consumer.new(channel, queue) 116 | consumer3 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{rand}-#{Time.now}", false, true) 117 | 118 | 119 | consumer1.consume.on_delivery do |method, header, payload| 120 | @consumer1_mailbox << payload 121 | end 122 | 123 | consumer2.consume(true).on_delivery do |method, header, payload| 124 | @consumer2_mailbox << payload 125 | end 126 | 127 | consumer3.consume(false) do 128 | puts "Consumer 3 is ready" 129 | end 130 | consumer3.on_delivery do |method, header, payload| 131 | @consumer3_mailbox << payload 132 | end 133 | 134 | 135 | exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout) 136 | messages.each do |message| 137 | exchange.publish(message) 138 | end 139 | end 140 | 141 | done(1.5) { 142 | @consumer1_mailbox.size.should == 34 143 | @consumer2_mailbox.size.should == 33 144 | @consumer3_mailbox.size.should == 33 145 | } 146 | end 147 | end # it 148 | end # context 149 | end # describe 150 | 151 | 152 | 153 | describe AMQ::Client::Async::Consumer do 154 | include EventedSpec::SpecHelper 155 | default_timeout 4 156 | 157 | context "registered with a callback" do 158 | it "runs that callback when basic.consume-ok arrives" do 159 | em_amqp_connect do |client| 160 | channel = AMQ::Client::Channel.new(client, 1) 161 | channel.open do 162 | queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true) 163 | queue.bind("amq.fanout") 164 | 165 | consumer1 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{Time.now}") 166 | consumer1.consume do 167 | done 168 | end # consume do 169 | end # open do 170 | end # em_amqp_connect 171 | end # it 172 | end # context 173 | end # describe 174 | -------------------------------------------------------------------------------- /lib/amq/client/settings.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require "amq/protocol/client" # TODO: "amq/protocol/constants" 4 | require "uri" 5 | 6 | module AMQ 7 | module Client 8 | # @see AMQ::Client::Settings.configure 9 | module Settings 10 | # @private 11 | AMQP_PORTS = {"amqp" => 5672, "amqps" => 5671}.freeze 12 | 13 | # @private 14 | AMQPS = "amqps".freeze 15 | 16 | # Default connection settings used by AMQ clients 17 | # 18 | # @see AMQ::Client::Settings.configure 19 | def self.default 20 | @default ||= { 21 | # server 22 | :host => "127.0.0.1", 23 | :port => AMQ::Protocol::DEFAULT_PORT, 24 | 25 | # login 26 | :user => "guest", 27 | :pass => "guest", 28 | :auth_mechanism => "PLAIN", 29 | :vhost => "/", 30 | 31 | # connection timeout 32 | :timeout => nil, 33 | 34 | # logging 35 | :logging => false, 36 | 37 | # ssl 38 | :ssl => false, 39 | 40 | # broker 41 | # if you want to load broker-specific extensions 42 | :broker => nil, 43 | 44 | :frame_max => 131072, 45 | :heartbeat => 0 46 | } 47 | end 48 | 49 | CLIENT_PROPERTIES = { 50 | :platform => ::RUBY_DESCRIPTION, 51 | :product => "AMQ Client", 52 | :information => "http://github.com/ruby-amqp/amq-client", 53 | :version => AMQ::Client::VERSION 54 | } 55 | 56 | def self.client_properties 57 | @client_properties ||= CLIENT_PROPERTIES 58 | end 59 | 60 | 61 | # Merges given configuration parameters with defaults and returns 62 | # the result. 63 | # 64 | # @param [Hash] Configuration parameters to use. 65 | # 66 | # @option settings [String] :host ("127.0.0.1") Hostname AMQ broker runs on. 67 | # @option settings [String] :port (5672) Port AMQ broker listens on. 68 | # @option settings [String] :vhost ("/") Virtual host to use. 69 | # @option settings [String] :user ("guest") Username to use for authentication. 70 | # @option settings [String] :pass ("guest") Password to use for authentication. 71 | # @option settings [String] :ssl (false) Should be use TLS (SSL) for connection? 72 | # @option settings [String] :timeout (nil) Connection timeout. 73 | # @option settings [String] :logging (false) Turns logging on or off. 74 | # @option settings [String] :broker (nil) Broker name (use if you intend to use broker-specific features). 75 | # @option settings [Fixnum] :frame_max (131072) Maximum frame size to use. If broker cannot support frames this large, broker's maximum value will be used instead. 76 | # 77 | # @return [Hash] Merged configuration parameters. 78 | def self.configure(settings = nil) 79 | case settings 80 | when Hash then 81 | if username = settings.delete(:username) 82 | settings[:user] ||= username 83 | end 84 | 85 | if password = settings.delete(:password) 86 | settings[:pass] ||= password 87 | end 88 | 89 | 90 | self.default.merge(settings) 91 | when String then 92 | settings = self.parse_amqp_url(settings) 93 | self.default.merge(settings) 94 | when NilClass then 95 | self.default 96 | end 97 | end 98 | 99 | # Parses AMQP connection URI and returns its components as a hash. 100 | # 101 | # h2. vhost naming schemes 102 | # 103 | # It is convenient to be able to specify the AMQP connection 104 | # parameters as a URI string, and various "amqp" URI schemes 105 | # exist. Unfortunately, there is no standard for these URIs, so 106 | # while the schemes share the basic idea, they differ in some 107 | # details. This implementation aims to encourage URIs that work 108 | # as widely as possible. 109 | # 110 | # The URI scheme should be "amqp", or "amqps" if SSL is required. 111 | # 112 | # The host, port, username and password are represented in the 113 | # authority component of the URI in the same way as in http URIs. 114 | # 115 | # The vhost is obtained from the first segment of the path, with the 116 | # leading slash removed. The path should contain only a single 117 | # segment (i.e, the only slash in it should be the leading one). 118 | # If the vhost is to include slashes or other reserved URI 119 | # characters, these should be percent-escaped. 120 | # 121 | # @example How vhost is parsed 122 | # 123 | # AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com") # => vhost is nil, so default (/) will be used 124 | # AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/") # => vhost is an empty string 125 | # AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/%2Fvault") # => vhost is /vault 126 | # AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/production") # => vhost is production 127 | # AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/a.b.c") # => vhost is a.b.c 128 | # AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/foo/bar") # => ArgumentError 129 | # 130 | # 131 | # @param [String] connection_string AMQP connection URI, à la JDBC connection string. For example: amqp://bus.megacorp.internal:5877. 132 | # @return [Hash] Connection parameters (:username, :password, :vhost, :host, :port, :ssl) 133 | # 134 | # @raise [ArgumentError] When connection URI schema is not amqp or amqps, or the path contains multiple segments 135 | # 136 | # @see http://bit.ly/ks8MXK Connecting to The Broker documentation guide 137 | # @api public 138 | def self.parse_amqp_url(connection_string) 139 | uri = ::URI.parse(connection_string) 140 | raise ArgumentError.new("Connection URI must use amqp or amqps schema (example: amqp://bus.megacorp.internal:5766), learn more at http://bit.ly/ks8MXK") unless %w{amqp amqps}.include?(uri.scheme) 141 | 142 | opts = {} 143 | 144 | opts[:scheme] = uri.scheme 145 | opts[:user] = URI.unescape(uri.user) if uri.user 146 | opts[:pass] = URI.unescape(uri.password) if uri.password 147 | opts[:host] = uri.host if uri.host 148 | opts[:port] = uri.port || AMQ::Client::Settings::AMQP_PORTS[uri.scheme] 149 | opts[:ssl] = uri.scheme == AMQ::Client::Settings::AMQPS 150 | if uri.path =~ %r{^/(.*)} 151 | raise ArgumentError.new("#{uri} has multiple-segment path; please percent-encode any slashes in the vhost name (e.g. /production => %2Fproduction). Learn more at http://bit.ly/amqp-gem-and-connection-uris") if $1.index('/') 152 | opts[:vhost] = URI.unescape($1) 153 | end 154 | 155 | opts 156 | end 157 | end 158 | end 159 | end 160 | --------------------------------------------------------------------------------