tcp-client 0.6.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,596 @@
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.closed?).to be true
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.closed?).to be false
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.closed?).to be true
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
+ it 'configures the socket' do
161
+ expect_any_instance_of(::Socket).to receive(:sync=).once.with(true)
162
+ expect_any_instance_of(::Socket).to receive(:setsockopt)
163
+ .once
164
+ .with(:TCP, :NODELAY, 1)
165
+ expect_any_instance_of(::Socket).to receive(:setsockopt)
166
+ .once
167
+ .with(:SOCKET, :KEEPALIVE, 1)
168
+ expect_any_instance_of(::Socket).to receive(:do_not_reverse_lookup=)
169
+ .once
170
+ .with(false)
171
+ expect_any_instance_of(::Socket).to receive(:connect)
172
+ TCPClient.new.connect('localhost:1234', configuration)
173
+ end
174
+
175
+ context 'when a timeout is specified' do
176
+ it 'checks the time' do
177
+ expect_any_instance_of(::Socket).to receive(:connect_nonblock)
178
+ .once
179
+ .with(kind_of(String), exception: false)
180
+ TCPClient.new.connect('localhost:1234', configuration, timeout: 10)
181
+ end
182
+
183
+ context 'when the connection can not be established in time' do
184
+ before do
185
+ allow_any_instance_of(::Socket).to receive(:connect_nonblock)
186
+ .and_return(:wait_writable)
187
+ end
188
+
189
+ it 'raises an exception' do
190
+ expect do
191
+ TCPClient.new.connect(
192
+ 'localhost:1234',
193
+ configuration,
194
+ timeout: 0.25
195
+ )
196
+ end.to raise_error(TCPClient::ConnectTimeoutError)
197
+ end
198
+
199
+ it 'allows to raise a custom exception' do
200
+ exception = Class.new(StandardError)
201
+ expect do
202
+ TCPClient.new.connect(
203
+ 'localhost:1234',
204
+ configuration,
205
+ timeout: 0.25,
206
+ exception: exception
207
+ )
208
+ end.to raise_error(exception)
209
+ end
210
+ end
211
+ end
212
+
213
+ context 'when a SocketError appears' do
214
+ it 'does not handle it' do
215
+ allow_any_instance_of(::Socket).to receive(:connect) {
216
+ raise SocketError
217
+ }
218
+ expect do
219
+ TCPClient.new.connect('localhost:1234', configuration)
220
+ end.to raise_error(SocketError)
221
+ end
222
+
223
+ context 'when normalize_network_errors is configured' do
224
+ let(:configuration) do
225
+ TCPClient::Configuration.create(normalize_network_errors: true)
226
+ end
227
+
228
+ SOCKET_ERRORS.each do |error_class|
229
+ it "raises a TCPClient::NetworkError when a #{error_class} appeared" do
230
+ allow_any_instance_of(::Socket).to receive(:connect) {
231
+ raise error_class
232
+ }
233
+ expect do
234
+ TCPClient.new.connect('localhost:1234', configuration)
235
+ end.to raise_error(TCPClient::NetworkError)
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ describe '#read' do
243
+ let(:data) { 'some bytes' }
244
+ let(:data_size) { data.bytesize }
245
+
246
+ before { allow_any_instance_of(::Socket).to receive(:connect) }
247
+
248
+ it 'reads from socket' do
249
+ expect_any_instance_of(::Socket).to receive(:read)
250
+ .once
251
+ .with(nil)
252
+ .and_return(data)
253
+ expect(client.read).to be data
254
+ end
255
+
256
+ context 'when a number of bytes is specified' do
257
+ it 'reads the requested number of bytes' do
258
+ expect_any_instance_of(::Socket).to receive(:read)
259
+ .once
260
+ .with(data_size)
261
+ .and_return(data)
262
+ expect(client.read(data_size)).to be data
263
+ end
264
+ end
265
+
266
+ context 'when a timeout is specified' do
267
+ it 'checks the time' do
268
+ expect_any_instance_of(::Socket).to receive(:read_nonblock)
269
+ .and_return(data)
270
+ expect(client.read(timeout: 10)).to be data
271
+ end
272
+
273
+ context 'when data can not be fetched in a single chunk' do
274
+ it 'reads chunk by chunk' do
275
+ expect_any_instance_of(::Socket).to receive(:read_nonblock)
276
+ .once
277
+ .with(data_size * 2, exception: false)
278
+ .and_return(data)
279
+ expect_any_instance_of(::Socket).to receive(:read_nonblock)
280
+ .once
281
+ .with(data_size, exception: false)
282
+ .and_return(data)
283
+ expect(client.read(data_size * 2, timeout: 10)).to eq data * 2
284
+ end
285
+ end
286
+
287
+ context 'when the data can not be read in time' do
288
+ before do
289
+ allow_any_instance_of(::Socket).to receive(:read_nonblock)
290
+ .and_return(:wait_readable)
291
+ end
292
+ it 'raises an exception' do
293
+ expect { client.read(timeout: 0.25) }.to raise_error(
294
+ TCPClient::ReadTimeoutError
295
+ )
296
+ end
297
+
298
+ it 'allows to raise a custom exception' do
299
+ exception = Class.new(StandardError)
300
+ expect do
301
+ client.read(timeout: 0.25, exception: exception)
302
+ end.to raise_error(exception)
303
+ end
304
+ end
305
+ end
306
+
307
+ context 'when a SocketError appears' do
308
+ it 'does not handle it' do
309
+ allow_any_instance_of(::Socket).to receive(:read) {
310
+ raise SocketError
311
+ }
312
+ expect { client.read(10) }.to raise_error(SocketError)
313
+ end
314
+
315
+ context 'when normalize_network_errors is configured' do
316
+ let(:configuration) do
317
+ TCPClient::Configuration.create(normalize_network_errors: true)
318
+ end
319
+
320
+ SOCKET_ERRORS.each do |error_class|
321
+ it "raises a TCPClient::NetworkError when a #{error_class} appeared" do
322
+ allow_any_instance_of(::Socket).to receive(:read) {
323
+ raise error_class
324
+ }
325
+ expect { client.read(12) }.to raise_error(TCPClient::NetworkError)
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
331
+
332
+ describe '#write' do
333
+ let(:data) { 'some bytes' }
334
+ let(:data_size) { data.bytesize }
335
+
336
+ before { allow_any_instance_of(::Socket).to receive(:connect) }
337
+
338
+ it 'writes to the socket' do
339
+ expect_any_instance_of(::Socket).to receive(:write)
340
+ .once
341
+ .with(data)
342
+ .and_return(data_size)
343
+ expect(client.write(data)).to be data_size
344
+ end
345
+
346
+ context 'when multiple data chunks are given' do
347
+ it 'writes each chunk' do
348
+ expect_any_instance_of(::Socket).to receive(:write)
349
+ .once
350
+ .with(data, data)
351
+ .and_return(data_size * 2)
352
+ expect(client.write(data, data)).to be data_size * 2
353
+ end
354
+ end
355
+
356
+ context 'when a timeout is specified' do
357
+ it 'checks the time' do
358
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
359
+ .and_return(data_size)
360
+ expect(client.write(data, timeout: 10)).to be data_size
361
+ end
362
+
363
+ context 'when data can not be written in a single chunk' do
364
+ let(:chunk1) { '1234567890' }
365
+ let(:chunk2) { '12345' }
366
+ let(:data1) { chunk1 + chunk2 }
367
+ let(:chunk3) { 'abcdefghijklm' }
368
+ let(:chunk4) { 'ABCDE' }
369
+ let(:data2) { chunk3 + chunk4 }
370
+
371
+ it 'writes chunk by chunk and part by part' do
372
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
373
+ .once
374
+ .with(data1, exception: false)
375
+ .and_return(chunk1.bytesize)
376
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
377
+ .once
378
+ .with(chunk2, exception: false)
379
+ .and_return(chunk2.bytesize)
380
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
381
+ .once
382
+ .with(data2, exception: false)
383
+ .and_return(chunk3.bytesize)
384
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
385
+ .once
386
+ .with(chunk4, exception: false)
387
+ .and_return(chunk4.bytesize)
388
+
389
+ expect(client.write(data1, data2, timeout: 10)).to be(
390
+ data1.bytesize + data2.bytesize
391
+ )
392
+ end
393
+ end
394
+
395
+ context 'when the data can not be written in time' do
396
+ before do
397
+ allow_any_instance_of(::Socket).to receive(:write_nonblock)
398
+ .and_return(:wait_writable)
399
+ end
400
+ it 'raises an exception' do
401
+ expect { client.write(data, timeout: 0.25) }.to raise_error(
402
+ TCPClient::WriteTimeoutError
403
+ )
404
+ end
405
+
406
+ it 'allows to raise a custom exception' do
407
+ exception = Class.new(StandardError)
408
+ expect do
409
+ client.write(data, timeout: 0.25, exception: exception)
410
+ end.to raise_error(exception)
411
+ end
412
+ end
413
+ end
414
+
415
+ context 'when a SocketError appears' do
416
+ before { allow_any_instance_of(::Socket).to receive(:connect) }
417
+
418
+ it 'does not handle it' do
419
+ allow_any_instance_of(::Socket).to receive(:write) {
420
+ raise SocketError
421
+ }
422
+ expect { client.write('some data') }.to raise_error(SocketError)
423
+ end
424
+
425
+ context 'when normalize_network_errors is configured' do
426
+ let(:configuration) do
427
+ TCPClient::Configuration.create(normalize_network_errors: true)
428
+ end
429
+
430
+ SOCKET_ERRORS.each do |error_class|
431
+ it "raises a TCPClient::NetworkError when a #{error_class} appeared" do
432
+ allow_any_instance_of(::Socket).to receive(:write) {
433
+ raise error_class
434
+ }
435
+ expect { client.write('some data') }.to raise_error(
436
+ TCPClient::NetworkError
437
+ )
438
+ end
439
+ end
440
+ end
441
+ end
442
+ end
443
+
444
+ describe '#with_deadline' do
445
+ subject(:client) { TCPClient.new }
446
+ let(:configuration) { TCPClient::Configuration.create(timeout: 60) }
447
+
448
+ before do
449
+ allow_any_instance_of(::Socket).to receive(:connect_nonblock)
450
+ allow_any_instance_of(::Socket).to receive(:read_nonblock) do |_, size|
451
+ 'r' * size
452
+ end
453
+ allow_any_instance_of(::Socket).to receive(
454
+ :write_nonblock
455
+ ) do |_, data|
456
+ data.bytesize
457
+ end
458
+ end
459
+
460
+ it 'allows to use a timeout value for all actions in the given block' do
461
+ expect_any_instance_of(::Socket).to receive(:connect_nonblock)
462
+ .once
463
+ .with(kind_of(String), exception: false)
464
+ expect_any_instance_of(::Socket).to receive(:read_nonblock)
465
+ .once
466
+ .with(12, exception: false)
467
+ .and_return('123456789012')
468
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
469
+ .once
470
+ .with('123456', exception: false)
471
+ .and_return(6)
472
+ expect_any_instance_of(::Socket).to receive(:read_nonblock)
473
+ .once
474
+ .with(7, exception: false)
475
+ .and_return('abcdefg')
476
+ expect_any_instance_of(::Socket).to receive(:read_nonblock)
477
+ .once
478
+ .with(7, exception: false)
479
+ .and_return('ABCDEFG')
480
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
481
+ .once
482
+ .with('abc', exception: false)
483
+ .and_return(3)
484
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
485
+ .once
486
+ .with('ABC', exception: false)
487
+ .and_return(3)
488
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
489
+ .once
490
+ .with('ABCDEF', exception: false)
491
+ .and_return(6)
492
+
493
+ client.with_deadline(10) do
494
+ expect(client.connect('localhost:1234', configuration)).to be client
495
+ expect(client.read(12)).to eq '123456789012'
496
+ expect(client.write('123456')).to be 6
497
+ expect(client.read(7)).to eq 'abcdefg'
498
+ expect(client.read(7)).to eq 'ABCDEFG'
499
+ expect(client.write('abc')).to be 3
500
+ expect(client.write('ABC', 'ABCDEF')).to be 9
501
+ end
502
+ end
503
+
504
+ context 'when called without a block' do
505
+ it 'raises an exception' do
506
+ expect { client.with_deadline(0.25) }.to raise_error(ArgumentError)
507
+ end
508
+ end
509
+
510
+ context 'when #connect fails' do
511
+ it 'raises an exception' do
512
+ expect_any_instance_of(::Socket).to receive(:connect_nonblock)
513
+ .and_return(:wait_writable)
514
+ expect do
515
+ client.with_deadline(0.25) do
516
+ client.connect('localhost:1234', configuration)
517
+ client.read(12)
518
+ client.write('Hello World!')
519
+ end
520
+ end.to raise_error(TCPClient::ConnectTimeoutError)
521
+ end
522
+ end
523
+
524
+ context 'when #read fails' do
525
+ it 'raises an exception' do
526
+ expect_any_instance_of(::Socket).to receive(:read_nonblock)
527
+ .and_return(:wait_readable)
528
+ expect do
529
+ client.with_deadline(0.25) do
530
+ client.connect('localhost:1234', configuration)
531
+ client.write('Hello World!')
532
+ client.read(12)
533
+ end
534
+ end.to raise_error(TCPClient::ReadTimeoutError)
535
+ end
536
+ end
537
+
538
+ context 'when #write fails' do
539
+ it 'raises an exception' do
540
+ expect_any_instance_of(::Socket).to receive(:write_nonblock)
541
+ .and_return(:wait_writable)
542
+ expect do
543
+ client.with_deadline(0.25) do
544
+ client.connect('localhost:1234', configuration)
545
+ client.read(12)
546
+ client.write('Hello World!')
547
+ end
548
+ end.to raise_error(TCPClient::WriteTimeoutError)
549
+ end
550
+ end
551
+ end
552
+ end
553
+
554
+ context 'when using SSL' do
555
+ let(:configuration) do
556
+ TCPClient::Configuration.create(
557
+ buffered: false,
558
+ reverse_lookup: false,
559
+ ssl: {
560
+ min_version: :TLS1_2,
561
+ max_version: :TLS1_3
562
+ }
563
+ )
564
+ end
565
+
566
+ before do
567
+ allow_any_instance_of(::Socket).to receive(:connect)
568
+ allow_any_instance_of(::OpenSSL::SSL::SSLSocket).to receive(:connect)
569
+ end
570
+
571
+ describe '#connect' do
572
+ it 'configures the SSL socket' do
573
+ # this produces a mock warning :(
574
+ # expect_any_instance_of(::OpenSSL::SSL::SSLContext).to receive(
575
+ # :set_params
576
+ # )
577
+ # .once
578
+ # .with(ssl_version: :TLSv1_2)
579
+ # .and_call_original
580
+ expect_any_instance_of(::OpenSSL::SSL::SSLSocket).to receive(
581
+ :sync_close=
582
+ )
583
+ .once
584
+ .with(true)
585
+ .and_call_original
586
+ expect_any_instance_of(::OpenSSL::SSL::SSLSocket).to receive(
587
+ :post_connection_check
588
+ )
589
+ .once
590
+ .with('localhost')
591
+
592
+ TCPClient.new.connect('localhost:1234', configuration)
593
+ end
594
+ end
595
+ end
596
+ end
data/tcp-client.gemspec CHANGED
@@ -23,15 +23,18 @@ Gem::Specification.new do |spec|
23
23
  spec.license = 'BSD-3-Clause'
