amqp-client 1.0.2 → 1.1.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.
@@ -3,7 +3,7 @@
3
3
  require "socket"
4
4
  require "uri"
5
5
  require "openssl"
6
- require_relative "./frames"
6
+ require_relative "./frame_bytes"
7
7
  require_relative "./channel"
8
8
  require_relative "./errors"
9
9
 
@@ -13,47 +13,32 @@ module AMQP
13
13
  class Connection
14
14
  # Establish a connection to an AMQP broker
15
15
  # @param uri [String] URL on the format amqp://username:password@hostname/vhost, use amqps:// for encrypted connection
16
- # @param read_loop_thread [Boolean] Set to false if you manually want to run the {#read_loop}
16
+ # @param read_loop_thread [Boolean] If true run {#read_loop} in a background thread,
17
+ # otherwise the user have to run it explicitly, without {#read_loop} the connection won't function
17
18
  # @option options [Boolean] connection_name (PROGRAM_NAME) Set a name for the connection to be able to identify
18
19
  # the client from the broker
19
20
  # @option options [Boolean] verify_peer (true) Verify broker's TLS certificate, set to false for self-signed certs
21
+ # @option options [Integer] connect_timeout (30) TCP connection timeout
20
22
  # @option options [Integer] heartbeat (0) Heartbeat timeout, defaults to 0 and relies on TCP keepalive instead
21
23
  # @option options [Integer] frame_max (131_072) Maximum frame size,
22
24
  # the smallest of the client's and the broker's values will be used
23
25
  # @option options [Integer] channel_max (2048) Maxium number of channels the client will be allowed to have open.
24
26
  # Maxium allowed is 65_536. The smallest of the client's and the broker's value will be used.
27
+ # @option options [String] keepalive (60:10:3) TCP keepalive setting, 60s idle, 10s interval between probes, 3 probes
25
28
  # @return [Connection]
26
- def self.connect(uri, read_loop_thread: true, **options)
29
+ def initialize(uri = "", read_loop_thread: true, **options)
27
30
  uri = URI.parse(uri)
28
31
  tls = uri.scheme == "amqps"
29
32
  port = port_from_env || uri.port || (tls ? 5671 : 5672)
30
33
  host = uri.host || "localhost"
31
34
  user = uri.user || "guest"
32
35
  password = uri.password || "guest"
33
- vhost = URI.decode_www_form_component(uri.path[1..-1] || "/")
36
+ vhost = URI.decode_www_form_component(uri.path[1..] || "/")
34
37
  options = URI.decode_www_form(uri.query || "").map! { |k, v| [k.to_sym, v] }.to_h.merge(options)
35
38
 
36
- socket = Socket.tcp host, port, connect_timeout: 20, resolv_timeout: 5
37
- enable_tcp_keepalive(socket)
38
- if tls
39
- cert_store = OpenSSL::X509::Store.new
40
- cert_store.set_default_paths
41
- context = OpenSSL::SSL::SSLContext.new
42
- context.cert_store = cert_store
43
- context.verify_mode = OpenSSL::SSL::VERIFY_PEER unless [false, "false", "none"].include? options[:verify_peer]
44
- socket = OpenSSL::SSL::SSLSocket.new(socket, context)
45
- socket.sync_close = true # closing the TLS socket also closes the TCP socket
46
- socket.hostname = host # SNI host
47
- socket.connect
48
- socket.post_connection_check(host) || raise(Error, "TLS certificate hostname doesn't match requested")
49
- end
39
+ socket = open_socket(host, port, tls, options)
50
40
  channel_max, frame_max, heartbeat = establish(socket, user, password, vhost, options)
51
- Connection.new(socket, channel_max, frame_max, heartbeat, read_loop_thread: read_loop_thread)
52
- end
53
41
 
54
- # Requires an already established TCP/TLS socket
55
- # @api private
56
- def initialize(socket, channel_max, frame_max, heartbeat, read_loop_thread: true)
57
42
  @socket = socket
