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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f7671f97abb22c56a6488378023cd84ad92a8f30
4
- data.tar.gz: ffd8c0513e0c41d4483249faaeeea3d9eea5aa21
2
+ SHA256:
3
+ metadata.gz: ef5b5d0eb1a76745be3671829841cbd1c9780d9c731c12e9933bafadf2c622ff
4
+ data.tar.gz: 0e2a91dc4a9159042ce590bd6c1a09ae61cd660ad18aa9a900a19fe04d2a599d
5
5
  SHA512:
6
- metadata.gz: 5678c3dd7c6e90aa2061f06c892a4889538baee925550db1f5a46fc3ff232a5c66849ae1fef8c24fe6ae06d1e066dc8bfc2570b6f996dfe89ce33effd6d8e86c
7
- data.tar.gz: 8c5ebfd3235211408111216a1dbf942266354f085e14cd629ae56eda7ddb91126b9b4ccbf3705aa2732cf28a45d45598a2aaeea7eabad9af08c1486abd5b26f5
6
+ metadata.gz: 314fb418807020a5d9aed021e6bb55063d343ea514ff5b06df75f6b0c894ac3c4e53c25fade813313205e4326841c391e441c2432ad758c6c0142f412cdf85e7
7
+ data.tar.gz: 23a506068c6b7b8c529b3c8d4500d709fb05f8fd8e8c1b40848bf08719104b684602a4816a77a5e79c5515e51dc3a1a367c1d132754ae3d9bc5e8c8f446c6b69
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # ChangeLog
2
2
 
3
+ ## Version 0.1.3
4
+
5
+ - `Reactor` -- Reduced object allocations.
6
+ - `Connection::TLS#_read` -- Handle `OpenSSL::SSL::SSLErrorWaitReadable`.
7
+
8
+ ## Version 0.1.2
9
+
10
+ - `Reactor` -- Reduced object allocations.
11
+ - `Connection::Error.translate` -- Handle `Errno::ENOTSOCK` errors.
12
+
13
+ ## Version 0.1.1
14
+
15
+ - `Connection`
16
+ - `#_write` -- Use `String#byteslice` instead of `String#slice` and
17
+ `String#bytesize` instead of `String#size`.
18
+ - `Error.translate` -- Translate `Errno::ECONNABORTED` as `Error::Reset`.
19
+
20
+ ## Version 0.1.0
21
+
22
+ - Cleaned up connection handling structure for JRuby support.
23
+ - `Connection`
24
+ - `PeerInfo` now not included by default and only available for servers.
25
+
3
26
  ## Version 0.1.0.beta5 _(September 4, 2014)_
4
27
 
5
28
  - `Tasks::OneOff#call`: Ensure that the task is marked as done even if an
data/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # License
2
2
 
3
- Copyright (C) 2014, Tasos Laskos <tasos.laskos@gmail.com>
3
+ Copyright (C) 2014-2017, Sarosys LLC <http://www.sarosys.com/>
4
4
  All rights reserved.
5
5
 
6
6
  Redistribution and use in source and binary forms, with or without modification,
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <table>
4
4
  <tr>
5
5
  <th>Version</th>
6
- <td>0.1.0.beta5</td>
6
+ <td>0.1.2</td>
7
7
  </tr>
8
8
  <tr>
9
9
  <th>Github page</th>
@@ -23,7 +23,7 @@
23
23
  </tr>
24
24
  <tr>
25
25
  <th>Copyright</th>
26
- <td>2014</td>
26
+ <td>2014-2017 <a href="http://www.sarosys.com">Sarosys LLC</a></td>
27
27
  </tr>
28
28
  <tr>
29
29
  <th>License</th>
@@ -51,7 +51,7 @@ on network connections -- and less so on generic tasks.
51
51
  - Rubies:
52
52
  - MRI >= 1.9
53
53
  - Rubinius
54
- - JRuby (Without OpenSSL support)
54
+ - JRuby
55
55
  - Operating Systems:
56
56
  - Linux
57
57
  - OSX
@@ -63,7 +63,7 @@ For examples please see the `examples/` directory.
63
63
 
64
64
  ## Installation
65
65
 
66
- gem install arachni-reactor --pre
66
+ gem install arachni-reactor
67
67
 
68
68
  ## Running the Specs
69
69
 
@@ -24,7 +24,7 @@ class Error < Arachni::Reactor::Error
24
24
  # @param [Block] block Block to run.
25
25
  def translate( &block )
26
26
  block.call
27
- rescue IOError, Errno::ENOTCONN => e
27
+ rescue IOError, Errno::ENOTCONN, Errno::ENOTSOCK => e
28
28
  raise_with_proper_backtrace( e, Closed )
