arachni-rpc-em 0.1.2 → 0.1.3dev1

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.
@@ -49,7 +49,7 @@ module EM
49
49
  if !::EM::reactor_running?
50
50
 
51
51
  Thread.new do
52
- ::EM::run do
52
+ ::EM.run do
53
53
  ::EM.error_handler do |e|
54
54
  $stderr.puts "Exception raised during event loop: " +
55
55
  "#{e.message} (#{e.class})\n#{(e.backtrace ||
@@ -88,7 +88,7 @@ module Protocol
88
88
  # cut them out as soon as possible
89
89
  #
90
90
  # don't buffer any data from unverified peers if SSL peer
91
- # veification has been enabled
91
+ # verification has been enabled
92
92
  #
93
93
  if ssl_opts? && !verified_peer? && @role == :server
94
94
  e = Arachni::RPC::Exceptions::SSLPeerVerificationFailed.new( 'Could not verify peer.' )
@@ -107,7 +107,7 @@ module Protocol
107
107
  while @buf.size >= 4
108
108
  if @buf.size >= 4 + ( size = @buf.unpack( 'N' ).first )
109
109
  @buf.slice!( 0, 4 )
110
- receive_object( serializer.load( @buf.slice!( 0, size ) ) )
110
+ receive_object( unserialize( @buf.slice!( 0, size ) ) )
111
111
  else
112
112
  break
113
113
  end
@@ -120,7 +120,7 @@ module Protocol
120
120
  # Will split the object in chunks of MAX_CHUNK_SIZE and transmit one at a time.
121
121
  #
122
122
  def send_object( obj )
123
- data = serializer.dump( obj )
123
+ data = serialize( obj )
124
124
  packed = [data.bytesize, data].pack( 'Na*' )
125
125
 
126
126
  while packed
@@ -134,7 +134,7 @@ module Protocol
134
134
  end
135
135
 
136
136
  #
137
- # Returns the preferred serializer based on the 'serializer' option of the server.
137
+ # Returns the preferred based on the 'serializer' option of the server.
138
138
  #
139
139
  # Defaults to <i>YAML</i>.
140
140
  #
@@ -143,9 +143,38 @@ module Protocol
143
143
  # @see http://eventmachine.rubyforge.org/EventMachine/Protocols/ObjectProtocol.html#M000369
144
144
  #
145
145
  def serializer
146
+ return @client_serializer if @client_serializer
147
+
146
148
  @opts[:serializer] ? @opts[:serializer] : YAML
147
149
  end
148
150
 
151
+ def fallback_serializer
152
+ @opts[:fallback_serializer] ? @opts[:serializer] : YAML
153
+ end
154
+
155
+ def serialize( obj )
156
+ serializer.dump obj
157
+ end
158
+
159
+ def unserialize( obj )
160
+ begin
161
+ r = serializer.load( obj )
162
+
163
+ if !r.is_a?( Hash ) && @opts[:fallback_serializer]
164
+ r = @opts[:fallback_serializer].load( obj )
165
+ @client_serializer = @opts[:fallback_serializer]
166
+ end
167
+
168
+ r
169
+ rescue Exception => e
170
+ raise if !@opts[:fallback_serializer]
171
+
172
+ @client_serializer = @opts[:fallback_serializer]
173
+
174
+ @opts[:fallback_serializer].load obj
175
+ end
176
+ end
177
+
149
178
  end
150
179
 
151
180
  end
@@ -82,53 +82,46 @@ class Server
82
82
 
83
83
  # the method call may block a little so tell EventMachine to
84
84
  # stick it in its own thread.
85
- # ::EM.defer( proc {
86
- res = Response.new
87
- peer = peer_ip_addr
85
+ res = Response.new
86
+ peer = peer_ip_addr
88
87
 
89
- begin
90
- # token-based authentication
91
- authenticate!
88
+ begin
89
+ # token-based authentication
90
+ authenticate!
92
91
 
93
- # grab the result of the method call
94
- res.merge!( @server.call( self ) )
92
+ # grab the result of the method call
93
+ res.merge!( @server.call( self ) )
95
94
 