58
43
  @channel_max = channel_max.zero? ? 65_536 : channel_max
59
44
  @frame_max = frame_max
@@ -66,6 +51,13 @@ module AMQP
66
51
  Thread.new { read_loop } if read_loop_thread
67
52
  end
68
53
 
54
+ # Alias for {#initialize}
55
+ # @see #initialize
56
+ # @deprecated
57
+ def self.connect(uri, read_loop_thread: true, **options)
58
+ new(uri, read_loop_thread: read_loop_thread, **options)
59
+ end
60
+
69
61
  # The max frame size negotiated between the client and the broker
70
62
  # @return [Integer]
71
63
  attr_reader :frame_max
@@ -142,9 +134,13 @@ module AMQP
142
134
  warn "AMQP-Client blocked by broker: #{blocked}" if blocked
143
135
  @write_lock.synchronize do
144
136
  warn "AMQP-Client unblocked by broker" if blocked
145
- @socket.write(*bytes)
137
+ if RUBY_ENGINE == "truffleruby"
138
+ bytes.each { |b| @socket.write b }
139
+ else
140
+ @socket.write(*bytes)
141
+ end
146
142
  end
147
- rescue IOError, OpenSSL::OpenSSLError, SystemCallError => e
143
+ rescue *READ_EXCEPTIONS => e
148
144
  raise Error::ConnectionClosed.new(*@closed) if @closed
149
145
 
150
146
  raise Error, "Could not write to socket, #{e.message}"
@@ -161,12 +157,12 @@ module AMQP
161
157
  frame_start = String.new(capacity: 7)
162
158
  frame_buffer = String.new(capacity: frame_max)
163
159
  loop do
164
- socket.read(7, frame_start)
160
+ socket.read(7, frame_start) || raise(IOError)
165
161
  type, channel_id, frame_size = frame_start.unpack("C S> L>")
166
162
  frame_max >= frame_size || raise(Error, "Frame size #{frame_size} larger than negotiated max frame size #{frame_max}")
167
163
 
168
164
  # read the frame content
169
- socket.read(frame_size, frame_buffer)
165
+ socket.read(frame_size, frame_buffer) || raise(IOError)
170
166
 
171
167
  # make sure that the frame end is correct
172
168
  frame_end = socket.readchar.ord
@@ -176,21 +172,26 @@ module AMQP
176
172
  parse_frame(type, channel_id, frame_buffer) || return
177
173
  end
178
174
  nil
179
- rescue IOError, OpenSSL::OpenSSLError, SystemCallError => e
175
+ rescue *READ_EXCEPTIONS => e
180
176
  @closed ||= [400, "read error: #{e.message}"]
181
177
  nil # ignore read errors
182
178
  ensure
183
179
  @closed ||= [400, "unknown"]
184
180
  @replies.close
185
181
  begin
186
- @socket.close
187
- rescue IOError, OpenSSL::OpenSSLError, SystemCallError
182
+ @write_lock.synchronize do
183
+ @socket.close
184
+ end
185
+ rescue *READ_EXCEPTIONS
188
186
  nil
189
187
  end
190
188
  end
191
189
 
192
190
  private
193
191
 
192
+ READ_EXCEPTIONS = [IOError, OpenSSL::OpenSSLError, SystemCallError,
193
+ RUBY_ENGINE == "jruby" ? java.lang.NullPointerException : nil].compact.freeze
194
+
194
195
  def parse_frame(type, channel_id, buf)
195
196
  case type
196
197
  when 1 # method frame
@@ -216,7 +217,7 @@ module AMQP
216
217
  @replies.push [:close_ok]
217
218
  return false
218
219
  when 60 # connection#blocked
219
- reason_len = buf.unpack1("@4 C")
220
+ reason_len = buf.getbyte(4)
220
221
  reason = buf.byteslice(5, reason_len).force_encoding("utf-8")
221
222
  @blocked = reason
