tcp-client 0.9.3 → 0.10.1
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/README.md +8 -5
- data/lib/tcp-client/address.rb +16 -7
- data/lib/tcp-client/configuration.rb +37 -45
- data/lib/tcp-client/deadline.rb +1 -3
- data/lib/tcp-client/default_configuration.rb +1 -1
- data/lib/tcp-client/errors.rb +13 -7
- data/lib/tcp-client/mixin/io_with_deadline.rb +126 -88
- data/lib/tcp-client/ssl_socket.rb +1 -1
- data/lib/tcp-client/version.rb +2 -1
- data/lib/tcp-client.rb +87 -51
- data/rakefile.rb +7 -1
- data/sample/google_ssl.rb +8 -5
- data/spec/tcp-client/address_spec.rb +15 -28
- data/spec/tcp-client/configuration_spec.rb +4 -4
- data/spec/tcp_client_spec.rb +227 -23
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02440fe17a1480ba18bec8b6918cea6420a065d997c423a6602e165e0f316564
|
4
|
+
data.tar.gz: 73e315f2c80a2f0f1cf0cb57b24f2f2cd45e10caffebd83dd47510ea85de1f25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb22bb974aacd8cfc9d4a1ebcc66393172ec949db8038ecbc37c1a4187bb8a1d703206625f25fb7dba5e336217d2de31475220aa4147bc20df26a5deff7bd7b7
|
7
|
+
data.tar.gz: 0fd048dda8d10ba76659276cd7f5c09e1fba73560d482a5cbbb35ba6db034192137552531dcd294d45a50a10a211839b318015d0546ded7df28c8f55246fe7fd
|
data/README.md
CHANGED
@@ -27,11 +27,14 @@ cfg = TCPClient::Configuration.create(
|
|
27
27
|
# - limit all network interactions to 1.5 seconds
|
28
28
|
# - use the Configuration cfg
|
29
29
|
# - send a simple HTTP get request
|
30
|
-
# - read
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
# - read the returned message and headers
|
31
|
+
response =
|
32
|
+
TCPClient.with_deadline(1.5, 'www.google.com:443', cfg) do |client|
|
33
|
+
client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n") #=> 40
|
34
|
+
client.readline("\r\n\r\n") #=> see response
|
35
|
+
end
|
36
|
+
|
37
|
+
puts(response)
|
35
38
|
```
|
36
39
|
|
37
40
|
For more samples see [the samples dir](https://github.com/mblumtritt/tcp-client/tree/main/sample)
|
data/lib/tcp-client/address.rb
CHANGED
@@ -41,7 +41,7 @@ class TCPClient
|
|
41
41
|
# @param addrinfo [Addrinfo] containing the addressed host and port
|
42
42
|
#
|
43
43
|
# @overload initialize(port)
|
44
|
-
#
|
44
|
+
# Addresses the port on the local machine.
|
45
45
|
#
|
46
46
|
# @example create an Address for localhost on port 80
|
47
47
|
# Address.new(80)
|
@@ -62,19 +62,28 @@ class TCPClient
|
|
62
62
|
@addrinfo.freeze
|
63
63
|
end
|
64
64
|
|
65
|
+
#
|
66
|
+
# @attribute [r] port
|
67
|
+
# @return [Integer] the port number
|
68
|
+
#
|
69
|
+
def port
|
70
|
+
@addrinfo.ip_port
|
71
|
+
end
|
72
|
+
|
65
73
|
#
|
66
74
|
# @return [String] text representation of self as "host:port"
|
67
75
|
#
|
68
76
|
def to_s
|
69
|
-
|
70
|
-
"#{@hostname}:#{@addrinfo.ip_port}"
|
77
|
+
hostname.index(':') ? "[#{hostname}]:#{port}" : "#{hostname}:#{port}"
|
71
78
|
end
|
72
79
|
|
73
80
|
#
|
74
|
-
#
|
81
|
+
# Convert `self` to a Hash containing host and port attribute.
|
82
|
+
#
|
83
|
+
# @return [Hash] host and port
|
75
84
|
#
|
76
85
|
def to_h
|
77
|
-
{ host:
|
86
|
+
{ host: hostname, port: port }
|
78
87
|
end
|
79
88
|
|
80
89
|
# @!visibility private
|
@@ -96,7 +105,7 @@ class TCPClient
|
|
96
105
|
end
|
97
106
|
|
98
107
|
def init_from_addrinfo(addrinfo)
|
99
|
-
@hostname
|
108
|
+
@hostname = addrinfo.getnameinfo(Socket::NI_NUMERICSERV).first
|
100
109
|
@addrinfo = addrinfo
|
101
110
|
end
|
102
111
|
|
@@ -109,7 +118,7 @@ class TCPClient
|
|
109
118
|
def from_string(str)
|
110
119
|
idx = str.rindex(':') or return nil, str.to_i
|
111
120
|
name = str[0, idx].delete_prefix('[').delete_suffix(']')
|
112
|
-
[name, str[idx + 1, str.size - idx].to_i]
|
121
|
+
[name.empty? ? nil : name, str[idx + 1, str.size - idx].to_i]
|
113
122
|
end
|
114
123
|
end
|
115
124
|
end
|
@@ -6,24 +6,14 @@ class TCPClient
|
|
6
6
|
#
|
7
7
|
# A Configuration is used to configure the behavior of a {TCPClient} instance.
|
8
8
|
#
|
9
|
-
# It allows to specify
|
9
|
+
# It allows to specify the monitor timeout, how to handle exceptions, if SSL
|
10
10
|
# should be used and to setup the underlying Socket.
|
11
11
|
#
|
12
12
|
class Configuration
|
13
13
|
#
|
14
|
-
#
|
15
|
-
# Shorthand to create a new configuration with given options.
|
16
|
-
#
|
17
|
-
# @example
|
18
|
-
# config = TCPClient::Configuration.create(buffered: false)
|
19
|
-
#
|
20
|
-
# @param options [Hash] see {#initialize} for details
|
21
|
-
#
|
22
|
-
# @return [Configuration] the initialized configuration
|
23
|
-
#
|
24
|
-
# @overload create(&block)
|
25
|
-
# Shorthand to create a new configuration within a code block.
|
14
|
+
# Shorthand to create a new configuration.
|
26
15
|
#
|
16
|
+
# @overload create()
|
27
17
|
# @example
|
28
18
|
# config = TCPClient::Configuration.create do |cfg|
|
29
19
|
# cfg.buffered = false
|
@@ -32,7 +22,13 @@ class TCPClient
|
|
32
22
|
#
|
33
23
|
# @yieldparam configuration {Configuration}
|
34
24
|
#
|
35
|
-
#
|
25
|
+
# @overload create(options)
|
26
|
+
# @example
|
27
|
+
# config = TCPClient::Configuration.create(buffered: false)
|
28
|
+
#
|
29
|
+
# @param options [{Symbol => Object}] see {#initialize} for details
|
30
|
+
#
|
31
|
+
# @return [Configuration] the initialized configuration
|
36
32
|
#
|
37
33
|
def self.create(options = {})
|
38
34
|
configuration = new(options)
|
@@ -41,25 +37,25 @@ class TCPClient
|
|
41
37
|
end
|
42
38
|
|
43
39
|
#
|
44
|
-
#
|
40
|
+
# Initializes the instance with given options.
|
45
41
|
#
|
46
|
-
# @param options [
|
42
|
+
# @param options [{Symbol => Object}]
|
47
43
|
# @option options [Boolean] :buffered, see {#buffered}
|
48
44
|
# @option options [Boolean] :keep_alive, see {#keep_alive}
|
49
45
|
# @option options [Boolean] :reverse_lookup, see {#reverse_lookup}
|
50
|
-
# @option options [
|
46
|
+
# @option options [{Symbol => Object}] :ssl_params, see {#ssl_params}
|
51
47
|
# @option options [Numeric] :connect_timeout, see {#connect_timeout}
|
52
|
-
# @option options [Exception] :connect_timeout_error, see
|
48
|
+
# @option options [Class<Exception>] :connect_timeout_error, see
|
53
49
|
# {#connect_timeout_error}
|
54
50
|
# @option options [Numeric] :read_timeout, see {#read_timeout}
|
55
|
-
# @option options [Exception] :read_timeout_error, see
|
51
|
+
# @option options [Class<Exception>] :read_timeout_error, see
|
52
|
+
# {#read_timeout_error}
|
56
53
|
# @option options [Numeric] :write_timeout, see {#write_timeout}
|
57
|
-
# @option options [Exception] :write_timeout_error, see
|
54
|
+
# @option options [Class<Exception>] :write_timeout_error, see
|
58
55
|
# {#write_timeout_error}
|
59
56
|
# @option options [Boolean] :normalize_network_errors, see
|
60
57
|
# {#normalize_network_errors}
|
61
58
|
#
|
62
|
-
#
|
63
59
|
def initialize(options = {})
|
64
60
|
@buffered = @keep_alive = @reverse_lookup = true
|
65
61
|
self.timeout = @ssl_params = nil
|
@@ -75,9 +71,8 @@ class TCPClient
|
|
75
71
|
#
|
76
72
|
# Enables/disables use of Socket-level buffering
|
77
73
|
#
|
78
|
-
# @return [
|
79
|
-
# (default)
|
80
|
-
# @return [false] if buffering is not allowed
|
74
|
+
# @return [Boolean] whether the connection is allowed to use internal
|
75
|
+
# buffers (default) or not
|
81
76
|
#
|
82
77
|
attr_reader :buffered
|
83
78
|
|
@@ -88,9 +83,8 @@ class TCPClient
|
|
88
83
|
#
|
89
84
|
# Enables/disables use of Socket-level keep alive handling.
|
90
85
|
#
|
91
|
-
# @return [
|
92
|
-
# (default)
|
93
|
-
# @return [false] if the connection should not check keep alive
|
86
|
+
# @return [Boolean] whether the connection is allowed to use keep alive
|
87
|
+
# signals (default) or not
|
94
88
|
#
|
95
89
|
attr_reader :keep_alive
|
96
90
|
|
@@ -101,9 +95,8 @@ class TCPClient
|
|
101
95
|
#
|
102
96
|
# Enables/disables address lookup.
|
103
97
|
#
|
104
|
-
# @return [
|
105
|
-
# (default)
|
106
|
-
# @return [false] if the address lookup is not required
|
98
|
+
# @return [Boolean] whether the connection is allowed to lookup the address
|
99
|
+
# (default) or not
|
107
100
|
#
|
108
101
|
attr_reader :reverse_lookup
|
109
102
|
|
@@ -113,16 +106,17 @@ class TCPClient
|
|
113
106
|
|
114
107
|
#
|
115
108
|
# @!parse attr_reader :ssl?
|
116
|
-
# @return [Boolean]
|
109
|
+
# @return [Boolean] whether SSL is configured, see {#ssl_params}
|
117
110
|
#
|
118
111
|
def ssl?
|
119
112
|
@ssl_params ? true : false
|
120
113
|
end
|
121
114
|
|
122
115
|
#
|
123
|
-
# Parameters used to initialize a SSL context.
|
116
|
+
# Parameters used to initialize a SSL context. SSL/TLS will only be used if
|
117
|
+
# this attribute is not `nil`.
|
124
118
|
#
|
125
|
-
# @return [
|
119
|
+
# @return [{Symbol => Object}] SSL parameters for the SSL context
|
126
120
|
# @return [nil] if no SSL should be used (default)
|
127
121
|
#
|
128
122
|
attr_reader :ssl_params
|
@@ -161,7 +155,7 @@ class TCPClient
|
|
161
155
|
# The exception class which will be raised if {TCPClient#connect} can not
|
162
156
|
# be finished in time.
|
163
157
|
#
|
164
|
-
# @return [Class] exception class raised
|
158
|
+
# @return [Class<Exception>] exception class raised
|
165
159
|
# @raise [NotAnExceptionError] if given argument is not an Exception class
|
166
160
|
#
|
167
161
|
attr_reader :connect_timeout_error
|
@@ -189,7 +183,7 @@ class TCPClient
|
|
189
183
|
# The exception class which will be raised if {TCPClient#read} can not be
|
190
184
|
# finished in time.
|
191
185
|
#
|
192
|
-
# @return [Class] exception class raised
|
186
|
+
# @return [Class<Exception>] exception class raised
|
193
187
|
# @raise [NotAnExceptionError] if given argument is not an Exception class
|
194
188
|
#
|
195
189
|
attr_reader :read_timeout_error
|
@@ -217,7 +211,7 @@ class TCPClient
|
|
217
211
|
# The exception class which will be raised if {TCPClient#write} can not be
|
218
212
|
# finished in time.
|
219
213
|
#
|
220
|
-
# @return [Class] exception class raised
|
214
|
+
# @return [Class<Exception>] exception class raised
|
221
215
|
# @raise [NotAnExceptionError] if given argument is not an Exception class
|
222
216
|
#
|
223
217
|
attr_reader :write_timeout_error
|
@@ -231,7 +225,7 @@ class TCPClient
|
|
231
225
|
# @attribute [w] timeout
|
232
226
|
# Shorthand to set maximum time in seconds for all timeout monitoring.
|
233
227
|
#
|
234
|
-
# @return [Numeric] maximum time in seconds for any
|
228
|
+
# @return [Numeric] maximum time in seconds for any action
|
235
229
|
# @return [nil] if all timeout monitoring should be disabled (default)
|
236
230
|
#
|
237
231
|
# @see #connect_timeout
|
@@ -244,9 +238,10 @@ class TCPClient
|
|
244
238
|
|
245
239
|
#
|
246
240
|
# @attribute [w] timeout_error
|
247
|
-
# Shorthand to set the exception class
|
241
|
+
# Shorthand to set the exception class which will by raised by any reached
|
242
|
+
# timeout.
|
248
243
|
#
|
249
|
-
# @return [Class] exception class raised
|
244
|
+
# @return [Class<Exception>] exception class raised
|
250
245
|
#
|
251
246
|
# @raise [NotAnExceptionError] if given argument is not an Exception class
|
252
247
|
#
|
@@ -270,9 +265,8 @@ class TCPClient
|
|
270
265
|
# manner. If this option is set to true all these error cases are raised as
|
271
266
|
# {NetworkError} and can be easily captured.
|
272
267
|
#
|
273
|
-
# @return [
|
274
|
-
# {NetworkError}
|
275
|
-
# @return [false] if socket/system errors should not be normalzed (default)
|
268
|
+
# @return [Boolean] whether all network exceptions should be raised as
|
269
|
+
# {NetworkError}, or not (default)
|
276
270
|
#
|
277
271
|
attr_reader :normalize_network_errors
|
278
272
|
|
@@ -281,9 +275,7 @@ class TCPClient
|
|
281
275
|
end
|
282
276
|
|
283
277
|
#
|
284
|
-
#
|
285
|
-
#
|
286
|
-
# @return [Hash<Symbol, Object>]
|
278
|
+
# @return [{Symbol => Object}] Hash containing all attributes
|
287
279
|
#
|
288
280
|
# @see #initialize
|
289
281
|
#
|
data/lib/tcp-client/deadline.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
class TCPClient
|
4
4
|
class Deadline
|
5
|
-
MONOTONIC = defined?(Process::CLOCK_MONOTONIC) ? true : false
|
6
|
-
|
7
5
|
def initialize(timeout)
|
8
6
|
timeout = timeout&.to_f
|
9
7
|
@deadline = timeout&.positive? ? now + timeout : 0
|
@@ -19,7 +17,7 @@ class TCPClient
|
|
19
17
|
|
20
18
|
private
|
21
19
|
|
22
|
-
if
|
20
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
23
21
|
def now
|
24
22
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
25
23
|
end
|
data/lib/tcp-client/errors.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
class TCPClient
|
4
4
|
#
|
5
|
-
# Raised when a SSL connection should be
|
5
|
+
# Raised when a SSL connection should be established but the OpenSSL gem is
|
6
|
+
# not available.
|
6
7
|
#
|
7
8
|
class NoOpenSSLError < RuntimeError
|
8
9
|
def initialize
|
@@ -11,7 +12,8 @@ class TCPClient
|
|
11
12
|
end
|
12
13
|
|
13
14
|
#
|
14
|
-
# Raised when a method requires a callback block but no such block is
|
15
|
+
# Raised when a method requires a callback block but no such block is
|
16
|
+
# specified.
|
15
17
|
#
|
16
18
|
class NoBlockGivenError < ArgumentError
|
17
19
|
def initialize
|
@@ -36,7 +38,7 @@ class TCPClient
|
|
36
38
|
#
|
37
39
|
class UnknownAttributeError < ArgumentError
|
38
40
|
#
|
39
|
-
# @param attribute [Object] the undefined
|
41
|
+
# @param attribute [Object] the undefined attribute
|
40
42
|
#
|
41
43
|
def initialize(attribute)
|
42
44
|
super("unknown attribute - #{attribute}")
|
@@ -58,15 +60,18 @@ class TCPClient
|
|
58
60
|
#
|
59
61
|
# Base exception class for all network related errors.
|
60
62
|
#
|
61
|
-
# Will be raised for any system level network error when
|
63
|
+
# Will be raised for any system level network error when
|
64
|
+
# {Configuration.normalize_network_errors} is configured.
|
62
65
|
#
|
63
|
-
# You should catch this exception class when you like to handle any relevant
|
66
|
+
# You should catch this exception class when you like to handle any relevant
|
67
|
+
# {TCPClient} error.
|
64
68
|
#
|
65
69
|
class NetworkError < StandardError
|
66
70
|
end
|
67
71
|
|
68
72
|
#
|
69
|
-
# Raised when a {TCPClient} instance should read/write from/to the network
|
73
|
+
# Raised when a {TCPClient} instance should read/write from/to the network
|
74
|
+
# but is not connected.
|
70
75
|
#
|
71
76
|
class NotConnectedError < NetworkError
|
72
77
|
def initialize
|
@@ -77,7 +82,8 @@ class TCPClient
|
|
77
82
|
#
|
78
83
|
# Base exception class for a detected timeout.
|
79
84
|
#
|
80
|
-
# You should catch this exception class when you like to handle any timeout
|
85
|
+
# You should catch this exception class when you like to handle any timeout
|
86
|
+
# error.
|
81
87
|
#
|
82
88
|
class TimeoutError < NetworkError
|
83
89
|
#
|
@@ -1,111 +1,149 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
module IOWithDeadlineMixin
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
3
|
+
class TCPClient
|
4
|
+
module IOWithDeadlineMixin
|
5
|
+
def self.included(mod)
|
6
|
+
methods = mod.instance_methods
|
7
|
+
if methods.index(:wait_writable) && methods.index(:wait_readable)
|
8
|
+
mod.include(ViaWaitMethod)
|
9
|
+
elsif methods.index(:to_io)
|
10
|
+
mod.include(ViaIOWaitMethod)
|
11
|
+
else
|
12
|
+
mod.include(ViaSelect)
|
13
|
+
end
|
13
14
|
end
|
14
|
-
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
return
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
read =
|
28
|
-
with_deadline(deadline, exception) do
|
29
|
-
read_nonblock(bytes_to_read - result.bytesize, exception: false)
|
30
|
-
end
|
31
|
-
next result += read if read
|
32
|
-
close
|
33
|
-
break
|
16
|
+
def read_with_deadline(nbytes, deadline, exception)
|
17
|
+
raise(exception) unless deadline.remaining_time
|
18
|
+
return fetch_avail(deadline, exception) if nbytes.nil?
|
19
|
+
return ''.b if nbytes.zero?
|
20
|
+
@buf ||= ''.b
|
21
|
+
while @buf.bytesize < nbytes
|
22
|
+
read = fetch_next(deadline, exception) and next @buf << read
|
23
|
+
close
|
24
|
+
break
|
25
|
+
end
|
26
|
+
fetch_buffer_slice(nbytes)
|
34
27
|
end
|
35
|
-
result
|
36
|
-
end
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
29
|
+
def readto_with_deadline(sep, deadline, exception)
|
30
|
+
raise(exception) unless deadline.remaining_time
|
31
|
+
@buf ||= ''.b
|
32
|
+
while (index = @buf.index(sep)).nil?
|
33
|
+
read = fetch_next(deadline, exception) and next @buf << read
|
34
|
+
close
|
35
|
+
break
|
36
|
+
end
|
37
|
+
index = @buf.index(sep)
|
38
|
+
return fetch_buffer_slice(index + sep.bytesize) if index
|
39
|
+
result = @buf
|
40
|
+
@buf = nil
|
41
|
+
result
|
50
42
|
end
|
51
|
-
end
|
52
43
|
|
53
|
-
|
54
|
-
|
44
|
+
def write_with_deadline(data, deadline, exception)
|
45
|
+
raise(exception) unless deadline.remaining_time
|
46
|
+
return 0 if (size = data.bytesize).zero?
|
47
|
+
result = 0
|
55
48
|
loop do
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
49
|
+
written =
|
50
|
+
with_deadline(deadline, exception) do
|
51
|
+
write_nonblock(data, exception: false)
|
52
|
+
end
|
53
|
+
(result += written) >= size and return result
|
54
|
+
data = data.byteslice(written, data.bytesize - written)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def fetch_avail(deadline, exception)
|
61
|
+
if @buf.nil?
|
62
|
+
result = fetch_next(deadline, exception) and return result
|
63
|
+
close
|
64
|
+
return ''.b
|
65
|
+
end
|
66
|
+
result = @buf
|
67
|
+
@buf = nil
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
def fetch_buffer_slice(size)
|
72
|
+
result = @buf.byteslice(0, size)
|
73
|
+
rest = @buf.bytesize - result.bytesize
|
74
|
+
@buf = rest.zero? ? nil : @buf.byteslice(size, rest)
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
def fetch_next(deadline, exception)
|
79
|
+
with_deadline(deadline, exception) do
|
80
|
+
read_nonblock(65_536, exception: false)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module ViaWaitMethod
|
85
|
+
private def with_deadline(deadline, exception)
|
86
|
+
loop do
|
87
|
+
case ret = yield
|
88
|
+
when :wait_writable
|
89
|
+
remaining_time = deadline.remaining_time or raise(exception)
|
90
|
+
raise(exception) if wait_writable(remaining_time).nil?
|
91
|
+
when :wait_readable
|
92
|
+
remaining_time = deadline.remaining_time or raise(exception)
|
93
|
+
raise(exception) if wait_readable(remaining_time).nil?
|
94
|
+
else
|
95
|
+
return ret
|
96
|
+
end
|
65
97
|
end
|
98
|
+
rescue Errno::ETIMEDOUT
|
99
|
+
raise(exception)
|
66
100
|
end
|
67
|
-
rescue Errno::ETIMEDOUT
|
68
|
-
raise(exception)
|
69
101
|
end
|
70
|
-
end
|
71
102
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
103
|
+
module ViaIOWaitMethod
|
104
|
+
private def with_deadline(deadline, exception)
|
105
|
+
loop do
|
106
|
+
case ret = yield
|
107
|
+
when :wait_writable
|
108
|
+
remaining_time = deadline.remaining_time or raise(exception)
|
109
|
+
raise(exception) if to_io.wait_writable(remaining_time).nil?
|
110
|
+
when :wait_readable
|
111
|
+
remaining_time = deadline.remaining_time or raise(exception)
|
112
|
+
raise(exception) if to_io.wait_readable(remaining_time).nil?
|
113
|
+
else
|
114
|
+
return ret
|
115
|
+
end
|
84
116
|
end
|
117
|
+
rescue Errno::ETIMEDOUT
|
118
|
+
raise(exception)
|
85
119
|
end
|
86
|
-
rescue Errno::ETIMEDOUT
|
87
|
-
raise(exception)
|
88
120
|
end
|
89
|
-
end
|
90
121
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
122
|
+
module ViaSelect
|
123
|
+
private def with_deadline(deadline, exception)
|
124
|
+
loop do
|
125
|
+
case ret = yield
|
126
|
+
when :wait_writable
|
127
|
+
remaining_time = deadline.remaining_time or raise(exception)
|
128
|
+
if ::IO.select(nil, [self], nil, remaining_time).nil?
|
129
|
+
raise(exception)
|
130
|
+
end
|
131
|
+
when :wait_readable
|
132
|
+
remaining_time = deadline.remaining_time or raise(exception)
|
133
|
+
if ::IO.select([self], nil, nil, remaining_time).nil?
|
134
|
+
raise(exception)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
return ret
|
138
|
+
end
|
103
139
|
end
|
140
|
+
rescue Errno::ETIMEDOUT
|
141
|
+
raise(exception)
|
104
142
|
end
|
105
|
-
rescue Errno::ETIMEDOUT
|
106
|
-
raise(exception)
|
107
143
|
end
|
144
|
+
|
145
|
+
private_constant(:ViaWaitMethod, :ViaIOWaitMethod, :ViaSelect)
|
108
146
|
end
|
109
147
|
|
110
|
-
private_constant(:
|
148
|
+
private_constant(:IOWithDeadlineMixin)
|
111
149
|
end
|
@@ -30,7 +30,7 @@ class TCPClient
|
|
30
30
|
::OpenSSL::SSL::SSLContext.new.tap do |ctx|
|
31
31
|
ctx.set_params(ssl_params)
|
32
32
|
ctx.session_cache_mode = CONTEXT_CACHE_MODE
|
33
|
-
ctx.session_new_cb = proc { |_,
|
33
|
+
ctx.session_new_cb = proc { |_, session| @new_session = session }
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|