arachni-reactor 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.md +29 -0
  4. data/README.md +79 -0
  5. data/Rakefile +53 -0
  6. data/lib/arachni/reactor.rb +679 -0
  7. data/lib/arachni/reactor/connection.rb +302 -0
  8. data/lib/arachni/reactor/connection/callbacks.rb +73 -0
  9. data/lib/arachni/reactor/connection/error.rb +114 -0
  10. data/lib/arachni/reactor/connection/peer_info.rb +92 -0
  11. data/lib/arachni/reactor/connection/tls.rb +107 -0
  12. data/lib/arachni/reactor/global.rb +26 -0
  13. data/lib/arachni/reactor/iterator.rb +251 -0
  14. data/lib/arachni/reactor/queue.rb +91 -0
  15. data/lib/arachni/reactor/tasks.rb +107 -0
  16. data/lib/arachni/reactor/tasks/base.rb +59 -0
  17. data/lib/arachni/reactor/tasks/delayed.rb +35 -0
  18. data/lib/arachni/reactor/tasks/one_off.rb +30 -0
  19. data/lib/arachni/reactor/tasks/periodic.rb +60 -0
  20. data/lib/arachni/reactor/tasks/persistent.rb +31 -0
  21. data/lib/arachni/reactor/version.rb +15 -0
  22. data/spec/arachni/reactor/connection/tls_spec.rb +332 -0
  23. data/spec/arachni/reactor/connection_spec.rb +58 -0
  24. data/spec/arachni/reactor/iterator_spec.rb +203 -0
  25. data/spec/arachni/reactor/queue_spec.rb +91 -0
  26. data/spec/arachni/reactor/tasks/base.rb +8 -0
  27. data/spec/arachni/reactor/tasks/delayed_spec.rb +54 -0
  28. data/spec/arachni/reactor/tasks/one_off_spec.rb +51 -0
  29. data/spec/arachni/reactor/tasks/periodic_spec.rb +40 -0
  30. data/spec/arachni/reactor/tasks/persistent_spec.rb +39 -0
  31. data/spec/arachni/reactor/tasks_spec.rb +136 -0
  32. data/spec/arachni/reactor_spec.rb +20 -0
  33. data/spec/arachni/reactor_tls_spec.rb +20 -0
  34. data/spec/spec_helper.rb +16 -0
  35. data/spec/support/fixtures/handlers/echo_client.rb +34 -0
  36. data/spec/support/fixtures/handlers/echo_client_tls.rb +10 -0
  37. data/spec/support/fixtures/handlers/echo_server.rb +12 -0
  38. data/spec/support/fixtures/handlers/echo_server_tls.rb +8 -0
  39. data/spec/support/fixtures/pems/cacert.pem +37 -0
  40. data/spec/support/fixtures/pems/client/cert.pem +37 -0
  41. data/spec/support/fixtures/pems/client/foo-cert.pem +39 -0
  42. data/spec/support/fixtures/pems/client/foo-key.pem +51 -0
  43. data/spec/support/fixtures/pems/client/key.pem +51 -0
  44. data/spec/support/fixtures/pems/server/cert.pem +37 -0
  45. data/spec/support/fixtures/pems/server/key.pem +51 -0
  46. data/spec/support/helpers/paths.rb +23 -0
  47. data/spec/support/helpers/utilities.rb +117 -0
  48. data/spec/support/lib/server_option_parser.rb +29 -0
  49. data/spec/support/lib/servers.rb +133 -0
  50. data/spec/support/lib/servers/runner.rb +13 -0
  51. data/spec/support/servers/echo.rb +14 -0
  52. data/spec/support/servers/echo_tls.rb +22 -0
  53. data/spec/support/servers/echo_unix.rb +14 -0
  54. data/spec/support/servers/echo_unix_tls.rb +22 -0
  55. data/spec/support/shared/connection.rb +778 -0
  56. data/spec/support/shared/reactor.rb +785 -0
  57. data/spec/support/shared/task.rb +21 -0
  58. metadata +141 -0
