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,318 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPRequestBodyTest < Minitest::Test
6
+ cover "HTTP::Request::Body*"
7
+
8
+ def build_body(source = "")
9
+ HTTP::Request::Body.new(source)
10
+ end
11
+
12
+ # #initialize
13
+
14
+ def test_initialize_when_body_is_nil_does_not_raise
15
+ HTTP::Request::Body.new(nil)
16
+ end
17
+
18
+ def test_initialize_when_body_is_a_string_does_not_raise
19
+ HTTP::Request::Body.new("string body")
20
+ end
21
+
22
+ def test_initialize_when_body_is_an_io_does_not_raise
23
+ HTTP::Request::Body.new(FakeIO.new("IO body"))
24
+ end
25
+
26
+ def test_initialize_when_body_is_an_enumerable_does_not_raise
27
+ HTTP::Request::Body.new(%w[bees cows])
28
+ end
29
+
30
+ def test_initialize_when_body_is_of_unrecognized_type_raises_error
31
+ assert_raises(HTTP::RequestError) { HTTP::Request::Body.new(123) }
32
+ end
33
+
34
+ # #source
35
+
36
+ def test_source_returns_the_original_object
37
+ assert_equal "", build_body("").source
38
+ end
39
+
40
+ # #size
41
+
42
+ def test_size_when_body_is_nil_returns_zero
43
+ assert_equal 0, build_body(nil).size
44
+ end
45
+
46
+ def test_size_when_body_is_a_string_returns_string_bytesize
47
+ assert_equal 21, build_body("\u041F\u0440\u0438\u0432\u0435\u0442, \u043C\u0438\u0440!").size
48
+ end
49
+
50
+ def test_size_when_body_is_an_io_with_size_returns_io_size
51
+ assert_equal 7, build_body(FakeIO.new("content")).size
52
+ end
53
+
54
+ def test_size_when_body_is_an_io_without_size_raises_request_error
55
+ assert_raises(HTTP::RequestError) { build_body(IO.pipe[0]).size }
56
+ end
57
+
58
+ def test_size_when_body_is_an_enumerable_raises_request_error
59
+ assert_raises(HTTP::RequestError) { build_body(%w[bees cows]).size }
60
+ end
61
+
62
+ # #empty?
63
+
64
+ def test_empty_when_body_is_nil_returns_true
65
+ assert_predicate build_body(nil), :empty?
66
+ end
67
+
68
+ def test_empty_when_body_is_a_string_returns_false
69
+ refute_predicate build_body("content"), :empty?
70
+ end
71
+
72
+ def test_empty_when_body_is_an_empty_string_returns_false
73
+ refute_predicate build_body(""), :empty?
74
+ end
75
+
76
+ # #loggable?
77
+
78
+ def test_loggable_when_body_is_a_text_string_returns_true
79
+ assert_predicate build_body("text content"), :loggable?
80
+ end
81
+
82
+ def test_loggable_when_body_is_a_binary_encoded_string_returns_true
83
+ assert_predicate build_body(String.new("\x89PNG\r\n", encoding: Encoding::BINARY)), :loggable?
84
+ end
85
+
86
+ def test_loggable_when_body_is_nil_returns_false
87
+ refute_predicate build_body(nil), :loggable?
88
+ end
89
+
90
+ def test_loggable_when_body_is_an_io_returns_false
91
+ refute_predicate build_body(FakeIO.new("IO body")), :loggable?
92
+ end
93
+
94
+ def test_loggable_when_body_is_an_enumerable_returns_false
95
+ refute_predicate build_body(%w[bees cows]), :loggable?
96
+ end
97
+
98
+ # #each
99
+
100
+ def test_each_when_body_is_nil_yields_nothing
101
+ chunks = build_body(nil).enum_for(:each).map(&:dup)
102
+
103
+ assert_equal [], chunks
104
+ end
105
+
106
+ def test_each_when_body_is_a_string_yields_the_string
107
+ chunks = build_body("content").enum_for(:each).map(&:dup)
108
+
109
+ assert_equal %w[content], chunks
110
+ end
111
+
112
+ def test_each_when_body_is_a_non_enumerable_io_yields_chunks_of_content
113
+ body = FakeIO.new(("a" * 16 * 1024) + ("b" * 10 * 1024))
114
+ chunks = build_body(body).enum_for(:each).map(&:dup)
115
+
116
+ assert_equal ("a" * 16 * 1024) + ("b" * 10 * 1024), chunks.sum("")
117
+ end
118
+
119
+ def test_each_when_body_is_a_pipe_yields_chunks_of_content
120
+ ios = IO.pipe
121
+ subject = build_body(ios[0])
122
+
123
+ writer = Thread.new(ios[1]) do |io|
124
+ io << "abcdef"
125
+ io.close
126
+ end
127
+
128
+ begin
129
+ chunks = subject.enum_for(:each).map(&:dup)
130
+
131
+ assert_equal "abcdef", chunks.sum("")
132
+ ensure
133
+ writer.join
134
+ end
135
+ end
136
+
137
+ def test_each_when_body_is_an_enumerable_io_yields_chunks_of_content
138
+ data = ("a" * 16 * 1024) + ("b" * 10 * 1024)
139
+ chunks = build_body(StringIO.new(data)).enum_for(:each).map(&:dup)
140
+
141
+ assert_equal data, chunks.sum("")
142
+ end
143
+
144
+ def test_each_when_body_is_an_enumerable_io_allows_multiple_enumerations
145
+ data = ("a" * 16 * 1024) + ("b" * 10 * 1024)
146
+ subject = build_body(StringIO.new(data))
147
+ results = []
148
+
149
+ 2.times do
150
+ result = ""
151
+ subject.each { |chunk| result += chunk }
152
+ results << result
153
+ end
154
+
155
+ assert_equal 2, results.count
156
+ assert(results.all?(data))
157
+ end
158
+
159
+ def test_each_when_body_is_an_enumerable_yields_elements
160
+ chunks = build_body(%w[bees cows]).enum_for(:each).map(&:dup)
161
+
162
+ assert_equal %w[bees cows], chunks
163
+ end
164
+
165
+ # #==
166
+
167
+ def test_eq_when_sources_are_equivalent_returns_true
168
+ body1 = HTTP::Request::Body.new("content")
169
+ body2 = HTTP::Request::Body.new("content")
170
+
171
+ assert_equal body1, body2
172
+ end
173
+
174
+ def test_eq_compares_by_value_not_identity
175
+ a = HTTP::Request::Body.new(+"same")
176
+ b = HTTP::Request::Body.new(+"same")
177
+
178
+ assert_equal a, b
179
+ end
180
+
181
+ def test_eq_uses_coercion_on_sources
182
+ a = HTTP::Request::Body.new([1])
183
+ b = HTTP::Request::Body.new([1.0])
184
+
185
+ assert_equal a, b
186
+ end
187
+
188
+ def test_eq_when_sources_are_not_equivalent_returns_false
189
+ body1 = HTTP::Request::Body.new("content")
190
+ body2 = HTTP::Request::Body.new(nil)
191
+
192
+ refute_equal body1, body2
193
+ end
194
+
195
+ def test_eq_when_objects_are_not_of_the_same_class_returns_false
196
+ body1 = HTTP::Request::Body.new("content")
197
+ body2 = "content"
198
+
199
+ refute_equal body1, body2
200
+ end
201
+
202
+ def test_eq_when_sources_are_both_truthy_but_different_returns_false
203
+ body1 = HTTP::Request::Body.new("alpha")
204
+ body2 = HTTP::Request::Body.new("beta")
205
+
206
+ refute_equal body1, body2
207
+ end
208
+
209
+ # #each return value
210
+
211
+ def test_each_return_value_when_body_is_a_string_returns_self
212
+ subject = build_body("content")
213
+
214
+ assert_same(subject, subject.each { |_| nil })
215
+ end
216
+
217
+ def test_each_return_value_when_body_is_nil_returns_self
218
+ subject = build_body(nil)
219
+
220
+ assert_same(subject, subject.each { |_| nil })
221
+ end
222
+
223
+ def test_each_return_value_when_body_is_an_io_returns_self
224
+ subject = build_body(StringIO.new("io content"))
225
+
226
+ assert_same(subject, subject.each { |_| nil })
227
+ end
228
+
229
+ def test_each_return_value_when_body_is_an_enumerable_returns_self
230
+ subject = build_body(%w[bees cows])
231
+
232
+ assert_same(subject, subject.each { |_| nil })
233
+ end
234
+
235
+ # #size error messages
236
+
237
+ def test_size_error_when_body_is_an_io_without_size_mentions_io_needing_size
238
+ err = assert_raises(HTTP::RequestError) { build_body(IO.pipe[0]).size }
239
+ assert_match(/IO object must respond to #size/, err.message)
240
+ end
241
+
242
+ def test_size_error_when_body_is_an_enumerable_mentions_undetermined_size
243
+ body = %w[bees cows]
244
+ err = assert_raises(HTTP::RequestError) { build_body(body).size }
245
+ assert_match(/cannot determine size of body/, err.message)
246
+ assert_includes err.message, body.inspect
247
+ assert_match(/Content-Length/, err.message)
248
+ assert_match(/chunked Transfer-Encoding/, err.message)
249
+ end
250
+
251
+ # #initialize error messages
252
+
253
+ def test_initialize_error_when_body_is_of_unrecognized_type_mentions_wrong_type
254
+ err = assert_raises(HTTP::RequestError) { HTTP::Request::Body.new(123) }
255
+ assert_match(/body of wrong type/, err.message)
256
+ assert_match(/Integer/, err.message)
257
+ end
258
+
259
+ # String subclass
260
+
261
+ def test_string_subclass_does_not_raise_on_initialization
262
+ string_subclass = Class.new(String)
263
+ HTTP::Request::Body.new(string_subclass.new("subclass body"))
264
+ end
265
+
266
+ def test_string_subclass_returns_correct_size
267
+ string_subclass = Class.new(String)
268
+
269
+ assert_equal 13, build_body(string_subclass.new("subclass body")).size
270
+ end
271
+
272
+ def test_string_subclass_yields_the_string_in_each
273
+ string_subclass = Class.new(String)
274
+ subject = build_body(string_subclass.new("subclass body"))
275
+ chunks = subject.enum_for(:each).map(&:dup)
276
+
277
+ assert_equal ["subclass body"], chunks
278
+ end
279
+
280
+ def test_string_subclass_is_loggable
281
+ string_subclass = Class.new(String)
282
+
283
+ assert_predicate build_body(string_subclass.new("subclass body")), :loggable?
284
+ end
285
+
286
+ # Body subclass comparison
287
+
288
+ def test_comparing_with_a_body_subclass_returns_true_for_equivalent
289
+ subclass = Class.new(HTTP::Request::Body)
290
+ body1 = HTTP::Request::Body.new("content")
291
+ body2 = subclass.new("content")
292
+
293
+ assert_equal body1, body2
294
+ end
295
+
296
+ # ProcIO
297
+
298
+ def test_proc_io_write_calls_the_block_with_data_and_returns_bytesize
299
+ received = nil
300
+ block = proc { |data| received = data }
301
+ proc_io = HTTP::Request::Body::ProcIO.new(block)
302
+
303
+ result = proc_io.write("hello")
304
+
305
+ assert_equal "hello", received
306
+ assert_equal 5, result
307
+ end
308
+
309
+ def test_proc_io_write_returns_correct_bytesize_for_multibyte_strings
310
+ block = proc { |_| }
311
+ proc_io = HTTP::Request::Body::ProcIO.new(block)
312
+
313
+ # "Привет" is 12 bytes in UTF-8
314
+ result = proc_io.write("\u041F\u0440\u0438\u0432\u0435\u0442")
315
+
316
+ assert_equal 12, result
317
+ end
318
+ end