tcp-client 0.11.0 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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