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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5535114ce1c4c4612aaef5e7eb88777a4807ab671d2f474d38175e3398d37358
4
- data.tar.gz: e72a5ce3ea83170a4bafc2d6179912d20ff88c8d945d9f8d06f40dae387b6961
3
+ metadata.gz: 192aecaee0d22accbabb0b03b00f987cf7902c4958d6d5dab823515f4e681408
4
+ data.tar.gz: 1adb47bbf1b800b6e3fa9f7767db4a5b63f0b23abe12547056e564bda0c76713
5
5
  SHA512:
6
- metadata.gz: cf30ba6e2171e8a005b398cbb467f8437d0653b15f066f610e87bf5d301464695bcd751dbf4b064ded581761c1c2abad25e71418d24d2c4a3001706f1e41f8dc
7
- data.tar.gz: f62e33d69f7e107c43d717f33bd9130538f0b4ff96a8f337145dfe11fb3ee3dac25ee83fec6777d301fa0eadeb0282760057a58da4736cd2adee0eba47c6552d
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
- ## 202503030
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 = HTTP.get('http://example.com')
120
- response.informational?
121
- # => true
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
- # 2xx
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
- # 3xx
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.success?
135
+ response.successful?
133
136
  # => false
137
+ ```
134
138
 
135
- response = HTTP.get('http://example.com', {}, {}, {no_redirect: false})
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
- response = HTTP.get('http://example.com')
141
+ ```ruby
142
+ response = HTTP.get('http://example.com/moved')
142
143
  response.redirection?
143
144
  # => false
144
- response.success?
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
@@ -2,5 +2,5 @@
2
2
  # HTTP::VERSION
3
3
 
4
4
  module HTTP
5
- VERSION = '0.22.0'
5
+ VERSION = '0.24.0'
6
6
  end
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[:use_ssl] ||= uri.use_ssl?
21
- options[:verify_mode] ||= OpenSSL::SSL::VERIFY_PEER
22
- http.options = options
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
@@ -12,7 +12,10 @@ module Net
12
12
  @code =~ /^2/ ? true : false
13
13
  end
14
14
  alias_method :success?, :successful?
15
- alias_method :ok?, :successful?
15
+
16
+ def ok?
17
+ @code == '200'
18
+ end
16
19
 
17
20
  def redirection?
18
21
  @code =~ /^3/ ? true : false
@@ -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
@@ -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.22.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: