tcp-client 0.2.3 → 0.3.0
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.
- checksums.yaml +4 -4
- data/lib/tcp-client.rb +7 -9
- data/lib/tcp-client/address.rb +5 -3
- data/lib/tcp-client/configuration.rb +7 -5
- data/lib/tcp-client/errors.rb +41 -18
- data/lib/tcp-client/mixin/io_with_deadline.rb +23 -23
- data/lib/tcp-client/ssl_socket.rb +3 -3
- data/lib/tcp-client/version.rb +1 -1
- data/test/tcp_client_test.rb +2 -2
- metadata +2 -3
- data/lib/tcp-client/mixin/io_timeout.rb +0 -118
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9ec3cdc6d442ed7e748f9993d32be41d3dbe20b06bbced35ba0f5ba96caf47f
|
4
|
+
data.tar.gz: ef601054036a671f2073e6b8bbe02e0c23c182a5646db715e27a96a8b32a2485
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd9f757eb175ecf86be1973126991e8a6b4cf50013c4d54854d9accff007e235003f5506b509729e8158ca1c43c05405ecc15802e207a719f44f45d22a4973d7
|
7
|
+
data.tar.gz: d30bd7e5361f310b49bf20357e814a495d0a3b7962f0a6a59e108cb978f0ca1785cc8ba5a4c349d90e0c191cd50dfb7a26b2084f7f36cb590f48237c592ea77c
|
data/lib/tcp-client.rb
CHANGED
@@ -29,7 +29,7 @@ class TCPClient
|
|
29
29
|
|
30
30
|
def connect(addr, configuration, exception: nil)
|
31
31
|
close
|
32
|
-
NoOpenSSL
|
32
|
+
raise(NoOpenSSL) if configuration.ssl? && !defined?(SSLSocket)
|
33
33
|
@address = Address.new(addr)
|
34
34
|
@cfg = configuration.dup
|
35
35
|
exception ||= configuration.connect_timeout_error
|
@@ -54,9 +54,9 @@ class TCPClient
|
|
54
54
|
|
55
55
|
def with_deadline(timeout)
|
56
56
|
previous_deadline = @deadline
|
57
|
-
NoBlockGiven
|
57
|
+
raise(NoBlockGiven) unless block_given?
|
58
58
|
tm = timeout&.to_f
|
59
|
-
InvalidDeadLine
|
59
|
+
raise(InvalidDeadLine) unless tm&.positive?
|
60
60
|
@deadline = Time.now + tm
|
61
61
|
yield(self)
|
62
62
|
ensure
|
@@ -64,7 +64,7 @@ class TCPClient
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def read(nbytes, timeout: nil, exception: nil)
|
67
|
-
NotConnected
|
67
|
+
raise(NotConnected) if closed?
|
68
68
|
timeout.nil? && @deadline and
|
69
69
|
return read_with_deadline(nbytes, @deadline, exception)
|
70
70
|
timeout = (timeout || @cfg.read_timeout).to_f
|
@@ -73,7 +73,7 @@ class TCPClient
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def write(*msg, timeout: nil, exception: nil)
|
76
|
-
NotConnected
|
76
|
+
raise(NotConnected) if closed?
|
77
77
|
timeout.nil? && @deadline and
|
78
78
|
return write_with_deadline(msg, @deadline, exception)
|
79
79
|
timeout = (timeout || @cfg.write_timeout).to_f
|
@@ -98,10 +98,8 @@ class TCPClient
|
|
98
98
|
|
99
99
|
def write_with_deadline(msg, deadline, exception)
|
100
100
|
exception ||= @cfg.write_timeout_error
|
101
|
-
|
102
|
-
|
103
|
-
result += @socket.write_with_deadline(chunk.b, deadline, exception)
|
101
|
+
msg.sum do |chunk|
|
102
|
+
@socket.write_with_deadline(chunk.b, deadline, exception)
|
104
103
|
end
|
105
|
-
result
|
106
104
|
end
|
107
105
|
end
|
data/lib/tcp-client/address.rb
CHANGED
@@ -10,10 +10,10 @@ class TCPClient
|
|
10
10
|
case addr
|
11
11
|
when self.class
|
12
12
|
init_from_selfclass(addr)
|
13
|
-
when Integer
|
14
|
-
init_from_addrinfo(Addrinfo.tcp(nil, addr))
|
15
13
|
when Addrinfo
|
16
14
|
init_from_addrinfo(addr)
|
15
|
+
when Integer
|
16
|
+
init_from_addrinfo(Addrinfo.tcp(nil, addr))
|
17
17
|
else
|
18
18
|
init_from_string(addr)
|
19
19
|
end
|
@@ -59,7 +59,9 @@ class TCPClient
|
|
59
59
|
def from_string(str)
|
60
60
|
idx = str.rindex(':') or return nil, str.to_i
|
61
61
|
name = str[0, idx]
|
62
|
-
|
62
|
+
if name.start_with?('[') && name.end_with?(']')
|
63
|
+
name = name[1, name.size - 2]
|
64
|
+
end
|
63
65
|
[name, str[idx + 1, str.size - idx].to_i]
|
64
66
|
end
|
65
67
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'errors'
|
2
4
|
|
3
5
|
class TCPClient
|
@@ -80,23 +82,23 @@ class TCPClient
|
|
80
82
|
end
|
81
83
|
|
82
84
|
def timeout_error=(exception)
|
83
|
-
|
85
|
+
raise(NotAnException, exception) unless exception_class?(exception)
|
84
86
|
@connect_timeout_error =
|
85
87
|
@read_timeout_error = @write_timeout_error = exception
|
86
88
|
end
|
87
89
|
|
88
90
|
def connect_timeout_error=(exception)
|
89
|
-
|
91
|
+
raise(NotAnException, exception) unless exception_class?(exception)
|
90
92
|
@connect_timeout_error = exception
|
91
93
|
end
|
92
94
|
|
93
95
|
def read_timeout_error=(exception)
|
94
|
-
|
96
|
+
raise(NotAnException, exception) unless exception_class?(exception)
|
95
97
|
@read_timeout_error = exception
|
96
98
|
end
|
97
99
|
|
98
100
|
def write_timeout_error=(exception)
|
99
|
-
|
101
|
+
raise(NotAnException, exception) unless exception_class?(exception)
|
100
102
|
@write_timeout_error = exception
|
101
103
|
end
|
102
104
|
|
@@ -134,7 +136,7 @@ class TCPClient
|
|
134
136
|
def set(attribute, value)
|
135
137
|
public_send("#{attribute}=", value)
|
136
138
|
rescue NoMethodError
|
137
|
-
|
139
|
+
raise(UnknownAttribute, attribute)
|
138
140
|
end
|
139
141
|
|
140
142
|
def seconds(value)
|
data/lib/tcp-client/errors.rb
CHANGED
@@ -4,45 +4,68 @@ require 'socket'
|
|
4
4
|
|
5
5
|
class TCPClient
|
6
6
|
class NoOpenSSL < RuntimeError
|
7
|
-
def
|
8
|
-
|
7
|
+
def initialize
|
8
|
+
super('OpenSSL is not avail')
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
class NoBlockGiven <
|
13
|
-
def
|
14
|
-
|
12
|
+
class NoBlockGiven < ArgumentError
|
13
|
+
def initialize
|
14
|
+
super('no block given')
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
class InvalidDeadLine < ArgumentError
|
19
|
-
def
|
20
|
-
|
19
|
+
def initialize(timeout)
|
20
|
+
super("invalid deadline - #{timeout}")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
class UnknownAttribute < ArgumentError
|
25
|
-
def
|
26
|
-
|
25
|
+
def initialize(attribute)
|
26
|
+
super("unknown attribute - #{attribute}")
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
class NotAnException < TypeError
|
31
|
-
def
|
32
|
-
|
31
|
+
def initialize(object)
|
32
|
+
super("not a valid exception class - #{object.inspect}")
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
class NotConnected <
|
37
|
-
def
|
38
|
-
|
36
|
+
class NotConnected < IOError
|
37
|
+
def initialize
|
38
|
+
super('client not connected')
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
TimeoutError
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
class TimeoutError < IOError
|
43
|
+
def initialize(message = nil)
|
44
|
+
super(message || "unable to #{action} in time")
|
45
|
+
end
|
46
|
+
|
47
|
+
def action
|
48
|
+
:process
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class ConnectTimeoutError < TimeoutError
|
53
|
+
def action
|
54
|
+
:connect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class ReadTimeoutError < TimeoutError
|
59
|
+
def action
|
60
|
+
:read
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class WriteTimeoutError < TimeoutError
|
65
|
+
def action
|
66
|
+
:write
|
67
|
+
end
|
68
|
+
end
|
46
69
|
|
47
70
|
Timeout = TimeoutError # backward compatibility
|
48
71
|
deprecate_constant(:Timeout)
|
@@ -1,38 +1,38 @@
|
|
1
1
|
module IOWithDeadlineMixin
|
2
2
|
def self.included(mod)
|
3
|
-
|
4
|
-
if
|
3
|
+
methods = mod.instance_methods
|
4
|
+
if methods.index(:wait_writable) && methods.index(:wait_readable)
|
5
5
|
mod.include(ViaWaitMethod)
|
6
6
|
else
|
7
7
|
mod.include(ViaSelect)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def read_with_deadline(
|
12
|
-
raise(
|
11
|
+
def read_with_deadline(bytes_to_read, deadline, exception)
|
12
|
+
raise(exception) if Time.now > deadline
|
13
13
|
result = ''.b
|
14
|
-
return result if
|
14
|
+
return result if bytes_to_read <= 0
|
15
15
|
loop do
|
16
16
|
read =
|
17
|
-
with_deadline(deadline,
|
18
|
-
read_nonblock(
|
17
|
+
with_deadline(deadline, exception) do
|
18
|
+
read_nonblock(bytes_to_read - result.bytesize, exception: false)
|
19
19
|
end
|
20
20
|
unless read
|
21
21
|
close
|
22
22
|
return result
|
23
23
|
end
|
24
24
|
result += read
|
25
|
-
return result if result.bytesize >=
|
25
|
+
return result if result.bytesize >= bytes_to_read
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def write_with_deadline(data, deadline,
|
30
|
-
raise(
|
29
|
+
def write_with_deadline(data, deadline, exception)
|
30
|
+
raise(exception) if Time.now > deadline
|
31
31
|
return 0 if (size = data.bytesize).zero?
|
32
32
|
result = 0
|
33
33
|
loop do
|
34
34
|
written =
|
35
|
-
with_deadline(deadline,
|
35
|
+
with_deadline(deadline, exception) do
|
36
36
|
write_nonblock(data, exception: false)
|
37
37
|
end
|
38
38
|
result += written
|
@@ -42,40 +42,40 @@ module IOWithDeadlineMixin
|
|
42
42
|
end
|
43
43
|
|
44
44
|
module ViaWaitMethod
|
45
|
-
private def with_deadline(deadline,
|
45
|
+
private def with_deadline(deadline, exception)
|
46
46
|
loop do
|
47
47
|
case ret = yield
|
48
48
|
when :wait_writable
|
49
|
-
raise(
|
50
|
-
raise(
|
49
|
+
raise(exception) if (remaining_time = deadline - Time.now) <= 0
|
50
|
+
raise(exception) if wait_writable(remaining_time).nil?
|
51
51
|
when :wait_readable
|
52
|
-
raise(
|
53
|
-
raise(
|
52
|
+
raise(exception) if (remaining_time = deadline - Time.now) <= 0
|
53
|
+
raise(exception) if wait_readable(remaining_time).nil?
|
54
54
|
else
|
55
55
|
return ret
|
56
56
|
end
|
57
57
|
end
|
58
58
|
rescue Errno::ETIMEDOUT
|
59
|
-
raise(
|
59
|
+
raise(exception)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
module ViaSelect
|
64
|
-
private def with_deadline(deadline,
|
64
|
+
private def with_deadline(deadline, exception)
|
65
65
|
loop do
|
66
66
|
case ret = yield
|
67
67
|
when :wait_writable
|
68
|
-
raise(
|
69
|
-
raise(
|
68
|
+
raise(exception) if (remaining_time = deadline - Time.now) <= 0
|
69
|
+
raise(exception) if ::IO.select(nil, [self], nil, remaining_time).nil?
|
70
70
|
when :wait_readable
|
71
|
-
raise(
|
72
|
-
raise(
|
71
|
+
raise(exception) if (remaining_time = deadline - Time.now) <= 0
|
72
|
+
raise(exception) if ::IO.select([self], nil, nil, remaining_time).nil?
|
73
73
|
else
|
74
74
|
return ret
|
75
75
|
end
|
76
76
|
end
|
77
77
|
rescue Errno::ETIMEDOUT
|
78
|
-
raise(
|
78
|
+
raise(exception)
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -24,9 +24,9 @@ class TCPClient
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def create_context(ssl_params)
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
context = OpenSSL::SSL::SSLContext.new
|
28
|
+
context.set_params(ssl_params)
|
29
|
+
context
|
30
30
|
end
|
31
31
|
|
32
32
|
def connect_to(address, check, timeout, exception)
|
data/lib/tcp-client/version.rb
CHANGED
data/test/tcp_client_test.rb
CHANGED
@@ -118,7 +118,7 @@ class TCPClientTest < MiniTest::Test
|
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
121
|
-
def
|
121
|
+
def test_read_write_deadline
|
122
122
|
TCPClient.open('localhost:1234', config) do |subject|
|
123
123
|
refute(subject.closed?)
|
124
124
|
assert_raises(TCPClient::TimeoutError) do
|
@@ -139,7 +139,7 @@ class TCPClientTest < MiniTest::Test
|
|
139
139
|
start_time = Time.now
|
140
140
|
TCPClient.new.connect('localhost:1234', ssl_config)
|
141
141
|
end
|
142
|
-
assert_in_delta(ssl_config.connect_timeout, Time.now - start_time, 0.
|
142
|
+
assert_in_delta(ssl_config.connect_timeout, Time.now - start_time, 0.25)
|
143
143
|
end
|
144
144
|
|
145
145
|
def test_connect_ssl_timeout
|
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.
|
4
|
+
version: 0.3.0
|
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-
|
11
|
+
date: 2021-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,7 +72,6 @@ files:
|
|
72
72
|
- lib/tcp-client/configuration.rb
|
73
73
|
- lib/tcp-client/default_configuration.rb
|
74
74
|
- lib/tcp-client/errors.rb
|
75
|
-
- lib/tcp-client/mixin/io_timeout.rb
|
76
75
|
- lib/tcp-client/mixin/io_with_deadline.rb
|
77
76
|
- lib/tcp-client/ssl_socket.rb
|
78
77
|
- lib/tcp-client/tcp_socket.rb
|
@@ -1,118 +0,0 @@
|
|
1
|
-
# I keep this file for backward compatibility.
|
2
|
-
# This may be removed in one of the next versions.
|
3
|
-
# Let me know if you like to use it elsewhere...
|
4
|
-
|
5
|
-
IOTimeoutError = Class.new(IOError) unless defined?(IOTimeoutError)
|
6
|
-
|
7
|
-
module IOTimeoutMixin
|
8
|
-
def self.included(mod)
|
9
|
-
im = mod.instance_methods
|
10
|
-
if im.index(:wait_writable) && im.index(:wait_readable)
|
11
|
-
mod.include(DeadlineMethods)
|
12
|
-
else
|
13
|
-
mod.include(DeadlineIO)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def read_with_deadline(nbytes, deadline, exception)
|
18
|
-
result = ''.b
|
19
|
-
return result if nbytes.zero?
|
20
|
-
loop do
|
21
|
-
junk_size = nbytes - result.bytesize
|
22
|
-
read =
|
23
|
-
with_deadline(deadline, exception) do
|
24
|
-
read_nonblock(junk_size, exception: false)
|
25
|
-
end
|
26
|
-
unless read
|
27
|
-
close
|
28
|
-
return result
|
29
|
-
end
|
30
|
-
result += read
|
31
|
-
return result if result.bytesize >= nbytes
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def read(nbytes, timeout: nil, exception: IOTimeoutError)
|
36
|
-
timeout = timeout.to_f
|
37
|
-
return read_all(nbytes) { |junk_size| super(junk_size) } if timeout <= 0
|
38
|
-
deadline = Time.now + timeout
|
39
|
-
read_all(nbytes) do |junk_size|
|
40
|
-
with_deadline(deadline, exception) do
|
41
|
-
read_nonblock(junk_size, exception: false)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def write(*msgs, timeout: nil, exception: IOTimeoutError)
|
47
|
-
timeout = timeout.to_f
|
48
|
-
return write_all(msgs.join.b) { |junk| super(junk) } if timeout <= 0
|
49
|
-
deadline = Time.now + timeout
|
50
|
-
write_all(msgs.join.b) do |junk|
|
51
|
-
with_deadline(deadline, exception) do
|
52
|
-
write_nonblock(junk, exception: false)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def read_all(nbytes)
|
60
|
-
return ''.b if nbytes.zero?
|
61
|
-
result = ''.b
|
62
|
-
loop do
|
63
|
-
unless read = yield(nbytes - result.bytesize)
|
64
|
-
close
|
65
|
-
return result
|
66
|
-
end
|
67
|
-
result += read
|
68
|
-
return result if result.bytesize >= nbytes
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def write_all(data)
|
73
|
-
return 0 if (size = data.bytesize).zero?
|
74
|
-
result = 0
|
75
|
-
loop do
|
76
|
-
written = yield(data)
|
77
|
-
result += written
|
78
|
-
return result if result >= size
|
79
|
-
data = data.byteslice(written, data.bytesize - written)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
module DeadlineMethods
|
84
|
-
private def with_deadline(deadline, exclass)
|
85
|
-
loop do
|
86
|
-
case ret = yield
|
87
|
-
when :wait_writable
|
88
|
-
raise(exclass) if (remaining_time = deadline - Time.now) <= 0
|
89
|
-
raise(exclass) if wait_writable(remaining_time).nil?
|
90
|
-
when :wait_readable
|
91
|
-
raise(exclass) if (remaining_time = deadline - Time.now) <= 0
|
92
|
-
raise(exclass) if wait_readable(remaining_time).nil?
|
93
|
-
else
|
94
|
-
return ret
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
module DeadlineIO
|
101
|
-
private def with_deadline(deadline, exclass)
|
102
|
-
loop do
|
103
|
-
case ret = yield
|
104
|
-
when :wait_writable
|
105
|
-
raise(exclass) if (remaining_time = deadline - Time.now) <= 0
|
106
|
-
raise(exclass) if ::IO.select(nil, [self], nil, remaining_time).nil?
|
107
|
-
when :wait_readable
|
108
|
-
raise(exclass) if (remaining_time = deadline - Time.now) <= 0
|
109
|
-
raise(exclass) if ::IO.select([self], nil, nil, remaining_time).nil?
|
110
|
-
else
|
111
|
-
return ret
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
private_constant(:DeadlineMethods, :DeadlineIO)
|
118
|
-
end
|