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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc62bca2078a00db24dc861aa876a6e2acac79dd1f2eb0cafbd29840d5169704
4
- data.tar.gz: c784e00a152bcf7ac5c1246517483a39f55beb3f7591881be58a4edeb847097e
3
+ metadata.gz: a122629b36ac22b95c2ee7ed0d520391cfc00136982e9c0c4a8c7d0ea4dd5b47
4
+ data.tar.gz: a63612520461f6d84abffbe71c750158b2d7d0c5227d4ae9de16b9cac5071540
5
5
  SHA512:
6
- metadata.gz: 77ff9adf39b16aab0ca83cd77c01f688a77efd675c287095e5b67c1b52f89cf0528ddbc0a1c206330703cf430c4f457ddbdcb369f5a0a8f57a6c143ee5266f7e
7
- data.tar.gz: 27df4b59116a68cf8232f471214e305d74f650b4e1be6cbd59302660f8d2998beef1fd7c382079311f9139c3465bda20981f3ff0993659f74807c00bb3942959
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
- # simple HTTP get request
27
- pp client.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
28
-
29
- # read "HTTP/1.1 " + 3 byte HTTP status code
30
- pp client.read(12)
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, ConnectTimeoutError)
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 read(nbytes, timeout: @read_timeout)
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
- @socket.read(nbytes, timeout: timeout, exception: ReadTimeoutError)
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: @write_timeout)
94
+ def write(*msg, timeout: nil, exception: WriteTimeoutError)
81
95
  NotConnected.raise!(self) if closed?
82
- @socket.write(*msg, timeout: timeout, exception: WriteTimeoutError)
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
@@ -26,7 +26,7 @@ class TCPClient
26
26
  end
27
27
 
28
28
  def to_h
29
- {host: @hostname, port: @addrinfo.ip_port}
29
+ { host: @hostname, port: @addrinfo.ip_port }
30
30
  end
31
31
 
32
32
  def ==(other)
@@ -1,3 +1,3 @@
1
1
  class TCPClient
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.1.4'.freeze
3
3
  end
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
- # simple HTTP get request
17
- pp client.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
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
- # read "HTTP/1.1 " + 3 byte HTTP status code
20
- pp client.read(12)
14
+ # read "HTTP/1.1 " + 3 byte HTTP status code
15
+ pp client.read(12)
16
+ end
21
17
  end
@@ -62,5 +62,4 @@ class AddressTest < MiniTest::Test
62
62
  assert(a == b)
63
63
  assert(a === b)
64
64
  end
65
-
66
65
  end
@@ -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.02)
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.1)
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.02)
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tcp-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Blumtritt