bunny 1.0.0.rc2 → 1.0.0.rc3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|