24
24
 
25
25
  spec.metadata['source_code_uri'] = 'https://github.com/mblumtritt/tcp-client'
26
+ spec.metadata['documentation_uri'] =
27
+ 'https://rubydoc.info/github/mblumtritt/tcp-client'
26
28
  spec.metadata['bug_tracker_uri'] =
27
29
  'https://github.com/mblumtritt/tcp-client/issues'
28
30
 
29
31
  spec.add_development_dependency 'bundler'
30
- spec.add_development_dependency 'minitest'
31
32
  spec.add_development_dependency 'rake'
33
+ spec.add_development_dependency 'rspec'
34
+ spec.add_development_dependency 'yard'
32
35
 
33
36
  all_files = Dir.chdir(__dir__) { `git ls-files -z`.split(0.chr) }
34
- spec.test_files = all_files.grep(%r{^test/})
37
+ spec.test_files = all_files.grep(%r{^spec/})
35
38
  spec.files = all_files - spec.test_files
36
39
 
37
40
  spec.extra_rdoc_files = %w[README.md LICENSE]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tcp-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Blumtritt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-16 00:00:00.000000000 Z
11
+ date: 2021-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: minitest
28
+ name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,21 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
@@ -85,19 +99,19 @@ files:
85
99
  - rakefile.rb
