amqp-client 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 337f82d8ebaf862fd8fd1acd17f9b71be2aaee6de02f0c7c59786b222d5c5c0c
4
- data.tar.gz: 040adffbda90616864a845517a1904efa5c1dbb25e30e63febe3f34b3cf4f752
3
+ metadata.gz: 30006ba97b26d7e73cf9503841e2248744db3d9e86d1a32fe4c9abecd9ee69f7
4
+ data.tar.gz: a59d667d971da49c2657e57efe385853adbef958fd191a01f0d1f4dbaaa1b8a8
5
5
  SHA512:
6
- metadata.gz: 5700b885356d0c36060ffb4a9ad819bc26672a3d89fef6283cb5c4789ae3ddeafeaa57917f3001947fbdc38954eabfb138b8a98ed4a187a66912e5fb47bb3200
7
- data.tar.gz: 034c2aa88d93bf45063d41b87e92cb20545deadf7c98930a7b6626e448a84036d234274c8c1066a85e6fd35a1274dfb392128a037f6fac7a330aee0af9907a74
6
+ metadata.gz: 7ddb8f409abf9e773a6551ff53629b702e8324ea7ff794f6b14f09ef9dd7cb27a752e853bb312a8d9da88b826a7714e84006d5bb01b5efeef7bbaed56f14323a
7
+ data.tar.gz: 642e332c0a031d27d7f364b66e0ae5beefef4bad402336f24052eef2679e306eec219369d7b8b2c1d8324eabafd0c40d45aa8feb474c3cd11ddc4578203f5bea
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2021-08-20
4
+
5
+ - Channel#wait_for_confirms is a smarter way of waiting for publish confirms
6
+ - Default connection_name to $PROGRAM_NAME
7
+
8
+ ## [0.2.3] - 2021-08-19
9
+
10
+ - Improved TLS/AMQPS support
11
+
12
+ ## [0.2.2] - 2021-08-19
13
+
14
+ - TLS port issue fixed
15
+
16
+ ## [0.2.1] - 2021-08-19
17
+
18
+ - More arguments to be passed to AMQP::Client::Queue
19
+ - Can require with 'amqp-client'
3
20
 
4
21
  ## [0.2.0] - 2021-08-19
5
22
 
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "amqp/client"
data/lib/amqp/client.rb CHANGED
@@ -123,8 +123,8 @@ module AMQP
123
123
  self
124
124
  end
125
125
 
126
- def subscribe(prefetch: 1, arguments: {}, &blk)
127
- @client.subscribe(@name, prefetch: prefetch, arguments: arguments, &blk)
126
+ def subscribe(no_ack: false, prefetch: 1, worker_threads: 1, arguments: {}, &blk)
127
+ @client.subscribe(@name, no_ack: no_ack, prefetch: prefetch, worker_threads: worker_threads, arguments: arguments, &blk)
128
128
  self
129
129
  end
130
130
 
@@ -6,15 +6,16 @@ module AMQP
6
6
  # AMQP Channel
7
7
  class Channel
8
8
  def initialize(connection, id)
9
- @replies = ::Queue.new
10
9
  @connection = connection
11
10
  @id = id
11
+ @replies = ::Queue.new
12
12
  @consumers = {}
13
- @confirm = nil
14
- @last_confirmed = 0
15
13
  @closed = nil
16
- @on_return = nil
17
14
  @open = false
15
+ @on_return = nil
16
+ @confirm = nil
17
+ @unconfirmed = ::Queue.new
18
+ @unconfirmed_empty = ::Queue.new
18
19
  end
19
20
 
20
21
  attr_reader :id, :consumers
@@ -124,15 +125,17 @@ module AMQP
124
125
  def basic_publish(body, exchange, routing_key, **properties)
125
126
  frame_max = @connection.frame_max - 8
126
127
  id = @id
128
+ mandatory = properties.delete(:mandatory) || false
127
129
 
128
130
  if 0 < body.bytesize && body.bytesize <= frame_max
129
- write_bytes FrameBytes.basic_publish(id, exchange, routing_key, properties.delete(:mandatory) || false),
131
+ write_bytes FrameBytes.basic_publish(id, exchange, routing_key, mandatory),
130
132
  FrameBytes.header(id, body.bytesize, properties),
131
133
  FrameBytes.body(id, body)
132
- return @confirm ? @confirm += 1 : nil
134
+ @unconfirmed.push @confirm += 1 if @confirm
135
+ return
133
136
  end
134
137
 
135
- write_bytes FrameBytes.basic_publish(id, exchange, routing_key, properties.delete(:mandatory) || false),
138
+ write_bytes FrameBytes.basic_publish(id, exchange, routing_key, mandatory),
136
139
  FrameBytes.header(id, body.bytesize, properties)
137
140
  pos = 0
138
141
  while pos < body.bytesize # split body into multiple frame_max frames
@@ -141,13 +144,14 @@ module AMQP
141
144
  write_bytes FrameBytes.body(id, body_part)
142
145
  pos += len
143
146
  end
144
- @confirm += 1 if @confirm
147
+ @unconfirmed.push @confirm += 1 if @confirm
148
+ nil
145
149
  end
146
150
 
147
151
  def basic_publish_confirm(body, exchange, routing_key, **properties)
148
152
  confirm_select(no_wait: true)
149
- id = basic_publish(body, exchange, routing_key, **properties)
150
- wait_for_confirm(id)
153
+ basic_publish(body, exchange, routing_key, **properties)
154
+ wait_for_confirms
151
155
  end
152
156
 
153
157
  # Consume from a queue
@@ -211,20 +215,32 @@ module AMQP
211
215
 
212
216
  write_bytes FrameBytes.confirm_select(@id, no_wait)
213
217
  expect :confirm_select_ok unless no_wait
214
- @confirms = ::Queue.new
215
218
  @confirm = 0
216
219
  end
217
220
 
218
- def wait_for_confirm(id)
219
- raise ArgumentError, "Confirm id has to a positive number" unless id&.positive?
220
- return true if @last_confirmed >= id
221
+ # Block until all publishes messages are confirmed
222
+ def wait_for_confirms
223
+ return true if @unconfirmed.empty?
224
+
225
+ @unconfirmed_empty.pop
226
+ end
221
227
 
228
+ def confirm(args)
229
+ ack_or_nack, delivery_tag, multiple = *args
222
230
  loop do
223
- ack, delivery_tag, multiple = @confirms.shift || break
224
- @last_confirmed = delivery_tag
225
- return ack if delivery_tag == id || (delivery_tag > id && multiple)
231
+ tag = @unconfirmed.pop(true)
232
+ break if tag == delivery_tag
233
+ next if multiple && tag < delivery_tag
234
+
235
+ @unconfirmed << tag # requeue
236
+ rescue ThreadError
237
+ break
238
+ end
239
+ return unless @unconfirmed.empty?
240
+
241
+ @unconfirmed_empty.num_waiting.times do
242
+ @unconfirmed_empty << ack_or_nack == :ack
226
243
  end
227
- false
228
244
  end
229
245
 
230
246
  def tx_select
@@ -246,10 +262,6 @@ module AMQP
246
262
  @replies.push(args)
247
263
  end
248
264
 
249
- def confirm(args)
250
- @confirms.push(args)
251
- end
252
-
253
265
  def message_returned(reply_code, reply_text, exchange, routing_key)
254
266
  Thread.new do
255
267
  body_size, properties = expect(:header)
@@ -15,7 +15,7 @@ module AMQP
15
15
 
16
16
  uri = URI.parse(uri)
17
17
  tls = uri.scheme == "amqps"
18
- port = port_from_env || uri.port || (@tls ? 5671 : 5672)
18
+ port = port_from_env || uri.port || (tls ? 5671 : 5672)
19
19
  host = uri.host || "localhost"
20
20
  user = uri.user || "guest"
21
21
  password = uri.password || "guest"
@@ -25,10 +25,15 @@ module AMQP
25
25
  socket = Socket.tcp host, port, connect_timeout: 20, resolv_timeout: 5
26
26
  enable_tcp_keepalive(socket)
27
27
  if tls
28
+ cert_store = OpenSSL::X509::Store.new
29
+ cert_store.set_default_paths
28
30
  context = OpenSSL::SSL::SSLContext.new
31
+ context.cert_store = cert_store
29
32
  context.verify_mode = OpenSSL::SSL::VERIFY_PEER unless [false, "false", "none"].include? options[:verify_peer]
30
33
  socket = OpenSSL::SSL::SSLSocket.new(socket, context)
31
34
  socket.sync_close = true # closing the TLS socket also closes the TCP socket
35
+ socket.hostname = host # SNI host
36
+ socket.connect
32
37
  end
33
38
  channel_max, frame_max, heartbeat = establish(socket, user, password, vhost, **options)
34
39
  Connection.new(socket, channel_max, frame_max, heartbeat, read_loop_thread: read_loop_thread)
@@ -159,7 +164,8 @@ module AMQP
159
164
  channel = @channels.delete(channel_id)
160
165
  channel.closed!(reply_code, reply_text, classid, methodid)
161
166
  when 41 # channel#close-ok
162
- @channels[channel_id].reply [:channel_close_ok]
167
+ channel = @channels.delete(channel_id)
168
+ channel.reply [:channel_close_ok]
163
169
  else raise AMQP::Client::UnsupportedMethodFrame, class_id, method_id
164
170
  end
165
171
  when 40 # exchange
@@ -258,10 +264,7 @@ module AMQP
258
264
  @channels[channel_id].reply [:basic_get_empty]
259
265
  when 80 # ack
260
266
  delivery_tag, multiple = buf.unpack("@11 Q> C")
261
- @channels[channel_id].confirm [:ack, delivery_tag, multiple]
262
- when 90 # reject
263
- delivery_tag, requeue = buf.unpack("@11 Q> C")
264
- @channels[channel_id].confirm [:reject, delivery_tag, requeue == 1]
267
+ @channels[channel_id].confirm [:ack, delivery_tag, multiple == 1]
265
268
  when 111 # recover-ok
266
269
  @channels[channel_id].reply [:basic_recover_ok]
267
270
  when 120 # nack
@@ -329,7 +332,8 @@ module AMQP
329
332
 
330
333
  case method_id
331
334
  when 10 # connection#start
332
- properties = CLIENT_PROPERTIES.merge({ connection_name: options[:connection_name] })
335
+ conn_name = options[:connection_name] || $PROGRAM_NAME
336
+ properties = CLIENT_PROPERTIES.merge({ connection_name: conn_name })
333
337
  socket.write FrameBytes.connection_start_ok "\u0000#{user}\u0000#{password}", properties
334
338
  when 30 # connection#tune
335
339
  channel_max, frame_max, heartbeat = buf.unpack("@11 S> L> S>")
@@ -69,7 +69,8 @@ module AMQP
69
69
  end
70
70
 
71
71
  if expiration
72
- expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string")
72
+ expiration = expiration.to_s if expiration.is_a?(Integer)
73
+ expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string or integer")
73
74
 
74
75
  flags |= (1 << 8)
75
76
  arr << expiration.bytesize << expiration
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AMQP
4
4
  class Client
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amqp-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Hörberg
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-19 00:00:00.000000000 Z
11
+ date: 2021-08-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Work in progress
14
14
  email:
@@ -29,6 +29,7 @@ files:
29
29
  - amqp-client.gemspec
30
30
  - bin/console
31
31
  - bin/setup
32
+ - lib/amqp-client.rb
32
33
  - lib/amqp/client.rb
33
34
  - lib/amqp/client/channel.rb
34
35
  - lib/amqp/client/connection.rb