ip-happy: do not set unnecessary timeout

When attempts on all addresses have been started, do no longer set any
EXPIRE_HAPPY_EYEBALLS timeouts.

Fixes #18767
Reported-by: Johannes Schindelin
Closes #18768
This commit is contained in:
Stefan Eissing 2025-09-29 16:38:55 +02:00 committed by Daniel Stenberg
parent d8823e855c
commit b022389757
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
8 changed files with 160 additions and 74 deletions

View file

@ -25,6 +25,7 @@
###########################################################################
#
import logging
import re
import pytest
from testenv import Env, CurlClient
@ -100,3 +101,34 @@ class TestEyeballs:
r.check_response(count=1, http_status=None, exitcode=False)
assert r.stats[0]['time_connect'] == 0 # no one should have listened
assert r.stats[0]['time_appconnect'] == 0 # did not happen either
# check timers when trying 3 unresponsive addresses
@pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'),
reason='curl lacks ipv6 support')
def test_06_13_timers(self, env: Env):
curl = CurlClient(env=env)
# ipv6 0100::/64 is supposed to go into the void (rfc6666)
r = curl.http_download(urls=['https://xxx.invalid/'], extra_args=[
'--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3',
'--connect-timeout', '1',
'--happy-eyeballs-timeout-ms', '123',
'--trace-config', 'timer,happy-eyeballs,tcp'
])
r.check_response(count=1, http_status=None, exitcode=False)
assert r.stats[0]['time_connect'] == 0 # no one connected
# check that we indeed started attempts on all 3 addresses
tcp_attempts = [line for line in r.trace_lines
if re.match(r'.*Trying \[100::[123]]:443', line)]
assert len(tcp_attempts) == 3, f'fond: {"".join(tcp_attempts)}\n{r.dump_logs()}'
# if the 0100::/64 really goes into the void, we should see 2 HAPPY_EYEBALLS
# timeouts being set here
failed_attempts = [line for line in r.trace_lines
if re.match(r'.*checked connect attempts: 0 ongoing', line)]
if len(failed_attempts):
# github CI fails right away with "Network is unreachable", slackers...
assert len(failed_attempts) == 3, f'found: {"".join(failed_attempts)}\n{r.dump_logs()}'
else:
# no immediately failed attempts, as should be
he_timers_set = [line for line in r.trace_lines
if re.match(r'.*\[TIMER] \[HAPPY_EYEBALLS] set for', line)]
assert len(he_timers_set) == 2, f'found: {"".join(he_timers_set)}\n{r.dump_logs()}'

View file

@ -895,6 +895,9 @@ class CurlClient:
if not isinstance(urls, list):
urls = [urls]
if options is not None and '--resolve' in options:
force_resolve = False
args = [self._curl, "-s", "--path-as-is"]
if 'CURL_TEST_EVENT' in os.environ:
args.append('--test-event')