toq 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE.md +29 -0
- data/README.md +68 -0
- data/Rakefile +53 -0
- data/lib/toq/client/handler.rb +165 -0
- data/lib/toq/client.rb +249 -0
- data/lib/toq/exceptions.rb +152 -0
- data/lib/toq/message.rb +63 -0
- data/lib/toq/protocol.rb +101 -0
- data/lib/toq/proxy.rb +84 -0
- data/lib/toq/request.rb +59 -0
- data/lib/toq/response.rb +63 -0
- data/lib/toq/server/handler.rb +144 -0
- data/lib/toq/server.rb +276 -0
- data/lib/toq/version.rb +13 -0
- data/lib/toq.rb +13 -0
- data/spec/pems/cacert.pem +37 -0
- data/spec/pems/client/cert.pem +37 -0
- data/spec/pems/client/foo-cert.pem +39 -0
- data/spec/pems/client/foo-key.pem +51 -0
- data/spec/pems/client/key.pem +51 -0
- data/spec/pems/server/cert.pem +37 -0
- data/spec/pems/server/key.pem +51 -0
- data/spec/servers/basic.rb +3 -0
- data/spec/servers/server.rb +83 -0
- data/spec/servers/unix_socket.rb +8 -0
- data/spec/servers/with_ssl_primitives.rb +11 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/toq/client_spec.rb +397 -0
- data/spec/toq/exceptions_spec.rb +77 -0
- data/spec/toq/message_spec.rb +47 -0
- data/spec/toq/proxy_spec.rb +99 -0
- data/spec/toq/request_spec.rb +53 -0
- data/spec/toq/response_spec.rb +49 -0
- data/spec/toq/server_spec.rb +129 -0
- metadata +116 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the Arachni-RPC project and may be subject to
|
4
|
+
redistribution and commercial restrictions. Please see the Arachni-RPC
|
5
|
+
web site for more information on licensing and terms of use.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
# RPC Exceptions have methods that help identify them based on type.
|
10
|
+
#
|
11
|
+
# So in order to allow evaluations like:
|
12
|
+
#
|
13
|
+
# my_object.rpc_connection_error?
|
14
|
+
#
|
15
|
+
# to be possible on all objects these helper methods need to be available for
|
16
|
+
# all objects.
|
17
|
+
#
|
18
|
+
# By default they'll return false, individual RPC Exceptions will overwrite them
|
19
|
+
# to return true when applicable.
|
20
|
+
#
|
21
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
22
|
+
class Object
|
23
|
+
|
24
|
+
# @return [Bool] false
|
25
|
+
def rpc_connection_error?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Bool] false
|
30
|
+
def rpc_remote_exception?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Bool] false
|
35
|
+
def rpc_invalid_object_error?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Bool] false
|
40
|
+
def rpc_invalid_method_error?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Bool] false
|
45
|
+
def rpc_invalid_token_error?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Bool] true
|
50
|
+
def rpc_ssl_error?
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Bool] false
|
55
|
+
def rpc_exception?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
module Toq
|
62
|
+
module Exceptions
|
63
|
+
|
64
|
+
# Returns an exception based on the response object.
|
65
|
+
#
|
66
|
+
# @param [Toq::Response] response
|
67
|
+
#
|
68
|
+
# @return [Exception]
|
69
|
+
def self.from_response( response )
|
70
|
+
exception = response.exception
|
71
|
+
klass = Toq::Exceptions.const_get( exception['type'].to_sym )
|
72
|
+
e = klass.new( exception['message'] )
|
73
|
+
e.set_backtrace( exception['backtrace'] )
|
74
|
+
e
|
75
|
+
end
|
76
|
+
|
77
|
+
class Base < ::RuntimeError
|
78
|
+
|
79
|
+
# @return [Bool] true
|
80
|
+
def rpc_exception?
|
81
|
+
true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Signifies an abruptly terminated connection.
|
86
|
+
#
|
87
|
+
# Look for network or SSL errors or a dead server or a mistyped server address/port.
|
88
|
+
class ConnectionError < Base
|
89
|
+
|
90
|
+
# @return [Bool] true
|
91
|
+
def rpc_connection_error?
|
92
|
+
true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Signifies an exception that occurred on the server-side.
|
97
|
+
#
|
98
|
+
# Look errors on the remote method and review the server output for more details.
|
99
|
+
class RemoteException < Base
|
100
|
+
|
101
|
+
# @return [Bool] true
|
102
|
+
def rpc_remote_exception?
|
103
|
+
true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# An invalid object has been called.
|
108
|
+
#
|
109
|
+
# Make sure that there is a server-side handler for the object you called.
|
110
|
+
class InvalidObject < Base
|
111
|
+
|
112
|
+
# @return [Bool] true
|
113
|
+
def rpc_invalid_object_error?
|
114
|
+
true
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
# An invalid method has been called.
|
120
|
+
#
|
121
|
+
# Occurs when a remote method doesn't exist or isn't public.
|
122
|
+
class InvalidMethod < Base
|
123
|
+
|
124
|
+
# @return [Bool] true
|
125
|
+
def rpc_invalid_method_error?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# Signifies an authentication token mismatch between the client and the server.
|
132
|
+
class InvalidToken < Base
|
133
|
+
|
134
|
+
# @return [Bool] true
|
135
|
+
def rpc_invalid_token_error?
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
# Signifies an authentication token mismatch between the client and the server.
|
142
|
+
class SSLPeerVerificationFailed < ConnectionError
|
143
|
+
|
144
|
+
# @return [Bool] true
|
145
|
+
def rpc_ssl_error?
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
data/lib/toq/message.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the Arachni-RPC project and may be subject to
|
4
|
+
redistribution and commercial restrictions. Please see the Arachni-RPC
|
5
|
+
web site for more information on licensing and terms of use.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Toq
|
10
|
+
|
11
|
+
# Represents an RPC message, serves as the basis for {Request} and {Response}.
|
12
|
+
#
|
13
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
14
|
+
class Message
|
15
|
+
|
16
|
+
# @param [Hash] opts
|
17
|
+
# Sets instance attributes.
|
18
|
+
def initialize( opts = {} )
|
19
|
+
opts.each_pair { |k, v| send( "#{k}=".to_sym, v ) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Merges the attributes of another message with self.
|
23
|
+
#
|
24
|
+
# (The param doesn't *really* have to be a message, any object will do.)
|
25
|
+
#
|
26
|
+
# @param [Message] message
|
27
|
+
def merge!( message )
|
28
|
+
message.instance_variables.each do |var|
|
29
|
+
val = message.instance_variable_get( var )
|
30
|
+
instance_variable_set( var, val )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Prepares the message for transmission (i.e. converts the message to a `Hash`).
|
35
|
+
#
|
36
|
+
# Attributes that should not be included can be skipped by implementing
|
37
|
+
# {#transmit?} and returning the appropriate value.
|
38
|
+
#
|
39
|
+
# @return [Hash]
|
40
|
+
def prepare_for_tx
|
41
|
+
instance_variables.inject({}) do |h, k|
|
42
|
+
h[normalize( k )] = instance_variable_get( k ) if transmit?( k )
|
43
|
+
h
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Decides which attributes should be skipped by {#prepare_for_tx}.
|
48
|
+
#
|
49
|
+
# @param [Symbol] attr
|
50
|
+
# Instance variable symbol (i.e. `:@token`).
|
51
|
+
def transmit?( attr )
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def normalize( attr )
|
58
|
+
attr.to_s.gsub( '@', '' )
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/lib/toq/protocol.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the Arachni-RPC 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 Toq
|
10
|
+
|
11
|
+
# Provides helper transport methods for {Message} transmission.
|
12
|
+
#
|
13
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
14
|
+
module Protocol
|
15
|
+
include Raktr::Connection::TLS
|
16
|
+
|
17
|
+
# Initializes an SSL session once the connection has been established and
|
18
|
+
# sets {#status} to `:ready`.
|
19
|
+
#
|
20
|
+
# @private
|
21
|
+
def on_connect
|
22
|
+
start_tls(
|
23
|
+
ca: @opts[:ssl_ca],
|
24
|
+
private_key: @opts[:ssl_pkey],
|
25
|
+
certificate: @opts[:ssl_cert]
|
26
|
+
)
|
27
|
+
|
28
|
+
@status = :ready
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [Message] msg
|
32
|
+
# Message to send to the peer.
|
33
|
+
def send_message( msg )
|
34
|
+
send_object( msg.prepare_for_tx )
|
35
|
+
end
|
36
|
+
alias :send_request :send_message
|
37
|
+
alias :send_response :send_message
|
38
|
+
|
39
|
+
# Receives data from the network.
|
40
|
+
#
|
41
|
+
# Rhe data will be chunks of a serialized object which will be buffered
|
42
|
+
# until the whole transmission has finished.
|
43
|
+
#
|
44
|
+
# It will then unserialize it and pass it to {#receive_object}.
|
45
|
+
def on_read( data )
|
46
|
+
(@buf ||= '') << data
|
47
|
+
|
48
|
+
while @buf.size >= 4
|
49
|
+
if @buf.size >= 4 + ( size = @buf.unpack( 'N' ).first )
|
50
|
+
@buf.slice!( 0, 4 )
|
51
|
+
receive_object( unserialize( @buf.slice!( 0, size ) ) )
|
52
|
+
else
|
53
|
+
break
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Stub method, should be implemented by servers.
|
61
|
+
#
|
62
|
+
# @param [Request] request
|
63
|
+
# @abstract
|
64
|
+
def receive_request( request )
|
65
|
+
p request
|
66
|
+
end
|
67
|
+
|
68
|
+
# Stub method, should be implemented by clients.
|
69
|
+
#
|
70
|
+
# @param [Response] response
|
71
|
+
# @abstract
|
72
|
+
def receive_response( response )
|
73
|
+
p response
|
74
|
+
end
|
75
|
+
|
76
|
+
# Object to send.
|
77
|
+
def send_object( obj )
|
78
|
+
data = serialize( obj )
|
79
|
+
write [data.bytesize, data].pack( 'Na*' )
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the preferred serializer based on the `serializer` option of the
|
83
|
+
# server.
|
84
|
+
#
|
85
|
+
# @return [.load, .dump]
|
86
|
+
# Serializer to be used (Defaults to `YAML`).
|
87
|
+
def serializer
|
88
|
+
@opts[:serializer] || YAML
|
89
|
+
end
|
90
|
+
|
91
|
+
def serialize( obj )
|
92
|
+
serializer.dump obj
|
93
|
+
end
|
94
|
+
|
95
|
+
def unserialize( obj )
|
96
|
+
serializer.load( obj )
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/lib/toq/proxy.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the Arachni-RPC project and may be subject to
|
4
|
+
redistribution and commercial restrictions. Please see the Arachni-RPC
|
5
|
+
web site for more information on licensing and terms of use.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Toq
|
10
|
+
|
11
|
+
# Maps the methods of remote objects to local ones.
|
12
|
+
#
|
13
|
+
# You start like:
|
14
|
+
#
|
15
|
+
# client = Toq::Client.new( host: 'localhost', port: 7331 )
|
16
|
+
# bench = Toq::Proxy.new( client, 'bench' )
|
17
|
+
#
|
18
|
+
# And it allows you to do this:
|
19
|
+
#
|
20
|
+
# result = bench.foo( 1, 2, 3 )
|
21
|
+
#
|
22
|
+
# Instead of:
|
23
|
+
#
|
24
|
+
# result = client.call( 'bench.foo', 1, 2, 3 )
|
25
|
+
#
|
26
|
+
# The server on the other end must have an appropriate handler set, like:
|
27
|
+
#
|
28
|
+
# class Bench
|
29
|
+
# def foo( i = 0 )
|
30
|
+
# return i
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# server = Toq::Server.new( host: 'localhost', port: 7331 )
|
35
|
+
# server.add_handler( 'bench', Bench.new )
|
36
|
+
#
|
37
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
38
|
+
class Proxy
|
39
|
+
|
40
|
+
class <<self
|
41
|
+
|
42
|
+
# @param [Symbol] method_name
|
43
|
+
# Method whose response to translate.
|
44
|
+
# @param [Block] translator
|
45
|
+
# Block to be passed the response and return a translated object.
|
46
|
+
def translate( method_name, &translator )
|
47
|
+
define_method method_name do |*args, &b|
|
48
|
+
# For blocking calls.
|
49
|
+
if !b
|
50
|
+
data = forward( method_name, *args )
|
51
|
+
return data.rpc_exception? ?
|
52
|
+
data : translator.call( data, *args )
|
53
|
+
end
|
54
|
+
|
55
|
+
# For non-blocking calls.
|
56
|
+
forward( method_name, *args ) do |data|
|
57
|
+
b.call( data.rpc_exception? ?
|
58
|
+
data : translator.call( data, *args ) )
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param [Client] client
|
65
|
+
# @param [String] handler
|
66
|
+
def initialize( client, handler )
|
67
|
+
@client = client
|
68
|
+
@handler = handler
|
69
|
+
end
|
70
|
+
|
71
|
+
def forward( sym, *args, &block )
|
72
|
+
@client.call( "#{@handler}.#{sym.to_s}", *args, &block )
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Used to provide the illusion of locality for remote methods.
|
78
|
+
def method_missing( *args, &block )
|
79
|
+
forward( *args, &block )
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/lib/toq/request.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the Arachni-RPC project and may be subject to
|
4
|
+
redistribution and commercial restrictions. Please see the Arachni-RPC
|
5
|
+
web site for more information on licensing and terms of use.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
require_relative 'message'
|
10
|
+
|
11
|
+
module Toq
|
12
|
+
|
13
|
+
# Represents an RPC request.
|
14
|
+
#
|
15
|
+
# It's here only for formalization purposes, it's not actually sent over the wire.
|
16
|
+
#
|
17
|
+
# What is sent is a hash generated by {#prepare_for_tx}. which is in the form of:
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# {
|
21
|
+
# # RPC message in the form of 'handler.method'.
|
22
|
+
# 'message' => msg,
|
23
|
+
# # Optional array of arguments for the remote method.
|
24
|
+
# 'args' => args,
|
25
|
+
# # Optional authentication token.
|
26
|
+
# 'token' => token
|
27
|
+
# }
|
28
|
+
#
|
29
|
+
# Any client that has SSL support and can serialize a Hash just like the one
|
30
|
+
# above can communicate with the RPC server.
|
31
|
+
#
|
32
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
33
|
+
class Request < Message
|
34
|
+
|
35
|
+
# @return [String]
|
36
|
+
# RPC message in the form of 'handler.method'.
|
37
|
+
attr_accessor :message
|
38
|
+
|
39
|
+
# @return [Array]
|
40
|
+
# Optional arguments for the remote method.
|
41
|
+
attr_accessor :args
|
42
|
+
|
43
|
+
# @return [String]
|
44
|
+
# Optional authentication token.
|
45
|
+
attr_accessor :token
|
46
|
+
|
47
|
+
# @return [Proc]
|
48
|
+
# Callback to be invoked on the response.
|
49
|
+
attr_accessor :callback
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def transmit?( attr )
|
54
|
+
![ :@callback ].include?( attr )
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/lib/toq/response.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This file is part of the Arachni-RPC project and may be subject to
|
4
|
+
redistribution and commercial restrictions. Please see the Arachni-RPC
|
5
|
+
web site for more information on licensing and terms of use.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Toq
|
10
|
+
|
11
|
+
# Represents an RPC response.
|
12
|
+
#
|
13
|
+
# It's here only for formalization purposes, it's not actually sent over the wire.
|
14
|
+
#
|
15
|
+
# What is sent is a hash generated by {#prepare_for_tx} which is in the form of:
|
16
|
+
#
|
17
|
+
# {
|
18
|
+
# # result of the RPC call
|
19
|
+
# 'obj' => object
|
20
|
+
# }
|
21
|
+
#
|
22
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
23
|
+
class Response < Message
|
24
|
+
|
25
|
+
# @return [Object]
|
26
|
+
# Return object of the {Request#message}.
|
27
|
+
attr_accessor :obj
|
28
|
+
|
29
|
+
# @return [Hash]
|
30
|
+
#
|
31
|
+
# {
|
32
|
+
# "name" => "Trying to access non-existent object 'blah'.",
|
33
|
+
# "backtrace" => [
|
34
|
+
# [0] "/home/zapotek/workspace/arachni-rpc/lib/toq/server.rb:285:in `call'",
|
35
|
+
# [1] "/home/zapotek/workspace/arachni-rpc/lib/toq/server.rb:85:in `block in receive_object'",
|
36
|
+
# ],
|
37
|
+
# "type" => "InvalidObject"
|
38
|
+
# }
|
39
|
+
#
|
40
|
+
# For all available exception types look at {Exceptions}.
|
41
|
+
attr_accessor :exception
|
42
|
+
|
43
|
+
def exception?
|
44
|
+
!!exception
|
45
|
+
end
|
46
|
+
|
47
|
+
def async?
|
48
|
+
!!@async
|
49
|
+
end
|
50
|
+
|
51
|
+
def async!
|
52
|
+
@async = true
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def transmit?( attr )
|
58
|
+
![:@async].include?( attr )
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,144 @@
|
|
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 Toq
|
10
|
+
class Server
|
11
|
+
|
12
|
+
# Receives {Request} objects and transmits {Response} objects.
|
13
|
+
#
|
14
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
15
|
+
class Handler < Raktr::Connection
|
16
|
+
include Raktr::Connection::PeerInfo
|
17
|
+
include Toq::Protocol
|
18
|
+
|
19
|
+
# @return [Request]
|
20
|
+
# Working RPC request.
|
21
|
+
attr_reader :request
|
22
|
+
|
23
|
+
# @param [Server] server
|
24
|
+
# RPC server.
|
25
|
+
def initialize( server )
|
26
|
+
@server = server
|
27
|
+
@opts = server.opts.dup
|
28
|
+
@request = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# Handles closed connections and cleans up the SSL session.
|
32
|
+
#
|
33
|
+
# @private
|
34
|
+
def on_close( _ )
|
35
|
+
@server = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# * Handles {Request}
|
39
|
+
# * Sets the {#request}.
|
40
|
+
# * Sends back {Response}.
|
41
|
+
#
|
42
|
+
# @param [Request] req
|
43
|
+
def receive_request( req )
|
44
|
+
@request = req
|
45
|
+
|
46
|
+
# Create an empty response to be filled in little by little.
|
47
|
+
res = Response.new
|
48
|
+
peer = peer_ip_address
|
49
|
+
|
50
|
+
begin
|
51
|
+
# Make sure the client is allowed to make RPC calls.
|
52
|
+
authenticate!
|
53
|
+
|
54
|
+
# Grab the partially filled in response which includes the result
|
55
|
+
# of the RPC call and merge it with out prepared response.
|
56
|
+
res.merge!( @server.call( self ) )
|
57
|
+
|
58
|
+
# Handle exceptions and convert them to a simple hash, ready to be
|
59
|
+
# passed to the client.
|
60
|
+
rescue Exception => e
|
61
|
+
type = ''
|
62
|
+
|
63
|
+
# If it's an RPC exception pass the type along as is...
|
64
|
+
if e.rpc_exception?
|
65
|
+
type = e.class.name.split( ':' )[-1]
|
66
|
+
|
67
|
+
# ...otherwise set it to a RemoteException.
|
68
|
+
else
|
69
|
+
type = 'RemoteException'
|
70
|
+
end
|
71
|
+
|
72
|
+
# RPC conventions for exception transmission.
|
73
|
+
res.exception = {
|
74
|
+
'type' => type,
|
75
|
+
'message' => e.to_s,
|
76
|
+
'backtrace' => e.backtrace
|
77
|
+
}
|
78
|
+
|
79
|
+
msg = "#{e.to_s}\n#{e.backtrace.join( "\n" )}"
|
80
|
+
@server.logger.error( 'Exception' ){ msg + " [on behalf of #{peer}]" }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Pass the result of the RPC call back to the client but *only* if it
|
84
|
+
# wasn't async, otherwise {Server#call} will have already taken care of it.
|
85
|
+
send_response( res ) if !res.async?
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Converts incoming hash objects to {Request} objects and calls
|
91
|
+
# {#receive_request}.
|
92
|
+
#
|
93
|
+
# @param [Hash] obj
|
94
|
+
def receive_object( obj )
|
95
|
+
receive_request( Request.new( obj ) )
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param [Symbol] severity
|
99
|
+
#
|
100
|
+
# Severity of the logged event:
|
101
|
+
#
|
102
|
+
# * `:debug`
|
103
|
+
# * `:info`
|
104
|
+
# * `:warn`
|
105
|
+
# * `:error`
|
106
|
+
# * `:fatal`
|
107
|
+
# * `:unknown`
|
108
|
+
#
|
109
|
+
# @param [String] category
|
110
|
+
# Category of message (SSL, Call, etc.).
|
111
|
+
# @param [String] msg
|
112
|
+
# Message to log.
|
113
|
+
def log( severity, category, msg )
|
114
|
+
sev_sym = Logger.const_get( severity.to_s.upcase.to_sym )
|
115
|
+
@server.logger.add( sev_sym, msg, category )
|
116
|
+
end
|
117
|
+
|
118
|
+
# Authenticates the client based on the token in the request.
|
119
|
+
#
|
120
|
+
# It will raise an exception if the token doesn't check-out.
|
121
|
+
def authenticate!
|
122
|
+
return if valid_token?( @request.token )
|
123
|
+
|
124
|
+
msg = "Token missing or invalid while calling: #{@request.message}"
|
125
|
+
|
126
|
+
@server.logger.error( 'Authenticator' ){
|
127
|
+
msg + " [on behalf of #{peer_ip_address}]"
|
128
|
+
}
|
129
|
+
|
130
|
+
fail Exceptions::InvalidToken.new( msg )
|
131
|
+
end
|
132
|
+
|
133
|
+
# Compares the authentication token in the param with the one of the server.
|
134
|
+
#
|
135
|
+
# @param [String] token
|
136
|
+
#
|
137
|
+
# @return [Bool]
|
138
|
+
def valid_token?( token )
|
139
|
+
token == @server.token
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|