http.rb 1.3.6 → 1.3.8

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: 198e221dc9a1ced43833cfa01886896755615ccc22d9773d0798627d0af1bd45
4
- data.tar.gz: 53d2e227daa4558d0f810980e2b86ec60fa17c4222c2e61e507c164097c25071
3
+ metadata.gz: 5cab4dfd9757c08463389bff8595489378a59ecb3149cbdfcc465ab6a508536e
4
+ data.tar.gz: 5fe112a78858fc9deee9a1b8ab29cf71325a7e0801fd6b6d33e3f48835aba2dd
5
5
  SHA512:
6
- metadata.gz: 2ef160053a5a5069e6be6714daf9229358ece7af8899ec0f5fcc8b98f9b5b4cb05c434d0c3a5a946f7e95a66cf55e19feb918f8096f8793b0bbc25780bd8545a
7
- data.tar.gz: 0a9721b58212b93670c368496c50bc19f7ea4d53d1b860728b6a3fd5ff8b2d7f81d388cee42a6f969bd65c5ca5584d126d75e1fb5092e35372312e32593c6cd7
6
+ metadata.gz: a9678a45ae024d10874c9fdd9781e785b64f2e9818c85cc991b7af6bff8f56d38d3cad1f53d606bf36ff758581a1c2106e3b38c5f88224aa6d5272833ee97482
7
+ data.tar.gz: 8f9c36f7f2496a752c55a3352eb867585e787d72d63e87c8a124576b849517e213bd997d92415d2819eaac3f21a47099bef2f3a2f8e85e473aea6d8a3c9ab560
data/CHANGELOG CHANGED
@@ -1,5 +1,31 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 20261011
4
+
5
+ 1.3.8: Headers are carried through a 301, 302, or 303.
6
+
7
+ 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.
8
+
9
+ 1. ~ HTTP.redirect_headers: + drop_content_type:, which leaves Content-Type behind.
10
+ 2. ~ HTTP.request: a 301, 302, or 303 is followed with redirect_headers, dropping Content-Type, rather than with no headers.
11
+ 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.
12
+ 4. ~ README.md: + Redirections, on what is carried through a redirect and what is not.
13
+ 5. ~ HTTP::VERSION: /1.3.7/1.3.8/
14
+
15
+
16
+ ## 20261011
17
+
18
+ 1.3.7: The options hash is no longer altered, so no_redirect, retries, and basic authentication survive a call and a redirect.
19
+
20
+ HTTP.request took no_redirect, username, password, and the retry options out of the caller's hash with delete. A hash passed twice had lost no_redirect by the second call, which then followed the redirect it was asked not to. And the redirect was made with what was left of the hash, so retries and basic authentication were lost at the first redirect, even to the same origin.
21
+
22
+ 1. ~ HTTP.request: works on a copy of the options hash, leaving the caller's as it was.
23
+ 2. ~ HTTP.request: a redirect is made with the options as given, less what goes no further than the origin.
24
+ 3. ~ lib/HTTP/REDIRECT.rb: + HTTP.redirect_options, which drops username and password where a redirect goes to another origin, as 1.3.6 does the credential headers.
25
+ 4. ~ test/HTTP/REDIRECT_test.rb: + the options hash left as it was, no_redirect kept for a second call, retries kept after a redirect, and basic authentication kept for the same origin and dropped for another. All but the last fail against 1.3.6, which dropped it for every origin.
26
+ 5. ~ HTTP::VERSION: /1.3.6/1.3.7/
27
+
28
+
3
29
  ## 20261011
4
30
 
5
31
  1.3.6: Authorization and Cookie headers go no further than the origin they were given to.
data/README.md CHANGED
@@ -93,6 +93,10 @@ 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
+ 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.
99
+
96
100
  ### Preventing redirections
97
101
 
