nats-pure 0.6.2 → 0.7.0

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: 705212803ac5f40ecb4b1ffa6bcb6fa16d0153ef90c2d46ef2f937c66d340503
4
- data.tar.gz: 580ac629b82778d88de22293bb5e1f501f8090c8cbd2635728cac38f819e6752
3
+ metadata.gz: d21f9a3f83d1aaa172f0835a2d74b5109a8e937429550981427b84325f97f1d3
4
+ data.tar.gz: 48ea0ef20a7c2611023573f883384d15ebf6bf9c342f37336b25157d79963098
5
5
  SHA512:
6
- metadata.gz: e41fc0cc6a94ba6e50d7eca22810c20b9cfe133a8cb89df4734b7bbdc4053a4d2dada84cf26ea71c54c4b578af57a24bb8739437489018dfa00637017a44b5aa
7
- data.tar.gz: 6e31b6352d5771f6f6da6541a9c045081919630357efe2535f3ac29687a7c2edca7ec26385bb2dbfd75ad0279e41fd76a857aa52e118b3f7b65c3f34cc587b34
6
+ metadata.gz: b72826e0d687cee2e303f3fae2abbc34ad6fe10ce35967d2201ecd3838c27f186e70c99c880288bfd2fbdc840edebfa80f4f31e282caafb15f899b41301a5a94
7
+ data.tar.gz: b5328d07ffa92de70d32cb37b28bf1c08db2866b58902dee2f761b6c0cf758c18037262fca4f42a3ab5bf6c4f0663abb733d8322cdfe8a2e30d8bd9de9a4139f
@@ -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
@@ -64,6 +64,12 @@ module NATS
64
64
  PING_REQUEST = ("PING#{CR_LF}".freeze)
65
65
  PONG_RESPONSE = ("PONG#{CR_LF}".freeze)
66
66
 
67
+ NATS_HDR_LINE = ("NATS/1.0#{CR_LF}".freeze)
68
+ STATUS_MSG_LEN = 3
69
+ STATUS_HDR = ("Status".freeze)
70
+ DESC_HDR = ("Description".freeze)
71
+ NATS_HDR_LINE_SIZE = (NATS_HDR_LINE.bytesize)
72
+
67
73
  SUB_OP = ('SUB'.freeze)
68
74
  EMPTY_MSG = (''.freeze)
69
75
 
@@ -91,6 +97,9 @@ module NATS
91
97
  # When we cannot connect since there are no servers available.
92
98
  class NoServersError < ConnectError; end
93
99
 
100
+ # When there are no subscribers available to respond.
101
+ class NoRespondersError < ConnectError; end
102
+
94
103
  # When the connection exhausts max number of pending pings replies.
95
104
  class StaleConnectionError < Error; end
96
105
 
@@ -346,6 +355,36 @@ module NATS
346
355
  @flush_queue << :pub if @flush_queue.empty?
347
356
  end
348
357
 
358
+ # Publishes a NATS::Msg that may include headers.
359
+ def publish_msg(msg)
360
+ raise TypeError, "nats: expected NATS::Msg, got #{msg.class.name}" unless msg.is_a?(Msg)
361
+ raise BadSubject if !msg.subject or msg.subject.empty?
362
+
363
+ msg.reply ||= ''
364
+ msg.data ||= ''
365
+ msg_size = msg.data.bytesize
366
+
367
+ # Accounting
368
+ @stats[:out_msgs] += 1
369
+ @stats[:out_bytes] += msg_size
370
+
371
+ if msg.header
372
+ hdr = ''
373
+ hdr << NATS_HDR_LINE
374
+ msg.header.each do |k, v|
375
+ hdr << "#{k}: #{v}#{CR_LF}"
376
+ end
377
+ hdr << CR_LF
378
+ hdr_len = hdr.bytesize
379
+ total_size = msg_size + hdr_len
380
+ send_command("HPUB #{msg.subject} #{msg.reply} #{hdr_len} #{total_size}\r\n#{hdr}#{msg.data}\r\n")
381
+ else
382
+ send_command("PUB #{msg.subject} #{msg.reply} #{msg_size}\r\n#{msg.data}\r\n")
383
+ end
384
+
385
+ @flush_queue << :pub if @flush_queue.empty?
386
+ end
387
+
349
388
  # Create subscription which is dispatched asynchronously
350
389
  # messages to a callback.
351
390
  def subscribe(subject, opts={}, &callback)
@@ -393,7 +432,8 @@ module NATS
393
432
  when 0 then cb.call
394
433
  when 1 then cb.call(msg.data)
395
434
  when 2 then cb.call(msg.data, msg.reply)
