simple_httpclient: distinguish DNS-resolve from TCP-connect in connect_timeout#3668
Open
HrachShah wants to merge 1 commit into
Open
simple_httpclient: distinguish DNS-resolve from TCP-connect in connect_timeout#3668HrachShah wants to merge 1 commit into
HrachShah wants to merge 1 commit into
Conversation
…t_timeout message Issue tornadoweb#3522 reports that when `connect_timeout` fires during the connect phase, the raised `HTTPTimeoutError` always carries the generic "Timeout while connecting" message. In practice the connect phase has two distinct steps — DNS resolution and TCP/TLS handshake — and a slow DNS server is a much more actionable failure mode than "the host didn't accept the connection in time". Users who hit `connect_timeout` had no way to tell the two apart from the message alone. This change makes `_HTTPConnection.run` pre-resolve the hostname through the TCP client's resolver (so `OverrideResolver` / `hostname_mapping` are honoured) and stores a `_connecting_phase` on the connection that tracks which step is in flight. The connect timeout callback is replaced with a thin `_on_connect_timeout` that reads the phase and forwards either "while resolving" or "while connecting" to the existing `_on_timeout` path. When the host is already a numeric IP literal (`is_valid_ip` is true), the resolver is skipped entirely so the timeout message still correctly says "connecting" and the original behaviour for IP literals is preserved. Happy-Eyeballs parallel address-family fallback inside `TCPClient.connect` is not reimplemented here; that fallback only matters on broken networks where DNS is unlikely to be the slow leg, so the trade-off is acceptable for the timeout-reporting use case. New test `test_connect_timeout_reports_resolving_phase` uses a hanging resolver and a non-IP hostname (`hostname.invalid`) to exercise the resolving-phase branch. The existing `test_connect_timeout` test continues to pass because `is_valid_ip` short-circuits the new code path for the IP-literal test URL. Verified with `pytest tornado/test/simple_httpclient_test.py`: 115 passed, 31 skipped. Without the fix, the new test fails with `'resolving' not found in 'Timeout while connecting'`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3522
When
connect_timeoutfires during the connect phase, the raisedHTTPTimeoutErroralways carries the generic "Timeout whileconnecting" message. In practice the connect phase has two distinct
steps — DNS resolution and TCP/TLS handshake — and a slow DNS
server is a much more actionable failure mode than "the host didn't
accept the connection in time". Users hitting
connect_timeouthadno way to tell the two apart from the message alone.
This change makes
_HTTPConnection.runpre-resolve the hostnamethrough the TCP client's resolver (so
OverrideResolver/hostname_mappingare honoured) and stores a_connecting_phaseonthe connection that tracks which step is in flight. The connect
timeout callback is replaced with a thin
_on_connect_timeoutthatreads the phase and forwards either "while resolving" or
"while connecting" to the existing
_on_timeoutpath.When the host is already a numeric IP literal (
is_valid_ipistrue), the resolver is skipped entirely so the timeout message
still correctly says "connecting" and the original behaviour for IP
literals is preserved. Happy-Eyeballs parallel address-family
fallback inside
TCPClient.connectis not reimplemented here; thatfallback only matters on broken networks where DNS is unlikely to
be the slow leg, so the trade-off is acceptable for the
timeout-reporting use case.
New test
test_connect_timeout_reports_resolving_phaseuses ahanging resolver and a non-IP hostname (
hostname.invalid) toexercise the resolving-phase branch. The existing
test_connect_timeouttest continues to pass becauseis_valid_ipshort-circuits the new code path for the IP-literal test URL.
Verified with
pytest tornado/test/simple_httpclient_test.py:115 passed, 31 skipped. Without the fix, the new test fails
with
'resolving' not found in 'Timeout while connecting'.