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/test/http_test.rb
ADDED
|
@@ -0,0 +1,818 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
require "support/dummy_server"
|
|
8
|
+
require "support/proxy_server"
|
|
9
|
+
|
|
10
|
+
class HTTPTest < Minitest::Test
|
|
11
|
+
cover "HTTP::Chainable*"
|
|
12
|
+
run_server(:dummy) { DummyServer.new }
|
|
13
|
+
run_server(:dummy_ssl) { DummyServer.new(ssl: true) }
|
|
14
|
+
|
|
15
|
+
# getting resources
|
|
16
|
+
|
|
17
|
+
def test_getting_resources_is_easy
|
|
18
|
+
response = HTTP.get dummy.endpoint
|
|
19
|
+
|
|
20
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_getting_resources_with_uri_instance
|
|
24
|
+
response = HTTP.get HTTP::URI.parse(dummy.endpoint)
|
|
25
|
+
|
|
26
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_getting_resources_with_query_string_parameters
|
|
30
|
+
response = HTTP.get "#{dummy.endpoint}/params", params: { foo: "bar" }
|
|
31
|
+
|
|
32
|
+
assert_match(/Params!/, response.to_s)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_getting_resources_with_query_string_parameters_in_uri_and_opts
|
|
36
|
+
response = HTTP.get "#{dummy.endpoint}/multiple-params?foo=bar", params: { baz: "quux" }
|
|
37
|
+
|
|
38
|
+
assert_match(/More Params!/, response.to_s)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_getting_resources_with_two_leading_slashes_in_path
|
|
42
|
+
HTTP.get "#{dummy.endpoint}//"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_getting_resources_with_headers
|
|
46
|
+
response = HTTP.accept("application/json").get dummy.endpoint
|
|
47
|
+
|
|
48
|
+
assert_includes response.to_s, "json"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# getting resources with a large request body + timeout variants
|
|
52
|
+
|
|
53
|
+
[:null, 6, { read: 2, write: 2, connect: 2 }, { global: 6, read: 2, write: 2, connect: 2 }].each do |timeout|
|
|
54
|
+
define_method :"test_large_request_body_with_timeout_#{timeout.inspect}" do
|
|
55
|
+
request_body = "\xE2\x80\x9C" * 1_000_000
|
|
56
|
+
client = HTTP.timeout(timeout)
|
|
57
|
+
response = client.post "#{dummy.endpoint}/echo-body", body: request_body
|
|
58
|
+
|
|
59
|
+
assert_equal request_body.b, response.body.to_s
|
|
60
|
+
assert_equal request_body.bytesize, response.headers["Content-Length"].to_i
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# with a block
|
|
65
|
+
|
|
66
|
+
def test_block_yields_the_response
|
|
67
|
+
HTTP.get(dummy.endpoint) do |response|
|
|
68
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_block_returns_the_block_value
|
|
73
|
+
result = HTTP.get(dummy.endpoint) { |response| response.status.code }
|
|
74
|
+
|
|
75
|
+
assert_equal 200, result
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_block_closes_the_connection_after_the_block
|
|
79
|
+
client = nil
|
|
80
|
+
HTTP.stub(:make_client, lambda { |opts|
|
|
81
|
+
client = HTTP::Client.new(opts)
|
|
82
|
+
original_close = client.method(:close)
|
|
83
|
+
client.define_singleton_method(:close) do
|
|
84
|
+
@test_closed = true
|
|
85
|
+
original_close.call
|
|
86
|
+
end
|
|
87
|
+
client.define_singleton_method(:test_closed?) { @test_closed == true }
|
|
88
|
+
client
|
|
89
|
+
}) do
|
|
90
|
+
HTTP.get(dummy.endpoint, &:status)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
assert_predicate client, :test_closed?, "expected close to have been called"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_block_closes_the_connection_even_when_the_block_raises
|
|
97
|
+
client = nil
|
|
98
|
+
|
|
99
|
+
HTTP.stub(:make_client, lambda { |opts|
|
|
100
|
+
client = HTTP::Client.new(opts)
|
|
101
|
+
original_close = client.method(:close)
|
|
102
|
+
client.define_singleton_method(:close) do
|
|
103
|
+
@test_closed = true
|
|
104
|
+
original_close.call
|
|
105
|
+
end
|
|
106
|
+
client.define_singleton_method(:test_closed?) { @test_closed == true }
|
|
107
|
+
client
|
|
108
|
+
}) do
|
|
109
|
+
assert_raises(RuntimeError) do
|
|
110
|
+
HTTP.get(dummy.endpoint) { raise "boom" }
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
assert_predicate client, :test_closed?, "expected close to have been called on error"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_block_works_with_chained_options
|
|
118
|
+
result = HTTP.headers("Accept" => "application/json").get(dummy.endpoint) do |response|
|
|
119
|
+
response.status.code
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
assert_equal 200, result
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_block_handles_nil_client_when_make_client_raises
|
|
126
|
+
HTTP.stub(:make_client, ->(*) { raise "boom" }) do
|
|
127
|
+
assert_raises(RuntimeError) { HTTP.get(dummy.endpoint) { nil } }
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# .retry
|
|
132
|
+
|
|
133
|
+
def test_retry_ensures_endpoint_counts_retries
|
|
134
|
+
assert_equal "retried 1x", HTTP.get("#{dummy.endpoint}/retry-2").to_s
|
|
135
|
+
assert_equal "retried 2x", HTTP.get("#{dummy.endpoint}/retry-2").to_s
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_retry_retries_the_request
|
|
139
|
+
response = HTTP.retriable(delay: 0, retry_statuses: 500...600).get "#{dummy.endpoint}/retry-2"
|
|
140
|
+
|
|
141
|
+
assert_equal "retried 2x", response.to_s
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_retry_retries_the_request_and_gives_access_to_failed_requests
|
|
145
|
+
err = nil
|
|
146
|
+
retry_callback = ->(_, _, res) { assert_match(/^retried \dx$/, res.to_s) }
|
|
147
|
+
begin
|
|
148
|
+
HTTP.retriable(
|
|
149
|
+
should_retry: ->(*) { true },
|
|
150
|
+
tries: 3,
|
|
151
|
+
delay: 0,
|
|
152
|
+
on_retry: retry_callback
|
|
153
|
+
).get "#{dummy.endpoint}/retry-2"
|
|
154
|
+
rescue HTTP::Error => e
|
|
155
|
+
err = e
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
assert_equal "retried 3x", err.response.to_s
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# posting forms to resources
|
|
162
|
+
|
|
163
|
+
def test_posting_forms_is_easy
|
|
164
|
+
response = HTTP.post "#{dummy.endpoint}/form", form: { example: "testing-form" }
|
|
165
|
+
|
|
166
|
+
assert_equal "passed :)", response.to_s
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# loading binary data
|
|
170
|
+
|
|
171
|
+
def test_binary_data_is_encoded_as_bytes
|
|
172
|
+
response = HTTP.get "#{dummy.endpoint}/bytes"
|
|
173
|
+
|
|
174
|
+
assert_equal Encoding::BINARY, response.to_s.encoding
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# loading endpoint with charset
|
|
178
|
+
|
|
179
|
+
def test_charset_uses_charset_from_headers
|
|
180
|
+
response = HTTP.get "#{dummy.endpoint}/iso-8859-1"
|
|
181
|
+
|
|
182
|
+
assert_equal Encoding::ISO8859_1, response.to_s.encoding
|
|
183
|
+
assert_equal "testæ", response.to_s.encode(Encoding::UTF_8)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def test_charset_with_encoding_option_respects_option
|
|
187
|
+
response = HTTP.get "#{dummy.endpoint}/iso-8859-1", encoding: Encoding::BINARY
|
|
188
|
+
|
|
189
|
+
assert_equal Encoding::BINARY, response.to_s.encoding
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# passing a string encoding type
|
|
193
|
+
|
|
194
|
+
def test_string_encoding_type_finds_encoding
|
|
195
|
+
response = HTTP.get dummy.endpoint, encoding: "ascii"
|
|
196
|
+
|
|
197
|
+
assert_equal Encoding::ASCII, response.to_s.encoding
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# loading text with no charset
|
|
201
|
+
|
|
202
|
+
def test_text_with_no_charset_is_binary_encoded
|
|
203
|
+
response = HTTP.get dummy.endpoint
|
|
204
|
+
|
|
205
|
+
assert_equal Encoding::BINARY, response.to_s.encoding
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# posting with an explicit body
|
|
209
|
+
|
|
210
|
+
def test_posting_with_explicit_body_is_easy
|
|
211
|
+
response = HTTP.post "#{dummy.endpoint}/body", body: "testing-body"
|
|
212
|
+
|
|
213
|
+
assert_equal "passed :)", response.to_s
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# with redirects
|
|
217
|
+
|
|
218
|
+
def test_redirects_is_easy_for_301
|
|
219
|
+
response = HTTP.follow.get("#{dummy.endpoint}/redirect-301")
|
|
220
|
+
|
|
221
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def test_redirects_is_easy_for_302
|
|
225
|
+
response = HTTP.follow.get("#{dummy.endpoint}/redirect-302")
|
|
226
|
+
|
|
227
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# head requests
|
|
231
|
+
|
|
232
|
+
def test_head_request_is_easy
|
|
233
|
+
response = HTTP.head dummy.endpoint
|
|
234
|
+
|
|
235
|
+
assert_equal 200, response.status.to_i
|
|
236
|
+
assert_match(/html/, response.headers["content-type"])
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# .auth
|
|
240
|
+
|
|
241
|
+
def test_auth_sets_authorization_header
|
|
242
|
+
client = HTTP.auth "abc"
|
|
243
|
+
|
|
244
|
+
assert_equal "abc", client.default_options.headers[:authorization]
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def test_auth_accepts_any_to_s_object
|
|
248
|
+
client = HTTP.auth fake(to_s: "abc")
|
|
249
|
+
|
|
250
|
+
assert_equal "abc", client.default_options.headers[:authorization]
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# .basic_auth
|
|
254
|
+
|
|
255
|
+
def test_basic_auth_fails_when_pass_is_not_given
|
|
256
|
+
assert_raises(ArgumentError) { HTTP.basic_auth(user: "[USER]") }
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def test_basic_auth_fails_when_user_is_not_given
|
|
260
|
+
assert_raises(ArgumentError) { HTTP.basic_auth(pass: "[PASS]") }
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def test_basic_auth_sets_authorization_header
|
|
264
|
+
client = HTTP.basic_auth user: "foo", pass: "bar"
|
|
265
|
+
|
|
266
|
+
assert_match(%r{^Basic [A-Za-z0-9+/]+=*$}, client.default_options.headers[:authorization])
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# .base_uri
|
|
270
|
+
|
|
271
|
+
def test_base_uri_resolves_relative_paths
|
|
272
|
+
response = HTTP.base_uri(dummy.endpoint).get("/")
|
|
273
|
+
|
|
274
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def test_base_uri_resolves_paths_without_leading_slash
|
|
278
|
+
response = HTTP.base_uri(dummy.endpoint).get("params?foo=bar")
|
|
279
|
+
|
|
280
|
+
assert_match(/Params!/, response.to_s)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def test_base_uri_ignores_base_uri_for_absolute_urls
|
|
284
|
+
response = HTTP.base_uri("https://other.example.com").get(dummy.endpoint)
|
|
285
|
+
|
|
286
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def test_base_uri_chains_base_uris
|
|
290
|
+
session = HTTP.base_uri("https://example.com").base_uri("api/v1")
|
|
291
|
+
|
|
292
|
+
assert_equal "https://example.com/api/v1", session.default_options.base_uri.to_s
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def test_base_uri_works_with_other_chainable_methods
|
|
296
|
+
response = HTTP.base_uri(dummy.endpoint)
|
|
297
|
+
.headers("Accept" => "application/json")
|
|
298
|
+
.get("/")
|
|
299
|
+
|
|
300
|
+
assert_includes response.to_s, "json"
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def test_base_uri_raises_for_uri_without_scheme
|
|
304
|
+
assert_raises(HTTP::Error) { HTTP.base_uri("/users") }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def test_base_uri_derives_persistent_host_from_base_uri
|
|
308
|
+
p_client = HTTP.base_uri(dummy.endpoint).persistent
|
|
309
|
+
|
|
310
|
+
assert_predicate p_client, :persistent?
|
|
311
|
+
ensure
|
|
312
|
+
p_client&.close
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def test_base_uri_raises_when_persistent_host_not_given_and_no_base_uri
|
|
316
|
+
assert_raises(ArgumentError) { HTTP.persistent }
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# .persistent
|
|
320
|
+
|
|
321
|
+
def test_persistent_with_host_returns_http_session
|
|
322
|
+
persistent_client = HTTP.persistent dummy.endpoint
|
|
323
|
+
|
|
324
|
+
assert_kind_of HTTP::Session, persistent_client
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def test_persistent_with_host_is_persistent
|
|
328
|
+
persistent_client = HTTP.persistent dummy.endpoint
|
|
329
|
+
|
|
330
|
+
assert_predicate persistent_client, :persistent?
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def test_persistent_with_block_returns_last_expression
|
|
334
|
+
assert_equal :http, HTTP.persistent(dummy.endpoint) { :http }
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def test_persistent_with_block_auto_closes_connection
|
|
338
|
+
closed = false
|
|
339
|
+
HTTP.persistent dummy.endpoint do |session|
|
|
340
|
+
original_close = session.method(:close)
|
|
341
|
+
session.define_singleton_method(:close) do
|
|
342
|
+
closed = true
|
|
343
|
+
original_close.call
|
|
344
|
+
end
|
|
345
|
+
session.get("/")
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
assert closed, "expected close to have been called"
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def test_persistent_when_initialization_raises_handles_nil_session
|
|
352
|
+
opts = HTTP.default_options
|
|
353
|
+
|
|
354
|
+
opts.stub(:merge, ->(*) { raise "boom" }) do
|
|
355
|
+
assert_raises(RuntimeError) { HTTP.persistent(dummy.endpoint) { nil } }
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def test_persistent_with_timeout_sets_keep_alive_timeout
|
|
360
|
+
persistent_client = HTTP.persistent dummy.endpoint, timeout: 100
|
|
361
|
+
options = persistent_client.default_options
|
|
362
|
+
|
|
363
|
+
assert_equal 100, options.keep_alive_timeout
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# .timeout
|
|
367
|
+
|
|
368
|
+
def test_timeout_null_sets_timeout_class_to_null
|
|
369
|
+
client = HTTP.timeout :null
|
|
370
|
+
|
|
371
|
+
assert_equal HTTP::Timeout::Null, client.default_options.timeout_class
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def test_timeout_per_operation_sets_timeout_class
|
|
375
|
+
client = HTTP.timeout read: 123
|
|
376
|
+
|
|
377
|
+
assert_equal HTTP::Timeout::PerOperation, client.default_options.timeout_class
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def test_timeout_per_operation_sets_timeout_options
|
|
381
|
+
client = HTTP.timeout read: 123
|
|
382
|
+
|
|
383
|
+
assert_equal({ read_timeout: 123 }, client.default_options.timeout_options)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def test_timeout_per_operation_long_form_keys
|
|
387
|
+
client = HTTP.timeout read_timeout: 123
|
|
388
|
+
|
|
389
|
+
assert_equal({ read_timeout: 123 }, client.default_options.timeout_options)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def test_timeout_all_per_operation_sets_all_options
|
|
393
|
+
client = HTTP.timeout read: 1, write: 2, connect: 3
|
|
394
|
+
|
|
395
|
+
assert_equal({ read_timeout: 1, write_timeout: 2, connect_timeout: 3 }, client.default_options.timeout_options)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def test_timeout_per_operation_frozen_hash_does_not_raise
|
|
399
|
+
frozen_options = { read: 123 }.freeze
|
|
400
|
+
HTTP.timeout(frozen_options)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def test_timeout_empty_hash_raises_argument_error
|
|
404
|
+
assert_raises(ArgumentError) { HTTP.timeout({}) }
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def test_timeout_unknown_key_raises_argument_error
|
|
408
|
+
assert_raises(ArgumentError) { HTTP.timeout(timeout: 2) }
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def test_timeout_both_short_and_long_form_raises_argument_error
|
|
412
|
+
assert_raises(ArgumentError) { HTTP.timeout(read: 2, read_timeout: 2) }
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def test_timeout_non_numeric_value_raises_argument_error
|
|
416
|
+
assert_raises(ArgumentError) { HTTP.timeout(read: "2") }
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def test_timeout_string_keys_raises_argument_error
|
|
420
|
+
assert_raises(ArgumentError) { HTTP.timeout("read" => 2) }
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def test_timeout_global_as_hash_key_sets_timeout_class
|
|
424
|
+
client = HTTP.timeout global: 60
|
|
425
|
+
|
|
426
|
+
assert_equal HTTP::Timeout::Global, client.default_options.timeout_class
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def test_timeout_global_as_hash_key_sets_timeout_option
|
|
430
|
+
client = HTTP.timeout global: 60
|
|
431
|
+
|
|
432
|
+
assert_equal({ global_timeout: 60 }, client.default_options.timeout_options)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def test_timeout_global_long_form_sets_timeout_class
|
|
436
|
+
client = HTTP.timeout global_timeout: 60
|
|
437
|
+
|
|
438
|
+
assert_equal HTTP::Timeout::Global, client.default_options.timeout_class
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def test_timeout_global_long_form_sets_timeout_option
|
|
442
|
+
client = HTTP.timeout global_timeout: 60
|
|
443
|
+
|
|
444
|
+
assert_equal({ global_timeout: 60 }, client.default_options.timeout_options)
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def test_timeout_combined_global_and_per_operation_sets_timeout_class
|
|
448
|
+
client = HTTP.timeout global: 60, read: 30, write: 20, connect: 5
|
|
449
|
+
|
|
450
|
+
assert_equal HTTP::Timeout::Global, client.default_options.timeout_class
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def test_timeout_combined_global_and_per_operation_sets_all_options
|
|
454
|
+
client = HTTP.timeout global: 60, read: 30, write: 20, connect: 5
|
|
455
|
+
expected = { read_timeout: 30, write_timeout: 20, connect_timeout: 5, global_timeout: 60 }
|
|
456
|
+
|
|
457
|
+
assert_equal expected, client.default_options.timeout_options
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def test_timeout_combined_global_and_partial_per_operation_sets_timeout_class
|
|
461
|
+
client = HTTP.timeout global: 60, read: 30
|
|
462
|
+
|
|
463
|
+
assert_equal HTTP::Timeout::Global, client.default_options.timeout_class
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def test_timeout_combined_global_and_partial_per_operation_includes_both
|
|
467
|
+
client = HTTP.timeout global: 60, read: 30
|
|
468
|
+
expected = { read_timeout: 30, global_timeout: 60 }
|
|
469
|
+
|
|
470
|
+
assert_equal expected, client.default_options.timeout_options
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def test_timeout_both_short_and_long_form_of_global_raises_argument_error
|
|
474
|
+
assert_raises(ArgumentError) { HTTP.timeout(global: 60, global_timeout: 60) }
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def test_timeout_non_numeric_global_raises_argument_error
|
|
478
|
+
assert_raises(ArgumentError) { HTTP.timeout(global: "60") }
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def test_timeout_global_numeric_sets_timeout_class
|
|
482
|
+
client = HTTP.timeout 123
|
|
483
|
+
|
|
484
|
+
assert_equal HTTP::Timeout::Global, client.default_options.timeout_class
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def test_timeout_global_numeric_sets_timeout_option
|
|
488
|
+
client = HTTP.timeout 123
|
|
489
|
+
|
|
490
|
+
assert_equal({ global_timeout: 123 }, client.default_options.timeout_options)
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def test_timeout_float_global_sets_timeout_option
|
|
494
|
+
client = HTTP.timeout 2.5
|
|
495
|
+
|
|
496
|
+
assert_equal({ global_timeout: 2.5 }, client.default_options.timeout_options)
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def test_timeout_unsupported_options_raises_argument_error
|
|
500
|
+
assert_raises(ArgumentError) { HTTP.timeout("invalid") }
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
# .cookies
|
|
504
|
+
|
|
505
|
+
def test_cookies_passes_correct_cookie_header
|
|
506
|
+
endpoint = "#{dummy.endpoint}/cookies"
|
|
507
|
+
|
|
508
|
+
assert_equal "abc: def", HTTP.cookies(abc: :def).get(endpoint).to_s
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def test_cookies_properly_works_with_cookies_from_response
|
|
512
|
+
endpoint = "#{dummy.endpoint}/cookies"
|
|
513
|
+
res = HTTP.get(endpoint).flush
|
|
514
|
+
|
|
515
|
+
assert_equal "foo: bar", HTTP.cookies(res.cookies).get(endpoint).to_s
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def test_cookies_replaces_previously_set_cookies
|
|
519
|
+
endpoint = "#{dummy.endpoint}/cookies"
|
|
520
|
+
client = HTTP.cookies(foo: 123, bar: 321).cookies(baz: :moo)
|
|
521
|
+
|
|
522
|
+
assert_equal "baz: moo", client.get(endpoint).to_s
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# .nodelay
|
|
526
|
+
|
|
527
|
+
def test_nodelay_sets_tcp_nodelay_on_underlying_socket
|
|
528
|
+
socket_spy_class = Class.new(TCPSocket) do
|
|
529
|
+
def self.setsockopt_calls
|
|
530
|
+
@setsockopt_calls ||= []
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def setsockopt(*args)
|
|
534
|
+
self.class.setsockopt_calls << args
|
|
535
|
+
super
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
HTTP.default_options = { socket_class: socket_spy_class }
|
|
540
|
+
|
|
541
|
+
HTTP.get(dummy.endpoint)
|
|
542
|
+
|
|
543
|
+
assert_equal [], socket_spy_class.setsockopt_calls
|
|
544
|
+
HTTP.nodelay.get(dummy.endpoint)
|
|
545
|
+
|
|
546
|
+
assert_equal [[Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1]], socket_spy_class.setsockopt_calls
|
|
547
|
+
ensure
|
|
548
|
+
HTTP.default_options = {}
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# .use
|
|
552
|
+
|
|
553
|
+
def test_use_turns_on_given_feature
|
|
554
|
+
client = HTTP.use :auto_deflate
|
|
555
|
+
|
|
556
|
+
assert_equal [:auto_deflate], client.default_options.features.keys
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
def test_use_auto_deflate_sends_gzipped_body
|
|
560
|
+
client = HTTP.use :auto_deflate
|
|
561
|
+
body = "Hello!"
|
|
562
|
+
response = client.post("#{dummy.endpoint}/echo-body", body: body)
|
|
563
|
+
encoded = response.to_s
|
|
564
|
+
|
|
565
|
+
assert_equal body, Zlib::GzipReader.new(StringIO.new(encoded)).read
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def test_use_auto_deflate_sends_deflated_body
|
|
569
|
+
client = HTTP.use auto_deflate: { method: "deflate" }
|
|
570
|
+
body = "Hello!"
|
|
571
|
+
response = client.post("#{dummy.endpoint}/echo-body", body: body)
|
|
572
|
+
encoded = response.to_s
|
|
573
|
+
|
|
574
|
+
assert_equal body, Zlib::Inflate.inflate(encoded)
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def test_use_auto_inflate_returns_raw_body_when_content_encoding_missing
|
|
578
|
+
client = HTTP.use :auto_inflate
|
|
579
|
+
body = "Hello!"
|
|
580
|
+
response = client.post("#{dummy.endpoint}/encoded-body", body: body)
|
|
581
|
+
|
|
582
|
+
assert_equal "#{body}-raw", response.to_s
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
def test_use_auto_inflate_returns_decoded_body
|
|
586
|
+
client = HTTP.use(:auto_inflate).headers("Accept-Encoding" => "gzip")
|
|
587
|
+
body = "Hello!"
|
|
588
|
+
response = client.post("#{dummy.endpoint}/encoded-body", body: body)
|
|
589
|
+
|
|
590
|
+
assert_equal "#{body}-gzipped", response.to_s
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
def test_use_auto_inflate_returns_deflated_body
|
|
594
|
+
client = HTTP.use(:auto_inflate).headers("Accept-Encoding" => "deflate")
|
|
595
|
+
body = "Hello!"
|
|
596
|
+
response = client.post("#{dummy.endpoint}/encoded-body", body: body)
|
|
597
|
+
|
|
598
|
+
assert_equal "#{body}-deflated", response.to_s
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def test_use_auto_inflate_returns_empty_body_for_204_with_gzip
|
|
602
|
+
client = HTTP.use(:auto_inflate).headers("Accept-Encoding" => "gzip")
|
|
603
|
+
body = "Hello!"
|
|
604
|
+
response = client.post("#{dummy.endpoint}/no-content-204", body: body)
|
|
605
|
+
|
|
606
|
+
assert_equal "", response.to_s
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def test_use_auto_inflate_returns_empty_body_for_204_with_deflate
|
|
610
|
+
client = HTTP.use(:auto_inflate).headers("Accept-Encoding" => "deflate")
|
|
611
|
+
body = "Hello!"
|
|
612
|
+
response = client.post("#{dummy.endpoint}/no-content-204", body: body)
|
|
613
|
+
|
|
614
|
+
assert_equal "", response.to_s
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def test_use_normalize_uri_normalizes_uri
|
|
618
|
+
response = HTTP.get "#{dummy.endpoint}/héllö-wörld"
|
|
619
|
+
|
|
620
|
+
assert_equal "hello world", response.to_s
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def test_use_normalize_uri_uses_custom_normalizer
|
|
624
|
+
client = HTTP.use(normalize_uri: { normalizer: :itself.to_proc })
|
|
625
|
+
response = client.get("#{dummy.endpoint}/héllö-wörld")
|
|
626
|
+
|
|
627
|
+
assert_equal 400, response.status.to_i
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
def test_use_normalize_uri_raises_if_custom_normalizer_returns_invalid_path
|
|
631
|
+
client = HTTP.use(normalize_uri: { normalizer: :itself.to_proc })
|
|
632
|
+
err = assert_raises(HTTP::RequestError) { client.get("#{dummy.endpoint}/hello\nworld") }
|
|
633
|
+
assert_equal 'Invalid request URI: "/hello\nworld"', err.message
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def test_use_normalize_uri_raises_if_custom_normalizer_returns_invalid_host
|
|
637
|
+
normalizer = lambda do |uri|
|
|
638
|
+
uri.instance_variable_set(:@host, "example\ncom")
|
|
639
|
+
uri
|
|
640
|
+
end
|
|
641
|
+
client = HTTP.use(normalize_uri: { normalizer: normalizer })
|
|
642
|
+
err = assert_raises(HTTP::RequestError) { client.get(dummy.endpoint) }
|
|
643
|
+
assert_match(/Invalid host: "example\\ncom/, err.message)
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
def test_use_normalize_uri_uses_default_normalizer
|
|
647
|
+
client = HTTP.use :normalize_uri
|
|
648
|
+
response = client.get("#{dummy.endpoint}/héllö-wörld")
|
|
649
|
+
|
|
650
|
+
assert_equal "hello world", response.to_s
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# dynamic verb tests
|
|
654
|
+
|
|
655
|
+
%i[put delete trace options connect patch].each do |verb|
|
|
656
|
+
define_method :"test_#{verb}_delegates_to_request" do
|
|
657
|
+
mock_client = Minitest::Mock.new
|
|
658
|
+
mock_client.expect(:request, nil, [verb, "http://example.com/"])
|
|
659
|
+
HTTP::Client.stub(:new, mock_client) do
|
|
660
|
+
HTTP.public_send(verb, "http://example.com/")
|
|
661
|
+
end
|
|
662
|
+
mock_client.verify
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# Request::Builder
|
|
667
|
+
|
|
668
|
+
def test_request_builder_builds_http_request_from_options
|
|
669
|
+
options = HTTP::Options.new
|
|
670
|
+
builder = HTTP::Request::Builder.new(options)
|
|
671
|
+
req = builder.build(:get, "http://example.com/")
|
|
672
|
+
|
|
673
|
+
assert_kind_of HTTP::Request, req
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
# .encoding
|
|
677
|
+
|
|
678
|
+
def test_encoding_returns_session_with_specified_encoding
|
|
679
|
+
session = HTTP::Client.new.encoding("UTF-8")
|
|
680
|
+
|
|
681
|
+
assert_kind_of HTTP::Session, session
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
# .via - proxy_headers tests (no actual proxy server needed)
|
|
685
|
+
|
|
686
|
+
def test_via_with_proxy_headers_as_third_argument
|
|
687
|
+
client = HTTP.via("proxy.example.com", 8080, { "X-Custom" => "val" })
|
|
688
|
+
proxy = client.default_options.proxy
|
|
689
|
+
|
|
690
|
+
assert_equal({ "X-Custom" => "val" }, proxy[:proxy_headers])
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
def test_via_with_proxy_headers_as_fifth_argument
|
|
694
|
+
hdrs = { "X-Custom" => "val" }
|
|
695
|
+
client = HTTP.via("proxy.example.com", 8080, "user", "pass", hdrs)
|
|
696
|
+
proxy = client.default_options.proxy
|
|
697
|
+
|
|
698
|
+
assert_equal({ "X-Custom" => "val" }, proxy[:proxy_headers])
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
def test_via_with_non_string_first_argument_skips_proxy_address
|
|
702
|
+
client = HTTP.via(nil, 8080, { "X-Custom" => "val" })
|
|
703
|
+
proxy = client.default_options.proxy
|
|
704
|
+
|
|
705
|
+
refute proxy.key?(:proxy_address)
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
# socket error unification
|
|
709
|
+
|
|
710
|
+
def test_unifies_socket_errors_into_http_connection_error
|
|
711
|
+
original_open = TCPSocket.method(:open)
|
|
712
|
+
stub_open = lambda do |*args|
|
|
713
|
+
raise SocketError if args[0] == "thishostshouldnotexists.com"
|
|
714
|
+
|
|
715
|
+
original_open.call(*args)
|
|
716
|
+
end
|
|
717
|
+
TCPSocket.stub(:open, stub_open) do
|
|
718
|
+
assert_raises(HTTP::ConnectionError) { HTTP.get "http://thishostshouldnotexists.com" }
|
|
719
|
+
assert_raises(HTTP::ConnectionError) { HTTP.get "http://127.0.0.1:111" }
|
|
720
|
+
end
|
|
721
|
+
end
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
class HTTPViaAnonymousProxyTest < Minitest::Test
|
|
725
|
+
run_server(:dummy) { DummyServer.new }
|
|
726
|
+
run_server(:dummy_ssl) { DummyServer.new(ssl: true) }
|
|
727
|
+
run_server(:proxy) { ProxyServer.new }
|
|
728
|
+
|
|
729
|
+
def ssl_client
|
|
730
|
+
HTTP::Client.new ssl_context: SSLHelper.client_context
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
def test_anonymous_proxy_proxies_the_request
|
|
734
|
+
response = HTTP.via(proxy.addr, proxy.port).get dummy.endpoint
|
|
735
|
+
|
|
736
|
+
assert_equal "true", response.headers["X-Proxied"]
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
def test_anonymous_proxy_responds_with_endpoint_body
|
|
740
|
+
response = HTTP.via(proxy.addr, proxy.port).get dummy.endpoint
|
|
741
|
+
|
|
742
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
def test_anonymous_proxy_raises_argument_error_if_no_port_given
|
|
746
|
+
assert_raises(HTTP::RequestError) { HTTP.via(proxy.addr) }
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
def test_anonymous_proxy_ignores_credentials
|
|
750
|
+
response = HTTP.via(proxy.addr, proxy.port, "username", "password").get dummy.endpoint
|
|
751
|
+
|
|
752
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
def test_anonymous_proxy_ssl_responds_with_endpoint_body
|
|
756
|
+
response = ssl_client.via(proxy.addr, proxy.port).get dummy_ssl.endpoint
|
|
757
|
+
|
|
758
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
def test_anonymous_proxy_ssl_ignores_credentials
|
|
762
|
+
response = ssl_client.via(proxy.addr, proxy.port, "username", "password").get dummy_ssl.endpoint
|
|
763
|
+
|
|
764
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
class HTTPViaAuthProxyTest < Minitest::Test
|
|
769
|
+
run_server(:dummy) { DummyServer.new }
|
|
770
|
+
run_server(:dummy_ssl) { DummyServer.new(ssl: true) }
|
|
771
|
+
run_server(:proxy) { AuthProxyServer.new }
|
|
772
|
+
|
|
773
|
+
def ssl_client
|
|
774
|
+
HTTP::Client.new ssl_context: SSLHelper.client_context
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
def test_auth_proxy_proxies_the_request
|
|
778
|
+
response = HTTP.via(proxy.addr, proxy.port, "username", "password").get dummy.endpoint
|
|
779
|
+
|
|
780
|
+
assert_equal "true", response.headers["X-Proxied"]
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
def test_auth_proxy_responds_with_endpoint_body
|
|
784
|
+
response = HTTP.via(proxy.addr, proxy.port, "username", "password").get dummy.endpoint
|
|
785
|
+
|
|
786
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
def test_auth_proxy_responds_with_407_when_wrong_credentials
|
|
790
|
+
response = HTTP.via(proxy.addr, proxy.port, "user", "pass").get dummy.endpoint
|
|
791
|
+
|
|
792
|
+
assert_equal 407, response.status.to_i
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
def test_auth_proxy_responds_with_407_if_no_credentials
|
|
796
|
+
response = HTTP.via(proxy.addr, proxy.port).get dummy.endpoint
|
|
797
|
+
|
|
798
|
+
assert_equal 407, response.status.to_i
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
def test_auth_proxy_ssl_responds_with_endpoint_body
|
|
802
|
+
response = ssl_client.via(proxy.addr, proxy.port, "username", "password").get dummy_ssl.endpoint
|
|
803
|
+
|
|
804
|
+
assert_match(/<!doctype html>/, response.to_s)
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
def test_auth_proxy_ssl_responds_with_407_when_wrong_credentials
|
|
808
|
+
response = ssl_client.via(proxy.addr, proxy.port, "user", "pass").get dummy_ssl.endpoint
|
|
809
|
+
|
|
810
|
+
assert_equal 407, response.status.to_i
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
def test_auth_proxy_ssl_responds_with_407_if_no_credentials
|
|
814
|
+
response = ssl_client.via(proxy.addr, proxy.port).get dummy_ssl.endpoint
|
|
815
|
+
|
|
816
|
+
assert_equal 407, response.status.to_i
|
|
817
|
+
end
|
|
818
|
+
end
|