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