arachni-reactor 0.1.0.beta5 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -199,10 +199,9 @@ class Reactor
199
199
  begin
200
200
  Connection::Error.translate do
201
201
  socket = options[:unix_socket] ?
202
- connect_unix( options[:unix_socket] ) :
203
- connect_tcp( options[:host], options[:port] )
202
+ connect_unix( options[:unix_socket] ) : connect_tcp
204
203
 
205
- connection.configure socket, :client
204
+ connection.configure options.merge( socket: socket, role: :client )
206
205
  attach connection
207
206
  end
208
207
  rescue Connection::Error => e
@@ -266,7 +265,7 @@ class Reactor
266
265
  listen_unix( options[:unix_socket] ) :
267
266
  listen_tcp( options[:host], options[:port] )
268
267
 
269
- server.configure socket, :server, server_handler
268
+ server.configure options.merge( socket: socket, role: :server, server_handler: server_handler )
270
269
  attach server
271
270
  end
272
271
  rescue Connection::Error => e
@@ -415,7 +414,7 @@ class Reactor
415
414
  def schedule( &block )
416
415
  fail_if_not_running
417
416
 
418
- if running? && in_same_thread?
417
+ if in_same_thread?
419
418
  block.call
420
419
  else
421
420
  next_tick(&block)
@@ -500,7 +499,7 @@ class Reactor
500
499
 
501
500
  schedule do
502
501
  connection.reactor = self
503
- @connections[connection.socket] = connection
502
+ @connections[connection.to_io] = connection
504
503
  connection.on_attach
505
504
  end
506
505
  end
@@ -517,7 +516,7 @@ class Reactor
517
516
 
518
517
  schedule do
519
518
  connection.on_detach
520
- @connections.delete connection.socket
519
+ @connections.delete connection.to_io
521
520
  connection.reactor = nil
522
521
  end
523
522
  end
@@ -525,7 +524,7 @@ class Reactor
525
524
  # @return [Bool]
526
525
  # `true` if the connection is attached, `false` otherwise.
527
526
  def attached?( connection )
528
- @connections.include? connection.socket
527
+ @connections.include? connection.to_io
529
528
  end
530
529
 
531
530
  private
@@ -552,25 +551,8 @@ class Reactor
552
551
  'The host OS does not support UNIX-domain sockets.'
553
552
  end
554
553
 
555
- def process_connections
556
- if @connections.empty?
557
- sleep @max_tick_interval
558
- return
559
- end
560
-
561
- # Get connections with available events - :read, :write, :error.
562
- selected = select_connections
563
-
564
- # Close connections that have errors.
565
- [selected.delete(:error)].flatten.compact.each(&:close)
566
-
567
- # Call the corresponding event on the connections.
568
- selected.each { |event, connections| connections.each(&"_#{event}".to_sym) }
569
- end
570
-
571
554
  def determine_connection_options( *args )
572
555
  options = {}
573
- host = port = unix_socket = nil
574
556
 
575
557
  if args[1].is_a? Integer
576
558
  options[:host], options[:port], options[:handler], *handler_options = *args
@@ -599,26 +581,13 @@ class Reactor
599
581
 
600
582
  # @return [Socket]
601
583
  # Connected socket.
602
- def connect_tcp( host, port )
584
+ def connect_tcp
603
585
  socket = Socket.new(
604
586
  Socket::Constants::AF_INET,
605
587
  Socket::Constants::SOCK_STREAM,
606
588
  Socket::Constants::IPPROTO_IP
607
589
  )
608
590
  socket.do_not_reverse_lookup = true
609
-
610
- # JRuby throws java.nio.channels.NotYetConnectedException even after
611
- # it returns the socket from Kernel.select, so wait for it to connect
612
- # before moving on.
613
- if self.class.jruby?
614
- socket.connect( Socket.sockaddr_in( port, host ) )
615
- else
616
- begin
617
- socket.connect_nonblock( Socket.sockaddr_in( port, host ) )
618
- rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
619
- end
620
- end
621
-
622
591
  socket
623
592
  end
624
593
 