96
- # handle exceptions and convert them to a simple hash,
97
- # ready to be passed to the client.
98
- rescue Exception => e
95
+ # handle exceptions and convert them to a simple hash,
96
+ # ready to be passed to the client.
97
+ rescue Exception => e
99
98
 
100
- type = ''
99
+ type = ''
101
100
 
102
- # if it's an RPC exception pass the type along as is
103
- if e.rpc_exception?
104
- type = e.class.name.split( ':' )[-1]
105
-
106
- # otherwise set it to a RemoteExeption
107
- else
108
- type = 'RemoteException'
109
- end
101
+ # if it's an RPC exception pass the type along as is
102
+ if e.rpc_exception?
103
+ type = e.class.name.split( ':' )[-1]
104
+ # otherwise set it to a RemoteExeption
105
+ else
106
+ type = 'RemoteException'
107
+ end
110
108
 
111
- res.obj = {
112
- 'exception' => e.to_s,
113
- 'backtrace' => e.backtrace,
114
- 'type' => type
115
- }
109
+ res.obj = {
110
+ 'exception' => e.to_s,
111
+ 'backtrace' => e.backtrace,
112
+ 'type' => type
113
+ }
116
114
 
117
- msg = "#{e.to_s}\n#{e.backtrace.join( "\n" )}"
118
- @server.logger.error( 'Exception' ){ msg + " [on behalf of #{peer}]" }
119
- end
115
+ msg = "#{e.to_s}\n#{e.backtrace.join( "\n" )}"
116
+ @server.logger.error( 'Exception' ){ msg + " [on behalf of #{peer}]" }
117
+ end
120
118
 
121
- # res
122
- # }, proc {
123
- # |res|
124
-
125
- #
126
- # pass the result of the RPC call back to the client
127
- # along with the callback ID but *only* if it wan't async
128
- # because server.call() will have already taken care of it
129
- #
130
- send_response( res ) if !res.async?
131
- # })
119
+ #
120
+ # pass the result of the RPC call back to the client
121
+ # along with the callback ID but *only* if it wan't async
122
+ # because server.call() will have already taken care of it
123
+ #
124
+ send_response( res ) if !res.async?
132
125
  end
133
126
 
134
127
  #
@@ -136,9 +129,6 @@ class Server
136
129
  #
137
130
  # It will raise an exception if the token doesn't check-out.
138
131
  #
139
- # @param [String] peer IP address of the client
140
- # @param [Hash] req request
141
- #
142
132
  def authenticate!
143
133
  if !valid_token?( @request.token )
144
134
 
@@ -148,7 +138,7 @@ class Server
148
138
  msg + " [on behalf of #{peer_ip_addr}]"
149
139
  }
150
140
 
151
- raise InvalidToken.new( msg )
141
+ fail InvalidToken.new( msg )
152
142
  end
153
143
  end
154
144
 
@@ -187,6 +177,9 @@ class Server
187
177
  # # http://eventmachine.rubyforge.org/EventMachine/Protocols/ObjectProtocol.html#M000369
188
178
  # :serializer => Marshal,
189
179
  #
180
+ # # serializer to use if the first choice fails
181
+ # :fallback_serializer => YAML,
182
+ #
190
183
  # #
191
184
  # # In order to enable peer verification one must first provide
192
185
  # # the following:
@@ -232,15 +225,14 @@ class Server
232
225
  # you can just decide dynamically based on the plethora of data which Ruby provides
233
226
  # by its 'Method' class.
234
227
  #
235
- # server.add_async_check {
236
- # |method|
228
+ # server.add_async_check do |method|
237
229
  # #
238
230
  # # Must return 'true' for async and 'false' for sync.
239
231
  # #
240
232
  # # Very simple check here...
241
233
  # #
242
234
  # 'async' == method.name.to_s.split( '_' )[0]
243
- # }
235
+ # end
244
236
  #
245
237
  # @param [Proc] &block
246
238
  #
@@ -310,13 +302,13 @@ class Server
310
302
  if !object_exist?( obj_name )
