amqp-client 1.1.5 → 1.1.7

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: d3a9c50ecceb8a9d86bfea50d67957c1b063c47d35d85291b29e79b2631de262
4
- data.tar.gz: 055da4bc7ab51bac9f6839bf225c4f737b8aa766320609031ed68fa6b709cb88
3
+ metadata.gz: 7b2125fb98c3f0172a05bce1cbf8aa0b6530df76685522c4d25772fa1edd2dc4
4
+ data.tar.gz: 204891c1da627b813f37b160029ab55ecf58c91481949b02dc5bf3b163288d0c
5
5
  SHA512:
6
- metadata.gz: 52e1eabb4edbadae0d7998946024b8f82b5bd0d3feb2f079cc96643c7dc4774aec3eed6f00563c8b689fc07546a9b1443605fca0f573e2f23fe42b3ff128fd7d
7
- data.tar.gz: 46b81356c5bcbb4931f76f676e2416fddfcc1d396624fff35f61d117dfcb77d3ec63c16878253bc4a0810526adfd2267d93a43466217901fa21886112710845d
6
+ metadata.gz: 290ce1a3d301e1119056e39eeb309b55d7fa0752e7bc4b012777ccab7c506f62eef255bffc7b62258bf8a56a22ea46e19f42c00ad91f4b8441b7ecf414c29c8d
7
+ data.tar.gz: 22070957cc3c58d77f7d6c15476943a9f23ab34496dfc473618c4625c4f8531eddcfc8fb92086bf566aaf548d650045c65617ac68d73a49ec07c4ad4f971030a
@@ -1,6 +1,12 @@
1
1
  name: Ruby
2
2
 
3
- on: [push,pull_request]
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
4
10
 
5
11
  jobs:
6
12
  tests:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.1.7] - 2024-05-12
4
+
5
+ - Support for Connection.update-secret
6
+ - Allow sub-second connect_timeout
7
+ - Fixed: undefinied variable if message was returned and no on_return block was set
8
+
9
+ ## [1.1.6] - 2024-03-26
10
+
11
+ - Fixed: Channel#wait_for_confirms now waits for all confirms, in a thread safe way
12
+ - Changed: When server sends Connection.blocked the client isn't write blocked anymore, and can continue consume for instance. However, the on_blocked/unblocked callbacks should be used and manually stop publishing as the server otherwise will stop reading from the client socket.
13
+
3
14
  ## [1.1.5] - 2024-03-15
4
15
 
5
16
  - Fixed: Correctly reference the `UnexpectedFrameEnd` exception
data/CODEOWNERS ADDED
@@ -0,0 +1 @@
1
+ * @84codes/customer
data/Gemfile CHANGED
@@ -11,6 +11,8 @@ gem "minitest", "~> 5.0"
11
11
 
12
12
  gem "rubocop", "~> 1.7"
13
13
 
14
+ gem "rubocop-minitest", require: false
15
+
14
16
  gem "yard", require: false
15
17
 
16
- gem "rubocop-minitest", require: false
18
+ gem "redcarpet", require: false, platforms: :ruby
data/README.md CHANGED
@@ -123,7 +123,7 @@ Or install it yourself as:
123
123
 
124
124
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
125
125
 
126
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the changelog and version number in `version.rb`, make a commit, and then run `bundle exec rake release:source_control_push`, which will create a git tag for the version, push git commits and the created tag. GitHub Actions will then push the `.gem` file to [rubygems.org](https://rubygems.org).
126
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the changelog and version number in `version.rb`, make a commit, and then run `bundle exec rake release:source_control_push`, which will create a git tag for the version, push git commits and the created tag. GitHub Actions will then push the `.gem` file to [rubygems.org](https://rubygems.org/gems/amqp-client).
127
127
 
128
128
  ## Contributing
129
129
 
data/Rakefile CHANGED
@@ -22,4 +22,8 @@ RuboCop::RakeTask.new do |task|
22
22
  task.requires << "rubocop-minitest"
23
23
  end
24
24
 
25
+ require "yard"
26
+
27
+ YARD::Rake::YardocTask.new
28
+
25
29
  task default: [:test, *(:rubocop if RUBY_ENGINE == "ruby")]
@@ -22,8 +22,9 @@ module AMQP
22
22
  @open = false
23
23
  @on_return = nil
24
24
  @confirm = nil
25
- @unconfirmed = ::Queue.new
26
- @unconfirmed_empty = ::Queue.new
25
+ @unconfirmed = []
26
+ @unconfirmed_lock = Mutex.new
27
+ @unconfirmed_empty = ConditionVariable.new
27
28
  @basic_gets = ::Queue.new
28
29
  end
29
30
 
@@ -60,7 +61,7 @@ module AMQP
60
61
  expect :channel_close_ok
61
62
  @replies.close
62
63
  @basic_gets.close
63
- @unconfirmed_empty.close
64
+ @unconfirmed_lock.synchronize { @unconfirmed_empty.broadcast }
64
65
  @consumers.each_value(&:close)
65
66
  nil
66
67
  end
@@ -73,7 +74,7 @@ module AMQP
73
74
  @closed = [level, code, reason, classid, methodid]
74
75
  @replies.close
75
76
  @basic_gets.close
76
- @unconfirmed_empty.close
77
+ @unconfirmed_lock.synchronize { @unconfirmed_empty.broadcast }
77
78
  @consumers.each_value(&:close)
78
79
  @consumers.each_value(&:clear) # empty the queues too, messages can't be acked anymore
79
80
  nil
@@ -267,12 +268,15 @@ module AMQP
267
268
  when true then properties[:delivery_mode] = 2
268
269
  when false then properties[:delivery_mode] = 1
269
270
  end
270
-
271
+ if @confirm
272
+ @unconfirmed_lock.synchronize do
273
+ @unconfirmed.push @confirm += 1
274
+ end
275
+ end
271
276
  if body.bytesize.between?(1, body_max)
272
277
  write_bytes FrameBytes.basic_publish(id, exchange, routing_key, mandatory),
273
278
  FrameBytes.header(id, body.bytesize, properties),
274
279
  FrameBytes.body(id, body)
275
- @unconfirmed.push @confirm += 1 if @confirm
276
280
  return
277
281
  end
278
282
 
@@ -285,7 +289,6 @@ module AMQP
285
289
  write_bytes FrameBytes.body(id, body_part)
286
290
  pos += len
287
291
  end
288
- @unconfirmed.push @confirm += 1 if @confirm
289
292
  nil
290
293
  end
291
294
 
@@ -396,42 +399,44 @@ module AMQP
396
399
  # @param no_wait [Boolean] If false the method will block until the broker has confirmed the request
397
400
  # @return [nil]
398
401
  def confirm_select(no_wait: false)
399
- return if @confirm
402
+ return if @confirm # fast path
400
403
 
401
- write_bytes FrameBytes.confirm_select(@id, no_wait)
402
- expect :confirm_select_ok unless no_wait
403
- @confirm = 0
404
+ @unconfirmed_lock.synchronize do
405
+ # check again in case another thread already did this while we waited for the lock
406
+ return if @confirm
407
+
408
+ write_bytes FrameBytes.confirm_select(@id, no_wait)
409
+ expect :confirm_select_ok unless no_wait
410
+ @confirm = 0
411
+ end
404
412
  nil
405
413
  end
406
414
 
407
415
  # Block until all publishes messages are confirmed
408
- # @return [Boolean] True if all message where positivly acknowledged, false if not
416
+ # @return nil
409
417
  def wait_for_confirms
410
- return true if @unconfirmed.empty?
411
-
412
- ok = @unconfirmed_empty.pop
413
- raise Error::Closed.new(@id, *@closed) if ok.nil?
414
-
415
- ok
418
+ @unconfirmed_lock.synchronize do
419
+ until @unconfirmed.empty?
420
+ @unconfirmed_empty.wait(@unconfirmed_lock)
421
+ raise Error::Closed.new(@id, *@closed) if @closed
422
+ end
423
+ end
416
424
  end
417
425
 
418
426
  # Called by Connection when received ack/nack from broker
419
427
  # @api private
420
428
  def confirm(args)
421
- ack_or_nack, delivery_tag, multiple = *args
422
- loop do
423
- tag = @unconfirmed.pop(true)
424
- break if tag == delivery_tag
425
- next if multiple && tag < delivery_tag
426
-
427
- @unconfirmed << tag # requeue
428
- rescue ThreadError
429
- break
429
+ _ack_or_nack, delivery_tag, multiple = *args
430
+ @unconfirmed_lock.synchronize do
431
+ case multiple
432
+ when true
433
+ idx = @unconfirmed.index(delivery_tag) || raise("Delivery tag not found")
434
+ @unconfirmed.shift(idx + 1)
435
+ when false
436
+ @unconfirmed.delete(delivery_tag) || raise("Delivery tag not found")
437
+ end
438
+ @unconfirmed_empty.broadcast if @unconfirmed.empty?
430
439
  end
431
- return unless @unconfirmed.empty?
432
-
433
- ok = ack_or_nack == :ack
434
- @unconfirmed_empty.push(ok) until @unconfirmed_empty.num_waiting.zero?
435
440
  end
436
441
 
437
442
  # @!endgroup
@@ -517,7 +522,7 @@ module AMQP
517
522
  if @on_return
518
523
  Thread.new { @on_return.call(next_msg) }
519
524
  else
520
- warn "AMQP-Client message returned: #{msg.inspect}"
525
+ warn "AMQP-Client message returned: #{next_msg.inspect}"
521
526
  end
522
527
  elsif next_msg.consumer_tag.nil?
523
528
  @basic_gets.push next_msg
@@ -18,7 +18,7 @@ module AMQP
18
18
  # @option options [Boolean] connection_name (PROGRAM_NAME) Set a name for the connection to be able to identify
19
19
  # the client from the broker
20
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
21
+ # @option options [Float] connect_timeout (30) TCP connection timeout
22
22
  # @option options [Integer] heartbeat (0) Heartbeat timeout, defaults to 0 and relies on TCP keepalive instead
23
23
  # @option options [Integer] frame_max (131_072) Maximum frame size,
24
24
  # the smallest of the client's and the broker's values will be used
@@ -54,6 +54,16 @@ module AMQP
54
54
  Thread.new { read_loop } if read_loop_thread
55
55
  end
56
56
 
57
+ # Indicates that the server is blocking publishes.
58
+ # If the client keeps publishing the server will stop reading from the socket.
59
+ # Use the #on_blocked callback to get notified when the server is resource constrained.
60
+ # @see #on_blocked
61
+ # @see #on_unblocked
62
+ # @return [Bool]
63
+ def blocked?
64
+ !@blocked.nil?
65
+ end
66
+
57
67
  # Alias for {#initialize}
58
68
  # @see #initialize
59
69
  # @deprecated
@@ -122,6 +132,16 @@ module AMQP
122
132
  nil
123
133
  end
124
134
 
135
+ # Update authentication secret, for example when an OAuth backend is used
136
+ # @param secret [String] The new secret
137
+ # @param reason [String] A reason to update it
138
+ # @return [nil]
139
+ def update_secret(secret, reason)
140
+ write_bytes FrameBytes.update_secret(secret, reason)
141
+ expect(:update_secret_ok)
142
+ nil
143
+ end
144
+
125
145
  # True if the connection is closed
126
146
  # @return [Boolean]
127
147
  def closed?
@@ -241,12 +261,12 @@ module AMQP
241
261
  reason_len = buf.getbyte(4)
242
262
  reason = buf.byteslice(5, reason_len).force_encoding("utf-8")
243
263
  @blocked = reason
244
- @write_lock.lock
245
264
  @on_blocked.call(reason)
246
265
  when 61 # connection#unblocked
247
- @write_lock.unlock
248
266
  @blocked = nil
249
267
  @on_unblocked.call
268
+ when 71 # connection#update_secret_ok
269
+ @replies.push [:update_secret_ok]
250
270
  else raise Error::UnsupportedMethodFrame, class_id, method_id
251
271
  end
252
272
  when 20 # channel
@@ -406,7 +426,7 @@ module AMQP
406
426
  # @return [Socket]
407
427
  # @return [OpenSSL::SSL::SSLSocket]
408
428
  def open_socket(host, port, tls, options)
409
- connect_timeout = options.fetch(:connect_timeout, 30).to_i
429
+ connect_timeout = options.fetch(:connect_timeout, 30).to_f
410
430
  socket = Socket.tcp host, port, connect_timeout: connect_timeout
411
431
  keepalive = options.fetch(:keepalive, "").split(":", 3).map!(&:to_i)
412
432
  enable_tcp_keepalive(socket, *keepalive)
@@ -81,6 +81,20 @@ module AMQP
81
81
  ].pack("C S> L> S> S> C")
82
82
  end
83
83
 
84
+ def self.update_secret(secret, reason)
85
+ frame_size = 4 + 4 + secret.bytesize + 1 + reason.bytesize
86
+ [
87
+ 1, # type: method
88
+ 0, # channel id
89
+ frame_size, # frame size
90
+ 10, # class: connection
91
+ 70, # method: close-ok
92
+ secret.bytesize, secret,
93
+ reason.bytesize, reason,
94
+ 206 # frame end
95
+ ].pack("C S> L> S> S> L>a* Ca* C")
96
+ end
97
+
84
98
  def self.channel_open(id)
85
99
  [
86
100
  1, # type: method
@@ -3,6 +3,6 @@
3
3
  module AMQP
4
4
  class Client
5
5
  # Version of the client library
6
- VERSION = "1.1.5"
6
+ VERSION = "1.1.7"
7
7
  end
8
8
  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: 1.1.5
4
+ version: 1.1.7
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: 2024-03-15 00:00:00.000000000 Z
11
+ date: 2024-05-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Modern AMQP 0-9-1 Ruby client
14
14
  email:
@@ -26,6 +26,7 @@ files:
26
26
  - ".rubocop_todo.yml"
27
27
  - ".yardopts"
28
28
  - CHANGELOG.md
29
+ - CODEOWNERS
29
30
  - Gemfile
30
31
  - LICENSE.txt
31
32
  - README.md
@@ -67,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
68
  - !ruby/object:Gem::Version
68
69
  version: '0'
69
70
  requirements: []
70
- rubygems_version: 3.5.3
71
+ rubygems_version: 3.5.9
71
72
  signing_key:
72
73
  specification_version: 4
73
74
  summary: AMQP 0-9-1 client