h3-proxy: fixes around H3 proxy

code:
- less exception handling in existing code
- true ip happy eyeballing
- enable certificate verification
- cf-h2-proxy: abort connection when server closed connection

tests:
- remove all --insecure and --proxy-insecure args
- make session reuse test_60_12 a working one
- resolve port conflicts between h2o and nghttpx
- use proxy args better
- make test_60_06 run shorter
- kill h2o at the end of tests, normal stop takes too long

Ref: 59213f8248 #21789
Follow-up to e78b1b3ecc #21153

Closes #21798
This commit is contained in:
Stefan Eissing 2026-05-27 16:50:18 +02:00 committed by Viktor Szakats
parent 59213f8248
commit e4139a73c8
No known key found for this signature in database
25 changed files with 442 additions and 365 deletions

View file

@ -684,12 +684,13 @@ class CurlClient:
def get_proxy_args(self, proto: str = 'http/1.1',
proxys: bool = True, tunnel: bool = False,
use_ip: bool = False, use_ipv6: bool = False):
use_ip: bool = False, use_ipv6: bool = False,
use_h2o: bool = False):
proxy_name = '[::1]' if use_ipv6 else \
self._server_addr if use_ip else self.env.proxy_domain
if proxys:
if tunnel:
pport = self.env.pts_port(proto)
pport = self.env.pts_port(proto, use_h2o=use_h2o)
elif proto == 'h3':
pport = self.env.h3proxys_port
else:

View file

@ -824,15 +824,16 @@ class Env:
@property
def h3proxys_port(self) -> int:
return self.CONFIG.ports["h3proxys"]
return self.CONFIG.ports["h2o_h3proxys"]
def pts_port(self, proto: str = "http/1.1") -> int:
def pts_port(self, proto: str = "http/1.1", use_h2o: bool = False) -> int:
# proxy tunnel port
prefix = 'h2o_' if use_h2o else ''
if proto == "h3":
return self.CONFIG.ports["h3proxys"]
return self.CONFIG.ports.get("h2o_h3proxys", 0)
if proto == "h2":
return self.CONFIG.ports["h2proxys"]
return self.CONFIG.ports["proxys"]
return self.CONFIG.ports.get(f"{prefix}h2proxys", 0)
return self.CONFIG.ports[f"{prefix}proxys"]
@property
def caddy(self) -> str:

View file

@ -160,6 +160,12 @@ class H2o:
)
return True
def kill(self, wait_dead=True):
if self._process:
self._process.kill()
return True
return False
def restart(self):
self.stop()
return self.start()
@ -317,9 +323,9 @@ class H2oProxy(H2o):
super().initial_start()
def startup(ports: Dict[str, int]) -> bool:
self._port = ports["h3proxys"]
self._h2_port = ports["h2proxys"]
self._h1_port = ports["proxys"]
self._port = ports["h2o_h3proxys"]
self._h2_port = ports["h2o_h2proxys"]
self._h1_port = ports["h2o_proxys"]
if self.start():
self.env.update_ports(ports)
return True
@ -331,9 +337,9 @@ class H2oProxy(H2o):
return alloc_ports_and_do(
{
"h3proxys": socket.SOCK_DGRAM,
"h2proxys": socket.SOCK_STREAM,
"proxys": socket.SOCK_STREAM,
"h2o_h3proxys": socket.SOCK_DGRAM,
"h2o_h2proxys": socket.SOCK_STREAM,
"h2o_proxys": socket.SOCK_STREAM,
},
startup,
self.env.gen_root,

View file

@ -47,7 +47,7 @@ class Nghttpx:
self._name = name
self._domain = domain
self._port = 0
self._https_port = 0
self._port_is_quic = False
self._cmd = env.nghttpx
self._run_dir = os.path.join(env.gen_dir, name)
self._pid_file = os.path.join(self._run_dir, 'nghttpx.pid')
@ -76,8 +76,12 @@ class Nghttpx:
return self.reload()
@property
def https_port(self):
return self._https_port
def port(self):
return self._port
@property
def port_is_quic(self):
return self._port_is_quic
def exists(self):
return self._cmd and os.path.exists(self._cmd)
@ -150,18 +154,14 @@ class Nghttpx:
curl = CurlClient(env=self.env, run_dir=self._tmp_dir)
try_until = datetime.now() + timeout
while datetime.now() < try_until:
if self._https_port > 0:
check_url = f'https://{self._domain}:{self._port}/'
r = curl.http_get(url=check_url, extra_args=[
'--trace', 'curl.trace', '--trace-time',
'--connect-timeout', '1'
])
else:
check_url = f'https://{self._domain}:{self._port}/'
r = curl.http_get(url=check_url, extra_args=[
'--trace', 'curl.trace', '--trace-time',
'--http3-only', '--connect-timeout', '1'
])
xargs = [
'--trace', 'curl.trace', '--trace-time',
'--connect-timeout', '1'
]
if self.port_is_quic:
xargs.extend(['--http3-only'])
check_url = f'https://{self._domain}:{self.port}/'
r = curl.http_get(url=check_url, extra_args=xargs)
if r.exit_code != 0:
return True
log.debug(f'waiting for nghttpx to stop responding: {r}')
@ -173,18 +173,14 @@ class Nghttpx:
curl = CurlClient(env=self.env, run_dir=self._tmp_dir)
try_until = datetime.now() + timeout
while datetime.now() < try_until:
if self._https_port > 0:
check_url = f'https://{self._domain}:{self._port}/'
r = curl.http_get(url=check_url, extra_args=[
'--trace', 'curl.trace', '--trace-time',
'--connect-timeout', '1'
])
else:
check_url = f'https://{self._domain}:{self._port}/'
r = curl.http_get(url=check_url, extra_args=[
'--http3-only', '--trace', 'curl.trace', '--trace-time',
'--connect-timeout', '1'
])
xargs = [
'--trace', 'curl.trace', '--trace-time',
'--connect-timeout', '1'
]
if self.port_is_quic:
xargs.extend(['--http3-only'])
check_url = f'https://{self._domain}:{self.port}/'
r = curl.http_get(url=check_url, extra_args=xargs)
if r.exit_code == 0:
return True
time.sleep(.1)
@ -216,13 +212,18 @@ class NghttpxQuic(Nghttpx):
def __init__(self, env: Env):
super().__init__(env=env, name='nghttpx-quic',
domain=env.domain1, cred_name=env.domain1)
self._https_port = env.https_port
self._https_port = 0
def initial_start(self):
super().initial_start()
def startup(ports: Dict[str, int]) -> bool:
self._port = ports['nghttpx_https']
self._https_port = ports['nghttpx_https']
if self.supports_h3():
self._port = self.env.h3_port
self._port_is_quic = True
else:
self._port = self._https_port
if self.start():
self.env.update_ports(ports)
return True
@ -240,10 +241,10 @@ class NghttpxQuic(Nghttpx):
creds = self.env.get_credentials(self._cred_name)
assert creds # convince pytype this is not None
self._loaded_cred_name = self._cred_name
args = [self._cmd, f'--frontend=*,{self._port};tls']
args = [self._cmd, f'--frontend=*,{self._https_port};tls']
if self.supports_h3():
args.extend([
f'--frontend=*,{self.env.h3_port};quic',
f'--frontend=*,{self._port};quic',
'--frontend-quic-early-data',
])
args.extend([