bunny 2.7.4 → 2.22.0

Sign up to get free protection for your applications and to get access to all the features.
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