bunny 2.7.4 → 2.22.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 (156) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +61 -35
  3. data/lib/bunny/channel.rb +186 -50
  4. data/lib/bunny/channel_id_allocator.rb +3 -1
  5. data/lib/bunny/consumer.rb +2 -2
  6. data/lib/bunny/consumer_work_pool.rb +2 -1
  7. data/lib/bunny/cruby/socket.rb +3 -0
  8. data/lib/bunny/cruby/ssl_socket.rb +6 -1
  9. data/lib/bunny/delivery_info.rb +1 -1
  10. data/lib/bunny/heartbeat_sender.rb +2 -1
  11. data/lib/bunny/jruby/ssl_socket.rb +5 -0
  12. data/lib/bunny/queue.rb +36 -8
  13. data/lib/bunny/reader_loop.rb +22 -10
  14. data/lib/bunny/session.rb +152 -65
  15. data/lib/bunny/test_kit.rb +14 -0
  16. data/lib/bunny/transport.rb +132 -49
  17. data/lib/bunny/version.rb +1 -1
  18. data/lib/bunny.rb +45 -4
  19. metadata +37 -225
  20. data/.github/ISSUE_TEMPLATE.md +0 -18
  21. data/.gitignore +0 -28
  22. data/.rspec +0 -1
  23. data/.travis.yml +0 -20
  24. data/.yardopts +0 -8
  25. data/CONTRIBUTING.md +0 -111
  26. data/ChangeLog.md +0 -1831
  27. data/Gemfile +0 -53
  28. data/LICENSE +0 -21
  29. data/Rakefile +0 -46
  30. data/benchmarks/basic_publish/with_128K_messages.rb +0 -35
  31. data/benchmarks/basic_publish/with_1k_messages.rb +0 -35
  32. data/benchmarks/basic_publish/with_4K_messages.rb +0 -35
  33. data/benchmarks/basic_publish/with_64K_messages.rb +0 -35
  34. data/benchmarks/channel_open.rb +0 -28
  35. data/benchmarks/mutex_and_monitor.rb +0 -42
  36. data/benchmarks/queue_declare.rb +0 -29
  37. data/benchmarks/queue_declare_and_bind.rb +0 -29
  38. data/benchmarks/queue_declare_bind_and_delete.rb +0 -29
  39. data/benchmarks/synchronized_sorted_set.rb +0 -53
  40. data/benchmarks/write_vs_write_nonblock.rb +0 -49
  41. data/bin/ci/before_build +0 -46
  42. data/bunny.gemspec +0 -35
  43. data/docker/Dockerfile +0 -16
  44. data/docker/docker-entrypoint.sh +0 -37
  45. data/docker-compose.yml +0 -18
  46. data/examples/connection/authentication_failure.rb +0 -16
  47. data/examples/connection/automatic_recovery_with_basic_get.rb +0 -40
  48. data/examples/connection/automatic_recovery_with_client_named_queues.rb +0 -36
  49. data/examples/connection/automatic_recovery_with_multiple_consumers.rb +0 -46
  50. data/examples/connection/automatic_recovery_with_republishing.rb +0 -109
  51. data/examples/connection/automatic_recovery_with_server_named_queues.rb +0 -35
  52. data/examples/connection/channel_level_exception.rb +0 -27
  53. data/examples/connection/disabled_automatic_recovery.rb +0 -34
  54. data/examples/connection/heartbeat.rb +0 -17
  55. data/examples/connection/manually_reconnecting_consumer.rb +0 -23
  56. data/examples/connection/manually_reconnecting_publisher.rb +0 -28
  57. data/examples/connection/unknown_host.rb +0 -16
  58. data/examples/consumers/high_and_low_priority.rb +0 -50
  59. data/examples/guides/exchanges/direct_exchange_routing.rb +0 -36
  60. data/examples/guides/exchanges/fanout_exchange_routing.rb +0 -28
  61. data/examples/guides/exchanges/headers_exchange_routing.rb +0 -31
  62. data/examples/guides/exchanges/mandatory_messages.rb +0 -30
  63. data/examples/guides/extensions/alternate_exchange.rb +0 -30
  64. data/examples/guides/extensions/basic_nack.rb +0 -33
  65. data/examples/guides/extensions/connection_blocked.rb +0 -35
  66. data/examples/guides/extensions/consumer_cancellation_notification.rb +0 -39
  67. data/examples/guides/extensions/dead_letter_exchange.rb +0 -32
  68. data/examples/guides/extensions/exchange_to_exchange_bindings.rb +0 -29
  69. data/examples/guides/extensions/per_message_ttl.rb +0 -36
  70. data/examples/guides/extensions/per_queue_message_ttl.rb +0 -36
  71. data/examples/guides/extensions/publisher_confirms.rb +0 -28
  72. data/examples/guides/extensions/queue_lease.rb +0 -26
  73. data/examples/guides/extensions/sender_selected_distribution.rb +0 -32
  74. data/examples/guides/getting_started/blabbr.rb +0 -27
  75. data/examples/guides/getting_started/hello_world.rb +0 -22
  76. data/examples/guides/getting_started/weathr.rb +0 -49
  77. data/examples/guides/queues/one_off_consumer.rb +0 -25
  78. data/examples/guides/queues/redeliveries.rb +0 -81
  79. data/profiling/basic_publish/with_4K_messages.rb +0 -33
  80. data/repl +0 -3
  81. data/spec/config/enabled_plugins +0 -1
  82. data/spec/config/rabbitmq.config +0 -19
  83. data/spec/higher_level_api/integration/basic_ack_spec.rb +0 -230
  84. data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -142
  85. data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -349
  86. data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +0 -54
  87. data/spec/higher_level_api/integration/basic_get_spec.rb +0 -80
  88. data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -82
  89. data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -74
  90. data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -57
  91. data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -152
  92. data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
  93. data/spec/higher_level_api/integration/channel_close_spec.rb +0 -25
  94. data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
  95. data/spec/higher_level_api/integration/connection_recovery_spec.rb +0 -471
  96. data/spec/higher_level_api/integration/connection_spec.rb +0 -559
  97. data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -83
  98. data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
  99. data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -75
  100. data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
  101. data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -237
  102. data/spec/higher_level_api/integration/exchange_delete_spec.rb +0 -105
  103. data/spec/higher_level_api/integration/exchange_unbind_spec.rb +0 -40
  104. data/spec/higher_level_api/integration/exclusive_queue_spec.rb +0 -28
  105. data/spec/higher_level_api/integration/heartbeat_spec.rb +0 -49
  106. data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
  107. data/spec/higher_level_api/integration/message_properties_access_spec.rb +0 -95
  108. data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +0 -24
  109. data/spec/higher_level_api/integration/publisher_confirms_spec.rb +0 -191
  110. data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -87
  111. data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
  112. data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -221
  113. data/spec/higher_level_api/integration/queue_delete_spec.rb +0 -41
  114. data/spec/higher_level_api/integration/queue_purge_spec.rb +0 -30
  115. data/spec/higher_level_api/integration/queue_unbind_spec.rb +0 -54
  116. data/spec/higher_level_api/integration/read_only_consumer_spec.rb +0 -60
  117. data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +0 -36
  118. data/spec/higher_level_api/integration/tls_connection_spec.rb +0 -222
  119. data/spec/higher_level_api/integration/tx_commit_spec.rb +0 -21
  120. data/spec/higher_level_api/integration/tx_rollback_spec.rb +0 -21
  121. data/spec/higher_level_api/integration/with_channel_spec.rb +0 -25
  122. data/spec/issues/issue100_spec.rb +0 -42
  123. data/spec/issues/issue141_spec.rb +0 -43
  124. data/spec/issues/issue202_spec.rb +0 -15
  125. data/spec/issues/issue224_spec.rb +0 -40
  126. data/spec/issues/issue465_spec.rb +0 -32
  127. data/spec/issues/issue78_spec.rb +0 -72
  128. data/spec/issues/issue83_spec.rb +0 -30
  129. data/spec/issues/issue97_attachment.json +0 -1
  130. data/spec/issues/issue97_spec.rb +0 -175
  131. data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -83
  132. data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -99
  133. data/spec/spec_helper.rb +0 -51
  134. data/spec/stress/channel_close_stress_spec.rb +0 -64
  135. data/spec/stress/channel_open_stress_spec.rb +0 -84
  136. data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
  137. data/spec/stress/concurrent_consumers_stress_spec.rb +0 -71
  138. data/spec/stress/concurrent_publishers_stress_spec.rb +0 -54
  139. data/spec/stress/connection_open_close_spec.rb +0 -52
  140. data/spec/stress/long_running_consumer_spec.rb +0 -84
  141. data/spec/tls/ca_certificate.pem +0 -29
  142. data/spec/tls/ca_key.pem +0 -52
  143. data/spec/tls/client_certificate.pem +0 -29
  144. data/spec/tls/client_key.pem +0 -51
  145. data/spec/tls/generate-server-cert.sh +0 -8
  146. data/spec/tls/server-openssl.cnf +0 -10
  147. data/spec/tls/server.csr +0 -16
  148. data/spec/tls/server_certificate.pem +0 -29
  149. data/spec/tls/server_key.pem +0 -51
  150. data/spec/unit/bunny_spec.rb +0 -15
  151. data/spec/unit/concurrent/atomic_fixnum_spec.rb +0 -35
  152. data/spec/unit/concurrent/condition_spec.rb +0 -82
  153. data/spec/unit/concurrent/linked_continuation_queue_spec.rb +0 -35
  154. data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +0 -73
  155. data/spec/unit/exchange_recovery_spec.rb +0 -39
  156. data/spec/unit/version_delivery_tag_spec.rb +0 -28
