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