222
223
  @write_lock.lock
@@ -256,7 +257,7 @@ module AMQP
256
257
  when 50 # queue
257
258
  case method_id
258
259
  when 11 # declare-ok
259
- queue_name_len = buf.unpack1("@4 C")
260
+ queue_name_len = buf.getbyte(4)
260
261
  queue_name = buf.byteslice(5, queue_name_len).force_encoding("utf-8")
261
262
  message_count, consumer_count = buf.byteslice(5 + queue_name_len, 8).unpack("L> L>")
262
263
  @channels[channel_id].reply [:queue_declare_ok, queue_name, message_count, consumer_count]
@@ -276,17 +277,17 @@ module AMQP
276
277
  when 11 # qos-ok
277
278
  @channels[channel_id].reply [:basic_qos_ok]
278
279
  when 21 # consume-ok
279
- tag_len = buf.unpack1("@4 C")
280
+ tag_len = buf.getbyte(4)
280
281
  tag = buf.byteslice(5, tag_len).force_encoding("utf-8")
281
282
  @channels[channel_id].reply [:basic_consume_ok, tag]
282
283
  when 30 # cancel
283
- tag_len = buf.unpack1("@4 C")
284
+ tag_len = buf.getbyte(4)
284
285
  tag = buf.byteslice(5, tag_len).force_encoding("utf-8")
285
- no_wait = buf[5 + tag_len].ord == 1
286
+ no_wait = buf.getbyte(5 + tag_len) == 1
286
287
  @channels[channel_id].close_consumer(tag)
287
288
  write_bytes FrameBytes.basic_cancel_ok(@id, tag) unless no_wait
288
289
  when 31 # cancel-ok
289
- tag_len = buf.unpack1("@4 C")
290
+ tag_len = buf.getbyte(4)
290
291
  tag = buf.byteslice(5, tag_len).force_encoding("utf-8")
291
292
  @channels[channel_id].reply [:basic_cancel_ok, tag]
292
293
  when 50 # return
@@ -294,23 +295,23 @@ module AMQP
294
295
  pos = 7
295
296
  reply_text = buf.byteslice(pos, reply_text_len).force_encoding("utf-8")
296
297
  pos += reply_text_len
297
- exchange_len = buf[pos].ord
298
+ exchange_len = buf.getbyte(pos)
298
299
  pos += 1
299
300
  exchange = buf.byteslice(pos, exchange_len).force_encoding("utf-8")
300
301
  pos += exchange_len
301
- routing_key_len = buf[pos].ord
302
+ routing_key_len = buf.getbyte(pos)
302
303
  pos += 1
303
304
  routing_key = buf.byteslice(pos, routing_key_len).force_encoding("utf-8")
304
305
  @channels[channel_id].message_returned(reply_code, reply_text, exchange, routing_key)
305
306
  when 60 # deliver
306
- ctag_len = buf[4].ord
307
+ ctag_len = buf.getbyte(4)
307
308
  consumer_tag = buf.byteslice(5, ctag_len).force_encoding("utf-8")
308
309
  pos = 5 + ctag_len
309
310
  delivery_tag, redelivered, exchange_len = buf.byteslice(pos, 10).unpack("Q> C C")
310
311
  pos += 8 + 1 + 1
311
312
  exchange = buf.byteslice(pos, exchange_len).force_encoding("utf-8")
312
313
  pos += exchange_len
313
- rk_len = buf[pos].ord
314
+ rk_len = buf.getbyte(pos)
314
315
  pos += 1
315
316
  routing_key = buf.byteslice(pos, rk_len).force_encoding("utf-8")
316
317
  @channels[channel_id].message_delivered(consumer_tag, delivery_tag, redelivered == 1, exchange, routing_key)
@@ -319,11 +320,11 @@ module AMQP
319
320
  pos = 14
320
321
  exchange = buf.byteslice(pos, exchange_len).force_encoding("utf-8")