@@ -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)
@@ -25,14 +25,41 @@ module Bunny
25
25
  DEFAULT_READ_TIMEOUT = 30.0
26
26
  DEFAULT_WRITE_TIMEOUT = 30.0
27
27
 
28
+ # mimics METHODS_MAP in ssl.rb but also lists TLS 1.3
29
+ # and string constants
30
+ TLS_VERSION_ALIASES = {
31
+ TLSv1: OpenSSL::SSL::TLS1_VERSION,
32
+ TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
33
+ TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
34
+ "1.0": OpenSSL::SSL::TLS1_VERSION,
35
+ "1.1": OpenSSL::SSL::TLS1_1_VERSION,
36
+ "1.2": OpenSSL::SSL::TLS1_2_VERSION,
37
+ OpenSSL::SSL::TLS1_VERSION => OpenSSL::SSL::TLS1_VERSION,
38
+ OpenSSL::SSL::TLS1_1_VERSION => OpenSSL::SSL::TLS1_1_VERSION,
39
+ OpenSSL::SSL::TLS1_2_VERSION => OpenSSL::SSL::TLS1_2_VERSION
40
+ }
41
+
42
+ # older OpenSSL versions won't support for TLS 1.3 and won't
43
+ # have this constant defined.
44
+ if defined?(OpenSSL::SSL::TLS1_3_VERSION)
45
+ TLS_VERSION_ALIASES["1.3"] = OpenSSL::SSL::TLS1_3_VERSION
46
+ TLS_VERSION_ALIASES[:TLSv1_3] = OpenSSL::SSL::TLS1_3_VERSION
47
+ TLS_VERSION_ALIASES[OpenSSL::SSL::TLS1_3_VERSION] = OpenSSL::SSL::TLS1_3_VERSION
48
+ end
49
+
50
+ TLS_VERSION_ALIASES.freeze
51
+
28
52
  attr_reader :session, :host, :port, :socket, :connect_timeout, :read_timeout, :write_timeout, :disconnect_timeout
