tcp-client 0.5.0 → 0.8.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: b3222937127a374a29b4f69d84a1e45d78ccc74bffee2ed05d849782385c5402
4
- data.tar.gz: 8a56cb5574794f4bbf0f5fcb1f91fbbdc033120eefc86604f881322ddfeedf44
3
+ metadata.gz: 14f787ad4e8e06910bfebf67755ae7142183df4c4fb090edce84aae7d497a2b6
4
+ data.tar.gz: 6349bea2eac6bac053c724e0c6a162b1fe99502e94cf1c6734854d97f194f37a
5
5
  SHA512:
6
- metadata.gz: a096f6f5d41be9f63e5018f014aced375ab5e4574a7ea165bd006be23a9246264fcf77269f6be4335ee6f930794b1ef3332e08ccd580d54601eef2468bdf282d
7
- data.tar.gz: ee8edc735c7de68b28688043c8bf6d2715f177941c4e14f4bb742a91c1fc8edf4d47d3c5273379d4f0ec029418506dc3e52f3bbacfbff6cc4466fe7d22bf40f5
6
+ metadata.gz: b53fafbf091a67a329832663c058b4e8243b36a779f8bc52cc7f37de556ad0aa016245e0a059489b972e6afaccfea7d26a63a735686d5f50d88036a2f4ecaca1
7
+ data.tar.gz: 4a6ff368e221073c5addfab51e31df1b094ee3f69e09ebe3f38d6d552ebc761b14c552a1a2cd90dcfd148b1ba34ebb1b2e3abfcea9622ded8413870f21a60285
data/README.md CHANGED
@@ -11,20 +11,22 @@ This Gem implements a TCP client with (optional) SSL support. It is an easy to u
11
11
  ```ruby
12
12
  require 'tcp-client'
13
13
 
14
- TCPClient.configure do |cfg|
15
- cfg.connect_timeout = 1 # limit connect time the server to 1 second
16
- cfg.ssl_params = { ssl_version: :TLSv1_2 } # use TLS 1.2
17
- end
18
-
19
- TCPClient.open('www.google.com:443') do |client|
20
- # next sequence should not last longer than 0.5 seconds
21
- client.with_deadline(0.5) do
22
- # simple HTTP get request
23
- pp client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
24
-
25
- # read "HTTP/1.1 " + 3 byte HTTP status code
26
- pp client.read(12)
27
- end
14
+ # create a configuration:
15
+ # - don't use internal buffering
16
+ # - use TLS 1.2 or TLS 1.3
17
+ cfg = TCPClient::Configuration.create(
18
+ buffered: false,
19
+ ssl_params: {min_version: :TLS1_2, max_version: :TLS1_3}
20
+ )
21
+
22
+ # request to Google.com:
23
+ # - limit all network interactions to 1.5 seconds
24
+ # - use the Configuration cfg
25
+ # - send a simple HTTP get request
26
+ # - read 12 byte: "HTTP/1.1 " + 3 byte HTTP status code
27
+ TCPClient.with_deadline(1.5, 'www.google.com:443', cfg) do |client|
28
+ client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n") # >= 40
29
+ client.read(12) # => "HTTP/1.1 200"
28
30
  end
29
31
  ```
30
32
 
@@ -41,13 +43,13 @@ gem 'tcp-client'
41
43
  and install it by running Bundler:
42
44
 
43
45
  ```bash
44
- $ bundle
46
+ bundle
45
47
  ```
46
48
 
47
49
  To install the gem globally use:
48
50
 
49
51
  ```bash
50
- $ gem install tcp-client
52
+ gem install tcp-client
51
53
  ```
52
54
 
53
55
  After that you need only a single line of code in your project to have all tools on board:
@@ -25,12 +25,16 @@ class TCPClient
25
25
  "#{@hostname}:#{@addrinfo.ip_port}"
26
26
  end
27
27
 
28
- def to_h
28
+ def to_hash
29
29
  { host: @hostname, port: @addrinfo.ip_port }
30
30
  end
31
31
 
32
+ def to_h(*args)
33
+ args.empty? ? to_hash : to_hash.slice(*args)
34
+ end
35
+
32
36
  def ==(other)
33
- to_h == other.to_h
37
+ to_hash == other.to_hash
34
38
  end
35
39
  alias eql? ==
36
40
 
@@ -13,6 +13,7 @@ class TCPClient
13
13
  attr_reader :buffered,
14
14
  :keep_alive,
15
15
  :reverse_lookup,
16
+ :normalize_network_errors,
16
17
  :connect_timeout,
17
18
  :read_timeout,
18
19
  :write_timeout,
@@ -27,6 +28,7 @@ class TCPClient
27
28
  @connect_timeout_error = ConnectTimeoutError
28
29
  @read_timeout_error = ReadTimeoutError
29
30
  @write_timeout_error = WriteTimeoutError
31
+ @normalize_network_errors = false
30
32
  options.each_pair { |attribute, value| set(attribute, value) }
31
33
  end
32
34
 
@@ -37,7 +39,7 @@ class TCPClient
37
39
 
38
40
  def initialize_copy(_org)
39
41
  super
40
- @ssl_params = @ssl_params.dup
42
+ @ssl_params = Hash[@ssl_params] if @ssl_params
41
43
  self
42
44
  end
43
45
 
@@ -47,8 +49,8 @@ class TCPClient
47
49
 
48
50
  def ssl=(value)
49
51
  @ssl_params =
50
- if Hash === value
51
- value.dup
52
+ if value.respond_to?(:to_hash)
53
+ Hash[value.to_hash]
52
54
  else
53
55
  value ? {} : nil
54
56
  end
@@ -66,6 +68,10 @@ class TCPClient
66
68
  @reverse_lookup = value ? true : false
67
69
  end
68
70
 
71
+ def normalize_network_errors=(value)
72
+ @normalize_network_errors = value ? true : false
73
+ end
74
+
69
75
  def timeout=(seconds)
70
76
  @connect_timeout = @write_timeout = @read_timeout = seconds(seconds)
71
77
  end
@@ -83,43 +89,47 @@ class TCPClient
83
89
  end
84
90
 
85
91
  def timeout_error=(exception)
86
- raise(NotAnException, exception) unless exception_class?(exception)
92
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
87
93
  @connect_timeout_error =
88
94
  @read_timeout_error = @write_timeout_error = exception
89
95
  end
90
96
 
91
97
  def connect_timeout_error=(exception)
92
- raise(NotAnException, exception) unless exception_class?(exception)
98
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
93
99
  @connect_timeout_error = exception
94
100
  end
95
101
 
96
102
  def read_timeout_error=(exception)
97
- raise(NotAnException, exception) unless exception_class?(exception)
103
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
98
104
  @read_timeout_error = exception
99
105
  end
100
106
 
101
107
  def write_timeout_error=(exception)
102
- raise(NotAnException, exception) unless exception_class?(exception)
108
+ raise(NotAnExceptionError, exception) unless exception_class?(exception)
103
109
  @write_timeout_error = exception
104
110
  end
105
111
 
106
- def to_h
112
+ def to_hash
107
113
  {
108
114
  buffered: @buffered,
109
115
  keep_alive: @keep_alive,
110
116
  reverse_lookup: @reverse_lookup,
111
117
  connect_timeout: @connect_timeout,
112
- read_timeout: @read_timeout,
113
- write_timeout: @write_timeout,
114
118
  connect_timeout_error: @connect_timeout_error,
119
+ read_timeout: @read_timeout,
115
120
  read_timeout_error: @read_timeout_error,
121
+ write_timeout: @write_timeout,
116
122
  write_timeout_error: @write_timeout_error,
117
123
  ssl_params: @ssl_params
118
124
  }
119
125
  end
120
126
 
127
+ def to_h(*args)
128
+ args.empty? ? to_hash : to_hash.slice(*args)
129
+ end
130
+
121
131
  def ==(other)
122
- to_h == other.to_h
132
+ to_hash == other.to_hash
123
133
  end
124
134
  alias eql? ==
125
135
 
@@ -136,7 +146,7 @@ class TCPClient
136
146
  def set(attribute, value)
