bunny 2.14.2 → 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 (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