29
- attr_reader :tls_context
53
+ attr_reader :tls_context, :verify_peer, :tls_ca_certificates, :tls_certificate_path, :tls_key_path
30
54
 
31
- attr_writer :read_timeout
55
+ def read_timeout=(v)
56
+ @read_timeout = v
57
+ @read_timeout = nil if @read_timeout == 0
58
+ end
32
59
 
33
60
  def initialize(session, host, port, opts)
34
61
  @session = session
35
- @session_thread = opts[:session_thread]
62
+ @session_error_handler = opts[:session_error_handler]
36
63
  @host = host
37
64
  @port = port
38
65
  @opts = opts
@@ -54,6 +81,8 @@ module Bunny
54
81
 
55
82
  @writes_mutex = @session.mutex_impl.new
56
83
 
84
+ @socket = nil
85
+
57
86
  prepare_tls_context(opts) if @tls_enabled
58
87
  end
59
88
 
@@ -77,10 +106,29 @@ module Bunny
77
106
 
78
107
 
79
108
  def connect
80
- if uses_ssl?
81
- @socket.connect
82
- if uses_tls? && @verify_peer
83
- @socket.post_connection_check(host)
109
+ if uses_tls?
110
+ begin
111
+ @socket.connect
112
+ rescue OpenSSL::SSL::SSLError => e
113
+ @logger.error { "TLS connection failed: #{e.message}" }
114
+ raise e
115
+ end
116
+
117
+ log_peer_certificate_info(Logger::DEBUG, @socket.peer_cert)
118
+ log_peer_certificate_chain_info(Logger::DEBUG, @socket.peer_cert_chain)
119
+
120
+ begin
121
+ @socket.post_connection_check(host) if @verify_peer
122
+ rescue OpenSSL::SSL::SSLError => e
123
+ @logger.error do
124
+ msg = "Peer verification of target server failed: #{e.message}. "
125
+ msg += "Target hostname: #{hostname}, see peer certificate chain details below."
126
+ msg
127
+ end
128
+ log_peer_certificate_info(Logger::ERROR, @socket.peer_cert)
129
+ log_peer_certificate_chain_info(Logger::ERROR, @socket.peer_cert_chain)
130
+
131
+ raise e
84
132
  end