137
147
  public_send("#{attribute}=", value)
138
148
  rescue NoMethodError
139
- raise(UnknownAttribute, attribute)
149
+ raise(UnknownAttributeError, attribute)
140
150
  end
141
151
 
142
152
  def seconds(value)
@@ -29,4 +29,6 @@ class TCPClient
29
29
  end
30
30
  end
31
31
  end
32
+
33
+ private_constant(:Deadline)
32
34
  end
@@ -1,43 +1,45 @@
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
+ NetworkError = Class.new(StandardError)
35
+
36
+ class NotConnectedError < NetworkError
35
37
  def initialize
36
38
  super('client not connected')
37
39
  end
38
40
  end
39
41
 
40
- class TimeoutError < IOError
42
+ class TimeoutError < NetworkError
41
43
  def initialize(message = nil)
42
44
  super(message || "unable to #{action} in time")
43
45
  end
@@ -65,6 +67,18 @@ class TCPClient
65
67
  end
66
68
  end
67
69
 
68
- Timeout = TimeoutError # backward compatibility
69
- deprecate_constant(:Timeout)
70
+ NoOpenSSL = NoOpenSSLError
71
+ NoBlockGiven = NoBlockGivenError
72
+ InvalidDeadLine = InvalidDeadLineError
73
+ UnknownAttribute = UnknownAttributeError
74
+ NotAnException = NotAnExceptionError
75
+ NotConnected = NotConnectedError
76
+ deprecate_constant(
77
+ :NoOpenSSL,
78
+ :NoBlockGiven,
79
+ :InvalidDeadLine,
80
+ :UnknownAttribute,
81
+ :NotAnException,
82
+ :NotConnected
83
+ )
70
84
  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
@@ -18,6 +18,7 @@ class TCPClient
18
18
  super(socket, create_context(ssl_params))
19
19
  self.sync_close = true
20
20
  self.hostname = address.hostname
21
+ check_new_session if @new_session
21
22
  deadline.valid? ? connect_with_deadline(deadline, exception) : connect
22
23
  post_connection_check(address.hostname) if should_verify?(ssl_params)
23
24
  end
@@ -25,7 +26,19 @@ class TCPClient
25
26
  private
26
27
 
27
28
  def create_context(ssl_params)
28
- OpenSSL::SSL::SSLContext.new.tap { |ctx| ctx.set_params(ssl_params) }
29
+ @new_session = nil
30
+ ::OpenSSL::SSL::SSLContext.new.tap do |ctx|
31
+ ctx.set_params(ssl_params)
32
+ ctx.session_cache_mode = CONTEXT_CACHE_MODE
33
+ ctx.session_new_cb = proc { |_, sess| @new_session = sess }
34
+ end
35
+ end
36
+
37
+ def check_new_session
38
+ time = @new_session.time.to_f + @new_session.timeout
39
+ if Process.clock_gettime(Process::CLOCK_REALTIME) < time
40
+ self.session = @new_session
41
+ end
29
42
  end
30
43
 
31
44
  def connect_with_deadline(deadline, exception)
@@ -33,8 +46,13 @@ class TCPClient
33
46
  end
34
47
 
35
48
  def should_verify?(ssl_params)
36
- ssl_params[:verify_mode] != OpenSSL::SSL::VERIFY_NONE
49
+ ssl_params[:verify_mode] != ::OpenSSL::SSL::VERIFY_NONE &&
50
+ context.verify_hostname
37
51
  end
52
+
53
+ CONTEXT_CACHE_MODE =
54
+ ::OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
55
+ ::OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
38
56
  end
39
57
 
40
58
  private_constant(:SSLSocket)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TCPClient
4
- VERSION = '0.5.0'
4
+ VERSION = '0.8.0'
5
5
  end
data/lib/tcp-client.rb CHANGED
@@ -10,40 +10,45 @@ require_relative 'tcp-client/default_configuration'
10
10
  require_relative 'tcp-client/version'
11
11
 
12
12
  class TCPClient
13
- def self.open(address, configuration = Configuration.default)
13
+ def self.open(address, configuration = nil)
14
14
  client = new
