nats 0.4.28 → 0.5.0.beta.12

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.
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
- EventMachine::start_server(NATSD::Server.host, NATSD::Server.port, NATSD::Connection)
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 "Client connection created", client_info, cid
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
- Server.num_connections += 1
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 "Connection timeout due to lack of auth credentials", cid
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 "Connection timeout due to lack of TLS/SSL negotiations", cid
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', strip_op($&)) if NATSD::Server.trace_flag?
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', strip_op($&)) if NATSD::Server.trace_flag?
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 INFO
153
- ctrace('INFO OP', strip_op($&)) if NATSD::Server.trace_flag?
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 Server.auth_ok?(config['user'], config['pass'])
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 unbind
239
- debug "Client connection closed", client_info, cid
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
- @subscriptions.each_value { |sub| Server.unsubscribe(sub) }
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 "Client Certificate:", cert ? cert : 'N/A', cid
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
@@ -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
 
@@ -1,7 +1,7 @@
1
1
 
2
2
  module NATSD #:nodoc:
3
3
 
4
- VERSION = '0.4.28'
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*\r\n/i
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' }
@@ -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, Syslog::LOG_USER ) unless Syslog.opened?
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 = (not @options[:ssl].nil?)
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