396
- else cb.call(msg.data, msg.reply, msg.subject)
435
+ when 3 then cb.call(msg.data, msg.reply, msg.subject)
436
+ else cb.call(msg.data, msg.reply, msg.subject, msg.header)
397
437
  end
398
438
  rescue => e
399
439
  synchronize do
@@ -411,11 +451,12 @@ module NATS
411
451
  # It times out in case the request is not retrieved within the
412
452
  # specified deadline.
413
453
  # If given a callback, then the request happens asynchronously.
414
- def request(subject, payload, opts={}, &blk)
415
- return unless subject
454
+ def request(subject, payload="", opts={}, &blk)
455
+ raise BadSubject if !subject or subject.empty?
416
456
 
417
457
  # If a block was given then fallback to method using auto unsubscribe.
418
458
  return old_request(subject, payload, opts, &blk) if blk
459
+ return old_request(subject, payload, opts) if opts[:old_style]
419
460
 
420
461
  token = nil
421
462
  inbox = nil
@@ -437,16 +478,81 @@ module NATS
437
478
 
438
479
  # Publish request and wait for reply.
439
480
  publish(subject, payload, inbox)
440
- with_nats_timeout(timeout) do
441
- @resp_sub.synchronize do
442
- future.wait(timeout)
481
+ begin
482
+ with_nats_timeout(timeout) do
483
+ @resp_sub.synchronize do
484
+ future.wait(timeout)
485
+ end
443
486
  end
487
+ rescue NATS::IO::Timeout => e
488
+ synchronize { @resp_map.delete(token) }
489
+ raise e
444
490
  end
445
491
 
446
- # Check if there is a response already
492
+ # Check if there is a response already.
447
493
  synchronize do
448
494
  result = @resp_map[token]
449
495
  response = result[:response]
496
+ @resp_map.delete(token)
497
+ end
498
+
499
+ if response and response.header
500
+ status = response.header[STATUS_HDR]
501
+ raise NoRespondersError if status == "503"
502
+ end
503
+
504
+ response
505
+ end
506
+
507
+ # Makes a NATS request using a NATS::Msg that may include headers.
508
+ def request_msg(msg, opts={})
509
+ raise TypeError, "nats: expected NATS::Msg, got #{msg.class.name}" unless msg.is_a?(Msg)
510
+ raise BadSubject if !msg.subject or msg.subject.empty?
511
+
512
+ token = nil
513
+ inbox = nil
514
+ future = nil
515
+ response = nil
516
+ timeout = opts[:timeout] ||= 0.5
517
+ synchronize do
518
+ start_resp_mux_sub! unless @resp_sub_prefix
519
+
520
+ # Create token for this request.
521
+ token = @nuid.next
522
+ inbox = "#{@resp_sub_prefix}.#{token}"
523
+
524
+ # Create the a future for the request that will
525
+ # get signaled when it receives the request.
526
+ future = @resp_sub.new_cond
527
+ @resp_map[token][:future] = future
528
+ end
529
+ msg.reply = inbox
530
+ msg.data ||= ''
531
+ msg_size = msg.data.bytesize
532
+
533
+ # Publish request and wait for reply.
534
+ publish_msg(msg)
535
+ begin
536
+ with_nats_timeout(timeout) do
537
+ @resp_sub.synchronize do
538
+ future.wait(timeout)
539
+ end
540
+ end
541
+ rescue NATS::IO::Timeout => e
542
+ synchronize { @resp_map.delete(token) }
543
+ raise e
544
+ end
545
+
546
+ # Check if there is a response already.
547
+ synchronize do
548
+ result = @resp_map[token]
549
+ response = result[:response]
550
+ @resp_map.delete(token)
551
+ end
552
+
553
+ if response and response.header
554
+ status = response.header[STATUS_HDR]
555
+ raise NoRespondersError if status == "503"
450
556
  end
451
557
 
452
558
  response
@@ -464,11 +570,13 @@ module NATS
464
570
  # the messages asynchronously and return the sid.
465
571
  if blk
466
572
  opts[:max] ||= 1
467
- s = subscribe(inbox, opts) do |msg, reply|
573
+ s = subscribe(inbox, opts) do |msg, reply, subject, header|
468
574
  case blk.arity
469
575
  when 0 then blk.call
470
576
  when 1 then blk.call(msg)
471
- else blk.call(msg, reply)
577
+ when 2 then blk.call(msg, reply)
578
+ when 3 then blk.call(msg, reply, subject)
579
+ else blk.call(msg, reply, subject, header)
472
580
  end
473
581
  end
474
582
  publish(subject, payload, inbox)
@@ -594,8 +702,7 @@ module NATS
594
702
  process_op_error(e)
595
703
  end
596
704
 
597
- def process_msg(subject, sid, reply, data)
598
- # Accounting
705
+ def process_msg(subject, sid, reply, data, header)
599
706
  @stats[:in_msgs] += 1
