From: Stefan Eissing Date: Wed, 7 Jan 2026 11:12:29 +0000 (+0100) Subject: http/3: add description for known server error codes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f6e8531c03edcef9da222d1794d52d14247cd5ce;p=thirdparty%2Fcurl.git http/3: add description for known server error codes When a server resets a stream with an error code, list that code and its known name in the failure message of the transfer. Ref: #20195 Closes #20202 --- diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 97ef98b971..43db734ef7 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -1372,7 +1372,9 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, (void)cf; *pnread = 0; if(stream->reset) { - failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->id); + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; } else if(!stream->resp_hds_complete) { diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index 464879788c..bcf9ab9549 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -2076,9 +2076,9 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, (void)cf; *pnread = 0; if(stream->reset) { - failf(data, - "HTTP/3 stream %" PRId64 " reset by server", - stream->s.id); + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->s.id, stream->error3, + vquic_h3_err_str(stream->error3)); return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; } else if(!stream->resp_hds_complete) { diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index e4140e776d..01f1bf5edd 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -855,7 +855,9 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream); *pnread = 0; if(stream->reset) { - failf(data, "HTTP/3 stream %" PRIu64 " reset by server", stream->id); + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", stream->id, result); diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index d7bfa99104..33ba12f476 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -742,6 +742,56 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, return CURLE_OK; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS +const char *vquic_h3_err_str(uint64_t error_code) +{ + if(error_code <= UINT_MAX) { + switch((unsigned int)error_code) { + case CURL_H3_ERR_NO_ERROR: + return "NO_ERROR"; + case CURL_H3_ERR_GENERAL_PROTOCOL_ERROR: + return "GENERAL_PROTOCOL_ERROR"; + case CURL_H3_ERR_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case CURL_H3_ERR_STREAM_CREATION_ERROR: + return "STREAM_CREATION_ERROR"; + case CURL_H3_ERR_CLOSED_CRITICAL_STREAM: + return "CLOSED_CRITICAL_STREAM"; + case CURL_H3_ERR_FRAME_UNEXPECTED: + return "FRAME_UNEXPECTED"; + case CURL_H3_ERR_FRAME_ERROR: + return "FRAME_ERROR"; + case CURL_H3_ERR_EXCESSIVE_LOAD: + return "EXCESSIVE_LOAD"; + case CURL_H3_ERR_ID_ERROR: + return "ID_ERROR"; + case CURL_H3_ERR_SETTINGS_ERROR: + return "SETTINGS_ERROR"; + case CURL_H3_ERR_MISSING_SETTINGS: + return "MISSING_SETTINGS"; + case CURL_H3_ERR_REQUEST_REJECTED: + return "REQUEST_REJECTED"; + case CURL_H3_ERR_REQUEST_CANCELLED: + return "REQUEST_CANCELLED"; + case CURL_H3_ERR_REQUEST_INCOMPLETE: + return "REQUEST_INCOMPLETE"; + case CURL_H3_ERR_MESSAGE_ERROR: + return "MESSAGE_ERROR"; + case CURL_H3_ERR_CONNECT_ERROR: + return "CONNECT_ERROR"; + case CURL_H3_ERR_VERSION_FALLBACK: + return "VERSION_FALLBACK"; + default: + break; + } + } + /* RFC 9114 ch. 8.1 + 9, reserved future error codes that are NO_ERROR */ + if((error_code >= 0x21) && !((error_code - 0x21) % 0x1f)) + return "NO_ERROR"; + return "unknown"; +} +#endif /* CURL_DISABLE_VERBOSE_STRINGS */ + #if defined(USE_NGTCP2) || defined(USE_NGHTTP3) static void *vquic_ngtcp2_malloc(size_t size, void *user_data) diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index e602b7ee6d..a6d3d0ea54 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -32,6 +32,33 @@ #define MAX_PKT_BURST 10 #define MAX_UDP_PAYLOAD_SIZE 1452 +/* definitions from RFC 9114, ch 8.1 */ +typedef enum { + CURL_H3_ERR_NO_ERROR = 0x0100, + CURL_H3_ERR_GENERAL_PROTOCOL_ERROR = 0x0101, + CURL_H3_ERR_INTERNAL_ERROR = 0x0102, + CURL_H3_ERR_STREAM_CREATION_ERROR = 0x0103, + CURL_H3_ERR_CLOSED_CRITICAL_STREAM = 0x0104, + CURL_H3_ERR_FRAME_UNEXPECTED = 0x0105, + CURL_H3_ERR_FRAME_ERROR = 0x0106, + CURL_H3_ERR_EXCESSIVE_LOAD = 0x0107, + CURL_H3_ERR_ID_ERROR = 0x0108, + CURL_H3_ERR_SETTINGS_ERROR = 0x0109, + CURL_H3_ERR_MISSING_SETTINGS = 0x010a, + CURL_H3_ERR_REQUEST_REJECTED = 0x010b, + CURL_H3_ERR_REQUEST_CANCELLED = 0x010c, + CURL_H3_ERR_REQUEST_INCOMPLETE = 0x010d, + CURL_H3_ERR_MESSAGE_ERROR = 0x010e, + CURL_H3_ERR_CONNECT_ERROR = 0x010f, + CURL_H3_ERR_VERSION_FALLBACK = 0x0110, +} vquic_h3_error; + +#ifndef CURL_DISABLE_VERBOSE_STRINGS +const char *vquic_h3_err_str(uint64_t error_code); +#else +#define vquic_h3_err_str(x) "" +#endif /* CURL_DISABLE_VERBOSE_STRINGS */ + struct cf_quic_ctx { curl_socket_t sockfd; /* connected UDP socket */ struct sockaddr_storage local_addr; /* address socket is bound to */