321
322
  pos += exchange_len
322
- routing_key_len = buf[pos].ord
323
+ routing_key_len = buf.getbyte(pos)
323
324
  pos += 1
324
325
  routing_key = buf.byteslice(pos, routing_key_len).force_encoding("utf-8")
325
- pos += routing_key_len
326
- _message_count = buf.byteslice(pos, 4).unpack1("L>")
326
+ # pos += routing_key_len
327
+ # message_count = buf.byteslice(pos, 4).unpack1("L>")
327
328
  @channels[channel_id].message_delivered(nil, delivery_tag, redelivered == 1, exchange, routing_key)
328
329
  when 72 # get-empty
329
330
  @channels[channel_id].basic_get_empty
@@ -357,7 +358,7 @@ module AMQP
357
358
  end
358
359
  when 2 # header
359
360
  body_size = buf.unpack1("@4 Q>")
360
- properties = Properties.decode(buf.byteslice(12, buf.bytesize - 12))
361
+ properties = Properties.decode(buf, 12)
361
362
  @channels[channel_id].header_delivered body_size, properties
362
363
  when 3 # body
363
364
  @channels[channel_id].body_delivered buf
@@ -377,21 +378,47 @@ module AMQP
377
378
  args
378
379
  end
379
380
 
381
+ # Connect to the host/port, optionally establish a TLS connection
382
+ # @return [Socket]
383
+ # @return [OpenSSL::SSL::SSLSocket]
384
+ def open_socket(host, port, tls, options)
385
+ connect_timeout = options.fetch(:connect_timeout, 30).to_i
386
+ socket = Socket.tcp host, port, connect_timeout: connect_timeout
387
+ keepalive = options.fetch(:keepalive, "").split(":", 3).map!(&:to_i)
388
+ enable_tcp_keepalive(socket, *keepalive)
389
+ if tls
390
+ cert_store = OpenSSL::X509::Store.new
391
+ cert_store.set_default_paths
392
+ context = OpenSSL::SSL::SSLContext.new
393
+ context.cert_store = cert_store
394
+ verify_peer = [false, "false", "none"].include? options[:verify_peer]
395
+ context.verify_mode = OpenSSL::SSL::VERIFY_PEER unless verify_peer
396
+ socket = OpenSSL::SSL::SSLSocket.new(socket, context)
397
+ socket.sync_close = true # closing the TLS socket also closes the TCP socket
398
+ socket.hostname = host # SNI host
399
+ socket.connect
400
+ socket.post_connection_check(host) || raise(Error, "TLS certificate hostname doesn't match requested")
401
+ end
402
+ socket
403
+ rescue SystemCallError, OpenSSL::OpenSSLError => e
404
+ raise Error, "Could not open a socket: #{e.message}"
405
+ end
406
+
380
407
  # Negotiate a connection
381
408
  # @return [Array<Integer, Integer, Integer>] channel_max, frame_max, heartbeat
382
- def self.establish(socket, user, password, vhost, options)
409
+ def establish(socket, user, password, vhost, options)
383
410
  channel_max, frame_max, heartbeat = nil
384
411
  socket.write "AMQP\x00\x00\x09\x01"
385
412
  buf = String.new(capacity: 4096)
386
413
  loop do
387
414
  begin
388
415
  socket.readpartial(4096, buf)
389
- rescue IOError, OpenSSL::OpenSSLError, SystemCallError => e
416
+ rescue *READ_EXCEPTIONS => e
390
417
  raise Error, "Could not establish AMQP connection: #{e.message}"
391
418
  end
392
419
 
393
420
  type, channel_id, frame_size = buf.unpack("C S> L>")
394
- frame_end = buf[frame_size + 7].ord
421
+ frame_end = buf.getbyte(frame_size + 7)
395
422
  raise UnexpectedFrameEndError, frame_end if frame_end != 206
396
423
 
397
424
  case type
@@ -428,32 +455,42 @@ module AMQP
428
455
  else raise Error, "Unexpected frame type: #{type}"
429
456
  end
430
457
  end
431
- rescue StandardError => e
458
+ rescue Exception => e
432
459
  begin
433
460
  socket.close
434
- rescue IOError, OpenSSL::OpenSSLError, SystemCallError
461
+ rescue *READ_EXCEPTIONS
435
462
  nil
436
463
  end
437
464
  raise e
438
465
  end
439
466
 
440
- def self.enable_tcp_keepalive(socket)
467
+ # Enable TCP keepalive, which is prefered to heartbeats
468
+ # @return [void]
469
+ def enable_tcp_keepalive(socket, idle = 60, interval = 10, count = 3)
441
470
  socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
442
- socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, 60)
443
- socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, 10)
444
- socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, 3)
471
+ if Socket.const_defined?(:TCP_KEEPIDLE) # linux/bsd
472
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, idle)
473
+ elsif RUBY_PLATFORM.include? "darwin" # os x
474
+ # https://www.quickhack.net/nom/blog/2018-01-19-enable-tcp-keepalive-of-macos-and-linux-in-ruby.html
475
+ socket.setsockopt(Socket::IPPROTO_TCP, 0x10, idle)
476
+ else # windows
477
+ return
478
+ end
479
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, interval)
480
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, count)
445
481
  rescue StandardError => e
446
482
  warn "AMQP-Client could not enable TCP keepalive on socket. #{e.inspect}"
447
483
  end
448
484
 
449
- def self.port_from_env
485
+ # Fetch the AMQP port number from ENV
486
+ # @return [Integer] A port number
487
+ # @return [nil] When the environment variable AMQP_PORT isn't set
488
+ def port_from_env
450
489
  return unless (port = ENV["AMQP_PORT"])
451
490
 
452
491
  port.to_i
453
492
  end
454
493
 
455
- private_class_method :establish, :enable_tcp_keepalive, :port_from_env
456
-
457
494
  CLIENT_PROPERTIES = {
458
495
  capabilities: {
459
496
  authentication_failure_close: true,
@@ -10,9 +10,7 @@ module AMQP
10
10
  # Having a class for each frame type is more expensive in terms of CPU and memory
11
11
  # @api private
12
12
  module FrameBytes
13
- module_function
14
-
15
- def connection_start_ok(response, properties)
13
+ def self.connection_start_ok(response, properties)
16
14
  prop_tbl = Table.encode(properties)
17
15
  [
18
16
  1, # type: method
@@ -28,7 +26,7 @@ module AMQP
28
26
  ].pack("C S> L> S> S> L>a* Ca* L>a* Ca* C")
29
27
  end
30
28
 
31
- def connection_tune_ok(channel_max, frame_max, heartbeat)
29
+ def self.connection_tune_ok(channel_max, frame_max, heartbeat)
32
30
  [
33
31
  1, # type: method
34
32
  0, # channel id
@@ -42,7 +40,7 @@ module AMQP
42
40
  ].pack("CS>L>S>S>S>L>S>C")
43
41
  end
44
42
 
45
- def connection_open(vhost)
43
+ def self.connection_open(vhost)
46
44
  [
47
45
  1, # type: method
48
46
  0, # channel id
@@ -56,7 +54,7 @@ module AMQP
56
54
  ].pack("C S> L> S> S> Ca* CCC")
57
55
  end
58
56
 
59
- def connection_close(code, reason)
57
+ def self.connection_close(code, reason)
60
58
  frame_size = 2 + 2 + 2 + 1 + reason.bytesize + 2 + 2
61
59
  [
62
60
  1, # type: method
@@ -72,7 +70,7 @@ module AMQP
72
70
  ].pack("C S> L> S> S> S> Ca* S> S> C")
73
71
  end
74
72
 
75
- def connection_close_ok
73
+ def self.connection_close_ok
76
74
  [
77
75
  1, # type: method
78
76
  0, # channel id
@@ -83,7 +81,7 @@ module AMQP
83
81
  ].pack("C S> L> S> S> C")
84
82
  end
85
83
 
86
- def channel_open(id)
84
+ def self.channel_open(id)
87
85
  [
88
86
  1, # type: method
89
87
  id, # channel id
@@ -95,7 +93,7 @@ module AMQP
95
93
  ].pack("C S> L> S> S> C C")
96
94
  end
97
95
 
98
- def channel_close(id, reason, code)
96
+ def self.channel_close(id, reason, code)
99
97
  frame_size = 2 + 2 + 2 + 1 + reason.bytesize + 2 + 2
100
98
  [
101
99
  1, # type: method
@@ -111,7 +109,7 @@ module AMQP
111
109
  ].pack("C S> L> S> S> S> Ca* S> S> C")
112
110
  end
113
111
 
114
- def channel_close_ok(id)
112
+ def self.channel_close_ok(id)
115
113
  [
116
114
  1, # type: method
117
115
  id, # channel id
@@ -122,7 +120,7 @@ module AMQP
122
120
  ].pack("C S> L> S> S> C")
123
121
  end
124
122
 
125
- def exchange_declare(id, name, type, passive, durable, auto_delete, internal, arguments)
123
+ def self.exchange_declare(id, name, type, passive, durable, auto_delete, internal, arguments)
126
124
  no_wait = false
127
125
  bits = 0
128
126
  bits |= (1 << 0) if passive
@@ -147,7 +145,7 @@ module AMQP
147
145
  ].pack("C S> L> S> S> S> Ca* Ca* C L>a* C")
148
146
  end
149
147
 
150
- def exchange_delete(id, name, if_unused, no_wait)
148
+ def self.exchange_delete(id, name, if_unused, no_wait)
151
149
  bits = 0
152
150
  bits |= (1 << 0) if if_unused
153
151
  bits |= (1 << 1) if no_wait
@@ -165,7 +163,7 @@ module AMQP
165
163
  ].pack("C S> L> S> S> S> Ca* C C")