311
303
  msg = "Trying to access non-existent object '#{obj_name}'."
312
304
  @logger.error( 'Call' ){ msg + " [on behalf of #{peer_ip_addr}]" }
313
- raise( InvalidObject.new( msg ) )
305
+ raise InvalidObject.new( msg )
314
306
  end
315
307
 
316
308
  if !public_method?( obj_name, meth_name )
317
309
  msg = "Trying to access non-public method '#{meth_name}'."
318
310
  @logger.error( 'Call' ){ msg + " [on behalf of #{peer_ip_addr}]" }
319
- raise( InvalidMethod.new( msg ) )
311
+ raise InvalidMethod.new( msg )
320
312
  end
321
313
 
322
314
  # the proxy needs to know whether this is an async call because if it
@@ -352,7 +344,7 @@ class Server
352
344
  @logger.info( 'System' ){ "Shutting down in #{wait_for} seconds..." }
353
345
 
354
346
  # don't die before returning
355
- EventMachine::add_timer( wait_for ) { ::EM.stop }
347
+ ::EM.add_timer( wait_for ) { ::EM.stop }
356
348
  true
357
349
  end
358
350
 
@@ -88,7 +88,7 @@ module SSL
88
88
  #
89
89
  def log( severity, progname, msg )
90
90
  warn "#{progname}: #{msg}" if severity == :error
91
- raise "#{progname}: #{msg}" if severity == :fatal
91
+ fail "#{progname}: #{msg}" if severity == :fatal
92
92
  end
93
93
 
94
94
  #
@@ -100,7 +100,7 @@ module SSL
100
100
  @ca_store = OpenSSL::X509::Store.new
101
101
  @ca_store.add_file( file )
102
102
  else
103
- raise "No CA certificate has been provided."
103
+ fail "No CA certificate has been provided."
104
104
  end
105
105
  end
106
106
 
@@ -144,17 +144,14 @@ module SSL
144
144
  # @see http://eventmachine.rubyforge.org/EventMachine/Connection.html#M000270
145
145
  #
146
146
  def ssl_handshake_completed
147
- if are_we_a_client? && ssl_opts? &&
148
- !OpenSSL::SSL.verify_certificate_identity( @last_seen_cert,
149
- @opts[:host] )
147
+ return if !are_we_a_client? || !ssl_opts? ||
148
+ OpenSSL::SSL.verify_certificate_identity( @last_seen_cert, @opts[:host] )
150
149
 
151
- log( :error, 'SSL',
152
- "The hostname '#{@server.opts[:host]}' " +
153
- "does not match the server certificate."
154
- )
150
+ log( :error, 'SSL',
151
+ "The hostname '#{@opts[:host]}' does not match the server certificate."
152
+ )
155
153
 
156
- connection_close
157
- end
154
+ close_connection
158
155
  end
159
156
 
160
157
  def are_we_a_client?
@@ -9,7 +9,7 @@
9
9
  module Arachni
10
10
  module RPC
11
11
  module EM
12
- VERSION = '0.1.2'
12
+ VERSION = '0.1.3dev1'
13
13
  end
14
14
  end
15
15
  end
@@ -8,6 +8,35 @@ describe Arachni::RPC::EM::Client do
8
8
  ]
9
9
  end
10
10
 
11
+ it "should be able to retain stability and consistency under heavy load" do
12
+ client = start_client( rpc_opts )
13
+
14
+ n = 400
15
+ cnt = 0
16
+
17
+ mismatches = []
18
+
19
+ n.times do |i|
20
+ client.call( 'test.foo', i ) do |res|
21
+ cnt += 1
22
+ mismatches << [i, res] if i != res
23
+ ::EM.stop if cnt == n || !mismatches.empty?
24
+ end
25
+ end
26
+
27
+ Arachni::RPC::EM.block
28
+ cnt.should > 0
29
+ mismatches.should be_empty
30
+ end
31
+
32
+ it "should throw error when connecting to inexistent server" do
33
+ start_client( rpc_opts.merge( :port => 9999 ) ).call( 'test.foo', @arg ) do |res|
34
+ res.rpc_connection_error?.should be_true
35
+ ::EM.stop
36
+ end
37
+ Arachni::RPC::EM.block
38
+ end
39
+
11
40
  describe "#initialize" do
