tcp-client 0.3.2 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b265053761a87007b7de21c3a7a369b778f013ae726bb26220a4fd9aeeaf71c
4
- data.tar.gz: 5a71b5dc86ed4ab9ef481e65c24aef0481c5e9d2c868f3f9975b801936d99691
3
+ metadata.gz: f36e18e6865766292ff44adf5cd46244e8944049f524553ba4168725454a2d0b
4
+ data.tar.gz: abd4ec63f48c1be4e73922aaed7702bdacebe398e6cda13887fcd2efaa62818c
5
5
  SHA512:
6
- metadata.gz: d16d436959c3e1c29962a5c474de8db3b4638e2d99aaf66fe4046b22e8159298cc433e014b14d9c9b3e01cafff1052743cf90896c396d86e1ccfc87d689553da
7
- data.tar.gz: 70f4ce83fd7c7c8aa4c066fb94e96ef62a5087c3f78e079cd473e8d45ab22e106eea7b3d7bf5965063fecb9b77c58b8548598f4a873d9480c5b501bc7854dea9
6
+ metadata.gz: 11b5302d3da2d0d46f35fb2370177916b1044b659b1f62bdbf0a30c39581844dd425487a6c94300b4a7afcf25eafef5828a6ef04cb0ff9bcae4fff01a9a312bb
7
+ data.tar.gz: 54b3505c1eb90565e0a80dab5c92a394883b74cf3bb975ed83df4c1affbad15e9acbfcee7ec3d0a5bb79c1486fa46fbfbd958fc9e43106b9a97dc22411c84335
data/.gitignore CHANGED
@@ -1,4 +1,4 @@
1
- .local/
1
+ local/
2
2
  tmp/
3
3
  pkg/
4
4
  gems.locked
data/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2017-2021, Mike Blumtritt. All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  A TCP client implementation with working timeout support.
4
4
 
5
5
  ## Description
6
- This gem implements a TCP client with (optional) SSL support. The motivation of this project is the need to have a _really working_ easy to use client which can handle time limits correctly. Unlike other implementations this client respects given/configurable time limits for each method (`connect`, `read`, `write`).
6
+
7
+ This Gem implements a TCP client with (optional) SSL support. It is an easy to use, versatile configurable client that can correctly handle time limits. Unlike other implementations, this client respects predefined/configurable time limits for each method (`connect`, `read`, `write`). Deadlines for a sequence of read/write actions can also be monitored.
7
8
 
8
9
  ## Sample
9
10
 
@@ -11,15 +12,15 @@ This gem implements a TCP client with (optional) SSL support. The motivation of
11
12
  require 'tcp-client'
12
13
 
13
14
  TCPClient.configure do |cfg|
14
- cfg.connect_timeout = 1 # second to connect the server
15
+ cfg.connect_timeout = 1 # limit connect time the server to 1 second
15
16
  cfg.ssl_params = { ssl_version: :TLSv1_2 } # use TLS 1.2
16
17
  end
17
18
 
18
19
  TCPClient.open('www.google.com:443') do |client|
19
- # query should not last longer than 0.5 seconds
20
+ # next sequence should not last longer than 0.5 seconds
20
21
  client.with_deadline(0.5) do
21
22
  # simple HTTP get request
22
- pp client.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
23
+ pp client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
23
24
 
24
25
  # read "HTTP/1.1 " + 3 byte HTTP status code
25
26
  pp client.read(12)
@@ -58,10 +58,7 @@ class TCPClient
58
58
 
59
59
  def from_string(str)
60
60
  idx = str.rindex(':') or return nil, str.to_i
61
- name = str[0, idx]
62
- if name.start_with?('[') && name.end_with?(']')
63
- name = name[1, name.size - 2]
64
- end
61
+ name = str[0, idx].delete_prefix('[').delete_suffix(']')
65
62
  [name, str[idx + 1, str.size - idx].to_i]
66
63
  end
67
64
  end
@@ -13,7 +13,6 @@ class TCPClient
13
13
  attr_reader :buffered,
14
14
  :keep_alive,
15
15
  :reverse_lookup,
16
- :timeout,
17
16
  :connect_timeout,
18
17
  :read_timeout,
19
18
  :write_timeout,
@@ -38,7 +37,7 @@ class TCPClient
38
37
 
39
38
  def initialize_copy(_org)
40
39
  super
41
- @ssl_params = @ssl_params.dup
40
+ @ssl_params = Hash[@ssl_params] if @ssl_params
42
41
  self
43
42
  end
44
43
 
@@ -47,9 +46,12 @@ class TCPClient
47
46
  end
48
47
 
49
48
  def ssl=(value)
50
- return @ssl_params = nil unless value
51
- return @ssl_params = value.dup if Hash === value
52
- @ssl_params ||= {}
49
+ @ssl_params =
50
+ if Hash === value
51
+ Hash[value]
52
+ else
53
+ value ? {} : nil
54
+ end
53
55
  end
54
56
 
55
57
  def buffered=(value)
@@ -65,8 +67,7 @@ class TCPClient
65
67
  end
66
68
 
67
69
  def timeout=(seconds)
68
- @timeout =
69
- @connect_timeout = @write_timeout = @read_timeout = seconds(seconds)
70
+ @connect_timeout = @write_timeout = @read_timeout = seconds(seconds)
70
71
  end
71
72
 
72
73
  def connect_timeout=(seconds)
@@ -107,7 +108,6 @@ class TCPClient
107
108
  buffered: @buffered,
108
109
  keep_alive: @keep_alive,
109
110
  reverse_lookup: @reverse_lookup,
110
- timeout: @timeout,
111
111
  connect_timeout: @connect_timeout,
112
112
  read_timeout: @read_timeout,
113
113
  write_timeout: @write_timeout,
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TCPClient
4
+ class Deadline
5
+ MONOTONIC = defined?(Process::CLOCK_MONOTONIC) ? true : false
6
+
7
+ def initialize(timeout)
8
+ timeout = timeout&.to_f
9
+ @deadline = timeout&.positive? ? now + timeout : 0
10
+ end
11
+
12
+ def valid?
13
+ @deadline != 0
14
+ end
15
+
16
+ def remaining_time
17
+ (@deadline != 0) && (remaining = @deadline - now) > 0 ? remaining : nil
18
+ end
19
+
20
+ private
21
+
22
+ if MONOTONIC
23
+ def now
24
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
25
+ end
26
+ else
27
+ def now
28
+ ::Time.now
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'configuration'
2
4
 
3
5
  class TCPClient
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'socket'
4
-
5
3
  class TCPClient
6
4
  class NoOpenSSL < RuntimeError
7
5
  def initialize
8
- super('OpenSSL is not avail')
6
+ super('OpenSSL is not available')
9
7
  end
10
8
  end
11
9
 
@@ -29,7 +27,7 @@ class TCPClient
29
27
 
30
28
  class NotAnException < TypeError
31
29
  def initialize(object)
32
- super("not a valid exception class - #{object.inspect}")
30
+ super("exception class required - #{object.inspect}")
33
31
  end
34
32
  end
35
33
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module IOWithDeadlineMixin
2
4
  def self.included(mod)
3
5
  methods = mod.instance_methods
@@ -9,25 +11,22 @@ module IOWithDeadlineMixin
9
11
  end
10
12
 
11
13
  def read_with_deadline(bytes_to_read, deadline, exception)
12
- raise(exception) if Time.now > deadline
14
+ raise(exception) unless deadline.remaining_time
13
15
  result = ''.b
14
- return result if bytes_to_read <= 0
15
- loop do
16
+ while result.bytesize < bytes_to_read
16
17
  read =
17
18
  with_deadline(deadline, exception) do
18
19
  read_nonblock(bytes_to_read - result.bytesize, exception: false)
19
20
  end
20
- unless read
21
- close
22
- return result
23
- end
24
- result += read
25
- return result if result.bytesize >= bytes_to_read
21
+ next result += read if read
22
+ close
23
+ break
26
24
  end
25
+ result
27
26
  end
28
27
 
29
28
  def write_with_deadline(data, deadline, exception)
30
- raise(exception) if Time.now > deadline
29
+ raise(exception) unless deadline.remaining_time
31
30
  return 0 if (size = data.bytesize).zero?
32
31
  result = 0
33
32
  loop do
@@ -46,10 +45,10 @@ module IOWithDeadlineMixin
46
45
  loop do
47
46
  case ret = yield
48
47
  when :wait_writable
49
- raise(exception) if (remaining_time = deadline - Time.now) <= 0
48
+ remaining_time = deadline.remaining_time or raise(exception)
50
49
  raise(exception) if wait_writable(remaining_time).nil?
51
50
  when :wait_readable
52
- raise(exception) if (remaining_time = deadline - Time.now) <= 0
51
+ remaining_time = deadline.remaining_time or raise(exception)
53
52
  raise(exception) if wait_readable(remaining_time).nil?
54
53
  else
55
54
  return ret
@@ -65,10 +64,10 @@ module IOWithDeadlineMixin
65
64
  loop do
66
65
  case ret = yield
67
66
  when :wait_writable
68
- raise(exception) if (remaining_time = deadline - Time.now) <= 0
67
+ remaining_time = deadline.remaining_time or raise(exception)
69
68
  raise(exception) if ::IO.select(nil, [self], nil, remaining_time).nil?
70
69
  when :wait_readable
71
- raise(exception) if (remaining_time = deadline - Time.now) <= 0
70
+ remaining_time = deadline.remaining_time or raise(exception)
72
71
  raise(exception) if ::IO.select([self], nil, nil, remaining_time).nil?
73
72
  else
74
73
  return ret
@@ -1,46 +1,39 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'openssl'
3
5
  rescue LoadError
4
6
  return
5
7
  end
6
8
 
9
+ require_relative 'deadline'
7
10
  require_relative 'mixin/io_with_deadline'
8
11
 
9
12
  class TCPClient
10
13
  class SSLSocket < ::OpenSSL::SSL::SSLSocket
11
14
  include IOWithDeadlineMixin
12
15
 
13
- def initialize(socket, address, configuration, exception)
16
+ def initialize(socket, address, configuration, deadline, exception)
14
17
  ssl_params = Hash[configuration.ssl_params]
15
- self.sync_close = true
16
18
  super(socket, create_context(ssl_params))
17
- connect_to(
18
- address,
19
- ssl_params[:verify_mode] != OpenSSL::SSL::VERIFY_NONE,
20
- configuration.connect_timeout,
21
- exception
22
- )
19
+ self.sync_close = true
20
+ self.hostname = address.hostname
21
+ deadline.valid? ? connect_with_deadline(deadline, exception) : connect
22
+ post_connection_check(address.hostname) if should_verify?(ssl_params)
23
23
  end
24
24
 
25
25
  private
26
26
 
27
27
  def create_context(ssl_params)
28
- context = OpenSSL::SSL::SSLContext.new
29
- context.set_params(ssl_params)
30
- context
28
+ OpenSSL::SSL::SSLContext.new.tap { |ctx| ctx.set_params(ssl_params) }
31
29
  end
32
30
 
33
- def connect_to(address, check, timeout, exception)
34
- self.hostname = address.hostname
35
- timeout = timeout.to_f
36
- if timeout.zero?
37
- connect
38
- else
39
- with_deadline(Time.now + timeout, exception) do
40
- connect_nonblock(exception: false)
41
- end
42
- end
43
- post_connection_check(address.hostname) if check
31
+ def connect_with_deadline(deadline, exception)
32
+ with_deadline(deadline, exception) { connect_nonblock(exception: false) }
33
+ end
34
+
35
+ def should_verify?(ssl_params)
36
+ ssl_params[:verify_mode] != OpenSSL::SSL::VERIFY_NONE
44
37
  end
45
38
  end
46
39
 
@@ -1,27 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
4
+ require_relative 'deadline'
2
5
  require_relative 'mixin/io_with_deadline'
3
6
 
4
7
  class TCPClient
5
8
  class TCPSocket < ::Socket
6
9
  include IOWithDeadlineMixin
7
10
 
8
- def initialize(address, configuration, exception)
11
+ def initialize(address, configuration, deadline, exception)
9
12
  super(address.addrinfo.ipv6? ? :INET6 : :INET, :STREAM)
10
13
  configure(configuration)
11
- connect_to(address, configuration.connect_timeout, exception)
14
+ connect_to(as_addr_in(address), deadline, exception)
12
15
  end
13
16
 
14
17
  private
15
18
 
16
- def connect_to(address, timeout, exception)
17
- addr =
18
- ::Socket.pack_sockaddr_in(
19
- address.addrinfo.ip_port,
20
- address.addrinfo.ip_address
21
- )
22
- timeout = timeout.to_f
23
- return connect(addr) if timeout.zero?
24
- with_deadline(Time.now + timeout, exception) do
19
+ def as_addr_in(address)
20
+ addrinfo = address.addrinfo
21
+ ::Socket.pack_sockaddr_in(addrinfo.ip_port, addrinfo.ip_address)
22
+ end
23
+
24
+ def connect_to(addr, deadline, exception)
25
+ return connect(addr) unless deadline.valid?
26
+ with_deadline(deadline, exception) do
25
27
  connect_nonblock(addr, exception: false)
26
28
  end
27
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class TCPClient
2
- VERSION = '0.3.2'.freeze
4
+ VERSION = '0.5.1'
3
5
  end
data/lib/tcp-client.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'tcp-client/errors'
4
4
  require_relative 'tcp-client/address'
5
+ require_relative 'tcp-client/deadline'
5
6
  require_relative 'tcp-client/tcp_socket'
6
7
  require_relative 'tcp-client/ssl_socket'
7
8
  require_relative 'tcp-client/configuration'
@@ -9,33 +10,33 @@ require_relative 'tcp-client/default_configuration'
9
10
  require_relative 'tcp-client/version'
10
11
 
11
12
  class TCPClient
12
- def self.open(addr, configuration = Configuration.default)
13
+ def self.open(address, configuration = Configuration.default)
13
14
  client = new
14
- client.connect(Address.new(addr), configuration)
15
- block_given? ? yield(client) : client
16
- ensure
17
- client&.close if block_given?
15
+ client.connect(Address.new(address), configuration)
16
+ return client unless block_given?
17
+ begin
18
+ yield(client)
19
+ ensure
20
+ client.close
21
+ end
18
22
  end
19
23
 
20
- attr_reader :address
24
+ attr_reader :address, :configuration
21
25
 
22
26
  def initialize
23
- @socket = @address = @deadline = @cfg = nil
27
+ @socket = @address = @deadline = @configuration = nil
24
28
  end
25
29
 
26
30
  def to_s
27
- @address ? @address.to_s : ''
31
+ @address&.to_s || ''
28
32
  end
29
33
 
30
- def connect(addr, configuration, exception: nil)
31
- close
34
+ def connect(address, configuration, exception: nil)
32
35
  raise(NoOpenSSL) if configuration.ssl? && !defined?(SSLSocket)
33
- @address = Address.new(addr)
34
- @cfg = configuration.dup
35
- exception ||= configuration.connect_timeout_error
36
- @socket = TCPSocket.new(@address, @cfg, exception)
37
- @cfg.ssl? &&
38
- @socket = SSLSocket.new(@socket, @address, configuration, exception)
36
+ close
37
+ @address = Address.new(address)
38
+ @configuration = configuration.dup.freeze
39
+ @socket = create_socket(exception)
39
40
  self
40
41
  end
41
42
 
@@ -55,9 +56,8 @@ class TCPClient
55
56
  def with_deadline(timeout)
56
57
  previous_deadline = @deadline
57
58
  raise(NoBlockGiven) unless block_given?
58
- tm = timeout&.to_f
59
- raise(InvalidDeadLine) unless tm&.positive?
60
- @deadline = Time.now + tm
59
+ @deadline = Deadline.new(timeout)
60
+ raise(InvalidDeadLine, timeout) unless @deadline.valid?
61
61
  yield(self)
62
62
  ensure
63
63
  @deadline = previous_deadline
@@ -67,18 +67,18 @@ class TCPClient
67
67
  raise(NotConnected) if closed?
68
68
  timeout.nil? && @deadline and
69
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)
70
+ deadline = Deadline.new(timeout || @configuration.read_timeout)
71
+ return @socket.read(nbytes) unless deadline.valid?
72
+ read_with_deadline(nbytes, deadline, exception)
73
73
  end
74
74
 
75
75
  def write(*msg, timeout: nil, exception: nil)
76
76
  raise(NotConnected) if closed?
77
77
  timeout.nil? && @deadline and
78
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)
79
+ deadline = Deadline.new(timeout || @configuration.read_timeout)
80
+ return @socket.write(*msg) unless deadline.valid?
81
+ write_with_deadline(msg, deadline, exception)
82
82
  end
83
83
 
84
84
  def flush
@@ -88,16 +88,21 @@ class TCPClient
88
88
 
89
89
  private
90
90
 
91
+ def create_socket(exception)
92
+ exception ||= @configuration.connect_timeout_error
93
+ deadline = Deadline.new(@configuration.connect_timeout)
94
+ @socket = TCPSocket.new(@address, @configuration, deadline, exception)
95
+ return @socket unless @configuration.ssl?
96
+ SSLSocket.new(@socket, @address, @configuration, deadline, exception)
97
+ end
98
+
91
99
  def read_with_deadline(nbytes, deadline, exception)
92
- @socket.read_with_deadline(
93
- nbytes,
94
- deadline,
95
- exception || @cfg.read_timeout_error
96
- )
100
+ exception ||= @configuration.read_timeout_error
101
+ @socket.read_with_deadline(nbytes, deadline, exception)
97
102
  end
98
103
 
99
104
  def write_with_deadline(msg, deadline, exception)
100
- exception ||= @cfg.write_timeout_error
105
+ exception ||= @configuration.write_timeout_error
101
106
  msg.sum do |chunk|
102
107
  @socket.write_with_deadline(chunk.b, deadline, exception)
103
108
  end
data/lib/tcp_client.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'tcp-client'
data/rakefile.rb CHANGED
@@ -11,7 +11,6 @@ CLOBBER << 'prj'
11
11
  task(:default) { exec('rake --tasks') }
12
12
 
13
13
  Rake::TestTask.new(:test) do |task|
14
- task.test_files = FileList['test/**/*_test.rb']
15
- task.ruby_opts = %w[-w]
16
- task.verbose = true
14
+ task.pattern = 'test/**/*_test.rb'
15
+ task.warning = task.verbose = true
17
16
  end
data/sample/google.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../lib/tcp-client'
2
4
 
3
5
  TCPClient.configure(
@@ -6,15 +8,14 @@ TCPClient.configure(
6
8
  read_timeout: 0.5 # seconds to read some bytes
7
9
  )
8
10
 
9
- # the following request sequence is not allowed
10
- # to last longer than 1.25 seconds:
11
+ # the following sequence is not allowed to last longer than 1.25 seconds:
11
12
  # 0.5 seconds to connect
12
13
  # + 0.25 seconds to write data
13
14
  # + 0.5 seconds to read a response
14
15
 
15
16
  TCPClient.open('www.google.com:80') do |client|
16
17
  # simple HTTP get request
17
- pp client.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
18
+ pp client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
18
19
 
19
20
  # read "HTTP/1.1 " + 3 byte HTTP status code
20
21
  pp client.read(12)
data/sample/google_ssl.rb CHANGED
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../lib/tcp-client'
2
4
 
3
5
  TCPClient.configure do |cfg|
4
- cfg.connect_timeout = 1 # second to connect the server
6
+ cfg.connect_timeout = 1 # limit connect time the server to 1 second
5
7
  cfg.ssl_params = { ssl_version: :TLSv1_2 } # use TLS 1.2
6
8
  end
7
9
 
8
10
  TCPClient.open('www.google.com:443') do |client|
9
- # query should not last longer than 0.5 seconds
11
+ # next sequence should not last longer than 0.5 seconds
10
12
  client.with_deadline(0.5) do
11
13
  # simple HTTP get request
12
- pp client.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
14
+ pp client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
13
15
 
14
16
  # read "HTTP/1.1 " + 3 byte HTTP status code
15
17
  pp client.read(12)
data/tcp-client.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative './lib/tcp-client/version'
4
4
 
5
- GemSpec = Gem::Specification.new do |spec|
5
+ Gem::Specification.new do |spec|
6
6
  spec.name = 'tcp-client'
7
7
  spec.version = TCPClient::VERSION
8
8
  spec.author = 'Mike Blumtritt'
@@ -11,16 +11,20 @@ GemSpec = Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = 'A TCP client implementation with working timeout support.'
13
13
  spec.description = <<~DESCRIPTION
14
- This gem implements a TCP client with (optional) SSL support. The
15
- motivation of this project is the need to have a _really working_
16
- easy to use client which can handle time limits correctly. Unlike
17
- other implementations this client respects given/configurable time
18
- limits for each method (`connect`, `read`, `write`).
14
+ This Gem implements a TCP client with (optional) SSL support.
15
+ It is an easy to use, versatile configurable client that can correctly
16
+ handle time limits.
17
+ Unlike other implementations, this client respects
18
+ predefined/configurable time limits for each method
19
+ (`connect`, `read`, `write`). Deadlines for a sequence of read/write
20
+ actions can also be monitored.
19
21
  DESCRIPTION
20
22
  spec.homepage = 'https://github.com/mblumtritt/tcp-client'
23
+ spec.license = 'BSD-3-Clause'
21
24
 
22
25
  spec.metadata['source_code_uri'] = 'https://github.com/mblumtritt/tcp-client'
23
- spec.metadata['bug_tracker_uri'] = 'https://github.com/mblumtritt/tcp-client/issues'
26
+ spec.metadata['bug_tracker_uri'] =
27
+ 'https://github.com/mblumtritt/tcp-client/issues'
24
28
 
25
29
  spec.add_development_dependency 'bundler'
26
30
  spec.add_development_dependency 'minitest'
@@ -30,5 +34,5 @@ GemSpec = Gem::Specification.new do |spec|
30
34
  spec.test_files = all_files.grep(%r{^test/})
31
35
  spec.files = all_files - spec.test_files
32
36
 
33
- spec.extra_rdoc_files = %w[README.md]
37
+ spec.extra_rdoc_files = %w[README.md LICENSE]
34
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
 
3
5
  class AddressTest < MiniTest::Test
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
 
3
5
  class ConfigurationTest < MiniTest::Test
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../test_helper'
4
+
5
+ class Deadlineest < MiniTest::Test
6
+ parallelize_me!
7
+
8
+ def test_validity
9
+ assert(TCPClient::Deadline.new(1).valid?)
10
+ assert(TCPClient::Deadline.new(0.0001).valid?)
11
+
12
+ refute(TCPClient::Deadline.new(0).valid?)
13
+ refute(TCPClient::Deadline.new(nil).valid?)
14
+ end
15
+
16
+ def test_remaining_time
17
+ assert(TCPClient::Deadline.new(1).remaining_time > 0)
18
+
19
+ assert_nil(TCPClient::Deadline.new(0).remaining_time)
20
+ assert_nil(TCPClient::Deadline.new(nil).remaining_time)
21
+
22
+ deadline = TCPClient::Deadline.new(0.2)
23
+ sleep(0.2)
24
+ assert_nil(deadline.remaining_time)
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
 
3
5
  class VersionTest < MiniTest::Test
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'test_helper'
2
4
 
3
5
  class TCPClientTest < MiniTest::Test
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.3.2
4
+ version: 0.5.1
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-07-11 00:00:00.000000000 Z
11
+ date: 2021-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,23 +53,28 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  description: |
56
- This gem implements a TCP client with (optional) SSL support. The
57
- motivation of this project is the need to have a _really working_
58
- easy to use client which can handle time limits correctly. Unlike
59
- other implementations this client respects given/configurable time
60
- limits for each method (`connect`, `read`, `write`).
56
+ This Gem implements a TCP client with (optional) SSL support.
57
+ It is an easy to use, versatile configurable client that can correctly
58
+ handle time limits.
59
+ Unlike other implementations, this client respects
60
+ predefined/configurable time limits for each method
61
+ (`connect`, `read`, `write`). Deadlines for a sequence of read/write
62
+ actions can also be monitored.
61
63
  email:
62
64
  executables: []
63
65
  extensions: []
64
66
  extra_rdoc_files:
65
67
  - README.md
68
+ - LICENSE
66
69
  files:
67
70
  - ".gitignore"
71
+ - LICENSE
68
72
  - README.md
69
73
  - gems.rb
70
74
  - lib/tcp-client.rb
71
75
  - lib/tcp-client/address.rb
72
76
  - lib/tcp-client/configuration.rb
77
+ - lib/tcp-client/deadline.rb
73
78
  - lib/tcp-client/default_configuration.rb
74
79
  - lib/tcp-client/errors.rb
75
80
  - lib/tcp-client/mixin/io_with_deadline.rb
@@ -83,12 +88,14 @@ files:
83
88
  - tcp-client.gemspec
84
89
  - test/tcp-client/address_test.rb
85
90
  - test/tcp-client/configuration_test.rb
91
+ - test/tcp-client/deadline_test.rb
86
92
  - test/tcp-client/default_configuration_test.rb
87
93
  - test/tcp-client/version_test.rb
88
94
  - test/tcp_client_test.rb
89
95
  - test/test_helper.rb
90
96
  homepage: https://github.com/mblumtritt/tcp-client
91
- licenses: []
97
+ licenses:
98
+ - BSD-3-Clause
92
99
  metadata:
93
100
  source_code_uri: https://github.com/mblumtritt/tcp-client
94
101
  bug_tracker_uri: https://github.com/mblumtritt/tcp-client/issues
@@ -114,6 +121,7 @@ summary: A TCP client implementation with working timeout support.
114
121
  test_files:
115
122
  - test/tcp-client/address_test.rb
116
123
  - test/tcp-client/configuration_test.rb
124
+ - test/tcp-client/deadline_test.rb
117
125
  - test/tcp-client/default_configuration_test.rb
118
126
  - test/tcp-client/version_test.rb
119
127
  - test/tcp_client_test.rb