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
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
RSpec.describe HTTP::Retriable::Performer do
|
|
4
|
-
let(:client) do
|
|
5
|
-
HTTP::Client.new
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
let(:response) do
|
|
9
|
-
HTTP::Response.new(
|
|
10
|
-
status: 200,
|
|
11
|
-
version: "1.1",
|
|
12
|
-
headers: {},
|
|
13
|
-
body: "Hello world!",
|
|
14
|
-
request: request
|
|
15
|
-
)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
let(:request) do
|
|
19
|
-
HTTP::Request.new(
|
|
20
|
-
verb: :get,
|
|
21
|
-
uri: "http://example.com"
|
|
22
|
-
)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
let(:perform_spy) { { counter: 0 } }
|
|
26
|
-
let(:counter_spy) { perform_spy[:counter] }
|
|
27
|
-
|
|
28
|
-
before do
|
|
29
|
-
stub_const("CustomException", Class.new(StandardError))
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def perform(options = {}, client_arg = client, request_arg = request, &block)
|
|
33
|
-
# by explicitly overwriting the default delay, we make a much faster test suite
|
|
34
|
-
default_options = { delay: 0 }
|
|
35
|
-
options = default_options.merge(options)
|
|
36
|
-
|
|
37
|
-
HTTP::Retriable::Performer
|
|
38
|
-
.new(options)
|
|
39
|
-
.perform(client_arg, request_arg) do
|
|
40
|
-
perform_spy[:counter] += 1
|
|
41
|
-
block ? yield : response
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def measure_wait
|
|
46
|
-
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
47
|
-
result = yield
|
|
48
|
-
t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
49
|
-
[t2 - t1, result]
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
describe "#perform" do
|
|
53
|
-
describe "expected exception" do
|
|
54
|
-
it "retries the request" do
|
|
55
|
-
expect do
|
|
56
|
-
perform(exceptions: [CustomException], tries: 2) do
|
|
57
|
-
raise CustomException
|
|
58
|
-
end
|
|
59
|
-
end.to raise_error HTTP::OutOfRetriesError
|
|
60
|
-
|
|
61
|
-
expect(counter_spy).to eq 2
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
describe "unexpected exception" do
|
|
66
|
-
it "does not retry the request" do
|
|
67
|
-
expect do
|
|
68
|
-
perform(exceptions: [], tries: 2) do
|
|
69
|
-
raise CustomException
|
|
70
|
-
end
|
|
71
|
-
end.to raise_error CustomException
|
|
72
|
-
|
|
73
|
-
expect(counter_spy).to eq 1
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
describe "expected status codes" do
|
|
78
|
-
def response(**options)
|
|
79
|
-
HTTP::Response.new(
|
|
80
|
-
{
|
|
81
|
-
status: 200,
|
|
82
|
-
version: "1.1",
|
|
83
|
-
headers: {},
|
|
84
|
-
body: "Hello world!",
|
|
85
|
-
request: request
|
|
86
|
-
}.merge(options)
|
|
87
|
-
)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it "retries the request" do
|
|
91
|
-
expect do
|
|
92
|
-
perform(retry_statuses: [200], tries: 2)
|
|
93
|
-
end.to raise_error HTTP::OutOfRetriesError
|
|
94
|
-
|
|
95
|
-
expect(counter_spy).to eq 2
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
describe "status codes can be expressed in many ways" do
|
|
99
|
-
[
|
|
100
|
-
301,
|
|
101
|
-
[200, 301, 485],
|
|
102
|
-
250...400,
|
|
103
|
-
[250...Float::INFINITY],
|
|
104
|
-
->(status_code) { status_code == 301 },
|
|
105
|
-
[->(status_code) { status_code == 301 }]
|
|
106
|
-
].each do |retry_statuses|
|
|
107
|
-
it retry_statuses.to_s do
|
|
108
|
-
expect do
|
|
109
|
-
perform(retry_statuses: retry_statuses, tries: 2) do
|
|
110
|
-
response(status: 301)
|
|
111
|
-
end
|
|
112
|
-
end.to raise_error HTTP::OutOfRetriesError
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
describe "unexpected status code" do
|
|
119
|
-
it "does not retry the request" do
|
|
120
|
-
expect(
|
|
121
|
-
perform(retry_statuses: [], tries: 2)
|
|
122
|
-
).to eq response
|
|
123
|
-
|
|
124
|
-
expect(counter_spy).to eq 1
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
describe "on_retry callback" do
|
|
129
|
-
it "calls the on_retry callback on each retry with exception" do
|
|
130
|
-
callback_call_spy = 0
|
|
131
|
-
|
|
132
|
-
callback_spy = proc do |callback_request, error, callback_response|
|
|
133
|
-
expect(callback_request).to eq request
|
|
134
|
-
expect(error).to be_a HTTP::TimeoutError
|
|
135
|
-
expect(callback_response).to be_nil
|
|
136
|
-
callback_call_spy += 1
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
expect do
|
|
140
|
-
perform(tries: 3, on_retry: callback_spy) do
|
|
141
|
-
raise HTTP::TimeoutError
|
|
142
|
-
end
|
|
143
|
-
end.to raise_error HTTP::OutOfRetriesError
|
|
144
|
-
|
|
145
|
-
expect(callback_call_spy).to eq 2
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
it "calls the on_retry callback on each retry with response" do
|
|
149
|
-
callback_call_spy = 0
|
|
150
|
-
|
|
151
|
-
callback_spy = proc do |callback_request, error, callback_response|
|
|
152
|
-
expect(callback_request).to eq request
|
|
153
|
-
expect(error).to be_nil
|
|
154
|
-
expect(callback_response).to be response
|
|
155
|
-
callback_call_spy += 1
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
expect do
|
|
159
|
-
perform(retry_statuses: [200], tries: 3, on_retry: callback_spy)
|
|
160
|
-
end.to raise_error HTTP::OutOfRetriesError
|
|
161
|
-
|
|
162
|
-
expect(callback_call_spy).to eq 2
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
describe "delay option" do
|
|
167
|
-
let(:timing_slack) { 0.05 }
|
|
168
|
-
|
|
169
|
-
it "can be a positive number" do
|
|
170
|
-
time, = measure_wait do
|
|
171
|
-
perform(delay: 0.1, tries: 3, should_retry: ->(*) { true })
|
|
172
|
-
rescue HTTP::OutOfRetriesError
|
|
173
|
-
end
|
|
174
|
-
expect(time).to be_within(timing_slack).of(0.2)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
it "can be a proc number" do
|
|
178
|
-
time, = measure_wait do
|
|
179
|
-
perform(delay: ->(attempt) { attempt / 10.0 }, tries: 3, should_retry: ->(*) { true })
|
|
180
|
-
rescue HTTP::OutOfRetriesError
|
|
181
|
-
end
|
|
182
|
-
expect(time).to be_within(timing_slack).of(0.3)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
it "receives correct retry number when a proc" do
|
|
186
|
-
retry_count = 0
|
|
187
|
-
retry_proc = proc do |attempt|
|
|
188
|
-
expect(attempt).to eq(retry_count).and(be > 0)
|
|
189
|
-
0
|
|
190
|
-
end
|
|
191
|
-
begin
|
|
192
|
-
perform(delay: retry_proc, should_retry: ->(*) { true }) do
|
|
193
|
-
retry_count += 1
|
|
194
|
-
response
|
|
195
|
-
end
|
|
196
|
-
rescue HTTP::OutOfRetriesError
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
describe "should_retry option" do
|
|
202
|
-
it "decides if the request should be retried" do # rubocop:disable RSpec/MultipleExpectations
|
|
203
|
-
retry_proc = proc do |req, err, res, attempt|
|
|
204
|
-
expect(req).to eq request
|
|
205
|
-
if res
|
|
206
|
-
expect(err).to be_nil
|
|
207
|
-
expect(res).to be response
|
|
208
|
-
else
|
|
209
|
-
expect(err).to be_a CustomException
|
|
210
|
-
expect(res).to be_nil
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
attempt < 5
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
begin
|
|
217
|
-
perform(should_retry: retry_proc) do
|
|
218
|
-
rand < 0.5 ? response : raise(CustomException)
|
|
219
|
-
end
|
|
220
|
-
rescue CustomException
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
expect(counter_spy).to eq 5
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
it "raises the original error if not retryable" do
|
|
227
|
-
retry_proc = ->(*) { false }
|
|
228
|
-
|
|
229
|
-
expect do
|
|
230
|
-
perform(should_retry: retry_proc) do
|
|
231
|
-
raise CustomException
|
|
232
|
-
end
|
|
233
|
-
end.to raise_error CustomException
|
|
234
|
-
|
|
235
|
-
expect(counter_spy).to eq 1
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
it "raises HTTP::OutOfRetriesError if retryable" do
|
|
239
|
-
retry_proc = ->(*) { true }
|
|
240
|
-
|
|
241
|
-
expect do
|
|
242
|
-
perform(should_retry: retry_proc) do
|
|
243
|
-
raise CustomException
|
|
244
|
-
end
|
|
245
|
-
end.to raise_error HTTP::OutOfRetriesError
|
|
246
|
-
|
|
247
|
-
expect(counter_spy).to eq 5
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
describe "connection closing" do
|
|
253
|
-
let(:client) { double(:client) }
|
|
254
|
-
|
|
255
|
-
it "does not close the connection if we get a propper response" do
|
|
256
|
-
expect(client).not_to receive(:close)
|
|
257
|
-
perform
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
it "closes the connection after each raiseed attempt" do
|
|
261
|
-
expect(client).to receive(:close).exactly(3).times
|
|
262
|
-
begin
|
|
263
|
-
perform(should_retry: ->(*) { true }, tries: 3)
|
|
264
|
-
rescue HTTP::OutOfRetriesError
|
|
265
|
-
end
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
it "closes the connection on an unexpected exception" do
|
|
269
|
-
expect(client).to receive(:close)
|
|
270
|
-
begin
|
|
271
|
-
perform do
|
|
272
|
-
raise CustomException
|
|
273
|
-
end
|
|
274
|
-
rescue CustomException
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
describe HTTP::OutOfRetriesError do
|
|
280
|
-
it "has the original exception as a cause if available" do
|
|
281
|
-
err = nil
|
|
282
|
-
begin
|
|
283
|
-
perform(exceptions: [CustomException]) do
|
|
284
|
-
raise CustomException
|
|
285
|
-
end
|
|
286
|
-
rescue described_class => e
|
|
287
|
-
err = e
|
|
288
|
-
end
|
|
289
|
-
expect(err.cause).to be_a CustomException
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
it "has the last raiseed response as an attribute" do
|
|
293
|
-
err = nil
|
|
294
|
-
begin
|
|
295
|
-
perform(should_retry: ->(*) { true })
|
|
296
|
-
rescue described_class => e
|
|
297
|
-
err = e
|
|
298
|
-
end
|
|
299
|
-
expect(err.response).to be response
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
end
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
RSpec.describe HTTP::URI::NORMALIZER do
|
|
4
|
-
describe "scheme" do
|
|
5
|
-
it "lower-cases scheme" do
|
|
6
|
-
expect(HTTP::URI::NORMALIZER.call("HttP://example.com").scheme).to eq "http"
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
describe "hostname" do
|
|
11
|
-
it "lower-cases hostname" do
|
|
12
|
-
expect(HTTP::URI::NORMALIZER.call("http://EXAMPLE.com").host).to eq "example.com"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it "decodes percent-encoded hostname" do
|
|
16
|
-
expect(HTTP::URI::NORMALIZER.call("http://ex%61mple.com").host).to eq "example.com"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "removes trailing period in hostname" do
|
|
20
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com.").host).to eq "example.com"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "IDN-encodes non-ASCII hostname" do
|
|
24
|
-
expect(HTTP::URI::NORMALIZER.call("http://exämple.com").host).to eq "xn--exmple-cua.com"
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
describe "path" do
|
|
29
|
-
it "ensures path is not empty" do
|
|
30
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com").path).to eq "/"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "preserves double slashes in path" do
|
|
34
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com//a///b").path).to eq "//a///b"
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "resolves single-dot segments in path" do
|
|
38
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/a/./b").path).to eq "/a/b"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "resolves double-dot segments in path" do
|
|
42
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/a/b/../c").path).to eq "/a/c"
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it "resolves leading double-dot segments in path" do
|
|
46
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/../a/b").path).to eq "/a/b"
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it "percent-encodes control characters in path" do
|
|
50
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/\x00\x7F\n").path).to eq "/%00%7F%0A"
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it "percent-encodes space in path" do
|
|
54
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/a b").path).to eq "/a%20b"
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
it "percent-encodes non-ASCII characters in path" do
|
|
58
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/キョ").path).to eq "/%E3%82%AD%E3%83%A7"
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it "does not percent-encode non-special characters in path" do
|
|
62
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/~.-_!$&()*,;=:@{}").path).to eq "/~.-_!$&()*,;=:@{}"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it "preserves escape sequences in path" do
|
|
66
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/%41").path).to eq "/%41"
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
describe "query" do
|
|
71
|
-
it "allows no query" do
|
|
72
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com").query).to be_nil
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
it "percent-encodes control characters in query" do
|
|
76
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/?\x00\x7F\n").query).to eq "%00%7F%0A"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
it "percent-encodes space in query" do
|
|
80
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/?a b").query).to eq "a%20b"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
it "percent-encodes non-ASCII characters in query" do
|
|
84
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com?キョ").query).to eq "%E3%82%AD%E3%83%A7"
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it "does not percent-encode non-special characters in query" do
|
|
88
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/?~.-_!$&()*,;=:@{}?").query).to eq "~.-_!$&()*,;=:@{}?"
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
it "preserves escape sequences in query" do
|
|
92
|
-
expect(HTTP::URI::NORMALIZER.call("http://example.com/?%41").query).to eq "%41"
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
data/spec/lib/http/uri_spec.rb
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
RSpec.describe HTTP::URI do
|
|
4
|
-
let(:example_ipv6_address) { "2606:2800:220:1:248:1893:25c8:1946" }
|
|
5
|
-
|
|
6
|
-
let(:example_http_uri_string) { "http://example.com" }
|
|
7
|
-
let(:example_https_uri_string) { "https://example.com" }
|
|
8
|
-
let(:example_ipv6_uri_string) { "https://[#{example_ipv6_address}]" }
|
|
9
|
-
|
|
10
|
-
subject(:http_uri) { described_class.parse(example_http_uri_string) }
|
|
11
|
-
subject(:https_uri) { described_class.parse(example_https_uri_string) }
|
|
12
|
-
subject(:ipv6_uri) { described_class.parse(example_ipv6_uri_string) }
|
|
13
|
-
|
|
14
|
-
it "knows URI schemes" do
|
|
15
|
-
expect(http_uri.scheme).to eq "http"
|
|
16
|
-
expect(https_uri.scheme).to eq "https"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "sets default ports for HTTP URIs" do
|
|
20
|
-
expect(http_uri.port).to eq 80
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "sets default ports for HTTPS URIs" do
|
|
24
|
-
expect(https_uri.port).to eq 443
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
describe "#host" do
|
|
28
|
-
it "strips brackets from IPv6 addresses" do
|
|
29
|
-
expect(ipv6_uri.host).to eq("2606:2800:220:1:248:1893:25c8:1946")
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
describe "#normalized_host" do
|
|
34
|
-
it "strips brackets from IPv6 addresses" do
|
|
35
|
-
expect(ipv6_uri.normalized_host).to eq("2606:2800:220:1:248:1893:25c8:1946")
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
describe "#host=" do
|
|
40
|
-
it "updates cached values for #host and #normalized_host" do
|
|
41
|
-
expect(http_uri.host).to eq("example.com")
|
|
42
|
-
expect(http_uri.normalized_host).to eq("example.com")
|
|
43
|
-
|
|
44
|
-
http_uri.host = "[#{example_ipv6_address}]"
|
|
45
|
-
|
|
46
|
-
expect(http_uri.host).to eq(example_ipv6_address)
|
|
47
|
-
expect(http_uri.normalized_host).to eq(example_ipv6_address)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "ensures IPv6 addresses are bracketed in the inner Addressable::URI" do
|
|
51
|
-
expect(http_uri.host).to eq("example.com")
|
|
52
|
-
expect(http_uri.normalized_host).to eq("example.com")
|
|
53
|
-
|
|
54
|
-
http_uri.host = example_ipv6_address
|
|
55
|
-
|
|
56
|
-
expect(http_uri.host).to eq(example_ipv6_address)
|
|
57
|
-
expect(http_uri.normalized_host).to eq(example_ipv6_address)
|
|
58
|
-
expect(http_uri.instance_variable_get(:@uri).host).to eq("[#{example_ipv6_address}]")
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
describe "#dup" do
|
|
63
|
-
it "doesn't share internal value between duplicates" do
|
|
64
|
-
duplicated_uri = http_uri.dup
|
|
65
|
-
duplicated_uri.host = "example.org"
|
|
66
|
-
|
|
67
|
-
expect(duplicated_uri.to_s).to eq("http://example.org")
|
|
68
|
-
expect(http_uri.to_s).to eq("http://example.com")
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|