amqp-client 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,40 +2,61 @@
2
2
 
3
3
  module AMQP
4
4
  class Client
5
- class Error < StandardError; end
5
+ # All errors raised inherit from this class
6
+ class Error < StandardError
7
+ # Raised when a frame that wasn't expected arrives
8
+ class UnexpectedFrame < Error
9
+ def initialize(expected, actual)
10
+ super "Expected frame type '#{expected}' but got '#{actual}'"
11
+ end
12
+ end
6
13
 
7
- # Raised when a frame that wasn't expected arrives
8
- class UnexpectedFrame < Error
9
- def initialize(expected, actual)
10
- super "Expected frame type '#{expected}' but got '#{actual}'"
14
+ # Raised when a frame doesn't end with 206
15
+ class UnexpectedFrameEnd < Error
16
+ def initialize(actual)
17
+ super "Expected frame end 206 but got '#{actual}'"
18
+ end
11
19
  end
12
- end
13
20
 
14
- # Raised when a frame doesn't end with 206
15
- class UnexpectedFrameEnd < Error
16
- def initialize(actual)
17
- super "Expected frame end 206 but got '#{actual}'"
21
+ # Should never be raised as we support all official frame types
22
+ class UnsupportedFrameType < Error
23
+ def initialize(type)
24
+ super "Unsupported frame type '#{type}'"
25
+ end
18
26
  end
19
- end
20
27
 
21
- # Should never be raised as we support all offical frame types
22
- class UnsupportedFrameType < Error
23
- def initialize(type)
24
- super "Unsupported frame type '#{type}'"
28
+ # Raised if a frame is received but not implemented
29
+ class UnsupportedMethodFrame < Error
30
+ def initialize(class_id, method_id)
31
+ super "Unsupported class/method: #{class_id} #{method_id}"
32
+ end
25
33
  end
26
- end
27
34
 
28
- # Raised if a frame is received but not implemented
29
- class UnsupportedMethodFrame < Error
30
- def initialize(class_id, method_id)
31
- super "Unsupported class/method: #{class_id} #{method_id}"
35
+ # Depending on close level a ConnectionClosed or ChannelClosed error is returned
36
+ class Closed < Error
37
+ def self.new(id, level, code, reason, classid = 0, methodid = 0)
38
+ case level
39
+ when :connection
40
+ ConnectionClosed.new(code, reason, classid, methodid)
41
+ when :channel
42
+ ChannelClosed.new(id, code, reason, classid, methodid)
43
+ else raise ArgumentError, "invalid level '#{level}'"
44
+ end
45
+ end
46
+ end
47
+
48
+ # Raised if channel is already closed
49
+ class ChannelClosed < Error
50
+ def initialize(id, code, reason, classid = 0, methodid = 0)
51
+ super "Channel[#{id}] closed (#{code}) #{reason} (#{classid}/#{methodid})"
52
+ end
32
53
  end
33
- end
34
54
 
35
- # Raised if channel is already closed
36
- class ChannelClosedError < Error
37
- def initialize(id, code, reason, classid = 0, methodid = 0)
38
- super "Channel[#{id}] closed (#{code}) #{reason} (#{classid}/#{methodid})"
55
+ # Raised if connection is unexpectedly closed
56
+ class ConnectionClosed < Error
57
+ def initialize(code, reason, classid = 0, methodid = 0)
58
+ super "Connection closed (#{code}) #{reason} (#{classid}/#{methodid})"
59
+ end
39
60
  end
40
61
  end
