tcp-client 0.4.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a9fe010bf08d26652d93212beee9ffe55275b3b051809a3093d2ff066719372
4
- data.tar.gz: bf23f4441be6f32d581dfb069afb09bfbb8a9ca78b90f3e0f2332f16abed89e8
3
+ metadata.gz: 582575d6b5a66a264900141fd5010a1a80a363e15d0ca369f262889d99d932d0
4
+ data.tar.gz: a0c2335a50addbf5f424d869cd2931256a1334a0a075530ef3873d9065246834
5
5
  SHA512:
6
- metadata.gz: c929de839e7335c4e8e950d0b96fb6028c74ba1ae6a1acec3bb4a572e54a6564d799a8ee8fdef51b77273a83a2af4c5cc29d66a4ed92c3647e1185e103d82fda
7
- data.tar.gz: 00dacdc8d09584e1f4421c7ca62786ab617eaed1005db955ca36da4c1294738591820159d282d27e24fe3194d84e9cd5cb02b47966f231af5d5a26336bcfa366
6
+ metadata.gz: b160bd656c4f6091a669cf65349e54aeaa2e7c057e0a5a3c896edf5e8ab91166a3a5cdafedfe84e4b04d5a79e0602fb2433c6ff9144e6881746f3607b8d8290b
7
+ data.tar.gz: 6726ebdfbc777258e62b3d915fa7e4a57f7ae514d6259ce5f0051ee649294142acf09faac490901e32083072d7a68dd4ce71fa697e0bf05c0d9556c0d57bb6a5
@@ -37,7 +37,7 @@ class TCPClient
37
37
 
38
38
  def initialize_copy(_org)
39
39
  super
40
- @ssl_params = @ssl_params.dup
40
+ @ssl_params = Hash[@ssl_params] if @ssl_params
41
41
  self
42
42
  end
43
43
 
@@ -48,7 +48,7 @@ class TCPClient
48
48
  def ssl=(value)
49
49
  @ssl_params =
50
50
  if Hash === value
51
- value.dup
51
+ Hash[value]
52
52
  else
53
53
  value ? {} : nil
54
54
  end
@@ -83,23 +83,23 @@ class TCPClient
83
83
  end
84
84
 
85
85
  def timeout_error=(exception)
86
- raise(NotAnException, exception) unless exception_class?(exception)
86
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
87
87
  @connect_timeout_error =
88
88
  @read_timeout_error = @write_timeout_error = exception
89
89
  end
90
90
 
91
91
  def connect_timeout_error=(exception)
92
- raise(NotAnException, exception) unless exception_class?(exception)
92
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
93
93
  @connect_timeout_error = exception
94
94
  end
95
95
 
96
96
  def read_timeout_error=(exception)
97
- raise(NotAnException, exception) unless exception_class?(exception)
97
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
98
98
  @read_timeout_error = exception
99
99
  end
100
100
 
101
101
  def write_timeout_error=(exception)
102
- raise(NotAnException, exception) unless exception_class?(exception)
102
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
103
103
  @write_timeout_error = exception
104
104
  end
105
105
 
@@ -136,7 +136,7 @@ class TCPClient
136
136
  def set(attribute, value)
137
137
  public_send("#{attribute}=", value)
138
138
  rescue NoMethodError
139
- raise(UnknownAttribute, attribute)
139
+ raise(UnknownAttributeError, attribute)
140
140
  end
141
141
 
142
142
  def seconds(value)
@@ -2,7 +2,7 @@
2
2
 
3
3
  class TCPClient
4
4
  class Deadline
5
- MONOTONIC = !!defined?(Process::CLOCK_MONOTONIC)
5
+ MONOTONIC = defined?(Process::CLOCK_MONOTONIC) ? true : false
6
6
 
7
7
  def initialize(timeout)
8
8
  timeout = timeout&.to_f
@@ -29,4 +29,6 @@ class TCPClient
29
29
  end
30
30
  end
31
31
  end
32
+
33
+ private_constant(:Deadline)
32
34
  end
@@ -1,37 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TCPClient
4
- class NoOpenSSL < RuntimeError
4
+ class NoOpenSSLError < RuntimeError
5
5
  def initialize
6
6
  super('OpenSSL is not available')
7
7
  end
8
8
  end
9
9
 
10
- class NoBlockGiven < ArgumentError
10
+ class NoBlockGivenError < ArgumentError
11
11
  def initialize
