From 3f9c165d512e36e2164f693c7799f9741cf30bda Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Sat, 6 Aug 2022 23:20:21 +0100 Subject: [PATCH] added request_timeout --- lib/httpx/connection.rb | 55 +++++++++++++++++++++++++---------------- lib/httpx/errors.rb | 12 +++------ lib/httpx/options.rb | 4 +-- lib/httpx/request.rb | 4 +++ sig/connection.rbs | 6 +++++ sig/errors.rbs | 15 ++++++++++- sig/options.rbs | 2 +- sig/request.rbs | 6 +++++ test/session_test.rb | 11 +++++++++ 9 files changed, 81 insertions(+), 34 deletions(-) diff --git a/lib/httpx/connection.rb b/lib/httpx/connection.rb index 640cfdc6..25c6c43a 100644 --- a/lib/httpx/connection.rb +++ b/lib/httpx/connection.rb @@ -432,27 +432,7 @@ module HTTPX @inflight += 1 parser.send(request) - write_timeout = request.write_timeout - 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? + set_request_timeouts(request) return unless @state == :inactive @@ -620,5 +600,38 @@ module HTTPX request.emit(:response, response) 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 diff --git a/lib/httpx/errors.rb b/lib/httpx/errors.rb index 9f9d22bb..f923c3f0 100644 --- a/lib/httpx/errors.rb +++ b/lib/httpx/errors.rb @@ -27,8 +27,9 @@ module HTTPX class RequestTimeoutError < TimeoutError attr_reader :request - def initialize(request, timeout) + def initialize(request, response, timeout) @request = request + @response = response super(timeout, "Timed out after #{timeout} seconds") end @@ -37,14 +38,7 @@ module HTTPX end end - class ReadTimeoutError < RequestTimeoutError - attr_reader :response - - def initialize(request, response, timeout) - @response = response - super(request, timeout) - end - end + class ReadTimeoutError < RequestTimeoutError; end class WriteTimeoutError < RequestTimeoutError; end diff --git a/lib/httpx/options.rb b/lib/httpx/options.rb index c92f5af0..9a04e342 100644 --- a/lib/httpx/options.rb +++ b/lib/httpx/options.rb @@ -10,8 +10,7 @@ module HTTPX OPERATION_TIMEOUT = 60 KEEP_ALIVE_TIMEOUT = 20 SETTINGS_TIMEOUT = 10 - READ_TIMEOUT = Float::INFINITY - WRITE_TIMEOUT = Float::INFINITY + READ_TIMEOUT = WRITE_TIMEOUT = REQUEST_TIMEOUT = Float::INFINITY # https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408 ip_address_families = begin @@ -38,6 +37,7 @@ module HTTPX keep_alive_timeout: KEEP_ALIVE_TIMEOUT, read_timeout: READ_TIMEOUT, write_timeout: WRITE_TIMEOUT, + request_timeout: REQUEST_TIMEOUT, }, :headers => {}, :window_size => WINDOW_SIZE, diff --git a/lib/httpx/request.rb b/lib/httpx/request.rb index 0349d40d..ddb5b49f 100644 --- a/lib/httpx/request.rb +++ b/lib/httpx/request.rb @@ -72,6 +72,10 @@ module HTTPX @options.timeout[:write_timeout] end + def request_timeout + @options.timeout[:request_timeout] + end + def trailers? defined?(@trailers) end diff --git a/sig/connection.rbs b/sig/connection.rbs index 39238023..272ac01e 100644 --- a/sig/connection.rbs +++ b/sig/connection.rbs @@ -103,5 +103,11 @@ module HTTPX def handle_error: (StandardError) -> 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 \ No newline at end of file diff --git a/sig/errors.rbs b/sig/errors.rbs index 74c026a7..104f01f5 100644 --- a/sig/errors.rbs +++ b/sig/errors.rbs @@ -20,7 +20,20 @@ module HTTPX class SettingsTimeoutError < TimeoutError 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 class ResolveError < Error diff --git a/sig/options.rbs b/sig/options.rbs index 7c1546ec..7a22b552 100644 --- a/sig/options.rbs +++ b/sig/options.rbs @@ -10,7 +10,7 @@ module HTTPX SETTINGS_TIMEOUT: Integer 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] def self.new: (?options) -> instance diff --git a/sig/request.rbs b/sig/request.rbs index 1c1f30aa..e4694621 100644 --- a/sig/request.rbs +++ b/sig/request.rbs @@ -50,6 +50,12 @@ module HTTPX def trailers?: () -> boolish + def read_timeout: () -> Numeric + + def write_timeout: () -> Numeric + + def request_timeout: () -> Numeric + class Body @headers: Headers @body: body_encoder? diff --git a/test/session_test.rb b/test/session_test.rb index c017e439..3fac6604 100644 --- a/test/session_test.rb +++ b/test/session_test.rb @@ -91,6 +91,17 @@ class SessionTest < Minitest::Test 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 # uri = build_uri("/delay/2") # session = HTTPX.with_timeout(operation_timeout: 1)