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
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Arachni::RPC::Exceptions do
|
4
|
+
|
5
|
+
describe '#rpc_exception?' do
|
6
|
+
context 'for RPC exceptions' do
|
7
|
+
subject { described_class::InvalidMethod.new.rpc_exception? }
|
8
|
+
it { should be_true }
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'for other exceptions' do
|
12
|
+
subject { ::Exception.new.rpc_connection_error? }
|
13
|
+
it { should be_false }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#rpc_connection_error?' do
|
18
|
+
context 'for ConnectionError' do
|
19
|
+
subject { described_class::ConnectionError.new.rpc_connection_error? }
|
20
|
+
it { should be_true }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'for other exceptions' do
|
24
|
+
subject { described_class::InvalidMethod.new.rpc_connection_error? }
|
25
|
+
it { should be_false }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#rpc_remote_exception?' do
|
30
|
+
context 'for RemoteException' do
|
31
|
+
subject { described_class::RemoteException.new.rpc_remote_exception? }
|
32
|
+
it { should be_true }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'for other exceptions' do
|
36
|
+
subject { described_class::InvalidMethod.new.rpc_remote_exception? }
|
37
|
+
it { should be_false }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#rpc_invalid_object_error?' do
|
42
|
+
context 'for invalid object RPC exceptions' do
|
43
|
+
subject { described_class::InvalidObject.new.rpc_invalid_object_error? }
|
44
|
+
it { should be_true }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'for other exceptions' do
|
48
|
+
subject { ::Exception.new.rpc_invalid_object_error? }
|
49
|
+
it { should be_false }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#rpc_invalid_method_error?' do
|
54
|
+
context 'for invalid method RPC exceptions' do
|
55
|
+
subject { described_class::InvalidMethod.new.rpc_invalid_method_error? }
|
56
|
+
it { should be_true }
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'for other exceptions' do
|
60
|
+
subject { ::Exception.new.rpc_invalid_method_error? }
|
61
|
+
it { should be_false }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#rpc_invalid_token_error?' do
|
66
|
+
context 'for RPC exceptions' do
|
67
|
+
subject { described_class::InvalidToken.new.rpc_invalid_token_error? }
|
68
|
+
it { should be_true }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'for other exceptions' do
|
72
|
+
subject { ::Exception.new.rpc_invalid_token_error? }
|
73
|
+
it { should be_false }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class MyMessage < Arachni::RPC::Message
|
4
|
+
attr_accessor :foo
|
5
|
+
attr_accessor :boo
|
6
|
+
|
7
|
+
def transmit?( attr )
|
8
|
+
attr == :@boo
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Arachni::RPC::Message do
|
13
|
+
let(:options) { { foo: 'foo val', boo: 'boo val' }}
|
14
|
+
subject { MyMessage.new( options ) }
|
15
|
+
|
16
|
+
describe '#initialize' do
|
17
|
+
it 'sets attributes' do
|
18
|
+
subject.foo == options[:foo]
|
19
|
+
subject.boo == options[:boo]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#merge!' do
|
24
|
+
it 'assigns the attribute values of the provided object to self' do
|
25
|
+
opts = { foo: 'my foo' }
|
26
|
+
my_msg = MyMessage.new( opts )
|
27
|
+
|
28
|
+
subject.merge!( my_msg )
|
29
|
+
|
30
|
+
subject.foo == opts[:foo]
|
31
|
+
subject.boo == options[:boo]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#prepare_for_tx' do
|
36
|
+
it 'converts self into a hash' do
|
37
|
+
subject.prepare_for_tx.class.should == Hash
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'skips attributes based on #transmit?' do
|
41
|
+
subject.prepare_for_tx.should include 'boo'
|
42
|
+
subject.prepare_for_tx.should_not include 'callback_id'
|
43
|
+
subject.prepare_for_tx.should_not include 'foo'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Translator < Arachni::RPC::Proxy
|
4
|
+
|
5
|
+
translate :foo do |response|
|
6
|
+
response.map(&:to_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
translate :delay do |response, arguments|
|
10
|
+
[arguments, response.map(&:to_s)]
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Arachni::RPC::Proxy do
|
16
|
+
|
17
|
+
def wait
|
18
|
+
Arachni::Reactor.global.wait rescue Arachni::Reactor::Error::NotRunning
|
19
|
+
end
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
if Arachni::Reactor.global.running?
|
23
|
+
Arachni::Reactor.stop
|
24
|
+
end
|
25
|
+
|
26
|
+
Arachni::Reactor.global.run_in_thread
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:translated_arguments) do
|
30
|
+
arguments.map(&:to_s)
|
31
|
+
end
|
32
|
+
let(:arguments) do
|
33
|
+
[
|
34
|
+
'one',
|
35
|
+
2,
|
36
|
+
{ three: 3 },
|
37
|
+
[ 4 ]
|
38
|
+
]
|
39
|
+
end
|
40
|
+
let(:reactor) { Arachni::Reactor.global }
|
41
|
+
let(:client) { start_client( rpc_opts ) }
|
42
|
+
let(:handler) { 'test' }
|
43
|
+
let(:translator) { Translator.new( client, handler ) }
|
44
|
+
subject do
|
45
|
+
Arachni::RPC::Proxy.new( client, handler )
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'forwards synchronous calls' do
|
49
|
+
subject.foo( arguments ).should == arguments
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'forwards synchronous calls' do
|
53
|
+
response = nil
|
54
|
+
subject.foo( arguments ) do |res|
|
55
|
+
response = res
|
56
|
+
Arachni::Reactor.stop
|
57
|
+
end
|
58
|
+
wait
|
59
|
+
|
60
|
+
response.should == arguments
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '.translate' do
|
64
|
+
context 'when a synchronous call' do
|
65
|
+
context 'does not return an exception' do
|
66
|
+
it 'returns the translated result' do
|
67
|
+
translator.foo( arguments ).should == translated_arguments
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'returns an exception' do
|
72
|
+
it 'returns the exception'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when an asynchronous call' do
|
77
|
+
context 'does not result in an exception' do
|
78
|
+
it 'calls the block with the translated result' do
|
79
|
+
response = nil
|
80
|
+
translator.foo( arguments ) do |res|
|
81
|
+
response = res
|
82
|
+
Arachni::Reactor.stop
|
83
|
+
end
|
84
|
+
wait
|
85
|
+
|
86
|
+
response.should == translated_arguments
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'results in an exception' do
|
91
|
+
it 'calls the block with the exception'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'passes the method arguments to the translator' do
|
96
|
+
translator.delay( arguments ).should == [arguments, translated_arguments]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Arachni::RPC::Request do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
describe '#message' do
|
7
|
+
it 'should be an accessor' do
|
8
|
+
subject.message = 'test'
|
9
|
+
subject.message.should == 'test'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#args' do
|
14
|
+
it 'should be an accessor' do
|
15
|
+
subject.args = %w(test)
|
16
|
+
subject.args.should == %w(test)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#token' do
|
21
|
+
it 'should be an accessor' do
|
22
|
+
subject.token = 'blah'
|
23
|
+
subject.token.should == 'blah'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#callback' do
|
28
|
+
it 'should be an accessor' do
|
29
|
+
called = false
|
30
|
+
subject.callback = proc { called = true }
|
31
|
+
subject.callback.call
|
32
|
+
called.should be_true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#prepare_for_tx' do
|
37
|
+
it 'should convert the request to a hash ready for transmission' do
|
38
|
+
subject.prepare_for_tx.should be_empty
|
39
|
+
|
40
|
+
described_class.new(
|
41
|
+
message: 'obj.method',
|
42
|
+
args: %w(test),
|
43
|
+
token: 'mytoken',
|
44
|
+
callback: proc{}
|
45
|
+
).prepare_for_tx.should =={
|
46
|
+
'args' => %w(test),
|
47
|
+
'message' => 'obj.method',
|
48
|
+
'token' => 'mytoken'
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Arachni::RPC::Response do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
describe '#obj' do
|
7
|
+
it 'should be an accessor' do
|
8
|
+
subject.obj = 'test'
|
9
|
+
subject.obj.should == 'test'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#exception' do
|
14
|
+
it 'should be an accessor' do
|
15
|
+
subject.exception = 'test'
|
16
|
+
subject.exception.should == 'test'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#exception?' do
|
21
|
+
context 'when #exception is not set' do
|
22
|
+
it 'returns false' do
|
23
|
+
subject.exception?.should be_false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when #exception is set' do
|
28
|
+
it 'returns true' do
|
29
|
+
subject.exception = 'stuff'
|
30
|
+
subject.exception?.should be_true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#async?' do
|
36
|
+
context 'by default' do
|
37
|
+
it 'should return false' do
|
38
|
+
subject.async?.should be_false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'after #async!' do
|
43
|
+
it 'should return false' do
|
44
|
+
subject.async!
|
45
|
+
subject.async?.should be_true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Arachni::RPC::Server
|
4
|
+
public :async?, :async_check, :object_exist?, :public_method?
|
5
|
+
attr_accessor :proxy
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Arachni::RPC::Server do
|
9
|
+
let(:options) { rpc_opts.merge( port: 7333 ) }
|
10
|
+
subject { start_server( options, true ) }
|
11
|
+
|
12
|
+
describe '#initialize' do
|
13
|
+
it 'should be able to properly setup class options' do
|
14
|
+
subject.opts.should == options
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when passed no connection information' do
|
18
|
+
it 'raises ArgumentError' do
|
19
|
+
begin
|
20
|
+
described_class.new({})
|
21
|
+
rescue => e
|
22
|
+
e.should be_kind_of ArgumentError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when passed a host but not a port' do
|
28
|
+
it 'raises ArgumentError' do
|
29
|
+
begin
|
30
|
+
described_class.new( host: 'test' )
|
31
|
+
rescue => e
|
32
|
+
e.should be_kind_of ArgumentError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when passed a port but not a host' do
|
38
|
+
it 'raises ArgumentError' do
|
39
|
+
begin
|
40
|
+
described_class.new( port: 9999 )
|
41
|
+
rescue => e
|
42
|
+
e.should be_kind_of ArgumentError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when passed an invalid port' do
|
48
|
+
it 'raises ArgumentError' do
|
49
|
+
begin
|
50
|
+
described_class.new( host: 'tt', port: 'blah' )
|
51
|
+
rescue => e
|
52
|
+
e.should be_kind_of ArgumentError
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'retains the supplied token' do
|
59
|
+
subject.token.should == options[:token]
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'has a Logger' do
|
63
|
+
subject.logger.class.should == ::Logger
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#alive?' do
|
67
|
+
it 'returns true' do
|
68
|
+
subject.should be_alive
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#async?' do
|
73
|
+
context 'when a method is async' do
|
74
|
+
it 'returns true' do
|
75
|
+
subject.async?( 'test', 'delay' ).should be_true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when a method is sync' do
|
80
|
+
it 'returns false' do
|
81
|
+
subject.async?( 'test', 'foo' ).should be_false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#async_check' do
|
87
|
+
context 'when a method is async' do
|
88
|
+
it 'returns true' do
|
89
|
+
subject.async_check( Test.new.method( :delay ) ).should be_true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when a method is sync' do
|
94
|
+
it 'returns false' do
|
95
|
+
subject.async_check( Test.new.method( :foo ) ).should be_false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#object_exist?' do
|
101
|
+
context 'when an object exists' do
|
102
|
+
it 'returns true' do
|
103
|
+
subject.object_exist?( 'test' ).should be_true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when an object does not exist' do
|
108
|
+
it 'returns false' do
|
109
|
+
subject.object_exist?( 'foo' ).should be_false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#public_method?' do
|
115
|
+
context 'when a method is public' do
|
116
|
+
it 'returns true' do
|
117
|
+
subject.public_method?( 'test', 'foo' ).should be_true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when a method is non-existent or not public' do
|
122
|
+
it 'returns false' do
|
123
|
+
subject.public_method?( 'test', 'bar' ).should be_false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|