nats-pure 2.4.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 -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