bunny 2.14.2 → 2.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +56 -39
  3. data/lib/bunny/channel.rb +89 -13
  4. data/lib/bunny/consumer.rb +2 -2
  5. data/lib/bunny/consumer_work_pool.rb +1 -1
  6. data/lib/bunny/cruby/socket.rb +3 -0
  7. data/lib/bunny/delivery_info.rb +1 -1
  8. data/lib/bunny/queue.rb +36 -5
  9. data/lib/bunny/reader_loop.rb +21 -13
  10. data/lib/bunny/session.rb +83 -25
  11. data/lib/bunny/transport.rb +49 -12
  12. data/lib/bunny/version.rb +1 -1
  13. data/lib/bunny.rb +45 -4
  14. metadata +37 -235
  15. data/.github/ISSUE_TEMPLATE.md +0 -18
  16. data/.gitignore +0 -28
  17. data/.rspec +0 -1
  18. data/.travis.yml +0 -31
  19. data/.yardopts +0 -8
  20. data/CONTRIBUTING.md +0 -132
  21. data/ChangeLog.md +0 -2072
  22. data/Gemfile +0 -55
  23. data/LICENSE +0 -21
  24. data/Rakefile +0 -54
  25. data/benchmarks/basic_publish/with_128K_messages.rb +0 -35
  26. data/benchmarks/basic_publish/with_1k_messages.rb +0 -35
  27. data/benchmarks/basic_publish/with_4K_messages.rb +0 -35
  28. data/benchmarks/basic_publish/with_64K_messages.rb +0 -35
  29. data/benchmarks/channel_open.rb +0 -28
  30. data/benchmarks/mutex_and_monitor.rb +0 -42
  31. data/benchmarks/queue_declare.rb +0 -29
  32. data/benchmarks/queue_declare_and_bind.rb +0 -29
  33. data/benchmarks/queue_declare_bind_and_delete.rb +0 -29
  34. data/benchmarks/synchronized_sorted_set.rb +0 -53
  35. data/benchmarks/write_vs_write_nonblock.rb +0 -49
  36. data/bunny.gemspec +0 -34
  37. data/docker/Dockerfile +0 -20
  38. data/docker/apt/preferences.d/erlang +0 -3
  39. data/docker/apt/sources.list.d/bintray.rabbitmq.list +0 -2
  40. data/docker/docker-entrypoint.sh +0 -26
  41. data/docker/rabbitmq.conf +0 -29
  42. data/docker-compose.yml +0 -28
  43. data/examples/connection/authentication_failure.rb +0 -16
  44. data/examples/connection/automatic_recovery_with_basic_get.rb +0 -40
  45. data/examples/connection/automatic_recovery_with_client_named_queues.rb +0 -36
  46. data/examples/connection/automatic_recovery_with_multiple_consumers.rb +0 -46
  47. data/examples/connection/automatic_recovery_with_republishing.rb +0 -109
  48. data/examples/connection/automatic_recovery_with_server_named_queues.rb +0 -35
  49. data/examples/connection/channel_level_exception.rb +0 -27
  50. data/examples/connection/disabled_automatic_recovery.rb +0 -34
  51. data/examples/connection/heartbeat.rb +0 -17
  52. data/examples/connection/manually_reconnecting_consumer.rb +0 -23
  53. data/examples/connection/manually_reconnecting_publisher.rb +0 -28
  54. data/examples/connection/unknown_host.rb +0 -16
  55. data/examples/consumers/high_and_low_priority.rb +0 -50
  56. data/examples/guides/exchanges/direct_exchange_routing.rb +0 -36
  57. data/examples/guides/exchanges/fanout_exchange_routing.rb +0 -28
  58. data/examples/guides/exchanges/headers_exchange_routing.rb +0 -31
  59. data/examples/guides/exchanges/mandatory_messages.rb +0 -30
  60. data/examples/guides/extensions/alternate_exchange.rb +0 -30
  61. data/examples/guides/extensions/basic_nack.rb +0 -33
  62. data/examples/guides/extensions/connection_blocked.rb +0 -35
  63. data/examples/guides/extensions/consumer_cancellation_notification.rb +0 -39
  64. data/examples/guides/extensions/dead_letter_exchange.rb +0 -32
  65. data/examples/guides/extensions/exchange_to_exchange_bindings.rb +0 -29
  66. data/examples/guides/extensions/per_message_ttl.rb +0 -36
  67. data/examples/guides/extensions/per_queue_message_ttl.rb +0 -36
  68. data/examples/guides/extensions/publisher_confirms.rb +0 -28
  69. data/examples/guides/extensions/queue_lease.rb +0 -26
  70. data/examples/guides/extensions/sender_selected_distribution.rb +0 -32
  71. data/examples/guides/getting_started/blabbr.rb +0 -27
  72. data/examples/guides/getting_started/hello_world.rb +0 -22
  73. data/examples/guides/getting_started/weathr.rb +0 -49
  74. data/examples/guides/queues/one_off_consumer.rb +0 -25
  75. data/examples/guides/queues/redeliveries.rb +0 -81
  76. data/profiling/basic_publish/with_4K_messages.rb +0 -33
  77. data/repl +0 -3
  78. data/spec/config/enabled_plugins +0 -1
  79. data/spec/config/rabbitmq.conf +0 -13
  80. data/spec/higher_level_api/integration/basic_ack_spec.rb +0 -230
  81. data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -142
  82. data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -349
  83. data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +0 -54
  84. data/spec/higher_level_api/integration/basic_get_spec.rb +0 -80
  85. data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -82
  86. data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -74
  87. data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -57
  88. data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -152
  89. data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
  90. data/spec/higher_level_api/integration/channel_close_spec.rb +0 -66
  91. data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
  92. data/spec/higher_level_api/integration/connection_recovery_spec.rb +0 -483
  93. data/spec/higher_level_api/integration/connection_spec.rb +0 -563
  94. data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -83
  95. data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
  96. data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -75
  97. data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
  98. data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -237
  99. data/spec/higher_level_api/integration/exchange_delete_spec.rb +0 -105
  100. data/spec/higher_level_api/integration/exchange_unbind_spec.rb +0 -40
  101. data/spec/higher_level_api/integration/exclusive_queue_spec.rb +0 -28
  102. data/spec/higher_level_api/integration/heartbeat_spec.rb +0 -49
  103. data/spec/higher_level_api/integration/message_properties_access_spec.rb +0 -95
  104. data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +0 -24
  105. data/spec/higher_level_api/integration/publisher_confirms_spec.rb +0 -191
  106. data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -87
  107. data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
  108. data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -285
  109. data/spec/higher_level_api/integration/queue_delete_spec.rb +0 -41
  110. data/spec/higher_level_api/integration/queue_purge_spec.rb +0 -30
  111. data/spec/higher_level_api/integration/queue_unbind_spec.rb +0 -54
  112. data/spec/higher_level_api/integration/read_only_consumer_spec.rb +0 -60
  113. data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +0 -36
  114. data/spec/higher_level_api/integration/tls_connection_spec.rb +0 -250
  115. data/spec/higher_level_api/integration/toxiproxy_spec.rb +0 -76
  116. data/spec/higher_level_api/integration/tx_commit_spec.rb +0 -21
  117. data/spec/higher_level_api/integration/tx_rollback_spec.rb +0 -21
  118. data/spec/higher_level_api/integration/with_channel_spec.rb +0 -25
  119. data/spec/issues/issue100_spec.rb +0 -42
  120. data/spec/issues/issue141_spec.rb +0 -43
  121. data/spec/issues/issue202_spec.rb +0 -15
  122. data/spec/issues/issue224_spec.rb +0 -40
  123. data/spec/issues/issue465_spec.rb +0 -32
  124. data/spec/issues/issue549_spec.rb +0 -30
  125. data/spec/issues/issue78_spec.rb +0 -72
  126. data/spec/issues/issue83_spec.rb +0 -30
  127. data/spec/issues/issue97_attachment.json +0 -1
  128. data/spec/issues/issue97_spec.rb +0 -175
  129. data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -83
  130. data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -99
  131. data/spec/spec_helper.rb +0 -47
  132. data/spec/stress/channel_close_stress_spec.rb +0 -64
  133. data/spec/stress/channel_open_stress_spec.rb +0 -84
  134. data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
  135. data/spec/stress/concurrent_consumers_stress_spec.rb +0 -71
  136. data/spec/stress/concurrent_publishers_stress_spec.rb +0 -54
  137. data/spec/stress/connection_open_close_spec.rb +0 -52
  138. data/spec/stress/merry_go_round_spec.rb +0 -105
  139. data/spec/tls/ca_certificate.pem +0 -29
  140. data/spec/tls/ca_key.pem +0 -52
  141. data/spec/tls/client_certificate.pem +0 -29
  142. data/spec/tls/client_key.pem +0 -51
  143. data/spec/tls/generate-server-cert.sh +0 -8
  144. data/spec/tls/server-openssl.cnf +0 -10
  145. data/spec/tls/server.csr +0 -16
  146. data/spec/tls/server_certificate.pem +0 -29
  147. data/spec/tls/server_key.pem +0 -51
  148. data/spec/toxiproxy_helper.rb +0 -28
  149. data/spec/unit/bunny_spec.rb +0 -15
  150. data/spec/unit/concurrent/atomic_fixnum_spec.rb +0 -35
  151. data/spec/unit/concurrent/condition_spec.rb +0 -82
  152. data/spec/unit/concurrent/linked_continuation_queue_spec.rb +0 -35
  153. data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +0 -73
  154. data/spec/unit/exchange_recovery_spec.rb +0 -39
  155. data/spec/unit/version_delivery_tag_spec.rb +0 -28