29
29
  rescue SocketError, Errno::ENOENT => e
30
30
  raise_with_proper_backtrace( e, HostNotFound )
@@ -35,7 +35,7 @@ class Error < Arachni::Reactor::Error
35
35
  # non-existent server.
36
36
  Errno::EADDRINUSE => e
37
37
  raise_with_proper_backtrace( e, Refused )
38
- rescue Errno::ECONNRESET => e
38
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED => e
39
39
  raise_with_proper_backtrace( e, Reset )
40
40
  rescue Errno::EACCES => e
41
41
  raise_with_proper_backtrace( e, Permission )
@@ -51,7 +51,7 @@ class Error < Arachni::Reactor::Error
51
51
  # aren't any specific exceptions for these.
52
52
  #
53
53
  # Why make things easy and clean, right?
54
- rescue OpenSSL::OpenSSLError => e
54
+ rescue OpenSSL::SSL::SSLError, OpenSSL::OpenSSLError => e
55
55
  raise_with_proper_backtrace( e, SSL )
56
56
  end
57
57
 
@@ -62,17 +62,79 @@ module TLS
62
62
  @socket = OpenSSL::SSL::SSLSocket.new( @socket, @ssl_context )
63
63
  @socket.sync_close = true
64
64
 
65
- begin
66
- @socket.connect_nonblock
67
- rescue IO::WaitReadable, IO::WaitWritable
68
- end
65
+ # We've switched to SSL, a connection needs to be re-established
66
+ # via the SSL handshake.
67
+ @connected = false
68
+
69
+ _connect if unix?
69
70
  end
70
71
 
71
72
  @socket
72
73
  end
73
74
 
75
+ # Performs an SSL handshake in addition to a plaintext connect operation.
76
+ #
77
+ # @private
78
+ def _connect
79
+ return if @ssl_connected
80
+
81
+ Error.translate do
82
+ @plaintext_connected ||= super
83
+ return if !@plaintext_connected
84
+
85
+ # Mark the connection as not connected due to the pending SSL handshake.
86
+ @connected = false
87
+
88
+ @socket.connect_nonblock
89
+ @ssl_connected = @connected = true
90
+ end
91
+ rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
92
+ rescue Error => e
93
+ close e
94
+ end
95
+
96
+ # First checks if there's a pending SSL #accept operation when this
97
+ # connection is a server handler which has been passed an accepted
98
+ # plaintext connection.
99
+ #
100
+ # @private
101
+ def _write( *args )
102
+ return ssl_accept if accept?
103
+
104
+ super( *args )
105
+ end
106
+
107
+ # First checks if there's a pending SSL #accept operation when this
108
+ # connection is a server handler which has been passed an accepted
109
+ # plaintext connection.
110
+ #
111
+ # @private
112
+ def _read
113
+ return ssl_accept if accept?
114
+
115
+ super
116
+ rescue OpenSSL::SSL::SSLErrorWaitReadable
117
+ end
118
+
74
119
  private
75
120
 
121
+ def ssl_accept
122
+ Error.translate do
123
+ @accepted = !!@socket.accept_nonblock
124
+ end
125
+ rescue IO::WaitReadable, IO::WaitWritable
126
+ rescue Error => e
127
+ close e
128
+ false
129
+ end
130
+
131
+ def accept?
132
+ return false if @accepted
133
+ return false if role != :server || !@socket.is_a?( OpenSSL::SSL::SSLSocket )
134
+
135
+ true
136
+ end
137
+
76
138
  # Accepts a new SSL client connection.
77
139
  #
78
140
  # @return [OpenSSL::SSL::SSLSocket, nil]
@@ -81,26 +143,20 @@ module TLS
81
143
  #
82
144
  # @private
83
145
  def socket_accept
84
- socket = nil
85
- begin
146
+ Error.translate do
86
147
  socket = to_io.accept_nonblock
87
- rescue IO::WaitReadable, IO::WaitWritable
88
- return
89
- end
90
-
91
- socket = OpenSSL::SSL::SSLSocket.new(
92
- socket,
93
- @ssl_context
94
- )
95
- socket.sync_close = true
96
148
 
97
- begin
98
- socket.accept_nonblock
99
- rescue IO::WaitReadable, IO::WaitWritable
149
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(
150
+ socket,
151
+ @ssl_context
152
+ )
153
+ ssl_socket.sync_close = true
154
+ ssl.accept if @start_immediately
155
+ ssl_socket
100
156
  end
