amqp-client 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +19 -2
- data/.gitignore +1 -0
- data/.rubocop_todo.yml +39 -33
- data/CHANGELOG.md +5 -0
- data/README.md +31 -0
- data/lib/amqp/client.rb +145 -77
- data/lib/amqp/client/channel.rb +239 -32
- data/lib/amqp/client/connection.rb +293 -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 +4 -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
|