Class: Arachni::RPC::EM::Server
- Inherits:
-
Object
- Object
- Arachni::RPC::EM::Server
- Includes:
- Arachni::RPC::Exceptions
- Defined in:
- lib/arachni/rpc/em/server.rb
Overview
EventMachine-based RPC server class.
It’s capable of:
-
performing and handling a few thousands requests per second (depending on call size, network conditions and the like)
-
TLS encryption
-
asynchronous and synchronous requests
-
handling asynchronous methods that require a block
@author: Tasos “Zapotek” Laskos <[email protected]>
Defined Under Namespace
Classes: Proxy
Instance Attribute Summary collapse
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
-
#opts ⇒ Object
readonly
Returns the value of attribute opts.
-
#token ⇒ Object
readonly
Returns the value of attribute token.
Instance Method Summary collapse
-
#add_async_check(&block) ⇒ Object
This is a way to identify methods that pass their result to a block instead of simply returning them (which is the most usual operation of async methods.
-
#add_handler(name, obj) ⇒ Object
Adds a handler by name:.
- #alive? ⇒ TrueClass
- #call(connection) ⇒ Object
-
#clear_handlers ⇒ Object
Clears all handlers and their associated information like methods and async check blocks.
-
#initialize(opts) ⇒ Server
constructor
Starts EventMachine and the RPC server.
-
#run ⇒ Object
Runs the server and blocks.
-
#shutdown ⇒ Object
Shuts down the server after 2 seconds.
-
#start ⇒ Object
Starts the server but does not block.
Constructor Details
#initialize(opts) ⇒ Server
Starts EventMachine and the RPC server.
opts example:
{
:host => 'localhost',
:port => 7331,
# optional authentication token, if it doesn't match the one
# set on the server-side you'll be getting exceptions.
:token => 'superdupersecret',
# optional serializer (defaults to YAML)
# see the 'serializer' method at:
# http://eventmachine.rubyforge.org/EventMachine/Protocols/ObjectProtocol.html#M000369
:serializer => Marshal,
# serializer to use if the first choice fails
:fallback_serializer => YAML,
#
# In order to enable peer verification one must first provide
# the following:
#
# SSL CA certificate
:ssl_ca => cwd + '/../spec/pems/cacert.pem',
# SSL private key
:ssl_pkey => cwd + '/../spec/pems/client/key.pem',
# SSL certificate
:ssl_cert => cwd + '/../spec/pems/client/cert.pem'
}
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/arachni/rpc/em/server.rb', line 197 def initialize( opts ) @opts = opts if @opts[:ssl_pkey] && @opts[:ssl_cert] if !File.exist?( @opts[:ssl_pkey] ) raise 'Could not find private key at: ' + @opts[:ssl_pkey] end if !File.exist?( @opts[:ssl_cert] ) raise 'Could not find certificate at: ' + @opts[:ssl_cert] end end @token = @opts[:token] @logger = ::Logger.new( STDOUT ) @logger.level = Logger::INFO @host, @port = @opts[:host], @opts[:port] clear_handlers end |
Instance Attribute Details
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
160 161 162 |
# File 'lib/arachni/rpc/em/server.rb', line 160 def logger @logger end |
#opts ⇒ Object (readonly)
Returns the value of attribute opts.
159 160 161 |
# File 'lib/arachni/rpc/em/server.rb', line 159 def opts @opts end |
#token ⇒ Object (readonly)
Returns the value of attribute token.
158 159 160 |
# File 'lib/arachni/rpc/em/server.rb', line 158 def token @token end |
Instance Method Details
#add_async_check(&block) ⇒ Object
This is a way to identify methods that pass their result to a block instead of simply returning them (which is the most usual operation of async methods.
So no need to change your coding conventions to fit the RPC stuff, you can just decide dynamically based on the plethora of data which Ruby provides by its ‘Method’ class.
server.add_async_check do |method|
#
# Must return 'true' for async and 'false' for sync.
#
# Very simple check here...
#
'async' == method.name.to_s.split( '_' )[0]
end
239 240 241 |
# File 'lib/arachni/rpc/em/server.rb', line 239 def add_async_check( &block ) @async_checks << block end |
#add_handler(name, obj) ⇒ Object
Adds a handler by name:
server.add_handler( 'myclass', MyClass.new )
251 252 253 254 255 256 257 258 259 260 |
# File 'lib/arachni/rpc/em/server.rb', line 251 def add_handler( name, obj ) @objects[name] = obj @methods[name] = Set.new # no lookup overhead please :) @async_methods[name] = Set.new obj.class.public_instance_methods( false ).each do |method| @methods[name] << method.to_s @async_methods[name] << method.to_s if async_check( obj.method( method ) ) end end |
#alive? ⇒ TrueClass
334 335 336 |
# File 'lib/arachni/rpc/em/server.rb', line 334 def alive? true end |
#call(connection) ⇒ Object
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/arachni/rpc/em/server.rb', line 292 def call( connection ) req = connection.request peer_ip_addr = connection.peer_ip_addr expr, args = req., req.args meth_name, obj_name = parse_expr( expr ) log_call( peer_ip_addr, expr, *args ) if !object_exist?( obj_name ) msg = "Trying to access non-existent object '#{obj_name}'." @logger.error( 'Call' ){ msg + " [on behalf of #{peer_ip_addr}]" } raise InvalidObject.new( msg ) end if !public_method?( obj_name, meth_name ) msg = "Trying to access non-public method '#{meth_name}'." @logger.error( 'Call' ){ msg + " [on behalf of #{peer_ip_addr}]" } raise InvalidMethod.new( msg ) end # the proxy needs to know whether this is an async call because if it # is we'll have already send the response. res = Response.new res.async! if async?( obj_name, meth_name ) if !res.async? res.obj = @objects[obj_name].send( meth_name.to_sym, *args ) else @objects[obj_name].send( meth_name.to_sym, *args ) do |obj| res.obj = obj connection.send_response( res ) end end res end |
#clear_handlers ⇒ Object
Clears all handlers and their associated information like methods and async check blocks.
266 267 268 269 270 271 272 |
# File 'lib/arachni/rpc/em/server.rb', line 266 def clear_handlers @objects = {} @methods = {} @async_checks = [] @async_methods = {} end |
#run ⇒ Object
Runs the server and blocks.
277 278 279 280 |
# File 'lib/arachni/rpc/em/server.rb', line 277 def run Arachni::RPC::EM.schedule { start } Arachni::RPC::EM.block end |
#shutdown ⇒ Object
Shuts down the server after 2 seconds
341 342 343 344 345 346 347 348 349 |
# File 'lib/arachni/rpc/em/server.rb', line 341 def shutdown wait_for = 2 @logger.info( 'System' ){ "Shutting down in #{wait_for} seconds..." } # don't die before returning ::EM.add_timer( wait_for ) { ::EM.stop } true end |
#start ⇒ Object
Starts the server but does not block.
285 286 287 288 289 290 |
# File 'lib/arachni/rpc/em/server.rb', line 285 def start @logger.info( 'System' ){ "RPC Server started." } @logger.info( 'System' ){ "Listening on #{@host}:#{@port}" } ::EM.start_server( @host, @port, Proxy, self ) end |