12
41
  it "should be able to properly assign class options (including :role)" do
13
42
  opts = rpc_opts.merge( :role => :client )
@@ -15,6 +44,19 @@ describe Arachni::RPC::EM::Client do
15
44
  end
16
45
  end
17
46
 
47
+ context 'when using a fallback serializer' do
48
+ context 'and the primary serializer fails' do
49
+ it 'should use the fallback' do
50
+ opts = rpc_opts.merge( port: 7333, serializer: YAML )
51
+ start_client( opts ).call( 'test.foo', @arg ).should == @arg
52
+
53
+ opts = rpc_opts.merge( port: 7333, serializer: Marshal )
54
+ start_client( opts ).call( 'test.foo', @arg ).should == @arg
55
+ end
56
+ end
57
+
58
+ end
59
+
18
60
  describe "raw interface" do
19
61
 
20
62
  context "when using Threads" do
@@ -131,35 +173,6 @@ describe Arachni::RPC::EM::Client do
131
173
  end
132
174
  end
133
175
 
134
- it "should be able to retain stability and consistency under heavy load" do
135
- client = start_client( rpc_opts )
136
-
137
- n = 400
138
- cnt = 0
139
-
140
- mismatches = []
141
-
142
- n.times do |i|
143
- client.call( 'test.foo', i ) do |res|
144
- cnt += 1
145
- mismatches << [i, res] if i != res
146
- ::EM.stop if cnt == n || !mismatches.empty?
147
- end
148
- end
149
-
150
- Arachni::RPC::EM.block
151
- cnt.should > 0
152
- mismatches.should be_empty
153
- end
154
-
155
- it "should throw error when connecting to inexistent server" do
156
- start_client( rpc_opts.merge( :port => 9999 ) ).call( 'test.foo', @arg ) do |res|
157
- res.rpc_connection_error?.should be_true
158
- ::EM.stop
159
- end
160
- Arachni::RPC::EM.block
161
- end
162
-
163
176
  context "when using valid SSL primitives" do
164
177
  it "should be able to establish a connection" do
165
178
  res = start_client( rpc_opts_with_ssl_primitives ).call( 'test.foo', @arg )
