amqp-client 0.1.0 → 0.2.3

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.
@@ -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 #{id} already closed"
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
@@ -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
- 4 + 4 + 6 + 4 + response.bytesize + 1, # frame size
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
- 0, # client props
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 queue_declare(id, name, passive, durable, exclusive, auto_delete)
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
- frame_size = 2 + 2 + 2 + 1 + name.bytesize + 1 + 4
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
- 0, # arguments
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 basic_get(id, queue_name, no_ack)
132
- frame_size = 2 + 2 + 2 + 1 + queue_name.bytesize + 2 + 2
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
- queue_name.bytesize, queue_name,
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
- frame_size = 2 + 2 + 8 + 2
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
- 0, # properties
343
+ props, # properties
172
344
  206 # frame end
173
- ].pack("C S> L> S> S> Q> S> C")
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