nats-pure 2.3.0 → 2.5.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +10 -3
  4. data/lib/nats/client.rb +7 -2
  5. data/lib/nats/io/client.rb +304 -282
  6. data/lib/nats/io/errors.rb +2 -0
  7. data/lib/nats/io/jetstream/api.rb +54 -47
  8. data/lib/nats/io/jetstream/errors.rb +30 -14
  9. data/lib/nats/io/jetstream/js/config.rb +9 -3
  10. data/lib/nats/io/jetstream/js/header.rb +15 -9
  11. data/lib/nats/io/jetstream/js/status.rb +11 -5
  12. data/lib/nats/io/jetstream/js/sub.rb +4 -2
  13. data/lib/nats/io/jetstream/js.rb +10 -8
  14. data/lib/nats/io/jetstream/manager.rb +104 -83
  15. data/lib/nats/io/jetstream/msg/ack.rb +15 -9
  16. data/lib/nats/io/jetstream/msg/ack_methods.rb +24 -22
  17. data/lib/nats/io/jetstream/msg/metadata.rb +9 -7
  18. data/lib/nats/io/jetstream/msg.rb +11 -4
  19. data/lib/nats/io/jetstream/pull_subscription.rb +21 -10
  20. data/lib/nats/io/jetstream/push_subscription.rb +3 -1
  21. data/lib/nats/io/jetstream.rb +125 -54
  22. data/lib/nats/io/kv/api.rb +7 -3
  23. data/lib/nats/io/kv/bucket_status.rb +7 -5
  24. data/lib/nats/io/kv/errors.rb +25 -2
  25. data/lib/nats/io/kv/manager.rb +19 -10
  26. data/lib/nats/io/kv.rb +359 -22
  27. data/lib/nats/io/msg.rb +19 -19
  28. data/lib/nats/io/parser.rb +23 -23
  29. data/lib/nats/io/rails.rb +2 -0
  30. data/lib/nats/io/subscription.rb +25 -22
  31. data/lib/nats/io/version.rb +4 -2
  32. data/lib/nats/io/websocket.rb +10 -8
  33. data/lib/nats/nuid.rb +33 -22
  34. data/lib/nats/service/callbacks.rb +22 -0
  35. data/lib/nats/service/endpoint.rb +155 -0
  36. data/lib/nats/service/errors.rb +44 -0
  37. data/lib/nats/service/group.rb +37 -0
  38. data/lib/nats/service/monitoring.rb +108 -0
  39. data/lib/nats/service/stats.rb +52 -0
  40. data/lib/nats/service/status.rb +66 -0
  41. data/lib/nats/service/validator.rb +31 -0
  42. data/lib/nats/service.rb +121 -0
  43. data/lib/nats/utils/list.rb +26 -0
  44. data/lib/nats-pure.rb +5 -0
  45. data/lib/nats.rb +10 -6
  46. metadata +176 -5
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2016-2021 The NATS Authors
2
4
  # Licensed under the Apache License, Version 2.0 (the "License");
3
5
  # you may not use this file except in compliance with the License.
@@ -12,22 +14,20 @@
12
14
  # limitations under the License.
13
15
  #
14
16
 
15
- require_relative 'parser'
16
- require_relative 'version'
17
- require_relative 'errors'
18
- require_relative 'msg'
19
- require_relative 'subscription'
20
- require_relative 'jetstream'
21
- require_relative "rails" if defined?(::Rails::Engine)
22
-
23
- require 'nats/nuid'
24
- require 'thread'
25
- require 'socket'
26
- require 'json'
27
- require 'monitor'
28
- require 'uri'
29
- require 'securerandom'
30
- require 'concurrent'
17
+ require_relative "parser"
18
+ require_relative "version"
19
+ require_relative "errors"
20
+ require_relative "msg"
21
+ require_relative "subscription"
22
+ require_relative "jetstream"
23
+
24
+ require "nats/nuid"
25
+ require "socket"
26
+ require "json"
27
+ require "monitor"
28
+ require "uri"
29
+ require "securerandom"
30
+ require "concurrent"
31
31
 
32
32
  begin
33
33
  require "openssl"
@@ -47,7 +47,7 @@ module NATS
47
47
  # nc.publish("hello", "world")
48
48
  # nc.close
49
49
  #
50
- def connect(uri=nil, opts={})
50
+ def connect(uri = nil, opts = {})
51
51
  nc = NATS::Client.new
52
52
  nc.connect(uri, opts)
53
53
 
@@ -101,25 +101,25 @@ module NATS
101
101
  include MonitorMixin
102
102
  include Status
103
103
 
104
- attr_reader :status, :server_info, :server_pool, :options, :connected_server, :stats, :uri, :subscription_executor, :reloader
104
+ attr_reader :status, :server_info, :server_pool, :options, :stats, :uri, :subscription_executor, :reloader
105
105
 
106
- DEFAULT_PORT = { nats: 4222, ws: 80, wss: 443 }.freeze
107
- DEFAULT_URI = ("nats://localhost:#{DEFAULT_PORT[:nats]}".freeze)
106
+ DEFAULT_PORT = {nats: 4222, ws: 80, wss: 443}.freeze
107
+ DEFAULT_URI = "nats://localhost:#{DEFAULT_PORT[:nats]}".freeze
108
108
 
109
- CR_LF = ("\r\n".freeze)
110
- CR_LF_SIZE = (CR_LF.bytesize)
109
+ CR_LF = "\r\n"
110
+ CR_LF_SIZE = CR_LF.bytesize
111
111
 
112
- PING_REQUEST = ("PING#{CR_LF}".freeze)
113
- PONG_RESPONSE = ("PONG#{CR_LF}".freeze)
112
+ PING_REQUEST = "PING#{CR_LF}".freeze
113
+ PONG_RESPONSE = "PONG#{CR_LF}".freeze
114
114
 
115
- NATS_HDR_LINE = ("NATS/1.0#{CR_LF}".freeze)
115
+ NATS_HDR_LINE = "NATS/1.0#{CR_LF}".freeze
116
116
  STATUS_MSG_LEN = 3
117
- STATUS_HDR = ("Status".freeze)
118
- DESC_HDR = ("Description".freeze)
119
- NATS_HDR_LINE_SIZE = (NATS_HDR_LINE.bytesize)
117
+ STATUS_HDR = "Status"
118
+ DESC_HDR = "Description"
119
+ NATS_HDR_LINE_SIZE = NATS_HDR_LINE.bytesize
120
120
 
121
- SUB_OP = ('SUB'.freeze)
122
- EMPTY_MSG = (''.freeze)
121
+ SUB_OP = "SUB"
122
+ EMPTY_MSG = ""
123
123
 
124
124
  INSTANCES = ObjectSpace::WeakMap.new # tracks all alive client instances
125
125
  private_constant :INSTANCES
@@ -136,6 +136,8 @@ module NATS
136
136
  # Re-establish connection in a new process after forking to start new threads.
137
137
  def after_fork
138
138
  INSTANCES.each do |client|
139
+ next if client.closed?
140
+
139
141
  if client.options[:reconnect]
140
142
  was_connected = !client.disconnected?
141
143
  client.send(:close_connection, Status::DISCONNECTED, true)
@@ -171,7 +173,7 @@ module NATS
171
173
  @ping_interval_thread = nil
172
174
 
173
175
  # Info that we get from the server
174
- @server_info = { }
176
+ @server_info = {}
175
177
 
176
178
  # URI from server to which we are currently connected
177
179
  @uri = nil
@@ -180,7 +182,7 @@ module NATS
180
182
  @status = nil
181
183
 
182
184
  # Subscriptions
183
- @subs = { }
185
+ @subs = {}
184
186
  @ssid = 0
185
187
 
186
188
  # Ping interval