85
133
 
86
134
  @status = :connected
@@ -122,7 +170,7 @@ module Bunny
122
170
  if @session.automatically_recover?
123
171
  @session.handle_network_failure(e)
124
172
  else
125
- @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
173
+ @session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
126
174
  end
127
175
  end
128
176
  end
@@ -146,24 +194,25 @@ module Bunny
146
194
  if @session.automatically_recover?
147
195
  @session.handle_network_failure(e)
148
196
  else
149
- @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
197
+ @session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
150
198
  end
151
199
  end
152
200
  end
153
201
  end
154
202
 
155
203
  # Writes data to the socket without timeout checks
156
- def write_without_timeout(data)
204
+ def write_without_timeout(data, raise_exceptions = false)
157
205
  begin
158
206
  @writes_mutex.synchronize { @socket.write(data) }
159
207
  @socket.flush
160
208
  rescue SystemCallError, Bunny::ConnectionError, IOError => e
161
209
  close
210
+ raise e if raise_exceptions
162
211
 
163
212
  if @session.automatically_recover?
164
213
  @session.handle_network_failure(e)
165
214
  else
166
- @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
215
+ @session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
167
216
  end
168
217
  end
169
218
  end
@@ -220,7 +269,7 @@ module Bunny
220
269
  if @session.automatically_recover?
221
270
  raise
222
271
  else
223
- @session_thread.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
272
+ @session_error_handler.raise(Bunny::NetworkFailure.new("detected a network failure: #{e.message}", e))
224
273
  end
225
274
  end
226
275
  end
@@ -298,17 +347,21 @@ module Bunny
298
347
  protected
299
348
 
300
349
  def tls_enabled?(opts)
301
- return opts[:tls] unless opts[:tls].nil?
302
- return opts[:ssl] unless opts[:ssl].nil?
350
+ return !!opts[:tls] unless opts[:tls].nil?
351
+ return !!opts[:ssl] unless opts[:ssl].nil?
303
352
  (opts[:port] == AMQ::Protocol::TLS_PORT) || false