@@ -641,6 +610,22 @@ class Reactor
641
610
  @connections.values.each(&:close)
642
611
  end
643
612
 
613
+ def process_connections
614
+ if @connections.empty?
615
+ sleep @max_tick_interval
616
+ return
617
+ end
618
+
619
+ # Get connections with available events - :read, :write, :error.
620
+ selected = self.select_connections
621
+
622
+ # Close connections that have errors.
623
+ selected.delete(:error)&.each(&:close)
624
+
625
+ # Call the corresponding event on the connections.
626
+ selected.each { |event, connections| connections.each(&"_#{event}".to_sym) }
627
+ end
628
+
644
629
  # @return [Hash]
645
630
  #
646
631
  # Connections grouped by their available events:
@@ -650,52 +635,71 @@ class Reactor
650
635
  # {Connection#has_outgoing_data? outgoing buffer).
651
636
  # * `:error`
652
637
  def select_connections
653
- grouped_sockets =
638
+ r = []
639
+ w = []
640
+ e = []
641
+
642
+ @connections.values.each do |connection|
643
+
644
+ # Required for OSX as it connects immediately and then #select returns
645
+ # nothing as there's no activity, given that, OpenSSL doesn't get a chance
646
+ # to do its handshake so explicitly connect pending sockets, bypassing #select.
647
+ connection._connect if !connection.connected?
648
+
649
+ socket = connection.socket
650
+
651
+ e << socket
652
+
653
+ if connection.listener? || connection.connected?
654
+ r << socket
655
+ end
656
+
657
+ if connection.connected? && connection.has_outgoing_data?
658
+ w << socket
659
+ end
660
+ end
661
+
662
+ selected_sockets =
654
663
  begin
655
664
  Connection::Error.translate do
656
- select(
657
- read_sockets,
658
- write_sockets,
659
- read_sockets, # Read sockets are actually all sockets.
660
- @select_timeout
661
- )
665
+ select( r, w, e, @select_timeout )
662
666
  end
663
- rescue Connection::Error
667
+ rescue Connection::Error => e
668
+ nil
664
669
  end
665
670
 
666
- return {} if !grouped_sockets
671
+ selected_sockets ||= [[],[],[]]
672
+
673
+ # SSL sockets maintain their own buffer whose state can't be checked by
674
+ # Kernel.select, leading to cases where the SSL buffer isn't empty,
675
+ # even though Kernel.select says that there's nothing to read.
676
+ #
677
+ # So force a read for SSL sockets to cover all our bases.
678
+ #
679
+ # This is apparent especially on JRuby.
680
+ if r.size != selected_sockets[0].size
681
+ (r - selected_sockets[0]).each do |socket|
682
+ next if !socket.is_a?( OpenSSL::SSL::SSLSocket )
683
+ selected_sockets[0] << socket
684
+ end
685
+ end
686
+
687
+ if selected_sockets[0].empty? && selected_sockets[1].empty? &&
688
+ selected_sockets[2].empty?
689
+ return {}
690
+ end
667
691
 
668
692
  {
669
693
  # Since these will be processed in order, it's better have the write
670
694
  # ones first to flush the buffers ASAP.
671
- write: connections_from_sockets( grouped_sockets[1] ),
672
- read: connections_from_sockets( grouped_sockets[0] ),
673
- error: connections_from_sockets( grouped_sockets[2] )
695
+ write: connections_from_sockets( selected_sockets[1] ),
696
+ read: connections_from_sockets( selected_sockets[0] ),
697
+ error: connections_from_sockets( selected_sockets[2] )
674
698
  }
675
699
  end
676
700
 
677
- # @return [Array<Socket>]
678
- # Sockets of all connections, we want to be ready to read at any time.
679
- def read_sockets
680
- @connections.keys
681
- end
682
-
683
- # @return [Array<Socket>]
684
- # Sockets of connections with
685
- # {Connection#has_outgoing_data? outgoing data}.
686
- def write_sockets
687
- @connections.map do |socket, connection|
688
- next if !connection.has_outgoing_data?
689
- socket
690
- end.compact
691
- end
692
-
693
701
  def connections_from_sockets( sockets )