data/lib/bunny/session.rb CHANGED
@@ -71,6 +71,7 @@ module Bunny
71
71
  # Default reconnection interval for TCP connection failures
72
72
  DEFAULT_NETWORK_RECOVERY_INTERVAL = 5.0
73
73
 
74
+ DEFAULT_RECOVERABLE_EXCEPTIONS = [StandardError, TCPConnectionFailedForAllHosts, TCPConnectionFailed, AMQ::Protocol::EmptyResponseError, SystemCallError, Timeout::Error, Bunny::ConnectionLevelException, Bunny::ConnectionClosedError]
74
75
 
75
76
  #
76
77
  # API
@@ -89,7 +90,9 @@ module Bunny
89
90
  # @return [Integer] Timeout for blocking protocol operations (queue.declare, queue.bind, etc), in milliseconds. Default is 15000.
90
91
  attr_reader :continuation_timeout
91
92
  attr_reader :network_recovery_interval
93
+ attr_reader :connection_name
92
94
  attr_accessor :socket_configurator
95
+ attr_accessor :recoverable_exceptions
93
96
 
94
97
  # @param [String, Hash] connection_string_or_opts Connection string or a hash of connection options
95
98
  # @param [Hash] optz Extra options not related to connection
@@ -108,7 +111,7 @@ module Bunny
108
111
  # @option connection_string_or_opts [String] :tls_key (nil) Path to client TLS/SSL private key file (.pem)