304
353
  end
305
354
 
355
+ def tls_ca_certificates_paths_from(opts)
356
+ Array(opts[:cacertfile] || opts[:tls_ca_certificates] || opts[:ssl_ca_certificates])
357
+ end
358
+
306
359
  def tls_certificate_path_from(opts)
307
- opts[:tls_cert] || opts[:ssl_cert] || opts[:tls_cert_path] || opts[:ssl_cert_path] || opts[:tls_certificate_path] || opts[:ssl_certificate_path]
360
+ opts[:certfile] || opts[:tls_cert] || opts[:ssl_cert] || opts[:tls_cert_path] || opts[:ssl_cert_path] || opts[:tls_certificate_path] || opts[:ssl_certificate_path]
308
361
  end
309
362
 
310
363
  def tls_key_path_from(opts)
311
- opts[:tls_key] || opts[:ssl_key] || opts[:tls_key_path] || opts[:ssl_key_path]
364
+ opts[:keyfile] || opts[:tls_key] || opts[:ssl_key] || opts[:tls_key_path] || opts[:ssl_key_path]
312
365
  end
313
366
 
314
367
  def tls_certificate_from(opts)
@@ -327,6 +380,29 @@ module Bunny
327
380
  end
328
381
  end
329
382
 
383
+ def peer_certificate_info(peer_cert, prefix = "Peer's leaf certificate")
384
+ exts = peer_cert.extensions.map { |x| x.value }
385
+ # Subject Alternative Names
386
+ sans = exts.select { |s| s =~ /^DNS/ }.map { |s| s.gsub(/^DNS:/, "") }
387
+
388
+ msg = "#{prefix} subject: #{peer_cert.subject}, "
389
+ msg += "subject alternative names: #{sans.join(', ')}, "
390
+ msg += "issuer: #{peer_cert.issuer}, "
391
+ msg += "not valid after: #{peer_cert.not_after}, "
392
+ msg += "X.509 usage extensions: #{exts.join(', ')}"
393
+
394
+ msg
395
+ end
396
+
397
+ def log_peer_certificate_info(severity, peer_cert, prefix = "Peer's leaf certificate")
398
+ @logger.add(severity) { peer_certificate_info(peer_cert, prefix) }
399
+ end
400
+
401
+ def log_peer_certificate_chain_info(severity, chain)
402
+ chain.each do |cert|
403
+ self.log_peer_certificate_info(severity, cert, "Peer's certificate chain entry")
404
+ end
405
+ end
330
406
 
331
407
  def inline_client_certificate_from(opts)
332
408
  opts[:tls_certificate] || opts[:ssl_cert_string] || opts[:tls_cert]
@@ -337,7 +413,7 @@ module Bunny
337
413
  end
338
414
 
339
415
  def prepare_tls_context(opts)
340
- if (opts[:verify_ssl] || opts[:verify_peer]).nil?
416
+ if opts.values_at(:verify_ssl, :verify_peer, :verify).all?(&:nil?)
341
417
  opts[:verify_peer] = true
342
418
  end
343
419
 
@@ -349,12 +425,22 @@ module Bunny
349
425
  @tls_key = tls_key_from(opts)
350
426
  @tls_certificate_store = opts[:tls_certificate_store]
351
427
 
352
- @tls_ca_certificates = opts.fetch(:tls_ca_certificates, default_tls_certificates)
353
- @verify_peer = (opts[:verify_ssl] || opts[:verify_peer])
428
+ @verify_peer = as_boolean(opts[:verify_ssl] || opts[:verify_peer] || opts[:verify])
354
429
 
355
430
  @tls_context = initialize_tls_context(OpenSSL::SSL::SSLContext.new, opts)
356
431
  end
357
432
 
433
+ def as_boolean(val)
434
+ case val
435
+ when true then true
436
+ when false then false
437
+ when "true" then true
438
+ when "false" then false
439
+ else
440
+ !!val
441
+ end
442
+ end
443
+
358
444
  def wrap_in_tls_socket(socket)
