bunny 1.7.0 → 2.17.0

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.
Files changed (141) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/.gitignore +6 -1
  4. data/.rspec +1 -3
  5. data/.travis.yml +21 -14
  6. data/CONTRIBUTING.md +132 -0
  7. data/ChangeLog.md +745 -1
  8. data/Gemfile +13 -13
  9. data/LICENSE +1 -1
  10. data/README.md +41 -75
  11. data/Rakefile +54 -0
  12. data/bunny.gemspec +4 -10
  13. data/docker-compose.yml +28 -0
  14. data/docker/Dockerfile +24 -0
  15. data/docker/apt/preferences.d/erlang +3 -0
  16. data/docker/apt/sources.list.d/bintray.rabbitmq.list +2 -0
  17. data/docker/docker-entrypoint.sh +26 -0
  18. data/docker/rabbitmq.conf +29 -0
  19. data/examples/connection/automatic_recovery_with_basic_get.rb +1 -1
  20. data/examples/connection/automatic_recovery_with_client_named_queues.rb +1 -1
  21. data/examples/connection/automatic_recovery_with_multiple_consumers.rb +1 -1
  22. data/examples/connection/automatic_recovery_with_republishing.rb +1 -1
  23. data/examples/connection/automatic_recovery_with_server_named_queues.rb +1 -1
  24. data/examples/connection/channel_level_exception.rb +1 -9
  25. data/examples/connection/disabled_automatic_recovery.rb +1 -1
  26. data/examples/connection/heartbeat.rb +1 -1
  27. data/examples/consumers/high_and_low_priority.rb +1 -1
  28. data/examples/guides/extensions/alternate_exchange.rb +2 -0
  29. data/examples/guides/getting_started/hello_world.rb +2 -0
  30. data/examples/guides/getting_started/weathr.rb +2 -0
  31. data/examples/guides/queues/one_off_consumer.rb +2 -0
  32. data/examples/guides/queues/redeliveries.rb +2 -0
  33. data/lib/bunny.rb +6 -2
  34. data/lib/bunny/channel.rb +192 -109
  35. data/lib/bunny/channel_id_allocator.rb +6 -4
  36. data/lib/bunny/concurrent/continuation_queue.rb +34 -13
  37. data/lib/bunny/consumer_work_pool.rb +34 -6
  38. data/lib/bunny/cruby/socket.rb +29 -16
  39. data/lib/bunny/cruby/ssl_socket.rb +20 -7
  40. data/lib/bunny/exceptions.rb +7 -1
  41. data/lib/bunny/exchange.rb +11 -7
  42. data/lib/bunny/get_response.rb +1 -1
  43. data/lib/bunny/heartbeat_sender.rb +3 -2
  44. data/lib/bunny/jruby/socket.rb +23 -6
  45. data/lib/bunny/jruby/ssl_socket.rb +5 -0
  46. data/lib/bunny/queue.rb +12 -10
  47. data/lib/bunny/reader_loop.rb +31 -18
  48. data/lib/bunny/session.rb +389 -134
  49. data/lib/bunny/test_kit.rb +14 -0
  50. data/lib/bunny/timeout.rb +1 -12
  51. data/lib/bunny/transport.rb +114 -67
  52. data/lib/bunny/version.rb +1 -1
  53. data/repl +1 -1
  54. data/spec/config/rabbitmq.conf +13 -0
  55. data/spec/higher_level_api/integration/basic_ack_spec.rb +154 -22
  56. data/spec/higher_level_api/integration/basic_cancel_spec.rb +77 -11
  57. data/spec/higher_level_api/integration/basic_consume_spec.rb +60 -55
  58. data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +6 -6
  59. data/spec/higher_level_api/integration/basic_get_spec.rb +31 -7
  60. data/spec/higher_level_api/integration/basic_nack_spec.rb +22 -19
  61. data/spec/higher_level_api/integration/basic_publish_spec.rb +11 -100
  62. data/spec/higher_level_api/integration/basic_qos_spec.rb +32 -4
  63. data/spec/higher_level_api/integration/basic_reject_spec.rb +94 -16
  64. data/spec/higher_level_api/integration/basic_return_spec.rb +4 -4
  65. data/spec/higher_level_api/integration/channel_close_spec.rb +51 -10
  66. data/spec/higher_level_api/integration/channel_open_spec.rb +12 -12
  67. data/spec/higher_level_api/integration/connection_recovery_spec.rb +412 -286
  68. data/spec/higher_level_api/integration/connection_spec.rb +284 -134
  69. data/spec/higher_level_api/integration/connection_stop_spec.rb +31 -19
  70. data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +17 -17
  71. data/spec/higher_level_api/integration/dead_lettering_spec.rb +14 -14
  72. data/spec/higher_level_api/integration/exchange_bind_spec.rb +5 -5
  73. data/spec/higher_level_api/integration/exchange_declare_spec.rb +32 -31
  74. data/spec/higher_level_api/integration/exchange_delete_spec.rb +12 -12
  75. data/spec/higher_level_api/integration/exchange_unbind_spec.rb +5 -5
  76. data/spec/higher_level_api/integration/exclusive_queue_spec.rb +5 -5
  77. data/spec/higher_level_api/integration/heartbeat_spec.rb +4 -4
  78. data/spec/higher_level_api/integration/message_properties_access_spec.rb +49 -49
  79. data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +2 -2
  80. data/spec/higher_level_api/integration/publisher_confirms_spec.rb +92 -27
  81. data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +19 -19
  82. data/spec/higher_level_api/integration/queue_bind_spec.rb +23 -23
  83. data/spec/higher_level_api/integration/queue_declare_spec.rb +129 -34
  84. data/spec/higher_level_api/integration/queue_delete_spec.rb +2 -2
  85. data/spec/higher_level_api/integration/queue_purge_spec.rb +5 -5
  86. data/spec/higher_level_api/integration/queue_unbind_spec.rb +6 -6
  87. data/spec/higher_level_api/integration/read_only_consumer_spec.rb +9 -9
  88. data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +10 -10
  89. data/spec/higher_level_api/integration/tls_connection_spec.rb +218 -112
  90. data/spec/higher_level_api/integration/toxiproxy_spec.rb +76 -0
  91. data/spec/higher_level_api/integration/tx_commit_spec.rb +1 -1
  92. data/spec/higher_level_api/integration/tx_rollback_spec.rb +1 -1
  93. data/spec/higher_level_api/integration/with_channel_spec.rb +2 -2
  94. data/spec/issues/issue100_spec.rb +11 -12
  95. data/spec/issues/issue141_spec.rb +13 -14
  96. data/spec/issues/issue202_spec.rb +1 -1
  97. data/spec/issues/issue224_spec.rb +5 -5
  98. data/spec/issues/issue465_spec.rb +32 -0
  99. data/spec/issues/issue549_spec.rb +30 -0
  100. data/spec/issues/issue78_spec.rb +21 -24
  101. data/spec/issues/issue83_spec.rb +5 -6
  102. data/spec/issues/issue97_spec.rb +44 -45
  103. data/spec/lower_level_api/integration/basic_cancel_spec.rb +15 -16
  104. data/spec/lower_level_api/integration/basic_consume_spec.rb +20 -21
  105. data/spec/spec_helper.rb +2 -19
  106. data/spec/stress/channel_close_stress_spec.rb +3 -3
  107. data/spec/stress/channel_open_stress_spec.rb +4 -4
  108. data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +7 -7
  109. data/spec/stress/concurrent_consumers_stress_spec.rb +18 -16
  110. data/spec/stress/concurrent_publishers_stress_spec.rb +16 -19
  111. data/spec/stress/connection_open_close_spec.rb +9 -9
  112. data/spec/stress/merry_go_round_spec.rb +105 -0
  113. data/spec/tls/ca_certificate.pem +27 -16
  114. data/spec/tls/ca_key.pem +52 -27
  115. data/spec/tls/client_certificate.pem +27 -16
  116. data/spec/tls/client_key.pem +49 -25
  117. data/spec/tls/generate-server-cert.sh +8 -0
  118. data/spec/tls/server-openssl.cnf +10 -0
  119. data/spec/tls/server.csr +16 -0
  120. data/spec/tls/server_certificate.pem +27 -16
  121. data/spec/tls/server_key.pem +49 -25
  122. data/spec/toxiproxy_helper.rb +28 -0
  123. data/spec/unit/bunny_spec.rb +5 -5
  124. data/spec/unit/concurrent/atomic_fixnum_spec.rb +6 -6
  125. data/spec/unit/concurrent/condition_spec.rb +8 -8
  126. data/spec/unit/concurrent/linked_continuation_queue_spec.rb +2 -2
  127. data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +16 -16
  128. data/spec/unit/exchange_recovery_spec.rb +39 -0
  129. data/spec/unit/version_delivery_tag_spec.rb +3 -3
  130. metadata +42 -35
  131. data/lib/bunny/system_timer.rb +0 -20
  132. data/spec/config/rabbitmq.config +0 -18
  133. data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
  134. data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
  135. data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
  136. data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
  137. data/spec/stress/long_running_consumer_spec.rb +0 -83
  138. data/spec/tls/cacert.pem +0 -18
  139. data/spec/tls/client_cert.pem +0 -18
  140. data/spec/tls/server_cert.pem +0 -18
  141. data/spec/unit/system_timer_spec.rb +0 -10