109
112
  # @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
110
113
  # @option connection_string_or_opts [String] :verify_peer (true) Whether TLS peer verification should be performed
111
- # @option connection_string_or_opts [Symbol] :tls_version (negotiated) What TLS version should be used (:TLSv1, :TLSv1_1, or :TLSv1_2)
114
+ # @option connection_string_or_opts [Symbol] :tls_protocol (negotiated) What TLS version should be used (:TLSv1, :TLSv1_1, or :TLSv1_2)
112
115
  # @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.
113
116
  # @option connection_string_or_opts [Integer] :continuation_timeout (15000) Timeout for client operations that expect a response (e.g. {Bunny::Queue#get}), in milliseconds.
114
117
  # @option connection_string_or_opts [Integer] :connection_timeout (30) Timeout in seconds for connecting to the server.
@@ -123,10 +126,15 @@ module Bunny
123
126
  # @option connection_string_or_opts [Boolean] :automatically_recover (true) Should automatically recover from network failures?
124
127
  # @option connection_string_or_opts [Integer] :recovery_attempts (nil) Max number of recovery attempts, nil means forever
125
128
  # @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.
129
+ # @option connection_string_or_opts [Proc] :recovery_attempt_started (nil) Will be called before every connection recovery attempt
130
+ # @option connection_string_or_opts [Proc] :recovery_completed (nil) Will be called after successful connection recovery
131
+ # @option connection_string_or_opts [Proc] :recovery_attempts_exhausted (nil) Will be called when the connection recovery failed after the specified amount of recovery attempts
126
132
  # @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)?
