mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-11-22 00:05:57 -05:00
supporting the retry-after header for redirections as well
This commit is contained in:
parent
1ad2e9cbcf
commit
01552757a0
@ -5,6 +5,7 @@ require "httpx/version"
|
|||||||
require "httpx/extensions"
|
require "httpx/extensions"
|
||||||
|
|
||||||
require "httpx/errors"
|
require "httpx/errors"
|
||||||
|
require "httpx/utils"
|
||||||
require "httpx/altsvc"
|
require "httpx/altsvc"
|
||||||
require "httpx/callbacks"
|
require "httpx/callbacks"
|
||||||
require "httpx/loggable"
|
require "httpx/loggable"
|
||||||
|
|||||||
@ -59,8 +59,26 @@ module HTTPX
|
|||||||
return ErrorResponse.new(request, error, options)
|
return ErrorResponse.new(request, error, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
retry_after = response.headers["retry-after"]
|
||||||
|
|
||||||
|
if retry_after
|
||||||
|
# Servers send the "Retry-After" header field to indicate how long the
|
||||||
|
# user agent ought to wait before making a follow-up request.
|
||||||
|
# When sent with any 3xx (Redirection) response, Retry-After indicates
|
||||||
|
# the minimum time that the user agent is asked to wait before issuing
|
||||||
|
# the redirected request.
|
||||||
|
#
|
||||||
|
retry_after = Utils.parse_retry_after(retry_after)
|
||||||
|
|
||||||
|
log { "redirecting after #{retry_after} secs..." }
|
||||||
|
pool.after(retry_after) do
|
||||||
connection = find_connection(retry_request, connections, options)
|
connection = find_connection(retry_request, connections, options)
|
||||||
connection.send(retry_request)
|
connection.send(retry_request)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
connection = find_connection(retry_request, connections, options)
|
||||||
|
connection.send(retry_request)
|
||||||
|
end
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -35,21 +35,12 @@ module HTTPX
|
|||||||
# the minimum time that the user agent is asked to wait before issuing
|
# the minimum time that the user agent is asked to wait before issuing
|
||||||
# the redirected request.
|
# the redirected request.
|
||||||
#
|
#
|
||||||
# The value of this field can be either an HTTP-date or a number of
|
def retry_after_rate_limit(_, response)
|
||||||
# seconds to delay after the response is received.
|
|
||||||
def retry_after_rate_limit(_request, response)
|
|
||||||
retry_after = response.headers["retry-after"]
|
retry_after = response.headers["retry-after"]
|
||||||
|
|
||||||
return unless retry_after
|
return unless retry_after
|
||||||
|
|
||||||
begin
|
Utils.parse_retry_after(retry_after)
|
||||||
# first: bet on it being an integer
|
|
||||||
Integer(retry_after)
|
|
||||||
rescue ArgumentError
|
|
||||||
# Then it's a datetime
|
|
||||||
time = Time.httpdate(retry_after)
|
|
||||||
time - Time.now
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
18
lib/httpx/utils.rb
Normal file
18
lib/httpx/utils.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module HTTPX
|
||||||
|
module Utils
|
||||||
|
module_function
|
||||||
|
|
||||||
|
# The value of this field can be either an HTTP-date or a number of
|
||||||
|
# seconds to delay after the response is received.
|
||||||
|
def parse_retry_after(retry_after)
|
||||||
|
# first: bet on it being an integer
|
||||||
|
Integer(retry_after)
|
||||||
|
rescue ArgumentError
|
||||||
|
# Then it's a datetime
|
||||||
|
time = Time.httpdate(retry_after)
|
||||||
|
time - Time.now
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -36,6 +36,19 @@ module Requests
|
|||||||
verify_status(response, 302)
|
verify_status(response, 302)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_plugin_follow_redirects_retry_after
|
||||||
|
session = HTTPX.plugin(SessionWithMockResponse[302, "retry-after" => "2"]).plugin(:follow_redirects)
|
||||||
|
|
||||||
|
before_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
|
||||||
|
response = session.get(max_redirect_uri(1))
|
||||||
|
after_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
|
||||||
|
|
||||||
|
verify_status(response, 200)
|
||||||
|
|
||||||
|
total_time = after_time - before_time
|
||||||
|
assert total_time >= 2, "request didn't take as expected to redirect (#{total_time} secs)"
|
||||||
|
end
|
||||||
|
|
||||||
def test_plugin_follow_insecure_no_insecure_downgrade
|
def test_plugin_follow_insecure_no_insecure_downgrade
|
||||||
return unless origin.start_with?("https")
|
return unless origin.start_with?("https")
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ module Requests
|
|||||||
verify_rated_responses(rate_limiter_session, 429)
|
verify_rated_responses(rate_limiter_session, 429)
|
||||||
|
|
||||||
total_time = after_time - before_time
|
total_time = after_time - before_time
|
||||||
assert_in_delta 2, total_time, 1, "request didn't take as expected to retry (#{total_time} secs)"
|
assert total_time >= 2, "request didn't take as expected to retry (#{total_time} secs)"
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_plugin_rate_limiter_retry_after_date
|
def test_plugin_rate_limiter_retry_after_date
|
||||||
@ -57,7 +57,7 @@ module Requests
|
|||||||
|
|
||||||
verify_rated_responses(rate_limiter_session, 429)
|
verify_rated_responses(rate_limiter_session, 429)
|
||||||
total_time = after_time - before_time
|
total_time = after_time - before_time
|
||||||
assert_in_delta 3, total_time, 1, "request didn't take as expected to retry (#{total_time} secs)"
|
assert total_time >= 2, "request didn't take as expected to retry (#{total_time} secs)"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@ -7,6 +7,10 @@ module SessionWithMockResponse
|
|||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module ResponseMethods
|
||||||
|
attr_writer :status
|
||||||
|
end
|
||||||
|
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
def initialize(*)
|
def initialize(*)
|
||||||
super
|
super
|
||||||
@ -19,11 +23,9 @@ module SessionWithMockResponse
|
|||||||
response.close
|
response.close
|
||||||
@mock_responses_counter -= 1
|
@mock_responses_counter -= 1
|
||||||
|
|
||||||
mock_response = @options.response_class.new(request,
|
response.status = Thread.current[:httpx_mock_response_status]
|
||||||
Thread.current[:httpx_mock_response_status],
|
response.merge_headers(Thread.current[:httpx_mock_response_headers])
|
||||||
"2.0",
|
super(request, response)
|
||||||
Thread.current[:httpx_mock_response_headers])
|
|
||||||
super(request, mock_response)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user