bunny 1.0.0.rc2 → 1.0.0.rc3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/ChangeLog.md +53 -0
- data/Gemfile +1 -1
- data/README.md +20 -12
- data/lib/bunny/channel.rb +11 -8
- data/lib/bunny/compatibility.rb +0 -1
- data/lib/bunny/consumer.rb +4 -4
- data/lib/bunny/exceptions.rb +28 -1
- data/lib/bunny/queue.rb +0 -1
- data/lib/bunny/session.rb +83 -8
- data/lib/bunny/system_timer.rb +1 -1
- data/lib/bunny/transport.rb +63 -29
- data/lib/bunny/version.rb +1 -1
- data/repl +3 -0
- data/spec/higher_level_api/integration/connection_spec.rb +5 -5
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +39 -18
- data/spec/higher_level_api/integration/queue_declare_spec.rb +19 -0
- data/spec/higher_level_api/integration/queue_delete_spec.rb +8 -7
- data/spec/higher_level_api/integration/queue_unbind_spec.rb +3 -3
- data/spec/higher_level_api/integration/tls_connection_spec.rb +58 -62
- data/spec/unit/system_timer_spec.rb +10 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b96591dc64422653dd1e514413d72e086cd2943b
|
4
|
+
data.tar.gz: b7b4930751e88b767acaa8ab7d6db95098cb7b26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e28a7d255892d518d0be18155546ab62702d59605219d736008552fd9e463e66d28ab768065eb81e08062d9ef08e01b8f8ef0ebe5a4873ae08edab5c9a2b0d08
|
7
|
+
data.tar.gz: 2a51e2d4c244f06c69b71cf1465c3fdff9a8d1d6b54069e2a62a7c26ce3f447df3aa209b67efd77e5925d46478d4c8f76b219bda6bb42ed6b454572be14b96be
|
data/.travis.yml
CHANGED
data/ChangeLog.md
CHANGED
@@ -1,3 +1,56 @@
|
|
1
|
+
## Changes between Bunny 1.0.0.rc2 and 1.0.0.rc3
|
2
|
+
|
3
|
+
### Support [Authentication Failure Notification](http://www.rabbitmq.com/auth-notification.html)
|
4
|
+
|
5
|
+
`Bunny::AuthenticationFailureError` is a new auth failure exception
|
6
|
+
that subclasses `Bunny::PossibleAuthenticationFailureError` for
|
7
|
+
backwards compatibility.
|
8
|
+
|
9
|
+
As such, `Bunny::PossibleAuthenticationFailureError`'s error message
|
10
|
+
has changed.
|
11
|
+
|
12
|
+
This extension is available in RabbitMQ 3.2+.
|
13
|
+
|
14
|
+
|
15
|
+
### Bunny::Session#exchange_exists?
|
16
|
+
|
17
|
+
`Bunny::Session#exchange_exists?` is a new predicate that makes it
|
18
|
+
easier to check if a exchange exists.
|
19
|
+
|
20
|
+
It uses a one-off channel and `exchange.declare` with `passive` set to true
|
21
|
+
under the hood.
|
22
|
+
|
23
|
+
### Bunny::Session#queue_exists?
|
24
|
+
|
25
|
+
`Bunny::Session#queue_exists?` is a new predicate that makes it
|
26
|
+
easier to check if a queue exists.
|
27
|
+
|
28
|
+
It uses a one-off channel and `queue.declare` with `passive` set to true
|
29
|
+
under the hood.
|
30
|
+
|
31
|
+
|
32
|
+
### Inline TLS Certificates and Keys
|
33
|
+
|
34
|
+
It is now possible to provide inline client
|
35
|
+
certificate and private key (as strings) instead
|
36
|
+
of filesystem paths. The options are the same:
|
37
|
+
|
38
|
+
* `:tls` which, when set to `true`, will set SSL context up and switch to TLS port (5671)
|
39
|
+
* `:tls_cert` which now can be a client certificate (public key) in PEM format
|
40
|
+
* `:tls_key` which now can be a client key (private key) in PEM format
|
41
|
+
* `:tls_ca_certificates` which is an array of string paths to CA certificates in PEM format
|
42
|
+
|
43
|
+
For example:
|
44
|
+
|
45
|
+
``` ruby
|
46
|
+
conn = Bunny.new(:tls => true,
|
47
|
+
:tls_cert => ENV["TLS_CERTIFICATE"],
|
48
|
+
:tls_key => ENV["TLS_PRIVATE_KEY"],
|
49
|
+
:tls_ca_certificates => ["./examples/tls/cacert.pem"])
|
50
|
+
```
|
51
|
+
|
52
|
+
|
53
|
+
|
1
54
|
## Changes between Bunny 1.0.0.rc1 and 1.0.0.rc2
|
2
55
|
|
3
56
|
### Ruby 1.8.7 Compatibility Fixes
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -61,11 +61,13 @@ Bunny `0.7.x` and earlier versions support RabbitMQ 1.x and 2.x.
|
|
61
61
|
|
62
62
|
## Project Maturity
|
63
63
|
|
64
|
-
Bunny is a
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
Bunny is a mature library (started in early 2009) library with
|
65
|
+
a stable public API.
|
66
|
+
|
67
|
+
Before version 0.9, **a lot** of functionality was missing. Version
|
68
|
+
0.9 can be considered to be "second birthday" for Bunny as it was
|
69
|
+
rewritten from scratch with over a dozen of preview releases over the
|
70
|
+
course of about a year.
|
69
71
|
|
70
72
|
We (the maintainers) made our best effort to keep the new version as
|
71
73
|
backwards compatible as possible but within reason.
|
@@ -90,7 +92,7 @@ gem install bunny
|
|
90
92
|
To use Bunny in a project managed with Bundler:
|
91
93
|
|
92
94
|
``` ruby
|
93
|
-
gem "bunny", ">= 1.0.0.
|
95
|
+
gem "bunny", ">= 1.0.0.rc2"
|
94
96
|
```
|
95
97
|
|
96
98
|
|
@@ -177,15 +179,21 @@ More detailed announcements can be found in the blogs
|
|
177
179
|
|
178
180
|
### Reporting Issues
|
179
181
|
|
180
|
-
If you find a bug, poor default, missing feature or find any part of
|
181
|
-
|
182
|
-
|
182
|
+
If you find a bug, poor default, missing feature or find any part of
|
183
|
+
the API inconvenient, please [file an
|
184
|
+
issue](http://github.com/ruby-amqp/bunny/issues) on GitHub. When
|
185
|
+
filing an issue, please specify which Bunny and RabbitMQ versions you
|
186
|
+
are using, provide recent RabbitMQ log file contents if possible, and
|
187
|
+
try to explain what behavior you expected and why. Bonus points for
|
188
|
+
contributing failing test cases.
|
183
189
|
|
184
190
|
|
185
191
|
## Other Ruby RabbitMQ Clients
|
186
192
|
|
187
|
-
Other widely used Ruby RabbitMQ clients are [
|
188
|
-
|
193
|
+
Other widely used Ruby RabbitMQ clients are [March
|
194
|
+
Hare](http://rubymarchhare.info) (JRuby-only) and [amqp
|
195
|
+
gem](http://rubyamqp.info). Both are mature libraries and require
|
196
|
+
RabbitMQ 2.x or 3.x.
|
189
197
|
|
190
198
|
|
191
199
|
## Contributing
|
@@ -202,7 +210,7 @@ then set up RabbitMQ vhosts with
|
|
202
210
|
|
203
211
|
and then run tests with
|
204
212
|
|
205
|
-
./bin/rspec -cfs spec
|
213
|
+
CI=true ./bin/rspec -cfs spec
|
206
214
|
|
207
215
|
After that create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
|
208
216
|
on GitHub.
|
data/lib/bunny/channel.rb
CHANGED
@@ -157,6 +157,7 @@ module Bunny
|
|
157
157
|
# @return [Hash<String, Bunny::Consumer>] Consumer instances declared on this channel
|
158
158
|
attr_reader :consumers
|
159
159
|
|
160
|
+
DEFAULT_CONTENT_TYPE = "application/octet-stream".freeze
|
160
161
|
|
161
162
|
# @param [Bunny::Session] connection AMQP 0.9.1 connection
|
162
163
|
# @param [Integer] id Channel id, pass nil to make Bunny automatically allocate it
|
@@ -524,23 +525,25 @@ module Bunny
|
|
524
525
|
else
|
525
526
|
1
|
526
527
|
end
|
527
|
-
|
528
|
-
|
528
|
+
|
529
|
+
opts[:delivery_mode] ||= mode
|
530
|
+
opts[:content_type] ||= DEFAULT_CONTENT_TYPE
|
531
|
+
opts[:priority] ||= 0
|
529
532
|
|
530
533
|
if @next_publish_seq_no > 0
|
531
534
|
@unconfirmed_set.add(@next_publish_seq_no)
|
532
535
|
@next_publish_seq_no += 1
|
533
536
|
end
|
534
537
|
|
535
|
-
|
538
|
+
frames = AMQ::Protocol::Basic::Publish.encode(@id,
|
536
539
|
payload,
|
537
|
-
|
540
|
+
opts,
|
538
541
|
exchange_name,
|
539
542
|
routing_key,
|
540
|
-
|
543
|
+
opts[:mandatory],
|
541
544
|
false,
|
542
545
|
@connection.frame_max)
|
543
|
-
@connection.send_frameset_without_timeout(
|
546
|
+
@connection.send_frameset_without_timeout(frames, self)
|
544
547
|
|
545
548
|
self
|
546
549
|
end
|
@@ -789,8 +792,8 @@ module Bunny
|
|
789
792
|
#
|
790
793
|
# @param [String, Bunny::Queue] queue Queue to consume from
|
791
794
|
# @param [String] consumer_tag Consumer tag (unique identifier), generated by Bunny by default
|
792
|
-
# @param [Boolean] no_ack (false) If
|
793
|
-
# If
|
795
|
+
# @param [Boolean] no_ack (false) If true, delivered messages will be automatically acknowledged.
|
796
|
+
# If false, manual acknowledgements will be necessary.
|
794
797
|
# @param [Boolean] exclusive (false) Should this consumer be exclusive?
|
795
798
|
# @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc
|
796
799
|
#
|
data/lib/bunny/compatibility.rb
CHANGED
data/lib/bunny/consumer.rb
CHANGED
@@ -26,8 +26,8 @@ module Bunny
|
|
26
26
|
# @param [Bunny::Queue,String] queue Queue messages will be consumed from
|
27
27
|
# @param [String] consumer_tag Consumer tag (unique identifier). Generally it is better to let Bunny generate one.
|
28
28
|
# Empty string means RabbitMQ will generate consumer tag.
|
29
|
-
# @param [Boolean] no_ack (
|
30
|
-
# If
|
29
|
+
# @param [Boolean] no_ack (true) If true, delivered messages will be automatically acknowledged.
|
30
|
+
# If false, manual acknowledgements will be necessary.
|
31
31
|
# @param [Boolean] exclusive (false) Should this consumer be exclusive?
|
32
32
|
# @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc
|
33
33
|
# @api public
|
@@ -93,13 +93,13 @@ module Bunny
|
|
93
93
|
# @return [Boolean] true if this consumer uses automatic acknowledgement mode
|
94
94
|
# @api public
|
95
95
|
def automatic_acknowledgement?
|
96
|
-
@no_ack ==
|
96
|
+
@no_ack == true
|
97
97
|
end
|
98
98
|
|
99
99
|
# @return [Boolean] true if this consumer uses manual (explicit) acknowledgement mode
|
100
100
|
# @api public
|
101
101
|
def manual_acknowledgement?
|
102
|
-
@no_ack ==
|
102
|
+
@no_ack == false
|
103
103
|
end
|
104
104
|
|
105
105
|
#
|
data/lib/bunny/exceptions.rb
CHANGED
@@ -87,11 +87,31 @@ module Bunny
|
|
87
87
|
@username = username
|
88
88
|
@vhost = vhost
|
89
89
|
|
90
|
-
super("
|
90
|
+
super("Authentication with RabbitMQ failed. Please check your connection settings. Username: #{username}, vhost: #{vhost}, password length: #{password_length}")
|
91
91
|
end # initialize(settings)
|
92
92
|
end # PossibleAuthenticationFailureError
|
93
93
|
|
94
94
|
|
95
|
+
# Raised when RabbitMQ closes TCP connection due to an authentication failure.
|
96
|
+
# Relies on RabbitMQ 3.2 Authentication Failure Notifications extension:
|
97
|
+
# http://www.rabbitmq.com/auth-notification.html
|
98
|
+
class AuthenticationFailureError < PossibleAuthenticationFailureError
|
99
|
+
|
100
|
+
#
|
101
|
+
# API
|
102
|
+
#
|
103
|
+
|
104
|
+
attr_reader :username, :vhost
|
105
|
+
|
106
|
+
def initialize(username, vhost, password_length)
|
107
|
+
@username = username
|
108
|
+
@vhost = vhost
|
109
|
+
|
110
|
+
super(username, vhost, password_length)
|
111
|
+
end # initialize(settings)
|
112
|
+
end # AuthenticationFailureError
|
113
|
+
|
114
|
+
|
95
115
|
# backwards compatibility
|
96
116
|
# @private
|
97
117
|
ConnectionError = TCPConnectionFailed
|
@@ -206,4 +226,11 @@ module Bunny
|
|
206
226
|
class ConnectionForced < ConnectionLevelException
|
207
227
|
end
|
208
228
|
|
229
|
+
# @private
|
230
|
+
class MissingTLSCertificateFile < Exception
|
231
|
+
end
|
232
|
+
|
233
|
+
# @private
|
234
|
+
class MissingTLSKeyFile < Exception
|
235
|
+
end
|
209
236
|
end
|
data/lib/bunny/queue.rb
CHANGED
data/lib/bunny/session.rb
CHANGED
@@ -20,7 +20,7 @@ require "amq/protocol/client"
|
|
20
20
|
require "amq/settings"
|
21
21
|
|
22
22
|
module Bunny
|
23
|
-
# Represents AMQP 0.9.1 connection (
|
23
|
+
# Represents AMQP 0.9.1 connection (to a RabbitMQ node).
|
24
24
|
# @see http://rubybunny.info/articles/connecting.html Connecting to RabbitMQ guide
|
25
25
|
class Session
|
26
26
|
|
@@ -51,11 +51,13 @@ module Bunny
|
|
51
51
|
# RabbitMQ client metadata
|
52
52
|
DEFAULT_CLIENT_PROPERTIES = {
|
53
53
|
:capabilities => {
|
54
|
-
:publisher_confirms
|
55
|
-
:consumer_cancel_notify
|
56
|
-
:exchange_exchange_bindings
|
57
|
-
:"basic.nack"
|
58
|
-
:"connection.blocked"
|
54
|
+
:publisher_confirms => true,
|
55
|
+
:consumer_cancel_notify => true,
|
56
|
+
:exchange_exchange_bindings => true,
|
57
|
+
:"basic.nack" => true,
|
58
|
+
:"connection.blocked" => true,
|
59
|
+
# See http://www.rabbitmq.com/auth-notification.html
|
60
|
+
:authentication_failure_close => true
|
59
61
|
},
|
60
62
|
:product => "Bunny",
|
61
63
|
:platform => ::RUBY_DESCRIPTION,
|
@@ -98,10 +100,12 @@ module Bunny
|
|
98
100
|
# @option connection_string_or_opts [String] :password ("guest") Password
|
99
101
|
# @option connection_string_or_opts [String] :vhost ("/") Virtual host to use
|
100
102
|
# @option connection_string_or_opts [Integer] :heartbeat (600) Heartbeat interval. 0 means no heartbeat.
|
103
|
+
# @option connection_string_or_opts [Integer] :network_recovery_interval (4) Recovery interval periodic network recovery will use. This includes initial pause after network failure.
|
101
104
|
# @option connection_string_or_opts [Boolean] :tls (false) Should TLS/SSL be used?
|
102
105
|
# @option connection_string_or_opts [String] :tls_cert (nil) Path to client TLS/SSL certificate file (.pem)
|
103
106
|
# @option connection_string_or_opts [String] :tls_key (nil) Path to client TLS/SSL private key file (.pem)
|
104
107
|
# @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
|
108
|
+
# @option connection_string_or_opts [Integer] :continuation_timeout (4000) Timeout for client operations that expect a response (e.g. {Bunny::Queue#get}), in milliseconds.
|
105
109
|
#
|
106
110
|
# @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
|
107
111
|
# @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
|
@@ -296,7 +300,7 @@ module Bunny
|
|
296
300
|
def with_channel(n = nil)
|
297
301
|
ch = create_channel(n)
|
298
302
|
yield ch
|
299
|
-
ch.close
|
303
|
+
ch.close if ch.open?
|
300
304
|
|
301
305
|
self
|
302
306
|
end
|
@@ -306,10 +310,12 @@ module Bunny
|
|
306
310
|
status == :connecting
|
307
311
|
end
|
308
312
|
|
313
|
+
# @return [Boolean] true if this AMQP 0.9.1 connection is closed
|
309
314
|
def closed?
|
310
315
|
status == :closed
|
311
316
|
end
|
312
317
|
|
318
|
+
# @return [Boolean] true if this AMQP 0.9.1 connection is open
|
313
319
|
def open?
|
314
320
|
(status == :open || status == :connected || status == :connecting) && @transport.open?
|
315
321
|
end
|
@@ -390,6 +396,46 @@ module Bunny
|
|
390
396
|
AMQ::Settings.parse_amqp_url(uri)
|
391
397
|
end
|
392
398
|
|
399
|
+
# Checks if a queue with given name exists.
|
400
|
+
#
|
401
|
+
# Implemented using queue.declare
|
402
|
+
# with passive set to true and a one-off (short lived) channel
|
403
|
+
# under the hood.
|
404
|
+
#
|
405
|
+
# @param [String] name Queue name
|
406
|
+
# @return [Boolean] true if queue exists
|
407
|
+
def queue_exists?(name)
|
408
|
+
ch = create_channel
|
409
|
+
begin
|
410
|
+
ch.queue(name, :passive => true)
|
411
|
+
true
|
412
|
+
rescue Bunny::NotFound => _
|
413
|
+
false
|
414
|
+
ensure
|
415
|
+
ch.close if ch.open?
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Checks if a exchange with given name exists.
|
420
|
+
#
|
421
|
+
# Implemented using exchange.declare
|
422
|
+
# with passive set to true and a one-off (short lived) channel
|
423
|
+
# under the hood.
|
424
|
+
#
|
425
|
+
# @param [String] name Exchange name
|
426
|
+
# @return [Boolean] true if exchange exists
|
427
|
+
def exchange_exists?(name)
|
428
|
+
ch = create_channel
|
429
|
+
begin
|
430
|
+
ch.exchange(name, :passive => true)
|
431
|
+
true
|
432
|
+
rescue Bunny::NotFound => _
|
433
|
+
false
|
434
|
+
ensure
|
435
|
+
ch.close if ch.open?
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
393
439
|
|
394
440
|
#
|
395
441
|
# Implementation
|
@@ -442,6 +488,13 @@ module Bunny
|
|
442
488
|
end
|
443
489
|
end
|
444
490
|
|
491
|
+
# Handles incoming frames and dispatches them.
|
492
|
+
#
|
493
|
+
# Channel methods (`channel.open-ok`, `channel.close-ok`) are
|
494
|
+
# handled by the session itself.
|
495
|
+
# Connection level errors result in exceptions being raised.
|
496
|
+
# Deliveries and other methods are passed on to channels to dispatch.
|
497
|
+
#
|
445
498
|
# @private
|
446
499
|
def handle_frame(ch_number, method)
|
447
500
|
@logger.debug "Session#handle_frame on #{ch_number}: #{method.inspect}"
|
@@ -782,6 +835,19 @@ module Bunny
|
|
782
835
|
end
|
783
836
|
end # send_frameset_without_timeout(frames)
|
784
837
|
|
838
|
+
# @private
|
839
|
+
def send_raw_without_timeout(data, channel)
|
840
|
+
# some developers end up sharing channels between threads and when multiple
|
841
|
+
# threads publish on the same channel aggressively, at some point frames will be
|
842
|
+
# delivered out of order and broker will raise 505 UNEXPECTED_FRAME exception.
|
843
|
+
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
844
|
+
# locking. Note that "single frame" methods do not need this kind of synchronization. MK.
|
845
|
+
channel.synchronize do
|
846
|
+
@transport.write(data)
|
847
|
+
signal_activity!
|
848
|
+
end
|
849
|
+
end # send_frameset_without_timeout(frames)
|
850
|
+
|
785
851
|
# @return [String]
|
786
852
|
# @api public
|
787
853
|
def to_s
|
@@ -823,7 +889,16 @@ module Bunny
|
|
823
889
|
raise Bunny::PossibleAuthenticationFailureError.new(self.user, self.vhost, self.password.size)
|
824
890
|
end
|
825
891
|
|
826
|
-
|
892
|
+
response = frame.decode_payload
|
893
|
+
if response.is_a?(AMQ::Protocol::Connection::Close)
|
894
|
+
@state = :closed
|
895
|
+
@logger.error "Authentication with RabbitMQ failed: #{response.reply_code} #{response.reply_text}"
|
896
|
+
raise Bunny::AuthenticationFailureError.new(self.user, self.vhost, self.password.size)
|
897
|
+
end
|
898
|
+
|
899
|
+
|
900
|
+
|
901
|
+
connection_tune = response
|
827
902
|
|
828
903
|
@frame_max = negotiate_value(@client_frame_max, connection_tune.frame_max)
|
829
904
|
@channel_max = negotiate_value(@client_channel_max, connection_tune.channel_max)
|
data/lib/bunny/system_timer.rb
CHANGED
@@ -7,7 +7,7 @@ module Bunny
|
|
7
7
|
class SystemTimer
|
8
8
|
# Executes a block of code, raising if the execution does not finish
|
9
9
|
# in the alloted period of time, in seconds.
|
10
|
-
def self.timeout(seconds, exception)
|
10
|
+
def self.timeout(seconds, exception = nil)
|
11
11
|
if seconds
|
12
12
|
::SystemTimer.timeout_after(seconds, exception) do
|
13
13
|
yield
|
data/lib/bunny/transport.rb
CHANGED
@@ -5,7 +5,7 @@ require "monitor"
|
|
5
5
|
begin
|
6
6
|
require "openssl"
|
7
7
|
rescue LoadError => le
|
8
|
-
puts "Could not load OpenSSL"
|
8
|
+
$stderr.puts "Could not load OpenSSL"
|
9
9
|
end
|
10
10
|
|
11
11
|
require "bunny/exceptions"
|
@@ -19,8 +19,10 @@ module Bunny
|
|
19
19
|
# API
|
20
20
|
#
|
21
21
|
|
22
|
+
# Default TCP connection timeout
|
22
23
|
DEFAULT_CONNECTION_TIMEOUT = 5.0
|
23
|
-
#
|
24
|
+
# Default TLS protocol version to use.
|
25
|
+
# Currently SSLv3, same as in RabbitMQ Java client
|
24
26
|
DEFAULT_TLS_PROTOCOL = "SSLv3"
|
25
27
|
|
26
28
|
|
@@ -36,21 +38,6 @@ module Bunny
|
|
36
38
|
|
37
39
|
@logger = session.logger
|
38
40
|
@tls_enabled = tls_enabled?(opts)
|
39
|
-
@tls_certificate_path = tls_certificate_path_from(opts)
|
40
|
-
@tls_key_path = tls_key_path_from(opts)
|
41
|
-
@tls_certificate = opts[:tls_certificate] || opts[:ssl_cert_string]
|
42
|
-
@tls_key = opts[:tls_key] || opts[:ssl_key_string]
|
43
|
-
@tls_certificate_store = opts[:tls_certificate_store]
|
44
|
-
|
45
|
-
default_ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE
|
46
|
-
default_ca_path = ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR
|
47
|
-
@tls_ca_certificates = opts.fetch(:tls_ca_certificates, [
|
48
|
-
default_ca_file,
|
49
|
-
File.join(default_ca_path, 'ca-certificates.crt'), # Ubuntu/Debian
|
50
|
-
File.join(default_ca_path, 'ca-bundle.crt'), # Amazon Linux & Fedora/RHEL
|
51
|
-
File.join(default_ca_path, 'ca-bundle.pem') # OpenSUSE
|
52
|
-
])
|
53
|
-
@verify_peer = opts[:verify_ssl] || opts[:verify_peer]
|
54
41
|
|
55
42
|
@read_write_timeout = opts[:socket_timeout] || 3
|
56
43
|
@read_write_timeout = nil if @read_write_timeout == 0
|
@@ -61,10 +48,9 @@ module Bunny
|
|
61
48
|
@writes_mutex = @session.mutex_impl.new
|
62
49
|
|
63
50
|
maybe_initialize_socket
|
64
|
-
prepare_tls_context if @tls_enabled
|
51
|
+
prepare_tls_context(opts) if @tls_enabled
|
65
52
|
end
|
66
53
|
|
67
|
-
|
68
54
|
def hostname
|
69
55
|
@host
|
70
56
|
end
|
@@ -234,7 +220,7 @@ module Bunny
|
|
234
220
|
def self.reacheable?(host, port, timeout)
|
235
221
|
begin
|
236
222
|
s = Bunny::Socket.open(host, port,
|
237
|
-
|
223
|
+
:socket_timeout => timeout)
|
238
224
|
|
239
225
|
true
|
240
226
|
rescue SocketError, Timeout::Error => e
|
@@ -252,8 +238,8 @@ module Bunny
|
|
252
238
|
begin
|
253
239
|
@socket = Bunny::Timeout.timeout(@connect_timeout, ClientTimeout) do
|
254
240
|
Bunny::Socket.open(@host, @port,
|
255
|
-
|
256
|
-
|
241
|
+
:keepalive => @opts[:keepalive],
|
242
|
+
:socket_timeout => @connect_timeout)
|
257
243
|
end
|
258
244
|
rescue StandardError, ClientTimeout => e
|
259
245
|
@status = :not_connected
|
@@ -289,8 +275,49 @@ module Bunny
|
|
289
275
|
opts[:tls_key] || opts[:ssl_key] || opts[:tls_key_path] || opts[:ssl_key_path]
|
290
276
|
end
|
291
277
|
|
292
|
-
def
|
293
|
-
|
278
|
+
def tls_certificate_from(opts)
|
279
|
+
begin
|
280
|
+
read_client_certificate!
|
281
|
+
rescue MissingTLSCertificateFile => e
|
282
|
+
inline_client_certificate_from(opts)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def tls_key_from(opts)
|
287
|
+
begin
|
288
|
+
read_client_key!
|
289
|
+
rescue MissingTLSKeyFile => e
|
290
|
+
inline_client_key_from(opts)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
def inline_client_certificate_from(opts)
|
296
|
+
opts[:tls_certificate] || opts[:ssl_cert_string]
|
297
|
+
end
|
298
|
+
|
299
|
+
def inline_client_key_from(opts)
|
300
|
+
opts[:tls_key] || opts[:ssl_key_string]
|
301
|
+
end
|
302
|
+
|
303
|
+
def prepare_tls_context(opts)
|
304
|
+
# client cert/key paths
|
305
|
+
@tls_certificate_path = tls_certificate_path_from(opts)
|
306
|
+
@tls_key_path = tls_key_path_from(opts)
|
307
|
+
# client cert/key
|
308
|
+
@tls_certificate = tls_certificate_from(opts)
|
309
|
+
@tls_key = tls_key_from(opts)
|
310
|
+
@tls_certificate_store = opts[:tls_certificate_store]
|
311
|
+
|
312
|
+
default_ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE
|
313
|
+
default_ca_path = ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR
|
314
|
+
@tls_ca_certificates = opts.fetch(:tls_ca_certificates, [
|
315
|
+
default_ca_file,
|
316
|
+
File.join(default_ca_path, 'ca-certificates.crt'), # Ubuntu/Debian
|
317
|
+
File.join(default_ca_path, 'ca-bundle.crt'), # Amazon Linux & Fedora/RHEL
|
318
|
+
File.join(default_ca_path, 'ca-bundle.pem') # OpenSUSE
|
319
|
+
])
|
320
|
+
@verify_peer = opts[:verify_ssl] || opts[:verify_peer]
|
294
321
|
|
295
322
|
@tls_context = initialize_tls_context(OpenSSL::SSL::SSLContext.new)
|
296
323
|
end
|
@@ -304,17 +331,24 @@ module Bunny
|
|
304
331
|
s
|
305
332
|
end
|
306
333
|
|
307
|
-
def
|
308
|
-
raise
|
334
|
+
def check_local_certificate_path!(s)
|
335
|
+
raise MissingTLSCertificateFile, "cannot read client TLS certificate from #{s}" unless File.file?(s) && File.readable?(s)
|
309
336
|
end
|
310
337
|
|
311
|
-
def
|
338
|
+
def check_local_key_path!(s)
|
339
|
+
raise MissingTLSKeyFile, "cannot read client TLS private key from #{s}" unless File.file?(s) && File.readable?(s)
|
340
|
+
end
|
341
|
+
|
342
|
+
def read_client_certificate!
|
312
343
|
if @tls_certificate_path
|
313
|
-
|
344
|
+
check_local_certificate_path!(@tls_certificate_path)
|
314
345
|
@tls_certificate = File.read(@tls_certificate_path)
|
315
346
|
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def read_client_key!
|
316
350
|
if @tls_key_path
|
317
|
-
|
351
|
+
check_local_key_path!(@tls_key_path)
|
318
352
|
@tls_key = File.read(@tls_key_path)
|
319
353
|
end
|
320
354
|
end
|
data/lib/bunny/version.rb
CHANGED
@@ -14,18 +14,18 @@ describe Bunny::Session do
|
|
14
14
|
context "when schema is not one of [amqp, amqps]" do
|
15
15
|
it "raises ArgumentError" do
|
16
16
|
expect {
|
17
|
-
described_class.new("http://
|
17
|
+
described_class.new("http://127.0.0.1")
|
18
18
|
}.to raise_error(ArgumentError, /amqp or amqps schema/)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
|
23
23
|
it "handles amqp:// URIs w/o path part" do
|
24
|
-
session = described_class.new("amqp://
|
24
|
+
session = described_class.new("amqp://127.0.0.1")
|
25
25
|
session.start
|
26
26
|
|
27
27
|
session.vhost.should == "/"
|
28
|
-
session.host.should == "
|
28
|
+
session.host.should == "127.0.0.1"
|
29
29
|
session.port.should == 5672
|
30
30
|
session.ssl?.should be_false
|
31
31
|
|
@@ -35,9 +35,9 @@ describe Bunny::Session do
|
|
35
35
|
|
36
36
|
context "when URI ends in a slash" do
|
37
37
|
it "parses vhost as an empty string" do
|
38
|
-
session = described_class.new("amqp://
|
38
|
+
session = described_class.new("amqp://127.0.0.1/")
|
39
39
|
|
40
|
-
session.hostname.should == "
|
40
|
+
session.hostname.should == "127.0.0.1"
|
41
41
|
session.port.should == 5672
|
42
42
|
session.vhost.should == ""
|
43
43
|
end
|
@@ -18,67 +18,88 @@ describe Bunny::Exchange, "#delete" do
|
|
18
18
|
x = ch.fanout("bunny.tests.exchanges.fanout#{rand}")
|
19
19
|
|
20
20
|
x.delete
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
# no exception as of RabbitMQ 3.2. MK.
|
22
|
+
x.delete
|
23
|
+
|
25
24
|
ch.exchanges.size.should == 0
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
29
28
|
|
30
29
|
context "with a name of a non-existent exchange" do
|
31
|
-
it "
|
30
|
+
it "DOES NOT rais an exception" do
|
32
31
|
ch = connection.create_channel
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
# no exception as of RabbitMQ 3.2. MK.
|
34
|
+
ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}")
|
35
|
+
ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}")
|
36
|
+
ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}")
|
37
|
+
ch.exchange_delete("sdkhflsdjflskdjflsd#{rand}")
|
37
38
|
end
|
38
39
|
end
|
39
|
-
|
40
|
+
|
40
41
|
context "with a name of 'amq.direct'" do
|
41
42
|
it "does not delete the exchange" do
|
42
43
|
ch = connection.create_channel
|
43
44
|
x = ch.direct('amq.direct')
|
44
|
-
|
45
|
+
|
45
46
|
x.delete.should == nil
|
46
47
|
end
|
47
48
|
end
|
48
|
-
|
49
|
+
|
49
50
|
context "with a name of 'amq.fanout'" do
|
50
51
|
it "does not delete the exchange" do
|
51
52
|
ch = connection.create_channel
|
52
53
|
x = ch.fanout('amq.fanout')
|
53
|
-
|
54
|
+
|
54
55
|
x.delete.should == nil
|
55
56
|
end
|
56
57
|
end
|
57
|
-
|
58
|
+
|
58
59
|
context "with a name of 'amq.topic'" do
|
59
60
|
it "does not delete the exchange" do
|
60
61
|
ch = connection.create_channel
|
61
62
|
x = ch.topic('amq.topic')
|
62
|
-
|
63
|
+
|
63
64
|
x.delete.should == nil
|
64
65
|
end
|
65
66
|
end
|
66
|
-
|
67
|
+
|
67
68
|
context "with a name of 'amq.headers'" do
|
68
69
|
it "does not delete the exchange" do
|
69
70
|
ch = connection.create_channel
|
70
71
|
x = ch.headers('amq.headers')
|
71
|
-
|
72
|
+
|
72
73
|
x.delete.should == nil
|
73
74
|
end
|
74
75
|
end
|
75
|
-
|
76
|
+
|
76
77
|
context "with a name of 'amq.match'" do
|
77
78
|
it "does not delete the exchange" do
|
78
79
|
ch = connection.create_channel
|
79
80
|
x = ch.headers('amq.match')
|
80
|
-
|
81
|
+
|
81
82
|
x.delete.should == nil
|
82
83
|
end
|
83
84
|
end
|
85
|
+
|
86
|
+
|
87
|
+
describe "#exchange_exists?" do
|
88
|
+
context "when a exchange exists" do
|
89
|
+
it "returns true" do
|
90
|
+
ch = connection.create_channel
|
91
|
+
|
92
|
+
connection.exchange_exists?("amq.fanout").should be_true
|
93
|
+
connection.exchange_exists?("amq.direct").should be_true
|
94
|
+
connection.exchange_exists?("amq.topic").should be_true
|
95
|
+
connection.exchange_exists?("amq.match").should be_true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when a exchange DOES NOT exist" do
|
100
|
+
it "returns false" do
|
101
|
+
connection.exchange_exists?("suf89u9a4jo3ndnakls##{Time.now.to_i}").should be_false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
84
105
|
end
|
@@ -136,6 +136,25 @@ describe Bunny::Queue do
|
|
136
136
|
end
|
137
137
|
|
138
138
|
|
139
|
+
describe "#queue_exists?" do
|
140
|
+
context "when a queue exists" do
|
141
|
+
it "returns true" do
|
142
|
+
ch = connection.create_channel
|
143
|
+
q = ch.queue("", :exlusive => true)
|
144
|
+
|
145
|
+
connection.queue_exists?(q.name).should be_true
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "when a queue DOES NOT exist" do
|
150
|
+
it "returns false" do
|
151
|
+
connection.queue_exists?("suf89u9a4jo3ndnakls##{Time.now.to_i}").should be_false
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
|
139
158
|
unless ENV["CI"]
|
140
159
|
# requires RabbitMQ 3.1+
|
141
160
|
context "when queue is declared with bounded length" do
|
@@ -19,9 +19,8 @@ describe Bunny::Queue, "#delete" do
|
|
19
19
|
q = ch.queue("")
|
20
20
|
|
21
21
|
q.delete
|
22
|
-
|
23
|
-
|
24
|
-
}.to raise_error(Bunny::NotFound)
|
22
|
+
# no exception as of RabbitMQ 3.2. MK.
|
23
|
+
q.delete
|
25
24
|
|
26
25
|
ch.queues.size.should == 0
|
27
26
|
end
|
@@ -29,12 +28,14 @@ describe Bunny::Queue, "#delete" do
|
|
29
28
|
|
30
29
|
|
31
30
|
context "with a name of an existing queue" do
|
32
|
-
it "
|
31
|
+
it "DOES NOT raise an exception" do
|
33
32
|
ch = connection.create_channel
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
# no exception as of RabbitMQ 3.2. MK.
|
35
|
+
ch.queue_delete("sdkhflsdjflskdjflsd#{rand}")
|
36
|
+
ch.queue_delete("sdkhflsdjflskdjflsd#{rand}")
|
37
|
+
ch.queue_delete("sdkhflsdjflskdjflsd#{rand}")
|
38
|
+
ch.queue_delete("sdkhflsdjflskdjflsd#{rand}")
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
@@ -47,8 +47,8 @@ describe Bunny::Queue, "NOT bound to an exchange" do
|
|
47
47
|
x = ch.fanout("amq.fanout")
|
48
48
|
q = ch.queue("", :exclusive => true)
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
# No exception as of RabbitMQ 3.2. MK.
|
51
|
+
q.unbind(x)
|
52
|
+
q.unbind(x)
|
53
53
|
end
|
54
54
|
end
|
@@ -2,24 +2,9 @@
|
|
2
2
|
require "spec_helper"
|
3
3
|
|
4
4
|
unless ENV["CI"]
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
:password => "bunny_password",
|
9
|
-
:vhost => "bunny_testbed",
|
10
|
-
:tls => true,
|
11
|
-
:tls_cert => "spec/tls/client_cert.pem",
|
12
|
-
:tls_key => "spec/tls/client_key.pem",
|
13
|
-
:tls_ca_certificates => ["./spec/tls/cacert.pem"])
|
14
|
-
c.start
|
15
|
-
c
|
16
|
-
end
|
17
|
-
|
18
|
-
after :each do
|
19
|
-
connection.close
|
20
|
-
end
|
21
|
-
|
22
|
-
it "provides the same API as a regular connection" do
|
5
|
+
shared_examples_for "successful TLS connection" do
|
6
|
+
it "succeeds" do
|
7
|
+
connection.should be_tls
|
23
8
|
ch = connection.create_channel
|
24
9
|
|
25
10
|
q = ch.queue("", :exclusive => true)
|
@@ -46,13 +31,15 @@ unless ENV["CI"]
|
|
46
31
|
end
|
47
32
|
|
48
33
|
|
49
|
-
describe "TLS connection to RabbitMQ
|
34
|
+
describe "TLS connection to RabbitMQ with client certificates" do
|
50
35
|
let(:connection) do
|
51
36
|
c = Bunny.new(:user => "bunny_gem",
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
37
|
+
:password => "bunny_password",
|
38
|
+
:vhost => "bunny_testbed",
|
39
|
+
:tls => true,
|
40
|
+
:tls_cert => "spec/tls/client_cert.pem",
|
41
|
+
:tls_key => "spec/tls/client_key.pem",
|
42
|
+
:tls_ca_certificates => ["./spec/tls/cacert.pem"])
|
56
43
|
c.start
|
57
44
|
c
|
58
45
|
end
|
@@ -61,30 +48,26 @@ unless ENV["CI"]
|
|
61
48
|
connection.close
|
62
49
|
end
|
63
50
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
q = ch.queue("", :exclusive => true)
|
68
|
-
x = ch.default_exchange
|
69
|
-
|
70
|
-
x.publish("xyzzy", :routing_key => q.name).
|
71
|
-
publish("xyzzy", :routing_key => q.name).
|
72
|
-
publish("xyzzy", :routing_key => q.name).
|
73
|
-
publish("xyzzy", :routing_key => q.name)
|
51
|
+
include_examples "successful TLS connection"
|
52
|
+
end
|
74
53
|
|
75
|
-
sleep 0.5
|
76
|
-
q.message_count.should == 4
|
77
54
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
55
|
+
describe "TLS connection to RabbitMQ without client certificates" do
|
56
|
+
let(:connection) do
|
57
|
+
c = Bunny.new(:user => "bunny_gem",
|
58
|
+
:password => "bunny_password",
|
59
|
+
:vhost => "bunny_testbed",
|
60
|
+
:tls => true,
|
61
|
+
:tls_ca_certificates => ["./spec/tls/cacert.pem"])
|
62
|
+
c.start
|
63
|
+
c
|
64
|
+
end
|
85
65
|
|
86
|
-
|
66
|
+
after :each do
|
67
|
+
connection.close
|
87
68
|
end
|
69
|
+
|
70
|
+
include_examples "successful TLS connection"
|
88
71
|
end
|
89
72
|
|
90
73
|
|
@@ -102,30 +85,43 @@ unless ENV["CI"]
|
|
102
85
|
connection.close
|
103
86
|
end
|
104
87
|
|
105
|
-
|
106
|
-
|
107
|
-
ch = connection.create_channel
|
88
|
+
include_examples "successful TLS connection"
|
89
|
+
end
|
108
90
|
|
109
|
-
q = ch.queue("", :exclusive => true)
|
110
|
-
x = ch.default_exchange
|
111
91
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
92
|
+
describe "TLS connection to RabbitMQ with a connection string and w/o client certificate and key" do
|
93
|
+
let(:connection) do
|
94
|
+
c = Bunny.new("amqps://bunny_gem:bunny_password@127.0.0.1/bunny_testbed",
|
95
|
+
:tls_ca_certificates => ["./spec/tls/cacert.pem"])
|
96
|
+
c.start
|
97
|
+
c
|
98
|
+
end
|
116
99
|
|
117
|
-
|
118
|
-
|
100
|
+
after :each do
|
101
|
+
connection.close
|
102
|
+
end
|
119
103
|
|
120
|
-
|
121
|
-
|
122
|
-
i += 1
|
123
|
-
end
|
124
|
-
sleep 1.0
|
125
|
-
i.should == 4
|
126
|
-
q.message_count.should == 0
|
104
|
+
include_examples "successful TLS connection"
|
105
|
+
end
|
127
106
|
|
128
|
-
|
107
|
+
|
108
|
+
describe "TLS connection to RabbitMQ with client certificates provided inline" do
|
109
|
+
let(:connection) do
|
110
|
+
c = Bunny.new(:user => "bunny_gem",
|
111
|
+
:password => "bunny_password",
|
112
|
+
:vhost => "bunny_testbed",
|
113
|
+
:tls => true,
|
114
|
+
:tls_cert => File.read("./spec/tls/client_cert.pem"),
|
115
|
+
:tls_key => File.read("./spec/tls/client_key.pem"),
|
116
|
+
:tls_ca_certificates => ["./spec/tls/cacert.pem"])
|
117
|
+
c.start
|
118
|
+
c
|
129
119
|
end
|
120
|
+
|
121
|
+
after :each do
|
122
|
+
connection.close
|
123
|
+
end
|
124
|
+
|
125
|
+
include_examples "successful TLS connection"
|
130
126
|
end
|
131
127
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
if RUBY_VERSION.to_f < 1.9
|
4
|
+
describe Bunny::SystemTimer do
|
5
|
+
it 'supports being called with a single argument' do
|
6
|
+
lambda {Bunny::SystemTimer::timeout(1) {}}.should_not raise_error
|
7
|
+
lambda {Bunny::SystemTimer::timeout(1, nil) {}}.should_not raise_error
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bunny
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Duncan
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-10-
|
15
|
+
date: 2013-10-24 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: amq-protocol
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- lib/bunny/version.rb
|
130
130
|
- lib/bunny/versioned_delivery_tag.rb
|
131
131
|
- profiling/basic_publish/with_4K_messages.rb
|
132
|
+
- repl
|
132
133
|
- spec/compatibility/queue_declare_spec.rb
|
133
134
|
- spec/compatibility/queue_declare_with_default_channel_spec.rb
|
134
135
|
- spec/higher_level_api/integration/basic_ack_spec.rb
|
@@ -197,6 +198,7 @@ files:
|
|
197
198
|
- spec/unit/concurrent/condition_spec.rb
|
198
199
|
- spec/unit/concurrent/linked_continuation_queue_spec.rb
|
199
200
|
- spec/unit/concurrent/synchronized_sorted_set_spec.rb
|
201
|
+
- spec/unit/system_timer_spec.rb
|
200
202
|
homepage: http://rubybunny.info
|
201
203
|
licenses:
|
202
204
|
- MIT
|
@@ -217,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
217
219
|
version: 1.3.1
|
218
220
|
requirements: []
|
219
221
|
rubyforge_project:
|
220
|
-
rubygems_version: 2.
|
222
|
+
rubygems_version: 2.1.9
|
221
223
|
signing_key:
|
222
224
|
specification_version: 4
|
223
225
|
summary: Popular easy to use Ruby client for RabbitMQ
|
@@ -290,4 +292,5 @@ test_files:
|
|
290
292
|
- spec/unit/concurrent/condition_spec.rb
|
291
293
|
- spec/unit/concurrent/linked_continuation_queue_spec.rb
|
292
294
|
- spec/unit/concurrent/synchronized_sorted_set_spec.rb
|
295
|
+
- spec/unit/system_timer_spec.rb
|
293
296
|
has_rdoc: true
|