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.
- checksums.yaml +4 -4
- data/README.md +56 -39
- data/lib/bunny/channel.rb +89 -13
- data/lib/bunny/consumer.rb +2 -2
- data/lib/bunny/consumer_work_pool.rb +1 -1
- data/lib/bunny/cruby/socket.rb +3 -0
- data/lib/bunny/delivery_info.rb +1 -1
- data/lib/bunny/queue.rb +36 -5
- data/lib/bunny/reader_loop.rb +21 -13
- data/lib/bunny/session.rb +83 -25
- data/lib/bunny/transport.rb +49 -12
- data/lib/bunny/version.rb +1 -1
- data/lib/bunny.rb +45 -4
- metadata +37 -235
- data/.github/ISSUE_TEMPLATE.md +0 -18
- data/.gitignore +0 -28
- data/.rspec +0 -1
- data/.travis.yml +0 -31
- data/.yardopts +0 -8
- data/CONTRIBUTING.md +0 -132
- data/ChangeLog.md +0 -2072
- data/Gemfile +0 -55
- data/LICENSE +0 -21
- data/Rakefile +0 -54
- data/benchmarks/basic_publish/with_128K_messages.rb +0 -35
- data/benchmarks/basic_publish/with_1k_messages.rb +0 -35
- data/benchmarks/basic_publish/with_4K_messages.rb +0 -35
- data/benchmarks/basic_publish/with_64K_messages.rb +0 -35
- data/benchmarks/channel_open.rb +0 -28
- data/benchmarks/mutex_and_monitor.rb +0 -42
- data/benchmarks/queue_declare.rb +0 -29
- data/benchmarks/queue_declare_and_bind.rb +0 -29
- data/benchmarks/queue_declare_bind_and_delete.rb +0 -29
- data/benchmarks/synchronized_sorted_set.rb +0 -53
- data/benchmarks/write_vs_write_nonblock.rb +0 -49
- data/bunny.gemspec +0 -34
- data/docker/Dockerfile +0 -20
- data/docker/apt/preferences.d/erlang +0 -3
- data/docker/apt/sources.list.d/bintray.rabbitmq.list +0 -2
- data/docker/docker-entrypoint.sh +0 -26
- data/docker/rabbitmq.conf +0 -29
- data/docker-compose.yml +0 -28
- data/examples/connection/authentication_failure.rb +0 -16
- data/examples/connection/automatic_recovery_with_basic_get.rb +0 -40
- data/examples/connection/automatic_recovery_with_client_named_queues.rb +0 -36
- data/examples/connection/automatic_recovery_with_multiple_consumers.rb +0 -46
- data/examples/connection/automatic_recovery_with_republishing.rb +0 -109
- data/examples/connection/automatic_recovery_with_server_named_queues.rb +0 -35
- data/examples/connection/channel_level_exception.rb +0 -27
- data/examples/connection/disabled_automatic_recovery.rb +0 -34
- data/examples/connection/heartbeat.rb +0 -17
- data/examples/connection/manually_reconnecting_consumer.rb +0 -23
- data/examples/connection/manually_reconnecting_publisher.rb +0 -28
- data/examples/connection/unknown_host.rb +0 -16
- data/examples/consumers/high_and_low_priority.rb +0 -50
- data/examples/guides/exchanges/direct_exchange_routing.rb +0 -36
- data/examples/guides/exchanges/fanout_exchange_routing.rb +0 -28
- data/examples/guides/exchanges/headers_exchange_routing.rb +0 -31
- data/examples/guides/exchanges/mandatory_messages.rb +0 -30
- data/examples/guides/extensions/alternate_exchange.rb +0 -30
- data/examples/guides/extensions/basic_nack.rb +0 -33
- data/examples/guides/extensions/connection_blocked.rb +0 -35
- data/examples/guides/extensions/consumer_cancellation_notification.rb +0 -39
- data/examples/guides/extensions/dead_letter_exchange.rb +0 -32
- data/examples/guides/extensions/exchange_to_exchange_bindings.rb +0 -29
- data/examples/guides/extensions/per_message_ttl.rb +0 -36
- data/examples/guides/extensions/per_queue_message_ttl.rb +0 -36
- data/examples/guides/extensions/publisher_confirms.rb +0 -28
- data/examples/guides/extensions/queue_lease.rb +0 -26
- data/examples/guides/extensions/sender_selected_distribution.rb +0 -32
- data/examples/guides/getting_started/blabbr.rb +0 -27
- data/examples/guides/getting_started/hello_world.rb +0 -22
- data/examples/guides/getting_started/weathr.rb +0 -49
- data/examples/guides/queues/one_off_consumer.rb +0 -25
- data/examples/guides/queues/redeliveries.rb +0 -81
- data/profiling/basic_publish/with_4K_messages.rb +0 -33
- data/repl +0 -3
- data/spec/config/enabled_plugins +0 -1
- data/spec/config/rabbitmq.conf +0 -13
- data/spec/higher_level_api/integration/basic_ack_spec.rb +0 -230
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -142
- data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -349
- data/spec/higher_level_api/integration/basic_consume_with_objects_spec.rb +0 -54
- data/spec/higher_level_api/integration/basic_get_spec.rb +0 -80
- data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -82
- data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -74
- data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -57
- data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -152
- data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
- data/spec/higher_level_api/integration/channel_close_spec.rb +0 -66
- data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
- data/spec/higher_level_api/integration/connection_recovery_spec.rb +0 -483
- data/spec/higher_level_api/integration/connection_spec.rb +0 -563
- data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -83
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -75
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -237
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +0 -105
- data/spec/higher_level_api/integration/exchange_unbind_spec.rb +0 -40
- data/spec/higher_level_api/integration/exclusive_queue_spec.rb +0 -28
- data/spec/higher_level_api/integration/heartbeat_spec.rb +0 -49
- data/spec/higher_level_api/integration/message_properties_access_spec.rb +0 -95
- data/spec/higher_level_api/integration/predeclared_exchanges_spec.rb +0 -24
- data/spec/higher_level_api/integration/publisher_confirms_spec.rb +0 -191
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -87
- data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
- data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -285
- data/spec/higher_level_api/integration/queue_delete_spec.rb +0 -41
- data/spec/higher_level_api/integration/queue_purge_spec.rb +0 -30
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +0 -54
- data/spec/higher_level_api/integration/read_only_consumer_spec.rb +0 -60
- data/spec/higher_level_api/integration/sender_selected_distribution_spec.rb +0 -36
- data/spec/higher_level_api/integration/tls_connection_spec.rb +0 -250
- data/spec/higher_level_api/integration/toxiproxy_spec.rb +0 -76
- data/spec/higher_level_api/integration/tx_commit_spec.rb +0 -21
- data/spec/higher_level_api/integration/tx_rollback_spec.rb +0 -21
- data/spec/higher_level_api/integration/with_channel_spec.rb +0 -25
- data/spec/issues/issue100_spec.rb +0 -42
- data/spec/issues/issue141_spec.rb +0 -43
- data/spec/issues/issue202_spec.rb +0 -15
- data/spec/issues/issue224_spec.rb +0 -40
- data/spec/issues/issue465_spec.rb +0 -32
- data/spec/issues/issue549_spec.rb +0 -30
- data/spec/issues/issue78_spec.rb +0 -72
- data/spec/issues/issue83_spec.rb +0 -30
- data/spec/issues/issue97_attachment.json +0 -1
- data/spec/issues/issue97_spec.rb +0 -175
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -83
- data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -99
- data/spec/spec_helper.rb +0 -47
- data/spec/stress/channel_close_stress_spec.rb +0 -64
- data/spec/stress/channel_open_stress_spec.rb +0 -84
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
- data/spec/stress/concurrent_consumers_stress_spec.rb +0 -71
- data/spec/stress/concurrent_publishers_stress_spec.rb +0 -54
- data/spec/stress/connection_open_close_spec.rb +0 -52
- data/spec/stress/merry_go_round_spec.rb +0 -105
- data/spec/tls/ca_certificate.pem +0 -29
- data/spec/tls/ca_key.pem +0 -52
- data/spec/tls/client_certificate.pem +0 -29
- data/spec/tls/client_key.pem +0 -51
- data/spec/tls/generate-server-cert.sh +0 -8
- data/spec/tls/server-openssl.cnf +0 -10
- data/spec/tls/server.csr +0 -16
- data/spec/tls/server_certificate.pem +0 -29
- data/spec/tls/server_key.pem +0 -51
- data/spec/toxiproxy_helper.rb +0 -28
- data/spec/unit/bunny_spec.rb +0 -15
- data/spec/unit/concurrent/atomic_fixnum_spec.rb +0 -35
- data/spec/unit/concurrent/condition_spec.rb +0 -82
- data/spec/unit/concurrent/linked_continuation_queue_spec.rb +0 -35
- data/spec/unit/concurrent/synchronized_sorted_set_spec.rb +0 -73
- data/spec/unit/exchange_recovery_spec.rb +0 -39
- 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] :
|
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]
|
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
|
-
@
|
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
|
-
|
642
|
-
|
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
|
-
|
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
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
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
|
-
|
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
|
-
|
810
|
-
|
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
|
-
@
|
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,
|
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
|
-
@
|
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(:
|
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
|
data/lib/bunny/transport.rb
CHANGED
@@ -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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
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
|
-
|
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 =~
|
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
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'],
|
62
|
-
if connection_string_or_opts.respond_to?(:keys) &&
|
63
|
-
|
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,
|
107
|
+
conn = Session.new(connection_string_or_opts, optz)
|
67
108
|
@default_connection ||= conn
|
68
109
|
|
69
110
|
conn
|