694
- sockets.map { |s| connection_from_socket( s ) }
695
- end
696
-
697
- def connection_from_socket( socket )
698
- @connections[socket]
702
+ sockets.map { |s| @connections[s.to_io] }
699
703
  end
700
704
 
701
705
  end
@@ -43,16 +43,32 @@ describe Arachni::Reactor::Connection::TLS do
43
43
  end
44
44
  end
45
45
 
46
+ before :each do
47
+ @accept_q = Queue.new
48
+ @accepted = nil
49
+ end
50
+
46
51
  let(:unix_socket) { unix_connect( @unix_socket ) }
47
52
  let(:unix_server_socket) { unix_server( port_to_socket( Servers.available_port ) ) }
48
53
 
49
- let(:echo_client) { tcp_connect( @host, @port ) }
54
+ let(:echo_client) { tcp_socket }
50
55
  let(:echo_client_handler) { EchoClientTLS.new }
51
56
 
52
57
  let(:peer_client_socket) { tcp_ssl_connect( host, port ) }
53
- let(:peer_server_socket) { tcp_ssl_server( host, port ) }
58
+ let(:peer_server_socket) do
59
+ s = tcp_ssl_server( host, port )
60
+ Thread.new do
61
+ begin
62
+ @accept_q << s.accept
63
+ rescue => e
64
+ ap e
65
+ end
66
+ end
67
+ s
68
+ end
69
+ let(:accepted) { @accepted ||= @accept_q.pop }
54
70
 
55
- let(:client_socket) { tcp_connect( host, port ) }
71
+ let(:client_socket) { tcp_socket }
56
72
  let(:server_socket) { tcp_server( host, port ) }
57
73
 
58
74
  let(:connection) { TLSHandler.new }
@@ -113,10 +129,12 @@ describe Arachni::Reactor::Connection::TLS do
113
129
  reactor.listen( host, port, TLSHandler, options )
114
130
 
115
131
  client.write data
132
+ sleep 1
133
+
116
134
  reactor.stop
117
135
  reactor.wait rescue Arachni::Reactor::Error::NotRunning
118
136
 
119
- received_data.should == data
137
+ expect(received_data).to eq data
120
138
  end
121
139
  end
122
140
 
@@ -244,7 +262,15 @@ describe Arachni::Reactor::Connection::TLS do
244
262
 
245
263
  context 'when connecting to a server' do
246
264
  let(:server) do
247
- tcp_ssl_server( host, port, server_ssl_options )
265
+ s = tcp_ssl_server( host, port, server_ssl_options )
266
+ Thread.new do
267
+ begin
268
+ @accept_q << s.accept
269
+ rescue => e
270
+ # ap e
271
+ end
272
+ end
273
+ s
248
274
  end
249
275
 
250
276
  before :each do
@@ -258,9 +284,7 @@ describe Arachni::Reactor::Connection::TLS do
258
284
  it 'connects successfully' do
259
285
  received = nil
260
286
  Thread.new do
261
- s = server.accept
262
- received = s.gets
263
-
287
+ received = accepted.gets
264
288
  reactor.stop
265
289
  end
266
290
 
@@ -279,10 +303,6 @@ describe Arachni::Reactor::Connection::TLS do
279
303
 
280
304
  context 'and no options have been provided' do
281
305
  it "passes #{Arachni::Reactor::Connection::Error} to #on_error" do
282
- Thread.new do
283
- server.accept
284
- end
285
-
286
306
  connection = nil
287
307
  reactor.run do
288
308
  connection = reactor.connect( host, port, TLSHandler )
@@ -297,8 +317,7 @@ describe Arachni::Reactor::Connection::TLS do
297
317
  it 'connects successfully' do
298
318
  received = nil
299
319
  t = Thread.new do
300
- s = server.accept
301
- received = s.gets
320
+ received = accepted.gets
302
321
  reactor.stop
303
322
  end
304
323
 
@@ -307,16 +326,13 @@ describe Arachni::Reactor::Connection::TLS do
307
326
  connection.write data
