http.rb 1.3.7 → 1.3.9

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: 6d2e0827d0ee0a2f4215eabfd6cd2f952bacb1d12dc657ab1a488ef85a91a46f
4
- data.tar.gz: 5d343996ad5cba48c51c9df5c7f0b31895f3a437d8be6cc15d5fdaff64075430
3
+ metadata.gz: 3ba4b45f56c0470076fa4e969e51f0e251f33c67a817aedc5a3e300a7d52d2a3
4
+ data.tar.gz: 4b882af236ee2c41c736cc4e256d1ee566a7e6cb4f81099c87f443c726a98f13
5
5
  SHA512:
6
- metadata.gz: 644e855a9c30b1ec23e0f2b3db27850ad9018574b37d1e25bcb63ce3aa11371d81a7a372c56cbd9c8f1a284ecf2cdaa0f7219b331da32c3d2fd3b6f4ea90df73
7
- data.tar.gz: 751e3dfc085e8e9c1af0fc372bf948404fd6ac4839eb79ada5d38f390ea9a35ec6ce51381deda5ede866a10f6dafbe208a24ad23b4b15d143fad51a9d557c429
6
+ metadata.gz: ec0592b2b3892f668d2a42a7b3d1d29ae56d4dc6743eda80ced5b8b9ea56b995a770e82b31c6dafc22c6245a4c85044f478bb64b71c5c8b930205dfedc17233f
7
+ data.tar.gz: 13b8ab56ab66abb3a17447b87ec82af1a509cf2a137266c728f04a8536b81db15b7f4711319f9eaae327b15d593abedd41929fff341e970263b6bf1f7eb4b71b
data/CHANGELOG CHANGED
@@ -1,5 +1,31 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 20261011
4
+
5
+ 1.3.9: Only a 301, 302, 303, 307, or 308 with a Location is followed.
6
+
7
+ Any 3xx was taken for a redirect. A 304 Not Modified carries no Location, so URI#merge was handed nil and raised ArgumentError, as did any 3xx without one. And a 300 Multiple Choices was followed to whatever its Location named, though it is a list of choices for the caller, the Location being only the server's preference among them. These are answers rather than redirects, and are now returned as they are.
8
+
9
+ 1. ~ lib/HTTP/REDIRECT.rb: + HTTP::REDIRECT::CODES (301, 302, 303, 307, 308); + HTTP.redirect?, which is true of a response with one of those codes and a Location.
10
+ 2. ~ HTTP.request: follows what redirect? is true of, rather than whatever begins with a 3.
11
+ 3. ~ test/HTTP/REDIRECT_test.rb: + 300 and 304 returned as they are, as is a 302 without a Location. All three fail against 1.3.8: the 300 and the 304 were followed, and the 302 raised.
12
+ 4. ~ README.md: + which responses are followed, under Redirections.
13
+ 5. ~ HTTP::VERSION: /1.3.8/1.3.9/
14
+
15
+
16
+ ## 20261011
17
+
18
+ 1.3.8: Headers are carried through a 301, 302, or 303.
19
+
20
+ A 301, 302, or 303 was followed with no headers at all, so a User-Agent, an Accept, or a Cookie given to the request was not sent to where it was redirected, whereas a 307 or 308 sent them all. Now every redirect carries them, less the credentials which 1.3.6 keeps to their origin, and less Content-Type where the request is made a GET, there being no body for it to describe.
21
+
22
+ 1. ~ HTTP.redirect_headers: + drop_content_type:, which leaves Content-Type behind.
23
+ 2. ~ HTTP.request: a 301, 302, or 303 is followed with redirect_headers, dropping Content-Type, rather than with no headers.
24
+ 3. ~ test/HTTP/REDIRECT_test.rb: + headers carried through each of 301, 302, 303, 307, and 308, and Content-Type left behind when a POST becomes a GET; credentials dropped for another origin by 301, 302, and 303 as well as by 307 and 308. The 301, 302, and 303 examples fail against 1.3.7.
25
+ 4. ~ README.md: + Redirections, on what is carried through a redirect and what is not.
26
+ 5. ~ HTTP::VERSION: /1.3.7/1.3.8/
27
+
28
+
3
29
  ## 20261011
4
30
 
5
31
  1.3.7: The options hash is no longer altered, so no_redirect, retries, and basic authentication survive a call and a redirect.
data/README.md CHANGED
@@ -93,6 +93,12 @@ HTTP.get('http://alice:secret@example.com')
93
93
 
94
94
  When both are supplied, the options hash wins.
95
95
 
96
+ ### Redirections
97
+
98
+ 301, 302, 303, 307, and 308 responses with a `Location` are followed. Other 3xx responses, such as 300 and 304, are returned as they are.
99
+
100
+ Redirects are followed with the headers they were given, except that `Authorization` and `Cookie`, and the `username:` and `password:` options, go no further than the origin (scheme, host, and port) they were given to. Where a redirect makes the request a GET, `Content-Type` is left behind, there being no body for it to describe.
101
+
96
102
  ### Preventing redirections
97
103
 