@@ -17,7 +17,9 @@ module Bunny
17
17
  #
18
18
 
19
19
  # @param [Integer] max_channel Max allowed channel id
20
- def initialize(max_channel = ((1 << 16) - 1))
20
+ def initialize(max_channel = ((1 << 11) - 1))
21
+ # channel 0 has special meaning in the protocol, so start
22
+ # allocator at 1
21
23
  @allocator = AMQ::IntAllocator.new(1, max_channel)
22
24
  @mutex = Monitor.new
23
25
  end
@@ -25,7 +27,7 @@ module Bunny
25
27
 
26
28
  # Returns next available channel id. This method is thread safe.
27
29
  #
28
- # @return [Fixnum]
30
+ # @return [Integer]
29
31
  # @api public
30
32
  # @see ChannelManager#release_channel_id
31
33
  # @see ChannelManager#reset_channel_id_allocator
@@ -37,7 +39,7 @@ module Bunny
37
39
 
38
40
  # Releases previously allocated channel id. This method is thread safe.
39
41
  #
40
- # @param [Fixnum] i Channel id to release
42
+ # @param [Integer] i Channel id to release
41
43
  # @api public
42
44
  # @see ChannelManager#next_channel_id
43
45
  # @see ChannelManager#reset_channel_id_allocator