308
327
  end
309
328
 
310
- received.should == data
329
+ sleep 1
330
+ expect(received).to eq data
311
331
  end
312
332
  end
313
333
 
314
334
  context 'and are invalid' do
315
335
  it "passes #{Arachni::Reactor::Connection::Error} to #on_error" do
316
- Thread.new do
317
- server.accept
318
- end
319
-
320
336
  connection = nil
321
337
  reactor.run do
322
338
  connection = reactor.connect( host, port, TLSHandler, client_invalid_ssl_options )
@@ -39,16 +39,32 @@ describe Arachni::Reactor::Connection do
39
39
  end
40
40
  end
41
41
 
42
+ before :each do
43
+ @accept_q = Queue.new
44
+ @accepted = nil
45
+ end
46
+
42
47
  let(:unix_socket) { unix_connect( @unix_socket ) }
43
48
  let(:unix_server_socket) { unix_server( port_to_socket( Servers.available_port ) ) }
44
49
 
45
- let(:echo_client) { tcp_connect( @host, @port ) }
50
+ let(:echo_client) { tcp_socket }
46
51
  let(:echo_client_handler) { EchoClient.new }
47
52
 
48
53
  let(:peer_client_socket) { tcp_connect( host, port ) }
49
- let(:peer_server_socket) { tcp_server( host, port ) }
54
+ let(:peer_server_socket) do
55
+ s = tcp_server( host, port )
56
+ Thread.new do
57
+ begin
58
+ @accept_q << s.accept
59
+ rescue => e
60
+ ap e
61
+ end
62
+ end
63
+ s
64
+ end
65
+ let(:accepted) { @accepted ||= @accept_q.pop }
50
66
 
51
- let(:client_socket) { tcp_connect( host, port ) }
67
+ let(:client_socket) { tcp_socket }
52
68
  let(:server_socket) { tcp_server( host, port ) }
53
69
 
54
70
  let(:connection) { Handler.new }
@@ -25,7 +25,7 @@ describe Arachni::Reactor::Iterator do
25
25
  end
26
26
 
27
27
  context 'when the concurrency is' do
28
- context 0 do
28
+ context '0' do
29
29
  let(:concurrency) { 0 }
30
30
 
31
31
  it "raises #{ArgumentError}" do
@@ -35,7 +35,9 @@ describe Arachni::Reactor::Tasks::Delayed do
35
35
  time = Time.now
36
36
  task.call while called < 1
37
37
 
38
- (Time.now - time).round(2).should == 0.25
38
+ elapsed = (Time.now - time).round(2)
39
+ elapsed.should >= 0.25
40
+ elapsed.should < 0.30
39
41
  end
40
42
 
41
43
  it 'calls #done' do
@@ -33,7 +33,9 @@ describe Arachni::Reactor::Tasks::Periodic do
33
33
  time = Time.now
34
34
  task.call while called < 5
35
35
 
36
- (Time.now - time).round(2).should == 1.25
36
+ elapsed = (Time.now - time).round(2)
37
+ elapsed.should >= 1.25
38
+ elapsed.should < 1.35
37
39
  end
38
40
  end
39
41
 
@@ -25,14 +25,14 @@ describe Arachni::Reactor::Tasks do
25
25
  context 'when it includes the given task' do
26
26
  it 'returns true' do
27
27
  subject << task
28
- subject.should include task
28
+ expect(subject.include?( task )).to be_truthy
29
29
  end
30
30
  end
31
31
 
32
32
  context 'when it does not includes the given task' do
33
33
  it 'returns false' do
34
34
  subject << task
35
- subject.should_not include another_task
35
+ expect(subject.include?( another_task )).to be_falsey
36
36
  end
37
37
  end
38
38
  end
@@ -125,8 +125,8 @@ describe Arachni::Reactor::Tasks do
125
125
 
126
126
  subject.call
127
127
 
128
- called_one.should be_true
129
- called_two.should be_true
128
+ called_one.should be_truthy
129
+ called_two.should be_truthy
130
130
  end
131
131
 
132
132
  it 'returns self' do
