amqp-client 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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