101
-
102
- socket
103
- rescue OpenSSL::SSL::SSLError
157
+ rescue IO::WaitReadable, IO::WaitWritable
158
+ rescue Error => e
159
+ close e
104
160
  end
105
161
 
106
162
  end
@@ -7,8 +7,8 @@
7
7
  =end
8
8
 
9
9
  require_relative 'connection/error'
10
- require_relative 'connection/peer_info'
11
10
  require_relative 'connection/callbacks'
11
+ require_relative 'connection/peer_info'
12
12
  require_relative 'connection/tls'
13
13
 
14
14
  module Arachni
@@ -16,7 +16,6 @@ class Reactor
16
16
 
17
17
  # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
18
18
  class Connection
19
- include PeerInfo
20
19
  include Callbacks
21
20
 
22
21
  # Maximum amount of data to be written or read at a time.
@@ -42,17 +41,21 @@ class Connection
42
41
  # `true` when using a UNIX-domain socket, `nil` if no {#socket} is
43
42
  # available, `false` otherwise.
44
43
  def unix?
44
+ return @is_unix if !@is_unix.nil?
45
45
  return if !to_io
46
46
  return false if !Arachni::Reactor.supports_unix_sockets?
47
- to_io.is_a?( UNIXServer ) || to_io.is_a?( UNIXSocket )
47
+
48
+ @is_unix = to_io.is_a?( UNIXServer ) || to_io.is_a?( UNIXSocket )
48
49
  end
49
50
 
50
51
  # @return [Bool]
51
52
  # `true` when using an Internet socket, `nil` if no {#socket} is
52
53
  # available, `false` otherwise.
53
54
  def inet?
55
+ return @is_inet if !@is_inet.nil?
54
56
  return if !to_io
55
- to_io.is_a?( TCPServer ) || to_io.is_a?( TCPSocket ) || to_io.is_a?( Socket )
57
+
58
+ @is_inet = to_io.is_a?( TCPServer ) || to_io.is_a?( TCPSocket ) || to_io.is_a?( Socket )
56
59
  end
57
60
 
58
61
  # @return [IO, nil]
@@ -65,8 +68,10 @@ class Connection
65
68
  # @return [Bool]
66
69
  # `true` if the connection is a server listener.
67
70
  def listener?
71
+ return @is_listener if !@is_listener.nil?
68
72
  return if !to_io
69
- to_io.is_a?( TCPServer ) || (unix? && to_io.is_a?( UNIXServer ))
73
+
74
+ @is_listener = to_io.is_a?( TCPServer ) || (unix? && to_io.is_a?( UNIXServer ))
70
75
  end
71
76
 
72
77
  # @note The data will be buffered and sent in future {Reactor} ticks.
@@ -176,48 +181,67 @@ class Connection
176
181
  nil
177
182
  end
178
183
 
179
- # @note Will call {#on_write} every time any of the buffer is consumed,
180
- # can be multiple times when performing partial writes.
181
- # @note Will call {#on_flush} once all of the buffer has been consumed.
182
- #
183
- # Processes a `write` event for this connection.
184
+ # Accepts a new client connection.
184
185
  #
185
- # Consumes and writes {BLOCK_SIZE} amount of data from the the beginning of
186
- # the {#write} buffer to the socket.
186
+ # @return [Connection, nil]
187
+ # New connection or `nil` if the socket isn't ready to accept new
188
+ # connections yet.
187
189
  #
188
- # @return [Integer]
189
- # Amount of the buffer consumed.
190
+ # @private
191
+ def accept
192
+ return if !(accepted = socket_accept)
193
+
194
+ connection = @server_handler.call
195
+ connection.configure socket: accepted, role: :server
196
+ @reactor.attach connection
197
+ connection
198
+ end
199
+
200
+ # @param [Socket] socket
201
+ # Ruby `Socket` associated with this connection.
202
+ # @param [Symbol] role
203
+ # `:server` or `:client`.
204
+ # @param [Block] server_handler
205
+ # Block that generates a handler as specified in {Reactor#listen}.
190
206
  #
191
207
  # @private
192
- def _write
193
- chunk = write_buffer.slice( 0, BLOCK_SIZE )
194
- total_written = 0
208
+ def configure( options = {} )
209
+ @socket = options[:socket]
210
+ @role = options[:role]
211
+ @host = options[:host]
212
+ @port = options[:port]
213
+ @server_handler = options[:server_handler]
214
+
215
+ # If we're a server without a handler then we're an accepted connection.
216
+ if unix? || role == :server
217
+ @connected = true
218
+ on_connect
219
+ end
195
220
 
