http.rb 0.22.0 → 0.24.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.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -1
- data/README.md +19 -32
- data/lib/HTTP/RETRY.rb +2 -1
- data/lib/HTTP/VERSION.rb +1 -1
- data/lib/HTTP/request.rb +4 -3
- data/lib/Net/HTTPResponse/StatusPredicates.rb +4 -1
- data/test/HTTP/RETRY_test.rb +14 -0
- data/test/HTTP/get_test.rb +63 -0
- data/test/Net/HTTPResponse/StatusPredicates_test.rb +28 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 192aecaee0d22accbabb0b03b00f987cf7902c4958d6d5dab823515f4e681408
|
|
4
|
+
data.tar.gz: 1adb47bbf1b800b6e3fa9f7767db4a5b63f0b23abe12547056e564bda0c76713
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 608a398a78d14c9ba1a40db7478510b5a25cc073898a9fa1759c783b3aec8b9f6846597662972f15552f561a6f9e8269485053aab2f707e874f0c753a98144df
|
|
7
|
+
data.tar.gz: 8d9e7449e938c01bad80526aa566cc9886c76df5020cbc556dd458e1acb6f0c2af1b23d3b53dc872df39ec6f27f1c828bbe5bcdc9cfea31c4b5cd3167e129c1d
|
data/CHANGELOG
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## 20260522
|
|
4
4
|
|
|
5
|
+
0.24.0: ok? predicate now means 200 specifically.
|
|
6
|
+
|
|
7
|
+
1. ~ Net::HTTPResponse::StatusPredicates: ok? is now defined as @code == '200' rather than aliased to successful?. The alias semantics (any 2xx) misled — "OK" is the reason-phrase for 200 specifically, not the 2xx class. Migration: callers wanting any-2xx should use successful? (or its alias success?).
|
|
8
|
+
2. + test/Net/HTTPResponse/StatusPredicates_test.rb: Specs for ok? against 200, other 2xx codes, and a 4xx code.
|
|
9
|
+
3. ~ README.md: Status-predicate section rewritten — single coherent reference for the predicate set, with ok? listed as 200-specific.
|
|
10
|
+
4. ~ HTTP::VERSION: /0.23.0/0.24.0/
|
|
11
|
+
5. ~ CHANGELOG: + 0.24.0 entry
|
|
12
|
+
|
|
13
|
+
## 20260522
|
|
14
|
+
|
|
15
|
+
0.23.0: Fix cross-scheme redirect SSL leak; clamp negative Retry-After.
|
|
16
|
+
|
|
17
|
+
1. ~ HTTP.request: Compute http-object SSL configuration via options.merge instead of mutating the caller's options hash with auto-derived use_ssl and verify_mode. The previous ||= writes meant an HTTPS→HTTP redirect carried use_ssl: true through to the recursive call against the HTTP host, attempting an SSL handshake on the plain-HTTP port. Cross-scheme redirects in both directions now re-derive use_ssl from each URI.
|
|
18
|
+
2. ~ HTTP.retry_after: Clamp the HTTP-date branch via delta && [delta, 0].max. A past Retry-After HTTP-date previously returned a negative delta; Kernel.sleep raises ArgumentError on negatives.
|
|
19
|
+
3. ~ test/HTTP/get_test.rb: + cross-scheme redirection specs (HTTPS→HTTP and HTTP→HTTPS) using a Net::HTTP.new stub to capture each Net::HTTP instance and assert use_ssl? per hop.
|
|
20
|
+
4. ~ test/HTTP/RETRY_test.rb: + specs for past-date Retry-After (clamps to 0) and negative-integer Retry-After (returns nil).
|
|
21
|
+
5. ~ HTTP::VERSION: /0.22.0/0.23.0/
|
|
22
|
+
6. ~ CHANGELOG: + 0.23.0 entry; fix 0.13.2 date typo (202503030 → 20250330).
|
|
23
|
+
|
|
24
|
+
## 20260522
|
|
25
|
+
|
|
5
26
|
0.22.0: Convert specs from RSpec to Minitest.
|
|
6
27
|
|
|
7
28
|
1. ~ spec/ → test/: All spec files moved to test/ and rewritten in Minitest spec style with `let`, double-quoted descriptions, and `_(...).must_*` expectations. On the TODO since at least 0.17.0; completed before 1.0 to ship into stability on the test framework that's here to stay.
|
|
@@ -206,7 +227,7 @@
|
|
|
206
227
|
2. ~ HTTP::VERSION: /0.13.2/0.13.3/
|
|
207
228
|
3. ~ http.rb.gemspec: Change date.
|
|
208
229
|
|
|
209
|
-
##
|
|
230
|
+
## 20250330
|
|
210
231
|
|
|
211
232
|
0.13.2: Change repo name to match gem name (/HTTP/http.rb/); + Use HTTP::VERSION; /require/require_relative/
|
|
212
233
|
|
data/README.md
CHANGED
|
@@ -114,48 +114,35 @@ options = {
|
|
|
114
114
|
|
|
115
115
|
### Response status predicate methods
|
|
116
116
|
|
|
117
|
+
Every response carries predicates for each status class:
|
|
118
|
+
|
|
117
119
|
```ruby
|
|
118
|
-
# 1xx
|
|
119
|
-
response
|
|
120
|
-
response.
|
|
121
|
-
#
|
|
120
|
+
response.informational? # 1xx
|
|
121
|
+
response.successful? # 2xx (aliased as success?)
|
|
122
|
+
response.redirection? # 3xx
|
|
123
|
+
response.client_error? # 4xx
|
|
124
|
+
response.server_error? # 5xx
|
|
125
|
+
response.error? # 4xx or 5xx
|
|
126
|
+
response.ok? # 200 specifically
|
|
127
|
+
```
|
|
122
128
|
|
|
123
|
-
|
|
124
|
-
response = HTTP.get('http://example.com')
|
|
125
|
-
response.success?
|
|
126
|
-
# => true
|
|
129
|
+
Redirects are followed by default, so a 3xx is only surfaced when `no_redirect` is set:
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
response = HTTP.get('http://example.com', {}, {}, {no_redirect: true})
|
|
131
|
+
```ruby
|
|
132
|
+
response = HTTP.get('http://example.com/moved', {}, {}, {no_redirect: true})
|
|
130
133
|
response.redirection?
|
|
131
134
|
# => true
|
|
132
|
-
response.
|
|
135
|
+
response.successful?
|
|
133
136
|
# => false
|
|
137
|
+
```
|
|
134
138
|
|
|
135
|
-
response
|
|
136
|
-
response.redirection?
|
|
137
|
-
# => false
|
|
138
|
-
response.success?
|
|
139
|
-
# => true
|
|
139
|
+
Without `no_redirect`, the redirect is followed and `response` is the final destination:
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
```ruby
|
|
142
|
+
response = HTTP.get('http://example.com/moved')
|
|
142
143
|
response.redirection?
|
|
143
144
|
# => false
|
|
144
|
-
response.
|
|
145
|
-
# => true
|
|
146
|
-
|
|
147
|
-
# 4xx
|
|
148
|
-
response = HTTP.get('http://example.com')
|
|
149
|
-
response.client_error?
|
|
150
|
-
# => true
|
|
151
|
-
response.error?
|
|
152
|
-
# => true
|
|
153
|
-
|
|
154
|
-
# 5xx
|
|
155
|
-
response = HTTP.get('http://example.com')
|
|
156
|
-
response.server_error?
|
|
157
|
-
# => true
|
|
158
|
-
response.error?
|
|
145
|
+
response.successful?
|
|
159
146
|
# => true
|
|
160
147
|
```
|
|
161
148
|
|
data/lib/HTTP/RETRY.rb
CHANGED
|
@@ -69,7 +69,8 @@ module HTTP
|
|
|
69
69
|
header.to_i
|
|
70
70
|
else
|
|
71
71
|
# Malformed HTTP-date — fall through to caller's backoff.
|
|
72
|
-
Time.httpdate(header) - Time.now rescue nil
|
|
72
|
+
delta = Time.httpdate(header) - Time.now rescue nil
|
|
73
|
+
delta && [delta, 0].max
|
|
73
74
|
end
|
|
74
75
|
end
|
|
75
76
|
module_function :retry_after
|
data/lib/HTTP/VERSION.rb
CHANGED
data/lib/HTTP/request.rb
CHANGED
|
@@ -17,9 +17,10 @@ module HTTP
|
|
|
17
17
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
18
18
|
no_redirect = options.delete(:no_redirect)
|
|
19
19
|
config = retry_config(options)
|
|
20
|
-
options
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
http.options = options.merge(
|
|
21
|
+
use_ssl: (options[:use_ssl] || uri.use_ssl?),
|
|
22
|
+
verify_mode: (options[:verify_mode] || OpenSSL::SSL::VERIFY_PEER)
|
|
23
|
+
)
|
|
23
24
|
request_object.headers = headers
|
|
24
25
|
request_object.basic_auth(uri.user, uri.password) if uri.user
|
|
25
26
|
verb = request_object.method.downcase.to_sym
|
data/test/HTTP/RETRY_test.rb
CHANGED
|
@@ -267,6 +267,20 @@ describe HTTP, ".retry_after" do
|
|
|
267
267
|
response = MockResponse.new(headers_hash: {'Retry-After' => 'not a date'})
|
|
268
268
|
_(HTTP.retry_after(response)).must_be_nil
|
|
269
269
|
end
|
|
270
|
+
|
|
271
|
+
it "clamps to 0 when the Retry-After HTTP-date is in the past" do
|
|
272
|
+
base = Time.utc(2026, 5, 22, 12, 0, 0)
|
|
273
|
+
retry_at_header = (base - 60).httpdate
|
|
274
|
+
response = MockResponse.new(headers_hash: {'Retry-After' => retry_at_header})
|
|
275
|
+
Time.stub(:now, base) do
|
|
276
|
+
_(HTTP.retry_after(response)).must_equal(0)
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it "returns nil for a negative integer Retry-After" do
|
|
281
|
+
response = MockResponse.new(headers_hash: {'Retry-After' => '-5'})
|
|
282
|
+
_(HTTP.retry_after(response)).must_be_nil
|
|
283
|
+
end
|
|
270
284
|
end
|
|
271
285
|
|
|
272
286
|
describe HTTP, ".backoff_delay" do
|
data/test/HTTP/get_test.rb
CHANGED
|
@@ -269,6 +269,69 @@ describe ".get" do
|
|
|
269
269
|
end
|
|
270
270
|
end
|
|
271
271
|
|
|
272
|
+
describe "with cross-scheme redirection" do
|
|
273
|
+
def capture_http_objects
|
|
274
|
+
http_objects = []
|
|
275
|
+
original_new = Net::HTTP.method(:new)
|
|
276
|
+
Net::HTTP.stub(:new, ->(*args){
|
|
277
|
+
http_object = original_new.call(*args)
|
|
278
|
+
http_objects << http_object
|
|
279
|
+
http_object
|
|
280
|
+
}) do
|
|
281
|
+
yield
|
|
282
|
+
end
|
|
283
|
+
http_objects
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
describe "from HTTPS to HTTP" do
|
|
287
|
+
let(:request_uri){'https://example.com/path'}
|
|
288
|
+
let(:redirect_uri){'http://redirected.com'}
|
|
289
|
+
|
|
290
|
+
before do
|
|
291
|
+
stub_request(:get, request_uri).
|
|
292
|
+
to_return(status: 301, body: '', headers: {'location' => redirect_uri})
|
|
293
|
+
stub_request(:get, redirect_uri).
|
|
294
|
+
to_return(status: 200, body: '', headers: {})
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "follows the redirect without carrying use_ssl over to the HTTP host" do
|
|
298
|
+
http_objects = capture_http_objects do
|
|
299
|
+
response = HTTP.get(request_uri)
|
|
300
|
+
_(response.success?).must_equal(true)
|
|
301
|
+
end
|
|
302
|
+
_(http_objects.size).must_equal(2)
|
|
303
|
+
_(http_objects[0].use_ssl?).must_equal(true)
|
|
304
|
+
_(http_objects[1].use_ssl?).must_equal(false)
|
|
305
|
+
assert_requested(:get, request_uri)
|
|
306
|
+
assert_requested(:get, redirect_uri)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
describe "from HTTP to HTTPS" do
|
|
311
|
+
let(:request_uri){'http://example.com/path'}
|
|
312
|
+
let(:redirect_uri){'https://redirected.com'}
|
|
313
|
+
|
|
314
|
+
before do
|
|
315
|
+
stub_request(:get, request_uri).
|
|
316
|
+
to_return(status: 301, body: '', headers: {'location' => redirect_uri})
|
|
317
|
+
stub_request(:get, redirect_uri).
|
|
318
|
+
to_return(status: 200, body: '', headers: {})
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it "enables use_ssl on the redirected HTTPS request" do
|
|
322
|
+
http_objects = capture_http_objects do
|
|
323
|
+
response = HTTP.get(request_uri)
|
|
324
|
+
_(response.success?).must_equal(true)
|
|
325
|
+
end
|
|
326
|
+
_(http_objects.size).must_equal(2)
|
|
327
|
+
_(http_objects[0].use_ssl?).must_equal(false)
|
|
328
|
+
_(http_objects[1].use_ssl?).must_equal(true)
|
|
329
|
+
assert_requested(:get, request_uri)
|
|
330
|
+
assert_requested(:get, redirect_uri)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
272
335
|
describe "no_redirect true" do
|
|
273
336
|
let(:request_uri){'http://example.com/path'}
|
|
274
337
|
let(:redirect_uri){'http://redirected.com'}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# test/Net/HTTPResponse/StatusPredicates_test.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../../helper'
|
|
4
|
+
|
|
5
|
+
describe Net::HTTPResponse::StatusPredicates do
|
|
6
|
+
let(:response_class) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include Net::HTTPResponse::StatusPredicates
|
|
9
|
+
def initialize(code); @code = code; end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "#ok?" do
|
|
14
|
+
it "returns true for 200" do
|
|
15
|
+
_(response_class.new('200').ok?).must_equal(true)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "returns false for other 2xx codes" do
|
|
19
|
+
['201', '202', '204'].each do |code|
|
|
20
|
+
_(response_class.new(code).ok?).must_equal(false)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns false for non-2xx codes" do
|
|
25
|
+
_(response_class.new('404').ok?).must_equal(false)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: http.rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.24.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thoran
|
|
@@ -117,6 +117,7 @@ files:
|
|
|
117
117
|
- test/HTTP/post_test.rb
|
|
118
118
|
- test/HTTP/put_test.rb
|
|
119
119
|
- test/HTTP/trace_test.rb
|
|
120
|
+
- test/Net/HTTPResponse/StatusPredicates_test.rb
|
|
120
121
|
- test/helper.rb
|
|
121
122
|
homepage: http://github.com/thoran/HTTP
|
|
122
123
|
licenses:
|