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.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +241 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +110 -13
  5. data/UPGRADING.md +491 -0
  6. data/http.gemspec +32 -29
  7. data/lib/http/base64.rb +11 -1
  8. data/lib/http/chainable/helpers.rb +62 -0
  9. data/lib/http/chainable/verbs.rb +136 -0
  10. data/lib/http/chainable.rb +232 -136
  11. data/lib/http/client.rb +158 -127
  12. data/lib/http/connection/internals.rb +141 -0
  13. data/lib/http/connection.rb +126 -97
  14. data/lib/http/content_type.rb +61 -6
  15. data/lib/http/errors.rb +25 -1
  16. data/lib/http/feature.rb +65 -5
  17. data/lib/http/features/auto_deflate.rb +124 -17
  18. data/lib/http/features/auto_inflate.rb +38 -15
  19. data/lib/http/features/caching/entry.rb +178 -0
  20. data/lib/http/features/caching/in_memory_store.rb +63 -0
  21. data/lib/http/features/caching.rb +216 -0
  22. data/lib/http/features/digest_auth.rb +234 -0
  23. data/lib/http/features/instrumentation.rb +97 -17
  24. data/lib/http/features/logging.rb +183 -5
  25. data/lib/http/features/normalize_uri.rb +17 -0
  26. data/lib/http/features/raise_error.rb +18 -3
  27. data/lib/http/form_data/composite_io.rb +106 -0
  28. data/lib/http/form_data/file.rb +95 -0
  29. data/lib/http/form_data/multipart/param.rb +62 -0
  30. data/lib/http/form_data/multipart.rb +106 -0
  31. data/lib/http/form_data/part.rb +52 -0
  32. data/lib/http/form_data/readable.rb +58 -0
  33. data/lib/http/form_data/urlencoded.rb +175 -0
  34. data/lib/http/form_data/version.rb +8 -0
  35. data/lib/http/form_data.rb +102 -0
  36. data/lib/http/headers/known.rb +3 -0
  37. data/lib/http/headers/normalizer.rb +17 -36
  38. data/lib/http/headers.rb +172 -65
  39. data/lib/http/mime_type/adapter.rb +24 -9
  40. data/lib/http/mime_type/json.rb +19 -4
  41. data/lib/http/mime_type.rb +21 -3
  42. data/lib/http/options/definitions.rb +189 -0
  43. data/lib/http/options.rb +172 -125
  44. data/lib/http/redirector.rb +80 -75
  45. data/lib/http/request/body.rb +87 -6
  46. data/lib/http/request/builder.rb +184 -0
  47. data/lib/http/request/proxy.rb +83 -0
  48. data/lib/http/request/writer.rb +76 -16
  49. data/lib/http/request.rb +214 -98
  50. data/lib/http/response/body.rb +103 -18
  51. data/lib/http/response/inflater.rb +35 -7
  52. data/lib/http/response/parser.rb +98 -4
  53. data/lib/http/response/status/reasons.rb +2 -4
  54. data/lib/http/response/status.rb +141 -31
  55. data/lib/http/response.rb +219 -61
  56. data/lib/http/retriable/delay_calculator.rb +38 -11
  57. data/lib/http/retriable/errors.rb +21 -0
  58. data/lib/http/retriable/performer.rb +82 -38
  59. data/lib/http/session.rb +280 -0
  60. data/lib/http/timeout/global.rb +147 -34
  61. data/lib/http/timeout/null.rb +155 -9
  62. data/lib/http/timeout/per_operation.rb +139 -18
  63. data/lib/http/uri/normalizer.rb +82 -0
  64. data/lib/http/uri/parsing.rb +182 -0
  65. data/lib/http/uri.rb +289 -124
  66. data/lib/http/version.rb +2 -1
  67. data/lib/http.rb +11 -2
  68. data/sig/deps.rbs +122 -0
  69. data/sig/http.rbs +1619 -0
  70. data/test/http/base64_test.rb +28 -0
  71. data/test/http/client_test.rb +739 -0
  72. data/test/http/connection_test.rb +1533 -0
  73. data/test/http/content_type_test.rb +190 -0
  74. data/test/http/errors_test.rb +28 -0
  75. data/test/http/feature_test.rb +49 -0
  76. data/test/http/features/auto_deflate_test.rb +317 -0
  77. data/test/http/features/auto_inflate_test.rb +213 -0
  78. data/test/http/features/caching_test.rb +942 -0
  79. data/test/http/features/digest_auth_test.rb +996 -0
  80. data/test/http/features/instrumentation_test.rb +246 -0
  81. data/test/http/features/logging_test.rb +654 -0
  82. data/test/http/features/normalize_uri_test.rb +41 -0
  83. data/test/http/features/raise_error_test.rb +77 -0
  84. data/test/http/form_data/composite_io_test.rb +215 -0
  85. data/test/http/form_data/file_test.rb +255 -0
  86. data/test/http/form_data/fixtures/the-http-gem.info +1 -0
  87. data/test/http/form_data/multipart_test.rb +303 -0
  88. data/test/http/form_data/part_test.rb +90 -0
  89. data/test/http/form_data/urlencoded_test.rb +164 -0
  90. data/test/http/form_data_test.rb +232 -0
  91. data/test/http/headers/normalizer_test.rb +93 -0
  92. data/test/http/headers_test.rb +888 -0
  93. data/test/http/mime_type/json_test.rb +39 -0
  94. data/test/http/mime_type_test.rb +150 -0
  95. data/test/http/options/base_uri_test.rb +148 -0
  96. data/test/http/options/body_test.rb +21 -0
  97. data/test/http/options/features_test.rb +38 -0
  98. data/test/http/options/form_test.rb +21 -0
  99. data/test/http/options/headers_test.rb +32 -0
  100. data/test/http/options/json_test.rb +21 -0
  101. data/test/http/options/merge_test.rb +78 -0
  102. data/test/http/options/new_test.rb +37 -0
  103. data/test/http/options/proxy_test.rb +32 -0
  104. data/test/http/options_test.rb +575 -0
  105. data/test/http/redirector_test.rb +639 -0
  106. data/test/http/request/body_test.rb +318 -0
  107. data/test/http/request/builder_test.rb +623 -0
  108. data/test/http/request/writer_test.rb +391 -0
  109. data/test/http/request_test.rb +1733 -0
  110. data/test/http/response/body_test.rb +292 -0
  111. data/test/http/response/parser_test.rb +105 -0
  112. data/test/http/response/status_test.rb +322 -0
  113. data/test/http/response_test.rb +502 -0
  114. data/test/http/retriable/delay_calculator_test.rb +194 -0
  115. data/test/http/retriable/errors_test.rb +71 -0
  116. data/test/http/retriable/performer_test.rb +551 -0
  117. data/test/http/session_test.rb +424 -0
  118. data/test/http/timeout/global_test.rb +239 -0
  119. data/test/http/timeout/null_test.rb +218 -0
  120. data/test/http/timeout/per_operation_test.rb +220 -0
  121. data/test/http/uri/normalizer_test.rb +89 -0
  122. data/test/http/uri_test.rb +1140 -0
  123. data/test/http/version_test.rb +15 -0
  124. data/test/http_test.rb +818 -0
  125. data/test/regression_tests.rb +27 -0
  126. data/test/support/dummy_server/encoding_routes.rb +47 -0
  127. data/test/support/dummy_server/routes.rb +201 -0
  128. data/test/support/dummy_server/servlet.rb +81 -0
  129. data/test/support/dummy_server.rb +200 -0
  130. data/{spec → test}/support/fakeio.rb +2 -2
  131. data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
  132. data/test/support/http_handling_shared/timeout_tests.rb +134 -0
  133. data/test/support/http_handling_shared.rb +11 -0
  134. data/test/support/proxy_server.rb +207 -0
  135. data/test/support/servers/runner.rb +67 -0
  136. data/{spec → test}/support/simplecov.rb +11 -2
  137. data/test/support/ssl_helper.rb +108 -0
  138. data/test/test_helper.rb +38 -0
  139. metadata +108 -168
  140. data/.github/workflows/ci.yml +0 -67
  141. data/.gitignore +0 -15
  142. data/.rspec +0 -1
  143. data/.rubocop/layout.yml +0 -8
  144. data/.rubocop/metrics.yml +0 -4
  145. data/.rubocop/rspec.yml +0 -9
  146. data/.rubocop/style.yml +0 -32
  147. data/.rubocop.yml +0 -11
  148. data/.rubocop_todo.yml +0 -219
  149. data/.yardopts +0 -2
  150. data/CHANGES_OLD.md +0 -1002
  151. data/Gemfile +0 -51
  152. data/Guardfile +0 -18
  153. data/Rakefile +0 -64
  154. data/lib/http/headers/mixin.rb +0 -34
  155. data/lib/http/retriable/client.rb +0 -37
  156. data/logo.png +0 -0
  157. data/spec/lib/http/client_spec.rb +0 -556
  158. data/spec/lib/http/connection_spec.rb +0 -88
  159. data/spec/lib/http/content_type_spec.rb +0 -47
  160. data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
  161. data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
  162. data/spec/lib/http/features/instrumentation_spec.rb +0 -81
  163. data/spec/lib/http/features/logging_spec.rb +0 -65
  164. data/spec/lib/http/features/raise_error_spec.rb +0 -62
  165. data/spec/lib/http/headers/mixin_spec.rb +0 -36
  166. data/spec/lib/http/headers/normalizer_spec.rb +0 -52
  167. data/spec/lib/http/headers_spec.rb +0 -527
  168. data/spec/lib/http/options/body_spec.rb +0 -15
  169. data/spec/lib/http/options/features_spec.rb +0 -33
  170. data/spec/lib/http/options/form_spec.rb +0 -15
  171. data/spec/lib/http/options/headers_spec.rb +0 -24
  172. data/spec/lib/http/options/json_spec.rb +0 -15
  173. data/spec/lib/http/options/merge_spec.rb +0 -68
  174. data/spec/lib/http/options/new_spec.rb +0 -30
  175. data/spec/lib/http/options/proxy_spec.rb +0 -20
  176. data/spec/lib/http/options_spec.rb +0 -13
  177. data/spec/lib/http/redirector_spec.rb +0 -530
  178. data/spec/lib/http/request/body_spec.rb +0 -211
  179. data/spec/lib/http/request/writer_spec.rb +0 -121
  180. data/spec/lib/http/request_spec.rb +0 -234
  181. data/spec/lib/http/response/body_spec.rb +0 -85
  182. data/spec/lib/http/response/parser_spec.rb +0 -74
  183. data/spec/lib/http/response/status_spec.rb +0 -253
  184. data/spec/lib/http/response_spec.rb +0 -262
  185. data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
  186. data/spec/lib/http/retriable/performer_spec.rb +0 -302
  187. data/spec/lib/http/uri/normalizer_spec.rb +0 -95
  188. data/spec/lib/http/uri_spec.rb +0 -71
  189. data/spec/lib/http_spec.rb +0 -535
  190. data/spec/regression_specs.rb +0 -24
  191. data/spec/spec_helper.rb +0 -89
  192. data/spec/support/black_hole.rb +0 -13
  193. data/spec/support/dummy_server/servlet.rb +0 -203
  194. data/spec/support/dummy_server.rb +0 -44
  195. data/spec/support/fuubar.rb +0 -21
  196. data/spec/support/http_handling_shared.rb +0 -190
  197. data/spec/support/proxy_server.rb +0 -39
  198. data/spec/support/servers/config.rb +0 -11
  199. data/spec/support/servers/runner.rb +0 -19
  200. data/spec/support/ssl_helper.rb +0 -104
  201. /data/{spec → test}/support/capture_warning.rb +0 -0
