amqp 1.1.0.pre1 → 1.1.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.md +5 -0
- data/Gemfile +0 -2
- data/README.md +30 -24
- data/amqp.gemspec +0 -1
- data/lib/amq/protocol/get_response.rb +55 -0
- data/lib/amqp.rb +237 -3
- data/lib/amqp/auth_mechanism_adapter.rb +69 -0
- data/lib/amqp/auth_mechanism_adapter/external.rb +27 -0
- data/lib/amqp/auth_mechanism_adapter/plain.rb +24 -0
- data/lib/amqp/callbacks.rb +67 -0
- data/lib/amqp/channel.rb +517 -76
- data/lib/amqp/consumer.rb +200 -32
- data/lib/amqp/consumer_tag_generator.rb +20 -0
- data/lib/amqp/deferrable.rb +5 -0
- data/lib/amqp/entity.rb +71 -0
- data/lib/amqp/exchange.rb +266 -17
- data/lib/amqp/framing/string/frame.rb +36 -0
- data/lib/amqp/handlers_registry.rb +28 -0
- data/lib/amqp/openable.rb +58 -0
- data/lib/amqp/queue.rb +526 -19
- data/lib/amqp/session.rb +943 -70
- data/lib/amqp/settings.rb +157 -0
- data/lib/amqp/version.rb +1 -1
- data/spec/integration/authentication_spec.rb +1 -1
- data/spec/integration/extensions/rabbitmq/publisher_confirmations_spec.rb +0 -15
- data/spec/integration/store_and_forward_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/amqp/client_spec.rb +3 -1
- data/spec/unit/amqp/connection_spec.rb +10 -41
- metadata +15 -19
- data/lib/amqp/client.rb +0 -100
- data/lib/amqp/connection.rb +0 -223
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
|
5
|
+
# Manages the encoding of credentials for the EXTERNAL authentication
|
6
|
+
# mechanism.
|
7
|
+
class AuthMechanismAdapter::External < AuthMechanismAdapter
|
8
|
+
|
9
|
+
auth_mechanism "EXTERNAL"
|
10
|
+
|
11
|
+
# Encodes a username and password for the EXTERNAL mechanism. Since
|
12
|
+
# authentication is handled by an encapsulating protocol like SSL or
|
13
|
+
# UNIX domain sockets, EXTERNAL doesn't pass along any username or
|
14
|
+
# password information at all and this method always returns the
|
15
|
+
# empty string.
|
16
|
+
#
|
17
|
+
# @param [String] username The username to encode. This parameter is
|
18
|
+
# ignored.
|
19
|
+
# @param [String] password The password to encode. This parameter is
|
20
|
+
# ignored.
|
21
|
+
# @return [String] The username and password, encoded for the
|
22
|
+
# EXTERNAL mechanism. This is always the empty string.
|
23
|
+
def encode_credentials(username, password)
|
24
|
+
""
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
|
5
|
+
# Manages the encoding of credentials for the PLAIN authentication
|
6
|
+
# mechanism.
|
7
|
+
class AuthMechanismAdapter::Plain < AuthMechanismAdapter
|
8
|
+
|
9
|
+
auth_mechanism "PLAIN"
|
10
|
+
|
11
|
+
# Encodes credentials for the given username and password. This
|
12
|
+
# involves sending the password across the wire in plaintext, so
|
13
|
+
# PLAIN authentication should only be used over a secure transport
|
14
|
+
# layer.
|
15
|
+
#
|
16
|
+
# @param [String] username The username to encode.
|
17
|
+
# @param [String] password The password to encode.
|
18
|
+
# @return [String] The username and password, encoded for the PLAIN
|
19
|
+
# mechanism.
|
20
|
+
def encode_credentials(username, password)
|
21
|
+
"\0#{username}\0#{password}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module AMQP
|
2
|
+
module Callbacks
|
3
|
+
|
4
|
+
def redefine_callback(event, callable = nil, &block)
|
5
|
+
f = (callable || block)
|
6
|
+
# yes, re-assign!
|
7
|
+
@callbacks[event] = [f]
|
8
|
+
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def define_callback(event, callable = nil, &block)
|
13
|
+
f = (callable || block)
|
14
|
+
|
15
|
+
@callbacks[event] ||= []
|
16
|
+
@callbacks[event] << f if f
|
17
|
+
|
18
|
+
self
|
19
|
+
end # define_callback(event, &block)
|
20
|
+
alias append_callback define_callback
|
21
|
+
|
22
|
+
def prepend_callback(event, &block)
|
23
|
+
@callbacks[event] ||= []
|
24
|
+
@callbacks[event].unshift(block)
|
25
|
+
|
26
|
+
self
|
27
|
+
end # prepend_callback(event, &block)
|
28
|
+
|
29
|
+
def clear_callbacks(event)
|
30
|
+
@callbacks[event].clear if @callbacks[event]
|
31
|
+
end # clear_callbacks(event)
|
32
|
+
|
33
|
+
|
34
|
+
def exec_callback(name, *args, &block)
|
35
|
+
list = Array(@callbacks[name])
|
36
|
+
if list.any?
|
37
|
+
list.each { |c| c.call(*args, &block) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def exec_callback_once(name, *args, &block)
|
42
|
+
list = (@callbacks.delete(name) || Array.new)
|
43
|
+
if list.any?
|
44
|
+
list.each { |c| c.call(*args, &block) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def exec_callback_yielding_self(name, *args, &block)
|
49
|
+
list = Array(@callbacks[name])
|
50
|
+
if list.any?
|
51
|
+
list.each { |c| c.call(self, *args, &block) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def exec_callback_once_yielding_self(name, *args, &block)
|
56
|
+
list = (@callbacks.delete(name) || Array.new)
|
57
|
+
|
58
|
+
if list.any?
|
59
|
+
list.each { |c| c.call(self, *args, &block) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def has_callback?(name)
|
64
|
+
@callbacks[name] && !@callbacks[name].empty?
|
65
|
+
end # has_callback?
|
66
|
+
end # Callbacks
|
67
|
+
end
|
data/lib/amqp/channel.rb
CHANGED
@@ -141,7 +141,19 @@ module AMQP
|
|
141
141
|
# Learn more in {file:docs/VendorSpecificExtensions.textile}
|
142
142
|
#
|
143
143
|
# @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Specification.pdf AMQP 0.9.1 specification (Section 2.2.5)
|
144
|
-
class Channel
|
144
|
+
class Channel
|
145
|
+
|
146
|
+
#
|
147
|
+
# Behaviours
|
148
|
+
#
|
149
|
+
|
150
|
+
extend RegisterEntityMixin
|
151
|
+
include Entity
|
152
|
+
extend ProtocolMethodHandlers
|
153
|
+
|
154
|
+
register_entity :queue, AMQP::Queue
|
155
|
+
register_entity :exchange, AMQP::Exchange
|
156
|
+
|
145
157
|
|
146
158
|
#
|
147
159
|
# API
|
@@ -156,6 +168,24 @@ module AMQP
|
|
156
168
|
# @return [Symbol]
|
157
169
|
attr_reader :status
|
158
170
|
|
171
|
+
DEFAULT_REPLY_TEXT = "Goodbye".freeze
|
172
|
+
|
173
|
+
attr_reader :id
|
174
|
+
|
175
|
+
attr_reader :exchanges_awaiting_declare_ok, :exchanges_awaiting_delete_ok
|
176
|
+
attr_reader :queues_awaiting_declare_ok, :queues_awaiting_delete_ok, :queues_awaiting_bind_ok, :queues_awaiting_unbind_ok, :queues_awaiting_purge_ok, :queues_awaiting_get_response
|
177
|
+
attr_reader :consumers_awaiting_consume_ok, :consumers_awaiting_cancel_ok
|
178
|
+
|
179
|
+
attr_accessor :flow_is_active
|
180
|
+
|
181
|
+
# Change publisher index. Publisher index is incremented
|
182
|
+
# by 1 after each Basic.Publish starting at 1. This is done
|
183
|
+
# on both client and server, hence this acknowledged messages
|
184
|
+
# can be matched via its delivery-tag.
|
185
|
+
#
|
186
|
+
# @api private
|
187
|
+
attr_writer :publisher_index
|
188
|
+
|
159
189
|
|
160
190
|
# @param [AMQP::Session] connection Connection to open this channel on. If not given, default AMQP
|
161
191
|
# connection (accessible via {AMQP.connection}) will be used.
|
@@ -211,7 +241,32 @@ module AMQP
|
|
211
241
|
id = self.class.next_channel_id
|
212
242
|
end
|
213
243
|
|
214
|
-
super(@connection
|
244
|
+
super(@connection)
|
245
|
+
|
246
|
+
@id = id
|
247
|
+
@exchanges = Hash.new
|
248
|
+
@queues = Hash.new
|
249
|
+
@consumers = Hash.new
|
250
|
+
@options = { :auto_recovery => @connection.auto_recovering? }.merge(options)
|
251
|
+
@auto_recovery = (!!@options[:auto_recovery])
|
252
|
+
|
253
|
+
# we must synchronize frameset delivery. MK.
|
254
|
+
@mutex = Mutex.new
|
255
|
+
|
256
|
+
reset_state!
|
257
|
+
|
258
|
+
# 65536 is here for cases when channel is opened without passing a callback in,
|
259
|
+
# otherwise channel_mix would be nil and it causes a lot of needless headaches.
|
260
|
+
# lets just have this default. MK.
|
261
|
+
channel_max = if @connection.open?
|
262
|
+
@connection.channel_max || 65536
|
263
|
+
else
|
264
|
+
65536
|
265
|
+
end
|
266
|
+
|
267
|
+
if channel_max != 0 && !(0..channel_max).include?(id)
|
268
|
+
raise ArgumentError.new("Max channel for the connection is #{channel_max}, given: #{id}")
|
269
|
+
end
|
215
270
|
|
216
271
|
# we need this deferrable to mimic what AMQP gem 0.7 does to enable
|
217
272
|
# the following (pseudo-synchronous) style of programming some people use in their
|
@@ -224,7 +279,7 @@ module AMQP
|
|
224
279
|
# ...
|
225
280
|
#
|
226
281
|
# Read more about EM::Deferrable#callback behavior in EventMachine documentation. MK.
|
227
|
-
@channel_is_open_deferrable =
|
282
|
+
@channel_is_open_deferrable = AMQP::Deferrable.new
|
228
283
|
|
229
284
|
@parameter_checks = {:queue => [:durable, :exclusive, :auto_delete, :arguments], :exchange => [:type, :durable, :arguments]}
|
230
285
|
|
@@ -263,7 +318,7 @@ module AMQP
|
|
263
318
|
return unless auto_recovering?
|
264
319
|
|
265
320
|
@channel_is_open_deferrable.fail
|
266
|
-
@channel_is_open_deferrable =
|
321
|
+
@channel_is_open_deferrable = AMQP::Deferrable.new
|
267
322
|
|
268
323
|
self.open do
|
269
324
|
@channel_is_open_deferrable.succeed
|
@@ -292,7 +347,7 @@ module AMQP
|
|
292
347
|
self.class.release_channel_id(old_id)
|
293
348
|
|
294
349
|
@channel_is_open_deferrable.fail
|
295
|
-
@channel_is_open_deferrable =
|
350
|
+
@channel_is_open_deferrable = AMQP::Deferrable.new
|
296
351
|
|
297
352
|
self.open do
|
298
353
|
@channel_is_open_deferrable.succeed
|
@@ -820,13 +875,13 @@ module AMQP
|
|
820
875
|
Queue.new(self, name, opts)
|
821
876
|
else
|
822
877
|
shim = Proc.new { |q, method|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
878
|
+
if block.arity == 1
|
879
|
+
block.call(q)
|
880
|
+
else
|
881
|
+
queue = find_queue(method.queue)
|
882
|
+
block.call(queue, method.consumer_count, method.message_count)
|
883
|
+
end
|
884
|
+
}
|
830
885
|
Queue.new(self, name, opts, &shim)
|
831
886
|
end
|
832
887
|
|
@@ -852,8 +907,14 @@ module AMQP
|
|
852
907
|
# @note Instantiated channels are opened by default. This method should only be used for error recovery after network connection loss.
|
853
908
|
# @api public
|
854
909
|
def open(&block)
|
855
|
-
|
910
|
+
@connection.send_frame(AMQ::Protocol::Channel::Open.encode(@id, AMQ::Protocol::EMPTY_STRING))
|
911
|
+
@connection.channels[@id] = self
|
912
|
+
self.status = :opening
|
913
|
+
|
914
|
+
self.redefine_callback :open, &block
|
856
915
|
end
|
916
|
+
alias reopen open
|
917
|
+
|
857
918
|
|
858
919
|
# @return [Boolean] true if channel is not closed.
|
859
920
|
# @api public
|
@@ -889,9 +950,9 @@ module AMQP
|
|
889
950
|
# @api public
|
890
951
|
def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
|
891
952
|
self.status = :closing
|
892
|
-
|
953
|
+
@connection.send_frame(AMQ::Protocol::Channel::Close.encode(@id, reply_code, reply_text, class_id, method_id))
|
893
954
|
|
894
|
-
|
955
|
+
self.redefine_callback :close, &block
|
895
956
|
end
|
896
957
|
|
897
958
|
# @endgroup
|
@@ -912,7 +973,10 @@ module AMQP
|
|
912
973
|
# @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.5.2.3.)
|
913
974
|
# @api public
|
914
975
|
def flow(active = false, &block)
|
915
|
-
|
976
|
+
@connection.send_frame(AMQ::Protocol::Channel::Flow.encode(@id, active))
|
977
|
+
|
978
|
+
self.redefine_callback :flow, &block
|
979
|
+
self
|
916
980
|
end
|
917
981
|
|
918
982
|
# @return [Boolean] True if flow in this channel is active (messages will be delivered to consumers that use this channel).
|
@@ -952,7 +1016,9 @@ module AMQP
|
|
952
1016
|
# @see #recover
|
953
1017
|
# @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
|
954
1018
|
def acknowledge(delivery_tag, multiple = false)
|
955
|
-
|
1019
|
+
@connection.send_frame(AMQ::Protocol::Basic::Ack.encode(self.id, delivery_tag, multiple))
|
1020
|
+
|
1021
|
+
self
|
956
1022
|
end # acknowledge(delivery_tag, multiple = false)
|
957
1023
|
|
958
1024
|
# Reject a message with given delivery tag.
|
@@ -961,8 +1027,14 @@ module AMQP
|
|
961
1027
|
# @see #acknowledge
|
962
1028
|
# @see #recover
|
963
1029
|
# @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
|
964
|
-
def reject(delivery_tag, requeue = true)
|
965
|
-
|
1030
|
+
def reject(delivery_tag, requeue = true, multi = false)
|
1031
|
+
if multi
|
1032
|
+
@connection.send_frame(AMQ::Protocol::Basic::Nack.encode(self.id, delivery_tag, multi, requeue))
|
1033
|
+
else
|
1034
|
+
@connection.send_frame(AMQ::Protocol::Basic::Reject.encode(self.id, delivery_tag, requeue))
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
self
|
966
1038
|
end # reject(delivery_tag, requeue = true)
|
967
1039
|
|
968
1040
|
# Notifies AMQ broker that consumer has recovered and unacknowledged messages need
|
@@ -975,7 +1047,10 @@ module AMQP
|
|
975
1047
|
# @see #acknowledge
|
976
1048
|
# @api public
|
977
1049
|
def recover(requeue = true, &block)
|
978
|
-
|
1050
|
+
@connection.send_frame(AMQ::Protocol::Basic::Recover.encode(@id, requeue))
|
1051
|
+
|
1052
|
+
self.redefine_callback :recover, &block
|
1053
|
+
self
|
979
1054
|
end # recover(requeue = false, &block)
|
980
1055
|
|
981
1056
|
# @endgroup
|
@@ -990,21 +1065,30 @@ module AMQP
|
|
990
1065
|
#
|
991
1066
|
# @api public
|
992
1067
|
def tx_select(&block)
|
993
|
-
|
1068
|
+
@connection.send_frame(AMQ::Protocol::Tx::Select.encode(@id))
|
1069
|
+
|
1070
|
+
self.redefine_callback :tx_select, &block
|
1071
|
+
self
|
994
1072
|
end # tx_select(&block)
|
995
1073
|
|
996
1074
|
# Commits AMQP transaction.
|
997
1075
|
#
|
998
1076
|
# @api public
|
999
1077
|
def tx_commit(&block)
|
1000
|
-
|
1078
|
+
@connection.send_frame(AMQ::Protocol::Tx::Commit.encode(@id))
|
1079
|
+
|
1080
|
+
self.redefine_callback :tx_commit, &block
|
1081
|
+
self
|
1001
1082
|
end # tx_commit(&block)
|
1002
1083
|
|
1003
1084
|
# Rolls AMQP transaction back.
|
1004
1085
|
#
|
1005
1086
|
# @api public
|
1006
1087
|
def tx_rollback(&block)
|
1007
|
-
|
1088
|
+
@connection.send_frame(AMQ::Protocol::Tx::Rollback.encode(@id))
|
1089
|
+
|
1090
|
+
self.redefine_callback :tx_rollback, &block
|
1091
|
+
self
|
1008
1092
|
end # tx_rollback(&block)
|
1009
1093
|
|
1010
1094
|
|
@@ -1021,7 +1105,7 @@ module AMQP
|
|
1021
1105
|
#
|
1022
1106
|
# @api public
|
1023
1107
|
def on_error(&block)
|
1024
|
-
|
1108
|
+
self.define_callback(:error, &block)
|
1025
1109
|
end
|
1026
1110
|
|
1027
1111
|
# @endgroup
|
@@ -1031,56 +1115,30 @@ module AMQP
|
|
1031
1115
|
|
1032
1116
|
def confirm_select(nowait = false, &block)
|
1033
1117
|
self.once_open do
|
1034
|
-
|
1118
|
+
if nowait && block
|
1119
|
+
raise ArgumentError, "confirm.select with nowait = true and a callback makes no sense"
|
1035
1120
|
end
|
1036
|
-
end
|
1037
1121
|
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
#
|
1042
|
-
# Implementation
|
1043
|
-
#
|
1122
|
+
@uses_publisher_confirmations = true
|
1123
|
+
reset_publisher_index!
|
1044
1124
|
|
1125
|
+
self.redefine_callback(:confirm_select, &block) unless nowait
|
1126
|
+
self.redefine_callback(:after_publish) do
|
1127
|
+
increment_publisher_index!
|
1128
|
+
end
|
1129
|
+
@connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, nowait))
|
1045
1130
|
|
1046
|
-
|
1047
|
-
# all channels. Consider using Channel#on_error instead. This method is here for sake
|
1048
|
-
# of backwards compatibility with 0.6.x and 0.7.x releases.
|
1049
|
-
#
|
1050
|
-
# @param [String] msg Error message that passed to previously defined handler
|
1051
|
-
#
|
1052
|
-
# @deprecated
|
1053
|
-
# @api public
|
1054
|
-
# @private
|
1055
|
-
def self.error(msg = nil, &block)
|
1056
|
-
if block
|
1057
|
-
@global_error_handler = block
|
1058
|
-
else
|
1059
|
-
@global_error_handler.call(msg) if @global_error_handler && msg
|
1131
|
+
self
|
1060
1132
|
end
|
1061
1133
|
end
|
1062
1134
|
|
1135
|
+
# @endgroup
|
1063
1136
|
|
1064
|
-
# Overrides AMQ::Client::Channel version to also call global callback
|
1065
|
-
# (if defined) for backwards compatibility.
|
1066
|
-
#
|
1067
|
-
# @private
|
1068
|
-
# @api private
|
1069
|
-
def handle_close(method)
|
1070
|
-
super(method)
|
1071
|
-
|
1072
|
-
self.class.error(method.reply_text)
|
1073
|
-
self.class.release_channel_id(@id)
|
1074
|
-
end
|
1075
1137
|
|
1076
|
-
# Overrides AMQ::Client::Channel version to also release the channel id
|
1077
1138
|
#
|
1078
|
-
#
|
1079
|
-
#
|
1080
|
-
|
1081
|
-
super(method)
|
1082
|
-
self.class.release_channel_id(@id)
|
1083
|
-
end
|
1139
|
+
# Implementation
|
1140
|
+
#
|
1141
|
+
|
1084
1142
|
# Resets channel state (for example, list of registered queue objects and so on).
|
1085
1143
|
#
|
1086
1144
|
# Most of the time, this method is not
|
@@ -1089,11 +1147,11 @@ module AMQP
|
|
1089
1147
|
# @private
|
1090
1148
|
# @api plugin
|
1091
1149
|
def reset(&block)
|
1092
|
-
# See
|
1150
|
+
# See AMQP::Channel
|
1093
1151
|
self.reset_state!
|
1094
1152
|
|
1095
1153
|
# there is no way to reset a deferrable; we have to use a new instance. MK.
|
1096
|
-
@channel_is_open_deferrable =
|
1154
|
+
@channel_is_open_deferrable = AMQP::Deferrable.new
|
1097
1155
|
@channel_is_open_deferrable.callback(&block)
|
1098
1156
|
|
1099
1157
|
@connection.on_connection do
|
@@ -1103,22 +1161,19 @@ module AMQP
|
|
1103
1161
|
end
|
1104
1162
|
end
|
1105
1163
|
|
1106
|
-
# @private
|
1107
|
-
# @api plugin
|
1108
|
-
def reset_state!
|
1109
|
-
super
|
1110
|
-
end # reset_state!
|
1111
|
-
|
1112
|
-
|
1113
1164
|
# Overrides superclass method to also re-create @channel_is_open_deferrable
|
1114
1165
|
#
|
1115
1166
|
# @api plugin
|
1116
1167
|
# @private
|
1117
|
-
def handle_connection_interruption(
|
1118
|
-
|
1168
|
+
def handle_connection_interruption(method = nil)
|
1169
|
+
@queues.each { |name, q| q.handle_connection_interruption(method) }
|
1170
|
+
@exchanges.each { |name, e| e.handle_connection_interruption(method) }
|
1171
|
+
|
1172
|
+
self.exec_callback_yielding_self(:after_connection_interruption)
|
1173
|
+
self.reset_state!
|
1119
1174
|
|
1120
1175
|
self.class.release_channel_id(@id) unless auto_recovering?
|
1121
|
-
@channel_is_open_deferrable =
|
1176
|
+
@channel_is_open_deferrable = AMQP::Deferrable.new
|
1122
1177
|
end
|
1123
1178
|
|
1124
1179
|
|
@@ -1178,6 +1233,392 @@ module AMQP
|
|
1178
1233
|
end # self.initialize_channel_id_allocator
|
1179
1234
|
|
1180
1235
|
|
1236
|
+
# @return [Boolean] true if this channel uses automatic recovery mode
|
1237
|
+
def auto_recovering?
|
1238
|
+
@auto_recovery
|
1239
|
+
end # auto_recovering?
|
1240
|
+
|
1241
|
+
|
1242
|
+
# @return [Hash<String, Consumer>]
|
1243
|
+
def consumers
|
1244
|
+
@consumers
|
1245
|
+
end # consumers
|
1246
|
+
|
1247
|
+
# @return [Array<Queue>] Collection of queues that were declared on this channel.
|
1248
|
+
def queues
|
1249
|
+
@queues.values
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
# @return [Array<Exchange>] Collection of exchanges that were declared on this channel.
|
1253
|
+
def exchanges
|
1254
|
+
@exchanges.values
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
|
1258
|
+
# AMQP connection this channel belongs to.
|
1259
|
+
#
|
1260
|
+
# @return [AMQP::Connection] Connection this channel belongs to.
|
1261
|
+
def connection
|
1262
|
+
@connection
|
1263
|
+
end # connection
|
1264
|
+
|
1265
|
+
# Synchronizes given block using this channel's mutex.
|
1266
|
+
# @api public
|
1267
|
+
def synchronize(&block)
|
1268
|
+
@mutex.synchronize(&block)
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
|
1272
|
+
|
1273
|
+
# @group QoS and flow handling
|
1274
|
+
|
1275
|
+
# Requests a specific quality of service. The QoS can be specified for the current channel
|
1276
|
+
# or for all channels on the connection.
|
1277
|
+
#
|
1278
|
+
# @note RabbitMQ as of 2.3.1 does not support prefetch_size.
|
1279
|
+
# @api public
|
1280
|
+
def qos(prefetch_size = 0, prefetch_count = 32, global = false, &block)
|
1281
|
+
@connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, prefetch_size, prefetch_count, global))
|
1282
|
+
|
1283
|
+
self.redefine_callback :qos, &block
|
1284
|
+
self
|
1285
|
+
end # qos
|
1286
|
+
|
1287
|
+
# @endgroup
|
1288
|
+
|
1289
|
+
|
1290
|
+
|
1291
|
+
# @group Error handling
|
1292
|
+
|
1293
|
+
|
1294
|
+
# Defines a callback that will be executed after TCP connection is interrupted (typically because of a network failure).
|
1295
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
1296
|
+
#
|
1297
|
+
# @api public
|
1298
|
+
def on_connection_interruption(&block)
|
1299
|
+
self.redefine_callback(:after_connection_interruption, &block)
|
1300
|
+
end # on_connection_interruption(&block)
|
1301
|
+
alias after_connection_interruption on_connection_interruption
|
1302
|
+
|
1303
|
+
|
1304
|
+
# Defines a callback that will be executed after TCP connection has recovered after a network failure
|
1305
|
+
# but before AMQP connection is re-opened.
|
1306
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
1307
|
+
#
|
1308
|
+
# @api public
|
1309
|
+
def before_recovery(&block)
|
1310
|
+
self.redefine_callback(:before_recovery, &block)
|
1311
|
+
end # before_recovery(&block)
|
1312
|
+
|
1313
|
+
# @private
|
1314
|
+
def run_before_recovery_callbacks
|
1315
|
+
self.exec_callback_yielding_self(:before_recovery)
|
1316
|
+
|
1317
|
+
@queues.each { |name, q| q.run_before_recovery_callbacks }
|
1318
|
+
@exchanges.each { |name, e| e.run_before_recovery_callbacks }
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
|
1322
|
+
|
1323
|
+
# Defines a callback that will be executed after AMQP connection has recovered after a network failure.
|
1324
|
+
# Only one callback can be defined (the one defined last replaces previously added ones).
|
1325
|
+
#
|
1326
|
+
# @api public
|
1327
|
+
def on_recovery(&block)
|
1328
|
+
self.redefine_callback(:after_recovery, &block)
|
1329
|
+
end # on_recovery(&block)
|
1330
|
+
alias after_recovery on_recovery
|
1331
|
+
|
1332
|
+
# @private
|
1333
|
+
def run_after_recovery_callbacks
|
1334
|
+
self.exec_callback_yielding_self(:after_recovery)
|
1335
|
+
|
1336
|
+
@queues.each { |name, q| q.run_after_recovery_callbacks }
|
1337
|
+
@exchanges.each { |name, e| e.run_after_recovery_callbacks }
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
|
1341
|
+
# Called by associated connection object when AMQP connection has been re-established
|
1342
|
+
# (for example, after a network failure).
|
1343
|
+
#
|
1344
|
+
# @api plugin
|
1345
|
+
def auto_recover
|
1346
|
+
return unless auto_recovering?
|
1347
|
+
|
1348
|
+
self.open do
|
1349
|
+
# exchanges must be recovered first because queue recovery includes recovery of bindings. MK.
|
1350
|
+
@exchanges.each { |name, e| e.auto_recover }
|
1351
|
+
@queues.each { |name, q| q.auto_recover }
|
1352
|
+
end
|
1353
|
+
end # auto_recover
|
1354
|
+
|
1355
|
+
# @endgroup
|
1356
|
+
|
1357
|
+
|
1358
|
+
# Publisher index is an index of the last message since
|
1359
|
+
# the confirmations were activated, started with 0. It's
|
1360
|
+
# incremented by 1 every time a message is published.
|
1361
|
+
# This is done on both client and server, hence this
|
1362
|
+
# acknowledged messages can be matched via its delivery-tag.
|
1363
|
+
#
|
1364
|
+
# @return [Integer] Current publisher index.
|
1365
|
+
# @api public
|
1366
|
+
def publisher_index
|
1367
|
+
@publisher_index ||= 0
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
# Resets publisher index to 0
|
1371
|
+
#
|
1372
|
+
# @api plugin
|
1373
|
+
def reset_publisher_index!
|
1374
|
+
@publisher_index = 0
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
|
1378
|
+
# This method is executed after publishing of each message via {Exchage#publish}.
|
1379
|
+
# Currently it just increments publisher index by 1, so messages
|
1380
|
+
# can be actually matched.
|
1381
|
+
#
|
1382
|
+
# @api plugin
|
1383
|
+
def increment_publisher_index!
|
1384
|
+
@publisher_index += 1
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
# @return [Boolean]
|
1388
|
+
def uses_publisher_confirmations?
|
1389
|
+
@uses_publisher_confirmations
|
1390
|
+
end # uses_publisher_confirmations?
|
1391
|
+
|
1392
|
+
|
1393
|
+
# Turn on confirmations for this channel and, if given,
|
1394
|
+
# register callback for basic.ack from the broker.
|
1395
|
+
#
|
1396
|
+
# @raise [RuntimeError] Occurs when confirmations are already activated.
|
1397
|
+
# @raise [RuntimeError] Occurs when nowait is true and block is given.
|
1398
|
+
# @param [Boolean] nowait Whether we expect Confirm.Select-Ok to be returned by the broker or not.
|
1399
|
+
#
|
1400
|
+
# @yield [basick_ack] Callback which will be executed every time we receive Basic.Ack from the broker.
|
1401
|
+
# @yieldparam [AMQ::Protocol::Basic::Ack] basick_ack Protocol method class instance.
|
1402
|
+
#
|
1403
|
+
# @return [self] self.
|
1404
|
+
def on_ack(nowait = false, &block)
|
1405
|
+
self.define_callback(:ack, &block) if block
|
1406
|
+
|
1407
|
+
self
|
1408
|
+
end
|
1409
|
+
|
1410
|
+
|
1411
|
+
# Register error callback for Basic.Nack. It's called
|
1412
|
+
# when message(s) is rejected.
|
1413
|
+
#
|
1414
|
+
# @return [self] self
|
1415
|
+
def on_nack(&block)
|
1416
|
+
self.define_callback(:nack, &block) if block
|
1417
|
+
|
1418
|
+
self
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
|
1422
|
+
|
1423
|
+
|
1424
|
+
# Handler for Confirm.Select-Ok. By default, it just
|
1425
|
+
# executes hook specified via the #confirmations method
|
1426
|
+
# with a single argument, a protocol method class
|
1427
|
+
# instance (an instance of AMQ::Protocol::Confirm::SelectOk)
|
1428
|
+
# and then it deletes the callback, since Confirm.Select
|
1429
|
+
# is supposed to be sent just once.
|
1430
|
+
#
|
1431
|
+
# @api plugin
|
1432
|
+
def handle_select_ok(method)
|
1433
|
+
self.exec_callback_once(:confirm_select, method)
|
1434
|
+
end
|
1435
|
+
|
1436
|
+
# Handler for Basic.Ack. By default, it just
|
1437
|
+
# executes hook specified via the #confirm method
|
1438
|
+
# with a single argument, a protocol method class
|
1439
|
+
# instance (an instance of AMQ::Protocol::Basic::Ack).
|
1440
|
+
#
|
1441
|
+
# @api plugin
|
1442
|
+
def handle_basic_ack(method)
|
1443
|
+
self.exec_callback(:ack, method)
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
|
1447
|
+
# Handler for Basic.Nack. By default, it just
|
1448
|
+
# executes hook specified via the #confirm_failed method
|
1449
|
+
# with a single argument, a protocol method class
|
1450
|
+
# instance (an instance of AMQ::Protocol::Basic::Nack).
|
1451
|
+
#
|
1452
|
+
# @api plugin
|
1453
|
+
def handle_basic_nack(method)
|
1454
|
+
self.exec_callback(:nack, method)
|
1455
|
+
end
|
1456
|
+
|
1457
|
+
|
1458
|
+
|
1459
|
+
#
|
1460
|
+
# Implementation
|
1461
|
+
#
|
1462
|
+
|
1463
|
+
def register_exchange(exchange)
|
1464
|
+
raise ArgumentError, "argument is nil!" if exchange.nil?
|
1465
|
+
|
1466
|
+
@exchanges[exchange.name] = exchange
|
1467
|
+
end # register_exchange(exchange)
|
1468
|
+
|
1469
|
+
# Finds exchange in the exchanges cache on this channel by name. Exchange only exists in the cache if
|
1470
|
+
# it was previously instantiated on this channel.
|
1471
|
+
#
|
1472
|
+
# @param [String] name Exchange name
|
1473
|
+
# @return [AMQP::Exchange] Exchange (if found)
|
1474
|
+
# @api plugin
|
1475
|
+
def find_exchange(name)
|
1476
|
+
@exchanges[name]
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
# @api plugin
|
1480
|
+
# @private
|
1481
|
+
def register_queue(queue)
|
1482
|
+
raise ArgumentError, "argument is nil!" if queue.nil?
|
1483
|
+
|
1484
|
+
@queues[queue.name] = queue
|
1485
|
+
end # register_queue(queue)
|
1486
|
+
|
1487
|
+
# @api plugin
|
1488
|
+
# @private
|
1489
|
+
def find_queue(name)
|
1490
|
+
@queues[name]
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
|
1494
|
+
RECOVERY_EVENTS = [:after_connection_interruption, :before_recovery, :after_recovery].freeze
|
1495
|
+
|
1496
|
+
|
1497
|
+
# @api plugin
|
1498
|
+
# @private
|
1499
|
+
def reset_state!
|
1500
|
+
@flow_is_active = true
|
1501
|
+
|
1502
|
+
@queues_awaiting_declare_ok = Array.new
|
1503
|
+
@exchanges_awaiting_declare_ok = Array.new
|
1504
|
+
|
1505
|
+
@queues_awaiting_delete_ok = Array.new
|
1506
|
+
|
1507
|
+
@exchanges_awaiting_delete_ok = Array.new
|
1508
|
+
@queues_awaiting_purge_ok = Array.new
|
1509
|
+
@queues_awaiting_bind_ok = Array.new
|
1510
|
+
@queues_awaiting_unbind_ok = Array.new
|
1511
|
+
@consumers_awaiting_consume_ok = Array.new
|
1512
|
+
@consumers_awaiting_cancel_ok = Array.new
|
1513
|
+
|
1514
|
+
@queues_awaiting_get_response = Array.new
|
1515
|
+
|
1516
|
+
@callbacks = @callbacks.delete_if { |k, v| !RECOVERY_EVENTS.include?(k) }
|
1517
|
+
@uses_publisher_confirmations = false
|
1518
|
+
end # reset_state!
|
1519
|
+
|
1520
|
+
|
1521
|
+
# @api plugin
|
1522
|
+
# @private
|
1523
|
+
def handle_open_ok(open_ok)
|
1524
|
+
self.status = :opened
|
1525
|
+
self.exec_callback_once_yielding_self(:open, open_ok)
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
# @api plugin
|
1529
|
+
# @private
|
1530
|
+
def handle_close_ok(close_ok)
|
1531
|
+
self.status = :closed
|
1532
|
+
self.connection.clear_frames_on(self.id)
|
1533
|
+
self.exec_callback_once_yielding_self(:close, close_ok)
|
1534
|
+
|
1535
|
+
self.class.release_channel_id(@id)
|
1536
|
+
end
|
1537
|
+
|
1538
|
+
# @api plugin
|
1539
|
+
# @private
|
1540
|
+
def handle_close(channel_close)
|
1541
|
+
self.status = :closed
|
1542
|
+
self.connection.clear_frames_on(self.id)
|
1543
|
+
|
1544
|
+
self.exec_callback_yielding_self(:error, channel_close)
|
1545
|
+
end
|
1546
|
+
|
1547
|
+
|
1548
|
+
|
1549
|
+
self.handle(AMQ::Protocol::Channel::OpenOk) do |connection, frame|
|
1550
|
+
channel = connection.channels[frame.channel]
|
1551
|
+
channel.handle_open_ok(frame.decode_payload)
|
1552
|
+
end
|
1553
|
+
|
1554
|
+
self.handle(AMQ::Protocol::Channel::CloseOk) do |connection, frame|
|
1555
|
+
method = frame.decode_payload
|
1556
|
+
channels = connection.channels
|
1557
|
+
|
1558
|
+
channel = channels[frame.channel]
|
1559
|
+
channels.delete(channel)
|
1560
|
+
channel.handle_close_ok(method)
|
1561
|
+
end
|
1562
|
+
|
1563
|
+
self.handle(AMQ::Protocol::Channel::Close) do |connection, frame|
|
1564
|
+
method = frame.decode_payload
|
1565
|
+
channels = connection.channels
|
1566
|
+
channel = channels[frame.channel]
|
1567
|
+
connection.send_frame(AMQ::Protocol::Channel::CloseOk.encode(frame.channel))
|
1568
|
+
channel.handle_close(method)
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
self.handle(AMQ::Protocol::Basic::QosOk) do |connection, frame|
|
1572
|
+
channel = connection.channels[frame.channel]
|
1573
|
+
channel.exec_callback(:qos, frame.decode_payload)
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
self.handle(AMQ::Protocol::Basic::RecoverOk) do |connection, frame|
|
1577
|
+
channel = connection.channels[frame.channel]
|
1578
|
+
channel.exec_callback(:recover, frame.decode_payload)
|
1579
|
+
end
|
1580
|
+
|
1581
|
+
self.handle(AMQ::Protocol::Channel::FlowOk) do |connection, frame|
|
1582
|
+
channel = connection.channels[frame.channel]
|
1583
|
+
method = frame.decode_payload
|
1584
|
+
|
1585
|
+
channel.flow_is_active = method.active
|
1586
|
+
channel.exec_callback(:flow, method)
|
1587
|
+
end
|
1588
|
+
|
1589
|
+
self.handle(AMQ::Protocol::Tx::SelectOk) do |connection, frame|
|
1590
|
+
channel = connection.channels[frame.channel]
|
1591
|
+
channel.exec_callback(:tx_select, frame.decode_payload)
|
1592
|
+
end
|
1593
|
+
|
1594
|
+
self.handle(AMQ::Protocol::Tx::CommitOk) do |connection, frame|
|
1595
|
+
channel = connection.channels[frame.channel]
|
1596
|
+
channel.exec_callback(:tx_commit, frame.decode_payload)
|
1597
|
+
end
|
1598
|
+
|
1599
|
+
self.handle(AMQ::Protocol::Tx::RollbackOk) do |connection, frame|
|
1600
|
+
channel = connection.channels[frame.channel]
|
1601
|
+
channel.exec_callback(:tx_rollback, frame.decode_payload)
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
self.handle(AMQ::Protocol::Confirm::SelectOk) do |connection, frame|
|
1605
|
+
method = frame.decode_payload
|
1606
|
+
channel = connection.channels[frame.channel]
|
1607
|
+
channel.handle_select_ok(method)
|
1608
|
+
end
|
1609
|
+
|
1610
|
+
self.handle(AMQ::Protocol::Basic::Ack) do |connection, frame|
|
1611
|
+
method = frame.decode_payload
|
1612
|
+
channel = connection.channels[frame.channel]
|
1613
|
+
channel.handle_basic_ack(method)
|
1614
|
+
end
|
1615
|
+
|
1616
|
+
self.handle(AMQ::Protocol::Basic::Nack) do |connection, frame|
|
1617
|
+
method = frame.decode_payload
|
1618
|
+
channel = connection.channels[frame.channel]
|
1619
|
+
channel.handle_basic_nack(method)
|
1620
|
+
end
|
1621
|
+
|
1181
1622
|
protected
|
1182
1623
|
|
1183
1624
|
@private
|