bunny 1.0.7 → 2.24.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 +5 -5
- data/README.md +92 -87
- data/lib/amq/protocol/extensions.rb +2 -0
- data/lib/bunny/authentication/credentials_encoder.rb +2 -0
- data/lib/bunny/authentication/external_mechanism_encoder.rb +2 -0
- data/lib/bunny/authentication/plain_mechanism_encoder.rb +2 -0
- data/lib/bunny/channel.rb +485 -186
- data/lib/bunny/channel_id_allocator.rb +8 -4
- data/lib/bunny/concurrent/atomic_fixnum.rb +2 -0
- data/lib/bunny/concurrent/condition.rb +2 -0
- data/lib/bunny/concurrent/continuation_queue.rb +37 -13
- data/lib/bunny/concurrent/synchronized_sorted_set.rb +2 -0
- data/lib/bunny/consumer.rb +20 -13
- data/lib/bunny/consumer_tag_generator.rb +6 -2
- data/lib/bunny/consumer_work_pool.rb +37 -7
- data/lib/bunny/cruby/socket.rb +51 -22
- data/lib/bunny/cruby/ssl_socket.rb +68 -5
- data/lib/bunny/delivery_info.rb +3 -1
- data/lib/bunny/exceptions.rb +27 -4
- data/lib/bunny/exchange.rb +35 -29
- data/lib/bunny/framing.rb +2 -0
- data/lib/bunny/get_response.rb +85 -0
- data/lib/bunny/heartbeat_sender.rb +9 -6
- data/lib/bunny/message_properties.rb +2 -0
- data/lib/bunny/queue.rb +89 -41
- data/lib/bunny/reader_loop.rb +72 -28
- data/lib/bunny/return_info.rb +2 -0
- data/lib/bunny/session.rb +621 -225
- data/lib/bunny/socket.rb +7 -12
- data/lib/bunny/ssl_socket.rb +7 -12
- data/lib/bunny/test_kit.rb +15 -0
- data/lib/bunny/timeout.rb +3 -12
- data/lib/bunny/timestamp.rb +24 -0
- data/lib/bunny/transport.rb +223 -98
- data/lib/bunny/version.rb +2 -1
- data/lib/bunny/versioned_delivery_tag.rb +2 -0
- data/lib/bunny.rb +54 -8
- metadata +38 -224
- data/.gitignore +0 -22
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/.travis.yml +0 -23
- data/.yardopts +0 -8
- data/ChangeLog.md +0 -1092
- data/Gemfile +0 -54
- data/LICENSE +0 -21
- 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/bin/ci/before_build.sh +0 -31
- data/bunny.gemspec +0 -40
- 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_server_named_queues.rb +0 -35
- data/examples/connection/channel_level_exception.rb +0 -35
- 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/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 -28
- 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 -20
- data/examples/guides/getting_started/weathr.rb +0 -47
- data/examples/guides/queues/one_off_consumer.rb +0 -23
- data/examples/guides/queues/redeliveries.rb +0 -79
- data/lib/bunny/compatibility.rb +0 -24
- data/lib/bunny/concurrent/linked_continuation_queue.rb +0 -61
- data/lib/bunny/jruby/socket.rb +0 -40
- data/lib/bunny/jruby/ssl_socket.rb +0 -53
- data/lib/bunny/system_timer.rb +0 -20
- data/profiling/basic_publish/with_4K_messages.rb +0 -33
- data/repl +0 -3
- data/spec/compatibility/queue_declare_spec.rb +0 -44
- data/spec/compatibility/queue_declare_with_default_channel_spec.rb +0 -33
- data/spec/higher_level_api/integration/basic_ack_spec.rb +0 -71
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +0 -76
- data/spec/higher_level_api/integration/basic_consume_spec.rb +0 -225
- 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 -48
- data/spec/higher_level_api/integration/basic_nack_spec.rb +0 -79
- data/spec/higher_level_api/integration/basic_publish_spec.rb +0 -89
- data/spec/higher_level_api/integration/basic_qos_spec.rb +0 -29
- data/spec/higher_level_api/integration/basic_recover_spec.rb +0 -18
- data/spec/higher_level_api/integration/basic_reject_spec.rb +0 -74
- data/spec/higher_level_api/integration/basic_return_spec.rb +0 -33
- data/spec/higher_level_api/integration/channel_close_spec.rb +0 -25
- data/spec/higher_level_api/integration/channel_flow_spec.rb +0 -21
- data/spec/higher_level_api/integration/channel_open_spec.rb +0 -57
- data/spec/higher_level_api/integration/confirm_select_spec.rb +0 -19
- data/spec/higher_level_api/integration/connection_spec.rb +0 -400
- data/spec/higher_level_api/integration/connection_stop_spec.rb +0 -26
- data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +0 -50
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +0 -128
- data/spec/higher_level_api/integration/dead_lettering_spec.rb +0 -52
- data/spec/higher_level_api/integration/exchange_bind_spec.rb +0 -31
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +0 -204
- 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 -31
- data/spec/higher_level_api/integration/merry_go_round_spec.rb +0 -85
- 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 -77
- data/spec/higher_level_api/integration/publishing_edge_cases_spec.rb +0 -65
- data/spec/higher_level_api/integration/queue_bind_spec.rb +0 -109
- data/spec/higher_level_api/integration/queue_declare_spec.rb +0 -190
- 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 -127
- 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 -44
- data/spec/issues/issue78_spec.rb +0 -75
- data/spec/issues/issue83_spec.rb +0 -31
- data/spec/issues/issue97_attachment.json +0 -1
- data/spec/issues/issue97_spec.rb +0 -176
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +0 -69
- data/spec/lower_level_api/integration/basic_consume_spec.rb +0 -100
- data/spec/spec_helper.rb +0 -64
- data/spec/stress/channel_open_stress_spec.rb +0 -51
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -28
- data/spec/stress/concurrent_consumers_stress_spec.rb +0 -69
- data/spec/stress/concurrent_publishers_stress_spec.rb +0 -57
- data/spec/stress/connection_open_close_spec.rb +0 -40
- data/spec/stress/long_running_consumer_spec.rb +0 -83
- data/spec/tls/cacert.pem +0 -18
- data/spec/tls/client_cert.pem +0 -18
- data/spec/tls/client_key.pem +0 -27
- data/spec/tls/server_cert.pem +0 -18
- data/spec/tls/server_key.pem +0 -27
- 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/system_timer_spec.rb +0 -10
- data/spec/unit/version_delivery_tag_spec.rb +0 -28
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "thread"
|
2
4
|
require "monitor"
|
3
5
|
require "amq/int_allocator"
|
@@ -17,7 +19,9 @@ module Bunny
|
|
17
19
|
#
|
18
20
|
|
19
21
|
# @param [Integer] max_channel Max allowed channel id
|
20
|
-
def initialize(max_channel = ((1 <<
|
22
|
+
def initialize(max_channel = ((1 << 11) - 1))
|
23
|
+
# channel 0 has special meaning in the protocol, so start
|
24
|
+
# allocator at 1
|
21
25
|
@allocator = AMQ::IntAllocator.new(1, max_channel)
|
22
26
|
@mutex = Monitor.new
|
23
27
|
end
|
@@ -25,7 +29,7 @@ module Bunny
|
|
25
29
|
|
26
30
|
# Returns next available channel id. This method is thread safe.
|
27
31
|
#
|
28
|
-
# @return [
|
32
|
+
# @return [Integer]
|
29
33
|
# @api public
|
30
34
|
# @see ChannelManager#release_channel_id
|
31
35
|
# @see ChannelManager#reset_channel_id_allocator
|
@@ -37,7 +41,7 @@ module Bunny
|
|
37
41
|
|
38
42
|
# Releases previously allocated channel id. This method is thread safe.
|
39
43
|
#
|
40
|
-
# @param [
|
44
|
+
# @param [Integer] i Channel id to release
|
41
45
|
# @api public
|
42
46
|
# @see ChannelManager#next_channel_id
|
43
47
|
# @see ChannelManager#reset_channel_id_allocator
|
@@ -51,7 +55,7 @@ module Bunny
|
|
51
55
|
# Returns true if given channel id has been previously allocated and not yet released.
|
52
56
|
# This method is thread safe.
|
53
57
|
#
|
54
|
-
# @param [
|
58
|
+
# @param [Integer] i Channel id to check
|
55
59
|
# @return [Boolean] true if given channel id has been previously allocated and not yet released
|
56
60
|
# @api public
|
57
61
|
# @see ChannelManager#next_channel_id
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "thread"
|
2
4
|
|
3
5
|
module Bunny
|
@@ -6,36 +8,58 @@ module Bunny
|
|
6
8
|
#
|
7
9
|
# @private
|
8
10
|
class ContinuationQueue
|
9
|
-
def initialize
|
10
|
-
@q
|
11
|
+
def initialize
|
12
|
+
@q = []
|
13
|
+
@lock = ::Mutex.new
|
14
|
+
@cond = ::ConditionVariable.new
|
11
15
|
end
|
12
16
|
|
13
|
-
def push(
|
14
|
-
@
|
17
|
+
def push(item)
|
18
|
+
@lock.synchronize do
|
19
|
+
@q.push(item)
|
20
|
+
@cond.signal
|
21
|
+
end
|
15
22
|
end
|
16
23
|
alias << push
|
17
24
|
|
18
25
|
def pop
|
19
|
-
|
26
|
+
poll
|
20
27
|
end
|
21
28
|
|
22
29
|
def poll(timeout_in_ms = nil)
|
23
|
-
|
24
|
-
|
25
|
-
|
30
|
+
timeout_in_sec = timeout_in_ms ? timeout_in_ms / 1000.0 : nil
|
31
|
+
|
32
|
+
@lock.synchronize do
|
33
|
+
started_at = Bunny::Timestamp.monotonic
|
34
|
+
while @q.empty?
|
35
|
+
wait = !(timeout_in_sec.nil?)
|
36
|
+
@cond.wait(@lock, timeout_in_sec)
|
37
|
+
|
38
|
+
if wait
|
39
|
+
ended_at = Bunny::Timestamp.monotonic
|
40
|
+
elapsed = ended_at - started_at
|
41
|
+
raise ::Timeout::Error if (elapsed > timeout_in_sec)
|
42
|
+
end
|
26
43
|
end
|
27
|
-
|
28
|
-
|
44
|
+
item = @q.shift
|
45
|
+
item
|
29
46
|
end
|
30
47
|
end
|
31
48
|
|
32
49
|
def clear
|
33
|
-
@
|
50
|
+
@lock.synchronize do
|
51
|
+
@q.clear
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def empty?
|
56
|
+
@q.empty?
|
34
57
|
end
|
35
58
|
|
36
|
-
def
|
37
|
-
@q.
|
59
|
+
def size
|
60
|
+
@q.size
|
38
61
|
end
|
62
|
+
alias length size
|
39
63
|
end
|
40
64
|
end
|
41
65
|
end
|
data/lib/bunny/consumer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bunny
|
2
4
|
# Base class that represents consumer interface. Subclasses of this class implement
|
3
5
|
# specific logic of handling consumer life cycle events. Note that when the only event
|
@@ -39,6 +41,8 @@ module Bunny
|
|
39
41
|
@arguments = arguments
|
40
42
|
# no_ack set to true = no manual ack = automatic ack. MK.
|
41
43
|
@no_ack = no_ack
|
44
|
+
|
45
|
+
@on_cancellation = []
|
42
46
|
end
|
43
47
|
|
44
48
|
# Defines message delivery handler
|
@@ -61,14 +65,16 @@ module Bunny
|
|
61
65
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
62
66
|
# @api public
|
63
67
|
def on_cancellation(&block)
|
64
|
-
@on_cancellation
|
68
|
+
@on_cancellation << block
|
65
69
|
self
|
66
70
|
end
|
67
71
|
|
68
72
|
# Invokes consumer cancellation notification handler
|
69
73
|
# @private
|
70
74
|
def handle_cancellation(basic_cancel)
|
71
|
-
@on_cancellation.
|
75
|
+
@on_cancellation.each do |fn|
|
76
|
+
fn.call(basic_cancel)
|
77
|
+
end
|
72
78
|
end
|
73
79
|
|
74
80
|
# Cancels this consumer. Messages for this consumer will no longer be delivered. If the queue
|
@@ -82,12 +88,12 @@ module Bunny
|
|
82
88
|
|
83
89
|
# @return [String] More detailed human-readable string representation of this consumer
|
84
90
|
def inspect
|
85
|
-
"#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}
|
91
|
+
"#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name} @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>"
|
86
92
|
end
|
87
93
|
|
88
94
|
# @return [String] Brief human-readable string representation of this consumer
|
89
95
|
def to_s
|
90
|
-
"#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}
|
96
|
+
"#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name} @consumer_tag=#{@consumer_tag}>"
|
91
97
|
end
|
92
98
|
|
93
99
|
# @return [Boolean] true if this consumer uses automatic acknowledgement mode
|
@@ -102,6 +108,16 @@ module Bunny
|
|
102
108
|
@no_ack == false
|
103
109
|
end
|
104
110
|
|
111
|
+
# @return [String] Name of the queue this consumer is on
|
112
|
+
# @api public
|
113
|
+
def queue_name
|
114
|
+
if @queue.respond_to?(:name)
|
115
|
+
@queue.name
|
116
|
+
else
|
117
|
+
@queue
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
105
121
|
#
|
106
122
|
# Recovery
|
107
123
|
#
|
@@ -110,14 +126,5 @@ module Bunny
|
|
110
126
|
def recover_from_network_failure
|
111
127
|
@channel.basic_consume_with(self)
|
112
128
|
end
|
113
|
-
|
114
|
-
# @private
|
115
|
-
def queue_name
|
116
|
-
if @queue.respond_to?(:name)
|
117
|
-
@queue.name
|
118
|
-
else
|
119
|
-
@queue
|
120
|
-
end
|
121
|
-
end
|
122
129
|
end
|
123
130
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bunny
|
2
4
|
# Used to generate consumer tags in the client
|
3
5
|
class ConsumerTagGenerator
|
@@ -8,7 +10,8 @@ module Bunny
|
|
8
10
|
|
9
11
|
# @return [String] Generated consumer tag
|
10
12
|
def generate
|
11
|
-
|
13
|
+
t = Bunny::Timestamp.now
|
14
|
+
"#{Kernel.rand}-#{t.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
12
15
|
end # generate
|
13
16
|
|
14
17
|
|
@@ -17,7 +20,8 @@ module Bunny
|
|
17
20
|
# @return [String] Unique string.
|
18
21
|
# @api public
|
19
22
|
def generate_prefixed(name = "bunny")
|
20
|
-
|
23
|
+
t = Bunny::Timestamp.now
|
24
|
+
"#{name}-#{t.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "thread"
|
2
4
|
|
3
5
|
module Bunny
|
@@ -15,10 +17,17 @@ module Bunny
|
|
15
17
|
|
16
18
|
attr_reader :threads
|
17
19
|
attr_reader :size
|
20
|
+
attr_reader :abort_on_exception
|
18
21
|
|
19
|
-
def initialize(size = 1)
|
22
|
+
def initialize(size = 1, abort_on_exception = false, shutdown_timeout = 60)
|
20
23
|
@size = size
|
24
|
+
@abort_on_exception = abort_on_exception
|
25
|
+
@shutdown_timeout = shutdown_timeout
|
26
|
+
@shutdown_mutex = ::Mutex.new
|
27
|
+
@shutdown_conditional = ::ConditionVariable.new
|
21
28
|
@queue = ::Queue.new
|
29
|
+
@paused = false
|
30
|
+
@running = false
|
22
31
|
end
|
23
32
|
|
24
33
|
|
@@ -31,6 +40,7 @@ module Bunny
|
|
31
40
|
|
32
41
|
@size.times do
|
33
42
|
t = Thread.new(&method(:run_loop))
|
43
|
+
t.abort_on_exception = true if abort_on_exception
|
34
44
|
@threads << t
|
35
45
|
end
|
36
46
|
|
@@ -41,7 +51,16 @@ module Bunny
|
|
41
51
|
@running
|
42
52
|
end
|
43
53
|
|
44
|
-
def
|
54
|
+
def backlog
|
55
|
+
@queue.length
|
56
|
+
end
|
57
|
+
|
58
|
+
def busy?
|
59
|
+
!@queue.empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
def shutdown(wait_for_workers = false)
|
63
|
+
was_running = running?
|
45
64
|
@running = false
|
46
65
|
|
47
66
|
@size.times do
|
@@ -49,20 +68,26 @@ module Bunny
|
|
49
68
|
throw :terminate
|
50
69
|
end
|
51
70
|
end
|
71
|
+
|
72
|
+
return if !(wait_for_workers && @shutdown_timeout && was_running)
|
73
|
+
|
74
|
+
@shutdown_mutex.synchronize do
|
75
|
+
@shutdown_conditional.wait(@shutdown_mutex, @shutdown_timeout) if busy?
|
76
|
+
end
|
52
77
|
end
|
53
78
|
|
54
79
|
def join(timeout = nil)
|
55
|
-
@threads.each { |t| t.join(timeout) }
|
80
|
+
(@threads || []).each { |t| t.join(timeout) }
|
56
81
|
end
|
57
82
|
|
58
83
|
def pause
|
59
84
|
@running = false
|
60
|
-
|
61
|
-
@threads.each { |t| t.stop }
|
85
|
+
@paused = true
|
62
86
|
end
|
63
87
|
|
64
88
|
def resume
|
65
89
|
@running = true
|
90
|
+
@paused = false
|
66
91
|
|
67
92
|
@threads.each { |t| t.run }
|
68
93
|
end
|
@@ -70,7 +95,7 @@ module Bunny
|
|
70
95
|
def kill
|
71
96
|
@running = false
|
72
97
|
|
73
|
-
@threads.each { |t| t.kill }
|
98
|
+
(@threads || []).each { |t| t.kill }
|
74
99
|
end
|
75
100
|
|
76
101
|
protected
|
@@ -78,17 +103,22 @@ module Bunny
|
|
78
103
|
def run_loop
|
79
104
|
catch(:terminate) do
|
80
105
|
loop do
|
106
|
+
Thread.stop if @paused
|
81
107
|
callable = @queue.pop
|
82
108
|
|
83
109
|
begin
|
84
110
|
callable.call
|
85
|
-
rescue ::
|
111
|
+
rescue ::StandardError => e
|
86
112
|
# TODO: use connection logger
|
87
113
|
$stderr.puts e.class.name
|
88
114
|
$stderr.puts e.message
|
89
115
|
end
|
90
116
|
end
|
91
117
|
end
|
118
|
+
|
119
|
+
@shutdown_mutex.synchronize do
|
120
|
+
@shutdown_conditional.signal unless busy?
|
121
|
+
end
|
92
122
|
end
|
93
123
|
end
|
94
124
|
end
|
data/lib/bunny/cruby/socket.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "socket"
|
2
4
|
|
3
5
|
module Bunny
|
@@ -6,23 +8,40 @@ module Bunny
|
|
6
8
|
#
|
7
9
|
# Heavily inspired by Dalli by Mike Perham.
|
8
10
|
# @private
|
9
|
-
|
11
|
+
module Socket
|
10
12
|
attr_accessor :options
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
READ_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitReadable)
|
15
|
+
# Ruby 2.1+
|
16
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable,
|
17
|
+
IO::EAGAINWaitReadable, IO::EWOULDBLOCKWaitReadable]
|
18
|
+
else
|
19
|
+
# 2.0
|
20
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable]
|
21
|
+
end
|
22
|
+
WRITE_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitWritable)
|
23
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable,
|
24
|
+
IO::EAGAINWaitWritable, IO::EWOULDBLOCKWaitWritable]
|
25
|
+
else
|
26
|
+
# 2.0
|
27
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
|
28
|
+
end
|
15
29
|
|
16
30
|
def self.open(host, port, options = {})
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
|
23
|
-
sock.options = {:host => host, :port => port}.merge(options)
|
24
|
-
sock
|
31
|
+
socket = ::Socket.tcp(host, port, nil, nil,
|
32
|
+
connect_timeout: options[:connect_timeout])
|
33
|
+
if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
|
34
|
+
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
|
25
35
|
end
|
36
|
+
socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
|
37
|
+
socket.instance_eval do
|
38
|
+
@__bunny_socket_eof_flag__ = false
|
39
|
+
end
|
40
|
+
socket.extend self
|
41
|
+
socket.options = { :host => host, :port => port }.merge(options)
|
42
|
+
socket
|
43
|
+
rescue Errno::ETIMEDOUT
|
44
|
+
raise ClientTimeout
|
26
45
|
end
|
27
46
|
|
28
47
|
# Reads given number of bytes with an optional timeout
|
@@ -35,7 +54,7 @@ module Bunny
|
|
35
54
|
def read_fully(count, timeout = nil)
|
36
55
|
return nil if @__bunny_socket_eof_flag__
|
37
56
|
|
38
|
-
value = ''
|
57
|
+
value = +''
|
39
58
|
begin
|
40
59
|
loop do
|
41
60
|
value << read_nonblock(count - value.bytesize)
|
@@ -62,22 +81,32 @@ module Bunny
|
|
62
81
|
# if this is not appropriate in your case.
|
63
82
|
#
|
64
83
|
# @param [String] data Data to write
|
84
|
+
# @param [Integer] timeout Timeout
|
65
85
|
#
|
66
86
|
# @api public
|
67
|
-
def write_nonblock_fully(data)
|
87
|
+
def write_nonblock_fully(data, timeout = nil)
|
68
88
|
return nil if @__bunny_socket_eof_flag__
|
69
89
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
90
|
+
length = data.bytesize
|
91
|
+
total_count = 0
|
92
|
+
count = 0
|
93
|
+
loop do
|
94
|
+
begin
|
95
|
+
count = self.write_nonblock(data)
|
96
|
+
rescue *WRITE_RETRY_EXCEPTION_CLASSES
|
97
|
+
if IO.select([], [self], nil, timeout)
|
98
|
+
retry
|
99
|
+
else
|
100
|
+
raise Timeout::Error, "IO timeout when writing to socket"
|
101
|
+
end
|
74
102
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
103
|
+
|
104
|
+
total_count += count
|
105
|
+
return total_count if total_count >= length
|
106
|
+
data = data.byteslice(count..-1)
|
78
107
|
end
|
79
108
|
|
80
|
-
data.bytesize
|
81
109
|
end
|
110
|
+
|
82
111
|
end
|
83
112
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "socket"
|
2
4
|
|
3
5
|
module Bunny
|
@@ -8,10 +10,26 @@ module Bunny
|
|
8
10
|
# methods found in Bunny::Socket.
|
9
11
|
class SSLSocket < OpenSSL::SSL::SSLSocket
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
READ_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitReadable)
|
14
|
+
# Ruby 2.1+
|
15
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable,
|
16
|
+
IO::EAGAINWaitReadable, IO::EWOULDBLOCKWaitReadable]
|
17
|
+
else
|
18
|
+
# 2.0
|
19
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable]
|
20
|
+
end
|
21
|
+
WRITE_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitWritable)
|
22
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable,
|
23
|
+
IO::EAGAINWaitWritable, IO::EWOULDBLOCKWaitWritable]
|
24
|
+
else
|
25
|
+
# 2.0
|
26
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
|
27
|
+
end
|
14
28
|
|
29
|
+
def initialize(*args)
|
30
|
+
super
|
31
|
+
@__bunny_socket_eof_flag__ = false
|
32
|
+
end
|
15
33
|
|
16
34
|
# Reads given number of bytes with an optional timeout
|
17
35
|
#
|
@@ -23,7 +41,7 @@ module Bunny
|
|
23
41
|
def read_fully(count, timeout = nil)
|
24
42
|
return nil if @__bunny_socket_eof_flag__
|
25
43
|
|
26
|
-
value = ''
|
44
|
+
value = +''
|
27
45
|
begin
|
28
46
|
loop do
|
29
47
|
value << read_nonblock(count - value.bytesize)
|
@@ -50,8 +68,53 @@ module Bunny
|
|
50
68
|
end
|
51
69
|
value
|
52
70
|
end
|
71
|
+
|
72
|
+
# Writes provided data using IO#write_nonblock, taking care of handling
|
73
|
+
# of exceptions it raises when writing would fail (e.g. due to socket buffer
|
74
|
+
# being full).
|
75
|
+
#
|
76
|
+
# IMPORTANT: this method will mutate (slice) the argument. Pass in duplicates
|
77
|
+
# if this is not appropriate in your case.
|
78
|
+
#
|
79
|
+
# @param [String] data Data to write
|
80
|
+
# @param [Integer] timeout Timeout
|
81
|
+
#
|
82
|
+
# @api public
|
83
|
+
def write_nonblock_fully(data, timeout = nil)
|
84
|
+
return nil if @__bunny_socket_eof_flag__
|
85
|
+
|
86
|
+
length = data.bytesize
|
87
|
+
total_count = 0
|
88
|
+
count = 0
|
89
|
+
loop do
|
90
|
+
begin
|
91
|
+
count = self.write_nonblock(data)
|
92
|
+
rescue OpenSSL::SSL::SSLError => e
|
93
|
+
if e.message == "write would block"
|
94
|
+
if IO.select([], [self], nil, timeout)
|
95
|
+
retry
|
96
|
+
else
|
97
|
+
raise Timeout::Error, "IO timeout when writing to socket"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
raise e
|
101
|
+
rescue *WRITE_RETRY_EXCEPTION_CLASSES
|
102
|
+
if IO.select([], [self], nil, timeout)
|
103
|
+
retry
|
104
|
+
else
|
105
|
+
raise Timeout::Error, "IO timeout when writing to socket"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
total_count += count
|
110
|
+
return total_count if total_count >= length
|
111
|
+
data = data.byteslice(count..-1)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
53
116
|
end
|
54
|
-
rescue LoadError
|
117
|
+
rescue LoadError
|
55
118
|
puts "Could not load OpenSSL"
|
56
119
|
end
|
57
120
|
end
|
data/lib/bunny/delivery_info.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bunny/versioned_delivery_tag"
|
2
4
|
|
3
5
|
module Bunny
|
@@ -37,7 +39,7 @@ module Bunny
|
|
37
39
|
@channel = channel
|
38
40
|
end
|
39
41
|
|
40
|
-
# Iterates over
|
42
|
+
# Iterates over delivery properties
|
41
43
|
# @see Enumerable#each
|
42
44
|
def each(*args, &block)
|
43
45
|
@hash.each(*args, &block)
|
data/lib/bunny/exceptions.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Bunny
|
2
4
|
# Base class for all Bunny exceptions
|
3
5
|
# @api public
|
4
6
|
class Exception < ::StandardError
|
5
7
|
end
|
6
8
|
|
9
|
+
class HostListDepleted < Exception
|
10
|
+
def initialize
|
11
|
+
super("No more hosts to try in the supplied list of hosts")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
7
15
|
# Indicates a network failure. If automatic network
|
8
16
|
# recovery mode is enabled, these will be typically handled
|
9
17
|
# by the client itself.
|
@@ -54,20 +62,29 @@ module Bunny
|
|
54
62
|
end
|
55
63
|
end
|
56
64
|
|
57
|
-
|
58
65
|
# Raised when TCP connection to RabbitMQ fails because of an unresolved
|
59
66
|
# hostname, connectivity problem, etc
|
60
67
|
class TCPConnectionFailed < Exception
|
61
68
|
attr_reader :hostname, :port
|
62
69
|
|
63
|
-
def initialize(e, hostname, port)
|
70
|
+
def initialize(e, hostname=nil, port=nil)
|
64
71
|
m = case e
|
65
72
|
when String then
|
66
73
|
e
|
67
|
-
when Exception then
|
74
|
+
when ::Exception then
|
68
75
|
e.message
|
69
76
|
end
|
70
|
-
|
77
|
+
if hostname && port
|
78
|
+
super("Could not establish TCP connection to #{hostname}:#{port}: #{m}")
|
79
|
+
else
|
80
|
+
super(m)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class TCPConnectionFailedForAllHosts < TCPConnectionFailed
|
86
|
+
def initialize
|
87
|
+
super("Could not establish TCP connection to any of the configured hosts", nil, nil)
|
71
88
|
end
|
72
89
|
end
|
73
90
|
|
@@ -82,6 +99,12 @@ module Bunny
|
|
82
99
|
end
|
83
100
|
end
|
84
101
|
|
102
|
+
class ConnectionAlreadyClosed < Exception
|
103
|
+
def initialize
|
104
|
+
super('Connection has been already closed')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
85
108
|
class ShutdownSignal < Exception
|
86
109
|
end
|
87
110
|
|