data/spec/pems/cacert.pem CHANGED
@@ -1,39 +1,37 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- +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
- 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
- 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
- 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
- Bz29wi4b7sAG/lQB9dCSgzY4KIAUlvqMe7A4uQuu5EtplFGVFvj8Wekai9BrhYWq
19
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- QP90tyRNWy5PNFbe6Clg0ulvYfk9fEBNWYRv7AboTBetXh5PIy1CHm7dMHpUcAZf
35
- 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
- 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
- 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
38
- WC2Jc17gy6ZvkI3wzvqe/eSyq2N8zRzYQEgaotDhkrbL81N0klZ6RoKkxeI=
2
+ MIIGaDCCBFCgAwIBAgIJAPBYwqAog9f1MA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
3
+ BAYTAkdSMRgwFgYDVQQDEw9BcmFjaG5pLXRlc3QtQ0ExKjAoBgkqhkiG9w0BCQEW
4
+ G2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNvbTAeFw0xMjExMDIxMDIyNTNaFw0y
5
+ MjEwMzExMDIyNTNaMFMxCzAJBgNVBAYTAkdSMRgwFgYDVQQDEw9BcmFjaG5pLXRl
6
+ c3QtQ0ExKjAoBgkqhkiG9w0BCQEWG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNv
7
+ bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANHbVRqyD7FT8KSCZLm1
8
+ YrOwMj78OXRjMb+ucPR+fncXR1TEuPlEFojLtzqtsaA5OJXpj+0toFP+2T4DeFlu
9
+ YT4TBrRQ3DxmeY2b5b6ZuurLMfMc1vU+dE92Tj4nuInBz3Z09aDQ8ZWXWzJ7uD3J
10
+ eNjOcj7Jc94AGFx05Tii9VriUmX+jXQALv5S5WGVtKt4p67mLm/2xD4JhS+a0+B8
11
+ s/Xo7l6EXaFeTsH5jgZDiY+f0Dpk6cM+pZ5AJVJiNonDJs8/nl9vdWPRH40GHsyN
12
+ H0/lo/wTxuth/TvX3DBB5hTi/9V5eYbLTLtE1oyXgBxNKrjDu8FVn/jUl8DAIdJL
13
+ n4vXwXI70wLyS6aF8uu9ApNnmSbTTc2scAKDmnlINLHqGGlpyleojqphDy3MfQYk
14
+ YT9QSXNKHlO4NMLFqrqM1F3hvM3MEteeM12gAeeJLY6YYYJafMMMh/e7kK/y5u+J
15
+ ype5t5N8GKskSRp0RvlRYfoH/lnyJd6FEyh9P0QHA4CKAadZBCfOcmwmTY/G0Kjn
16
+ 3Y7r7BmJb4PIEcDqUjXwuyq6ZHjx7sawuGG6eXhIGln0JtSymy4j+h+xlh0S7O8B
17
+ Ti6dMVxg+DNTkEJz2O00IIBcyToqZ26XovFkN5ueRNOROB3YVpldmpbLyuOQae/D
18
+ 4Gc21bEWoR7OAaY2PRl4r563AgMBAAGjggE9MIIBOTAdBgNVHQ4EFgQU8VOtNUbZ
19
+ rnbX0gRWIds5yaT+uCwwgYMGA1UdIwR8MHqAFPFTrTVG2a5219IEViHbOcmk/rgs
20
+ oVekVTBTMQswCQYDVQQGEwJHUjEYMBYGA1UEAxMPQXJhY2huaS10ZXN0LUNBMSow
21
+ KAYJKoZIhvcNAQkBFhthcmFjaG5pQGFyYWNobmktc2Nhbm5lci5jb22CCQDwWMKg
22
+ KIPX9TAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBBjAJBgNVHRIE
23
+ AjAAMCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRl
24
+ MCYGA1UdEQQfMB2BG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNvbTAOBgNVHQ8B
25
+ Af8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggIBAAAAebhmNydIUT+sZpvWM5JKNQdq
26
+ YtnghnpZyM37cxLmFIeDKKPTw/3lhDViRRqQaaFTI7ukcqgloPmhMuymQT4o11Fw
27
+ XLoQuIeSgS7Mg6eZF3CUKoEy1KnlluegDXtDI+WH/EQHlokBvoMaClltj6vsfqI/
28
+ K9K2MAXUKi8K7NRq6VYO1QPtrBfrX9VmLyndbYm8lSG5oDkGGh8NjVgHHZDgrQAQ
29
+ 2EmsWbE9yMZ6yl+AaaE5XrbPWnBI8rK77WP93JYVAhmcaQiJzPfMw3sb2QojKdak
30
+ 7fvJzAjBeXAoTP5Mu/E+BPPgELzB/DnRaWlrYsAQeRZBV+I3+k5CCVqdOAdJCk5Y
31
+ dFNTppHfwVaDj5qKOmodzdUDcDL6ynl15t6WHgj2yBwsDVpWsvbqyitZkemLFwrf
32
+ hAedR3dKr+IxrOynST1w4LorDorcGK/DqhD475bQ9EDel5nW18hotUeeeO+K3qc7
33
+ iGgj7zTKfmhzL/KMotir/SQYYxQbbtLkkhXDaNVlWiYkHotOzrNbpKAFM776CI47
34
+ KTXG88FydcycGHYU8SQLEbEDiowAikr2u+CUHKrJvz2Wr8jkWaMCSfgCyokcY6vR
35
+ IIqnrpYHhX2FmKf+tRB8o3KXM6uiOSUvaW23LHcs0OKqcJ0XHOkhTMDFZ82eDZzl
36
+ CXJQkVNhmc0Y9prF
39
37
  -----END CERTIFICATE-----