nats-pure 0.5.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/nats/io/client.rb +371 -27
- data/lib/nats/io/parser.rb +17 -3
- data/lib/nats/io/version.rb +3 -3
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1283c1d3b53b281eec2c1fb01643b3efd51c7b2468a54006f4afc9b0aa6cfffd
|
4
|
+
data.tar.gz: d6777d4b80d57b44da776e75be073f67fd76c3a7d23cf66fa7a2dc96f74afe64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb44bb3872296a3cce5416777b2e85c5c2046fb64a545157fd3661754a31311b9bf2d032494d5eb1e34a08c1e0d2f3982538a098f0066f30c2a5c16823ee8ec8
|
7
|
+
data.tar.gz: f5a2e1f1a836392030c1abc0d8ee65a3b19e2bb371d7c3cae0c8107ae5444a13609275f85c6ec12c06070218e1220ec36bb57eba4d8a7c86b4ac314eba6ddc4e
|
data/lib/nats/io/client.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2016-
|
1
|
+
# Copyright 2016-2021 The NATS Authors
|
2
2
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
3
|
# you may not use this file except in compliance with the License.
|
4
4
|
# You may obtain a copy of the License at
|
@@ -28,6 +28,15 @@ rescue LoadError
|
|
28
28
|
end
|
29
29
|
|
30
30
|
module NATS
|
31
|
+
class << self
|
32
|
+
def connect(uri=nil, opts={})
|
33
|
+
nc = NATS::IO::Client.new
|
34
|
+
nc.connect(uri, opts)
|
35
|
+
|
36
|
+
nc
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
31
40
|
module IO
|
32
41
|
|
33
42
|
DEFAULT_PORT = 4222
|
@@ -64,6 +73,12 @@ module NATS
|
|
64
73
|
PING_REQUEST = ("PING#{CR_LF}".freeze)
|
65
74
|
PONG_RESPONSE = ("PONG#{CR_LF}".freeze)
|
66
75
|
|
76
|
+
NATS_HDR_LINE = ("NATS/1.0#{CR_LF}".freeze)
|
77
|
+
STATUS_MSG_LEN = 3
|
78
|
+
STATUS_HDR = ("Status".freeze)
|
79
|
+
DESC_HDR = ("Description".freeze)
|
80
|
+
NATS_HDR_LINE_SIZE = (NATS_HDR_LINE.bytesize)
|
81
|
+
|
67
82
|
SUB_OP = ('SUB'.freeze)
|
68
83
|
EMPTY_MSG = (''.freeze)
|
69
84
|
|
@@ -91,6 +106,9 @@ module NATS
|
|
91
106
|
# When we cannot connect since there are no servers available.
|
92
107
|
class NoServersError < ConnectError; end
|
93
108
|
|
109
|
+
# When there are no subscribers available to respond.
|
110
|
+
class NoRespondersError < ConnectError; end
|
111
|
+
|
94
112
|
# When the connection exhausts max number of pending pings replies.
|
95
113
|
class StaleConnectionError < Error; end
|
96
114
|
|
@@ -174,16 +192,52 @@ module NATS
|
|
174
192
|
# Hostname of current server; used for when TLS host
|
175
193
|
# verification is enabled.
|
176
194
|
@hostname = nil
|
195
|
+
@single_url_connect_used = false
|
196
|
+
|
197
|
+
# Track whether connect has been already been called.
|
198
|
+
@connect_called = false
|
177
199
|
|
178
200
|
# New style request/response implementation.
|
179
201
|
@resp_sub = nil
|
180
202
|
@resp_map = nil
|
181
203
|
@resp_sub_prefix = nil
|
182
204
|
@nuid = NATS::NUID.new
|
205
|
+
|
206
|
+
# NKEYS
|
207
|
+
@user_credentials = nil
|
208
|
+
@nkeys_seed = nil
|
209
|
+
@user_nkey_cb = nil
|
210
|
+
@user_jwt_cb = nil
|
211
|
+
@signature_cb = nil
|
183
212
|
end
|
184
213
|
|
185
214
|
# Establishes connection to NATS.
|
186
|
-
def connect(opts={})
|
215
|
+
def connect(uri=nil, opts={})
|
216
|
+
synchronize do
|
217
|
+
# In case it has been connected already, then do not need to call this again.
|
218
|
+
return if @connect_called
|
219
|
+
@connect_called = true
|
220
|
+
end
|
221
|
+
|
222
|
+
# Convert URI to string if needed.
|
223
|
+
uri = uri.to_s if uri.is_a?(URI)
|
224
|
+
|
225
|
+
case uri
|
226
|
+
when String
|
227
|
+
# Initialize TLS defaults in case any url is using it.
|
228
|
+
srvs = opts[:servers] = process_uri(uri)
|
229
|
+
if srvs.any? {|u| u.scheme == 'tls'} and !opts[:tls]
|
230
|
+
tls_context = OpenSSL::SSL::SSLContext.new
|
231
|
+
tls_context.set_params
|
232
|
+
opts[:tls] = {
|
233
|
+
context: tls_context
|
234
|
+
}
|
235
|
+
end
|
236
|
+
@single_url_connect_used = true if srvs.size == 1
|
237
|
+
when Hash
|
238
|
+
opts = uri
|
239
|
+
end
|
240
|
+
|
187
241
|
opts[:verbose] = false if opts[:verbose].nil?
|
188
242
|
opts[:pedantic] = false if opts[:pedantic].nil?
|
189
243
|
opts[:reconnect] = true if opts[:reconnect].nil?
|
@@ -226,6 +280,11 @@ module NATS
|
|
226
280
|
class << self; alias_method :request, :old_request; end
|
227
281
|
end
|
228
282
|
|
283
|
+
# NKEYS
|
284
|
+
@user_credentials ||= opts[:user_credentials]
|
285
|
+
@nkeys_seed ||= opts[:nkeys_seed]
|
286
|
+
setup_nkeys_connect if @user_credentials or @nkeys_seed
|
287
|
+
|
229
288
|
# Check for TLS usage
|
230
289
|
@tls = @options[:tls]
|
231
290
|
|
@@ -245,7 +304,12 @@ module NATS
|
|
245
304
|
@status = CONNECTING
|
246
305
|
|
247
306
|
# Use the hostname from the server for TLS hostname verification.
|
248
|
-
|
307
|
+
if client_using_secure_connection? and single_url_connect_used?
|
308
|
+
# Always reuse the original hostname used to connect.
|
309
|
+
@hostname ||= srv[:hostname]
|
310
|
+
else
|
311
|
+
@hostname = srv[:hostname]
|
312
|
+
end
|
249
313
|
|
250
314
|
# Established TCP connection successfully so can start connect
|
251
315
|
process_connect_init
|
@@ -312,6 +376,36 @@ module NATS
|
|
312
376
|
@flush_queue << :pub if @flush_queue.empty?
|
313
377
|
end
|
314
378
|
|
379
|
+
# Publishes a NATS::Msg that may include headers.
|
380
|
+
def publish_msg(msg)
|
381
|
+
raise TypeError, "nats: expected NATS::Msg, got #{msg.class.name}" unless msg.is_a?(Msg)
|
382
|
+
raise BadSubject if !msg.subject or msg.subject.empty?
|
383
|
+
|
384
|
+
msg.reply ||= ''
|
385
|
+
msg.data ||= ''
|
386
|
+
msg_size = msg.data.bytesize
|
387
|
+
|
388
|
+
# Accounting
|
389
|
+
@stats[:out_msgs] += 1
|
390
|
+
@stats[:out_bytes] += msg_size
|
391
|
+
|
392
|
+
if msg.header
|
393
|
+
hdr = ''
|
394
|
+
hdr << NATS_HDR_LINE
|
395
|
+
msg.header.each do |k, v|
|
396
|
+
hdr << "#{k}: #{v}#{CR_LF}"
|
397
|
+
end
|
398
|
+
hdr << CR_LF
|
399
|
+
hdr_len = hdr.bytesize
|
400
|
+
total_size = msg_size + hdr_len
|
401
|
+
send_command("HPUB #{msg.subject} #{msg.reply} #{hdr_len} #{total_size}\r\n#{hdr}#{msg.data}\r\n")
|
402
|
+
else
|
403
|
+
send_command("PUB #{msg.subject} #{msg.reply} #{msg_size}\r\n#{msg.data}\r\n")
|
404
|
+
end
|
405
|
+
|
406
|
+
@flush_queue << :pub if @flush_queue.empty?
|
407
|
+
end
|
408
|
+
|
315
409
|
# Create subscription which is dispatched asynchronously
|
316
410
|
# messages to a callback.
|
317
411
|
def subscribe(subject, opts={}, &callback)
|
@@ -359,7 +453,8 @@ module NATS
|
|
359
453
|
when 0 then cb.call
|
360
454
|
when 1 then cb.call(msg.data)
|
361
455
|
when 2 then cb.call(msg.data, msg.reply)
|
362
|
-
|
456
|
+
when 3 then cb.call(msg.data, msg.reply, msg.subject)
|
457
|
+
else cb.call(msg.data, msg.reply, msg.subject, msg.header)
|
363
458
|
end
|
364
459
|
rescue => e
|
365
460
|
synchronize do
|
@@ -377,11 +472,12 @@ module NATS
|
|
377
472
|
# It times out in case the request is not retrieved within the
|
378
473
|
# specified deadline.
|
379
474
|
# If given a callback, then the request happens asynchronously.
|
380
|
-
def request(subject, payload, opts={}, &blk)
|
381
|
-
|
475
|
+
def request(subject, payload="", opts={}, &blk)
|
476
|
+
raise BadSubject if !subject or subject.empty?
|
382
477
|
|
383
478
|
# If a block was given then fallback to method using auto unsubscribe.
|
384
479
|
return old_request(subject, payload, opts, &blk) if blk
|
480
|
+
return old_request(subject, payload, opts) if opts[:old_style]
|
385
481
|
|
386
482
|
token = nil
|
387
483
|
inbox = nil
|
@@ -403,16 +499,81 @@ module NATS
|
|
403
499
|
|
404
500
|
# Publish request and wait for reply.
|
405
501
|
publish(subject, payload, inbox)
|
406
|
-
|
407
|
-
|
408
|
-
|
502
|
+
begin
|
503
|
+
with_nats_timeout(timeout) do
|
504
|
+
@resp_sub.synchronize do
|
505
|
+
future.wait(timeout)
|
506
|
+
end
|
409
507
|
end
|
508
|
+
rescue NATS::IO::Timeout => e
|
509
|
+
synchronize { @resp_map.delete(token) }
|
510
|
+
raise e
|
410
511
|
end
|
411
512
|
|
412
|
-
# Check if there is a response already
|
513
|
+
# Check if there is a response already.
|
413
514
|
synchronize do
|
414
515
|
result = @resp_map[token]
|
415
516
|
response = result[:response]
|
517
|
+
@resp_map.delete(token)
|
518
|
+
end
|
519
|
+
|
520
|
+
if response and response.header
|
521
|
+
status = response.header[STATUS_HDR]
|
522
|
+
raise NoRespondersError if status == "503"
|
523
|
+
end
|
524
|
+
|
525
|
+
response
|
526
|
+
end
|
527
|
+
|
528
|
+
# Makes a NATS request using a NATS::Msg that may include headers.
|
529
|
+
def request_msg(msg, opts={})
|
530
|
+
raise TypeError, "nats: expected NATS::Msg, got #{msg.class.name}" unless msg.is_a?(Msg)
|
531
|
+
raise BadSubject if !msg.subject or msg.subject.empty?
|
532
|
+
|
533
|
+
token = nil
|
534
|
+
inbox = nil
|
535
|
+
future = nil
|
536
|
+
response = nil
|
537
|
+
timeout = opts[:timeout] ||= 0.5
|
538
|
+
synchronize do
|
539
|
+
start_resp_mux_sub! unless @resp_sub_prefix
|
540
|
+
|
541
|
+
# Create token for this request.
|
542
|
+
token = @nuid.next
|
543
|
+
inbox = "#{@resp_sub_prefix}.#{token}"
|
544
|
+
|
545
|
+
# Create the a future for the request that will
|
546
|
+
# get signaled when it receives the request.
|
547
|
+
future = @resp_sub.new_cond
|
548
|
+
@resp_map[token][:future] = future
|
549
|
+
end
|
550
|
+
msg.reply = inbox
|
551
|
+
msg.data ||= ''
|
552
|
+
msg_size = msg.data.bytesize
|
553
|
+
|
554
|
+
# Publish request and wait for reply.
|
555
|
+
publish_msg(msg)
|
556
|
+
begin
|
557
|
+
with_nats_timeout(timeout) do
|
558
|
+
@resp_sub.synchronize do
|
559
|
+
future.wait(timeout)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
rescue NATS::IO::Timeout => e
|
563
|
+
synchronize { @resp_map.delete(token) }
|
564
|
+
raise e
|
565
|
+
end
|
566
|
+
|
567
|
+
# Check if there is a response already.
|
568
|
+
synchronize do
|
569
|
+
result = @resp_map[token]
|
570
|
+
response = result[:response]
|
571
|
+
@resp_map.delete(token)
|
572
|
+
end
|
573
|
+
|
574
|
+
if response and response.header
|
575
|
+
status = response.header[STATUS_HDR]
|
576
|
+
raise NoRespondersError if status == "503"
|
416
577
|
end
|
417
578
|
|
418
579
|
response
|
@@ -430,11 +591,13 @@ module NATS
|
|
430
591
|
# the messages asynchronously and return the sid.
|
431
592
|
if blk
|
432
593
|
opts[:max] ||= 1
|
433
|
-
s = subscribe(inbox, opts) do |msg, reply|
|
594
|
+
s = subscribe(inbox, opts) do |msg, reply, subject, header|
|
434
595
|
case blk.arity
|
435
596
|
when 0 then blk.call
|
436
597
|
when 1 then blk.call(msg)
|
437
|
-
|
598
|
+
when 2 then blk.call(msg, reply)
|
599
|
+
when 3 then blk.call(msg, reply, subject)
|
600
|
+
else blk.call(msg, reply, subject, header)
|
438
601
|
end
|
439
602
|
end
|
440
603
|
publish(subject, payload, inbox)
|
@@ -473,6 +636,11 @@ module NATS
|
|
473
636
|
end
|
474
637
|
response = sub.response
|
475
638
|
|
639
|
+
if response and response.header
|
640
|
+
status = response.header[STATUS_HDR]
|
641
|
+
raise NoRespondersError if status == "503"
|
642
|
+
end
|
643
|
+
|
476
644
|
response
|
477
645
|
end
|
478
646
|
|
@@ -560,8 +728,7 @@ module NATS
|
|
560
728
|
process_op_error(e)
|
561
729
|
end
|
562
730
|
|
563
|
-
def process_msg(subject, sid, reply, data)
|
564
|
-
# Accounting
|
731
|
+
def process_msg(subject, sid, reply, data, header)
|
565
732
|
@stats[:in_msgs] += 1
|
566
733
|
@stats[:in_bytes] += data.size
|
567
734
|
|
@@ -570,7 +737,7 @@ module NATS
|
|
570
737
|
synchronize { sub = @subs[sid] }
|
571
738
|
return unless sub
|
572
739
|
|
573
|
-
|
740
|
+
err = nil
|
574
741
|
sub.synchronize do
|
575
742
|
sub.received += 1
|
576
743
|
|
@@ -591,7 +758,8 @@ module NATS
|
|
591
758
|
# do so here already while holding the lock and return
|
592
759
|
if sub.future
|
593
760
|
future = sub.future
|
594
|
-
|
761
|
+
hdr = process_hdr(header)
|
762
|
+
sub.response = Msg.new(subject: subject, reply: reply, data: data, header: hdr)
|
595
763
|
future.signal
|
596
764
|
|
597
765
|
return
|
@@ -600,20 +768,54 @@ module NATS
|
|
600
768
|
# and should be able to consume messages in parallel.
|
601
769
|
if sub.pending_queue.size >= sub.pending_msgs_limit \
|
602
770
|
or sub.pending_size >= sub.pending_bytes_limit then
|
603
|
-
|
771
|
+
err = SlowConsumer.new("nats: slow consumer, messages dropped")
|
604
772
|
else
|
773
|
+
hdr = process_hdr(header)
|
774
|
+
|
605
775
|
# Only dispatch message when sure that it would not block
|
606
776
|
# the main read loop from the parser.
|
607
|
-
|
777
|
+
msg = Msg.new(subject: subject, reply: reply, data: data, header: hdr)
|
778
|
+
sub.pending_queue << msg
|
608
779
|
sub.pending_size += data.size
|
609
780
|
end
|
610
781
|
end
|
611
782
|
end
|
612
783
|
|
613
784
|
synchronize do
|
614
|
-
@last_err =
|
615
|
-
@err_cb.call(
|
616
|
-
end if
|
785
|
+
@last_err = err
|
786
|
+
@err_cb.call(err) if @err_cb
|
787
|
+
end if err
|
788
|
+
end
|
789
|
+
|
790
|
+
def process_hdr(header)
|
791
|
+
hdr = nil
|
792
|
+
if header
|
793
|
+
hdr = {}
|
794
|
+
lines = header.lines
|
795
|
+
|
796
|
+
# Check if it is an inline status and description.
|
797
|
+
if lines.count <= 2
|
798
|
+
status_hdr = lines.first.rstrip
|
799
|
+
hdr[STATUS_HDR] = status_hdr.slice(NATS_HDR_LINE_SIZE-1, STATUS_MSG_LEN)
|
800
|
+
|
801
|
+
if NATS_HDR_LINE_SIZE+2 < status_hdr.bytesize
|
802
|
+
desc = status_hdr.slice(NATS_HDR_LINE_SIZE+STATUS_MSG_LEN, status_hdr.bytesize)
|
803
|
+
hdr[DESC_HDR] = desc unless desc.empty?
|
804
|
+
end
|
805
|
+
end
|
806
|
+
begin
|
807
|
+
lines.slice(1, header.size).each do |line|
|
808
|
+
line.rstrip!
|
809
|
+
next if line.empty?
|
810
|
+
key, value = line.strip.split(/\s*:\s*/, 2)
|
811
|
+
hdr[key] = value
|
812
|
+
end
|
813
|
+
rescue => e
|
814
|
+
err = e
|
815
|
+
end
|
816
|
+
end
|
817
|
+
|
818
|
+
hdr
|
617
819
|
end
|
618
820
|
|
619
821
|
def process_info(line)
|
@@ -634,7 +836,8 @@ module NATS
|
|
634
836
|
if connect_urls
|
635
837
|
srvs = []
|
636
838
|
connect_urls.each do |url|
|
637
|
-
|
839
|
+
scheme = client_using_secure_connection? ? "tls" : "nats"
|
840
|
+
u = URI.parse("#{scheme}://#{url}")
|
638
841
|
|
639
842
|
# Skip in case it is the current server which we already know
|
640
843
|
next if @uri.host == u.host && @uri.port == u.port
|
@@ -753,6 +956,10 @@ module NATS
|
|
753
956
|
@uri.scheme == "tls" || @tls
|
754
957
|
end
|
755
958
|
|
959
|
+
def single_url_connect_used?
|
960
|
+
@single_url_connect_used
|
961
|
+
end
|
962
|
+
|
756
963
|
def send_command(command)
|
757
964
|
@pending_size += command.bytesize
|
758
965
|
@pending_queue << command
|
@@ -774,13 +981,31 @@ module NATS
|
|
774
981
|
}
|
775
982
|
cs[:name] = @options[:name] if @options[:name]
|
776
983
|
|
777
|
-
|
984
|
+
case
|
985
|
+
when auth_connection?
|
778
986
|
if @uri.password
|
779
987
|
cs[:user] = @uri.user
|
780
988
|
cs[:pass] = @uri.password
|
781
989
|
else
|
782
990
|
cs[:auth_token] = @uri.user
|
783
991
|
end
|
992
|
+
when @user_credentials
|
993
|
+
nonce = @server_info[:nonce]
|
994
|
+
cs[:jwt] = @user_jwt_cb.call
|
995
|
+
cs[:sig] = @signature_cb.call(nonce)
|
996
|
+
when @nkeys_seed
|
997
|
+
nonce = @server_info[:nonce]
|
998
|
+
cs[:nkey] = @user_nkey_cb.call
|
999
|
+
cs[:sig] = @signature_cb.call(nonce)
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
if @server_info[:headers]
|
1003
|
+
cs[:headers] = @server_info[:headers]
|
1004
|
+
cs[:no_responders] = if @options[:no_responders] == false
|
1005
|
+
@options[:no_responders]
|
1006
|
+
else
|
1007
|
+
@server_info[:headers]
|
1008
|
+
end
|
784
1009
|
end
|
785
1010
|
|
786
1011
|
"CONNECT #{cs.to_json}#{CR_LF}"
|
@@ -933,8 +1158,13 @@ module NATS
|
|
933
1158
|
if !line or line.empty?
|
934
1159
|
raise ConnectError.new("nats: protocol exception, INFO not received")
|
935
1160
|
end
|
936
|
-
|
937
|
-
|
1161
|
+
|
1162
|
+
if match = line.match(NATS::Protocol::INFO)
|
1163
|
+
info_json = match.captures.first
|
1164
|
+
process_info(info_json)
|
1165
|
+
else
|
1166
|
+
raise ConnectError.new("nats: protocol exception, INFO not valid")
|
1167
|
+
end
|
938
1168
|
|
939
1169
|
case
|
940
1170
|
when (server_using_secure_connection? and client_using_secure_connection?)
|
@@ -1021,7 +1251,12 @@ module NATS
|
|
1021
1251
|
@stats[:reconnects] += 1
|
1022
1252
|
|
1023
1253
|
# Set hostname to use for TLS hostname verification
|
1024
|
-
|
1254
|
+
if client_using_secure_connection? and single_url_connect_used?
|
1255
|
+
# Reuse original hostname name in case of using TLS.
|
1256
|
+
@hostname ||= srv[:hostname]
|
1257
|
+
else
|
1258
|
+
@hostname = srv[:hostname]
|
1259
|
+
end
|
1025
1260
|
|
1026
1261
|
# Established TCP connection successfully so can start connect
|
1027
1262
|
process_connect_init
|
@@ -1241,6 +1476,115 @@ module NATS
|
|
1241
1476
|
connect_timeout: DEFAULT_CONNECT_TIMEOUT
|
1242
1477
|
})
|
1243
1478
|
end
|
1479
|
+
|
1480
|
+
def setup_nkeys_connect
|
1481
|
+
begin
|
1482
|
+
require 'nkeys'
|
1483
|
+
require 'base64'
|
1484
|
+
rescue LoadError
|
1485
|
+
raise(Error, "nkeys is not installed")
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
case
|
1489
|
+
when @nkeys_seed
|
1490
|
+
@user_nkey_cb = proc {
|
1491
|
+
seed = File.read(@nkeys_seed).chomp
|
1492
|
+
kp = NKEYS::from_seed(seed)
|
1493
|
+
|
1494
|
+
# Take a copy since original will be gone with the wipe.
|
1495
|
+
pub_key = kp.public_key.dup
|
1496
|
+
kp.wipe!
|
1497
|
+
|
1498
|
+
pub_key
|
1499
|
+
}
|
1500
|
+
|
1501
|
+
@signature_cb = proc { |nonce|
|
1502
|
+
seed = File.read(@nkeys_seed).chomp
|
1503
|
+
kp = NKEYS::from_seed(seed)
|
1504
|
+
raw_signed = kp.sign(nonce)
|
1505
|
+
kp.wipe!
|
1506
|
+
encoded = Base64.urlsafe_encode64(raw_signed)
|
1507
|
+
encoded.gsub('=', '')
|
1508
|
+
}
|
1509
|
+
when @user_credentials
|
1510
|
+
# When the credentials are within a single decorated file.
|
1511
|
+
@user_jwt_cb = proc {
|
1512
|
+
jwt_start = "BEGIN NATS USER JWT".freeze
|
1513
|
+
found = false
|
1514
|
+
jwt = nil
|
1515
|
+
File.readlines(@user_credentials).each do |line|
|
1516
|
+
case
|
1517
|
+
when found
|
1518
|
+
jwt = line.chomp
|
1519
|
+
break
|
1520
|
+
when line.include?(jwt_start)
|
1521
|
+
found = true
|
1522
|
+
end
|
1523
|
+
end
|
1524
|
+
raise(Error, "No JWT found in #{@user_credentials}") if not found
|
1525
|
+
|
1526
|
+
jwt
|
1527
|
+
}
|
1528
|
+
|
1529
|
+
@signature_cb = proc { |nonce|
|
1530
|
+
seed_start = "BEGIN USER NKEY SEED".freeze
|
1531
|
+
found = false
|
1532
|
+
seed = nil
|
1533
|
+
File.readlines(@user_credentials).each do |line|
|
1534
|
+
case
|
1535
|
+
when found
|
1536
|
+
seed = line.chomp
|
1537
|
+
break
|
1538
|
+
when line.include?(seed_start)
|
1539
|
+
found = true
|
1540
|
+
end
|
1541
|
+
end
|
1542
|
+
raise(Error, "No nkey user seed found in #{@user_credentials}") if not found
|
1543
|
+
|
1544
|
+
kp = NKEYS::from_seed(seed)
|
1545
|
+
raw_signed = kp.sign(nonce)
|
1546
|
+
|
1547
|
+
# seed is a reference so also cleared when doing wipe,
|
1548
|
+
# which can be done since Ruby strings are mutable.
|
1549
|
+
kp.wipe
|
1550
|
+
encoded = Base64.urlsafe_encode64(raw_signed)
|
1551
|
+
|
1552
|
+
# Remove padding
|
1553
|
+
encoded.gsub('=', '')
|
1554
|
+
}
|
1555
|
+
end
|
1556
|
+
end
|
1557
|
+
|
1558
|
+
def process_uri(uris)
|
1559
|
+
connect_uris = []
|
1560
|
+
uris.split(',').each do |uri|
|
1561
|
+
opts = {}
|
1562
|
+
|
1563
|
+
# Scheme
|
1564
|
+
if uri.include?("://")
|
1565
|
+
scheme, uri = uri.split("://")
|
1566
|
+
opts[:scheme] = scheme
|
1567
|
+
else
|
1568
|
+
opts[:scheme] = 'nats'
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
# UserInfo
|
1572
|
+
if uri.include?("@")
|
1573
|
+
userinfo, endpoint = uri.split("@")
|
1574
|
+
host, port = endpoint.split(":")
|
1575
|
+
opts[:userinfo] = userinfo
|
1576
|
+
else
|
1577
|
+
host, port = uri.split(":")
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
# Host and Port
|
1581
|
+
opts[:host] = host || "localhost"
|
1582
|
+
opts[:port] = port || DEFAULT_PORT
|
1583
|
+
|
1584
|
+
connect_uris << URI::Generic.build(opts)
|
1585
|
+
end
|
1586
|
+
connect_uris
|
1587
|
+
end
|
1244
1588
|
end
|
1245
1589
|
|
1246
1590
|
# Implementation adapted from https://github.com/redis/redis-rb
|
@@ -1370,7 +1714,7 @@ module NATS
|
|
1370
1714
|
end
|
1371
1715
|
end
|
1372
1716
|
|
1373
|
-
Msg = Struct.new(:subject, :reply, :data)
|
1717
|
+
Msg = Struct.new(:subject, :reply, :data, :header, keyword_init: true)
|
1374
1718
|
|
1375
1719
|
class Subscription
|
1376
1720
|
include MonitorMixin
|
data/lib/nats/io/parser.rb
CHANGED
@@ -16,6 +16,7 @@ module NATS
|
|
16
16
|
module Protocol
|
17
17
|
|
18
18
|
MSG = /\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i
|
19
|
+
HMSG = /\AHMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?([\d]+)\s+(\d+)\r\n/i
|
19
20
|
OK = /\A\+OK\s*\r\n/i
|
20
21
|
ERR = /\A-ERR\s+('.+')?\r\n/i
|
21
22
|
PING = /\APING\s*\r\n/i
|
@@ -49,6 +50,7 @@ module NATS
|
|
49
50
|
@sid = nil
|
50
51
|
@reply = nil
|
51
52
|
@needed = nil
|
53
|
+
@header_needed = nil
|
52
54
|
end
|
53
55
|
|
54
56
|
def parse(data)
|
@@ -61,6 +63,10 @@ module NATS
|
|
61
63
|
@buf = $'
|
62
64
|
@sub, @sid, @reply, @needed = $1, $2.to_i, $4, $5.to_i
|
63
65
|
@parse_state = AWAITING_MSG_PAYLOAD
|
66
|
+
when HMSG
|
67
|
+
@buf = $'
|
68
|
+
@sub, @sid, @reply, @header_needed, @needed = $1, $2.to_i, $4, $5.to_i, $6.to_i
|
69
|
+
@parse_state = AWAITING_MSG_PAYLOAD
|
64
70
|
when OK # No-op right now
|
65
71
|
@buf = $'
|
66
72
|
when ERR
|
@@ -89,9 +95,17 @@ module NATS
|
|
89
95
|
|
90
96
|
when AWAITING_MSG_PAYLOAD
|
91
97
|
return unless (@needed && @buf.bytesize >= (@needed + CR_LF_SIZE))
|
92
|
-
|
93
|
-
|
94
|
-
|
98
|
+
if @header_needed
|
99
|
+
hbuf = @buf.slice(0, @header_needed)
|
100
|
+
payload = @buf.slice(@header_needed, (@needed-@header_needed))
|
101
|
+
@nc.process_msg(@sub, @sid, @reply, payload, hbuf)
|
102
|
+
@buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
|
103
|
+
else
|
104
|
+
@nc.process_msg(@sub, @sid, @reply, @buf.slice(0, @needed), nil)
|
105
|
+
@buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
|
106
|
+
end
|
107
|
+
|
108
|
+
@sub = @sid = @reply = @needed = @header_needed = nil
|
95
109
|
@parse_state = AWAITING_CONTROL_LINE
|
96
110
|
@buf = nil if (@buf && @buf.empty?)
|
97
111
|
end
|
data/lib/nats/io/version.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2016-
|
1
|
+
# Copyright 2016-2021 The NATS Authors
|
2
2
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
3
3
|
# you may not use this file except in compliance with the License.
|
4
4
|
# You may obtain a copy of the License at
|
@@ -15,8 +15,8 @@
|
|
15
15
|
module NATS
|
16
16
|
module IO
|
17
17
|
# NOTE: These are all announced to the server on CONNECT
|
18
|
-
VERSION = "0.
|
19
|
-
LANG = "#{RUBY_ENGINE}
|
18
|
+
VERSION = "0.7.2"
|
19
|
+
LANG = "#{RUBY_ENGINE}#{RUBY_VERSION}".freeze
|
20
20
|
PROTOCOL = 1
|
21
21
|
end
|
22
22
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nats-pure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Waldemar Quevedo
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: NATS is an open-source, high-performance, lightweight cloud messaging
|
14
14
|
system.
|
@@ -26,7 +26,7 @@ homepage: https://nats.io
|
|
26
26
|
licenses:
|
27
27
|
- Apache-2.0
|
28
28
|
metadata: {}
|
29
|
-
post_install_message:
|
29
|
+
post_install_message:
|
30
30
|
rdoc_options: []
|
31
31
|
require_paths:
|
32
32
|
- lib
|
@@ -41,9 +41,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '0'
|
43
43
|
requirements: []
|
44
|
-
|
45
|
-
|
46
|
-
signing_key:
|
44
|
+
rubygems_version: 3.2.22
|
45
|
+
signing_key:
|
47
46
|
specification_version: 4
|
48
47
|
summary: NATS is an open-source, high-performance, lightweight cloud messaging system.
|
49
48
|
test_files: []
|