├── .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 |
--------------------------------------------------------------------------------