12
12
  super('no block given')
13
13
  end
14
14
  end
15
15
 
16
- class InvalidDeadLine < ArgumentError
16
+ class InvalidDeadLineError < ArgumentError
17
17
  def initialize(timeout)
18
18
  super("invalid deadline - #{timeout}")
19
19
  end
20
20
  end
21
21
 
22
- class UnknownAttribute < ArgumentError
22
+ class UnknownAttributeError < ArgumentError
23
23
  def initialize(attribute)
24
24
  super("unknown attribute - #{attribute}")
25
25
  end
26
26
  end
27
27
 
28
- class NotAnException < TypeError
28
+ class NotAnExceptionError < TypeError
29
29
  def initialize(object)
30
30
  super("exception class required - #{object.inspect}")
31
31
  end
32
32
  end
33
33
 
34
- class NotConnected < IOError
34
+ class NotConnectedError < IOError
35
35
  def initialize
36
36
  super('client not connected')
37
37
  end
@@ -65,6 +65,18 @@ class TCPClient
65
65
  end
66
66
  end
67
67
 
68
- Timeout = TimeoutError # backward compatibility
69
- deprecate_constant(:Timeout)
68
+ NoOpenSSL = NoOpenSSLError
69
+ NoBlockGiven = NoBlockGivenError
70
+ InvalidDeadLine = InvalidDeadLineError
71
+ UnknownAttribute = UnknownAttributeError
72
+ NotAnException = NotAnExceptionError
73
+ NotConnected = NotConnectedError
74
+ deprecate_constant(
75
+ :NoOpenSSL,
76
+ :NoBlockGiven,
77
+ :InvalidDeadLine,
78
+ :UnknownAttribute,
79
+ :NotAnException,
80
+ :NotConnected
81
+ )
70
82
  end
@@ -5,6 +5,8 @@ module IOWithDeadlineMixin
5
5
  methods = mod.instance_methods
6
6
  if methods.index(:wait_writable) && methods.index(:wait_readable)
7
7
  mod.include(ViaWaitMethod)
8
+ elsif methods.index(:to_io)
9
+ mod.include(ViaIOWaitMethod)
8
10
  else
9
11
  mod.include(ViaSelect)
10
12
  end
@@ -12,6 +14,13 @@ module IOWithDeadlineMixin
12
14
 
13
15
  def read_with_deadline(bytes_to_read, deadline, exception)
14
16
  raise(exception) unless deadline.remaining_time
17
+ if bytes_to_read.nil?
18
+ return(
19
+ with_deadline(deadline, exception) do
20
+ read_nonblock(65_536, exception: false)
21
+ end
22
+ )
23
+ end
15
24
  result = ''.b
16
25
  while result.bytesize < bytes_to_read
17
26
  read =
@@ -59,6 +68,25 @@ module IOWithDeadlineMixin
59
68
  end
60
69
  end
61
70
 
71
+ module ViaIOWaitMethod
72
+ private def with_deadline(deadline, exception)
73
+ loop do
74
+ case ret = yield
75
+ when :wait_writable
76
+ remaining_time = deadline.remaining_time or raise(exception)
77
+ raise(exception) if to_io.wait_writable(remaining_time).nil?
78
+ when :wait_readable
79
+ remaining_time = deadline.remaining_time or raise(exception)
80
+ raise(exception) if to_io.wait_readable(remaining_time).nil?
81
+ else
82
+ return ret
83
+ end
84
+ end
85
+ rescue Errno::ETIMEDOUT
86
+ raise(exception)
87
+ end
88
+ end
89
+
62
90
  module ViaSelect
63
91
  private def with_deadline(deadline, exception)
64
92
  loop do
@@ -78,5 +106,5 @@ module IOWithDeadlineMixin
78
106
  end
79
107
  end
80
108
 
81
- private_constant(:ViaWaitMethod, :ViaSelect)
109
+ private_constant(:ViaWaitMethod, :ViaIOWaitMethod, :ViaSelect)
82
110
  end
@@ -13,12 +13,11 @@ class TCPClient
13
13
  class SSLSocket < ::OpenSSL::SSL::SSLSocket
14
14
  include IOWithDeadlineMixin
15
15
 
16
- def initialize(socket, address, configuration, exception)
16
+ def initialize(socket, address, configuration, deadline, exception)
17
17
  ssl_params = Hash[configuration.ssl_params]