133
+ # @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.
127
134
  #
128
135
  # @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
129
136
  # @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
137
+ # @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.
130
138
  #
131
139
  # @see http://rubybunny.info/articles/connecting.html Connecting to RabbitMQ guide
132
140
  # @see http://rubybunny.info/articles/tls.html TLS/SSL guide
@@ -169,8 +177,9 @@ module Bunny
169
177
  @automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil?
170
178
  true
171
179
  else
172
- opts[:automatically_recover] || opts[:automatic_recovery]
180
+ opts[:automatically_recover] | opts[:automatic_recovery]
173
181
  end
182
+ @recovering_from_network_failure = false
174
183
  @max_recovery_attempts = opts[:recovery_attempts]
175
184
  @recovery_attempts = @max_recovery_attempts
176
185
  # When this is set, connection attempts won't be reset after
@@ -184,6 +193,7 @@ module Bunny
184
193
  @continuation_timeout = opts.fetch(:continuation_timeout, DEFAULT_CONTINUATION_TIMEOUT)
185
194
 
186
195
  @status = :not_connected
196
+ @manually_closed = false
187
197
  @blocked = false
188
198
 
189
199
  # these are negotiated with the broker during the connection tuning phase
@@ -195,7 +205,9 @@ module Bunny
195
205
  @client_heartbeat = self.heartbeat_from(opts)
196
206
 
197
207
  client_props = opts[:properties] || opts[:client_properties] || {}
208
+ @connection_name = client_props[:connection_name] || opts[:connection_name]
198
209
  @client_properties = DEFAULT_CLIENT_PROPERTIES.merge(client_props)
210
+ .merge(connection_name: connection_name)
199
211
  @mechanism = normalize_auth_mechanism(opts.fetch(:auth_mechanism, "PLAIN"))
200
212
  @credentials_encoder = credentials_encoder_for(@mechanism)
201
213
  @locale = @opts.fetch(:locale, DEFAULT_LOCALE)
@@ -211,9 +223,14 @@ module Bunny
211
223
  @address_index_mutex = @mutex_impl.new
212
224
 
213
225
  @channels = Hash.new
214
- @recovery_completed = opts[:recovery_completed]
215
226
 
216
- @origin_thread = Thread.current
227
+ @recovery_attempt_started = opts[:recovery_attempt_started]
228
+ @recovery_completed = opts[:recovery_completed]
229
+ @recovery_attempts_exhausted = opts[:recovery_attempts_exhausted]
230
+
231
+ @session_error_handler = opts.fetch(:session_error_handler, Thread.current)
232
+
233
+ @recoverable_exceptions = DEFAULT_RECOVERABLE_EXCEPTIONS.dup
217
234
 
