bunny 0.9.0.pre13 → 0.9.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.md +23 -0
- data/README.md +2 -2
- data/lib/bunny.rb +15 -2
- data/lib/bunny/authentication/credentials_encoder.rb +18 -0
- data/lib/bunny/authentication/external_mechanism_encoder.rb +1 -0
- data/lib/bunny/authentication/plain_mechanism_encoder.rb +2 -0
- data/lib/bunny/channel.rb +9 -3
- data/lib/bunny/channel_id_allocator.rb +8 -0
- data/lib/bunny/concurrent/condition.rb +2 -0
- data/lib/bunny/concurrent/continuation_queue.rb +3 -0
- data/lib/bunny/concurrent/linked_continuation_queue.rb +3 -0
- data/lib/bunny/consumer.rb +2 -0
- data/lib/bunny/consumer_tag_generator.rb +1 -0
- data/lib/bunny/consumer_work_pool.rb +2 -0
- data/lib/bunny/delivery_info.rb +18 -2
- data/lib/bunny/exceptions.rb +49 -10
- data/lib/bunny/framing.rb +3 -0
- data/lib/bunny/heartbeat_sender.rb +4 -1
- data/lib/bunny/message_properties.rb +22 -0
- data/lib/bunny/queue.rb +49 -0
- data/lib/bunny/reader_loop.rb +1 -0
- data/lib/bunny/return_info.rb +11 -0
- data/lib/bunny/session.rb +53 -19
- data/lib/bunny/socket.rb +1 -0
- data/lib/bunny/ssl_socket.rb +29 -5
- data/lib/bunny/system_timer.rb +2 -0
- data/lib/bunny/test_kit.rb +4 -3
- data/lib/bunny/transport.rb +41 -25
- data/lib/bunny/version.rb +2 -1
- data/spec/compatibility/queue_declare_spec.rb +4 -0
- data/spec/compatibility/queue_declare_with_default_channel_spec.rb +33 -0
- data/spec/higher_level_api/integration/basic_get_spec.rb +35 -0
- data/spec/higher_level_api/integration/basic_nack_spec.rb +19 -1
- data/spec/higher_level_api/integration/connection_spec.rb +5 -0
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +23 -2
- data/spec/higher_level_api/integration/tls_connection_spec.rb +47 -0
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +8 -1
- data/spec/tls/cacert.pem +18 -0
- data/spec/tls/client_cert.pem +18 -0
- data/spec/tls/client_key.pem +27 -0
- data/spec/tls/server_cert.pem +18 -0
- data/spec/tls/server_key.pem +27 -0
- metadata +16 -2
data/lib/bunny/framing.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module Bunny
|
2
|
+
# @private
|
2
3
|
module Framing
|
3
4
|
ENCODINGS_SUPPORTED = defined? Encoding
|
4
5
|
HEADER_SLICE = (0..6).freeze
|
5
6
|
DATA_SLICE = (7..-1).freeze
|
6
7
|
PAYLOAD_SLICE = (0..-2).freeze
|
7
8
|
|
9
|
+
# @private
|
8
10
|
module String
|
9
11
|
class Frame < AMQ::Protocol::Frame
|
10
12
|
def self.decode(string)
|
@@ -30,6 +32,7 @@ module Bunny
|
|
30
32
|
end # String
|
31
33
|
|
32
34
|
|
35
|
+
# @private
|
33
36
|
module IO
|
34
37
|
class Frame < AMQ::Protocol::Frame
|
35
38
|
def self.decode(io)
|
@@ -3,6 +3,9 @@ require "amq/protocol/client"
|
|
3
3
|
require "amq/protocol/frame"
|
4
4
|
|
5
5
|
module Bunny
|
6
|
+
# Periodically sends heartbeats, keeping track of the last publishing activity.
|
7
|
+
#
|
8
|
+
# @private
|
6
9
|
class HeartbeatSender
|
7
10
|
|
8
11
|
#
|
@@ -50,7 +53,7 @@ module Bunny
|
|
50
53
|
@logger.error "I/O error in the hearbeat sender: #{ioe.message}"
|
51
54
|
stop
|
52
55
|
rescue Exception => e
|
53
|
-
@logger.error "
|
56
|
+
@logger.error "Error in the hearbeat sender: #{e.message}"
|
54
57
|
stop
|
55
58
|
end
|
56
59
|
end
|
@@ -14,82 +14,104 @@ module Bunny
|
|
14
14
|
# API
|
15
15
|
#
|
16
16
|
|
17
|
+
# @private
|
17
18
|
def initialize(properties)
|
18
19
|
@properties = properties
|
19
20
|
end
|
20
21
|
|
22
|
+
# Iterates over the message properties
|
23
|
+
# @see Enumerable#each
|
21
24
|
def each(*args, &block)
|
22
25
|
@properties.each(*args, &block)
|
23
26
|
end
|
24
27
|
|
28
|
+
# Accesses message properties by key
|
29
|
+
# @see Hash#[]
|
25
30
|
def [](k)
|
26
31
|
@properties[k]
|
27
32
|
end
|
28
33
|
|
34
|
+
# @return [Hash] Hash representation of this delivery info
|
29
35
|
def to_hash
|
30
36
|
@properties
|
31
37
|
end
|
32
38
|
|
39
|
+
# @private
|
33
40
|
def to_s
|
34
41
|
to_hash.to_s
|
35
42
|
end
|
36
43
|
|
44
|
+
# @private
|
37
45
|
def inspect
|
38
46
|
to_hash.inspect
|
39
47
|
end
|
40
48
|
|
49
|
+
# @return [String] (Optional) content type of the message, as set by the publisher
|
41
50
|
def content_type
|
42
51
|
@properties[:content_type]
|
43
52
|
end
|
44
53
|
|
54
|
+
# @return [String] (Optional) content encoding of the message, as set by the publisher
|
45
55
|
def content_encoding
|
46
56
|
@properties[:content_encoding]
|
47
57
|
end
|
48
58
|
|
59
|
+
# @return [String] Message headers
|
49
60
|
def headers
|
50
61
|
@properties[:headers]
|
51
62
|
end
|
52
63
|
|
64
|
+
# @return [Integer] Delivery mode (persistent or transient)
|
53
65
|
def delivery_mode
|
54
66
|
@properties[:delivery_mode]
|
55
67
|
end
|
56
68
|
|
69
|
+
# @return [Integer] Message priority, as set by the publisher
|
57
70
|
def priority
|
58
71
|
@properties[:priority]
|
59
72
|
end
|
60
73
|
|
74
|
+
# @return [String] What message this message is a reply to (or corresponds to), as set by the publisher
|
61
75
|
def correlation_id
|
62
76
|
@properties[:correlation_id]
|
63
77
|
end
|
64
78
|
|
79
|
+
# @return [String] (Optional) How to reply to the publisher (usually a reply queue name)
|
65
80
|
def reply_to
|
66
81
|
@properties[:reply_to]
|
67
82
|
end
|
68
83
|
|
84
|
+
# @return [String] Message expiration, as set by the publisher
|
69
85
|
def expiration
|
70
86
|
@properties[:expiration]
|
71
87
|
end
|
72
88
|
|
89
|
+
# @return [String] Message ID, as set by the publisher
|
73
90
|
def message_id
|
74
91
|
@properties[:message_id]
|
75
92
|
end
|
76
93
|
|
94
|
+
# @return [Time] Message timestamp, as set by the publisher
|
77
95
|
def timestamp
|
78
96
|
@properties[:timestamp]
|
79
97
|
end
|
80
98
|
|
99
|
+
# @return [String] Message type, as set by the publisher
|
81
100
|
def type
|
82
101
|
@properties[:type]
|
83
102
|
end
|
84
103
|
|
104
|
+
# @return [String] Publishing user, as set by the publisher
|
85
105
|
def user_id
|
86
106
|
@properties[:user_id]
|
87
107
|
end
|
88
108
|
|
109
|
+
# @return [String] Publishing application, as set by the publisher
|
89
110
|
def app_id
|
90
111
|
@properties[:app_id]
|
91
112
|
end
|
92
113
|
|
114
|
+
# @return [String] Cluster ID, as set by the publisher
|
93
115
|
def cluster_id
|
94
116
|
@properties[:cluster_id]
|
95
117
|
end
|
data/lib/bunny/queue.rb
CHANGED
@@ -256,6 +256,55 @@ module Bunny
|
|
256
256
|
end
|
257
257
|
end
|
258
258
|
|
259
|
+
# @param [Hash] opts Options
|
260
|
+
#
|
261
|
+
# @option opts [Boolean] :ack (false) Will the message be acknowledged manually?
|
262
|
+
# @option opts [Float] :timeout (1.0) Max amount of time, in seconds, to wait before raising an exception
|
263
|
+
#
|
264
|
+
# @return [Array] Triple of delivery info, message properties and message content.
|
265
|
+
# If the queue is empty, all three will be nils.
|
266
|
+
#
|
267
|
+
# @raises [Bunny::ClientException] When no message is available in the amount of time provided via the :timeout option
|
268
|
+
#
|
269
|
+
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
270
|
+
# @see Bunny::Queue#subscribe
|
271
|
+
# @api public
|
272
|
+
#
|
273
|
+
# @example
|
274
|
+
# conn = Bunny.new
|
275
|
+
# conn.start
|
276
|
+
#
|
277
|
+
# ch = conn.create_channel
|
278
|
+
# q = ch.queue("test1")
|
279
|
+
# x = ch.default_exchange
|
280
|
+
# x.publish("Hello, everybody!", :routing_key => 'test1')
|
281
|
+
#
|
282
|
+
# delivery_info, properties, payload = q.pop_waiting(:timeout => 0.5)
|
283
|
+
#
|
284
|
+
# puts "This is the message: " + payload + "\n\n"
|
285
|
+
# conn.close
|
286
|
+
def pop_waiting(opts = {:ack => false, :timeout => 1.0}, &block)
|
287
|
+
delivery_info, properties, content = nil, nil, nil
|
288
|
+
|
289
|
+
Bunny::Timer.timeout(opts[:timeout], ClientTimeout) do
|
290
|
+
loop do
|
291
|
+
delivery_info, properties, content = @channel.basic_get(@name, opts)
|
292
|
+
|
293
|
+
if !content.nil?
|
294
|
+
break
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
if block
|
300
|
+
block.call(delivery_info, properties, content)
|
301
|
+
else
|
302
|
+
[delivery_info, properties, content]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
alias get_waiting pop_waiting
|
306
|
+
|
307
|
+
|
259
308
|
# Publishes a message to the queue via default exchange. Takes the same arguments
|
260
309
|
# as {Bunny::Exchange#publish}
|
261
310
|
#
|
data/lib/bunny/reader_loop.rb
CHANGED
data/lib/bunny/return_info.rb
CHANGED
@@ -24,38 +24,49 @@ module Bunny
|
|
24
24
|
}
|
25
25
|
end
|
26
26
|
|
27
|
+
# Iterates over the returned delivery properties
|
28
|
+
# @see Enumerable#each
|
27
29
|
def each(*args, &block)
|
28
30
|
@hash.each(*args, &block)
|
29
31
|
end
|
30
32
|
|
33
|
+
# Accesses returned delivery properties by key
|
34
|
+
# @see Hash#[]
|
31
35
|
def [](k)
|
32
36
|
@hash[k]
|
33
37
|
end
|
34
38
|
|
39
|
+
# @return [Hash] Hash representation of this returned delivery info
|
35
40
|
def to_hash
|
36
41
|
@hash
|
37
42
|
end
|
38
43
|
|
44
|
+
# @private
|
39
45
|
def to_s
|
40
46
|
to_hash.to_s
|
41
47
|
end
|
42
48
|
|
49
|
+
# @private
|
43
50
|
def inspect
|
44
51
|
to_hash.inspect
|
45
52
|
end
|
46
53
|
|
54
|
+
# @return [Integer] Reply (status) code of the cause
|
47
55
|
def reply_code
|
48
56
|
@basic_return.reply_code
|
49
57
|
end
|
50
58
|
|
59
|
+
# @return [Integer] Reply (status) text of the cause, explaining why the message was returned
|
51
60
|
def reply_text
|
52
61
|
@basic_return.reply_text
|
53
62
|
end
|
54
63
|
|
64
|
+
# @return [String] Exchange the message was published to
|
55
65
|
def exchange
|
56
66
|
@basic_return.exchange
|
57
67
|
end
|
58
68
|
|
69
|
+
# @return [String] Routing key the message has
|
59
70
|
def routing_key
|
60
71
|
@basic_return.routing_key
|
61
72
|
end
|
data/lib/bunny/session.rb
CHANGED
@@ -54,8 +54,10 @@ module Bunny
|
|
54
54
|
:information => "http://rubybunny.info",
|
55
55
|
}
|
56
56
|
|
57
|
+
# @private
|
57
58
|
DEFAULT_LOCALE = "en_GB"
|
58
59
|
|
60
|
+
# Default reconnection interval for TCP connection failures
|
59
61
|
DEFAULT_NETWORK_RECOVERY_INTERVAL = 5.0
|
60
62
|
|
61
63
|
|
@@ -63,6 +65,8 @@ module Bunny
|
|
63
65
|
# API
|
64
66
|
#
|
65
67
|
|
68
|
+
# @return [Bunny::Transport]
|
69
|
+
attr_reader :transport
|
66
70
|
attr_reader :status, :host, :port, :heartbeat, :user, :pass, :vhost, :frame_max, :threaded
|
67
71
|
attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales
|
68
72
|
attr_reader :default_channel
|
@@ -138,6 +142,8 @@ module Bunny
|
|
138
142
|
@channels = Hash.new
|
139
143
|
|
140
144
|
self.reset_continuations
|
145
|
+
|
146
|
+
self.initialize_transport
|
141
147
|
end
|
142
148
|
|
143
149
|
# @return [String] RabbitMQ hostname (or IP address) used
|
@@ -169,7 +175,20 @@ module Bunny
|
|
169
175
|
@threaded
|
170
176
|
end
|
171
177
|
|
172
|
-
|
178
|
+
def configure_socket(&block)
|
179
|
+
raise ArgumentError, "No block provided!" if block.nil?
|
180
|
+
|
181
|
+
if @transport
|
182
|
+
@transport.configure_socket(&block)
|
183
|
+
else
|
184
|
+
@socket_configurator = block
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Starts the connection process.
|
189
|
+
#
|
190
|
+
# @see http://rubybunny.info/articles/getting_started.html
|
191
|
+
# @see http://rubybunny.info/articles/connecting.html
|
173
192
|
# @api public
|
174
193
|
def start
|
175
194
|
return self if connected?
|
@@ -183,6 +202,13 @@ module Bunny
|
|
183
202
|
self.maybe_close_transport
|
184
203
|
self.initialize_transport
|
185
204
|
|
205
|
+
@transport.initialize_socket
|
206
|
+
@transport.connect
|
207
|
+
|
208
|
+
if @socket_configurator
|
209
|
+
@transport.configure_socket(&@socket_configurator)
|
210
|
+
end
|
211
|
+
|
186
212
|
self.init_connection
|
187
213
|
self.open_connection
|
188
214
|
|
@@ -198,6 +224,9 @@ module Bunny
|
|
198
224
|
self
|
199
225
|
end
|
200
226
|
|
227
|
+
# Socket operation timeout used by this connection
|
228
|
+
# @return [Integer]
|
229
|
+
# @private
|
201
230
|
def read_write_timeout
|
202
231
|
@transport.read_write_timeout
|
203
232
|
end
|
@@ -397,6 +426,7 @@ module Bunny
|
|
397
426
|
raise @last_connection_error if @last_connection_error
|
398
427
|
end
|
399
428
|
|
429
|
+
# @private
|
400
430
|
def handle_frameset(ch_number, frames)
|
401
431
|
method = frames.first
|
402
432
|
|
@@ -486,11 +516,15 @@ module Bunny
|
|
486
516
|
when 501 then
|
487
517
|
FrameError
|
488
518
|
when 503 then
|
489
|
-
|
519
|
+
CommandInvalid
|
490
520
|
when 504 then
|
491
521
|
ChannelError
|
492
522
|
when 505 then
|
493
523
|
UnexpectedFrame
|
524
|
+
when 506 then
|
525
|
+
ResourceError
|
526
|
+
when 541 then
|
527
|
+
InternalError
|
494
528
|
else
|
495
529
|
raise "Unknown reply code: #{frame.reply_code}, text: #{frame.reply_text}"
|
496
530
|
end
|
@@ -615,7 +649,7 @@ module Bunny
|
|
615
649
|
# Sends multiple frames, one by one. For thread safety this method takes a channel
|
616
650
|
# object and synchronizes on it.
|
617
651
|
#
|
618
|
-
# @
|
652
|
+
# @private
|
619
653
|
def send_frameset(frames, channel)
|
620
654
|
# some developers end up sharing channels between threads and when multiple
|
621
655
|
# threads publish on the same channel aggressively, at some point frames will be
|
@@ -632,7 +666,7 @@ module Bunny
|
|
632
666
|
# object and synchronizes on it. Uses transport implementation that does not perform
|
633
667
|
# timeout control.
|
634
668
|
#
|
635
|
-
# @
|
669
|
+
# @private
|
636
670
|
def send_frameset_without_timeout(frames, channel)
|
637
671
|
# some developers end up sharing channels between threads and when multiple
|
638
672
|
# threads publish on the same channel aggressively, at some point frames will be
|
@@ -653,7 +687,7 @@ module Bunny
|
|
653
687
|
|
654
688
|
protected
|
655
689
|
|
656
|
-
# @
|
690
|
+
# @private
|
657
691
|
def init_connection
|
658
692
|
self.send_preamble
|
659
693
|
|
@@ -668,7 +702,7 @@ module Bunny
|
|
668
702
|
@status = :connected
|
669
703
|
end
|
670
704
|
|
671
|
-
# @
|
705
|
+
# @private
|
672
706
|
def open_connection
|
673
707
|
@transport.send_frame(AMQ::Protocol::Connection::StartOk.encode(@client_properties, @mechanism, self.encode_credentials(username, password), @locale))
|
674
708
|
@logger.debug "Sent connection.start-ok"
|
@@ -732,7 +766,7 @@ module Bunny
|
|
732
766
|
0 == val || val.nil?
|
733
767
|
end
|
734
768
|
|
735
|
-
# @
|
769
|
+
# @private
|
736
770
|
def negotiate_value(client_value, server_value)
|
737
771
|
return server_value if client_value == :server
|
738
772
|
|
@@ -743,59 +777,59 @@ module Bunny
|
|
743
777
|
end
|
744
778
|
end
|
745
779
|
|
746
|
-
# @
|
780
|
+
# @private
|
747
781
|
def initialize_heartbeat_sender
|
748
782
|
@logger.debug "Initializing heartbeat sender..."
|
749
783
|
@heartbeat_sender = HeartbeatSender.new(@transport, @logger)
|
750
784
|
@heartbeat_sender.start(@heartbeat)
|
751
785
|
end
|
752
786
|
|
753
|
-
# @
|
787
|
+
# @private
|
754
788
|
def maybe_shutdown_heartbeat_sender
|
755
789
|
@heartbeat_sender.stop if @heartbeat_sender
|
756
790
|
end
|
757
791
|
|
758
|
-
# @
|
792
|
+
# @private
|
759
793
|
def initialize_transport
|
760
794
|
@transport = Transport.new(self, @host, @port, @opts.merge(:session_thread => Thread.current))
|
761
795
|
end
|
762
796
|
|
763
|
-
# @
|
797
|
+
# @private
|
764
798
|
def maybe_close_transport
|
765
799
|
@transport.close if @transport
|
766
800
|
end
|
767
801
|
|
768
802
|
# Sends AMQ protocol header (also known as preamble).
|
769
|
-
# @
|
803
|
+
# @private
|
770
804
|
def send_preamble
|
771
805
|
@transport.write(AMQ::Protocol::PREAMBLE)
|
772
806
|
@logger.debug "Sent protocol preamble"
|
773
807
|
end
|
774
808
|
|
775
809
|
|
776
|
-
# @
|
810
|
+
# @private
|
777
811
|
def encode_credentials(username, password)
|
778
812
|
@credentials_encoder.encode_credentials(username, password)
|
779
813
|
end # encode_credentials(username, password)
|
780
814
|
|
781
|
-
# @
|
815
|
+
# @private
|
782
816
|
def credentials_encoder_for(mechanism)
|
783
817
|
Authentication::CredentialsEncoder.for_session(self)
|
784
818
|
end
|
785
819
|
|
786
820
|
if defined?(JRUBY_VERSION)
|
787
|
-
# @
|
821
|
+
# @private
|
788
822
|
def reset_continuations
|
789
823
|
@continuations = Concurrent::LinkedContinuationQueue.new
|
790
824
|
end
|
791
825
|
else
|
792
|
-
# @
|
826
|
+
# @private
|
793
827
|
def reset_continuations
|
794
828
|
@continuations = Concurrent::ContinuationQueue.new
|
795
829
|
end
|
796
830
|
end
|
797
831
|
|
798
|
-
# @
|
832
|
+
# @private
|
799
833
|
def wait_on_continuations
|
800
834
|
unless @threaded
|
801
835
|
reader_loop.run_once until @continuations.length > 0
|
@@ -804,7 +838,7 @@ module Bunny
|
|
804
838
|
@continuations.poll(@continuation_timeout)
|
805
839
|
end
|
806
840
|
|
807
|
-
# @
|
841
|
+
# @private
|
808
842
|
def init_logger(level)
|
809
843
|
@logger = ::Logger.new(@logfile)
|
810
844
|
@logger.level = normalize_log_level(level)
|
@@ -813,7 +847,7 @@ module Bunny
|
|
813
847
|
@logger
|
814
848
|
end
|
815
849
|
|
816
|
-
# @
|
850
|
+
# @private
|
817
851
|
def normalize_log_level(level)
|
818
852
|
case level
|
819
853
|
when :debug, Logger::DEBUG, "debug" then Logger::DEBUG
|