18
18
  super(socket, create_context(ssl_params))
19
19
  self.sync_close = true
20
20
  self.hostname = address.hostname
21
- deadline = Deadline.new(configuration.connect_timeout)
22
21
  deadline.valid? ? connect_with_deadline(deadline, exception) : connect
23
22
  post_connection_check(address.hostname) if should_verify?(ssl_params)
24
23
  end
@@ -26,9 +25,7 @@ class TCPClient
26
25
  private
27
26
 
28
27
  def create_context(ssl_params)
29
- context = OpenSSL::SSL::SSLContext.new
30
- context.set_params(ssl_params)
31
- context
28
+ OpenSSL::SSL::SSLContext.new.tap { |ctx| ctx.set_params(ssl_params) }
32
29
  end
33
30
 
34
31
  def connect_with_deadline(deadline, exception)
@@ -36,7 +33,8 @@ class TCPClient
36
33
  end
37
34
 
38
35
  def should_verify?(ssl_params)
39
- ssl_params[:verify_mode] != OpenSSL::SSL::VERIFY_NONE
36
+ ssl_params[:verify_mode] != OpenSSL::SSL::VERIFY_NONE &&
37
+ context.verify_hostname
40
38
  end
41
39
  end
42
40
 
@@ -8,21 +8,20 @@ class TCPClient
8
8
  class TCPSocket < ::Socket
9
9
  include IOWithDeadlineMixin
10
10
 
11
- def initialize(address, configuration, exception)
11
+ def initialize(address, configuration, deadline, exception)
12
12
  super(address.addrinfo.ipv6? ? :INET6 : :INET, :STREAM)
13
13
  configure(configuration)
14
- deadline = Deadline.new(configuration.connect_timeout)
15
- connect_to(address, deadline, exception)
14
+ connect_to(as_addr_in(address), deadline, exception)
16
15
  end
17
16
 
18
17
  private
19
18
 
20
- def connect_to(address, deadline, exception)
21
- addr =
22
- ::Socket.pack_sockaddr_in(
23
- address.addrinfo.ip_port,
24
- address.addrinfo.ip_address
25
- )
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)
26
25
  return connect(addr) unless deadline.valid?
27
26
  with_deadline(deadline, exception) do
28
27
  connect_nonblock(addr, exception: false)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TCPClient
4
- VERSION = '0.4.1'
4
+ VERSION = '0.7.0'
5
5
  end
data/lib/tcp-client.rb CHANGED
@@ -10,33 +10,46 @@ require_relative 'tcp-client/default_configuration'
10
10
  require_relative 'tcp-client/version'
11
11
 
12
12
  class TCPClient
13
- def self.open(addr, configuration = Configuration.default)
13
+ def self.open(address, configuration = Configuration.default)
14
14
  client = new
15
- client.connect(Address.new(addr), configuration)
15
+ client.connect(Address.new(address), configuration)
16
16
  block_given? ? yield(client) : client
17
17
  ensure
18
- client&.close if block_given?
18
+ client.close if block_given?
19
19
  end
20
20
 
21
- attr_reader :address
21
+ def self.with_deadline(
22
+ timeout,
23
+ address,
24
+ configuration = Configuration.default
25
+ )
26
+ client = nil
27
+ raise(NoBlockGivenError) unless block_given?
28
+ address = Address.new(address)
29
+ client = new
30
+ client.with_deadline(timeout) do
31
+ yield(client.connect(address, configuration))
32
+ end
33
+ ensure
34
+ client&.close
35
+ end
36
+
37
+ attr_reader :address, :configuration
22
38
 
23
39
  def initialize
24
- @socket = @address = @deadline = @cfg = nil
40
+ @socket = @address = @deadline = @configuration = nil
25
41
  end
26
42
 
27
43
  def to_s
28
- @address ? @address.to_s : ''
44
+ @address&.to_s || ''
29
45
  end
30
46
 
31
- def connect(addr, configuration, exception: nil)
32
- raise(NoOpenSSL) if configuration.ssl? && !defined?(SSLSocket)
33
- close
34
- @address = Address.new(addr)
35
- @cfg = configuration.dup
36
- exception ||= configuration.connect_timeout_error
37
- @socket = TCPSocket.new(@address, @cfg, exception)
38
- @cfg.ssl? &&
39
- @socket = SSLSocket.new(@socket, @address, configuration, exception)
47
+ def connect(address, configuration, timeout: nil, exception: nil)
48
+ close if @socket
49
+ raise(NoOpenSSLError) if configuration.ssl? && !defined?(SSLSocket)
50
+ @address = Address.new(address)
51
+ @configuration = configuration.dup.freeze
52
+ @socket = create_socket(timeout, exception)
40
53
  self
