arachni-rpc-em 0.1.3 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,180 @@
1
+ =begin
2
+
3
+ This file is part of the Arachni-RPC EM project and may be subject to
4
+ redistribution and commercial restrictions. Please see the Arachni-RPC EM
5
+ web site for more information on licensing and terms of use.
6
+
7
+ =end
8
+
9
+ module Arachni
10
+ module RPC::EM
11
+ class Client
12
+
13
+ #
14
+ # Transmits `Arachni::RPC::Request` objects and calls callbacks once an
15
+ # `Arachni::RPC::Response` is received.
16
+ #
17
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
18
+ #
19
+ class Handler < EventMachine::Connection
20
+ include Protocol
21
+ include ConnectionUtilities
22
+
23
+ # Default amount of tries for failed requests.
24
+ DEFAULT_TRIES = 9
25
+
26
+ # @return [Symbol] Status of the connection, can be:
27
+ #
28
+ # * `:idle` -- Just initialized.
29
+ # * `:ready` -- A connection has been established.
30
+ # * `:pending` -- Sending request and awaiting response.
31
+ # * `:done` -- Response received and callback invoked -- ready to be reused.
32
+ # * `:closed` -- Connection closed.
33
+ attr_reader :status
34
+
35
+ # Prepares an RPC connection and sets {#status} to `:idle`.
36
+ #
37
+ # @param [Hash] opts
38
+ # @option opts [Integer] :max_retries (9)
39
+ # Default amount of tries for failed requests.
40
+ #
41
+ # @option opts [Client] :base
42
+ # Client instance (needed to {Client#push_connection push} ourselves
43
+ # back to its connection pool once we're done and we're ready to be reused.)
44
+ def initialize( opts )
45
+ @opts = opts.dup
46
+
47
+ @max_retries = @opts[:max_retries] || DEFAULT_TRIES
48
+
49
+ @client = @opts[:client]
50
+
51
+ @opts[:tries] ||= 0
52
+ @tries ||= @opts[:tries]
53
+
54
+ @status = :idle
55
+
56
+ @request = nil
57
+ assume_client_role!
58
+ end
59
+
60
+ # Sends an RPC request (i.e. performs an RPC call) and sets {#status}
61
+ # to `:pending`.
62
+ #
63
+ # @param [Arachni::RPC::Request] req
64
+ def send_request( req )
65
+ @request = req
66
+ @status = :pending
67
+ super( req )
68
+ end
69
+
70
+ # @note Pushes itself to the client's connection pool to be re-used.
71
+ #
72
+ # Handles responses to RPC requests, calls its callback and sets {#status}
73
+ # to `:done`.
74
+ #
75
+ # @param [Arachni::RPC::Response] res
76
+ #
77
+ def receive_response( res )
78
+ if exception?( res )
79
+ res.obj = RPC::Exceptions.from_response( res )
80
+ end
81
+
82
+ @request.callback.call( res.obj ) if @request.callback
83
+ ensure
84
+ @request = nil # Help the GC out.
85
+ @status = :done
86
+ @opts[:tries] = @tries = 0
87
+ @client.push_connection self
88
+ end
89
+
90
+ # Initializes an SSL session once the connection has been established and
91
+ # sets {#status} # to `:ready`.
92
+ #
93
+ # @private
94
+ def post_init
95
+ @status = :ready
96
+ start_ssl
97
+ end
98
+
99
+ # Handles closed connections, cleans up the SSL session, retries (if
100
+ # necessary) and sets {#status} to `:closed`.
101
+ #
102
+ # @private
103
+ def unbind( reason )
104
+ end_ssl
105
+
106
+ # If there is a request and a callback and the callback hasn't yet be
107
+ # called (i.e. not done) then we got here by error so retry.
108
+ if @request && @request.callback && !done?
109
+ if retry? #&& reason == Errno::ECONNREFUSED
110
+ #p 'RETRY'
111
+ #p @client.connection_count
112
+ retry_request
113
+ else
114
+ #p 'FAIL'
115
+ #p @client.connection_count
116
+ e = RPC::Exceptions::ConnectionError.new( "Connection closed [#{reason}]" )
117
+ @request.callback.call( e )
118
+ @client.connection_failed self
119
+ end
120
+ return
121
+ end
122
+
123
+ close_without_retry
124
+ end
125
+
126
+ # @return [Boolean]
127
+ # `true` when the connection has been closed, `false` otherwise.
128
+ def closed?
129
+ @status == :closed
130
+ end
131
+
132
+ # @note If `true`, the connection can be re-used.
133
+ #
134
+ # @return [Boolean]
135
+ # `true` when the connection is done, `false` otherwise.
136
+ def done?
137
+ @status == :done
138
+ end
139
+
140
+ # Closes the connection without triggering a retry operation and sets
141
+ # {#status} to `:closed`.
142
+ def close_without_retry
143
+ @request = nil
144
+ @status = :closed
145
+ close_connection
146
+ end
147
+
148
+ private
149
+
150
+ def retry_request
151
+ opts = @opts.dup
152
+ opts[:tries] += 1
153
+
154
+ req = @request.dup
155
+
156
+ @tries += 1
157
+ ::EM.next_tick {
158
+ ::EM::Timer.new( 0.2 ) {
159
+ address = opts[:socket] ? opts[:socket] : [opts[:host], opts[:port]]
160
+ ::EM.connect( *[address, self.class, opts ].flatten ).send_request( req )
161
+ }
162
+ }
163
+
164
+ close_without_retry
165
+ end
166
+
167
+ def retry?
168
+ @tries < @max_retries
169
+ end
170
+
171
+ # @param [Arachni::RPC::Response] res
172
+ def exception?( res )
173
+ res.obj.is_a?( Hash ) && res.obj['exception'] ? true : false
174
+ end
175
+
176
+ end
177
+
178
+ end
179
+ end
180
+ end
@@ -13,13 +13,11 @@ module EM
13
13
  #