@@ -203,10 +205,10 @@ module NATS
203
205
  @last_err = nil
204
206
 
205
207
  # Async callbacks, no ops by default.
206
- @err_cb = proc { }
207
- @close_cb = proc { }
208
- @disconnect_cb = proc { }
209
- @reconnect_cb = proc { }
208
+ @err_cb = proc {}
209
+ @close_cb = proc {}
210
+ @disconnect_cb = proc {}
211
+ @reconnect_cb = proc {}
210
212
 
211
213
  # Secure TLS options
212
214
  @tls = nil
@@ -240,6 +242,9 @@ module NATS
240
242
  # Draining
241
243
  @drain_t = nil
242
244
 
245
+ # Service API
246
+ @_services = nil
247
+
243
248
  # Prepare for calling connect or automatic delayed connection
244
249
  parse_and_validate_options if uri || opts.any?
245
250
 
@@ -250,7 +255,7 @@ module NATS
250
255
  end
251
256
 
252
257
  # Prepare connecting to NATS, but postpone real connection until first usage.
253
- def connect(uri=nil, opts={})
258
+ def connect(uri = nil, opts = {})
254
259
  if uri || opts.any?
255
260
  @initial_uri = uri
256
261
  @initial_options = opts
@@ -268,6 +273,19 @@ module NATS
268
273
  self
269
274
  end
270
275
 
276
+ def force_reconnect
277
+ synchronize do
278
+ return true if reconnecting?
279
+
280
+ if closed? || draining? || disconnected?
281
+ raise NATS::IO::ConnectionClosedError
282
+ end
283
+
284
+ initiate_reconnect
285
+ true
286
+ end
287
+ end
288
+
271
289
  private def parse_and_validate_options
272
290
  # Reset these in case we have reconnected via fork.
273
291
  @server_pool = []
@@ -294,8 +312,8 @@ module NATS
294
312
  when String
295
313
  # Initialize TLS defaults in case any url is using it.
296
314
  srvs = opts[:servers] = process_uri(uri)
297
- if srvs.any? {|u| %w[tls wss].include? u.scheme } and !opts[:tls]
298
- opts[:tls] = { context: tls_context }
315
+ if srvs.any? { |u| %w[tls wss].include? u.scheme } && !opts[:tls]
316
+ opts[:tls] = {context: tls_context}
299
317
  end
300
318
  @single_url_connect_used = true if srvs.size == 1
301
319
  when Hash
@@ -313,16 +331,17 @@ module NATS
313
331
  opts[:max_outstanding_pings] = NATS::IO::DEFAULT_PING_MAX if opts[:max_outstanding_pings].nil?
314
332
 
315
333
  # Override with ENV
316
- opts[:verbose] = ENV['NATS_VERBOSE'].downcase == 'true' unless ENV['NATS_VERBOSE'].nil?
317
- opts[:pedantic] = ENV['NATS_PEDANTIC'].downcase == 'true' unless ENV['NATS_PEDANTIC'].nil?
318
- opts[:reconnect] = ENV['NATS_RECONNECT'].downcase == 'true' unless ENV['NATS_RECONNECT'].nil?
319
- opts[:reconnect_time_wait] = ENV['NATS_RECONNECT_TIME_WAIT'].to_i unless ENV['NATS_RECONNECT_TIME_WAIT'].nil?
320
- opts[:ignore_discovered_urls] = ENV['NATS_IGNORE_DISCOVERED_URLS'].downcase == 'true' unless ENV['NATS_IGNORE_DISCOVERED_URLS'].nil?
321
- opts[:max_reconnect_attempts] = ENV['NATS_MAX_RECONNECT_ATTEMPTS'].to_i unless ENV['NATS_MAX_RECONNECT_ATTEMPTS'].nil?
322
- opts[:ping_interval] = ENV['NATS_PING_INTERVAL'].to_i unless ENV['NATS_PING_INTERVAL'].nil?
323
- opts[:max_outstanding_pings] = ENV['NATS_MAX_OUTSTANDING_PINGS'].to_i unless ENV['NATS_MAX_OUTSTANDING_PINGS'].nil?
334
+ opts[:verbose] = ENV["NATS_VERBOSE"].downcase == "true" unless ENV["NATS_VERBOSE"].nil?
335
+ opts[:pedantic] = ENV["NATS_PEDANTIC"].downcase == "true" unless ENV["NATS_PEDANTIC"].nil?
336
+ opts[:reconnect] = ENV["NATS_RECONNECT"].downcase == "true" unless ENV["NATS_RECONNECT"].nil?
337
+ opts[:reconnect_time_wait] = ENV["NATS_RECONNECT_TIME_WAIT"].to_i unless ENV["NATS_RECONNECT_TIME_WAIT"].nil?
338
+ opts[:ignore_discovered_urls] = ENV["NATS_IGNORE_DISCOVERED_URLS"].downcase == "true" unless ENV["NATS_IGNORE_DISCOVERED_URLS"].nil?
339
+ opts[:max_reconnect_attempts] = ENV["NATS_MAX_RECONNECT_ATTEMPTS"].to_i unless ENV["NATS_MAX_RECONNECT_ATTEMPTS"].nil?
340
+ opts[:ping_interval] = ENV["NATS_PING_INTERVAL"].to_i unless ENV["NATS_PING_INTERVAL"].nil?
341
+ opts[:max_outstanding_pings] = ENV["NATS_MAX_OUTSTANDING_PINGS"].to_i unless ENV["NATS_MAX_OUTSTANDING_PINGS"].nil?
324
342
  opts[:connect_timeout] ||= NATS::IO::DEFAULT_CONNECT_TIMEOUT
325
343
  opts[:drain_timeout] ||= NATS::IO::DEFAULT_DRAIN_TIMEOUT
344
+ opts[:close_timeout] ||= NATS::IO::DEFAULT_CLOSE_TIMEOUT
326
345
  @options = opts
327
346
 
328
347
  # Process servers in the NATS cluster and pick one to connect
@@ -330,14 +349,14 @@ module NATS
330
349
  uris.shuffle! unless @options[:dont_randomize_servers]
331
350
  uris.each do |u|
332
351
  nats_uri = case u
333
- when URI
334
- u.dup
335
- else
336
- URI.parse(u)
337
- end
352
+ when URI
353
+ u.dup
354
+ else
355
+ URI.parse(u)
356
+ end
338
357
  @server_pool << {
339
- :uri => nats_uri,
340
- :hostname => nats_uri.hostname
358
+ uri: nats_uri,
359
+ hostname: nats_uri.hostname
341
360
  }
342
361
  end
343
362
 
@@ -354,7 +373,7 @@ module NATS
354
373
  @user_credentials ||= opts[:user_credentials]
355
374
  @nkeys_seed ||= opts[:nkeys_seed]
356
375
 
357
- setup_nkeys_connect if @user_credentials or @nkeys_seed
376
+ setup_nkeys_connect if @user_credentials || @nkeys_seed
358
377
 
359
378
  # Tokens, if set will take preference over the user@server uri token
360
379
  @auth_token ||= opts[:auth_token]
@@ -377,7 +396,7 @@ module NATS
377
396
  srv = select_next_server
378
397
 
379
398
  # Use the hostname from the server for TLS hostname verification.
380
- if client_using_secure_connection? and single_url_connect_used?
399
+ if client_using_secure_connection? && single_url_connect_used?
381
400
  # Always reuse the original hostname used to connect.
382
401
  @hostname ||= srv[:hostname]
383
402
  else
@@ -450,8 +469,8 @@ module NATS
450
469
  self
451
470
  end
452
471
 
453
- def publish(subject, msg=EMPTY_MSG, opt_reply=nil, **options, &blk)
454
- raise NATS::IO::BadSubject if !subject or subject.empty?
472
+ def publish(subject, msg = EMPTY_MSG, opt_reply = nil, **options, &blk)
473
+ raise NATS::IO::BadSubject if !subject || subject.empty?
455
474
  if options[:header]