15
15
  client.connect(Address.new(address), configuration)
16
- return client unless block_given?
17
- begin
18
- yield(client)
19
- ensure
20
- client.close
16
+ block_given? ? yield(client) : client
17
+ ensure
18
+ client.close if block_given?
19
+ end
20
+
21
+ def self.with_deadline(timeout, address, configuration = nil)
22
+ client = nil
23
+ raise(NoBlockGivenError) unless block_given?
24
+ address = Address.new(address)
25
+ client = new
26
+ client.with_deadline(timeout) do
27
+ yield(client.connect(address, configuration))
21
28
  end
29
+ ensure
30
+ client&.close
22
31
  end
23
32
 
24
33
  attr_reader :address, :configuration
25
34
 
26
- def initialize
27
- @socket = @address = @deadline = @configuration = nil
28
- end
29
-
30
35
  def to_s
31
36
  @address&.to_s || ''
32
37
  end
33
38
 
34
- def connect(address, configuration, exception: nil)
35
- raise(NoOpenSSL) if configuration.ssl? && !defined?(SSLSocket)
36
- close
39
+ def connect(address, configuration = nil, timeout: nil, exception: nil)
40
+ close if @socket
41
+ raise(NoOpenSSLError) if configuration.ssl? && !defined?(SSLSocket)
37
42
  @address = Address.new(address)
38
- @configuration = configuration.dup.freeze
39
- @socket = create_socket(exception)
43
+ @configuration = (configuration || Configuration.default).dup
44
+ @socket = create_socket(timeout, exception)
40
45
  self
41
46
  end
42
47
 
43
48
  def close
44
49
  @socket&.close
45
50
  self
46
- rescue IOError
51
+ rescue *NETWORK_ERRORS
47
52
  self
48
53
  ensure
49
54
  @socket = @deadline = nil
@@ -55,66 +60,77 @@ class TCPClient
55
60
 
56
61
  def with_deadline(timeout)
57
62
  previous_deadline = @deadline
58
- raise(NoBlockGiven) unless block_given?
63
+ raise(NoBlockGivenError) unless block_given?
59
64
  @deadline = Deadline.new(timeout)
60
- raise(InvalidDeadLine, timeout) unless @deadline.valid?
65
+ raise(InvalidDeadLineError, timeout) unless @deadline.valid?
61
66
  yield(self)
62
67
  ensure
63
68
  @deadline = previous_deadline
64
69
  end
65
70
 
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 || @configuration.read_timeout)
71
- return @socket.read(nbytes) unless deadline.valid?
72
- read_with_deadline(nbytes, deadline, exception)
71
+ def read(nbytes = nil, timeout: nil, exception: nil)
72
+ raise(NotConnectedError) if closed?
73
+ deadline = create_deadline(timeout, configuration.read_timeout)
74
+ return stem_errors { @socket.read(nbytes) } unless deadline.valid?
75
+ exception ||= configuration.read_timeout_error
76
+ stem_errors(exception) do
77
+ @socket.read_with_deadline(nbytes, deadline, exception)
78
+ end
73
79
  end
74
80
 
75
81
  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 || @configuration.read_timeout)
80
- return @socket.write(*msg) unless deadline.valid?
81
- write_with_deadline(msg, deadline, exception)
82
+ raise(NotConnectedError) if closed?
83
+ deadline = create_deadline(timeout, configuration.write_timeout)
84
+ return stem_errors { @socket.write(*msg) } unless deadline.valid?
85
+ exception ||= configuration.write_timeout_error
86
+ stem_errors(exception) do
87
+ msg.sum do |chunk|
88
+ @socket.write_with_deadline(chunk.b, deadline, exception)
89
+ end
90
+ end
82
91
  end
83
92
 
84
93
  def flush
85
- @socket.flush unless closed?
94
+ stem_errors { @socket&.flush }
86
95
  self
87
96
  end
88
97
 
89
98
  private
90
99
 
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
- @configuration.ssl? ? as_ssl_socket(socket, deadline, exception) : socket
100
+ def create_deadline(timeout, default)
101
+ timeout.nil? && @deadline ? @deadline : Deadline.new(timeout || default)
96
102
  end
