tcp-client 0.10.0 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,800 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'helper'
4
-
5
- SOCKET_ERRORS = [
6
- Errno::EADDRNOTAVAIL,
7
- Errno::ECONNABORTED,
8
- Errno::ECONNREFUSED,
9
- Errno::ECONNRESET,
10
- Errno::EHOSTUNREACH,
11
- Errno::EINVAL,
12
- Errno::ENETUNREACH,
13
- Errno::EPIPE,
14
- IOError,
15
- SocketError,
16
- ::OpenSSL::SSL::SSLError
17
- ].freeze
18
-
19
- RSpec.describe TCPClient do
20
- subject(:client) { TCPClient.new.connect('localhost:1234', configuration) }
21
- let(:configuration) do
22
- TCPClient::Configuration.create(buffered: false, reverse_lookup: false)
23
- end
24
-
25
- describe 'a new instance' do
26
- subject(:client) { TCPClient.new }
27
-
28
- it 'is closed' do
29
- expect(client).to be_closed
30
- end
31
-
32
- it 'has no address' do
33
- expect(client.address).to be_nil
34
- end
35
-
36
- it 'returns no address string' do
37
- expect(client.to_s).to eq ''
38
- end
39
-
40
- it 'has no configuration' do
41
- expect(client.configuration).to be_nil
42
- end
43
-
44
- it 'failes when read is called' do
45
- expect { client.read(42) }.to raise_error(TCPClient::NotConnectedError)
46
- end
47
-
48
- it 'failes when write is called' do
49
- expect { client.write('?!') }.to raise_error(TCPClient::NotConnectedError)
50
- end
51
-
52
- it 'can be closed' do
53
- expect_any_instance_of(::Socket).not_to receive(:close)
54
- expect(client.close).to be client
55
- end
56
-
57
- it 'can be flushed' do
58
- expect_any_instance_of(::Socket).not_to receive(:flush)
59
- expect(client.flush).to be client
60
- end
61
- end
62
-
63
- describe 'a connected instance' do
64
- before { allow_any_instance_of(::Socket).to receive(:connect) }
65
-
66
- it 'is not closed' do
67
- expect(client).not_to be_closed
68
- end
69
-
70
- it 'has an address' do
71
- expect(client.address).to be_a TCPClient::Address
72
- end
73
-
74
- it 'returns an address string' do
75
- expect(client.to_s).to eq 'localhost:1234'
76
- end
77
-
78
- it 'uses the given configuration' do
79
- expect(client.configuration).to be configuration
80
- end
81
-
82
- it 'allows to read data' do
83
- result = '-' * 42
84
- allow_any_instance_of(::Socket).to receive(:read)
85
- .with(42)
86
- .and_return(result)
87
- expect(client.read(42)).to be result
88
- end
89
-
90
- it 'allows to write data' do
91
- allow_any_instance_of(::Socket).to receive(:write)
92
- .with('!' * 21)
93
- .and_return(21)
94
- expect(client.write('!' * 21)).to be 21
95
- end
96
-
97
- it 'can be closed' do
98
- expect_any_instance_of(::Socket).to receive(:close)
99
- expect(client.close).to be client
100
- end
101
-
102
- it 'can be flushed' do
103
- expect_any_instance_of(::Socket).to receive(:flush)
104
- expect(client.flush).to be client
105
- end
106
- end
107
-
108
- describe 'an instance after #connect failed' do
109
- subject(:client) do
110
- TCPClient.new.tap do |instance|
111
- instance.connect('', configuration)
112
- rescue StandardError
113
- Errno::EADDRNOTAVAIL
114
- end
115
- end
116
-
117
- it 'is closed' do
118
- expect(client).to be_closed
119
- end
120
-
121
- it 'has an address' do
122
- expect(client.address).to be_a TCPClient::Address
123
- end
124
-
125
- it 'returns an address string' do
126
- expect(client.to_s).to eq 'localhost:0'
127
- end
128
-
129
- it 'uses the given configuration' do
130
- expect(client.configuration).to be configuration
131
- end
132
-
133
- it 'failes when read is called' do
134
- expect { client.read(42) }.to raise_error(TCPClient::NotConnectedError)
135
- end
136
-
137
- it 'failes when write is called' do
138
- expect { client.write('?!') }.to raise_error(TCPClient::NotConnectedError)
139
- end
140
-
141
- it 'can be closed' do
142
- expect_any_instance_of(::Socket).not_to receive(:close)
143
- expect(client.close).to be client
144
- end
145
-
146
- it 'can be flushed' do
147
- expect_any_instance_of(::Socket).not_to receive(:flush)
148
- expect(client.flush).to be client
149
- end
150
- end
151
-
152
- xdescribe '.open' do
153
- end
154
-
155
- xdescribe '.with_deadline' do
156
- end
157
-
158
- context 'when not using SSL' do
159
- describe '#connect' do
160
- subject(:client) { TCPClient.new }
161
-
162
- it 'configures the socket' do
163
- expect_any_instance_of(::Socket).to receive(:sync=).once.with(true)
164
- expect_any_instance_of(::Socket).to receive(:setsockopt)
165
- .once
166
- .with(:TCP, :NODELAY, 1)
167
- expect_any_instance_of(::Socket).to receive(:setsockopt)
168
- .once
169
- .with(:SOCKET, :KEEPALIVE, 1)
170
- expect_any_instance_of(::Socket).to receive(:do_not_reverse_lookup=)
171
- .once
172
- .with(false)
173
- expect_any_instance_of(::Socket).to receive(:connect)
174
- client.connect('localhost:1234', configuration)
175
- end
176
-
177
- context 'when a timeout is specified' do
178
- it 'checks the time' do
179
- expect_any_instance_of(::Socket).to receive(:connect_nonblock)
180
- .once
181
- .with(kind_of(String), exception: false)
182
- client.connect('localhost:1234', configuration, timeout: 10)
183
- end
184
-
185
- it 'is returns itself' do
186
- allow_any_instance_of(::Socket).to receive(:connect_nonblock).with(
187
- kind_of(String),
188
- exception: false
189
- )
190
- result = client.connect('localhost:1234', configuration, timeout: 10)
191
-
192
- expect(client).to be client
193
- end
194
-
195
- it 'is not closed' do
196
- allow_any_instance_of(::Socket).to receive(:connect_nonblock).with(
197
- kind_of(String),
198
- exception: false
199
- )
200
- client.connect('localhost:1234', configuration, timeout: 10)
201
- expect(client).not_to be_closed
202
- end
203
-
204
- context 'when the connection can not be established in time' do
205
- before do
206
- allow_any_instance_of(::Socket).to receive(:connect_nonblock)
207
- .and_return(:wait_writable)
208
- end
209
-
210
- it 'raises an exception' do
211
- expect do
212
- client.connect('localhost:1234', configuration, timeout: 0.1)
213
- end.to raise_error(TCPClient::ConnectTimeoutError)
214
- end
215
-
216
- it 'allows to raise a custom exception' do
217
- exception = Class.new(StandardError)
218
- expect do
219
- client.connect(
220
- 'localhost:1234',
221
- configuration,
222
- timeout: 0.1,
223
- exception: exception
224
- )
225
- end.to raise_error(exception)
226
- end
227
-
228
- it 'is still closed' do
229
- begin
230
- client.connect('localhost:1234', configuration, timeout: 0.1)
231
- rescue TCPClient::ConnectTimeoutError
232
- end
233
- expect(client).to be_closed
234
- end
235
- end
236
- end
237
-
238
- context 'when a SocketError appears' do
239
- it 'does not handle it' do
240
- allow_any_instance_of(::Socket).to receive(:connect) {
241
- raise SocketError
242
- }
243
- expect do
244
- TCPClient.new.connect('localhost:1234', configuration)
245
- end.to raise_error(SocketError)
246
- end
247
-
248
- context 'when normalize_network_errors is configured' do
249
- let(:configuration) do
250
- TCPClient::Configuration.create(normalize_network_errors: true)
251
- end
252
-
253
- SOCKET_ERRORS.each do |error_class|
254
- it "raises TCPClient::NetworkError when a #{error_class} appeared" do
255
- allow_any_instance_of(::Socket).to receive(:connect) {
256
- raise error_class
257
- }
258
- expect do
259
- TCPClient.new.connect('localhost:1234', configuration)
260
- end.to raise_error(TCPClient::NetworkError)
261
- end
262
- end
263
- end
264
- end
265
- end
266
-
267
- describe '#read' do
268
- let(:data) { 'some bytes' }
269
- let(:data_size) { data.bytesize }
270
-
271
- before { allow_any_instance_of(::Socket).to receive(:connect) }
272
-
273
- it 'reads from socket' do
274
- expect_any_instance_of(::Socket).to receive(:read)
275
- .once
276
- .with(nil)
277
- .and_return(data)
278
- expect(client.read).to be data
279
- end
280
-
281
- context 'when a number of bytes is specified' do
282
- it 'reads the requested number of bytes' do
283
- expect_any_instance_of(::Socket).to receive(:read)
284
- .once
285
- .with(data_size)
286
- .and_return(data)
287
- expect(client.read(data_size)).to be data
288
- end
289
- end
290
-
291
- context 'when a timeout is specified' do
292
- it 'checks the time' do
293
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
294
- .and_return(data)
295
- expect(client.read(timeout: 10)).to be data
296
- end
297
-
298
- context 'when socket closed before any data can be read' do
299
- it 'returns empty buffer' do
300
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
301
- .and_return(nil)
302
- expect(client.read(timeout: 10)).to be_empty
303
- end
304
-
305
- it 'is closed' do
306
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
307
- .and_return(nil)
308
-
309
- client.read(timeout: 10)
310
- expect(client).to be_closed
311
- end
312
- end
313
-
314
- context 'when data can not be fetched in a single chunk' do
315
- it 'reads chunk by chunk' do
316
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
317
- .once
318
- .with(instance_of(Integer), exception: false)
319
- .and_return(data)
320
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
321
- .once
322
- .with(instance_of(Integer), exception: false)
323
- .and_return(data)
324
- expect(client.read(data_size * 2, timeout: 10)).to eq data * 2
325
- end
326
-
327
- context 'when socket closed before enough data is avail' do
328
- it 'returns available data only' do
329
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
330
- .once
331
- .with(instance_of(Integer), exception: false)
332
- .and_return(data)
333
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
334
- .once
335
- .with(instance_of(Integer), exception: false)
336
- .and_return(nil)
337
- expect(client.read(data_size * 2, timeout: 10)).to eq data
338
- end
339
-
340
- it 'is closed' do
341
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
342
- .once
343
- .with(instance_of(Integer), exception: false)
344
- .and_return(data)
345
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
346
- .once
347
- .with(instance_of(Integer), exception: false)
348
- .and_return(nil)
349
- client.read(data_size * 2, timeout: 10)
350
- expect(client).to be_closed
351
- end
352
- end
353
- end
354
-
355
- context 'when the data can not be read in time' do
356
- before do
357
- allow_any_instance_of(::Socket).to receive(:read_nonblock)
358
- .and_return(:wait_readable)
359
- end
360
- it 'raises an exception' do
361
- expect { client.read(timeout: 0.25) }.to raise_error(
362
- TCPClient::ReadTimeoutError
363
- )
364
- end
365
-
366
- it 'allows to raise a custom exception' do
367
- exception = Class.new(StandardError)
368
- expect do
369
- client.read(timeout: 0.25, exception: exception)
370
- end.to raise_error(exception)
371
- end
372
- end
373
- end
374
-
375
- context 'when a SocketError appears' do
376
- it 'does not handle it' do
377
- allow_any_instance_of(::Socket).to receive(:read) {
378
- raise SocketError
379
- }
380
- expect { client.read(10) }.to raise_error(SocketError)
381
- end
382
-
383
- context 'when normalize_network_errors is configured' do
384
- let(:configuration) do
385
- TCPClient::Configuration.create(normalize_network_errors: true)
386
- end
387
-
388
- SOCKET_ERRORS.each do |error_class|
389
- it "raises a TCPClient::NetworkError when a #{error_class} appeared" do
390
- allow_any_instance_of(::Socket).to receive(:read) {
391
- raise error_class
392
- }
393
- expect { client.read(12) }.to raise_error(TCPClient::NetworkError)
394
- end
395
- end
396
- end
397
- end
398
- end
399
-
400
- describe '#readline' do
401
- before { allow_any_instance_of(::Socket).to receive(:connect) }
402
-
403
- it 'reads from socket' do
404
- expect_any_instance_of(::Socket).to receive(:readline)
405
- .once
406
- .with($/, chomp: false)
407
- .and_return("Hello World\n")
408
- expect(client.readline).to eq "Hello World\n"
409
- end
410
-
411
- context 'when a separator is specified' do
412
- it 'forwards the separator' do
413
- expect_any_instance_of(::Socket).to receive(:readline)
414
- .once
415
- .with('/', chomp: false)
416
- .and_return('Hello/')
417
- expect(client.readline('/')).to eq 'Hello/'
418
- end
419
- end
420
-
421
- context 'when chomp is true' do
422
- it 'forwards the flag' do
423
- expect_any_instance_of(::Socket).to receive(:readline)
424
- .once
425
- .with($/, chomp: true)
426
- .and_return('Hello World')
427
- expect(client.readline(chomp: true)).to eq 'Hello World'
428
- end
429
- end
430
-
431
- context 'when a timeout is specified' do
432
- it 'checks the time' do
433
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
434
- .and_return("Hello World\nHello World\n")
435
- expect(client.readline(timeout: 10)).to eq "Hello World\n"
436
- end
437
-
438
- it 'optional chomps the line' do
439
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
440
- .and_return("Hello World\nHello World\n")
441
- expect(client.readline(chomp: true, timeout: 10)).to eq 'Hello World'
442
- end
443
-
444
- it 'uses the given separator' do
445
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
446
- .and_return("Hello/World\n")
447
- expect(client.readline('/', timeout: 10)).to eq 'Hello/'
448
- end
449
-
450
- context 'when data can not be fetched in a single chunk' do
451
- it 'reads chunk by chunk' do
452
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
453
- .once
454
- .with(instance_of(Integer), exception: false)
455
- .and_return('Hello ')
456
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
457
- .once
458
- .with(instance_of(Integer), exception: false)
459
- .and_return('World')
460
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
461
- .once
462
- .with(instance_of(Integer), exception: false)
463
- .and_return("\nAnd so...")
464
- expect(client.readline(timeout: 10)).to eq "Hello World\n"
465
- end
466
-
467
- context 'when socket closed before enough data is avail' do
468
- it 'returns available data only' do
469
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
470
- .once
471
- .with(instance_of(Integer), exception: false)
472
- .and_return('Hello ')
473
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
474
- .once
475
- .with(instance_of(Integer), exception: false)
476
- .and_return(nil)
477
- expect(client.readline(timeout: 10)).to eq "Hello "
478
- end
479
-
480
- it 'is closed' do
481
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
482
- .once
483
- .with(instance_of(Integer), exception: false)
484
- .and_return('Hello ')
485
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
486
- .once
487
- .with(instance_of(Integer), exception: false)
488
- .and_return(nil)
489
- client.readline(timeout: 10)
490
- expect(client).to be_closed
491
- end
492
- end
493
- end
494
-
495
- context 'when the data can not be read in time' do
496
- before do
497
- allow_any_instance_of(::Socket).to receive(:read_nonblock)
498
- .and_return(:wait_readable)
499
- end
500
- it 'raises an exception' do
501
- expect { client.readline(timeout: 0.25) }.to raise_error(
502
- TCPClient::ReadTimeoutError
503
- )
504
- end
505
-
506
- it 'allows to raise a custom exception' do
507
- exception = Class.new(StandardError)
508
- expect do
509
- client.read(timeout: 0.25, exception: exception)
510
- end.to raise_error(exception)
511
- end
512
- end
513
- end
514
-
515
- context 'when a SocketError appears' do
516
- it 'does not handle it' do
517
- allow_any_instance_of(::Socket).to receive(:read) {
518
- raise SocketError
519
- }
520
- expect { client.read(10) }.to raise_error(SocketError)
521
- end
522
-
523
- context 'when normalize_network_errors is configured' do
524
- let(:configuration) do
525
- TCPClient::Configuration.create(normalize_network_errors: true)
526
- end
527
-
528
- SOCKET_ERRORS.each do |error_class|
529
- it "raises a TCPClient::NetworkError when a #{error_class} appeared" do
530
- allow_any_instance_of(::Socket).to receive(:read) {
531
- raise error_class
532
- }
533
- expect { client.read(12) }.to raise_error(TCPClient::NetworkError)
534
- end
535
- end
536
- end
537
- end
538
- end
539
-
540
- describe '#write' do
541
- let(:data) { 'some bytes' }
542
- let(:data_size) { data.bytesize }
543
-
544
- before { allow_any_instance_of(::Socket).to receive(:connect) }
545
-
546
- it 'writes to the socket' do
547
- expect_any_instance_of(::Socket).to receive(:write)
548
- .once
549
- .with(data)
550
- .and_return(data_size)
551
- expect(client.write(data)).to be data_size
552
- end
553
-
554
- context 'when multiple data chunks are given' do
555
- it 'writes each chunk' do
556
- expect_any_instance_of(::Socket).to receive(:write)
557
- .once
558
- .with(data, data)
559
- .and_return(data_size * 2)
560
- expect(client.write(data, data)).to be data_size * 2
561
- end
562
- end
563
-
564
- context 'when a timeout is specified' do
565
- it 'checks the time' do
566
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
567
- .and_return(data_size)
568
- expect(client.write(data, timeout: 10)).to be data_size
569
- end
570
-
571
- context 'when data can not be written in a single chunk' do
572
- let(:chunk1) { '1234567890' }
573
- let(:chunk2) { '12345' }
574
- let(:data1) { chunk1 + chunk2 }
575
- let(:chunk3) { 'abcdefghijklm' }
576
- let(:chunk4) { 'ABCDE' }
577
- let(:data2) { chunk3 + chunk4 }
578
-
579
- it 'writes chunk by chunk and part by part' do
580
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
581
- .once
582
- .with(data1, exception: false)
583
- .and_return(chunk1.bytesize)
584
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
585
- .once
586
- .with(chunk2, exception: false)
587
- .and_return(chunk2.bytesize)
588
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
589
- .once
590
- .with(data2, exception: false)
591
- .and_return(chunk3.bytesize)
592
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
593
- .once
594
- .with(chunk4, exception: false)
595
- .and_return(chunk4.bytesize)
596
-
597
- expect(client.write(data1, data2, timeout: 10)).to be(
598
- data1.bytesize + data2.bytesize
599
- )
600
- end
601
- end
602
-
603
- context 'when the data can not be written in time' do
604
- before do
605
- allow_any_instance_of(::Socket).to receive(:write_nonblock)
606
- .and_return(:wait_writable)
607
- end
608
- it 'raises an exception' do
609
- expect { client.write(data, timeout: 0.25) }.to raise_error(
610
- TCPClient::WriteTimeoutError
611
- )
612
- end
613
-
614
- it 'allows to raise a custom exception' do
615
- exception = Class.new(StandardError)
616
- expect do
617
- client.write(data, timeout: 0.25, exception: exception)
618
- end.to raise_error(exception)
619
- end
620
- end
621
- end
622
-
623
- context 'when a SocketError appears' do
624
- before { allow_any_instance_of(::Socket).to receive(:connect) }
625
-
626
- it 'does not handle it' do
627
- allow_any_instance_of(::Socket).to receive(:write) {
628
- raise SocketError
629
- }
630
- expect { client.write('some data') }.to raise_error(SocketError)
631
- end
632
-
633
- context 'when normalize_network_errors is configured' do
634
- let(:configuration) do
635
- TCPClient::Configuration.create(normalize_network_errors: true)
636
- end
637
-
638
- SOCKET_ERRORS.each do |error_class|
639
- it "raises a TCPClient::NetworkError when a #{error_class} appeared" do
640
- allow_any_instance_of(::Socket).to receive(:write) {
641
- raise error_class
642
- }
643
- expect { client.write('some data') }.to raise_error(
644
- TCPClient::NetworkError
645
- )
646
- end
647
- end
648
- end
649
- end
650
- end
651
-
652
- describe '#with_deadline' do
653
- subject(:client) { TCPClient.new }
654
- let(:configuration) { TCPClient::Configuration.create(timeout: 60) }
655
-
656
- before do
657
- allow_any_instance_of(::Socket).to receive(:connect_nonblock)
658
- allow_any_instance_of(::Socket).to receive(:read_nonblock) do |_, size|
659
- 'r' * size
660
- end
661
- allow_any_instance_of(::Socket).to receive(
662
- :write_nonblock
663
- ) do |_, data|
664
- data.bytesize
665
- end
666
- end
667
-
668
- it 'allows to use a timeout value for all actions in the given block' do
669
- expect_any_instance_of(::Socket).to receive(:connect_nonblock)
670
- .once
671
- .with(kind_of(String), exception: false)
672
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
673
- .once
674
- .with(instance_of(Integer), exception: false)
675
- .and_return('123456789012abcdefgAB')
676
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
677
- .once
678
- .with('123456', exception: false)
679
- .and_return(6)
680
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
681
- .once
682
- .with(instance_of(Integer), exception: false)
683
- .and_return('CDEFG')
684
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
685
- .once
686
- .with('abc', exception: false)
687
- .and_return(3)
688
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
689
- .once
690
- .with('ABC', exception: false)
691
- .and_return(3)
692
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
693
- .once
694
- .with('ABCDEF', exception: false)
695
- .and_return(6)
696
-
697
- client.with_deadline(10) do
698
- expect(client.connect('localhost:1234', configuration)).to be client
699
- expect(client.read(12)).to eq '123456789012'
700
- expect(client.write('123456')).to be 6
701
- expect(client.read(7)).to eq 'abcdefg'
702
- expect(client.read(7)).to eq 'ABCDEFG'
703
- expect(client.write('abc')).to be 3
704
- expect(client.write('ABC', 'ABCDEF')).to be 9
705
- end
706
- end
707
-
708
- context 'when called without a block' do
709
- it 'raises an exception' do
710
- expect { client.with_deadline(0.25) }.to raise_error(ArgumentError)
711
- end
712
- end
713
-
714
- context 'when #connect fails' do
715
- it 'raises an exception' do
716
- expect_any_instance_of(::Socket).to receive(:connect_nonblock)
717
- .and_return(:wait_writable)
718
- expect do
719
- client.with_deadline(0.25) do
720
- client.connect('localhost:1234', configuration)
721
- client.read(12)
722
- client.write('Hello World!')
723
- end
724
- end.to raise_error(TCPClient::ConnectTimeoutError)
725
- end
726
- end
727
-
728
- context 'when #read fails' do
729
- it 'raises an exception' do
730
- expect_any_instance_of(::Socket).to receive(:read_nonblock)
731
- .and_return(:wait_readable)
732
- expect do
733
- client.with_deadline(0.25) do
734
- client.connect('localhost:1234', configuration)
735
- client.write('Hello World!')
736
- client.read(12)
737
- end
738
- end.to raise_error(TCPClient::ReadTimeoutError)
739
- end
740
- end
741
-
742
- context 'when #write fails' do
743
- it 'raises an exception' do
744
- expect_any_instance_of(::Socket).to receive(:write_nonblock)
745
- .and_return(:wait_writable)
746
- expect do
747
- client.with_deadline(0.25) do
748
- client.connect('localhost:1234', configuration)
749
- client.read(12)
750
- client.write('Hello World!')
751
- end
752
- end.to raise_error(TCPClient::WriteTimeoutError)
753
- end
754
- end
755
- end
756
- end
757
-
758
- context 'when using SSL' do
759
- let(:configuration) do
760
- TCPClient::Configuration.create(
761
- buffered: false,
762
- reverse_lookup: false,
763
- ssl: {
764
- min_version: :TLS1_2,
765
- max_version: :TLS1_3
766
- }
767
- )
768
- end
769
-
770
- before do
771
- allow_any_instance_of(::Socket).to receive(:connect)
772
- allow_any_instance_of(::OpenSSL::SSL::SSLSocket).to receive(:connect)
773
- end
774
-
775
- describe '#connect' do
776
- it 'configures the SSL socket' do
777
- # this produces a mock warning :(
778
- # expect_any_instance_of(::OpenSSL::SSL::SSLContext).to receive(
779
- # :set_params
780
- # )
781
- # .once
782
- # .with(ssl_version: :TLSv1_2)
783
- # .and_call_original
784
- expect_any_instance_of(::OpenSSL::SSL::SSLSocket).to receive(
785
- :sync_close=
786
- )
787
- .once
788
- .with(true)
789
- .and_call_original
790
- expect_any_instance_of(::OpenSSL::SSL::SSLSocket).to receive(
791
- :post_connection_check
792
- )
793
- .once
794
- .with('localhost')
795
-
796
- TCPClient.new.connect('localhost:1234', configuration)
797
- end
798
- end
799
- end
800
- end