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