359
445
  raise ArgumentError, "cannot wrap nil into TLS socket, @tls_context is nil. This is a Bunny bug." unless socket
360
446
  raise "cannot wrap a socket into TLS socket, @tls_context is nil. This is a Bunny bug." unless @tls_context
@@ -391,19 +477,22 @@ module Bunny
391
477
  end
392
478
  end
393
479
 
394
- def initialize_tls_context(ctx, opts={})
480
+ def initialize_tls_context(ctx, opts = {})
395
481
  ctx.cert = OpenSSL::X509::Certificate.new(@tls_certificate) if @tls_certificate
396
- ctx.key = OpenSSL::PKey::RSA.new(@tls_key) if @tls_key
482
+ ctx.key = OpenSSL::PKey.read(@tls_key) if @tls_key
397
483
  ctx.cert_store = if @tls_certificate_store
398
484
  @tls_certificate_store
399
485
  else
486
+ # this ivar exists so that this value can be exposed in the API
487
+ @tls_ca_certificates = tls_ca_certificates_paths_from(opts)
400
488
  initialize_tls_certificate_store(@tls_ca_certificates)
401
489
  end
490
+ should_silence_warnings = opts.fetch(:tls_silence_warnings, false)
402
491
 
403
- if !@tls_certificate
492
+ if !@tls_certificate && !should_silence_warnings
404
493
  @logger.warn <<-MSG
405
- Using TLS but no client certificate is provided! If RabbitMQ is configured to verify peer
406
- certificate, connection upgrade will fail!
494
+ Using TLS but no client certificate is provided. If RabbitMQ is configured to require & verify peer
495
+ certificate, connection will be rejected. Learn more at https://www.rabbitmq.com/ssl.html
407
496
  MSG
408
497
  end
409
498
  if @tls_certificate && !@tls_key
@@ -415,45 +504,33 @@ certificate, connection upgrade will fail!
415
504
  else
416
505
  OpenSSL::SSL::VERIFY_NONE
417
506
  end
507
+ @logger.debug { "Will use peer verification mode #{verify_mode}" }
418
508
  ctx.verify_mode = verify_mode
419
509
 
420
- if !@verify_peer
510
+ if !@verify_peer && !should_silence_warnings
421
511
  @logger.warn <<-MSG
422
512
  Using TLS but peer hostname verification is disabled. This is convenient for local development
423
- but prone to man-in-the-middle attacks. Please set verify_peer: true in production!
513
+ but prone to man-in-the-middle attacks. Please set verify_peer: true in production. Learn more at https://www.rabbitmq.com/ssl.html
424
514
  MSG
425
515
  end
426
516
 
427
- ssl_version = opts[:tls_protocol] || opts[:ssl_version]
428
- ctx.ssl_version = ssl_version if ssl_version
517
+ ssl_version = opts[:tls_protocol] || opts[:ssl_version] || :TLSv1_2
518
+ if ssl_version
519
+ v = tls_version_constant(ssl_version)
520
+ ctx.min_version = v
521
+ ctx.max_version = v
522
+ end
429
523
 
430
524
  ctx
431
525
  end
432
526
 
433
- def default_tls_certificates
434
- if defined?(JRUBY_VERSION)
435
- # see https://github.com/jruby/jruby/issues/1055. MK.
436
- []
437
- else
438
- default_ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE
439
- default_ca_path = ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR
440
-
441
- [
442
- default_ca_file,
443
- File.join(default_ca_path, 'ca-certificates.crt'), # Ubuntu/Debian
444
- File.join(default_ca_path, 'ca-bundle.crt'), # Amazon Linux & Fedora/RHEL
445
- File.join(default_ca_path, 'ca-bundle.pem') # OpenSUSE
446
- ].uniq
447
- end
448
- end
449
-
450
527
  def initialize_tls_certificate_store(certs)
451
528
  cert_files = []
452
529
  cert_inlines = []
453
530
  certs.each do |cert|
454
531
  # if it starts with / or C:/ then it's a file path that may or may not
