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