@@ -17,6 +17,16 @@ def tcp_connect( host, port )
17
17
  TCPSocket.new( host, port )
18
18
  end
19
19
 
20
+ def tcp_socket
21
+ socket = Socket.new(
22
+ Socket::Constants::AF_INET,
23
+ Socket::Constants::SOCK_STREAM,
24
+ Socket::Constants::IPPROTO_IP
25
+ )
26
+ socket.do_not_reverse_lookup = true
27
+ socket
28
+ end
29
+
20
30
  def tcp_write( host, port, data )
21
31
  s = tcp_connect( host, port )
22
32
  s.write data
@@ -45,6 +55,10 @@ def tcp_server( host, port )
45
55
  TCPServer.new( host, port )
46
56
  end
47
57
 
58
+ def tcp_ssl_socket( host, port, options = {} )
59
+ convert_client_to_ssl( tcp_socket, options )
60
+ end
61
+
48
62
  def tcp_ssl_connect( host, port, options = {} )
49
63
  convert_client_to_ssl( tcp_connect( host, port ), options )
50
64
  end
@@ -100,7 +114,7 @@ def convert_server_to_ssl( server, options = {} )
100
114
  context.cert.version = 2
101
115
  context.cert.serial = 1
102
116
 
103
- context.cert.sign( context.key, OpenSSL::Digest::SHA1.new )
117
+ context.cert.sign( context.key, OpenSSL::Digest::SHA256.new )
104
118
  end
105
119
 
106
120
  OpenSSL::SSL::SSLServer.new( server, context )
@@ -26,7 +26,7 @@ class Servers
26
26
  return [host_for(name), port_for(name)] if server_info[:pid] && up?( name )
27
27
 
28
28
  server_info[:pid] = Process.spawn(
29
- 'ruby', RUNNER, server_info[:path], '-p', server_info[:port].to_s,
29
+ RbConfig.ruby, RUNNER, server_info[:path], '-p', server_info[:port].to_s,
30
30
  '-o', host_for( name )
31
31
  )
32
32
 
@@ -4,10 +4,10 @@ loop do
4
4
  Thread.new server.accept do |socket|
5
5
  begin
6
6
  loop do
7
- next if !(line = socket.gets)
8
- socket.write( line )
7
+ next if (data = socket.gets).to_s.empty?
8
+ socket.write( data )
9
9
  end
10
- rescue EOFError, Errno::EPIPE
10
+ rescue EOFError, Errno::EPIPE, Errno::ECONNRESET
11
11
  socket.close
12
12
  end
13
13
  end
@@ -12,10 +12,10 @@ loop do
12
12
  Thread.new do
13
13
  begin
14
14
  loop do
15
- next if (line = socket.gets).to_s.empty?
16
- socket.write( line )
15
+ next if (data = socket.gets).to_s.empty?
16
+ socket.write( data )
17
17
  end
18
- rescue EOFError, Errno::EPIPE
18
+ rescue EOFError, Errno::EPIPE, Errno::ECONNRESET
19
19
  socket.close
20
20
  end
21
21
  end
@@ -4,10 +4,10 @@ loop do
4
4
  Thread.new server.accept do |socket|
5
5
  begin
6
6
  loop do
7
- next if !(line = socket.gets)
8
- socket.write( line )
7
+ next if (data = socket.gets).to_s.empty?
8
+ socket.write( data )
9
9
  end
10
- rescue EOFError, Errno::EPIPE
10
+ rescue EOFError, Errno::EPIPE, Errno::ECONNRESET
11
11
  socket.close
12
12
  end
13
13
  end
@@ -12,10 +12,10 @@ loop do
12
12
  Thread.new do
13
13
  begin
14
14
  loop do
15
- next if (line = socket.gets).to_s.empty?
16
- socket.write( line )
15
+ next if (data = socket.gets).to_s.empty?
16
+ socket.write( data )
17
17
  end
18
- rescue EOFError, Errno::EPIPE
18
+ rescue EOFError, Errno::EPIPE, Errno::ECONNRESET
19
19
  socket.close
20
20
  end
21
21
  end