@@ -0,0 +1,1733 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPRequestTest < Minitest::Test
6
+ cover "HTTP::Request*"
7
+
8
+ def build_request(verb: :get, uri: "http://example.com/foo?bar=baz", headers: { accept: "text/html" }, proxy: {},
9
+ **)
10
+ HTTP::Request.new(verb: verb, uri: uri, headers: headers, proxy: proxy, **)
11
+ end
12
+
13
+ # #initialize
14
+
15
+ def test_initialize_provides_a_headers_accessor
16
+ assert_kind_of HTTP::Headers, build_request.headers
17
+ end
18
+
19
+ def test_initialize_provides_a_scheme_accessor
20
+ assert_equal :http, build_request.scheme
21
+ end
22
+
23
+ def test_initialize_provides_a_verb_accessor
24
+ assert_equal :get, build_request.verb
25
+ end
26
+
27
+ def test_initialize_provides_a_uri_accessor
28
+ assert_equal HTTP::URI.parse("http://example.com/foo?bar=baz"), build_request.uri
29
+ end
30
+
31
+ def test_initialize_provides_a_proxy_accessor
32
+ assert_equal({}, build_request.proxy)
33
+ end
34
+
35
+ def test_initialize_provides_a_version_accessor_defaulting_to_1_1
36
+ assert_equal "1.1", build_request.version
37
+ end
38
+
39
+ def test_initialize_provides_a_body_accessor
40
+ assert_instance_of HTTP::Request::Body, build_request.body
41
+ end
42
+
43
+ def test_initialize_provides_a_uri_normalizer_accessor
44
+ assert_equal HTTP::URI::NORMALIZER, build_request.uri_normalizer
45
+ end
46
+
47
+ def test_initialize_stores_a_custom_uri_normalizer
48
+ custom = ->(uri) { HTTP::URI.parse(uri) }
49
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/", uri_normalizer: custom)
50
+
51
+ assert_equal custom, req.uri_normalizer
52
+ end
53
+
54
+ def test_initialize_stores_a_custom_version
55
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/", version: "2.0")
56
+
57
+ assert_equal "2.0", req.version
58
+ end
59
+
60
+ def test_initialize_stores_the_proxy_hash
61
+ p = { proxy_address: "proxy.example.com", proxy_port: 8080 }
62
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/", proxy: p)
63
+
64
+ assert_equal p, req.proxy
65
+ end
66
+
67
+ def test_initialize_downcases_and_symbolizes_the_verb
68
+ req = HTTP::Request.new(verb: "POST", uri: "http://example.com/")
69
+
70
+ assert_equal :post, req.verb
71
+ end
72
+
73
+ def test_initialize_downcases_the_scheme
74
+ req = HTTP::Request.new(verb: :get, uri: "HTTP://example.com/")
75
+
76
+ assert_equal :http, req.scheme
77
+ end
78
+
79
+ def test_initialize_accepts_https_scheme
80
+ req = HTTP::Request.new(verb: :get, uri: "https://example.com/")
81
+
82
+ assert_equal :https, req.scheme
83
+ end
84
+
85
+ def test_initialize_accepts_ws_scheme
86
+ req = HTTP::Request.new(verb: :get, uri: "ws://example.com/")
87
+
88
+ assert_equal :ws, req.scheme
89
+ end
90
+
91
+ def test_initialize_accepts_wss_scheme
92
+ req = HTTP::Request.new(verb: :get, uri: "wss://example.com/")
93
+
94
+ assert_equal :wss, req.scheme
95
+ end
96
+
97
+ def test_initialize_stores_body_source
98
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/", body: "hello")
99
+
100
+ assert_equal "hello", req.body.source
101
+ end
102
+
103
+ def test_initialize_wraps_non_body_body_in_body_object
104
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/", body: "hello")
105
+
106
+ assert_instance_of HTTP::Request::Body, req.body
107
+ end
108
+
109
+ def test_initialize_passes_through_an_existing_body_object
110
+ existing_body = HTTP::Request::Body.new("hello")
111
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/", body: existing_body)
112
+
113
+ assert_same existing_body, req.body
114
+ end
115
+
116
+ def test_initialize_passes_through_a_body_subclass
117
+ subclass_body = Class.new(HTTP::Request::Body).new("hello")
118
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/", body: subclass_body)
119
+
120
+ assert_same subclass_body, req.body
121
+ end
122
+
123
+ def test_initialize_sets_given_headers
124
+ assert_equal "text/html", build_request.headers["Accept"]
125
+ end
126
+
127
+ def test_initialize_raises_invalid_error_for_uri_without_scheme
128
+ err = assert_raises(HTTP::URI::InvalidError) do
129
+ HTTP::Request.new(verb: :get, uri: "example.com/")
130
+ end
131
+ assert_match(/invalid URI/, err.message)
132
+ end
133
+
134
+ def test_initialize_raises_argument_error_for_nil_uri
135
+ err = assert_raises(ArgumentError) do
136
+ HTTP::Request.new(verb: :get, uri: nil)
137
+ end
138
+ assert_equal "uri is nil", err.message
139
+ end
140
+
141
+ def test_initialize_raises_argument_error_for_empty_string_uri
142
+ err = assert_raises(ArgumentError) do
143
+ HTTP::Request.new(verb: :get, uri: "")
144
+ end
145
+ assert_equal "uri is empty", err.message
146
+ end
147
+
148
+ def test_initialize_does_not_raise_for_non_string_non_empty_uri_like_objects
149
+ uri = HTTP::URI.parse("http://example.com/")
150
+ req = HTTP::Request.new(verb: :get, uri: uri)
151
+
152
+ assert_equal :http, req.scheme
153
+ end
154
+
155
+ def test_initialize_raises_invalid_error_for_malformed_uri
156
+ err = assert_raises(HTTP::URI::InvalidError) do
157
+ HTTP::Request.new(verb: :get, uri: ":")
158
+ end
159
+ assert_match(/invalid URI/, err.message)
160
+ end
161
+
162
+ def test_initialize_raises_unsupported_scheme_error_for_unsupported_scheme
163
+ err = assert_raises(HTTP::Request::UnsupportedSchemeError) do
164
+ HTTP::Request.new(verb: :get, uri: "ftp://example.com/")
165
+ end
166
+ assert_match(/unknown scheme/, err.message)
167
+ end
168
+
169
+ def test_initialize_raises_unsupported_method_error_for_unknown_verbs
170
+ err = assert_raises(HTTP::Request::UnsupportedMethodError) do
171
+ HTTP::Request.new(verb: :foobar, uri: "http://example.com/")
172
+ end
173
+ assert_match(/unknown method/, err.message)
174
+ end
175
+
176
+ def test_initialize_includes_verb_in_unsupported_method_error_message
177
+ err = assert_raises(HTTP::Request::UnsupportedMethodError) do
178
+ HTTP::Request.new(verb: :foobar, uri: "http://example.com/")
179
+ end
180
+
181
+ assert_includes err.message, "foobar"
182
+ end
183
+
184
+ def test_initialize_includes_uri_in_invalid_error_message_for_missing_scheme
185
+ err = assert_raises(HTTP::URI::InvalidError) do
186
+ HTTP::Request.new(verb: :get, uri: "example.com/")
187
+ end
188
+
189
+ assert_includes err.message, "example.com/"
190
+ end
191
+
192
+ def test_initialize_includes_scheme_in_unsupported_scheme_error_message
193
+ err = assert_raises(HTTP::Request::UnsupportedSchemeError) do
194
+ HTTP::Request.new(verb: :get, uri: "ftp://example.com/")
195
+ end
196
+
197
+ assert_includes err.message, "ftp"
198
+ end
199
+
200
+ def test_initialize_defaults_proxy_to_an_empty_hash
201
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/")
202
+
203
+ assert_equal({}, req.proxy)
204
+ end
205
+
206
+ def test_initialize_sets_default_headers_when_headers_arg_is_nil
207
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/")
208
+
209
+ assert_equal "example.com", req.headers["Host"]
210
+ assert_equal HTTP::Request::USER_AGENT, req.headers["User-Agent"]
211
+ end
212
+
213
+ # Host header
214
+
215
+ def test_host_header_defaults_to_the_host_from_the_uri
216
+ assert_equal "example.com", build_request.headers["Host"]
217
+ end
218
+
219
+ def test_host_header_with_non_standard_port_includes_the_port
220
+ request = build_request(uri: "http://example.com:3000/")
221
+
222
+ assert_equal "example.com:3000", request.headers["Host"]
223
+ end
224
+
225
+ def test_host_header_with_standard_https_port_omits_the_port
226
+ request = build_request(uri: "https://example.com/")
227
+
228
+ assert_equal "example.com", request.headers["Host"]
229
+ end
230
+
231
+ def test_host_header_with_non_standard_https_port_includes_the_port
232
+ request = build_request(uri: "https://example.com:8443/")
233
+
234
+ assert_equal "example.com:8443", request.headers["Host"]
235
+ end
236
+
237
+ def test_host_header_when_explicitly_given_uses_the_given_host
238
+ request = build_request(headers: { accept: "text/html", host: "github.com" })
239
+
240
+ assert_equal "github.com", request.headers["Host"]
241
+ end
242
+
243
+ def test_host_header_when_host_contains_whitespace_raises_request_error
244
+ normalizer = lambda { |uri|
245
+ u = HTTP::URI.parse(uri)
246
+ u.host = "exam ple.com"
247
+ u
248
+ }
249
+
250
+ assert_raises(HTTP::RequestError) do
251
+ HTTP::Request.new(verb: :get, uri: "http://example.com/", uri_normalizer: normalizer)
252
+ end
253
+ end
254
+
255
+ def test_host_header_when_host_contains_whitespace_includes_invalid_host_in_error
256
+ normalizer = lambda { |uri|
257
+ u = HTTP::URI.parse(uri)
258
+ u.host = "exam ple.com"
259
+ u
260
+ }
261
+
262
+ err = assert_raises(HTTP::RequestError) do
263
+ HTTP::Request.new(verb: :get, uri: "http://example.com/", uri_normalizer: normalizer)
264
+ end
265
+
266
+ assert_includes err.message, "exam ple.com".inspect
267
+ end
268
+
269
+ # User-Agent header
270
+
271
+ def test_user_agent_header_defaults_to_http_request_user_agent
272
+ assert_equal HTTP::Request::USER_AGENT, build_request.headers["User-Agent"]
273
+ end
274
+
275
+ def test_user_agent_header_when_explicitly_given_uses_it
276
+ request = build_request(headers: { accept: "text/html", user_agent: "MrCrawly/123" })
277
+
278
+ assert_equal "MrCrawly/123", request.headers["User-Agent"]
279
+ end
280
+
281
+ # #using_proxy?
282
+
283
+ def test_using_proxy_with_empty_proxy_hash_returns_false
284
+ refute_predicate build_request(proxy: {}), :using_proxy?
285
+ end
286
+
287
+ def test_using_proxy_with_one_key_in_proxy_returns_false
288
+ refute_predicate build_request(proxy: { proxy_address: "proxy.example.com" }), :using_proxy?
289
+ end
290
+
291
+ def test_using_proxy_with_two_keys_in_proxy_returns_true
292
+ assert_predicate build_request(proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 }), :using_proxy?
293
+ end
294
+
295
+ def test_using_proxy_with_four_keys_in_proxy_returns_true
296
+ proxy = { proxy_address: "proxy.example.com", proxy_port: 8080,
297
+ proxy_username: "user", proxy_password: "pass" }
298
+
299
+ assert_predicate build_request(proxy: proxy), :using_proxy?
300
+ end
301
+
302
+ # #using_authenticated_proxy?
303
+
304
+ def test_using_authenticated_proxy_with_empty_proxy_hash_returns_false
305
+ refute_predicate build_request(proxy: {}), :using_authenticated_proxy?
306
+ end
307
+
308
+ def test_using_authenticated_proxy_with_two_keys_returns_false
309
+ refute_predicate build_request(proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 }),
310
+ :using_authenticated_proxy?
311
+ end
312
+
313
+ def test_using_authenticated_proxy_with_three_keys_returns_false
314
+ proxy = { proxy_address: "proxy.example.com", proxy_port: 8080, proxy_username: "user" }
315
+
316
+ refute_predicate build_request(proxy: proxy), :using_authenticated_proxy?
317
+ end
318
+
319
+ def test_using_authenticated_proxy_with_four_keys_returns_true
320
+ proxy = { proxy_address: "proxy.example.com", proxy_port: 8080,
321
+ proxy_username: "user", proxy_password: "pass" }
322
+
323
+ assert_predicate build_request(proxy: proxy), :using_authenticated_proxy?
324
+ end
325
+
326
+ # #redirect
327
+
328
+ def test_redirect_has_correct_uri
329
+ request = build_request(
330
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
331
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
332
+ )
333
+ redirected = request.redirect("http://blog.example.com/")
334
+
335
+ assert_equal HTTP::URI.parse("http://blog.example.com/"), redirected.uri
336
+ end
337
+
338
+ def test_redirect_has_correct_verb
339
+ request = build_request(
340
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
341
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
342
+ )
343
+ redirected = request.redirect("http://blog.example.com/")
344
+
345
+ assert_equal request.verb, redirected.verb
346
+ end
347
+
348
+ def test_redirect_has_correct_body
349
+ request = build_request(
350
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
351
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
352
+ )
353
+ redirected = request.redirect("http://blog.example.com/")
354
+
355
+ assert_equal request.body, redirected.body
356
+ end
357
+
358
+ def test_redirect_has_correct_proxy
359
+ request = build_request(
360
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
361
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
362
+ )
363
+ redirected = request.redirect("http://blog.example.com/")
364
+
365
+ assert_equal request.proxy, redirected.proxy
366
+ end
367
+
368
+ def test_redirect_presets_new_host_header
369
+ request = build_request(
370
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
371
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
372
+ )
373
+ redirected = request.redirect("http://blog.example.com/")
374
+
375
+ assert_equal "blog.example.com", redirected.headers["Host"]
376
+ end
377
+
378
+ def test_redirect_preserves_version
379
+ req = HTTP::Request.new(
380
+ verb: :post, uri: "http://example.com/", body: "The Ultimate Question", version: "2.0"
381
+ )
382
+ redir = req.redirect("http://blog.example.com/")
383
+
384
+ assert_equal "2.0", redir.version
385
+ end
386
+
387
+ def test_redirect_preserves_uri_normalizer
388
+ custom = ->(uri) { HTTP::URI.parse(uri) }
389
+ req = HTTP::Request.new(
390
+ verb: :post, uri: "http://example.com/", body: "The Ultimate Question", uri_normalizer: custom
391
+ )
392
+ redir = req.redirect("http://blog.example.com/")
393
+
394
+ assert_equal custom, redir.uri_normalizer
395
+ end
396
+
397
+ def test_redirect_preserves_accept_header
398
+ request = build_request(
399
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
400
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
401
+ )
402
+ redirected = request.redirect("http://blog.example.com/")
403
+
404
+ assert_equal "text/html", redirected.headers["Accept"]
405
+ end
406
+
407
+ # redirect with non-standard port
408
+
409
+ def test_redirect_with_non_standard_port_has_correct_uri
410
+ request = build_request(
411
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
412
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
413
+ )
414
+ redirected = request.redirect("http://example.com:8080")
415
+
416
+ assert_equal HTTP::URI.parse("http://example.com:8080"), redirected.uri
417
+ end
418
+
419
+ def test_redirect_with_non_standard_port_has_correct_verb
420
+ request = build_request(
421
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
422
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
423
+ )
424
+ redirected = request.redirect("http://example.com:8080")
425
+
426
+ assert_equal request.verb, redirected.verb
427
+ end
428
+
429
+ def test_redirect_with_non_standard_port_has_correct_body
430
+ request = build_request(
431
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
432
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
433
+ )
434
+ redirected = request.redirect("http://example.com:8080")
435
+
436
+ assert_equal request.body, redirected.body
437
+ end
438
+
439
+ def test_redirect_with_non_standard_port_has_correct_proxy
440
+ request = build_request(
441
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
442
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
443
+ )
444
+ redirected = request.redirect("http://example.com:8080")
445
+
446
+ assert_equal request.proxy, redirected.proxy
447
+ end
448
+
449
+ def test_redirect_with_non_standard_port_presets_new_host_header
450
+ request = build_request(
451
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
452
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
453
+ )
454
+ redirected = request.redirect("http://example.com:8080")
455
+
456
+ assert_equal "example.com:8080", redirected.headers["Host"]
457
+ end
458
+
459
+ # redirect with schema-less absolute URL
460
+
461
+ def test_redirect_with_schema_less_absolute_url_has_correct_uri
462
+ request = build_request(
463
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
464
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
465
+ )
466
+ redirected = request.redirect("//another.example.com/blog")
467
+
468
+ assert_equal HTTP::URI.parse("http://another.example.com/blog"), redirected.uri
469
+ end
470
+
471
+ def test_redirect_with_schema_less_absolute_url_has_correct_verb
472
+ request = build_request(
473
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
474
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
475
+ )
476
+ redirected = request.redirect("//another.example.com/blog")
477
+
478
+ assert_equal request.verb, redirected.verb
479
+ end
480
+
481
+ def test_redirect_with_schema_less_absolute_url_has_correct_body
482
+ request = build_request(
483
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
484
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
485
+ )
486
+ redirected = request.redirect("//another.example.com/blog")
487
+
488
+ assert_equal request.body, redirected.body
489
+ end
490
+
491
+ def test_redirect_with_schema_less_absolute_url_has_correct_proxy
492
+ request = build_request(
493
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
494
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
495
+ )
496
+ redirected = request.redirect("//another.example.com/blog")
497
+
498
+ assert_equal request.proxy, redirected.proxy
499
+ end
500
+
501
+ def test_redirect_with_schema_less_absolute_url_presets_new_host_header
502
+ request = build_request(
503
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
504
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
505
+ )
506
+ redirected = request.redirect("//another.example.com/blog")
507
+
508
+ assert_equal "another.example.com", redirected.headers["Host"]
509
+ end
510
+
511
+ # redirect with relative URL
512
+
513
+ def test_redirect_with_relative_url_has_correct_uri
514
+ request = build_request(
515
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
516
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
517
+ )
518
+ redirected = request.redirect("/blog")
519
+
520
+ assert_equal HTTP::URI.parse("http://example.com/blog"), redirected.uri
521
+ end
522
+
523
+ def test_redirect_with_relative_url_has_correct_verb
524
+ request = build_request(
525
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
526
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
527
+ )
528
+ redirected = request.redirect("/blog")
529
+
530
+ assert_equal request.verb, redirected.verb
531
+ end
532
+
533
+ def test_redirect_with_relative_url_has_correct_body
534
+ request = build_request(
535
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
536
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
537
+ )
538
+ redirected = request.redirect("/blog")
539
+
540
+ assert_equal request.body, redirected.body
541
+ end
542
+
543
+ def test_redirect_with_relative_url_has_correct_proxy
544
+ request = build_request(
545
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
546
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
547
+ )
548
+ redirected = request.redirect("/blog")
549
+
550
+ assert_equal request.proxy, redirected.proxy
551
+ end
552
+
553
+ def test_redirect_with_relative_url_keeps_host_header
554
+ request = build_request(
555
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
556
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
557
+ )
558
+ redirected = request.redirect("/blog")
559
+
560
+ assert_equal "example.com", redirected.headers["Host"]
561
+ end
562
+
563
+ def test_redirect_with_relative_url_and_non_standard_port_has_correct_uri
564
+ request = HTTP::Request.new(
565
+ verb: :post, uri: "http://example.com:8080/",
566
+ headers: { accept: "text/html" },
567
+ proxy: { proxy_username: "douglas", proxy_password: "adams" },
568
+ body: "The Ultimate Question"
569
+ )
570
+ redirected = request.redirect("/blog")
571
+
572
+ assert_equal HTTP::URI.parse("http://example.com:8080/blog"), redirected.uri
573
+ end
574
+
575
+ # redirect with relative URL missing leading slash
576
+
577
+ def test_redirect_with_relative_url_missing_leading_slash_has_correct_uri
578
+ request = build_request(
579
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
580
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
581
+ )
582
+ redirected = request.redirect("blog")
583
+
584
+ assert_equal HTTP::URI.parse("http://example.com/blog"), redirected.uri
585
+ end
586
+
587
+ def test_redirect_with_relative_url_missing_leading_slash_has_correct_verb
588
+ request = build_request(
589
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
590
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
591
+ )
592
+ redirected = request.redirect("blog")
593
+
594
+ assert_equal request.verb, redirected.verb
595
+ end
596
+
597
+ def test_redirect_with_relative_url_missing_leading_slash_has_correct_body
598
+ request = build_request(
599
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
600
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
601
+ )
602
+ redirected = request.redirect("blog")
603
+
604
+ assert_equal request.body, redirected.body
605
+ end
606
+
607
+ def test_redirect_with_relative_url_missing_leading_slash_has_correct_proxy
608
+ request = build_request(
609
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
610
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
611
+ )
612
+ redirected = request.redirect("blog")
613
+
614
+ assert_equal request.proxy, redirected.proxy
615
+ end
616
+
617
+ def test_redirect_with_relative_url_missing_leading_slash_keeps_host_header
618
+ request = build_request(
619
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
620
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
621
+ )
622
+ redirected = request.redirect("blog")
623
+
624
+ assert_equal "example.com", redirected.headers["Host"]
625
+ end
626
+
627
+ def test_redirect_with_relative_url_missing_leading_slash_and_non_standard_port
628
+ request = HTTP::Request.new(
629
+ verb: :post, uri: "http://example.com:8080/",
630
+ headers: { accept: "text/html" },
631
+ proxy: { proxy_username: "douglas", proxy_password: "adams" },
632
+ body: "The Ultimate Question"
633
+ )
634
+ redirected = request.redirect("blog")
635
+
636
+ assert_equal HTTP::URI.parse("http://example.com:8080/blog"), redirected.uri
637
+ end
638
+
639
+ # redirect with new verb
640
+
641
+ def test_redirect_with_new_verb_has_correct_verb
642
+ request = build_request(
643
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
644
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
645
+ )
646
+ redirected = request.redirect("http://blog.example.com/", :get)
647
+
648
+ assert_equal :get, redirected.verb
649
+ end
650
+
651
+ def test_redirect_with_new_verb_sets_body_to_nil_for_get_redirect
652
+ request = build_request(
653
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
654
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
655
+ )
656
+ redirected = request.redirect("http://blog.example.com/", :get)
657
+
658
+ assert_nil redirected.body.source
659
+ end
660
+
661
+ def test_redirect_with_verb_changed_to_non_get_preserves_body
662
+ request = build_request(
663
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
664
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
665
+ )
666
+ redir = request.redirect("http://blog.example.com/", :put)
667
+
668
+ assert_equal "The Ultimate Question", redir.body.source
669
+ end
670
+
671
+ # redirect with sensitive headers - same origin
672
+
673
+ def test_redirect_same_origin_preserves_authorization_header
674
+ request = build_request(
675
+ verb: :post, uri: "http://example.com/",
676
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
677
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
678
+ )
679
+ redirected = request.redirect("/other-path")
680
+
681
+ assert_equal "Bearer token123", redirected.headers["Authorization"]
682
+ end
683
+
684
+ def test_redirect_same_origin_preserves_cookie_header
685
+ request = build_request(
686
+ verb: :post, uri: "http://example.com/",
687
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
688
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
689
+ )
690
+ redirected = request.redirect("/other-path")
691
+
692
+ assert_equal "session=abc", redirected.headers["Cookie"]
693
+ end
694
+
695
+ # redirect with sensitive headers - different host
696
+
697
+ def test_redirect_different_host_strips_authorization_header
698
+ request = build_request(
699
+ verb: :post, uri: "http://example.com/",
700
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
701
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
702
+ )
703
+ redirected = request.redirect("http://other.example.com/")
704
+
705
+ assert_nil redirected.headers["Authorization"]
706
+ end
707
+
708
+ def test_redirect_different_host_strips_cookie_header
709
+ request = build_request(
710
+ verb: :post, uri: "http://example.com/",
711
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
712
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
713
+ )
714
+ redirected = request.redirect("http://other.example.com/")
715
+
716
+ assert_nil redirected.headers["Cookie"]
717
+ end
718
+
719
+ # redirect with sensitive headers - different scheme
720
+
721
+ def test_redirect_different_scheme_strips_authorization_header
722
+ request = build_request(
723
+ verb: :post, uri: "http://example.com/",
724
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
725
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
726
+ )
727
+ redirected = request.redirect("https://example.com/")
728
+
729
+ assert_nil redirected.headers["Authorization"]
730
+ end
731
+
732
+ def test_redirect_different_scheme_strips_cookie_header
733
+ request = build_request(
734
+ verb: :post, uri: "http://example.com/",
735
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
736
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
737
+ )
738
+ redirected = request.redirect("https://example.com/")
739
+
740
+ assert_nil redirected.headers["Cookie"]
741
+ end
742
+
743
+ # redirect with sensitive headers - different port
744
+
745
+ def test_redirect_different_port_strips_authorization_header
746
+ request = build_request(
747
+ verb: :post, uri: "http://example.com/",
748
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
749
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
750
+ )
751
+ redirected = request.redirect("http://example.com:8080/")
752
+
753
+ assert_nil redirected.headers["Authorization"]
754
+ end
755
+
756
+ def test_redirect_different_port_strips_cookie_header
757
+ request = build_request(
758
+ verb: :post, uri: "http://example.com/",
759
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
760
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
761
+ )
762
+ redirected = request.redirect("http://example.com:8080/")
763
+
764
+ assert_nil redirected.headers["Cookie"]
765
+ end
766
+
767
+ # redirect with sensitive headers - schema-less URL with different host
768
+
769
+ def test_redirect_schema_less_different_host_strips_authorization_header
770
+ request = build_request(
771
+ verb: :post, uri: "http://example.com/",
772
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
773
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
774
+ )
775
+ redirected = request.redirect("//other.example.com/path")
776
+
777
+ assert_nil redirected.headers["Authorization"]
778
+ end
779
+
780
+ def test_redirect_schema_less_different_host_strips_cookie_header
781
+ request = build_request(
782
+ verb: :post, uri: "http://example.com/",
783
+ headers: { accept: "text/html", authorization: "Bearer token123", cookie: "session=abc" },
784
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
785
+ )
786
+ redirected = request.redirect("//other.example.com/path")
787
+
788
+ assert_nil redirected.headers["Cookie"]
789
+ end
790
+
791
+ # redirect with Content-Type header
792
+
793
+ def test_redirect_post_preserves_content_type
794
+ request = build_request(
795
+ verb: :post, uri: "http://example.com/",
796
+ headers: { accept: "text/html", content_type: "application/json" },
797
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
798
+ )
799
+ redir = request.redirect("http://blog.example.com/")
800
+
801
+ assert_equal "application/json", redir.headers["Content-Type"]
802
+ end
803
+
804
+ def test_redirect_to_get_strips_content_type
805
+ request = build_request(
806
+ verb: :post, uri: "http://example.com/",
807
+ headers: { accept: "text/html", content_type: "application/json" },
808
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
809
+ )
810
+ redir = request.redirect("http://blog.example.com/", :get)
811
+
812
+ assert_nil redir.headers["Content-Type"]
813
+ end
814
+
815
+ def test_redirect_always_strips_host_header_before_redirect
816
+ request = build_request(
817
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
818
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
819
+ )
820
+ redir = request.redirect("/other-path")
821
+
822
+ assert_equal "example.com", redir.headers["Host"]
823
+ end
824
+
825
+ def test_redirect_does_not_mutate_original_request_headers
826
+ request = build_request(
827
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
828
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
829
+ )
830
+ original_accept = request.headers["Accept"]
831
+ request.redirect("http://other.example.com/", :get)
832
+
833
+ assert_equal original_accept, request.headers["Accept"]
834
+ assert_equal "example.com", request.headers["Host"]
835
+ end
836
+
837
+ def test_redirect_preserves_body_source_on_non_get_redirect
838
+ request = build_request(
839
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
840
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
841
+ )
842
+ redir = request.redirect("http://blog.example.com/")
843
+
844
+ assert_equal "The Ultimate Question", redir.body.source
845
+ end
846
+
847
+ def test_redirect_creates_a_new_body_object
848
+ request = build_request(
849
+ verb: :post, uri: "http://example.com/", headers: { accept: "text/html" },
850
+ proxy: { proxy_username: "douglas", proxy_password: "adams" }, body: "The Ultimate Question"
851
+ )
852
+ redir = request.redirect("http://blog.example.com/")
853
+
854
+ refute_same request.body, redir.body
855
+ end
856
+
857
+ # #headline
858
+
859
+ def test_headline_returns_the_request_line
860
+ assert_equal "GET /foo?bar=baz HTTP/1.1", build_request.headline
861
+ end
862
+
863
+ def test_headline_with_encoded_query_does_not_unencode_query_part
864
+ encoded_query = "t=1970-01-01T01%3A00%3A00%2B01%3A00"
865
+ request = build_request(uri: "http://example.com/foo/?#{encoded_query}")
866
+
867
+ assert_equal "GET /foo/?#{encoded_query} HTTP/1.1", request.headline
868
+ end
869
+
870
+ def test_headline_with_non_ascii_path_encodes_non_ascii_part
871
+ request = build_request(uri: "http://example.com/\u30AD\u30E7")
872
+
873
+ assert_equal "GET /%E3%82%AD%E3%83%A7 HTTP/1.1", request.headline
874
+ end
875
+
876
+ def test_headline_with_fragment_omits_fragment_part
877
+ request = build_request(uri: "http://example.com/foo#bar")
878
+
879
+ assert_equal "GET /foo HTTP/1.1", request.headline
880
+ end
881
+
882
+ def test_headline_with_proxy_uses_absolute_uri_in_request_line
883
+ request = build_request(proxy: { user: "user", pass: "pass" })
884
+
885
+ assert_equal "GET http://example.com/foo?bar=baz HTTP/1.1", request.headline
886
+ end
887
+
888
+ def test_headline_with_proxy_and_fragment_omits_fragment
889
+ request = build_request(uri: "http://example.com/foo#bar", proxy: { user: "user", pass: "pass" })
890
+
891
+ assert_equal "GET http://example.com/foo HTTP/1.1", request.headline
892
+ end
893
+
894
+ def test_headline_with_proxy_and_https_uses_relative_uri
895
+ request = build_request(uri: "https://example.com/foo?bar=baz", proxy: { user: "user", pass: "pass" })
896
+
897
+ assert_equal "GET /foo?bar=baz HTTP/1.1", request.headline
898
+ end
899
+
900
+ def test_headline_with_custom_version_includes_version
901
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/", version: "2.0")
902
+
903
+ assert_equal "GET / HTTP/2.0", req.headline
904
+ end
905
+
906
+ def test_headline_with_non_get_verb_upcases_the_verb
907
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/")
908
+
909
+ assert_equal "POST / HTTP/1.1", req.headline
910
+ end
911
+
912
+ def test_headline_with_uri_containing_whitespace_raises_request_error
913
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/foo")
914
+ req.uri.path = "/foo bar"
915
+
916
+ err = assert_raises(HTTP::RequestError) { req.headline }
917
+
918
+ assert_includes err.message, "Invalid request URI"
919
+ assert_includes err.message, "/foo bar".inspect
920
+ end
921
+
922
+ # #socket_host
923
+
924
+ def test_socket_host_without_proxy_returns_uri_host
925
+ assert_equal "example.com", build_request(proxy: {}).socket_host
926
+ end
927
+
928
+ def test_socket_host_with_proxy_returns_proxy_address
929
+ request = build_request(proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 })
930
+
931
+ assert_equal "proxy.example.com", request.socket_host
932
+ end
933
+
934
+ # #socket_port
935
+
936
+ def test_socket_port_without_proxy_returns_uri_port
937
+ assert_equal 80, build_request(proxy: {}).socket_port
938
+ end
939
+
940
+ def test_socket_port_without_proxy_with_explicit_port_returns_explicit_port
941
+ request = build_request(uri: "http://example.com:3000/", proxy: {})
942
+
943
+ assert_equal 3000, request.socket_port
944
+ end
945
+
946
+ def test_socket_port_without_proxy_with_https_returns_443
947
+ request = build_request(uri: "https://example.com/", proxy: {})
948
+
949
+ assert_equal 443, request.socket_port
950
+ end
951
+
952
+ def test_socket_port_with_proxy_returns_proxy_port
953
+ request = build_request(proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 })
954
+
955
+ assert_equal 8080, request.socket_port
956
+ end
957
+
958
+ # #stream
959
+
960
+ def test_stream_without_proxy_writes_request_to_socket
961
+ io = StringIO.new
962
+ build_request(proxy: {}).stream(io)
963
+
964
+ assert_includes io.string, "GET /foo?bar=baz HTTP/1.1"
965
+ end
966
+
967
+ def test_stream_without_proxy_does_not_include_proxy_headers
968
+ io = StringIO.new
969
+ build_request(proxy: {}).stream(io)
970
+
971
+ refute_includes io.string, "Proxy-Authorization"
972
+ end
973
+
974
+ def test_stream_with_proxy_headers_but_not_using_proxy_does_not_include_them
975
+ io = StringIO.new
976
+ build_request(proxy: { proxy_headers: { "X-Leak" => "nope" } }).stream(io)
977
+
978
+ refute_includes io.string, "X-Leak"
979
+ end
980
+
981
+ def test_stream_with_http_proxy_merges_proxy_headers
982
+ io = StringIO.new
983
+ request = build_request(proxy: {
984
+ proxy_address: "proxy.example.com",
985
+ proxy_port: 8080,
986
+ proxy_headers: { "X-Proxy" => "value" }
987
+ })
988
+ request.stream(io)
989
+
990
+ assert_includes io.string, "X-Proxy: value"
991
+ end
992
+
993
+ def test_stream_with_https_proxy_and_proxy_headers_does_not_merge
994
+ io = StringIO.new
995
+ request = build_request(
996
+ uri: "https://example.com/foo",
997
+ proxy: {
998
+ proxy_address: "proxy.example.com",
999
+ proxy_port: 8080,
1000
+ proxy_username: "user",
1001
+ proxy_password: "pass",
1002
+ proxy_headers: { "X-Proxy" => "nope" }
1003
+ }
1004
+ )
1005
+ request.stream(io)
1006
+ output = io.string
1007
+
1008
+ refute_includes output, "X-Proxy"
1009
+ refute_includes output, "Proxy-Authorization"
1010
+ end
1011
+
1012
+ def test_stream_with_authenticated_http_proxy_includes_proxy_authorization
1013
+ io = StringIO.new
1014
+ request = build_request(proxy: {
1015
+ proxy_address: "proxy.example.com",
1016
+ proxy_port: 8080,
1017
+ proxy_username: "user",
1018
+ proxy_password: "pass"
1019
+ })
1020
+ request.stream(io)
1021
+
1022
+ assert_includes io.string, "Proxy-Authorization: Basic"
1023
+ end
1024
+
1025
+ # #connect_using_proxy
1026
+
1027
+ def test_connect_using_proxy_writes_a_connect_request
1028
+ io = StringIO.new
1029
+ request = build_request(
1030
+ uri: "https://example.com/foo",
1031
+ proxy: {
1032
+ proxy_address: "proxy.example.com",
1033
+ proxy_port: 8080,
1034
+ proxy_username: "user",
1035
+ proxy_password: "pass"
1036
+ }
1037
+ )
1038
+ request.connect_using_proxy(io)
1039
+ output = io.string
1040
+
1041
+ assert_includes output, "CONNECT example.com:443 HTTP/1.1"
1042
+ end
1043
+
1044
+ def test_connect_using_proxy_includes_proxy_auth_headers
1045
+ io = StringIO.new
1046
+ request = build_request(
1047
+ uri: "https://example.com/foo",
1048
+ proxy: {
1049
+ proxy_address: "proxy.example.com",
1050
+ proxy_port: 8080,
1051
+ proxy_username: "user",
1052
+ proxy_password: "pass"
1053
+ }
1054
+ )
1055
+ request.connect_using_proxy(io)
1056
+ output = io.string
1057
+
1058
+ assert_includes output, "Proxy-Authorization: Basic"
1059
+ end
1060
+
1061
+ def test_connect_using_proxy_includes_host_header
1062
+ io = StringIO.new
1063
+ request = build_request(
1064
+ uri: "https://example.com/foo",
1065
+ proxy: {
1066
+ proxy_address: "proxy.example.com",
1067
+ proxy_port: 8080,
1068
+ proxy_username: "user",
1069
+ proxy_password: "pass"
1070
+ }
1071
+ )
1072
+ request.connect_using_proxy(io)
1073
+ output = io.string
1074
+
1075
+ assert_includes output, "Host: example.com"
1076
+ end
1077
+
1078
+ def test_connect_using_proxy_includes_user_agent_header
1079
+ io = StringIO.new
1080
+ request = build_request(
1081
+ uri: "https://example.com/foo",
1082
+ proxy: {
1083
+ proxy_address: "proxy.example.com",
1084
+ proxy_port: 8080,
1085
+ proxy_username: "user",
1086
+ proxy_password: "pass"
1087
+ }
1088
+ )
1089
+ request.connect_using_proxy(io)
1090
+ output = io.string
1091
+
1092
+ assert_includes output, "User-Agent:"
1093
+ end
1094
+
1095
+ # #proxy_connect_header
1096
+
1097
+ def test_proxy_connect_header_returns_connect_headline
1098
+ request = build_request(uri: "https://example.com/")
1099
+
1100
+ assert_equal "CONNECT example.com:443 HTTP/1.1", request.proxy_connect_header
1101
+ end
1102
+
1103
+ def test_proxy_connect_header_with_non_standard_port_includes_the_port
1104
+ request = build_request(uri: "https://example.com:8443/")
1105
+
1106
+ assert_equal "CONNECT example.com:8443 HTTP/1.1", request.proxy_connect_header
1107
+ end
1108
+
1109
+ def test_proxy_connect_header_with_custom_version_includes_the_version
1110
+ req = HTTP::Request.new(verb: :get, uri: "https://example.com/", version: "2.0")
1111
+
1112
+ assert_equal "CONNECT example.com:443 HTTP/2.0", req.proxy_connect_header
1113
+ end
1114
+
1115
+ # #proxy_connect_headers
1116
+
1117
+ def test_proxy_connect_headers_with_authenticated_proxy_includes_proxy_authorization
1118
+ request = build_request(
1119
+ uri: "https://example.com/",
1120
+ proxy: {
1121
+ proxy_address: "proxy.example.com",
1122
+ proxy_port: 8080,
1123
+ proxy_username: "user",
1124
+ proxy_password: "pass"
1125
+ }
1126
+ )
1127
+ hdrs = request.proxy_connect_headers
1128
+
1129
+ assert_match(/^Basic /, hdrs["Proxy-Authorization"])
1130
+ end
1131
+
1132
+ def test_proxy_connect_headers_with_authenticated_proxy_includes_host_header
1133
+ request = build_request(
1134
+ uri: "https://example.com/",
1135
+ proxy: {
1136
+ proxy_address: "proxy.example.com",
1137
+ proxy_port: 8080,
1138
+ proxy_username: "user",
1139
+ proxy_password: "pass"
1140
+ }
1141
+ )
1142
+ hdrs = request.proxy_connect_headers
1143
+
1144
+ assert_equal "example.com", hdrs["Host"]
1145
+ end
1146
+
1147
+ def test_proxy_connect_headers_with_authenticated_proxy_includes_user_agent
1148
+ request = build_request(
1149
+ uri: "https://example.com/",
1150
+ proxy: {
1151
+ proxy_address: "proxy.example.com",
1152
+ proxy_port: 8080,
1153
+ proxy_username: "user",
1154
+ proxy_password: "pass"
1155
+ }
1156
+ )
1157
+ hdrs = request.proxy_connect_headers
1158
+
1159
+ assert_equal HTTP::Request::USER_AGENT, hdrs["User-Agent"]
1160
+ end
1161
+
1162
+ def test_proxy_connect_headers_with_unauthenticated_proxy_no_proxy_authorization
1163
+ request = build_request(
1164
+ uri: "https://example.com/",
1165
+ proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 }
1166
+ )
1167
+ hdrs = request.proxy_connect_headers
1168
+
1169
+ assert_nil hdrs["Proxy-Authorization"]
1170
+ end
1171
+
1172
+ def test_proxy_connect_headers_with_unauthenticated_proxy_includes_host
1173
+ request = build_request(
1174
+ uri: "https://example.com/",
1175
+ proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 }
1176
+ )
1177
+ hdrs = request.proxy_connect_headers
1178
+
1179
+ assert_equal "example.com", hdrs["Host"]
1180
+ end
1181
+
1182
+ def test_proxy_connect_headers_with_unauthenticated_proxy_includes_user_agent
1183
+ request = build_request(
1184
+ uri: "https://example.com/",
1185
+ proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 }
1186
+ )
1187
+ hdrs = request.proxy_connect_headers
1188
+
1189
+ assert_equal HTTP::Request::USER_AGENT, hdrs["User-Agent"]
1190
+ end
1191
+
1192
+ def test_proxy_connect_headers_with_proxy_headers_includes_custom_headers
1193
+ request = build_request(
1194
+ uri: "https://example.com/",
1195
+ proxy: {
1196
+ proxy_address: "proxy.example.com",
1197
+ proxy_port: 8080,
1198
+ proxy_headers: { "X-Custom" => "value" }
1199
+ }
1200
+ )
1201
+ hdrs = request.proxy_connect_headers
1202
+
1203
+ assert_equal "value", hdrs["X-Custom"]
1204
+ end
1205
+
1206
+ def test_proxy_connect_headers_without_proxy_headers_key_only_includes_host_and_user_agent
1207
+ request = build_request(
1208
+ uri: "https://example.com/",
1209
+ proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 }
1210
+ )
1211
+ hdrs = request.proxy_connect_headers
1212
+
1213
+ assert_instance_of HTTP::Headers, hdrs
1214
+ assert_equal %w[Host User-Agent], hdrs.keys
1215
+ end
1216
+
1217
+ # #include_proxy_headers
1218
+
1219
+ def test_include_proxy_headers_with_proxy_headers_and_authenticated_proxy_merges
1220
+ request = build_request(proxy: {
1221
+ proxy_address: "proxy.example.com",
1222
+ proxy_port: 8080,
1223
+ proxy_username: "user",
1224
+ proxy_password: "pass",
1225
+ proxy_headers: { "X-Proxy" => "value" }
1226
+ })
1227
+ request.include_proxy_headers
1228
+
1229
+ assert_equal "value", request.headers["X-Proxy"]
1230
+ end
1231
+
1232
+ def test_include_proxy_headers_with_proxy_headers_and_authenticated_proxy_adds_auth
1233
+ request = build_request(proxy: {
1234
+ proxy_address: "proxy.example.com",
1235
+ proxy_port: 8080,
1236
+ proxy_username: "user",
1237
+ proxy_password: "pass",
1238
+ proxy_headers: { "X-Proxy" => "value" }
1239
+ })
1240
+ request.include_proxy_headers
1241
+
1242
+ assert_match(/^Basic /, request.headers["Proxy-Authorization"])
1243
+ end
1244
+
1245
+ def test_include_proxy_headers_with_proxy_headers_but_unauthenticated_merges
1246
+ request = build_request(proxy: {
1247
+ proxy_address: "proxy.example.com",
1248
+ proxy_port: 8080,
1249
+ proxy_headers: { "X-Proxy" => "value" }
1250
+ })
1251
+ request.include_proxy_headers
1252
+
1253
+ assert_equal "value", request.headers["X-Proxy"]
1254
+ end
1255
+
1256
+ def test_include_proxy_headers_with_proxy_headers_but_unauthenticated_no_auth
1257
+ request = build_request(proxy: {
1258
+ proxy_address: "proxy.example.com",
1259
+ proxy_port: 8080,
1260
+ proxy_headers: { "X-Proxy" => "value" }
1261
+ })
1262
+ request.include_proxy_headers
1263
+
1264
+ assert_nil request.headers["Proxy-Authorization"]
1265
+ end
1266
+
1267
+ def test_include_proxy_headers_without_proxy_headers_key_still_adds_auth
1268
+ request = build_request(proxy: {
1269
+ proxy_address: "proxy.example.com",
1270
+ proxy_port: 8080,
1271
+ proxy_username: "user",
1272
+ proxy_password: "pass"
1273
+ })
1274
+ request.include_proxy_headers
1275
+
1276
+ assert_match(/^Basic /, request.headers["Proxy-Authorization"])
1277
+ end
1278
+
1279
+ def test_include_proxy_headers_without_proxy_headers_key_does_not_raise
1280
+ request = build_request(proxy: {
1281
+ proxy_address: "proxy.example.com",
1282
+ proxy_port: 8080,
1283
+ proxy_username: "user",
1284
+ proxy_password: "pass"
1285
+ })
1286
+ headers_before = request.headers.to_h.except("Proxy-Authorization")
1287
+ request.include_proxy_headers
1288
+ headers_after = request.headers.to_h.except("Proxy-Authorization")
1289
+
1290
+ assert_equal headers_before, headers_after
1291
+ end
1292
+
1293
+ # #include_proxy_authorization_header
1294
+
1295
+ def test_include_proxy_authorization_header_sets_header
1296
+ request = build_request(proxy: {
1297
+ proxy_address: "proxy.example.com",
1298
+ proxy_port: 8080,
1299
+ proxy_username: "user",
1300
+ proxy_password: "pass"
1301
+ })
1302
+ request.include_proxy_authorization_header
1303
+
1304
+ assert_equal request.proxy_authorization_header, request.headers["Proxy-Authorization"]
1305
+ end
1306
+
1307
+ # #proxy_authorization_header
1308
+
1309
+ def test_proxy_authorization_header_returns_basic_auth_header
1310
+ request = build_request(proxy: {
1311
+ proxy_address: "proxy.example.com",
1312
+ proxy_port: 8080,
1313
+ proxy_username: "user",
1314
+ proxy_password: "pass"
1315
+ })
1316
+
1317
+ assert request.proxy_authorization_header.start_with?("Basic ")
1318
+ end
1319
+
1320
+ def test_proxy_authorization_header_encodes_username_and_password
1321
+ request = build_request(proxy: {
1322
+ proxy_address: "proxy.example.com",
1323
+ proxy_port: 8080,
1324
+ proxy_username: "user",
1325
+ proxy_password: "pass"
1326
+ })
1327
+ expected_digest = ["user:pass"].pack("m0")
1328
+
1329
+ assert_equal "Basic #{expected_digest}", request.proxy_authorization_header
1330
+ end
1331
+
1332
+ # #inspect
1333
+
1334
+ def test_inspect_returns_a_useful_string_representation
1335
+ request_uri = "http://example.com/foo?bar=baz"
1336
+
1337
+ assert_equal "#<HTTP::Request/1.1 GET #{request_uri}>", build_request(uri: request_uri).inspect
1338
+ end
1339
+
1340
+ def test_inspect_includes_the_class_name
1341
+ assert_includes build_request.inspect, "HTTP::Request"
1342
+ end
1343
+
1344
+ def test_inspect_includes_the_version
1345
+ assert_includes build_request.inspect, "1.1"
1346
+ end
1347
+
1348
+ def test_inspect_includes_the_uppercased_verb
1349
+ assert_includes build_request.inspect, "GET"
1350
+ end
1351
+
1352
+ def test_inspect_includes_the_uri
1353
+ request_uri = "http://example.com/foo?bar=baz"
1354
+
1355
+ assert_includes build_request(uri: request_uri).inspect, request_uri
1356
+ end
1357
+
1358
+ def test_inspect_with_post_verb_shows_post
1359
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/")
1360
+
1361
+ assert_includes req.inspect, "POST"
1362
+ end
1363
+
1364
+ def test_inspect_works_when_verb_is_a_symbol
1365
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/")
1366
+
1367
+ assert_includes req.inspect, "GET"
1368
+ assert_equal "#<HTTP::Request/1.1 GET http://example.com/>", req.inspect
1369
+ end
1370
+
1371
+ # #port (private)
1372
+
1373
+ def test_port_returns_the_default_port_when_uri_has_no_explicit_port
1374
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/")
1375
+
1376
+ assert_equal 80, req.socket_port
1377
+ end
1378
+
1379
+ def test_port_returns_the_explicit_port_when_uri_specifies_one
1380
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com:9292/")
1381
+
1382
+ assert_equal 9292, req.socket_port
1383
+ end
1384
+
1385
+ def test_port_returns_https_default_port
1386
+ req = HTTP::Request.new(verb: :get, uri: "https://example.com/")
1387
+
1388
+ assert_equal 443, req.socket_port
1389
+ end
1390
+
1391
+ def test_port_returns_ws_default_port
1392
+ req = HTTP::Request.new(verb: :get, uri: "ws://example.com/")
1393
+
1394
+ assert_equal 80, req.socket_port
1395
+ end
1396
+
1397
+ def test_port_returns_wss_default_port
1398
+ req = HTTP::Request.new(verb: :get, uri: "wss://example.com/")
1399
+
1400
+ assert_equal 443, req.socket_port
1401
+ end
1402
+
1403
+ # #default_host_header_value (private)
1404
+
1405
+ def test_default_host_header_value_omits_port_for_standard_http
1406
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/")
1407
+
1408
+ assert_equal "example.com", req.headers["Host"]
1409
+ end
1410
+
1411
+ def test_default_host_header_value_omits_port_for_standard_ws
1412
+ req = HTTP::Request.new(verb: :get, uri: "ws://example.com/")
1413
+
1414
+ assert_equal "example.com", req.headers["Host"]
1415
+ end
1416
+
1417
+ def test_default_host_header_value_omits_port_for_standard_wss
1418
+ req = HTTP::Request.new(verb: :get, uri: "wss://example.com/")
1419
+
1420
+ assert_equal "example.com", req.headers["Host"]
1421
+ end
1422
+
1423
+ def test_default_host_header_value_includes_port_for_non_standard_ws
1424
+ req = HTTP::Request.new(verb: :get, uri: "ws://example.com:8080/")
1425
+
1426
+ assert_equal "example.com:8080", req.headers["Host"]
1427
+ end
1428
+
1429
+ # #parse_uri! (private)
1430
+
1431
+ def test_parse_uri_raises_argument_error_for_empty_string_subclass
1432
+ string_subclass = Class.new(String)
1433
+ err = assert_raises(ArgumentError) do
1434
+ HTTP::Request.new(verb: :get, uri: string_subclass.new(""))
1435
+ end
1436
+ assert_equal "uri is empty", err.message
1437
+ end
1438
+
1439
+ def test_parse_uri_normalizes_uppercase_scheme_to_lowercase_symbol
1440
+ req = HTTP::Request.new(verb: :get, uri: "HTTP://example.com/")
1441
+
1442
+ assert_equal :http, req.scheme
1443
+ end
1444
+
1445
+ def test_parse_uri_normalizes_mixed_case_scheme
1446
+ req = HTTP::Request.new(verb: :get, uri: "HtTpS://example.com/")
1447
+
1448
+ assert_equal :https, req.scheme
1449
+ end
1450
+
1451
+ # #prepare_headers (private)
1452
+
1453
+ def test_prepare_headers_sets_default_host_and_user_agent_when_headers_nil
1454
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/")
1455
+
1456
+ assert_equal "example.com", req.headers["Host"]
1457
+ assert_equal HTTP::Request::USER_AGENT, req.headers["User-Agent"]
1458
+ end
1459
+
1460
+ # #prepare_body (private)
1461
+
1462
+ def test_prepare_body_wraps_a_string_body_in_request_body
1463
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/", body: "test")
1464
+
1465
+ assert_instance_of HTTP::Request::Body, req.body
1466
+ assert_equal "test", req.body.source
1467
+ end
1468
+
1469
+ # #validate_method_and_scheme! (private)
1470
+
1471
+ def test_validate_method_and_scheme_raises_http_uri_invalid_error_for_missing_scheme
1472
+ err = assert_raises(HTTP::URI::InvalidError) do
1473
+ HTTP::Request.new(verb: :get, uri: "example.com/")
1474
+ end
1475
+ assert_kind_of HTTP::URI::InvalidError, err
1476
+ end
1477
+
1478
+ # #redirect (additional mutation killing tests)
1479
+
1480
+ def test_redirect_post_to_get_sets_body_source_to_nil
1481
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/", body: "data")
1482
+ redir = req.redirect("http://other.com/", :get)
1483
+
1484
+ assert_nil redir.body.source
1485
+ end
1486
+
1487
+ def test_redirect_post_to_post_preserves_body_source
1488
+ req = HTTP::Request.new(verb: :post, uri: "http://example.com/", body: "data")
1489
+ redir = req.redirect("http://other.com/", :post)
1490
+
1491
+ assert_equal "data", redir.body.source
1492
+ end
1493
+
1494
+ # #redirect_headers (private)
1495
+
1496
+ def test_redirect_headers_include_original_non_stripped_headers
1497
+ req = HTTP::Request.new(
1498
+ verb: :post,
1499
+ uri: "http://example.com/",
1500
+ headers: { accept: "text/html", "X-Custom" => "val" }
1501
+ )
1502
+ redir = req.redirect("/other")
1503
+
1504
+ assert_equal "text/html", redir.headers["Accept"]
1505
+ assert_equal "val", redir.headers["X-Custom"]
1506
+ end
1507
+
1508
+ # #headline (additional mutation killing tests)
1509
+
1510
+ def test_headline_returns_a_string
1511
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/path")
1512
+ headline = req.headline
1513
+
1514
+ assert_instance_of String, headline
1515
+ assert_equal "GET /path HTTP/1.1", headline
1516
+ end
1517
+
1518
+ def test_headline_converts_symbol_verb_to_uppercase_string
1519
+ req = HTTP::Request.new(verb: :delete, uri: "http://example.com/")
1520
+
1521
+ assert_equal "DELETE / HTTP/1.1", req.headline
1522
+ end
1523
+
1524
+ # #initialize (additional mutation killing tests)
1525
+
1526
+ def test_initialize_uses_http_uri_normalizer_by_default
1527
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/")
1528
+
1529
+ assert_equal HTTP::URI::NORMALIZER, req.uri_normalizer
1530
+ end
1531
+
1532
+ def test_initialize_converts_string_verb_via_to_s_before_downcase
1533
+ req = HTTP::Request.new(verb: "GET", uri: "http://example.com/")
1534
+
1535
+ assert_equal :get, req.verb
1536
+ end
1537
+
1538
+ # #stream (additional mutation killing tests)
1539
+
1540
+ def test_stream_creates_a_writer_and_streams_to_socket
1541
+ io = StringIO.new
1542
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/path")
1543
+ req.stream(io)
1544
+
1545
+ assert_match(%r{^GET /path HTTP/1\.1\r\n}, io.string)
1546
+ assert_includes io.string, "Host: example.com"
1547
+ end
1548
+
1549
+ # #socket_host (additional mutation killing tests)
1550
+
1551
+ def test_socket_host_with_proxy_that_has_proxy_address_returns_value
1552
+ request = build_request(proxy: { proxy_address: "myproxy.com", proxy_port: 3128 })
1553
+
1554
+ assert_equal "myproxy.com", request.socket_host
1555
+ end
1556
+
1557
+ # #socket_port (additional mutation killing tests)
1558
+
1559
+ def test_socket_port_with_proxy_that_has_proxy_port_returns_value
1560
+ request = build_request(proxy: { proxy_address: "myproxy.com", proxy_port: 3128 })
1561
+
1562
+ assert_equal 3128, request.socket_port
1563
+ end
1564
+
1565
+ # #using_proxy? (additional mutation killing tests)
1566
+
1567
+ def test_using_proxy_with_nil_proxy_returns_false
1568
+ req = HTTP::Request.new(verb: :get, uri: "http://example.com/", proxy: {})
1569
+
1570
+ refute_predicate req, :using_proxy?
1571
+ end
1572
+
1573
+ def test_using_proxy_with_exactly_two_keys_returns_true
1574
+ request = build_request(proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 })
1575
+
1576
+ assert_predicate request, :using_proxy?
1577
+ end
1578
+
1579
+ def test_using_proxy_with_three_keys_returns_true
1580
+ request = build_request(proxy: { proxy_address: "proxy.example.com", proxy_port: 8080, extra: "x" })
1581
+
1582
+ assert_predicate request, :using_proxy?
1583
+ end
1584
+
1585
+ # #using_authenticated_proxy? (additional mutation killing tests)
1586
+
1587
+ def test_using_authenticated_proxy_with_exactly_four_keys_returns_true
1588
+ proxy = { proxy_address: "proxy.example.com", proxy_port: 8080,
1589
+ proxy_username: "user", proxy_password: "pass" }
1590
+ request = build_request(proxy: proxy)
1591
+
1592
+ assert_predicate request, :using_authenticated_proxy?
1593
+ end
1594
+
1595
+ def test_using_authenticated_proxy_with_five_keys_returns_true
1596
+ proxy = { proxy_address: "proxy.example.com", proxy_port: 8080,
1597
+ proxy_username: "user", proxy_password: "pass", extra: "x" }
1598
+ request = build_request(proxy: proxy)
1599
+
1600
+ assert_predicate request, :using_authenticated_proxy?
1601
+ end
1602
+
1603
+ # #include_proxy_headers (additional mutation killing tests)
1604
+
1605
+ def test_include_proxy_headers_authenticated_no_proxy_headers_key_does_not_merge_nil
1606
+ request = build_request(proxy: {
1607
+ proxy_address: "proxy.example.com",
1608
+ proxy_port: 8080,
1609
+ proxy_username: "user",
1610
+ proxy_password: "pass"
1611
+ })
1612
+ header_count_before = request.headers.to_h.except("Proxy-Authorization").size
1613
+ request.include_proxy_headers
1614
+ header_count_after = request.headers.to_h.except("Proxy-Authorization").size
1615
+
1616
+ assert_equal header_count_before, header_count_after
1617
+ end
1618
+
1619
+ def test_include_proxy_headers_authenticated_no_proxy_headers_key_does_not_raise
1620
+ request = build_request(proxy: {
1621
+ proxy_address: "proxy.example.com",
1622
+ proxy_port: 8080,
1623
+ proxy_username: "user",
1624
+ proxy_password: "pass"
1625
+ })
1626
+ request.include_proxy_headers
1627
+
1628
+ assert_match(/^Basic /, request.headers["Proxy-Authorization"])
1629
+ end
1630
+
1631
+ # #proxy_authorization_header (additional mutation killing tests)
1632
+
1633
+ def test_proxy_authorization_header_encodes_correct_username_and_password
1634
+ request = build_request(proxy: {
1635
+ proxy_address: "proxy.example.com",
1636
+ proxy_port: 8080,
1637
+ proxy_username: "alice",
1638
+ proxy_password: "secret"
1639
+ })
1640
+ expected = "Basic #{['alice:secret'].pack('m0')}"
1641
+
1642
+ assert_equal expected, request.proxy_authorization_header
1643
+ end
1644
+
1645
+ # #proxy_connect_headers (additional mutation killing tests)
1646
+
1647
+ def test_proxy_connect_headers_authenticated_no_proxy_headers_returns_http_headers
1648
+ request = build_request(
1649
+ uri: "https://example.com/",
1650
+ proxy: {
1651
+ proxy_address: "proxy.example.com",
1652
+ proxy_port: 8080,
1653
+ proxy_username: "user",
1654
+ proxy_password: "pass"
1655
+ }
1656
+ )
1657
+ hdrs = request.proxy_connect_headers
1658
+
1659
+ assert_instance_of HTTP::Headers, hdrs
1660
+ end
1661
+
1662
+ def test_proxy_connect_headers_authenticated_no_proxy_headers_does_not_include_nil
1663
+ request = build_request(
1664
+ uri: "https://example.com/",
1665
+ proxy: {
1666
+ proxy_address: "proxy.example.com",
1667
+ proxy_port: 8080,
1668
+ proxy_username: "user",
1669
+ proxy_password: "pass"
1670
+ }
1671
+ )
1672
+ hdrs = request.proxy_connect_headers
1673
+
1674
+ assert_equal %w[Host User-Agent Proxy-Authorization], hdrs.keys
1675
+ end
1676
+
1677
+ def test_proxy_connect_headers_unauthenticated_with_proxy_headers_includes_custom
1678
+ request = build_request(
1679
+ uri: "https://example.com/",
1680
+ proxy: {
1681
+ proxy_address: "proxy.example.com",
1682
+ proxy_port: 8080,
1683
+ proxy_headers: { "X-Custom" => "val" }
1684
+ }
1685
+ )
1686
+ hdrs = request.proxy_connect_headers
1687
+
1688
+ assert_equal "val", hdrs["X-Custom"]
1689
+ end
1690
+
1691
+ def test_proxy_connect_headers_unauthenticated_with_proxy_headers_includes_host
1692
+ request = build_request(
1693
+ uri: "https://example.com/",
1694
+ proxy: {
1695
+ proxy_address: "proxy.example.com",
1696
+ proxy_port: 8080,
1697
+ proxy_headers: { "X-Custom" => "val" }
1698
+ }
1699
+ )
1700
+ hdrs = request.proxy_connect_headers
1701
+
1702
+ assert_equal "example.com", hdrs["Host"]
1703
+ end
1704
+
1705
+ def test_proxy_connect_headers_unauthenticated_no_proxy_headers_does_not_raise
1706
+ request = build_request(
1707
+ uri: "https://example.com/",
1708
+ proxy: { proxy_address: "proxy.example.com", proxy_port: 8080 }
1709
+ )
1710
+ hdrs = request.proxy_connect_headers
1711
+
1712
+ assert_instance_of HTTP::Headers, hdrs
1713
+ assert_equal %w[Host User-Agent], hdrs.keys
1714
+ end
1715
+
1716
+ # #connect_using_proxy (additional mutation killing tests)
1717
+
1718
+ def test_connect_using_proxy_writes_valid_connect_request_line
1719
+ io = StringIO.new
1720
+ request = build_request(
1721
+ uri: "https://example.com:8443/foo",
1722
+ proxy: {
1723
+ proxy_address: "proxy.example.com",
1724
+ proxy_port: 8080,
1725
+ proxy_username: "user",
1726
+ proxy_password: "pass"
1727
+ }
1728
+ )
1729
+ request.connect_using_proxy(io)
1730
+
1731
+ assert_match(%r{^CONNECT example\.com:8443 HTTP/1\.1\r\n}, io.string)
1732
+ end
1733
+ end