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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 741d87f63f2851dc25639453f9cd5ed68b3e2bfb66f45f1b5fe6caa9b3e352dd
4
- data.tar.gz: 1af44d28a31788e37fbb50d14f966ff30805ee42614ae37a7ab3599edd0de4f0
3
+ metadata.gz: 1283c1d3b53b281eec2c1fb01643b3efd51c7b2468a54006f4afc9b0aa6cfffd
4
+ data.tar.gz: d6777d4b80d57b44da776e75be073f67fd76c3a7d23cf66fa7a2dc96f74afe64
5
5
  SHA512:
6
- metadata.gz: 432d3b3cf28752ec19c6278763ec5e1ef5992ee9d57796aeadb41b978028b3700c699795134f46297c982d9fc98fc3979d0760842f74fb528977e62b66431c9c
7
- data.tar.gz: 758a3b3deb4c2b14198bca42b647d2b11726c08d7150a914a8835a64fa6720d57d28c2207ddcd6ad40995427902ab1afd6705f2ab4c4ea1adbe9e131ccc1ea32
6
+ metadata.gz: bb44bb3872296a3cce5416777b2e85c5c2046fb64a545157fd3661754a31311b9bf2d032494d5eb1e34a08c1e0d2f3982538a098f0066f30c2a5c16823ee8ec8
7
+ data.tar.gz: f5a2e1f1a836392030c1abc0d8ee65a3b19e2bb371d7c3cae0c8107ae5444a13609275f85c6ec12c06070218e1220ec36bb57eba4d8a7c86b4ac314eba6ddc4e
@@ -1,4 +1,4 @@
1
- # Copyright 2016-2018 The NATS Authors
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
- @hostname = srv[:hostname]
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
- else cb.call(msg.data, msg.reply, msg.subject)
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
- return unless subject
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
- with_nats_timeout(timeout) do
407
- @resp_sub.synchronize do
408
- future.wait(timeout)
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
- else blk.call(msg, reply)
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
- sc = nil
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
- sub.response = Msg.new(subject, reply, data)
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
- sc = SlowConsumer.new("nats: slow consumer, messages dropped")
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
- sub.pending_queue << Msg.new(subject, reply, data)
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 = sc
615
- @err_cb.call(sc) if @err_cb
616
- end if sc
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
- u = URI.parse("nats://#{url}")
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
- if auth_connection?
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
- _, info_json = line.split(' ')
937
- process_info(info_json)
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
- @hostname = srv[:hostname]
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
@@ -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
- @nc.process_msg(@sub, @sid, @reply, @buf.slice(0, @needed))
93
- @buf = @buf.slice((@needed + CR_LF_SIZE), @buf.bytesize)
94
- @sub = @sid = @reply = @needed = nil
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
@@ -1,4 +1,4 @@
1
- # Copyright 2016-2018 The NATS Authors
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.5.0"
19
- LANG = "#{RUBY_ENGINE}2".freeze
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.5.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: 2018-04-04 00:00:00.000000000 Z
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
- rubyforge_project:
45
- rubygems_version: 2.7.3
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: []