tcp-client 0.1.3 → 0.1.4
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 -12
- data/lib/tcp-client.rb +31 -8
- data/lib/tcp-client/address.rb +1 -1
- data/lib/tcp-client/version.rb +1 -1
- data/sample/google_ssl.rb +7 -11
- data/test/tcp-client/address_test.rb +0 -1
- data/test/tcp_client_test.rb +45 -7
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a122629b36ac22b95c2ee7ed0d520391cfc00136982e9c0c4a8c7d0ea4dd5b47
|
4
|
+
data.tar.gz: a63612520461f6d84abffbe71c750158b2d7d0c5227d4ae9de16b9cac5071540
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31d0760aa3f16564d171ba27dc0c3a919490356319265aa099762ef7f1e9ff57a20e968315d0d70beb291d62405040e01ec76aa4dfd78a229f3d91c0a7db029b
|
7
|
+
data.tar.gz: 265a8cda03d5c8f62138311f05a40756dd7e052c8b55d6e43412b3cf51dcea91e98c6ba38943bedf4b41de7af6b0d3dc86700197f7eafd4919093abd59d15351
|
data/README.md
CHANGED
@@ -12,22 +12,18 @@ require 'tcp-client'
|
|
12
12
|
|
13
13
|
TCPClient.configure do |cfg|
|
14
14
|
cfg.connect_timeout = 1 # second to connect the server
|
15
|
-
cfg.write_timeout = 0.25 # seconds to write a single data junk
|
16
|
-
cfg.read_timeout = 0.5 # seconds to read some bytes
|
17
15
|
cfg.ssl_params = { ssl_version: :TLSv1_2 } # use TLS 1.2
|
18
16
|
end
|
19
17
|
|
20
|
-
# the following request sequence is not allowed to last longer than 2 seconds:
|
21
|
-
# 1 second to connect (incl. SSL handshake etc.)
|
22
|
-
# + 0.25 seconds to write data
|
23
|
-
# + 0.5 seconds to read a response
|
24
|
-
|
25
18
|
TCPClient.open('www.google.com:443') do |client|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
19
|
+
# query should not last longer than 0.5 seconds
|
20
|
+
client.with_deadline(0.5) do
|
21
|
+
# simple HTTP get request
|
22
|
+
pp client.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
|
23
|
+
|
24
|
+
# read "HTTP/1.1 " + 3 byte HTTP status code
|
25
|
+
pp client.read(12)
|
26
|
+
end
|
31
27
|
end
|
32
28
|
```
|
33
29
|
|
data/lib/tcp-client.rb
CHANGED
@@ -41,20 +41,20 @@ class TCPClient
|
|
41
41
|
|
42
42
|
def initialize
|
43
43
|
@socket = @address = @write_timeout = @read_timeout = nil
|
44
|
+
@deadline = nil
|
44
45
|
end
|
45
46
|
|
46
47
|
def to_s
|
47
48
|
@address ? @address.to_s : ''
|
48
49
|
end
|
49
50
|
|
50
|
-
def connect(addr, configuration)
|
51
|
+
def connect(addr, configuration, exception: ConnectTimeoutError)
|
51
52
|
close
|
52
53
|
NoOpenSSL.raise! if configuration.ssl? && !defined?(SSLSocket)
|
53
54
|
@address = Address.new(addr)
|
54
|
-
@socket = TCPSocket.new(@address, configuration,
|
55
|
+
@socket = TCPSocket.new(@address, configuration, exception)
|
55
56
|
configuration.ssl? &&
|
56
|
-
@socket =
|
57
|
-
SSLSocket.new(@socket, @address, configuration, ConnectTimeoutError)
|
57
|
+
@socket = SSLSocket.new(@socket, @address, configuration, exception)
|
58
58
|
@write_timeout = configuration.write_timeout
|
59
59
|
@read_timeout = configuration.read_timeout
|
60
60
|
self
|
@@ -66,24 +66,47 @@ class TCPClient
|
|
66
66
|
self
|
67
67
|
rescue IOError
|
68
68
|
self
|
69
|
+
ensure
|
70
|
+
@deadline = nil
|
69
71
|
end
|
70
72
|
|
71
73
|
def closed?
|
72
74
|
@socket.nil? || @socket.closed?
|
73
75
|
end
|
74
76
|
|
75
|
-
def
|
77
|
+
def with_deadline(timeout)
|
78
|
+
raise('no block given') unless block_given?
|
79
|
+
raise('deadline already used') if @deadline
|
80
|
+
tm = timeout&.to_f
|
81
|
+
raise(ArgumentError, "invalid deadline - #{timeout}") unless tm.positive?
|
82
|
+
@deadline = Time.now + tm
|
83
|
+
yield(self)
|
84
|
+
ensure
|
85
|
+
@deadline = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def read(nbytes, timeout: nil, exception: ReadTimeoutError)
|
76
89
|
NotConnected.raise!(self) if closed?
|
77
|
-
|
90
|
+
time = timeout || remaining_time(exception) || @read_timeout
|
91
|
+
@socket.read(nbytes, timeout: time, exception: exception)
|
78
92
|
end
|
79
93
|
|
80
|
-
def write(*msg, timeout:
|
94
|
+
def write(*msg, timeout: nil, exception: WriteTimeoutError)
|
81
95
|
NotConnected.raise!(self) if closed?
|
82
|
-
|
96
|
+
time = timeout || remaining_time(exception) || @write_timeout
|
97
|
+
@socket.write(*msg, timeout: time, exception: exception)
|
83
98
|
end
|
84
99
|
|
85
100
|
def flush
|
86
101
|
@socket.flush unless closed?
|
87
102
|
self
|
88
103
|
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def remaining_time(exception)
|
108
|
+
return unless @deadline
|
109
|
+
remaining_time = @deadline - Time.now
|
110
|
+
0 < remaining_time ? remaining_time : raise(exception)
|
111
|
+
end
|
89
112
|
end
|
data/lib/tcp-client/address.rb
CHANGED
data/lib/tcp-client/version.rb
CHANGED
data/sample/google_ssl.rb
CHANGED
@@ -2,20 +2,16 @@ require_relative '../lib/tcp-client'
|
|
2
2
|
|
3
3
|
TCPClient.configure do |cfg|
|
4
4
|
cfg.connect_timeout = 1 # second to connect the server
|
5
|
-
cfg.write_timeout = 0.25 # seconds to write a single data junk
|
6
|
-
cfg.read_timeout = 0.5 # seconds to read some bytes
|
7
5
|
cfg.ssl_params = { ssl_version: :TLSv1_2 } # use TLS 1.2
|
8
6
|
end
|
9
7
|
|
10
|
-
# the following request sequence is not allowed to last longer than 2 seconds:
|
11
|
-
# 1 second to connect (incl. SSL handshake etc.)
|
12
|
-
# + 0.25 seconds to write data
|
13
|
-
# + 0.5 seconds to read a response
|
14
|
-
|
15
8
|
TCPClient.open('www.google.com:443') do |client|
|
16
|
-
#
|
17
|
-
|
9
|
+
# query should not last longer than 0.5 seconds
|
10
|
+
client.with_deadline(0.5) do
|
11
|
+
# simple HTTP get request
|
12
|
+
pp client.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
|
18
13
|
|
19
|
-
|
20
|
-
|
14
|
+
# read "HTTP/1.1 " + 3 byte HTTP status code
|
15
|
+
pp client.read(12)
|
16
|
+
end
|
21
17
|
end
|
data/test/tcp_client_test.rb
CHANGED
@@ -3,6 +3,8 @@ require_relative 'test_helper'
|
|
3
3
|
class TCPClientTest < MiniTest::Test
|
4
4
|
parallelize_me!
|
5
5
|
|
6
|
+
HUGE_AMOUNT_OF_DATA = Array.new(2024, '?' * 1024).freeze
|
7
|
+
|
6
8
|
attr_reader :config
|
7
9
|
|
8
10
|
def setup
|
@@ -65,7 +67,7 @@ class TCPClientTest < MiniTest::Test
|
|
65
67
|
start_time = Time.now
|
66
68
|
subject.read(42, timeout: timeout)
|
67
69
|
end
|
68
|
-
assert_in_delta(timeout, Time.now - start_time, 0.
|
70
|
+
assert_in_delta(timeout, Time.now - start_time, 0.11)
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
@@ -81,27 +83,63 @@ class TCPClientTest < MiniTest::Test
|
|
81
83
|
start_time = nil
|
82
84
|
assert_raises(TCPClient::WriteTimeoutError) do
|
83
85
|
start_time = Time.now
|
84
|
-
|
85
|
-
# send 1MB to avoid any TCP stack buffering
|
86
|
-
args = Array.new(2024, '?' * 1024)
|
87
|
-
subject.write(*args, timeout: timeout)
|
86
|
+
subject.write(*HUGE_AMOUNT_OF_DATA, timeout: timeout)
|
88
87
|
end
|
89
88
|
assert_in_delta(timeout, Time.now - start_time, 0.02)
|
90
89
|
end
|
91
90
|
end
|
92
91
|
|
93
92
|
def test_write_timeout
|
94
|
-
check_write_timeout(0.
|
93
|
+
check_write_timeout(0.01)
|
95
94
|
check_write_timeout(0.25)
|
96
95
|
end
|
97
96
|
|
97
|
+
def test_write_deadline
|
98
|
+
TCPClient.open('localhost:1234', config) do |subject|
|
99
|
+
refute(subject.closed?)
|
100
|
+
assert_raises(TCPClient::WriteTimeoutError) do
|
101
|
+
subject.with_deadline(0.25) do |*args|
|
102
|
+
assert_equal([subject], args)
|
103
|
+
loop { subject.write('some data here') }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_read_deadline
|
110
|
+
TCPClient.open('localhost:1234', config) do |subject|
|
111
|
+
refute(subject.closed?)
|
112
|
+
assert_raises(TCPClient::ReadTimeoutError) do
|
113
|
+
subject.with_deadline(0.25) do |*args|
|
114
|
+
assert_equal([subject], args)
|
115
|
+
loop { subject.read(0) }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_read_write_deadline
|
122
|
+
TCPClient.open('localhost:1234', config) do |subject|
|
123
|
+
refute(subject.closed?)
|
124
|
+
assert_raises(TCPClient::TimeoutError) do
|
125
|
+
subject.with_deadline(0.25) do |*args|
|
126
|
+
assert_equal([subject], args)
|
127
|
+
loop do
|
128
|
+
subject.write('HUGE_AMOUNT_OF_DATA')
|
129
|
+
subject.read(0)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
98
136
|
def check_connect_timeout(ssl_config)
|
99
137
|
start_time = nil
|
100
138
|
assert_raises(TCPClient::ConnectTimeoutError) do
|
101
139
|
start_time = Time.now
|
102
140
|
TCPClient.new.connect('localhost:1234', ssl_config)
|
103
141
|
end
|
104
|
-
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.11)
|
105
143
|
end
|
106
144
|
|
107
145
|
def test_connect_ssl_timeout
|