196
- begin
197
- Error.translate do
198
- # Send out the buffer, **all** of it, or at least try to.
199
- loop do
200
- total_written += written = @socket.write_nonblock( chunk )
201
- write_buffer.slice!( 0, written )
221
+ nil
222
+ end
202
223
 
203
- # Call #on_write every time any of the buffer is consumed.
204
- on_write
224
+ def connected?
225
+ !!@connected
226
+ end
205
227
 
206
- break if written == chunk.size
207
- chunk.slice!( 0, written )
208
- end
209
- end
228
+ # @private
229
+ def _connect
230
+ return true if unix? || connected?
210
231
 
211
- # Not ready to read or write yet, we'll catch it on future Reactor ticks.
212
- rescue IO::WaitReadable, IO::WaitWritable
232
+ begin
233
+ Error.translate do
234
+ socket.connect_nonblock( Socket.sockaddr_in( @port, @host ) )
235
+ end
236
+ # Already connected. :)
237
+ rescue Errno::EISCONN, Errno::EALREADY
213
238
  end
214
239
 
215
- if write_buffer.empty?
216
- @socket.flush
217
- on_flush
218
- end
240
+ @connected = true
241
+ on_connect
219
242
 
220
- total_written
243
+ true
244
+ rescue IO::WaitReadable, IO::WaitWritable, Errno::EINPROGRESS
221
245
  rescue Error => e
222
246
  close e
223
247
  end
@@ -230,7 +254,8 @@ class Connection
230
254
  #
231
255
  # @private
232
256
  def _read
233
- return accept if listener?
257
+ return _connect if !listener? && !connected?
258
+ return accept if listener?
234
259
 
235
260
  Error.translate do
236
261
  on_read @socket.read_nonblock( BLOCK_SIZE )
@@ -242,38 +267,52 @@ class Connection
242
267
  close e
243
268
  end
244
269
 
245
- # Accepts a new client connection.
270
+ # @note Will call {#on_write} every time any of the buffer is consumed,
271
+ # can be multiple times when performing partial writes.
272
+ # @note Will call {#on_flush} once all of the buffer has been consumed.
246
273
  #
247
- # @return [Connection, nil]
248
- # New connection or `nil` if the socket isn't ready to accept new
249
- # connections yet.
274
+ # Processes a `write` event for this connection.
275
+ #
276
+ # Consumes and writes {BLOCK_SIZE} amount of data from the the beginning of
277
+ # the {#write} buffer to the socket.
278
+ #
279
+ # @return [Integer]
280
+ # Amount of the buffer consumed.
250
281
  #
251
282
  # @private
252
- def accept
253
- return if !(accepted = socket_accept)
283
+ def _write
284
+ return _connect if !connected?
254
285
 
255
- connection = @server_handler.call
256
- connection.configure accepted, :server
257
- @reactor.attach connection
258
- connection
259
- end
286
+ chunk = write_buffer.byteslice( 0, BLOCK_SIZE )
287
+ total_written = 0
260
288
 
261
- # @param [Socket] socket
262
- # Ruby `Socket` associated with this connection.
263
- # @param [Symbol] role
264
- # `:server` or `:client`.
265
- # @param [Block] server_handler
266
- # Block that generates a handler as specified in {Reactor#listen}.
267
- #
268
- # @private
269
- def configure( socket, role, server_handler = nil )
270
- @socket = socket
271
- @role = role
272
- @server_handler = server_handler
289
+ begin
290
+ Error.translate do
291
+ # Send out the chunk, **all** of it, or at least try to.
292
+ loop do
293
+ total_written += written = @socket.write_nonblock( chunk )
294
+ @write_buffer = @write_buffer.byteslice( written..-1 )
273
295
 
274
- on_connect
296
+ # Call #on_write every time any of the buffer is consumed.
297
+ on_write
275
298
 
276
- nil
299
+ break if written == chunk.bytesize
300
+ chunk = chunk.byteslice( written..-1 )
301
+ end
302
+ end
303
+
304
+ # Not ready to read or write yet, we'll catch it on future Reactor ticks.
305
+ rescue IO::WaitReadable, IO::WaitWritable
306
+ end
307
+
308
+ if write_buffer.empty?
309
+ @socket.flush
310
+ on_flush
311
+ end
312
+
313
+ total_written
314
+ rescue Error => e
315
+ close e
277
316
  end
278
317
 
279
318
  private
@@ -9,7 +9,7 @@
9
9
  module Arachni
10
10
  class Reactor
11
11
 
12
- VERSION = '0.1.0.beta5'
12
+ VERSION = '0.1.3'
13
13
 
14
14
  end
15
15
  end