455
532
  # exist (e.g. a default OpenSSL path). MK.
456
- if File.readable?(cert) || cert =~ /^([a-z]:?)?\//i
533
+ if File.readable?(cert) || cert =~ /\A([a-z]:?)?\//i
457
534
  cert_files.push(cert)
458
535
  else
459
536
  cert_inlines.push(cert)
@@ -461,10 +538,8 @@ but prone to man-in-the-middle attacks. Please set verify_peer: true in producti
461
538
  end
462
539
  @logger.debug { "Using CA certificates at #{cert_files.join(', ')}" }
463
540
  @logger.debug { "Using #{cert_inlines.count} inline CA certificates" }
464
- if certs.empty?
465
- @logger.error "No CA certificates found, add one with :tls_ca_certificates"
466
- end
467
541
  OpenSSL::X509::Store.new.tap do |store|
542
+ store.set_default_paths
468
543
  cert_files.select { |path| File.readable?(path) }.
469
544
  each { |path| store.add_file(path) }
470
545
  cert_inlines.
@@ -472,6 +547,14 @@ but prone to man-in-the-middle attacks. Please set verify_peer: true in producti
472
547
  end
473
548
  end
474
549
 
550
+
551
+ def tls_version_constant(value)
552
+ # OpenSSL::SSL::TLS1_3_VERSION and similar constants
553
+ # are just integers, so use the value itself as fallback since
554
+ # there is no class to case switch on
555
+ TLS_VERSION_ALIASES[value] || value
556
+ end
557
+
475
558
  def timeout_from(options)
476
559
  options[:connect_timeout] || options[:connection_timeout] || options[:timeout] || DEFAULT_CONNECTION_TIMEOUT
477
560
  end
data/lib/bunny/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "2.7.4"
5
+ VERSION = "2.22.0"
6
6
  end
data/lib/bunny.rb CHANGED
@@ -53,17 +53,58 @@ module Bunny
53
53
  # Instantiates a new connection. The actual network
54
54
  # connection is started with {Bunny::Session#start}
55
55
  #
