amq-client 0.5.0
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.
- data/.gitignore +8 -0
- data/.gitmodules +9 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/CONTRIBUTORS +3 -0
- data/Gemfile +27 -0
- data/LICENSE +20 -0
- data/README.textile +61 -0
- data/amq-client.gemspec +34 -0
- data/bin/jenkins.sh +23 -0
- data/bin/set_test_suite_realms_up.sh +24 -0
- data/examples/coolio_adapter/basic_consume.rb +49 -0
- data/examples/coolio_adapter/basic_consume_with_acknowledgements.rb +43 -0
- data/examples/coolio_adapter/basic_consume_with_rejections.rb +43 -0
- data/examples/coolio_adapter/basic_publish.rb +35 -0
- data/examples/coolio_adapter/channel_close.rb +24 -0
- data/examples/coolio_adapter/example_helper.rb +39 -0
- data/examples/coolio_adapter/exchange_declare.rb +28 -0
- data/examples/coolio_adapter/kitchen_sink1.rb +48 -0
- data/examples/coolio_adapter/queue_bind.rb +32 -0
- data/examples/coolio_adapter/queue_purge.rb +32 -0
- data/examples/coolio_adapter/queue_unbind.rb +37 -0
- data/examples/eventmachine_adapter/authentication/plain_password_with_custom_role_credentials.rb +36 -0
- data/examples/eventmachine_adapter/authentication/plain_password_with_default_role_credentials.rb +27 -0
- data/examples/eventmachine_adapter/authentication/plain_password_with_incorrect_credentials.rb +18 -0
- data/examples/eventmachine_adapter/basic_cancel.rb +49 -0
- data/examples/eventmachine_adapter/basic_consume.rb +51 -0
- data/examples/eventmachine_adapter/basic_consume_with_acknowledgements.rb +45 -0
- data/examples/eventmachine_adapter/basic_consume_with_rejections.rb +45 -0
- data/examples/eventmachine_adapter/basic_get.rb +57 -0
- data/examples/eventmachine_adapter/basic_get_with_empty_queue.rb +53 -0
- data/examples/eventmachine_adapter/basic_publish.rb +38 -0
- data/examples/eventmachine_adapter/basic_qos.rb +29 -0
- data/examples/eventmachine_adapter/basic_recover.rb +29 -0
- data/examples/eventmachine_adapter/basic_return.rb +34 -0
- data/examples/eventmachine_adapter/channel_close.rb +24 -0
- data/examples/eventmachine_adapter/channel_flow.rb +36 -0
- data/examples/eventmachine_adapter/channel_level_exception_handling.rb +44 -0
- data/examples/eventmachine_adapter/example_helper.rb +39 -0
- data/examples/eventmachine_adapter/exchange_declare.rb +54 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb +31 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +56 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb +46 -0
- data/examples/eventmachine_adapter/kitchen_sink1.rb +50 -0
- data/examples/eventmachine_adapter/queue_bind.rb +32 -0
- data/examples/eventmachine_adapter/queue_declare.rb +34 -0
- data/examples/eventmachine_adapter/queue_purge.rb +32 -0
- data/examples/eventmachine_adapter/queue_unbind.rb +37 -0
- data/examples/eventmachine_adapter/tx_commit.rb +29 -0
- data/examples/eventmachine_adapter/tx_rollback.rb +29 -0
- data/examples/eventmachine_adapter/tx_select.rb +27 -0
- data/examples/socket_adapter/basics.rb +19 -0
- data/examples/socket_adapter/connection.rb +53 -0
- data/examples/socket_adapter/multiple_connections.rb +17 -0
- data/irb.rb +66 -0
- data/lib/amq/client.rb +15 -0
- data/lib/amq/client/adapter.rb +356 -0
- data/lib/amq/client/adapters/coolio.rb +221 -0
- data/lib/amq/client/adapters/event_machine.rb +228 -0
- data/lib/amq/client/adapters/socket.rb +89 -0
- data/lib/amq/client/channel.rb +338 -0
- data/lib/amq/client/connection.rb +246 -0
- data/lib/amq/client/entity.rb +117 -0
- data/lib/amq/client/exceptions.rb +86 -0
- data/lib/amq/client/exchange.rb +163 -0
- data/lib/amq/client/extensions/rabbitmq.rb +5 -0
- data/lib/amq/client/extensions/rabbitmq/basic.rb +36 -0
- data/lib/amq/client/extensions/rabbitmq/confirm.rb +254 -0
- data/lib/amq/client/framing/io/frame.rb +32 -0
- data/lib/amq/client/framing/string/frame.rb +62 -0
- data/lib/amq/client/logging.rb +56 -0
- data/lib/amq/client/mixins/anonymous_entity.rb +21 -0
- data/lib/amq/client/mixins/status.rb +62 -0
- data/lib/amq/client/protocol/get_response.rb +55 -0
- data/lib/amq/client/queue.rb +450 -0
- data/lib/amq/client/settings.rb +83 -0
- data/lib/amq/client/version.rb +5 -0
- data/spec/benchmarks/adapters.rb +77 -0
- data/spec/client/framing/io_frame_spec.rb +57 -0
- data/spec/client/framing/string_frame_spec.rb +57 -0
- data/spec/client/protocol/get_response_spec.rb +79 -0
- data/spec/integration/coolio/basic_ack_spec.rb +41 -0
- data/spec/integration/coolio/basic_get_spec.rb +73 -0
- data/spec/integration/coolio/basic_return_spec.rb +33 -0
- data/spec/integration/coolio/channel_close_spec.rb +26 -0
- data/spec/integration/coolio/channel_flow_spec.rb +46 -0
- data/spec/integration/coolio/spec_helper.rb +31 -0
- data/spec/integration/coolio/tx_commit_spec.rb +40 -0
- data/spec/integration/coolio/tx_rollback_spec.rb +44 -0
- data/spec/integration/eventmachine/basic_ack_spec.rb +40 -0
- data/spec/integration/eventmachine/basic_get_spec.rb +73 -0
- data/spec/integration/eventmachine/basic_return_spec.rb +35 -0
- data/spec/integration/eventmachine/channel_close_spec.rb +26 -0
- data/spec/integration/eventmachine/channel_flow_spec.rb +32 -0
- data/spec/integration/eventmachine/spec_helper.rb +22 -0
- data/spec/integration/eventmachine/tx_commit_spec.rb +47 -0
- data/spec/integration/eventmachine/tx_rollback_spec.rb +35 -0
- data/spec/regression/bad_frame_slicing_in_adapters_spec.rb +59 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/unit/client/adapter_spec.rb +49 -0
- data/spec/unit/client/entity_spec.rb +49 -0
- data/spec/unit/client/logging_spec.rb +60 -0
- data/spec/unit/client/mixins/status_spec.rb +72 -0
- data/spec/unit/client/settings_spec.rb +27 -0
- data/spec/unit/client_spec.rb +11 -0
- data/tasks.rb +11 -0
- metadata +202 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "amq/client/entity"
|
|
4
|
+
require "amq/client/adapter"
|
|
5
|
+
|
|
6
|
+
module AMQ
|
|
7
|
+
module Client
|
|
8
|
+
class Channel < Entity
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# API
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ChannelOutOfBadError < StandardError # TODO: inherit from some AMQP error class defined in amq-protocol or use it straight away.
|
|
16
|
+
def initialize(max, given)
|
|
17
|
+
super("Channel max is #{max}, #{given} given.")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
DEFAULT_REPLY_TEXT = "Goodbye".freeze
|
|
23
|
+
|
|
24
|
+
attr_reader :id
|
|
25
|
+
|
|
26
|
+
attr_reader :exchanges_awaiting_declare_ok, :exchanges_awaiting_delete_ok
|
|
27
|
+
attr_reader :queues_awaiting_declare_ok, :queues_awaiting_delete_ok, :queues_awaiting_bind_ok, :queues_awaiting_unbind_ok, :queues_awaiting_purge_ok, :queues_awaiting_consume_ok, :queues_awaiting_cancel_ok, :queues_awaiting_get_response
|
|
28
|
+
|
|
29
|
+
attr_accessor :flow_is_active
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def initialize(client, id)
|
|
33
|
+
super(client)
|
|
34
|
+
|
|
35
|
+
@id = id
|
|
36
|
+
@exchanges = Hash.new
|
|
37
|
+
@queues = Hash.new
|
|
38
|
+
@consumers = Hash.new
|
|
39
|
+
|
|
40
|
+
reset_state!
|
|
41
|
+
|
|
42
|
+
# 65536 is here for cases when channel is opened without passing a callback in,
|
|
43
|
+
# otherwise channel_mix would be nil and it causes a lot of needless headaches.
|
|
44
|
+
# lets just have this default. MK.
|
|
45
|
+
channel_max = client.connection.channel_max || 65536
|
|
46
|
+
|
|
47
|
+
if channel_max != 0 && !(0..channel_max).include?(id)
|
|
48
|
+
raise ChannelOutOfBadError.new(channel_max, id)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def consumers
|
|
53
|
+
@consumers
|
|
54
|
+
end # consumers
|
|
55
|
+
|
|
56
|
+
# @return [Array<Queue>] Collection of queues that were declared on this channel.
|
|
57
|
+
def queues
|
|
58
|
+
@queues.values
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [Array<Exchange>] Collection of exchanges that were declared on this channel.
|
|
62
|
+
def exchanges
|
|
63
|
+
@exchanges.values
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# AMQP connection this channel belongs to.
|
|
68
|
+
#
|
|
69
|
+
# @return [AMQ::Client::Connection] Connection this channel belongs to.
|
|
70
|
+
def connection
|
|
71
|
+
@client.connection
|
|
72
|
+
end # connection
|
|
73
|
+
|
|
74
|
+
# Opens AMQP channel.
|
|
75
|
+
#
|
|
76
|
+
# @api public
|
|
77
|
+
def open(&block)
|
|
78
|
+
@client.send Protocol::Channel::Open.encode(@id, AMQ::Protocol::EMPTY_STRING)
|
|
79
|
+
@client.connection.channels[@id] = self
|
|
80
|
+
self.status = :opening
|
|
81
|
+
|
|
82
|
+
self.define_callback :open, &block
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Closes AMQP channel.
|
|
86
|
+
#
|
|
87
|
+
# @api public
|
|
88
|
+
def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
|
|
89
|
+
@client.send Protocol::Channel::Close.encode(@id, reply_code, reply_text, class_id, method_id)
|
|
90
|
+
|
|
91
|
+
self.define_callback :close, &block
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# Acknowledge one or all messages on the channel.
|
|
96
|
+
#
|
|
97
|
+
# @api public
|
|
98
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
|
|
99
|
+
def acknowledge(delivery_tag, multiple = false)
|
|
100
|
+
@client.send(Protocol::Basic::Ack.encode(self.id, delivery_tag, multiple))
|
|
101
|
+
|
|
102
|
+
self
|
|
103
|
+
end # acknowledge(delivery_tag, multiple = false)
|
|
104
|
+
|
|
105
|
+
# Reject a message with given delivery tag.
|
|
106
|
+
#
|
|
107
|
+
# @api public
|
|
108
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
|
|
109
|
+
def reject(delivery_tag, requeue = true)
|
|
110
|
+
@client.send(Protocol::Basic::Reject.encode(self.id, delivery_tag, requeue))
|
|
111
|
+
|
|
112
|
+
self
|
|
113
|
+
end # reject(delivery_tag, requeue = true)
|
|
114
|
+
|
|
115
|
+
# Requests a specific quality of service. The QoS can be specified for the current channel
|
|
116
|
+
# or for all channels on the connection.
|
|
117
|
+
#
|
|
118
|
+
# @note RabbitMQ as of 2.3.1 does not support prefetch_size.
|
|
119
|
+
# @api public
|
|
120
|
+
def qos(prefetch_size = 0, prefetch_count = 32, global = false, &block)
|
|
121
|
+
@client.send Protocol::Basic::Qos.encode(@id, prefetch_size, prefetch_count, global)
|
|
122
|
+
|
|
123
|
+
self.redefine_callback :qos, &block
|
|
124
|
+
self
|
|
125
|
+
end # qos(prefetch_size = 4096, prefetch_count = 32, global = false, &block)
|
|
126
|
+
|
|
127
|
+
# Notifies AMQ broker that consumer has recovered and unacknowledged messages need
|
|
128
|
+
# to be redelivered.
|
|
129
|
+
#
|
|
130
|
+
# @return [Channel] self
|
|
131
|
+
#
|
|
132
|
+
# @note RabbitMQ as of 2.3.1 does not support basic.recover with requeue = false.
|
|
133
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.16.)
|
|
134
|
+
# @api public
|
|
135
|
+
def recover(requeue = true, &block)
|
|
136
|
+
@client.send(Protocol::Basic::Recover.encode(@id, requeue))
|
|
137
|
+
|
|
138
|
+
self.redefine_callback :recover, &block
|
|
139
|
+
self
|
|
140
|
+
end # recover(requeue = false, &block)
|
|
141
|
+
|
|
142
|
+
# Asks the peer to pause or restart the flow of content data sent to a consumer.
|
|
143
|
+
# This is a simple flowcontrol mechanism that a peer can use to avoid overflowing its
|
|
144
|
+
# queues or otherwise finding itself receiving more messages than it can process. Note that
|
|
145
|
+
# this method is not intended for window control. It does not affect contents returned to
|
|
146
|
+
# Queue#get callers.
|
|
147
|
+
#
|
|
148
|
+
# @param [Boolean] Desired flow state.
|
|
149
|
+
#
|
|
150
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.5.2.3.)
|
|
151
|
+
# @api public
|
|
152
|
+
def flow(active = false, &block)
|
|
153
|
+
@client.send Protocol::Channel::Flow.encode(@id, active)
|
|
154
|
+
|
|
155
|
+
self.redefine_callback :flow, &block
|
|
156
|
+
self
|
|
157
|
+
end # flow(active = false, &block)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# Sets the channel to use standard transactions. One must use this method at least
|
|
161
|
+
# once on a channel before using #tx_tommit or tx_rollback methods.
|
|
162
|
+
#
|
|
163
|
+
# @api public
|
|
164
|
+
def tx_select(&block)
|
|
165
|
+
@client.send Protocol::Tx::Select.encode(@id)
|
|
166
|
+
|
|
167
|
+
self.redefine_callback :tx_select, &block
|
|
168
|
+
self
|
|
169
|
+
end # tx_select(&block)
|
|
170
|
+
|
|
171
|
+
# Commits AMQP transaction.
|
|
172
|
+
#
|
|
173
|
+
# @api public
|
|
174
|
+
def tx_commit(&block)
|
|
175
|
+
@client.send Protocol::Tx::Commit.encode(@id)
|
|
176
|
+
|
|
177
|
+
self.redefine_callback :tx_commit, &block
|
|
178
|
+
self
|
|
179
|
+
end # tx_commit(&block)
|
|
180
|
+
|
|
181
|
+
# Rolls AMQP transaction back.
|
|
182
|
+
#
|
|
183
|
+
# @api public
|
|
184
|
+
def tx_rollback(&block)
|
|
185
|
+
@client.send Protocol::Tx::Rollback.encode(@id)
|
|
186
|
+
|
|
187
|
+
self.redefine_callback :tx_rollback, &block
|
|
188
|
+
self
|
|
189
|
+
end # tx_rollback(&block)
|
|
190
|
+
|
|
191
|
+
# @return [Boolean] True if flow in this channel is active (messages will be delivered to consumers that use this channel).
|
|
192
|
+
#
|
|
193
|
+
# @api public
|
|
194
|
+
def flow_is_active?
|
|
195
|
+
@flow_is_active
|
|
196
|
+
end # flow_is_active?
|
|
197
|
+
|
|
198
|
+
def on_error(&block)
|
|
199
|
+
self.define_callback(:close, &block)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
#
|
|
204
|
+
# Implementation
|
|
205
|
+
#
|
|
206
|
+
|
|
207
|
+
def register_exchange(exchange)
|
|
208
|
+
raise ArgumentError, "argument is nil!" if exchange.nil?
|
|
209
|
+
|
|
210
|
+
@exchanges[exchange.name] = exchange
|
|
211
|
+
end # register_exchange(exchange)
|
|
212
|
+
|
|
213
|
+
def find_exchange(name)
|
|
214
|
+
@exchanges[name]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def register_queue(queue)
|
|
218
|
+
raise ArgumentError, "argument is nil!" if queue.nil?
|
|
219
|
+
|
|
220
|
+
@queues[queue.name] = queue
|
|
221
|
+
end # register_queue(queue)
|
|
222
|
+
|
|
223
|
+
def find_queue(name)
|
|
224
|
+
@queues[name]
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def reset_state!
|
|
229
|
+
@flow_is_active = true
|
|
230
|
+
|
|
231
|
+
@queues_awaiting_declare_ok = Array.new
|
|
232
|
+
@exchanges_awaiting_declare_ok = Array.new
|
|
233
|
+
|
|
234
|
+
@queues_awaiting_delete_ok = Array.new
|
|
235
|
+
|
|
236
|
+
@exchanges_awaiting_delete_ok = Array.new
|
|
237
|
+
@queues_awaiting_purge_ok = Array.new
|
|
238
|
+
@queues_awaiting_bind_ok = Array.new
|
|
239
|
+
@queues_awaiting_unbind_ok = Array.new
|
|
240
|
+
@queues_awaiting_consume_ok = Array.new
|
|
241
|
+
@queues_awaiting_cancel_ok = Array.new
|
|
242
|
+
|
|
243
|
+
@queues_awaiting_get_response = Array.new
|
|
244
|
+
end # reset_state!
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def on_connection_interruption(exception = nil)
|
|
248
|
+
self.reset_state!
|
|
249
|
+
end # on_connection_interruption
|
|
250
|
+
|
|
251
|
+
def handle_open_ok(method)
|
|
252
|
+
self.status = :opened
|
|
253
|
+
self.exec_callback_once_yielding_self(:open, method)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def handle_close_ok(method)
|
|
257
|
+
self.status = :closed
|
|
258
|
+
self.exec_callback_once_yielding_self(:close, method)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def handle_close(method, exception = nil)
|
|
262
|
+
puts "In Channel#handle_close"
|
|
263
|
+
self.status = :closed
|
|
264
|
+
self.exec_callback_once_yielding_self(:close, method)
|
|
265
|
+
|
|
266
|
+
self.on_connection_interruption(exception)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# === Handlers ===
|
|
270
|
+
|
|
271
|
+
self.handle(Protocol::Channel::OpenOk) do |client, frame|
|
|
272
|
+
channel = client.connection.channels[frame.channel]
|
|
273
|
+
channel.handle_open_ok(frame.decode_payload)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
self.handle(Protocol::Channel::CloseOk) do |client, frame|
|
|
277
|
+
method = frame.decode_payload
|
|
278
|
+
channels = client.connection.channels
|
|
279
|
+
|
|
280
|
+
channel = channels[frame.channel]
|
|
281
|
+
channels.delete(channel)
|
|
282
|
+
|
|
283
|
+
channel.handle_close_ok(method)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
self.handle(Protocol::Channel::Close) do |client, frame|
|
|
287
|
+
exception = nil
|
|
288
|
+
|
|
289
|
+
begin
|
|
290
|
+
method = frame.decode_payload
|
|
291
|
+
rescue AMQ::Protocol::Error => e
|
|
292
|
+
exception = e
|
|
293
|
+
end
|
|
294
|
+
channels = client.connection.channels
|
|
295
|
+
channel = channels[frame.channel]
|
|
296
|
+
|
|
297
|
+
if exception
|
|
298
|
+
channel.handle_close(method, exception)
|
|
299
|
+
else
|
|
300
|
+
channel.handle_close(method)
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
self.handle(Protocol::Basic::QosOk) do |client, frame|
|
|
305
|
+
channel = client.connection.channels[frame.channel]
|
|
306
|
+
channel.exec_callback(:qos, frame.decode_payload)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
self.handle(Protocol::Basic::RecoverOk) do |client, frame|
|
|
310
|
+
channel = client.connection.channels[frame.channel]
|
|
311
|
+
channel.exec_callback(:recover, frame.decode_payload)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
self.handle(Protocol::Channel::FlowOk) do |client, frame|
|
|
315
|
+
channel = client.connection.channels[frame.channel]
|
|
316
|
+
method = frame.decode_payload
|
|
317
|
+
|
|
318
|
+
channel.flow_is_active = method.active
|
|
319
|
+
channel.exec_callback(:flow, method)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
self.handle(Protocol::Tx::SelectOk) do |client, frame|
|
|
323
|
+
channel = client.connection.channels[frame.channel]
|
|
324
|
+
channel.exec_callback(:tx_select, frame.decode_payload)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
self.handle(Protocol::Tx::CommitOk) do |client, frame|
|
|
328
|
+
channel = client.connection.channels[frame.channel]
|
|
329
|
+
channel.exec_callback(:tx_commit, frame.decode_payload)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
self.handle(Protocol::Tx::RollbackOk) do |client, frame|
|
|
333
|
+
channel = client.connection.channels[frame.channel]
|
|
334
|
+
channel.exec_callback(:tx_rollback, frame.decode_payload)
|
|
335
|
+
end
|
|
336
|
+
end # Channel
|
|
337
|
+
end # Client
|
|
338
|
+
end # AMQ
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "amq/client"
|
|
4
|
+
require "amq/client/entity"
|
|
5
|
+
|
|
6
|
+
module AMQ
|
|
7
|
+
module Client
|
|
8
|
+
# AMQP connection object handles various connection lifecycle events
|
|
9
|
+
# (for example, Connection.Start-Ok, Connection.Tune-Ok and Connection.Close
|
|
10
|
+
# methods)
|
|
11
|
+
#
|
|
12
|
+
# AMQP connection has multiple channels accessible via {Connection#channels} reader.
|
|
13
|
+
class Connection < Entity
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
# Behaviors
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
include StatusMixin
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# API
|
|
24
|
+
#
|
|
25
|
+
|
|
26
|
+
# TODO: make it possible to override these from, say, amqp gem or bunny
|
|
27
|
+
CLIENT_PROPERTIES = {
|
|
28
|
+
:platform => ::RUBY_DESCRIPTION,
|
|
29
|
+
:product => "AMQ Client",
|
|
30
|
+
:version => AMQ::Client::VERSION,
|
|
31
|
+
:homepage => "https://github.com/ruby-amqp/amq-client"
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
# Client capabilities
|
|
35
|
+
#
|
|
36
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.2.1)
|
|
37
|
+
attr_accessor :client_properties
|
|
38
|
+
|
|
39
|
+
# Server capabilities
|
|
40
|
+
#
|
|
41
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.1.3)
|
|
42
|
+
attr_reader :server_properties
|
|
43
|
+
|
|
44
|
+
# Authentication mechanism used.
|
|
45
|
+
#
|
|
46
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.2)
|
|
47
|
+
attr_reader :mechanism
|
|
48
|
+
|
|
49
|
+
# Security response data.
|
|
50
|
+
#
|
|
51
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Sections 1.4.2.2 and 1.4.2.4.1)
|
|
52
|
+
attr_reader :response
|
|
53
|
+
|
|
54
|
+
# The locale defines the language in which the server will send reply texts.
|
|
55
|
+
#
|
|
56
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.2)
|
|
57
|
+
attr_reader :locale
|
|
58
|
+
|
|
59
|
+
# Channels within this connection.
|
|
60
|
+
#
|
|
61
|
+
# @see http://bit.ly/hw2ELX AMQP 0.9.1 specification (Section 2.2.5)
|
|
62
|
+
attr_reader :channels
|
|
63
|
+
|
|
64
|
+
# Maximum channel number that the server permits this connection to use.
|
|
65
|
+
# Usable channel numbers are in the range 1..channel_max.
|
|
66
|
+
# Zero indicates no specified limit.
|
|
67
|
+
#
|
|
68
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Sections 1.4.2.5.1 and 1.4.2.6.1)
|
|
69
|
+
attr_accessor :channel_max
|
|
70
|
+
|
|
71
|
+
# Maximum frame size that the server permits this connection to use.
|
|
72
|
+
#
|
|
73
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Sections 1.4.2.5.2 and 1.4.2.6.2)
|
|
74
|
+
attr_accessor :frame_max
|
|
75
|
+
|
|
76
|
+
# The delay, in seconds, of the connection heartbeat that the server wants.
|
|
77
|
+
# Zero means the server does not want a heartbeat.
|
|
78
|
+
#
|
|
79
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.5.3)
|
|
80
|
+
attr_accessor :heartbeat_interval
|
|
81
|
+
|
|
82
|
+
attr_reader :known_hosts
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def initialize(client, mechanism, response, locale, client_properties = nil)
|
|
88
|
+
@mechanism = mechanism
|
|
89
|
+
@response = response
|
|
90
|
+
@locale = locale
|
|
91
|
+
|
|
92
|
+
@channels = Hash.new
|
|
93
|
+
@client_properties = client_properties || {
|
|
94
|
+
:platform => "Ruby #{RUBY_VERSION}",
|
|
95
|
+
:product => "AMQ Client",
|
|
96
|
+
:information => "http://github.com/ruby-amqp/amq-client",
|
|
97
|
+
:version => AMQ::Client::VERSION
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
reset_state!
|
|
101
|
+
|
|
102
|
+
super(client)
|
|
103
|
+
|
|
104
|
+
@client.connections.push(self)
|
|
105
|
+
|
|
106
|
+
# Default errback.
|
|
107
|
+
# You might want to override it, otherwise it'll
|
|
108
|
+
# crash your program. It's the expected behaviour
|
|
109
|
+
# if it's a synchronous one, but not if you use
|
|
110
|
+
# some kind of event loop like EventMachine etc.
|
|
111
|
+
self.define_callback(:close) { |exception| raise(exception) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def settings
|
|
116
|
+
@client.settings
|
|
117
|
+
end # settings
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
#
|
|
121
|
+
# Connection class methods
|
|
122
|
+
#
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Sends connection.open to the server.
|
|
126
|
+
#
|
|
127
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.7)
|
|
128
|
+
def open(vhost = "/")
|
|
129
|
+
@client.send Protocol::Connection::Open.encode(vhost)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# Sends connection.close to the server.
|
|
134
|
+
#
|
|
135
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.9)
|
|
136
|
+
def close(reply_code = 200, reply_text = "Goodbye", class_id = 0, method_id = 0)
|
|
137
|
+
@client.send Protocol::Connection::Close.encode(reply_code, reply_text, class_id, method_id)
|
|
138
|
+
closing!
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def reset_state!
|
|
143
|
+
end # reset_state!
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
#
|
|
148
|
+
# Implementation
|
|
149
|
+
#
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Handles connection.open
|
|
153
|
+
#
|
|
154
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.1.)
|
|
155
|
+
def start_ok(method)
|
|
156
|
+
@server_properties = method.server_properties
|
|
157
|
+
|
|
158
|
+
# it's not clear whether we should transition to :opening state here
|
|
159
|
+
# or in #open but in case authentication fails, it would be strange to have
|
|
160
|
+
# @status undefined. So lets do this. MK.
|
|
161
|
+
opening!
|
|
162
|
+
@client.send Protocol::Connection::StartOk.encode(@client_properties, @mechanism, @response, @locale)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# Handles connection.open-Ok
|
|
167
|
+
#
|
|
168
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.8.)
|
|
169
|
+
def handle_open_ok(method)
|
|
170
|
+
@known_hosts = method.known_hosts
|
|
171
|
+
|
|
172
|
+
opened!
|
|
173
|
+
# async adapters need this callback to proceed with
|
|
174
|
+
# Adapter.connect block evaluation
|
|
175
|
+
@client.connection_successful if @client.respond_to?(:connection_successful)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Handles connection.tune-ok
|
|
179
|
+
#
|
|
180
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.6)
|
|
181
|
+
def handle_tune(method)
|
|
182
|
+
@channel_max = method.channel_max
|
|
183
|
+
@frame_max = method.frame_max
|
|
184
|
+
@heartbeat_interval = @client.heartbeat_interval || method.heartbeat
|
|
185
|
+
|
|
186
|
+
@client.send Protocol::Connection::TuneOk.encode(@channel_max, [settings[:frame_max], @frame_max].min, @heartbeat_interval)
|
|
187
|
+
end # handle_tune(method)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Handles connection.close
|
|
191
|
+
#
|
|
192
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.5.2.9)
|
|
193
|
+
def handle_close(method)
|
|
194
|
+
self.channels.each { |key, value| value.on_connection_interruption }
|
|
195
|
+
|
|
196
|
+
closed!
|
|
197
|
+
# TODO: use proper exception class, provide protocol class (we know method.class_id and method.method_id) as well!
|
|
198
|
+
self.error RuntimeError.new(method.reply_text)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Handles connection.close-ok
|
|
202
|
+
#
|
|
203
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.4.2.10)
|
|
204
|
+
def handle_close_ok(method)
|
|
205
|
+
closed!
|
|
206
|
+
@client.disconnection_successful
|
|
207
|
+
end # handle_close_ok(method)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def on_connection_interruption
|
|
211
|
+
@channels.each { |n, c| c.on_connection_interruption }
|
|
212
|
+
end # on_connection_interruption
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
#
|
|
217
|
+
# Handlers
|
|
218
|
+
#
|
|
219
|
+
|
|
220
|
+
self.handle(Protocol::Connection::Start) do |client, frame|
|
|
221
|
+
client.connection.start_ok(frame.decode_payload)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
self.handle(Protocol::Connection::Tune) do |client, frame|
|
|
225
|
+
client.connection.handle_tune(frame.decode_payload)
|
|
226
|
+
|
|
227
|
+
client.connection.open(client.settings[:vhost] || "/")
|
|
228
|
+
client.open_successful
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
self.handle(Protocol::Connection::OpenOk) do |client, frame|
|
|
232
|
+
client.connection.handle_open_ok(frame.decode_payload)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
self.handle(Protocol::Connection::Close) do |client, frame|
|
|
236
|
+
client.connection.handle_close(frame.decode_payload)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
self.handle(Protocol::Connection::CloseOk) do |client, frame|
|
|
240
|
+
client.connection.handle_close_ok(frame.decode_payload)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
end # Connection
|
|
245
|
+
end # Client
|
|
246
|
+
end # AMQ
|