added request_timeout

This commit is contained in:
HoneyryderChuck 2022-08-06 23:20:21 +01:00
parent c907942c9c
commit 3f9c165d51
9 changed files with 81 additions and 34 deletions

View File

@ -432,27 +432,7 @@ module HTTPX
@inflight += 1 @inflight += 1
parser.send(request) parser.send(request)
write_timeout = request.write_timeout set_request_timeouts(request)
request.once(:headers) do
@timers.after(write_timeout) do
if request.state != :done
@write_buffer.clear
error = WriteTimeoutError.new(request, write_timeout)
on_error(error)
end
end
end unless write_timeout.nil? || write_timeout.infinite?
read_timeout = request.read_timeout
request.once(:done) do
@timers.after(read_timeout) do
if request.response.nil? || !request.response.finished?
@write_buffer.clear
error = ReadTimeoutError.new(request, request.response, read_timeout)
on_error(error)
end
end
end unless read_timeout.nil? || read_timeout.infinite?
return unless @state == :inactive return unless @state == :inactive
@ -620,5 +600,38 @@ module HTTPX
request.emit(:response, response) request.emit(:response, response)
end end
end end
def set_request_timeouts(request)
write_timeout = request.write_timeout
request.once(:headers) do
@timers.after(write_timeout) { write_timeout_callback(request, write_timeout) }
end unless write_timeout.nil? || write_timeout.infinite?
read_timeout = request.read_timeout
request.once(:done) do
@timers.after(read_timeout) { read_timeout_callback(request, read_timeout) }
end unless read_timeout.nil? || read_timeout.infinite?
request_timeout = request.request_timeout
request.once(:headers) do
@timers.after(request_timeout) { read_timeout_callback(request, request_timeout, RequestTimeoutError) }
end unless request_timeout.nil? || request_timeout.infinite?
end
def write_timeout_callback(request, write_timeout)
return if request.state == :done
@write_buffer.clear
error = WriteTimeoutError.new(request, nil, write_timeout)
on_error(error)
end
def read_timeout_callback(request, read_timeout, error_type = WriteTimeoutError)
return if request.response && request.response.finished?
@write_buffer.clear
error = error_type.new(request, request.response, read_timeout)
on_error(error)
end
end end
end end

View File

@ -27,8 +27,9 @@ module HTTPX
class RequestTimeoutError < TimeoutError class RequestTimeoutError < TimeoutError
attr_reader :request attr_reader :request
def initialize(request, timeout) def initialize(request, response, timeout)
@request = request @request = request
@response = response
super(timeout, "Timed out after #{timeout} seconds") super(timeout, "Timed out after #{timeout} seconds")
end end
@ -37,14 +38,7 @@ module HTTPX
end end
end end
class ReadTimeoutError < RequestTimeoutError class ReadTimeoutError < RequestTimeoutError; end
attr_reader :response
def initialize(request, response, timeout)
@response = response
super(request, timeout)
end
end
class WriteTimeoutError < RequestTimeoutError; end class WriteTimeoutError < RequestTimeoutError; end

View File

@ -10,8 +10,7 @@ module HTTPX
OPERATION_TIMEOUT = 60 OPERATION_TIMEOUT = 60
KEEP_ALIVE_TIMEOUT = 20 KEEP_ALIVE_TIMEOUT = 20
SETTINGS_TIMEOUT = 10 SETTINGS_TIMEOUT = 10
READ_TIMEOUT = Float::INFINITY READ_TIMEOUT = WRITE_TIMEOUT = REQUEST_TIMEOUT = Float::INFINITY
WRITE_TIMEOUT = Float::INFINITY
# https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408 # https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
ip_address_families = begin ip_address_families = begin
@ -38,6 +37,7 @@ module HTTPX
keep_alive_timeout: KEEP_ALIVE_TIMEOUT, keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
read_timeout: READ_TIMEOUT, read_timeout: READ_TIMEOUT,
write_timeout: WRITE_TIMEOUT, write_timeout: WRITE_TIMEOUT,
request_timeout: REQUEST_TIMEOUT,
}, },
:headers => {}, :headers => {},
:window_size => WINDOW_SIZE, :window_size => WINDOW_SIZE,

View File

@ -72,6 +72,10 @@ module HTTPX
@options.timeout[:write_timeout] @options.timeout[:write_timeout]
end end
def request_timeout
@options.timeout[:request_timeout]
end
def trailers? def trailers?
defined?(@trailers) defined?(@trailers)
end end

View File

@ -103,5 +103,11 @@ module HTTPX
def handle_error: (StandardError) -> void def handle_error: (StandardError) -> void
def purge_after_closed: () -> void def purge_after_closed: () -> void
def set_request_timeouts: (Request request) -> void
def write_timeout_callback: (Request request, Numeric write_timeout)
def read_timeout_callback: (Request request, Numeric read_timeout, singleton(RequestTimeoutError) error_type)
end end
end end

View File

@ -20,7 +20,20 @@ module HTTPX
class SettingsTimeoutError < TimeoutError class SettingsTimeoutError < TimeoutError
end end
class ResolveTimeoutError < TimeoutError class ResolveTimeoutError < RequestTimeoutError
end
class RequestTimeoutError < TimeoutError
attr_reader request: Request
attr_reader response: response?
def initialize: (Request request, response? response, Numeric timeout) -> void
end
class ReadTimeoutError < TimeoutError
end
class WriteTimeoutError < RequestTimeoutError
end end
class ResolveError < Error class ResolveError < Error

View File

@ -10,7 +10,7 @@ module HTTPX
SETTINGS_TIMEOUT: Integer SETTINGS_TIMEOUT: Integer
DEFAULT_OPTIONS: Hash[Symbol, untyped] DEFAULT_OPTIONS: Hash[Symbol, untyped]
type timeout_type = :connect_timeout | :settings_timeout | :operation_timeout | :keep_alive_timeout | :total_timeout | :read_timeout | :write_timeout type timeout_type = :connect_timeout | :settings_timeout | :operation_timeout | :keep_alive_timeout | :total_timeout | :read_timeout | :write_timeout | :request_timeout
type timeout = Hash[timeout_type, Numeric] type timeout = Hash[timeout_type, Numeric]
def self.new: (?options) -> instance def self.new: (?options) -> instance

View File

@ -50,6 +50,12 @@ module HTTPX
def trailers?: () -> boolish def trailers?: () -> boolish
def read_timeout: () -> Numeric
def write_timeout: () -> Numeric
def request_timeout: () -> Numeric
class Body class Body
@headers: Headers @headers: Headers
@body: body_encoder? @body: body_encoder?

View File

@ -91,6 +91,17 @@ class SessionTest < Minitest::Test
end end
end end
def test_session_timeouts_request_timeout
uri = build_uri("/drip?numbytes=10&duration=4&delay=2&code=200")
session = HTTPX.with_timeout(request_timeout: 3, operation_timeout: 10)
response = session.get(uri)
verify_error_response(response, HTTPX::RequestTimeoutError)
uri = build_uri("/drip?numbytes=10&duration=2&delay=0&code=200")
response1 = session.get(uri)
verify_status(response1, 200)
end
# def test_http_timeouts_operation_timeout # def test_http_timeouts_operation_timeout
# uri = build_uri("/delay/2") # uri = build_uri("/delay/2")
# session = HTTPX.with_timeout(operation_timeout: 1) # session = HTTPX.with_timeout(operation_timeout: 1)