arachni-rpc-em 0.1.3 → 0.2

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