arachni-rpc 0.1.3 → 0.2.0
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 +9 -3
- data/LICENSE.md +1 -1
- data/README.md +28 -34
- data/Rakefile +16 -19
- data/lib/arachni/rpc.rb +4 -8
- data/lib/arachni/rpc/client.rb +236 -0
- data/lib/arachni/rpc/client/handler.rb +167 -0
- data/lib/arachni/rpc/exceptions.rb +14 -38
- data/lib/arachni/rpc/message.rb +7 -15
- data/lib/arachni/rpc/protocol.rb +103 -0
- data/lib/arachni/rpc/proxy.rb +86 -0
- data/lib/arachni/rpc/request.rb +18 -36
- data/lib/arachni/rpc/response.rb +21 -35
- data/lib/arachni/rpc/server.rb +278 -0
- data/lib/arachni/rpc/server/handler.rb +145 -0
- data/lib/arachni/rpc/version.rb +3 -1
- data/spec/arachni/rpc/client_spec.rb +400 -0
- data/spec/arachni/rpc/exceptions_spec.rb +77 -0
- data/spec/arachni/rpc/message_spec.rb +47 -0
- data/spec/arachni/rpc/proxy_spec.rb +99 -0
- data/spec/arachni/rpc/request_spec.rb +53 -0
- data/spec/arachni/rpc/response_spec.rb +49 -0
- data/spec/arachni/rpc/server_spec.rb +129 -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
- metadata +78 -21
- data/lib/arachni/rpc/remote_object_mapper.rb +0 -65
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cbbd82c15cfd3db423e6c200ca21a1bfc34ed6d7
|
4
|
+
data.tar.gz: 828f98fbc48632790ff1bb42f723e4e068c924a3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 75c2287cb0aa85c1cdd66c25068a96db4f180bac1723fde3b46ad451c34afe9a7f45901c6f99b988bc5b4758e6f3fdf763b4f5b8b0ff8395b2cd7abd1c0e6e72
|
7
|
+
data.tar.gz: 1a5fe36d458864aec8f45871e6d650841a47eb0d70ce22f78726eafddaf129ded96c8efd8dbf44dc6ff574b763a57112f1d2e56f895f78d884fb7f326d7daa81
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
# ChangeLog
|
2
2
|
|
3
|
+
## Version 0.2.0
|
4
|
+
|
5
|
+
- Added `Arachni::Reactor`-based RPC client/server implementation.
|
6
|
+
|
3
7
|
## Version 0.1.3
|
4
|
-
|
5
|
-
-
|
8
|
+
|
9
|
+
- Removed `Arachni::RPC::Request#do_not_defer` and `Arachni::RPC::Request#defer?`.
|
10
|
+
- All RPC exceptions now inherit from `RuntimeError` instead of `Exception`.
|
6
11
|
|
7
12
|
## Version 0.1.2
|
13
|
+
|
8
14
|
- Code cleanup.
|
9
|
-
- Arachni::RPC::Request#do_not_defer
|
15
|
+
- `Arachni::RPC::Request#do_not_defer!` => `Arachni::RPC::Request#do_not_defer`.
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# Arachni-RPC
|
2
|
+
|
2
3
|
<table>
|
3
4
|
<tr>
|
4
5
|
<th>Version</th>
|
5
|
-
<td>0.
|
6
|
+
<td>0.2.0</td>
|
6
7
|
</tr>
|
7
8
|
<tr>
|
8
9
|
<th>Github page</th>
|
@@ -14,7 +15,7 @@
|
|
14
15
|
</tr>
|
15
16
|
<tr>
|
16
17
|
<th>Author</th>
|
17
|
-
<td><a href="mailto:tasos.laskos@
|
18
|
+
<td><a href="mailto:tasos.laskos@arachni-scanner.com">Tasos "Zapotek" Laskos</a></td>
|
18
19
|
</tr>
|
19
20
|
<tr>
|
20
21
|
<th>Twitter</th>
|
@@ -22,7 +23,7 @@
|
|
22
23
|
</tr>
|
23
24
|
<tr>
|
24
25
|
<th>Copyright</th>
|
25
|
-
<td>2011-
|
26
|
+
<td>2011-2014</td>
|
26
27
|
</tr>
|
27
28
|
<tr>
|
28
29
|
<th>License</th>
|
@@ -32,56 +33,49 @@
|
|
32
33
|
|
33
34
|
## Synopsis
|
34
35
|
|
35
|
-
Arachni-RPC is a simple and lightweight Remote Procedure Call protocol
|
36
|
-
|
37
|
-
|
36
|
+
Arachni-RPC is a simple and lightweight Remote Procedure Call protocol and implementation
|
37
|
+
which provides the basis for <a href="http://arachni-scanner.com">Arachni</a>'s
|
38
|
+
distributed infrastructure.
|
38
39
|
|
39
|
-
|
40
|
-
- <a href="http://github.com/Arachni/arachni-rpc-pure">Arachni-RPC Pure</a> -- Provides a synchronous client using pure Ruby OpenSSL sockets and has no 3rd party dependencies.
|
40
|
+
(Based on the [Arachni::Reactor](https://github.com/Arachni/arachni-reactor) framework.)
|
41
41
|
|
42
42
|
## Features
|
43
43
|
|
44
|
-
- Extremely lightweight
|
45
|
-
- Very simple design
|
46
|
-
-
|
44
|
+
- Extremely lightweight.
|
45
|
+
- Very simple design.
|
46
|
+
- TLS encryption.
|
47
|
+
- Configurable serializer.
|
48
|
+
- Can intercept RPC responses and translate them into native objects for
|
49
|
+
when using serializers that only support basic types, like JSON or MessagePack.
|
50
|
+
- Token-based authentication.
|
51
|
+
- Pure-Ruby.
|
52
|
+
- Multi-platform, tested on:
|
53
|
+
- Linux
|
54
|
+
- OSX
|
55
|
+
- Windows
|
47
56
|
|
48
57
|
## Installation
|
49
58
|
|
50
|
-
|
51
|
-
(it'll most likely be installed as a dependency for some other project) but in case you want to some instructions follow.
|
52
|
-
|
53
|
-
### Gem
|
54
|
-
|
55
|
-
```gem install arachni-rpc```
|
56
|
-
|
57
|
-
### Source
|
58
|
-
|
59
|
-
If you want to clone the repository and work with the source code:
|
60
|
-
|
61
|
-
git co git://github.com/arachni/arachni-rpc.git
|
62
|
-
cd arachni-rpc
|
63
|
-
rake install
|
64
|
-
|
59
|
+
gem install arachni-rpc
|
65
60
|
|
66
61
|
## Running the Specs
|
67
62
|
|
68
|
-
|
69
|
-
gem install rspec
|
70
|
-
|
71
|
-
Then:
|
72
|
-
|
63
|
+
bundle install
|
73
64
|
rake spec
|
74
65
|
|
75
66
|
## Protocol specifications
|
76
67
|
|
77
|
-
You can find
|
68
|
+
You can find the RPC protocol specification at the
|
69
|
+
[Wiki](https://github.com/Arachni/arachni-rpc/wiki).
|
78
70
|
|
79
71
|
## Bug reports/Feature requests
|
80
|
-
|
72
|
+
|
73
|
+
Please send your feedback using GitHub's issue system at
|
81
74
|
[http://github.com/arachni/arachni-rpc/issues](http://github.com/arachni/arachni-rpc/issues).
|
82
75
|
|
83
76
|
|
84
77
|
## License
|
85
|
-
|
78
|
+
|
79
|
+
Arachni-RPC is provided under the 3-clause BSD license.
|
86
80
|
See the [LICENSE](file.LICENSE.html) file for more information.
|
87
81
|
|
data/Rakefile
CHANGED
@@ -19,38 +19,35 @@ end
|
|
19
19
|
|
20
20
|
task default: [ :build, :spec ]
|
21
21
|
|
22
|
-
desc
|
22
|
+
desc 'Generate docs'
|
23
23
|
task :docs do
|
24
|
-
outdir = "../arachni-rpc-
|
25
|
-
sh "
|
24
|
+
outdir = "../arachni-rpc-docs"
|
25
|
+
sh "rm -rf #{outdir}"
|
26
|
+
sh "mkdir -p #{outdir}"
|
26
27
|
|
27
|
-
sh "yardoc
|
28
|
-
\"Arachni-RPC\" \
|
29
|
-
lib/* -o #{outdir} \
|
30
|
-
- CHANGELOG.md LICENSE.md"
|
28
|
+
sh "yardoc -o #{outdir}"
|
31
29
|
|
32
|
-
|
33
|
-
sh "rm -rf .yard*"
|
30
|
+
sh "rm -rf .yardoc"
|
34
31
|
end
|
35
32
|
|
36
|
-
desc
|
33
|
+
desc 'Clean up'
|
37
34
|
task :clean do
|
38
|
-
sh
|
35
|
+
sh 'rm *.gem || true'
|
39
36
|
end
|
40
37
|
|
41
|
-
desc
|
42
|
-
task :
|
43
|
-
sh
|
38
|
+
desc 'Build the arachni-rpc gem.'
|
39
|
+
task build: [ :clean ] do
|
40
|
+
sh 'gem build arachni-rpc.gemspec'
|
44
41
|
end
|
45
42
|
|
46
|
-
desc
|
47
|
-
task :
|
43
|
+
desc 'Build and install the arachni gem.'
|
44
|
+
task install: [ :build ] do
|
48
45
|
sh "gem install arachni-rpc-#{Arachni::RPC::VERSION}.gem"
|
49
46
|
end
|
50
47
|
|
51
|
-
desc
|
52
|
-
task :
|
48
|
+
desc 'Push a new version to Rubygems'
|
49
|
+
task publish: [ :build ] do
|
53
50
|
sh "git tag -a v#{Arachni::RPC::VERSION} -m 'Version #{Arachni::RPC::VERSION}'"
|
54
51
|
sh "gem push arachni-rpc-#{Arachni::RPC::VERSION}.gem"
|
55
52
|
end
|
56
|
-
task :
|
53
|
+
task release: [ :publish ]
|
data/lib/arachni/rpc.rb
CHANGED
@@ -6,12 +6,8 @@
|
|
6
6
|
|
7
7
|
=end
|
8
8
|
|
9
|
-
require '
|
10
|
-
|
11
|
-
require File.join( File.expand_path( File.dirname( __FILE__ ) ), 'rpc', 'version' )
|
12
|
-
require File.join( File.expand_path( File.dirname( __FILE__ ) ), 'rpc', 'exceptions' )
|
13
|
-
require File.join( File.expand_path( File.dirname( __FILE__ ) ), 'rpc', 'message' )
|
14
|
-
require File.join( File.expand_path( File.dirname( __FILE__ ) ), 'rpc', 'request' )
|
15
|
-
require File.join( File.expand_path( File.dirname( __FILE__ ) ), 'rpc', 'response' )
|
16
|
-
require File.join( File.expand_path( File.dirname( __FILE__ ) ), 'rpc', 'remote_object_mapper' )
|
9
|
+
require 'arachni/reactor'
|
17
10
|
|
11
|
+
%w(version exceptions message request response proxy protocol client server).each do |f|
|
12
|
+
require_relative "rpc/#{f}"
|
13
|
+
end
|
@@ -0,0 +1,236 @@
|
|
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 Arachni
|
10
|
+
module RPC
|
11
|
+
|
12
|
+
require_relative 'client/handler'
|
13
|
+
|
14
|
+
# Simple RPC client capable of:
|
15
|
+
#
|
16
|
+
# * TLS encryption.
|
17
|
+
# * Asynchronous and synchronous requests.
|
18
|
+
# * Handling remote asynchronous calls that defer their result.
|
19
|
+
#
|
20
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
21
|
+
class Client
|
22
|
+
|
23
|
+
# Default amount of connections to maintain in the re-use pool.
|
24
|
+
DEFAULT_CONNECTION_POOL_SIZE = 1
|
25
|
+
|
26
|
+
# @return [Hash]
|
27
|
+
# Options hash.
|
28
|
+
attr_reader :opts
|
29
|
+
|
30
|
+
# @return [Integer]
|
31
|
+
# Amount of connections in the pool.
|
32
|
+
attr_reader :connection_count
|
33
|
+
|
34
|
+
# @example Example options:
|
35
|
+
#
|
36
|
+
# {
|
37
|
+
# :host => 'localhost',
|
38
|
+
# :port => 7331,
|
39
|
+
#
|
40
|
+
# # optional authentication token, if it doesn't match the one
|
41
|
+
# # set on the server-side you'll be getting exceptions.
|
42
|
+
# :token => 'superdupersecret',
|
43
|
+
#
|
44
|
+
# :serializer => Marshal,
|
45
|
+
#
|
46
|
+
# :max_retries => 0,
|
47
|
+
#
|
48
|
+
# # In order to enable peer verification one must first provide
|
49
|
+
# # the following:
|
50
|
+
# # SSL CA certificate
|
51
|
+
# :ssl_ca => cwd + '/../spec/pems/cacert.pem',
|
52
|
+
# # SSL private key
|
53
|
+
# :ssl_pkey => cwd + '/../spec/pems/client/key.pem',
|
54
|
+
# # SSL certificate
|
55
|
+
# :ssl_cert => cwd + '/../spec/pems/client/cert.pem'
|
56
|
+
# }
|
57
|
+
#
|
58
|
+
# @param [Hash] opts
|
59
|
+
# @option opts [String] :host Hostname/IP address.
|
60
|
+
# @option opts [Integer] :port Port number.
|
61
|
+
# @option opts [String] :socket Path to UNIX domain socket.
|
62
|
+
# @option opts [Integer] :connection_pool_size (1)
|
63
|
+
# Amount of connections to keep open.
|
64
|
+
# @option opts [String] :token Optional authentication token.
|
65
|
+
# @option opts [.dump, .load] :serializer (YAML)
|
66
|
+
# Serializer to use for message transmission.
|
67
|
+
# @option opts [Integer] :max_retries
|
68
|
+
# How many times to retry failed requests.
|
69
|
+
# @option opts [String] :ssl_ca SSL CA certificate.
|
70
|
+
# @option opts [String] :ssl_pkey SSL private key.
|
71
|
+
# @option opts [String] :ssl_cert SSL certificate.
|
72
|
+
def initialize( opts )
|
73
|
+
@opts = opts.merge( role: :client )
|
74
|
+
@token = @opts[:token]
|
75
|
+
|
76
|
+
@host, @port = @opts[:host], @opts[:port].to_i
|
77
|
+
@socket = @opts[:socket]
|
78
|
+
|
79
|
+
if !@socket && !(@host || @port)
|
80
|
+
fail ArgumentError, 'Needs either a :socket or :host and :port options.'
|
81
|
+
end
|
82
|
+
|
83
|
+
@port = @port.to_i
|
84
|
+
|
85
|
+
if @host && @port <= 0
|
86
|
+
fail ArgumentError, "Invalid port: #{@port}"
|
87
|
+
end
|
88
|
+
|
89
|
+
@pool_size = @opts[:connection_pool_size] || DEFAULT_CONNECTION_POOL_SIZE
|
90
|
+
|
91
|
+
@reactor = Reactor.global
|
92
|
+
|
93
|
+
@connections = @reactor.create_queue
|
94
|
+
@connection_count = 0
|
95
|
+
end
|
96
|
+
|
97
|
+
# Connection factory, will re-use or create new connections as needed to
|
98
|
+
# accommodate the workload.
|
99
|
+
#
|
100
|
+
# @param [Block] block
|
101
|
+
# Block to be passed a {Handler connection}.
|
102
|
+
#
|
103
|
+
# @return [Boolean]
|
104
|
+
# `true` if a new connection had to be established, `false` if an existing
|
105
|
+
# one was re-used.
|
106
|
+
def connect( &block )
|
107
|
+
if @connections.empty? && @connection_count < @pool_size
|
108
|
+
opts = @socket ? @socket : [@host, @port]
|
109
|
+
block.call @reactor.connect( *[opts, Handler, @opts.merge( client: self )].flatten )
|
110
|
+
increment_connection_counter
|
111
|
+
return true
|
112
|
+
end
|
113
|
+
|
114
|
+
pop_block = proc do |conn|
|
115
|
+
# Some connections may have died while they were waiting in the
|
116
|
+
# queue, get rid of them and start all over in case the queue has
|
117
|
+
# been emptied.
|
118
|
+
if !conn.done?
|
119
|
+
connection_failed conn
|
120
|
+
connect( &block )
|
121
|
+
next
|
122
|
+
end
|
123
|
+
|
124
|
+
block.call conn
|
125
|
+
end
|
126
|
+
|
127
|
+
@connections.pop( &pop_block )
|
128
|
+
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
# Close all connections.
|
133
|
+
def close
|
134
|
+
@reactor.on_tick do |task|
|
135
|
+
@connections.pop(&:close_without_retry)
|
136
|
+
task.done if @connections.empty?
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def increment_connection_counter
|
141
|
+
@connection_count += 1
|
142
|
+
end
|
143
|
+
|
144
|
+
# {Handler#done? Finished} {Handler}s push themselves here to be re-used.
|
145
|
+
#
|
146
|
+
# @param [Handler] connection
|
147
|
+
def push_connection( connection )
|
148
|
+
@connections << connection
|
149
|
+
end
|
150
|
+
|
151
|
+
# Handles failed connections.
|
152
|
+
#
|
153
|
+
# @param [Handler] connection
|
154
|
+
def connection_failed( connection )
|
155
|
+
@connection_count -= 1
|
156
|
+
connection.close_without_retry
|
157
|
+
end
|
158
|
+
|
159
|
+
# Calls a remote method and grabs the result.
|
160
|
+
#
|
161
|
+
# There are 2 ways to perform a call, async (non-blocking) and sync (blocking).
|
162
|
+
#
|
163
|
+
# @example To perform an async call you need to provide a block to handle the result.
|
164
|
+
#
|
165
|
+
# server.call( 'handler.method', arg1, arg2 ) do |res|
|
166
|
+
# do_stuff( res )
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# @example To perform a sync (blocking), call without a block.
|
170
|
+
#
|
171
|
+
# res = server.call( 'handler.method', arg1, arg2 )
|
172
|
+
#
|
173
|
+
# @param [String] msg
|
174
|
+
# RPC message in the form of `handler.method`.
|
175
|
+
# @param [Array] args
|
176
|
+
# Collection of arguments to be passed to the method.
|
177
|
+
# @param [Block] block
|
178
|
+
def call( msg, *args, &block )
|
179
|
+
req = Request.new(
|
180
|
+
message: msg,
|
181
|
+
args: args,
|
182
|
+
callback: block,
|
183
|
+
token: @token
|
184
|
+
)
|
185
|
+
|
186
|
+
block_given? ? call_async( req ) : call_sync( req )
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def set_exception( req, e )
|
192
|
+
msg = @socket ? " for '#{@socket}'." : " for '#{@host}:#{@port}'."
|
193
|
+
|
194
|
+
exc = (e.is_a?( Reactor::Connection::Error::SSL ) ?
|
195
|
+
Exceptions::SSLPeerVerificationFailed : Exceptions::ConnectionError
|
196
|
+
).new( e.to_s + msg )
|
197
|
+
|
198
|
+
exc.set_backtrace e.backtrace
|
199
|
+
req.callback.call exc
|
200
|
+
end
|
201
|
+
|
202
|
+
def call_async( req, &block )
|
203
|
+
req.callback = block if block_given?
|
204
|
+
|
205
|
+
begin
|
206
|
+
connect do |connection|
|
207
|
+
error = (connection.is_a?( Exception ) and connection) || connection.error
|
208
|
+
next set_exception( req, error ) if error
|
209
|
+
|
210
|
+
connection.send_request( req )
|
211
|
+
end
|
212
|
+
rescue => e
|
213
|
+
set_exception( req, e )
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def call_sync( req )
|
218
|
+
# If we're in the Reactor thread use a Fiber and if we're not use a Thread.
|
219
|
+
if @reactor.in_same_thread?
|
220
|
+
fail 'Cannot perform synchronous calls when running in the ' +
|
221
|
+
"#{Arachni::Reactor} loop."
|
222
|
+
end
|
223
|
+
|
224
|
+
q = Queue.new
|
225
|
+
call_async( req ) { |obj| q << obj }
|
226
|
+
ret = q.pop
|
227
|
+
|
228
|
+
raise ret if ret.is_a?( Exception )
|
229
|
+
|
230
|
+
ret
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
end
|