86
100
  - sample/google.rb
87
101
  - sample/google_ssl.rb
102
+ - spec/helper.rb
103
+ - spec/tcp-client/address_spec.rb
104
+ - spec/tcp-client/configuration_spec.rb
105
+ - spec/tcp-client/default_configuration_spec.rb
106
+ - spec/tcp-client/version_spec.rb
107
+ - spec/tcp_client_spec.rb
88
108
  - tcp-client.gemspec
89
- - test/helper.rb
90
- - test/tcp-client/address_test.rb
91
- - test/tcp-client/configuration_test.rb
92
- - test/tcp-client/deadline_test.rb
93
- - test/tcp-client/default_configuration_test.rb
94
- - test/tcp-client/version_test.rb
95
- - test/tcp_client_test.rb
96
109
  homepage: https://github.com/mblumtritt/tcp-client
97
110
  licenses:
98
111
  - BSD-3-Clause
99
112
  metadata:
100
113
  source_code_uri: https://github.com/mblumtritt/tcp-client
114
+ documentation_uri: https://rubydoc.info/github/mblumtritt/tcp-client
101
115
  bug_tracker_uri: https://github.com/mblumtritt/tcp-client/issues
102
116
  post_install_message:
103
117
  rdoc_options: []
@@ -119,10 +133,9 @@ signing_key:
119
133
  specification_version: 4
120
134
  summary: A TCP client implementation with working timeout support.
121
135
  test_files:
122
- - test/helper.rb
123
- - test/tcp-client/address_test.rb
124
- - test/tcp-client/configuration_test.rb
125
- - test/tcp-client/deadline_test.rb
126
- - test/tcp-client/default_configuration_test.rb
127
- - test/tcp-client/version_test.rb
128
- - test/tcp_client_test.rb
136
+ - spec/helper.rb
137
+ - spec/tcp-client/address_spec.rb
138
+ - spec/tcp-client/configuration_spec.rb
139
+ - spec/tcp-client/default_configuration_spec.rb
140
+ - spec/tcp-client/version_spec.rb
141
+ - spec/tcp_client_spec.rb