218
235
  self.reset_continuations
219
236
  self.initialize_transport
@@ -340,6 +357,14 @@ module Bunny
340
357
  self
341
358
  end
342
359
 
360
+ def update_secret(value, reason)
361
+ @transport.send_frame(AMQ::Protocol::Connection::UpdateSecret.encode(value, reason))
362
+ @last_update_secret_ok = wait_on_continuations
363
+ raise_if_continuation_resulted_in_a_connection_error!
364
+
365
+ @last_update_secret_ok
366
+ end
367
+
343
368
  # Socket operation write timeout used by this connection
344
369
  # @return [Integer]
345
370
  # @private
@@ -518,6 +543,24 @@ module Bunny
518
543
  end
519
544
  end
520
545
 
546
+ # Defines a callable (e.g. a block) that will be called
547
+ # before every connection recovery attempt.
548
+ def before_recovery_attempt_starts(&block)
549
+ @recovery_attempt_started = block
550
+ end
551
+
552
+ # Defines a callable (e.g. a block) that will be called
553
+ # after successful connection recovery.
554
+ def after_recovery_completed(&block)
555
+ @recovery_completed = block
556
+ end
557
+
558
+ # Defines a callable (e.g. a block) that will be called
559
+ # when the connection recovery failed after the specified
560
+ # numbers of recovery attempts.
561
+ def after_recovery_attempts_exhausted(&block)
562
+ @recovery_attempts_exhausted = block
563
+ end
521
564
 
522
565
  #
523
566
  # Implementation
@@ -630,6 +673,8 @@ module Bunny
630
673
  when AMQ::Protocol::Connection::Unblocked then
631
674
  @blocked = false
632
675
  @unblock_callback.call(method) if @unblock_callback
676
+ when AMQ::Protocol::Connection::UpdateSecretOk then
677
+ @continuations.push(method)
633
678
  when AMQ::Protocol::Channel::Close then
634
679
  begin
635
680
  ch = synchronised_find_channel(ch_number)
@@ -638,8 +683,12 @@ module Bunny
638
683
  # avoid doing that while holding a mutex lock. MK.
639
684
  ch.handle_method(method)
640
685
  ensure
641
- # synchronises on @channel_mutex under the hood
642
- self.unregister_channel(ch)
686
+ if ch.nil?
687
+ @logger.warn "Received a server-sent channel.close but the channel was not found locally. Ignoring the frame."
688
+ else
689
+ # synchronises on @channel_mutex under the hood
690
+ self.unregister_channel(ch)
691
+ end
643
692
  end
644
693
  when AMQ::Protocol::Basic::GetEmpty then
645
694
  ch = find_channel(ch_number)
@@ -710,9 +759,7 @@ module Bunny
710
759
 
711
760
  # @private
712
761
  def recoverable_network_failure?(exception)
713
- # No reasonably smart strategy was suggested in a few years.
714
- # So just recover unconditionally. MK.
715
- true
762
+ @recoverable_exceptions.any? {|x| exception.kind_of? x}
716
763
  end
717
764
 
718
765
  # @private
@@ -733,6 +780,7 @@ module Bunny
733
780
  def recover_from_network_failure
734
781
  sleep @network_recovery_interval
735
782
  @logger.debug "Will attempt connection recovery..."
783
+ notify_of_recovery_attempt_start
736
784
 
737
785
  self.initialize_transport
738
786
 
@@ -756,19 +804,23 @@ module Bunny
756
804
  rescue HostListDepleted
757
805
  reset_address_index
758
806
  retry
