http 5.3.1 → 6.0.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 +4 -4
- data/CHANGELOG.md +241 -41
- data/LICENSE.txt +1 -1
- data/README.md +110 -13
- data/UPGRADING.md +491 -0
- data/http.gemspec +32 -29
- data/lib/http/base64.rb +11 -1
- data/lib/http/chainable/helpers.rb +62 -0
- data/lib/http/chainable/verbs.rb +136 -0
- data/lib/http/chainable.rb +232 -136
- data/lib/http/client.rb +158 -127
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +126 -97
- data/lib/http/content_type.rb +61 -6
- data/lib/http/errors.rb +25 -1
- data/lib/http/feature.rb +65 -5
- data/lib/http/features/auto_deflate.rb +124 -17
- data/lib/http/features/auto_inflate.rb +38 -15
- data/lib/http/features/caching/entry.rb +178 -0
- data/lib/http/features/caching/in_memory_store.rb +63 -0
- data/lib/http/features/caching.rb +216 -0
- data/lib/http/features/digest_auth.rb +234 -0
- data/lib/http/features/instrumentation.rb +97 -17
- data/lib/http/features/logging.rb +183 -5
- data/lib/http/features/normalize_uri.rb +17 -0
- data/lib/http/features/raise_error.rb +18 -3
- data/lib/http/form_data/composite_io.rb +106 -0
- data/lib/http/form_data/file.rb +95 -0
- data/lib/http/form_data/multipart/param.rb +62 -0
- data/lib/http/form_data/multipart.rb +106 -0
- data/lib/http/form_data/part.rb +52 -0
- data/lib/http/form_data/readable.rb +58 -0
- data/lib/http/form_data/urlencoded.rb +175 -0
- data/lib/http/form_data/version.rb +8 -0
- data/lib/http/form_data.rb +102 -0
- data/lib/http/headers/known.rb +3 -0
- data/lib/http/headers/normalizer.rb +17 -36
- data/lib/http/headers.rb +172 -65
- data/lib/http/mime_type/adapter.rb +24 -9
- data/lib/http/mime_type/json.rb +19 -4
- data/lib/http/mime_type.rb +21 -3
- data/lib/http/options/definitions.rb +189 -0
- data/lib/http/options.rb +172 -125
- data/lib/http/redirector.rb +80 -75
- data/lib/http/request/body.rb +87 -6
- data/lib/http/request/builder.rb +184 -0
- data/lib/http/request/proxy.rb +83 -0
- data/lib/http/request/writer.rb +76 -16
- data/lib/http/request.rb +214 -98
- data/lib/http/response/body.rb +103 -18
- data/lib/http/response/inflater.rb +35 -7
- data/lib/http/response/parser.rb +98 -4
- data/lib/http/response/status/reasons.rb +2 -4
- data/lib/http/response/status.rb +141 -31
- data/lib/http/response.rb +219 -61
- data/lib/http/retriable/delay_calculator.rb +38 -11
- data/lib/http/retriable/errors.rb +21 -0
- data/lib/http/retriable/performer.rb +82 -38
- data/lib/http/session.rb +280 -0
- data/lib/http/timeout/global.rb +147 -34
- data/lib/http/timeout/null.rb +155 -9
- data/lib/http/timeout/per_operation.rb +139 -18
- data/lib/http/uri/normalizer.rb +82 -0
- data/lib/http/uri/parsing.rb +182 -0
- data/lib/http/uri.rb +289 -124
- data/lib/http/version.rb +2 -1
- data/lib/http.rb +11 -2
- data/sig/deps.rbs +122 -0
- data/sig/http.rbs +1619 -0
- data/test/http/base64_test.rb +28 -0
- data/test/http/client_test.rb +739 -0
- data/test/http/connection_test.rb +1533 -0
- data/test/http/content_type_test.rb +190 -0
- data/test/http/errors_test.rb +28 -0
- data/test/http/feature_test.rb +49 -0
- data/test/http/features/auto_deflate_test.rb +317 -0
- data/test/http/features/auto_inflate_test.rb +213 -0
- data/test/http/features/caching_test.rb +942 -0
- data/test/http/features/digest_auth_test.rb +996 -0
- data/test/http/features/instrumentation_test.rb +246 -0
- data/test/http/features/logging_test.rb +654 -0
- data/test/http/features/normalize_uri_test.rb +41 -0
- data/test/http/features/raise_error_test.rb +77 -0
- data/test/http/form_data/composite_io_test.rb +215 -0
- data/test/http/form_data/file_test.rb +255 -0
- data/test/http/form_data/fixtures/the-http-gem.info +1 -0
- data/test/http/form_data/multipart_test.rb +303 -0
- data/test/http/form_data/part_test.rb +90 -0
- data/test/http/form_data/urlencoded_test.rb +164 -0
- data/test/http/form_data_test.rb +232 -0
- data/test/http/headers/normalizer_test.rb +93 -0
- data/test/http/headers_test.rb +888 -0
- data/test/http/mime_type/json_test.rb +39 -0
- data/test/http/mime_type_test.rb +150 -0
- data/test/http/options/base_uri_test.rb +148 -0
- data/test/http/options/body_test.rb +21 -0
- data/test/http/options/features_test.rb +38 -0
- data/test/http/options/form_test.rb +21 -0
- data/test/http/options/headers_test.rb +32 -0
- data/test/http/options/json_test.rb +21 -0
- data/test/http/options/merge_test.rb +78 -0
- data/test/http/options/new_test.rb +37 -0
- data/test/http/options/proxy_test.rb +32 -0
- data/test/http/options_test.rb +575 -0
- data/test/http/redirector_test.rb +639 -0
- data/test/http/request/body_test.rb +318 -0
- data/test/http/request/builder_test.rb +623 -0
- data/test/http/request/writer_test.rb +391 -0
- data/test/http/request_test.rb +1733 -0
- data/test/http/response/body_test.rb +292 -0
- data/test/http/response/parser_test.rb +105 -0
- data/test/http/response/status_test.rb +322 -0
- data/test/http/response_test.rb +502 -0
- data/test/http/retriable/delay_calculator_test.rb +194 -0
- data/test/http/retriable/errors_test.rb +71 -0
- data/test/http/retriable/performer_test.rb +551 -0
- data/test/http/session_test.rb +424 -0
- data/test/http/timeout/global_test.rb +239 -0
- data/test/http/timeout/null_test.rb +218 -0
- data/test/http/timeout/per_operation_test.rb +220 -0
- data/test/http/uri/normalizer_test.rb +89 -0
- data/test/http/uri_test.rb +1140 -0
- data/test/http/version_test.rb +15 -0
- data/test/http_test.rb +818 -0
- data/test/regression_tests.rb +27 -0
- data/test/support/dummy_server/encoding_routes.rb +47 -0
- data/test/support/dummy_server/routes.rb +201 -0
- data/test/support/dummy_server/servlet.rb +81 -0
- data/test/support/dummy_server.rb +200 -0
- data/{spec → test}/support/fakeio.rb +2 -2
- data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
- data/test/support/http_handling_shared/timeout_tests.rb +134 -0
- data/test/support/http_handling_shared.rb +11 -0
- data/test/support/proxy_server.rb +207 -0
- data/test/support/servers/runner.rb +67 -0
- data/{spec → test}/support/simplecov.rb +11 -2
- data/test/support/ssl_helper.rb +108 -0
- data/test/test_helper.rb +38 -0
- metadata +108 -168
- data/.github/workflows/ci.yml +0 -67
- data/.gitignore +0 -15
- data/.rspec +0 -1
- data/.rubocop/layout.yml +0 -8
- data/.rubocop/metrics.yml +0 -4
- data/.rubocop/rspec.yml +0 -9
- data/.rubocop/style.yml +0 -32
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -219
- data/.yardopts +0 -2
- data/CHANGES_OLD.md +0 -1002
- data/Gemfile +0 -51
- data/Guardfile +0 -18
- data/Rakefile +0 -64
- data/lib/http/headers/mixin.rb +0 -34
- data/lib/http/retriable/client.rb +0 -37
- data/logo.png +0 -0
- data/spec/lib/http/client_spec.rb +0 -556
- data/spec/lib/http/connection_spec.rb +0 -88
- data/spec/lib/http/content_type_spec.rb +0 -47
- data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
- data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
- data/spec/lib/http/features/instrumentation_spec.rb +0 -81
- data/spec/lib/http/features/logging_spec.rb +0 -65
- data/spec/lib/http/features/raise_error_spec.rb +0 -62
- data/spec/lib/http/headers/mixin_spec.rb +0 -36
- data/spec/lib/http/headers/normalizer_spec.rb +0 -52
- data/spec/lib/http/headers_spec.rb +0 -527
- data/spec/lib/http/options/body_spec.rb +0 -15
- data/spec/lib/http/options/features_spec.rb +0 -33
- data/spec/lib/http/options/form_spec.rb +0 -15
- data/spec/lib/http/options/headers_spec.rb +0 -24
- data/spec/lib/http/options/json_spec.rb +0 -15
- data/spec/lib/http/options/merge_spec.rb +0 -68
- data/spec/lib/http/options/new_spec.rb +0 -30
- data/spec/lib/http/options/proxy_spec.rb +0 -20
- data/spec/lib/http/options_spec.rb +0 -13
- data/spec/lib/http/redirector_spec.rb +0 -530
- data/spec/lib/http/request/body_spec.rb +0 -211
- data/spec/lib/http/request/writer_spec.rb +0 -121
- data/spec/lib/http/request_spec.rb +0 -234
- data/spec/lib/http/response/body_spec.rb +0 -85
- data/spec/lib/http/response/parser_spec.rb +0 -74
- data/spec/lib/http/response/status_spec.rb +0 -253
- data/spec/lib/http/response_spec.rb +0 -262
- data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
- data/spec/lib/http/retriable/performer_spec.rb +0 -302
- data/spec/lib/http/uri/normalizer_spec.rb +0 -95
- data/spec/lib/http/uri_spec.rb +0 -71
- data/spec/lib/http_spec.rb +0 -535
- data/spec/regression_specs.rb +0 -24
- data/spec/spec_helper.rb +0 -89
- data/spec/support/black_hole.rb +0 -13
- data/spec/support/dummy_server/servlet.rb +0 -203
- data/spec/support/dummy_server.rb +0 -44
- data/spec/support/fuubar.rb +0 -21
- data/spec/support/http_handling_shared.rb +0 -190
- data/spec/support/proxy_server.rb +0 -39
- data/spec/support/servers/config.rb +0 -11
- data/spec/support/servers/runner.rb +0 -19
- data/spec/support/ssl_helper.rb +0 -104
- /data/{spec → test}/support/capture_warning.rb +0 -0
data/lib/http/uri.rb
CHANGED
|
@@ -1,211 +1,376 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "uri"
|
|
4
4
|
|
|
5
5
|
module HTTP
|
|
6
|
+
# HTTP URI with scheme, authority, path, query, and fragment components
|
|
7
|
+
#
|
|
8
|
+
# Stores URI components as instance variables. Addressable is only used
|
|
9
|
+
# when parsing non-ASCII (IRI) strings; ASCII URIs use stdlib's URI.parse.
|
|
6
10
|
class URI
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# @return [String] The
|
|
11
|
+
# The URI given was not valid
|
|
12
|
+
class InvalidError < HTTP::RequestError; end
|
|
13
|
+
|
|
14
|
+
# URI scheme (e.g. "http", "https")
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# uri.scheme # => "http"
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
# @return [String, nil] The URI scheme
|
|
21
|
+
attr_reader :scheme
|
|
22
|
+
|
|
23
|
+
# User component for authentication
|
|
24
|
+
#
|
|
25
|
+
# @example
|
|
26
|
+
# uri.user # => "admin"
|
|
27
|
+
#
|
|
28
|
+
# @api public
|
|
29
|
+
# @return [String, nil] The user component
|
|
30
|
+
attr_reader :user
|
|
31
|
+
|
|
32
|
+
# Password component for authentication
|
|
33
|
+
#
|
|
34
|
+
# @example
|
|
35
|
+
# uri.password # => "secret"
|
|
36
|
+
#
|
|
37
|
+
# @api public
|
|
38
|
+
# @return [String, nil] The password component
|
|
39
|
+
attr_reader :password
|
|
40
|
+
|
|
41
|
+
# Host, either a domain name or IP address
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# uri.host # => "example.com"
|
|
45
|
+
#
|
|
46
|
+
# @api public
|
|
47
|
+
# @return [String, nil] The host of the URI
|
|
26
48
|
attr_reader :host
|
|
27
49
|
|
|
28
|
-
# Normalized host
|
|
29
|
-
#
|
|
50
|
+
# Normalized host
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# uri.normalized_host # => "example.com"
|
|
30
54
|
#
|
|
31
|
-
# @
|
|
55
|
+
# @api public
|
|
56
|
+
# @return [String, nil] The normalized host of the URI
|
|
32
57
|
attr_reader :normalized_host
|
|
33
58
|
|
|
59
|
+
# URI path component
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
# uri.path # => "/foo"
|
|
63
|
+
#
|
|
64
|
+
# @api public
|
|
65
|
+
# @return [String] The path component
|
|
66
|
+
attr_accessor :path
|
|
67
|
+
|
|
68
|
+
# URI query string
|
|
69
|
+
#
|
|
70
|
+
# @example
|
|
71
|
+
# uri.query # => "q=1"
|
|
72
|
+
#
|
|
73
|
+
# @api public
|
|
74
|
+
# @return [String, nil] The query component
|
|
75
|
+
attr_accessor :query
|
|
76
|
+
|
|
77
|
+
# URI fragment
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# uri.fragment # => "section1"
|
|
81
|
+
#
|
|
82
|
+
# @api public
|
|
83
|
+
# @return [String, nil] The fragment component
|
|
84
|
+
attr_reader :fragment
|
|
85
|
+
|
|
86
|
+
# HTTP scheme string
|
|
34
87
|
# @private
|
|
35
88
|
HTTP_SCHEME = "http"
|
|
36
89
|
|
|
90
|
+
# HTTPS scheme string
|
|
37
91
|
# @private
|
|
38
92
|
HTTPS_SCHEME = "https"
|
|
39
93
|
|
|
94
|
+
# Pattern matching characters requiring percent-encoding
|
|
40
95
|
# @private
|
|
41
|
-
PERCENT_ENCODE = /[^\x21-\x7E]
|
|
96
|
+
PERCENT_ENCODE = /[^\x21-\x7E]+/
|
|
42
97
|
|
|
98
|
+
# Default ports for supported URI schemes
|
|
43
99
|
# @private
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
100
|
+
DEFAULT_PORTS = {
|
|
101
|
+
"http" => 80,
|
|
102
|
+
"https" => 443,
|
|
103
|
+
"ws" => 80,
|
|
104
|
+
"wss" => 443
|
|
105
|
+
}.freeze
|
|
55
106
|
|
|
56
|
-
#
|
|
107
|
+
# Pattern for characters that stdlib's URI.parse silently modifies
|
|
108
|
+
# @private
|
|
109
|
+
NEEDS_ADDRESSABLE = /[^\x20-\x7E]/
|
|
110
|
+
|
|
111
|
+
# Creates an HTTP::URI instance from the given keyword arguments
|
|
112
|
+
#
|
|
113
|
+
# @example
|
|
114
|
+
# HTTP::URI.new(scheme: "http", host: "example.com")
|
|
57
115
|
#
|
|
58
|
-
# @param [
|
|
116
|
+
# @param [String, nil] scheme URI scheme
|
|
117
|
+
# @param [String, nil] user for basic authentication
|
|
118
|
+
# @param [String, nil] password for basic authentication
|
|
119
|
+
# @param [String, nil] host name component (IPv6 addresses must be bracketed)
|
|
120
|
+
# @param [Integer, nil] port network port to connect to
|
|
121
|
+
# @param [String, nil] path component to request
|
|
122
|
+
# @param [String, nil] query component distinct from path
|
|
123
|
+
# @param [String, nil] fragment component at the end of the URI
|
|
59
124
|
#
|
|
125
|
+
# @api public
|
|
60
126
|
# @return [HTTP::URI] new URI instance
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
127
|
+
def initialize(scheme: nil, user: nil, password: nil, host: nil,
|
|
128
|
+
port: nil, path: nil, query: nil, fragment: nil)
|
|
129
|
+
@scheme = scheme
|
|
130
|
+
@user = user
|
|
131
|
+
@password = password
|
|
132
|
+
@raw_host = host
|
|
133
|
+
@host = process_ipv6_brackets(host)
|
|
134
|
+
@normalized_host = normalize_host(@host)
|
|
135
|
+
@port = port
|
|
136
|
+
@path = path || ""
|
|
137
|
+
@query = query
|
|
138
|
+
@fragment = fragment
|
|
65
139
|
end
|
|
66
140
|
|
|
67
|
-
#
|
|
141
|
+
# Are these URI objects equal after normalization
|
|
142
|
+
#
|
|
143
|
+
# @example
|
|
144
|
+
# HTTP::URI.parse("http://example.com") == HTTP::URI.parse("http://example.com")
|
|
68
145
|
#
|
|
69
|
-
# @param [
|
|
70
|
-
# @param [TrueClass, FalseClass] sort should key/value pairs be sorted first?
|
|
146
|
+
# @param [Object] other URI to compare this one with
|
|
71
147
|
#
|
|
72
|
-
# @
|
|
73
|
-
|
|
74
|
-
|
|
148
|
+
# @api public
|
|
149
|
+
# @return [TrueClass, FalseClass] are the URIs equivalent (after normalization)?
|
|
150
|
+
def ==(other)
|
|
151
|
+
other.is_a?(URI) && String(normalize).eql?(String(other.normalize))
|
|
75
152
|
end
|
|
76
153
|
|
|
77
|
-
#
|
|
154
|
+
# Are these URI objects equal without normalization
|
|
78
155
|
#
|
|
79
|
-
# @
|
|
156
|
+
# @example
|
|
157
|
+
# uri = HTTP::URI.parse("http://example.com")
|
|
158
|
+
# uri.eql?(HTTP::URI.parse("http://example.com"))
|
|
80
159
|
#
|
|
81
|
-
# @
|
|
160
|
+
# @param [Object] other URI to compare this one with
|
|
82
161
|
#
|
|
83
|
-
# @
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
end
|
|
162
|
+
# @api public
|
|
163
|
+
# @return [TrueClass, FalseClass] are the URIs equivalent?
|
|
164
|
+
def eql?(other)
|
|
165
|
+
other.is_a?(URI) && String(self).eql?(String(other))
|
|
88
166
|
end
|
|
89
167
|
|
|
90
|
-
#
|
|
168
|
+
# Hash value based off the normalized form of a URI
|
|
91
169
|
#
|
|
92
|
-
# @
|
|
170
|
+
# @example
|
|
171
|
+
# HTTP::URI.parse("http://example.com").hash
|
|
93
172
|
#
|
|
94
|
-
# @
|
|
95
|
-
# @
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
#
|
|
101
|
-
# @option options_or_uri [String, #to_str] :fragment component at the end of the URI
|
|
173
|
+
# @api public
|
|
174
|
+
# @return [Integer] A hash of the URI
|
|
175
|
+
def hash
|
|
176
|
+
@hash ||= [self.class, String(self)].hash
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Sets the host component for the URI
|
|
102
180
|
#
|
|
103
|
-
# @
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
181
|
+
# @example
|
|
182
|
+
# uri = HTTP::URI.parse("http://example.com")
|
|
183
|
+
# uri.host = "other.com"
|
|
184
|
+
#
|
|
185
|
+
# @param [String, #to_str] new_host The new host component
|
|
186
|
+
# @api public
|
|
187
|
+
# @return [void]
|
|
188
|
+
def host=(new_host)
|
|
189
|
+
@raw_host = process_ipv6_brackets(new_host, brackets: true)
|
|
190
|
+
@host = process_ipv6_brackets(@raw_host)
|
|
191
|
+
@normalized_host = normalize_host(@host)
|
|
192
|
+
end
|
|
113
193
|
|
|
114
|
-
|
|
115
|
-
|
|
194
|
+
# Port number, either as specified or the default
|
|
195
|
+
#
|
|
196
|
+
# @example
|
|
197
|
+
# HTTP::URI.parse("http://example.com").port
|
|
198
|
+
#
|
|
199
|
+
# @api public
|
|
200
|
+
# @return [Integer, nil] port number
|
|
201
|
+
def port
|
|
202
|
+
@port || default_port
|
|
116
203
|
end
|
|
117
204
|
|
|
118
|
-
#
|
|
205
|
+
# Default port for the URI scheme
|
|
119
206
|
#
|
|
120
|
-
# @
|
|
207
|
+
# @example
|
|
208
|
+
# HTTP::URI.parse("http://example.com").default_port # => 80
|
|
121
209
|
#
|
|
122
|
-
# @
|
|
123
|
-
|
|
124
|
-
|
|
210
|
+
# @api public
|
|
211
|
+
# @return [Integer, nil] default port or nil for unknown schemes
|
|
212
|
+
def default_port
|
|
213
|
+
DEFAULT_PORTS[@scheme&.downcase]
|
|
125
214
|
end
|
|
126
215
|
|
|
127
|
-
#
|
|
216
|
+
# The origin (scheme + host + port) per RFC 6454
|
|
128
217
|
#
|
|
129
|
-
# @
|
|
218
|
+
# @example
|
|
219
|
+
# HTTP::URI.parse("http://example.com").origin # => "http://example.com"
|
|
130
220
|
#
|
|
131
|
-
# @
|
|
132
|
-
|
|
133
|
-
|
|
221
|
+
# @api public
|
|
222
|
+
# @return [String] origin of the URI
|
|
223
|
+
def origin
|
|
224
|
+
port_suffix = ":#{port}" unless port.eql?(default_port)
|
|
225
|
+
"#{String(@scheme).downcase}://#{String(@raw_host).downcase}#{port_suffix}"
|
|
134
226
|
end
|
|
135
227
|
|
|
136
|
-
#
|
|
228
|
+
# The path and query for use in an HTTP request line
|
|
137
229
|
#
|
|
138
|
-
# @
|
|
139
|
-
|
|
140
|
-
|
|
230
|
+
# @example
|
|
231
|
+
# HTTP::URI.parse("http://example.com/path?q=1").request_uri # => "/path?q=1"
|
|
232
|
+
#
|
|
233
|
+
# @api public
|
|
234
|
+
# @return [String] request URI string
|
|
235
|
+
def request_uri
|
|
236
|
+
"#{'/' if @path.empty?}#{@path}#{"?#{@query}" if @query}"
|
|
141
237
|
end
|
|
142
238
|
|
|
143
|
-
#
|
|
239
|
+
# Returns a new URI with the specified components removed
|
|
144
240
|
#
|
|
145
|
-
# @
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
-
|
|
241
|
+
# @example
|
|
242
|
+
# HTTP::URI.parse("http://example.com#frag").omit(:fragment)
|
|
243
|
+
#
|
|
244
|
+
# @param components [Symbol] URI components to remove
|
|
245
|
+
# @api public
|
|
246
|
+
# @return [HTTP::URI] new URI without the specified components
|
|
247
|
+
def omit(*components)
|
|
248
|
+
self.class.new(
|
|
249
|
+
**{ scheme: @scheme, user: @user, password: @password, host: @raw_host,
|
|
250
|
+
port: @port, path: @path, query: @query, fragment: @fragment }.except(*components)
|
|
251
|
+
)
|
|
252
|
+
end
|
|
149
253
|
|
|
150
|
-
|
|
151
|
-
|
|
254
|
+
# Resolves another URI against this one per RFC 3986
|
|
255
|
+
#
|
|
256
|
+
# @example
|
|
257
|
+
# HTTP::URI.parse("http://example.com/foo/").join("bar")
|
|
258
|
+
#
|
|
259
|
+
# @param [String, URI] other the URI to resolve
|
|
260
|
+
#
|
|
261
|
+
# @api public
|
|
262
|
+
# @return [HTTP::URI] resolved URI
|
|
263
|
+
def join(other)
|
|
264
|
+
base = self.class.percent_encode(String(self))
|
|
265
|
+
ref = self.class.percent_encode(String(other))
|
|
266
|
+
self.class.parse(::URI.join(base, ref))
|
|
152
267
|
end
|
|
153
268
|
|
|
154
|
-
#
|
|
269
|
+
# Returns a normalized copy of the URI
|
|
155
270
|
#
|
|
156
|
-
#
|
|
157
|
-
|
|
158
|
-
|
|
271
|
+
# Lowercases scheme and host, strips default port. Used by {#==}
|
|
272
|
+
# to compare URIs for equivalence.
|
|
273
|
+
#
|
|
274
|
+
# @example
|
|
275
|
+
# HTTP::URI.parse("HTTP://EXAMPLE.COM:80").normalize
|
|
276
|
+
#
|
|
277
|
+
# @api public
|
|
278
|
+
# @return [HTTP::URI] normalized URI
|
|
279
|
+
def normalize
|
|
280
|
+
self.class.new(
|
|
281
|
+
scheme: @scheme&.downcase,
|
|
282
|
+
user: @user,
|
|
283
|
+
password: @password,
|
|
284
|
+
host: @raw_host&.downcase,
|
|
285
|
+
port: (@port unless port.eql?(default_port)),
|
|
286
|
+
path: @path.empty? && @raw_host ? "/" : @path,
|
|
287
|
+
query: @query,
|
|
288
|
+
fragment: @fragment
|
|
289
|
+
)
|
|
159
290
|
end
|
|
160
291
|
|
|
292
|
+
# Checks whether the URI scheme is HTTP
|
|
293
|
+
#
|
|
294
|
+
# @example
|
|
295
|
+
# HTTP::URI.parse("http://example.com").http?
|
|
296
|
+
#
|
|
297
|
+
# @api public
|
|
161
298
|
# @return [True] if URI is HTTP
|
|
162
299
|
# @return [False] otherwise
|
|
163
300
|
def http?
|
|
164
|
-
HTTP_SCHEME
|
|
301
|
+
HTTP_SCHEME.eql?(@scheme)
|
|
165
302
|
end
|
|
166
303
|
|
|
304
|
+
# Checks whether the URI scheme is HTTPS
|
|
305
|
+
#
|
|
306
|
+
# @example
|
|
307
|
+
# HTTP::URI.parse("https://example.com").https?
|
|
308
|
+
#
|
|
309
|
+
# @api public
|
|
167
310
|
# @return [True] if URI is HTTPS
|
|
168
311
|
# @return [False] otherwise
|
|
169
312
|
def https?
|
|
170
|
-
HTTPS_SCHEME
|
|
313
|
+
HTTPS_SCHEME.eql?(@scheme)
|
|
171
314
|
end
|
|
172
315
|
|
|
173
|
-
#
|
|
316
|
+
# Duplicates the URI object
|
|
317
|
+
#
|
|
318
|
+
# @example
|
|
319
|
+
# HTTP::URI.parse("http://example.com").dup
|
|
320
|
+
#
|
|
321
|
+
# @api public
|
|
322
|
+
# @return [HTTP::URI] duplicated URI
|
|
174
323
|
def dup
|
|
175
|
-
self.class.new
|
|
324
|
+
self.class.new(
|
|
325
|
+
scheme: @scheme, user: @user, password: @password, host: @raw_host,
|
|
326
|
+
port: @port, path: @path, query: @query, fragment: @fragment
|
|
327
|
+
)
|
|
176
328
|
end
|
|
177
329
|
|
|
178
330
|
# Convert an HTTP::URI to a String
|
|
179
331
|
#
|
|
332
|
+
# @example
|
|
333
|
+
# HTTP::URI.parse("http://example.com").to_s
|
|
334
|
+
#
|
|
335
|
+
# @api public
|
|
180
336
|
# @return [String] URI serialized as a String
|
|
181
337
|
def to_s
|
|
182
|
-
|
|
338
|
+
str = +""
|
|
339
|
+
str << "#{@scheme}:" if @scheme
|
|
340
|
+
str << authority_string if @raw_host
|
|
341
|
+
str << @path
|
|
342
|
+
str << "?#{@query}" if @query
|
|
343
|
+
str << "##{@fragment}" if @fragment
|
|
344
|
+
str
|
|
183
345
|
end
|
|
184
346
|
alias to_str to_s
|
|
185
347
|
|
|
348
|
+
# Returns human-readable representation of URI
|
|
349
|
+
#
|
|
350
|
+
# @example
|
|
351
|
+
# HTTP::URI.parse("http://example.com").inspect
|
|
352
|
+
#
|
|
353
|
+
# @api public
|
|
186
354
|
# @return [String] human-readable representation of URI
|
|
187
355
|
def inspect
|
|
188
|
-
format("#<%s:0x%014x URI:%s>", self.class
|
|
356
|
+
format("#<%s:0x%014x URI:%s>", self.class, object_id << 1, self)
|
|
189
357
|
end
|
|
190
358
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
# Process a URI host, adding or removing surrounding brackets if the host is an IPv6 address.
|
|
359
|
+
# Pattern matching interface
|
|
194
360
|
#
|
|
195
|
-
# @
|
|
196
|
-
#
|
|
361
|
+
# @example
|
|
362
|
+
# uri.deconstruct_keys(%i[scheme host])
|
|
197
363
|
#
|
|
198
|
-
# @
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
raw_host
|
|
206
|
-
end
|
|
207
|
-
rescue IPAddr::Error
|
|
208
|
-
raw_host
|
|
364
|
+
# @param keys [Array<Symbol>, nil] keys to extract, or nil for all
|
|
365
|
+
# @return [Hash{Symbol => Object}]
|
|
366
|
+
# @api public
|
|
367
|
+
def deconstruct_keys(keys)
|
|
368
|
+
hash = { scheme: @scheme, host: @host, port: port, path: @path,
|
|
369
|
+
query: @query, fragment: @fragment, user: @user, password: @password }
|
|
370
|
+
keys ? hash.slice(*keys) : hash
|
|
209
371
|
end
|
|
210
372
|
end
|
|
211
373
|
end
|
|
374
|
+
|
|
375
|
+
require "http/uri/parsing"
|
|
376
|
+
require "http/uri/normalizer"
|
data/lib/http/version.rb
CHANGED
data/lib/http.rb
CHANGED
|
@@ -5,8 +5,8 @@ require "http/timeout/null"
|
|
|
5
5
|
require "http/timeout/per_operation"
|
|
6
6
|
require "http/timeout/global"
|
|
7
7
|
require "http/chainable"
|
|
8
|
+
require "http/session"
|
|
8
9
|
require "http/client"
|
|
9
|
-
require "http/retriable/client"
|
|
10
10
|
require "http/connection"
|
|
11
11
|
require "http/options"
|
|
12
12
|
require "http/feature"
|
|
@@ -21,7 +21,16 @@ module HTTP
|
|
|
21
21
|
extend Chainable
|
|
22
22
|
|
|
23
23
|
class << self
|
|
24
|
-
#
|
|
24
|
+
# Set default headers and return a chainable session
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# HTTP[:accept => "text/html"].get("https://example.com")
|
|
28
|
+
#
|
|
29
|
+
# @param headers [Hash] headers to set
|
|
30
|
+
#
|
|
31
|
+
# @return [HTTP::Session]
|
|
32
|
+
#
|
|
33
|
+
# @api public
|
|
25
34
|
alias [] headers
|
|
26
35
|
end
|
|
27
36
|
end
|
data/sig/deps.rbs
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Overrides for stdlib RBS signatures
|
|
2
|
+
|
|
3
|
+
class ::StringIO
|
|
4
|
+
# Override: Ruby's StringIO#read accepts nil outbuf
|
|
5
|
+
def read: (?int? length, ?string? buf) -> String?
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class ::File
|
|
9
|
+
# Override: Ruby's File.new accepts keyword args like binmode:
|
|
10
|
+
def initialize: (string | _ToPath | int file_name, ?string | int mode, ?int perm, **untyped) -> void
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ::Pathname
|
|
14
|
+
# Override: Ruby's Pathname#open accepts keyword args like binmode:
|
|
15
|
+
def open: (?string | int mode, ?int perm, **untyped) -> ::File
|
|
16
|
+
| [T] (?string | int mode, ?int perm, **untyped) { (::File) -> T } -> T
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Stubs for external dependencies without RBS definitions
|
|
20
|
+
|
|
21
|
+
module Addressable
|
|
22
|
+
class URI
|
|
23
|
+
class InvalidURIError < StandardError
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.parse: (untyped) -> Addressable::URI
|
|
27
|
+
def self.form_encode: (untyped, ?bool) -> String
|
|
28
|
+
def self.normalize_path: (untyped) -> String
|
|
29
|
+
|
|
30
|
+
def initialize: (?untyped) -> void
|
|
31
|
+
def scheme: () -> String?
|
|
32
|
+
def scheme=: (untyped) -> untyped
|
|
33
|
+
def normalized_scheme: () -> String?
|
|
34
|
+
def authority: () -> String?
|
|
35
|
+
def normalized_authority: () -> String?
|
|
36
|
+
def user: () -> String?
|
|
37
|
+
def user=: (untyped) -> untyped
|
|
38
|
+
def password: () -> String?
|
|
39
|
+
def password=: (untyped) -> untyped
|
|
40
|
+
def host: () -> String?
|
|
41
|
+
def host=: (untyped) -> untyped
|
|
42
|
+
def normalized_host: () -> String?
|
|
43
|
+
def port: () -> Integer?
|
|
44
|
+
def port=: (untyped) -> untyped
|
|
45
|
+
def normalized_port: () -> Integer?
|
|
46
|
+
def path: () -> String
|
|
47
|
+
def path=: (untyped) -> untyped
|
|
48
|
+
def normalized_path: () -> String
|
|
49
|
+
def query: () -> String?
|
|
50
|
+
def query=: (untyped) -> untyped
|
|
51
|
+
def query_values: (?untyped?) -> untyped
|
|
52
|
+
def query_values=: (untyped) -> untyped
|
|
53
|
+
def normalized_query: () -> String?
|
|
54
|
+
def request_uri: () -> String
|
|
55
|
+
def request_uri=: (untyped) -> untyped
|
|
56
|
+
def fragment: () -> String?
|
|
57
|
+
def normalized_fragment: () -> String?
|
|
58
|
+
def fragment=: (untyped) -> untyped
|
|
59
|
+
def omit: (*Symbol) -> Addressable::URI
|
|
60
|
+
def join: (untyped) -> Addressable::URI
|
|
61
|
+
def normalize: () -> Addressable::URI
|
|
62
|
+
def origin: () -> String
|
|
63
|
+
def default_port: () -> Integer?
|
|
64
|
+
def to_s: () -> String
|
|
65
|
+
def ==: (untyped) -> bool
|
|
66
|
+
def eql?: (untyped) -> bool
|
|
67
|
+
def hash: () -> Integer
|
|
68
|
+
def dup: () -> Addressable::URI
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Override: stdlib zlib RBS incorrectly marks level/strategy as required
|
|
73
|
+
module Zlib
|
|
74
|
+
class GzipWriter
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def initialize: (untyped io, ?Integer? level, ?Integer? strategy, **untyped opts) -> void
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module LLHttp
|
|
82
|
+
class Error < StandardError
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class Parser
|
|
86
|
+
def initialize: (untyped, ?type: Symbol) -> void
|
|
87
|
+
def reset: () -> void
|
|
88
|
+
def <<: (String) -> void
|
|
89
|
+
def status_code: () -> Integer
|
|
90
|
+
def http_major: () -> Integer
|
|
91
|
+
def http_minor: () -> Integer
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class Delegate
|
|
95
|
+
def initialize: () -> void
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Supplement: Numeric#to_i and #to_f are not in the stdlib RBS
|
|
100
|
+
class Numeric
|
|
101
|
+
def to_i: () -> Integer
|
|
102
|
+
def to_f: () -> Float
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
module IO::WaitReadable
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
module IO::WaitWritable
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class IO
|
|
112
|
+
# Missing from rbs core (available since Ruby 3.2)
|
|
113
|
+
class TimeoutError < IOError
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
class EAGAINWaitReadable < Errno::EAGAIN
|
|
117
|
+
include IO::WaitReadable
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def wait_readable: (?Numeric?) -> (IO | true | nil)
|
|
121
|
+
def wait_writable: (?Numeric?) -> (IO | true | nil)
|
|
122
|
+
end
|