http 6.0.0-java → 6.0.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/http.gemspec +2 -2
- data/lib/http/version.rb +1 -1
- metadata +4 -79
- data/CHANGELOG.md +0 -267
- data/CONTRIBUTING.md +0 -26
- data/SECURITY.md +0 -17
- data/UPGRADING.md +0 -491
- data/sig/deps.rbs +0 -122
- data/test/http/base64_test.rb +0 -28
- data/test/http/client_test.rb +0 -739
- data/test/http/connection_test.rb +0 -1533
- data/test/http/content_type_test.rb +0 -190
- data/test/http/errors_test.rb +0 -28
- data/test/http/feature_test.rb +0 -49
- data/test/http/features/auto_deflate_test.rb +0 -317
- data/test/http/features/auto_inflate_test.rb +0 -213
- data/test/http/features/caching_test.rb +0 -942
- data/test/http/features/digest_auth_test.rb +0 -996
- data/test/http/features/instrumentation_test.rb +0 -246
- data/test/http/features/logging_test.rb +0 -654
- data/test/http/features/normalize_uri_test.rb +0 -41
- data/test/http/features/raise_error_test.rb +0 -77
- data/test/http/form_data/composite_io_test.rb +0 -215
- data/test/http/form_data/file_test.rb +0 -255
- data/test/http/form_data/fixtures/the-http-gem.info +0 -1
- data/test/http/form_data/multipart_test.rb +0 -303
- data/test/http/form_data/part_test.rb +0 -90
- data/test/http/form_data/urlencoded_test.rb +0 -164
- data/test/http/form_data_test.rb +0 -232
- data/test/http/headers/normalizer_test.rb +0 -93
- data/test/http/headers_test.rb +0 -888
- data/test/http/mime_type/json_test.rb +0 -39
- data/test/http/mime_type_test.rb +0 -150
- data/test/http/options/base_uri_test.rb +0 -148
- data/test/http/options/body_test.rb +0 -21
- data/test/http/options/features_test.rb +0 -38
- data/test/http/options/form_test.rb +0 -21
- data/test/http/options/headers_test.rb +0 -32
- data/test/http/options/json_test.rb +0 -21
- data/test/http/options/merge_test.rb +0 -78
- data/test/http/options/new_test.rb +0 -37
- data/test/http/options/proxy_test.rb +0 -32
- data/test/http/options_test.rb +0 -575
- data/test/http/redirector_test.rb +0 -639
- data/test/http/request/body_test.rb +0 -318
- data/test/http/request/builder_test.rb +0 -623
- data/test/http/request/writer_test.rb +0 -391
- data/test/http/request_test.rb +0 -1733
- data/test/http/response/body_test.rb +0 -292
- data/test/http/response/parser_test.rb +0 -105
- data/test/http/response/status_test.rb +0 -322
- data/test/http/response_test.rb +0 -502
- data/test/http/retriable/delay_calculator_test.rb +0 -194
- data/test/http/retriable/errors_test.rb +0 -71
- data/test/http/retriable/performer_test.rb +0 -551
- data/test/http/session_test.rb +0 -424
- data/test/http/timeout/global_test.rb +0 -239
- data/test/http/timeout/null_test.rb +0 -218
- data/test/http/timeout/per_operation_test.rb +0 -220
- data/test/http/uri/normalizer_test.rb +0 -89
- data/test/http/uri_test.rb +0 -1140
- data/test/http/version_test.rb +0 -15
- data/test/http_test.rb +0 -818
- data/test/regression_tests.rb +0 -27
- data/test/support/capture_warning.rb +0 -10
- data/test/support/dummy_server/encoding_routes.rb +0 -47
- data/test/support/dummy_server/routes.rb +0 -201
- data/test/support/dummy_server/servlet.rb +0 -81
- data/test/support/dummy_server.rb +0 -200
- data/test/support/fakeio.rb +0 -21
- data/test/support/http_handling_shared/connection_reuse_tests.rb +0 -97
- data/test/support/http_handling_shared/timeout_tests.rb +0 -134
- data/test/support/http_handling_shared.rb +0 -11
- data/test/support/proxy_server.rb +0 -207
- data/test/support/servers/runner.rb +0 -67
- data/test/support/simplecov.rb +0 -28
- data/test/support/ssl_helper.rb +0 -108
- data/test/test_helper.rb +0 -38
data/UPGRADING.md
DELETED
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
# Upgrading to HTTP.rb 6.0
|
|
2
|
-
|
|
3
|
-
This guide covers all breaking changes between v5.x and v6.0 and shows how to
|
|
4
|
-
update your code.
|
|
5
|
-
|
|
6
|
-
## Ruby version
|
|
7
|
-
|
|
8
|
-
**v6 requires Ruby 3.2+.** Drop support for Ruby 2.x and 3.0/3.1.
|
|
9
|
-
|
|
10
|
-
## Quick reference
|
|
11
|
-
|
|
12
|
-
| What changed | v5 | v6 |
|
|
13
|
-
|---|---|---|
|
|
14
|
-
| Chainable return type | `HTTP::Client` | `HTTP::Session` |
|
|
15
|
-
| `HTTP.persistent` return type | `HTTP::Client` | `HTTP::Session` (pools per origin) |
|
|
16
|
-
| `.retriable` return type | `HTTP::Retriable::Client` | `HTTP::Session` |
|
|
17
|
-
| `response.cookies` | `HTTP::CookieJar` | `Array<HTTP::Cookie>` |
|
|
18
|
-
| `response["Header"]` | Works (via `Headers::Mixin`) | Removed — use `response.headers["Header"]` |
|
|
19
|
-
| `request["Header"]` | Works (via `Headers::Mixin`) | Removed — use `request.headers["Header"]` |
|
|
20
|
-
| `status.even?`, `status.zero?`, etc. | Works (via `Delegator`) | Removed — use `status.code.even?` |
|
|
21
|
-
| `build_request` | On `Client`, `Session`, `HTTP` | Removed — use `HTTP::Request::Builder` |
|
|
22
|
-
| Options API | Accepts `Hash` or keywords | Keywords only |
|
|
23
|
-
| `addressable` gem | Required dependency | Optional (only for non-ASCII URIs) |
|
|
24
|
-
| `URI` setters (`scheme=`, etc.) | Available | Removed |
|
|
25
|
-
| `URI#join`, `URI#omit` | Returns `Addressable::URI` | Returns `HTTP::URI` |
|
|
26
|
-
| `readpartial` at EOF | Returns `nil` | Raises `EOFError` |
|
|
27
|
-
| Timeout defaults | 0.25s for omitted operations | No timeout for omitted operations |
|
|
28
|
-
| Global + per-op timeouts | Mutually exclusive | Can be combined |
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## Breaking changes in detail
|
|
33
|
-
|
|
34
|
-
### 1. Chainable methods return `HTTP::Session` instead of `HTTP::Client`
|
|
35
|
-
|
|
36
|
-
All chainable configuration methods (`.headers`, `.timeout`, `.cookies`, `.auth`,
|
|
37
|
-
`.follow`, `.via`, `.use`, `.encoding`, `.nodelay`, `.basic_auth`, `.accept`)
|
|
38
|
-
now return a thread-safe `HTTP::Session` instead of `HTTP::Client`.
|
|
39
|
-
|
|
40
|
-
`Session` creates a fresh `Client` for each request, making it safe to share
|
|
41
|
-
across threads. The HTTP verb methods (`.get`, `.post`, etc.) and
|
|
42
|
-
`.default_options` work the same way.
|
|
43
|
-
|
|
44
|
-
```ruby
|
|
45
|
-
# v5
|
|
46
|
-
client = HTTP.headers("Accept" => "application/json")
|
|
47
|
-
client.is_a?(HTTP::Client) # => true
|
|
48
|
-
|
|
49
|
-
# v6
|
|
50
|
-
session = HTTP.headers("Accept" => "application/json")
|
|
51
|
-
session.is_a?(HTTP::Session) # => true
|
|
52
|
-
session.get("https://example.com") # works the same
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**Action:** Update any `is_a?(HTTP::Client)` checks on the return value of
|
|
56
|
-
chainable methods to check for `HTTP::Session`.
|
|
57
|
-
|
|
58
|
-
### 2. `HTTP.persistent` returns `HTTP::Session` with connection pooling
|
|
59
|
-
|
|
60
|
-
`HTTP.persistent` now returns an `HTTP::Session` that pools one persistent
|
|
61
|
-
`HTTP::Client` per origin. This means cross-origin redirects work automatically
|
|
62
|
-
instead of raising `StateError`.
|
|
63
|
-
|
|
64
|
-
```ruby
|
|
65
|
-
# v5
|
|
66
|
-
client = HTTP.persistent("https://api.example.com")
|
|
67
|
-
client.is_a?(HTTP::Client) # => true
|
|
68
|
-
# Cross-origin redirects raise StateError
|
|
69
|
-
|
|
70
|
-
# v6
|
|
71
|
-
session = HTTP.persistent("https://api.example.com")
|
|
72
|
-
session.is_a?(HTTP::Session) # => true
|
|
73
|
-
# Cross-origin redirects work — each origin gets its own connection
|
|
74
|
-
session.close # shuts down all pooled connections
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Chaining on a persistent session now shares the connection pool:
|
|
78
|
-
|
|
79
|
-
```ruby
|
|
80
|
-
# v5 — this broke connection reuse
|
|
81
|
-
HTTP.persistent("https://api.example.com").headers("X-Token" => "abc").get("/users")
|
|
82
|
-
|
|
83
|
-
# v6 — works correctly, shares the parent's connection pool
|
|
84
|
-
HTTP.persistent("https://api.example.com").headers("X-Token" => "abc").get("/users")
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 3. `.retriable` returns `HTTP::Session` instead of `HTTP::Retriable::Client`
|
|
88
|
-
|
|
89
|
-
Retry is now a session-level option. The `HTTP::Retriable::Client` and
|
|
90
|
-
`HTTP::Retriable::Session` classes no longer exist.
|
|
91
|
-
|
|
92
|
-
```ruby
|
|
93
|
-
# v5
|
|
94
|
-
client = HTTP.retriable(tries: 3)
|
|
95
|
-
client.is_a?(HTTP::Retriable::Client) # => true
|
|
96
|
-
|
|
97
|
-
# v6
|
|
98
|
-
session = HTTP.retriable(tries: 3)
|
|
99
|
-
session.is_a?(HTTP::Session) # => true
|
|
100
|
-
session.get("https://example.com") # retries up to 3 times
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 4. Options hashes replaced with keyword arguments
|
|
104
|
-
|
|
105
|
-
Methods across the public API now require keyword arguments. Passing an explicit
|
|
106
|
-
`Hash` as a positional argument no longer works, and unrecognized keywords raise
|
|
107
|
-
`ArgumentError`.
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
# v5 — both work
|
|
111
|
-
HTTP.get("https://example.com", body: "data")
|
|
112
|
-
HTTP.get("https://example.com", {body: "data"})
|
|
113
|
-
|
|
114
|
-
opts = {body: "data", headers: {"Content-Type" => "text/plain"}}
|
|
115
|
-
HTTP.get("https://example.com", opts)
|
|
116
|
-
|
|
117
|
-
# v6 — keywords only
|
|
118
|
-
HTTP.get("https://example.com", body: "data")
|
|
119
|
-
|
|
120
|
-
opts = {body: "data", headers: {"Content-Type" => "text/plain"}}
|
|
121
|
-
HTTP.get("https://example.com", **opts) # note the double-splat
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
This applies to constructors too:
|
|
125
|
-
|
|
126
|
-
```ruby
|
|
127
|
-
# v5
|
|
128
|
-
HTTP::Options.new({response: :body})
|
|
129
|
-
HTTP::Client.new({timeout_class: HTTP::Timeout::Global})
|
|
130
|
-
|
|
131
|
-
# v6
|
|
132
|
-
HTTP::Options.new(response: :body)
|
|
133
|
-
HTTP::Client.new(timeout_class: HTTP::Timeout::Global)
|
|
134
|
-
|
|
135
|
-
# If you have an options hash, use double-splat:
|
|
136
|
-
opts = {response: :body}
|
|
137
|
-
HTTP::Options.new(**opts)
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Affected methods: all HTTP verb methods, `request`, `follow`, `retriable`,
|
|
141
|
-
`URI.new`, `Request.new`, `Response.new`, `Options.new`, `Client.new`,
|
|
142
|
-
`Session.new`.
|
|
143
|
-
|
|
144
|
-
### 5. `Headers::Mixin` removed — no more `[]`/`[]=` on Request and Response
|
|
145
|
-
|
|
146
|
-
`Request` and `Response` no longer include `Headers::Mixin`, so you can't use
|
|
147
|
-
bracket access directly on them.
|
|
148
|
-
|
|
149
|
-
```ruby
|
|
150
|
-
# v5
|
|
151
|
-
response = HTTP.get("https://example.com")
|
|
152
|
-
response["Content-Type"] # => "text/html"
|
|
153
|
-
request["Authorization"] = "Bearer token"
|
|
154
|
-
|
|
155
|
-
# v6
|
|
156
|
-
response = HTTP.get("https://example.com")
|
|
157
|
-
response.headers["Content-Type"] # => "text/html"
|
|
158
|
-
request.headers["Authorization"] = "Bearer token"
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### 6. `Response#cookies` returns `Array` instead of `CookieJar`
|
|
162
|
-
|
|
163
|
-
```ruby
|
|
164
|
-
# v5
|
|
165
|
-
response.cookies # => #<HTTP::CookieJar ...>
|
|
166
|
-
response.cookies.each { |cookie| puts cookie.name }
|
|
167
|
-
jar = response.cookies
|
|
168
|
-
jar["session_id"] # CookieJar lookup
|
|
169
|
-
|
|
170
|
-
# v6
|
|
171
|
-
response.cookies # => [#<HTTP::Cookie ...>, ...]
|
|
172
|
-
response.cookies.each { |cookie| puts cookie.name }
|
|
173
|
-
cookie = response.cookies.find { |c| c.name == "session_id" }
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
The `cookies` chainable option also changed — the last `.cookies()` call wins
|
|
177
|
-
(no implicit merging):
|
|
178
|
-
|
|
179
|
-
```ruby
|
|
180
|
-
# v5 — cookies merged across calls
|
|
181
|
-
HTTP.cookies(a: "1").cookies(b: "2") # sends both a=1 and b=2
|
|
182
|
-
|
|
183
|
-
# v6 — last call wins
|
|
184
|
-
HTTP.cookies(a: "1").cookies(b: "2") # sends only b=2
|
|
185
|
-
HTTP.cookies(a: "1", b: "2") # sends both a=1 and b=2
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### 7. `Response::Status` no longer delegates to `Integer`
|
|
189
|
-
|
|
190
|
-
`Status` is no longer a `Delegator` subclass. It uses `Comparable` and
|
|
191
|
-
`Forwardable` instead, providing `to_i`, `to_int`, `<=>`, and named predicates.
|
|
192
|
-
|
|
193
|
-
```ruby
|
|
194
|
-
status = response.status
|
|
195
|
-
|
|
196
|
-
# Still works in v6
|
|
197
|
-
status.to_i # => 200
|
|
198
|
-
status == 200 # => true (via Comparable + to_int)
|
|
199
|
-
(200..299).cover?(status) # => true (via to_int)
|
|
200
|
-
status.ok? # => true
|
|
201
|
-
status.success? # => true
|
|
202
|
-
status.to_s # => "200 OK"
|
|
203
|
-
status.code # => 200
|
|
204
|
-
status.reason # => "OK"
|
|
205
|
-
|
|
206
|
-
# v5 only — breaks in v6
|
|
207
|
-
status.even? # NoMethodError — use status.code.even?
|
|
208
|
-
status.between?(200, 299) # NoMethodError — use status.code.between?(200, 299)
|
|
209
|
-
status + 1 # NoMethodError — use status.code + 1
|
|
210
|
-
status.__getobj__ # NoMethodError — use status.code
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
**Action:** Replace any direct `Integer` method calls on status objects with
|
|
214
|
-
`status.code.<method>`.
|
|
215
|
-
|
|
216
|
-
### 8. `build_request` removed — use `HTTP::Request::Builder`
|
|
217
|
-
|
|
218
|
-
The `build_request` method has been removed from `Client`, `Session`, and the
|
|
219
|
-
top-level `HTTP` module.
|
|
220
|
-
|
|
221
|
-
```ruby
|
|
222
|
-
# v5
|
|
223
|
-
request = HTTP.build_request(:get, "https://example.com")
|
|
224
|
-
request = HTTP.headers("Accept" => "application/json")
|
|
225
|
-
.build_request(:post, "https://example.com", json: {name: "test"})
|
|
226
|
-
|
|
227
|
-
# v6
|
|
228
|
-
options = HTTP::Options.new(headers: {"Accept" => "application/json"},
|
|
229
|
-
json: {name: "test"})
|
|
230
|
-
builder = HTTP::Request::Builder.new(options)
|
|
231
|
-
request = builder.build(:post, "https://example.com")
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### 9. `readpartial` raises `EOFError` instead of returning `nil`
|
|
235
|
-
|
|
236
|
-
`Connection#readpartial`, `Body#readpartial`, and `Inflater#readpartial` now
|
|
237
|
-
raise `EOFError` at end-of-stream instead of returning `nil`, conforming to
|
|
238
|
-
Ruby's `IO#readpartial` contract.
|
|
239
|
-
|
|
240
|
-
```ruby
|
|
241
|
-
# v5
|
|
242
|
-
loop do
|
|
243
|
-
chunk = response.body.readpartial
|
|
244
|
-
break if chunk.nil?
|
|
245
|
-
process(chunk)
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
# v6
|
|
249
|
-
loop do
|
|
250
|
-
chunk = response.body.readpartial
|
|
251
|
-
process(chunk)
|
|
252
|
-
rescue EOFError
|
|
253
|
-
break
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
# Or use the simpler iterator API (works in both versions):
|
|
257
|
-
response.body.each { |chunk| process(chunk) }
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### 10. Timeout behavior changes
|
|
261
|
-
|
|
262
|
-
#### No more 0.25s default for omitted per-operation timeouts
|
|
263
|
-
|
|
264
|
-
```ruby
|
|
265
|
-
# v5 — omitted operations default to 0.25s
|
|
266
|
-
HTTP.timeout(read: 30).get(url)
|
|
267
|
-
# write and connect timeouts are 0.25s
|
|
268
|
-
|
|
269
|
-
# v6 — omitted operations have no timeout
|
|
270
|
-
HTTP.timeout(read: 30).get(url)
|
|
271
|
-
# write and connect have no timeout limit
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
**Action:** If you relied on the implicit 0.25s timeout, set all three
|
|
275
|
-
operations explicitly:
|
|
276
|
-
|
|
277
|
-
```ruby
|
|
278
|
-
HTTP.timeout(read: 0.25, write: 0.25, connect: 0.25).get(url)
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
#### Stricter timeout options parsing
|
|
282
|
-
|
|
283
|
-
```ruby
|
|
284
|
-
# v6 — rejects unknown keys
|
|
285
|
-
HTTP.timeout(read: 5, keep_alive: 10)
|
|
286
|
-
# => ArgumentError: unknown timeout options: keep_alive
|
|
287
|
-
|
|
288
|
-
# v6 — rejects mixed short/long forms
|
|
289
|
-
HTTP.timeout(read: 5, write_timeout: 3)
|
|
290
|
-
# => ArgumentError (use one form consistently)
|
|
291
|
-
|
|
292
|
-
# Valid in v6
|
|
293
|
-
HTTP.timeout(read: 5, write: 3, connect: 2)
|
|
294
|
-
HTTP.timeout(read_timeout: 5, write_timeout: 3, connect_timeout: 2)
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
#### Global and per-operation timeouts can be combined
|
|
298
|
-
|
|
299
|
-
```ruby
|
|
300
|
-
# v5 — mutually exclusive, raises ArgumentError
|
|
301
|
-
HTTP.timeout(global: 60, read: 30)
|
|
302
|
-
|
|
303
|
-
# v6 — works: 60s overall, 30s max per read, 10s max per write, 5s max per connect
|
|
304
|
-
HTTP.timeout(global: 60, read: 30, write: 10, connect: 5)
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
### 11. `addressable` is no longer a runtime dependency
|
|
308
|
-
|
|
309
|
-
`addressable` is lazy-loaded only when parsing non-ASCII (IRI) URIs. If your
|
|
310
|
-
code uses non-ASCII characters in URIs, add `addressable` to your Gemfile:
|
|
311
|
-
|
|
312
|
-
```ruby
|
|
313
|
-
# Gemfile
|
|
314
|
-
gem "addressable"
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
ASCII-only URIs use Ruby's stdlib `URI` parser exclusively and do not need
|
|
318
|
-
`addressable`.
|
|
319
|
-
|
|
320
|
-
### 12. `HTTP::URI` API changes
|
|
321
|
-
|
|
322
|
-
#### Removed setter methods
|
|
323
|
-
|
|
324
|
-
URI objects are now effectively immutable. Setter methods (`scheme=`, `host=`,
|
|
325
|
-
`port=`, `path=`, `query=`, `fragment=`, `user=`, `password=`, etc.) have been
|
|
326
|
-
removed.
|
|
327
|
-
|
|
328
|
-
```ruby
|
|
329
|
-
# v5
|
|
330
|
-
uri = HTTP::URI.parse("https://example.com")
|
|
331
|
-
uri.scheme = "http"
|
|
332
|
-
uri.path = "/api"
|
|
333
|
-
|
|
334
|
-
# v6 — construct a new URI instead
|
|
335
|
-
uri = HTTP::URI.parse("http://example.com/api")
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
#### `join` and `omit` now return `HTTP::URI`
|
|
339
|
-
|
|
340
|
-
```ruby
|
|
341
|
-
# v5
|
|
342
|
-
uri = HTTP::URI.parse("https://example.com")
|
|
343
|
-
joined = uri.join("/path")
|
|
344
|
-
joined.is_a?(Addressable::URI) # => true
|
|
345
|
-
|
|
346
|
-
# v6
|
|
347
|
-
joined = uri.join("/path")
|
|
348
|
-
joined.is_a?(HTTP::URI) # => true
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
#### Removed `query_values` / `query_values=`
|
|
352
|
-
|
|
353
|
-
```ruby
|
|
354
|
-
# v5
|
|
355
|
-
uri.query_values = {"page" => "1", "per" => "10"}
|
|
356
|
-
uri.query_values # => {"page" => "1", "per" => "10"}
|
|
357
|
-
|
|
358
|
-
# v6 — use stdlib
|
|
359
|
-
uri = HTTP::URI.parse("https://example.com")
|
|
360
|
-
query = URI.encode_www_form(page: 1, per: 10)
|
|
361
|
-
uri = HTTP::URI.parse("https://example.com?#{query}")
|
|
362
|
-
|
|
363
|
-
# or use the params option:
|
|
364
|
-
HTTP.get("https://example.com", params: {page: 1, per: 10})
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
#### Removed `form_encode`
|
|
368
|
-
|
|
369
|
-
```ruby
|
|
370
|
-
# v5
|
|
371
|
-
HTTP::URI.form_encode(page: 1, per: 10)
|
|
372
|
-
|
|
373
|
-
# v6
|
|
374
|
-
URI.encode_www_form(page: 1, per: 10)
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
#### `HTTP::URI.new` no longer accepts `Addressable::URI`
|
|
378
|
-
|
|
379
|
-
```ruby
|
|
380
|
-
# v5
|
|
381
|
-
addr = Addressable::URI.parse("https://example.com")
|
|
382
|
-
HTTP::URI.new(addr) # works
|
|
383
|
-
|
|
384
|
-
# v6
|
|
385
|
-
HTTP::URI.new(addr) # ArgumentError
|
|
386
|
-
HTTP::URI.parse("https://example.com") # use parse instead
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### 13. Error class changes
|
|
390
|
-
|
|
391
|
-
#### Malformed URI errors
|
|
392
|
-
|
|
393
|
-
```ruby
|
|
394
|
-
# v5
|
|
395
|
-
HTTP.get("not a uri")
|
|
396
|
-
# => HTTP::UnsupportedSchemeError or Addressable::URI::InvalidURIError
|
|
397
|
-
|
|
398
|
-
# v6
|
|
399
|
-
HTTP.get("not a uri")
|
|
400
|
-
# => HTTP::URI::InvalidError (for malformed URIs)
|
|
401
|
-
|
|
402
|
-
HTTP.get(nil)
|
|
403
|
-
# => ArgumentError (for nil/empty URIs)
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
#### New `ConnectionError` subclasses
|
|
407
|
-
|
|
408
|
-
`ConnectionError` now has more specific subclasses for targeted rescue:
|
|
409
|
-
|
|
410
|
-
- `HTTP::ResponseHeaderError` — header parsing failed
|
|
411
|
-
- `HTTP::SocketReadError` — socket read failed
|
|
412
|
-
- `HTTP::SocketWriteError` — socket write failed
|
|
413
|
-
|
|
414
|
-
```ruby
|
|
415
|
-
# v5
|
|
416
|
-
rescue HTTP::ConnectionError => e
|
|
417
|
-
# all connection errors
|
|
418
|
-
|
|
419
|
-
# v6 — you can be more specific
|
|
420
|
-
rescue HTTP::SocketReadError => e
|
|
421
|
-
# only read failures
|
|
422
|
-
rescue HTTP::ConnectionError => e
|
|
423
|
-
# still catches all connection errors (superclass)
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
### 14. Security: credential stripping on cross-origin redirects
|
|
427
|
-
|
|
428
|
-
`Authorization` and `Cookie` headers are now automatically stripped when
|
|
429
|
-
following redirects to a different origin (scheme + host + port). This is a
|
|
430
|
-
security improvement, but may break code that intentionally sends credentials
|
|
431
|
-
across origins.
|
|
432
|
-
|
|
433
|
-
---
|
|
434
|
-
|
|
435
|
-
## New features (non-breaking)
|
|
436
|
-
|
|
437
|
-
These are new in v6 and require no migration, but are worth knowing about:
|
|
438
|
-
|
|
439
|
-
### Block form for verb methods
|
|
440
|
-
|
|
441
|
-
Auto-closes the connection after the block returns:
|
|
442
|
-
|
|
443
|
-
```ruby
|
|
444
|
-
body = HTTP.get("https://example.com") do |response|
|
|
445
|
-
response.body.to_s
|
|
446
|
-
end
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
### `HTTP.base_uri`
|
|
450
|
-
|
|
451
|
-
Set a base URI for relative paths:
|
|
452
|
-
|
|
453
|
-
```ruby
|
|
454
|
-
api = HTTP.base_uri("https://api.example.com/v1")
|
|
455
|
-
api.get("users") # GET https://api.example.com/v1/users
|
|
456
|
-
api.get("posts") # GET https://api.example.com/v1/posts
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
### HTTP Digest Authentication
|
|
460
|
-
|
|
461
|
-
```ruby
|
|
462
|
-
HTTP.digest_auth(user: "admin", pass: "secret").get("https://example.com/protected")
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### HTTP Caching (RFC 7234)
|
|
466
|
-
|
|
467
|
-
```ruby
|
|
468
|
-
HTTP.use(:caching).get("https://example.com") # caches with in-memory store
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
### Pattern matching
|
|
472
|
-
|
|
473
|
-
```ruby
|
|
474
|
-
case response
|
|
475
|
-
in { status: { code: 200..299 }, content_type: { mime_type: "application/json" } }
|
|
476
|
-
response.parse
|
|
477
|
-
in { status: { code: 404 } }
|
|
478
|
-
nil
|
|
479
|
-
end
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
### `Feature#on_request` and `Feature#around_request` hooks
|
|
483
|
-
|
|
484
|
-
New feature lifecycle hooks called before/around each request attempt (including
|
|
485
|
-
retries), useful for instrumentation and circuit breakers.
|
|
486
|
-
|
|
487
|
-
### `PURGE` HTTP method
|
|
488
|
-
|
|
489
|
-
```ruby
|
|
490
|
-
HTTP.request(:purge, "https://cdn.example.com/asset")
|
|
491
|
-
```
|
data/sig/deps.rbs
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
# Overrides for stdlib RBS signatures
|
|
2
|
-
|
|
3
|
-
class ::StringIO
|
|
4
|
-
# Override: Ruby's StringIO#read accepts nil outbuf
|
|
5
|
-
def read: (?int? length, ?string? buf) -> String?
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
class ::File
|
|
9
|
-
# Override: Ruby's File.new accepts keyword args like binmode:
|
|
10
|
-
def initialize: (string | _ToPath | int file_name, ?string | int mode, ?int perm, **untyped) -> void
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
class ::Pathname
|
|
14
|
-
# Override: Ruby's Pathname#open accepts keyword args like binmode:
|
|
15
|
-
def open: (?string | int mode, ?int perm, **untyped) -> ::File
|
|
16
|
-
| [T] (?string | int mode, ?int perm, **untyped) { (::File) -> T } -> T
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Stubs for external dependencies without RBS definitions
|
|
20
|
-
|
|
21
|
-
module Addressable
|
|
22
|
-
class URI
|
|
23
|
-
class InvalidURIError < StandardError
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def self.parse: (untyped) -> Addressable::URI
|
|
27
|
-
def self.form_encode: (untyped, ?bool) -> String
|
|
28
|
-
def self.normalize_path: (untyped) -> String
|
|
29
|
-
|
|
30
|
-
def initialize: (?untyped) -> void
|
|
31
|
-
def scheme: () -> String?
|
|
32
|
-
def scheme=: (untyped) -> untyped
|
|
33
|
-
def normalized_scheme: () -> String?
|
|
34
|
-
def authority: () -> String?
|
|
35
|
-
def normalized_authority: () -> String?
|
|
36
|
-
def user: () -> String?
|
|
37
|
-
def user=: (untyped) -> untyped
|
|
38
|
-
def password: () -> String?
|
|
39
|
-
def password=: (untyped) -> untyped
|
|
40
|
-
def host: () -> String?
|
|
41
|
-
def host=: (untyped) -> untyped
|
|
42
|
-
def normalized_host: () -> String?
|
|
43
|
-
def port: () -> Integer?
|
|
44
|
-
def port=: (untyped) -> untyped
|
|
45
|
-
def normalized_port: () -> Integer?
|
|
46
|
-
def path: () -> String
|
|
47
|
-
def path=: (untyped) -> untyped
|
|
48
|
-
def normalized_path: () -> String
|
|
49
|
-
def query: () -> String?
|
|
50
|
-
def query=: (untyped) -> untyped
|
|
51
|
-
def query_values: (?untyped?) -> untyped
|
|
52
|
-
def query_values=: (untyped) -> untyped
|
|
53
|
-
def normalized_query: () -> String?
|
|
54
|
-
def request_uri: () -> String
|
|
55
|
-
def request_uri=: (untyped) -> untyped
|
|
56
|
-
def fragment: () -> String?
|
|
57
|
-
def normalized_fragment: () -> String?
|
|
58
|
-
def fragment=: (untyped) -> untyped
|
|
59
|
-
def omit: (*Symbol) -> Addressable::URI
|
|
60
|
-
def join: (untyped) -> Addressable::URI
|
|
61
|
-
def normalize: () -> Addressable::URI
|
|
62
|
-
def origin: () -> String
|
|
63
|
-
def default_port: () -> Integer?
|
|
64
|
-
def to_s: () -> String
|
|
65
|
-
def ==: (untyped) -> bool
|
|
66
|
-
def eql?: (untyped) -> bool
|
|
67
|
-
def hash: () -> Integer
|
|
68
|
-
def dup: () -> Addressable::URI
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Override: stdlib zlib RBS incorrectly marks level/strategy as required
|
|
73
|
-
module Zlib
|
|
74
|
-
class GzipWriter
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
def initialize: (untyped io, ?Integer? level, ?Integer? strategy, **untyped opts) -> void
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
module LLHttp
|
|
82
|
-
class Error < StandardError
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
class Parser
|
|
86
|
-
def initialize: (untyped, ?type: Symbol) -> void
|
|
87
|
-
def reset: () -> void
|
|
88
|
-
def <<: (String) -> void
|
|
89
|
-
def status_code: () -> Integer
|
|
90
|
-
def http_major: () -> Integer
|
|
91
|
-
def http_minor: () -> Integer
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
class Delegate
|
|
95
|
-
def initialize: () -> void
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Supplement: Numeric#to_i and #to_f are not in the stdlib RBS
|
|
100
|
-
class Numeric
|
|
101
|
-
def to_i: () -> Integer
|
|
102
|
-
def to_f: () -> Float
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
module IO::WaitReadable
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
module IO::WaitWritable
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
class IO
|
|
112
|
-
# Missing from rbs core (available since Ruby 3.2)
|
|
113
|
-
class TimeoutError < IOError
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
class EAGAINWaitReadable < Errno::EAGAIN
|
|
117
|
-
include IO::WaitReadable
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def wait_readable: (?Numeric?) -> (IO | true | nil)
|
|
121
|
-
def wait_writable: (?Numeric?) -> (IO | true | nil)
|
|
122
|
-
end
|
data/test/http/base64_test.rb
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "test_helper"
|
|
4
|
-
|
|
5
|
-
class HTTPBase64Test < Minitest::Test
|
|
6
|
-
cover "HTTP::Base64*"
|
|
7
|
-
|
|
8
|
-
def encoder
|
|
9
|
-
@encoder ||= begin
|
|
10
|
-
klass = Class.new { include HTTP::Base64 }
|
|
11
|
-
klass.new
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def test_encode64_encodes_a_string_using_strict_base64
|
|
16
|
-
assert_equal "aGVsbG8=", encoder.send(:encode64, "hello")
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def test_encode64_produces_output_that_round_trips_back_to_the_original_input
|
|
20
|
-
input = "user:password"
|
|
21
|
-
|
|
22
|
-
assert_equal input, encoder.send(:encode64, input).unpack1("m0")
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def test_encode64_encodes_empty_string
|
|
26
|
-
assert_equal "", encoder.send(:encode64, "")
|
|
27
|
-
end
|
|
28
|
-
end
|