@@ -51,7 +53,7 @@ module Bunny
51
53
  # Returns true if given channel id has been previously allocated and not yet released.
52
54
  # This method is thread safe.
53
55
  #
54
- # @param [Fixnum] i Channel id to check
56
+ # @param [Integer] i Channel id to check
55
57
  # @return [Boolean] true if given channel id has been previously allocated and not yet released
56
58
  # @api public
57
59
  # @see ChannelManager#next_channel_id
@@ -6,36 +6,57 @@ module Bunny
6
6
  #
7
7
  # @private
8
8
  class ContinuationQueue
9
- def initialize(*args, &block)
10
- @q = ::Queue.new(*args)
9
+ def initialize
10
+ @q = []
11
+ @lock = ::Mutex.new
12
+ @cond = ::ConditionVariable.new
11
13
  end
12
14
 
13
- def push(*args)
14
- @q.push(*args)
15
+ def push(item)
16
+ @lock.synchronize do
17
+ @q.push(item)
18
+ @cond.signal
19
+ end
15
20
  end
16
21
  alias << push
17
22
 
18
23
  def pop
19
- @q.pop
24
+ poll
20
25
  end
21
26
 
22
27
  def poll(timeout_in_ms = nil)
23
- if timeout_in_ms
24
- Bunny::Timeout.timeout(timeout_in_ms / 1000.0, ::Timeout::Error) do
25
- @q.pop
28
+ timeout = timeout_in_ms ? timeout_in_ms / 1000.0 : nil
29
+
30
+ @lock.synchronize do
31
+ timeout_strikes_at = Time.now.utc + (timeout || 0)
32
+ while @q.empty?
33
+ wait = if timeout
34
+ timeout_strikes_at - Time.now.utc
35
+ else
36
+ nil
37
+ end
38
+ @cond.wait(@lock, wait)
39
+ raise ::Timeout::Error if wait && Time.now.utc >= timeout_strikes_at
26
40
  end
27
- else
28
- @q.pop
41
+ item = @q.shift
42
+ item
29
43
  end
30
44
  end
31
45
 
32
46
  def clear
33
- @q.clear
47
+ @lock.synchronize do
48
+ @q.clear
49
+ end
50
+ end
51
+
52
+ def empty?
53
+ @q.empty?
34
54
  end
35
55
 
36
- def method_missing(selector, *args, &block)
37
- @q.__send__(selector, *args, &block)
56
+ def size
57
+ @q.size
38
58
  end
59
+ alias length size
39
60
  end
40
61
  end
41
62
  end
@@ -15,10 +15,17 @@ module Bunny
15
15
 
16
16
  attr_reader :threads
17
17
  attr_reader :size
18
+ attr_reader :abort_on_exception
18
19
 
19
- def initialize(size = 1)
20
+ def initialize(size = 1, abort_on_exception = false, shutdown_timeout = 60)
20
21
  @size = size
22
+ @abort_on_exception = abort_on_exception
23
+ @shutdown_timeout = shutdown_timeout
24
+ @shutdown_mutex = ::Mutex.new
25
+ @shutdown_conditional = ::ConditionVariable.new
21
26
  @queue = ::Queue.new
27
+ @paused = false
28
+ @running = false
22
29
  end
23
30
 
24
31
 
@@ -31,6 +38,7 @@ module Bunny
31
38
 
32
39
  @size.times do
33
40
  t = Thread.new(&method(:run_loop))
41
+ t.abort_on_exception = true if abort_on_exception
34
42
  @threads << t
35
43
  end
36
44
 
@@ -41,7 +49,16 @@ module Bunny
41
49
  @running
42
50
  end
43
51
 
44
- def shutdown
52
+ def backlog
53
+ @queue.length
54
+ end
55
+
56
+ def busy?
57
+ !@queue.empty?
58
+ end
59
+
60
+ def shutdown(wait_for_workers = false)
61
+ was_running = running?
45
62
  @running = false
46
63
 
47
64
  @size.times do
@@ -49,20 +66,26 @@ module Bunny
49
66
  throw :terminate
50
67
  end
51
68
  end
69
+
70
+ return if !(wait_for_workers && @shutdown_timeout && was_running)
71
+
72
+ @shutdown_mutex.synchronize do
73
+ @shutdown_conditional.wait(@shutdown_mutex, @shutdown_timeout)
74
+ end
52
75
  end
53
76
 
54
77
  def join(timeout = nil)
55
- @threads.each { |t| t.join(timeout) }
78
+ (@threads || []).each { |t| t.join(timeout) }
56
79
  end
57
80
 
58
81
  def pause
59
82
  @running = false
60
-
61
- @threads.each { |t| t.stop }
83
+ @paused = true
62
84
  end
63
85
 
64
86
  def resume
65
87
  @running = true
88
+ @paused = false
66
89
 
67
90
  @threads.each { |t| t.run }
68
91
  end
@@ -70,7 +93,7 @@ module Bunny
70
93
  def kill
71
94
  @running = false
72
95
 
73
- @threads.each { |t| t.kill }
96
+ (@threads || []).each { |t| t.kill }
74
97
  end
75
98
 
76
99
  protected
@@ -78,6 +101,7 @@ module Bunny
78
101
  def run_loop
79
102
  catch(:terminate) do
80
103
  loop do
104
+ Thread.stop if @paused
81
105
  callable = @queue.pop
82
106
 
83
107
  begin
@@ -89,6 +113,10 @@ module Bunny
89
113
  end
90
114
  end
91
115
  end
116
+
117
+ @shutdown_mutex.synchronize do
118
+ @shutdown_conditional.signal unless busy?
119
+ end
92
120
  end
93
121
  end
94
122
  end
@@ -6,27 +6,40 @@ module Bunny
6
6
  #
7
7
  # Heavily inspired by Dalli by Mike Perham.
8
8
  # @private
9
- class Socket < TCPSocket
9
+ module Socket
10
10
  attr_accessor :options
11
11
 
12
- # IO::WaitReadable is 1.9+ only
13
- READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
14
- READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
15
-
16
- # IO::WaitWritable is 1.9+ only
17
- WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
18
- WRITE_RETRY_EXCEPTION_CLASSES << IO::WaitWritable if IO.const_defined?(:WaitWritable)
12
+ READ_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitReadable)
13
+ # Ruby 2.1+
14
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable,
15
+ IO::EAGAINWaitReadable, IO::EWOULDBLOCKWaitReadable]
16
+ else
17
+ # 2.0
18
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable]
19
+ end
20
+ WRITE_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitWritable)
21
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable,
22
+ IO::EAGAINWaitWritable, IO::EWOULDBLOCKWaitWritable]
23
+ else
24
+ # 2.0
25
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
26
+ end
19
27
 
20
28
  def self.open(host, port, options = {})
21
- Timeout.timeout(options[:connect_timeout], ClientTimeout) do
22
- sock = new(host, port)
23
- if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
24
- sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
25
- end
26
- sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
27
- sock.options = {:host => host, :port => port}.merge(options)
28
- sock
29
+ socket = ::Socket.tcp(host, port, nil, nil,
30
+ connect_timeout: options[:connect_timeout])
31
+ if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
32
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
33
+ end
34
+ socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
35
+ socket.instance_eval do
36
+ @__bunny_socket_eof_flag__ = false
29
37
  end
38
+ socket.extend self
39
+ socket.options = { :host => host, :port => port }.merge(options)
40
+ socket
41
+ rescue Errno::ETIMEDOUT
42
+ raise ClientTimeout
30
43
  end
31
44
 
32
45
  # Reads given number of bytes with an optional timeout
@@ -8,13 +8,26 @@ module Bunny
8
8
  # methods found in Bunny::Socket.
9
9
  class SSLSocket < OpenSSL::SSL::SSLSocket
10
10
 
11
- # IO::WaitReadable is 1.9+ only
12
- READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
13
- READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
11
+ READ_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitReadable)
12
+ # Ruby 2.1+
13
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable,
14
+ IO::EAGAINWaitReadable, IO::EWOULDBLOCKWaitReadable]
15
+ else
16
+ # 2.0
17
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable]
18
+ end
19
+ WRITE_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitWritable)
20
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable,
21
+ IO::EAGAINWaitWritable, IO::EWOULDBLOCKWaitWritable]
22
+ else
23
+ # 2.0
24
+ [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
25
+ end
14
26
 
15
- # IO::WaitWritable is 1.9+ only
16
- WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
17
- WRITE_RETRY_EXCEPTION_CLASSES << IO::WaitWritable if IO.const_defined?(:WaitWritable)
27
+ def initialize(*args)
28
+ super
29
+ @__bunny_socket_eof_flag__ = false
30
+ end
18
31
 
19
32
  # Reads given number of bytes with an optional timeout
20
33
  #
@@ -99,7 +112,7 @@ module Bunny
99
112
  end
100
113
 
101
114
  end
102
- rescue LoadError => le
115
+ rescue LoadError
103
116
  puts "Could not load OpenSSL"
104
117
  end
105
118
  end
@@ -69,7 +69,7 @@ module Bunny
69
69
  m = case e
70
70
  when String then
71
71
  e
72
- when Exception then
72
+ when ::Exception then
73
73
  e.message
74
74
  end
75
75
  if hostname && port
@@ -97,6 +97,12 @@ module Bunny
97
97
  end
98
98
  end
99
99
 
100
+ class ConnectionAlreadyClosed < Exception
101
+ def initialize
102
+ super('Connection has been already closed')
103
+ end
104
+ end
105
+
100
106
  class ShutdownSignal < Exception
101
107
  end
102
108
 
@@ -1,3 +1,5 @@
1
+ require 'amq/protocol'
2
+
1
3
  module Bunny
2
4
  # Represents AMQP 0.9.1 exchanges.
3
5
  #
@@ -80,6 +82,8 @@ module Bunny
80
82
  @internal = @options[:internal]
81
83
  @arguments = @options[:arguments]
82
84
 
85
+ @bindings = Set.new
86
+
83
87
  declare! unless opts[:no_declare] || predeclared? || (@name == AMQ::Protocol::EMPTY_STRING)
84
88
 
85
89
  @channel.register_exchange(self)
@@ -169,6 +173,7 @@ module Bunny
169
173
  # @api public
170
174
  def bind(source, opts = {})
171
175
  @channel.exchange_bind(source, self, opts)
176
+ @bindings.add(source: source, opts: opts)
172
177
 
173
178
  self
174
179
  end
@@ -189,6 +194,7 @@ module Bunny
189
194
  # @api public
190
195
  def unbind(source, opts = {})
191
196
  @channel.exchange_unbind(source, self, opts)
197
+ @bindings.delete(source: source, opts: opts)
192
198
 
193
199
  self
194
200
  end
@@ -214,8 +220,11 @@ module Bunny
214
220
 
215
221
  # @private
216
222
  def recover_from_network_failure
217
- # puts "Recovering exchange #{@name} from network failure"
218
- declare! unless predefined?
223
+ declare! unless @options[:no_declare] ||predefined?
224
+
225
+ @bindings.each do |b|
226
+ bind(b[:source], b[:opts])
227
+ end
219
228
  end
220
229
 
221
230
 
@@ -245,11 +254,6 @@ module Bunny
245
254
  @channel.exchange_declare(@name, @type, @options)
246
255
  end
247
256
 
248
- # @private
249
- def self.add_default_options(name, opts, block)
250
- { :exchange => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
251
- end
252
-
253
257
  # @private
254
258
  def self.add_default_options(name, opts)
255
259
  # :nowait is always false for Bunny
@@ -20,7 +20,7 @@ module Bunny
20
20
  attr_reader :channel
21
21
 
22
22
  # @private
23
- def initialize(get_ok, consumer, channel)
23
+ def initialize(get_ok, channel)
24
24
  @get_ok = get_ok
25
25
  @hash = {
26
26
  :delivery_tag => @get_ok.delivery_tag,
@@ -29,6 +29,7 @@ module Bunny
29
29
  @interval = [(period / 2) - 1, 0.4].max
30
30
 
31
31
  @thread = Thread.new(&method(:run))
32
+ @thread.report_on_exception = false if @thread.respond_to?(:report_on_exception)
32
33
  end
33
34
  end
34
35
 
@@ -62,8 +63,8 @@ module Bunny
62
63
  now = Time.now
63
64
 
64
65
  if now > (@last_activity_time + @interval)
65
- @logger.debug "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}"
66
- @transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode)
66
+ @logger.debug { "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}" }
67
+ @transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode, true)
67
68
  end
68
69
  end
69
70
  end
@@ -5,7 +5,22 @@ module Bunny
5
5
  # TCP socket extension that uses Socket#readpartial to avoid excessive CPU
6
6
  # burn after some time. See issue #165.
7
7
  # @private
8
- class Socket < Bunny::Socket
8
+ module Socket
9
+ include Bunny::Socket
10
+
11
+ def self.open(host, port, options = {})
12
+ socket = ::Socket.tcp(host, port, nil, nil,
13
+ connect_timeout: options[:connect_timeout])
14
+ if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
15
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
16
+ end
17
+ socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
18
+ socket.extend self
19
+ socket.options = { :host => host, :port => port }.merge(options)
20
+ socket
21
+ rescue Errno::ETIMEDOUT
22
+ raise ClientTimeout
23
+ end
9
24
 
10
25
  # Reads given number of bytes with an optional timeout
11
26
  #
@@ -15,17 +30,17 @@ module Bunny
15
30
  # @return [String] Data read from the socket
16
31
  # @api public
17
32
  def read_fully(count, timeout = nil)
18
- return nil if @__bunny_socket_eof_flag__
19
-
20
33
  value = ''
34
+
21
35
  begin
22
36
  loop do
23
- value << readpartial(count - value.bytesize)
37
+ value << read_nonblock(count - value.bytesize)
24
38
  break if value.bytesize >= count
25
39
  end
26
40
  rescue EOFError
27
- # @eof will break Rubinius' TCPSocket implementation. MK.
28
- @__bunny_socket_eof_flag__ = true
41
+ # JRuby specific fix via https://github.com/jruby/jruby/issues/1694#issuecomment-54873532
42
+ IO.select([self], nil, nil, timeout)
43
+ retry
29
44
  rescue *READ_RETRY_EXCEPTION_CLASSES
30
45
  if IO.select([self], nil, nil, timeout)
31
46
  retry
@@ -33,8 +48,10 @@ module Bunny
33
48
  raise Timeout::Error, "IO timeout when reading #{count} bytes"
34
49
  end
35
50
  end
51
+
36
52
  value
37
53
  end # read_fully
54
+
38
55
  end
39
56
  end
40
57
  end