14
14
  # Helper methods to be included in EventMachine::Connection classes
15
15
  #
16
- # @author: Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
16
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
17
17
  #
18
18
  module ConnectionUtilities
19
19
 
20
- #
21
20
  # @return [String] IP address of the client
22
- #
23
21
  def peer_ip_addr
24
22
  begin
25
23
  if peername = get_peername
@@ -12,7 +12,7 @@ module RPC
12
12
  #
13
13
  # Provides some convenient methods for EventMachine's Reactor.
14
14
  #
15
- # @author: Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
15
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
16
16
  #
17
17
  module EM
18
18
 
@@ -24,27 +24,21 @@ module EM
24
24
  extend self
25
25
  end
26
26
 
27
+ # @note Will make sure EM is running first.
27
28
  #
28
- # Schedules a block to be run in the EM reactor.
29
- #
30
- # @param [Proc] &block
31
- #
29
+ # @param [Block] block Block to be run in the EM reactor.
32
30
  def schedule( &block )
33
31
  ensure_em_running
34
32
  ::EM.schedule( &block )
35
33
  end
36
34
 
37
- #
38
35
  # Blocks until the Reactor stops running
39
- #
40
36
  def block
41
37
  # beware of deadlocks, we can't join our own thread
42
38
  ::EM.reactor_thread.join if ::EM.reactor_thread && !::EM::reactor_thread?
43
39
  end
44
40
 
45
- #
46
41
  # Puts the Reactor in its own thread and runs it.
47
- #
48
42
  def ensure_em_running
49
43
  if !::EM::reactor_running?
50
44
 
@@ -11,64 +11,18 @@ module RPC
11
11
  module EM
12
12
 
13
13
  #
14
- # Provides helper transport methods for {Message} transmission.
14
+ # Provides helper transport methods for `Arachni::RPC::Message` transmission.
15
15
  #
16
- # @author: Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
16
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
17
17
  #
18
18
  module Protocol
19
19
  include ::Arachni::RPC::EM::SSL
20
20
 
21
- # send a maximum of 16kb of data per tick
21
+ # Send a maximum of 16kb of data per tick.
22
22
  MAX_CHUNK_SIZE = 1024 * 16
23
23
 
24
- # become a server
25
- def assume_server_role!
26
- @role = :server
27
- end
28
-
29
- # become a client
30
- def assume_client_role!
31
- @role = :client
32
- end
33
-
34
- #
35
- # Stub method, should be implemented by servers.
36
- #
37
- # @param [Arachni::RPC::EM::Request] request
38
- #
39
- def receive_request( request )
40
- p request
41
- end
42
-
43
- #
44
- # Stub method, should be implemented by clients.
45
- #
46
- # @param [Arachni::RPC::EM::Response] response
47
- #
48
- def receive_response( response )
49
- p response
50
- end
51
-
52
- #
53
- # Converts incoming hash objects to Requests or Response
54
- # (depending on the assumed role) and calls receive_request() or receive_response()
55
- # accordingly.
56
- #
57
- # @param [Hash] obj
58
- #
59
- def receive_object( obj )
60
- if @role == :server
61
- receive_request( Request.new( obj ) )
62
- else
63
- receive_response( Response.new( obj ) )
64
- end
65
- end
66
-
67
- #
68
- # Sends a message to the peer.
69
- #
70
- # @param [Arachni::RPC::EM::Message] msg
71
- #
24
+ # @param [Arachni::RPC::Message] msg
25
+ # Message to send to the peer.
72
26
  def send_message( msg )
