tcp-client 0.3.2 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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