nats-pure 0.5.0 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|