bunny 0.9.0.pre5 → 0.9.0.pre6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ playground/*
18
18
  *.org
19
19
  repl-*
20
20
  debug/*
21
+ *.dump
data/ChangeLog.md CHANGED
@@ -1,3 +1,49 @@
1
+ ## Changes between Bunny 0.9.0.pre5 and 0.9.0.pre6
2
+
3
+ ### Automatic Network Failure Recovery
4
+
5
+ Automatic Network Failure Recovery is a new Bunny feature that was earlier
6
+ impemented and vetted out in [amqp gem](http://rubyamqp.info). What it does
7
+ is, when a network activity loop detects an issue, it will try to
8
+ periodically recover [first TCP, then] AMQP 0.9.1 connection, reopen
9
+ all channels, recover all exchanges, queues, bindings and consumers
10
+ on those channels (to be clear: this only includes entities and consumers added via
11
+ Bunny).
12
+
13
+ Publishers and consumers will continue operating shortly after the network
14
+ connection recovers.
15
+
16
+ Learn more in the [Error Handling and Recovery](http://rubybunny.info/articles/error_handling.html)
17
+ documentation guide.
18
+
19
+ ### Confirms Listeners
20
+
21
+ Bunny now supports listeners (callbacks) on
22
+
23
+ ``` ruby
24
+ ch.confirm_select do |delivery_tag, multiple, nack|
25
+ # handle confirms (e.g. perform retries) here
26
+ end
27
+ ```
28
+
29
+ Contributed by Greg Brockman.
30
+
31
+ ### Publisher Confirms Improvements
32
+
33
+ Publisher confirms implementation now uses non-strict equality (`<=`) for
34
+ cases when multiple messages are confirmed by RabbitMQ at once.
35
+
36
+ `Bunny::Channel#unconfirmed_set` is now part of the public API that lets
37
+ developers access unconfirmed delivery tags to perform retries and such.
38
+
39
+ Contributed by Greg Brockman.
40
+
41
+ ### Publisher Confirms Concurrency Fix
42
+
43
+ `Bunny::Channel#wait_for_confirms` will now correctly block the calling
44
+ thread until all pending confirms are received.
45
+
46
+
1
47
  ## Changes between Bunny 0.9.0.pre4 and 0.9.0.pre5
2
48
 
3
49
  ### Channel Errors Reset
data/README.md CHANGED
@@ -37,7 +37,7 @@ gem install bunny --pre
37
37
  To use Bunny 0.9.x in a project managed with Bundler:
38
38
 
39
39
  ``` ruby
40
- gem "bunny", ">= 0.9.0.pre4" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
40
+ gem "bunny", ">= 0.9.0.pre6" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
41
41
  ```
42
42
 
43
43
 
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ begin
12
+ conn = Bunny.new("amqp://guest8we78w7e8:guest2378278@127.0.0.1")
13
+ conn.start
14
+ rescue Bunny::PossibleAuthenticationFailureError => e
15
+ puts "Could not authenticate as #{conn.username}"
16
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch = conn.create_channel
15
+ x = ch.topic("bunny.examples.recovery.topic", :durable => false)
16
+ q = ch.queue("bunny.examples.recovery.client_named_queue1", :durable => false)
17
+
18
+ q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def")
19
+
20
+ q.subscribe do |delivery_info, metadata, payload|
21
+ puts "Consumed #{payload}"
22
+ end
23
+
24
+ loop do
25
+ sleep 3
26
+ puts "Tick"
27
+ x.publish(rand.to_s, :routing_key => ["abc", "def", "ghi", "xyz"].sample)
28
+ end
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch1 = conn.create_channel
15
+ x1 = ch1.topic("bunny.examples.recovery.e1", :durable => false)
16
+ q1 = ch1.queue("bunny.examples.recovery.q1", :durable => false)
17
+
18
+ q1.bind(x1, :routing_key => "abc").bind(x1, :routing_key => "def")
19
+
20
+ ch2 = conn.create_channel
21
+ x2 = ch2.topic("bunny.examples.recovery.e2", :durable => false)
22
+ q2 = ch2.queue("bunny.examples.recovery.q2", :durable => false)
23
+
24
+ q2.bind(x2, :routing_key => "abc").bind(x2, :routing_key => "def")
25
+
26
+ q1.subscribe do |delivery_info, metadata, payload|
27
+ puts "Consumed #{payload} at stage one"
28
+ x2.publish(payload, :routing_key => ["abc", "def", "xyz"].sample)
29
+ end
30
+
31
+ q2.subscribe do |delivery_info, metadata, payload|
32
+ puts "Consumed #{payload} at stage two"
33
+ end
34
+
35
+ loop do
36
+ sleep 3
37
+ rk = ["abc", "def", "ghi", "xyz"].sample
38
+ puts "Publishing with routing key #{rk}"
39
+ x1.publish(rand.to_s, :routing_key => rk)
40
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch = conn.create_channel
15
+ x = ch.topic("bunny.examples.recovery.topic", :durable => false)
16
+ q = ch.queue("", :durable => false)
17
+
18
+ q.bind(x, :routing_key => "abc").bind(x, :routing_key => "def")
19
+
20
+ q.subscribe do |delivery_info, metadata, payload|
21
+ puts "Consumed #{payload}"
22
+ end
23
+
24
+ loop do
25
+ sleep 3
26
+ puts "Tick"
27
+ x.publish(rand.to_s, :routing_key => ["abc", "def", "ghi", "xyz"].sample)
28
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ begin
15
+ ch1 = conn.create_channel
16
+ ch1.queue_delete("queue_that_should_not_exist#{rand}")
17
+ rescue Bunny::NotFound => e
18
+ puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
19
+ end
20
+
21
+
22
+ begin
23
+ ch2 = conn.create_channel
24
+ q = "bunny.examples.recovery.q#{rand}"
25
+
26
+ ch2.queue_declare(q, :durable => false)
27
+ ch2.queue_declare(q, :durable => true)
28
+ rescue Bunny::PreconditionFailed => e
29
+ puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
30
+ ensure
31
+ conn.create_channel.queue_delete(q)
32
+ end
33
+
34
+ puts "Disconnecting..."
35
+ conn.close
@@ -8,6 +8,9 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
8
 
9
9
  require 'bunny'
10
10
 
11
- conn = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com")
12
- conn.start
13
-
11
+ begin
12
+ conn = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com")
13
+ conn.start
14
+ rescue Bunny::TCPConnectionFailed => e
15
+ puts "Connection to #{conn.hostname} failed"
16
+ end
data/lib/bunny/channel.rb CHANGED
@@ -18,7 +18,7 @@ module Bunny
18
18
  #
19
19
 
20
20
  attr_accessor :id, :connection, :status, :work_pool
21
- attr_reader :next_publish_seq_no, :queues, :exchanges
21
+ attr_reader :next_publish_seq_no, :queues, :exchanges, :unconfirmed_set, :consumers
22
22
 
23
23
 
24
24
  def initialize(connection = nil, id = nil, work_pool = ConsumerWorkPool.new(1))
@@ -215,6 +215,8 @@ module Bunny
215
215
  end
216
216
  raise_if_continuation_resulted_in_a_channel_error!
217
217
 
218
+ @prefetch_count = prefetch_count
219
+
218
220
  @last_basic_qos_ok
219
221
  end
220
222
 
@@ -572,15 +574,17 @@ module Bunny
572
574
 
573
575
  # confirm.*
574
576
 
575
- def confirm_select
577
+ def confirm_select(callback=nil)
576
578
  raise_if_no_longer_open!
577
579
 
578
580
  if @next_publish_seq_no == 0
579
- @confirms_continuations = []
581
+ @confirms_continuations = ::Queue.new
580
582
  @unconfirmed_set = Set.new
581
583
  @next_publish_seq_no = 1
582
584
  end
583
585
 
586
+ @confirms_callback = callback
587
+
584
588
  @connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, false))
585
589
  Bunny::Timer.timeout(1, ClientTimeout) do
586
590
  @last_confirm_select_ok = @continuations.pop
@@ -597,6 +601,47 @@ module Bunny
597
601
  end
598
602
 
599
603
 
604
+ #
605
+ # Recovery
606
+ #
607
+
608
+ def recover_from_network_failure
609
+ # puts "Recovering channel #{@id} from network failure..."
610
+ recover_prefetch_setting
611
+ recover_exchanges
612
+ # this includes recovering bindings
613
+ recover_queues
614
+ recover_consumers
615
+ end
616
+
617
+ def recover_prefetch_setting
618
+ basic_qos(@prefetch_count) if @prefetch_count
619
+ end
620
+
621
+ def recover_exchanges
622
+ @exchanges.values.dup.each do |x|
623
+ x.recover_from_network_failure
624
+ end
625
+ end
626
+
627
+ def recover_queues
628
+ @queues.values.dup.each do |q|
629
+ q.recover_from_network_failure
630
+ end
631
+ end
632
+
633
+ def recover_consumers
634
+ unless @consumers.empty?
635
+ @work_pool = ConsumerWorkPool.new(@work_pool.size)
636
+ @work_pool.start
637
+ end
638
+ @consumers.values.dup.each do |c|
639
+ c.recover_from_network_failure
640
+ end
641
+ end
642
+
643
+
644
+
600
645
  #
601
646
  # Implementation
602
647
  #
@@ -660,10 +705,8 @@ module Bunny
660
705
  when AMQ::Protocol::Confirm::SelectOk then
661
706
  @continuations.push(method)
662
707
  when AMQ::Protocol::Basic::Ack then
663
- # TODO: implement confirm listeners
664
708
  handle_ack_or_nack(method.delivery_tag, method.multiple, false)
665
709
  when AMQ::Protocol::Basic::Nack then
666
- # TODO: implement confirm listeners
667
710
  handle_ack_or_nack(method.delivery_tag, method.multiple, true)
668
711
  when AMQ::Protocol::Channel::Close then
669
712
  # puts "Exception on channel #{@id}: #{method.reply_code} #{method.reply_text}"
@@ -711,7 +754,7 @@ module Bunny
711
754
 
712
755
  def handle_ack_or_nack(delivery_tag, multiple, nack)
713
756
  if multiple
714
- @unconfirmed_set.delete_if { |i| i < delivery_tag }
757
+ @unconfirmed_set.delete_if { |i| i <= delivery_tag }
715
758
  else
716
759
  @unconfirmed_set.delete(delivery_tag)
717
760
  end
@@ -720,6 +763,8 @@ module Bunny
720
763
  @only_acks_received = (@only_acks_received && !nack)
721
764
 
722
765
  @confirms_continuations.push(true) if @unconfirmed_set.empty?
766
+
767
+ @confirms_callback.call(delivery_tag, multiple, nack) if @confirms_callback
723
768
  end
724
769
  end
725
770
 
@@ -730,6 +775,14 @@ module Bunny
730
775
  @work_pool.start unless @work_pool.started?
731
776
  end
732
777
 
778
+ def maybe_pause_consumer_work_pool!
779
+ @work_pool.pause if @work_pool && @work_pool.started?
780
+ end
781
+
782
+ def maybe_kill_consumer_work_pool!
783
+ @work_pool.kill if @work_pool && @work_pool.started?
784
+ end
785
+
733
786
  def read_next_frame(options = {})
734
787
  @connection.read_next_frame(options = {})
735
788
  end
@@ -739,11 +792,15 @@ module Bunny
739
792
  def synchronize(&block)
740
793
  @publishing_mutex.synchronize(&block)
741
794
  end
742
-
795
+
743
796
  def deregister_queue(queue)
744
797
  @queues.delete(queue.name)
745
798
  end
746
799
 
800
+ def deregister_queue_named(name)
801
+ @queues.delete(name)
802
+ end
803
+
747
804
  def register_queue(queue)
748
805
  @queues[queue.name] = queue
749
806
  end
@@ -751,7 +808,7 @@ module Bunny
751
808
  def find_queue(name)
752
809
  @queues[name]
753
810
  end
754
-
811
+
755
812
  def deregister_exchange(exchange)
756
813
  @exchanges.delete(exchange.name)
757
814
  end
@@ -58,5 +58,13 @@ module Bunny
58
58
  def inspect
59
59
  "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>"
60
60
  end
61
+
62
+ #
63
+ # Recovery
64
+ #
65
+
66
+ def recover_from_network_failure
67
+ @channel.basic_consume_with(self)
68
+ end
61
69
  end
62
70
  end
@@ -9,6 +9,8 @@ module Bunny
9
9
  # API
10
10
  #
11
11
 
12
+ attr_reader :size
13
+
12
14
  def initialize(size = 1)
13
15
  @size = size
14
16
  @queue = ::Queue.new
@@ -46,6 +48,18 @@ module Bunny
46
48
  @threads.each { |t| t.join }
47
49
  end
48
50
 
51
+ def pause
52
+ @threads.each { |t| t.stop }
53
+ end
54
+
55
+ def resume
56
+ @threads.each { |t| t.run }
57
+ end
58
+
59
+ def kill
60
+ @threads.each { |t| t.kill }
61
+ end
62
+
49
63
  protected
50
64
 
51
65
  def run_loop
@@ -16,9 +16,9 @@ module Bunny
16
16
  class ConnectionClosedError < StandardError
17
17
  def initialize(frame)
18
18
  if frame.respond_to?(:method_class)
19
- super("Trying to send frame through a closed connection. Frame is #{frame.inspect}")
20
- else
21
19
  super("Trying to send frame through a closed connection. Frame is #{frame.inspect}, method class is #{frame.method_class}")
20
+ else
21
+ super("Trying to send frame through a closed connection. Frame is #{frame.inspect}")
22
22
  end
23
23
  end
24
24
  end
@@ -44,7 +44,6 @@ module Bunny
44
44
  ConnectionError = TCPConnectionFailed
45
45
  ServerDownError = TCPConnectionFailed
46
46
 
47
- # TODO
48
47
  class ForcedChannelCloseError < StandardError; end
49
48
  class ForcedConnectionCloseError < StandardError; end
50
49
  class MessageError < StandardError; end
@@ -131,4 +130,10 @@ module Bunny
131
130
 
132
131
  class ChannelError < ConnectionLevelException
133
132
  end
133
+
134
+ class InvalidCommand < ConnectionLevelException
135
+ end
136
+
137
+ class UnexpectedFrame < ConnectionLevelException
138
+ end
134
139
  end
@@ -126,6 +126,11 @@ module Bunny
126
126
  self
127
127
  end
128
128
 
129
+ def recover_from_network_failure
130
+ # puts "Recovering exchange #{@name} from network failure"
131
+ declare! unless predefined?
132
+ end
133
+
129
134
 
130
135
  #
131
136
  # Implementation
@@ -139,6 +144,11 @@ module Bunny
139
144
  end
140
145
  end
141
146
 
147
+ # @return [Boolean] true if this exchange is a pre-defined one (amq.direct, amq.fanout, amq.match and so on)
148
+ def predefined?
149
+ @name && ((@name == AMQ::Protocol::EMPTY_STRING) || !!(@name =~ /^amq\./i))
150
+ end # predefined?
151
+
142
152
  protected
143
153
 
144
154
  # @private
@@ -18,10 +18,15 @@ module Bunny
18
18
  @thread = Thread.new(&method(:run_loop))
19
19
  end
20
20
 
21
+ def resume
22
+ start
23
+ end
24
+
25
+
21
26
  def run_loop
22
27
  loop do
23
28
  begin
24
- break if @stopping
29
+ break if @stopping || @network_is_down
25
30
 
26
31
  frame = @transport.read_next_frame
27
32
  @session.signal_activity!
@@ -50,6 +55,10 @@ module Bunny
50
55
  # should happen per operation and not in this loop
51
56
  rescue Errno::EBADF => ebadf
52
57
  # ignored, happens when we loop after the transport has already been closed
58
+ rescue AMQ::Protocol::EmptyResponseError, IOError, Errno::EPIPE, Errno::EAGAIN => e
59
+ puts "Exception in the main loop: #{e.class.name}"
60
+ @network_is_down = true
61
+ @session.handle_network_failure(e)
53
62
  rescue Exception => e
54
63
  puts e.class.name
55
64
  puts e.message
data/lib/bunny/queue.rb CHANGED
@@ -26,6 +26,8 @@ module Bunny
26
26
  @auto_delete = @options[:auto_delete]
27
27
  @arguments = @options[:arguments]
28
28
 
29
+ @bindings = Array.new
30
+
29
31
  @default_consumer = nil
30
32
 
31
33
  declare! unless opts[:no_declare]
@@ -65,12 +67,33 @@ module Bunny
65
67
  def bind(exchange, opts = {})
66
68
  @channel.queue_bind(@name, exchange, opts)
67
69
 
70
+ exchange_name = if exchange.respond_to?(:name)
71
+ exchange.name
72
+ else
73
+ exchange
74
+ end
75
+
76
+
77
+ # store bindings for automatic recovery. We need to be very careful to
78
+ # not cause an infinite rebinding loop here when we recover. MK.
79
+ binding = { :exchange => exchange_name, :routing_key => (opts[:routing_key] || opts[:key]), :arguments => opts[:arguments] }
80
+ @bindings.push(binding) unless @bindings.include?(binding)
81
+
68
82
  self
69
83
  end
70
84
 
71
85
  def unbind(exchange, opts = {})
72
86
  @channel.queue_unbind(@name, exchange, opts)
73
87
 
88
+ exchange_name = if exchange.respond_to?(:name)
89
+ exchange.name
90
+ else
91
+ exchange
92
+ end
93
+
94
+
95
+ @bindings.delete_if { |b| b[:exchange] == exchange_name && b[:routing_key] == (opts[:routing_key] || opts[:key]) && b[:arguments] == opts[:arguments] }
96
+
74
97
  self
75
98
  end
76
99
 
@@ -84,11 +107,12 @@ module Bunny
84
107
 
85
108
  ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag)
86
109
  consumer = Consumer.new(@channel,
87
- @name,
110
+ self,
88
111
  ctag,
89
112
  !opts[:ack],
90
113
  opts[:exclusive],
91
114
  opts[:arguments])
115
+ puts "Added consumer #{ctag} on queue #{@name}"
92
116
  consumer.on_delivery(&block)
93
117
  consumer.on_cancellation(&opts[:on_cancellation]) if opts[:on_cancellation]
94
118
 
@@ -172,6 +196,34 @@ module Bunny
172
196
  s[:consumer_count]
173
197
  end
174
198
 
199
+ #
200
+ # Recovery
201
+ #
202
+
203
+ def recover_from_network_failure
204
+ # puts "Recovering queue #{@name} from network failure"
205
+
206
+ if self.server_named?
207
+ old_name = @name.dup
208
+ @name = AMQ::Protocol::EMPTY_STRING
209
+
210
+ @channel.deregister_queue_named(old_name)
211
+ end
212
+
213
+ declare!
214
+ begin
215
+ @channel.register_queue(self)
216
+ rescue Exception => e
217
+ puts "Caught #{e.inspect} while registering #{@name}!"
218
+ end
219
+ recover_bindings
220
+ end
221
+
222
+ def recover_bindings
223
+ @bindings.each do |b|
224
+ self.bind(b[:exchange], b)
225
+ end
226
+ end
175
227
 
176
228
 
177
229
  #
data/lib/bunny/session.rb CHANGED
@@ -111,13 +111,15 @@ module Bunny
111
111
  alias ssl? uses_ssl?
112
112
 
113
113
  def start
114
- @status = :connecting
114
+ @continuations = ::Queue.new
115
+ @status = :connecting
115
116
 
116
117
  self.initialize_transport
117
118
 
118
119
  self.init_connection
119
120
  self.open_connection
120
121
 
122
+ @event_loop = nil
121
123
  self.start_main_loop
122
124
 
123
125
  @default_channel = self.create_channel
@@ -233,9 +235,7 @@ module Bunny
233
235
  def close_connection(sync = true)
234
236
  @transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0))
235
237
 
236
- if @heartbeat_sender
237
- @heartbeat_sender.stop
238
- end
238
+ maybe_shutdown_heartbeat_sender
239
239
  @status = :not_connected
240
240
 
241
241
  if sync
@@ -303,6 +303,60 @@ module Bunny
303
303
  end
304
304
  end
305
305
 
306
+ def handle_network_failure(exception)
307
+ if !recovering_from_network_failure?
308
+ @recovering_from_network_failure = true
309
+ if recoverable_network_failure?(exception)
310
+ # puts "Recovering from a network failure..."
311
+ @channels.each do |n, ch|
312
+ ch.maybe_kill_consumer_work_pool!
313
+ end
314
+ maybe_shutdown_heartbeat_sender
315
+
316
+ recover_from_network_failure
317
+ else
318
+ # TODO: investigate if we can be a bit smarter here. MK.
319
+ end
320
+ end
321
+ end
322
+
323
+ def recoverable_network_failure?(exception)
324
+ # TODO: investigate if we can be a bit smarter here. MK.
325
+ true
326
+ end
327
+
328
+ def recovering_from_network_failure?
329
+ @recovering_from_network_failure
330
+ end
331
+
332
+ def recover_from_network_failure
333
+ begin
334
+ # puts "About to start recovery..."
335
+ start
336
+
337
+ if open?
338
+ @recovering_from_network_failure = false
339
+
340
+ recover_channels
341
+ end
342
+ rescue TCPConnectionFailed, AMQ::Protocol::EmptyResponseError => e
343
+ # puts "TCP connection failed, reconnecting in 5 seconds"
344
+ sleep 5.0
345
+ retry if recoverable_network_failure?(e)
346
+ end
347
+ end
348
+
349
+ def recover_channels
350
+ # default channel is reopened right after connection
351
+ # negotiation is completed, so make sure we do not try to open
352
+ # it twice. MK.
353
+ @channels.reject { |n, ch| ch == @default_channel }.each do |n, ch|
354
+ ch.open
355
+
356
+ ch.recover_from_network_failure
357
+ end
358
+ end
359
+
306
360
  def send_raw(*args)
307
361
  @transport.write(*args)
308
362
  end
@@ -311,8 +365,14 @@ module Bunny
311
365
  case frame
312
366
  when AMQ::Protocol::Connection::Close then
313
367
  klass = case frame.reply_code
368
+ when 503 then
369
+ InvalidCommand
314
370
  when 504 then
315
371
  ChannelError
372
+ when 504 then
373
+ UnexpectedFrame
374
+ else
375
+ raise "Unknown reply code: #{frame.reply_code}, text: #{frame.reply_text}"
316
376
  end
317
377
 
318
378
  klass.new("Connection-level error: #{frame.reply_text}", self, frame)
@@ -488,13 +548,18 @@ module Bunny
488
548
  end
489
549
 
490
550
  def initialize_heartbeat_sender
551
+ # puts "Initializing heartbeat sender..."
491
552
  @heartbeat_sender = HeartbeatSender.new(@transport)
492
553
  @heartbeat_sender.start(@heartbeat)
493
554
  end
494
555
 
556
+ def maybe_shutdown_heartbeat_sender
557
+ @heartbeat_sender.stop if @heartbeat_sender
558
+ end
559
+
495
560
 
496
561
  def initialize_transport
497
- @transport = Transport.new(@host, @port, @opts)
562
+ @transport = Transport.new(self, @host, @port, @opts)
498
563
  end
499
564
 
500
565
  # Sends AMQ protocol header (also known as preamble).
@@ -14,12 +14,13 @@ module Bunny
14
14
  DEFAULT_CONNECTION_TIMEOUT = 5.0
15
15
 
16
16
 
17
- attr_reader :host, :port, :socket, :connect_timeout
17
+ attr_reader :session, :host, :port, :socket, :connect_timeout
18
18
 
19
- def initialize(host, port, opts)
20
- @host = host
21
- @port = port
22
- @opts = opts
19
+ def initialize(session, host, port, opts)
20
+ @session = session
21
+ @host = host
22
+ @port = port
23
+ @opts = opts
23
24
 
24
25
  @ssl = opts[:ssl] || false
25
26
  @ssl_cert = opts[:ssl_cert]
@@ -61,24 +62,18 @@ module Bunny
61
62
  # @raise [ClientTimeout]
62
63
  def write(*args)
63
64
  begin
64
- raise Bunny::ConnectionError.new('No connection - socket has not been created', @host, @port) if !@socket
65
+ raise Bunny::ConnectionError.new("No connection: socket is nil. ", @host, @port) if !@socket
65
66
  if @read_write_timeout
66
67
  Bunny::Timer.timeout(@read_write_timeout, Bunny::ClientTimeout) do
67
- @socket.write(*args)
68
+ @socket.write(*args) if open?
68
69
  end
69
70
  else
70
- @socket.write(*args)
71
+ @socket.write(*args) if open?
71
72
  end
72
73
  rescue Errno::EPIPE, Errno::EAGAIN, Bunny::ClientTimeout, IOError => e
73
74
  close
74
75
 
75
- m = case e
76
- when String then
77
- e
78
- when Exception then
79
- e.message
80
- end
81
- raise Bunny::ConnectionError.new(m, @host, @port)
76
+ @session.handle_network_failure(e)
82
77
  end
83
78
  end
84
79
  alias send_raw write
@@ -97,7 +92,7 @@ module Bunny
97
92
  end
98
93
 
99
94
  def flush
100
- @socket.flush
95
+ @socket.flush if @socket
101
96
  end
102
97
 
103
98
  def read_fully(*args)
@@ -135,13 +130,31 @@ module Bunny
135
130
  # @private
136
131
  def send_frame(frame)
137
132
  if closed?
138
- raise ConnectionClosedError.new(frame)
133
+ @session.handle_network_failure(ConnectionClosedError.new(frame))
139
134
  else
140
135
  send_raw(frame.encode)
141
136
  end
142
137
  end
143
138
 
144
139
 
140
+ def self.reacheable?(host, port, timeout)
141
+ begin
142
+ s = Bunny::Socket.open(host, port,
143
+ :socket_timeout => timeout)
144
+
145
+ true
146
+ rescue SocketError, Timeout::Error => e
147
+ false
148
+ ensure
149
+ s.close if s
150
+ end
151
+ end
152
+
153
+ def self.ping!(host, port, timeout)
154
+ raise ConnectionTimeout.new("#{host}:#{port} is unreachable") if !reacheable?(host, port, timeout)
155
+ end
156
+
157
+
145
158
  protected
146
159
 
147
160
  def initialize_socket
data/lib/bunny/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Bunny
4
- VERSION = "0.9.0.pre5"
4
+ VERSION = "0.9.0.pre6"
5
5
  end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe Bunny::Transport, ".reachable?" do
4
+ it "returns true for google.com, 80" do
5
+ Bunny::Transport.reacheable?("google.com", 80, 1).should be_true
6
+ end
7
+
8
+ it "returns true for google.com, 8088" do
9
+ Bunny::Transport.reacheable?("google.com", 8088, 1).should be_false
10
+ end
11
+
12
+ it "returns false for google1237982792837.com, 8277" do
13
+ Bunny::Transport.reacheable?("google1237982792837.com", 8277, 1).should be_false
14
+ end
15
+ end
metadata CHANGED
@@ -1,213 +1,213 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: bunny
3
- version: !ruby/object:Gem::Version
4
- version: 0.9.0.pre5
3
+ version: !ruby/object:Gem::Version
5
4
  prerelease: 6
5
+ version: 0.9.0.pre6
6
6
  platform: ruby
7
- authors:
8
- - Chris Duncan
9
- - Eric Lindvall
10
- - Jakub Stastny aka botanicus
11
- - Michael S. Klishin
12
- - Stefan Kaes
13
- autorequire:
7
+ authors:
8
+ - Chris Duncan
9
+ - Eric Lindvall
10
+ - Jakub Stastny aka botanicus
11
+ - Michael S. Klishin
12
+ - Stefan Kaes
13
+ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-01-09 00:00:00.000000000 Z
17
- dependencies:
18
- - !ruby/object:Gem::Dependency
19
- name: amq-protocol
20
- version_requirements: !ruby/object:Gem::Requirement
21
- requirements:
22
- - - ! '>='
23
- - !ruby/object:Gem::Version
24
- version: 1.0.1
25
- none: false
26
- requirement: !ruby/object:Gem::Requirement
27
- requirements:
28
- - - ! '>='
29
- - !ruby/object:Gem::Version
30
- version: 1.0.1
31
- none: false
32
- prerelease: false
33
- type: :runtime
16
+
17
+ date: 2013-01-14 00:00:00 Z
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: amq-protocol
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 1.0.1
28
+ type: :runtime
29
+ version_requirements: *id001
34
30
  description: Easy to use synchronous Ruby client for RabbitMQ
35
- email:
36
- - !binary |-
37
- Y2VsbGRlZUBnbWFpbC5jb20=
38
- - !binary |-
39
- ZXJpY0A1c3RvcHMuY29t
40
- - !binary |-
41
- c3Rhc3RueUAxMDFpZGVhcy5jeg==
42
- - !binary |-
43
- bWljaGFlbEBub3ZlbWJlcmFpbi5jb20=
44
- - !binary |-
45
- c2thZXNAcmFpbHNleHByZXNzLmRl
31
+ email:
32
+ - celldee@gmail.com
33
+ - eric@5stops.com
34
+ - stastny@101ideas.cz
35
+ - michael@novemberain.com
36
+ - skaes@railsexpress.de
46
37
  executables: []
38
+
47
39
  extensions: []
48
- extra_rdoc_files:
49
- - README.md
50
- files:
51
- - .gitignore
52
- - .rspec
53
- - .travis.yml
54
- - .yardopts
55
- - ChangeLog.md
56
- - Gemfile
57
- - LICENSE
58
- - README.md
59
- - bin/ci/before_build.sh
60
- - bunny.gemspec
61
- - examples/connection/heartbeat.rb
62
- - examples/connection/unknown_host.rb
63
- - examples/guides/exchanges/direct_exchange_routing.rb
64
- - examples/guides/exchanges/fanout_exchange_routing.rb
65
- - examples/guides/exchanges/headers_exchange_routing.rb
66
- - examples/guides/exchanges/mandatory_messages.rb
67
- - examples/guides/extensions/alternate_exchange.rb
68
- - examples/guides/extensions/basic_nack.rb
69
- - examples/guides/extensions/consumer_cancellation_notification.rb
70
- - examples/guides/extensions/dead_letter_exchange.rb
71
- - examples/guides/extensions/exchange_to_exchange_bindings.rb
72
- - examples/guides/extensions/per_message_ttl.rb
73
- - examples/guides/extensions/per_queue_message_ttl.rb
74
- - examples/guides/extensions/publisher_confirms.rb
75
- - examples/guides/extensions/queue_lease.rb
76
- - examples/guides/extensions/sender_selected_distribution.rb
77
- - examples/guides/getting_started/blabbr.rb
78
- - examples/guides/getting_started/hello_world.rb
79
- - examples/guides/getting_started/weathr.rb
80
- - examples/guides/queues/redeliveries.rb
81
- - lib/bunny.rb
82
- - lib/bunny/authentication/credentials_encoder.rb
83
- - lib/bunny/authentication/external_mechanism_encoder.rb
84
- - lib/bunny/authentication/plain_mechanism_encoder.rb
85
- - lib/bunny/channel.rb
86
- - lib/bunny/channel_id_allocator.rb
87
- - lib/bunny/compatibility.rb
88
- - lib/bunny/concurrent/condition.rb
89
- - lib/bunny/consumer.rb
90
- - lib/bunny/consumer_tag_generator.rb
91
- - lib/bunny/consumer_work_pool.rb
92
- - lib/bunny/delivery_info.rb
93
- - lib/bunny/exceptions.rb
94
- - lib/bunny/exchange.rb
95
- - lib/bunny/framing.rb
96
- - lib/bunny/heartbeat_sender.rb
97
- - lib/bunny/main_loop.rb
98
- - lib/bunny/message_properties.rb
99
- - lib/bunny/queue.rb
100
- - lib/bunny/return_info.rb
101
- - lib/bunny/session.rb
102
- - lib/bunny/socket.rb
103
- - lib/bunny/system_timer.rb
104
- - lib/bunny/transport.rb
105
- - lib/bunny/version.rb
106
- - spec/compatibility/queue_declare_spec.rb
107
- - spec/higher_level_api/integration/basic_ack_spec.rb
108
- - spec/higher_level_api/integration/basic_cancel_spec.rb
109
- - spec/higher_level_api/integration/basic_consume_spec.rb
110
- - spec/higher_level_api/integration/basic_get_spec.rb
111
- - spec/higher_level_api/integration/basic_nack_spec.rb
112
- - spec/higher_level_api/integration/basic_publish_spec.rb
113
- - spec/higher_level_api/integration/basic_qos_spec.rb
114
- - spec/higher_level_api/integration/basic_recover_spec.rb
115
- - spec/higher_level_api/integration/basic_reject_spec.rb
116
- - spec/higher_level_api/integration/basic_return_spec.rb
117
- - spec/higher_level_api/integration/channel_close_spec.rb
118
- - spec/higher_level_api/integration/channel_flow_spec.rb
119
- - spec/higher_level_api/integration/channel_open_spec.rb
120
- - spec/higher_level_api/integration/channel_open_stress_spec.rb
121
- - spec/higher_level_api/integration/confirm_select_spec.rb
122
- - spec/higher_level_api/integration/connection_spec.rb
123
- - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
124
- - spec/higher_level_api/integration/dead_lettering_spec.rb
125
- - spec/higher_level_api/integration/exchange_bind_spec.rb
126
- - spec/higher_level_api/integration/exchange_declare_spec.rb
127
- - spec/higher_level_api/integration/exchange_delete_spec.rb
128
- - spec/higher_level_api/integration/exchange_unbind_spec.rb
129
- - spec/higher_level_api/integration/message_properties_access_spec.rb
130
- - spec/higher_level_api/integration/publisher_confirms_spec.rb
131
- - spec/higher_level_api/integration/queue_bind_spec.rb
132
- - spec/higher_level_api/integration/queue_declare_spec.rb
133
- - spec/higher_level_api/integration/queue_delete_spec.rb
134
- - spec/higher_level_api/integration/queue_purge_spec.rb
135
- - spec/higher_level_api/integration/queue_unbind_spec.rb
136
- - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
137
- - spec/higher_level_api/integration/tx_commit_spec.rb
138
- - spec/higher_level_api/integration/tx_rollback_spec.rb
139
- - spec/issues/issue78_spec.rb
140
- - spec/issues/issue83_spec.rb
141
- - spec/lower_level_api/integration/basic_cancel_spec.rb
142
- - spec/lower_level_api/integration/basic_consume_spec.rb
143
- - spec/spec_helper.rb
144
- - spec/unit/bunny_spec.rb
145
- - spec/unit/concurrent/condition_spec.rb
40
+
41
+ extra_rdoc_files:
42
+ - README.md
43
+ files:
44
+ - .gitignore
45
+ - .rspec
46
+ - .travis.yml
47
+ - .yardopts
48
+ - ChangeLog.md
49
+ - Gemfile
50
+ - LICENSE
51
+ - README.md
52
+ - bin/ci/before_build.sh
53
+ - bunny.gemspec
54
+ - examples/connection/authentication_failure.rb
55
+ - examples/connection/automatic_recovery_with_client_named_queues.rb
56
+ - examples/connection/automatic_recovery_with_multiple_consumers.rb
57
+ - examples/connection/automatic_recovery_with_server_named_queues.rb
58
+ - examples/connection/channel_level_exception.rb
59
+ - examples/connection/heartbeat.rb
60
+ - examples/connection/unknown_host.rb
61
+ - examples/guides/exchanges/direct_exchange_routing.rb
62
+ - examples/guides/exchanges/fanout_exchange_routing.rb
63
+ - examples/guides/exchanges/headers_exchange_routing.rb
64
+ - examples/guides/exchanges/mandatory_messages.rb
65
+ - examples/guides/extensions/alternate_exchange.rb
66
+ - examples/guides/extensions/basic_nack.rb
67
+ - examples/guides/extensions/consumer_cancellation_notification.rb
68
+ - examples/guides/extensions/dead_letter_exchange.rb
69
+ - examples/guides/extensions/exchange_to_exchange_bindings.rb
70
+ - examples/guides/extensions/per_message_ttl.rb
71
+ - examples/guides/extensions/per_queue_message_ttl.rb
72
+ - examples/guides/extensions/publisher_confirms.rb
73
+ - examples/guides/extensions/queue_lease.rb
74
+ - examples/guides/extensions/sender_selected_distribution.rb
75
+ - examples/guides/getting_started/blabbr.rb
76
+ - examples/guides/getting_started/hello_world.rb
77
+ - examples/guides/getting_started/weathr.rb
78
+ - examples/guides/queues/redeliveries.rb
79
+ - lib/bunny.rb
80
+ - lib/bunny/authentication/credentials_encoder.rb
81
+ - lib/bunny/authentication/external_mechanism_encoder.rb
82
+ - lib/bunny/authentication/plain_mechanism_encoder.rb
83
+ - lib/bunny/channel.rb
84
+ - lib/bunny/channel_id_allocator.rb
85
+ - lib/bunny/compatibility.rb
86
+ - lib/bunny/concurrent/condition.rb
87
+ - lib/bunny/consumer.rb
88
+ - lib/bunny/consumer_tag_generator.rb
89
+ - lib/bunny/consumer_work_pool.rb
90
+ - lib/bunny/delivery_info.rb
91
+ - lib/bunny/exceptions.rb
92
+ - lib/bunny/exchange.rb
93
+ - lib/bunny/framing.rb
94
+ - lib/bunny/heartbeat_sender.rb
95
+ - lib/bunny/main_loop.rb
96
+ - lib/bunny/message_properties.rb
97
+ - lib/bunny/queue.rb
98
+ - lib/bunny/return_info.rb
99
+ - lib/bunny/session.rb
100
+ - lib/bunny/socket.rb
101
+ - lib/bunny/system_timer.rb
102
+ - lib/bunny/transport.rb
103
+ - lib/bunny/version.rb
104
+ - spec/compatibility/queue_declare_spec.rb
105
+ - spec/higher_level_api/integration/basic_ack_spec.rb
106
+ - spec/higher_level_api/integration/basic_cancel_spec.rb
107
+ - spec/higher_level_api/integration/basic_consume_spec.rb
108
+ - spec/higher_level_api/integration/basic_get_spec.rb
109
+ - spec/higher_level_api/integration/basic_nack_spec.rb
110
+ - spec/higher_level_api/integration/basic_publish_spec.rb
111
+ - spec/higher_level_api/integration/basic_qos_spec.rb
112
+ - spec/higher_level_api/integration/basic_recover_spec.rb
113
+ - spec/higher_level_api/integration/basic_reject_spec.rb
114
+ - spec/higher_level_api/integration/basic_return_spec.rb
115
+ - spec/higher_level_api/integration/channel_close_spec.rb
116
+ - spec/higher_level_api/integration/channel_flow_spec.rb
117
+ - spec/higher_level_api/integration/channel_open_spec.rb
118
+ - spec/higher_level_api/integration/channel_open_stress_spec.rb
119
+ - spec/higher_level_api/integration/confirm_select_spec.rb
120
+ - spec/higher_level_api/integration/connection_spec.rb
121
+ - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
122
+ - spec/higher_level_api/integration/dead_lettering_spec.rb
123
+ - spec/higher_level_api/integration/exchange_bind_spec.rb
124
+ - spec/higher_level_api/integration/exchange_declare_spec.rb
125
+ - spec/higher_level_api/integration/exchange_delete_spec.rb
126
+ - spec/higher_level_api/integration/exchange_unbind_spec.rb
127
+ - spec/higher_level_api/integration/message_properties_access_spec.rb
128
+ - spec/higher_level_api/integration/publisher_confirms_spec.rb
129
+ - spec/higher_level_api/integration/queue_bind_spec.rb
130
+ - spec/higher_level_api/integration/queue_declare_spec.rb
131
+ - spec/higher_level_api/integration/queue_delete_spec.rb
132
+ - spec/higher_level_api/integration/queue_purge_spec.rb
133
+ - spec/higher_level_api/integration/queue_unbind_spec.rb
134
+ - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
135
+ - spec/higher_level_api/integration/tx_commit_spec.rb
136
+ - spec/higher_level_api/integration/tx_rollback_spec.rb
137
+ - spec/issues/issue78_spec.rb
138
+ - spec/issues/issue83_spec.rb
139
+ - spec/lower_level_api/integration/basic_cancel_spec.rb
140
+ - spec/lower_level_api/integration/basic_consume_spec.rb
141
+ - spec/spec_helper.rb
142
+ - spec/unit/bunny_spec.rb
143
+ - spec/unit/concurrent/condition_spec.rb
144
+ - spec/unit/transport_spec.rb
146
145
  homepage: http://github.com/ruby-amqp/bunny
147
- licenses:
148
- - MIT
149
- post_install_message:
146
+ licenses:
147
+ - MIT
148
+ post_install_message:
150
149
  rdoc_options: []
151
- require_paths:
152
- - lib
153
- required_ruby_version: !ruby/object:Gem::Requirement
154
- requirements:
155
- - - ! '>='
156
- - !ruby/object:Gem::Version
157
- version: !binary |-
158
- MA==
150
+
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
159
154
  none: false
160
- required_rubygems_version: !ruby/object:Gem::Requirement
161
- requirements:
162
- - - !binary |-
163
- Pg==
164
- - !ruby/object:Gem::Version
165
- version: 1.3.1
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: "0"
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
160
  none: false
161
+ requirements:
162
+ - - ">"
163
+ - !ruby/object:Gem::Version
164
+ version: 1.3.1
167
165
  requirements: []
168
- rubyforge_project:
166
+
167
+ rubyforge_project:
169
168
  rubygems_version: 1.8.24
170
- signing_key:
169
+ signing_key:
171
170
  specification_version: 3
172
171
  summary: Easy to use synchronous Ruby client for RabbitMQ
173
- test_files:
174
- - spec/compatibility/queue_declare_spec.rb
175
- - spec/higher_level_api/integration/basic_ack_spec.rb
176
- - spec/higher_level_api/integration/basic_cancel_spec.rb
177
- - spec/higher_level_api/integration/basic_consume_spec.rb
178
- - spec/higher_level_api/integration/basic_get_spec.rb
179
- - spec/higher_level_api/integration/basic_nack_spec.rb
180
- - spec/higher_level_api/integration/basic_publish_spec.rb
181
- - spec/higher_level_api/integration/basic_qos_spec.rb
182
- - spec/higher_level_api/integration/basic_recover_spec.rb
183
- - spec/higher_level_api/integration/basic_reject_spec.rb
184
- - spec/higher_level_api/integration/basic_return_spec.rb
185
- - spec/higher_level_api/integration/channel_close_spec.rb
186
- - spec/higher_level_api/integration/channel_flow_spec.rb
187
- - spec/higher_level_api/integration/channel_open_spec.rb
188
- - spec/higher_level_api/integration/channel_open_stress_spec.rb
189
- - spec/higher_level_api/integration/confirm_select_spec.rb
190
- - spec/higher_level_api/integration/connection_spec.rb
191
- - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
192
- - spec/higher_level_api/integration/dead_lettering_spec.rb
193
- - spec/higher_level_api/integration/exchange_bind_spec.rb
194
- - spec/higher_level_api/integration/exchange_declare_spec.rb
195
- - spec/higher_level_api/integration/exchange_delete_spec.rb
196
- - spec/higher_level_api/integration/exchange_unbind_spec.rb
197
- - spec/higher_level_api/integration/message_properties_access_spec.rb
198
- - spec/higher_level_api/integration/publisher_confirms_spec.rb
199
- - spec/higher_level_api/integration/queue_bind_spec.rb
200
- - spec/higher_level_api/integration/queue_declare_spec.rb
201
- - spec/higher_level_api/integration/queue_delete_spec.rb
202
- - spec/higher_level_api/integration/queue_purge_spec.rb
203
- - spec/higher_level_api/integration/queue_unbind_spec.rb
204
- - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
205
- - spec/higher_level_api/integration/tx_commit_spec.rb
206
- - spec/higher_level_api/integration/tx_rollback_spec.rb
207
- - spec/issues/issue78_spec.rb
208
- - spec/issues/issue83_spec.rb
209
- - spec/lower_level_api/integration/basic_cancel_spec.rb
210
- - spec/lower_level_api/integration/basic_consume_spec.rb
211
- - spec/spec_helper.rb
212
- - spec/unit/bunny_spec.rb
213
- - spec/unit/concurrent/condition_spec.rb
172
+ test_files:
173
+ - spec/compatibility/queue_declare_spec.rb
174
+ - spec/higher_level_api/integration/basic_ack_spec.rb
175
+ - spec/higher_level_api/integration/basic_cancel_spec.rb
176
+ - spec/higher_level_api/integration/basic_consume_spec.rb
177
+ - spec/higher_level_api/integration/basic_get_spec.rb
178
+ - spec/higher_level_api/integration/basic_nack_spec.rb
179
+ - spec/higher_level_api/integration/basic_publish_spec.rb
180
+ - spec/higher_level_api/integration/basic_qos_spec.rb
181
+ - spec/higher_level_api/integration/basic_recover_spec.rb
182
+ - spec/higher_level_api/integration/basic_reject_spec.rb
183
+ - spec/higher_level_api/integration/basic_return_spec.rb
184
+ - spec/higher_level_api/integration/channel_close_spec.rb
185
+ - spec/higher_level_api/integration/channel_flow_spec.rb
186
+ - spec/higher_level_api/integration/channel_open_spec.rb
187
+ - spec/higher_level_api/integration/channel_open_stress_spec.rb
188
+ - spec/higher_level_api/integration/confirm_select_spec.rb
189
+ - spec/higher_level_api/integration/connection_spec.rb
190
+ - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
191
+ - spec/higher_level_api/integration/dead_lettering_spec.rb
192
+ - spec/higher_level_api/integration/exchange_bind_spec.rb
193
+ - spec/higher_level_api/integration/exchange_declare_spec.rb
194
+ - spec/higher_level_api/integration/exchange_delete_spec.rb
195
+ - spec/higher_level_api/integration/exchange_unbind_spec.rb
196
+ - spec/higher_level_api/integration/message_properties_access_spec.rb
197
+ - spec/higher_level_api/integration/publisher_confirms_spec.rb
198
+ - spec/higher_level_api/integration/queue_bind_spec.rb
199
+ - spec/higher_level_api/integration/queue_declare_spec.rb
200
+ - spec/higher_level_api/integration/queue_delete_spec.rb
201
+ - spec/higher_level_api/integration/queue_purge_spec.rb
202
+ - spec/higher_level_api/integration/queue_unbind_spec.rb
203
+ - spec/higher_level_api/integration/sender_selected_distribution_spec.rb
204
+ - spec/higher_level_api/integration/tx_commit_spec.rb
205
+ - spec/higher_level_api/integration/tx_rollback_spec.rb
206
+ - spec/issues/issue78_spec.rb
207
+ - spec/issues/issue83_spec.rb
208
+ - spec/lower_level_api/integration/basic_cancel_spec.rb
209
+ - spec/lower_level_api/integration/basic_consume_spec.rb
210
+ - spec/spec_helper.rb
211
+ - spec/unit/bunny_spec.rb
212
+ - spec/unit/concurrent/condition_spec.rb
213
+ - spec/unit/transport_spec.rb