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
|
@@ -0,0 +1,1140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require "addressable/uri"
|
|
5
|
+
|
|
6
|
+
class HTTPURITest < Minitest::Test
|
|
7
|
+
cover "HTTP::URI*"
|
|
8
|
+
|
|
9
|
+
def example_ipv6_address
|
|
10
|
+
"2606:2800:220:1:248:1893:25c8:1946"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def example_http_uri_string
|
|
14
|
+
"http://example.com"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def example_https_uri_string
|
|
18
|
+
"https://example.com"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def example_ipv6_uri_string
|
|
22
|
+
"https://[#{example_ipv6_address}]"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def http_uri
|
|
26
|
+
@http_uri ||= HTTP::URI.parse(example_http_uri_string)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def https_uri
|
|
30
|
+
@https_uri ||= HTTP::URI.parse(example_https_uri_string)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def ipv6_uri
|
|
34
|
+
@ipv6_uri ||= HTTP::URI.parse(example_ipv6_uri_string)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_knows_uri_schemes
|
|
38
|
+
assert_equal "http", http_uri.scheme
|
|
39
|
+
assert_equal "https", https_uri.scheme
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_sets_default_ports_for_http_uris
|
|
43
|
+
assert_equal 80, http_uri.port
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_sets_default_ports_for_https_uris
|
|
47
|
+
assert_equal 443, https_uri.port
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_host_strips_brackets_from_ipv6_addresses
|
|
51
|
+
assert_equal "2606:2800:220:1:248:1893:25c8:1946", ipv6_uri.host
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_normalized_host_strips_brackets_from_ipv6_addresses
|
|
55
|
+
assert_equal "2606:2800:220:1:248:1893:25c8:1946", ipv6_uri.normalized_host
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_inspect_returns_a_human_readable_representation
|
|
59
|
+
assert_match(%r{#<HTTP::URI:0x\h+ URI:http://example\.com>}, http_uri.inspect)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_host_assignment_updates_cached_values_for_host_and_normalized_host
|
|
63
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
64
|
+
|
|
65
|
+
assert_equal "example.com", uri.host
|
|
66
|
+
assert_equal "example.com", uri.normalized_host
|
|
67
|
+
|
|
68
|
+
uri.host = "[#{example_ipv6_address}]"
|
|
69
|
+
|
|
70
|
+
assert_equal example_ipv6_address, uri.host
|
|
71
|
+
assert_equal example_ipv6_address, uri.normalized_host
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_host_assignment_ensures_ipv6_addresses_are_bracketed_in_the_raw_host
|
|
75
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
76
|
+
|
|
77
|
+
assert_equal "example.com", uri.host
|
|
78
|
+
assert_equal "example.com", uri.normalized_host
|
|
79
|
+
|
|
80
|
+
uri.host = example_ipv6_address
|
|
81
|
+
|
|
82
|
+
assert_equal example_ipv6_address, uri.host
|
|
83
|
+
assert_equal example_ipv6_address, uri.normalized_host
|
|
84
|
+
assert_equal "[#{example_ipv6_address}]", uri.instance_variable_get(:@raw_host)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_form_encode_encodes_key_value_pairs
|
|
88
|
+
assert_equal "foo=bar&baz=quux", HTTP::URI.form_encode({ foo: "bar", baz: "quux" })
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_initialize_raises_argument_error_for_positional_argument
|
|
92
|
+
assert_raises(ArgumentError) { HTTP::URI.new(42) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_http_predicate_returns_true_for_http_uris
|
|
96
|
+
assert_predicate http_uri, :http?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_http_predicate_returns_false_for_https_uris
|
|
100
|
+
refute_predicate https_uri, :http?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def test_eql_returns_true_for_equivalent_uris
|
|
104
|
+
assert http_uri.eql?(HTTP::URI.parse(example_http_uri_string))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_eql_returns_false_for_non_uri_objects
|
|
108
|
+
refute http_uri.eql?("http://example.com")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def test_hash_returns_an_integer
|
|
112
|
+
assert_kind_of Integer, http_uri.hash
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def test_dup_doesnt_share_internal_value_between_duplicates
|
|
116
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
117
|
+
duplicated_uri = uri.dup
|
|
118
|
+
duplicated_uri.host = "example.org"
|
|
119
|
+
|
|
120
|
+
assert_equal "http://example.org", duplicated_uri.to_s
|
|
121
|
+
assert_equal "http://example.com", uri.to_s
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_dup_returns_an_http_uri_instance
|
|
125
|
+
assert_instance_of HTTP::URI, http_uri.dup
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_dup_preserves_all_uri_components
|
|
129
|
+
uri = HTTP::URI.parse("http://user:pass@example.com:8080/path?q=1#frag")
|
|
130
|
+
duped = uri.dup
|
|
131
|
+
|
|
132
|
+
assert_equal "http", duped.scheme
|
|
133
|
+
assert_equal "user", duped.user
|
|
134
|
+
assert_equal "pass", duped.password
|
|
135
|
+
assert_equal "example.com", duped.host
|
|
136
|
+
assert_equal 8080, duped.port
|
|
137
|
+
assert_equal "/path", duped.path
|
|
138
|
+
assert_equal "q=1", duped.query
|
|
139
|
+
assert_equal "frag", duped.fragment
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def test_dup_preserves_ipv6_host_with_brackets
|
|
143
|
+
duped = ipv6_uri.dup
|
|
144
|
+
|
|
145
|
+
assert_equal example_ipv6_address, duped.host
|
|
146
|
+
assert_equal "https://[#{example_ipv6_address}]", duped.to_s
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_parse_returns_the_same_object_when_given_an_http_uri
|
|
150
|
+
assert_same http_uri, HTTP::URI.parse(http_uri)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def test_parse_returns_a_new_http_uri_when_given_a_string
|
|
154
|
+
result = HTTP::URI.parse("http://example.com")
|
|
155
|
+
|
|
156
|
+
assert_instance_of HTTP::URI, result
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_parse_returns_the_same_object_when_given_a_uri_subclass_instance
|
|
160
|
+
subclass = Class.new(HTTP::URI)
|
|
161
|
+
sub_uri = subclass.new(scheme: "http", host: "example.com")
|
|
162
|
+
# is_a?(self) returns true for subclasses; instance_of? does not
|
|
163
|
+
assert_same sub_uri, HTTP::URI.parse(sub_uri)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_parse_raises_invalid_error_for_nil
|
|
167
|
+
err = assert_raises(HTTP::URI::InvalidError) do
|
|
168
|
+
HTTP::URI.parse(nil)
|
|
169
|
+
end
|
|
170
|
+
assert_equal "invalid URI: nil", err.message
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_parse_raises_invalid_error_for_malformed_uri
|
|
174
|
+
err = assert_raises(HTTP::URI::InvalidError) do
|
|
175
|
+
HTTP::URI.parse(":")
|
|
176
|
+
end
|
|
177
|
+
assert_equal 'invalid URI: ":"', err.message
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def test_form_encode_sorts_key_value_pairs_when_sort_is_true
|
|
181
|
+
unsorted = HTTP::URI.form_encode([[:z, 1], [:a, 2]])
|
|
182
|
+
sorted = HTTP::URI.form_encode([[:z, 1], [:a, 2]], sort: true)
|
|
183
|
+
|
|
184
|
+
assert_equal "z=1&a=2", unsorted
|
|
185
|
+
assert_equal "a=2&z=1", sorted
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def test_form_encode_encodes_newlines_as_percent_0a
|
|
189
|
+
assert_equal "text=hello%0Aworld", HTTP::URI.form_encode({ text: "hello\nworld" })
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_form_encode_sorts_by_string_representation_of_keys
|
|
193
|
+
result = HTTP::URI.form_encode([[2, "b"], [10, "a"]], sort: true)
|
|
194
|
+
|
|
195
|
+
assert_equal "10=a&2=b", result
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def test_percent_encode_returns_nil_when_given_nil
|
|
199
|
+
assert_nil HTTP::URI.send(:percent_encode, nil)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def test_percent_encode_returns_the_same_string_when_no_encoding_is_needed
|
|
203
|
+
assert_equal "hello", HTTP::URI.send(:percent_encode, "hello")
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_percent_encode_encodes_non_ascii_characters_as_percent_encoded_utf8_bytes
|
|
207
|
+
assert_equal "h%C3%A9llo", HTTP::URI.send(:percent_encode, "héllo")
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def test_percent_encode_encodes_multi_byte_characters_into_multiple_percent_encoded_sequences
|
|
211
|
+
# U+1F600 (grinning face) is 4 bytes in UTF-8: F0 9F 98 80
|
|
212
|
+
result = HTTP::URI.send(:percent_encode, "\u{1F600}")
|
|
213
|
+
|
|
214
|
+
assert_equal "%F0%9F%98%80", result
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def test_percent_encode_encodes_spaces_as_percent_20
|
|
218
|
+
assert_equal "hello%20world", HTTP::URI.send(:percent_encode, "hello world")
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def test_percent_encode_does_not_encode_printable_ascii_characters
|
|
222
|
+
printable = (0x21..0x7E).map(&:chr).join
|
|
223
|
+
|
|
224
|
+
assert_equal printable, HTTP::URI.send(:percent_encode, printable)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def test_percent_encode_uses_uppercase_hex_digits
|
|
228
|
+
result = HTTP::URI.send(:percent_encode, "\xFF".b.encode(Encoding::UTF_8, Encoding::ISO_8859_1))
|
|
229
|
+
|
|
230
|
+
assert_equal "%C3%BF", result
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def remove_dot_segments(path)
|
|
234
|
+
HTTP::URI.send(:remove_dot_segments, path)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def test_remove_dot_segments_resolves_parent_directory_references
|
|
238
|
+
assert_equal "/a/c", remove_dot_segments("/a/b/../c")
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def test_remove_dot_segments_removes_current_directory_references
|
|
242
|
+
assert_equal "/a/b/c", remove_dot_segments("/a/./b/c")
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def test_remove_dot_segments_resolves_multiple_parent_references
|
|
246
|
+
assert_equal "/c", remove_dot_segments("/a/b/../../c")
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def test_remove_dot_segments_clamps_parent_references_above_root
|
|
250
|
+
assert_equal "/a", remove_dot_segments("/../a")
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def test_remove_dot_segments_preserves_paths_without_dot_segments
|
|
254
|
+
assert_equal "/a/b/c", remove_dot_segments("/a/b/c")
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def test_remove_dot_segments_preserves_trailing_slash_after_parent_reference
|
|
258
|
+
assert_equal "/a/", remove_dot_segments("/a/b/..")
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def test_remove_dot_segments_resolves_current_directory_at_end_of_path
|
|
262
|
+
assert_equal "/a/b/", remove_dot_segments("/a/b/.")
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def test_remove_dot_segments_handles_standalone_dot
|
|
266
|
+
assert_equal "", remove_dot_segments(".")
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def test_remove_dot_segments_handles_standalone_dot_dot
|
|
270
|
+
assert_equal "", remove_dot_segments("..")
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def test_remove_dot_segments_handles_leading_dot_slash_prefix
|
|
274
|
+
assert_equal "a", remove_dot_segments("./a")
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def test_remove_dot_segments_handles_leading_dot_dot_slash_prefix
|
|
278
|
+
assert_equal "a", remove_dot_segments("../a")
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def test_remove_dot_segments_handles_empty_path
|
|
282
|
+
assert_equal "", remove_dot_segments("")
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def test_remove_dot_segments_pops_empty_segment_when_dot_dot_follows_double_slash
|
|
286
|
+
assert_equal "/", remove_dot_segments("//..")
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def test_normalizer_normalizes_an_empty_path_to_slash
|
|
290
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com")
|
|
291
|
+
|
|
292
|
+
assert_equal "/", result.path
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def test_normalizer_preserves_non_empty_paths
|
|
296
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com/foo/bar")
|
|
297
|
+
|
|
298
|
+
assert_equal "/foo/bar", result.path
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def test_normalizer_removes_dot_segments_from_paths
|
|
302
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com/a/b/../c")
|
|
303
|
+
|
|
304
|
+
assert_equal "/a/c", result.path
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def test_normalizer_percent_encodes_non_ascii_characters_in_paths
|
|
308
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com/p\u00E4th")
|
|
309
|
+
|
|
310
|
+
assert_includes result.path, "%"
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def test_normalizer_percent_encodes_non_ascii_characters_in_query_strings
|
|
314
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com/?q=v\u00E4lue")
|
|
315
|
+
|
|
316
|
+
assert_includes result.query, "%"
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def test_normalizer_returns_an_http_uri_instance
|
|
320
|
+
assert_instance_of HTTP::URI, HTTP::URI::NORMALIZER.call("http://example.com/path")
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def test_normalizer_lowercases_the_scheme
|
|
324
|
+
result = HTTP::URI::NORMALIZER.call("HTTP://example.com")
|
|
325
|
+
|
|
326
|
+
assert_equal "http", result.scheme
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def test_normalizer_lowercases_the_host
|
|
330
|
+
result = HTTP::URI::NORMALIZER.call("http://EXAMPLE.COM")
|
|
331
|
+
|
|
332
|
+
assert_equal "example.com", result.host
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def test_normalizer_omits_default_http_port
|
|
336
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com:80/path")
|
|
337
|
+
|
|
338
|
+
assert_equal "http://example.com/path", result.to_s
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def test_normalizer_omits_default_https_port
|
|
342
|
+
result = HTTP::URI::NORMALIZER.call("https://example.com:443/path")
|
|
343
|
+
|
|
344
|
+
assert_equal "https://example.com/path", result.to_s
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def test_normalizer_preserves_non_default_port
|
|
348
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com:8080/path")
|
|
349
|
+
|
|
350
|
+
assert_equal "http://example.com:8080/path", result.to_s
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def test_normalizer_preserves_ipv6_host
|
|
354
|
+
result = HTTP::URI::NORMALIZER.call("http://[::1]:8080/path")
|
|
355
|
+
|
|
356
|
+
assert_equal "http://[::1]:8080/path", result.to_s
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def test_normalizer_preserves_user_info
|
|
360
|
+
result = HTTP::URI::NORMALIZER.call("http://user:pass@example.com/path")
|
|
361
|
+
|
|
362
|
+
assert_equal "user", result.user
|
|
363
|
+
assert_equal "pass", result.password
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def test_normalizer_preserves_fragment
|
|
367
|
+
result = HTTP::URI::NORMALIZER.call("http://example.com/path#frag")
|
|
368
|
+
|
|
369
|
+
assert_equal "frag", result.fragment
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def test_equality_returns_false_when_compared_to_a_non_uri_object
|
|
373
|
+
refute_equal "http://example.com", http_uri
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def test_equality_returns_true_for_uris_that_normalize_to_the_same_form
|
|
377
|
+
uri1 = HTTP::URI.parse("HTTP://EXAMPLE.COM")
|
|
378
|
+
uri2 = HTTP::URI.parse("http://example.com")
|
|
379
|
+
|
|
380
|
+
assert_equal uri1, uri2
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def test_equality_returns_false_for_uris_that_normalize_differently
|
|
384
|
+
uri1 = HTTP::URI.parse("http://example.com/a")
|
|
385
|
+
uri2 = HTTP::URI.parse("http://example.com/b")
|
|
386
|
+
|
|
387
|
+
refute_equal uri1, uri2
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def test_equality_returns_true_when_compared_to_a_uri_subclass_instance
|
|
391
|
+
subclass = Class.new(HTTP::URI)
|
|
392
|
+
sub_uri = subclass.new(scheme: "http", host: "example.com")
|
|
393
|
+
|
|
394
|
+
assert_equal http_uri, sub_uri
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def test_eql_returns_false_for_uris_with_different_string_representations
|
|
398
|
+
uri1 = HTTP::URI.parse("http://example.com")
|
|
399
|
+
uri2 = HTTP::URI.parse("http://example.com/")
|
|
400
|
+
|
|
401
|
+
refute uri1.eql?(uri2)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def test_eql_returns_true_for_a_uri_subclass_instance_with_same_string
|
|
405
|
+
subclass = Class.new(HTTP::URI)
|
|
406
|
+
sub_uri = subclass.new(scheme: "http", host: "example.com")
|
|
407
|
+
|
|
408
|
+
assert http_uri.eql?(sub_uri)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def test_hash_returns_the_same_value_on_repeated_calls
|
|
412
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
413
|
+
first = uri.hash
|
|
414
|
+
second = uri.hash
|
|
415
|
+
|
|
416
|
+
assert_equal first, second
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def test_hash_returns_a_composite_hash_of_class_and_string_representation
|
|
420
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
421
|
+
|
|
422
|
+
assert_equal [HTTP::URI, uri.to_s].hash, uri.hash
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def test_port_returns_the_explicit_port_when_one_is_set
|
|
426
|
+
uri = HTTP::URI.parse("http://example.com:8080")
|
|
427
|
+
|
|
428
|
+
assert_equal 8080, uri.port
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def test_origin_returns_scheme_and_host_for_http_uris
|
|
432
|
+
assert_equal "http://example.com", http_uri.origin
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def test_origin_returns_scheme_and_host_for_https_uris
|
|
436
|
+
assert_equal "https://example.com", https_uri.origin
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def test_origin_includes_non_default_port
|
|
440
|
+
uri = HTTP::URI.parse("http://example.com:8080")
|
|
441
|
+
|
|
442
|
+
assert_equal "http://example.com:8080", uri.origin
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def test_origin_omits_default_http_port
|
|
446
|
+
uri = HTTP::URI.parse("http://example.com:80")
|
|
447
|
+
|
|
448
|
+
assert_equal "http://example.com", uri.origin
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def test_origin_omits_default_https_port
|
|
452
|
+
uri = HTTP::URI.parse("https://example.com:443")
|
|
453
|
+
|
|
454
|
+
assert_equal "https://example.com", uri.origin
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def test_origin_normalizes_scheme_to_lowercase
|
|
458
|
+
uri = HTTP::URI.parse("HTTP://example.com")
|
|
459
|
+
|
|
460
|
+
assert_equal "http://example.com", uri.origin
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def test_origin_normalizes_host_to_lowercase
|
|
464
|
+
uri = HTTP::URI.parse("http://EXAMPLE.COM")
|
|
465
|
+
|
|
466
|
+
assert_equal "http://example.com", uri.origin
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def test_origin_preserves_ipv6_brackets
|
|
470
|
+
assert_equal "https://[2606:2800:220:1:248:1893:25c8:1946]", ipv6_uri.origin
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def test_origin_excludes_user_info
|
|
474
|
+
uri = HTTP::URI.parse("http://user:pass@example.com")
|
|
475
|
+
|
|
476
|
+
assert_equal "http://example.com", uri.origin
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def test_origin_handles_uri_with_no_scheme
|
|
480
|
+
uri = HTTP::URI.new(host: "example.com")
|
|
481
|
+
|
|
482
|
+
assert_equal "://example.com", uri.origin
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def test_origin_handles_uri_with_no_host
|
|
486
|
+
uri = HTTP::URI.new(path: "/foo")
|
|
487
|
+
|
|
488
|
+
assert_equal "://", uri.origin
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def test_request_uri_returns_path_for_a_simple_uri
|
|
492
|
+
uri = HTTP::URI.parse("http://example.com/path")
|
|
493
|
+
|
|
494
|
+
assert_equal "/path", uri.request_uri
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def test_request_uri_returns_path_and_query
|
|
498
|
+
uri = HTTP::URI.parse("http://example.com/path?q=1")
|
|
499
|
+
|
|
500
|
+
assert_equal "/path?q=1", uri.request_uri
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def test_request_uri_returns_slash_for_empty_path
|
|
504
|
+
assert_equal "/", http_uri.request_uri
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def test_request_uri_returns_slash_with_query_for_empty_path
|
|
508
|
+
uri = HTTP::URI.parse("http://example.com?q=1")
|
|
509
|
+
|
|
510
|
+
assert_equal "/?q=1", uri.request_uri
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def test_request_uri_preserves_trailing_question_mark_with_empty_query
|
|
514
|
+
uri = HTTP::URI.parse("http://example.com/path?")
|
|
515
|
+
|
|
516
|
+
assert_equal "/path?", uri.request_uri
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def test_omit_returns_an_http_uri_instance
|
|
520
|
+
full_uri = HTTP::URI.parse("http://user:pass@example.com:8080/path?q=1#frag")
|
|
521
|
+
|
|
522
|
+
assert_instance_of HTTP::URI, full_uri.omit(:fragment)
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def test_omit_removes_the_fragment_component
|
|
526
|
+
full_uri = HTTP::URI.parse("http://user:pass@example.com:8080/path?q=1#frag")
|
|
527
|
+
|
|
528
|
+
assert_nil full_uri.omit(:fragment).fragment
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def test_omit_removes_multiple_components
|
|
532
|
+
full_uri = HTTP::URI.parse("http://user:pass@example.com:8080/path?q=1#frag")
|
|
533
|
+
result = full_uri.omit(:query, :fragment)
|
|
534
|
+
|
|
535
|
+
assert_nil result.query
|
|
536
|
+
assert_nil result.fragment
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def test_omit_preserves_all_other_components_when_omitting_fragment
|
|
540
|
+
full_uri = HTTP::URI.parse("http://user:pass@example.com:8080/path?q=1#frag")
|
|
541
|
+
result = full_uri.omit(:fragment)
|
|
542
|
+
|
|
543
|
+
assert_equal "http", result.scheme
|
|
544
|
+
assert_equal "user", result.user
|
|
545
|
+
assert_equal "pass", result.password
|
|
546
|
+
assert_equal "example.com", result.host
|
|
547
|
+
assert_equal 8080, result.port
|
|
548
|
+
assert_equal "/path", result.path
|
|
549
|
+
assert_equal "q=1", result.query
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def test_omit_does_not_add_default_port_when_omitting_components
|
|
553
|
+
uri = HTTP::URI.parse("http://example.com/path#frag")
|
|
554
|
+
|
|
555
|
+
assert_equal "http://example.com/path", uri.omit(:fragment).to_s
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
def test_omit_preserves_ipv6_host_when_omitting_components
|
|
559
|
+
uri = HTTP::URI.parse("https://[::1]:8080/path#frag")
|
|
560
|
+
|
|
561
|
+
assert_equal "https://[::1]:8080/path", uri.omit(:fragment).to_s
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
def test_omit_returns_unchanged_uri_when_no_components_given
|
|
565
|
+
full_uri = HTTP::URI.parse("http://user:pass@example.com:8080/path?q=1#frag")
|
|
566
|
+
|
|
567
|
+
assert_equal full_uri.to_s, full_uri.omit.to_s
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def test_join_resolves_a_relative_path
|
|
571
|
+
result = HTTP::URI.parse("http://example.com/foo/").join("bar")
|
|
572
|
+
|
|
573
|
+
assert_equal "http://example.com/foo/bar", result.to_s
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def test_join_resolves_an_absolute_path
|
|
577
|
+
result = HTTP::URI.parse("http://example.com/foo").join("/bar")
|
|
578
|
+
|
|
579
|
+
assert_equal "http://example.com/bar", result.to_s
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def test_join_resolves_a_full_uri
|
|
583
|
+
result = HTTP::URI.parse("http://example.com/foo").join("http://other.com/bar")
|
|
584
|
+
|
|
585
|
+
assert_equal "http://other.com/bar", result.to_s
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def test_join_returns_an_http_uri_instance
|
|
589
|
+
result = HTTP::URI.parse("http://example.com/foo/").join("bar")
|
|
590
|
+
|
|
591
|
+
assert_instance_of HTTP::URI, result
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def test_join_accepts_an_http_uri_as_argument
|
|
595
|
+
other = HTTP::URI.parse("http://other.com/bar")
|
|
596
|
+
result = HTTP::URI.parse("http://example.com/foo").join(other)
|
|
597
|
+
|
|
598
|
+
assert_equal "http://other.com/bar", result.to_s
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def test_join_percent_encodes_non_ascii_characters_in_the_base_uri
|
|
602
|
+
result = HTTP::URI.parse("http://example.com/K\u00F6nig/").join("bar")
|
|
603
|
+
|
|
604
|
+
assert_equal "http://example.com/K%C3%B6nig/bar", result.to_s
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def test_join_percent_encodes_non_ascii_characters_in_the_other_uri
|
|
608
|
+
result = HTTP::URI.parse("http://example.com/").join("/K\u00F6nig")
|
|
609
|
+
|
|
610
|
+
assert_equal "http://example.com/K%C3%B6nig", result.to_s
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def test_http_predicate_returns_false_for_non_http_https_schemes
|
|
614
|
+
uri = HTTP::URI.parse("ftp://example.com")
|
|
615
|
+
|
|
616
|
+
refute_predicate uri, :http?
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def test_https_predicate_returns_true_for_https_uris
|
|
620
|
+
assert_predicate https_uri, :https?
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def test_https_predicate_returns_false_for_http_uris
|
|
624
|
+
refute_predicate http_uri, :https?
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def test_https_predicate_returns_false_for_non_http_https_schemes
|
|
628
|
+
uri = HTTP::URI.parse("ftp://example.com")
|
|
629
|
+
|
|
630
|
+
refute_predicate uri, :https?
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def test_to_s_returns_the_string_representation
|
|
634
|
+
assert_equal "http://example.com", http_uri.to_s
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
def test_to_str_is_aliased_to_to_s
|
|
638
|
+
assert_equal http_uri.to_s, http_uri.to_str
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def test_inspect_includes_the_class_name
|
|
642
|
+
assert_includes http_uri.inspect, "HTTP::URI"
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
def test_inspect_includes_the_uri_string
|
|
646
|
+
assert_includes http_uri.inspect, "URI:http://example.com"
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
def test_inspect_formats_the_object_id_correctly_with_shift
|
|
650
|
+
expected_hex = format("%014x", http_uri.object_id << 1)
|
|
651
|
+
|
|
652
|
+
assert_includes http_uri.inspect, expected_hex
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
def test_initialize_accepts_keyword_arguments
|
|
656
|
+
uri = HTTP::URI.new(scheme: "http", host: "example.com")
|
|
657
|
+
|
|
658
|
+
assert_equal "http", uri.scheme
|
|
659
|
+
assert_equal "example.com", uri.host
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def test_initialize_raises_argument_error_for_an_addressable_uri
|
|
663
|
+
addr_uri = Addressable::URI.parse("http://example.com")
|
|
664
|
+
|
|
665
|
+
assert_raises(ArgumentError) { HTTP::URI.new(addr_uri) }
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
def test_initialize_raises_argument_error_for_a_positional_argument
|
|
669
|
+
assert_raises(ArgumentError) { HTTP::URI.new(42) }
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
def test_initialize_works_with_no_arguments
|
|
673
|
+
uri = HTTP::URI.new
|
|
674
|
+
|
|
675
|
+
assert_instance_of HTTP::URI, uri
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
def test_deconstruct_keys_returns_all_keys_when_given_nil
|
|
679
|
+
full_uri = HTTP::URI.parse("http://user:pass@example.com:8080/path?q=1#frag")
|
|
680
|
+
result = full_uri.deconstruct_keys(nil)
|
|
681
|
+
|
|
682
|
+
assert_equal "http", result[:scheme]
|
|
683
|
+
assert_equal "example.com", result[:host]
|
|
684
|
+
assert_equal 8080, result[:port]
|
|
685
|
+
assert_equal "/path", result[:path]
|
|
686
|
+
assert_equal "q=1", result[:query]
|
|
687
|
+
assert_equal "frag", result[:fragment]
|
|
688
|
+
assert_equal "user", result[:user]
|
|
689
|
+
assert_equal "pass", result[:password]
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
def test_deconstruct_keys_returns_only_requested_keys
|
|
693
|
+
result = http_uri.deconstruct_keys(%i[scheme host])
|
|
694
|
+
|
|
695
|
+
assert_equal({ scheme: "http", host: "example.com" }, result)
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
def test_deconstruct_keys_excludes_unrequested_keys
|
|
699
|
+
result = http_uri.deconstruct_keys([:host])
|
|
700
|
+
|
|
701
|
+
refute_includes result.keys, :scheme
|
|
702
|
+
refute_includes result.keys, :port
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
def test_deconstruct_keys_returns_empty_hash_for_empty_keys
|
|
706
|
+
assert_equal({}, http_uri.deconstruct_keys([]))
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def test_deconstruct_keys_returns_correct_port_for_https_uris
|
|
710
|
+
assert_equal 443, https_uri.deconstruct_keys([:port])[:port]
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def test_deconstruct_keys_supports_pattern_matching_with_case_in
|
|
714
|
+
matched = case http_uri
|
|
715
|
+
in { scheme: "http", host: /example/ }
|
|
716
|
+
true
|
|
717
|
+
else
|
|
718
|
+
false
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
assert matched
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
def test_process_ipv6_brackets_handles_ipv4_addresses
|
|
725
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
726
|
+
uri.host = "192.168.1.1"
|
|
727
|
+
|
|
728
|
+
assert_equal "192.168.1.1", uri.host
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def test_process_ipv6_brackets_handles_regular_hostnames
|
|
732
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
733
|
+
uri.host = "example.org"
|
|
734
|
+
|
|
735
|
+
assert_equal "example.org", uri.host
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
def test_process_ipv6_brackets_handles_invalid_ip_address_strings_gracefully
|
|
739
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
740
|
+
uri.host = "not-an-ip"
|
|
741
|
+
|
|
742
|
+
assert_equal "not-an-ip", uri.host
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
def test_parse_raises_invalid_error_with_message_containing_inspect_for_non_stringable_objects
|
|
746
|
+
obj = Object.new
|
|
747
|
+
def obj.to_str
|
|
748
|
+
raise NoMethodError
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
err = assert_raises(HTTP::URI::InvalidError) do
|
|
752
|
+
HTTP::URI.parse(obj)
|
|
753
|
+
end
|
|
754
|
+
assert_kind_of HTTP::URI::InvalidError, err
|
|
755
|
+
assert_includes err.message, "invalid URI: "
|
|
756
|
+
assert_includes err.message, obj.inspect
|
|
757
|
+
refute_equal obj.inspect, err.message
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
def test_parse_raises_invalid_error_for_an_object_whose_to_str_raises_type_error
|
|
761
|
+
obj = Object.new
|
|
762
|
+
def obj.to_str
|
|
763
|
+
raise TypeError
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
err = assert_raises(HTTP::URI::InvalidError) do
|
|
767
|
+
HTTP::URI.parse(obj)
|
|
768
|
+
end
|
|
769
|
+
assert_kind_of HTTP::URI::InvalidError, err
|
|
770
|
+
assert_includes err.message, obj.inspect
|
|
771
|
+
refute_equal obj.inspect, err.message
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
def test_parse_via_parse_components_parses_non_ascii_uri_with_all_components
|
|
775
|
+
uri = HTTP::URI.parse("http://us\u00E9r:p\u00E4ss@ex\u00E4mple.com:9090/p\u00E4th?q=v\u00E4l#fr\u00E4g")
|
|
776
|
+
|
|
777
|
+
assert_equal "us\u00E9r", uri.user
|
|
778
|
+
assert_equal "p\u00E4ss", uri.password
|
|
779
|
+
assert_equal 9090, uri.port
|
|
780
|
+
assert_includes String(uri), "fr\u00E4g"
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
def test_parse_via_parse_components_parses_an_ascii_uri_via_stdlib
|
|
784
|
+
uri = HTTP::URI.parse("http://example.com/path?q=1#frag")
|
|
785
|
+
|
|
786
|
+
assert_equal "http", uri.scheme
|
|
787
|
+
assert_equal "example.com", uri.host
|
|
788
|
+
assert_equal "/path", uri.path
|
|
789
|
+
assert_equal "q=1", uri.query
|
|
790
|
+
assert_equal "frag", uri.fragment
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
def test_parse_via_parse_components_strips_default_port_when_parsing_ascii_uri
|
|
794
|
+
uri = HTTP::URI.parse("http://example.com:80/path")
|
|
795
|
+
|
|
796
|
+
assert_equal "http://example.com/path", uri.to_s
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
def test_parse_via_parse_components_falls_back_to_addressable_when_stdlib_fails
|
|
800
|
+
uri = HTTP::URI.parse("http://example.com/path with spaces")
|
|
801
|
+
|
|
802
|
+
assert_equal "http", uri.scheme
|
|
803
|
+
assert_equal "example.com", uri.host
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
def test_parse_via_parse_components_raises_invalid_error_for_invalid_non_ascii_uri
|
|
807
|
+
err = assert_raises(HTTP::URI::InvalidError) do
|
|
808
|
+
HTTP::URI.parse("ht\u00FCtp://[invalid")
|
|
809
|
+
end
|
|
810
|
+
assert_kind_of HTTP::URI::InvalidError, err
|
|
811
|
+
assert_includes err.message, "invalid URI:"
|
|
812
|
+
assert_includes err.message, "invalid"
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
def test_parse_via_parse_components_raises_invalid_error_for_stdlib_invalid_uri
|
|
816
|
+
err = assert_raises(HTTP::URI::InvalidError) do
|
|
817
|
+
HTTP::URI.parse("http://exam ple.com")
|
|
818
|
+
end
|
|
819
|
+
assert_kind_of HTTP::URI::InvalidError, err
|
|
820
|
+
assert_includes err.message, "invalid URI:"
|
|
821
|
+
assert_includes err.message, "exam ple.com"
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
def test_parse_via_parse_components_parses_non_ascii_uri_preserving_fragment
|
|
825
|
+
uri = HTTP::URI.parse("http://ex\u00E4mple.com/path#sec\u00F6tion")
|
|
826
|
+
|
|
827
|
+
assert_equal "sec\u00F6tion", uri.fragment
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def test_parse_via_parse_components_parses_non_ascii_uri_preserving_user_without_password
|
|
831
|
+
uri = HTTP::URI.parse("http://\u00FCser@ex\u00E4mple.com/")
|
|
832
|
+
|
|
833
|
+
assert_equal "\u00FCser", uri.user
|
|
834
|
+
assert_nil uri.password
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
def test_parse_via_parse_components_routes_ascii_control_characters_to_addressable
|
|
838
|
+
uri = HTTP::URI.parse("http://example.com/?\x00\x7F\n")
|
|
839
|
+
|
|
840
|
+
assert_equal "\x00\x7F\n", uri.query
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
def test_to_s_serializes_scheme_only_uri
|
|
844
|
+
uri = HTTP::URI.new(scheme: "http")
|
|
845
|
+
|
|
846
|
+
assert_equal "http:", uri.to_s
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
def test_to_s_omits_scheme_prefix_when_scheme_is_nil
|
|
850
|
+
uri = HTTP::URI.new(host: "example.com", path: "/path")
|
|
851
|
+
|
|
852
|
+
assert_equal "//example.com/path", uri.to_s
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
def test_to_s_serializes_uri_with_user_and_password
|
|
856
|
+
uri = HTTP::URI.new(scheme: "http", user: "admin", password: "secret", host: "example.com")
|
|
857
|
+
|
|
858
|
+
assert_equal "http://admin:secret@example.com", uri.to_s
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
def test_to_s_serializes_uri_with_user_but_no_password
|
|
862
|
+
uri = HTTP::URI.new(scheme: "http", user: "admin", host: "example.com")
|
|
863
|
+
|
|
864
|
+
assert_equal "http://admin@example.com", uri.to_s
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
def test_to_s_serializes_uri_with_explicit_port
|
|
868
|
+
uri = HTTP::URI.new(scheme: "http", host: "example.com", port: 8080)
|
|
869
|
+
|
|
870
|
+
assert_equal "http://example.com:8080", uri.to_s
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def test_to_s_serializes_uri_with_query
|
|
874
|
+
uri = HTTP::URI.new(scheme: "http", host: "example.com", path: "/path", query: "a=1")
|
|
875
|
+
|
|
876
|
+
assert_equal "http://example.com/path?a=1", uri.to_s
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
def test_to_s_serializes_uri_with_fragment
|
|
880
|
+
uri = HTTP::URI.new(scheme: "http", host: "example.com", path: "/path", fragment: "sec")
|
|
881
|
+
|
|
882
|
+
assert_equal "http://example.com/path#sec", uri.to_s
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def test_to_s_serializes_uri_with_all_components
|
|
886
|
+
uri = HTTP::URI.new(
|
|
887
|
+
scheme: "http", user: "u", password: "p", host: "h.com",
|
|
888
|
+
port: 9090, path: "/x", query: "q=1", fragment: "f"
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
assert_equal "http://u:p@h.com:9090/x?q=1#f", uri.to_s
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def test_to_s_serializes_path_only_uri
|
|
895
|
+
uri = HTTP::URI.new(path: "/just/a/path")
|
|
896
|
+
|
|
897
|
+
assert_equal "/just/a/path", uri.to_s
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
def test_to_s_serializes_uri_without_host_omitting_double_slash
|
|
901
|
+
uri = HTTP::URI.new(scheme: "mailto", path: "user@example.com")
|
|
902
|
+
|
|
903
|
+
assert_equal "mailto:user@example.com", uri.to_s
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
def test_to_s_serializes_query_only_uri_without_host
|
|
907
|
+
uri = HTTP::URI.new(path: "/p", query: "q=1")
|
|
908
|
+
|
|
909
|
+
assert_equal "/p?q=1", uri.to_s
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
def test_to_s_serializes_fragment_only_uri_without_host
|
|
913
|
+
uri = HTTP::URI.new(path: "/p", fragment: "f")
|
|
914
|
+
|
|
915
|
+
assert_equal "/p#f", uri.to_s
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
def test_normalize_lowercases_the_scheme
|
|
919
|
+
uri = HTTP::URI.new(scheme: "HTTP", host: "example.com")
|
|
920
|
+
|
|
921
|
+
assert_equal "http", uri.normalize.scheme
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
def test_normalize_lowercases_the_host
|
|
925
|
+
uri = HTTP::URI.new(scheme: "http", host: "EXAMPLE.COM")
|
|
926
|
+
|
|
927
|
+
assert_equal "example.com", uri.normalize.host
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
def test_normalize_strips_default_port
|
|
931
|
+
uri = HTTP::URI.new(scheme: "http", host: "example.com", port: 80, path: "/path")
|
|
932
|
+
|
|
933
|
+
assert_nil uri.normalize.instance_variable_get(:@port)
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
def test_normalize_preserves_non_default_port
|
|
937
|
+
uri = HTTP::URI.parse("http://example.com:8080/path")
|
|
938
|
+
normalized = uri.normalize
|
|
939
|
+
|
|
940
|
+
assert_equal 8080, normalized.instance_variable_get(:@port)
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
def test_normalize_normalizes_empty_path_to_slash_when_host_is_present
|
|
944
|
+
uri = HTTP::URI.new(scheme: "http", host: "example.com")
|
|
945
|
+
|
|
946
|
+
assert_equal "/", uri.normalize.path
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
def test_normalize_preserves_non_empty_path
|
|
950
|
+
uri = HTTP::URI.parse("http://example.com/foo")
|
|
951
|
+
|
|
952
|
+
assert_equal "/foo", uri.normalize.path
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
def test_normalize_preserves_user
|
|
956
|
+
uri = HTTP::URI.parse("http://myuser@example.com/")
|
|
957
|
+
|
|
958
|
+
assert_equal "myuser", uri.normalize.user
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
def test_normalize_preserves_password
|
|
962
|
+
uri = HTTP::URI.parse("http://u:mypass@example.com/")
|
|
963
|
+
|
|
964
|
+
assert_equal "mypass", uri.normalize.password
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
def test_normalize_preserves_query
|
|
968
|
+
uri = HTTP::URI.parse("http://example.com/?q=val")
|
|
969
|
+
|
|
970
|
+
assert_equal "q=val", uri.normalize.query
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
def test_normalize_preserves_fragment
|
|
974
|
+
uri = HTTP::URI.parse("http://example.com/#frag")
|
|
975
|
+
|
|
976
|
+
assert_equal "frag", uri.normalize.fragment
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
def test_normalize_handles_nil_scheme
|
|
980
|
+
uri = HTTP::URI.new(host: "example.com")
|
|
981
|
+
|
|
982
|
+
assert_nil uri.normalize.scheme
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
def test_normalize_handles_nil_host
|
|
986
|
+
uri = HTTP::URI.new(scheme: "http", path: "/path")
|
|
987
|
+
|
|
988
|
+
assert_nil uri.normalize.host
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
def test_normalize_does_not_normalize_empty_path_to_slash_without_host
|
|
992
|
+
uri = HTTP::URI.new(scheme: "http")
|
|
993
|
+
|
|
994
|
+
assert_equal "", uri.normalize.path
|
|
995
|
+
end
|
|
996
|
+
|
|
997
|
+
def test_normalize_returns_a_complete_normalized_string
|
|
998
|
+
uri = HTTP::URI.parse("HTTP://USER:PASS@EXAMPLE.COM:8080/path?q=1#frag")
|
|
999
|
+
normalized = uri.normalize
|
|
1000
|
+
|
|
1001
|
+
assert_equal "http://USER:PASS@example.com:8080/path?q=1#frag", String(normalized)
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
def test_normalized_host_lowercases_the_host
|
|
1005
|
+
uri = HTTP::URI.new(host: "EXAMPLE.COM")
|
|
1006
|
+
|
|
1007
|
+
assert_equal "example.com", uri.normalized_host
|
|
1008
|
+
end
|
|
1009
|
+
|
|
1010
|
+
def test_normalized_host_decodes_percent_encoded_characters
|
|
1011
|
+
uri = HTTP::URI.new(host: "%65%78ample.com")
|
|
1012
|
+
|
|
1013
|
+
assert_equal "example.com", uri.normalized_host
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
def test_normalized_host_decodes_multiple_percent_encoded_characters
|
|
1017
|
+
uri = HTTP::URI.new(host: "%65%78%61mple.com")
|
|
1018
|
+
|
|
1019
|
+
assert_equal "example.com", uri.normalized_host
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
def test_normalized_host_strips_trailing_dot_from_domain
|
|
1023
|
+
uri = HTTP::URI.new(host: "example.com.")
|
|
1024
|
+
|
|
1025
|
+
assert_equal "example.com", uri.normalized_host
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
def test_normalized_host_returns_nil_for_nil_host
|
|
1029
|
+
uri = HTTP::URI.new
|
|
1030
|
+
|
|
1031
|
+
assert_nil uri.normalized_host
|
|
1032
|
+
end
|
|
1033
|
+
|
|
1034
|
+
def test_normalized_host_encodes_idn_non_ascii_hostnames_to_ascii
|
|
1035
|
+
uri = HTTP::URI.new(host: "ex\u00E4mple.com")
|
|
1036
|
+
|
|
1037
|
+
assert_equal "xn--exmple-cua.com", uri.normalized_host
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
def test_normalized_host_does_not_idn_encode_already_ascii_hostnames
|
|
1041
|
+
uri = HTTP::URI.new(host: "example.com")
|
|
1042
|
+
|
|
1043
|
+
assert_equal "example.com", uri.normalized_host
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1046
|
+
def test_host_assignment_applies_normalize_host_to_the_new_host
|
|
1047
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
1048
|
+
uri.host = "NEW-HOST.COM."
|
|
1049
|
+
|
|
1050
|
+
assert_equal "new-host.com", uri.normalized_host
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def test_default_port_returns_default_port_for_uppercase_scheme
|
|
1054
|
+
uri = HTTP::URI.new(scheme: "HTTP")
|
|
1055
|
+
|
|
1056
|
+
assert_equal 80, uri.default_port
|
|
1057
|
+
end
|
|
1058
|
+
|
|
1059
|
+
def test_default_port_returns_nil_for_unknown_scheme
|
|
1060
|
+
uri = HTTP::URI.new(scheme: "ftp")
|
|
1061
|
+
|
|
1062
|
+
assert_nil uri.default_port
|
|
1063
|
+
end
|
|
1064
|
+
|
|
1065
|
+
def test_default_port_returns_default_port_for_ws_scheme
|
|
1066
|
+
uri = HTTP::URI.new(scheme: "ws")
|
|
1067
|
+
|
|
1068
|
+
assert_equal 80, uri.default_port
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1071
|
+
def test_default_port_returns_default_port_for_wss_scheme
|
|
1072
|
+
uri = HTTP::URI.new(scheme: "wss")
|
|
1073
|
+
|
|
1074
|
+
assert_equal 443, uri.default_port
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
def test_origin_lowercases_an_uppercase_scheme
|
|
1078
|
+
uri = HTTP::URI.new(scheme: "HTTP", host: "example.com")
|
|
1079
|
+
|
|
1080
|
+
assert_equal "http://example.com", uri.origin
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
def test_process_ipv6_brackets_returns_nil_host_as_nil
|
|
1084
|
+
uri = HTTP::URI.new(host: nil)
|
|
1085
|
+
|
|
1086
|
+
assert_nil uri.host
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
def test_process_ipv6_brackets_does_not_strip_brackets_from_ipv4_addresses
|
|
1090
|
+
uri = HTTP::URI.new(host: "192.168.1.1")
|
|
1091
|
+
|
|
1092
|
+
assert_equal "192.168.1.1", uri.host
|
|
1093
|
+
assert_equal "192.168.1.1", uri.instance_variable_get(:@raw_host)
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
def test_process_ipv6_brackets_does_not_bracket_ipv4_addresses_in_host_assignment
|
|
1097
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
1098
|
+
uri.host = "10.0.0.1"
|
|
1099
|
+
|
|
1100
|
+
assert_equal "http://10.0.0.1", uri.to_s
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
def test_parse_error_messages_uses_inspect_in_the_rescue
|
|
1104
|
+
obj = Object.new
|
|
1105
|
+
def obj.to_s
|
|
1106
|
+
"CUSTOM_TO_S"
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
def obj.to_str
|
|
1110
|
+
raise NoMethodError
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
err = assert_raises(HTTP::URI::InvalidError) do
|
|
1114
|
+
HTTP::URI.parse(obj)
|
|
1115
|
+
end
|
|
1116
|
+
|
|
1117
|
+
refute_includes err.message, "CUSTOM_TO_S"
|
|
1118
|
+
end
|
|
1119
|
+
|
|
1120
|
+
def test_dup_does_not_copy_memoized_hash_ivar
|
|
1121
|
+
uri = HTTP::URI.parse("http://example.com")
|
|
1122
|
+
uri.hash # memoize @hash
|
|
1123
|
+
|
|
1124
|
+
duped = uri.dup
|
|
1125
|
+
|
|
1126
|
+
refute duped.instance_variable_defined?(:@hash)
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
def test_normalize_strips_port_443_for_https
|
|
1130
|
+
uri = HTTP::URI.new(scheme: "https", host: "example.com", port: 443, path: "/")
|
|
1131
|
+
|
|
1132
|
+
assert_nil uri.normalize.instance_variable_get(:@port)
|
|
1133
|
+
end
|
|
1134
|
+
|
|
1135
|
+
def test_normalize_does_not_strip_non_default_port
|
|
1136
|
+
uri = HTTP::URI.new(scheme: "http", host: "example.com", port: 9090, path: "/")
|
|
1137
|
+
|
|
1138
|
+
assert_equal 9090, uri.normalize.instance_variable_get(:@port)
|
|
1139
|
+
end
|
|
1140
|
+
end
|