tcp-client 0.5.0 → 0.8.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: 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