tcp-client 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/tcp-client.rb +38 -27
- data/lib/tcp-client/configuration.rb +55 -14
- data/lib/tcp-client/errors.rb +12 -0
- data/lib/tcp-client/version.rb +1 -1
- data/test/tcp-client/configuration_test.rb +44 -1
- data/test/tcp_client_test.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 404b30eadc3a1f6f255257328ad49b1cd0827bd502f86e08d283bb94a1ab6447
|
4
|
+
data.tar.gz: e4825578860b8b74ae180988beecf1faef75230e456e20da9a5ce86baa4c729e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 662962d4c32d91547f15623c92a8ae7951cb96189619bea97ea55d02725a2d05c7ea665f92d27e9dbbbb3e541d1e5bb04b40437a54e986d1240fa19b77bd91ae
|
7
|
+
data.tar.gz: dbc18f0ee4b6d025f5fe1a2a9be257ad280687dc3b165d4e2bfbc3d68976548172df2da865a58ad0801ac6c0b50244d33438f08570c33f852143bb6c16309ab1
|
data/lib/tcp-client.rb
CHANGED
@@ -20,22 +20,22 @@ class TCPClient
|
|
20
20
|
attr_reader :address
|
21
21
|
|
22
22
|
def initialize
|
23
|
-
@socket = @address = @
|
23
|
+
@socket = @address = @deadline = @cfg = nil
|
24
24
|
end
|
25
25
|
|
26
26
|
def to_s
|
27
27
|
@address ? @address.to_s : ''
|
28
28
|
end
|
29
29
|
|
30
|
-
def connect(addr, configuration, exception:
|
30
|
+
def connect(addr, configuration, exception: nil)
|
31
31
|
close
|
32
32
|
NoOpenSSL.raise! if configuration.ssl? && !defined?(SSLSocket)
|
33
33
|
@address = Address.new(addr)
|
34
|
-
@
|
35
|
-
configuration.
|
34
|
+
@cfg = configuration.dup
|
35
|
+
exception ||= configuration.connect_timeout_error
|
36
|
+
@socket = TCPSocket.new(@address, @cfg, exception)
|
37
|
+
@cfg.ssl? &&
|
36
38
|
@socket = SSLSocket.new(@socket, @address, configuration, exception)
|
37
|
-
@write_timeout = configuration.write_timeout
|
38
|
-
@read_timeout = configuration.read_timeout
|
39
39
|
self
|
40
40
|
end
|
41
41
|
|
@@ -53,8 +53,8 @@ class TCPClient
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def with_deadline(timeout)
|
56
|
-
NoBlockGiven.raise! unless block_given?
|
57
56
|
previous_deadline = @deadline
|
57
|
+
NoBlockGiven.raise! unless block_given?
|
58
58
|
tm = timeout&.to_f
|
59
59
|
InvalidDeadLine.raise! unless tm&.positive?
|
60
60
|
@deadline = Time.now + tm
|
@@ -63,34 +63,45 @@ class TCPClient
|
|
63
63
|
@deadline = previous_deadline
|
64
64
|
end
|
65
65
|
|
66
|
-
def read(nbytes, timeout: nil, exception:
|
66
|
+
def read(nbytes, timeout: nil, exception: nil)
|
67
67
|
NotConnected.raise! if closed?
|
68
|
-
|
69
|
-
return
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
@socket.read_with_deadline(nbytes, Time.now + timeout, exception)
|
74
|
-
else
|
75
|
-
@socket.read(nbytes)
|
76
|
-
end
|
68
|
+
timeout.nil? && @deadline and
|
69
|
+
return read_with_deadline(nbytes, @deadline, exception)
|
70
|
+
timeout = (timeout || @cfg.read_timeout).to_f
|
71
|
+
return @socket.read(nbytes) unless timeout.positive?
|
72
|
+
read_with_deadline(nbytes, Time.now + timeout, exception)
|
77
73
|
end
|
78
74
|
|
79
|
-
def write(*msg, timeout: nil, exception:
|
75
|
+
def write(*msg, timeout: nil, exception: nil)
|
80
76
|
NotConnected.raise! if closed?
|
81
|
-
|
82
|
-
return
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
@socket.write_with_deadline(msg.join.b, Time.now + timeout, exception)
|
87
|
-
else
|
88
|
-
@socket.write(*msg)
|
89
|
-
end
|
77
|
+
timeout.nil? && @deadline and
|
78
|
+
return write_with_deadline(msg, @deadline, exception)
|
79
|
+
timeout = (timeout || @cfg.write_timeout).to_f
|
80
|
+
return @socket.write(*msg) unless timeout.positive?
|
81
|
+
write_with_deadline(msg, Time.now + timeout, exception)
|
90
82
|
end
|
91
83
|
|
92
84
|
def flush
|
93
85
|
@socket.flush unless closed?
|
94
86
|
self
|
95
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def read_with_deadline(nbytes, deadline, exception)
|
92
|
+
@socket.read_with_deadline(
|
93
|
+
nbytes,
|
94
|
+
deadline,
|
95
|
+
exception || @cfg.read_timeout_error
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_with_deadline(msg, deadline, exception)
|
100
|
+
exception ||= @cfg.write_timeout_error
|
101
|
+
result = 0
|
102
|
+
msg.each do |chunk|
|
103
|
+
result += @socket.write_with_deadline(chunk.b, deadline, exception)
|
104
|
+
end
|
105
|
+
result
|
106
|
+
end
|
96
107
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'errors'
|
2
|
+
|
1
3
|
class TCPClient
|
2
4
|
class Configuration
|
3
5
|
def self.create(options = {})
|
@@ -6,15 +8,38 @@ class TCPClient
|
|
6
8
|
ret
|
7
9
|
end
|
8
10
|
|
9
|
-
attr_reader :buffered,
|
11
|
+
attr_reader :buffered,
|
12
|
+
:keep_alive,
|
13
|
+
:reverse_lookup,
|
14
|
+
:timeout,
|
15
|
+
:connect_timeout,
|
16
|
+
:read_timeout,
|
17
|
+
:write_timeout,
|
18
|
+
:connect_timeout_error,
|
19
|
+
:read_timeout_error,
|
20
|
+
:write_timeout_error
|
10
21
|
attr_accessor :ssl_params
|
11
22
|
|
12
23
|
def initialize(options = {})
|
13
24
|
@buffered = @keep_alive = @reverse_lookup = true
|
14
25
|
self.timeout = @ssl_params = nil
|
26
|
+
@connect_timeout_error = ConnectTimeoutError
|
27
|
+
@read_timeout_error = ReadTimeoutError
|
28
|
+
@write_timeout_error = WriteTimeoutError
|
15
29
|
options.each_pair { |attribute, value| set(attribute, value) }
|
16
30
|
end
|
17
31
|
|
32
|
+
def freeze
|
33
|
+
@ssl_params.freeze
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize_copy(_org)
|
38
|
+
super
|
39
|
+
@ssl_params = @ssl_params.dup
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
18
43
|
def ssl?
|
19
44
|
@ssl_params ? true : false
|
20
45
|
end
|
@@ -38,32 +63,41 @@ class TCPClient
|
|
38
63
|
end
|
39
64
|
|
40
65
|
def timeout=(seconds)
|
41
|
-
@timeout =
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
def connect_timeout
|
46
|
-
@connect_timeout || @timeout
|
66
|
+
@timeout =
|
67
|
+
@connect_timeout = @write_timeout = @read_timeout = seconds(seconds)
|
47
68
|
end
|
48
69
|
|
49
70
|
def connect_timeout=(seconds)
|
50
71
|
@connect_timeout = seconds(seconds)
|
51
72
|
end
|
52
73
|
|
53
|
-
def
|
54
|
-
@
|
74
|
+
def read_timeout=(seconds)
|
75
|
+
@read_timeout = seconds(seconds)
|
55
76
|
end
|
56
77
|
|
57
78
|
def write_timeout=(seconds)
|
58
79
|
@write_timeout = seconds(seconds)
|
59
80
|
end
|
60
81
|
|
61
|
-
def
|
62
|
-
|
82
|
+
def timeout_error=(exception)
|
83
|
+
NotAnException.raise!(exception) unless exception_class?(exception)
|
84
|
+
@connect_timeout_error =
|
85
|
+
@read_timeout_error = @write_timeout_error = exception
|
63
86
|
end
|
64
87
|
|
65
|
-
def
|
66
|
-
|
88
|
+
def connect_timeout_error=(exception)
|
89
|
+
NotAnException.raise!(exception) unless exception_class?(exception)
|
90
|
+
@connect_timeout_error = exception
|
91
|
+
end
|
92
|
+
|
93
|
+
def read_timeout_error=(exception)
|
94
|
+
NotAnException.raise!(exception) unless exception_class?(exception)
|
95
|
+
@read_timeout_error = exception
|
96
|
+
end
|
97
|
+
|
98
|
+
def write_timeout_error=(exception)
|
99
|
+
NotAnException.raise!(exception) unless exception_class?(exception)
|
100
|
+
@write_timeout_error = exception
|
67
101
|
end
|
68
102
|
|
69
103
|
def to_h
|
@@ -75,6 +109,9 @@ class TCPClient
|
|
75
109
|
connect_timeout: @connect_timeout,
|
76
110
|
read_timeout: @read_timeout,
|
77
111
|
write_timeout: @write_timeout,
|
112
|
+
connect_timeout_error: @connect_timeout_error,
|
113
|
+
read_timeout_error: @read_timeout_error,
|
114
|
+
write_timeout_error: @write_timeout_error,
|
78
115
|
ssl_params: @ssl_params
|
79
116
|
}
|
80
117
|
end
|
@@ -90,10 +127,14 @@ class TCPClient
|
|
90
127
|
|
91
128
|
private
|
92
129
|
|
130
|
+
def exception_class?(value)
|
131
|
+
value.is_a?(Class) && value < Exception
|
132
|
+
end
|
133
|
+
|
93
134
|
def set(attribute, value)
|
94
135
|
public_send("#{attribute}=", value)
|
95
136
|
rescue NoMethodError
|
96
|
-
raise(
|
137
|
+
UnknownAttribute.raise!(attribute)
|
97
138
|
end
|
98
139
|
|
99
140
|
def seconds(value)
|
data/lib/tcp-client/errors.rb
CHANGED
@@ -21,6 +21,18 @@ class TCPClient
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
class UnknownAttribute < ArgumentError
|
25
|
+
def self.raise!(attribute)
|
26
|
+
raise(self, "unknown attribute - #{attribute}", caller(1))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class NotAnException < TypeError
|
31
|
+
def self.raise!(object)
|
32
|
+
raise(self, "not a valid exception class - #{object.inspect}", caller(1))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
24
36
|
class NotConnected < SocketError
|
25
37
|
def self.raise!
|
26
38
|
raise(self, 'client not connected', caller(1))
|
data/lib/tcp-client/version.rb
CHANGED
@@ -39,7 +39,8 @@ class ConfigurationTest < MiniTest::Test
|
|
39
39
|
connect_timeout: 1,
|
40
40
|
read_timeout: 2,
|
41
41
|
write_timeout: 3,
|
42
|
-
ssl: true
|
42
|
+
ssl: true,
|
43
|
+
connect_timeout_error: IOError
|
43
44
|
)
|
44
45
|
refute(subject.buffered)
|
45
46
|
refute(subject.keep_alive)
|
@@ -48,6 +49,8 @@ class ConfigurationTest < MiniTest::Test
|
|
48
49
|
assert_same(2, subject.read_timeout)
|
49
50
|
assert_same(3, subject.write_timeout)
|
50
51
|
assert(subject.ssl?)
|
52
|
+
assert_same(IOError, subject.connect_timeout_error)
|
53
|
+
assert_same(TCPClient::ReadTimeoutError, subject.read_timeout_error)
|
51
54
|
end
|
52
55
|
|
53
56
|
def test_invalid_option
|
@@ -88,6 +91,18 @@ class ConfigurationTest < MiniTest::Test
|
|
88
91
|
assert_same(42, subject.write_timeout)
|
89
92
|
end
|
90
93
|
|
94
|
+
def test_timeout_error_overwrite
|
95
|
+
subject = TCPClient::Configuration.new
|
96
|
+
assert_same(TCPClient::ConnectTimeoutError, subject.connect_timeout_error)
|
97
|
+
assert_same(TCPClient::ReadTimeoutError, subject.read_timeout_error)
|
98
|
+
assert_same(TCPClient::WriteTimeoutError, subject.write_timeout_error)
|
99
|
+
|
100
|
+
subject.timeout_error = IOError
|
101
|
+
assert_same(IOError, subject.connect_timeout_error)
|
102
|
+
assert_same(IOError, subject.read_timeout_error)
|
103
|
+
assert_same(IOError, subject.write_timeout_error)
|
104
|
+
end
|
105
|
+
|
91
106
|
def test_compare
|
92
107
|
a = TCPClient::Configuration.new
|
93
108
|
b = TCPClient::Configuration.new
|
@@ -95,4 +110,32 @@ class ConfigurationTest < MiniTest::Test
|
|
95
110
|
assert(a == b)
|
96
111
|
assert(a === b)
|
97
112
|
end
|
113
|
+
|
114
|
+
def test_dup
|
115
|
+
source =
|
116
|
+
TCPClient::Configuration.new(
|
117
|
+
buffered: false,
|
118
|
+
keep_alive: false,
|
119
|
+
reverse_lookup: false,
|
120
|
+
connect_timeout: 1,
|
121
|
+
read_timeout: 2,
|
122
|
+
write_timeout: 3,
|
123
|
+
ssl: {
|
124
|
+
ssl_version: :TLSv1_2
|
125
|
+
}
|
126
|
+
)
|
127
|
+
shadow = source.dup.freeze
|
128
|
+
|
129
|
+
# some changes
|
130
|
+
source.buffered = true
|
131
|
+
source.write_timeout = 5
|
132
|
+
source.ssl_params[:err] = true
|
133
|
+
source.timeout_error = IOError
|
134
|
+
|
135
|
+
refute_equal(source.__id__, shadow.__id__)
|
136
|
+
refute(shadow.buffered)
|
137
|
+
assert_equal(3, shadow.write_timeout)
|
138
|
+
assert_equal({ ssl_version: :TLSv1_2 }, shadow.ssl_params)
|
139
|
+
assert_same(TCPClient::ReadTimeoutError, shadow.read_timeout_error)
|
140
|
+
end
|
98
141
|
end
|
data/test/tcp_client_test.rb
CHANGED
@@ -67,7 +67,7 @@ class TCPClientTest < MiniTest::Test
|
|
67
67
|
start_time = Time.now
|
68
68
|
subject.read(42, timeout: timeout)
|
69
69
|
end
|
70
|
-
assert_in_delta(timeout, Time.now - start_time, 0.
|
70
|
+
assert_in_delta(timeout, Time.now - start_time, 0.15)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
@@ -85,7 +85,7 @@ class TCPClientTest < MiniTest::Test
|
|
85
85
|
start_time = Time.now
|
86
86
|
subject.write(*HUGE_AMOUNT_OF_DATA, timeout: timeout)
|
87
87
|
end
|
88
|
-
assert_in_delta(timeout, Time.now - start_time, 0.
|
88
|
+
assert_in_delta(timeout, Time.now - start_time, 0.15)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
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.2.
|
4
|
+
version: 0.2.2
|
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-02-
|
11
|
+
date: 2021-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|