97
103
 
98
- def as_ssl_socket(socket, deadline, exception)
99
- SSLSocket.new(socket, @address, @configuration, deadline, exception)
100
- rescue StandardError => e
101
- begin
102
- socket.close
103
- rescue IOError
104
- # ignore!
104
+ def create_socket(timeout, exception)
105
+ deadline = create_deadline(timeout, configuration.connect_timeout)
106
+ exception ||= configuration.connect_timeout_error
107
+ stem_errors(exception) do
108
+ @socket = TCPSocket.new(address, configuration, deadline, exception)
109
+ return @socket unless configuration.ssl?
110
+ SSLSocket.new(@socket, address, configuration, deadline, exception)
105
111
  end
106
- raise(e, cause: e.cause)
107
112
  end
108
113
 
109
- def read_with_deadline(nbytes, deadline, exception)
110
- exception ||= @configuration.read_timeout_error
111
- @socket.read_with_deadline(nbytes, deadline, exception)
114
+ def stem_errors(except = nil)
115
+ yield
116
+ rescue *NETWORK_ERRORS => e
117
+ raise unless configuration.normalize_network_errors
118
+ (except && e.is_a?(except)) ? raise : raise(NetworkError, e)
112
119
  end
113
120
 
114
- def write_with_deadline(msg, deadline, exception)
115
- exception ||= @configuration.write_timeout_error
116
- msg.sum do |chunk|
117
- @socket.write_with_deadline(chunk.b, deadline, exception)
118
- end
119
- end
121
+ NETWORK_ERRORS =
122
+ [
123
+ Errno::EADDRNOTAVAIL,
124
+ Errno::ECONNABORTED,
125
+ Errno::ECONNREFUSED,
126
+ Errno::ECONNRESET,
127
+ Errno::EHOSTUNREACH,
128
+ Errno::EINVAL,
129
+ Errno::ENETUNREACH,
130
+ Errno::EPIPE,
131
+ IOError,
132
+ SocketError
133
+ ].tap do |errors|
134
+ errors << ::OpenSSL::SSL::SSLError if defined?(::OpenSSL::SSL::SSLError)
135
+ end.freeze
120
136
  end
data/rakefile.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rake/clean'
4
- require 'rake/testtask'
5
4
  require 'bundler/gem_tasks'
5
+ require 'rspec/core/rake_task'
6
6
 
7
7
  $stdout.sync = $stderr.sync = true
8
8
 
@@ -10,7 +10,4 @@ CLOBBER << 'prj'
10
10
 
11
11
  task(:default) { exec('rake --tasks') }
12
12
 
13
- Rake::TestTask.new(:test) do |task|
14
- task.pattern = 'test/**/*_test.rb'
15
- task.warning = task.verbose = true
16
- end
13
+ RSpec::Core::RakeTask.new { |task| task.ruby_opts = %w[-w] }
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,24 @@
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
+ # - don't use internal buffering
7
+ # - use TLS 1.2 or TLS 1.3
8
+ cfg =
9
+ TCPClient::Configuration.create(
10
+ buffered: false,
11
+ ssl_params: {
12
+ min_version: :TLS1_2,
13
+ max_version: :TLS1_3
14
+ }
15
+ )
15
16
 
16
- # read "HTTP/1.1 " + 3 byte HTTP status code
17
- pp client.read(12)
18
- end
17
+ # request to Google.com:
18
+ # - limit all network interactions to 1.5 seconds
19
+ # - use the Configuration cfg
20
+ # - send a simple HTTP get request
21
+ # - read 12 byte: "HTTP/1.1 " + 3 byte HTTP status code
22
+ TCPClient.with_deadline(1.5, 'www.google.com:443', cfg) do |client|
23
+ p client.write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n")
24
+ p client.read(12)
19
25
  end
data/spec/helper.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core'
4
+ require_relative '../lib/tcp-client'
5
+
6
+ $stdout.sync = $stderr.sync = true
7
+
8
+ RSpec.configure do |config|
9
+ config.disable_monkey_patching!
10
+ config.warnings = true
11
+ config.order = :random
12
+ end