From fac8a620377a1383b269ea1763b5f1b948bb500c Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Thu, 2 Nov 2023 23:11:27 +0000 Subject: [PATCH 1/2] bail out on dns answer when connection already closed there are situations where a connection may already be closed before dns response is received for it. such an example is connection coalescing, when happy eyeballs takes over, first address arrives, a coalescing situation is detected, and then the connection and it's happy eyeballs cousin are both closed, **before** the cound connection has been resolved --- lib/httpx/resolver/resolver.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/httpx/resolver/resolver.rb b/lib/httpx/resolver/resolver.rb index 0f42adb3..4b92e4eb 100644 --- a/lib/httpx/resolver/resolver.rb +++ b/lib/httpx/resolver/resolver.rb @@ -62,8 +62,11 @@ module HTTPX addresses.first.to_s != connection.origin.host.to_s log { "resolver: A response, applying resolution delay..." } @pool.after(0.05) do - # double emission check - emit_resolved_connection(connection, addresses) unless connection.addresses && addresses.intersect?(connection.addresses) + unless connection.state == :closed || + # double emission check + (connection.addresses && addresses.intersect?(connection.addresses)) + emit_resolved_connection(connection, addresses) + end end else emit_resolved_connection(connection, addresses) From f477871bfa1f3a2dc0859f71a6af2821b6059a8e Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Fri, 3 Nov 2023 12:12:05 +0000 Subject: [PATCH 2/2] reset timer baseline interval when adding new timers due to how read timeouts are added on request transitions, timers may enter the pool **before** a new tick happens, and are therefore accounted for when the timers are fired after the current tick. This patch resets the timer, which will force a new tick before they may fire again. --- lib/httpx/timers.rb | 2 ++ regression_tests/bug_1_1_0_test.rb | 34 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 regression_tests/bug_1_1_0_test.rb diff --git a/lib/httpx/timers.rb b/lib/httpx/timers.rb index 874a3a28..600a9f77 100644 --- a/lib/httpx/timers.rb +++ b/lib/httpx/timers.rb @@ -24,6 +24,8 @@ module HTTPX interval << callback + @next_interval_at = nil + interval end diff --git a/regression_tests/bug_1_1_0_test.rb b/regression_tests/bug_1_1_0_test.rb new file mode 100644 index 00000000..18ce9171 --- /dev/null +++ b/regression_tests/bug_1_1_0_test.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "webrick" +require "webrick/httpproxy" +require "test_helper" +require "support/http_helpers" +require "support/proxy_helper" +require "support/minitest_extensions" + +class Bug_1_1_0_Test < Minitest::Test + include HTTPHelpers + + def test_read_timeout_firing_too_soon_before_select + timeout = { read_timeout: 1 } + + uri = build_uri("/get") + + begin + response = HTTPX.get(uri, timeout: timeout) + response.raise_for_status + sleep 2 + response = HTTPX.get(uri, timeout: timeout) + response.raise_for_status + rescue HTTPX::ReadTimeoutError + raise Minitest::Assertion, "should not have raised a read timeout error" + end + end + + private + + def scheme + "http://" + end +end