ngtcp2: check error code on connect failure

Access the error codes of ngtcp2 when a connect attempt failes. Trace
the information for analysis. Treat errors as permanent failure by
default, trigger retrying only when the server refused without
indicating an error.

Closes #18521
This commit is contained in:
Stefan Eissing 2025-09-11 14:12:04 +02:00 committed by Daniel Stenberg
parent b178d58266
commit 115fe808f2
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
2 changed files with 29 additions and 8 deletions

View file

@ -2578,11 +2578,31 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
out:
if(result == CURLE_RECV_ERROR && ctx->qconn &&
ngtcp2_conn_in_draining_period(ctx->qconn)) {
/* When a QUIC server instance is shutting down, it may send us a
* CONNECTION_CLOSE right away. Our connection then enters the DRAINING
* state. The CONNECT may work in the near future again. Indicate
* that as a "weird" reply. */
result = CURLE_WEIRD_SERVER_REPLY;
const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn);
result = CURLE_COULDNT_CONNECT;
if(cerr) {
CURL_TRC_CF(data, cf, "connect error, type=%d, code=%"
FMT_PRIu64,
cerr->type, (curl_uint64_t)cerr->error_code);
switch(cerr->type) {
case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION:
CURL_TRC_CF(data, cf, "error in version negotiation");
break;
default:
if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) {
CURL_TRC_CF(data, cf, "crypto error, tls alert=%u",
(unsigned int)(cerr->error_code & 0xffu));
}
else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) {
CURL_TRC_CF(data, cf, "connection refused by server");
/* When a QUIC server instance is shutting down, it may send us a
* CONNECTION_CLOSE with this code right away. We want
* to keep on trying in this case. */
result = CURLE_WEIRD_SERVER_REPLY;
}
}
}
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS

View file

@ -124,15 +124,16 @@ class Nghttpx:
running = self._process
self._process = None
os.kill(running.pid, signal.SIGQUIT)
end_wait = datetime.now() + timeout
end_wait = datetime.now() + timedelta(seconds=5)
if not self.start(wait_live=False):
self._process = running
return False
while datetime.now() < end_wait:
try:
log.debug(f'waiting for nghttpx({running.pid}) to exit.')
running.wait(2)
running.wait(1)
log.debug(f'nghttpx({running.pid}) terminated -> {running.returncode}')
running = None
break
except subprocess.TimeoutExpired:
log.warning(f'nghttpx({running.pid}), not shut down yet.')
@ -142,7 +143,7 @@ class Nghttpx:
os.kill(running.pid, signal.SIGKILL)
running.terminate()
running.wait(1)
return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT))
return self.wait_live(timeout=timeout)
return False
def wait_dead(self, timeout: timedelta):