amqp-client 0.1.0 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +19 -2
- data/.gitignore +1 -0
- data/.rubocop_todo.yml +39 -33
- data/CHANGELOG.md +18 -0
- data/README.md +31 -0
- data/lib/amqp-client.rb +3 -0
- data/lib/amqp/client.rb +145 -77
- data/lib/amqp/client/channel.rb +239 -32
- data/lib/amqp/client/connection.rb +298 -42
- data/lib/amqp/client/errors.rb +7 -2
- data/lib/amqp/client/frames.rb +361 -16
- data/lib/amqp/client/message.rb +11 -1
- data/lib/amqp/client/properties.rb +201 -0
- data/lib/amqp/client/table.rb +123 -0
- data/lib/amqp/client/version.rb +1 -1
- metadata +5 -3
- data/Gemfile.lock +0 -42
data/lib/amqp/client/errors.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AMQP
|
2
4
|
class Client
|
3
5
|
class Error < StandardError; end
|
@@ -16,21 +18,24 @@ module AMQP
|
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
21
|
+
# Should never be raised as we support all offical frame types
|
19
22
|
class UnsupportedFrameType < Error
|
20
23
|
def initialize(type)
|
21
24
|
super "Unsupported frame type '#{type}'"
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
28
|
+
# Raised if a frame is received but not implemented
|
25
29
|
class UnsupportedMethodFrame < Error
|
26
30
|
def initialize(class_id, method_id)
|
27
31
|
super "Unsupported class/method: #{class_id} #{method_id}"
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
35
|
+
# Raised if channel is already closed
|
31
36
|
class ChannelClosedError < Error
|
32
|
-
def initialize(id)
|
33
|
-
super "Channel
|
37
|
+
def initialize(id, code, reason, classid = 0, methodid = 0)
|
38
|
+
super "Channel[#{id}] closed (#{code}) #{reason} (#{classid}/#{methodid})"
|
34
39
|
end
|
35
40
|
end
|
36
41
|
end
|
data/lib/amqp/client/frames.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "./properties"
|
4
|
+
require_relative "./table"
|
5
|
+
|
3
6
|
module AMQP
|
4
7
|
# Generate binary data for different frames
|
5
8
|
# Each frame type implemented as a method
|
@@ -7,19 +10,20 @@ module AMQP
|
|
7
10
|
module FrameBytes
|
8
11
|
module_function
|
9
12
|
|
10
|
-
def connection_start_ok(response)
|
13
|
+
def connection_start_ok(response, properties)
|
14
|
+
prop_tbl = Table.encode(properties)
|
11
15
|
[
|
12
16
|
1, # type: method
|
13
17
|
0, # channel id
|
14
|
-
|
18
|
+
2 + 2 + 4 + prop_tbl.bytesize + 6 + 4 + response.bytesize + 1, # frame size
|
15
19
|
10, # class id
|
16
20
|
11, # method id
|
17
|
-
|
21
|
+
prop_tbl.bytesize, prop_tbl, # client props
|
18
22
|
5, "PLAIN", # mechanism
|
19
23
|
response.bytesize, response,
|
20
24
|
0, "", # locale
|
21
25
|
206 # frame end
|
22
|
-
].pack("C S> L> S> S> L> Ca* L>a* Ca* C")
|
26
|
+
].pack("C S> L> S> S> L>a* Ca* L>a* Ca* C")
|
23
27
|
end
|
24
28
|
|
25
29
|
def connection_tune_ok(channel_max, frame_max, heartbeat)
|
@@ -105,7 +109,101 @@ module AMQP
|
|
105
109
|
].pack("C S> L> S> S> S> Ca* S> S> C")
|
106
110
|
end
|
107
111
|
|
108
|
-
def
|
112
|
+
def channel_close_ok(id)
|
113
|
+
[
|
114
|
+
1, # type: method
|
115
|
+
id, # channel id
|
116
|
+
4, # frame size
|
117
|
+
20, # class: channel
|
118
|
+
41, # method: close-ok
|
119
|
+
206 # frame end
|
120
|
+
].pack("C S> L> S> S> C")
|
121
|
+
end
|
122
|
+
|
123
|
+
def exchange_declare(id, name, type, passive, durable, auto_delete, internal, arguments)
|
124
|
+
no_wait = false
|
125
|
+
bits = 0
|
126
|
+
bits |= (1 << 0) if passive
|
127
|
+
bits |= (1 << 1) if durable
|
128
|
+
bits |= (1 << 2) if auto_delete
|
129
|
+
bits |= (1 << 3) if internal
|
130
|
+
bits |= (1 << 4) if no_wait
|
131
|
+
tbl = Table.encode(arguments)
|
132
|
+
frame_size = 2 + 2 + 2 + 1 + name.bytesize + 1 + type.bytesize + 1 + 4 + tbl.bytesize
|
133
|
+
[
|
134
|
+
1, # type: method
|
135
|
+
id, # channel id
|
136
|
+
frame_size, # frame size
|
137
|
+
40, # class: exchange
|
138
|
+
10, # method: declare
|
139
|
+
0, # reserved1
|
140
|
+
name.bytesize, name,
|
141
|
+
type.bytesize, type,
|
142
|
+
bits,
|
143
|
+
tbl.bytesize, tbl, # arguments
|
144
|
+
206 # frame end
|
145
|
+
].pack("C S> L> S> S> S> Ca* Ca* C L>a* C")
|
146
|
+
end
|
147
|
+
|
148
|
+
def exchange_delete(id, name, if_unused, no_wait)
|
149
|
+
bits = 0
|
150
|
+
bits |= (1 << 0) if if_unused
|
151
|
+
bits |= (1 << 1) if no_wait
|
152
|
+
frame_size = 2 + 2 + 2 + 1 + name.bytesize + 1
|
153
|
+
[
|
154
|
+
1, # type: method
|
155
|
+
id, # channel id
|
156
|
+
frame_size, # frame size
|
157
|
+
40, # class: exchange
|
158
|
+
20, # method: delete
|
159
|
+
0, # reserved1
|
160
|
+
name.bytesize, name,
|
161
|
+
bits,
|
162
|
+
206 # frame end
|
163
|
+
].pack("C S> L> S> S> S> Ca* C C")
|
164
|
+
end
|
165
|
+
|
166
|
+
def exchange_bind(id, destination, source, binding_key, no_wait, arguments)
|
167
|
+
tbl = Table.encode(arguments)
|
168
|
+
frame_size = 2 + 2 + 2 + 1 + destination.bytesize + 1 + source.bytesize + 1 +
|
169
|
+
binding_key.bytesize + 1 + 4 + tbl.bytesize
|
170
|
+
[
|
171
|
+
1, # type: method
|
172
|
+
id, # channel id
|
173
|
+
frame_size, # frame size
|
174
|
+
40, # class: exchange
|
175
|
+
30, # method: bind
|
176
|
+
0, # reserved1
|
177
|
+
destination.bytesize, destination,
|
178
|
+
source.bytesize, source,
|
179
|
+
binding_key.bytesize, binding_key,
|
180
|
+
no_wait ? 1 : 0,
|
181
|
+
tbl.bytesize, tbl, # arguments
|
182
|
+
206 # frame end
|
183
|
+
].pack("C S> L> S> S> S> Ca* Ca* Ca* C L>a* C")
|
184
|
+
end
|
185
|
+
|
186
|
+
def exchange_unbind(id, destination, source, binding_key, no_wait, arguments)
|
187
|
+
tbl = Table.encode(arguments)
|
188
|
+
frame_size = 2 + 2 + 2 + 1 + destination.bytesize + 1 + source.bytesize + 1 +
|
189
|
+
binding_key.bytesize + 1 + 4 + tbl.bytesize
|
190
|
+
[
|
191
|
+
1, # type: method
|
192
|
+
id, # channel id
|
193
|
+
frame_size, # frame size
|
194
|
+
40, # class: exchange
|
195
|
+
40, # method: unbind
|
196
|
+
0, # reserved1
|
197
|
+
destination.bytesize, destination,
|
198
|
+
source.bytesize, source,
|
199
|
+
binding_key.bytesize, binding_key,
|
200
|
+
no_wait ? 1 : 0,
|
201
|
+
tbl.bytesize, tbl, # arguments
|
202
|
+
206 # frame end
|
203
|
+
].pack("C S> L> S> S> S> Ca* Ca* Ca* C L>a* C")
|
204
|
+
end
|
205
|
+
|
206
|
+
def queue_declare(id, name, passive, durable, exclusive, auto_delete, arguments)
|
109
207
|
no_wait = false
|
110
208
|
bits = 0
|
111
209
|
bits |= (1 << 0) if passive
|
@@ -113,7 +211,8 @@ module AMQP
|
|
113
211
|
bits |= (1 << 2) if exclusive
|
114
212
|
bits |= (1 << 3) if auto_delete
|
115
213
|
bits |= (1 << 4) if no_wait
|
116
|
-
|
214
|
+
tbl = Table.encode(arguments)
|
215
|
+
frame_size = 2 + 2 + 2 + 1 + name.bytesize + 1 + 4 + tbl.bytesize
|
117
216
|
[
|
118
217
|
1, # type: method
|
119
218
|
id, # channel id
|
@@ -123,13 +222,85 @@ module AMQP
|
|
123
222
|
0, # reserved1
|
124
223
|
name.bytesize, name,
|
125
224
|
bits,
|
126
|
-
|
225
|
+
tbl.bytesize, tbl, # arguments
|
226
|
+
206 # frame end
|
227
|
+
].pack("C S> L> S> S> S> Ca* C L>a* C")
|
228
|
+
end
|
229
|
+
|
230
|
+
def queue_delete(id, name, if_unused, if_empty, no_wait)
|
231
|
+
bits = 0
|
232
|
+
bits |= (1 << 0) if if_unused
|
233
|
+
bits |= (1 << 1) if if_empty
|
234
|
+
bits |= (1 << 2) if no_wait
|
235
|
+
frame_size = 2 + 2 + 2 + 1 + name.bytesize + 1
|
236
|
+
[
|
237
|
+
1, # type: method
|
238
|
+
id, # channel id
|
239
|
+
frame_size, # frame size
|
240
|
+
50, # class: queue
|
241
|
+
40, # method: declare
|
242
|
+
0, # reserved1
|
243
|
+
name.bytesize, name,
|
244
|
+
bits,
|
245
|
+
206 # frame end
|
246
|
+
].pack("C S> L> S> S> S> Ca* C C")
|
247
|
+
end
|
248
|
+
|
249
|
+
def queue_bind(id, queue, exchange, binding_key, no_wait, arguments)
|
250
|
+
tbl = Table.encode(arguments)
|
251
|
+
frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1 + exchange.bytesize + 1 +
|
252
|
+
binding_key.bytesize + 1 + 4 + tbl.bytesize
|
253
|
+
[
|
254
|
+
1, # type: method
|
255
|
+
id, # channel id
|
256
|
+
frame_size, # frame size
|
257
|
+
50, # class: queue
|
258
|
+
20, # method: bind
|
259
|
+
0, # reserved1
|
260
|
+
queue.bytesize, queue,
|
261
|
+
exchange.bytesize, exchange,
|
262
|
+
binding_key.bytesize, binding_key,
|
263
|
+
no_wait ? 1 : 0,
|
264
|
+
tbl.bytesize, tbl, # arguments
|
127
265
|
206 # frame end
|
128
|
-
].pack("C S> L> S> S> S> Ca* C L> C")
|
266
|
+
].pack("C S> L> S> S> S> Ca* Ca* Ca* C L>a* C")
|
129
267
|
end
|
130
268
|
|
131
|
-
def
|
132
|
-
|
269
|
+
def queue_unbind(id, queue, exchange, binding_key, arguments)
|
270
|
+
tbl = Table.encode(arguments)
|
271
|
+
frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1 + exchange.bytesize + 1 + binding_key.bytesize + 4 + tbl.bytesize
|
272
|
+
[
|
273
|
+
1, # type: method
|
274
|
+
id, # channel id
|
275
|
+
frame_size, # frame size
|
276
|
+
50, # class: queue
|
277
|
+
50, # method: unbind
|
278
|
+
0, # reserved1
|
279
|
+
queue.bytesize, queue,
|
280
|
+
exchange.bytesize, exchange,
|
281
|
+
binding_key.bytesize, binding_key,
|
282
|
+
tbl.bytesize, tbl, # arguments
|
283
|
+
206 # frame end
|
284
|
+
].pack("C S> L> S> S> S> Ca* Ca* Ca* L>a* C")
|
285
|
+
end
|
286
|
+
|
287
|
+
def queue_purge(id, queue, no_wait)
|
288
|
+
frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1
|
289
|
+
[
|
290
|
+
1, # type: method
|
291
|
+
id, # channel id
|
292
|
+
frame_size, # frame size
|
293
|
+
50, # class: queue
|
294
|
+
30, # method: purge
|
295
|
+
0, # reserved1
|
296
|
+
queue.bytesize, queue,
|
297
|
+
no_wait ? 1 : 0,
|
298
|
+
206 # frame end
|
299
|
+
].pack("C S> L> S> S> S> Ca* C C")
|
300
|
+
end
|
301
|
+
|
302
|
+
def basic_get(id, queue, no_ack)
|
303
|
+
frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1
|
133
304
|
[
|
134
305
|
1, # type: method
|
135
306
|
id, # channel id
|
@@ -137,13 +308,13 @@ module AMQP
|
|
137
308
|
60, # class: basic
|
138
309
|
70, # method: get
|
139
310
|
0, # reserved1
|
140
|
-
|
311
|
+
queue.bytesize, queue,
|
141
312
|
no_ack ? 1 : 0,
|
142
313
|
206 # frame end
|
143
314
|
].pack("C S> L> S> S> S> Ca* C C")
|
144
315
|
end
|
145
316
|
|
146
|
-
def basic_publish(id, exchange, routing_key)
|
317
|
+
def basic_publish(id, exchange, routing_key, mandatory)
|
147
318
|
frame_size = 2 + 2 + 2 + 1 + exchange.bytesize + 1 + routing_key.bytesize + 1
|
148
319
|
[
|
149
320
|
1, # type: method
|
@@ -154,13 +325,14 @@ module AMQP
|
|
154
325
|
0, # reserved1
|
155
326
|
exchange.bytesize, exchange,
|
156
327
|
routing_key.bytesize, routing_key,
|
157
|
-
0, # bits, mandatory/immediate
|
328
|
+
mandatory ? 1 : 0, # bits, mandatory/immediate
|
158
329
|
206 # frame end
|
159
330
|
].pack("C S> L> S> S> S> Ca* Ca* C C")
|
160
331
|
end
|
161
332
|
|
162
333
|
def header(id, body_size, properties)
|
163
|
-
|
334
|
+
props = Properties.new(**properties).encode
|
335
|
+
frame_size = 2 + 2 + 8 + props.bytesize
|
164
336
|
[
|
165
337
|
2, # type: header
|
166
338
|
id, # channel id
|
@@ -168,9 +340,9 @@ module AMQP
|
|
168
340
|
60, # class: basic
|
169
341
|
0, # weight
|
170
342
|
body_size,
|
171
|
-
|
343
|
+
props, # properties
|
172
344
|
206 # frame end
|
173
|
-
].pack("C S> L> S> S> Q>
|
345
|
+
].pack("C S> L> S> S> Q> a* C")
|
174
346
|
end
|
175
347
|
|
176
348
|
def body(id, body_part)
|
@@ -182,5 +354,178 @@ module AMQP
|
|
182
354
|
206 # frame end
|
183
355
|
].pack("C S> L> a* C")
|
184
356
|
end
|
357
|
+
|
358
|
+
def basic_consume(id, queue, tag, no_ack, exclusive, arguments)
|
359
|
+
no_local = false
|
360
|
+
no_wait = false
|
361
|
+
bits = 0
|
362
|
+
bits |= (1 << 0) if no_local
|
363
|
+
bits |= (1 << 1) if no_ack
|
364
|
+
bits |= (1 << 2) if exclusive
|
365
|
+
bits |= (1 << 3) if no_wait
|
366
|
+
tbl = Table.encode(arguments)
|
367
|
+
frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1 + tag.bytesize + 1 + 4 + tbl.bytesize
|
368
|
+
[
|
369
|
+
1, # type: method
|
370
|
+
id, # channel id
|
371
|
+
frame_size, # frame size
|
372
|
+
60, # class: basic
|
373
|
+
20, # method: consume
|
374
|
+
0, # reserved1
|
375
|
+
queue.bytesize, queue,
|
376
|
+
tag.bytesize, tag,
|
377
|
+
bits, # bits
|
378
|
+
tbl.bytesize, tbl, # arguments
|
379
|
+
206 # frame end
|
380
|
+
].pack("C S> L> S> S> S> Ca* Ca* C L>a* C")
|
381
|
+
end
|
382
|
+
|
383
|
+
def basic_cancel(id, consumer_tag, no_wait: false)
|
384
|
+
frame_size = 2 + 2 + 1 + consumer_tag.bytesize + 1
|
385
|
+
[
|
386
|
+
1, # type: method
|
387
|
+
id, # channel id
|
388
|
+
frame_size, # frame size
|
389
|
+
60, # class: basic
|
390
|
+
30, # method: cancel
|
391
|
+
consumer_tag.bytesize, consumer_tag,
|
392
|
+
no_wait ? 1 : 0,
|
393
|
+
206 # frame end
|
394
|
+
].pack("C S> L> S> S> Ca* C C")
|
395
|
+
end
|
396
|
+
|
397
|
+
def basic_cancel_ok(id, consumer_tag)
|
398
|
+
frame_size = 2 + 2 + 1 + consumer_tag.bytesize + 1
|
399
|
+
[
|
400
|
+
1, # type: method
|
401
|
+
id, # channel id
|
402
|
+
frame_size, # frame size
|
403
|
+
60, # class: basic
|
404
|
+
31, # method: cancel-ok
|
405
|
+
consumer_tag.bytesize, consumer_tag,
|
406
|
+
206 # frame end
|
407
|
+
].pack("C S> L> S> S> Ca* C")
|
408
|
+
end
|
409
|
+
|
410
|
+
def basic_ack(id, delivery_tag, multiple)
|
411
|
+
frame_size = 2 + 2 + 8 + 1
|
412
|
+
[
|
413
|
+
1, # type: method
|
414
|
+
id, # channel id
|
415
|
+
frame_size, # frame size
|
416
|
+
60, # class: basic
|
417
|
+
80, # method: ack
|
418
|
+
delivery_tag,
|
419
|
+
multiple ? 1 : 0,
|
420
|
+
206 # frame end
|
421
|
+
].pack("C S> L> S> S> Q> C C")
|
422
|
+
end
|
423
|
+
|
424
|
+
def basic_nack(id, delivery_tag, multiple, requeue)
|
425
|
+
bits = 0
|
426
|
+
bits |= (1 << 0) if multiple
|
427
|
+
bits |= (1 << 1) if requeue
|
428
|
+
frame_size = 2 + 2 + 8 + 1
|
429
|
+
[
|
430
|
+
1, # type: method
|
431
|
+
id, # channel id
|
432
|
+
frame_size, # frame size
|
433
|
+
60, # class: basic
|
434
|
+
120, # method: nack
|
435
|
+
delivery_tag,
|
436
|
+
bits,
|
437
|
+
206 # frame end
|
438
|
+
].pack("C S> L> S> S> Q> C C")
|
439
|
+
end
|
440
|
+
|
441
|
+
def basic_reject(id, delivery_tag, requeue)
|
442
|
+
frame_size = 2 + 2 + 8 + 1
|
443
|
+
[
|
444
|
+
1, # type: method
|
445
|
+
id, # channel id
|
446
|
+
frame_size, # frame size
|
447
|
+
60, # class: basic
|
448
|
+
90, # method: reject
|
449
|
+
delivery_tag,
|
450
|
+
requeue ? 1 : 0,
|
451
|
+
206 # frame end
|
452
|
+
].pack("C S> L> S> S> Q> C C")
|
453
|
+
end
|
454
|
+
|
455
|
+
def basic_qos(id, prefetch_size, prefetch_count, global)
|
456
|
+
frame_size = 2 + 2 + 4 + 2 + 1
|
457
|
+
[
|
458
|
+
1, # type: method
|
459
|
+
id, # channel id
|
460
|
+
frame_size, # frame size
|
461
|
+
60, # class: basic
|
462
|
+
10, # method: qos
|
463
|
+
prefetch_size,
|
464
|
+
prefetch_count,
|
465
|
+
global ? 1 : 0,
|
466
|
+
206 # frame end
|
467
|
+
].pack("C S> L> S> S> L> S> C C")
|
468
|
+
end
|
469
|
+
|
470
|
+
def basic_recover(id, requeue)
|
471
|
+
frame_size = 2 + 2 + 1
|
472
|
+
[
|
473
|
+
1, # type: method
|
474
|
+
id, # channel id
|
475
|
+
frame_size, # frame size
|
476
|
+
60, # class: basic
|
477
|
+
110, # method: recover
|
478
|
+
requeue ? 1 : 0,
|
479
|
+
206 # frame end
|
480
|
+
].pack("C S> L> S> S> C C")
|
481
|
+
end
|
482
|
+
|
483
|
+
def confirm_select(id, no_wait)
|
484
|
+
[
|
485
|
+
1, # type: method
|
486
|
+
id, # channel id
|
487
|
+
5, # frame size
|
488
|
+
85, # class: confirm
|
489
|
+
10, # method: select
|
490
|
+
no_wait ? 1 : 0,
|
491
|
+
206 # frame end
|
492
|
+
].pack("C S> L> S> S> C C")
|
493
|
+
end
|
494
|
+
|
495
|
+
def tx_select(id)
|
496
|
+
frame_size = 2 + 2
|
497
|
+
[
|
498
|
+
1, # type: method
|
499
|
+
id, # channel id
|
500
|
+
frame_size, # frame size
|
501
|
+
90, # class: tx
|
502
|
+
10, # method: select
|
503
|
+
206 # frame end
|
504
|
+
].pack("C S> L> S> S> C")
|
505
|
+
end
|
506
|
+
|
507
|
+
def tx_commit(id)
|
508
|
+
frame_size = 2 + 2
|
509
|
+
[
|
510
|
+
1, # type: method
|
511
|
+
id, # channel id
|
512
|
+
frame_size, # frame size
|
513
|
+
90, # class: tx
|
514
|
+
20, # method: commit
|
515
|
+
206 # frame end
|
516
|
+
].pack("C S> L> S> S> C")
|
517
|
+
end
|
518
|
+
|
519
|
+
def tx_rollback(id)
|
520
|
+
frame_size = 2 + 2
|
521
|
+
[
|
522
|
+
1, # type: method
|
523
|
+
id, # channel id
|
524
|
+
frame_size, # frame size
|
525
|
+
90, # class: tx
|
526
|
+
30, # method: rollback
|
527
|
+
206 # frame end
|
528
|
+
].pack("C S> L> S> S> C")
|
529
|
+
end
|
185
530
|
end
|
186
531
|
end
|