98
104
  ```ruby
data/lib/HTTP/REDIRECT.rb CHANGED
@@ -3,18 +3,29 @@
3
3
 
4
4
  module HTTP
5
5
  module REDIRECT
6
+ CODES = %w{301 302 303 307 308}.freeze
6
7
  CREDENTIAL_HEADERS = %w{authorization cookie}.freeze
7
8
  end
8
9
 
10
+ # A 300 or a 304 is an answer rather than somewhere else to go, and a 3xx without a
11
+ # Location has nowhere to send it.
12
+ def redirect?(response)
13
+ REDIRECT::CODES.include?(response.code) && !response['location'].to_s.empty?
14
+ end
15
+ module_function :redirect?
16
+
9
17
  def same_origin?(a, b)
10
18
  [a.scheme, a.host, a.port] == [b.scheme, b.host, b.port]
11
19
  end
12
20
  module_function :same_origin?
13
21
 
14
22
  # Credentials are for the origin they were given to, so they go no further than it.
15
- def redirect_headers(headers, from_uri, to_uri)
16
- return headers if same_origin?(from_uri, to_uri)
17
- headers.reject{|k, v| REDIRECT::CREDENTIAL_HEADERS.include?(k.downcase)}
23
+ # A request made into a GET has no body for a Content-Type to describe.
24
+ def redirect_headers(headers, from_uri, to_uri, drop_content_type: false)
25
+ headers.reject do |k, v|
26
+ (drop_content_type && k.downcase == 'content-type') ||
27
+ (!same_origin?(from_uri, to_uri) && REDIRECT::CREDENTIAL_HEADERS.include?(k.downcase))
28
+ end
18
29
  end
19
30
  module_function :redirect_headers
20
31
 
data/lib/HTTP/VERSION.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # HTTP::VERSION
3
3
 
4
4
  module HTTP
5
- VERSION = '1.3.7'
5
+ VERSION = '1.3.9'
6
6
  end
data/lib/HTTP/request.rb CHANGED
@@ -40,7 +40,7 @@ module HTTP
40
40
  http.request(request_object)
41
41
  end
42
42
  )
43
- if response.code =~ /^3/
43
+ if redirect?(response)
44
44
  if block_given? && no_redirect
45
45
  yield response
46
46
  elsif no_redirect
@@ -52,7 +52,7 @@ module HTTP
52
52
  data = METHODS::WITH_BODY.include?(request_method) ? request_object.body : {}
53
53
  response = send(request_method, redirect_uri.to_s, data, redirect_headers(headers, uri, redirect_uri), onward_options, &block)
54
54
  else
55
- response = get(redirect_uri.to_s, {}, {}, onward_options, &block)
55
+ response = get(redirect_uri.to_s, {}, redirect_headers(headers, uri, redirect_uri, drop_content_type: true), onward_options, &block)
56
56
  end
57
57
  end
58
58
  if block_given?
@@ -7,6 +7,23 @@ describe "redirect behaviour" do
7
7
  let(:same_origin_uri){'http://example.com/elsewhere'}
8
8
  let(:other_origin_uri){'http://redirected.com/path'}
9
9
 
10
+ describe "headers" do
11
+ %w{301 302 303 307 308}.each do |code|
12
+ it "are carried through a #{code}" do
13
+ stub_request(:get, uri).to_return(status: code.to_i, headers: {'location' => other_origin_uri})
14
+ stub_request(:get, other_origin_uri).with(headers: {'User-Agent' => 'Redback', 'Accept' => 'text/html'}).to_return(status: 200)
15
+ _(HTTP.get(uri, {}, {'User-Agent' => 'Redback', 'Accept' => 'text/html'}).success?).must_equal(true)
16
+ end
17
+ end
18
+
19
+ it "leave Content-Type behind when a POST becomes a GET" do
20
+ stub_request(:post, uri).to_return(status: 303, headers: {'location' => same_origin_uri})
21
+ stub_request(:get, same_origin_uri).to_return(status: 200)
22
+ HTTP.post(uri, {a: 1}, {'Content-Type' => 'application/json', 'User-Agent' => 'Redback'})
23
+ assert_requested(:get, same_origin_uri){|request| !request.headers.key?('Content-Type') && request.headers['User-Agent'] == 'Redback'}
24
+ end
25
+ end
26
+
10
27
  describe "credentials" do
11
28
  it "are kept for the same origin" do
12
29
  stub_request(:get, uri).to_return(status: 307, headers: {'location' => same_origin_uri})
@@ -14,7 +31,7 @@ describe "redirect behaviour" do
14
31
  _(HTTP.get(uri, {}, {'Authorization' => 'Bearer x', 'Cookie' => 'session=1'}).success?).must_equal(true)
15
32
  end
16
33
 
17
- %w{307 308}.each do |code|
34
+ %w{301 302 303 307 308}.each do |code|
18
35
  it "are dropped for another origin via #{code}" do
19
36
  stub_request(:get, uri).to_return(status: code.to_i, headers: {'location' => other_origin_uri})
20
37
  stub_request(:get, other_origin_uri).to_return(status: 200)
@@ -79,4 +96,19 @@ describe "redirect behaviour" do
79
96
  assert_requested(:get, other_origin_uri){|request| !request.headers.key?('Authorization')}
80
97
  end
81
98
  end
99
+
100
+ describe "3xx responses which are not followed" do
101
+ %w{300 304}.each do |code|
102
+ it "return a #{code} as it is" do
103
+ stub_request(:get, uri).to_return(status: code.to_i, headers: {'location' => other_origin_uri})
104
+ _(HTTP.get(uri).code).must_equal(code)
105
+ assert_not_requested(:get, other_origin_uri)
106
+ end
107
+ end
108
+
109
+ it "return a redirect without a Location as it is" do
110
+ stub_request(:get, uri).to_return(status: 302)
111
+ _(HTTP.get(uri).code).must_equal('302')
112
+ end
113
+ end
82
114
  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: 1.3.7
4
+ version: 1.3.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran