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.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/.gitignore +6 -1
- data/.rspec +1 -3
- data/.travis.yml +21 -14
- data/CONTRIBUTING.md +132 -0
- data/ChangeLog.md +745 -1
- data/Gemfile +13 -13
- data/LICENSE +1 -1
- data/README.md +41 -75
- data/Rakefile +54 -0
- data/bunny.gemspec +4 -10
- data/docker-compose.yml +28 -0
- data/docker/Dockerfile +24 -0
- data/docker/apt/preferences.d/erlang +3 -0
- data/docker/apt/sources.list.d/bintray.rabbitmq.list +2 -0
- data/docker/docker-entrypoint.sh +26 -0
- data/docker/rabbitmq.conf +29 -0
- data/examples/connection/automatic_recovery_with_basic_get.rb +1 -1
- data/examples/connection/automatic_recovery_with_client_named_queues.rb +1 -1
- data/examples/connection/automatic_recovery_with_multiple_consumers.rb +1 -1
- data/examples/connection/automatic_recovery_with_republishing.rb +1 -1
- data/examples/connection/automatic_recovery_with_server_named_queues.rb +1 -1
- data/examples/connection/channel_level_exception.rb +1 -9
- data/examples/connection/disabled_automatic_recovery.rb +1 -1
- data/examples/connection/heartbeat.rb +1 -1
- data/examples/consumers/high_and_low_priority.rb +1 -1
- data/examples/guides/extensions/alternate_exchange.rb +2 -0
- data/examples/guides/getting_started/hello_world.rb +2 -0
- data/examples/guides/getting_started/weathr.rb +2 -0
- data/examples/guides/queues/one_off_consumer.rb +2 -0
- data/examples/guides/queues/redeliveries.rb +2 -0
- data/lib/bunny.rb +6 -2
- data/lib/bunny/channel.rb +192 -109
- data/lib/bunny/channel_id_allocator.rb +6 -4
- data/lib/bunny/concurrent/continuation_queue.rb +34 -13
- data/lib/bunny/consumer_work_pool.rb +34 -6
- data/lib/bunny/cruby/socket.rb +29 -16
- data/lib/bunny/cruby/ssl_socket.rb +20 -7
- data/lib/bunny/exceptions.rb +7 -1
- data/lib/bunny/exchange.rb +11 -7
- data/lib/bunny/get_response.rb +1 -1
- data/lib/bunny/heartbeat_sender.rb +3 -2
- data/lib/bunny/jruby/socket.rb +23 -6
- data/lib/bunny/jruby/ssl_socket.rb +5 -0
- data/lib/bunny/queue.rb +12 -10
- data/lib/bunny/reader_loop.rb +31 -18
- data/lib/bunny/session.rb +389 -134
- data/lib/bunny/test_kit.rb +14 -0
- data/lib/bunny/timeout.rb +1 -12
- data/lib/bunny/transport.rb +114 -67
- data/lib/bunny/version.rb +1 -1
- data/repl +1 -1
- data/spec/config/rabbitmq.conf +13 -0
- data/spec/higher_level_api/integration/basic_ack_spec.rb +154 -22
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +77 -11
- data/spec/higher_level_api/integration/basic_consume_spec.rb +60 -55
- data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +6 -6
- data/spec/higher_level_api/integration/basic_get_spec.rb +31 -7
- data/spec/higher_level_api/integration/basic_nack_spec.rb +22 -19
- data/spec/higher_level_api/integration/basic_publish_spec.rb +11 -100
- data/spec/higher_level_api/integration/basic_qos_spec.rb +32 -4
- data/spec/higher_level_api/integration/basic_reject_spec.rb +94 -16
- data/spec/higher_level_api/integration/basic_return_spec.rb +4 -4
- data/spec/higher_level_api/integration/channel_close_spec.rb +51 -10
- data/spec/higher_level_api/integration/channel_open_spec.rb +12 -12
- data/spec/higher_level_api/integration/connection_recovery_spec.rb +412 -286
- data/spec/higher_level_api/integration/connection_spec.rb +284 -134
- data/spec/higher_level_api/integration/connection_stop_spec.rb +31 -19
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +17 -17
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +14 -14
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +5 -5
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +32 -31
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +12 -12
- data/spec/higher_level_api/integration/exchange_unbind_spec.rb +5 -5
- data/spec/higher_level_api/integration/exclusive_queue_spec.rb +5 -5
- data/spec/higher_level_api/integration/heartbeat_spec.rb +4 -4
- data/spec/higher_level_api/integration/message_properties_access_spec.rb +49 -49
- data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +2 -2
- data/spec/higher_level_api/integration/publisher_confirms_spec.rb +92 -27
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +19 -19
- data/spec/higher_level_api/integration/queue_bind_spec.rb +23 -23
- data/spec/higher_level_api/integration/queue_declare_spec.rb +129 -34
- data/spec/higher_level_api/integration/queue_delete_spec.rb +2 -2
- data/spec/higher_level_api/integration/queue_purge_spec.rb +5 -5
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +6 -6
- data/spec/higher_level_api/integration/read_only_consumer_spec.rb +9 -9
- data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +10 -10
- data/spec/higher_level_api/integration/tls_connection_spec.rb +218 -112
- data/spec/higher_level_api/integration/toxiproxy_spec.rb +76 -0
- data/spec/higher_level_api/integration/tx_commit_spec.rb +1 -1
- data/spec/higher_level_api/integration/tx_rollback_spec.rb +1 -1
- data/spec/higher_level_api/integration/with_channel_spec.rb +2 -2
- data/spec/issues/issue100_spec.rb +11 -12
- data/spec/issues/issue141_spec.rb +13 -14
- data/spec/issues/issue202_spec.rb +1 -1
- data/spec/issues/issue224_spec.rb +5 -5
- data/spec/issues/issue465_spec.rb +32 -0
- data/spec/issues/issue549_spec.rb +30 -0
- data/spec/issues/issue78_spec.rb +21 -24
- data/spec/issues/issue83_spec.rb +5 -6
- data/spec/issues/issue97_spec.rb +44 -45
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +15 -16
- data/spec/lower_level_api/integration/basic_consume_spec.rb +20 -21
- data/spec/spec_helper.rb +2 -19
- data/spec/stress/channel_close_stress_spec.rb +3 -3
- data/spec/stress/channel_open_stress_spec.rb +4 -4
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +7 -7
- data/spec/stress/concurrent_consumers_stress_spec.rb +18 -16
- data/spec/stress/concurrent_publishers_stress_spec.rb +16 -19
- data/spec/stress/connection_open_close_spec.rb +9 -9
- data/spec/stress/merry_go_round_spec.rb +105 -0
- data/spec/tls/ca_certificate.pem +27 -16
- data/spec/tls/ca_key.pem +52 -27
- data/spec/tls/client_certificate.pem +27 -16
- data/spec/tls/client_key.pem +49 -25
- data/spec/tls/generate-server-cert.sh +8 -0
- data/spec/tls/server-openssl.cnf +10 -0
- data/spec/tls/server.csr +16 -0
- data/spec/tls/server_certificate.pem +27 -16
- data/spec/tls/server_key.pem +49 -25
- data/spec/toxiproxy_helper.rb +28 -0
- data/spec/unit/bunny_spec.rb +5 -5
- data/spec/unit/concurrent/atomic_fixnum_spec.rb +6 -6
- data/spec/unit/concurrent/condition_spec.rb +8 -8
- data/spec/unit/concurrent/linked_continuation_queue_spec.rb +2 -2
- data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +16 -16
- data/spec/unit/exchange_recovery_spec.rb +39 -0
- data/spec/unit/version_delivery_tag_spec.rb +3 -3
- metadata +42 -35
- data/lib/bunny/system_timer.rb +0 -20
- data/spec/config/rabbitmq.config +0 -18
- data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
- data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
- data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
- data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
- data/spec/stress/long_running_consumer_spec.rb +0 -83
- data/spec/tls/cacert.pem +0 -18
- data/spec/tls/client_cert.pem +0 -18
- data/spec/tls/server_cert.pem +0 -18
- data/spec/unit/system_timer_spec.rb +0 -10
data/lib/bunny/test_kit.rb
CHANGED
@@ -1,9 +1,23 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require "timeout"
|
4
|
+
|
2
5
|
module Bunny
|
3
6
|
# Unit, integration and stress testing toolkit
|
4
7
|
class TestKit
|
5
8
|
class << self
|
6
9
|
|
10
|
+
def poll_while(timeout = 60, &probe)
|
11
|
+
Timeout.timeout(timeout) {
|
12
|
+
sleep 0.1 while probe.call
|
13
|
+
}
|
14
|
+
end
|
15
|
+
def poll_until(timeout = 60, &probe)
|
16
|
+
Timeout.timeout(timeout) {
|
17
|
+
sleep 0.1 until probe.call
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
7
21
|
# @return [Integer] Random integer in the range of [a, b]
|
8
22
|
# @api private
|
9
23
|
def random_in_range(a, b)
|
data/lib/bunny/timeout.rb
CHANGED
@@ -1,16 +1,5 @@
|
|
1
1
|
module Bunny
|
2
|
-
|
3
|
-
# Ruby 1.8) and SystemTimer (the gem)
|
4
|
-
Timeout = if RUBY_VERSION < "1.9"
|
5
|
-
begin
|
6
|
-
require "bunny/system_timer"
|
7
|
-
Bunny::SystemTimer
|
8
|
-
rescue LoadError
|
9
|
-
Timeout
|
10
|
-
end
|
11
|
-
else
|
12
|
-
Timeout
|
13
|
-
end
|
2
|
+
Timeout = ::Timeout
|
14
3
|
|
15
4
|
# Backwards compatibility
|
16
5
|
# @private
|
data/lib/bunny/transport.rb
CHANGED
@@ -4,7 +4,7 @@ require "monitor"
|
|
4
4
|
|
5
5
|
begin
|
6
6
|
require "openssl"
|
7
|
-
rescue LoadError =>
|
7
|
+
rescue LoadError => _le
|
8
8
|
$stderr.puts "Could not load OpenSSL"
|
9
9
|
end
|
10
10
|
|
@@ -20,19 +20,22 @@ module Bunny
|
|
20
20
|
#
|
21
21
|
|
22
22
|
# Default TCP connection timeout
|
23
|
-
DEFAULT_CONNECTION_TIMEOUT =
|
23
|
+
DEFAULT_CONNECTION_TIMEOUT = 30.0
|
24
24
|
|
25
|
-
DEFAULT_READ_TIMEOUT =
|
26
|
-
DEFAULT_WRITE_TIMEOUT =
|
25
|
+
DEFAULT_READ_TIMEOUT = 30.0
|
26
|
+
DEFAULT_WRITE_TIMEOUT = 30.0
|
27
27
|
|
28
28
|
attr_reader :session, :host, :port, :socket, :connect_timeout, :read_timeout, :write_timeout, :disconnect_timeout
|
29
|
-
attr_reader :tls_context
|
29
|
+
attr_reader :tls_context, :verify_peer, :tls_ca_certificates, :tls_certificate_path, :tls_key_path
|
30
30
|
|
31
|
-
|
31
|
+
def read_timeout=(v)
|
32
|
+
@read_timeout = v
|
33
|
+
@read_timeout = nil if @read_timeout == 0
|
34
|
+
end
|
32
35
|
|
33
36
|
def initialize(session, host, port, opts)
|
34
37
|
@session = session
|
35
|
-
@
|
38
|
+
@session_error_handler = opts[:session_error_handler]
|
36
39
|
@host = host
|
37
40
|
@port = port
|
38
41
|
@opts = opts
|
@@ -54,6 +57,8 @@ module Bunny
|
|
54
57
|
|
55
58
|
@writes_mutex = @session.mutex_impl.new
|
56
59
|
|
60
|
+
@socket = nil
|
61
|
+
|
57
62
|
prepare_tls_context(opts) if @tls_enabled
|
58
63
|
end
|
59
64
|
|
@@ -77,10 +82,29 @@ module Bunny
|
|
77
82
|
|
78
83
|
|
79
84
|
def connect
|
80
|
-
if
|
81
|
-
|
82
|
-
|
83
|
-
|
85
|
+
if uses_tls?
|
86
|
+
begin
|
87
|
+
@socket.connect
|
88
|
+
rescue OpenSSL::SSL::SSLError => e
|
89
|
+
@logger.error { "TLS connection failed: #{e.message}" }
|
90
|
+
raise e
|
91
|
+
end
|
92
|
+
|
93
|
+
log_peer_certificate_info(Logger::DEBUG, @socket.peer_cert)
|
94
|
+
log_peer_certificate_chain_info(Logger::DEBUG, @socket.peer_cert_chain)
|
95
|
+
|
96
|
+
begin
|
97
|
+
@socket.post_connection_check(host) if @verify_peer
|
98
|
+
rescue OpenSSL::SSL::SSLError => e
|
99
|
+
@logger.error do
|
100
|
+
msg = "Peer verification of target server failed: #{e.message}. "
|
101
|
+
msg += "Target hostname: #{hostname}, see peer certificate chain details below."
|
102
|
+
msg
|
103
|
+
end
|
104
|
+
log_peer_certificate_info(Logger::ERROR, @socket.peer_cert)
|
105
|
+
log_peer_certificate_chain_info(Logger::ERROR, @socket.peer_cert_chain)
|
106
|
+
|
107
|
+
raise e
|
84
108
|
end
|
85
109
|
|
86
110
|
@status = :connected
|
@@ -92,7 +116,7 @@ module Bunny
|
|
92
116
|
end
|
93
117
|
|
94
118
|
def connected?
|
95
|
-
:
|
119
|
+
:connected == @status && open?
|
96
120
|
end
|
97
121
|
|
98
122
|
def configure_socket(&block)
|
@@ -122,7 +146,7 @@ module Bunny
|
|
122
146
|
if @session.automatically_recover?
|
123
147
|
@session.handle_network_failure(e)
|
124
148
|
else
|
125
|
-
@
|
149
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
|
126
150
|
end
|
127
151
|
end
|
128
152
|
end
|
@@ -146,24 +170,25 @@ module Bunny
|
|
146
170
|
if @session.automatically_recover?
|
147
171
|
@session.handle_network_failure(e)
|
148
172
|
else
|
149
|
-
@
|
173
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
|
150
174
|
end
|
151
175
|
end
|
152
176
|
end
|
153
177
|
end
|
154
178
|
|
155
179
|
# Writes data to the socket without timeout checks
|
156
|
-
def write_without_timeout(data)
|
180
|
+
def write_without_timeout(data, raise_exceptions = false)
|
157
181
|
begin
|
158
182
|
@writes_mutex.synchronize { @socket.write(data) }
|
159
183
|
@socket.flush
|
160
184
|
rescue SystemCallError, Bunny::ConnectionError, IOError => e
|
161
185
|
close
|
186
|
+
raise e if raise_exceptions
|
162
187
|
|
163
188
|
if @session.automatically_recover?
|
164
189
|
@session.handle_network_failure(e)
|
165
190
|
else
|
166
|
-
@
|
191
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
|
167
192
|
end
|
168
193
|
end
|
169
194
|
end
|
@@ -218,9 +243,9 @@ module Bunny
|
|
218
243
|
@status = :not_connected
|
219
244
|
|
220
245
|
if @session.automatically_recover?
|
221
|
-
|
246
|
+
raise
|
222
247
|
else
|
223
|
-
@
|
248
|
+
@session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
|
224
249
|
end
|
225
250
|
end
|
226
251
|
end
|
@@ -233,17 +258,13 @@ module Bunny
|
|
233
258
|
# Exposed primarily for Bunny::Channel
|
234
259
|
# @private
|
235
260
|
def read_next_frame(opts = {})
|
236
|
-
header
|
237
|
-
# TODO: network issues here will sometimes cause
|
238
|
-
# the socket method return an empty string. We need to log
|
239
|
-
# and handle this better.
|
240
|
-
# type, channel, size = begin
|
241
|
-
# AMQ::Protocol::Frame.decode_header(header)
|
242
|
-
# rescue AMQ::Protocol::EmptyResponseError => e
|
243
|
-
# puts "Got AMQ::Protocol::EmptyResponseError, header is #{header.inspect}"
|
244
|
-
# end
|
261
|
+
header = read_fully(7)
|
245
262
|
type, channel, size = AMQ::Protocol::Frame.decode_header(header)
|
246
|
-
payload
|
263
|
+
payload = if size > 0
|
264
|
+
read_fully(size)
|
265
|
+
else
|
266
|
+
''
|
267
|
+
end
|
247
268
|
frame_end = read_fully(1)
|
248
269
|
|
249
270
|
# 1) the size is miscalculated
|
@@ -263,7 +284,7 @@ module Bunny
|
|
263
284
|
:connect_timeout => timeout)
|
264
285
|
|
265
286
|
true
|
266
|
-
rescue SocketError, Timeout::Error =>
|
287
|
+
rescue SocketError, Timeout::Error => _e
|
267
288
|
false
|
268
289
|
ensure
|
269
290
|
s.close if s
|
@@ -292,7 +313,7 @@ module Bunny
|
|
292
313
|
end
|
293
314
|
|
294
315
|
def post_initialize_socket
|
295
|
-
@socket = if uses_tls?
|
316
|
+
@socket = if uses_tls? and !@socket.is_a?(Bunny::SSLSocketImpl)
|
296
317
|
wrap_in_tls_socket(@socket)
|
297
318
|
else
|
298
319
|
@socket
|
@@ -302,23 +323,27 @@ module Bunny
|
|
302
323
|
protected
|
303
324
|
|
304
325
|
def tls_enabled?(opts)
|
305
|
-
return opts[:tls] unless opts[:tls].nil?
|
306
|
-
return opts[:ssl] unless opts[:ssl].nil?
|
326
|
+
return !!opts[:tls] unless opts[:tls].nil?
|
327
|
+
return !!opts[:ssl] unless opts[:ssl].nil?
|
307
328
|
(opts[:port] == AMQ::Protocol::TLS_PORT) || false
|
308
329
|
end
|
309
330
|
|
331
|
+
def tls_ca_certificates_paths_from(opts)
|
332
|
+
Array(opts[:cacertfile] || opts[:tls_ca_certificates] || opts[:ssl_ca_certificates])
|
333
|
+
end
|
334
|
+
|
310
335
|
def tls_certificate_path_from(opts)
|
311
|
-
opts[:tls_cert] || opts[:ssl_cert] || opts[:tls_cert_path] || opts[:ssl_cert_path] || opts[:tls_certificate_path] || opts[:ssl_certificate_path]
|
336
|
+
opts[:certfile] || opts[:tls_cert] || opts[:ssl_cert] || opts[:tls_cert_path] || opts[:ssl_cert_path] || opts[:tls_certificate_path] || opts[:ssl_certificate_path]
|
312
337
|
end
|
313
338
|
|
314
339
|
def tls_key_path_from(opts)
|
315
|
-
opts[:tls_key] || opts[:ssl_key] || opts[:tls_key_path] || opts[:ssl_key_path]
|
340
|
+
opts[:keyfile] || opts[:tls_key] || opts[:ssl_key] || opts[:tls_key_path] || opts[:ssl_key_path]
|
316
341
|
end
|
317
342
|
|
318
343
|
def tls_certificate_from(opts)
|
319
344
|
begin
|
320
345
|
read_client_certificate!
|
321
|
-
rescue MissingTLSCertificateFile =>
|
346
|
+
rescue MissingTLSCertificateFile => _e
|
322
347
|
inline_client_certificate_from(opts)
|
323
348
|
end
|
324
349
|
end
|
@@ -326,11 +351,34 @@ module Bunny
|
|
326
351
|
def tls_key_from(opts)
|
327
352
|
begin
|
328
353
|
read_client_key!
|
329
|
-
rescue MissingTLSKeyFile =>
|
354
|
+
rescue MissingTLSKeyFile => _e
|
330
355
|
inline_client_key_from(opts)
|
331
356
|
end
|
332
357
|
end
|
333
358
|
|
359
|
+
def peer_certificate_info(peer_cert, prefix = "Peer's leaf certificate")
|
360
|
+
exts = peer_cert.extensions.map { |x| x.value }
|
361
|
+
# Subject Alternative Names
|
362
|
+
sans = exts.select { |s| s =~ /^DNS/ }.map { |s| s.gsub(/^DNS:/, "") }
|
363
|
+
|
364
|
+
msg = "#{prefix} subject: #{peer_cert.subject}, "
|
365
|
+
msg += "subject alternative names: #{sans.join(', ')}, "
|
366
|
+
msg += "issuer: #{peer_cert.issuer}, "
|
367
|
+
msg += "not valid after: #{peer_cert.not_after}, "
|
368
|
+
msg += "X.509 usage extensions: #{exts.join(', ')}"
|
369
|
+
|
370
|
+
msg
|
371
|
+
end
|
372
|
+
|
373
|
+
def log_peer_certificate_info(severity, peer_cert, prefix = "Peer's leaf certificate")
|
374
|
+
@logger.add(severity) { peer_certificate_info(peer_cert, prefix) }
|
375
|
+
end
|
376
|
+
|
377
|
+
def log_peer_certificate_chain_info(severity, chain)
|
378
|
+
chain.each do |cert|
|
379
|
+
self.log_peer_certificate_info(severity, cert, "Peer's certificate chain entry")
|
380
|
+
end
|
381
|
+
end
|
334
382
|
|
335
383
|
def inline_client_certificate_from(opts)
|
336
384
|
opts[:tls_certificate] || opts[:ssl_cert_string] || opts[:tls_cert]
|
@@ -341,7 +389,7 @@ module Bunny
|
|
341
389
|
end
|
342
390
|
|
343
391
|
def prepare_tls_context(opts)
|
344
|
-
if (
|
392
|
+
if opts.values_at(:verify_ssl, :verify_peer, :verify).all?(&:nil?)
|
345
393
|
opts[:verify_peer] = true
|
346
394
|
end
|
347
395
|
|
@@ -353,17 +401,32 @@ module Bunny
|
|
353
401
|
@tls_key = tls_key_from(opts)
|
354
402
|
@tls_certificate_store = opts[:tls_certificate_store]
|
355
403
|
|
356
|
-
@
|
357
|
-
@verify_peer = (opts[:verify_ssl] || opts[:verify_peer])
|
404
|
+
@verify_peer = as_boolean(opts[:verify_ssl] || opts[:verify_peer] || opts[:verify])
|
358
405
|
|
359
406
|
@tls_context = initialize_tls_context(OpenSSL::SSL::SSLContext.new, opts)
|
360
407
|
end
|
361
408
|
|
409
|
+
def as_boolean(val)
|
410
|
+
case val
|
411
|
+
when true then true
|
412
|
+
when false then false
|
413
|
+
when "true" then true
|
414
|
+
when "false" then false
|
415
|
+
else
|
416
|
+
!!val
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
362
420
|
def wrap_in_tls_socket(socket)
|
363
421
|
raise ArgumentError, "cannot wrap nil into TLS socket, @tls_context is nil. This is a Bunny bug." unless socket
|
364
422
|
raise "cannot wrap a socket into TLS socket, @tls_context is nil. This is a Bunny bug." unless @tls_context
|
365
423
|
|
366
424
|
s = Bunny::SSLSocketImpl.new(socket, @tls_context)
|
425
|
+
|
426
|
+
# always set the SNI server name if possible since RFC 3546 and RFC 6066 both state
|
427
|
+
# that TLS clients supporting the extensions can talk to TLS servers that do not
|
428
|
+
s.hostname = @host if s.respond_to?(:hostname)
|
429
|
+
|
367
430
|
s.sync_close = true
|
368
431
|
s
|
369
432
|
end
|
@@ -396,13 +459,15 @@ module Bunny
|
|
396
459
|
ctx.cert_store = if @tls_certificate_store
|
397
460
|
@tls_certificate_store
|
398
461
|
else
|
462
|
+
# this ivar exists so that this value can be exposed in the API
|
463
|
+
@tls_ca_certificates = tls_ca_certificates_paths_from(opts)
|
399
464
|
initialize_tls_certificate_store(@tls_ca_certificates)
|
400
465
|
end
|
401
466
|
|
402
467
|
if !@tls_certificate
|
403
468
|
@logger.warn <<-MSG
|
404
|
-
Using TLS but no client certificate is provided
|
405
|
-
certificate, connection
|
469
|
+
Using TLS but no client certificate is provided. If RabbitMQ is configured to require & verify peer
|
470
|
+
certificate, connection will be rejected. Learn more at https://www.rabbitmq.com/ssl.html
|
406
471
|
MSG
|
407
472
|
end
|
408
473
|
if @tls_certificate && !@tls_key
|
@@ -414,12 +479,13 @@ certificate, connection upgrade will fail!
|
|
414
479
|
else
|
415
480
|
OpenSSL::SSL::VERIFY_NONE
|
416
481
|
end
|
482
|
+
@logger.debug { "Will use peer verification mode #{verify_mode}" }
|
417
483
|
ctx.verify_mode = verify_mode
|
418
484
|
|
419
485
|
if !@verify_peer
|
420
486
|
@logger.warn <<-MSG
|
421
487
|
Using TLS but peer hostname verification is disabled. This is convenient for local development
|
422
|
-
but prone man-in-the-middle attacks. Please set :
|
488
|
+
but prone to man-in-the-middle attacks. Please set verify_peer: true in production. Learn more at https://www.rabbitmq.com/ssl.html
|
423
489
|
MSG
|
424
490
|
end
|
425
491
|
|
@@ -429,41 +495,22 @@ but prone man-in-the-middle attacks. Please set :verify_peer => true in producti
|
|
429
495
|
ctx
|
430
496
|
end
|
431
497
|
|
432
|
-
def default_tls_certificates
|
433
|
-
if defined?(JRUBY_VERSION)
|
434
|
-
# see https://github.com/jruby/jruby/issues/1055. MK.
|
435
|
-
[]
|
436
|
-
else
|
437
|
-
default_ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE
|
438
|
-
default_ca_path = ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR
|
439
|
-
|
440
|
-
[
|
441
|
-
default_ca_file,
|
442
|
-
File.join(default_ca_path, 'ca-certificates.crt'), # Ubuntu/Debian
|
443
|
-
File.join(default_ca_path, 'ca-bundle.crt'), # Amazon Linux & Fedora/RHEL
|
444
|
-
File.join(default_ca_path, 'ca-bundle.pem') # OpenSUSE
|
445
|
-
].uniq
|
446
|
-
end
|
447
|
-
end
|
448
|
-
|
449
498
|
def initialize_tls_certificate_store(certs)
|
450
499
|
cert_files = []
|
451
500
|
cert_inlines = []
|
452
501
|
certs.each do |cert|
|
453
|
-
# if it starts with / then it's a file path that may or may not
|
454
|
-
#
|
455
|
-
if File.readable?(cert) || cert =~
|
502
|
+
# if it starts with / or C:/ then it's a file path that may or may not
|
503
|
+
# exist (e.g. a default OpenSSL path). MK.
|
504
|
+
if File.readable?(cert) || cert =~ /^([a-z]:?)?\//i
|
456
505
|
cert_files.push(cert)
|
457
506
|
else
|
458
507
|
cert_inlines.push(cert)
|
459
508
|
end
|
460
509
|
end
|
461
|
-
@logger.debug "Using CA certificates at #{cert_files.join(', ')}"
|
462
|
-
@logger.debug "Using #{cert_inlines.count} inline CA certificates"
|
463
|
-
if certs.empty?
|
464
|
-
@logger.error "No CA certificates found, add one with :tls_ca_certificates"
|
465
|
-
end
|
510
|
+
@logger.debug { "Using CA certificates at #{cert_files.join(', ')}" }
|
511
|
+
@logger.debug { "Using #{cert_inlines.count} inline CA certificates" }
|
466
512
|
OpenSSL::X509::Store.new.tap do |store|
|
513
|
+
store.set_default_paths
|
467
514
|
cert_files.select { |path| File.readable?(path) }.
|
468
515
|
each { |path| store.add_file(path) }
|
469
516
|
cert_inlines.
|
data/lib/bunny/version.rb
CHANGED
data/repl
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
listeners.tcp.1 = 0.0.0.0:5672
|
2
|
+
|
3
|
+
listeners.ssl.default = 5671
|
4
|
+
|
5
|
+
# mounted by docker-compose
|
6
|
+
ssl_options.cacertfile = /spec/tls/ca_certificate.pem
|
7
|
+
ssl_options.certfile = /spec/tls/server_certificate.pem
|
8
|
+
ssl_options.keyfile = /spec/tls/server_key.pem
|
9
|
+
|
10
|
+
ssl_options.verify = verify_none
|
11
|
+
ssl_options.fail_if_no_peer_cert = false
|
12
|
+
|
13
|
+
loopback_users = none
|
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Bunny::Channel, "#ack" do
|
4
4
|
let(:connection) do
|
5
|
-
c = Bunny.new(:
|
5
|
+
c = Bunny.new(username: "bunny_gem", password: "bunny_password", vhost: "bunny_testbed")
|
6
6
|
c.start
|
7
7
|
c
|
8
8
|
end
|
@@ -14,50 +14,98 @@ describe Bunny::Channel, "#ack" do
|
|
14
14
|
context "with a valid (known) delivery tag" do
|
15
15
|
it "acknowledges a message" do
|
16
16
|
ch = connection.create_channel
|
17
|
-
q = ch.queue("bunny.basic.ack.manual-acks", :
|
17
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
18
18
|
x = ch.default_exchange
|
19
19
|
|
20
|
-
x.publish("bunneth", :
|
20
|
+
x.publish("bunneth", routing_key: q.name)
|
21
21
|
sleep 0.5
|
22
|
-
q.message_count.
|
23
|
-
delivery_details, properties, content = q.pop(:
|
22
|
+
expect(q.message_count).to eq 1
|
23
|
+
delivery_details, properties, content = q.pop(manual_ack: true)
|
24
24
|
|
25
25
|
ch.ack(delivery_details.delivery_tag, true)
|
26
|
-
|
26
|
+
ch.close
|
27
|
+
|
28
|
+
ch = connection.create_channel
|
29
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
30
|
+
expect(q.message_count).to eq 0
|
31
|
+
ch.close
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with a valid (known) delivery tag (multiple = true)" do
|
36
|
+
it "acknowledges a message" do
|
37
|
+
ch = connection.create_channel
|
38
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
39
|
+
x = ch.default_exchange
|
27
40
|
|
41
|
+
x.publish("bunneth", routing_key: q.name)
|
42
|
+
x.publish("bunneth", routing_key: q.name)
|
43
|
+
sleep 0.5
|
44
|
+
expect(q.message_count).to eq 2
|
45
|
+
delivery_details_1, _properties, _content = q.pop(manual_ack: true)
|
46
|
+
delivery_details_2, _properties, _content = q.pop(manual_ack: true)
|
47
|
+
|
48
|
+
ch.ack(delivery_details_2.delivery_tag, true)
|
49
|
+
ch.close
|
50
|
+
|
51
|
+
ch = connection.create_channel
|
52
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
53
|
+
expect(q.message_count).to eq 0
|
28
54
|
ch.close
|
29
55
|
end
|
30
56
|
end
|
31
57
|
|
58
|
+
context "with a valid (known) delivery tag (multiple = false)" do
|
59
|
+
it "acknowledges a message" do
|
60
|
+
ch = connection.create_channel
|
61
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
62
|
+
x = ch.default_exchange
|
63
|
+
|
64
|
+
x.publish("bunneth", routing_key: q.name)
|
65
|
+
x.publish("bunneth", routing_key: q.name)
|
66
|
+
sleep 0.5
|
67
|
+
expect(q.message_count).to eq 2
|
68
|
+
delivery_details_1, _properties, _content = q.pop(manual_ack: true)
|
69
|
+
delivery_details_2, _properties, _content = q.pop(manual_ack: true)
|
70
|
+
|
71
|
+
ch.ack(delivery_details_2.delivery_tag, false)
|
72
|
+
ch.close
|
73
|
+
|
74
|
+
ch = connection.create_channel
|
75
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
76
|
+
expect(q.message_count).to eq 1
|
77
|
+
ch.close
|
78
|
+
end
|
79
|
+
end
|
32
80
|
|
33
81
|
context "with a valid (known) delivery tag and automatic ack mode" do
|
34
82
|
it "results in a channel exception" do
|
35
83
|
ch = connection.create_channel
|
36
|
-
q = ch.queue("bunny.basic.ack.manual-acks", :
|
84
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
37
85
|
x = ch.default_exchange
|
38
86
|
|
39
|
-
q.subscribe(:
|
87
|
+
q.subscribe(manual_ack: false) do |delivery_info, properties, payload|
|
40
88
|
ch.ack(delivery_info.delivery_tag, false)
|
41
89
|
end
|
42
90
|
|
43
|
-
x.publish("bunneth", :
|
91
|
+
x.publish("bunneth", routing_key: q.name)
|
44
92
|
sleep 0.5
|
45
|
-
|
93
|
+
expect do
|
46
94
|
q.message_count
|
47
|
-
end.
|
95
|
+
end.to raise_error(Bunny::ChannelAlreadyClosed)
|
48
96
|
end
|
49
97
|
end
|
50
98
|
|
51
99
|
context "with an invalid (random) delivery tag" do
|
52
100
|
it "causes a channel-level error" do
|
53
101
|
ch = connection.create_channel
|
54
|
-
q = ch.queue("bunny.basic.ack.unknown-delivery-tag", :
|
102
|
+
q = ch.queue("bunny.basic.ack.unknown-delivery-tag", exclusive: true)
|
55
103
|
x = ch.default_exchange
|
56
104
|
|
57
|
-
x.publish("bunneth", :
|
105
|
+
x.publish("bunneth", routing_key: q.name)
|
58
106
|
sleep 0.5
|
59
|
-
q.message_count.
|
60
|
-
_, _, content = q.pop(:
|
107
|
+
expect(q.message_count).to eq 1
|
108
|
+
_, _, content = q.pop(manual_ack: true)
|
61
109
|
|
62
110
|
ch.on_error do |ch, channel_close|
|
63
111
|
@channel_close = channel_close
|
@@ -65,33 +113,117 @@ describe Bunny::Channel, "#ack" do
|
|
65
113
|
ch.ack(82, true)
|
66
114
|
sleep 0.25
|
67
115
|
|
68
|
-
@channel_close.reply_code.
|
116
|
+
expect(@channel_close.reply_code).to eq AMQ::Protocol::PreconditionFailed::VALUE
|
69
117
|
end
|
70
118
|
end
|
71
119
|
|
72
120
|
context "with a valid (known) delivery tag" do
|
73
121
|
it "gets a depricated message warning for using :ack" do
|
74
122
|
ch = connection.create_channel
|
75
|
-
q = ch.queue("bunny.basic.ack.manual-acks", :
|
123
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
76
124
|
x = ch.default_exchange
|
77
125
|
|
78
|
-
x.publish("bunneth", :
|
126
|
+
x.publish("bunneth", routing_key: q.name)
|
79
127
|
sleep 0.5
|
80
|
-
q.message_count.
|
128
|
+
expect(q.message_count).to eq 1
|
81
129
|
|
82
130
|
orig_stderr = $stderr
|
83
131
|
$stderr = StringIO.new
|
84
132
|
|
85
|
-
delivery_details, properties, content = q.pop(:
|
133
|
+
delivery_details, properties, content = q.pop(ack: true)
|
86
134
|
|
87
135
|
$stderr.rewind
|
88
|
-
$stderr.string.chomp.
|
136
|
+
expect($stderr.string.chomp).to eq("[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead.\n[DEPRECATION] `:ack` is deprecated. Please use `:manual_ack` instead.")
|
89
137
|
|
90
138
|
$stderr = orig_stderr
|
91
139
|
|
92
140
|
ch.ack(delivery_details.delivery_tag, true)
|
93
|
-
|
141
|
+
ch.close
|
142
|
+
|
143
|
+
ch = connection.create_channel
|
144
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
145
|
+
expect(q.message_count).to eq 0
|
146
|
+
ch.close
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe Bunny::Channel, "#basic_ack" do
|
152
|
+
let(:connection) do
|
153
|
+
c = Bunny.new(username: "bunny_gem", password: "bunny_password", vhost: "bunny_testbed")
|
154
|
+
c.start
|
155
|
+
c
|
156
|
+
end
|
94
157
|
|
158
|
+
after :each do
|
159
|
+
connection.close if connection.open?
|
160
|
+
end
|
161
|
+
|
162
|
+
context "with a valid (known) delivery tag (multiple = true)" do
|
163
|
+
it "acknowledges a message" do
|
164
|
+
ch = connection.create_channel
|
165
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
166
|
+
x = ch.default_exchange
|
167
|
+
|
168
|
+
x.publish("bunneth", routing_key: q.name)
|
169
|
+
x.publish("bunneth", routing_key: q.name)
|
170
|
+
sleep 0.5
|
171
|
+
expect(q.message_count).to eq 2
|
172
|
+
delivery_details_1, _properties, _content = q.pop(manual_ack: true)
|
173
|
+
delivery_details_2, _properties, _content = q.pop(manual_ack: true)
|
174
|
+
|
175
|
+
ch.basic_ack(delivery_details_2.delivery_tag.to_i, true)
|
176
|
+
ch.close
|
177
|
+
|
178
|
+
ch = connection.create_channel
|
179
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
180
|
+
expect(q.message_count).to eq 0
|
181
|
+
ch.close
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "with a valid (known) delivery tag (multiple = false)" do
|
186
|
+
it "acknowledges a message" do
|
187
|
+
ch = connection.create_channel
|
188
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
189
|
+
x = ch.default_exchange
|
190
|
+
|
191
|
+
x.publish("bunneth", routing_key: q.name)
|
192
|
+
x.publish("bunneth", routing_key: q.name)
|
193
|
+
sleep 0.5
|
194
|
+
expect(q.message_count).to eq 2
|
195
|
+
delivery_details_1, _properties, _content = q.pop(manual_ack: true)
|
196
|
+
delivery_details_2, _properties, _content = q.pop(manual_ack: true)
|
197
|
+
|
198
|
+
ch.basic_ack(delivery_details_2.delivery_tag.to_i, false)
|
199
|
+
ch.close
|
200
|
+
|
201
|
+
ch = connection.create_channel
|
202
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
203
|
+
expect(q.message_count).to eq 1
|
204
|
+
ch.close
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "with a valid (known) delivery tag (multiple = default)" do
|
209
|
+
it "acknowledges a message" do
|
210
|
+
ch = connection.create_channel
|
211
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
212
|
+
x = ch.default_exchange
|
213
|
+
|
214
|
+
x.publish("bunneth", routing_key: q.name)
|
215
|
+
x.publish("bunneth", routing_key: q.name)
|
216
|
+
sleep 0.5
|
217
|
+
expect(q.message_count).to eq 2
|
218
|
+
delivery_details_1, _properties, _content = q.pop(manual_ack: true)
|
219
|
+
delivery_details_2, _properties, _content = q.pop(manual_ack: true)
|
220
|
+
|
221
|
+
ch.basic_ack(delivery_details_2.delivery_tag.to_i)
|
222
|
+
ch.close
|
223
|
+
|
224
|
+
ch = connection.create_channel
|
225
|
+
q = ch.queue("bunny.basic.ack.manual-acks", exclusive: true)
|
226
|
+
expect(q.message_count).to eq 1
|
95
227
|
ch.close
|
96
228
|
end
|
97
229
|
end
|