73
27
  ::EM.schedule { send_object( msg.prepare_for_tx ) }
74
28
  end
@@ -78,18 +32,14 @@ module Protocol
78
32
  #
79
33
  # Receives data from the network.
80
34
  #
81
- # In this case the data will be chunks of a serialized object which
82
- # will be buffered until the whole transmission has finished.
35
+ # Rhe data will be chunks of a serialized object which will be buffered
36
+ # until the whole transmission has finished.
83
37
  #
84
- # It will then unresialize it and pass it to receive_object().
38
+ # It will then unserialize it and pass it to {#receive_object}.
85
39
  #
86
40
  def receive_data( data )
87
- #
88
- # cut them out as soon as possible
89
- #
90
- # don't buffer any data from unverified peers if SSL peer
91
- # verification has been enabled
92
- #
41
+ # Break out early if the request is sent by an unverified peer and SSL
42
+ # peer verification has been enabled.
93
43
  if ssl_opts? && !verified_peer? && @role == :server
94
44
  e = Arachni::RPC::Exceptions::SSLPeerVerificationFailed.new( 'Could not verify peer.' )
95
45
  send_response Response.new( :obj => {
@@ -114,11 +64,45 @@ module Protocol
114
64
  end
115
65
  end
116
66
 
67
+ private
68
+
69
+ # Stub method, should be implemented by servers.
70
+ #
71
+ # @param [Arachni::RPC::Request] request
72
+ # @abstract
73
+ def receive_request( request )
74
+ p request
75
+ end
76
+
77
+ #
78
+ # Stub method, should be implemented by clients.
117
79
  #
118
- # Sends a ruby object over the network
80
+ # @param [Arachni::RPC::Response] response
81
+ # @abstract
82
+ def receive_response( response )
83
+ p response
84
+ end
85
+
86
+ #
87
+ # Converts incoming hash objects to `Arachni::RPC::Request` and
88
+ # `Arachni::RPC::Response` objects (depending on the assumed role) and calls
89
+ # {#receive_request} or {#receive_response} accordingly.
90
+ #
91
+ # @param [Hash] obj
92
+ #
93
+ def receive_object( obj )
94
+ if @role == :server
95
+ receive_request( Request.new( obj ) )
96
+ else
97
+ receive_response( Response.new( obj ) )
98
+ end
99
+ end
100
+
119
101
  #
120
- # Will split the object in chunks of MAX_CHUNK_SIZE and transmit one at a time.
102
+ # @note Will split the object in chunks of MAX_CHUNK_SIZE and transmit one
103
+ # at a time.
121
104
  #
105
+ # @param [Object] obj Object to send.
122
106
  def send_object( obj )
123
107
  data = serialize( obj )
124
108
  packed = [data.bytesize, data].pack( 'Na*' )
@@ -133,15 +117,20 @@ module Protocol
133
117
  end
134
118
  end
135
119
 
120
+ # Become a server.
121
+ def assume_server_role!
122
+ @role = :server
123
+ end
124
+
125
+ # Become a client.
126
+ def assume_client_role!
127
+ @role = :client
128
+ end
129
+
130
+ # Returns the preferred serializer based on the `serializer` option of the
131
+ # server.
136
132
  #
137
- # Returns the preferred based on the 'serializer' option of the server.
138
- #
139
- # Defaults to <i>YAML</i>.
140
- #
141
- # @return [Class] serializer to be used
142
- #
143
- # @see http://eventmachine.rubyforge.org/EventMachine/Protocols/ObjectProtocol.html#M000369
144
- #
133
+ # @return [.load, .dump] Serializer to be used (Defaults to `YAML`).
145
134
  def serializer
146
135
  return @client_serializer if @client_serializer
147
136
 
@@ -10,159 +10,27 @@ module Arachni
10
10
  module RPC
11
11
  module EM
12
12
 
13
+ require_relative 'server/handler'
14
+
15
+ # EventMachine-based RPC server.
13
16
  #
14
- # EventMachine-based RPC server class.
15
- #
16
- # It's capable of:
17
- # - performing and handling a few thousands requests per second (depending on call size, network conditions and the like)
18
- # - TLS encryption
19
- # - asynchronous and synchronous requests
20
- # - handling asynchronous methods that require a block
21
- #
22
- # @author: Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
23
- #
17
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
24
18
  class Server
25
19
  include ::Arachni::RPC::Exceptions
26
20
 
27
- #
28
- # Handles EventMachine's connection stuff.
29
- #
30
- # It's responsible for TLS, serializing, transmitting and receiving objects,
31
- # as well as authenticating the client using the token.
32
- #
33
- # It also handles and forwards exceptions.
34
- #
35
- # @author: Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
36
- #
37
- class Proxy < EventMachine::Connection
38
- include ::Arachni::RPC::EM::Protocol
39
- include ::Arachni::RPC::Exceptions
40
- include ::Arachni::RPC::EM::ConnectionUtilities
41
-
42
- INACTIVITY_TIMEOUT = 10
43
-
44
- attr_reader :request
45
-
46
- def initialize( server )
47
- super
48
- @server = server
49
- @opts = server.opts
50
-
51
- assume_server_role!
52
-
53
- @id = nil
54
- @request = nil
55
-
56
- # do not tolerate long periods of
57
- # inactivity in order to avoid zombie connections
58
- set_comm_inactivity_timeout( INACTIVITY_TIMEOUT )
59
- end
60
-
61
- # starts TLS
62
- def post_init
63
- start_ssl
64
- end
65
-
66
- def unbind
67
- end_ssl
68
- end
69
-
70
- def log( severity, progname, msg )
71
- sev_sym = Logger.const_get( severity.to_s.upcase.to_sym )
72
- @server.logger.add( sev_sym, msg, progname )
73
- end
74
-
75
- #
76
- # Handles requests and sends back the responses.
77
- #
78
- # @param [Arachni::RPC::EM::Request] req
79
- #
80
- def receive_request( req )
81
- @request = req
82
-
83
- # the method call may block a little so tell EventMachine to
84
- # stick it in its own thread.
85
- res = Response.new
86
- peer = peer_ip_addr
87
-
88
- begin
89
- # token-based authentication
90
- authenticate!
91
-
92
- # grab the result of the method call
93
- res.merge!( @server.call( self ) )
94
-
95
- # handle exceptions and convert them to a simple hash,
96
- # ready to be passed to the client.
97
- rescue Exception => e
98
-
99
- type = ''
100
-
101
- # if it's an RPC exception pass the type along as is
102
- if e.rpc_exception?
103
- type = e.class.name.split( ':' )[-1]
104
- # otherwise set it to a RemoteExeption
105
- else
106
- type = 'RemoteException'
107
- end
108
-
109
- res.obj = {
110
- 'exception' => e.to_s,
111
- 'backtrace' => e.backtrace,
112
- 'type' => type
113
- }
114
-
115
- msg = "#{e.to_s}\n#{e.backtrace.join( "\n" )}"
116
- @server.logger.error( 'Exception' ){ msg + " [on behalf of #{peer}]" }
117
- end
118
-
119
- #
120
- # pass the result of the RPC call back to the client
121
- # along with the callback ID but *only* if it wan't async
122
- # because server.call() will have already taken care of it
123
- #
124
- send_response( res ) if !res.async?
125
- end
126
-
127
- #
128
- # Authenticates the client based on the token in the request.
129
- #
130
- # It will raise an exception if the token doesn't check-out.
131
- #
132
- def authenticate!
133
- if !valid_token?( @request.token )
134
-
135
- msg = 'Token missing or invalid while calling: ' + @request.message
136
-
137
- @server.logger.error( 'Authenticator' ){
138
- msg + " [on behalf of #{peer_ip_addr}]"
139
- }
140
-
141
- fail InvalidToken.new( msg )
142
- end
143
- end
144
-
145
- #
146
- # Compares the authentication token in the param with the one of the server.
147
- #
148
- # @param [String] token
149
- #
150
- # @return [Bool]
151
- #
152
- def valid_token?( token )
153
- token == @server.token
154
- end
155
-
156
- end
157
-
21
+ # @return [String] Authentication token.
158
22
  attr_reader :token
23
+
24
+ # @return [Hash] Configuration options.
159
25
  attr_reader :opts
26
+
27
+ # @return [Logger]
160
28
  attr_reader :logger
161
29
 
162
30
  #
163
31
  # Starts EventMachine and the RPC server.
164
32
  #
165
- # opts example:
33
+ # @example Example options:
166
34
  #
167
35
  # {
168
36
  # :host => 'localhost',
@@ -173,8 +41,6 @@ class Server
173
41
  # :token => 'superdupersecret',
174
42
  #
175
43
  # # optional serializer (defaults to YAML)
176
- # # see the 'serializer' method at:
177
- # # http://eventmachine.rubyforge.org/EventMachine/Protocols/ObjectProtocol.html#M000369
178
44
  # :serializer => Marshal,
179
45
  #
180
46
  # # serializer to use if the first choice fails
@@ -193,17 +59,30 @@ class Server
193
59
  # }
194
60
  #
195
61
  # @param [Hash] opts
62
+ # @option opts [String] :host Hostname/IP address.
63
+ # @option opts [Integer] :port Port number.
64
+ # @option opts [String] :socket Path to UNIX domain socket.
65
+ # @option opts [String] :token Optional authentication token.
66
+ # @option opts [.dump, .load] :serializer (YAML)
67
+ # Serializer to use for message transmission.
68
+ # @option opts [.dump, .load] :fallback_serializer
69
+ # Optional fallback serializer to be used when the primary one fails.
70
+ # @option opts [Integer] :max_retries
71
+ # How many times to retry failed requests.
72
+ # @option opts [String] :ssl_ca SSL CA certificate.
73
+ # @option opts [String] :ssl_pkey SSL private key.
74
+ # @option opts [String] :ssl_cert SSL certificate.
196
75
  #
197
76
  def initialize( opts )
198
- @opts = opts
77
+ @opts = opts
199
78
 
200
79
  if @opts[:ssl_pkey] && @opts[:ssl_cert]
201
80
  if !File.exist?( @opts[:ssl_pkey] )
202
- raise 'Could not find private key at: ' + @opts[:ssl_pkey]
81
+ raise "Could not find private key at: #{@opts[:ssl_pkey]}"
203
82
  end
204
83
 
205
84
  if !File.exist?( @opts[:ssl_cert] )
206
- raise 'Could not find certificate at: ' + @opts[:ssl_cert]
85
+ raise "Could not find certificate at: #{@opts[:ssl_cert]}"
207
86
  end
208
87
  end
209
88
 
@@ -213,17 +92,19 @@ class Server
213
92
  @logger.level = Logger::INFO
214
93
 
215
94
  @host, @port = @opts[:host], @opts[:port]
95
+ @socket = @opts[:socket]
96
+
97
+ if !@socket && !(@host || @port)
98
+ fail ArgumentError, 'Needs either a :socket or :host and :port options.'
99
+ end
100
+
101
+ @port = @port.to_i
216
102
 
217
103
  clear_handlers
218
104
  end
219
105
 
220
106
  #
221
- # This is a way to identify methods that pass their result to a block
222
- # instead of simply returning them (which is the most usual operation of async methods.
223
- #
224
- # So no need to change your coding conventions to fit the RPC stuff,
225
- # you can just decide dynamically based on the plethora of data which Ruby provides
226
- # by its 'Method' class.
107
+ # @example
227
108
  #
228
109
  # server.add_async_check do |method|
229
110
  # #
@@ -234,35 +115,39 @@ class Server
234
115
  # 'async' == method.name.to_s.split( '_' )[0]
235
116
  # end
236
117
  #
237
- # @param [Proc] &block
118
+ # @param [Block] block
119
+ # Block to identify methods that pass their result to a block instead of
120
+ # simply returning them (which is the most usual operation of async methods).
238
121
  #
239
122
  def add_async_check( &block )
240
123
  @async_checks << block
241
124
  end
242
125
 
243
126
  #
244
- # Adds a handler by name:
127
+ # @example
245
128
  #
246
129
  # server.add_handler( 'myclass', MyClass.new )
247
130
  #
248
- # @param [String] name name via which to make the object available over RPC
249
- # @param [Object] obj object instance
131
+ # @param [String] name
132
+ # Name by which to make the object available over RPC.
133
+ # @param [Object] obj Instantiated server object to expose.
250
134
  #
251
135
  def add_handler( name, obj )
252
- @objects[name] = obj
253
- @methods[name] = Set.new # no lookup overhead please :)
136
+ @objects[name] = obj
137
+ @methods[name] = Set.new
254
138
  @async_methods[name] = Set.new
255
139
 
256
140
  obj.class.public_instance_methods( false ).each do |method|
257
- @methods[name] << method.to_s
141
+ @methods[name] << method.to_s
258
142
  @async_methods[name] << method.to_s if async_check( obj.method( method ) )
259
143
  end
260
144
  end
261
145
 
146
+ # Clears all handlers and their associated information like methods and
147
+ # async check blocks.
262
148
  #
263
- # Clears all handlers and their associated information like methods
264
- # and async check blocks.
265
- #
149
+ # @see #add_handler
150
+ # @see #add_async_check
266
151
  def clear_handlers
267
152
  @objects = {}
268
153
  @methods = {}
@@ -271,27 +156,33 @@ class Server
271
156
  @async_methods = {}
272
157
  end
273
158
 
274
- #
275
- # Runs the server and blocks.
276
- #
159
+ # Runs the server and blocks while EM ir running.
277
160
  def run
278
161
  Arachni::RPC::EM.schedule { start }
279
162
  Arachni::RPC::EM.block
280
163
  end
281
164
 
282
- #
283
165
  # Starts the server but does not block.
284
- #
285
166
  def start
286
- @logger.info( 'System' ){ "RPC Server started." }
287
- @logger.info( 'System' ){ "Listening on #{@host}:#{@port}" }
167
+ @logger.info( 'System' ){ 'RPC Server started.' }
168
+ @logger.info( 'System' ) do
169
+ interface = @socket ? @socket : "#{@host}:#{@port}"
170
+ "Listening on #{interface}"
171
+ end
288
172
 
289
- ::EM.start_server( @host, @port, Proxy, self )
173
+ opts = @socket ? @socket : [@host, @port]
174
+ ::EM.start_server( *[opts, Handler, self].flatten )
290
175
  end
291
176
 
177
+ # @note If the called method is asynchronous it will be sent by this method
178
+ # directly, otherwise it will be handled by the {Handler}.
179
+ #
180
+ # @param [Handler] connection
181
+ # Connection with request information.
182
+ #
183
+ # @return [Arachni::RPC::Response]
292
184
  def call( connection )
293
-
294
- req = connection.request
185
+ req = connection.request
295
186
  peer_ip_addr = connection.peer_ip_addr
296
187
 
297
188
  expr, args = req.message, req.args
@@ -311,40 +202,40 @@ class Server
311
202
  raise InvalidMethod.new( msg )
312
203
  end
313
204
 
314
- # the proxy needs to know whether this is an async call because if it
315
- # is we'll have already send the response.
205
+ # The handler needs to know if this is an async call because if it is
206
+ # we'll have already send the response and it doesn't need to do
207
+ # transmit anything.
316
208
  res = Response.new
317
209
  res.async! if async?( obj_name, meth_name )
318
210
 
319
- if !res.async?
320
- res.obj = @objects[obj_name].send( meth_name.to_sym, *args )
321
- else
211
+ if res.async?
322
212
  @objects[obj_name].send( meth_name.to_sym, *args ) do |obj|
323
213
  res.obj = obj
324
214
  connection.send_response( res )
325
215
  end
216
+ else
217
+ res.obj = @objects[obj_name].send( meth_name.to_sym, *args )
326
218
  end
327
219
 
328
220
  res
329
221
  end
330
222
 
331
- #
332
223
  # @return [TrueClass]
333
- #
334
224
  def alive?
335
225
  true
336
226
  end
337
227
 
338
- #
339
228
  # Shuts down the server after 2 seconds
340
- #
341
229
  def shutdown
342
230
  wait_for = 2
343
231
 
344
232
  @logger.info( 'System' ){ "Shutting down in #{wait_for} seconds..." }
345
233
 
346
- # don't die before returning
347
- ::EM.add_timer( wait_for ) { ::EM.stop }
234
+ # Don't die before returning...
235
+ ::EM.add_timer( wait_for ) do
236
+ File.unlink( @socket ) if @socket
237
+ ::EM.stop
238
+ end
348
239
  true
349
240
  end
350
241
 
@@ -359,7 +250,6 @@ class Server
359
250
  false
360
251
  end
361
252
 
362
-
363
253
  def log_call( peer_ip_addr, expr, *args )
364
254
  msg = "#{expr}"
365
255