456
475
  return publish_msg(NATS::Msg.new(subject: subject, data: msg, reply: opt_reply, header: options[:header]))
457
476
  end
@@ -468,10 +487,10 @@ module NATS
468
487
  # Publishes a NATS::Msg that may include headers.
469
488
  def publish_msg(msg)
470
489
  raise TypeError, "nats: expected NATS::Msg, got #{msg.class.name}" unless msg.is_a?(Msg)
471
- raise NATS::IO::BadSubject if !msg.subject or msg.subject.empty?
490
+ raise NATS::IO::BadSubject if !msg.subject || msg.subject.empty?
472
491
 
473
- msg.reply ||= ''
474
- msg.data ||= ''
492
+ msg.reply ||= "".dup
493
+ msg.data ||= "".dup
475
494
  msg_size = msg.data.bytesize
476
495
 
477
496
  # Accounting
@@ -479,7 +498,7 @@ module NATS
479
498
  @stats[:out_bytes] += msg_size
480
499
 
481
500
  if msg.header
482
- hdr = ''
501
+ hdr = "".dup
483
502
  hdr << NATS_HDR_LINE
484
503
  msg.header.each do |k, v|
485
504
  hdr << "#{k}: #{v}#{CR_LF}"
@@ -497,7 +516,7 @@ module NATS
497
516
 
498
517
  # Create subscription which is dispatched asynchronously
499
518
  # messages to a callback.
500
- def subscribe(subject, opts={}, &callback)
519
+ def subscribe(subject, opts = {}, &callback)
501
520
  raise NATS::IO::ConnectionDrainingError.new("nats: connection draining") if draining?
502
521
 
503
522
  sid = nil
@@ -508,7 +527,7 @@ module NATS
508
527
  sub.nc = self
509
528
  sub.sid = sid
510
529
  end
511
- opts[:pending_msgs_limit] ||= NATS::IO::DEFAULT_SUB_PENDING_MSGS_LIMIT
530
+ opts[:pending_msgs_limit] ||= NATS::IO::DEFAULT_SUB_PENDING_MSGS_LIMIT
512
531
  opts[:pending_bytes_limit] ||= NATS::IO::DEFAULT_SUB_PENDING_BYTES_LIMIT
513
532
 
514
533
  sub.subject = subject
@@ -516,7 +535,7 @@ module NATS
516
535
  sub.received = 0
517
536
  sub.queue = opts[:queue] if opts[:queue]
518
537
  sub.max = opts[:max] if opts[:max]
519
- sub.pending_msgs_limit = opts[:pending_msgs_limit]
538
+ sub.pending_msgs_limit = opts[:pending_msgs_limit]
520
539
  sub.pending_bytes_limit = opts[:pending_bytes_limit]
521
540
  sub.pending_queue = SizedQueue.new(sub.pending_msgs_limit)
522
541
  sub.processing_concurrency = opts[:processing_concurrency] if opts.key?(:processing_concurrency)
@@ -540,8 +559,8 @@ module NATS
540
559
  # It times out in case the request is not retrieved within the
541
560
  # specified deadline.
542
561
  # If given a callback, then the request happens asynchronously.
543
- def request(subject, payload="", **opts, &blk)
544
- raise NATS::IO::BadSubject if !subject or subject.empty?
562
+ def request(subject, payload = "", **opts, &blk)
563
+ raise NATS::IO::BadSubject if !subject || subject.empty?
545
564
 
546
565
  # If a block was given then fallback to method using auto unsubscribe.
547
566
  return old_request(subject, payload, opts, &blk) if blk
@@ -572,7 +591,7 @@ module NATS
572
591
  # Publish request and wait for reply.
573
592
  publish(subject, payload, inbox)
574
593
  begin
575
- MonotonicTime::with_nats_timeout(timeout) do
594
+ MonotonicTime.with_nats_timeout(timeout) do
576
595
  @resp_sub.synchronize do
577
596
  future.wait(timeout)
578
597
  end
@@ -589,7 +608,7 @@ module NATS
589
608
  @resp_map.delete(token)
590
609
  end
591
610
 
592
- if response and response.header
611
+ if response&.header
593
612
  status = response.header[STATUS_HDR]
594
613
  raise NATS::IO::NoRespondersError if status == "503"
595
614
  end
@@ -600,7 +619,7 @@ module NATS
600
619
  # request_msg makes a NATS request using a NATS::Msg that may include headers.
601
620
  def request_msg(msg, **opts)
602
621
  raise TypeError, "nats: expected NATS::Msg, got #{msg.class.name}" unless msg.is_a?(Msg)
603
- raise NATS::IO::BadSubject if !msg.subject or msg.subject.empty?
622
+ raise NATS::IO::BadSubject if !msg.subject || msg.subject.empty?
604
623
 
605
624
  token = nil
606
625
  inbox = nil
@@ -620,13 +639,13 @@ module NATS
620
639
  @resp_map[token][:future] = future
621
640
  end
622
641
  msg.reply = inbox
623
- msg.data ||= ''
624
- msg_size = msg.data.bytesize
642
+ msg.data ||= ""
643
+ msg.data.bytesize
625
644
 
626
645
  # Publish request and wait for reply.
627
646
  publish_msg(msg)
628
647
  begin
629
- MonotonicTime::with_nats_timeout(timeout) do
648
+ MonotonicTime.with_nats_timeout(timeout) do
630
649
  @resp_sub.synchronize do
631
650
  future.wait(timeout)
632
651
  end
@@ -643,7 +662,7 @@ module NATS
643
662
  @resp_map.delete(token)
644
663
  end
645
664
 
646
- if response and response.header
665
+ if response&.header
647
666
  status = response.header[STATUS_HDR]
648
667
  raise NATS::IO::NoRespondersError if status == "503"
649
668
  end
@@ -655,7 +674,7 @@ module NATS
655
674
  # expecting a single response or raising a timeout in case the request
656
675
  # is not retrieved within the specified deadline.
657
676
  # If given a callback, then the request happens asynchronously.
658
- def old_request(subject, payload, opts={}, &blk)
677
+ def old_request(subject, payload, opts = {}, &blk)
659
678
  return unless subject
660
679
  inbox = new_inbox
661
680
 
@@ -704,13 +723,13 @@ module NATS
704
723
  # Publish the request and then wait for the response...
705
724
  publish(subject, payload, inbox)
706
725
 
707
- MonotonicTime::with_nats_timeout(timeout) do
726
+ MonotonicTime.with_nats_timeout(timeout) do
708
727
  future.wait(timeout)
709
728
  end
710
729
  end
711
730
  response = sub.response
712
731
 
713
- if response and response.header
732
+ if response&.header
714
733
  status = response.header[STATUS_HDR]
715
734
  raise NATS::IO::NoRespondersError if status == "503"
716
735
  end
@@ -719,7 +738,7 @@ module NATS
719
738
  end
720
739
 
721
740
  # Send a ping and wait for a pong back within a timeout.
722
- def flush(timeout=10)
741
+ def flush(timeout = 10)
723
742
  # Schedule sending a PING, and block until we receive PONG back,
724
743
  # or raise a timeout in case the response is past the deadline.
725
744
  pong = @pongs.new_cond
@@ -729,18 +748,18 @@ module NATS
729
748
  # Flush once pong future has been prepared
730
749
  @pending_queue << PING_REQUEST
731
750
  @flush_queue << :ping
732
- MonotonicTime::with_nats_timeout(timeout) do
751
+ MonotonicTime.with_nats_timeout(timeout) do
733
752
  pong.wait(timeout)
734
753
  end
735
754
  end
736
755
  end
737
756
 
738
- alias :servers :server_pool
757
+ alias_method :servers, :server_pool
739
758
 
740
759
  # discovered_servers returns the NATS Servers that have been discovered
741
760
  # via INFO protocol updates.
742
761
  def discovered_servers
743
- servers.select {|s| s[:discovered] }
762
+ servers.select { |s| s[:discovered] }
744
763
  end
745
764
 
746
765
  # Close connection to NATS, flushing in case connection is alive
@@ -781,7 +800,7 @@ module NATS
781
800
  end
782
801
 
783
802
  def draining?
784
- if @status == DRAINING_PUBS or @status == DRAINING_SUBS
803
+ if (@status == DRAINING_PUBS) || (@status == DRAINING_SUBS)
785
804
  return true
786
805
  end
787
806
 
@@ -834,12 +853,16 @@ module NATS
834
853
  # @option params [String] :domain JetStream Domain to use for the requests.
835
854
  # @option params [Float] :timeout Default timeout to use for JS requests.
836
855
  # @return [NATS::JetStream]
837
- def jetstream(opts={})
856
+ def jetstream(opts = {})
838
857
  ::NATS::JetStream.new(self, opts)
839
858
  end
840
859
  alias_method :JetStream, :jetstream
841
860
  alias_method :jsm, :jetstream
842
861
 
862
+ def services
863
+ synchronize { @_services ||= Services.new(self) }
864
+ end
865
+
843
866
  private
844
867
 
845
868
  def validate_settings!
@@ -856,10 +879,8 @@ module NATS
856
879
  # so has to be done under the lock.
857
880
  synchronize do
858
881
  # Symbolize keys from parsed info line
859
- @server_info = parsed_info.reduce({}) do |info, (k,v)|
882
+ @server_info = parsed_info.each_with_object({}) do |(k, v), info|
860
883
  info[k.to_sym] = v
861
-
862
- info
863
884
  end
864
885
 
865
886
  # Detect any announced server that we might not be aware of...
@@ -878,7 +899,7 @@ module NATS
878
899
  srv[:uri].hostname == u.hostname && srv[:uri].port == u.port
879
900
  end
880
901
 
881
- if not present
902
+ if !present
882
903
  # Let explicit user and pass options set the credentials.
883
904
  u.user = options[:user] if options[:user]
884
905
  u.password = options[:pass] if options[:pass]
@@ -890,7 +911,7 @@ module NATS
890
911
  end
891
912
 
892
913
  # NOTE: Auto discovery won't work here when TLS host verification is enabled.
893
- srv = { :uri => u, :reconnect_attempts => 0, :discovered => true, :hostname => u.hostname }
914
+ srv = {uri: u, reconnect_attempts: 0, discovered: true, hostname: u.hostname}
894
915
  srvs << srv
895
916
  end
896
917
  end
@@ -913,13 +934,13 @@ module NATS
913
934
  # Check if the first line has an inline status and description.
914
935
  if lines.count > 0
915
936
  status_hdr = lines.first.rstrip
916
- status = status_hdr.slice(NATS_HDR_LINE_SIZE-1, STATUS_MSG_LEN)
937
+ status = status_hdr.slice(NATS_HDR_LINE_SIZE - 1, STATUS_MSG_LEN)
917
938
 
918
- if status and !status.empty?
939
+ if status && !status.empty?
919
940
  hdr[STATUS_HDR] = status
920
941
 
921
- if NATS_HDR_LINE_SIZE+2 < status_hdr.bytesize
922
- desc = status_hdr.slice(NATS_HDR_LINE_SIZE+STATUS_MSG_LEN, status_hdr.bytesize)
942
+ if NATS_HDR_LINE_SIZE + 2 < status_hdr.bytesize
943
+ desc = status_hdr.slice(NATS_HDR_LINE_SIZE + STATUS_MSG_LEN, status_hdr.bytesize)
923
944
  hdr[DESC_HDR] = desc unless desc.empty?
924
945
  end
925
946
  end
@@ -932,7 +953,7 @@ module NATS
932
953
  hdr[key] = value
933
954
  end
934
955
  rescue => e
935
- err = e
956
+ e
936
957
  end
937
958
  end
938
959
 
@@ -945,7 +966,7 @@ module NATS
945
966
  # Take first pong wait and signal any flush in case there was one
946
967
  @pongs.synchronize do
947
968
  pong = @pongs.pop
948
- pong.signal unless pong.nil?
969
+ pong&.signal
949
970
  end
950
971
  @pings_outstanding -= 1
951
972
  @pongs_received += 1
@@ -965,10 +986,9 @@ module NATS
965
986
  # while holding the lock.
966
987
  e = synchronize do
967
988
  current = server_pool.first
968
- case
969
- when err =~ /'Stale Connection'/
989
+ if err =~ /'Stale Connection'/
970
990
  @last_err = NATS::IO::StaleConnectionError.new(err)
971
- when current && current[:auth_required]
991
+ elsif current && current[:auth_required]
972
992
  # We cannot recover from auth errors so mark it to avoid
973
993
  # retrying to unecessarily next time.
974
994
  current[:error_received] = true
@@ -1018,8 +1038,8 @@ module NATS
1018
1038
  elsif sub.pending_queue
1019
1039
  # Async subscribers use a sized queue for processing
1020
1040
  # and should be able to consume messages in parallel.
1021
- if sub.pending_queue.size >= sub.pending_msgs_limit \
1022
- or sub.pending_size >= sub.pending_bytes_limit then
1041
+ if (sub.pending_queue.size >= sub.pending_msgs_limit) \
1042
+ || (sub.pending_size >= sub.pending_bytes_limit)
1023
1043
  err = NATS::IO::SlowConsumer.new("nats: slow consumer, messages dropped")
1024
1044
  else
1025
1045
  hdr = process_hdr(header)
@@ -1033,10 +1053,12 @@ module NATS
1033
1053
  end
1034
1054
  end
1035
1055
 
1036
- synchronize do
1037
- @last_err = err
1038
- err_cb_call(self, err, sub) if @err_cb
1039
- end if err
1056
+ if err
1057
+ synchronize do
1058
+ @last_err = err
1059
+ err_cb_call(self, err, sub) if @err_cb
1060
+ end
1061
+ end
1040
1062
  end
1041
1063
 
1042
1064
  def select_next_server
@@ -1102,7 +1124,7 @@ module NATS
1102
1124
 
1103
1125
  # Auto unsubscribes the server by sending UNSUB command and throws away
1104
1126
  # subscription in case already present and has received enough messages.
1105
- def unsubscribe(sub, opt_max=nil)
1127
+ def unsubscribe(sub, opt_max = nil)
1106
1128
  sid = nil
1107
1129
  closed = nil
1108
1130
  sub.synchronize do
@@ -1119,7 +1141,7 @@ module NATS
1119
1141
  return unless sub
1120
1142
  synchronize do
1121
1143
  sub.max = opt_max
1122
- @subs.delete(sid) unless (sub.max && (sub.received < sub.max))
1144
+ @subs.delete(sid) unless sub.max && (sub.received < sub.max)
1123
1145
  end
1124
1146
 
1125
1147
  sub.synchronize do
@@ -1139,8 +1161,9 @@ module NATS
1139
1161
  send_command("UNSUB #{sid}#{CR_LF}")
1140
1162
  @flush_queue << :drain
1141
1163
 
1164
+ sub.synchronize { sub.drained = true }
1142
1165
  synchronize { sub = @subs[sid] }
1143
- return unless sub
1166
+ nil unless sub
1144
1167
  end
1145
1168
 
1146
1169
  def do_drain
@@ -1157,16 +1180,16 @@ module NATS
1157
1180
  force_flush!
1158
1181
 
1159
1182
  # Wait until all subs have no pending messages.
