raktr 0.0.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE.md +29 -0
- data/README.md +77 -0
- data/Rakefile +53 -0
- data/lib/raktr/connection/callbacks.rb +71 -0
- data/lib/raktr/connection/error.rb +120 -0
- data/lib/raktr/connection/peer_info.rb +90 -0
- data/lib/raktr/connection/tls.rb +164 -0
- data/lib/raktr/connection.rb +339 -0
- data/lib/raktr/global.rb +24 -0
- data/lib/raktr/iterator.rb +249 -0
- data/lib/raktr/queue.rb +89 -0
- data/lib/raktr/tasks/base.rb +57 -0
- data/lib/raktr/tasks/delayed.rb +33 -0
- data/lib/raktr/tasks/one_off.rb +30 -0
- data/lib/raktr/tasks/periodic.rb +58 -0
- data/lib/raktr/tasks/persistent.rb +29 -0
- data/lib/raktr/tasks.rb +105 -0
- data/lib/raktr/version.rb +13 -0
- data/lib/raktr.rb +707 -0
- data/spec/raktr/connection/tls_spec.rb +348 -0
- data/spec/raktr/connection_spec.rb +74 -0
- data/spec/raktr/iterator_spec.rb +203 -0
- data/spec/raktr/queue_spec.rb +91 -0
- data/spec/raktr/tasks/base.rb +8 -0
- data/spec/raktr/tasks/delayed_spec.rb +71 -0
- data/spec/raktr/tasks/one_off_spec.rb +66 -0
- data/spec/raktr/tasks/periodic_spec.rb +57 -0
- data/spec/raktr/tasks/persistent_spec.rb +54 -0
- data/spec/raktr/tasks_spec.rb +155 -0
- data/spec/raktr_spec.rb +20 -0
- data/spec/raktr_tls_spec.rb +20 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/fixtures/handlers/echo_client.rb +34 -0
- data/spec/support/fixtures/handlers/echo_client_tls.rb +10 -0
- data/spec/support/fixtures/handlers/echo_server.rb +12 -0
- data/spec/support/fixtures/handlers/echo_server_tls.rb +8 -0
- data/spec/support/fixtures/pems/cacert.pem +37 -0
- data/spec/support/fixtures/pems/client/cert.pem +37 -0
- data/spec/support/fixtures/pems/client/foo-cert.pem +39 -0
- data/spec/support/fixtures/pems/client/foo-key.pem +51 -0
- data/spec/support/fixtures/pems/client/key.pem +51 -0
- data/spec/support/fixtures/pems/server/cert.pem +37 -0
- data/spec/support/fixtures/pems/server/key.pem +51 -0
- data/spec/support/helpers/paths.rb +23 -0
- data/spec/support/helpers/utilities.rb +135 -0
- data/spec/support/lib/server_option_parser.rb +29 -0
- data/spec/support/lib/servers/runner.rb +13 -0
- data/spec/support/lib/servers.rb +133 -0
- data/spec/support/servers/echo.rb +14 -0
- data/spec/support/servers/echo_tls.rb +22 -0
- data/spec/support/servers/echo_unix.rb +14 -0
- data/spec/support/servers/echo_unix_tls.rb +22 -0
- data/spec/support/shared/connection.rb +696 -0
- data/spec/support/shared/raktr.rb +834 -0
- data/spec/support/shared/task.rb +21 -0
- 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
|