http.rb 1.4.0 → 1.5.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 +14 -0
- data/README.md +21 -1
- data/lib/HTTP/REDIRECT.rb +12 -1
- data/lib/HTTP/VERSION.rb +1 -1
- data/lib/HTTP/request.rb +3 -1
- data/test/HTTP/REDIRECT_test.rb +40 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72cf60480847b43b60b63378d803acae19a12c127ceb9695ed8b0eca99ca57a4
|
|
4
|
+
data.tar.gz: aed0e179795a1f408823cb5cecca6bb2c877a1e4a90c3a31f21a115a5270a2e6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b8e095a766f24d074324536e89dc07871bc8670785fe14f95470a06e64a4107e0c7c2df44f746c8dbd9c31d7b79336e8548aaf5601e4121db86f5787c1ee66bb
|
|
7
|
+
data.tar.gz: c5637e2490a1605d4112162bad17c12d70e1536af814901c1bdc2572eb730f15e54b0eb8db2db3ff6057d872b9969b7852c5767872b9108ce257a160a9ebf24f
|
data/CHANGELOG
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 20261011
|
|
4
|
+
|
|
5
|
+
1.5.0: + max_redirects; + HTTP::TooManyRedirects.
|
|
6
|
+
|
|
7
|
+
Redirects were followed without limit, each by a call nested in the last, so a redirect loop ran until SystemStackError, which against a local server came after 2,179 requests. Now no more than max_redirects are followed, 20 unless given, which is where browsers stop, and one more raises HTTP::TooManyRedirects.
|
|
8
|
+
|
|
9
|
+
1. ~ lib/HTTP/REDIRECT.rb: + HTTP::REDIRECT::MAX (20); + HTTP::TooManyRedirects, whose response is the redirect which was not followed.
|
|
10
|
+
2. ~ HTTP.redirect_options: + max_redirects, which the redirect is made with one fewer of.
|
|
11
|
+
3. ~ HTTP.request: + max_redirects option, which defaults to HTTP::REDIRECT::MAX and is not passed through to Net::HTTP; HTTP::TooManyRedirects is raised at a redirect when none are left. max_redirects: 0 raises at the first redirect, where no_redirect: true returns it, and no_redirect wins where both are given.
|
|
12
|
+
4. ~ test/HTTP/REDIRECT_test.rb: + as many redirects followed as allowed, TooManyRedirects for one more with the redirect not followed, 0 raising at the first, no_redirect winning over it, a loop stopped at the default, and the option not passed through to Net::HTTP; max_redirects among the options left as they were. All fail against 1.4.0, which passed max_redirects through to Net::HTTP, where there is no max_redirects= for it.
|
|
13
|
+
5. ~ README.md: + Limiting redirections; + max_redirects among the options which aren't passed through to Net::HTTP.
|
|
14
|
+
6. ~ HTTP::VERSION: /1.4.0/1.5.0/
|
|
15
|
+
|
|
16
|
+
|
|
3
17
|
## 20261011
|
|
4
18
|
|
|
5
19
|
1.4.0: The method on a redirect is as RFC 9110 has it: a DELETE redirected by a 301 or 302 is now a DELETE, a PUT a PUT, and a HEAD a HEAD.
|
data/README.md
CHANGED
|
@@ -119,6 +119,22 @@ HTTP.get('http://example.com', {}, {}, {no_redirect: true})
|
|
|
119
119
|
# => #<Net::HTTPResponse @code=3xx>
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
### Limiting redirections
|
|
123
|
+
|
|
124
|
+
Up to 20 redirects are followed by default. One more raises `HTTP::TooManyRedirects`, whose `response` is the redirect which wasn't followed:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
HTTP.get('http://example.com', {}, {}, {max_redirects: 5})
|
|
128
|
+
|
|
129
|
+
begin
|
|
130
|
+
HTTP.get('http://example.com/loop')
|
|
131
|
+
rescue HTTP::TooManyRedirects => e
|
|
132
|
+
e.response['location']
|
|
133
|
+
end
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`max_redirects: 0` raises at the first redirect, whereas `no_redirect: true` returns it.
|
|
137
|
+
|
|
122
138
|
### Retries
|
|
123
139
|
|
|
124
140
|
Retries are disabled by default. Enable them by passing `retries:` in the options hash.
|
|
@@ -216,12 +232,16 @@ HTTP.request_with_body(:propfind, 'http://example.com/path', xml, {'Content-Type
|
|
|
216
232
|
Both take the same `(method, uri, args_or_data, headers, options, &block)` arguments as the named methods, with the method as the first argument, and the named methods delegate to them. For full control over the request object, drop down to `HTTP.request(uri, request_object, headers, options, &block)`.
|
|
217
233
|
|
|
218
234
|
## Allowed values for the options hash
|
|
219
|
-
#### (These pass through to Net::HTTP, except for `no_redirect`, `username`, and `password`.)
|
|
235
|
+
#### (These pass through to Net::HTTP, except for `no_redirect`, `max_redirects`, `username`, and `password`.)
|
|
220
236
|
|
|
221
237
|
```Ruby
|
|
222
238
|
no_redirect
|
|
223
239
|
# Prevents redirection if a 3xx response is encountered.
|
|
224
240
|
|
|
241
|
+
max_redirects
|
|
242
|
+
# The most redirects followed before HTTP::TooManyRedirects is raised.
|
|
243
|
+
# Defaults to 20.
|
|
244
|
+
|
|
225
245
|
username
|
|
226
246
|
# Username for HTTP basic authentication. Takes precedence over any
|
|
227
247
|
# credentials embedded in the URI.
|
data/lib/HTTP/REDIRECT.rb
CHANGED
|
@@ -5,6 +5,16 @@ module HTTP
|
|
|
5
5
|
module REDIRECT
|
|
6
6
|
CODES = %w{301 302 303 307 308}.freeze
|
|
7
7
|
CREDENTIAL_HEADERS = %w{authorization cookie}.freeze
|
|
8
|
+
MAX = 20
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class TooManyRedirects < StandardError
|
|
12
|
+
attr_reader :response
|
|
13
|
+
|
|
14
|
+
def initialize(response)
|
|
15
|
+
@response = response
|
|
16
|
+
super("Too many redirects: #{response.code} to #{response['location']}")
|
|
17
|
+
end
|
|
8
18
|
end
|
|
9
19
|
|
|
10
20
|
# A 300 or a 304 is an answer rather than somewhere else to go, and a 3xx without a
|
|
@@ -52,7 +62,8 @@ module HTTP
|
|
|
52
62
|
end
|
|
53
63
|
module_function :body_headers
|
|
54
64
|
|
|
55
|
-
def redirect_options(options, from_uri, to_uri)
|
|
65
|
+
def redirect_options(options, from_uri, to_uri, max_redirects)
|
|
66
|
+
options = options.merge(max_redirects: max_redirects - 1)
|
|
56
67
|
return options if same_origin?(from_uri, to_uri)
|
|
57
68
|
options.reject{|k, v| %i{username password}.include?(k)}
|
|
58
69
|
end
|
data/lib/HTTP/VERSION.rb
CHANGED
data/lib/HTTP/request.rb
CHANGED
|
@@ -19,6 +19,7 @@ module HTTP
|
|
|
19
19
|
given_options = options
|
|
20
20
|
options = options.dup
|
|
21
21
|
no_redirect = options.delete(:no_redirect)
|
|
22
|
+
max_redirects = options.delete(:max_redirects) || REDIRECT::MAX
|
|
22
23
|
username = options.delete(:username)
|
|
23
24
|
password = options.delete(:password)
|
|
24
25
|
config = retry_config(options)
|
|
@@ -41,8 +42,9 @@ module HTTP
|
|
|
41
42
|
end
|
|
42
43
|
)
|
|
43
44
|
if !no_redirect && redirect?(response)
|
|
45
|
+
raise TooManyRedirects.new(response) if max_redirects < 1
|
|
44
46
|
redirect_uri = uri.merge(response['location'])
|
|
45
|
-
onward_options = redirect_options(given_options, uri, redirect_uri)
|
|
47
|
+
onward_options = redirect_options(given_options, uri, redirect_uri, max_redirects)
|
|
46
48
|
redirect_method = redirect_method(response.code, request_method)
|
|
47
49
|
if redirect_method != request_method
|
|
48
50
|
return request_without_body(redirect_method, redirect_uri, {}, redirect_headers(headers, uri, redirect_uri, drop_content_type: true), onward_options, &block)
|
data/test/HTTP/REDIRECT_test.rb
CHANGED
|
@@ -59,9 +59,9 @@ describe "redirect behaviour" do
|
|
|
59
59
|
it "is not altered by a call" do
|
|
60
60
|
stub_request(:get, uri).to_return(status: 302, headers: {'location' => same_origin_uri})
|
|
61
61
|
stub_request(:get, same_origin_uri).to_return(status: 200)
|
|
62
|
-
options = {no_redirect: false, username: 'alice', password: 'secret', retries: 1}
|
|
62
|
+
options = {no_redirect: false, max_redirects: 3, username: 'alice', password: 'secret', retries: 1}
|
|
63
63
|
HTTP.get(uri, {}, {}, options)
|
|
64
|
-
_(options).must_equal({no_redirect: false, username: 'alice', password: 'secret', retries: 1})
|
|
64
|
+
_(options).must_equal({no_redirect: false, max_redirects: 3, username: 'alice', password: 'secret', retries: 1})
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
it "keeps no_redirect for a second call" do
|
|
@@ -215,4 +215,42 @@ describe "redirect behaviour" do
|
|
|
215
215
|
end
|
|
216
216
|
end
|
|
217
217
|
end
|
|
218
|
+
|
|
219
|
+
describe "max_redirects" do
|
|
220
|
+
before do
|
|
221
|
+
stub_request(:get, 'http://example.com/0').to_return(status: 302, headers: {'location' => '/1'})
|
|
222
|
+
stub_request(:get, 'http://example.com/1').to_return(status: 302, headers: {'location' => '/2'})
|
|
223
|
+
stub_request(:get, 'http://example.com/2').to_return(status: 200)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
it "follows as many redirects as it is allowed" do
|
|
227
|
+
_(HTTP.get('http://example.com/0', {}, {}, {max_redirects: 2}).success?).must_equal(true)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it "raises TooManyRedirects, with the redirect not followed, when there are more" do
|
|
231
|
+
error = _(->{HTTP.get('http://example.com/0', {}, {}, {max_redirects: 1})}).must_raise(HTTP::TooManyRedirects)
|
|
232
|
+
_(error.response['location']).must_equal('/2')
|
|
233
|
+
assert_not_requested(:get, 'http://example.com/2')
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it "raises at the first redirect when it is 0" do
|
|
237
|
+
_(->{HTTP.get('http://example.com/0', {}, {}, {max_redirects: 0})}).must_raise(HTTP::TooManyRedirects)
|
|
238
|
+
assert_not_requested(:get, 'http://example.com/1')
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it "gives way to no_redirect" do
|
|
242
|
+
_(HTTP.get('http://example.com/0', {}, {}, {max_redirects: 0, no_redirect: true}).code).must_equal('302')
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it "stops a redirect loop at the default" do
|
|
246
|
+
stub_request(:get, uri).to_return(status: 302, headers: {'location' => uri})
|
|
247
|
+
_(->{HTTP.get(uri)}).must_raise(HTTP::TooManyRedirects)
|
|
248
|
+
assert_requested(:get, uri, times: HTTP::REDIRECT::MAX + 1)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it "is not passed through to Net::HTTP" do
|
|
252
|
+
stub_request(:get, uri).to_return(status: 200)
|
|
253
|
+
_(HTTP.get(uri, {}, {}, {max_redirects: 5}).success?).must_equal(true)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
218
256
|
end
|