600
707
  @stats[:in_bytes] += data.size
601
708
 
@@ -604,7 +711,7 @@ module NATS
604
711
  synchronize { sub = @subs[sid] }
605
712
  return unless sub
606
713
 
607
- sc = nil
714
+ err = nil
608
715
  sub.synchronize do
609
716
  sub.received += 1
610
717
 
@@ -625,7 +732,8 @@ module NATS
625
732
  # do so here already while holding the lock and return
626
733
  if sub.future
627
734
  future = sub.future
628
- sub.response = Msg.new(subject, reply, data)
735
+ hdr = process_hdr(header)
736
+ sub.response = Msg.new(subject: subject, reply: reply, data: data, header: hdr)
629
737
  future.signal
630
738
 
631
739
  return
@@ -634,20 +742,54 @@ module NATS
634
742
  # and should be able to consume messages in parallel.
635
743
  if sub.pending_queue.size >= sub.pending_msgs_limit \
636
744
  or sub.pending_size >= sub.pending_bytes_limit then
637
- sc = SlowConsumer.new("nats: slow consumer, messages dropped")
745
+ err = SlowConsumer.new("nats: slow consumer, messages dropped")
638
746
  else
747
+ hdr = process_hdr(header)
748
+
639
749
  # Only dispatch message when sure that it would not block
640
750
  # the main read loop from the parser.
641
- sub.pending_queue << Msg.new(subject, reply, data)
751
+ msg = Msg.new(subject: subject, reply: reply, data: data, header: hdr)
752
+ sub.pending_queue << msg
642
753
  sub.pending_size += data.size
643
754
  end
644
755
  end
645
756
  end
646
757
 
647
758
  synchronize do
648
- @last_err = sc
649
- @err_cb.call(sc) if @err_cb
650
- end if sc
759
+ @last_err = err
760
+ @err_cb.call(err) if @err_cb
761
+ end if err
762
+ end
763
+
764
+ def process_hdr(header)
765
+ hdr = nil
766
+ if header
767
+ hdr = {}
768
+ lines = header.lines
769
+
770
+ # Check if it is an inline status and description.
771
+ if lines.count <= 2
772
+ status_hdr = lines.first.rstrip
773
+ hdr[STATUS_HDR] = status_hdr.slice(NATS_HDR_LINE_SIZE-1, STATUS_MSG_LEN)
774
+
775
+ if NATS_HDR_LINE_SIZE+2 < status_hdr.bytesize
776
+ desc = status_hdr.slice(NATS_HDR_LINE_SIZE+STATUS_MSG_LEN, status_hdr.bytesize)
777
+ hdr[DESC_HDR] = desc unless desc.empty?
778
+ end
779
+ end
780
+ begin
781
+ lines.slice(1, header.size).each do |line|
782
+ line.rstrip!
783
+ next if line.empty?
784
+ key, value = line.strip.split(/\s*:\s*/, 2)
785
+ hdr[key] = value
786
+ end
787
+ rescue => e
788
+ err = e
789
+ end
790
+ end
791
+
792
+ hdr
651
793
  end
652
794
 
653
795
  def process_info(line)
@@ -831,6 +973,15 @@ module NATS
831
973
  cs[:sig] = @signature_cb.call(nonce)
832
974
  end
833
975
 
976
+ if @server_info[:headers]
977
+ cs[:headers] = @server_info[:headers]
978
+ cs[:no_responders] = if @options[:no_responders] == false
979
+ @options[:no_responders]
980
+ else
981
+ @server_info[:headers]
982
+ end
983
+ end
984
+
834
985
  "CONNECT #{cs.to_json}#{CR_LF}"
835
986
  end
836
987
 
@@ -1537,7 +1688,7 @@ module NATS
1537
1688
  end
1538
1689
  end
1539
1690
 
1540
- Msg = Struct.new(:subject, :reply, :data)
1691
+ Msg = Struct.new(:subject, :reply, :data, :header, keyword_init: true)
1541
1692
 
1542
1693
  class Subscription
1543
1694
  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
@@ -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.6.2"
19
- LANG = "#{RUBY_ENGINE}2".freeze
18
+ VERSION = "0.7.0"
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.6.2
4
+ version: 0.7.0
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: 2019-07-22 00:00:00.000000000 Z
11
+ date: 2021-08-17 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,8 +41,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
41
41
  - !ruby/object:Gem::Version
42
42
  version: '0'
43
43
  requirements: []
44
- rubygems_version: 3.0.3
45
- signing_key:
44
+ rubygems_version: 3.2.22
45
+ signing_key:
46
46
  specification_version: 4
47
47
  summary: NATS is an open-source, high-performance, lightweight cloud messaging system.
48
48
  test_files: []