tcp-client 0.9.1 → 0.10.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/.yardopts +5 -0
- data/README.md +11 -10
- data/gems.rb +2 -1
- data/lib/tcp-client/address.rb +31 -15
- data/lib/tcp-client/configuration.rb +132 -99
- data/lib/tcp-client/deadline.rb +1 -3
- data/lib/tcp-client/default_configuration.rb +20 -8
- data/lib/tcp-client/errors.rb +34 -14
- data/lib/tcp-client/mixin/io_with_deadline.rb +46 -16
- data/lib/tcp-client/version.rb +2 -4
- data/lib/tcp-client.rb +169 -100
- data/rakefile.rb +6 -1
- data/sample/google_ssl.rb +8 -5
- data/spec/tcp-client/address_spec.rb +15 -28
- data/spec/tcp-client/configuration_spec.rb +7 -6
- data/spec/tcp_client_spec.rb +227 -23
- data/tcp-client.gemspec +9 -11
- metadata +5 -4
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
|
@@ -20,9 +22,12 @@ class TCPClient
|
|
20
22
|
end
|
21
23
|
|
22
24
|
#
|
23
|
-
# Raised when
|
25
|
+
# Raised when an invalid timeout value was specified.
|
24
26
|
#
|
25
27
|
class InvalidDeadLineError < ArgumentError
|
28
|
+
#
|
29
|
+
# @param timeout [Object] the invalid value
|
30
|
+
#
|
26
31
|
def initialize(timeout)
|
27
32
|
super("invalid deadline - #{timeout}")
|
28
33
|
end
|
@@ -32,6 +37,9 @@ class TCPClient
|
|
32
37
|
# Raised by {Configuration} when an undefined attribute should be set.
|
33
38
|
#
|
34
39
|
class UnknownAttributeError < ArgumentError
|
40
|
+
#
|
41
|
+
# @param attribute [Object] the undefined attribute
|
42
|
+
#
|
35
43
|
def initialize(attribute)
|
36
44
|
super("unknown attribute - #{attribute}")
|
37
45
|
end
|
@@ -41,6 +49,9 @@ class TCPClient
|
|
41
49
|
# Raised when a given timeout exception parameter is not an exception class.
|
42
50
|
#
|
43
51
|
class NotAnExceptionError < TypeError
|
52
|
+
#
|
53
|
+
# @param object [Object] the invalid object
|
54
|
+
#
|
44
55
|
def initialize(object)
|
45
56
|
super("exception class required - #{object.inspect}")
|
46
57
|
end
|
@@ -49,15 +60,18 @@ class TCPClient
|
|
49
60
|
#
|
50
61
|
# Base exception class for all network related errors.
|
51
62
|
#
|
52
|
-
# 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.
|
53
65
|
#
|
54
|
-
# 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.
|
55
68
|
#
|
56
69
|
class NetworkError < StandardError
|
57
70
|
end
|
58
71
|
|
59
72
|
#
|
60
|
-
# 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.
|
61
75
|
#
|
62
76
|
class NotConnectedError < NetworkError
|
63
77
|
def initialize
|
@@ -68,23 +82,26 @@ class TCPClient
|
|
68
82
|
#
|
69
83
|
# Base exception class for a detected timeout.
|
70
84
|
#
|
71
|
-
# 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.
|
72
87
|
#
|
73
88
|
class TimeoutError < NetworkError
|
74
89
|
#
|
75
90
|
# Initializes the instance with an optional message.
|
76
91
|
#
|
77
|
-
#
|
92
|
+
# The message will be generated from {#action} when not specified.
|
93
|
+
#
|
78
94
|
# @overload initialize
|
79
95
|
# @overload initialize(message)
|
80
96
|
#
|
81
|
-
# @param message [
|
97
|
+
# @param message [#to_s] the error message
|
82
98
|
#
|
83
99
|
def initialize(message = nil)
|
84
100
|
super(message || "unable to #{action} in time")
|
85
101
|
end
|
86
102
|
|
87
103
|
#
|
104
|
+
# @attribute [r] action
|
88
105
|
# @return [Symbol] the action which timed out
|
89
106
|
#
|
90
107
|
def action
|
@@ -97,7 +114,8 @@ class TCPClient
|
|
97
114
|
#
|
98
115
|
class ConnectTimeoutError < TimeoutError
|
99
116
|
#
|
100
|
-
# @
|
117
|
+
# @attribute [r] action
|
118
|
+
# @return [Symbol] the action which timed out: `:connect`
|
101
119
|
#
|
102
120
|
def action
|
103
121
|
:connect
|
@@ -105,11 +123,12 @@ class TCPClient
|
|
105
123
|
end
|
106
124
|
|
107
125
|
#
|
108
|
-
# Raised by default whenever a {TCPClient
|
126
|
+
# Raised by default whenever a {TCPClient#read} timed out.
|
109
127
|
#
|
110
128
|
class ReadTimeoutError < TimeoutError
|
111
129
|
#
|
112
|
-
# @
|
130
|
+
# @attribute [r] action
|
131
|
+
# @return [Symbol] the action which timed out: :read`
|
113
132
|
#
|
114
133
|
def action
|
115
134
|
:read
|
@@ -117,11 +136,12 @@ class TCPClient
|
|
117
136
|
end
|
118
137
|
|
119
138
|
#
|
120
|
-
# Raised by default whenever a {TCPClient
|
139
|
+
# Raised by default whenever a {TCPClient#write} timed out.
|
121
140
|
#
|
122
141
|
class WriteTimeoutError < TimeoutError
|
123
142
|
#
|
124
|
-
# @
|
143
|
+
# @attribute [r] action
|
144
|
+
# @return [Symbol] the action which timed out: `:write`
|
125
145
|
#
|
126
146
|
def action
|
127
147
|
:write
|
@@ -13,25 +13,30 @@ module IOWithDeadlineMixin # :nodoc:
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def read_with_deadline(
|
16
|
+
def read_with_deadline(nbytes, deadline, exception)
|
17
17
|
raise(exception) unless deadline.remaining_time
|
18
|
-
if
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
24
25
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
fetch_buffer_slice(nbytes)
|
27
|
+
end
|
28
|
+
|
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
|
32
34
|
close
|
33
35
|
break
|
34
36
|
end
|
37
|
+
index = @buf.index(sep) and return fetch_buffer_slice(index + sep.bytesize)
|
38
|
+
result = @buf
|
39
|
+
@buf = nil
|
35
40
|
result
|
36
41
|
end
|
37
42
|
|
@@ -44,12 +49,37 @@ module IOWithDeadlineMixin # :nodoc:
|
|
44
49
|
with_deadline(deadline, exception) do
|
45
50
|
write_nonblock(data, exception: false)
|
46
51
|
end
|
47
|
-
result += written
|
48
|
-
return result if result >= size
|
52
|
+
(result += written) >= size and return result
|
49
53
|
data = data.byteslice(written, data.bytesize - written)
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
57
|
+
private
|
58
|
+
|
59
|
+
def fetch_avail(deadline, exception)
|
60
|
+
if @buf.nil?
|
61
|
+
result = fetch_next(deadline, exception) and return result
|
62
|
+
close
|
63
|
+
return ''.b
|
64
|
+
end
|
65
|
+
result = @buf
|
66
|
+
@buf = nil
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
def fetch_buffer_slice(size)
|
71
|
+
result = @buf.byteslice(0, size)
|
72
|
+
rest = @buf.bytesize - result.bytesize
|
73
|
+
@buf = rest.zero? ? nil : @buf.byteslice(size, rest)
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
def fetch_next(deadline, exception)
|
78
|
+
with_deadline(deadline, exception) do
|
79
|
+
read_nonblock(65_536, exception: false)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
53
83
|
module ViaWaitMethod
|
54
84
|
private def with_deadline(deadline, exception)
|
55
85
|
loop do
|
data/lib/tcp-client/version.rb
CHANGED
data/lib/tcp-client.rb
CHANGED
@@ -15,71 +15,85 @@ require_relative 'tcp-client/version'
|
|
15
15
|
# All connect/read/write actions can be monitored to ensure that all actions
|
16
16
|
# terminate before given time limits - or raise an exception.
|
17
17
|
#
|
18
|
-
# @example
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# client.read(12)
|
22
|
-
# end
|
23
|
-
# # => "HTTP/1.1 200"
|
18
|
+
# @example request to Google.com and limit network interactions to 1.5 seconds
|
19
|
+
# # create a configuration to use at least TLS 1.2
|
20
|
+
# cfg = TCPClient::Configuration.create(ssl_params: {min_version: :TLS1_2})
|
24
21
|
#
|
22
|
+
# response =
|
23
|
+
# TCPClient.with_deadline(1.5, 'www.google.com:443', cfg) do |client|
|
24
|
+
# client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n") #=> 40
|
25
|
+
# client.readline("\r\n\r\n") #=> see response
|
26
|
+
# end
|
27
|
+
# # response contains the returned message and header
|
25
28
|
#
|
26
29
|
class TCPClient
|
27
30
|
#
|
28
31
|
# Creates a new instance which is connected to the server on the given
|
29
|
-
# address
|
32
|
+
# `address`.
|
33
|
+
#
|
34
|
+
# If no `configuration` is given, the {.default_configuration} will be used.
|
35
|
+
#
|
36
|
+
# @overload open(address, configuration = nil)
|
37
|
+
# @yieldparam client [TCPClient] the connected client
|
38
|
+
#
|
39
|
+
# @return [Object] the block result
|
40
|
+
#
|
41
|
+
# @overload open(address, configuration = nil)
|
42
|
+
# @return [TCPClient] the connected client
|
30
43
|
#
|
31
44
|
# If an optional block is given, then the block's result is returned and the
|
32
45
|
# connection will be closed when the block execution ends.
|
33
|
-
# This can be used to create an ad-hoc connection which is
|
46
|
+
# This can be used to create an ad-hoc connection which is guaranteed to be
|
34
47
|
# closed.
|
35
48
|
#
|
36
|
-
# If no block is
|
49
|
+
# If no block is given the connected client instance is returned.
|
37
50
|
# This can be used as a shorthand to create & connect a client.
|
38
51
|
#
|
39
|
-
# @param address [Address, String, Addrinfo, Integer] the address
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# @yieldreturn [Object] any result
|
44
|
-
#
|
45
|
-
# @return [Object, TCPClient] the block result or the connected client
|
52
|
+
# @param address [Address, String, Addrinfo, Integer] the target address see
|
53
|
+
# {Address#initialize} for valid formats
|
54
|
+
# @param configuration [Configuration] the {Configuration} to be used for
|
55
|
+
# the new instance
|
46
56
|
#
|
47
57
|
# @see #connect
|
48
58
|
#
|
49
59
|
def self.open(address, configuration = nil)
|
50
60
|
client = new
|
51
|
-
client.connect(
|
61
|
+
client.connect(address, configuration)
|
52
62
|
block_given? ? yield(client) : client
|
53
63
|
ensure
|
54
64
|
client.close if block_given?
|
55
65
|
end
|
56
66
|
|
57
67
|
#
|
58
|
-
# Yields
|
59
|
-
# address and
|
60
|
-
#
|
61
|
-
# It also limits all {#read} and {#write} actions within the block to a given
|
62
|
-
# time.
|
68
|
+
# Yields an instance which is connected to the server on the given
|
69
|
+
# `address`. It limits all {#read} and {#write} actions within the block to
|
70
|
+
# the given time.
|
63
71
|
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# limit.
|
72
|
+
# It ensures to close the connection when the block execution ends and returns
|
73
|
+
# the block's result.
|
67
74
|
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
75
|
+
# This can be used to create an ad-hoc connection which is guaranteed to be
|
76
|
+
# closed and which {#read}/{#write} call sequence should not last longer than
|
77
|
+
# the `timeout` seconds.
|
78
|
+
#
|
79
|
+
# If no `configuration` is given, the {.default_configuration} will be used.
|
80
|
+
#
|
81
|
+
# @param timeout [Numeric] maximum time in seconds for all {#read} and
|
82
|
+
# {#write} calls within the block
|
83
|
+
# @param address [Address, String, Addrinfo, Integer] the target address see
|
84
|
+
# {Address#initialize} for valid formats
|
85
|
+
# @param configuration [Configuration] the {Configuration} to be used for
|
86
|
+
# the instance
|
71
87
|
#
|
72
88
|
# @yieldparam client [TCPClient] the connected client
|
73
|
-
# @yieldreturn [Object] any result
|
74
89
|
#
|
75
|
-
# @return [Object] the block result
|
90
|
+
# @return [Object] the block's result
|
76
91
|
#
|
77
92
|
# @see #with_deadline
|
78
93
|
#
|
79
94
|
def self.with_deadline(timeout, address, configuration = nil)
|
80
95
|
client = nil
|
81
96
|
raise(NoBlockGivenError) unless block_given?
|
82
|
-
address = Address.new(address)
|
83
97
|
client = new
|
84
98
|
client.with_deadline(timeout) do
|
85
99
|
yield(client.connect(address, configuration))
|
@@ -89,89 +103,168 @@ class TCPClient
|
|
89
103
|
end
|
90
104
|
|
91
105
|
#
|
92
|
-
# @return [Address] the address used
|
106
|
+
# @return [Address] the address used by this client instance
|
93
107
|
#
|
94
108
|
attr_reader :address
|
95
109
|
|
96
110
|
#
|
97
|
-
# @return [Configuration] the configuration used by this client
|
111
|
+
# @return [Configuration] the configuration used by this client instance
|
98
112
|
#
|
99
113
|
attr_reader :configuration
|
100
114
|
|
101
115
|
#
|
102
|
-
#
|
103
|
-
# @return [Boolean]
|
116
|
+
# @!parse attr_reader :closed?
|
117
|
+
# @return [Boolean] whether the connection is closed
|
104
118
|
#
|
105
119
|
def closed?
|
106
120
|
@socket.nil? || @socket.closed?
|
107
121
|
end
|
108
122
|
|
109
123
|
#
|
110
|
-
#
|
124
|
+
# Close the current connection if connected.
|
111
125
|
#
|
112
|
-
# @
|
126
|
+
# @return [TCPClient] itself
|
113
127
|
#
|
114
|
-
def
|
115
|
-
@
|
128
|
+
def close
|
129
|
+
@socket&.close
|
130
|
+
self
|
131
|
+
rescue *NETWORK_ERRORS
|
132
|
+
self
|
133
|
+
ensure
|
134
|
+
@socket = @deadline = nil
|
116
135
|
end
|
117
136
|
|
118
137
|
#
|
119
|
-
# Establishes a new connection to a given address
|
138
|
+
# Establishes a new connection to a server on given `address`.
|
139
|
+
#
|
140
|
+
# It accepts a connection-specific `configuration` or uses the
|
141
|
+
# {.default_configuration}.
|
120
142
|
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
# configure the behavior per connection.
|
143
|
+
# The optional `timeout` and `exception` parameters allow to override the
|
144
|
+
# `connect_timeout` and `connect_timeout_error` values.
|
124
145
|
#
|
125
|
-
# @param address [Address, String, Addrinfo, Integer] the address
|
126
|
-
#
|
127
|
-
# @param
|
128
|
-
#
|
146
|
+
# @param address [Address, String, Addrinfo, Integer] the target address, see
|
147
|
+
# {Address#initialize} for valid formats
|
148
|
+
# @param configuration [Configuration] the {Configuration} to be used for
|
149
|
+
# this instance
|
150
|
+
# @param timeout [Numeric] maximum time in seconds to connect
|
151
|
+
# @param exception [Class<Exception>] exception class to be used when the
|
152
|
+
# connect timeout reached
|
129
153
|
#
|
130
|
-
# @return [
|
154
|
+
# @return [TCPClient] itself
|
131
155
|
#
|
132
156
|
# @raise {NoOpenSSLError} if SSL should be used but OpenSSL is not avail
|
133
157
|
#
|
158
|
+
# @see NetworkError
|
159
|
+
#
|
134
160
|
def connect(address, configuration = nil, timeout: nil, exception: nil)
|
135
161
|
close if @socket
|
136
|
-
@address = Address.new(address)
|
137
162
|
@configuration = (configuration || Configuration.default).dup
|
138
163
|
raise(NoOpenSSLError) if @configuration.ssl? && !defined?(SSLSocket)
|
164
|
+
@address = stem_errors { Address.new(address) }
|
139
165
|
@socket = create_socket(timeout, exception)
|
140
166
|
self
|
141
167
|
end
|
142
168
|
|
143
169
|
#
|
144
|
-
#
|
170
|
+
# Flushes all internal buffers (write all buffered data).
|
145
171
|
#
|
146
|
-
# @return [
|
172
|
+
# @return [TCPClient] itself
|
147
173
|
#
|
148
|
-
def
|
149
|
-
@socket&.
|
150
|
-
self
|
151
|
-
rescue *NETWORK_ERRORS
|
174
|
+
def flush
|
175
|
+
stem_errors { @socket&.flush }
|
152
176
|
self
|
153
|
-
ensure
|
154
|
-
@socket = @deadline = nil
|
155
177
|
end
|
156
178
|
|
157
179
|
#
|
158
|
-
#
|
180
|
+
# Read the given `nbytes` or the next available buffer from server.
|
181
|
+
#
|
182
|
+
# The optional `timeout` and `exception` parameters allow to override the
|
183
|
+
# `read_timeout` and `read_timeout_error` values of the used {#configuration}.
|
184
|
+
#
|
185
|
+
# @param nbytes [Integer] the number of bytes to read
|
186
|
+
# @param timeout [Numeric] maximum time in seconds to read
|
187
|
+
# @param exception [Class<Exception>] exception class to be used when the
|
188
|
+
# read timeout reached
|
189
|
+
#
|
190
|
+
# @return [String] the read buffer
|
191
|
+
#
|
192
|
+
# @raise [NotConnectedError] if {#connect} was not called before
|
193
|
+
#
|
194
|
+
# @see NetworkError
|
195
|
+
#
|
196
|
+
def read(nbytes = nil, timeout: nil, exception: nil)
|
197
|
+
raise(NotConnectedError) if closed?
|
198
|
+
deadline = create_deadline(timeout, configuration.read_timeout)
|
199
|
+
return stem_errors { @socket.read(nbytes) } unless deadline.valid?
|
200
|
+
exception ||= configuration.read_timeout_error
|
201
|
+
stem_errors(exception) do
|
202
|
+
@socket.read_with_deadline(nbytes, deadline, exception)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# Reads the next line from server.
|
208
|
+
#
|
209
|
+
# The standard record separator is used as `separator`.
|
159
210
|
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
# method to define such a deadline.
|
211
|
+
# The optional `timeout` and `exception` parameters allow to override the
|
212
|
+
# `read_timeout` and `read_timeout_error` values of the used {#configuration}.
|
163
213
|
#
|
164
|
-
# @
|
214
|
+
# @param separator [String] the line separator to be used
|
215
|
+
# @param timeout [Numeric] maximum time in seconds to read
|
216
|
+
# @param exception [Class<Exception>] exception class to be used when the
|
217
|
+
# read timeout reached
|
218
|
+
#
|
219
|
+
# @return [String] the read line
|
220
|
+
#
|
221
|
+
# @raise [NotConnectedError] if {#connect} was not called before
|
222
|
+
#
|
223
|
+
# @see NetworkError
|
224
|
+
#
|
225
|
+
def readline(separator = $/, chomp: false, timeout: nil, exception: nil)
|
226
|
+
raise(NotConnectedError) if closed?
|
227
|
+
deadline = create_deadline(timeout, configuration.read_timeout)
|
228
|
+
unless deadline.valid?
|
229
|
+
return stem_errors { @socket.readline(separator, chomp: chomp) }
|
230
|
+
end
|
231
|
+
exception ||= configuration.read_timeout_error
|
232
|
+
line =
|
233
|
+
stem_errors(exception) do
|
234
|
+
@socket.readto_with_deadline(separator, deadline, exception)
|
235
|
+
end
|
236
|
+
chomp ? line.chomp : line
|
237
|
+
end
|
238
|
+
|
239
|
+
#
|
240
|
+
# @return [String] the currently used address as text.
|
241
|
+
#
|
242
|
+
# @see Address#to_s
|
243
|
+
#
|
244
|
+
def to_s
|
245
|
+
@address&.to_s || ''
|
246
|
+
end
|
247
|
+
|
248
|
+
#
|
249
|
+
# Executes a block with a given overall time limit.
|
250
|
+
#
|
251
|
+
# When you like to ensure that a complete {#read}/{#write} communication
|
252
|
+
# sequence with the server is finished before a given amount of time you use
|
253
|
+
# this method.
|
254
|
+
#
|
255
|
+
# @example ensure to send SMTP welcome message and receive a 4 byte answer
|
165
256
|
# answer = client.with_deadline(2.5) do
|
166
|
-
# client.write('
|
167
|
-
# client.read(
|
257
|
+
# client.write('HELO')
|
258
|
+
# client.read(4)
|
168
259
|
# end
|
260
|
+
# # answer is EHLO when server speaks fluent SMPT
|
169
261
|
#
|
170
|
-
# @param timeout [Numeric] maximum time in seconds for all {#read} and
|
262
|
+
# @param timeout [Numeric] maximum time in seconds for all {#read} and
|
263
|
+
# {#write} calls within the block
|
171
264
|
#
|
172
265
|
# @yieldparam client [TCPClient] self
|
173
266
|
#
|
174
|
-
# @return [Object]
|
267
|
+
# @return [Object] the block's result
|
175
268
|
#
|
176
269
|
# @raise [NoBlockGivenError] if the block is missing
|
177
270
|
#
|
@@ -186,37 +279,23 @@ class TCPClient
|
|
186
279
|
end
|
187
280
|
|
188
281
|
#
|
189
|
-
#
|
282
|
+
# Writes the given `messages` to the server.
|
190
283
|
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
284
|
+
# The optional `timeout` and `exception` parameters allow to override the
|
285
|
+
# `write_timeout` and `write_timeout_error` values of the used
|
286
|
+
# {#configuration}.
|
194
287
|
#
|
195
|
-
# @
|
196
|
-
#
|
197
|
-
# @
|
198
|
-
#
|
199
|
-
def read(nbytes = nil, timeout: nil, exception: nil)
|
200
|
-
raise(NotConnectedError) if closed?
|
201
|
-
deadline = create_deadline(timeout, configuration.read_timeout)
|
202
|
-
return stem_errors { @socket.read(nbytes) } unless deadline.valid?
|
203
|
-
exception ||= configuration.read_timeout_error
|
204
|
-
stem_errors(exception) do
|
205
|
-
@socket.read_with_deadline(nbytes, deadline, exception)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
#
|
210
|
-
# Write the given messages to the server.
|
211
|
-
#
|
212
|
-
# @param messages [Array<String>] messages to write
|
213
|
-
# @param timeout [Numeric] maximum time in seconds to read; used to override the configuration's +write_timeout+.
|
214
|
-
# @param exception [Class] exception class to be used when the read timeout reached; used to override the configuration's +write_timeout_error+.
|
288
|
+
# @param messages [Array<String>] one or more messages to write
|
289
|
+
# @param timeout [Numeric] maximum time in seconds to write
|
290
|
+
# @param exception [Class<Exception>] exception class to be used when the
|
291
|
+
# write timeout reached
|
215
292
|
#
|
216
293
|
# @return [Integer] bytes written
|
217
294
|
#
|
218
295
|
# @raise [NotConnectedError] if {#connect} was not called before
|
219
296
|
#
|
297
|
+
# @see NetworkError
|
298
|
+
#
|
220
299
|
def write(*messages, timeout: nil, exception: nil)
|
221
300
|
raise(NotConnectedError) if closed?
|
222
301
|
deadline = create_deadline(timeout, configuration.write_timeout)
|
@@ -229,16 +308,6 @@ class TCPClient
|
|
229
308
|
end
|
230
309
|
end
|
231
310
|
|
232
|
-
#
|
233
|
-
# Flush all internal buffers (write all through).
|
234
|
-
#
|
235
|
-
# @return [self]
|
236
|
-
#
|
237
|
-
def flush
|
238
|
-
stem_errors { @socket&.flush }
|
239
|
-
self
|
240
|
-
end
|
241
|
-
|
242
311
|
private
|
243
312
|
|
244
313
|
def create_deadline(timeout, default)
|
data/rakefile.rb
CHANGED
@@ -7,7 +7,12 @@ require 'yard'
|
|
7
7
|
|
8
8
|
$stdout.sync = $stderr.sync = true
|
9
9
|
|
10
|
-
|
10
|
+
CLEAN << 'prj' << 'doc'
|
11
|
+
|
12
|
+
CLOBBER << '.yardoc'
|
13
|
+
|
11
14
|
task(:default) { exec('rake --tasks') }
|
15
|
+
|
12
16
|
RSpec::Core::RakeTask.new { |task| task.ruby_opts = %w[-w] }
|
17
|
+
|
13
18
|
YARD::Rake::YardocTask.new { |task| task.stats_options = %w[--list-undoc] }
|
data/sample/google_ssl.rb
CHANGED
@@ -18,8 +18,11 @@ cfg =
|
|
18
18
|
# - limit all network interactions to 1.5 seconds
|
19
19
|
# - use the Configuration cfg
|
20
20
|
# - send a simple HTTP get request
|
21
|
-
# - read
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
# - read the returned message and headers
|
22
|
+
response =
|
23
|
+
TCPClient.with_deadline(1.5, 'www.google.com:443', cfg) do |client|
|
24
|
+
client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n") #=> 40
|
25
|
+
client.readline("\r\n\r\n") #=> see response
|
26
|
+
end
|
27
|
+
|
28
|
+
puts(response)
|