http 3.1.0 → 5.3.1
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 +5 -5
- data/.github/workflows/ci.yml +67 -0
- data/.gitignore +6 -9
- data/.rspec +0 -4
- data/.rubocop/layout.yml +8 -0
- data/.rubocop/metrics.yml +4 -0
- data/.rubocop/rspec.yml +9 -0
- data/.rubocop/style.yml +32 -0
- data/.rubocop.yml +9 -108
- data/.rubocop_todo.yml +219 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +67 -0
- data/{CHANGES.md → CHANGES_OLD.md} +358 -0
- data/Gemfile +19 -10
- data/LICENSE.txt +1 -1
- data/README.md +53 -85
- data/Rakefile +3 -11
- data/SECURITY.md +17 -0
- data/http.gemspec +15 -6
- data/lib/http/base64.rb +12 -0
- data/lib/http/chainable.rb +71 -41
- data/lib/http/client.rb +73 -52
- data/lib/http/connection.rb +28 -18
- data/lib/http/content_type.rb +12 -7
- data/lib/http/errors.rb +19 -0
- data/lib/http/feature.rb +18 -1
- data/lib/http/features/auto_deflate.rb +27 -6
- data/lib/http/features/auto_inflate.rb +32 -6
- data/lib/http/features/instrumentation.rb +69 -0
- data/lib/http/features/logging.rb +53 -0
- data/lib/http/features/normalize_uri.rb +17 -0
- data/lib/http/features/raise_error.rb +22 -0
- data/lib/http/headers/known.rb +3 -0
- data/lib/http/headers/normalizer.rb +69 -0
- data/lib/http/headers.rb +72 -49
- data/lib/http/mime_type/adapter.rb +3 -1
- data/lib/http/mime_type/json.rb +1 -0
- data/lib/http/options.rb +31 -28
- data/lib/http/redirector.rb +56 -4
- data/lib/http/request/body.rb +31 -0
- data/lib/http/request/writer.rb +29 -9
- data/lib/http/request.rb +76 -41
- data/lib/http/response/body.rb +6 -4
- data/lib/http/response/inflater.rb +1 -1
- data/lib/http/response/parser.rb +78 -26
- data/lib/http/response/status.rb +4 -3
- data/lib/http/response.rb +45 -27
- data/lib/http/retriable/client.rb +37 -0
- data/lib/http/retriable/delay_calculator.rb +64 -0
- data/lib/http/retriable/errors.rb +14 -0
- data/lib/http/retriable/performer.rb +153 -0
- data/lib/http/timeout/global.rb +29 -47
- data/lib/http/timeout/null.rb +12 -8
- data/lib/http/timeout/per_operation.rb +32 -57
- data/lib/http/uri.rb +75 -1
- data/lib/http/version.rb +1 -1
- data/lib/http.rb +2 -2
- data/spec/lib/http/client_spec.rb +189 -36
- data/spec/lib/http/connection_spec.rb +31 -6
- data/spec/lib/http/features/auto_inflate_spec.rb +40 -23
- data/spec/lib/http/features/instrumentation_spec.rb +81 -0
- data/spec/lib/http/features/logging_spec.rb +65 -0
- data/spec/lib/http/features/raise_error_spec.rb +62 -0
- data/spec/lib/http/headers/normalizer_spec.rb +52 -0
- data/spec/lib/http/headers_spec.rb +53 -18
- data/spec/lib/http/options/headers_spec.rb +6 -2
- data/spec/lib/http/options/merge_spec.rb +16 -16
- data/spec/lib/http/redirector_spec.rb +147 -3
- data/spec/lib/http/request/body_spec.rb +71 -4
- data/spec/lib/http/request/writer_spec.rb +45 -2
- data/spec/lib/http/request_spec.rb +11 -5
- data/spec/lib/http/response/body_spec.rb +5 -5
- data/spec/lib/http/response/parser_spec.rb +74 -0
- data/spec/lib/http/response/status_spec.rb +3 -3
- data/spec/lib/http/response_spec.rb +83 -7
- data/spec/lib/http/retriable/delay_calculator_spec.rb +69 -0
- data/spec/lib/http/retriable/performer_spec.rb +302 -0
- data/spec/lib/http/uri/normalizer_spec.rb +95 -0
- data/spec/lib/http/uri_spec.rb +39 -0
- data/spec/lib/http_spec.rb +121 -68
- data/spec/regression_specs.rb +7 -0
- data/spec/spec_helper.rb +22 -21
- data/spec/support/black_hole.rb +1 -1
- data/spec/support/dummy_server/servlet.rb +42 -11
- data/spec/support/dummy_server.rb +9 -8
- data/spec/support/fuubar.rb +21 -0
- data/spec/support/http_handling_shared.rb +62 -66
- data/spec/support/simplecov.rb +19 -0
- data/spec/support/ssl_helper.rb +4 -4
- metadata +66 -27
- data/.coveralls.yml +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -36
data/lib/http/timeout/global.rb
CHANGED
|
@@ -3,27 +3,25 @@
|
|
|
3
3
|
require "timeout"
|
|
4
4
|
require "io/wait"
|
|
5
5
|
|
|
6
|
-
require "http/timeout/
|
|
6
|
+
require "http/timeout/null"
|
|
7
7
|
|
|
8
8
|
module HTTP
|
|
9
9
|
module Timeout
|
|
10
|
-
class Global <
|
|
11
|
-
attr_reader :time_left, :total_timeout
|
|
12
|
-
|
|
10
|
+
class Global < Null
|
|
13
11
|
def initialize(*args)
|
|
14
12
|
super
|
|
15
|
-
|
|
13
|
+
|
|
14
|
+
@timeout = @time_left = options.fetch(:global_timeout)
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
# To future me: Don't remove this again, past you was smarter.
|
|
19
18
|
def reset_counter
|
|
20
|
-
@time_left =
|
|
21
|
-
@total_timeout = time_left
|
|
19
|
+
@time_left = @timeout
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
def connect(socket_class, host, port, nodelay = false)
|
|
25
23
|
reset_timer
|
|
26
|
-
::Timeout.timeout(time_left,
|
|
24
|
+
::Timeout.timeout(@time_left, ConnectTimeoutError) do
|
|
27
25
|
@socket = socket_class.open(host, port)
|
|
28
26
|
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
|
|
29
27
|
end
|
|
@@ -37,19 +35,17 @@ module HTTP
|
|
|
37
35
|
begin
|
|
38
36
|
@socket.connect_nonblock
|
|
39
37
|
rescue IO::WaitReadable
|
|
40
|
-
|
|
41
|
-
log_time
|
|
38
|
+
wait_readable_or_timeout
|
|
42
39
|
retry
|
|
43
40
|
rescue IO::WaitWritable
|
|
44
|
-
|
|
45
|
-
log_time
|
|
41
|
+
wait_writable_or_timeout
|
|
46
42
|
retry
|
|
47
43
|
end
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
# Read from the socket
|
|
51
|
-
def readpartial(size)
|
|
52
|
-
perform_io { read_nonblock(size) }
|
|
47
|
+
def readpartial(size, buffer = nil)
|
|
48
|
+
perform_io { read_nonblock(size, buffer) }
|
|
53
49
|
end
|
|
54
50
|
|
|
55
51
|
# Write to the socket
|
|
@@ -61,22 +57,12 @@ module HTTP
|
|
|
61
57
|
|
|
62
58
|
private
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def write_nonblock(data)
|
|
70
|
-
@socket.write_nonblock(data)
|
|
71
|
-
end
|
|
72
|
-
else
|
|
73
|
-
def read_nonblock(size)
|
|
74
|
-
@socket.read_nonblock(size, :exception => false)
|
|
75
|
-
end
|
|
60
|
+
def read_nonblock(size, buffer = nil)
|
|
61
|
+
@socket.read_nonblock(size, buffer, :exception => false)
|
|
62
|
+
end
|
|
76
63
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
end
|
|
64
|
+
def write_nonblock(data)
|
|
65
|
+
@socket.write_nonblock(data, :exception => false)
|
|
80
66
|
end
|
|
81
67
|
|
|
82
68
|
# Perform the given I/O operation with the given argument
|
|
@@ -84,20 +70,18 @@ module HTTP
|
|
|
84
70
|
reset_timer
|
|
85
71
|
|
|
86
72
|
loop do
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
else return result
|
|
95
|
-
end
|
|
96
|
-
rescue IO::WaitReadable
|
|
97
|
-
wait_readable_or_timeout
|
|
98
|
-
rescue IO::WaitWritable
|
|
99
|
-
wait_writable_or_timeout
|
|
73
|
+
result = yield
|
|
74
|
+
|
|
75
|
+
case result
|
|
76
|
+
when :wait_readable then wait_readable_or_timeout
|
|
77
|
+
when :wait_writable then wait_writable_or_timeout
|
|
78
|
+
when NilClass then return :eof
|
|
79
|
+
else return result
|
|
100
80
|
end
|
|
81
|
+
rescue IO::WaitReadable
|
|
82
|
+
wait_readable_or_timeout
|
|
83
|
+
rescue IO::WaitWritable
|
|
84
|
+
wait_writable_or_timeout
|
|
101
85
|
end
|
|
102
86
|
rescue EOFError
|
|
103
87
|
:eof
|
|
@@ -105,13 +89,13 @@ module HTTP
|
|
|
105
89
|
|
|
106
90
|
# Wait for a socket to become readable
|
|
107
91
|
def wait_readable_or_timeout
|
|
108
|
-
@socket.to_io.wait_readable(time_left)
|
|
92
|
+
@socket.to_io.wait_readable(@time_left)
|
|
109
93
|
log_time
|
|
110
94
|
end
|
|
111
95
|
|
|
112
96
|
# Wait for a socket to become writable
|
|
113
97
|
def wait_writable_or_timeout
|
|
114
|
-
@socket.to_io.wait_writable(time_left)
|
|
98
|
+
@socket.to_io.wait_writable(@time_left)
|
|
115
99
|
log_time
|
|
116
100
|
end
|
|
117
101
|
|
|
@@ -123,9 +107,7 @@ module HTTP
|
|
|
123
107
|
|
|
124
108
|
def log_time
|
|
125
109
|
@time_left -= (Time.now - @started)
|
|
126
|
-
if time_left <= 0
|
|
127
|
-
raise TimeoutError, "Timed out after using the allocated #{total_timeout} seconds"
|
|
128
|
-
end
|
|
110
|
+
raise TimeoutError, "Timed out after using the allocated #{@timeout} seconds" if @time_left <= 0
|
|
129
111
|
|
|
130
112
|
reset_timer
|
|
131
113
|
end
|
data/lib/http/timeout/null.rb
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "forwardable"
|
|
4
3
|
require "io/wait"
|
|
5
4
|
|
|
6
5
|
module HTTP
|
|
7
6
|
module Timeout
|
|
8
7
|
class Null
|
|
9
|
-
extend Forwardable
|
|
10
|
-
|
|
11
|
-
def_delegators :@socket, :close, :closed?
|
|
12
|
-
|
|
13
8
|
attr_reader :options, :socket
|
|
14
9
|
|
|
15
|
-
def initialize(options = {})
|
|
10
|
+
def initialize(options = {})
|
|
16
11
|
@options = options
|
|
17
12
|
end
|
|
18
13
|
|
|
@@ -27,6 +22,14 @@ module HTTP
|
|
|
27
22
|
@socket.connect
|
|
28
23
|
end
|
|
29
24
|
|
|
25
|
+
def close
|
|
26
|
+
@socket&.close
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def closed?
|
|
30
|
+
@socket&.closed?
|
|
31
|
+
end
|
|
32
|
+
|
|
30
33
|
# Configures the SSL connection and starts the connection
|
|
31
34
|
def start_tls(host, ssl_socket_class, ssl_context)
|
|
32
35
|
@socket = ssl_socket_class.new(socket, ssl_context)
|
|
@@ -36,13 +39,14 @@ module HTTP
|
|
|
36
39
|
connect_ssl
|
|
37
40
|
|
|
38
41
|
return unless ssl_context.verify_mode == OpenSSL::SSL::VERIFY_PEER
|
|
42
|
+
return if ssl_context.respond_to?(:verify_hostname) && !ssl_context.verify_hostname
|
|
39
43
|
|
|
40
44
|
@socket.post_connection_check(host)
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
# Read from the socket
|
|
44
|
-
def readpartial(size)
|
|
45
|
-
@socket.readpartial(size)
|
|
48
|
+
def readpartial(size, buffer = nil)
|
|
49
|
+
@socket.readpartial(size, buffer)
|
|
46
50
|
rescue EOFError
|
|
47
51
|
:eof
|
|
48
52
|
end
|
|
@@ -11,8 +11,6 @@ module HTTP
|
|
|
11
11
|
WRITE_TIMEOUT = 0.25
|
|
12
12
|
READ_TIMEOUT = 0.25
|
|
13
13
|
|
|
14
|
-
attr_reader :read_timeout, :write_timeout, :connect_timeout
|
|
15
|
-
|
|
16
14
|
def initialize(*args)
|
|
17
15
|
super
|
|
18
16
|
|
|
@@ -22,7 +20,7 @@ module HTTP
|
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
def connect(socket_class, host, port, nodelay = false)
|
|
25
|
-
::Timeout.timeout(connect_timeout,
|
|
23
|
+
::Timeout.timeout(@connect_timeout, ConnectTimeoutError) do
|
|
26
24
|
@socket = socket_class.open(host, port)
|
|
27
25
|
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
|
|
28
26
|
end
|
|
@@ -36,65 +34,42 @@ module HTTP
|
|
|
36
34
|
end
|
|
37
35
|
end
|
|
38
36
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
:eof
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# Read data from the socket
|
|
62
|
-
def readpartial(size)
|
|
63
|
-
timeout = false
|
|
64
|
-
loop do
|
|
65
|
-
result = @socket.read_nonblock(size, :exception => false)
|
|
66
|
-
|
|
67
|
-
return :eof if result.nil?
|
|
68
|
-
return result if result != :wait_readable
|
|
69
|
-
|
|
70
|
-
raise TimeoutError, "Read timed out after #{read_timeout} seconds" if timeout
|
|
71
|
-
# marking the socket for timeout. Why is this not being raised immediately?
|
|
72
|
-
# it seems there is some race-condition on the network level between calling
|
|
73
|
-
# #read_nonblock and #wait_readable, in which #read_nonblock signalizes waiting
|
|
74
|
-
# for reads, and when waiting for x seconds, it returns nil suddenly without completing
|
|
75
|
-
# the x seconds. In a normal case this would be a timeout on wait/read, but it can
|
|
76
|
-
# also mean that the socket has been closed by the server. Therefore we "mark" the
|
|
77
|
-
# socket for timeout and try to read more bytes. If it returns :eof, it's all good, no
|
|
78
|
-
# timeout. Else, the first timeout was a proper timeout.
|
|
79
|
-
# This hack has to be done because io/wait#wait_readable doesn't provide a value for when
|
|
80
|
-
# the socket is closed by the server, and HTTP::Parser doesn't provide the limit for the chunks.
|
|
81
|
-
timeout = true unless @socket.to_io.wait_readable(read_timeout)
|
|
82
|
-
end
|
|
37
|
+
# Read data from the socket
|
|
38
|
+
def readpartial(size, buffer = nil)
|
|
39
|
+
timeout = false
|
|
40
|
+
loop do
|
|
41
|
+
result = @socket.read_nonblock(size, buffer, :exception => false)
|
|
42
|
+
|
|
43
|
+
return :eof if result.nil?
|
|
44
|
+
return result if result != :wait_readable
|
|
45
|
+
|
|
46
|
+
raise TimeoutError, "Read timed out after #{@read_timeout} seconds" if timeout
|
|
47
|
+
|
|
48
|
+
# marking the socket for timeout. Why is this not being raised immediately?
|
|
49
|
+
# it seems there is some race-condition on the network level between calling
|
|
50
|
+
# #read_nonblock and #wait_readable, in which #read_nonblock signalizes waiting
|
|
51
|
+
# for reads, and when waiting for x seconds, it returns nil suddenly without completing
|
|
52
|
+
# the x seconds. In a normal case this would be a timeout on wait/read, but it can
|
|
53
|
+
# also mean that the socket has been closed by the server. Therefore we "mark" the
|
|
54
|
+
# socket for timeout and try to read more bytes. If it returns :eof, it's all good, no
|
|
55
|
+
# timeout. Else, the first timeout was a proper timeout.
|
|
56
|
+
# This hack has to be done because io/wait#wait_readable doesn't provide a value for when
|
|
57
|
+
# the socket is closed by the server, and HTTP::Parser doesn't provide the limit for the chunks.
|
|
58
|
+
timeout = true unless @socket.to_io.wait_readable(@read_timeout)
|
|
83
59
|
end
|
|
60
|
+
end
|
|
84
61
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
62
|
+
# Write data to the socket
|
|
63
|
+
def write(data)
|
|
64
|
+
timeout = false
|
|
65
|
+
loop do
|
|
66
|
+
result = @socket.write_nonblock(data, :exception => false)
|
|
67
|
+
return result unless result == :wait_writable
|
|
91
68
|
|
|
92
|
-
|
|
69
|
+
raise TimeoutError, "Write timed out after #{@write_timeout} seconds" if timeout
|
|
93
70
|
|
|
94
|
-
|
|
95
|
-
end
|
|
71
|
+
timeout = true unless @socket.to_io.wait_writable(@write_timeout)
|
|
96
72
|
end
|
|
97
|
-
|
|
98
73
|
end
|
|
99
74
|
end
|
|
100
75
|
end
|
data/lib/http/uri.rb
CHANGED
|
@@ -9,7 +9,6 @@ module HTTP
|
|
|
9
9
|
def_delegators :@uri, :scheme, :normalized_scheme, :scheme=
|
|
10
10
|
def_delegators :@uri, :user, :normalized_user, :user=
|
|
11
11
|
def_delegators :@uri, :password, :normalized_password, :password=
|
|
12
|
-
def_delegators :@uri, :host, :normalized_host, :host=
|
|
13
12
|
def_delegators :@uri, :authority, :normalized_authority, :authority=
|
|
14
13
|
def_delegators :@uri, :origin, :origin=
|
|
15
14
|
def_delegators :@uri, :normalized_port, :port=
|
|
@@ -20,12 +19,40 @@ module HTTP
|
|
|
20
19
|
def_delegators :@uri, :fragment, :normalized_fragment, :fragment=
|
|
21
20
|
def_delegators :@uri, :omit, :join, :normalize
|
|
22
21
|
|
|
22
|
+
# Host, either a domain name or IP address. If the host is an IPv6 address, it will be returned
|
|
23
|
+
# without brackets surrounding it.
|
|
24
|
+
#
|
|
25
|
+
# @return [String] The host of the URI
|
|
26
|
+
attr_reader :host
|
|
27
|
+
|
|
28
|
+
# Normalized host, either a domain name or IP address. If the host is an IPv6 address, it will
|
|
29
|
+
# be returned without brackets surrounding it.
|
|
30
|
+
#
|
|
31
|
+
# @return [String] The normalized host of the URI
|
|
32
|
+
attr_reader :normalized_host
|
|
33
|
+
|
|
23
34
|
# @private
|
|
24
35
|
HTTP_SCHEME = "http"
|
|
25
36
|
|
|
26
37
|
# @private
|
|
27
38
|
HTTPS_SCHEME = "https"
|
|
28
39
|
|
|
40
|
+
# @private
|
|
41
|
+
PERCENT_ENCODE = /[^\x21-\x7E]+/.freeze
|
|
42
|
+
|
|
43
|
+
# @private
|
|
44
|
+
NORMALIZER = lambda do |uri|
|
|
45
|
+
uri = HTTP::URI.parse uri
|
|
46
|
+
|
|
47
|
+
HTTP::URI.new(
|
|
48
|
+
:scheme => uri.normalized_scheme,
|
|
49
|
+
:authority => uri.normalized_authority,
|
|
50
|
+
:path => uri.path.empty? ? "/" : percent_encode(Addressable::URI.normalize_path(uri.path)),
|
|
51
|
+
:query => percent_encode(uri.query),
|
|
52
|
+
:fragment => uri.normalized_fragment
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
29
56
|
# Parse the given URI string, returning an HTTP::URI object
|
|
30
57
|
#
|
|
31
58
|
# @param [HTTP::URI, String, #to_str] uri to parse
|
|
@@ -47,6 +74,19 @@ module HTTP
|
|
|
47
74
|
Addressable::URI.form_encode(form_values, sort)
|
|
48
75
|
end
|
|
49
76
|
|
|
77
|
+
# Percent-encode all characters matching a regular expression.
|
|
78
|
+
#
|
|
79
|
+
# @param [String] string raw string
|
|
80
|
+
#
|
|
81
|
+
# @return [String] encoded value
|
|
82
|
+
#
|
|
83
|
+
# @private
|
|
84
|
+
def self.percent_encode(string)
|
|
85
|
+
string&.gsub(PERCENT_ENCODE) do |substr|
|
|
86
|
+
substr.encode(Encoding::UTF_8).bytes.map { |c| format("%%%02X", c) }.join
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
50
90
|
# Creates an HTTP::URI instance from the given options
|
|
51
91
|
#
|
|
52
92
|
# @param [Hash, Addressable::URI] options_or_uri
|
|
@@ -70,6 +110,9 @@ module HTTP
|
|
|
70
110
|
else
|
|
71
111
|
raise TypeError, "expected Hash for options, got #{options_or_uri.class}"
|
|
72
112
|
end
|
|
113
|
+
|
|
114
|
+
@host = process_ipv6_brackets(@uri.host)
|
|
115
|
+
@normalized_host = process_ipv6_brackets(@uri.normalized_host)
|
|
73
116
|
end
|
|
74
117
|
|
|
75
118
|
# Are these URI objects equal? Normalizes both URIs prior to comparison
|
|
@@ -97,6 +140,17 @@ module HTTP
|
|
|
97
140
|
@hash ||= to_s.hash * -1
|
|
98
141
|
end
|
|
99
142
|
|
|
143
|
+
# Sets the host component for the URI.
|
|
144
|
+
#
|
|
145
|
+
# @param [String, #to_str] new_host The new host component.
|
|
146
|
+
# @return [void]
|
|
147
|
+
def host=(new_host)
|
|
148
|
+
@uri.host = process_ipv6_brackets(new_host, :brackets => true)
|
|
149
|
+
|
|
150
|
+
@host = process_ipv6_brackets(@uri.host)
|
|
151
|
+
@normalized_host = process_ipv6_brackets(@uri.normalized_host)
|
|
152
|
+
end
|
|
153
|
+
|
|
100
154
|
# Port number, either as specified or the default if unspecified
|
|
101
155
|
#
|
|
102
156
|
# @return [Integer] port number
|
|
@@ -133,5 +187,25 @@ module HTTP
|
|
|
133
187
|
def inspect
|
|
134
188
|
format("#<%s:0x%014x URI:%s>", self.class.name, object_id << 1, to_s)
|
|
135
189
|
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
# Process a URI host, adding or removing surrounding brackets if the host is an IPv6 address.
|
|
194
|
+
#
|
|
195
|
+
# @param [Boolean] brackets When true, brackets will be added to IPv6 addresses if missing. When
|
|
196
|
+
# false, they will be removed if present.
|
|
197
|
+
#
|
|
198
|
+
# @return [String] Host with IPv6 address brackets added or removed
|
|
199
|
+
def process_ipv6_brackets(raw_host, brackets: false)
|
|
200
|
+
ip = IPAddr.new(raw_host)
|
|
201
|
+
|
|
202
|
+
if ip.ipv6?
|
|
203
|
+
brackets ? "[#{ip}]" : ip.to_s
|
|
204
|
+
else
|
|
205
|
+
raw_host
|
|
206
|
+
end
|
|
207
|
+
rescue IPAddr::Error
|
|
208
|
+
raw_host
|
|
209
|
+
end
|
|
136
210
|
end
|
|
137
211
|
end
|
data/lib/http/version.rb
CHANGED
data/lib/http.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "http/parser"
|
|
4
|
-
|
|
5
3
|
require "http/errors"
|
|
6
4
|
require "http/timeout/null"
|
|
7
5
|
require "http/timeout/per_operation"
|
|
8
6
|
require "http/timeout/global"
|
|
9
7
|
require "http/chainable"
|
|
10
8
|
require "http/client"
|
|
9
|
+
require "http/retriable/client"
|
|
11
10
|
require "http/connection"
|
|
12
11
|
require "http/options"
|
|
12
|
+
require "http/feature"
|
|
13
13
|
require "http/request"
|
|
14
14
|
require "http/request/writer"
|
|
15
15
|
require "http/response"
|