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,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class HTTPFeaturesInstrumentationTest < Minitest::Test
6
+ cover "HTTP::Features::Instrumentation*"
7
+
8
+ def instrumenter_class
9
+ @instrumenter_class ||= Class.new(HTTP::Features::Instrumentation::NullInstrumenter) do
10
+ attr_reader :events
11
+
12
+ def initialize
13
+ super
14
+ @events = []
15
+ end
16
+
17
+ def start(name, payload)
18
+ events << { type: :start, name: name, payload: payload.dup }
19
+ end
20
+
21
+ def finish(name, payload)
22
+ events << { type: :finish, name: name, payload: payload.dup }
23
+ end
24
+ end
25
+ end
26
+
27
+ def instrumenter
28
+ @instrumenter ||= instrumenter_class.new
29
+ end
30
+
31
+ def feature
32
+ @feature ||= HTTP::Features::Instrumentation.new(instrumenter: instrumenter)
33
+ end
34
+
35
+ def request
36
+ @request ||= HTTP::Request.new(
37
+ verb: :post,
38
+ uri: "https://example.com/",
39
+ headers: { accept: "application/json" },
40
+ body: '{"hello": "world!"}'
41
+ )
42
+ end
43
+
44
+ def response
45
+ @response ||= HTTP::Response.new(
46
+ version: "1.1",
47
+ status: 200,
48
+ headers: { content_type: "application/json" },
49
+ body: '{"success": true}',
50
+ request: request
51
+ )
52
+ end
53
+
54
+ # -- initialization --
55
+
56
+ def test_initialization_uses_null_instrumenter_when_no_instrumenter_provided
57
+ f = HTTP::Features::Instrumentation.new
58
+
59
+ assert_instance_of HTTP::Features::Instrumentation::NullInstrumenter, f.instrumenter
60
+ end
61
+
62
+ def test_initialization_sets_name_to_request_http_by_default
63
+ f = HTTP::Features::Instrumentation.new
64
+
65
+ assert_equal "request.http", f.name
66
+ end
67
+
68
+ def test_initialization_sets_error_name_to_error_http_by_default
69
+ f = HTTP::Features::Instrumentation.new
70
+
71
+ assert_equal "error.http", f.error_name
72
+ end
73
+
74
+ def test_initialization_uses_a_custom_namespace
75
+ f = HTTP::Features::Instrumentation.new(namespace: "my_app")
76
+
77
+ assert_equal "request.my_app", f.name
78
+ assert_equal "error.my_app", f.error_name
79
+ end
80
+
81
+ # -- around_request --
82
+
83
+ def test_around_request_emits_start_event_with_correct_name
84
+ feature.around_request(request) { response }
85
+ first_event = instrumenter.events.first
86
+
87
+ assert_equal "start_request.http", first_event[:name]
88
+ end
89
+
90
+ def test_around_request_emits_start_event_with_request_payload
91
+ feature.around_request(request) { response }
92
+ first_event = instrumenter.events.first
93
+
94
+ assert_equal({ request: request }, first_event[:payload])
95
+ end
96
+
97
+ def test_around_request_finishes_instrumentation_span_with_response
98
+ feature.around_request(request) { response }
99
+ last_finish = instrumenter.events.reverse.find { |e| e[:type] == :finish }
100
+
101
+ assert_equal({ request: request, response: response }, last_finish[:payload])
102
+ end
103
+
104
+ def test_around_request_emits_main_event_with_correct_name
105
+ feature.around_request(request) { response }
106
+ main_finishes = instrumenter.events.select { |e| e[:type] == :finish && e[:name] == "request.http" }
107
+
108
+ assert_equal 1, main_finishes.length
109
+ end
110
+
111
+ def test_around_request_returns_the_response_from_the_block
112
+ result = feature.around_request(request) { response }
113
+
114
+ assert_same response, result
115
+ end
116
+
117
+ def test_around_request_passes_the_request_to_the_block
118
+ received = nil
119
+ feature.around_request(request) do |req|
120
+ received = req
121
+ response
122
+ end
123
+
124
+ assert_same request, received
125
+ end
126
+
127
+ def test_around_request_finishes_span_even_when_block_raises
128
+ assert_raises(RuntimeError) do
129
+ feature.around_request(request) { raise "boom" }
130
+ end
131
+ last_finish = instrumenter.events.reverse.find { |e| e[:type] == :finish }
132
+
133
+ assert_equal({ request: request }, last_finish[:payload])
134
+ end
135
+
136
+ # -- on_error --
137
+
138
+ def test_on_error_instruments_the_error_with_correct_event_name
139
+ error = HTTP::TimeoutError.new
140
+ feature.on_error(request, error)
141
+ finish_event = instrumenter.events.find { |e| e[:type] == :finish }
142
+
143
+ assert_equal "error.http", finish_event[:name]
144
+ end
145
+
146
+ def test_on_error_logs_the_error_payload
147
+ error = HTTP::TimeoutError.new
148
+ feature.on_error(request, error)
149
+ finish_event = instrumenter.events.find { |e| e[:type] == :finish }
150
+
151
+ assert_equal({ request: request, error: error }, finish_event[:payload])
152
+ end
153
+
154
+ # -- NullInstrumenter --
155
+
156
+ def test_null_instrumenter_start_is_callable
157
+ null_instrumenter = HTTP::Features::Instrumentation::NullInstrumenter.new
158
+ null_instrumenter.start("test", {})
159
+ end
160
+
161
+ def test_null_instrumenter_finish_is_callable
162
+ null_instrumenter = HTTP::Features::Instrumentation::NullInstrumenter.new
163
+ null_instrumenter.finish("test", {})
164
+ end
165
+
166
+ def test_null_instrumenter_instrument_is_callable_without_a_block
167
+ null_instrumenter = HTTP::Features::Instrumentation::NullInstrumenter.new
168
+ null_instrumenter.instrument("test")
169
+ end
170
+
171
+ def test_null_instrumenter_instrument_yields_the_payload_to_the_block
172
+ null_instrumenter = HTTP::Features::Instrumentation::NullInstrumenter.new
173
+ received = nil
174
+ null_instrumenter.instrument("test", { key: "value" }) { |payload| received = payload }
175
+
176
+ assert_equal({ key: "value" }, received)
177
+ end
178
+
179
+ def test_null_instrumenter_instrument_defaults_payload_to_empty_hash
180
+ null_instrumenter = HTTP::Features::Instrumentation::NullInstrumenter.new
181
+ received = nil
182
+ null_instrumenter.instrument("test") { |payload| received = payload }
183
+
184
+ assert_equal({}, received)
185
+ end
186
+
187
+ def test_null_instrumenter_instrument_passes_name_to_start_and_finish
188
+ names = []
189
+ inst = HTTP::Features::Instrumentation::NullInstrumenter.new
190
+ inst.define_singleton_method(:start) { |name, _payload| names << [:start, name] }
191
+ inst.define_singleton_method(:finish) { |name, _payload| names << [:finish, name] }
192
+ inst.instrument("test.event") { nil }
193
+
194
+ assert_equal [[:start, "test.event"], [:finish, "test.event"]], names
195
+ end
196
+
197
+ def test_null_instrumenter_instrument_calls_finish_even_when_block_raises
198
+ finished = false
199
+ inst = HTTP::Features::Instrumentation::NullInstrumenter.new
200
+ inst.define_singleton_method(:finish) { |_name, _payload| finished = true }
201
+ assert_raises(RuntimeError) do
202
+ inst.instrument("test") { raise "boom" }
203
+ end
204
+ assert finished
205
+ end
206
+
207
+ # -- with an instrumenter that unconditionally yields --
208
+
209
+ def test_with_yielding_instrumenter_returns_the_response_even_without_payload
210
+ yielding_instrumenter = Class.new do
211
+ def instrument(_name, _payload = {})
212
+ yield
213
+ end
214
+
215
+ def start(_name, _payload = {}); end
216
+ def finish(_name, _payload = {}); end
217
+ end.new
218
+
219
+ feat = HTTP::Features::Instrumentation.new(instrumenter: yielding_instrumenter)
220
+ req = HTTP::Request.new(verb: :get, uri: "https://example.com/", headers: {})
221
+ resp = HTTP::Response.new(
222
+ version: "1.1",
223
+ status: 200,
224
+ body: "",
225
+ request: req
226
+ )
227
+ result = feat.around_request(req) { resp }
228
+
229
+ assert_same resp, result
230
+ end
231
+
232
+ def test_with_yielding_instrumenter_does_not_raise_local_jump_error_in_on_error
233
+ yielding_instrumenter = Class.new do
234
+ def instrument(_name, _payload = {})
235
+ yield
236
+ end
237
+
238
+ def start(_name, _payload = {}); end
239
+ def finish(_name, _payload = {}); end
240
+ end.new
241
+
242
+ feat = HTTP::Features::Instrumentation.new(instrumenter: yielding_instrumenter)
243
+ req = HTTP::Request.new(verb: :get, uri: "https://example.com/", headers: {})
244
+ feat.on_error(req, HTTP::TimeoutError.new)
245
+ end
246
+ end