1160
- drain_timeout = MonotonicTime::now + @options[:drain_timeout]
1183
+ drain_timeout = MonotonicTime.now + @options[:drain_timeout]
1161
1184
  to_delete = []
1162
1185
 
1163
1186
  loop do
1164
- break if MonotonicTime::now > drain_timeout
1187
+ break if MonotonicTime.now > drain_timeout
1165
1188
  sleep 0.1
1166
1189
 
1167
1190
  # Wait until all subs are done.
1168
1191
  @subs.each do |_, sub|
1169
- if sub != @resp_sub and sub.pending_queue.size == 0
1192
+ if (sub != @resp_sub) && (sub.pending_queue.size == 0)
1170
1193
  to_delete << sub
1171
1194
  end
1172
1195
  end
@@ -1179,7 +1202,7 @@ module NATS
1179
1202
 
1180
1203
  # Wait until only the resp mux is remaining or there are no subscriptions.
1181
1204
  if @subs.count == 1
1182
- sid, sub = @subs.first
1205
+ _, sub = @subs.first
1183
1206
  if sub == @resp_sub
1184
1207
  break
1185
1208
  end
@@ -1191,7 +1214,7 @@ module NATS
1191
1214
  subscription_executor.shutdown
1192
1215
  subscription_executor.wait_for_termination(@options[:drain_timeout])
1193
1216
 
1194
- if MonotonicTime::now > drain_timeout
1217
+ if MonotonicTime.now > drain_timeout
1195
1218
  e = NATS::IO::DrainTimeoutError.new("nats: draining connection timed out")
1196
1219
  err_cb_call(self, e, nil) if @err_cb
1197
1220
  end
@@ -1228,27 +1251,26 @@ module NATS
1228
1251
 
1229
1252
  def connect_command
1230
1253
  cs = {
1231
- :verbose => @options[:verbose],
1232
- :pedantic => @options[:pedantic],
1233
- :lang => NATS::IO::LANG,
1234
- :version => NATS::IO::VERSION,
1235
- :protocol => NATS::IO::PROTOCOL
1254
+ verbose: @options[:verbose],
1255
+ pedantic: @options[:pedantic],
1256
+ lang: NATS::IO::LANG,
1257
+ version: NATS::IO::VERSION,
1258
+ protocol: NATS::IO::PROTOCOL
1236
1259
  }
1237
1260
  cs[:name] = @options[:name] if @options[:name]
1238
1261
 
1239
- case
1240
- when auth_connection?
1262
+ if auth_connection?
1241
1263
  if @uri.password
1242
1264
  cs[:user] = @uri.user
1243
1265
  cs[:pass] = @uri.password
1244
1266
  else
1245
1267
  cs[:auth_token] = @uri.user
1246
1268
  end
1247
- when @user_jwt_cb && @signature_cb
1269
+ elsif @user_jwt_cb && @signature_cb
1248
1270
  nonce = @server_info[:nonce]
1249
1271
  cs[:jwt] = @user_jwt_cb.call
1250
1272
  cs[:sig] = @signature_cb.call(nonce)
1251
- when @user_nkey_cb && @signature_cb
1273
+ elsif @user_nkey_cb && @signature_cb
1252
1274
  nonce = @server_info[:nonce]
1253
1275
  cs[:nkey] = @user_nkey_cb.call
1254
1276
  cs[:sig] = @signature_cb.call(nonce)
@@ -1259,10 +1281,10 @@ module NATS
1259
1281
  if @server_info[:headers]
1260
1282
  cs[:headers] = @server_info[:headers]
1261
1283
  cs[:no_responders] = if @options[:no_responders] == false
1262
- @options[:no_responders]
1263
- else
1264
- @server_info[:headers]
1265
- end
1284
+ @options[:no_responders]
1285
+ else
1286
+ @server_info[:headers]
1287
+ end
1266
1288
  end
1267
1289
 
1268
1290
  "CONNECT #{cs.to_json}#{CR_LF}"
@@ -1282,30 +1304,8 @@ module NATS
1282
1304
 
1283
1305
  # If we were connected and configured to reconnect,
1284
1306
  # then trigger disconnect and start reconnection logic
1285
- if connected? and should_reconnect?
1286
- @status = RECONNECTING
1287
- @io.close if @io
1288
- @io = nil
1289
-
1290
- # TODO: Reconnecting pending buffer?
1291
-
1292
- # Do reconnect under a different thread than the one
1293
- # in which we got the error.
1294
- Thread.new do
1295
- begin
1296
- # Abort currently running reads in case they're around
1297
- # FIXME: There might be more graceful way here...
1298
- @read_loop_thread.exit if @read_loop_thread.alive?
1299
- @flusher_thread.exit if @flusher_thread.alive?
1300
- @ping_interval_thread.exit if @ping_interval_thread.alive?
1301
-
1302
- attempt_reconnect
1303
- rescue NATS::IO::NoServersError => e
1304
- @last_err = e
1305
- close
1306
- end
1307
- end
1308
-
1307
+ if connected? && should_reconnect?
1308
+ initiate_reconnect
1309
1309
  Thread.exit
1310
1310
  return
1311
1311
  end
@@ -1318,29 +1318,52 @@ module NATS
1318
1318
  close
1319
1319
  end
1320
1320
 
1321
+ def initiate_reconnect
1322
+ @status = RECONNECTING
1323
+ @io&.close
1324
+ @io = nil
1325
+
1326
+ # TODO: Reconnecting pending buffer?
1327
+
1328
+ # Do reconnect under a different thread than the one
1329
+ # in which we got the error.
1330
+ Thread.new do
1331
+ # Abort currently running reads in case they're around
1332
+ # FIXME: There might be more graceful way here...
1333
+ @read_loop_thread.exit if @read_loop_thread.alive?
1334
+ @flusher_thread.exit if @flusher_thread.alive?
1335
+ @ping_interval_thread.exit if @ping_interval_thread.alive?
1336
+
1337
+ @subscription_executor.shutdown
1338
+
1339
+ attempt_reconnect
1340
+ rescue NATS::IO::NoServersError => e
1341
+ @last_err = e
1342
+ close
1343
+ end
1344
+ end
1345
+
1321
1346
  # Gathers data from the socket and sends it to the parser.
1322
1347
  def read_loop
1323
1348
  loop do
1324
- begin
1325
- should_bail = synchronize do
1326
- # FIXME: In case of reconnect as well?
1327
- @status == CLOSED or @status == RECONNECTING
1328
- end
1329
- if !@io or @io.closed? or should_bail
1330
- return
1331
- end
1332
-
1333
- # TODO: Remove timeout and just wait to be ready
1334
- data = @io.read(NATS::IO::MAX_SOCKET_READ_BYTES)
1335
- @parser.parse(data) if data
1336
- rescue Errno::ETIMEDOUT
1337
- # FIXME: We do not really need a timeout here...
1338
- retry
1339
- rescue => e
1340
- # In case of reading/parser errors, trigger
1341
- # reconnection logic in case desired.
1342
- process_op_error(e)
1349
+ should_bail = synchronize do
1350
+ # FIXME: In case of reconnect as well?
1351
+ @status == CLOSED or @status == RECONNECTING
1352
+ end
1353
+ if !@io || @io.closed? || should_bail
1354
+ return
1343
1355
  end
1356
+
1357
+ # TODO: Remove timeout and just wait to be ready
1358
+ data = @io.read(NATS::IO::MAX_SOCKET_READ_BYTES)
1359
+ @parser.parse(data) if data
1360
+ rescue Errno::ETIMEDOUT
1361
+ # FIXME: We do not really need a timeout here...
1362
+ retry
1363
+ rescue => e
1364
+ # In case of reading/parser errors, trigger
1365
+ # reconnection logic in case desired.
1366
+ process_op_error(e)
1344
1367
  end
