bunny 2.11.0 → 2.12.0.rc1
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/CONTRIBUTING.md +1 -1
- data/ChangeLog.md +62 -1
- data/Gemfile +9 -6
- data/README.md +3 -2
- data/bunny.gemspec +1 -2
- data/docker-compose.yml +9 -1
- data/docker/Dockerfile +6 -6
- data/lib/bunny/channel.rb +77 -39
- data/lib/bunny/consumer_work_pool.rb +1 -0
- data/lib/bunny/cruby/ssl_socket.rb +5 -0
- data/lib/bunny/heartbeat_sender.rb +2 -1
- data/lib/bunny/jruby/ssl_socket.rb +5 -0
- data/lib/bunny/queue.rb +0 -5
- data/lib/bunny/reader_loop.rb +4 -0
- data/lib/bunny/session.rb +22 -14
- data/lib/bunny/transport.rb +4 -2
- data/lib/bunny/version.rb +1 -1
- data/repl +1 -1
- data/spec/higher_level_api/integration/toxiproxy_spec.rb +75 -0
- data/spec/issues/issue549_spec.rb +30 -0
- data/spec/toxiproxy_helper.rb +28 -0
- metadata +17 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 80a243c18a410ace3d56439bf7cbb60141112e388db773e7b3fe1bbe6594ba30
|
4
|
+
data.tar.gz: 548aa57383b638a79a2b4863255c8235304b498a850204977ed965118d9131c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc92d15fa248ed121180d78db848b4b8399104e4d72aea6cbadca4a14ece080cd893a6db75b8c29fc02c002510ad15a0949492ce19062d8bb4b78e90d84500f9
|
7
|
+
data.tar.gz: 7cc116eec7ae1119d8216853af7f266e202756b983b910ae56c129ab0b2dbd93bb55213c2e682e56c3e56c5aac98ba2e04e92b57873702c51e358f0975112929
|
data/CONTRIBUTING.md
CHANGED
@@ -90,7 +90,7 @@ Version >= 1.6.0+ is required for compose version 2 syntax.
|
|
90
90
|
After those have been installed (and the `docker-compose` command is available on your command line path), run
|
91
91
|
|
92
92
|
```
|
93
|
-
docker-compose up
|
93
|
+
docker-compose build && docker-compose up
|
94
94
|
```
|
95
95
|
|
96
96
|
The first time you do this, it will take some time, since it has to download everything it needs
|
data/ChangeLog.md
CHANGED
@@ -1,4 +1,64 @@
|
|
1
|
-
## Changes between Bunny 2.
|
1
|
+
## Changes between Bunny 2.11.0 and 2.12.0 (unreleased)
|
2
|
+
|
3
|
+
### More Defensive Treatment of `queue.declare-ok` Responses
|
4
|
+
|
5
|
+
Responses for `queue.declare` are now checked against a memoized
|
6
|
+
queue name (but only if the queue is not server-named). This helps
|
7
|
+
avoids scenarios with overlapping/concurrent requests due to high
|
8
|
+
network latency as demonstrated in [#558](https://github.com/ruby-amqp/bunny/issues/558).
|
9
|
+
|
10
|
+
"Mismatched" responses will be ignored: Bunny channel API would throw
|
11
|
+
an exception for such declarations and there would be no way to "return to"
|
12
|
+
even if a matching response arrived and was matched with one of the pending
|
13
|
+
requests in a reasonable period of time.
|
14
|
+
|
15
|
+
As part of this work a new Toxiproxy-based test suite was introduced
|
16
|
+
to Bunny.
|
17
|
+
|
18
|
+
GitHub issue: [#558](https://github.com/ruby-amqp/bunny/issues/558)
|
19
|
+
|
20
|
+
Reproduction steps contributed by Brian Morton and Scott Bonebraker.
|
21
|
+
|
22
|
+
### I/O Exceptions from Heartbeat Sender are Now Silent
|
23
|
+
|
24
|
+
Heartbeat sender's purpose is to notify the peer, not so much
|
25
|
+
to detect local connectivity failures; those will be detected
|
26
|
+
by the I/O loop and transport.
|
27
|
+
|
28
|
+
For single threaded connection users that prefer to roll their own
|
29
|
+
recovery strategies getting exceptions from the heartbeat sender
|
30
|
+
was counterproductive and painful to deal with.
|
31
|
+
|
32
|
+
As part of this work a new Toxiproxy-based test suite was introduced
|
33
|
+
to Bunny.
|
34
|
+
|
35
|
+
GitHub issue: [#559](https://github.com/ruby-amqp/bunny/issues/559)
|
36
|
+
|
37
|
+
Contributed by Scott Bonebraker.
|
38
|
+
|
39
|
+
### Connection Recovery Will Fail When Max Retry Attempt Limit is Exceeded
|
40
|
+
|
41
|
+
GitHub issue: [#549](https://github.com/ruby-amqp/bunny/issues/549)
|
42
|
+
|
43
|
+
Contributed by Arlandis Word.
|
44
|
+
|
45
|
+
### Squashed Warnings
|
46
|
+
|
47
|
+
Many warnings have been eliminated.
|
48
|
+
|
49
|
+
GitHub issue: [#563](https://github.com/ruby-amqp/bunny/issues/563)
|
50
|
+
|
51
|
+
Contributed by @dacto.
|
52
|
+
|
53
|
+
|
54
|
+
### API Reference Corrections
|
55
|
+
|
56
|
+
GitHub issue: [#557](https://github.com/ruby-amqp/bunny/pull/557)
|
57
|
+
|
58
|
+
Contributed by Bruno Costa.
|
59
|
+
|
60
|
+
|
61
|
+
## Changes between Bunny 2.10.0 and 2.11.0 (Jun 21st, 2018)
|
2
62
|
|
3
63
|
### More Reliable System-wide Trusted Certificate Directory Detection
|
4
64
|
|
@@ -17,6 +77,7 @@ Contributed by Ana María Martínez Gómez.
|
|
17
77
|
`2.10.0` is a maintenance release that introduces a couple of
|
18
78
|
**minor potentially breaking changes**.
|
19
79
|
|
80
|
+
|
20
81
|
### Disabling Heartbeats Also Disables TCP Socket Read Timeouts
|
21
82
|
|
22
83
|
Disabling heartbeats will now disable TCP socket read timeouts.
|
data/Gemfile
CHANGED
@@ -11,7 +11,7 @@ extend Module.new {
|
|
11
11
|
|
12
12
|
local_path = File.expand_path("../vendor/#{name}", __FILE__)
|
13
13
|
if File.exist?(local_path)
|
14
|
-
super name, options.merge(:
|
14
|
+
super name, options.merge(path: local_path).
|
15
15
|
delete_if { |key, _| [:git, :branch].include?(key) }
|
16
16
|
else
|
17
17
|
super name, *args
|
@@ -19,21 +19,24 @@ extend Module.new {
|
|
19
19
|
end
|
20
20
|
}
|
21
21
|
|
22
|
-
gem "rake", ">=
|
22
|
+
gem "rake", ">= 12.3.1"
|
23
23
|
gem "effin_utf8"
|
24
24
|
|
25
25
|
group :development do
|
26
26
|
gem "yard"
|
27
27
|
|
28
|
-
gem "redcarpet", :
|
29
|
-
gem "ruby-prof", :
|
28
|
+
gem "redcarpet", platform: :mri
|
29
|
+
gem "ruby-prof", platform: :mri
|
30
30
|
|
31
|
-
gem "
|
31
|
+
gem "ripl"
|
32
|
+
gem "ripl-multi_line"
|
33
|
+
gem "ripl-irb"
|
32
34
|
end
|
33
35
|
|
34
36
|
group :test do
|
35
37
|
gem "rspec", "~> 3.5.0"
|
36
38
|
gem "rabbitmq_http_api_client", "~> 1.9.1", require: "rabbitmq/http/client"
|
39
|
+
gem "toxiproxy", "~> 1.0.3"
|
37
40
|
end
|
38
41
|
|
39
42
|
gemspec
|
@@ -44,7 +47,7 @@ def custom_gem(name, options = Hash.new)
|
|
44
47
|
local_path = File.expand_path("../vendor/#{name}", __FILE__)
|
45
48
|
if File.exist?(local_path)
|
46
49
|
puts "Using #{name} from #{local_path}..."
|
47
|
-
gem name, options.merge(:
|
50
|
+
gem name, options.merge(path: local_path).delete_if { |key, _| [:git, :branch].include?(key) }
|
48
51
|
else
|
49
52
|
gem name, options
|
50
53
|
end
|
data/README.md
CHANGED
@@ -71,8 +71,9 @@ a stable public API.
|
|
71
71
|
Change logs per release series:
|
72
72
|
|
73
73
|
* [master](https://github.com/ruby-amqp/bunny/blob/master/ChangeLog.md)
|
74
|
+
* [2.11.x](https://github.com/ruby-amqp/bunny/blob/2.11.x-stable/ChangeLog.md)
|
75
|
+
* [2.10.x](https://github.com/ruby-amqp/bunny/blob/2.10.x-stable/ChangeLog.md)
|
74
76
|
* [2.9.x](https://github.com/ruby-amqp/bunny/blob/2.9.x-stable/ChangeLog.md)
|
75
|
-
* [2.8.x](https://github.com/ruby-amqp/bunny/blob/2.8.x-stable/ChangeLog.md)
|
76
77
|
|
77
78
|
|
78
79
|
|
@@ -95,7 +96,7 @@ gem install bunny
|
|
95
96
|
To use Bunny in a project managed with Bundler:
|
96
97
|
|
97
98
|
``` ruby
|
98
|
-
gem "bunny", ">= 2.
|
99
|
+
gem "bunny", ">= 2.11.0"
|
99
100
|
```
|
100
101
|
|
101
102
|
|
data/bunny.gemspec
CHANGED
@@ -24,10 +24,9 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.email = ["michael.s.klishin@gmail.com"]
|
25
25
|
|
26
26
|
# Dependencies
|
27
|
-
s.
|
27
|
+
s.add_runtime_dependency 'amq-protocol', '~> 2.3', '>= 2.3.0'
|
28
28
|
|
29
29
|
# Files.
|
30
|
-
s.has_rdoc = true
|
31
30
|
s.extra_rdoc_files = ["README.md"]
|
32
31
|
s.files = `git ls-files`.split("\n")
|
33
32
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
data/docker-compose.yml
CHANGED
data/docker/Dockerfile
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
FROM
|
1
|
+
FROM ubuntu:bionic
|
2
2
|
|
3
3
|
RUN apt-get -q update && \
|
4
|
-
apt-get install -yq --no-install-recommends wget ca-certificates
|
4
|
+
apt-get install -yq --no-install-recommends gnupg1 wget ca-certificates apt-transport-https
|
5
5
|
|
6
|
-
RUN echo 'deb
|
7
|
-
wget -O- https://
|
6
|
+
RUN echo 'deb https://dl.bintray.com/rabbitmq/debian bionic main erlang' > /etc/apt/sources.list.d/rabbitmq.list && \
|
7
|
+
wget -O - 'https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc' | apt-key add -
|
8
8
|
|
9
9
|
RUN apt-get -q update && \
|
10
|
-
apt-get install -yq
|
10
|
+
apt-get install -yq rabbitmq-server
|
11
11
|
|
12
12
|
COPY docker-entrypoint.sh /
|
13
13
|
|
14
14
|
ENTRYPOINT /docker-entrypoint.sh
|
15
15
|
|
16
|
-
EXPOSE 5671 5672 15672
|
16
|
+
EXPOSE 5671 5672 15672
|
data/lib/bunny/channel.rb
CHANGED
@@ -265,15 +265,6 @@ module Bunny
|
|
265
265
|
@status == :closed
|
266
266
|
end
|
267
267
|
|
268
|
-
def to_s
|
269
|
-
oid = ("0x%x" % (self.object_id << 1))
|
270
|
-
"<#{self.class.name}:#{oid} number=#{@channel.id} @open=#{open?} connection=#{@connection.to_s}>"
|
271
|
-
end
|
272
|
-
|
273
|
-
def inspect
|
274
|
-
to_s
|
275
|
-
end
|
276
|
-
|
277
268
|
#
|
278
269
|
# @group Backwards compatibility with 0.8.0
|
279
270
|
#
|
@@ -657,7 +648,7 @@ module Bunny
|
|
657
648
|
|
658
649
|
@connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, 0, count, global))
|
659
650
|
|
660
|
-
|
651
|
+
with_continuation_timeout do
|
661
652
|
@last_basic_qos_ok = wait_on_continuations
|
662
653
|
end
|
663
654
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -678,7 +669,7 @@ module Bunny
|
|
678
669
|
raise_if_no_longer_open!
|
679
670
|
|
680
671
|
@connection.send_frame(AMQ::Protocol::Basic::Recover.encode(@id, requeue))
|
681
|
-
|
672
|
+
with_continuation_timeout do
|
682
673
|
@last_basic_recover_ok = wait_on_continuations
|
683
674
|
end
|
684
675
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -885,7 +876,7 @@ module Bunny
|
|
885
876
|
arguments))
|
886
877
|
|
887
878
|
begin
|
888
|
-
|
879
|
+
with_continuation_timeout do
|
889
880
|
@last_basic_consume_ok = wait_on_continuations
|
890
881
|
end
|
891
882
|
rescue Exception => e
|
@@ -935,7 +926,7 @@ module Bunny
|
|
935
926
|
consumer.arguments))
|
936
927
|
|
937
928
|
begin
|
938
|
-
|
929
|
+
with_continuation_timeout do
|
939
930
|
@last_basic_consume_ok = wait_on_continuations
|
940
931
|
end
|
941
932
|
rescue Exception => e
|
@@ -970,7 +961,7 @@ module Bunny
|
|
970
961
|
def basic_cancel(consumer_tag)
|
971
962
|
@connection.send_frame(AMQ::Protocol::Basic::Cancel.encode(@id, consumer_tag, false))
|
972
963
|
|
973
|
-
|
964
|
+
with_continuation_timeout do
|
974
965
|
@last_basic_cancel_ok = wait_on_continuations
|
975
966
|
end
|
976
967
|
|
@@ -994,7 +985,8 @@ module Bunny
|
|
994
985
|
|
995
986
|
# Declares a queue using queue.declare AMQP 0.9.1 method.
|
996
987
|
#
|
997
|
-
# @param [String] name
|
988
|
+
# @param [String] name The name of the queue or an empty string to let RabbitMQ generate a name.
|
989
|
+
# Note that LF and CR characters will be stripped from the value.
|
998
990
|
# @param [Hash] opts Queue properties
|
999
991
|
#
|
1000
992
|
# @option opts [Boolean] durable (false) Should information about this queue be persisted to disk so that it
|
@@ -1012,16 +1004,28 @@ module Bunny
|
|
1012
1004
|
def queue_declare(name, opts = {})
|
1013
1005
|
raise_if_no_longer_open!
|
1014
1006
|
|
1015
|
-
|
1016
|
-
|
1007
|
+
# strip trailing new line and carriage returns
|
1008
|
+
# just like RabbitMQ does
|
1009
|
+
safe_name = name.gsub(/[\r\n]/, "")
|
1010
|
+
@pending_queue_declare_name = safe_name
|
1011
|
+
@connection.send_frame(
|
1012
|
+
AMQ::Protocol::Queue::Declare.encode(@id,
|
1013
|
+
@pending_queue_declare_name,
|
1017
1014
|
opts.fetch(:passive, false),
|
1018
1015
|
opts.fetch(:durable, false),
|
1019
1016
|
opts.fetch(:exclusive, false),
|
1020
1017
|
opts.fetch(:auto_delete, false),
|
1021
1018
|
false,
|
1022
1019
|
opts[:arguments]))
|
1023
|
-
@last_queue_declare_ok = wait_on_continuations
|
1024
1020
|
|
1021
|
+
begin
|
1022
|
+
with_continuation_timeout do
|
1023
|
+
@last_queue_declare_ok = wait_on_continuations
|
1024
|
+
end
|
1025
|
+
ensure
|
1026
|
+
# clear pending continuation context if it belongs to us
|
1027
|
+
@pending_queue_declare_name = nil if @pending_queue_declare_name == safe_name
|
1028
|
+
end
|
1025
1029
|
raise_if_continuation_resulted_in_a_channel_error!
|
1026
1030
|
|
1027
1031
|
@last_queue_declare_ok
|
@@ -1046,7 +1050,7 @@ module Bunny
|
|
1046
1050
|
opts[:if_unused],
|
1047
1051
|
opts[:if_empty],
|
1048
1052
|
false))
|
1049
|
-
|
1053
|
+
with_continuation_timeout do
|
1050
1054
|
@last_queue_delete_ok = wait_on_continuations
|
1051
1055
|
end
|
1052
1056
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1066,7 +1070,7 @@ module Bunny
|
|
1066
1070
|
|
1067
1071
|
@connection.send_frame(AMQ::Protocol::Queue::Purge.encode(@id, name, false))
|
1068
1072
|
|
1069
|
-
|
1073
|
+
with_continuation_timeout do
|
1070
1074
|
@last_queue_purge_ok = wait_on_continuations
|
1071
1075
|
end
|
1072
1076
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1102,7 +1106,7 @@ module Bunny
|
|
1102
1106
|
(opts[:routing_key] || opts[:key]),
|
1103
1107
|
false,
|
1104
1108
|
opts[:arguments]))
|
1105
|
-
|
1109
|
+
with_continuation_timeout do
|
1106
1110
|
@last_queue_bind_ok = wait_on_continuations
|
1107
1111
|
end
|
1108
1112
|
|
@@ -1137,7 +1141,7 @@ module Bunny
|
|
1137
1141
|
exchange_name,
|
1138
1142
|
opts[:routing_key],
|
1139
1143
|
opts[:arguments]))
|
1140
|
-
|
1144
|
+
with_continuation_timeout do
|
1141
1145
|
@last_queue_unbind_ok = wait_on_continuations
|
1142
1146
|
end
|
1143
1147
|
|
@@ -1150,26 +1154,30 @@ module Bunny
|
|
1150
1154
|
|
1151
1155
|
# @group Exchange operations (exchange.*)
|
1152
1156
|
|
1153
|
-
# Declares a
|
1157
|
+
# Declares a exchange using echange.declare AMQP 0.9.1 method.
|
1154
1158
|
#
|
1155
|
-
# @param [String] name
|
1159
|
+
# @param [String] name The name of the exchange. Note that LF and CR characters
|
1160
|
+
# will be stripped from the value.
|
1156
1161
|
# @param [String,Symbol] type Exchange type, e.g. :fanout or :topic
|
1157
1162
|
# @param [Hash] opts Exchange properties
|
1158
1163
|
#
|
1159
|
-
# @option opts [Boolean] durable (false) Should information about this
|
1164
|
+
# @option opts [Boolean] durable (false) Should information about this exchange be persisted to disk so that it
|
1160
1165
|
# can survive broker restarts? Typically set to true for long-lived exchanges.
|
1161
|
-
# @option opts [Boolean] auto_delete (false) Should this
|
1166
|
+
# @option opts [Boolean] auto_delete (false) Should this exchange be deleted when it is no longer used?
|
1162
1167
|
# @option opts [Boolean] passive (false) If true, exchange will be checked for existence. If it does not
|
1163
1168
|
# exist, {Bunny::NotFound} will be raised.
|
1164
1169
|
#
|
1165
1170
|
# @return [AMQ::Protocol::Exchange::DeclareOk] RabbitMQ response
|
1166
|
-
# @see http://rubybunny.info/articles/
|
1171
|
+
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
1167
1172
|
# @api public
|
1168
1173
|
def exchange_declare(name, type, opts = {})
|
1169
1174
|
raise_if_no_longer_open!
|
1170
1175
|
|
1176
|
+
# strip trailing new line and carriage returns
|
1177
|
+
# just like RabbitMQ does
|
1178
|
+
safe_name = name.gsub(/[\r\n]/, "")
|
1171
1179
|
@connection.send_frame(AMQ::Protocol::Exchange::Declare.encode(@id,
|
1172
|
-
|
1180
|
+
safe_name,
|
1173
1181
|
type.to_s,
|
1174
1182
|
opts.fetch(:passive, false),
|
1175
1183
|
opts.fetch(:durable, false),
|
@@ -1177,7 +1185,7 @@ module Bunny
|
|
1177
1185
|
opts.fetch(:internal, false),
|
1178
1186
|
false, # nowait
|
1179
1187
|
opts[:arguments]))
|
1180
|
-
|
1188
|
+
with_continuation_timeout do
|
1181
1189
|
@last_exchange_declare_ok = wait_on_continuations
|
1182
1190
|
end
|
1183
1191
|
|
@@ -1202,7 +1210,7 @@ module Bunny
|
|
1202
1210
|
name,
|
1203
1211
|
opts[:if_unused],
|
1204
1212
|
false))
|
1205
|
-
|
1213
|
+
with_continuation_timeout do
|
1206
1214
|
@last_exchange_delete_ok = wait_on_continuations
|
1207
1215
|
end
|
1208
1216
|
|
@@ -1246,7 +1254,7 @@ module Bunny
|
|
1246
1254
|
opts[:routing_key],
|
1247
1255
|
false,
|
1248
1256
|
opts[:arguments]))
|
1249
|
-
|
1257
|
+
with_continuation_timeout do
|
1250
1258
|
@last_exchange_bind_ok = wait_on_continuations
|
1251
1259
|
end
|
1252
1260
|
|
@@ -1290,7 +1298,7 @@ module Bunny
|
|
1290
1298
|
opts[:routing_key],
|
1291
1299
|
false,
|
1292
1300
|
opts[:arguments]))
|
1293
|
-
|
1301
|
+
with_continuation_timeout do
|
1294
1302
|
@last_exchange_unbind_ok = wait_on_continuations
|
1295
1303
|
end
|
1296
1304
|
|
@@ -1318,7 +1326,7 @@ module Bunny
|
|
1318
1326
|
raise_if_no_longer_open!
|
1319
1327
|
|
1320
1328
|
@connection.send_frame(AMQ::Protocol::Channel::Flow.encode(@id, active))
|
1321
|
-
|
1329
|
+
with_continuation_timeout do
|
1322
1330
|
@last_channel_flow_ok = wait_on_continuations
|
1323
1331
|
end
|
1324
1332
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1339,7 +1347,7 @@ module Bunny
|
|
1339
1347
|
raise_if_no_longer_open!
|
1340
1348
|
|
1341
1349
|
@connection.send_frame(AMQ::Protocol::Tx::Select.encode(@id))
|
1342
|
-
|
1350
|
+
with_continuation_timeout do
|
1343
1351
|
@last_tx_select_ok = wait_on_continuations
|
1344
1352
|
end
|
1345
1353
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1355,7 +1363,7 @@ module Bunny
|
|
1355
1363
|
raise_if_no_longer_open!
|
1356
1364
|
|
1357
1365
|
@connection.send_frame(AMQ::Protocol::Tx::Commit.encode(@id))
|
1358
|
-
|
1366
|
+
with_continuation_timeout do
|
1359
1367
|
@last_tx_commit_ok = wait_on_continuations
|
1360
1368
|
end
|
1361
1369
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1370,7 +1378,7 @@ module Bunny
|
|
1370
1378
|
raise_if_no_longer_open!
|
1371
1379
|
|
1372
1380
|
@connection.send_frame(AMQ::Protocol::Tx::Rollback.encode(@id))
|
1373
|
-
|
1381
|
+
with_continuation_timeout do
|
1374
1382
|
@last_tx_rollback_ok = wait_on_continuations
|
1375
1383
|
end
|
1376
1384
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1417,7 +1425,7 @@ module Bunny
|
|
1417
1425
|
@confirms_callback = callback
|
1418
1426
|
|
1419
1427
|
@connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, false))
|
1420
|
-
|
1428
|
+
with_continuation_timeout do
|
1421
1429
|
@last_confirm_select_ok = wait_on_continuations
|
1422
1430
|
end
|
1423
1431
|
raise_if_continuation_resulted_in_a_channel_error!
|
@@ -1589,7 +1597,11 @@ module Bunny
|
|
1589
1597
|
|
1590
1598
|
# @return [String] Brief human-readable representation of the channel
|
1591
1599
|
def to_s
|
1592
|
-
"#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s}>"
|
1600
|
+
"#<#{self.class.name}:#{object_id} @id=#{self.number} @connection=#{@connection.to_s}> @open=#{open?}"
|
1601
|
+
end
|
1602
|
+
|
1603
|
+
def inspect
|
1604
|
+
to_s
|
1593
1605
|
end
|
1594
1606
|
|
1595
1607
|
|
@@ -1597,6 +1609,11 @@ module Bunny
|
|
1597
1609
|
# Implementation
|
1598
1610
|
#
|
1599
1611
|
|
1612
|
+
# @private
|
1613
|
+
def with_continuation_timeout(&block)
|
1614
|
+
Bunny::Timeout.timeout(wait_on_continuations_timeout, ClientTimeout, &block)
|
1615
|
+
end
|
1616
|
+
|
1600
1617
|
# @private
|
1601
1618
|
def register_consumer(consumer_tag, consumer)
|
1602
1619
|
@consumer_mutex.synchronize do
|
@@ -1620,12 +1637,33 @@ module Bunny
|
|
1620
1637
|
end
|
1621
1638
|
end
|
1622
1639
|
|
1640
|
+
# @private
|
1641
|
+
def pending_server_named_queue_declaration?
|
1642
|
+
@pending_queue_declare_name && @pending_queue_declare_name.empty?
|
1643
|
+
end
|
1644
|
+
|
1645
|
+
# @private
|
1646
|
+
def can_accept_queue_declare_ok?(method)
|
1647
|
+
@pending_queue_declare_name == method.queue ||
|
1648
|
+
pending_server_named_queue_declaration?
|
1649
|
+
end
|
1650
|
+
|
1623
1651
|
# @private
|
1624
1652
|
def handle_method(method)
|
1625
1653
|
@logger.debug { "Channel#handle_frame on channel #{@id}: #{method.inspect}" }
|
1626
1654
|
case method
|
1627
1655
|
when AMQ::Protocol::Queue::DeclareOk then
|
1628
|
-
|
1656
|
+
# safeguard against late arrivals of responses and
|
1657
|
+
# so on, see ruby-amqp/bunny#558
|
1658
|
+
if can_accept_queue_declare_ok?(method)
|
1659
|
+
@continuations.push(method)
|
1660
|
+
else
|
1661
|
+
if !pending_server_named_queue_declaration?
|
1662
|
+
# this response is for an outdated/overwritten
|
1663
|
+
# queue.declare, drop it
|
1664
|
+
@logger.warn "Received a queue.declare-ok response for a mismatching queue (#{method.queue} instead of #{@pending_queue_declare_name}) on channel #{@id} possibly due to a timeout, ignoring it"
|
1665
|
+
end
|
1666
|
+
end
|
1629
1667
|
when AMQ::Protocol::Queue::DeleteOk then
|
1630
1668
|
@continuations.push(method)
|
1631
1669
|
when AMQ::Protocol::Queue::PurgeOk then
|
@@ -24,6 +24,11 @@ module Bunny
|
|
24
24
|
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
|
25
25
|
end
|
26
26
|
|
27
|
+
def initialize(*args)
|
28
|
+
super
|
29
|
+
@__bunny_socket_eof_flag__ = false
|
30
|
+
end
|
31
|
+
|
27
32
|
# Reads given number of bytes with an optional timeout
|
28
33
|
#
|
29
34
|
# @param [Integer] count How many bytes to read
|
@@ -29,6 +29,7 @@ module Bunny
|
|
29
29
|
@interval = [(period / 2) - 1, 0.4].max
|
30
30
|
|
31
31
|
@thread = Thread.new(&method(:run))
|
32
|
+
@thread.report_on_exception = false if @thread.respond_to?(:report_on_exception)
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
@@ -63,7 +64,7 @@ module Bunny
|
|
63
64
|
|
64
65
|
if now > (@last_activity_time + @interval)
|
65
66
|
@logger.debug { "Sending a heartbeat, last activity time: #{@last_activity_time}, interval (s): #{@interval}" }
|
66
|
-
@transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode)
|
67
|
+
@transport.write_without_timeout(AMQ::Protocol::HeartbeatFrame.encode, true)
|
67
68
|
end
|
68
69
|
end
|
69
70
|
end
|
@@ -8,6 +8,11 @@ module Bunny
|
|
8
8
|
# methods found in Bunny::Socket.
|
9
9
|
class SSLSocket < Bunny::SSLSocket
|
10
10
|
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
@__bunny_socket_eof_flag__ = false
|
14
|
+
end
|
15
|
+
|
11
16
|
# Reads given number of bytes with an optional timeout
|
12
17
|
#
|
13
18
|
# @param [Integer] count How many bytes to read
|
data/lib/bunny/queue.rb
CHANGED
@@ -375,11 +375,6 @@ module Bunny
|
|
375
375
|
|
376
376
|
protected
|
377
377
|
|
378
|
-
# @private
|
379
|
-
def self.add_default_options(name, opts, block)
|
380
|
-
{ :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
|
381
|
-
end
|
382
|
-
|
383
378
|
# @private
|
384
379
|
def self.add_default_options(name, opts)
|
385
380
|
# :nowait is always false for Bunny
|
data/lib/bunny/reader_loop.rb
CHANGED
data/lib/bunny/session.rb
CHANGED
@@ -78,7 +78,7 @@ module Bunny
|
|
78
78
|
|
79
79
|
# @return [Bunny::Transport]
|
80
80
|
attr_reader :transport
|
81
|
-
attr_reader :status, :
|
81
|
+
attr_reader :status, :heartbeat, :user, :pass, :vhost, :frame_max, :channel_max, :threaded
|
82
82
|
attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales
|
83
83
|
attr_reader :channel_id_allocator
|
84
84
|
# Authentication mechanism, e.g. "PLAIN" or "EXTERNAL"
|
@@ -152,15 +152,17 @@ module Bunny
|
|
152
152
|
@addresses = self.addresses_from(opts)
|
153
153
|
@address_index = 0
|
154
154
|
|
155
|
-
|
156
|
-
@logger = opts.fetch(:logger, init_default_logger(log_file, log_level))
|
157
|
-
|
155
|
+
@transport = nil
|
158
156
|
@user = self.username_from(opts)
|
159
157
|
@pass = self.password_from(opts)
|
160
158
|
@vhost = self.vhost_from(opts)
|
161
159
|
@threaded = opts.fetch(:threaded, true)
|
162
160
|
|
161
|
+
# re-init, see above
|
162
|
+
@logger = opts.fetch(:logger, init_default_logger(log_file, log_level))
|
163
|
+
|
163
164
|
validate_connection_options(opts)
|
165
|
+
@last_connection_error = nil
|
164
166
|
|
165
167
|
# should automatic recovery from network failures be used?
|
166
168
|
@automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil?
|
@@ -188,6 +190,7 @@ module Bunny
|
|
188
190
|
@client_channel_max = normalize_client_channel_max(opts.fetch(:channel_max, DEFAULT_CHANNEL_MAX))
|
189
191
|
# will be-renegotiated during connection tuning steps. MK.
|
190
192
|
@channel_max = @client_channel_max
|
193
|
+
@heartbeat_sender = nil
|
191
194
|
@client_heartbeat = self.heartbeat_from(opts)
|
192
195
|
|
193
196
|
client_props = opts[:properties] || opts[:client_properties] || {}
|
@@ -308,10 +311,6 @@ module Bunny
|
|
308
311
|
@transport.post_initialize_socket
|
309
312
|
@transport.connect
|
310
313
|
|
311
|
-
if @socket_configurator
|
312
|
-
@transport.configure_socket(&@socket_configurator)
|
313
|
-
end
|
314
|
-
|
315
314
|
self.init_connection
|
316
315
|
self.open_connection
|
317
316
|
|
@@ -418,7 +417,7 @@ module Bunny
|
|
418
417
|
@status_mutex.synchronize { @status == :closed }
|
419
418
|
end
|
420
419
|
|
421
|
-
# @return [Boolean] true if this AMQP 0.9.1 connection has been
|
420
|
+
# @return [Boolean] true if this AMQP 0.9.1 connection has been closed by the user (as opposed to the server)
|
422
421
|
def manually_closed?
|
423
422
|
@status_mutex.synchronize { @manually_closed == true }
|
424
423
|
end
|
@@ -757,6 +756,7 @@ module Bunny
|
|
757
756
|
end
|
758
757
|
else
|
759
758
|
@logger.error "Ran out of recovery attempts (limit set to #{@max_recovery_attempts})"
|
759
|
+
self.close
|
760
760
|
end
|
761
761
|
end
|
762
762
|
|
@@ -1078,9 +1078,13 @@ module Bunny
|
|
1078
1078
|
# still recommend not sharing channels between threads except for consumer-only cases in the docs. MK.
|
1079
1079
|
channel.synchronize do
|
1080
1080
|
# see rabbitmq/rabbitmq-server#156
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1081
|
+
if open?
|
1082
|
+
data = frames.reduce("") { |acc, frame| acc << frame.encode }
|
1083
|
+
@transport.write(data)
|
1084
|
+
signal_activity!
|
1085
|
+
else
|
1086
|
+
raise ConnectionClosedError.new(frames)
|
1087
|
+
end
|
1084
1088
|
end
|
1085
1089
|
end # send_frameset(frames)
|
1086
1090
|
|
@@ -1096,8 +1100,12 @@ module Bunny
|
|
1096
1100
|
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
1097
1101
|
# locking. See a note about "single frame" methods in a comment in `send_frameset`. MK.
|
1098
1102
|
channel.synchronize do
|
1099
|
-
|
1100
|
-
|
1103
|
+
if open?
|
1104
|
+
frames.each { |frame| self.send_frame_without_timeout(frame, false) }
|
1105
|
+
signal_activity!
|
1106
|
+
else
|
1107
|
+
raise ConnectionClosedError.new(frames)
|
1108
|
+
end
|
1101
1109
|
end
|
1102
1110
|
end # send_frameset_without_timeout(frames)
|
1103
1111
|
|
data/lib/bunny/transport.rb
CHANGED
@@ -28,7 +28,6 @@ module Bunny
|
|
28
28
|
attr_reader :session, :host, :port, :socket, :connect_timeout, :read_timeout, :write_timeout, :disconnect_timeout
|
29
29
|
attr_reader :tls_context, :verify_peer, :tls_ca_certificates, :tls_certificate_path, :tls_key_path
|
30
30
|
|
31
|
-
attr_writer :read_timeout
|
32
31
|
def read_timeout=(v)
|
33
32
|
@read_timeout = v
|
34
33
|
@read_timeout = nil if @read_timeout == 0
|
@@ -58,6 +57,8 @@ module Bunny
|
|
58
57
|
|
59
58
|
@writes_mutex = @session.mutex_impl.new
|
60
59
|
|
60
|
+
@socket = nil
|
61
|
+
|
61
62
|
prepare_tls_context(opts) if @tls_enabled
|
62
63
|
end
|
63
64
|
|
@@ -157,12 +158,13 @@ module Bunny
|
|
157
158
|
end
|
158
159
|
|
159
160
|
# Writes data to the socket without timeout checks
|
160
|
-
def write_without_timeout(data)
|
161
|
+
def write_without_timeout(data, raise_exceptions = false)
|
161
162
|
begin
|
162
163
|
@writes_mutex.synchronize { @socket.write(data) }
|
163
164
|
@socket.flush
|
164
165
|
rescue SystemCallError, Bunny::ConnectionError, IOError => e
|
165
166
|
close
|
167
|
+
raise e if raise_exceptions
|
166
168
|
|
167
169
|
if @session.automatically_recover?
|
168
170
|
@session.handle_network_failure(e)
|
data/lib/bunny/version.rb
CHANGED
data/repl
CHANGED
@@ -0,0 +1,75 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require_relative "../../toxiproxy_helper"
|
3
|
+
|
4
|
+
if ::Toxiproxy.running?
|
5
|
+
describe Bunny::Channel, "#basic_publish" do
|
6
|
+
include RabbitMQ::Toxiproxy
|
7
|
+
|
8
|
+
|
9
|
+
after :each do
|
10
|
+
@connection.close if @connection.open?
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when the the connection detects missed heartbeats with automatic recovery" do
|
14
|
+
before(:each) do
|
15
|
+
setup_toxiproxy
|
16
|
+
@connection = Bunny.new(user: "bunny_gem", password: "bunny_password", vhost: "bunny_testbed",
|
17
|
+
host: "localhost:11111", heartbeat_timeout: 1, automatically_recover: true)
|
18
|
+
@connection.start
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:queue_name) { "bunny.basic.publish.queue#{rand}" }
|
22
|
+
|
23
|
+
it "raises a ConnectionClosedError" do
|
24
|
+
ch = @connection.create_channel
|
25
|
+
begin
|
26
|
+
rabbitmq_toxiproxy.down do
|
27
|
+
sleep 2
|
28
|
+
expect { ch.default_exchange.publish("", :routing_key => queue_name) }.to raise_error(Bunny::ConnectionClosedError)
|
29
|
+
end
|
30
|
+
ensure
|
31
|
+
cleanup_toxiproxy
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when the the connection detects missed heartbeats without automatic recovery" do
|
37
|
+
before(:each) do
|
38
|
+
setup_toxiproxy
|
39
|
+
@connection = Bunny.new(user: "bunny_gem", password: "bunny_password", vhost: "bunny_testbed",
|
40
|
+
host: "localhost:11111", heartbeat_timeout: 1, automatically_recover: false, threaded: false)
|
41
|
+
@connection.start
|
42
|
+
end
|
43
|
+
|
44
|
+
it "does not raise an exception on session thread" do
|
45
|
+
rabbitmq_toxiproxy.down do
|
46
|
+
sleep 5
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "recovery attempt limit that's exceeded" do
|
52
|
+
before(:each) do
|
53
|
+
setup_toxiproxy
|
54
|
+
@connection = Bunny.new(user: "bunny_gem", password: "bunny_password", vhost: "bunny_testbed",
|
55
|
+
host: "localhost:11111", heartbeat_timeout: 1, automatically_recover: true, network_recovery_interval: 1,
|
56
|
+
recovery_attempts: 3, reset_recovery_attempts_after_reconnection: true)
|
57
|
+
@connection.start
|
58
|
+
end
|
59
|
+
|
60
|
+
it "permanently closes connection" do
|
61
|
+
expect(@connection.open?).to be(true)
|
62
|
+
|
63
|
+
rabbitmq_toxiproxy.down do
|
64
|
+
sleep 6
|
65
|
+
end
|
66
|
+
# give the connection oen last chance to recover
|
67
|
+
sleep 3
|
68
|
+
|
69
|
+
expect(@connection.closed?).to be(true)
|
70
|
+
end
|
71
|
+
end # context
|
72
|
+
end # describe
|
73
|
+
else
|
74
|
+
puts "Toxiproxy isn't running, some examples will be skipped"
|
75
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bunny::Session do
|
4
|
+
context 'when retry attempts have been exhausted' do
|
5
|
+
let(:io) { StringIO.new } # keep test output clear
|
6
|
+
|
7
|
+
def create_session
|
8
|
+
described_class.new(
|
9
|
+
host: 'fake.host',
|
10
|
+
recovery_attempts: 0,
|
11
|
+
connection_timeout: 0,
|
12
|
+
network_recovery_interval: 0,
|
13
|
+
logfile: io,
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'closes the session' do
|
18
|
+
session = create_session
|
19
|
+
session.handle_network_failure(StandardError.new)
|
20
|
+
expect(session.closed?).to be true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'stops the reader loop' do
|
24
|
+
session = create_session
|
25
|
+
reader_loop = session.reader_loop
|
26
|
+
session.handle_network_failure(StandardError.new)
|
27
|
+
expect(reader_loop.stopping?).to be true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RabbitMQ
|
2
|
+
module Toxiproxy
|
3
|
+
RABBITMQ_UPSTREAM_HOST = if !ENV["LOCAL_RABBITMQ"].nil?
|
4
|
+
# a local Toxiproxy/RabbitMQ combination
|
5
|
+
"localhost"
|
6
|
+
else
|
7
|
+
# docker-compose
|
8
|
+
"rabbitmq"
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup_toxiproxy
|
12
|
+
::Toxiproxy.populate([{
|
13
|
+
name: "rabbitmq",
|
14
|
+
listen: "0.0.0.0:11111",
|
15
|
+
upstream: "#{RABBITMQ_UPSTREAM_HOST}:5672"
|
16
|
+
}])
|
17
|
+
rabbitmq_toxiproxy.enable
|
18
|
+
end
|
19
|
+
|
20
|
+
def cleanup_toxiproxy
|
21
|
+
::Toxiproxy.populate()
|
22
|
+
end
|
23
|
+
|
24
|
+
def rabbitmq_toxiproxy
|
25
|
+
::Toxiproxy[/rabbitmq/]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
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: 2.
|
4
|
+
version: 2.12.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Duncan
|
@@ -12,13 +12,16 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2018-
|
15
|
+
date: 2018-09-08 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: amq-protocol
|
19
19
|
requirement: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
21
|
- - "~>"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '2.3'
|
24
|
+
- - ">="
|
22
25
|
- !ruby/object:Gem::Version
|
23
26
|
version: 2.3.0
|
24
27
|
type: :runtime
|
@@ -26,6 +29,9 @@ dependencies:
|
|
26
29
|
version_requirements: !ruby/object:Gem::Requirement
|
27
30
|
requirements:
|
28
31
|
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.3'
|
34
|
+
- - ">="
|
29
35
|
- !ruby/object:Gem::Version
|
30
36
|
version: 2.3.0
|
31
37
|
description: Easy to use, feature complete Ruby client for RabbitMQ 3.3 and later
|
@@ -174,6 +180,7 @@ files:
|
|
174
180
|
- spec/higher_level_api/integration/read_only_consumer_spec.rb
|
175
181
|
- spec/higher_level_api/integration/sender_selected_distribution_spec.rb
|
176
182
|
- spec/higher_level_api/integration/tls_connection_spec.rb
|
183
|
+
- spec/higher_level_api/integration/toxiproxy_spec.rb
|
177
184
|
- spec/higher_level_api/integration/tx_commit_spec.rb
|
178
185
|
- spec/higher_level_api/integration/tx_rollback_spec.rb
|
179
186
|
- spec/higher_level_api/integration/with_channel_spec.rb
|
@@ -182,6 +189,7 @@ files:
|
|
182
189
|
- spec/issues/issue202_spec.rb
|
183
190
|
- spec/issues/issue224_spec.rb
|
184
191
|
- spec/issues/issue465_spec.rb
|
192
|
+
- spec/issues/issue549_spec.rb
|
185
193
|
- spec/issues/issue78_spec.rb
|
186
194
|
- spec/issues/issue83_spec.rb
|
187
195
|
- spec/issues/issue97_attachment.json
|
@@ -205,6 +213,7 @@ files:
|
|
205
213
|
- spec/tls/server.csr
|
206
214
|
- spec/tls/server_certificate.pem
|
207
215
|
- spec/tls/server_key.pem
|
216
|
+
- spec/toxiproxy_helper.rb
|
208
217
|
- spec/unit/bunny_spec.rb
|
209
218
|
- spec/unit/concurrent/atomic_fixnum_spec.rb
|
210
219
|
- spec/unit/concurrent/condition_spec.rb
|
@@ -227,12 +236,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
227
236
|
version: '2.2'
|
228
237
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
238
|
requirements:
|
230
|
-
- - "
|
239
|
+
- - ">"
|
231
240
|
- !ruby/object:Gem::Version
|
232
|
-
version:
|
241
|
+
version: 1.3.1
|
233
242
|
requirements: []
|
234
243
|
rubyforge_project:
|
235
|
-
rubygems_version: 2.
|
244
|
+
rubygems_version: 2.7.7
|
236
245
|
signing_key:
|
237
246
|
specification_version: 4
|
238
247
|
summary: Popular easy to use Ruby client for RabbitMQ
|
@@ -275,6 +284,7 @@ test_files:
|
|
275
284
|
- spec/higher_level_api/integration/read_only_consumer_spec.rb
|
276
285
|
- spec/higher_level_api/integration/sender_selected_distribution_spec.rb
|
277
286
|
- spec/higher_level_api/integration/tls_connection_spec.rb
|
287
|
+
- spec/higher_level_api/integration/toxiproxy_spec.rb
|
278
288
|
- spec/higher_level_api/integration/tx_commit_spec.rb
|
279
289
|
- spec/higher_level_api/integration/tx_rollback_spec.rb
|
280
290
|
- spec/higher_level_api/integration/with_channel_spec.rb
|
@@ -283,6 +293,7 @@ test_files:
|
|
283
293
|
- spec/issues/issue202_spec.rb
|
284
294
|
- spec/issues/issue224_spec.rb
|
285
295
|
- spec/issues/issue465_spec.rb
|
296
|
+
- spec/issues/issue549_spec.rb
|
286
297
|
- spec/issues/issue78_spec.rb
|
287
298
|
- spec/issues/issue83_spec.rb
|
288
299
|
- spec/issues/issue97_attachment.json
|
@@ -306,6 +317,7 @@ test_files:
|
|
306
317
|
- spec/tls/server.csr
|
307
318
|
- spec/tls/server_certificate.pem
|
308
319
|
- spec/tls/server_key.pem
|
320
|
+
- spec/toxiproxy_helper.rb
|
309
321
|
- spec/unit/bunny_spec.rb
|
310
322
|
- spec/unit/concurrent/atomic_fixnum_spec.rb
|
311
323
|
- spec/unit/concurrent/condition_spec.rb
|