nats 0.4.28 → 0.5.0.beta.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/HISTORY.md +16 -0
- data/README.md +8 -5
- data/bin/nats-queue +14 -5
- data/bin/nats-request +12 -3
- data/bin/nats-sub +13 -5
- data/lib/nats/client.rb +189 -35
- data/lib/nats/server.rb +27 -4
- data/lib/nats/server/cluster.rb +102 -0
- data/lib/nats/server/connection.rb +47 -17
- data/lib/nats/server/connz.rb +0 -2
- data/lib/nats/server/const.rb +14 -2
- data/lib/nats/server/options.rb +19 -2
- data/lib/nats/server/route.rb +279 -0
- data/lib/nats/server/server.rb +35 -10
- data/lib/nats/server/varz.rb +1 -0
- data/nats.gemspec +6 -4
- metadata +38 -26
data/lib/nats/server.rb
CHANGED
@@ -13,6 +13,8 @@ require "#{ep}/server/server"
|
|
13
13
|
require "#{ep}/server/sublist"
|
14
14
|
require "#{ep}/server/connection"
|
15
15
|
require "#{ep}/server/options"
|
16
|
+
require "#{ep}/server/cluster"
|
17
|
+
require "#{ep}/server/route"
|
16
18
|
require "#{ep}/server/const"
|
17
19
|
require "#{ep}/server/util"
|
18
20
|
require "#{ep}/server/varz"
|
@@ -22,12 +24,13 @@ require "#{ep}/server/connz"
|
|
22
24
|
NATSD::Server.setup(ARGV.dup)
|
23
25
|
|
24
26
|
# Event Loop
|
25
|
-
EM.run
|
27
|
+
EM.run do
|
28
|
+
|
26
29
|
log "Starting #{NATSD::APP_NAME} version #{NATSD::VERSION} on port #{NATSD::Server.port}"
|
27
|
-
log "TLS/SSL Support Enabled" if NATSD::Server.options[:ssl]
|
30
|
+
log "TLS/SSL Support Enabled" if NATSD::Server.options[:ssl]
|
28
31
|
begin
|
29
32
|
EM.set_descriptor_table_size(32768) # Requires Root privileges
|
30
|
-
|
33
|
+
EM.start_server(NATSD::Server.host, NATSD::Server.port, NATSD::Connection)
|
31
34
|
rescue => e
|
32
35
|
log "Could not start server on port #{NATSD::Server.port}"
|
33
36
|
log_error
|
@@ -44,4 +47,24 @@ EM.run {
|
|
44
47
|
exit(1)
|
45
48
|
end
|
46
49
|
end
|
47
|
-
|
50
|
+
|
51
|
+
###################
|
52
|
+
# CLUSTER SETUP
|
53
|
+
###################
|
54
|
+
|
55
|
+
# Check to see if we need to fire up a routing listen port
|
56
|
+
if NATSD::Server.options[:cluster_port]
|
57
|
+
begin
|
58
|
+
log "Starting routing on port #{NATSD::Server.options[:cluster_port]}"
|
59
|
+
EM.start_server(NATSD::Server.host, NATSD::Server.options[:cluster_port], NATSD::Route)
|
60
|
+
rescue => e
|
61
|
+
log "Could not start routing server on port #{NATSD::Server.options[:cluster_port]}"
|
62
|
+
log_error
|
63
|
+
exit(1)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# If we have active connections, solicit them now..
|
68
|
+
NATSD::Server.solicit_routes if NATSD::Server.options[:cluster_routes]
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module NATSD #:nodoc: all
|
4
|
+
|
5
|
+
class Server
|
6
|
+
class << self
|
7
|
+
attr_reader :opt_routes, :route_auth_required, :route_ssl_required, :reconnect_interval
|
8
|
+
attr_accessor :num_routes
|
9
|
+
|
10
|
+
alias route_auth_required? :route_auth_required
|
11
|
+
alias route_ssl_required? :route_ssl_required
|
12
|
+
|
13
|
+
def connected_routes
|
14
|
+
@routes ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_route(route)
|
18
|
+
connected_routes << route unless route.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove_route(route)
|
22
|
+
connected_routes.delete(route) unless route.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def route_info_string
|
26
|
+
@route_info = {
|
27
|
+
:server_id => Server.id,
|
28
|
+
:host => @options[:cluster_net] || host,
|
29
|
+
:port => @options[:cluster_port],
|
30
|
+
:version => VERSION,
|
31
|
+
:auth_required => route_auth_required?,
|
32
|
+
:ssl_required => false, # FIXME!
|
33
|
+
:max_payload => @max_payload
|
34
|
+
}
|
35
|
+
@route_info.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
def route_key(route_url)
|
39
|
+
r = URI.parse(route_url)
|
40
|
+
"#{r.host}:#{r.port}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def route_auth_ok?(user, pass)
|
44
|
+
user == @options[:cluster_user] && pass == @options[:cluster_pass]
|
45
|
+
end
|
46
|
+
|
47
|
+
def solicit_routes #:nodoc:
|
48
|
+
@opt_routes = []
|
49
|
+
NATSD::Server.options[:cluster_routes].each do |r_url|
|
50
|
+
opt_routes << { :route => r_url, :uri => URI.parse(r_url), :key => route_key(r_url) }
|
51
|
+
end
|
52
|
+
try_to_connect_routes
|
53
|
+
end
|
54
|
+
|
55
|
+
def try_to_connect_routes #:nodoc:
|
56
|
+
opt_routes.each do |route|
|
57
|
+
# FIXME, Strip auth
|
58
|
+
debug "Trying to connect to route: #{route[:route]}"
|
59
|
+
EM.connect(route[:uri].host, route[:uri].port, NATSD::Route, route)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def broadcast_proto_to_routes(proto)
|
64
|
+
connected_routes.each { |r| r.queue_data(proto) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def rsid_qsub(rsid)
|
68
|
+
cid, sid = parse_rsid(rsid)
|
69
|
+
conn = Server.connections[cid]
|
70
|
+
sub = conn.subscriptions[sid]
|
71
|
+
sub if sub.qgroup
|
72
|
+
rescue
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_rsid(rsid)
|
77
|
+
m = RSID.match(rsid)
|
78
|
+
return [m[1].to_i, m[2]] if m
|
79
|
+
end
|
80
|
+
|
81
|
+
def routed_sid(sub)
|
82
|
+
"RSID:#{sub.conn.cid}:#{sub.sid}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def route_sub_proto(sub)
|
86
|
+
return "SUB #{sub.subject} #{routed_sid(sub)}#{CR_LF}" if sub.qgroup.nil?
|
87
|
+
return "SUB #{sub.subject} #{sub.qgroup} #{routed_sid(sub)}#{CR_LF}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def broadcast_sub_to_routes(sub)
|
91
|
+
broadcast_proto_to_routes(route_sub_proto(sub))
|
92
|
+
end
|
93
|
+
|
94
|
+
def broadcast_unsub_to_routes(sub)
|
95
|
+
opt_max_str = " #{sub.max_responses}" unless sub.max_responses.nil?
|
96
|
+
broadcast_proto_to_routes("UNSUB #{routed_sid(sub)}#{opt_max_str}#{CR_LF}")
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -3,7 +3,7 @@ module NATSD #:nodoc: all
|
|
3
3
|
module Connection #:nodoc: all
|
4
4
|
|
5
5
|
attr_accessor :in_msgs, :out_msgs, :in_bytes, :out_bytes
|
6
|
-
attr_reader :cid, :closing, :last_activity, :writev_size
|
6
|
+
attr_reader :cid, :closing, :last_activity, :writev_size, :subscriptions
|
7
7
|
alias :closing? :closing
|
8
8
|
|
9
9
|
def flush_data
|
@@ -52,7 +52,7 @@ module NATSD #:nodoc: all
|
|
52
52
|
@writev_size = 0
|
53
53
|
@parse_state = AWAITING_CONTROL_LINE
|
54
54
|
send_info
|
55
|
-
debug "
|
55
|
+
debug "#{type} connection created", client_info, cid
|
56
56
|
if Server.ssl_required?
|
57
57
|
debug "Starting TLS/SSL", client_info, cid
|
58
58
|
flush_data
|
@@ -62,7 +62,7 @@ module NATSD #:nodoc: all
|
|
62
62
|
@auth_pending = EM.add_timer(NATSD::Server.auth_timeout) { connect_auth_timeout } if Server.auth_required?
|
63
63
|
@ping_timer = EM.add_periodic_timer(NATSD::Server.ping_interval) { send_ping }
|
64
64
|
@pings_outstanding = 0
|
65
|
-
|
65
|
+
inc_connections
|
66
66
|
return if max_connections_exceeded?
|
67
67
|
end
|
68
68
|
|
@@ -79,12 +79,12 @@ module NATSD #:nodoc: all
|
|
79
79
|
|
80
80
|
def connect_auth_timeout
|
81
81
|
error_close AUTH_REQUIRED
|
82
|
-
debug "
|
82
|
+
debug "#{type} connection timeout due to lack of auth credentials", cid
|
83
83
|
end
|
84
84
|
|
85
85
|
def connect_ssl_timeout
|
86
86
|
error_close SSL_REQUIRED
|
87
|
-
debug "
|
87
|
+
debug "#{type} connection timeout due to lack of TLS/SSL negotiations", cid
|
88
88
|
end
|
89
89
|
|
90
90
|
def receive_data(data)
|
@@ -131,12 +131,12 @@ module NATSD #:nodoc: all
|
|
131
131
|
queue_data(INVALID_SID_NOEXIST) if @pedantic
|
132
132
|
end
|
133
133
|
when PING
|
134
|
-
ctrace('PING OP'
|
134
|
+
ctrace('PING OP') if NATSD::Server.trace_flag?
|
135
135
|
@buf = $'
|
136
136
|
queue_data(PONG_RESPONSE)
|
137
137
|
flush_data
|
138
138
|
when PONG
|
139
|
-
ctrace('PONG OP'
|
139
|
+
ctrace('PONG OP') if NATSD::Server.trace_flag?
|
140
140
|
@buf = $'
|
141
141
|
@pings_outstanding -= 1
|
142
142
|
when CONNECT
|
@@ -149,8 +149,8 @@ module NATSD #:nodoc: all
|
|
149
149
|
queue_data(INVALID_CONFIG)
|
150
150
|
log_error
|
151
151
|
end
|
152
|
-
when
|
153
|
-
ctrace('
|
152
|
+
when INFO_REQ
|
153
|
+
ctrace('INFO_REQUEST OP') if NATSD::Server.trace_flag?
|
154
154
|
return connect_auth_timeout if @auth_pending
|
155
155
|
@buf = $'
|
156
156
|
send_info
|
@@ -196,6 +196,14 @@ module NATSD #:nodoc: all
|
|
196
196
|
queue_data("INFO #{Server.info_string}#{CR_LF}")
|
197
197
|
end
|
198
198
|
|
199
|
+
# Placeholder
|
200
|
+
def process_info(info)
|
201
|
+
end
|
202
|
+
|
203
|
+
def auth_ok?(user, pass)
|
204
|
+
Server.auth_ok?(user, pass)
|
205
|
+
end
|
206
|
+
|
199
207
|
def process_connect_config(config)
|
200
208
|
@verbose = config['verbose'] unless config['verbose'].nil?
|
201
209
|
@pedantic = config['pedantic'] unless config['pedantic'].nil?
|
@@ -203,18 +211,18 @@ module NATSD #:nodoc: all
|
|
203
211
|
return queue_data(OK) unless Server.auth_required?
|
204
212
|
|
205
213
|
EM.cancel_timer(@auth_pending)
|
206
|
-
if
|
214
|
+
if auth_ok?(config['user'], config['pass'])
|
207
215
|
queue_data(OK) if @verbose
|
208
216
|
@auth_pending = nil
|
209
217
|
else
|
210
218
|
error_close AUTH_FAILED
|
211
|
-
debug "Authorization failed for connection", cid
|
219
|
+
debug "Authorization failed for #{type.downcase} connection", cid
|
212
220
|
end
|
213
221
|
end
|
214
222
|
|
215
223
|
def delete_subscriber(sub)
|
216
224
|
ctrace('DELSUB OP', sub.subject, sub.qgroup, sub.sid) if NATSD::Server.trace_flag?
|
217
|
-
Server.unsubscribe(sub)
|
225
|
+
Server.unsubscribe(sub, is_route?)
|
218
226
|
@subscriptions.delete(sub.sid)
|
219
227
|
end
|
220
228
|
|
@@ -235,25 +243,38 @@ module NATSD #:nodoc: all
|
|
235
243
|
debug "Message payload size exceeded (#{sizes}), closing connection"
|
236
244
|
end
|
237
245
|
|
238
|
-
def
|
239
|
-
|
246
|
+
def inc_connections
|
247
|
+
Server.num_connections += 1
|
248
|
+
Server.connections[cid] = self
|
249
|
+
end
|
250
|
+
|
251
|
+
def dec_connections
|
240
252
|
Server.num_connections -= 1
|
241
|
-
|
253
|
+
Server.connections.delete(cid)
|
254
|
+
end
|
255
|
+
|
256
|
+
def process_unbind
|
257
|
+
dec_connections
|
242
258
|
EM.cancel_timer(@ssl_pending) if @ssl_pending
|
243
259
|
@ssl_pending = nil
|
244
260
|
EM.cancel_timer(@auth_pending) if @auth_pending
|
245
261
|
@auth_pending = nil
|
246
262
|
EM.cancel_timer(@ping_timer) if @ping_timer
|
247
263
|
@ping_timer = nil
|
248
|
-
|
264
|
+
@subscriptions.each_value { |sub| Server.unsubscribe(sub) }
|
249
265
|
@closing = true
|
250
266
|
end
|
251
267
|
|
268
|
+
def unbind
|
269
|
+
debug "Client connection closed", client_info, cid
|
270
|
+
process_unbind
|
271
|
+
end
|
272
|
+
|
252
273
|
def ssl_handshake_completed
|
253
274
|
EM.cancel_timer(@ssl_pending)
|
254
275
|
@ssl_pending = nil
|
255
276
|
cert = get_peer_cert
|
256
|
-
debug "
|
277
|
+
debug "#{type} Certificate:", cert ? cert : 'N/A', cid
|
257
278
|
end
|
258
279
|
|
259
280
|
# FIXME! Cert accepted by default
|
@@ -268,6 +289,15 @@ module NATSD #:nodoc: all
|
|
268
289
|
def strip_op(op='')
|
269
290
|
op.dup.sub(CR_LF, EMPTY)
|
270
291
|
end
|
292
|
+
|
293
|
+
def is_route?
|
294
|
+
false
|
295
|
+
end
|
296
|
+
|
297
|
+
def type
|
298
|
+
'Client'
|
299
|
+
end
|
300
|
+
|
271
301
|
end
|
272
302
|
|
273
303
|
end
|
data/lib/nats/server/connz.rb
CHANGED
@@ -33,7 +33,6 @@ module NATSD #:nodoc: all
|
|
33
33
|
|
34
34
|
class Server
|
35
35
|
class << self
|
36
|
-
|
37
36
|
def dump_connections
|
38
37
|
conns, total = [], 0
|
39
38
|
ObjectSpace.each_object(NATSD::Connection) do |c|
|
@@ -47,7 +46,6 @@ module NATSD #:nodoc: all
|
|
47
46
|
:connections => conns
|
48
47
|
}
|
49
48
|
end
|
50
|
-
|
51
49
|
end
|
52
50
|
end
|
53
51
|
|
data/lib/nats/server/const.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
module NATSD #:nodoc:
|
3
3
|
|
4
|
-
VERSION = '0.
|
4
|
+
VERSION = '0.5.0.beta.12'
|
5
5
|
APP_NAME = 'nats-server'
|
6
6
|
|
7
7
|
DEFAULT_PORT = 4222
|
@@ -12,17 +12,23 @@ module NATSD #:nodoc:
|
|
12
12
|
AWAITING_MSG_PAYLOAD = 2
|
13
13
|
|
14
14
|
# Ops - See protocol.txt for more info
|
15
|
-
INFO = /\AINFO\s
|
15
|
+
INFO = /\AINFO\s*([^\r\n]*)\r\n/i
|
16
16
|
PUB_OP = /\APUB\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i
|
17
|
+
MSG = /\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n/i
|
17
18
|
SUB_OP = /\ASUB\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?([^\s]+)\r\n/i
|
18
19
|
UNSUB_OP = /\AUNSUB\s+([^\s]+)\s*(\s+(\d+))?\r\n/i
|
19
20
|
PING = /\APING\s*\r\n/i
|
20
21
|
PONG = /\APONG\s*\r\n/i
|
22
|
+
INFO_REQ = /\AINFO_REQ\s*\r\n/i
|
23
|
+
|
21
24
|
CONNECT = /\ACONNECT\s+([^\r\n]+)\r\n/i
|
22
25
|
UNKNOWN = /\A(.*)\r\n/
|
23
26
|
CTRL_C = /\006/
|
24
27
|
CTRL_D = /\004/
|
25
28
|
|
29
|
+
ERR_RESP = /\A-ERR\s+('.+')?\r\n/i
|
30
|
+
OK_RESP = /\A\+OK\s*\r\n/i #:nodoc:
|
31
|
+
|
26
32
|
# RESPONSES
|
27
33
|
CR_LF = "\r\n".freeze
|
28
34
|
CR_LF_SIZE = CR_LF.bytesize
|
@@ -52,6 +58,9 @@ module NATSD #:nodoc:
|
|
52
58
|
SUB = /^([^\.\*>\s]+|>$|\*)(\.([^\.\*>\s]+|>$|\*))*$/
|
53
59
|
SUB_NO_WC = /^([^\.\*>\s]+)(\.([^\.\*>\s]+))*$/
|
54
60
|
|
61
|
+
# Router Subscription Identifiers
|
62
|
+
RSID = /RSID:(\d+):(\S+)/
|
63
|
+
|
55
64
|
# Some sane default thresholds
|
56
65
|
|
57
66
|
# 1k should be plenty since payloads sans connect string are separate
|
@@ -79,6 +88,9 @@ module NATSD #:nodoc:
|
|
79
88
|
DEFAULT_PING_INTERVAL = 120
|
80
89
|
DEFAULT_PING_MAX = 2
|
81
90
|
|
91
|
+
# Route Reconnect
|
92
|
+
DEFAULT_ROUTE_RECONNECT_INTERVAL = 1.0
|
93
|
+
|
82
94
|
# HTTP
|
83
95
|
RACK_JSON_HDR = { 'Content-Type' => 'application/json' }
|
84
96
|
RACK_TEXT_HDR = { 'Content-Type' => 'text/plain' }
|
data/lib/nats/server/options.rb
CHANGED
@@ -20,6 +20,8 @@ module NATSD
|
|
20
20
|
|
21
21
|
opts.on("-m", "--http_port PORT", "Use HTTP PORT ") { |port| @options[:http_port] = port.to_i }
|
22
22
|
|
23
|
+
opts.on("-r", "--cluster_port PORT", "Use Cluster PORT ") { |port| @options[:cluster_port] = port.to_i }
|
24
|
+
|
23
25
|
opts.on("-c", "--config FILE", "Configuration File") { |file| @options[:config_file] = file }
|
24
26
|
|
25
27
|
opts.separator ""
|
@@ -106,6 +108,21 @@ module NATSD
|
|
106
108
|
@options[:ping_max] = ping['max_outstanding'] if @options[:ping_max].nil?
|
107
109
|
end
|
108
110
|
|
111
|
+
if cluster = config['cluster']
|
112
|
+
@options[:cluster_port] = cluster['port'] if @options[:cluster_port].nil?
|
113
|
+
if auth = cluster['authorization']
|
114
|
+
@options[:cluster_user] = auth['user'] if @options[:cluster_user].nil?
|
115
|
+
@options[:cluster_pass] = auth['password'] if @options[:cluster_pass].nil?
|
116
|
+
@options[:cluster_pass] = auth['pass'] if @options[:cluster_pass].nil?
|
117
|
+
@options[:cluster_token] = auth['token'] if @options[:cluster_token].nil?
|
118
|
+
@options[:cluster_auth_timeout] = auth['timeout'] if @options[:cluster_auth_timeout].nil?
|
119
|
+
@route_auth_required = true
|
120
|
+
end
|
121
|
+
if routes = cluster['routes']
|
122
|
+
@options[:cluster_routes] = routes if @options[:cluster_routes].nil?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
109
126
|
rescue => e
|
110
127
|
log "Could not read configuration file: #{e}"
|
111
128
|
exit 1
|
@@ -120,7 +137,7 @@ module NATSD
|
|
120
137
|
|
121
138
|
def open_syslog
|
122
139
|
return unless @options[:syslog]
|
123
|
-
Syslog.open("#{@options[:syslog]}", Syslog::LOG_PID,
|
140
|
+
Syslog.open("#{@options[:syslog]}", Syslog::LOG_PID, Syslog::LOG_USER) unless Syslog.opened?
|
124
141
|
end
|
125
142
|
|
126
143
|
def close_syslog
|
@@ -173,7 +190,7 @@ module NATSD
|
|
173
190
|
|
174
191
|
@auth_required = (not @options[:user].nil?)
|
175
192
|
|
176
|
-
@ssl_required =
|
193
|
+
@ssl_required = @options[:ssl]
|
177
194
|
|
178
195
|
# Pings
|
179
196
|
@options[:ping_interval] ||= DEFAULT_PING_INTERVAL
|
@@ -0,0 +1,279 @@
|
|
1
|
+
module NATSD #:nodoc: all
|
2
|
+
|
3
|
+
# Need to make this a class with EM > 1.0
|
4
|
+
class Route < EventMachine::Connection #:nodoc:
|
5
|
+
|
6
|
+
include Connection
|
7
|
+
|
8
|
+
attr_reader :rid, :remote_rid, :closing, :r_obj, :reconnecting
|
9
|
+
alias :peer_info :client_info
|
10
|
+
alias :reconnecting? :reconnecting
|
11
|
+
|
12
|
+
def initialize(route=nil)
|
13
|
+
@r_obj = route
|
14
|
+
end
|
15
|
+
|
16
|
+
def solicited?
|
17
|
+
r_obj != nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def connection_completed
|
21
|
+
debug "Route connected", rid
|
22
|
+
return unless reconnecting?
|
23
|
+
|
24
|
+
# Kill reconnect if we got here from there
|
25
|
+
cancel_reconnect
|
26
|
+
@buf, @closing = nil, false
|
27
|
+
post_init
|
28
|
+
end
|
29
|
+
|
30
|
+
def post_init
|
31
|
+
@rid = Server.rid
|
32
|
+
@subscriptions = {}
|
33
|
+
@in_msgs = @out_msgs = @in_bytes = @out_bytes = 0
|
34
|
+
@writev_size = 0
|
35
|
+
@parse_state = AWAITING_CONTROL_LINE
|
36
|
+
|
37
|
+
# Queue up auth if needed and we solicited the connection
|
38
|
+
debug "Route connection created", peer_info, rid
|
39
|
+
|
40
|
+
# queue up auth if needed and we solicited the connection
|
41
|
+
if solicited?
|
42
|
+
debug "Route sent authorization", rid
|
43
|
+
send_auth
|
44
|
+
else
|
45
|
+
# FIXME, separate variables for timeout?
|
46
|
+
@auth_pending = EM.add_timer(NATSD::Server.auth_timeout) { connect_auth_timeout } if Server.route_auth_required?
|
47
|
+
end
|
48
|
+
|
49
|
+
send_info
|
50
|
+
@ping_timer = EM.add_periodic_timer(NATSD::Server.ping_interval) { send_ping }
|
51
|
+
@pings_outstanding = 0
|
52
|
+
inc_connections
|
53
|
+
send_local_subs_to_route
|
54
|
+
end
|
55
|
+
|
56
|
+
# TODO: Make sure max_requested is also propogated on reconnect
|
57
|
+
def send_local_subs_to_route
|
58
|
+
ObjectSpace.each_object(NATSD::Connection) do |c|
|
59
|
+
next if c.closing? || c.type != 'Client'
|
60
|
+
c.subscriptions.each_value do |sub|
|
61
|
+
queue_data(NATSD::Server.route_sub_proto(sub))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def process_connect_route_config(config)
|
67
|
+
@verbose = config['verbose'] unless config['verbose'].nil?
|
68
|
+
@pedantic = config['pedantic'] unless config['pedantic'].nil?
|
69
|
+
|
70
|
+
return queue_data(OK) unless Server.route_auth_required?
|
71
|
+
|
72
|
+
EM.cancel_timer(@auth_pending)
|
73
|
+
if auth_ok?(config['user'], config['pass'])
|
74
|
+
debug "Route received proper credentials", rid
|
75
|
+
queue_data(OK) if @verbose
|
76
|
+
@auth_pending = nil
|
77
|
+
else
|
78
|
+
error_close AUTH_FAILED
|
79
|
+
debug "Authorization failed for #{type.downcase} connection", rid
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def connect_auth_timeout
|
84
|
+
error_close AUTH_REQUIRED
|
85
|
+
debug "#{type} connection timeout due to lack of auth credentials", rid
|
86
|
+
end
|
87
|
+
|
88
|
+
def receive_data(data)
|
89
|
+
@buf = @buf ? @buf << data : data
|
90
|
+
return close_connection if @buf =~ /(\006|\004)/ # ctrl+c or ctrl+d for telnet friendly
|
91
|
+
|
92
|
+
while (@buf && !@closing)
|
93
|
+
case @parse_state
|
94
|
+
when AWAITING_CONTROL_LINE
|
95
|
+
case @buf
|
96
|
+
when MSG
|
97
|
+
ctrace('MSG OP', strip_op($&)) if NATSD::Server.trace_flag?
|
98
|
+
return connect_auth_timeout if @auth_pending
|
99
|
+
@buf = $'
|
100
|
+
@parse_state = AWAITING_MSG_PAYLOAD
|
101
|
+
@msg_sub, @msg_sid, @msg_reply, @msg_size = $1, $2, $4, $5.to_i
|
102
|
+
if (@msg_size > NATSD::Server.max_payload)
|
103
|
+
debug_print_msg_too_big(@msg_size)
|
104
|
+
error_close PAYLOAD_TOO_BIG
|
105
|
+
end
|
106
|
+
queue_data(INVALID_SUBJECT) if (@pedantic && !(@msg_sub =~ SUB_NO_WC))
|
107
|
+
when SUB_OP
|
108
|
+
ctrace('SUB OP', strip_op($&)) if NATSD::Server.trace_flag?
|
109
|
+
return connect_auth_timeout if @auth_pending
|
110
|
+
@buf = $'
|
111
|
+
sub, qgroup, sid = $1, $3, $4
|
112
|
+
return queue_data(INVALID_SUBJECT) if !($1 =~ SUB)
|
113
|
+
return queue_data(INVALID_SID_TAKEN) if @subscriptions[sid]
|
114
|
+
sub = Subscriber.new(self, sub, sid, qgroup, 0)
|
115
|
+
@subscriptions[sid] = sub
|
116
|
+
Server.subscribe(sub, is_route?)
|
117
|
+
queue_data(OK) if @verbose
|
118
|
+
when UNSUB_OP
|
119
|
+
ctrace('UNSUB OP', strip_op($&)) if NATSD::Server.trace_flag?
|
120
|
+
return connect_auth_timeout if @auth_pending
|
121
|
+
@buf = $'
|
122
|
+
sid, sub = $1, @subscriptions[$1]
|
123
|
+
if sub
|
124
|
+
# If we have set max_responses, we will unsubscribe once we have received
|
125
|
+
# the appropriate amount of responses.
|
126
|
+
sub.max_responses = ($2 && $3) ? $3.to_i : nil
|
127
|
+
delete_subscriber(sub) unless (sub.max_responses && (sub.num_responses < sub.max_responses))
|
128
|
+
queue_data(OK) if @verbose
|
129
|
+
else
|
130
|
+
queue_data(INVALID_SID_NOEXIST) if @pedantic
|
131
|
+
end
|
132
|
+
when PING
|
133
|
+
ctrace('PING OP') if NATSD::Server.trace_flag?
|
134
|
+
@buf = $'
|
135
|
+
queue_data(PONG_RESPONSE)
|
136
|
+
flush_data
|
137
|
+
when PONG
|
138
|
+
ctrace('PONG OP') if NATSD::Server.trace_flag?
|
139
|
+
@buf = $'
|
140
|
+
@pings_outstanding -= 1
|
141
|
+
when CONNECT
|
142
|
+
ctrace('CONNECT OP', strip_op($&)) if NATSD::Server.trace_flag?
|
143
|
+
@buf = $'
|
144
|
+
begin
|
145
|
+
config = JSON.parse($1)
|
146
|
+
process_connect_route_config(config)
|
147
|
+
rescue => e
|
148
|
+
queue_data(INVALID_CONFIG)
|
149
|
+
log_error
|
150
|
+
end
|
151
|
+
when INFO_REQ
|
152
|
+
ctrace('INFO_REQUEST OP') if NATSD::Server.trace_flag?
|
153
|
+
return connect_auth_timeout if @auth_pending
|
154
|
+
@buf = $'
|
155
|
+
send_info
|
156
|
+
when INFO
|
157
|
+
ctrace('INFO OP', strip_op($&)) if NATSD::Server.trace_flag?
|
158
|
+
return connect_auth_timeout if @auth_pending
|
159
|
+
@buf = $'
|
160
|
+
process_info($1)
|
161
|
+
when ERR_RESP
|
162
|
+
ctrace('-ERR', $1) if NATSD::Server.trace_flag?
|
163
|
+
close_connection
|
164
|
+
exit
|
165
|
+
when OK_RESP
|
166
|
+
ctrace('+OK') if NATSD::Server.trace_flag?
|
167
|
+
@buf = $'
|
168
|
+
when UNKNOWN
|
169
|
+
ctrace('Unknown Op', strip_op($&)) if NATSD::Server.trace_flag?
|
170
|
+
return connect_auth_timeout if @auth_pending
|
171
|
+
@buf = $'
|
172
|
+
queue_data(UNKNOWN_OP)
|
173
|
+
else
|
174
|
+
# If we are here we do not have a complete line yet that we understand.
|
175
|
+
# If too big, cut the connection off.
|
176
|
+
if @buf.bytesize > NATSD::Server.max_control_line
|
177
|
+
debug_print_controlline_too_big(@buf.bytesize)
|
178
|
+
close_connection
|
179
|
+
end
|
180
|
+
return
|
181
|
+
end
|
182
|
+
@buf = nil if (@buf && @buf.empty?)
|
183
|
+
|
184
|
+
when AWAITING_MSG_PAYLOAD
|
185
|
+
return unless (@buf.bytesize >= (@msg_size + CR_LF_SIZE))
|
186
|
+
msg = @buf.slice(0, @msg_size)
|
187
|
+
|
188
|
+
ctrace('Processing routed msg', @msg_sub, @msg_reply, msg) if NATSD::Server.trace_flag?
|
189
|
+
queue_data(OK) if @verbose
|
190
|
+
|
191
|
+
# We deliver normal subscriptions like a client publish, which
|
192
|
+
# eliminates the duplicate traversal over the route. However,
|
193
|
+
# qgroups are sent individually per group for only the route
|
194
|
+
# with the intended subscriber, since route interest is L2
|
195
|
+
# semantics, we deliver those direct.
|
196
|
+
if (sub = Server.rsid_qsub(@msg_sid))
|
197
|
+
# Allows nil reply to not have extra space
|
198
|
+
reply = @msg_reply + ' ' if @msg_reply
|
199
|
+
Server.deliver_to_subscriber(sub, @msg_sub, reply, msg)
|
200
|
+
else
|
201
|
+
Server.route_to_subscribers(@msg_sub, @msg_reply, msg, is_route?)
|
202
|
+
end
|
203
|
+
|
204
|
+
@in_msgs += 1
|
205
|
+
@in_bytes += @msg_size
|
206
|
+
@buf = @buf.slice((@msg_size + CR_LF_SIZE), @buf.bytesize)
|
207
|
+
@msg_sub = @msg_size = @reply = nil
|
208
|
+
@parse_state = AWAITING_CONTROL_LINE
|
209
|
+
@buf = nil if (@buf && @buf.empty?)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def send_auth
|
215
|
+
return unless r_obj[:uri].user
|
216
|
+
cs = { :user => r_obj[:uri].user, :pass => r_obj[:uri].password }
|
217
|
+
queue_data("CONNECT #{cs.to_json}#{CR_LF}")
|
218
|
+
end
|
219
|
+
|
220
|
+
def send_info
|
221
|
+
queue_data("INFO #{Server.route_info_string}#{CR_LF}")
|
222
|
+
end
|
223
|
+
|
224
|
+
def process_info(info_json)
|
225
|
+
info = JSON.parse(info_json)
|
226
|
+
@remote_rid = info['server_id'] unless info['server_id'].nil?
|
227
|
+
super(info_json)
|
228
|
+
end
|
229
|
+
|
230
|
+
def auth_ok?(user, pass)
|
231
|
+
Server.route_auth_ok?(user, pass)
|
232
|
+
end
|
233
|
+
|
234
|
+
def inc_connections
|
235
|
+
Server.num_routes += 1
|
236
|
+
Server.add_route(self)
|
237
|
+
end
|
238
|
+
|
239
|
+
def dec_connections
|
240
|
+
Server.num_routes -= 1
|
241
|
+
Server.remove_route(self)
|
242
|
+
end
|
243
|
+
|
244
|
+
def try_reconnect
|
245
|
+
debug "Trying to reconnect route", peer_info, rid
|
246
|
+
EM.reconnect(r_obj[:uri].host, r_obj[:uri].port, self)
|
247
|
+
end
|
248
|
+
|
249
|
+
def cancel_reconnect
|
250
|
+
EM.cancel_timer(@reconnect_timer) if @reconnect_timer
|
251
|
+
@reconnect_timer = nil
|
252
|
+
@reconnecting = false
|
253
|
+
end
|
254
|
+
|
255
|
+
def unbind
|
256
|
+
return if reconnecting?
|
257
|
+
debug "Route connection closed", peer_info, rid
|
258
|
+
process_unbind
|
259
|
+
if solicited?
|
260
|
+
@reconnecting = true
|
261
|
+
@reconnect_timer = EM.add_periodic_timer(NATSD::DEFAULT_ROUTE_RECONNECT_INTERVAL) { try_reconnect }
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def ctrace(*args)
|
266
|
+
trace(args, "r: #{rid}")
|
267
|
+
end
|
268
|
+
|
269
|
+
def is_route?
|
270
|
+
true
|
271
|
+
end
|
272
|
+
|
273
|
+
def type
|
274
|
+
'Route'
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|