41
54
  end
42
55
 
@@ -55,48 +68,48 @@ class TCPClient
55
68
 
56
69
  def with_deadline(timeout)
57
70
  previous_deadline = @deadline
58
- raise(NoBlockGiven) unless block_given?
71
+ raise(NoBlockGivenError) unless block_given?
59
72
  @deadline = Deadline.new(timeout)
60
- raise(InvalidDeadLine, timeout) unless @deadline.valid?
73
+ raise(InvalidDeadLineError, timeout) unless @deadline.valid?
61
74
  yield(self)
62
75
  ensure
63
76
  @deadline = previous_deadline
64
77
  end
65
78
 
66
- def read(nbytes, timeout: nil, exception: nil)
67
- raise(NotConnected) if closed?
68
- timeout.nil? && @deadline and
69
- return read_with_deadline(nbytes, @deadline, exception)
70
- deadline = Deadline.new(timeout || @cfg.read_timeout)
79
+ def read(nbytes = nil, timeout: nil, exception: nil)
80
+ raise(NotConnectedError) if closed?
81
+ deadline = create_deadline(timeout, configuration.read_timeout)
71
82
  return @socket.read(nbytes) unless deadline.valid?
72
- read_with_deadline(nbytes, deadline, exception)
83
+ exception ||= configuration.read_timeout_error
84
+ @socket.read_with_deadline(nbytes, deadline, exception)
73
85
  end
74
86
 
75
87
  def write(*msg, timeout: nil, exception: nil)
76
- raise(NotConnected) if closed?
77
- timeout.nil? && @deadline and
78
- return write_with_deadline(msg, @deadline, exception)
79
- deadline = Deadline.new(timeout || @cfg.read_timeout)
88
+ raise(NotConnectedError) if closed?
89
+ deadline = create_deadline(timeout, configuration.write_timeout)
80
90
  return @socket.write(*msg) unless deadline.valid?
81
- write_with_deadline(msg, deadline, exception)
91
+ exception ||= configuration.write_timeout_error
92
+ msg.sum do |chunk|
93
+ @socket.write_with_deadline(chunk.b, deadline, exception)
94
+ end
82
95
  end
83
96
 
84
97
  def flush
85
- @socket.flush unless closed?
98
+ @socket&.flush
86
99
  self
87
100
  end
88
101
 
89
102
  private
90
103
 
91
- def read_with_deadline(nbytes, deadline, exception)
92
- exception ||= @cfg.read_timeout_error
93
- @socket.read_with_deadline(nbytes, deadline, exception)
104
+ def create_deadline(timeout, default)
105
+ timeout.nil? && @deadline ? @deadline : Deadline.new(timeout || default)
94
106
  end
95
107
 
96
- def write_with_deadline(msg, deadline, exception)
97
- exception ||= @cfg.write_timeout_error
98
- msg.sum do |chunk|
99
- @socket.write_with_deadline(chunk.b, deadline, exception)
100
- end
108
+ def create_socket(timeout, exception)
109
+ deadline = create_deadline(timeout, configuration.connect_timeout)
110
+ exception ||= configuration.connect_timeout_error
111
+ @socket = TCPSocket.new(address, configuration, deadline, exception)
112
+ return @socket unless configuration.ssl?
113
+ SSLSocket.new(@socket, address, configuration, deadline, exception)
101
114
  end
102
115
  end
data/lib/tcp_client.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'tcp-client'
data/sample/google.rb CHANGED
@@ -2,21 +2,20 @@
2
2
 
3
3
  require_relative '../lib/tcp-client'
4
4
 
5
- TCPClient.configure(
6
- connect_timeout: 0.5, # seconds to connect the server
7
- write_timeout: 0.25, # seconds to write a single data junk
8
- read_timeout: 0.5 # seconds to read some bytes
9
- )
10
-
11
- # the following sequence is not allowed to last longer than 1.25 seconds:
12
- # 0.5 seconds to connect
13
- # + 0.25 seconds to write data
14
- # + 0.5 seconds to read a response
5
+ # global configuration.
6
+ # - 0.5 seconds to connect the server
7
+ # - 0.25 seconds to write a single data junk
8
+ # - 0.25 seconds to read some bytes
9
+ TCPClient.configure do |cfg|
10
+ cfg.connect_timeout = 0.5
11
+ cfg.write_timeout = 0.25
12
+ cfg.read_timeout = 0.25
13
+ end
15
14
 
15
+ # request to Google:
16
+ # - send a simple HTTP get request
17
+ # - read 12 byte: "HTTP/1.1 " + 3 byte HTTP status code
16
18
  TCPClient.open('www.google.com:80') do |client|
17
- # simple HTTP get request
18
- pp client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
19
-
20
- # read "HTTP/1.1 " + 3 byte HTTP status code
21
- pp client.read(12)
19
+ p client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
20
+ p client.read(12)
22
21
  end
data/sample/google_ssl.rb CHANGED
@@ -2,18 +2,23 @@
2
2
 
3
3
  require_relative '../lib/tcp-client'
4
4
 
5
- TCPClient.configure do |cfg|
6
- cfg.connect_timeout = 1 # limit connect time the server to 1 second
7
- cfg.ssl_params = { ssl_version: :TLSv1_2 } # use TLS 1.2
8
- end
9
-
10
- TCPClient.open('www.google.com:443') do |client|
11
- # next sequence should not last longer than 0.5 seconds
12
- client.with_deadline(0.5) do
13
- # simple HTTP get request
14
- pp client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
5
+ # create a configuration.
6
+ # - use TLS 1.2
7
+ # - don't use internal buffering
8
+ cfg =
9
+ TCPClient::Configuration.create(
10
+ buffered: false,
11
+ ssl_params: {
12
+ ssl_version: :TLSv1_2
13
+ }
14
+ )
15
15
 
16
- # read "HTTP/1.1 " + 3 byte HTTP status code
17
- pp client.read(12)
18
- end
16
+ # request to Google:
17
+ # - limit all interactions to 0.5 seconds
18
+ # - use the Configuration cfg
19
+ # - send a simple HTTP get request
20
+ # - read 12 byte: "HTTP/1.1 " + 3 byte HTTP status code
21
+ TCPClient.with_deadline(0.5, 'www.google.com:443', cfg) do |client|
22
+ p client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
23
+ p client.read(12)
19
24
  end
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'
@@ -20,9 +20,11 @@ GemSpec = Gem::Specification.new do |spec|
20
20
  actions can also be monitored.
21
21
  DESCRIPTION
22
22
  spec.homepage = 'https://github.com/mblumtritt/tcp-client'
23
+ spec.license = 'BSD-3-Clause'
23
24
 
24
25
  spec.metadata['source_code_uri'] = 'https://github.com/mblumtritt/tcp-client'
25
- 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'
26
28
 
27
29
  spec.add_development_dependency 'bundler'
28
30
  spec.add_development_dependency 'minitest'
@@ -32,5 +34,5 @@ GemSpec = Gem::Specification.new do |spec|
32
34
  spec.test_files = all_files.grep(%r{^test/})
33
35
  spec.files = all_files - spec.test_files
34
36
 
35
- spec.extra_rdoc_files = %w[README.md]
37
+ spec.extra_rdoc_files = %w[README.md LICENSE]
36
38
  end
data/test/helper.rb ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'minitest/parallel'
5
+ require_relative '../lib/tcp-client'
6
+
7
+ # this pseudo-server never reads or writes anything
8
+ PseudoServer = TCPServer.new('localhost', 0)
9
+ Minitest.after_run { PseudoServer.close }
10
+
11
+ class Test < MiniTest::Test
12
+ parallelize_me!
13
+ end
14
+
15
+ class Timing
16
+ def initialize
17
+ @start_time = nil
18
+ end
19
+
20
+ def started?
21
+ @start_time != nil
22
+ end
23
+
24
+ def start
25
+ @start_time = now
26
+ end
27
+
28
+ def elapsed
29
+ now - @start_time
30
+ end
31
+
32
+ if defined?(Process::CLOCK_MONOTONIC)
33
+ def now
34
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
35
+ end
36
+ else
37
+ def now
38
+ ::Time.now
39
+ end
40
+ end
41
+ end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../test_helper'
4
-
5
- class AddressTest < MiniTest::Test
6
- parallelize_me!
3
+ require_relative '../helper'
7
4
 
5
+ class AddressTest < Test
8
6
  def test_create_from_integer
9
7
  subject = TCPClient::Address.new(42)
10
8
  assert_equal('localhost:42', subject.to_s)
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../test_helper'
4
-
5
- class ConfigurationTest < MiniTest::Test
6
- parallelize_me!
3
+ require_relative '../helper'
7
4
 
5
+ class ConfigurationTest < Test
8
6
  def test_defaults
9
7
  subject = TCPClient::Configuration.new
10
8
  assert(subject.buffered)
@@ -1,25 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../test_helper'
3
+ require_relative '../helper'
4
4
 
5
- class Deadlineest < MiniTest::Test
6
- parallelize_me!
5
+ class DeadlineTest < Test
6
+ Deadline = TCPClient.const_get(:Deadline)
7
7
 
8
8
  def test_validity
9
- assert(TCPClient::Deadline.new(1).valid?)
10
- assert(TCPClient::Deadline.new(0.0001).valid?)
9
+ assert(Deadline.new(1).valid?)
10
+ assert(Deadline.new(0.0001).valid?)
11
11
 
12
- refute(TCPClient::Deadline.new(0).valid?)
13
- refute(TCPClient::Deadline.new(nil).valid?)
12
+ refute(Deadline.new(0).valid?)
13
+ refute(Deadline.new(nil).valid?)
14
14
  end
15
15
 
16
16
  def test_remaining_time
17
- assert(TCPClient::Deadline.new(1).remaining_time > 0)
17
+ assert(Deadline.new(1).remaining_time > 0)
18
18
 
19
- assert_nil(TCPClient::Deadline.new(0).remaining_time)
20
- assert_nil(TCPClient::Deadline.new(nil).remaining_time)
19
+ assert_nil(Deadline.new(0).remaining_time)
20
+ assert_nil(Deadline.new(nil).remaining_time)
21
21
 
22
- deadline = TCPClient::Deadline.new(0.2)
22
+ deadline = Deadline.new(0.2)
23
23
  sleep(0.2)
24
24
  assert_nil(deadline.remaining_time)
25
25
  end
@@ -1,4 +1,4 @@
1
- require_relative '../test_helper'
1
+ require_relative '../helper'
2
2
 
3
3
  class DefauktConfigurationTest < MiniTest::Test
4
4
  def test_default
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../test_helper'
4
-
5
- class VersionTest < MiniTest::Test
6
- parallelize_me!
3
+ require_relative '../helper'
7
4
 
5
+ class VersionTest < Test
8
6
  def test_format
9
7
  assert_match(/\d+\.\d+\.\d+/, TCPClient::VERSION)
10
8
  end
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'test_helper'
3
+ require_relative 'helper'
4
4
 
5
- class TCPClientTest < MiniTest::Test
6
- parallelize_me!
7
-
8
- HUGE_AMOUNT_OF_DATA = Array.new(2024, '?' * 1024).freeze
5
+ class TCPClientTest < Test
6
+ HUGE_AMOUNT_OF_DATA = Array.new(1024, '?' * 2048).freeze
9
7
 
10
8
  attr_reader :config
11
9
 
@@ -17,20 +15,26 @@ class TCPClientTest < MiniTest::Test
17
15
  PseudoServer.local_address.ip_port
18
16
  end
19
17
 
18
+ def address
19
+ "localhost:#{port}"
20
+ end
21
+
20
22
  def test_defaults
21
23
  subject = TCPClient.new
22
24
  assert(subject.closed?)
23
25
  assert_equal('', subject.to_s)
24
26
  assert_nil(subject.address)
25
27
  subject.close
26
- assert_raises(TCPClient::NotConnected) { subject.write('hello world!') }
27
- assert_raises(TCPClient::NotConnected) { subject.read(42) }
28
+ assert_raises(TCPClient::NotConnectedError) do
29
+ subject.write('hello world!')
30
+ end
31
+ assert_raises(TCPClient::NotConnectedError) { subject.read(42) }
28
32
  end
29
33
 
30
34
  def create_nonconnected_client
31
35
  client = TCPClient.new
32
36
  client.connect('', config)
33
- client
37
+ :you_should_not_get_this
34
38
  rescue Errno::EADDRNOTAVAIL
35
39
  client
36
40
  end
@@ -44,17 +48,19 @@ class TCPClientTest < MiniTest::Test
44
48
  assert_equal('localhost', subject.address.hostname)
45
49
  assert_instance_of(Addrinfo, subject.address.addrinfo)
46
50
  assert_same(0, subject.address.addrinfo.ip_port)
47
- assert_raises(TCPClient::NotConnected) { subject.write('hello world!') }
48
- assert_raises(TCPClient::NotConnected) { subject.read(42) }
51
+ assert_raises(TCPClient::NotConnectedError) do
52
+ subject.write('hello world!')
53
+ end
54
+ assert_raises(TCPClient::NotConnectedError) { subject.read(42) }
49
55
  end
50
56
 
51
57
  def test_connected_state
52
- TCPClient.open("localhost:#{port}", config) do |subject|
58
+ TCPClient.open(address, config) do |subject|
53
59
  refute(subject.closed?)
54
- assert_equal("localhost:#{port}", subject.to_s)
60
+ assert_equal(address, subject.to_s)
55
61
  refute_nil(subject.address)
56
62
  address_when_opened = subject.address
57
- assert_equal("localhost:#{port}", subject.address.to_s)
63
+ assert_equal(address, subject.address.to_s)
58
64
  assert_equal('localhost', subject.address.hostname)
59
65
  assert_instance_of(Addrinfo, subject.address.addrinfo)
60
66
  assert_same(port, subject.address.addrinfo.ip_port)
@@ -66,14 +72,14 @@ class TCPClientTest < MiniTest::Test
66
72
  end
67
73
 
68
74
  def check_read_timeout(timeout)
69
- TCPClient.open("localhost:#{port}", config) do |subject|
75
+ TCPClient.open(address, config) do |subject|
70
76
  refute(subject.closed?)
71
- start_time = nil
77
+ timing = Timing.new
72
78
  assert_raises(TCPClient::ReadTimeoutError) do
73
- start_time = Time.now
79
+ timing.start
74
80
  subject.read(42, timeout: timeout)
75
81
  end
76
- assert_in_delta(timeout, Time.now - start_time, 0.15)
82
+ assert_in_delta(timeout, timing.elapsed, 0.15)
77
83
  end
78
84
  end
79
85
 
@@ -84,14 +90,14 @@ class TCPClientTest < MiniTest::Test
84
90
  end
85
91
 
86
92
  def check_write_timeout(timeout)
87
- TCPClient.open("localhost:#{port}", config) do |subject|
93
+ TCPClient.open(address, config) do |subject|
88
94
  refute(subject.closed?)
89
- start_time = nil
95
+ timing = Timing.new
90
96
  assert_raises(TCPClient::WriteTimeoutError) do
91
- start_time = Time.now
97
+ timing.start
92
98
  subject.write(*HUGE_AMOUNT_OF_DATA, timeout: timeout)
93
99
  end
94
- assert_in_delta(timeout, Time.now - start_time, 0.15)
100
+ assert_in_delta(timeout, timing.elapsed, 0.15)
95
101
  end
96
102
  end
97
103
 
@@ -101,7 +107,7 @@ class TCPClientTest < MiniTest::Test
101
107
  end
102
108
 
103
109
  def test_write_deadline
104
- TCPClient.open("localhost:#{port}", config) do |subject|
110
+ TCPClient.open(address, config) do |subject|
105
111
  refute(subject.closed?)
106
112
  assert_raises(TCPClient::WriteTimeoutError) do
107
113
  subject.with_deadline(0.25) do |*args|
@@ -113,25 +119,25 @@ class TCPClientTest < MiniTest::Test
113
119
  end
114
120
 
115
121
  def test_read_deadline
116
- TCPClient.open("localhost:#{port}", config) do |subject|
122
+ TCPClient.open(address, config) do |subject|
117
123
  refute(subject.closed?)
118
124
  assert_raises(TCPClient::ReadTimeoutError) do
119
125
  subject.with_deadline(0.25) do |*args|
120
126
  assert_equal([subject], args)
121
- loop { subject.read(0) }
127
+ loop { subject.read(42) }
122
128
  end
123
129
  end
124
130
  end
125
131
  end
126
132
 
127
133
  def test_read_write_deadline
128
- TCPClient.open("localhost:#{port}", config) do |subject|
134
+ TCPClient.open(address, config) do |subject|
129
135
  refute(subject.closed?)
130
136
  assert_raises(TCPClient::TimeoutError) do
131
137
  subject.with_deadline(0.25) do |*args|
132
138
  assert_equal([subject], args)
133
139
  loop do
134
- subject.write('HUGE_AMOUNT_OF_DATA')
140
+ subject.write('some data')
135
141
  subject.read(0)
136
142
  end
137
143
  end
@@ -140,12 +146,12 @@ class TCPClientTest < MiniTest::Test
140
146
  end
141
147
 
142
148
  def check_connect_timeout(ssl_config)
143
- start_time = nil
149
+ timing = Timing.new
144
150
  assert_raises(TCPClient::ConnectTimeoutError) do
145
- start_time = Time.now
146
- TCPClient.new.connect("localhost:#{port}", ssl_config)
151
+ timing.start
152
+ TCPClient.new.connect(address, ssl_config)
147
153
  end
148
- assert_in_delta(ssl_config.connect_timeout, Time.now - start_time, 0.25)
154
+ assert_in_delta(ssl_config.connect_timeout, timing.elapsed, 0.25)
149
155
  end
150
156
 
151
157
  def test_connect_ssl_timeout
@@ -160,4 +166,19 @@ class TCPClientTest < MiniTest::Test
160
166
  ssl_config.connect_timeout = 1.5
161
167
  check_connect_timeout(ssl_config)
162
168
  end
169
+
170
+ def test_deadline
171
+ assert(TCPClient.with_deadline(0.15, address, config, &:itself).closed?)
172
+ end
173
+
174
+ def test_deadline_timeout
175
+ timing = Timing.new
176
+ assert_raises(TCPClient::ReadTimeoutError) do
177
+ timing.start
178
+ TCPClient.with_deadline(0.15, address, config) do |client|
179
+ client.read(42)
180
+ end
181
+ end
182
+ assert_in_delta(0.15, timing.elapsed, 0.15)
183
+ end
163
184
  end
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.4.1
4
+ version: 0.7.0
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-08-01 00:00:00.000000000 Z
11
+ date: 2021-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -65,6 +65,7 @@ executables: []
65
65
  extensions: []
66
66
  extra_rdoc_files:
67
67
  - README.md
68
+ - LICENSE
68
69
  files:
69
70
  - ".gitignore"
70
71
  - LICENSE
@@ -85,15 +86,16 @@ files:
85
86
  - sample/google.rb
86
87
  - sample/google_ssl.rb
87
88
  - tcp-client.gemspec
89
+ - test/helper.rb
88
90
  - test/tcp-client/address_test.rb
89
91
  - test/tcp-client/configuration_test.rb
90
92
  - test/tcp-client/deadline_test.rb
91
93
  - test/tcp-client/default_configuration_test.rb
92
94
  - test/tcp-client/version_test.rb
93
95
  - test/tcp_client_test.rb
94
- - test/test_helper.rb
95
96
  homepage: https://github.com/mblumtritt/tcp-client
96
- licenses: []
97
+ licenses:
98
+ - BSD-3-Clause
97
99
  metadata:
98
100
  source_code_uri: https://github.com/mblumtritt/tcp-client
99
101
  bug_tracker_uri: https://github.com/mblumtritt/tcp-client/issues
@@ -112,15 +114,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
114
  - !ruby/object:Gem::Version
113
115
  version: '0'
114
116
  requirements: []
115
- rubygems_version: 3.2.22
117
+ rubygems_version: 3.2.28
116
118
  signing_key:
117
119
  specification_version: 4
118
120
  summary: A TCP client implementation with working timeout support.
119
121
  test_files:
122
+ - test/helper.rb
120
123
  - test/tcp-client/address_test.rb
121
124
  - test/tcp-client/configuration_test.rb
122
125
  - test/tcp-client/deadline_test.rb
123
126
  - test/tcp-client/default_configuration_test.rb
124
127
  - test/tcp-client/version_test.rb
125
128
  - test/tcp_client_test.rb
126
- - test/test_helper.rb
data/test/test_helper.rb DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'minitest/autorun'
4
- require 'minitest/parallel'
5
- require_relative '../lib/tcp-client'
6
-
7
- # this pseudo-server never reads or writes anything
8
- PseudoServer = TCPServer.new('localhost', 0)
9
- Minitest.after_run { PseudoServer.close }