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,888 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class HTTPHeadersTest < Minitest::Test
|
|
6
|
+
cover "HTTP::Headers*"
|
|
7
|
+
|
|
8
|
+
def headers
|
|
9
|
+
@headers ||= HTTP::Headers.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_is_enumerable
|
|
13
|
+
assert_kind_of Enumerable, headers
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# -- #set -------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
def test_set_sets_header_value
|
|
19
|
+
headers.set "Accept", "application/json"
|
|
20
|
+
|
|
21
|
+
assert_equal "application/json", headers["Accept"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_set_allows_retrieval_via_normalized_header_name
|
|
25
|
+
headers.set :content_type, "application/json"
|
|
26
|
+
|
|
27
|
+
assert_equal "application/json", headers["Content-Type"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_set_overwrites_previous_value
|
|
31
|
+
headers.set :set_cookie, "hoo=ray"
|
|
32
|
+
headers.set :set_cookie, "woo=hoo"
|
|
33
|
+
|
|
34
|
+
assert_equal "woo=hoo", headers["Set-Cookie"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_set_allows_set_multiple_values
|
|
38
|
+
headers.set :set_cookie, "hoo=ray"
|
|
39
|
+
headers.set :set_cookie, %w[hoo=ray woo=hoo]
|
|
40
|
+
|
|
41
|
+
assert_equal %w[hoo=ray woo=hoo], headers["Set-Cookie"]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_set_fails_with_empty_header_name
|
|
45
|
+
assert_raises(HTTP::HeaderError) { headers.set "", "foo bar" }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
["foo bar", "foo bar: ok\nfoo", "evil-header: evil-value\nfoo"].each do |name|
|
|
49
|
+
define_method :"test_set_fails_with_invalid_header_name_#{name.inspect}" do
|
|
50
|
+
assert_raises(HTTP::HeaderError) { headers.set name, "baz" }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_set_fails_with_invalid_header_value
|
|
55
|
+
assert_raises(HTTP::HeaderError) { headers.set "foo", "bar\nEvil-Header: evil-value" }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# -- #[]= ------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
def test_bracket_assign_sets_header_value
|
|
61
|
+
headers["Accept"] = "application/json"
|
|
62
|
+
|
|
63
|
+
assert_equal "application/json", headers["Accept"]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_bracket_assign_allows_retrieval_via_normalized_header_name
|
|
67
|
+
headers[:content_type] = "application/json"
|
|
68
|
+
|
|
69
|
+
assert_equal "application/json", headers["Content-Type"]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_bracket_assign_overwrites_previous_value
|
|
73
|
+
headers[:set_cookie] = "hoo=ray"
|
|
74
|
+
headers[:set_cookie] = "woo=hoo"
|
|
75
|
+
|
|
76
|
+
assert_equal "woo=hoo", headers["Set-Cookie"]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_bracket_assign_allows_set_multiple_values
|
|
80
|
+
headers[:set_cookie] = "hoo=ray"
|
|
81
|
+
headers[:set_cookie] = %w[hoo=ray woo=hoo]
|
|
82
|
+
|
|
83
|
+
assert_equal %w[hoo=ray woo=hoo], headers["Set-Cookie"]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# -- #delete ----------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
def test_delete_removes_given_header
|
|
89
|
+
headers.set "Content-Type", "application/json"
|
|
90
|
+
headers.delete "Content-Type"
|
|
91
|
+
|
|
92
|
+
assert_nil headers["Content-Type"]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_delete_removes_header_that_matches_normalized_version_of_specified_name
|
|
96
|
+
headers.set "Content-Type", "application/json"
|
|
97
|
+
headers.delete :content_type
|
|
98
|
+
|
|
99
|
+
assert_nil headers["Content-Type"]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_delete_calls_to_s_on_non_string_name_argument
|
|
103
|
+
headers.set "Content-Type", "application/json"
|
|
104
|
+
name = fake(to_s: "Content-Type")
|
|
105
|
+
headers.delete name
|
|
106
|
+
|
|
107
|
+
assert_nil headers["Content-Type"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_delete_fails_with_empty_header_name
|
|
111
|
+
headers.set "Content-Type", "application/json"
|
|
112
|
+
|
|
113
|
+
assert_raises(HTTP::HeaderError) { headers.delete "" }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
["foo bar", "foo bar: ok\nfoo"].each do |name|
|
|
117
|
+
define_method :"test_delete_fails_with_invalid_header_name_#{name.inspect}" do
|
|
118
|
+
headers.set "Content-Type", "application/json"
|
|
119
|
+
|
|
120
|
+
assert_raises(HTTP::HeaderError) { headers.delete name }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# -- #add -------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
def test_add_sets_header_value
|
|
127
|
+
headers.add "Accept", "application/json"
|
|
128
|
+
|
|
129
|
+
assert_equal "application/json", headers["Accept"]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def test_add_allows_retrieval_via_normalized_header_name
|
|
133
|
+
headers.add :content_type, "application/json"
|
|
134
|
+
|
|
135
|
+
assert_equal "application/json", headers["Content-Type"]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_add_appends_new_value_if_header_exists
|
|
139
|
+
headers.add "Set-Cookie", "hoo=ray"
|
|
140
|
+
headers.add :set_cookie, "woo=hoo"
|
|
141
|
+
|
|
142
|
+
assert_equal %w[hoo=ray woo=hoo], headers["Set-Cookie"]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_add_allows_append_multiple_values
|
|
146
|
+
headers.add :set_cookie, "hoo=ray"
|
|
147
|
+
headers.add :set_cookie, %w[woo=hoo yup=pie]
|
|
148
|
+
|
|
149
|
+
assert_equal %w[hoo=ray woo=hoo yup=pie], headers["Set-Cookie"]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def test_add_fails_with_empty_header_name
|
|
153
|
+
assert_raises(HTTP::HeaderError) { headers.add("", "foobar") }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
["foo bar", "foo bar: ok\nfoo"].each do |name|
|
|
157
|
+
define_method :"test_add_fails_with_invalid_header_name_#{name.inspect}" do
|
|
158
|
+
assert_raises(HTTP::HeaderError) { headers.add name, "baz" }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def test_add_fails_with_invalid_header_value
|
|
163
|
+
assert_raises(HTTP::HeaderError) { headers.add "foo", "bar\nEvil-Header: evil-value" }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_add_fails_when_header_name_is_not_a_string_or_symbol
|
|
167
|
+
err = assert_raises(HTTP::HeaderError) { headers.add 2, "foo" }
|
|
168
|
+
assert_includes err.message, "2"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def test_add_includes_inspect_formatted_name_in_error_for_non_string_symbol
|
|
172
|
+
obj = Object.new
|
|
173
|
+
def obj.to_s = "plain"
|
|
174
|
+
def obj.inspect = "INSPECTED"
|
|
175
|
+
|
|
176
|
+
err = assert_raises(HTTP::HeaderError) { headers.add obj, "foo" }
|
|
177
|
+
assert_includes err.message, "INSPECTED"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def test_add_uses_normalized_name_as_wire_name_for_symbol_keys_in_to_a
|
|
181
|
+
headers.add :content_type, "application/json"
|
|
182
|
+
|
|
183
|
+
assert_equal [["Content-Type", "application/json"]], headers.to_a
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def test_add_preserves_original_string_as_wire_name_for_string_keys_in_to_a
|
|
187
|
+
headers.add "auth_key", "secret"
|
|
188
|
+
|
|
189
|
+
assert_equal [%w[auth_key secret]], headers.to_a
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_add_calls_to_s_on_symbol_name_for_normalization
|
|
193
|
+
headers.add :accept, "text/html"
|
|
194
|
+
|
|
195
|
+
assert_equal [["Accept", "text/html"]], headers.to_a
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# -- #get -------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
def test_get_returns_array_of_associated_values
|
|
201
|
+
headers.set("Content-Type", "application/json")
|
|
202
|
+
|
|
203
|
+
assert_equal %w[application/json], headers.get("Content-Type")
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_get_normalizes_header_name
|
|
207
|
+
headers.set("Content-Type", "application/json")
|
|
208
|
+
|
|
209
|
+
assert_equal %w[application/json], headers.get(:content_type)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def test_get_when_header_does_not_exist_returns_empty_array
|
|
213
|
+
headers.set("Content-Type", "application/json")
|
|
214
|
+
|
|
215
|
+
assert_equal [], headers.get(:accept)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def test_get_calls_to_s_on_non_string_name_argument
|
|
219
|
+
headers.set("Content-Type", "application/json")
|
|
220
|
+
name = fake(to_s: "Content-Type")
|
|
221
|
+
|
|
222
|
+
assert_equal %w[application/json], headers.get(name)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def test_get_fails_with_empty_header_name
|
|
226
|
+
headers.set("Content-Type", "application/json")
|
|
227
|
+
|
|
228
|
+
assert_raises(HTTP::HeaderError) { headers.get("") }
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
["foo bar", "foo bar: ok\nfoo"].each do |name|
|
|
232
|
+
define_method :"test_get_fails_with_invalid_header_name_#{name.inspect}" do
|
|
233
|
+
headers.set("Content-Type", "application/json")
|
|
234
|
+
|
|
235
|
+
assert_raises(HTTP::HeaderError) { headers.get name }
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# -- #[] -------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
def test_bracket_when_header_does_not_exist_returns_nil
|
|
242
|
+
assert_nil headers[:accept]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def test_bracket_single_value_normalizes_header_name
|
|
246
|
+
headers.set "Content-Type", "application/json"
|
|
247
|
+
|
|
248
|
+
refute_nil headers[:content_type]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def test_bracket_single_value_returns_single_value
|
|
252
|
+
headers.set "Content-Type", "application/json"
|
|
253
|
+
|
|
254
|
+
assert_equal "application/json", headers[:content_type]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def test_bracket_multiple_values_normalizes_header_name
|
|
258
|
+
headers.add :set_cookie, "hoo=ray"
|
|
259
|
+
headers.add :set_cookie, "woo=hoo"
|
|
260
|
+
|
|
261
|
+
refute_nil headers[:set_cookie]
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def test_bracket_multiple_values_returns_array_of_associated_values
|
|
265
|
+
headers.add :set_cookie, "hoo=ray"
|
|
266
|
+
headers.add :set_cookie, "woo=hoo"
|
|
267
|
+
|
|
268
|
+
assert_equal %w[hoo=ray woo=hoo], headers[:set_cookie]
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def test_bracket_returns_nil_for_missing_header
|
|
272
|
+
headers.set "Content-Type", "text/plain"
|
|
273
|
+
result = headers[:nonexistent]
|
|
274
|
+
|
|
275
|
+
assert_nil result
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def test_bracket_returns_string_for_single_value
|
|
279
|
+
headers.set "Content-Type", "text/plain"
|
|
280
|
+
|
|
281
|
+
result = headers["Content-Type"]
|
|
282
|
+
|
|
283
|
+
assert_instance_of String, result
|
|
284
|
+
assert_equal "text/plain", result
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def test_bracket_returns_array_for_multiple_values
|
|
288
|
+
headers.add :cookie, "a=1"
|
|
289
|
+
headers.add :cookie, "b=2"
|
|
290
|
+
|
|
291
|
+
result = headers[:cookie]
|
|
292
|
+
|
|
293
|
+
assert_instance_of Array, result
|
|
294
|
+
assert_equal %w[a=1 b=2], result
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# -- #include? --------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
def test_include_tells_whenever_given_headers_is_set_or_not
|
|
300
|
+
headers.add :content_type, "application/json"
|
|
301
|
+
headers.add :set_cookie, "hoo=ray"
|
|
302
|
+
headers.add :set_cookie, "woo=hoo"
|
|
303
|
+
|
|
304
|
+
assert_includes headers, "Content-Type"
|
|
305
|
+
assert_includes headers, "Set-Cookie"
|
|
306
|
+
refute_includes headers, "Accept"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def test_include_normalizes_given_header_name
|
|
310
|
+
headers.add :content_type, "application/json"
|
|
311
|
+
headers.add :set_cookie, "hoo=ray"
|
|
312
|
+
headers.add :set_cookie, "woo=hoo"
|
|
313
|
+
|
|
314
|
+
assert_includes headers, :content_type
|
|
315
|
+
assert_includes headers, :set_cookie
|
|
316
|
+
refute_includes headers, :accept
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def test_include_calls_to_s_on_non_string_name_argument
|
|
320
|
+
headers.add :content_type, "application/json"
|
|
321
|
+
headers.add :set_cookie, "hoo=ray"
|
|
322
|
+
headers.add :set_cookie, "woo=hoo"
|
|
323
|
+
name = fake(to_s: "Content-Type")
|
|
324
|
+
|
|
325
|
+
assert_includes headers, name
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def test_include_finds_headers_added_with_non_canonical_string_keys
|
|
329
|
+
h = HTTP::Headers.new
|
|
330
|
+
h.add("x-custom", "value")
|
|
331
|
+
|
|
332
|
+
assert_includes h, "x-custom"
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# -- #to_h ------------------------------------------------------------------
|
|
336
|
+
|
|
337
|
+
def test_to_h_returns_a_hash
|
|
338
|
+
headers.add :content_type, "application/json"
|
|
339
|
+
headers.add :set_cookie, "hoo=ray"
|
|
340
|
+
headers.add :set_cookie, "woo=hoo"
|
|
341
|
+
|
|
342
|
+
assert_kind_of Hash, headers.to_h
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def test_to_h_returns_hash_with_normalized_keys
|
|
346
|
+
headers.add :content_type, "application/json"
|
|
347
|
+
headers.add :set_cookie, "hoo=ray"
|
|
348
|
+
headers.add :set_cookie, "woo=hoo"
|
|
349
|
+
|
|
350
|
+
assert_equal %w[Content-Type Set-Cookie].sort, headers.to_h.keys.sort
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def test_to_h_single_value_provides_value_as_is
|
|
354
|
+
headers.add :content_type, "application/json"
|
|
355
|
+
headers.add :set_cookie, "hoo=ray"
|
|
356
|
+
headers.add :set_cookie, "woo=hoo"
|
|
357
|
+
|
|
358
|
+
assert_equal "application/json", headers.to_h["Content-Type"]
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def test_to_h_multiple_values_provides_array_of_values
|
|
362
|
+
headers.add :content_type, "application/json"
|
|
363
|
+
headers.add :set_cookie, "hoo=ray"
|
|
364
|
+
headers.add :set_cookie, "woo=hoo"
|
|
365
|
+
|
|
366
|
+
assert_equal %w[hoo=ray woo=hoo], headers.to_h["Set-Cookie"]
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# -- #to_a ------------------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
def test_to_a_returns_an_array
|
|
372
|
+
headers.add :content_type, "application/json"
|
|
373
|
+
headers.add :set_cookie, "hoo=ray"
|
|
374
|
+
headers.add :set_cookie, "woo=hoo"
|
|
375
|
+
|
|
376
|
+
assert_kind_of Array, headers.to_a
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def test_to_a_returns_array_of_key_value_pairs_with_normalized_keys
|
|
380
|
+
headers.add :content_type, "application/json"
|
|
381
|
+
headers.add :set_cookie, "hoo=ray"
|
|
382
|
+
headers.add :set_cookie, "woo=hoo"
|
|
383
|
+
|
|
384
|
+
assert_equal [
|
|
385
|
+
%w[Content-Type application/json],
|
|
386
|
+
%w[Set-Cookie hoo=ray],
|
|
387
|
+
%w[Set-Cookie woo=hoo]
|
|
388
|
+
], headers.to_a
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def test_to_a_returns_two_element_arrays
|
|
392
|
+
headers.add :content_type, "application/json"
|
|
393
|
+
headers.add :set_cookie, "hoo=ray"
|
|
394
|
+
headers.add :set_cookie, "woo=hoo"
|
|
395
|
+
|
|
396
|
+
headers.to_a.each do |pair|
|
|
397
|
+
assert_equal 2, pair.size, "Expected each element to be a [name, value] pair"
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def test_to_a_returns_wire_name_as_first_element
|
|
402
|
+
h = HTTP::Headers.new
|
|
403
|
+
h.add "X_Custom", "val"
|
|
404
|
+
|
|
405
|
+
assert_equal [%w[X_Custom val]], h.to_a
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# -- #deconstruct_keys ------------------------------------------------------
|
|
409
|
+
|
|
410
|
+
def test_deconstruct_keys_returns_all_keys_as_snake_case_symbols_when_given_nil
|
|
411
|
+
headers.add :content_type, "application/json"
|
|
412
|
+
headers.add :set_cookie, "hoo=ray"
|
|
413
|
+
headers.add :set_cookie, "woo=hoo"
|
|
414
|
+
|
|
415
|
+
result = headers.deconstruct_keys(nil)
|
|
416
|
+
|
|
417
|
+
assert_equal "application/json", result[:content_type]
|
|
418
|
+
assert_equal %w[hoo=ray woo=hoo], result[:set_cookie]
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def test_deconstruct_keys_converts_header_names_to_snake_case_symbols
|
|
422
|
+
headers.add :content_type, "application/json"
|
|
423
|
+
headers.add :set_cookie, "hoo=ray"
|
|
424
|
+
headers.add :set_cookie, "woo=hoo"
|
|
425
|
+
|
|
426
|
+
assert_includes headers.deconstruct_keys(nil).keys, :content_type
|
|
427
|
+
assert_includes headers.deconstruct_keys(nil).keys, :set_cookie
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def test_deconstruct_keys_returns_only_requested_keys
|
|
431
|
+
headers.add :content_type, "application/json"
|
|
432
|
+
headers.add :set_cookie, "hoo=ray"
|
|
433
|
+
headers.add :set_cookie, "woo=hoo"
|
|
434
|
+
|
|
435
|
+
result = headers.deconstruct_keys([:content_type])
|
|
436
|
+
|
|
437
|
+
assert_equal({ content_type: "application/json" }, result)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def test_deconstruct_keys_excludes_unrequested_keys
|
|
441
|
+
headers.add :content_type, "application/json"
|
|
442
|
+
headers.add :set_cookie, "hoo=ray"
|
|
443
|
+
headers.add :set_cookie, "woo=hoo"
|
|
444
|
+
|
|
445
|
+
refute_includes headers.deconstruct_keys([:content_type]).keys, :set_cookie
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def test_deconstruct_keys_returns_empty_hash_for_empty_keys
|
|
449
|
+
headers.add :content_type, "application/json"
|
|
450
|
+
headers.add :set_cookie, "hoo=ray"
|
|
451
|
+
headers.add :set_cookie, "woo=hoo"
|
|
452
|
+
|
|
453
|
+
assert_equal({}, headers.deconstruct_keys([]))
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def test_deconstruct_keys_supports_pattern_matching_with_case_in
|
|
457
|
+
headers.add :content_type, "application/json"
|
|
458
|
+
headers.add :set_cookie, "hoo=ray"
|
|
459
|
+
headers.add :set_cookie, "woo=hoo"
|
|
460
|
+
|
|
461
|
+
matched = case headers
|
|
462
|
+
in { content_type: /json/ }
|
|
463
|
+
true
|
|
464
|
+
else
|
|
465
|
+
false
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
assert matched
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# -- #inspect ---------------------------------------------------------------
|
|
472
|
+
|
|
473
|
+
def test_inspect_returns_a_human_readable_representation
|
|
474
|
+
headers.set :set_cookie, %w[hoo=ray woo=hoo]
|
|
475
|
+
|
|
476
|
+
assert_equal "#<HTTP::Headers>", headers.inspect
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# -- #keys ------------------------------------------------------------------
|
|
480
|
+
|
|
481
|
+
def test_keys_returns_uniq_keys_only
|
|
482
|
+
headers.add :content_type, "application/json"
|
|
483
|
+
headers.add :set_cookie, "hoo=ray"
|
|
484
|
+
headers.add :set_cookie, "woo=hoo"
|
|
485
|
+
|
|
486
|
+
assert_equal 2, headers.keys.size
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def test_keys_normalizes_keys
|
|
490
|
+
headers.add :content_type, "application/json"
|
|
491
|
+
headers.add :set_cookie, "hoo=ray"
|
|
492
|
+
headers.add :set_cookie, "woo=hoo"
|
|
493
|
+
|
|
494
|
+
assert_includes headers.keys, "Content-Type"
|
|
495
|
+
assert_includes headers.keys, "Set-Cookie"
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# -- #each ------------------------------------------------------------------
|
|
499
|
+
|
|
500
|
+
def test_each_yields_each_key_value_pair_separately
|
|
501
|
+
headers.add :set_cookie, "hoo=ray"
|
|
502
|
+
headers.add :content_type, "application/json"
|
|
503
|
+
headers.add :set_cookie, "woo=hoo"
|
|
504
|
+
|
|
505
|
+
yielded = headers.map { |pair| pair }
|
|
506
|
+
|
|
507
|
+
assert_equal 3, yielded.size
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def test_each_yields_headers_in_the_same_order_they_were_added
|
|
511
|
+
headers.add :set_cookie, "hoo=ray"
|
|
512
|
+
headers.add :content_type, "application/json"
|
|
513
|
+
headers.add :set_cookie, "woo=hoo"
|
|
514
|
+
|
|
515
|
+
yielded = headers.map { |pair| pair }
|
|
516
|
+
|
|
517
|
+
assert_equal [
|
|
518
|
+
%w[Set-Cookie hoo=ray],
|
|
519
|
+
%w[Content-Type application/json],
|
|
520
|
+
%w[Set-Cookie woo=hoo]
|
|
521
|
+
], yielded
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
def test_each_yields_header_keys_specified_as_symbols_in_normalized_form
|
|
525
|
+
headers.add :set_cookie, "hoo=ray"
|
|
526
|
+
headers.add :content_type, "application/json"
|
|
527
|
+
headers.add :set_cookie, "woo=hoo"
|
|
528
|
+
|
|
529
|
+
keys = headers.each.map(&:first)
|
|
530
|
+
|
|
531
|
+
assert_equal %w[Set-Cookie Content-Type Set-Cookie], keys
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def test_each_yields_headers_specified_as_strings_without_conversion
|
|
535
|
+
headers.add :set_cookie, "hoo=ray"
|
|
536
|
+
headers.add :content_type, "application/json"
|
|
537
|
+
headers.add :set_cookie, "woo=hoo"
|
|
538
|
+
headers.add "X_kEy", "value"
|
|
539
|
+
|
|
540
|
+
keys = headers.each.map(&:first)
|
|
541
|
+
|
|
542
|
+
assert_equal %w[Set-Cookie Content-Type Set-Cookie X_kEy], keys
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def test_each_returns_self_instance_if_block_given
|
|
546
|
+
assert_same(headers, headers.each { |*| nil })
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def test_each_returns_enumerator_if_no_block_given
|
|
550
|
+
assert_kind_of Enumerator, headers.each
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
def test_each_yields_two_element_arrays
|
|
554
|
+
headers.add :set_cookie, "hoo=ray"
|
|
555
|
+
headers.add :content_type, "application/json"
|
|
556
|
+
headers.add :set_cookie, "woo=hoo"
|
|
557
|
+
|
|
558
|
+
headers.each do |pair|
|
|
559
|
+
assert_equal 2, pair.size
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# -- .empty? ----------------------------------------------------------------
|
|
564
|
+
|
|
565
|
+
def test_empty_initially_is_true
|
|
566
|
+
assert_empty headers
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
def test_empty_when_header_exists_is_false
|
|
570
|
+
headers.add :accept, "text/plain"
|
|
571
|
+
|
|
572
|
+
refute_empty headers
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def test_empty_when_last_header_was_removed_is_true
|
|
576
|
+
headers.add :accept, "text/plain"
|
|
577
|
+
headers.delete :accept
|
|
578
|
+
|
|
579
|
+
assert_empty headers
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
# -- #hash ------------------------------------------------------------------
|
|
583
|
+
|
|
584
|
+
def test_hash_equals_if_two_headers_equals
|
|
585
|
+
left = HTTP::Headers.new
|
|
586
|
+
right = HTTP::Headers.new
|
|
587
|
+
|
|
588
|
+
left.add :accept, "text/plain"
|
|
589
|
+
right.add :accept, "text/plain"
|
|
590
|
+
|
|
591
|
+
assert_equal left.hash, right.hash
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# -- #== -------------------------------------------------------------------
|
|
595
|
+
|
|
596
|
+
def test_eq_compares_header_keys_and_values
|
|
597
|
+
left = HTTP::Headers.new
|
|
598
|
+
right = HTTP::Headers.new
|
|
599
|
+
|
|
600
|
+
left.add :accept, "text/plain"
|
|
601
|
+
right.add :accept, "text/plain"
|
|
602
|
+
|
|
603
|
+
assert_equal left, right
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def test_eq_allows_comparison_with_array_of_key_value_pairs
|
|
607
|
+
left = HTTP::Headers.new
|
|
608
|
+
left.add :accept, "text/plain"
|
|
609
|
+
|
|
610
|
+
assert_equal [%w[Accept text/plain]], left.to_a
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def test_eq_sensitive_to_headers_order
|
|
614
|
+
left = HTTP::Headers.new
|
|
615
|
+
right = HTTP::Headers.new
|
|
616
|
+
|
|
617
|
+
left.add :accept, "text/plain"
|
|
618
|
+
left.add :cookie, "woo=hoo"
|
|
619
|
+
right.add :cookie, "woo=hoo"
|
|
620
|
+
right.add :accept, "text/plain"
|
|
621
|
+
|
|
622
|
+
refute_equal left, right
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def test_eq_sensitive_to_header_values_order
|
|
626
|
+
left = HTTP::Headers.new
|
|
627
|
+
right = HTTP::Headers.new
|
|
628
|
+
|
|
629
|
+
left.add :cookie, "hoo=ray"
|
|
630
|
+
left.add :cookie, "woo=hoo"
|
|
631
|
+
right.add :cookie, "woo=hoo"
|
|
632
|
+
right.add :cookie, "hoo=ray"
|
|
633
|
+
|
|
634
|
+
refute_equal left, right
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
def test_eq_returns_false_when_compared_to_object_without_to_a
|
|
638
|
+
left = HTTP::Headers.new
|
|
639
|
+
left.add :accept, "text/plain"
|
|
640
|
+
|
|
641
|
+
refute_equal left, 42
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
# -- #dup -------------------------------------------------------------------
|
|
645
|
+
|
|
646
|
+
def test_dup_returns_an_http_headers_instance
|
|
647
|
+
headers.set :content_type, "application/json"
|
|
648
|
+
dupped = headers.dup
|
|
649
|
+
|
|
650
|
+
assert_kind_of HTTP::Headers, dupped
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
def test_dup_is_not_the_same_object
|
|
654
|
+
headers.set :content_type, "application/json"
|
|
655
|
+
dupped = headers.dup
|
|
656
|
+
|
|
657
|
+
refute_same headers, dupped
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def test_dup_has_headers_copied
|
|
661
|
+
headers.set :content_type, "application/json"
|
|
662
|
+
dupped = headers.dup
|
|
663
|
+
|
|
664
|
+
assert_equal "application/json", dupped[:content_type]
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def test_dup_modifying_copy_modifies_dupped_copy
|
|
668
|
+
headers.set :content_type, "application/json"
|
|
669
|
+
dupped = headers.dup
|
|
670
|
+
dupped.set :content_type, "text/plain"
|
|
671
|
+
|
|
672
|
+
assert_equal "text/plain", dupped[:content_type]
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
def test_dup_modifying_copy_does_not_affect_original_headers
|
|
676
|
+
headers.set :content_type, "application/json"
|
|
677
|
+
dupped = headers.dup
|
|
678
|
+
dupped.set :content_type, "text/plain"
|
|
679
|
+
|
|
680
|
+
assert_equal "application/json", headers[:content_type]
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def test_dup_deep_copies_internal_pile_entries
|
|
684
|
+
headers.set :content_type, "application/json"
|
|
685
|
+
dupped = headers.dup
|
|
686
|
+
|
|
687
|
+
original_pile = headers.instance_variable_get(:@pile)
|
|
688
|
+
dupped_pile = dupped.instance_variable_get(:@pile)
|
|
689
|
+
|
|
690
|
+
# The outer arrays should be different objects
|
|
691
|
+
refute_same original_pile, dupped_pile
|
|
692
|
+
|
|
693
|
+
# Each inner array should also be a different object
|
|
694
|
+
original_pile.each_with_index do |entry, i|
|
|
695
|
+
refute_same entry, dupped_pile[i]
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
# -- validate_value (via #add) ----------------------------------------------
|
|
700
|
+
|
|
701
|
+
def test_validate_value_raises_header_error_when_value_contains_newline
|
|
702
|
+
err = assert_raises(HTTP::HeaderError) { headers.add "X-Test", "foo\nbar" }
|
|
703
|
+
assert_includes err.message, "foo"
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
def test_validate_value_accepts_values_without_newlines
|
|
707
|
+
headers.add "X-Test", "foobar"
|
|
708
|
+
|
|
709
|
+
assert_equal "foobar", headers["X-Test"]
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
def test_validate_value_calls_to_s_on_non_string_values
|
|
713
|
+
numeric_value = 42
|
|
714
|
+
headers.add "X-Number", numeric_value
|
|
715
|
+
|
|
716
|
+
assert_equal "42", headers["X-Number"]
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def test_validate_value_raises_header_error_when_to_s_result_contains_newline
|
|
720
|
+
evil = fake(to_s: "good\nevil")
|
|
721
|
+
|
|
722
|
+
assert_raises(HTTP::HeaderError) { headers.add "X-Evil", evil }
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
def test_validate_value_includes_inspected_value_in_error_message
|
|
726
|
+
err = assert_raises(HTTP::HeaderError) { headers.add "Test", "bad\nvalue" }
|
|
727
|
+
|
|
728
|
+
assert_includes err.message, '"bad\nvalue"'
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def test_validate_value_raises_header_error_when_value_contains_carriage_return
|
|
732
|
+
assert_raises(HTTP::HeaderError) { headers.add "X-Test", "foo\rbar" }
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
def test_validate_value_raises_header_error_when_value_contains_crlf
|
|
736
|
+
assert_raises(HTTP::HeaderError) { headers.add "X-Test", "foo\r\nbar" }
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
# -- #merge! ----------------------------------------------------------------
|
|
740
|
+
|
|
741
|
+
def test_merge_bang_leaves_headers_not_presented_in_other_as_is
|
|
742
|
+
headers.set :host, "example.com"
|
|
743
|
+
headers.set :accept, "application/json"
|
|
744
|
+
headers.merge! accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
745
|
+
|
|
746
|
+
assert_equal "example.com", headers[:host]
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
def test_merge_bang_overwrites_existing_values
|
|
750
|
+
headers.set :host, "example.com"
|
|
751
|
+
headers.set :accept, "application/json"
|
|
752
|
+
headers.merge! accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
753
|
+
|
|
754
|
+
assert_equal "plain/text", headers[:accept]
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
def test_merge_bang_appends_other_headers_not_presented_in_base
|
|
758
|
+
headers.set :host, "example.com"
|
|
759
|
+
headers.set :accept, "application/json"
|
|
760
|
+
headers.merge! accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
761
|
+
|
|
762
|
+
assert_equal %w[hoo=ray woo=hoo], headers[:cookie]
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
def test_merge_bang_accepts_an_http_headers_instance
|
|
766
|
+
other = HTTP::Headers.new
|
|
767
|
+
other.set :accept, "text/xml"
|
|
768
|
+
|
|
769
|
+
h = HTTP::Headers.new
|
|
770
|
+
h.set :accept, "application/json"
|
|
771
|
+
h.merge!(other)
|
|
772
|
+
|
|
773
|
+
assert_equal "text/xml", h[:accept]
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
def test_merge_bang_uses_set_so_existing_values_are_replaced
|
|
777
|
+
h = HTTP::Headers.new
|
|
778
|
+
h.add :accept, "text/html"
|
|
779
|
+
h.add :accept, "text/plain"
|
|
780
|
+
h[:accept] = "application/json"
|
|
781
|
+
|
|
782
|
+
assert_equal "application/json", h[:accept]
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
# -- #merge -----------------------------------------------------------------
|
|
786
|
+
|
|
787
|
+
def test_merge_returns_an_http_headers_instance
|
|
788
|
+
headers.set :host, "example.com"
|
|
789
|
+
headers.set :accept, "application/json"
|
|
790
|
+
merged = headers.merge accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
791
|
+
|
|
792
|
+
assert_kind_of HTTP::Headers, merged
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
def test_merge_is_not_the_same_object
|
|
796
|
+
headers.set :host, "example.com"
|
|
797
|
+
headers.set :accept, "application/json"
|
|
798
|
+
merged = headers.merge accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
799
|
+
|
|
800
|
+
refute_same headers, merged
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def test_merge_does_not_affect_original_headers
|
|
804
|
+
headers.set :host, "example.com"
|
|
805
|
+
headers.set :accept, "application/json"
|
|
806
|
+
merged = headers.merge accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
807
|
+
|
|
808
|
+
refute_equal merged.to_h, headers.to_h
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
def test_merge_leaves_headers_not_presented_in_other_as_is
|
|
812
|
+
headers.set :host, "example.com"
|
|
813
|
+
headers.set :accept, "application/json"
|
|
814
|
+
merged = headers.merge accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
815
|
+
|
|
816
|
+
assert_equal "example.com", merged[:host]
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
def test_merge_overwrites_existing_values
|
|
820
|
+
headers.set :host, "example.com"
|
|
821
|
+
headers.set :accept, "application/json"
|
|
822
|
+
merged = headers.merge accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
823
|
+
|
|
824
|
+
assert_equal "plain/text", merged[:accept]
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
def test_merge_appends_other_headers_not_presented_in_base
|
|
828
|
+
headers.set :host, "example.com"
|
|
829
|
+
headers.set :accept, "application/json"
|
|
830
|
+
merged = headers.merge accept: "plain/text", cookie: %w[hoo=ray woo=hoo]
|
|
831
|
+
|
|
832
|
+
assert_equal %w[hoo=ray woo=hoo], merged[:cookie]
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
# -- .coerce ----------------------------------------------------------------
|
|
836
|
+
|
|
837
|
+
def test_coerce_accepts_any_object_that_respond_to_to_hash
|
|
838
|
+
hashie = fake(to_hash: { "accept" => "json" })
|
|
839
|
+
|
|
840
|
+
assert_equal "json", HTTP::Headers.coerce(hashie)["accept"]
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
def test_coerce_accepts_any_object_that_respond_to_to_h
|
|
844
|
+
hashie = fake(to_h: { "accept" => "json" })
|
|
845
|
+
|
|
846
|
+
assert_equal "json", HTTP::Headers.coerce(hashie)["accept"]
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
def test_coerce_accepts_any_object_that_respond_to_to_a
|
|
850
|
+
hashie = fake(to_a: [%w[accept json]])
|
|
851
|
+
|
|
852
|
+
assert_equal "json", HTTP::Headers.coerce(hashie)["accept"]
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
def test_coerce_fails_if_given_object_cannot_be_coerced
|
|
856
|
+
obj = Object.new
|
|
857
|
+
def obj.respond_to?(*); end
|
|
858
|
+
def obj.inspect = "INSPECTED"
|
|
859
|
+
def obj.to_s = "plain"
|
|
860
|
+
|
|
861
|
+
err = assert_raises(HTTP::Error) { HTTP::Headers.coerce obj }
|
|
862
|
+
assert_includes err.message, "INSPECTED"
|
|
863
|
+
end
|
|
864
|
+
|
|
865
|
+
def test_coerce_with_duplicate_header_keys_adds_all_headers
|
|
866
|
+
hdrs = { "Set-Cookie" => "hoo=ray", "set_cookie" => "woo=hoo", :set_cookie => "ta=da" }
|
|
867
|
+
expected = [%w[Set-Cookie hoo=ray], %w[set_cookie woo=hoo], %w[Set-Cookie ta=da]]
|
|
868
|
+
|
|
869
|
+
assert_equal expected.sort, HTTP::Headers.coerce(hdrs).to_a.sort
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
def test_coerce_is_aliased_as_bracket
|
|
873
|
+
result = HTTP::Headers["Content-Type" => "text/plain"]
|
|
874
|
+
|
|
875
|
+
assert_instance_of HTTP::Headers, result
|
|
876
|
+
assert_equal "text/plain", result["Content-Type"]
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
# -- .normalizer ------------------------------------------------------------
|
|
880
|
+
|
|
881
|
+
def test_normalizer_returns_a_normalizer_instance
|
|
882
|
+
assert_instance_of HTTP::Headers::Normalizer, HTTP::Headers.normalizer
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def test_normalizer_returns_the_same_instance_on_subsequent_calls
|
|
886
|
+
assert_same HTTP::Headers.normalizer, HTTP::Headers.normalizer
|
|
887
|
+
end
|
|
888
|
+
end
|