1345
1368
  end
1346
1369
 
@@ -1352,7 +1375,7 @@ module NATS
1352
1375
  @flush_queue.pop
1353
1376
 
1354
1377
  should_bail = synchronize do
1355
- (@status != CONNECTED && !draining? ) || @status == CONNECTING
1378
+ (@status != CONNECTED && !draining?) || @status == CONNECTING
1356
1379
  end
1357
1380
  return if should_bail
1358
1381
 
@@ -1373,17 +1396,19 @@ module NATS
1373
1396
  # until reaching the max pending queue size.
1374
1397
  cmds = []
1375
1398
  cmds << @pending_queue.pop until @pending_queue.empty?
1376
- begin
1377
- @io.write(cmds.join) unless cmds.empty?
1378
- rescue => e
1379
- synchronize do
1380
- @last_err = e
1381
- err_cb_call(self, e, nil) if @err_cb
1382
- end
1399
+ if @io
1400
+ begin
1401
+ @io.write(cmds.join) unless cmds.empty?
1402
+ rescue => e
1403
+ synchronize do
1404
+ @last_err = e
1405
+ err_cb_call(self, e, nil) if @err_cb
1406
+ end
1383
1407
 
1384
- process_op_error(e)
1385
- return
1386
- end if @io
1408
+ process_op_error(e)
1409
+ nil
1410
+ end
1411
+ end
1387
1412
  end
1388
1413
 
1389
1414
  def ping_interval_loop
@@ -1409,27 +1434,26 @@ module NATS
1409
1434
  def process_connect_init
1410
1435
  # FIXME: Can receive PING as well here in recent versions.
1411
1436
  line = @io.read_line(options[:connect_timeout])
1412
- if !line or line.empty?
1437
+ if !line || line.empty?
1413
1438
  raise NATS::IO::ConnectError.new("nats: protocol exception, INFO not received")
1414
1439
  end
1415
1440
 
1416
- if match = line.match(NATS::Protocol::INFO)
1441
+ if (match = line.match(NATS::Protocol::INFO))
1417
1442
  info_json = match.captures.first
1418
1443
  process_info(info_json)
1419
1444
  else
1420
1445
  raise NATS::IO::ConnectError.new("nats: protocol exception, INFO not valid")
1421
1446
  end
1422
1447
 
1423
- case
1424
- when (server_using_secure_connection? and client_using_secure_connection?)
1448
+ if server_using_secure_connection? && client_using_secure_connection?
1425
1449
  @io.setup_tls!
1426
1450
  # Server > v2.9.19 returns tls_required regardless of no_tls for WebSocket config being used so need to check URI.
1427
- when (server_using_secure_connection? and !client_using_secure_connection? and @uri.scheme != "ws")
1428
- raise NATS::IO::ConnectError.new('TLS/SSL required by server')
1451
+ elsif server_using_secure_connection? && !client_using_secure_connection? && (@uri.scheme != "ws")
1452
+ raise NATS::IO::ConnectError.new("TLS/SSL required by server")
1429
1453
  # Server < v2.9.19 requiring TLS/SSL over websocket but not requiring it over standard protocol
1430
1454
  # doesn't send `tls_required` in its INFO so we need to check the URI scheme for WebSocket.
1431
- when (client_using_secure_connection? and !server_using_secure_connection? and @uri.scheme != "wss")
1432
- raise NATS::IO::ConnectError.new('TLS/SSL not supported by server')
1455
+ elsif client_using_secure_connection? && !server_using_secure_connection? && (@uri.scheme != "wss")
1456
+ raise NATS::IO::ConnectError.new("TLS/SSL not supported by server")
1433
1457
  else
1434
1458
  # Otherwise, use a regular connection.
1435
1459
  end
@@ -1450,6 +1474,7 @@ module NATS
1450
1474
 
1451
1475
  case next_op
1452
1476
  when NATS::Protocol::PONG
1477
+ # do nothing
1453
1478
  when NATS::Protocol::ERR
1454
1479
  if @server_info[:auth_required]
1455
1480
  raise NATS::IO::AuthError.new($1)
@@ -1463,7 +1488,7 @@ module NATS
1463
1488
 
1464
1489
  # Reconnect logic, this is done while holding the lock.
1465
1490
  def attempt_reconnect
1466
- @disconnect_cb.call(@last_err) if @disconnect_cb
1491
+ @disconnect_cb&.call(@last_err)
1467
1492
 
1468
1493
  # Clear sticky error
1469
1494
  @last_err = nil
@@ -1474,7 +1499,7 @@ module NATS
1474
1499
  srv = select_next_server
1475
1500
 
1476
1501
  # Set hostname to use for TLS hostname verification
1477
- if client_using_secure_connection? and single_url_connect_used?
1502
+ if client_using_secure_connection? && single_url_connect_used?
1478
1503
  # Reuse original hostname name in case of using TLS.
1479
1504
  @hostname ||= srv[:hostname]
1480
1505
  else
@@ -1540,10 +1565,10 @@ module NATS
1540
1565
 
1541
1566
  # Dispatch the reconnected callback while holding lock
1542
1567
  # which we should have already
1543
- @reconnect_cb.call if @reconnect_cb
1568
+ @reconnect_cb&.call
1544
1569
  end
1545
1570
 
1546
- def close_connection(conn_status, do_cbs=true)
1571
+ def close_connection(conn_status, do_cbs = true)
1547
1572
  synchronize do
1548
1573
  @connect_called = false
1549
1574
  if @status == CLOSED
@@ -1558,18 +1583,21 @@ module NATS
1558
1583
 
1559
1584
  # FIXME: More graceful way of handling the following?
1560
1585
  # Ensure ping interval and flusher are not running anymore
1561
- if @ping_interval_thread and @ping_interval_thread.alive?
1586
+ if @ping_interval_thread&.alive?
1562
1587
  @ping_interval_thread.exit
1563
1588
  end
1564
1589
 
1565
- if @flusher_thread and @flusher_thread.alive?
1590
+ if @flusher_thread&.alive?
1566
1591
  @flusher_thread.exit
1567
1592
  end
1568
1593
 
1569
- if @read_loop_thread and @read_loop_thread.alive?
1594
+ if @read_loop_thread&.alive?
1570
1595
  @read_loop_thread.exit
1571
1596
  end
1572
1597
 
1598
+ @subscription_executor&.shutdown
1599
+ @subscription_executor&.wait_for_termination(options[:close_timeout])
1600
+
1573
1601
  # TODO: Delete any other state which we are not using here too.
1574
1602
  synchronize do
1575
1603
  @pongs.synchronize do
@@ -1581,24 +1609,26 @@ module NATS
1581
1609
 
1582
1610
  # Try to write any pending flushes in case
1583
1611
  # we have a connection then close it.
1584
- should_flush = (@pending_queue && @io && @io.socket && !@io.closed?)
1585
- begin
1586
- cmds = []
1587
- cmds << @pending_queue.pop until @pending_queue.empty?
1588
-
1589
- # FIXME: Fails when empty on TLS connection?
1590
- @io.write(cmds.join) unless cmds.empty?
1591
- rescue => e
1592
- @last_err = e
1593
- err_cb_call(self, e, nil) if @err_cb
1594
- end if should_flush
1612
+ should_flush = @pending_queue && @io && @io.socket && !@io.closed?
1613
+ if should_flush
1614
+ begin
1615
+ cmds = []
1616
+ cmds << @pending_queue.pop until @pending_queue.empty?
1617
+
1618
+ # FIXME: Fails when empty on TLS connection?
1619
+ @io.write(cmds.join) unless cmds.empty?
1620
+ rescue => e
1621
+ @last_err = e
1622
+ err_cb_call(self, e, nil) if @err_cb
1623
+ end
1624
+ end
1595
1625
 