166
164
  end
167
165
 
168
- def exchange_bind(id, destination, source, binding_key, no_wait, arguments)
166
+ def self.exchange_bind(id, destination, source, binding_key, no_wait, arguments)
169
167
  tbl = Table.encode(arguments)
170
168
  frame_size = 2 + 2 + 2 + 1 + destination.bytesize + 1 + source.bytesize + 1 +
171
169
  binding_key.bytesize + 1 + 4 + tbl.bytesize
@@ -185,7 +183,7 @@ module AMQP
185
183
  ].pack("C S> L> S> S> S> Ca* Ca* Ca* C L>a* C")
186
184
  end
187
185
 
188
- def exchange_unbind(id, destination, source, binding_key, no_wait, arguments)
186
+ def self.exchange_unbind(id, destination, source, binding_key, no_wait, arguments)
189
187
  tbl = Table.encode(arguments)
190
188
  frame_size = 2 + 2 + 2 + 1 + destination.bytesize + 1 + source.bytesize + 1 +
191
189
  binding_key.bytesize + 1 + 4 + tbl.bytesize
@@ -205,7 +203,7 @@ module AMQP
205
203
  ].pack("C S> L> S> S> S> Ca* Ca* Ca* C L>a* C")
206
204
  end
207
205
 
208
- def queue_declare(id, name, passive, durable, exclusive, auto_delete, arguments)
206
+ def self.queue_declare(id, name, passive, durable, exclusive, auto_delete, arguments)
209
207
  no_wait = false
210
208
  bits = 0
211
209
  bits |= (1 << 0) if passive
@@ -229,7 +227,7 @@ module AMQP
229
227
  ].pack("C S> L> S> S> S> Ca* C L>a* C")
230
228
  end
231
229
 
232
- def queue_delete(id, name, if_unused, if_empty, no_wait)
230
+ def self.queue_delete(id, name, if_unused, if_empty, no_wait)
233
231
  bits = 0
