tcp-client 0.10.1 → 0.11.2

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,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(result).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