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 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: []