@@ -0,0 +1,13 @@
1
+ require 'ap'
2
+ require_relative '../../../../lib/arachni/reactor'
3
+
4
+ Thread.abort_on_exception = true
5
+
6
+ Dir["#{File.expand_path(File.dirname(__FILE__) + '/../..' )}/**/*.rb"].each do |f|
7
+ next if f.include?( '/servers/' ) || f.include?( 'shared' )
8
+ require f
9
+ end
10
+
11
+ $options = ServerOptionParser.parse
12
+
13
+ load ARGV[0]
@@ -0,0 +1,14 @@
1
+ server = tcp_server( $options[:host], $options[:port] )
2
+
3
+ loop do
4
+ Thread.new server.accept do |socket|
5
+ begin
6
+ loop do
7
+ next if !(line = socket.gets)
8
+ socket.write( line )
9
+ end
10
+ rescue EOFError, Errno::EPIPE
11
+ socket.close
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ server = tcp_ssl_server( $options[:host], $options[:port] )
2
+
3
+ loop do
4
+ socket = nil
5
+ begin
6
+ socket = server.accept
7
+ rescue => e
8
+ # ap e
9
+ next
10
+ end
11
+
12
+ Thread.new do
13
+ begin
14
+ loop do
15
+ next if (line = socket.gets).to_s.empty?
16
+ socket.write( line )
17
+ end
18
+ rescue EOFError, Errno::EPIPE
19
+ socket.close
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ server = unix_server( port_to_socket( $options[:port] ) )
2
+
3
+ loop do
4
+ Thread.new server.accept do |socket|
5
+ begin
6
+ loop do
7
+ next if !(line = socket.gets)
8
+ socket.write( line )
9
+ end
10
+ rescue EOFError, Errno::EPIPE
11
+ socket.close
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ server = unix_ssl_server( port_to_socket( $options[:port] ) )
2
+
3
+ loop do
4
+ socket = nil
5
+ begin
6
+ socket = server.accept
7
+ rescue => e
8
+ # ap e
9
+ next
10
+ end
11
+
12
+ Thread.new do
13
+ begin
14
+ loop do
15
+ next if (line = socket.gets).to_s.empty?
16
+ socket.write( line )
17
+ end
18
+ rescue EOFError, Errno::EPIPE
19
+ socket.close
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,778 @@
1
+ shared_examples_for 'Arachni::Reactor::Connection' do
2
+ after(:each) do
3
+ next if !@reactor
4
+
5
+ if @reactor.running?
6
+ @reactor.stop
7
+ end
8
+
9
+ @reactor = nil
10
+ end
11
+
12
+ let(:host){ '127.0.0.1' }
13
+ let(:port){ Servers.available_port }
14
+ let(:reactor) { @reactor = Arachni::Reactor.new }
15
+ let(:block_size) { Arachni::Reactor::Connection::BLOCK_SIZE }
16
+ let(:data) { 'b' * 5 * block_size }
17
+ let(:configured) do
18
+ connection.reactor = reactor
19
+ connection.configure socket, role, server_handler
20
+ connection
21
+ end
22
+
23
+ # This needs to be put in a file of its own.
24
+ describe '::PeerInfo' do
25
+ describe '#peer_address_info' do
26
+ context 'when using an IP socket' do
27
+ let(:connection) { echo_client_handler }
28
+ let(:role) { :client }
29
+ let(:socket) { client_socket }
30
+
31
+ it 'returns IP address information' do
32
+ s = peer_server_socket
33
+ configured
34
+
35
+ Thread.new do
36
+ s = s.accept
37
+ end
38
+
39
+ IO.select( nil, [configured.socket] )
40
+
41
+ info = {}
42
+ info[:protocol], info[:port], info[:hostname], info[:ip_address] = s.to_io.addr
43
+ configured.peer_address_info.should == info
44
+
45
+ info = {}
46
+ info[:protocol], info[:port], info[:hostname], info[:ip_address] = s.to_io.addr(false)
47
+ configured.peer_address_info(false).should == info
48
+
49
+ info = {}
50
+ info[:protocol], info[:port], info[:hostname], info[:ip_address] = s.to_io.addr(true)
51
+ configured.peer_address_info(true).should == info
52
+ end
53
+ end
54
+
55
+ context 'when using UNIX-domain socket',
56
+ if: Arachni::Reactor.supports_unix_sockets? do
57
+
58
+ let(:connection) { echo_client_handler }
59
+ let(:role) { :client }
60
+ let(:socket) { unix_socket }
61
+
62
+ it 'returns socket information' do
63
+ configured
64
+
65
+ IO.select( nil, [configured.socket] )
66
+
67
+ info = {}
68
+ info[:protocol], info[:path] = 'AF_UNIX', socket.path
69
+ configured.peer_address_info.should == info
70
+ end
71
+ end
72
+ end
73
+
74
+ describe '#peer_hostname' do
75
+ let(:connection) { echo_client_handler }
76
+ let(:role) { :client }
77
+ let(:socket) { client_socket }
78
+
79
+ it 'returns the peer hostname' do
80
+ s = peer_server_socket
81
+ configured
82
+
83
+ Thread.new do
84
+ s = s.accept
85
+ end
86
+
87
+ IO.select( nil, [configured.socket] )
88
+
89
+ configured.peer_hostname.should == s.to_io.addr(true)[2]
90
+ end
91
+ end
92
+
93
+ describe '#peer_ip_address' do
94
+ let(:connection) { echo_client_handler }
95
+ let(:role) { :client }
96
+ let(:socket) { client_socket }
97
+
98
+ it 'returns the peer IP address' do
99
+ s = peer_server_socket
100
+ configured
101
+
102
+ Thread.new do
103
+ s = s.accept
104
+ end
105
+
106
+ IO.select( nil, [configured.socket] )
107
+
108
+ configured.peer_ip_address.should == s.to_io.addr[3]
109
+ end
110
+ end
111
+
112
+ describe '#peer_port' do
113
+ let(:connection) { echo_client_handler }
114
+ let(:role) { :client }
115
+ let(:socket) { client_socket }
116
+
117
+ it 'returns the peer IP address' do
118
+ s = peer_server_socket
119
+ configured
120
+
121
+ Thread.new do
122
+ s = s.accept
123
+ end
124
+
125
+ IO.select( nil, [configured.socket] )
126
+
127
+ configured.peer_port.should == s.to_io.addr[1]
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#configure' do
133
+ let(:socket) { client_socket }
134
+ let(:role) { :client }
135
+
136
+ it 'sets #socket' do
137
+ peer_server_socket
138
+ configured.socket.to_io.should == socket
139
+ end
140
+
141
+ it 'sets #role' do
142
+ peer_server_socket
143
+ configured.role.should == :client
144
+ end
145
+
146
+ it 'attaches it to the reactor' do
147
+ # Just to initialize it.
148
+ peer_server_socket
149
+
150
+ reactor.run_block do
151
+ reactor.attach configured
152
+
153
+ c_socket, c_connection = reactor.connections.first.to_a
154
+
155
+ c_socket.to_io.should == socket
156
+ c_connection.should == connection
157
+ end
158
+ end
159
+
160
+ it 'calls #on_connect' do
161
+ peer_server_socket
162
+ connection.should receive(:on_connect)
163
+ connection.reactor = reactor
164
+ connection.configure socket, role
165
+ end
166
+ end
167
+
168
+ describe '#unix?' do
169
+ context 'when using an IP socket' do
170
+ let(:connection) { echo_client_handler }
171
+ let(:role) { :client }
172
+ let(:socket) { client_socket }
173
+
174
+ it 'returns false' do
175
+ s = peer_server_socket
176
+ configured
177
+ configured.should_not be_unix
178
+ end
179
+ end
180
+
181
+ context 'when using UNIX-domain socket',
182
+ if: Arachni::Reactor.supports_unix_sockets? do
183
+
184
+ let(:connection) { echo_client_handler }
185
+ let(:role) { :client }
186
+ let(:socket) { unix_socket }
187
+
188
+ it 'returns true' do
189
+ s = peer_server_socket
190
+ configured
191
+ configured.should be_unix
192
+ end
193
+ end
194
+ end
195
+
196
+ describe '#inet?' do
197
+ context 'when using an IP socket' do
198
+ let(:connection) { echo_client_handler }
199
+ let(:role) { :client }
200
+ let(:socket) { client_socket }
201
+
202
+ it 'returns false' do
203
+ s = peer_server_socket
204
+ configured
205
+ configured.should be_inet
206
+ end
207
+ end
208
+
209
+ context 'when using UNIX-domain socket',
210
+ if: Arachni::Reactor.supports_unix_sockets? do
211
+
212
+ let(:connection) { echo_client_handler }
213
+ let(:role) { :client }
214
+ let(:socket) { unix_socket }
215
+
216
+ it 'returns false' do
217
+ s = peer_server_socket
218
+ configured
219
+ configured.should_not be_inet
220
+ end
221
+ end
222
+ end
223
+
224
+ describe '#to_io' do
225
+ context 'when the connection is a server listener' do
226
+ let(:role) { :server }
227
+
228
+ context 'when using an IP socket' do
229
+ let(:socket) { server_socket }
230
+
231
+ it 'returns TCPServer' do
232
+ reactor.run_in_thread
233
+ configured
234
+
235
+ configured.to_io.should be_kind_of TCPServer
236
+ end
237
+ end
238
+
239
+ context 'when using UNIX-domain socket',
240
+ if: Arachni::Reactor.supports_unix_sockets? do
241
+
242
+ let(:connection) { echo_client_handler }
243
+ let(:role) { :client }
244
+ let(:socket) { unix_server_socket }
245
+
246
+ it 'returns UNIXServer' do
247
+ peer_server_socket
248
+ configured.to_io.should be_instance_of UNIXServer
249
+ end
250
+ end
251
+ end
252
+
253
+ context 'when the connection is a server handler' do
254
+ let(:role) { :server }
255
+
256
+ context 'when using an IP socket' do
257
+ let(:socket) { server_socket }
258
+
259
+ it 'returns TCPSocket' do
260
+ reactor.run_in_thread
261
+ configured
262
+
263
+ Thread.new do
264
+ client = peer_client_socket
265
+ client.write( data )
266
+ end
267
+
268
+ IO.select [configured.socket]
269
+ configured.accept.to_io.should be_kind_of TCPSocket
270
+ end
271
+ end
272
+ end
273
+
274
+ context 'when the connection is a client' do
275
+ context 'when using an IP socket' do
276
+ let(:role) { :client }
277
+ let(:socket) { client_socket }
278
+
279
+ it 'returns TCPSocket' do
280
+ peer_server_socket
281
+ configured.to_io.should be_instance_of TCPSocket
282
+ end
283
+ end
284
+
285
+ context 'when using UNIX-domain socket',
286
+ if: Arachni::Reactor.supports_unix_sockets? do
287
+
288
+ let(:role) { :client }
289
+ let(:socket) { unix_socket }
290
+
291
+ it 'returns UNIXSocket' do
292
+ peer_server_socket
293
+ configured.to_io.should be_instance_of UNIXSocket
294
+ end
295
+ end
296
+ end
297
+ end
298
+
299
+ describe '#listener?' do
300
+ context 'when the connection is a server listener' do
301
+ let(:role) { :server }
302
+
303
+ context 'when using an IP socket' do
304
+ let(:socket) { server_socket }
305
+
306
+ it 'returns true' do
307
+ reactor.run_in_thread
308
+ configured
309
+
310
+ configured.should be_listener
311
+ end
312
+ end
313
+
314
+ context 'when using UNIX-domain socket',
315
+ if: Arachni::Reactor.supports_unix_sockets? do
316
+
317
+ let(:connection) { echo_client_handler }
318
+ let(:role) { :client }
319
+ let(:socket) { unix_server_socket }
320
+
321
+ it 'returns true' do
322
+ peer_server_socket
323
+ configured.should be_listener
324
+ end
325
+ end
326
+ end
327
+
328
+ context 'when the connection is a server handler' do
329
+ let(:role) { :server }
330
+
331
+ context 'when using an IP socket' do
332
+ let(:socket) { server_socket }
333
+
334
+ it 'returns false' do
335
+ reactor.run_in_thread
336
+ configured
337
+
338
+ Thread.new do
339
+ client = peer_client_socket
340
+ client.write( data )
341
+ end
342
+
343
+ IO.select [configured.socket]
344
+ configured.accept.should_not be_listener
345
+ end
346
+ end
347
+ end
348
+
349
+ context 'when the connection is a client' do
350
+ context 'when using an IP socket' do
351
+ let(:role) { :client }
352
+ let(:socket) { client_socket }
353
+
354
+ it 'returns false' do
355
+ peer_server_socket
356
+ configured.should_not be_listener
357
+ end
358
+ end
359
+
360
+ context 'when using UNIX-domain socket',
361
+ if: Arachni::Reactor.supports_unix_sockets? do
362
+
363
+ let(:role) { :client }
364
+ let(:socket) { unix_socket }
365
+
366
+ it 'returns false' do
367
+ peer_server_socket
368
+ configured.should_not be_listener
369
+ end
370
+ end
371
+ end
372
+ end
373
+
374
+ describe '#attach' do
375
+ let(:socket) { client_socket }
376
+ let(:role) { :client }
377
+
378
+ it 'attaches the connection to a Reactor' do
379
+ peer_server_socket
380
+ configured
381
+
382
+ reactor.run_in_thread
383
+
384
+ connection.attach( reactor ).should be_true
385
+ sleep 1
386
+
387
+ reactor.attached?( configured ).should be_true
388
+ end
389
+
390
+ it 'calls #on_attach' do
391
+ peer_server_socket
392
+ configured
393
+
394
+ reactor.run_in_thread
395
+
396
+ configured.should receive(:on_attach)
397
+ connection.attach reactor
398
+
399
+ sleep 1
400
+ end
401
+
402
+ context 'when the connection is already attached' do
403
+ context 'to the same Reactor' do
404
+ it 'does nothing' do
405
+ peer_server_socket
406
+ configured
407
+
408
+ reactor.run_in_thread
409
+
410
+ connection.attach reactor
411
+ sleep 0.1 while connection.detached?
412
+
413
+ connection.attach( reactor ).should be_false
414
+ end
415
+ end
416
+
417
+ context 'to a different Reactor' do
418
+ it 'detaches it first' do
419
+ peer_server_socket
420
+ configured
421
+
422
+ reactor.run_in_thread
423
+
424
+ connection.attach reactor
425
+ sleep 0.1 while connection.detached?
426
+
427
+ r = Arachni::Reactor.new
428
+ r.run_in_thread
429
+
430
+ configured.should receive(:on_detach)
431
+ connection.attach( r ).should be_true
432
+
433
+ sleep 2
434
+
435
+ r.attached?( configured ).should be_true
436
+ end
437
+ end
438
+ end
439
+ end
440
+
441
+ describe '#detach' do
442
+ let(:socket) { client_socket }
443
+ let(:role) { :client }
444
+
445
+ it 'detaches the connection from the reactor' do
446
+ peer_server_socket
447
+ configured
448
+
449
+ reactor.run_in_thread
450
+
451
+ connection.attach reactor
452
+ sleep 0.1 while !connection.attached?
453
+
454
+ connection.detach
455
+ sleep 0.1 while connection.attached?
456
+
457
+ reactor.attached?( configured ).should be_false
458
+ end
459
+
460
+ it 'calls #on_detach' do
461
+ peer_server_socket
462
+ configured
463
+
464
+ reactor.run_in_thread
465
+
466
+ connection.attach reactor
467
+ sleep 0.1 while !connection.attached?
468
+
469
+ configured.should receive(:on_detach)
470
+ connection.detach
471
+
472
+ sleep 0.1 while connection.attached?
473
+ end
474
+ end
475
+
476
+ describe '#write' do
477
+ let(:connection) { echo_client_handler }
478
+ let(:role) { :client }
479
+ let(:socket) { client_socket }
480
+
481
+ it 'appends the given data to the send-buffer' do
482
+ s = peer_server_socket
483
+ reactor.run_in_thread
484
+
485
+ configured
486
+
487
+ all_read = false
488
+ received = ''
489
+
490
+ t = Thread.new do
491
+ s = s.accept
492
+ received << s.read( data.size ) while received.size != data.size
493
+ all_read = true
494
+ end
495
+
496
+ configured.write data
497
+
498
+ # Wait for the reactor to update the buffer.
499
+ sleep 0.1 while !configured.has_outgoing_data?
500
+
501
+ while !all_read
502
+ IO.select( nil, [configured.socket], nil, 1 ) rescue IOError
503
+ next if configured._write != 0
504
+
505
+ IO.select( [configured.socket], nil, nil, 1 ) rescue IOError
506
+ end
507
+
508
+ t.join
509
+
510
+ received.should == data
511
+ end
512
+ end
513
+
514
+ describe '#accept' do
515
+ let(:socket) { server_socket }
516
+ let(:role) { :server }
517
+ let(:data) { "data\n" }
518
+
519
+ it 'accepts a new client connection' do
520
+ reactor.run_in_thread
521
+ configured
522
+
523
+ client = nil
524
+
525
+ Thread.new do
526
+ client = peer_client_socket
527
+ client.write( data )
528
+ end
529
+
530
+ IO.select [configured.socket]
531
+ server = configured.accept
532
+
533
+ server.should be_kind_of connection.class
534
+
535
+ IO.select [server.socket]
536
+
537
+ sleep 0.1 while !server.received_data
538
+ server.received_data.should == data
539
+
540
+ client.close
541
+ end
542
+ end
543
+
544
+ describe '#_read' do
545
+ context 'when the connection is a socket' do
546
+ let(:connection) { echo_client_handler }
547
+ let(:role) { :client }
548
+ let(:socket) { client_socket }
549
+
550
+ it "reads a maximum of #{Arachni::Reactor::Connection::BLOCK_SIZE} bytes at a time" do
551
+ s = peer_server_socket
552
+ configured
553
+
554
+ Thread.new do
555
+ s = s.accept
556
+ s.write data
557
+ s.flush
558
+ end
559
+
560
+ while configured.received_data.to_s.size != data.size
561
+ pre = configured.received_data.to_s.size
562
+ configured._read
563
+ (configured.received_data.to_s.size - pre).should <= block_size
564
+ end
565
+
566
+ configured.received_data.size.should == data.size
567
+ end
568
+
569
+ it 'passes the data to #on_read' do
570
+ s = peer_server_socket
571
+ configured
572
+
573
+ data = "test\n"
574
+
575
+ Thread.new do
576
+ s = s.accept
577
+ s.write data
578
+ s.flush
579
+ end
580
+
581
+ configured._read while !configured.received_data
582
+ configured.received_data.should == data
583
+ end
584
+ end
585
+
586
+ context 'when the connection is a server' do
587
+ let(:socket) { server_socket }
588
+ let(:role) { :server }
589
+ let(:data) { "data\n" }
590
+
591
+ it 'accepts a new client connection' do
592
+ configured
593
+ reactor.run_in_thread
594
+
595
+ client = nil
596
+
597
+ q = Queue.new
598
+ Thread.new do
599
+ client = peer_client_socket
600
+ q << client.write( data )
601
+ end
602
+
603
+ IO.select [configured.socket]
604
+ server = configured._read
605
+
606
+ server.should be_kind_of connection.class
607
+
608
+ sleep 0.1 while !server.received_data
609
+ server.received_data.should == data
610
+
611
+ client.close
612
+ end
613
+ end
614
+ end
615
+
616
+ describe '#_write' do
617
+ before :each do
618
+ reactor.run_in_thread
619
+ end
620
+
621
+ let(:connection) { echo_client_handler }
622
+ let(:role) { :client }
623
+ let(:socket) { echo_client }
624
+
625
+ it "consumes the write-buffer a maximum of #{Arachni::Reactor::Connection::BLOCK_SIZE} bytes at a time" do
626
+ configured.write data
627
+ sleep 0.1 while !configured.has_outgoing_data?
628
+
629
+ writes = 0
630
+ while configured.has_outgoing_data?
631
+ IO.select( nil, [configured.socket] )
632
+ if (written = configured._write) == 0
633
+ IO.select( [configured.socket], nil, nil, 1 )
634
+ next
635
+ end
636
+
637
+ written.should == block_size
638
+ writes += 1
639
+ end
640
+
641
+ writes.should > 1
642
+ end
643
+
644
+ it 'calls #on_write' do
645
+ configured.write data
646
+ sleep 0.1 while !configured.has_outgoing_data?
647
+
648
+ writes = 0
649
+ while configured.has_outgoing_data?
650
+
651
+ IO.select( nil, [configured.socket] )
652
+ if configured._write == 0
653
+ IO.select( [configured.socket] )
654
+ next
655
+ end
656
+
657
+ writes += 1
658
+ end
659
+
660
+ configured.on_write_count.should == writes
661
+ end
662
+
663
+ context 'when the buffer is entirely consumed' do
664
+ it 'calls #on_flush' do
665
+ configured.write data
666
+ sleep 0.1 while !configured.has_outgoing_data?
667
+
668
+ while configured.has_outgoing_data?
669
+ IO.select( nil, [configured.socket] )
670
+
671
+ if (written = configured._write) == 0
672
+ IO.select( [configured.socket] )
673
+ next
674
+ end
675
+
676
+ written.should == block_size
677
+ end
678
+
679
+ configured.called_on_flush.should be_true
680
+ end
681
+ end
682
+ end
683
+
684
+ describe '#has_outgoing_data?' do
685
+ let(:role) { :client }
686
+ let(:socket) { echo_client }
687
+
688
+ context 'when the send-buffer is not empty' do
689
+ it 'returns true' do
690
+ reactor.run_in_thread
691
+
692
+ configured.write 'test'
693
+ sleep 0.1 while !configured.has_outgoing_data?
694
+
695
+ configured.has_outgoing_data?.should be_true
696
+ end
697
+ end
698
+
699
+ context 'when the send-buffer is empty' do
700
+ it 'returns false' do
701
+ configured.has_outgoing_data?.should be_false
702
+ end
703
+ end
704
+ end
705
+
706
+ describe '#closed?' do
707
+ let(:role) { :client }
708
+ let(:socket) { echo_client }
709
+
710
+ context 'when the connection has been closed' do
711
+ it 'returns true' do
712
+ reactor.run do
713
+ configured.close
714
+ end
715
+
716
+ configured.should be_closed
717
+ end
718
+ end
719
+
720
+ context 'when the send-buffer is empty' do
721
+ it 'returns false' do
722
+ configured.should_not be_closed
723
+ end
724
+ end
725
+ end
726
+
727
+ describe '#close_without_callback' do
728
+ let(:role) { :client }
729
+ let(:socket) { echo_client }
730
+
731
+ it 'closes the #socket' do
732
+ reactor.run_in_thread
733
+ configured.socket.should receive(:close)
734
+ configured.close_without_callback
735
+ end
736
+
737
+ it 'detaches the connection from the reactor' do
738
+ configured
739
+
740
+ reactor.run_block do
741
+ reactor.attach configured
742
+ reactor.connections.should be_any
743
+ configured.close_without_callback
744
+ reactor.connections.should be_empty
745
+ end
746
+ end
747
+
748
+ it 'does not call #on_close' do
749
+ reactor.run_in_thread
750
+ configured.should_not receive(:on_close)
751
+ configured.close_without_callback
752
+ end
753
+ end
754
+
755
+ describe '#close' do
756
+ let(:role) { :client }
757
+ let(:socket) { echo_client }
758
+
759
+ before(:each) { reactor.run_in_thread }
760
+
761
+ it 'calls #close_without_callback' do
762
+ configured.should receive(:close_without_callback)
763
+ configured.close
764
+ end
765
+
766
+ it 'calls #on_close' do
767
+ configured.should receive(:on_close)
768
+ configured.close
769
+ end
770
+
771
+ context 'when a reason is given' do
772
+ it 'is passed to #on_close' do
773
+ configured.should receive(:on_close).with(:my_reason)
774
+ configured.close :my_reason
775
+ end
776
+ end
777
+ end
778
+ end