234
232
  bits |= (1 << 0) if if_unused
235
233
  bits |= (1 << 1) if if_empty
@@ -248,7 +246,7 @@ module AMQP
248
246
  ].pack("C S> L> S> S> S> Ca* C C")
249
247
  end
250
248
 
251
- def queue_bind(id, queue, exchange, binding_key, no_wait, arguments)
249
+ def self.queue_bind(id, queue, exchange, binding_key, no_wait, arguments)
252
250
  tbl = Table.encode(arguments)
253
251
  frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1 + exchange.bytesize + 1 +
254
252
  binding_key.bytesize + 1 + 4 + tbl.bytesize
@@ -268,7 +266,7 @@ module AMQP
268
266
  ].pack("C S> L> S> S> S> Ca* Ca* Ca* C L>a* C")
269
267
  end
270
268
 
271
- def queue_unbind(id, queue, exchange, binding_key, arguments)
269
+ def self.queue_unbind(id, queue, exchange, binding_key, arguments)
272
270
  tbl = Table.encode(arguments)
273
271
  frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1 + exchange.bytesize + 1 +
274
272
  binding_key.bytesize + 4 + tbl.bytesize
@@ -287,7 +285,7 @@ module AMQP
287
285
  ].pack("C S> L> S> S> S> Ca* Ca* Ca* L>a* C")
288
286
  end
289
287
 
290
- def queue_purge(id, queue, no_wait)
288
+ def self.queue_purge(id, queue, no_wait)
291
289
  frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1
292
290
  [
293
291
  1, # type: method
@@ -302,7 +300,7 @@ module AMQP
302
300
  ].pack("C S> L> S> S> S> Ca* C C")
303
301
  end
304
302
 
305
- def basic_get(id, queue, no_ack)
303
+ def self.basic_get(id, queue, no_ack)
306
304
  frame_size = 2 + 2 + 2 + 1 + queue.bytesize + 1
307
305
  [
308
306
  1, # type: method
@@ -317,7 +315,7 @@ module AMQP
317
315
  ].pack("C S> L> S> S> S> Ca* C C")
318
316
  end
319
317
 
320
- def basic_publish(id, exchange, routing_key, mandatory)
318
+ def self.basic_publish(id, exchange, routing_key, mandatory)
321
319
  frame_size = 2 + 2 + 2 + 1 + exchange.bytesize + 1 + routing_key.bytesize + 1
322
320
  [
323
321
  1, # type: method
@@ -333,8 +331,8 @@ module AMQP
333
331
  ].pack("C S> L> S> S> S> Ca* Ca* C C")
334
332
  end
335
333
 
336
- def header(id, body_size, properties)
337
- props = Properties.new(**properties).encode
334
+ def self.header(id, body_size, properties)
335
+ props = Properties.encode(properties)
338
336
  frame_size = 2 + 2 + 8 + props.bytesize
339
337
  [
340
338
  2, # type: header
@@ -348,7 +346,7 @@ module AMQP
348
346
  ].pack("C S> L> S> S> Q> a* C")
349
347
  end
350
348
 
351
- def body(id, body_part)
349
+ def self.body(id, body_part)
352
350
  [
353
351
  3, # type: body
354
352
  id, # channel id
@@ -358,7 +356,7 @@ module AMQP
358
356
  ].pack("C S> L> a* C")
359
357
  end
360
358
 
361
- def basic_consume(id, queue, tag, no_ack, exclusive, arguments)
359
+ def self.basic_consume(id, queue, tag, no_ack, exclusive, arguments)
362
360
  no_local = false
363
361
  no_wait = false
364
362
  bits = 0
@@ -383,7 +381,7 @@ module AMQP
383
381
  ].pack("C S> L> S> S> S> Ca* Ca* C L>a* C")
384
382
  end
385
383
 