41
62
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AMQP
4
+ class Client
5
+ # High level representation of an exchange
6
+ class Exchange
7
+ # Should only be initialized from the Client
8
+ # @api private
9
+ def initialize(client, name)
10
+ @client = client
11
+ @name = name
12
+ end
13
+
14
+ # Publish to the exchange
15
+ # @param body [String] The message body
16
+ # @param routing_key [String] The routing key of the message,
17
+ # the exchange may use this when routing the message to bound queues
18
+ # @param properties [Properties]
19
+ # @option properties [String] content_type Content type of the message body
20
+ # @option properties [String] content_encoding Content encoding of the body
21
+ # @option properties [Hash<String, Object>] headers Custom headers
22
+ # @option properties [Integer] delivery_mode 2 for persisted message, transient messages for all other values
23
+ # @option properties [Integer] priority A priority of the message (between 0 and 255)
24
+ # @option properties [Integer] correlation_id A correlation id, most often used used for RPC communication
25
+ # @option properties [String] reply_to Queue to reply RPC responses to
26
+ # @option properties [Integer, String] expiration Number of seconds the message will stay in the queue
27
+ # @option properties [String] message_id Can be used to uniquely identify the message, e.g. for deduplication
28
+ # @option properties [Date] timestamp Often used for the time the message was originally generated
29
+ # @option properties [String] type Can indicate what kind of message this is
30
+ # @option properties [String] user_id Can be used to verify that this is the user that published the message
31
+ # @option properties [String] app_id Can be used to indicates which app that generated the message
32
+ # @return [Exchange] self
33
+ def publish(body, routing_key, **properties)
34
+ @client.publish(body, @name, routing_key, **properties)
35
+ self
36
+ end
37
+
38
+ # Bind to another exchange
39
+ # @param exchange [String] Name of the exchange to bind to
40
+ # @param binding_key [String] Binding key on which messages that match might be routed (depending on exchange type)
41
+ # @param arguments [Hash] Message headers to match on (only relevant for header exchanges)
42
+ # @return [Exchange] self
43
+ def bind(exchange, binding_key, arguments: {})
44
+ @client.exchange_bind(@name, exchange, binding_key, arguments: arguments)
45
+ self
46
+ end
47
+
48
+ # Unbind from another exchange
49
+ # @param exchange [String] Name of the exchange to unbind from
50
+ # @param binding_key [String] Binding key which the queue is bound to the exchange with
51
+ # @param arguments [Hash] Arguments matching the binding that's being removed
52
+ # @return [Exchange] self
53
+ def unbind(exchange, binding_key, arguments: {})
54
+ @client.exchange_unbind(@name, exchange, binding_key, arguments: arguments)
55
+ self
56
+ end
57
+
58
+ # Delete the exchange
59
+ # @return [nil]
60
+ def delete
61
+ @client.delete_exchange(@name)
62
+ nil
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,533 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./properties"
4
+ require_relative "./table"
5
+
6
+ module AMQP
7
+ class Client
8
+ # Generate binary data for different frames
9
+ # Each frame type implemented as a method
10
+ # Having a class for each frame type is more expensive in terms of CPU and memory
11
+ # @api private
12
+ module FrameBytes
13
+ def self.connection_start_ok(response, properties)
14
+ prop_tbl = Table.encode(properties)
15
+ [
16
+ 1, # type: method
17
+ 0, # channel id
18
+ 2 + 2 + 4 + prop_tbl.bytesize + 6 + 4 + response.bytesize + 1, # frame size
19
+ 10, # class id
20
+ 11, # method id
21
+ prop_tbl.bytesize, prop_tbl, # client props
22
+ 5, "PLAIN", # mechanism
23
+ response.bytesize, response,
24
+ 0, "", # locale
25
+ 206 # frame end
26
+ ].pack("C S> L> S> S> L>a* Ca* L>a* Ca* C")
27
+ end
28
+
29
+ def self.connection_tune_ok(channel_max, frame_max, heartbeat)
30
+ [
31
+ 1, # type: method
32
+ 0, # channel id
33
+ 12, # frame size
34
+ 10, # class: connection
35
+ 31, # method: tune-ok
36
+ channel_max,
37
+ frame_max,
38
+ heartbeat,
39
+ 206 # frame end
40
+ ].pack("CS>L>S>S>S>L>S>C")
41
+ end
42
+
43
+ def self.connection_open(vhost)
44
+ [
45
+ 1, # type: method
46
+ 0, # channel id
47
+ 2 + 2 + 1 + vhost.bytesize + 1 + 1, # frame_size
48
+ 10, # class: connection
49
+ 40, # method: open
50
+ vhost.bytesize, vhost,
51
+ 0, # reserved1
52
+ 0, # reserved2
53
+ 206 # frame end
54
+ ].pack("C S> L> S> S> Ca* CCC")
55
+ end
56
+
57
+ def self.connection_close(code, reason)
58
+ frame_size = 2 + 2 + 2 + 1 + reason.bytesize + 2 + 2
59
+ [
60
+ 1, # type: method
61
+ 0, # channel id
62
+ frame_size, # frame size
63
+ 10, # class: connection
64
+ 50, # method: close
65
+ code,
66
+ reason.bytesize, reason,
67
+ 0, # error class id
68
+ 0, # error method id
69
+ 206 # frame end
70
+ ].pack("C S> L> S> S> S> Ca* S> S> C")
71
+ end
72
+
73
+ def self.connection_close_ok
74
+ [
75
+ 1, # type: method
76
+ 0, # channel id
77
+ 4, # frame size
78
+ 10, # class: connection
79
+ 51, # method: close-ok
80
+ 206 # frame end
81
+ ].pack("C S> L> S> S> C")
82
+ end
83
+
84
+ def self.channel_open(id)
85
+ [
86
+ 1, # type: method
87
+ id, # channel id
88
+ 5, # frame size
89
+ 20, # class: channel
90
+ 10, # method: open
91
+ 0, # reserved1
92
+ 206 # frame end
93
+ ].pack("C S> L> S> S> C C")
94
+ end
95
+
96
+ def self.channel_close(id, reason, code)
97
+ frame_size = 2 + 2 + 2 + 1 + reason.bytesize + 2 + 2
98
+ [
99
+ 1, # type: method
100
+ id, # channel id
101
+ frame_size, # frame size
102
+ 20, # class: channel
103
+ 40, # method: close
104
+ code,
105
+ reason.bytesize, reason,
106
+ 0, # error class id
107
+ 0, # error method id
108
+ 206 # frame end
109
+ ].pack("C S> L> S> S> S> Ca* S> S> C")
110
+ end
111
+
112
+ def self.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 self.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 self.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 self.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 self.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 self.queue_declare(id, name, passive, durable, exclusive, auto_delete, arguments)
207
+ no_wait = false
208
+ bits = 0
209
+ bits |= (1 << 0) if passive
210
+ bits |= (1 << 1) if durable
211
+ bits |= (1 << 2) if exclusive
212
+ bits |= (1 << 3) if auto_delete
213
+ bits |= (1 << 4) if no_wait
214
+ tbl = Table.encode(arguments)
215
+ frame_size = 2 + 2 + 2 + 1 + name.bytesize + 1 + 4 + tbl.bytesize
216
+ [
217
+ 1, # type: method
218
+ id, # channel id
219
+ frame_size, # frame size
220
+ 50, # class: queue
221
+ 10, # method: declare
222
+ 0, # reserved1
223
+ name.bytesize, name,
224
+ bits,
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 self.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 self.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
265
+ 206 # frame end
266
+ ].pack("C S> L> S> S> S> Ca* Ca* Ca* C L>a* C")
267
+ end
268
+
269
+ def self.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 +
272
+ binding_key.bytesize + 4 + tbl.bytesize
273
+ [
274
+ 1, # type: method
275
+ id, # channel id
276
+ frame_size, # frame size
277
+ 50, # class: queue
278
+ 50, # method: unbind
279
+ 0, # reserved1
280
+ queue.bytesize, queue,
281
+ exchange.bytesize, exchange,
282
+ binding_key.bytesize, binding_key,
283
+ tbl.bytesize, tbl, # arguments
284
+ 206 # frame end
285
+ ].pack("C S> L> S> S> S> Ca* Ca* Ca* L>a* C")
286
+ end
287
+
288
+ def self.queue_purge(id, queue, no_wait)
289
+ frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1
290
+ [
291
+ 1, # type: method
292
+ id, # channel id
293
+ frame_size, # frame size
294
+ 50, # class: queue
295
+ 30, # method: purge
296
+ 0, # reserved1
297
+ queue.bytesize, queue,
298
+ no_wait ? 1 : 0,
299
+ 206 # frame end
300
+ ].pack("C S> L> S> S> S> Ca* C C")
301
+ end
302
+
303
+ def self.basic_get(id, queue, no_ack)
304
+ frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1
305
+ [
306
+ 1, # type: method
307
+ id, # channel id
308
+ frame_size, # frame size
309
+ 60, # class: basic
310
+ 70, # method: get
311
+ 0, # reserved1
312
+ queue.bytesize, queue,
313
+ no_ack ? 1 : 0,
314
+ 206 # frame end
315
+ ].pack("C S> L> S> S> S> Ca* C C")
316
+ end
317
+
318
+ def self.basic_publish(id, exchange, routing_key, mandatory)
319
+ frame_size = 2 + 2 + 2 + 1 + exchange.bytesize + 1 + routing_key.bytesize + 1
320
+ [
321
+ 1, # type: method
322
+ id, # channel id
323
+ frame_size, # frame size
324
+ 60, # class: basic
325
+ 40, # method: publish
326
+ 0, # reserved1
327
+ exchange.bytesize, exchange,
328
+ routing_key.bytesize, routing_key,
329
+ mandatory ? 1 : 0, # bits, mandatory/immediate
330
+ 206 # frame end
331
+ ].pack("C S> L> S> S> S> Ca* Ca* C C")
332
+ end
333
+
334
+ def self.header(id, body_size, properties)
335
+ props = Properties.encode(properties)
336
+ frame_size = 2 + 2 + 8 + props.bytesize
337
+ [
338
+ 2, # type: header
339
+ id, # channel id
340
+ frame_size, # frame size
341
+ 60, # class: basic
342
+ 0, # weight
343
+ body_size,
344
+ props, # properties
345
+ 206 # frame end
346
+ ].pack("C S> L> S> S> Q> a* C")
347
+ end
348
+
349
+ def self.body(id, body_part)
350
+ [
351
+ 3, # type: body
352
+ id, # channel id
353
+ body_part.bytesize, # frame size
354
+ body_part,
355
+ 206 # frame end
356
+ ].pack("C S> L> a* C")
357
+ end
358
+
359
+ def self.basic_consume(id, queue, tag, no_ack, exclusive, arguments)
360
+ no_local = false
361
+ no_wait = false
362
+ bits = 0
363
+ bits |= (1 << 0) if no_local
364
+ bits |= (1 << 1) if no_ack
365
+ bits |= (1 << 2) if exclusive
366
+ bits |= (1 << 3) if no_wait
367
+ tbl = Table.encode(arguments)
368
+ frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1 + tag.bytesize + 1 + 4 + tbl.bytesize
369
+ [
370
+ 1, # type: method
371
+ id, # channel id
372
+ frame_size, # frame size
373
+ 60, # class: basic
374
+ 20, # method: consume
375
+ 0, # reserved1
376
+ queue.bytesize, queue,
377
+ tag.bytesize, tag,
378
+ bits, # bits
379
+ tbl.bytesize, tbl, # arguments
380
+ 206 # frame end
381
+ ].pack("C S> L> S> S> S> Ca* Ca* C L>a* C")
382
+ end
383
+
384
+ def self.basic_cancel(id, consumer_tag, no_wait: false)
385
+ frame_size = 2 + 2 + 1 + consumer_tag.bytesize + 1
386
+ [
387
+ 1, # type: method
388
+ id, # channel id
389
+ frame_size, # frame size
390
+ 60, # class: basic
391
+ 30, # method: cancel
392
+ consumer_tag.bytesize, consumer_tag,
393
+ no_wait ? 1 : 0,
394
+ 206 # frame end
395
+ ].pack("C S> L> S> S> Ca* C C")
396
+ end
397
+
398
+ def self.basic_cancel_ok(id, consumer_tag)
399
+ frame_size = 2 + 2 + 1 + consumer_tag.bytesize + 1
400
+ [
401
+ 1, # type: method
402
+ id, # channel id
403
+ frame_size, # frame size
404
+ 60, # class: basic
405
+ 31, # method: cancel-ok
406
+ consumer_tag.bytesize, consumer_tag,
407
+ 206 # frame end
408
+ ].pack("C S> L> S> S> Ca* C")
409
+ end
410
+
411
+ def self.basic_ack(id, delivery_tag, multiple)
412
+ frame_size = 2 + 2 + 8 + 1
413
+ [
414
+ 1, # type: method
415
+ id, # channel id
416
+ frame_size, # frame size
417
+ 60, # class: basic
418
+ 80, # method: ack
419
+ delivery_tag,
420
+ multiple ? 1 : 0,
421
+ 206 # frame end
422
+ ].pack("C S> L> S> S> Q> C C")
423
+ end
424
+
425
+ def self.basic_nack(id, delivery_tag, multiple, requeue)
426
+ bits = 0
427
+ bits |= (1 << 0) if multiple
428
+ bits |= (1 << 1) if requeue
429
+ frame_size = 2 + 2 + 8 + 1
430
+ [
431
+ 1, # type: method
432
+ id, # channel id
433
+ frame_size, # frame size
434
+ 60, # class: basic
435
+ 120, # method: nack
436
+ delivery_tag,
437
+ bits,
438
+ 206 # frame end
439
+ ].pack("C S> L> S> S> Q> C C")
440
+ end
441
+
442
+ def self.basic_reject(id, delivery_tag, requeue)
443
+ frame_size = 2 + 2 + 8 + 1
444
+ [
445
+ 1, # type: method
446
+ id, # channel id
447
+ frame_size, # frame size
448
+ 60, # class: basic
449
+ 90, # method: reject
450
+ delivery_tag,
451
+ requeue ? 1 : 0,
452
+ 206 # frame end
453
+ ].pack("C S> L> S> S> Q> C C")
454
+ end
455
+
456
+ def self.basic_qos(id, prefetch_size, prefetch_count, global)
457
+ frame_size = 2 + 2 + 4 + 2 + 1
458
+ [
459
+ 1, # type: method
460
+ id, # channel id
461
+ frame_size, # frame size
462
+ 60, # class: basic
463
+ 10, # method: qos
464
+ prefetch_size,
465
+ prefetch_count,
466
+ global ? 1 : 0,
467
+ 206 # frame end
468
+ ].pack("C S> L> S> S> L> S> C C")
469
+ end
470
+
471
+ def self.basic_recover(id, requeue)
472
+ frame_size = 2 + 2 + 1
473
+ [
474
+ 1, # type: method
475
+ id, # channel id
476
+ frame_size, # frame size
477
+ 60, # class: basic
478
+ 110, # method: recover
479
+ requeue ? 1 : 0,
480
+ 206 # frame end
481
+ ].pack("C S> L> S> S> C C")
482
+ end
483
+
484
+ def self.confirm_select(id, no_wait)
485
+ [
486
+ 1, # type: method
487
+ id, # channel id
488
+ 5, # frame size
489
+ 85, # class: confirm
490
+ 10, # method: select
491
+ no_wait ? 1 : 0,
492
+ 206 # frame end
493
+ ].pack("C S> L> S> S> C C")
494
+ end
495
+
496
+ def self.tx_select(id)
497
+ frame_size = 2 + 2
498
+ [
499
+ 1, # type: method
500
+ id, # channel id
501
+ frame_size, # frame size
502
+ 90, # class: tx
503
+ 10, # method: select
504
+ 206 # frame end
505
+ ].pack("C S> L> S> S> C")
506
+ end
507
+
508
+ def self.tx_commit(id)
509
+ frame_size = 2 + 2
510
+ [
511
+ 1, # type: method
512
+ id, # channel id
513
+ frame_size, # frame size
514
+ 90, # class: tx
515
+ 20, # method: commit
516
+ 206 # frame end
517
+ ].pack("C S> L> S> S> C")
518
+ end
519
+
520
+ def self.tx_rollback(id)
521
+ frame_size = 2 + 2
522
+ [
523
+ 1, # type: method
524
+ id, # channel id
525
+ frame_size, # frame size
526
+ 90, # class: tx
527
+ 30, # method: rollback
528
+ 206 # frame end
529
+ ].pack("C S> L> S> S> C")
530
+ end
531
+ end
532
+ end
533
+ end