nats-pure 2.3.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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