amqp 1.1.0.pre1 → 1.1.0.pre2
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/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
|