386
- def basic_cancel(id, consumer_tag, no_wait: false)
384
+ def self.basic_cancel(id, consumer_tag, no_wait: false)
387
385
  frame_size = 2 + 2 + 1 + consumer_tag.bytesize + 1
388
386
  [
389
387
  1, # type: method
@@ -397,7 +395,7 @@ module AMQP
397
395
  ].pack("C S> L> S> S> Ca* C C")
398
396
  end
399
397
 
400
- def basic_cancel_ok(id, consumer_tag)
398
+ def self.basic_cancel_ok(id, consumer_tag)
401
399
  frame_size = 2 + 2 + 1 + consumer_tag.bytesize + 1
402
400
  [
403
401
  1, # type: method
@@ -410,7 +408,7 @@ module AMQP
410
408
  ].pack("C S> L> S> S> Ca* C")
411
409
  end
412
410
 
413
- def basic_ack(id, delivery_tag, multiple)
411
+ def self.basic_ack(id, delivery_tag, multiple)
414
412
  frame_size = 2 + 2 + 8 + 1
415
413
  [
416
414
  1, # type: method
@@ -424,7 +422,7 @@ module AMQP
424
422
  ].pack("C S> L> S> S> Q> C C")
425
423
  end
426
424
 
427
- def basic_nack(id, delivery_tag, multiple, requeue)
425
+ def self.basic_nack(id, delivery_tag, multiple, requeue)
428
426
  bits = 0
429
427
  bits |= (1 << 0) if multiple
430
428
  bits |= (1 << 1) if requeue
@@ -441,7 +439,7 @@ module AMQP
441
439
  ].pack("C S> L> S> S> Q> C C")
442
440
  end
443
441
 
444
- def basic_reject(id, delivery_tag, requeue)
442
+ def self.basic_reject(id, delivery_tag, requeue)
445
443
  frame_size = 2 + 2 + 8 + 1
446
444
  [
447
445
  1, # type: method
@@ -455,7 +453,7 @@ module AMQP
455
453
  ].pack("C S> L> S> S> Q> C C")
456
454
  end
457
455
 
458
- def basic_qos(id, prefetch_size, prefetch_count, global)
456
+ def self.basic_qos(id, prefetch_size, prefetch_count, global)
459
457
  frame_size = 2 + 2 + 4 + 2 + 1
460
458
  [
461
459
  1, # type: method
@@ -470,7 +468,7 @@ module AMQP
470
468
  ].pack("C S> L> S> S> L> S> C C")
471
469
  end
472
470
 
473
- def basic_recover(id, requeue)
471
+ def self.basic_recover(id, requeue)
474
472
  frame_size = 2 + 2 + 1
475
473
  [
476
474
  1, # type: method
@@ -483,7 +481,7 @@ module AMQP
483
481
  ].pack("C S> L> S> S> C C")
484
482
  end
485
483
 
486
- def confirm_select(id, no_wait)
484
+ def self.confirm_select(id, no_wait)
487
485
  [
488
486
  1, # type: method
489
487
  id, # channel id
@@ -495,7 +493,7 @@ module AMQP
495
493
  ].pack("C S> L> S> S> C C")
496
494
  end
497
495
 
498
- def tx_select(id)
496
+ def self.tx_select(id)
499
497
  frame_size = 2 + 2
500
498
  [
501
499
  1, # type: method
@@ -507,7 +505,7 @@ module AMQP
507
505
  ].pack("C S> L> S> S> C")
508
506
  end
509
507
 
510
- def tx_commit(id)
508
+ def self.tx_commit(id)
511
509
  frame_size = 2 + 2
512
510
  [
513
511
  1, # type: method
@@ -519,7 +517,7 @@ module AMQP
519
517
  ].pack("C S> L> S> S> C")
520
518
  end
521
519
 
522
- def tx_rollback(id)
520
+ def self.tx_rollback(id)
523
521
  frame_size = 2 + 2
524
522
  [
525
523
  1, # type: method