56
+ # @param [String, Hash] connection_string_or_opts Connection string or a hash of connection options
57
+ # @param [Hash] optz Extra options not related to connection
58
+ #
59
+ # @option connection_string_or_opts [String] :host ("127.0.0.1") Hostname or IP address to connect to
60
+ # @option connection_string_or_opts [Array<String>] :hosts (["127.0.0.1"]) list of hostname or IP addresses to select hostname from when connecting
61
+ # @option connection_string_or_opts [Array<String>] :addresses (["127.0.0.1:5672"]) list of addresses to select hostname and port from when connecting
62
+ # @option connection_string_or_opts [Integer] :port (5672) Port RabbitMQ listens on
63
+ # @option connection_string_or_opts [String] :username ("guest") Username
64
+ # @option connection_string_or_opts [String] :password ("guest") Password
65
+ # @option connection_string_or_opts [String] :vhost ("/") Virtual host to use
66
+ # @option connection_string_or_opts [Integer, Symbol] :heartbeat (:server) Heartbeat timeout to offer to the server. :server means use the value suggested by RabbitMQ. 0 means heartbeats and socket read timeouts will be disabled (not recommended).
67
+ # @option connection_string_or_opts [Integer] :network_recovery_interval (4) Recovery interval periodic network recovery will use. This includes initial pause after network failure.
68
+ # @option connection_string_or_opts [Boolean] :tls (false) Should TLS/SSL be used?
69
+ # @option connection_string_or_opts [String] :tls_cert (nil) Path to client TLS/SSL certificate file (.pem)
70
+ # @option connection_string_or_opts [String] :tls_key (nil) Path to client TLS/SSL private key file (.pem)
71
+ # @option connection_string_or_opts [Array<String>] :tls_ca_certificates Array of paths to TLS/SSL CA files (.pem), by default detected from OpenSSL configuration
72
+ # @option connection_string_or_opts [String] :verify_peer (true) Whether TLS peer verification should be performed
73
+ # @option connection_string_or_opts [Symbol] :tls_protocol (negotiated) What TLS version should be used (:TLSv1, :TLSv1_1, or :TLSv1_2)
74
+ # @option connection_string_or_opts [Integer] :channel_max (2047) Maximum number of channels allowed on this connection, minus 1 to account for the special channel 0.
75
+ # @option connection_string_or_opts [Integer] :continuation_timeout (15000) Timeout for client operations that expect a response (e.g. {Bunny::Queue#get}), in milliseconds.
76
+ # @option connection_string_or_opts [Integer] :connection_timeout (30) Timeout in seconds for connecting to the server.
77
+ # @option connection_string_or_opts [Integer] :read_timeout (30) TCP socket read timeout in seconds. If heartbeats are disabled this will be ignored.
78
+ # @option connection_string_or_opts [Integer] :write_timeout (30) TCP socket write timeout in seconds.
79
+ # @option connection_string_or_opts [Proc] :hosts_shuffle_strategy a callable that reorders a list of host strings, defaults to Array#shuffle
80
+ # @option connection_string_or_opts [Proc] :recovery_completed a callable that will be called when a network recovery is performed
81
+ # @option connection_string_or_opts [Logger] :logger The logger. If missing, one is created using :log_file and :log_level.
82
+ # @option connection_string_or_opts [IO, String] :log_file The file or path to use when creating a logger. Defaults to STDOUT.
83
+ # @option connection_string_or_opts [IO, String] :logfile DEPRECATED: use :log_file instead. The file or path to use when creating a logger. Defaults to STDOUT.
84
+ # @option connection_string_or_opts [Integer] :log_level The log level to use when creating a logger. Defaults to LOGGER::WARN
85
+ # @option connection_string_or_opts [Boolean] :automatically_recover (true) Should automatically recover from network failures?
86
+ # @option connection_string_or_opts [Integer] :recovery_attempts (nil) Max number of recovery attempts, nil means forever
87
+ # @option connection_string_or_opts [Integer] :reset_recovery_attempts_after_reconnection (true) Should recovery attempt counter be reset after successful reconnection? When set to false, the attempt counter will last through the entire lifetime of the connection object.
88
+ # @option connection_string_or_opts [Proc] :recovery_attempt_started (nil) Will be called before every connection recovery attempt
89
+ # @option connection_string_or_opts [Proc] :recovery_completed (nil) Will be called after successful connection recovery
90
+ # @option connection_string_or_opts [Boolean] :recover_from_connection_close (true) Should this connection recover after receiving a server-sent connection.close (e.g. connection was force closed)?
91
+ # @option connection_string_or_opts [Object] :session_error_handler (Thread.current) Object which responds to #raise that will act as a session error handler. Defaults to Thread.current, which will raise asynchronous exceptions in the thread that created the session.
92
+ #
93
+ # @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
94
+ # @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
95
+ # @option optz [String] :connection_name (nil) Client-provided connection name, if any. Note that the value returned does not uniquely identify a connection and cannot be used as a connection identifier in HTTP API requests.
96
+ #
56
97
  # @return [Bunny::Session]
57
98
  # @see Bunny::Session#start
58
99
  # @see http://rubybunny.info/articles/getting_started.html
59
100
  # @see http://rubybunny.info/articles/connecting.html
60
101
  # @api public
61
- def self.new(connection_string_or_opts = ENV['RABBITMQ_URL'], opts = {})
62
- if connection_string_or_opts.respond_to?(:keys) && opts.empty?
63
- opts = connection_string_or_opts
102
+ def self.new(connection_string_or_opts = ENV['RABBITMQ_URL'], optz = {})
103
+ if connection_string_or_opts.respond_to?(:keys) && optz.empty?
104
+ optz = connection_string_or_opts
64
105
  end
65
106
 
66
- conn = Session.new(connection_string_or_opts, opts)
107
+ conn = Session.new(connection_string_or_opts, optz)
67
108
  @default_connection ||= conn
68
109
 
69
110
  conn