1596
1626
  # Destroy any remaining subscriptions.
1597
1627
  @subs.clear
1598
1628
 
1599
1629
  if do_cbs
1600
- @disconnect_cb.call(@last_err) if @disconnect_cb
1601
- @close_cb.call if @close_cb
1630
+ @disconnect_cb&.call(@last_err)
1631
+ @close_cb&.call
1602
1632
  end
1603
1633
 
1604
1634
  @status = conn_status
@@ -1630,8 +1660,11 @@ module NATS
1630
1660
 
1631
1661
  # Subscription handling thread pool
1632
1662
  @subscription_executor = Concurrent::ThreadPoolExecutor.new(
1633
- name: 'nats:subscription', # threads will be given names like nats:subscription-worker-1
1663
+ name: "nats:subscription", # threads will be given names like nats:subscription-worker-1
1634
1664
  max_threads: NATS::IO::DEFAULT_TOTAL_SUB_CONCURRENCY,
1665
+ # JRuby has a bug on certain Java version of not creating new threads:
1666
+ # https://github.com/ruby-concurrency/concurrent-ruby/issues/864
1667
+ min_threads: defined?(JRUBY_VERSION) ? 2 : 0
1635
1668
  )
1636
1669
  end
1637
1670
 
@@ -1639,7 +1672,7 @@ module NATS
1639
1672
  # for the new style request response.
1640
1673
  def start_resp_mux_sub!
1641
1674
  @resp_sub_prefix = new_inbox
1642
- @resp_map = Hash.new { |h,k| h[k] = { }}
1675
+ @resp_map = Hash.new { |h, k| h[k] = {} }
1643
1676
 
1644
1677
  @resp_sub = Subscription.new
1645
1678
  @resp_sub.subject = "#{@resp_sub_prefix}.*"
@@ -1653,7 +1686,7 @@ module NATS
1653
1686
  @resp_sub.callback = proc do |msg|
1654
1687
  # Pick the token and signal the request under the mutex
1655
1688
  # from the subscription itself.
1656
- token = msg.subject.split('.').last
1689
+ token = msg.subject.split(".").last
1657
1690
  future = nil
1658
1691
  synchronize do
1659
1692
  future = @resp_map[token][:future]
@@ -1685,7 +1718,7 @@ module NATS
1685
1718
  return false if server[:error_received]
1686
1719
 
1687
1720
  # We will retry a number of times to reconnect to a server.
1688
- return server[:reconnect_attempts] <= @options[:max_reconnect_attempts]
1721
+ server[:reconnect_attempts] <= @options[:max_reconnect_attempts]
1689
1722
  end
1690
1723
 
1691
1724
  def should_delay_connect?(server)
@@ -1702,35 +1735,34 @@ module NATS
1702
1735
 
1703
1736
  def create_socket
1704
1737
  socket_class = case @uri.scheme
1705
- when "nats", "tls"
1706
- NATS::IO::Socket
1707
- when "ws", "wss"
1708
- require_relative 'websocket'
1709
- NATS::IO::WebSocket
1710
- else
1711
- raise NotImplementedError, "#{@uri.scheme} protocol is not supported, check NATS cluster URL spelling"
1712
- end
1738
+ when "nats", "tls"
1739
+ NATS::IO::Socket
1740
+ when "ws", "wss"
1741
+ require_relative "websocket"
1742
+ NATS::IO::WebSocket
1743
+ else
1744
+ raise NotImplementedError, "#{@uri.scheme} protocol is not supported, check NATS cluster URL spelling"
1745
+ end
1713
1746
 
1714
1747
  socket_class.new(
1715
1748
  uri: @uri,
1716
- tls: { context: tls_context, hostname: @hostname },
1717
- connect_timeout: NATS::IO::DEFAULT_CONNECT_TIMEOUT,
1749
+ tls: {context: tls_context, hostname: @hostname},
1750
+ connect_timeout: NATS::IO::DEFAULT_CONNECT_TIMEOUT
1718
1751
  )
1719
1752
  end
1720
1753
 
1721
1754
  def setup_nkeys_connect
1722
1755
  begin
1723
- require 'nkeys'
1724
- require 'base64'
1756
+ require "nkeys"
1757
+ require "base64"
1725
1758
  rescue LoadError
1726
1759
  raise(Error, "nkeys is not installed")
1727
1760
  end
1728
1761
 
1729
- case
1730
- when @nkeys_seed
1762
+ if @nkeys_seed
1731
1763
  @user_nkey_cb = nkey_cb_for_nkey_file(@nkeys_seed)
1732
1764
  @signature_cb = signature_cb_for_nkey_file(@nkeys_seed)
1733
- when @user_credentials
1765
+ elsif @user_credentials
1734
1766
  # When the credentials are within a single decorated file.
1735
1767
  @user_jwt_cb = jwt_cb_for_creds_file(@user_credentials)
1736
1768
  @signature_cb = signature_cb_for_creds_file(@user_credentials)
@@ -1740,18 +1772,18 @@ module NATS
1740
1772
  def signature_cb_for_nkey_file(nkey)
1741
1773
  proc { |nonce|
1742
1774
  seed = File.read(nkey).chomp
1743
- kp = NKEYS::from_seed(seed)
1775
+ kp = NKEYS.from_seed(seed)
1744
1776
  raw_signed = kp.sign(nonce)
1745
1777
  kp.wipe!
1746
1778
  encoded = Base64.urlsafe_encode64(raw_signed)
1747
- encoded.gsub('=', '')
1779
+ encoded.gsub("=", "")
1748
1780
  }
1749
1781
  end
1750
1782
 
1751
1783
  def nkey_cb_for_nkey_file(nkey)
