raktr 0.0.1

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 +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE.md +29 -0
  4. data/README.md +77 -0
  5. data/Rakefile +53 -0
  6. data/lib/raktr/connection/callbacks.rb +71 -0
  7. data/lib/raktr/connection/error.rb +120 -0
  8. data/lib/raktr/connection/peer_info.rb +90 -0
  9. data/lib/raktr/connection/tls.rb +164 -0
  10. data/lib/raktr/connection.rb +339 -0
  11. data/lib/raktr/global.rb +24 -0
  12. data/lib/raktr/iterator.rb +249 -0
  13. data/lib/raktr/queue.rb +89 -0
  14. data/lib/raktr/tasks/base.rb +57 -0
  15. data/lib/raktr/tasks/delayed.rb +33 -0
  16. data/lib/raktr/tasks/one_off.rb +30 -0
  17. data/lib/raktr/tasks/periodic.rb +58 -0
  18. data/lib/raktr/tasks/persistent.rb +29 -0
  19. data/lib/raktr/tasks.rb +105 -0
  20. data/lib/raktr/version.rb +13 -0
  21. data/lib/raktr.rb +707 -0
  22. data/spec/raktr/connection/tls_spec.rb +348 -0
  23. data/spec/raktr/connection_spec.rb +74 -0
  24. data/spec/raktr/iterator_spec.rb +203 -0
  25. data/spec/raktr/queue_spec.rb +91 -0
  26. data/spec/raktr/tasks/base.rb +8 -0
  27. data/spec/raktr/tasks/delayed_spec.rb +71 -0
  28. data/spec/raktr/tasks/one_off_spec.rb +66 -0
  29. data/spec/raktr/tasks/periodic_spec.rb +57 -0
  30. data/spec/raktr/tasks/persistent_spec.rb +54 -0
  31. data/spec/raktr/tasks_spec.rb +155 -0
  32. data/spec/raktr_spec.rb +20 -0
  33. data/spec/raktr_tls_spec.rb +20 -0
  34. data/spec/spec_helper.rb +17 -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 +135 -0
  48. data/spec/support/lib/server_option_parser.rb +29 -0
  49. data/spec/support/lib/servers/runner.rb +13 -0
  50. data/spec/support/lib/servers.rb +133 -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 +696 -0
  56. data/spec/support/shared/raktr.rb +834 -0
  57. data/spec/support/shared/task.rb +21 -0
  58. metadata +140 -0
@@ -0,0 +1,834 @@
1
+ shared_examples_for 'Raktr' do
2
+ after(:each) do
3
+ @socket.close if @socket
4
+ @socket = nil
5
+
6
+ next if !@raktr
7
+
8
+ if @raktr.running?
9
+ @raktr.stop
10
+ end
11
+
12
+ @raktr = nil
13
+ end
14
+
15
+ klass = Raktr
16
+
17
+ subject { @raktr ||= klass.new }
18
+ let(:raktr) { subject }
19
+ let(:data) { ('blah' * 999999) + "\n\n" }
20
+
21
+ describe '.global' do
22
+ it 'returns a Reactor' do
23
+ klass.global.should be_kind_of klass
24
+ end
25
+
26
+ it 'caches the instance' do
27
+ global = klass.global
28
+ klass.global.should == global
29
+ end
30
+ end
31
+
32
+ describe '.stop' do
33
+ it 'stops the global reactor' do
34
+ global = klass.global
35
+ klass.global.run_in_thread
36
+ klass.stop
37
+
38
+ global.wait
39
+ end
40
+
41
+ it 'destroys the global instance' do
42
+ global = klass.global
43
+ klass.stop
44
+
45
+ klass.object_id.should_not == global.object_id
46
+ end
47
+ end
48
+
49
+ describe '#initialize' do
50
+ describe :max_tick_interval do
51
+ it 'sets the maximum amount of time for each loop interval'
52
+ end
53
+
54
+ describe :select_timeout do
55
+ it 'sets the max waiting time for socket activity'
56
+ end
57
+ end
58
+
59
+ describe '#create_iterator' do
60
+ let(:iterator) { subject.create_iterator( 1..10 ) }
61
+
62
+ it 'creates a new Iterator' do
63
+ iterator.should be_kind_of klass::Iterator
64
+ end
65
+
66
+ it 'assigns this Reactor as the scheduler' do
67
+ iterator.raktr.should == subject
68
+ end
69
+ end
70
+
71
+ describe '#create_queue' do
72
+ let(:queue) { subject.create_queue }
73
+
74
+ it 'creates a new Queue' do
75
+ queue.should be_kind_of klass::Queue
76
+ end
77
+
78
+ it 'assigns this Reactor as the scheduler' do
79
+ queue.raktr.should == subject
80
+ end
81
+ end
82
+
83
+ describe '#ticks' do
84
+ context 'when the reactor is' do
85
+ context 'not running' do
86
+ it 'returns 0' do
87
+ subject.ticks.should == 0
88
+ end
89
+ end
90
+
91
+ context 'running' do
92
+ it 'returns the amount of loop iterations' do
93
+ run_reactor_in_thread
94
+ sleep 1
95
+ subject.ticks.should > 1
96
+ end
97
+ end
98
+
99
+ context 'stopped' do
100
+ it 'sets it to 0' do
101
+ run_reactor_in_thread
102
+ sleep 1
103
+ subject.stop
104
+ sleep 0.1 while subject.running?
105
+
106
+ subject.ticks.should == 0
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#run' do
113
+ it 'runs the reactor loop' do
114
+ run_reactor_in_thread
115
+ sleep 1
116
+ subject.ticks.should > 0
117
+ end
118
+
119
+ context 'when a block is given' do
120
+ it 'is called ASAP' do
121
+ subject.run do
122
+ subject.should be_running
123
+ subject.ticks.should == 0
124
+ subject.stop
125
+ end
126
+ end
127
+ end
128
+
129
+ context 'when already running' do
130
+ it "raises #{klass::Error::AlreadyRunning}" do
131
+ subject.run_in_thread
132
+ expect { subject.run }.to raise_error klass::Error::AlreadyRunning
133
+ end
134
+ end
135
+ end
136
+
137
+ describe '#run_in_thread' do
138
+ it 'runs the Reactor in a Thread' do
139
+ thread = subject.run_in_thread
140
+ subject.should be_running
141
+ thread.should_not == Thread.current
142
+ subject.thread.should == thread
143
+ end
144
+
145
+ context 'when already running' do
146
+ it "raises #{klass::Error::AlreadyRunning}" do
147
+ subject.run_in_thread
148
+ expect { subject.run_in_thread }.to raise_error klass::Error::AlreadyRunning
149
+ end
150
+ end
151
+ end
152
+
153
+ describe '#run_block' do
154
+ it 'runs the reactor loop just for the given block' do
155
+ running = false
156
+ subject.run_block do
157
+ running = subject.running?
158
+ end
159
+
160
+ subject.should_not be_running
161
+ running.should be_truthy
162
+ end
163
+
164
+ context 'when no block is given' do
165
+ it "raises #{ArgumentError}" do
166
+ expect { subject.run_block }.to raise_error ArgumentError
167
+ end
168
+ end
169
+
170
+ context 'when already running' do
171
+ it "raises #{klass::Error::AlreadyRunning}" do
172
+ run_reactor_in_thread
173
+ expect { subject.run_block{} }.to raise_error klass::Error::AlreadyRunning
174
+ end
175
+ end
176
+ end
177
+
178
+ describe '#wait' do
179
+ it 'waits for the reactor to stop running' do
180
+ subject.run_in_thread
181
+
182
+ start = Time.now
183
+ subject.delay 2 do
184
+ subject.stop
185
+ end
186
+ subject.wait
187
+
188
+ subject.should_not be_running
189
+ (Time.now - start).to_i.should == 2
190
+ end
191
+
192
+ context 'when the reactor is not running' do
193
+ it "raises #{klass::Error::NotRunning}" do
194
+ expect do
195
+ subject.wait{}
196
+ end.to raise_error klass::Error::NotRunning
197
+ end
198
+ end
199
+ end
200
+
201
+ describe '#on_error' do
202
+ it 'sets a task to be passed raised exceptions' do
203
+ run_reactor_in_thread
204
+
205
+ e = nil
206
+ subject.on_error do |_, error|
207
+ e = error
208
+ end
209
+
210
+ subject.next_tick do
211
+ raise
212
+ end
213
+
214
+ sleep 0.1 while !e
215
+
216
+ e.should be_kind_of RuntimeError
217
+ end
218
+
219
+ context 'when the reactor is not running' do
220
+ it "raises #{klass::Error::NotRunning}" do
221
+ expect do
222
+ subject.next_tick{}
223
+ end.to raise_error klass::Error::NotRunning
224
+ end
225
+ end
226
+ end
227
+
228
+ describe '#on_shutdown' do
229
+ it 'calls the given blocks during shutdown' do
230
+ subject.run_in_thread
231
+
232
+ count = 0
233
+ 2.times do
234
+ subject.on_shutdown do
235
+ count += 1
236
+ end
237
+ end
238
+
239
+ sleep 1
240
+
241
+ count.should == 0
242
+
243
+ subject.stop
244
+ subject.wait
245
+
246
+ count.should == 2
247
+ end
248
+
249
+ context 'when the reactor is not running' do
250
+ it "raises #{klass::Error::NotRunning}" do
251
+ expect do
252
+ subject.on_shutdown{}
253
+ end.to raise_error klass::Error::NotRunning
254
+ end
255
+ end
256
+ end
257
+
258
+ describe '#on_tick' do
259
+ it "schedules a task to be run at each tick in the #{klass}#thread" do
260
+ counted_ticks = 0
261
+ reactor_thread = nil
262
+
263
+ thread = run_reactor_in_thread
264
+
265
+ ticks = []
266
+ subject.on_tick do
267
+ reactor_thread = Thread.current
268
+ ticks << subject.ticks
269
+ end
270
+
271
+ sleep 1
272
+
273
+ # Logged ticks should be sequential.
274
+ ticks.size.times do |i|
275
+ next if !ticks[i+1]
276
+
277
+ (ticks[i+1] - ticks[i]).should == 1
278
+ end
279
+
280
+ reactor_thread.should be_kind_of Thread
281
+ reactor_thread.should_not == Thread.current
282
+ thread.should == reactor_thread
283
+ end
284
+
285
+ context 'when the reactor is not running' do
286
+ it "raises #{klass::Error::NotRunning}" do
287
+ expect do
288
+ subject.on_tick{}
289
+ end.to raise_error klass::Error::NotRunning
290
+ end
291
+ end
292
+ end
293
+
294
+ describe '#schedule' do
295
+ context 'when the reactor is running' do
296
+ context 'in the same thread' do
297
+ it 'calls the block right away' do
298
+ subject.run_block do
299
+ out_tick = subject.ticks
300
+ in_tick = nil
301
+
302
+ subject.schedule do
303
+ in_tick = subject.ticks
304
+ end
305
+
306
+ out_tick.should == in_tick
307
+ end
308
+ end
309
+ end
310
+
311
+ context 'in a different thread' do
312
+ it 'calls the block at the next tick' do
313
+ t = run_reactor_in_thread
314
+
315
+ subject.schedule do
316
+ subject.should be_in_same_thread
317
+ subject.stop
318
+ end
319
+ t.join
320
+ end
321
+ end
322
+ end
323
+
324
+ context 'when the reactor is not running' do
325
+ it "raises #{klass::Error::NotRunning}" do
326
+ expect do
327
+ subject.schedule{}
328
+ end.to raise_error klass::Error::NotRunning
329
+ end
330
+ end
331
+ end
332
+
333
+ describe '#next_tick' do
334
+ it "schedules a task to be run at the next tick in the #{klass}#thread" do
335
+ thread = run_reactor_in_thread
336
+
337
+ reactor_thread = nil
338
+ subject.next_tick do
339
+ reactor_thread = Thread.current
340
+ end
341
+
342
+ sleep 0.1 while !reactor_thread
343
+
344
+ reactor_thread.should be_kind_of Thread
345
+ reactor_thread.should_not == Thread.current
346
+ thread.should == reactor_thread
347
+ end
348
+
349
+ context 'when the reactor is not running' do
350
+ it "raises #{klass::Error::NotRunning}" do
351
+ expect do
352
+ subject.next_tick{}
353
+ end.to raise_error klass::Error::NotRunning
354
+ end
355
+ end
356
+ end
357
+
358
+ describe '#at_interval' do
359
+ it "schedules a task to be run at the given interval in the #{klass}#thread" do
360
+ counted_ticks = 0
361
+ reactor_thread = nil
362
+
363
+ thread = run_reactor_in_thread
364
+
365
+ subject.at_interval 0.5 do
366
+ reactor_thread = Thread.current
367
+ counted_ticks += 1
368
+ end
369
+
370
+ sleep 2
371
+
372
+ counted_ticks.should == 3
373
+
374
+ reactor_thread.should be_kind_of Thread
375
+ reactor_thread.should_not == Thread.current
376
+ thread.should == reactor_thread
377
+ end
378
+
379
+ context 'when the reactor is not running' do
380
+ it "raises #{klass::Error::NotRunning}" do
381
+ expect do
382
+ subject.at_interval(1){}
383
+ end.to raise_error klass::Error::NotRunning
384
+ end
385
+ end
386
+ end
387
+
388
+ describe '#delay' do
389
+ it "schedules a task to be run at the given time in the #{klass}#thread" do
390
+ counted_ticks = 0
391
+ reactor_thread = nil
392
+ call_time = nil
393
+
394
+ thread = run_reactor_in_thread
395
+
396
+ subject.delay 1 do
397
+ reactor_thread = Thread.current
398
+ call_time = Time.now
399
+ counted_ticks += 1
400
+ end
401
+
402
+ sleep 3
403
+
404
+ (Time.now - call_time).to_i.should == 1
405
+ counted_ticks.should == 1
406
+
407
+ reactor_thread.should be_kind_of Thread
408
+ reactor_thread.should_not == Thread.current
409
+ thread.should == reactor_thread
410
+ end
411
+
412
+ context 'when the reactor is not running' do
413
+ it "raises #{klass::Error::NotRunning}" do
414
+ expect do
415
+ subject.delay(1){}
416
+ end.to raise_error klass::Error::NotRunning
417
+ end
418
+ end
419
+ end
420
+
421
+ describe '#thread' do
422
+ context 'when the reactor is' do
423
+ context 'not running' do
424
+ it 'returns nil' do
425
+ subject.thread.should be_nil
426
+ end
427
+ end
428
+
429
+ context 'running' do
430
+ it 'returns the thread of the reactor loop' do
431
+ thread = raktr.run_in_thread
432
+
433
+ subject.thread.should == thread
434
+ subject.thread.should_not == Thread.current
435
+ end
436
+ end
437
+
438
+ context 'stopped' do
439
+ it 'sets it to nil' do
440
+ raktr.run_in_thread
441
+ sleep 1
442
+ subject.stop
443
+ sleep 0.1 while subject.running?
444
+
445
+ subject.thread.should be_nil
446
+ end
447
+ end
448
+ end
449
+ end
450
+
451
+ describe '#in_same_thread?' do
452
+ context 'when running in the same thread as the reactor loop' do
453
+ it 'returns true' do
454
+ t = run_reactor_in_thread
455
+ sleep 0.1
456
+
457
+ subject.next_tick do
458
+ subject.should be_in_same_thread
459
+ subject.stop
460
+ end
461
+
462
+ t.join
463
+ end
464
+ end
465
+ context 'when not running in the same thread as the reactor loop' do
466
+ it 'returns false' do
467
+ run_reactor_in_thread
468
+ sleep 0.1
469
+
470
+ subject.should_not be_in_same_thread
471
+ end
472
+ end
473
+ context 'when the reactor is not running' do
474
+ it "raises #{klass::Error::NotRunning}" do
475
+ expect { subject.in_same_thread? }.to raise_error klass::Error::NotRunning
476
+ end
477
+ end
478
+ end
479
+
480
+ describe '#running?' do
481
+ context 'when the reactor is running' do
482
+ it 'returns true' do
483
+ run_reactor_in_thread
484
+
485
+ subject.should be_running
486
+ end
487
+ end
488
+
489
+ context 'when the reactor is not running' do
490
+ it 'returns false' do
491
+ subject.should_not be_running
492
+ end
493
+ end
494
+
495
+ context 'when the reactor has been stopped' do
496
+ it 'returns false' do
497
+ run_reactor_in_thread
498
+
499
+ Timeout.timeout 10 do
500
+ sleep 0.1 while !subject.running?
501
+ end
502
+
503
+ subject.should be_running
504
+ subject.stop
505
+
506
+ Timeout.timeout 10 do
507
+ sleep 0.1 while subject.running?
508
+ end
509
+
510
+ subject.should_not be_running
511
+ end
512
+ end
513
+
514
+ context 'when the reactor thread has been killed' do
515
+ it 'returns false' do
516
+ run_reactor_in_thread
517
+
518
+ Timeout.timeout 10 do
519
+ sleep 0.1 while !subject.running?
520
+ end
521
+
522
+ subject.should be_running
523
+ subject.thread.kill
524
+
525
+ Timeout.timeout 10 do
526
+ sleep 0.1 while subject.thread.alive?
527
+ end
528
+
529
+ subject.should_not be_running
530
+ end
531
+ end
532
+ end
533
+
534
+ describe '#stop' do
535
+ it 'stops the reactor' do
536
+ subject.run_in_thread
537
+
538
+ subject.should be_running
539
+ subject.stop
540
+
541
+ Timeout.timeout 10 do
542
+ sleep 0.1 while subject.running?
543
+ end
544
+
545
+ subject.should_not be_running
546
+ end
547
+ end
548
+
549
+ describe '#connect' do
550
+ context 'when using UNIX domain sockets',
551
+ if: Raktr.supports_unix_sockets? do
552
+
553
+ it "returns #{klass::Connection}" do
554
+ subject.run_block do
555
+ subject.connect( @unix_socket, echo_client_handler ).should be_kind_of klass::Connection
556
+ end
557
+ end
558
+
559
+ it 'establishes a connection' do
560
+ outside_thread = Thread.current
561
+ subject.run do
562
+ Thread.current[:outside_thread] = outside_thread
563
+ Thread.current[:data] = data
564
+
565
+ subject.connect( @unix_socket, echo_client_handler ) do |c|
566
+ def c.on_connect
567
+ super
568
+ write Thread.current[:data]
569
+ end
570
+
571
+ def c.on_close( _ )
572
+ Thread.current[:outside_thread][:received_data] = received_data
573
+ end
574
+ end
575
+ end
576
+
577
+ outside_thread[:received_data].should == data
578
+ end
579
+
580
+ context 'when the socket is invalid' do
581
+ it "calls #on_close with #{klass::Connection::Error::HostNotFound}" do
582
+ outside_thread = Thread.current
583
+ subject.run do
584
+ Thread.current[:outside_thread] = outside_thread
585
+
586
+ subject.connect( 'blahblah', echo_client_handler ) do |c|
587
+ def c.on_close( reason )
588
+ Thread.current[:outside_thread][:error] = reason
589
+ raktr.stop
590
+ end
591
+ end
592
+ end
593
+
594
+ Thread.current[:outside_thread][:error].should be_a_kind_of klass::Connection::Error::HostNotFound
595
+ end
596
+ end
597
+ end
598
+
599
+ context 'when using TCP sockets' do
600
+ it "returns #{klass::Connection}" do
601
+ subject.run_block do
602
+ subject.connect( @host, @port, echo_client_handler ).should be_kind_of klass::Connection
603
+ end
604
+ end
605
+
606
+ it 'establishes a connection' do
607
+ outside_thread = Thread.current
608
+ subject.run do
609
+ Thread.current[:outside_thread] = outside_thread
610
+ Thread.current[:data] = data
611
+
612
+ subject.connect( @host, @port, echo_client_handler ) do |c|
613
+ def c.on_connect
614
+ super
615
+ write Thread.current[:data]
616
+ end
617
+
618
+ def c.on_close( _ )
619
+ Thread.current[:outside_thread][:received_data] = received_data
620
+ end
621
+ end
622
+ end
623
+
624
+ outside_thread[:received_data].should == data
625
+ end
626
+
627
+ context 'when the host is invalid' do
628
+ it "calls #on_close with #{klass::Connection::Error::HostNotFound}" do
629
+ outside_thread = Thread.current
630
+ subject.run do
631
+ Thread.current[:outside_thread] = outside_thread
632
+
633
+ subject.connect( 'blahblah', 9876, echo_client_handler ) do |c|
634
+ def c.on_close( reason )
635
+ Thread.current[:outside_thread][:error] = reason
636
+ raktr.stop
637
+ end
638
+ end
639
+ end
640
+
641
+ Thread.current[:outside_thread][:error].should be_a_kind_of klass::Connection::Error::HostNotFound
642
+ end
643
+ end
644
+
645
+ context 'when the port is invalid' do
646
+ it "calls #on_close with #{klass::Connection::Error::Refused}" do
647
+ outside_thread = Thread.current
648
+ subject.run do
649
+ Thread.current[:outside_thread] = outside_thread
650
+
651
+ subject.connect( @host, @port + 1, echo_client_handler ) do |c|
652
+ def c.on_close( reason )
653
+ # Depending on when the error was caught, there
654
+ # may not be a reason available.
655
+ if reason
656
+ Thread.current[:outside_thread][:error] = reason.class
657
+ else
658
+ Thread.current[:outside_thread][:error] = :error
659
+ end
660
+
661
+ raktr.stop
662
+ end
663
+ end
664
+ end
665
+
666
+ [:error, klass::Connection::Error::Closed,
667
+ klass::Connection::Error::Refused,
668
+ Raktr::Connection::Error::BrokenPipe
669
+ ].should include Thread.current[:outside_thread][:error]
670
+ end
671
+ end
672
+ end
673
+
674
+ context 'when handler options have been provided' do
675
+ it 'initializes the handler with them' do
676
+ options = [:blah, { some: 'stuff' }]
677
+
678
+ subject.run_block do
679
+ subject.connect( @host, @port, echo_client_handler, *options ).
680
+ initialization_args.should == options
681
+ end
682
+ end
683
+ end
684
+
685
+ context 'when the reactor is not running' do
686
+ it "raises #{klass::Error::NotRunning}" do
687
+ expect do
688
+ subject.connect( 'blahblah', echo_client_handler )
689
+ end.to raise_error klass::Error::NotRunning
690
+ end
691
+ end
692
+ end
693
+
694
+ describe '#listen' do
695
+ let(:host) { '127.0.0.1' }
696
+ let(:port) { Servers.available_port }
697
+ let(:unix_socket) { port_to_socket Servers.available_port }
698
+
699
+ context 'when using UNIX domain sockets',
700
+ if: Raktr.supports_unix_sockets? do
701
+
702
+ it "returns #{klass::Connection}" do
703
+ subject.run_block do
704
+ subject.listen( unix_socket, echo_server_handler ).should be_kind_of klass::Connection
705
+ end
706
+ end
707
+
708
+ it 'listens for incoming connections' do
709
+ subject.run_in_thread
710
+
711
+ subject.listen( unix_socket, echo_server_handler )
712
+
713
+ @socket = unix_writer.call( unix_socket, data )
714
+ @socket.read( data.size ).should == data
715
+ end
716
+
717
+ context 'when the socket is invalid' do
718
+ it 'calls #on_close' do
719
+ outside_thread = Thread.current
720
+ subject.run do
721
+ Thread.current[:outside_thread] = outside_thread
722
+
723
+ subject.listen( '/socket', echo_server_handler ) do |c|
724
+ def c.on_close( reason )
725
+ # Depending on when the error was caught, there
726
+ # may not be a reason available.
727
+ if reason
728
+ Thread.current[:outside_thread][:error] = reason.class
729
+ else
730
+ Thread.current[:outside_thread][:error] = :error
731
+ end
732
+
733
+ raktr.stop
734
+ end
735
+ end
736
+ end
737
+
738
+ [:error, klass::Connection::Error::Permission].should include Thread.current[:outside_thread][:error]
739
+ end
740
+ end
741
+ end
742
+
743
+ context 'when using TCP sockets' do
744
+ it "returns #{klass::Connection}" do
745
+ subject.run_block do
746
+ subject.listen( host, port, echo_server_handler ).should be_kind_of klass::Connection
747
+ end
748
+ end
749
+
750
+ it 'listens for incoming connections' do
751
+ subject.run_in_thread
752
+
753
+ subject.listen( host, port, echo_server_handler )
754
+
755
+ @socket = tcp_writer.call( host, port, data )
756
+ @socket.read( data.size ).should == data
757
+ end
758
+
759
+ context 'when the host is invalid' do
760
+ it 'calls #on_close' do
761
+ outside_thread = Thread.current
762
+ subject.run do
763
+ Thread.current[:outside_thread] = outside_thread
764
+
765
+ subject.listen( 'host', port, echo_server_handler ) do |c|
766
+ def c.on_close( reason )
767
+ # Depending on when the error was caught, there
768
+ # may not be a reason available.
769
+ if reason
770
+ Thread.current[:outside_thread][:error] = reason.class
771
+ else
772
+ Thread.current[:outside_thread][:error] = :error
773
+ end
774
+
775
+ raktr.stop
776
+ end
777
+ end
778
+ end
779
+
780
+ [:error, klass::Connection::Error::HostNotFound].should include Thread.current[:outside_thread][:error]
781
+ end
782
+ end
783
+
784
+ context 'when the port is invalid', if: !Gem.win_platform? do
785
+ it 'calls #on_close' do
786
+ outside_thread = Thread.current
787
+ subject.run do
788
+ Thread.current[:outside_thread] = outside_thread
789
+
790
+ subject.listen( host, 50, echo_server_handler ) do |c|
791
+ def c.on_close( reason )
792
+ # Depending on when the error was caught, there
793
+ # may not be a reason available.
794
+ if reason
795
+ Thread.current[:outside_thread][:error] = reason.class
796
+ else
797
+ Thread.current[:outside_thread][:error] = :error
798
+ end
799
+
800
+ raktr.stop
801
+ end
802
+ end
803
+ end
804
+
805
+ [:error, klass::Connection::Error::Permission].should include Thread.current[:outside_thread][:error]
806
+ end
807
+ end
808
+ end
809
+
810
+ context 'when handler options have been provided' do
811
+ it 'initializes the handler with them' do
812
+ options = [:blah, { some: 'stuff' }]
813
+
814
+ subject.run_in_thread
815
+
816
+ subject.listen( host, port, echo_server_handler, *options )
817
+
818
+ @socket = tcp_writer.call( host, port, data )
819
+
820
+ sleep 5
821
+
822
+ subject.connections.values.first.initialization_args.should == options
823
+ end
824
+ end
825
+
826
+ context 'when the reactor is not running' do
827
+ it "raises #{klass::Error::NotRunning}" do
828
+ expect do
829
+ subject.listen( host, port, echo_server_handler )
830
+ end.to raise_error klass::Error::NotRunning
831
+ end
832
+ end
833
+ end
834
+ end