759
- rescue TCPConnectionFailedForAllHosts, TCPConnectionFailed, AMQ::Protocol::EmptyResponseError, SystemCallError, Timeout::Error => e
760
- @logger.warn "TCP connection failed, reconnecting in #{@network_recovery_interval} seconds"
761
- if should_retry_recovery?
762
- decrement_recovery_attemp_counter!
763
- if recoverable_network_failure?(e)
807
+ rescue => e
808
+ if recoverable_network_failure?(e)
809
+ @logger.warn "TCP connection failed"
810
+ if should_retry_recovery?
811
+ @logger.warn "Reconnecting in #{@network_recovery_interval} seconds"
812
+ decrement_recovery_attemp_counter!
764
813
  announce_network_failure_recovery
765
814
  retry
815
+ else
816
+ @logger.error "Ran out of recovery attempts (limit set to #{@max_recovery_attempts}), giving up"
817
+ @transport.close
818
+ self.close(false)
819
+ @manually_closed = false
820
+ notify_of_recovery_attempts_exhausted
766
821
  end
767
822
  else
768
- @logger.error "Ran out of recovery attempts (limit set to #{@max_recovery_attempts}), giving up"
769
- @transport.close
770
- self.close(false)
771
- @manually_closed = false
823
+ raise e
772
824
  end
773
825
  end
774
826
 
@@ -806,8 +858,9 @@ module Bunny
806
858
  end
807
859
  end
808
860
 
809
- def after_recovery_completed(&block)
810
- @recovery_completed = block
861
+ # @private
862
+ def notify_of_recovery_attempt_start
863
+ @recovery_attempt_started.call if @recovery_attempt_started
811
864
  end
812
865
 
813
866
  # @private
@@ -815,6 +868,11 @@ module Bunny
815
868
  @recovery_completed.call if @recovery_completed
816
869
  end
817
870
 
871
+ # @private
872
+ def notify_of_recovery_attempts_exhausted
873
+ @recovery_attempts_exhausted.call if @recovery_attempts_exhausted
874
+ end
875
+
818
876
  # @private
819
877
  def instantiate_connection_level_exception(frame)
820
878
  case frame
@@ -850,7 +908,7 @@ module Bunny
850
908
 
851
909
  clean_up_on_shutdown
852
910
  if threaded?
853
- @origin_thread.raise(@last_connection_error)
911
+ @session_error_handler.raise(@last_connection_error)
854
912
  else
855
913
  raise @last_connection_error
856
914
  end
@@ -1007,7 +1065,7 @@ module Bunny
1007
1065
 
1008
1066
  # @private
1009
1067
  def reader_loop
1010
- @reader_loop ||= ReaderLoop.new(@transport, self, Thread.current)
1068
+ @reader_loop ||= ReaderLoop.new(@transport, self, @session_error_handler)
1011
1069
  end
1012
1070
 
1013
1071
  # @private
@@ -1270,7 +1328,7 @@ module Bunny
1270
1328
  end
1271
1329
 
1272
1330
  if threaded?
1273
- @origin_thread.raise(e)
1331
+ @session_error_handler.raise(e)
1274
1332
  else
1275
1333
  raise e
1276
1334
  end
@@ -1316,8 +1374,8 @@ module Bunny
1316
1374
  @transport = Transport.new(self,
1317
1375
  host_from_address(address),
1318
1376
  port_from_address(address),
1319
- @opts.merge(:session_thread => @origin_thread)
1320
- )
1377
+ @opts.merge(:session_error_handler => @session_error_handler)
1378
+ )
1321
1379
 
1322
1380
  # Reset the cached progname for the logger only when no logger was provided
1323
1381
  @default_logger.progname = self.to_s
@@ -25,6 +25,30 @@ 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
53
  attr_reader :tls_context, :verify_peer, :tls_ca_certificates, :tls_certificate_path, :tls_key_path
30
54
 
@@ -35,7 +59,7 @@ module Bunny
35
59
 
36
60
  def initialize(session, host, port, opts)
37
61
  @session = session
38
- @session_thread = opts[:session_thread]
62
+ @session_error_handler = opts[:session_error_handler]
39
63
  @host = host
40
64
  @port = port
41
65
  @opts = opts
