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,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPContentTypeTest < Minitest::Test
6
+ cover "HTTP::ContentType*"
7
+
8
+ # -- .parse -----------------------------------------------------------------
9
+
10
+ def test_parse_text_plain_has_correct_mime_type
11
+ ct = HTTP::ContentType.parse "text/plain"
12
+
13
+ assert_equal "text/plain", ct.mime_type
14
+ end
15
+
16
+ def test_parse_text_plain_has_correct_charset
17
+ ct = HTTP::ContentType.parse "text/plain"
18
+
19
+ assert_nil ct.charset
20
+ end
21
+
22
+ def test_parse_mixed_case_text_plain_has_correct_mime_type
23
+ ct = HTTP::ContentType.parse "tEXT/plaIN"
24
+
25
+ assert_equal "text/plain", ct.mime_type
26
+ end
27
+
28
+ def test_parse_mixed_case_text_plain_has_correct_charset
29
+ ct = HTTP::ContentType.parse "tEXT/plaIN"
30
+
31
+ assert_nil ct.charset
32
+ end
33
+
34
+ def test_parse_text_plain_with_charset_has_correct_mime_type
35
+ ct = HTTP::ContentType.parse "text/plain; charset=utf-8"
36
+
37
+ assert_equal "text/plain", ct.mime_type
38
+ end
39
+
40
+ def test_parse_text_plain_with_charset_has_correct_charset
41
+ ct = HTTP::ContentType.parse "text/plain; charset=utf-8"
42
+
43
+ assert_equal "utf-8", ct.charset
44
+ end
45
+
46
+ def test_parse_text_plain_with_quoted_charset_has_correct_mime_type
47
+ ct = HTTP::ContentType.parse 'text/plain; charset="utf-8"'
48
+
49
+ assert_equal "text/plain", ct.mime_type
50
+ end
51
+
52
+ def test_parse_text_plain_with_quoted_charset_has_correct_charset
53
+ ct = HTTP::ContentType.parse 'text/plain; charset="utf-8"'
54
+
55
+ assert_equal "utf-8", ct.charset
56
+ end
57
+
58
+ def test_parse_text_plain_with_mixed_case_charset_has_correct_mime_type
59
+ ct = HTTP::ContentType.parse "text/plain; charSET=utf-8"
60
+
61
+ assert_equal "text/plain", ct.mime_type
62
+ end
63
+
64
+ def test_parse_text_plain_with_mixed_case_charset_has_correct_charset
65
+ ct = HTTP::ContentType.parse "text/plain; charSET=utf-8"
66
+
67
+ assert_equal "utf-8", ct.charset
68
+ end
69
+
70
+ def test_parse_with_extra_params_has_correct_mime_type
71
+ ct = HTTP::ContentType.parse "text/plain; foo=bar; charset=utf-8"
72
+
73
+ assert_equal "text/plain", ct.mime_type
74
+ end
75
+
76
+ def test_parse_with_extra_params_has_correct_charset
77
+ ct = HTTP::ContentType.parse "text/plain; foo=bar; charset=utf-8"
78
+
79
+ assert_equal "utf-8", ct.charset
80
+ end
81
+
82
+ def test_parse_with_no_spaces_has_correct_mime_type
83
+ ct = HTTP::ContentType.parse "text/plain;charset=utf-8;foo=bar"
84
+
85
+ assert_equal "text/plain", ct.mime_type
86
+ end
87
+
88
+ def test_parse_with_no_spaces_has_correct_charset
89
+ ct = HTTP::ContentType.parse "text/plain;charset=utf-8;foo=bar"
90
+
91
+ assert_equal "utf-8", ct.charset
92
+ end
93
+
94
+ def test_parse_nil_returns_nil_mime_type
95
+ ct = HTTP::ContentType.parse nil
96
+
97
+ assert_nil ct.mime_type
98
+ end
99
+
100
+ def test_parse_nil_returns_nil_charset
101
+ ct = HTTP::ContentType.parse nil
102
+
103
+ assert_nil ct.charset
104
+ end
105
+
106
+ def test_parse_empty_string_returns_nil_mime_type
107
+ ct = HTTP::ContentType.parse ""
108
+
109
+ assert_nil ct.mime_type
110
+ end
111
+
112
+ def test_parse_empty_string_returns_nil_charset
113
+ ct = HTTP::ContentType.parse ""
114
+
115
+ assert_nil ct.charset
116
+ end
117
+
118
+ def test_parse_with_whitespace_strips_whitespace_from_mime_type
119
+ ct = HTTP::ContentType.parse " text/plain ; charset= utf-8 "
120
+
121
+ assert_equal "text/plain", ct.mime_type
122
+ end
123
+
124
+ def test_parse_with_whitespace_strips_whitespace_from_charset
125
+ ct = HTTP::ContentType.parse " text/plain ; charset= utf-8 "
126
+
127
+ assert_equal "utf-8", ct.charset
128
+ end
129
+
130
+ # -- #deconstruct_keys ------------------------------------------------------
131
+
132
+ def test_deconstruct_keys_returns_all_keys_when_given_nil
133
+ ct = HTTP::ContentType.new("text/html", "utf-8")
134
+
135
+ assert_equal({ mime_type: "text/html", charset: "utf-8" }, ct.deconstruct_keys(nil))
136
+ end
137
+
138
+ def test_deconstruct_keys_returns_only_requested_keys
139
+ ct = HTTP::ContentType.new("text/html", "utf-8")
140
+
141
+ assert_equal({ mime_type: "text/html" }, ct.deconstruct_keys([:mime_type]))
142
+ end
143
+
144
+ def test_deconstruct_keys_excludes_unrequested_keys
145
+ ct = HTTP::ContentType.new("text/html", "utf-8")
146
+
147
+ refute_includes ct.deconstruct_keys([:mime_type]).keys, :charset
148
+ end
149
+
150
+ def test_deconstruct_keys_returns_empty_hash_for_empty_keys
151
+ ct = HTTP::ContentType.new("text/html", "utf-8")
152
+
153
+ assert_equal({}, ct.deconstruct_keys([]))
154
+ end
155
+
156
+ def test_deconstruct_keys_returns_nil_values_when_attributes_are_nil
157
+ ct = HTTP::ContentType.new
158
+
159
+ assert_equal({ mime_type: nil, charset: nil }, ct.deconstruct_keys(nil))
160
+ end
161
+
162
+ def test_deconstruct_keys_supports_pattern_matching_with_case_in
163
+ ct = HTTP::ContentType.new("text/html", "utf-8")
164
+
165
+ matched = case ct
166
+ in { mime_type: /html/ }
167
+ true
168
+ else
169
+ false
170
+ end
171
+
172
+ assert matched
173
+ end
174
+
175
+ # -- #initialize ------------------------------------------------------------
176
+
177
+ def test_initialize_stores_mime_type_and_charset
178
+ ct = HTTP::ContentType.new("text/html", "utf-8")
179
+
180
+ assert_equal "text/html", ct.mime_type
181
+ assert_equal "utf-8", ct.charset
182
+ end
183
+
184
+ def test_initialize_defaults_to_nil
185
+ ct = HTTP::ContentType.new
186
+
187
+ assert_nil ct.mime_type
188
+ assert_nil ct.charset
189
+ end
190
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPStatusErrorTest < Minitest::Test
6
+ cover "HTTP::StatusError*"
7
+
8
+ def response
9
+ @response ||= HTTP::Response.new(
10
+ status: 404,
11
+ version: "1.1",
12
+ body: "Not Found",
13
+ request: HTTP::Request.new(verb: :get, uri: "http://example.com/")
14
+ )
15
+ end
16
+
17
+ def error
18
+ @error ||= HTTP::StatusError.new(response)
19
+ end
20
+
21
+ def test_response_returns_the_response
22
+ assert_same response, error.response
23
+ end
24
+
25
+ def test_message_includes_the_status_code
26
+ assert_equal "Unexpected status code 404", error.message
27
+ end
28
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPFeatureTest < Minitest::Test
6
+ cover "HTTP::Feature*"
7
+
8
+ def feature
9
+ @feature ||= HTTP::Feature.new
10
+ end
11
+
12
+ def request
13
+ @request ||= HTTP::Request.new(verb: :get, uri: "http://example.com/")
14
+ end
15
+
16
+ def response
17
+ @response ||= HTTP::Response.new(
18
+ version: "1.1",
19
+ status: 200,
20
+ body: "OK",
21
+ request: request
22
+ )
23
+ end
24
+
25
+ def test_wrap_request_returns_the_same_request_object
26
+ assert_same request, feature.wrap_request(request)
27
+ end
28
+
29
+ def test_wrap_response_returns_the_same_response_object
30
+ assert_same response, feature.wrap_response(response)
31
+ end
32
+
33
+ def test_on_request_does_not_raise
34
+ feature.on_request(request)
35
+ end
36
+
37
+ def test_around_request_yields_the_request_and_returns_the_response
38
+ result = feature.around_request(request) do |req|
39
+ assert_same(request, req)
40
+ response
41
+ end
42
+
43
+ assert_same response, result
44
+ end
45
+
46
+ def test_on_error_does_not_raise
47
+ feature.on_error(request, RuntimeError.new("boom"))
48
+ end
49
+ end
@@ -0,0 +1,317 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPFeaturesAutoDeflateTest < Minitest::Test
6
+ cover "HTTP::Features::AutoDeflate*"
7
+
8
+ # Returns a body source that can only be iterated once; second iteration yields nothing.
9
+ def single_use_source(chunks)
10
+ consumed = false
11
+ fake(each: proc { |&block|
12
+ unless consumed
13
+ consumed = true
14
+ chunks.each { |chunk| block.call(chunk) }
15
+ end
16
+ })
17
+ end
18
+
19
+ def subject_under_test
20
+ @subject_under_test ||= HTTP::Features::AutoDeflate.new
21
+ end
22
+
23
+ def test_raises_error_for_wrong_type
24
+ err = assert_raises(HTTP::Error) { HTTP::Features::AutoDeflate.new(method: :wrong) }
25
+ assert_equal "Only gzip and deflate methods are supported", err.message
26
+ end
27
+
28
+ def test_accepts_gzip_method
29
+ assert_equal "gzip", HTTP::Features::AutoDeflate.new(method: :gzip).method
30
+ end
31
+
32
+ def test_accepts_deflate_method
33
+ assert_equal "deflate", HTTP::Features::AutoDeflate.new(method: :deflate).method
34
+ end
35
+
36
+ def test_accepts_string_as_method
37
+ assert_equal "gzip", HTTP::Features::AutoDeflate.new(method: "gzip").method
38
+ end
39
+
40
+ def test_uses_gzip_by_default
41
+ assert_equal "gzip", subject_under_test.method
42
+ end
43
+
44
+ def test_is_a_feature
45
+ assert_kind_of HTTP::Feature, subject_under_test
46
+ end
47
+
48
+ # -- #wrap_request --
49
+
50
+ def build_request
51
+ custom_normalizer = ->(uri) { HTTP::URI::NORMALIZER.call(uri) }
52
+ HTTP::Request.new(
53
+ verb: :post,
54
+ uri: "http://example.com/",
55
+ headers: { "Content-Length" => "4" },
56
+ body: "data",
57
+ version: "2.0",
58
+ proxy: { proxy_host: "proxy.example.com" },
59
+ uri_normalizer: custom_normalizer
60
+ )
61
+ end
62
+
63
+ def test_wrap_request_when_method_is_nil_returns_the_original_request
64
+ sut = HTTP::Features::AutoDeflate.new
65
+ sut.instance_variable_set(:@method, nil)
66
+ request = build_request
67
+
68
+ assert_same request, sut.wrap_request(request)
69
+ end
70
+
71
+ def test_wrap_request_when_body_is_empty_returns_the_original_request
72
+ empty_request = HTTP::Request.new(
73
+ verb: :post,
74
+ uri: "http://example.com/"
75
+ )
76
+
77
+ assert_same empty_request, subject_under_test.wrap_request(empty_request)
78
+ end
79
+
80
+ def test_wrap_request_with_gzip_returns_a_new_request
81
+ request = build_request
82
+ result = subject_under_test.wrap_request(request)
83
+
84
+ refute_same request, result
85
+ end
86
+
87
+ def test_wrap_request_with_gzip_returns_an_http_request
88
+ request = build_request
89
+ result = subject_under_test.wrap_request(request)
90
+
91
+ assert_instance_of HTTP::Request, result
92
+ end
93
+
94
+ def test_wrap_request_with_gzip_sets_content_encoding_header
95
+ request = build_request
96
+ result = subject_under_test.wrap_request(request)
97
+
98
+ assert_equal "gzip", result.headers["Content-Encoding"]
99
+ end
100
+
101
+ def test_wrap_request_with_gzip_removes_content_length_header
102
+ request = build_request
103
+ result = subject_under_test.wrap_request(request)
104
+
105
+ refute_includes result.headers.to_h.keys.map(&:downcase), "content-length"
106
+ end
107
+
108
+ def test_wrap_request_with_gzip_preserves_the_verb
109
+ request = build_request
110
+ result = subject_under_test.wrap_request(request)
111
+
112
+ assert_equal :post, result.verb
113
+ end
114
+
115
+ def test_wrap_request_with_gzip_preserves_the_uri
116
+ request = build_request
117
+ result = subject_under_test.wrap_request(request)
118
+
119
+ assert_equal request.uri, result.uri
120
+ end
121
+
122
+ def test_wrap_request_with_gzip_preserves_the_version
123
+ request = build_request
124
+ result = subject_under_test.wrap_request(request)
125
+
126
+ assert_equal request.version, result.version
127
+ end
128
+
129
+ def test_wrap_request_with_gzip_wraps_the_body_in_a_gzipped_body
130
+ request = build_request
131
+ result = subject_under_test.wrap_request(request)
132
+
133
+ assert_instance_of HTTP::Features::AutoDeflate::GzippedBody, result.body
134
+ end
135
+
136
+ def test_wrap_request_with_gzip_compresses_the_original_request_body_data
137
+ request = build_request
138
+ result = subject_under_test.wrap_request(request)
139
+ compressed = result.body.each.to_a.join
140
+ decompressed = Zlib::GzipReader.new(StringIO.new(compressed)).read
141
+
142
+ assert_equal "data", decompressed
143
+ end
144
+
145
+ def test_wrap_request_with_gzip_preserves_the_proxy
146
+ request = build_request
147
+ result = subject_under_test.wrap_request(request)
148
+
149
+ assert_equal request.proxy, result.proxy
150
+ end
151
+
152
+ def test_wrap_request_with_gzip_preserves_the_uri_normalizer
153
+ custom_normalizer = ->(uri) { HTTP::URI::NORMALIZER.call(uri) }
154
+ request = HTTP::Request.new(
155
+ verb: :post,
156
+ uri: "http://example.com/",
157
+ headers: { "Content-Length" => "4" },
158
+ body: "data",
159
+ version: "2.0",
160
+ proxy: { proxy_host: "proxy.example.com" },
161
+ uri_normalizer: custom_normalizer
162
+ )
163
+ result = subject_under_test.wrap_request(request)
164
+
165
+ assert_same custom_normalizer, result.uri_normalizer
166
+ end
167
+
168
+ def test_wrap_request_with_deflate_sets_content_encoding_to_deflate
169
+ sut = HTTP::Features::AutoDeflate.new(method: :deflate)
170
+ request = build_request
171
+ result = sut.wrap_request(request)
172
+
173
+ assert_equal "deflate", result.headers["Content-Encoding"]
174
+ end
175
+
176
+ def test_wrap_request_with_deflate_wraps_the_body_in_a_deflated_body
177
+ sut = HTTP::Features::AutoDeflate.new(method: :deflate)
178
+ request = build_request
179
+ result = sut.wrap_request(request)
180
+
181
+ assert_instance_of HTTP::Features::AutoDeflate::DeflatedBody, result.body
182
+ end
183
+
184
+ # -- #deflated_body --
185
+
186
+ def test_deflated_body_when_method_is_unknown_returns_nil
187
+ sut = HTTP::Features::AutoDeflate.new
188
+ sut.instance_variable_set(:@method, "unknown")
189
+
190
+ assert_nil sut.deflated_body(%w[bees cows])
191
+ end
192
+
193
+ def test_deflated_body_when_method_is_gzip_yields_gzipped_content
194
+ sut = HTTP::Features::AutoDeflate.new(method: :gzip)
195
+ deflated_body = sut.deflated_body(%w[bees cows])
196
+ result = deflated_body.each.to_a.join
197
+ decompressed = Zlib::GzipReader.new(StringIO.new(result)).read
198
+
199
+ assert_equal "beescows", decompressed
200
+ end
201
+
202
+ def test_deflated_body_when_method_is_gzip_caches_compressed_content_when_size_is_called
203
+ sut = HTTP::Features::AutoDeflate.new(method: :gzip)
204
+ deflated_body = sut.deflated_body(%w[bees cows])
205
+ size = deflated_body.size
206
+ result = deflated_body.each.to_a.join
207
+ decompressed = Zlib::GzipReader.new(StringIO.new(result)).read
208
+
209
+ assert_equal result.bytesize, size
210
+ assert_equal "beescows", decompressed
211
+ end
212
+
213
+ def test_deflated_body_when_method_is_gzip_reuses_cached_compression_on_subsequent_size_calls
214
+ single_use_body = single_use_source(%w[bees cows])
215
+ gzipped = HTTP::Features::AutoDeflate::GzippedBody.new(single_use_body)
216
+ first_size = gzipped.size
217
+
218
+ assert_equal first_size, gzipped.size
219
+ end
220
+
221
+ def test_deflated_body_when_method_is_deflate_yields_deflated_content
222
+ sut = HTTP::Features::AutoDeflate.new(method: :deflate)
223
+ deflated_body = sut.deflated_body(%w[bees cows])
224
+ deflated = Zlib::Deflate.deflate("beescows")
225
+
226
+ assert_equal deflated, deflated_body.each.to_a.join
227
+ end
228
+
229
+ def test_deflated_body_when_method_is_deflate_caches_compressed_content_when_size_is_called
230
+ sut = HTTP::Features::AutoDeflate.new(method: :deflate)
231
+ deflated_body = sut.deflated_body(%w[bees cows])
232
+ deflated = Zlib::Deflate.deflate("beescows")
233
+
234
+ assert_equal deflated.bytesize, deflated_body.size
235
+ assert_equal deflated, deflated_body.each.to_a.join
236
+ end
237
+
238
+ # -- CompressedBody --
239
+
240
+ def test_compressed_body_initialize_sets_source_to_nil_via_super
241
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(%w[hello world])
242
+
243
+ assert_nil body.source
244
+ end
245
+
246
+ def test_compressed_body_each_returns_an_enumerator_when_no_block_is_given
247
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(%w[hello world])
248
+ enum = body.each
249
+
250
+ assert_instance_of Enumerator, enum
251
+ end
252
+
253
+ def test_compressed_body_each_returns_self_when_block_is_given
254
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(%w[hello world])
255
+ result = body.each { |_chunk| nil }
256
+
257
+ assert_same body, result
258
+ end
259
+
260
+ def test_compressed_body_each_yields_compressed_data_via_compress_when_not_cached
261
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(%w[hello world])
262
+ chunks = body.each.map { |chunk| chunk }
263
+
264
+ refute_empty chunks
265
+ end
266
+
267
+ def test_compressed_body_each_reads_from_cache_when_size_was_called_first_on_single_use_body
268
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(single_use_source(%w[hello world]))
269
+ expected_size = body.size
270
+ chunks = body.each.map { |chunk| chunk }
271
+ actual = chunks.join
272
+
273
+ assert_equal expected_size, actual.bytesize
274
+ decompressed = Zlib::GzipReader.new(StringIO.new(actual)).read
275
+
276
+ assert_equal "helloworld", decompressed
277
+ end
278
+
279
+ def test_compressed_body_each_cleans_up_tempfile_after_reading_cached_data
280
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(%w[hello world])
281
+ body.size
282
+ path = body.instance_variable_get(:@compressed).path
283
+ body.each { |_| nil }
284
+
285
+ refute_path_exists path
286
+ end
287
+
288
+ def test_compressed_body_each_can_be_enumerated_multiple_times_via_to_enum
289
+ inner_body = %w[hello world]
290
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(inner_body)
291
+ first_result = body.each.to_a.join
292
+ # After each consumes via compress, we need a fresh body to test enum
293
+ body2 = HTTP::Features::AutoDeflate::GzippedBody.new(inner_body)
294
+
295
+ assert_equal first_result, body2.each.to_a.join
296
+ end
297
+
298
+ # -- GzippedBody --
299
+
300
+ def test_gzipped_body_compress_produces_valid_gzip_data
301
+ body = HTTP::Features::AutoDeflate::GzippedBody.new(%w[hello world])
302
+ compressed = body.each.to_a.join
303
+ decompressed = Zlib::GzipReader.new(StringIO.new(compressed)).read
304
+
305
+ assert_equal "helloworld", decompressed
306
+ end
307
+
308
+ # -- DeflatedBody --
309
+
310
+ def test_deflated_body_compress_produces_valid_deflate_data
311
+ body = HTTP::Features::AutoDeflate::DeflatedBody.new(%w[hello world])
312
+ compressed = body.each.to_a.join
313
+ decompressed = Zlib::Inflate.inflate(compressed)
314
+
315
+ assert_equal "helloworld", decompressed
316
+ end
317
+ end