nats 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY.md +3 -0
- data/README.md +52 -10
- data/bin/nats-pub +3 -2
- data/bin/nats-queue +2 -1
- data/bin/nats-request +2 -1
- data/bin/nats-sub +3 -2
- data/lib/nats/client.rb +137 -21
- data/lib/nats/version.rb +1 -1
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da544e9e03c583ba122aebfae0813c138de976f84da6255255aa414b8d637f82
|
4
|
+
data.tar.gz: a98249f31459f3a6d917d3bc6660c9ac8a727c0af5b77cac5f445b6969de670b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0230ee1118324856fc933c80292623e858115feeb8423c7ab66e5f0d4fb75e23cf804183117423279aa4a7b684af40bfa2a5ae52f225eb7601a7a292291d6443
|
7
|
+
data.tar.gz: b9b7de57143d9898310ce65f591ca217c55becb5085287c2f0ad2d9982862af17aa2834786a34596aa0bde6c4e4198f4a9e9cda4d9e3438061fd028bb67a2809
|
data/HISTORY.md
CHANGED
data/README.md
CHANGED
@@ -3,16 +3,7 @@
|
|
3
3
|
A [Ruby](http://ruby-lang.org) client for the [NATS messaging system](https://nats.io).
|
4
4
|
|
5
5
|
[](https://www.apache.org/licenses/LICENSE-2.0)
|
6
|
-
[ gem.
|
6
|
+
[](http://travis-ci.org/nats-io/nats.rb) [](https://rubygems.org/gems/nats/versions/0.11.0) [](https://www.rubydoc.info/gems/nats)
|
16
7
|
|
17
8
|
## Getting Started
|
18
9
|
|
@@ -23,6 +14,16 @@ nats-sub foo &
|
|
23
14
|
nats-pub foo 'Hello World!'
|
24
15
|
```
|
25
16
|
|
17
|
+
Starting from [v0.11.0](https://github.com/nats-io/nats.py/releases/tag/v0.11.0) release,
|
18
|
+
you can also optionally install [NKEYS](https://github.com/nats-io/nkeys.rb) in order to use
|
19
|
+
the new NATS v2.0 auth features:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install nkeys
|
23
|
+
```
|
24
|
+
|
25
|
+
If you're looking for a non-EventMachine alternative, check out the [nats-pure](https://github.com/nats-io/nats-pure.rb) gem.
|
26
|
+
|
26
27
|
## Basic Usage
|
27
28
|
|
28
29
|
```ruby
|
@@ -285,6 +286,47 @@ NATS.start {
|
|
285
286
|
}
|
286
287
|
```
|
287
288
|
|
289
|
+
### New Authentication (Nkeys and User Credentials)
|
290
|
+
|
291
|
+
This requires server with version >= 2.0.0
|
292
|
+
|
293
|
+
NATS servers have a new security and authentication mechanism to authenticate with user credentials and NKEYS. A single file containing the JWT and NKEYS to authenticate against a NATS v2 server can be set with the `user_credentials` option:
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
require 'nats/client'
|
297
|
+
|
298
|
+
NATS.start("tls://connect.ngs.global", user_credentials: "/path/to/creds") do |nc|
|
299
|
+
nc.subscribe("hello") do |msg|
|
300
|
+
puts "[Received] #{msg}"
|
301
|
+
end
|
302
|
+
nc.publish('hello', 'world')
|
303
|
+
end
|
304
|
+
```
|
305
|
+
|
306
|
+
This will create two callback handlers to present the user JWT and sign the nonce challenge from the server. The core client library never has direct access to your private key and simply performs the callback for signing the server challenge. The library will load and wipe and clear the objects it uses for each connect or reconnect.
|
307
|
+
|
308
|
+
Bare NKEYS are also supported. The nkey seed should be in a read only file, e.g. `seed.txt`.
|
309
|
+
|
310
|
+
```bash
|
311
|
+
> cat seed.txt
|
312
|
+
# This is my seed nkey!
|
313
|
+
SUAGMJH5XLGZKQQWAWKRZJIGMOU4HPFUYLXJMXOO5NLFEO2OOQJ5LPRDPM
|
314
|
+
```
|
315
|
+
|
316
|
+
Then in the client specify the path to the seed using the `nkeys_seed` option:
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
require 'nats/client'
|
320
|
+
|
321
|
+
NATS.start("tls://connect.ngs.global", nkeys_seed: "path/to/seed.txt") do |nc|
|
322
|
+
nc.subscribe("hello") do |msg|
|
323
|
+
puts "[Received] #{msg}"
|
324
|
+
end
|
325
|
+
nc.publish('hello', 'world')
|
326
|
+
end
|
327
|
+
|
328
|
+
```
|
329
|
+
|
288
330
|
## License
|
289
331
|
|
290
332
|
Unless otherwise noted, the NATS source files are distributed under
|
data/bin/nats-pub
CHANGED
@@ -18,12 +18,13 @@ require 'rubygems'
|
|
18
18
|
require 'nats/client'
|
19
19
|
|
20
20
|
def usage
|
21
|
-
puts "Usage: nats-pub <subject> <msg> [-s server]"; exit
|
21
|
+
puts "Usage: nats-pub <subject> <msg> [-s server] [--creds CREDS]"; exit
|
22
22
|
end
|
23
23
|
|
24
24
|
args = ARGV.dup
|
25
25
|
opts_parser = OptionParser.new do |opts|
|
26
26
|
opts.on('-s SERVER') { |server| $nats_server = server }
|
27
|
+
opts.on('--creds CREDS') { |creds| $creds = creds }
|
27
28
|
end
|
28
29
|
args = opts_parser.parse!(args)
|
29
30
|
|
@@ -33,7 +34,7 @@ msg ||= 'Hello World'
|
|
33
34
|
|
34
35
|
NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
|
35
36
|
|
36
|
-
NATS.start(:
|
37
|
+
NATS.start($nats_server, user_credentials: $creds) do
|
37
38
|
NATS.publish(subject, msg) { NATS.stop }
|
38
39
|
end
|
39
40
|
|
data/bin/nats-queue
CHANGED
@@ -28,6 +28,7 @@ opts_parser = OptionParser.new do |opts|
|
|
28
28
|
opts.on('-s SERVER') { |server| $nats_server = server }
|
29
29
|
opts.on('-t','--time') { $show_time = true }
|
30
30
|
opts.on('-r','--raw') { $show_raw = true }
|
31
|
+
opts.on('--creds CREDS') { |creds| $creds = creds }
|
31
32
|
end
|
32
33
|
args = opts_parser.parse!(args)
|
33
34
|
|
@@ -54,7 +55,7 @@ end
|
|
54
55
|
|
55
56
|
NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
|
56
57
|
|
57
|
-
NATS.start(:
|
58
|
+
NATS.start($nats_server, user_credentials: $creds) do
|
58
59
|
puts "Listening on [#{subject}], queue group [#{queue_group}]" unless $show_raw
|
59
60
|
NATS.subscribe(subject, :queue => queue_group) { |msg, _, sub|
|
60
61
|
puts decorate(sub, msg)
|
data/bin/nats-request
CHANGED
@@ -29,6 +29,7 @@ opts_parser = OptionParser.new do |opts|
|
|
29
29
|
opts.on('-t','--time') { $show_time = true }
|
30
30
|
opts.on('-r','--raw') { $show_raw = true }
|
31
31
|
opts.on('-n RESPONSES') { |responses| $responses = Integer(responses) if Integer(responses) > 0 }
|
32
|
+
opts.on('--creds CREDS') { |creds| $creds = creds }
|
32
33
|
end
|
33
34
|
args = opts_parser.parse!(args)
|
34
35
|
|
@@ -55,7 +56,7 @@ end
|
|
55
56
|
|
56
57
|
NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
|
57
58
|
|
58
|
-
NATS.start(:
|
59
|
+
NATS.start($nats_server, user_credentials: $creds) do
|
59
60
|
NATS.request(subject, msg) { |(msg, reply)|
|
60
61
|
puts decorate(msg)
|
61
62
|
exit! if $responses && ($responses-=1) < 1
|
data/bin/nats-sub
CHANGED
@@ -20,7 +20,7 @@ require 'nats/client'
|
|
20
20
|
['TERM', 'INT'].each { |s| trap(s) { puts; exit! } }
|
21
21
|
|
22
22
|
def usage
|
23
|
-
puts "Usage: nats-sub <subject> [-s server] [-t] [-r]"; exit
|
23
|
+
puts "Usage: nats-sub <subject> [-s server] [--creds CREDS] [-t] [-r]"; exit
|
24
24
|
end
|
25
25
|
|
26
26
|
args = ARGV.dup
|
@@ -28,6 +28,7 @@ opts_parser = OptionParser.new do |opts|
|
|
28
28
|
opts.on('-s SERVER') { |server| $nats_server = server }
|
29
29
|
opts.on('-t','--time') { $show_time = true }
|
30
30
|
opts.on('-r','--raw') { $show_raw = true }
|
31
|
+
opts.on('--creds CREDS') { |creds| $creds = creds }
|
31
32
|
end
|
32
33
|
args = opts_parser.parse!(args)
|
33
34
|
|
@@ -53,7 +54,7 @@ end
|
|
53
54
|
|
54
55
|
NATS.on_error { |err| puts "Server Error: #{err}"; exit! }
|
55
56
|
|
56
|
-
NATS.start(:
|
57
|
+
NATS.start($nats_server, user_credentials: $creds) do
|
57
58
|
puts "Listening on [#{subject}]" unless $show_raw
|
58
59
|
NATS.subscribe(subject) { |msg, _, sub| puts decorate(sub, msg) }
|
59
60
|
end
|
data/lib/nats/client.rb
CHANGED
@@ -72,6 +72,7 @@ module NATS
|
|
72
72
|
# Parser
|
73
73
|
AWAITING_CONTROL_LINE = 1 #:nodoc:
|
74
74
|
AWAITING_MSG_PAYLOAD = 2 #:nodoc:
|
75
|
+
AWAITING_INFO_LINE = 3 # :nodoc:
|
75
76
|
|
76
77
|
class Error < StandardError; end #:nodoc:
|
77
78
|
|
@@ -150,7 +151,9 @@ module NATS
|
|
150
151
|
def connect(uri=nil, opts={}, &blk)
|
151
152
|
case uri
|
152
153
|
when String
|
153
|
-
|
154
|
+
# Initialize TLS defaults in case any url is using it.
|
155
|
+
uris = opts[:uri] = process_uri(uri)
|
156
|
+
opts[:tls] ||= {} if uris.any? {|u| u.scheme == 'tls'}
|
154
157
|
when Hash
|
155
158
|
opts = uri
|
156
159
|
end
|
@@ -482,7 +485,16 @@ module NATS
|
|
482
485
|
# Drain mode
|
483
486
|
@draining = false
|
484
487
|
@drained_subs = false
|
485
|
-
|
488
|
+
|
489
|
+
# NKEYS
|
490
|
+
@user_credentials = options[:user_credentials] if options[:user_credentials]
|
491
|
+
@nkeys_seed = options[:nkeys_seed] if options[:nkeys_seed]
|
492
|
+
@user_nkey_cb = nil
|
493
|
+
@user_jwt_cb = nil
|
494
|
+
@signature_cb = nil
|
495
|
+
|
496
|
+
# NKEYS
|
497
|
+
setup_nkeys_connect if @user_credentials or @nkeys_seed
|
486
498
|
end
|
487
499
|
|
488
500
|
# Publish a message to a given subject, with optional reply subject and completion block
|
@@ -770,7 +782,7 @@ module NATS
|
|
770
782
|
end
|
771
783
|
|
772
784
|
def auth_connection?
|
773
|
-
!@uri.user.nil? || @options[:token]
|
785
|
+
!@uri.user.nil? || @options[:token] || @server_info[:auth_required]
|
774
786
|
end
|
775
787
|
|
776
788
|
def connect_command #:nodoc:
|
@@ -782,7 +794,16 @@ module NATS
|
|
782
794
|
:protocol => ::NATS::PROTOCOL_VERSION,
|
783
795
|
:echo => !@options[:no_echo]
|
784
796
|
}
|
797
|
+
|
785
798
|
case
|
799
|
+
when @options[:user_credentials]
|
800
|
+
nonce = @server_info[:nonce]
|
801
|
+
cs[:jwt] = @user_jwt_cb.call
|
802
|
+
cs[:sig] = @signature_cb.call(nonce)
|
803
|
+
when @options[:nkeys_seed]
|
804
|
+
nonce = @server_info[:nonce]
|
805
|
+
cs[:nkey] = @user_nkey_cb.call
|
806
|
+
cs[:sig] = @signature_cb.call(nonce)
|
786
807
|
when @options[:token]
|
787
808
|
cs[:auth_token] = @options[:token]
|
788
809
|
when @uri.password.nil?
|
@@ -853,6 +874,15 @@ module NATS
|
|
853
874
|
|
854
875
|
while (@buf)
|
855
876
|
case @parse_state
|
877
|
+
when AWAITING_INFO_LINE
|
878
|
+
case @buf
|
879
|
+
when INFO
|
880
|
+
@buf = $'
|
881
|
+
process_connect_init($1)
|
882
|
+
else
|
883
|
+
# If we are here we do not have a complete line yet that we understand.
|
884
|
+
return
|
885
|
+
end
|
856
886
|
when AWAITING_CONTROL_LINE
|
857
887
|
case @buf
|
858
888
|
when MSG
|
@@ -901,7 +931,7 @@ module NATS
|
|
901
931
|
end
|
902
932
|
end
|
903
933
|
|
904
|
-
def
|
934
|
+
def process_connect_init(info) # :nodoc:
|
905
935
|
# Each JSON parser uses a different key/value pair to use symbol keys
|
906
936
|
# instead of strings when parsing. Passing all three pairs assures each
|
907
937
|
# parser gets what it needs. For the json gem :symbolize_name, for yajl
|
@@ -931,9 +961,29 @@ module NATS
|
|
931
961
|
close_connection_after_writing
|
932
962
|
end
|
933
963
|
end
|
964
|
+
send_connect_command
|
965
|
+
|
966
|
+
# Only initial INFO command is treated specially for auth reasons,
|
967
|
+
# the rest are processed asynchronously to discover servers.
|
968
|
+
@parse_state = AWAITING_CONTROL_LINE
|
969
|
+
process_info(info)
|
970
|
+
process_connect
|
971
|
+
|
972
|
+
if @server_info[:auth_required]
|
973
|
+
current = server_pool.first
|
974
|
+
current[:auth_required] = true
|
975
|
+
|
976
|
+
# Send pending connect followed by ping/pong to ensure we're authorized.
|
977
|
+
queue_server_rt { current[:auth_ok] = true }
|
978
|
+
end
|
979
|
+
flush_pending
|
980
|
+
end
|
981
|
+
|
982
|
+
def process_info(info_line) #:nodoc:
|
983
|
+
info = JSON.parse(info_line, :symbolize_keys => true, :symbolize_names => true, :symbol_keys => true)
|
934
984
|
|
935
985
|
# Detect any announced server that we might not be aware of...
|
936
|
-
connect_urls =
|
986
|
+
connect_urls = info[:connect_urls]
|
937
987
|
if connect_urls
|
938
988
|
srvs = []
|
939
989
|
|
@@ -963,15 +1013,7 @@ module NATS
|
|
963
1013
|
server_pool.push(*srvs)
|
964
1014
|
end
|
965
1015
|
|
966
|
-
|
967
|
-
current = server_pool.first
|
968
|
-
current[:auth_required] = true
|
969
|
-
# Send pending connect followed by ping/pong to ensure we're authorized.
|
970
|
-
queue_server_rt { current[:auth_ok] = true }
|
971
|
-
flush_pending
|
972
|
-
end
|
973
|
-
|
974
|
-
@server_info
|
1016
|
+
info
|
975
1017
|
end
|
976
1018
|
|
977
1019
|
def client_using_secure_connection?
|
@@ -1002,22 +1044,19 @@ module NATS
|
|
1002
1044
|
end
|
1003
1045
|
|
1004
1046
|
def connection_completed #:nodoc:
|
1005
|
-
@parse_state =
|
1047
|
+
@parse_state = AWAITING_INFO_LINE
|
1006
1048
|
|
1007
1049
|
# Delay sending CONNECT or any other command here until we are sure
|
1008
1050
|
# that we have a valid established secure connection.
|
1009
1051
|
return if (@ssl or @tls)
|
1010
1052
|
|
1011
|
-
# Mark that we established already TCP connection to the server
|
1012
|
-
#
|
1013
|
-
# we have processed the INFO line sent by the server and done the handshake.
|
1053
|
+
# Mark that we established already TCP connection to the server,
|
1054
|
+
# when using TLS we only do so after handshake has been completed.
|
1014
1055
|
@connected = true
|
1015
|
-
process_connect
|
1016
1056
|
end
|
1017
1057
|
|
1018
1058
|
def ssl_handshake_completed
|
1019
1059
|
@connected = true
|
1020
|
-
process_connect
|
1021
1060
|
end
|
1022
1061
|
|
1023
1062
|
def process_connect #:nodoc:
|
@@ -1174,7 +1213,6 @@ module NATS
|
|
1174
1213
|
current[:reconnect_attempts] ||= 0
|
1175
1214
|
current[:reconnect_attempts] += 1
|
1176
1215
|
|
1177
|
-
send_connect_command
|
1178
1216
|
begin
|
1179
1217
|
EM.reconnect(@uri.host, @uri.port, self)
|
1180
1218
|
rescue
|
@@ -1201,6 +1239,84 @@ module NATS
|
|
1201
1239
|
true
|
1202
1240
|
end
|
1203
1241
|
|
1242
|
+
def setup_nkeys_connect
|
1243
|
+
begin
|
1244
|
+
require 'nkeys'
|
1245
|
+
require 'base64'
|
1246
|
+
rescue LoadError
|
1247
|
+
raise(Error, "nkeys is not installed")
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
case
|
1251
|
+
when @nkeys_seed
|
1252
|
+
@user_nkey_cb = proc {
|
1253
|
+
seed = File.read(@nkeys_seed).chomp
|
1254
|
+
kp = NKEYS::from_seed(seed)
|
1255
|
+
|
1256
|
+
# Take a copy since original will be gone with the wipe.
|
1257
|
+
pub_key = kp.public_key.dup
|
1258
|
+
kp.wipe!
|
1259
|
+
|
1260
|
+
pub_key
|
1261
|
+
}
|
1262
|
+
|
1263
|
+
@signature_cb = proc { |nonce|
|
1264
|
+
seed = File.read(@nkeys_seed).chomp
|
1265
|
+
kp = NKEYS::from_seed(seed)
|
1266
|
+
raw_signed = kp.sign(nonce)
|
1267
|
+
kp.wipe!
|
1268
|
+
encoded = Base64.urlsafe_encode64(raw_signed)
|
1269
|
+
encoded.gsub('=', '')
|
1270
|
+
}
|
1271
|
+
when @user_credentials
|
1272
|
+
# When the credentials are within a single decorated file.
|
1273
|
+
@user_jwt_cb = proc {
|
1274
|
+
jwt_start = "BEGIN NATS USER JWT".freeze
|
1275
|
+
found = false
|
1276
|
+
jwt = nil
|
1277
|
+
File.readlines(@user_credentials).each do |line|
|
1278
|
+
case
|
1279
|
+
when found
|
1280
|
+
jwt = line.chomp
|
1281
|
+
break
|
1282
|
+
when line.include?(jwt_start)
|
1283
|
+
found = true
|
1284
|
+
end
|
1285
|
+
end
|
1286
|
+
raise(Error, "No JWT found in #{@user_credentials}") if not found
|
1287
|
+
|
1288
|
+
jwt
|
1289
|
+
}
|
1290
|
+
|
1291
|
+
@signature_cb = proc { |nonce|
|
1292
|
+
seed_start = "BEGIN USER NKEY SEED".freeze
|
1293
|
+
found = false
|
1294
|
+
seed = nil
|
1295
|
+
File.readlines(@user_credentials).each do |line|
|
1296
|
+
case
|
1297
|
+
when found
|
1298
|
+
seed = line.chomp
|
1299
|
+
break
|
1300
|
+
when line.include?(seed_start)
|
1301
|
+
found = true
|
1302
|
+
end
|
1303
|
+
end
|
1304
|
+
raise(Error, "No nkey user seed found in #{@user_credentials}") if not found
|
1305
|
+
|
1306
|
+
kp = NKEYS::from_seed(seed)
|
1307
|
+
raw_signed = kp.sign(nonce)
|
1308
|
+
|
1309
|
+
# seed is a reference so also cleared when doing wipe,
|
1310
|
+
# which can be done since Ruby strings are mutable.
|
1311
|
+
kp.wipe
|
1312
|
+
encoded = Base64.urlsafe_encode64(raw_signed)
|
1313
|
+
|
1314
|
+
# Remove padding
|
1315
|
+
encoded.gsub('=', '')
|
1316
|
+
}
|
1317
|
+
end
|
1318
|
+
end
|
1319
|
+
|
1204
1320
|
# Parse out URIs which can now be an array of server choices
|
1205
1321
|
# The server pool will contain both explicit and implicit members.
|
1206
1322
|
def process_uri_options #:nodoc
|
data/lib/nats/version.rb
CHANGED
metadata
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nats
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Collison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.2'
|
20
|
-
- - "
|
20
|
+
- - "~>"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '1.2'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - "
|
27
|
+
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '1.2'
|
30
|
-
- - "
|
30
|
+
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '1.2'
|
33
33
|
description: NATS is an open-source, high-performance, lightweight cloud messaging
|
@@ -88,8 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
requirements: []
|
91
|
-
|
92
|
-
rubygems_version: 2.7.3
|
91
|
+
rubygems_version: 3.0.3
|
93
92
|
signing_key:
|
94
93
|
specification_version: 4
|
95
94
|
summary: NATS is an open-source, high-performance, lightweight cloud messaging system.
|