98
102
  ```ruby
data/lib/HTTP/REDIRECT.rb CHANGED
@@ -12,9 +12,18 @@ module HTTP
12
12
  module_function :same_origin?
13
13
 
14
14
  # 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)}
15
+ # A request made into a GET has no body for a Content-Type to describe.
16
+ def redirect_headers(headers, from_uri, to_uri, drop_content_type: false)
17
+ headers.reject do |k, v|
18
+ (drop_content_type && k.downcase == 'content-type') ||
19
+ (!same_origin?(from_uri, to_uri) && REDIRECT::CREDENTIAL_HEADERS.include?(k.downcase))
20
+ end
18
21
  end
19
22
  module_function :redirect_headers
23
+
24
+ def redirect_options(options, from_uri, to_uri)
25
+ return options if same_origin?(from_uri, to_uri)
26
+ options.reject{|k, v| %i{username password}.include?(k)}
27
+ end
28
+ module_function :redirect_options
20
29
  end
data/lib/HTTP/VERSION.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # HTTP::VERSION
3
3
 
4
4
  module HTTP
5
- VERSION = '1.3.6'
5
+ VERSION = '1.3.8'
6
6
  end
data/lib/HTTP/request.rb CHANGED
@@ -16,6 +16,8 @@ module HTTP
16
16
  def request(uri, request_object, headers = {}, options = {}, &block)
17
17
  uri = uri.is_a?(URI) ? uri : URI.parse(uri)
18
18
  http = Net::HTTP.new(uri.host, uri.port)
19
+ given_options = options
20
+ options = options.dup
19
21
  no_redirect = options.delete(:no_redirect)
20
22
  username = options.delete(:username)
21
23
  password = options.delete(:password)
@@ -45,11 +47,12 @@ module HTTP
45
47
  return response
46
48
  end
47
49
  redirect_uri = uri.merge(response['location'])
50
+ onward_options = redirect_options(given_options, uri, redirect_uri)
48
51
  if response.code =~ /^30[78]$/
49
52
  data = METHODS::WITH_BODY.include?(request_method) ? request_object.body : {}
50
- response = send(request_method, redirect_uri.to_s, data, redirect_headers(headers, uri, redirect_uri), options, &block)
53
+ response = send(request_method, redirect_uri.to_s, data, redirect_headers(headers, uri, redirect_uri), onward_options, &block)
51
54
  else
52
- response = get(redirect_uri.to_s, {}, {}, options, &block)
55
+ response = get(redirect_uri.to_s, {}, redirect_headers(headers, uri, redirect_uri, drop_content_type: true), onward_options, &block)
53
56
  end
54
57
  end
55
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)
@@ -37,4 +54,46 @@ describe "redirect behaviour" do
37
54
  assert_requested(:get, 'http://example.com:8080/path'){|request| !request.headers.key?('Cookie')}
38
55
  end
39
56
  end
57
+
58
+ describe "the options hash" do
59
+ it "is not altered by a call" do
60
+ stub_request(:get, uri).to_return(status: 302, headers: {'location' => same_origin_uri})
61
+ stub_request(:get, same_origin_uri).to_return(status: 200)
62
+ options = {no_redirect: false, username: 'alice', password: 'secret', retries: 1}
63
+ HTTP.get(uri, {}, {}, options)
64
+ _(options).must_equal({no_redirect: false, username: 'alice', password: 'secret', retries: 1})
65
+ end
66
+
67
+ it "keeps no_redirect for a second call" do
68
+ stub_request(:get, uri).to_return(status: 302, headers: {'location' => other_origin_uri})
69
+ options = {no_redirect: true}
70
+ HTTP.get(uri, {}, {}, options)
71
+ _(HTTP.get(uri, {}, {}, options).redirection?).must_equal(true)
72
+ assert_not_requested(:get, other_origin_uri)
73
+ end
74
+
75
+ it "keeps retrying after a redirect" do
76
+ HTTP::RETRY.stub(:sleep, nil) do
77
+ stub_request(:get, uri).to_return(status: 302, headers: {'location' => same_origin_uri})
78
+ stub_request(:get, same_origin_uri).to_return(status: 503).then.to_return(status: 200)
79
+ _(HTTP.get(uri, {}, {}, {retries: 1}).success?).must_equal(true)
80
+ assert_requested(:get, same_origin_uri, times: 2)
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "basic authentication options" do
86
+ it "are kept for the same origin" do
87
+ stub_request(:get, uri).with(basic_auth: ['alice', 'secret']).to_return(status: 302, headers: {'location' => same_origin_uri})
88
+ stub_request(:get, same_origin_uri).with(basic_auth: ['alice', 'secret']).to_return(status: 200)
89
+ _(HTTP.get(uri, {}, {}, {username: 'alice', password: 'secret'}).success?).must_equal(true)
90
+ end
91
+
92
+ it "are dropped for another origin" do
93
+ stub_request(:get, uri).with(basic_auth: ['alice', 'secret']).to_return(status: 307, headers: {'location' => other_origin_uri})
94
+ stub_request(:get, other_origin_uri).to_return(status: 200)
95
+ HTTP.get(uri, {}, {}, {username: 'alice', password: 'secret'})
96
+ assert_requested(:get, other_origin_uri){|request| !request.headers.key?('Authorization')}
97
+ end
98
+ end
40
99
  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.6
4
+ version: 1.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran