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.
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