amq-client 1.0.0.pre2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,10 +1,13 @@
1
- /*.gem
1
+ bin/*
2
2
  .bundle
3
- vendor/bundle
4
- .yardoc/*
5
3
  doc/*
6
- .rvmrc
4
+ /*.gem
7
5
  # see http://bit.ly/h2WJPm for reasoning
8
6
  Gemfile.lock
9
- vendor
7
+ .rbenv-gemsets
8
+ .rbenv-version
10
9
  .rbx/*
10
+ .rvmrc
11
+ vendor
12
+ vendor/bundle
13
+ .yardoc/*
@@ -18,4 +18,7 @@ branches:
18
18
  only:
19
19
  - master
20
20
  - 0.9.x-stable
21
- - 0.8.x-stable
21
+ - 0.8.x-stable
22
+
23
+ services:
24
+ - rabbitmq
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
 
21
21
  # Dependencies
22
22
  s.add_dependency "eventmachine"
23
- s.add_dependency "amq-protocol", ">= 0.9.4"
23
+ s.add_dependency "amq-protocol", ">= 1.2.0"
24
24
 
25
25
 
26
26
  # RubyForge
@@ -1,24 +1,26 @@
1
1
  #!/bin/sh
2
2
 
3
+ ${RABBITMQCTL:="sudo rabbitmqctl"}
4
+
3
5
  # guest:guest has full access to /
4
6
 
5
- sudo rabbitmqctl add_vhost /
6
- sudo rabbitmqctl add_user guest guest
7
- sudo rabbitmqctl set_permissions -p / guest ".*" ".*" ".*"
7
+ $RABBITMQCTL add_vhost /
8
+ $RABBITMQCTL add_user guest guest
9
+ $RABBITMQCTL set_permissions -p / guest ".*" ".*" ".*"
8
10
 
9
11
 
10
12
  # guest:guest has full access to amq_client_testbed
11
13
  # amq_client_gem:amq_client_gem has full access to /amq_client_testbed
12
14
 
13
- sudo rabbitmqctl delete_vhost "amq_client_testbed"
14
- sudo rabbitmqctl add_vhost "amq_client_testbed"
15
- sudo rabbitmqctl delete_user amq_client_gem
16
- sudo rabbitmqctl add_user amq_client_gem amq_client_gem_password
17
- sudo rabbitmqctl set_permissions -p amq_client_testbed guest ".*" ".*" ".*"
18
- sudo rabbitmqctl set_permissions -p amq_client_testbed amq_client_gem ".*" ".*" ".*"
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 ".*" ".*" ".*"
19
21
 
20
22
 
21
23
  # amqp_gem_reader:reader_password has read access to amq_client_testbed
22
24
 
23
- sudo rabbitmqctl add_user amq_client_gem_reader reader_password
24
- sudo rabbitmqctl set_permissions -p amq_client_testbed amq_client_gem_reader "^---$" "^---$" ".*"
25
+ $RABBITMQCTL add_user amq_client_gem_reader reader_password
26
+ $RABBITMQCTL set_permissions -p amq_client_testbed amq_client_gem_reader "^---$" "^---$" ".*"
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "amq/client/logging"
4
4
  require "amq/client/settings"
5
+ require "amq/client/async/auth_mechanism_adapter"
5
6
  require "amq/client/async/entity"
6
7
  require "amq/client/async/channel"
7
8
 
@@ -274,13 +275,13 @@ module AMQ
274
275
  # @return [Fixnum] Heartbeat interval this client uses, in seconds.
275
276
  # @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.4.2.6)
276
277
  def heartbeat_interval
277
- @settings[:heartbeat] || @settings[:heartbeat_interval] || 0
278
+ @heartbeat_interval
278
279
  end # heartbeat_interval
279
280
 
280
281
  # Returns true if heartbeats are enabled (heartbeat interval is greater than 0)
281
282
  # @return [Boolean]
282
283
  def heartbeats_enabled?
283
- self.heartbeat_interval > 0
284
+ @heartbeat_interval && (@heartbeat_interval > 0)
284
285
  end
285
286
 
286
287
 
@@ -368,6 +369,8 @@ module AMQ
368
369
  # @private
369
370
  # @api plugin
370
371
  def handle_connection_interruption
372
+ self.cancel_heartbeat_sender
373
+
371
374
  @channels.each { |n, c| c.handle_connection_interruption }
372
375
  self.exec_callback_yielding_self(:after_connection_interruption)
373
376
  end # handle_connection_interruption
@@ -509,9 +512,17 @@ module AMQ
509
512
  # @api plugin
510
513
  # @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595
511
514
  def encode_credentials(username, password)
512
- "\0#{username}\0#{password}"
515
+ auth_mechanism_adapter.encode_credentials(username, password)
513
516
  end # encode_credentials(username, password)
514
517
 
518
+ # Retrieves an AuthMechanismAdapter that will encode credentials for
519
+ # this Adapter.
520
+ #
521
+ # @api plugin
522
+ def auth_mechanism_adapter
523
+ @auth_mechanism_adapter ||= AuthMechanismAdapter.for_adapter(self)
524
+ end
525
+
515
526
 
516
527
  # Processes a single frame.
517
528
  #
@@ -600,7 +611,7 @@ module AMQ
600
611
  # @status undefined. So lets do this. MK.
601
612
  opening!
602
613
 
603
- self.send_frame(Protocol::Connection::StartOk.encode(@client_properties, @mechanism, self.encode_credentials(username, password), @locale))
614
+ self.send_frame(Protocol::Connection::StartOk.encode(@client_properties, mechanism, self.encode_credentials(username, password), @locale))
604
615
  end
605
616
 
606
617
 
@@ -608,12 +619,16 @@ module AMQ
608
619
  #
609
620
  # @api plugin
610
621
  # @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.4.2.6)
611
- def handle_tune(tune_ok)
612
- @channel_max = tune_ok.channel_max.freeze
613
- @frame_max = tune_ok.frame_max.freeze
614
- @heartbeat_interval = self.heartbeat_interval || tune_ok.heartbeat
622
+ def handle_tune(connection_tune)
623
+ @channel_max = connection_tune.channel_max.freeze
624
+ @frame_max = connection_tune.frame_max.freeze
625
+
626
+ client_heartbeat = @settings[:heartbeat] || @settings[:heartbeat_interval] || 0
627
+
628
+ @heartbeat_interval = negotiate_heartbeat_value(client_heartbeat, connection_tune.heartbeat)
615
629
 
616
630
  self.send_frame(Protocol::Connection::TuneOk.encode(@channel_max, [settings[:frame_max], @frame_max].min, @heartbeat_interval))
631
+ self.initialize_heartbeat_sender if heartbeats_enabled?
617
632
  end # handle_tune(method)
618
633
 
619
634
 
@@ -652,6 +667,14 @@ module AMQ
652
667
 
653
668
  protected
654
669
 
670
+ def negotiate_heartbeat_value(client_value, server_value)
671
+ if client_value == 0 || server_value == 0
672
+ [client_value, server_value].max
673
+ else
674
+ [client_value, server_value].min
675
+ end
676
+ end
677
+
655
678
  # Returns next frame from buffer whenever possible
656
679
  #
657
680
  # @api private
@@ -275,6 +275,10 @@ module AMQ
275
275
  AMQ::Client::TCPConnectionFailed
276
276
  end # self.tcp_connection_failure_exception_class
277
277
 
278
+ def initialize_heartbeat_sender
279
+ # TODO
280
+ end
281
+
278
282
  def handle_skipped_hearbeats
279
283
  # TODO
280
284
  end
@@ -31,10 +31,10 @@ module AMQ
31
31
  # @option settings [String] :vhost ("/") Virtual host to use.
32
32
  # @option settings [String] :user ("guest") Username to use for authentication.
33
33
  # @option settings [String] :pass ("guest") Password to use for authentication.
34
+ # @option settings [String] :auth_mechanism ("PLAIN") SASL authentication mechanism to use.
34
35
  # @option settings [String] :ssl (false) Should be use TLS (SSL) for connection?
35
36
  # @option settings [String] :timeout (nil) Connection timeout.
36
- # @option settings [String] :logging (false) Turns logging on or off.
37
- # @option settings [String] :broker (nil) Broker name (use if you intend to use broker-specific features).
37
+ # @option settings [Fixnum] :heartbeat (0) Connection heartbeat, in seconds.
38
38
  # @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.
39
39
  #
40
40
  # @param [Hash] settings
@@ -164,7 +164,7 @@ module AMQ
164
164
  raise self.class.authentication_failure_exception_class.new(settings)
165
165
  }
166
166
 
167
- @mechanism = "PLAIN"
167
+ @mechanism = @settings.fetch(:auth_mechanism, "PLAIN")
168
168
  @locale = @settings.fetch(:locale, "en_GB")
169
169
  @client_properties = Settings.client_properties.merge(@settings.fetch(:client_properties, Hash.new))
170
170
 
@@ -172,8 +172,6 @@ module AMQ
172
172
 
173
173
  self.reset
174
174
  self.set_pending_connect_timeout((@settings[:timeout] || 3).to_f) unless defined?(JRUBY_VERSION)
175
-
176
- self.initialize_heartbeat_sender if self.heartbeats_enabled?
177
175
  end # initialize(*args)
178
176
 
179
177
 
@@ -267,8 +265,6 @@ module AMQ
267
265
  @handling_skipped_hearbeats = false
268
266
  @last_server_heartbeat = Time.now
269
267
 
270
- self.initialize_heartbeat_sender if self.heartbeat_interval > 0
271
-
272
268
  self.handshake
273
269
  end
274
270
 
@@ -356,7 +352,7 @@ module AMQ
356
352
  def handle_skipped_hearbeats
357
353
  if !@handling_skipped_hearbeats && @tcp_connection_established && !@intentionally_closing_connection
358
354
  @handling_skipped_hearbeats = true
359
- @heartbeats_timer.cancel
355
+ self.cancel_heartbeat_sender
360
356
 
361
357
  self.run_skipped_heartbeats_callbacks
362
358
  end
@@ -368,6 +364,11 @@ module AMQ
368
364
  @heartbeats_timer = EventMachine::PeriodicTimer.new(self.heartbeat_interval, &method(:send_heartbeat))
369
365
  end
370
366
 
367
+ # @private
368
+ def cancel_heartbeat_sender
369
+ @heartbeats_timer.cancel if @heartbeats_timer
370
+ end
371
+
371
372
 
372
373
 
373
374
  self.handle(Protocol::Connection::Start) do |connection, frame|
@@ -0,0 +1,69 @@
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<String>] 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
@@ -0,0 +1,27 @@
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
@@ -0,0 +1,24 @@
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
@@ -133,7 +133,7 @@ module AMQ
133
133
  # Acknowledge one or all messages on the channel.
134
134
  #
135
135
  # @api public
136
- # @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.13.)
136
+ # @see files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol reference (Section 1.8.3.13.)
137
137
  def acknowledge(delivery_tag, multiple = false)
138
138
  @connection.send_frame(Protocol::Basic::Ack.encode(self.id, delivery_tag, multiple))
139
139
 
@@ -143,7 +143,7 @@ module AMQ
143
143
  # Reject a message with given delivery tag.
144
144
  #
145
145
  # @api public
146
- # @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.14.)
146
+ # @see files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol reference (Section 1.8.3.14.)
147
147
  def reject(delivery_tag, requeue = true)
148
148
  @connection.send_frame(Protocol::Basic::Reject.encode(self.id, delivery_tag, requeue))
149
149
 
@@ -156,7 +156,7 @@ module AMQ
156
156
  # @return [Channel] self
157
157
  #
158
158
  # @note RabbitMQ as of 2.3.1 does not support basic.recover with requeue = false.
159
- # @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.8.3.16.)
159
+ # @see files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol reference (Section 1.8.3.16.)
160
160
  # @api public
161
161
  def recover(requeue = true, &block)
162
162
  @connection.send_frame(Protocol::Basic::Recover.encode(@id, requeue))
@@ -181,7 +181,7 @@ module AMQ
181
181
 
182
182
  self.redefine_callback :qos, &block
183
183
  self
184
- end # qos(prefetch_size = 4096, prefetch_count = 32, global = false, &block)
184
+ end # qos
185
185
 
186
186
  # Asks the peer to pause or restart the flow of content data sent to a consumer.
187
187
  # This is a simple flow­control mechanism that a peer can use to avoid overflowing its
@@ -191,7 +191,7 @@ module AMQ
191
191
  #
192
192
  # @param [Boolean] active Desired flow state.
193
193
  #
194
- # @see http://bit.ly/amqp091reference AMQP 0.9.1 protocol reference (Section 1.5.2.3.)
194
+ # @see files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol reference (Section 1.5.2.3.)
195
195
  # @api public
196
196
  def flow(active = false, &block)
197
197
  @connection.send_frame(Protocol::Channel::Flow.encode(@id, active))
@@ -413,6 +413,7 @@ module AMQ
413
413
  def handle_close(channel_close)
414
414
  self.status = :closed
415
415
  self.connection.clear_frames_on(self.id)
416
+
416
417
  self.exec_callback_yielding_self(:error, channel_close)
417
418
 
418
419
  self.handle_connection_interruption(channel_close)
@@ -10,17 +10,17 @@ module AMQ
10
10
  module RabbitMQ
11
11
  module Basic
12
12
  module ConsumerMixin
13
-
13
+
14
14
  def on_cancel(&block)
15
15
  self.append_callback(:scancel, &block)
16
16
 
17
17
  self
18
18
  end # on_cancel(&block)
19
-
19
+
20
20
  def handle_cancel(basic_cancel)
21
21
  self.exec_callback(:scancel, basic_cancel)
22
22
  end # handle_cancel(basic_cancel)
23
-
23
+
24
24
  def self.included receiver
25
25
  receiver.handle(Protocol::Basic::Cancel) do |connection, method_frame|
26
26
  channel = connection.channels[method_frame.channel]
@@ -31,17 +31,17 @@ module AMQ
31
31
  consumer.handle_cancel(basic_cancel) if consumer
32
32
  end
33
33
  end
34
-
34
+
35
35
  end # ConsumerMixin
36
-
36
+
37
37
  module QueueMixin
38
-
38
+
39
39
  # @api public
40
40
  def on_cancel(&block)
41
41
  @default_consumer.on_cancel(&block)
42
42
  end # on_cancel(&block)
43
43
  end
44
-
44
+
45
45
  end # Basic
46
46
  end # RabbitMQ
47
47
  end # Extensions
@@ -2,13 +2,13 @@
2
2
 
3
3
  require "amq/client/async/extensions/rabbitmq/cancel"
4
4
 
5
- # Basic.Nack
5
+ # Basic.Cancel
6
6
  module AMQ
7
7
  module Client
8
8
  # backwards compatibility
9
9
  # @private
10
10
  Extensions = Async::Extensions unless defined?(Extensions)
11
-
11
+
12
12
  module Settings
13
13
  CLIENT_PROPERTIES[:capabilities] ||= {}
14
14
  CLIENT_PROPERTIES[:capabilities][:consumer_cancel_notify] = true
@@ -23,9 +23,10 @@ module AMQ
23
23
  :port => AMQ::Protocol::DEFAULT_PORT,
24
24
 
25
25
  # login
26
- :user => "guest",
27
- :pass => "guest",
28
- :vhost => "/",
26
+ :user => "guest",
27
+ :pass => "guest",
28
+ :auth_mechanism => "PLAIN",
29
+ :vhost => "/",
29
30
 
30
31
  # connection timeout
31
32
  :timeout => nil,
@@ -51,7 +52,7 @@ module AMQ
51
52
  :information => "http://github.com/ruby-amqp/amq-client",
52
53
  :version => AMQ::Client::VERSION
53
54
  }
54
-
55
+
55
56
  def self.client_properties
56
57
  @client_properties ||= CLIENT_PROPERTIES
57
58
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AMQ
4
4
  module Client
5
- VERSION = "1.0.0.pre2"
5
+ VERSION = "1.0.0"
6
6
  end
7
7
  end
@@ -15,7 +15,7 @@ describe "AMQ::Client::CoolioClient", "Basic.Return", :nojruby => true do
15
15
  coolio_amqp_connect do |client|
16
16
  channel = AMQ::Client::Channel.new(client, 1)
17
17
  channel.open do
18
- queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true)
18
+ queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, false)
19
19
 
20
20
  # need to delete the queue manually because we don't start consumption,
21
21
  # hence, no auto-delete
@@ -26,14 +26,14 @@ describe "AMQ::Client::CoolioClient", "Basic.Return", :nojruby => true do
26
26
  end
27
27
 
28
28
  messages.each do |message|
29
- exchange.publish(message, AMQ::Protocol::EMPTY_STRING, {}, false, true)
29
+ exchange.publish(message, AMQ::Protocol::EMPTY_STRING, {}, true, false)
30
30
  end
31
31
  end
32
32
 
33
33
  done(0.6) { @returned_messages.size == messages.size }
34
34
  end
35
35
 
36
- @returned_messages.should == ["NO_CONSUMERS"] * messages.size
36
+ @returned_messages.should == ["NO_ROUTE"] * messages.size
37
37
  end
38
38
  end
39
- end
39
+ end
@@ -34,7 +34,6 @@ describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do
34
34
  exchange.declare(true, false, false, false) do
35
35
  @callback_fired = true
36
36
  end
37
- delayed(0.1) { exchange.delete }
38
37
  done(0.5)
39
38
  end
40
39
  end
@@ -10,7 +10,7 @@ describe AMQ::Client::EventMachineClient, "Basic.Publish" do
10
10
  default_timeout 21.0
11
11
 
12
12
  context "when message size exceeds negotiated frame size" do
13
- let(:message) { "z" * 1024 * 1024 * 8 }
13
+ let(:message) { "z" * 1024 * 1024 * 2 }
14
14
 
15
15
  it "correctly frames things" do
16
16
  @received_messages = []
@@ -41,11 +41,13 @@ describe AMQ::Client::EventMachineClient, "Basic.Publish" do
41
41
  end
42
42
  end
43
43
 
44
- done(14.0) {
44
+ delayed(15.0) {
45
45
  # we don't care about the exact number, just the fact that there are
46
46
  # no UNEXPECTED_FRAME connection-level exceptions. MK.
47
47
  @received_messages.size.should > 10
48
48
  @received_messages.all? {|m| m == message}.should be_true
49
+
50
+ done
49
51
  }
50
52
  end
51
53
  end # em_amqp_connect
@@ -25,7 +25,7 @@ describe AMQ::Client::EventMachineClient, "Basic.Return" do
25
25
  end
26
26
 
27
27
  messages.each do |message|
28
- exchange.publish(message, AMQ::Protocol::EMPTY_STRING, {}, false, true)
28
+ exchange.publish(message, AMQ::Protocol::EMPTY_STRING, {}, true, false)
29
29
  end
30
30
  end
31
31
 
@@ -34,7 +34,7 @@ describe AMQ::Client::EventMachineClient, "Basic.Return" do
34
34
  }
35
35
  end
36
36
 
37
- @returned_messages.should == ["NO_CONSUMERS"] * messages.size
37
+ @returned_messages.should == ["NO_ROUTE"] * messages.size
38
38
  end
39
39
  end
40
- end
40
+ end
@@ -57,7 +57,7 @@ if mri?
57
57
  30.times do
58
58
  Thread.new do
59
59
  messages.each do |message|
60
- exchange.publish(message, queue.name, {}, false, true)
60
+ exchange.publish(message, queue.name, {}, false, false)
61
61
  end
62
62
  end
63
63
  end
@@ -76,4 +76,4 @@ if mri?
76
76
  end # it
77
77
  end # context
78
78
  end # describe
79
- end
79
+ end
@@ -10,6 +10,69 @@ describe AMQ::Client::EventMachineClient, "Connection.Start" do
10
10
  include EventedSpec::SpecHelper
11
11
  default_timeout 0.5
12
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
+
13
76
  context "with valid credentials" do
14
77
  it "should trigger the callback" do
15
78
  em do
@@ -0,0 +1,76 @@
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"
@@ -65,7 +65,6 @@ describe AMQ::Client::EventMachineClient, "Exchange.Declare" do
65
65
  exchange.declare(true, false, false, false) do
66
66
  @callback_fired = true
67
67
  end
68
- delayed(0.1) { exchange.delete }
69
68
  done(0.3)
70
69
  end
71
70
  end
metadata CHANGED
@@ -1,73 +1,68 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: amq-client
3
- version: !ruby/object:Gem::Version
4
- hash: -1880058884
5
- prerelease: 6
6
- segments:
7
- - 1
8
- - 0
9
- - 0
10
- - pre
11
- - 2
12
- version: 1.0.0.pre2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
13
6
  platform: ruby
14
- authors:
7
+ authors:
15
8
  - Jakub Stastny
16
9
  - Michael S. Klishin
17
10
  - Theo Hultberg
18
11
  - Mark Abramov
19
- autorequire:
12
+ autorequire:
20
13
  bindir: bin
21
14
  cert_chain: []
22
-
23
- date: 2012-07-03 00:00:00 Z
24
- dependencies:
25
- - !ruby/object:Gem::Dependency
15
+ date: 2013-03-23 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
26
18
  name: eventmachine
27
- prerelease: false
28
- requirement: &id001 !ruby/object:Gem::Requirement
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: !binary |-
24
+ MA==
29
25
  none: false
30
- requirements:
26
+ requirement: !ruby/object:Gem::Requirement
27
+ requirements:
31
28
  - - ">="
32
- - !ruby/object:Gem::Version
33
- hash: 3
34
- segments:
35
- - 0
36
- version: "0"
29
+ - !ruby/object:Gem::Version
30
+ version: !binary |-
31
+ MA==
32
+ none: false
33
+ prerelease: false
37
34
  type: :runtime
38
- version_requirements: *id001
39
- - !ruby/object:Gem::Dependency
35
+ - !ruby/object:Gem::Dependency
40
36
  name: amq-protocol
41
- prerelease: false
42
- requirement: &id002 !ruby/object:Gem::Requirement
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 1.2.0
43
42
  none: false
44
- requirements:
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
45
  - - ">="
46
- - !ruby/object:Gem::Version
47
- hash: 51
48
- segments:
49
- - 0
50
- - 9
51
- - 4
52
- version: 0.9.4
46
+ - !ruby/object:Gem::Version
47
+ version: 1.2.0
48
+ none: false
49
+ prerelease: false
53
50
  type: :runtime
54
- version_requirements: *id002
55
51
  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
56
- email:
57
- - stastny@101ideas.cz
52
+ email:
53
+ - !binary |-
54
+ c3Rhc3RueUAxMDFpZGVhcy5jeg==
58
55
  - michael@novemberain.com
59
56
  executables: []
60
-
61
57
  extensions: []
62
-
63
- extra_rdoc_files:
58
+ extra_rdoc_files:
64
59
  - README.textile
65
- files:
66
- - .gitignore
67
- - .gitmodules
68
- - .rspec
69
- - .travis.yml
70
- - .yardopts
60
+ files:
61
+ - ".gitignore"
62
+ - ".gitmodules"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - ".yardopts"
71
66
  - Gemfile
72
67
  - LICENSE
73
68
  - README.textile
@@ -153,6 +148,9 @@ files:
153
148
  - lib/amq/client/async/adapters/coolio.rb
154
149
  - lib/amq/client/async/adapters/event_machine.rb
155
150
  - lib/amq/client/async/adapters/eventmachine.rb
151
+ - lib/amq/client/async/auth_mechanism_adapter.rb
152
+ - lib/amq/client/async/auth_mechanism_adapter/external.rb
153
+ - lib/amq/client/async/auth_mechanism_adapter/plain.rb
156
154
  - lib/amq/client/async/callbacks.rb
157
155
  - lib/amq/client/async/channel.rb
158
156
  - lib/amq/client/async/consumer.rb
@@ -210,6 +208,7 @@ files:
210
208
  - spec/integration/eventmachine/concurrent_basic_publish_spec.rb
211
209
  - spec/integration/eventmachine/connection_close_spec.rb
212
210
  - spec/integration/eventmachine/connection_start_spec.rb
211
+ - spec/integration/eventmachine/consumer_cancellation_notification_spec.rb
213
212
  - spec/integration/eventmachine/exchange_declare_spec.rb
214
213
  - spec/integration/eventmachine/queue_declare_spec.rb
215
214
  - spec/integration/eventmachine/regressions/amqp_gem_issue66_spec.rb
@@ -227,39 +226,29 @@ files:
227
226
  - tasks.rb
228
227
  homepage: http://github.com/ruby-amqp/amq-client
229
228
  licenses: []
230
-
231
- post_install_message:
229
+ post_install_message:
232
230
  rdoc_options: []
233
-
234
- require_paths:
231
+ require_paths:
235
232
  - lib
236
- required_ruby_version: !ruby/object:Gem::Requirement
233
+ required_ruby_version: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: !binary |-
238
+ MA==
237
239
  none: false
238
- requirements:
240
+ required_rubygems_version: !ruby/object:Gem::Requirement
241
+ requirements:
239
242
  - - ">="
240
- - !ruby/object:Gem::Version
241
- hash: 3
242
- segments:
243
- - 0
244
- version: "0"
245
- required_rubygems_version: !ruby/object:Gem::Requirement
243
+ - !ruby/object:Gem::Version
244
+ version: !binary |-
245
+ MA==
246
246
  none: false
247
- requirements:
248
- - - ">"
249
- - !ruby/object:Gem::Version
250
- hash: 25
251
- segments:
252
- - 1
253
- - 3
254
- - 1
255
- version: 1.3.1
256
247
  requirements: []
257
-
258
248
  rubyforge_project: amq-client
259
249
  rubygems_version: 1.8.24
260
- signing_key:
250
+ signing_key:
261
251
  specification_version: 3
262
252
  summary: amq-client is a fully-featured, low-level AMQP 0.9.1 client
263
253
  test_files: []
264
-
265
- has_rdoc:
254
+ has_rdoc: