tcp-client 0.4.1 → 0.7.0

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: 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 }