@@ -146,7 +170,7 @@ module Bunny
146
170
  if @session.automatically_recover?
147
171
  @session.handle_network_failure(e)
148
172
  else
149
- @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))
150
174
  end
151
175
  end
152
176
  end
@@ -170,7 +194,7 @@ module Bunny
170
194
  if @session.automatically_recover?
171
195
  @session.handle_network_failure(e)
172
196
  else
173
- @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))
174
198
  end
175
199
  end
176
200
  end
@@ -188,7 +212,7 @@ module Bunny
188
212
  if @session.automatically_recover?
189
213
  @session.handle_network_failure(e)
190
214
  else
191
- @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))
192
216
  end
193
217
  end
194
218
  end
@@ -245,7 +269,7 @@ module Bunny
245
269
  if @session.automatically_recover?
246
270
  raise
247
271
  else
248
- @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))
249
273
  end
250
274
  end
251
275
  end
@@ -453,9 +477,9 @@ module Bunny
453
477
  end
454
478
  end
455
479
 
456
- def initialize_tls_context(ctx, opts={})
480
+ def initialize_tls_context(ctx, opts = {})
457
481
  ctx.cert = OpenSSL::X509::Certificate.new(@tls_certificate) if @tls_certificate
458
- ctx.key = OpenSSL::PKey::RSA.new(@tls_key) if @tls_key
482
+ ctx.key = OpenSSL::PKey.read(@tls_key) if @tls_key
459
483
  ctx.cert_store = if @tls_certificate_store
460
484
  @tls_certificate_store
461
485
  else
@@ -463,8 +487,9 @@ module Bunny
463
487
  @tls_ca_certificates = tls_ca_certificates_paths_from(opts)
464
488
  initialize_tls_certificate_store(@tls_ca_certificates)
465
489
  end
490
+ should_silence_warnings = opts.fetch(:tls_silence_warnings, false)
466
491
 
467
- if !@tls_certificate
492
+ if !@tls_certificate && !should_silence_warnings
468
493
  @logger.warn <<-MSG
469
494
  Using TLS but no client certificate is provided. If RabbitMQ is configured to require & verify peer
470
495
  certificate, connection will be rejected. Learn more at https://www.rabbitmq.com/ssl.html
@@ -482,15 +507,19 @@ certificate, connection will be rejected. Learn more at https://www.rabbitmq.com
482
507
  @logger.debug { "Will use peer verification mode #{verify_mode}" }
483
508
  ctx.verify_mode = verify_mode
484
509
 
485
- if !@verify_peer
510
+ if !@verify_peer && !should_silence_warnings
486
511
  @logger.warn <<-MSG
487
512
  Using TLS but peer hostname verification is disabled. This is convenient for local development
488
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
489
514
  MSG
490
515
  end
491
516
 
492
- ssl_version = opts[:tls_protocol] || opts[:ssl_version]
493
- 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
494
523
 
495
524
  ctx
496
525
  end
@@ -501,7 +530,7 @@ but prone to man-in-the-middle attacks. Please set verify_peer: true in producti
501
530
  certs.each do |cert|
502
531
  # if it starts with / or C:/ then it's a file path that may or may not
503
532
  # exist (e.g. a default OpenSSL path). MK.
504
- if File.readable?(cert) || cert =~ /^([a-z]:?)?\//i
533
+ if File.readable?(cert) || cert =~ /\A([a-z]:?)?\//i
505
534
  cert_files.push(cert)
506
535
  else
507
536
  cert_inlines.push(cert)
@@ -518,6 +547,14 @@ but prone to man-in-the-middle attacks. Please set verify_peer: true in producti
518
547
  end
519
548
  end
520
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
+
521
558
  def timeout_from(options)
522
559
  options[:connect_timeout] || options[:connection_timeout] || options[:timeout] || DEFAULT_CONNECTION_TIMEOUT
523
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.14.2"
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