fix(quic): bound the client handshake

QUIC_SETTINGS never set HandshakeIdleTimeoutMs, so it inherited the
120s idle timeout. The constructor blocks until msquic reports the
connection either up or shut down, which meant dialling a host that does
not answer parked the caller for minutes. Bound it to 10s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
catbot 2026-08-25 17:06:51 +00:00
commit f14b85694f
2 changed files with 10 additions and 0 deletions

View file

@ -555,6 +555,12 @@ static HQUIC OpenClientConfiguration(const std::string& alpn, const QUICClientCr
QUIC_SETTINGS settings{};
settings.IsSet.IdleTimeoutMs = 1;
settings.IdleTimeoutMs = 120'000;
// Bound the handshake separately from the (much longer) idle timeout.
// The constructor blocks until msquic reports the connection either up or
// shut down; without this, dialling a host that never answers leaves it
// waiting for the idle timeout — minutes rather than seconds.
settings.IsSet.HandshakeIdleTimeoutMs = 1;
settings.HandshakeIdleTimeoutMs = 10'000;
// Keep the connection alive across long idle gaps. msquic sends PING frames
// on its own timer thread (independent of the app), so a request/response
// connection survives even while the app is blocked for a long time between
@ -618,6 +624,8 @@ ClientQUIC::ClientQUIC(const char* host, std::uint16_t port, std::string alpnIn,
QUIC_STATUS handshakeStatus = QUIC_STATUS_SUCCESS;
{
std::unique_lock lk(impl->mtx);
// Bounded by HandshakeIdleTimeoutMs above: an unreachable peer ends in
// SHUTDOWN_INITIATED_BY_TRANSPORT rather than waiting here forever.
impl->cv.wait(lk, [&]{ return impl->connected || impl->closed; });
if (impl->connected) return;
handshakeStatus = impl->shutdownStatus;

View file

@ -180,6 +180,8 @@ namespace Crafter {
// Client constructor: connects to host:port using QUIC. ALPN must
// match the listener. Throws QUICException on connect failure.
// Blocks for at most the handshake idle timeout (10s), so a host that
// never answers fails in seconds rather than at the idle timeout.
ClientQUIC(const char* host, std::uint16_t port, std::string alpn,
QUICClientCredentials creds = {});
ClientQUIC(std::string host, std::uint16_t port, std::string alpn,