1752
1784
  proc {
1753
1785
  seed = File.read(nkey).chomp
1754
- kp = NKEYS::from_seed(seed)
1786
+ kp = NKEYS.from_seed(seed)
1755
1787
 
1756
1788
  # Take a copy since original will be gone with the wipe.
1757
1789
  pub_key = kp.public_key.dup
@@ -1763,21 +1795,20 @@ module NATS
1763
1795
 
1764
1796
  def jwt_cb_for_creds_file(creds)
1765
1797
  proc {
1766
- jwt_start = "BEGIN NATS USER JWT".freeze
1798
+ jwt_start = "BEGIN NATS USER JWT"
1767
1799
  found = false
1768
1800
  jwt = nil
1769
1801
 
1770
1802
  File.readlines(creds).each do |line|
1771
- case
1772
- when found
1803
+ if found
1773
1804
  jwt = line.chomp
1774
1805
  break
1775
- when line.include?(jwt_start)
1806
+ elsif line.include?(jwt_start)
1776
1807
  found = true
1777
1808
  end
1778
1809
  end
1779
1810
 
1780
- raise(Error, "No JWT found in #{creds}") if not found
1811
+ raise(Error, "No JWT found in #{creds}") if !found
1781
1812
 
1782
1813
  jwt
1783
1814
  }
@@ -1785,23 +1816,22 @@ module NATS
1785
1816
 
1786
1817
  def signature_cb_for_creds_file(creds)
1787
1818
  proc { |nonce|
1788
- seed_start = "BEGIN USER NKEY SEED".freeze
1819
+ seed_start = "BEGIN USER NKEY SEED"
1789
1820
  found = false
1790
1821
  seed = nil
1791
1822
 
1792
1823
  File.readlines(creds).each do |line|
1793
- case
1794
- when found
1824
+ if found
1795
1825
  seed = line.chomp
1796
1826
  break
1797
- when line.include?(seed_start)
1827
+ elsif line.include?(seed_start)
1798
1828
  found = true
1799
1829
  end
1800
1830
  end
1801
1831
 
1802
- raise(Error, "No nkey user seed found in #{creds}") if not found
1832
+ raise(Error, "No nkey user seed found in #{creds}") if !found
1803
1833
 
1804
- kp = NKEYS::from_seed(seed)
1834
+ kp = NKEYS.from_seed(seed)
1805
1835
  raw_signed = kp.sign(nonce)
1806
1836
 
1807
1837
  # seed is a reference so also cleared when doing wipe,
@@ -1810,14 +1840,12 @@ module NATS
1810
1840
  encoded = Base64.urlsafe_encode64(raw_signed)
1811
1841
 
1812
1842
  # Remove padding
1813
- encoded.gsub('=', '')
1843
+ encoded.gsub("=", "")
1814
1844
  }
1815
1845
  end
1816
1846
 
1817
1847
  def process_uri(uris)
1818
- uris.split(',').map do |uri|
1819
- opts = {}
1820
-
1848
+ uris.gsub(/\s+/, "").split(",").map do |uri|
1821
1849
  # Scheme
1822
1850
  uri = "nats://#{uri}" if !uri.include?("://")
1823
1851
 
@@ -1825,7 +1853,7 @@ module NATS
1825
1853
 
1826
1854
  # Host and Port
1827
1855
  uri_object.hostname ||= "localhost"
1828
- uri_object.port ||= DEFAULT_PORT.fetch(uri.scheme.to_sym, DEFAULT_PORT[:nats])
1856
+ uri_object.port ||= DEFAULT_PORT.fetch(uri_object.scheme.to_sym, DEFAULT_PORT[:nats])
1829
1857
 
1830
1858
  uri_object
1831
1859
  end
@@ -1859,9 +1887,10 @@ module NATS
1859
1887
  DEFAULT_CONNECT_TIMEOUT = 2
1860
1888
  DEFAULT_READ_WRITE_TIMEOUT = 2
1861
1889
  DEFAULT_DRAIN_TIMEOUT = 30
1890
+ DEFAULT_CLOSE_TIMEOUT = 30
1862
1891
 
1863
1892
  # Default Pending Limits
1864
- DEFAULT_SUB_PENDING_MSGS_LIMIT = 65536
1893
+ DEFAULT_SUB_PENDING_MSGS_LIMIT = 65536
1865
1894
  DEFAULT_SUB_PENDING_BYTES_LIMIT = 65536 * 1024
1866
1895
 
1867
1896
  DEFAULT_TOTAL_SUB_CONCURRENCY = 24
@@ -1871,7 +1900,7 @@ module NATS
1871
1900
  class Socket
1872
1901
  attr_accessor :socket
1873
1902
 
1874
- def initialize(options={})
1903
+ def initialize(options = {})
1875
1904
  @uri = options[:uri]
1876
1905
  @connect_timeout = options[:connect_timeout]
1877
1906
  @write_timeout = options[:write_timeout]
@@ -1883,13 +1912,11 @@ module NATS
1883
1912
  def connect
1884
1913
  addrinfo = ::Socket.getaddrinfo(@uri.hostname, nil, ::Socket::AF_UNSPEC, ::Socket::SOCK_STREAM)
1885
1914
  addrinfo.each_with_index do |ai, i|
1886
- begin
1887
- @socket = connect_addrinfo(ai, @uri.port, @connect_timeout)
1888
- break
1889
- rescue SystemCallError => e
1890
- # Give up if no more available
1891
- raise e if addrinfo.length == i+1
1892
- end
1915
+ @socket = connect_addrinfo(ai, @uri.port, @connect_timeout)
1916
+ break
1917
+ rescue SystemCallError => e
1918
+ # Give up if no more available
1919
+ raise e if addrinfo.length == i + 1
1893
1920
  end
1894
1921
 
1895
1922
  # Set TCP no delay by default
@@ -1912,7 +1939,7 @@ module NATS
1912
1939
  @socket = tls_socket
1913
1940
  end
1914
1941
 
1915
- def read_line(deadline=nil)
1942
+ def read_line(deadline = nil)
1916
1943
  # FIXME: Should accumulate and read in a non blocking way instead
1917
1944
  unless ::IO.select([@socket], nil, nil, deadline)
1918
1945
  raise NATS::IO::SocketTimeoutError
@@ -1920,10 +1947,9 @@ module NATS
1920
1947
  @socket.gets
1921
1948
  end
1922
1949
 
1923
- def read(max_bytes, deadline=nil)
1924
-
1950
+ def read(max_bytes, deadline = nil)
1925
1951
  begin
1926
- return @socket.read_nonblock(max_bytes)
1952
+ @socket.read_nonblock(max_bytes)
1927
1953
  rescue ::IO::WaitReadable
1928
1954
  if ::IO.select([@socket], nil, nil, deadline)
1929
1955
  retry
@@ -1938,7 +1964,7 @@ module NATS
1938
1964
  end
1939
1965
  end
1940
1966
  rescue EOFError => e
1941
- if RUBY_ENGINE == 'jruby' and e.message == 'No message available'
1967
+ if (RUBY_ENGINE == "jruby") && (e.message == "No message available")
1942
1968
  # FIXME: <EOFError: No message available> can happen in jruby
1943
1969
  # even though seems it is temporary and eventually possible
1944
1970
  # to read from socket.
@@ -1947,32 +1973,29 @@ module NATS
1947
1973
  raise Errno::ECONNRESET
1948
1974
  end
1949
1975
 
1950
- def write(data, deadline=nil)
1976
+ def write(data, deadline = nil)
1951
1977
  length = data.bytesize
1952
1978
  total_written = 0
1953
1979
 
1954
1980
  loop do
1955
- begin
1956
- written = @socket.write_nonblock(data)
1957
-
1958
- total_written += written
1959
- break total_written if total_written >= length
1960
- data = data.byteslice(written..-1)
1961
- rescue ::IO::WaitWritable
1962
- if ::IO.select(nil, [@socket], nil, deadline)
1963
- retry
1964
- else
1965
- raise NATS::IO::SocketTimeoutError
1966
- end
1967
- rescue ::IO::WaitReadable
1968
- if ::IO.select([@socket], nil, nil, deadline)
1969
- retry
1970
- else
1971
- raise NATS::IO::SocketTimeoutError
1972
- end
1981
+ written = @socket.write_nonblock(data)
1982
+
1983
+ total_written += written
1984
+ break total_written if total_written >= length
1985
+ data = data.byteslice(written..-1)
1986
+ rescue ::IO::WaitWritable
1987
+ if ::IO.select(nil, [@socket], nil, deadline)
1988
+ retry
1989
+ else
1990
+ raise NATS::IO::SocketTimeoutError
1991
+ end
1992
+ rescue ::IO::WaitReadable
1993
+ if ::IO.select([@socket], nil, nil, deadline)
1994
+ retry
1995
+ else
1996
+ raise NATS::IO::SocketTimeoutError
1973
1997
  end
1974
1998
  end
1975
-
1976
1999
  rescue EOFError
1977
2000
  raise Errno::ECONNRESET
1978
2001
  end
@@ -2017,14 +2040,13 @@ module NATS
2017
2040
  # Implementation of MonotonicTime adapted from
2018
2041
  # https://github.com/ruby-concurrency/concurrent-ruby/
2019
2042
  class << self
2020
- case
2021
- when defined?(Process::CLOCK_MONOTONIC)
2043
+ if defined?(Process::CLOCK_MONOTONIC)
2022
2044
  def now
2023
2045
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
2024
2046
  end
2025
- when RUBY_ENGINE == 'jruby'
2047
+ elsif RUBY_ENGINE == "jruby"
2026
2048
  def now
2027
- java.lang.System.nanoTime() / 1_000_000_000.0
2049
+ java.lang.System.nanoTime / 1_000_000_000.0
2028
2050
  end
2029
2051
  else
2030
2052
  def now