fix(quic): stop ~ClientQUIC aborting the process on teardown #8
2 changed files with 10 additions and 0 deletions
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>
commit
f14b85694f
|
|
@ -555,6 +555,12 @@ static HQUIC OpenClientConfiguration(const std::string& alpn, const QUICClientCr
|
||||||
QUIC_SETTINGS settings{};
|
QUIC_SETTINGS settings{};
|
||||||
settings.IsSet.IdleTimeoutMs = 1;
|
settings.IsSet.IdleTimeoutMs = 1;
|
||||||
settings.IdleTimeoutMs = 120'000;
|
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
|
// 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
|
// 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
|
// 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;
|
QUIC_STATUS handshakeStatus = QUIC_STATUS_SUCCESS;
|
||||||
{
|
{
|
||||||
std::unique_lock lk(impl->mtx);
|
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; });
|
impl->cv.wait(lk, [&]{ return impl->connected || impl->closed; });
|
||||||
if (impl->connected) return;
|
if (impl->connected) return;
|
||||||
handshakeStatus = impl->shutdownStatus;
|
handshakeStatus = impl->shutdownStatus;
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,8 @@ namespace Crafter {
|
||||||
|
|
||||||
// Client constructor: connects to host:port using QUIC. ALPN must
|
// Client constructor: connects to host:port using QUIC. ALPN must
|
||||||
// match the listener. Throws QUICException on connect failure.
|
// 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,
|
ClientQUIC(const char* host, std::uint16_t port, std::string alpn,
|
||||||
QUICClientCredentials creds = {});
|
QUICClientCredentials creds = {});
|
||||||
ClientQUIC(std::string host, std::uint16_t port, std::string alpn,
|
ClientQUIC(std::string host, std::uint16_t port, std::string alpn,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue