From: Amaury Denoyelle Date: Fri, 11 Jul 2025 12:25:30 +0000 (+0200) Subject: BUG/MEDIUM: h3: handle interim response properly on FE side X-Git-Tag: v3.3-dev4~77 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e7b3a69c594ab14cf25e9b8d038635aef4b8229f;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: h3: handle interim response properly on FE side On frontend side, HTTP/3 layer is responsible to transcode an HTX response message into HTTP/3 HEADERS frame. This operations is handled via h3_resp_headers_send(). Prior to this patch, if HTX EOM was encountered in the HTX message after response transcoding, was reported to the QMUX layer. This will in turn cause FIN stream bit to be set when the response is emitted. However, this is not correct as a single HTX response can be constitued of several interim message, each delimited by EOM block. Most of the time, this bug will cause the client to close the connection as it is invalid to receive an interim response with FIN bit set. Fixes this by now properly differentiate interim and final response. During interim response transcoding, the new flag H3_SF_SENT_INTERIM will be set, which will prevent to be reported. Thus, will only be notified for the final response. This must be backported up to 2.6. Note that it relies on the previous patch which also must be taken. --- diff --git a/src/h3.c b/src/h3.c index 2adda3d21..f3267db01 100644 --- a/src/h3.c +++ b/src/h3.c @@ -150,9 +150,10 @@ struct h3c { DECLARE_STATIC_POOL(pool_head_h3c, "h3c", sizeof(struct h3c)); -#define H3_SF_UNI_INIT 0x00000001 /* stream type not parsed for unidirectional stream */ -#define H3_SF_UNI_NO_H3 0x00000002 /* unidirectional stream does not carry H3 frames */ -#define H3_SF_HAVE_CLEN 0x00000004 /* content-length header is present; relevant either for request or response depending on the side of the connection */ +#define H3_SF_UNI_INIT 0x00000001 /* stream type not parsed for unidirectional stream */ +#define H3_SF_UNI_NO_H3 0x00000002 /* unidirectional stream does not carry H3 frames */ +#define H3_SF_HAVE_CLEN 0x00000004 /* content-length header is present; relevant either for request or response depending on the side of the connection */ +#define H3_SF_SENT_INTERIM 0x00000008 /* last response sent is 1xx interim. Used on FE side only. */ struct h3s { struct h3c *h3c; @@ -2146,6 +2147,7 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx) */ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx) { + struct h3s *h3s = qcs->ctx; int err; struct buffer outbuf; struct buffer headers_buf = BUF_NULL; @@ -2182,6 +2184,15 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx) TRACE_ERROR("invalid response status code", H3_EV_STRM_SEND, qcs->qcc->conn, qcs); goto err; } + else if (status >= 100 && status < 200) { + TRACE_USER("handling interim HTX response", H3_EV_STRM_SEND, qcs->qcc->conn, qcs); + BUG_ON(conn_is_back(qcs->qcc->conn)); /* H3_SF_SENT_INTERIM is FE side only. */ + h3s->flags |= H3_SF_SENT_INTERIM; + } + else { + TRACE_USER("handling final HTX response", H3_EV_STRM_SEND, qcs->qcc->conn, qcs); + h3s->flags &= ~H3_SF_SENT_INTERIM; + } } else if (type == HTX_BLK_HDR) { if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) { @@ -2644,6 +2655,7 @@ static int h3_resp_data_send(struct qcs *qcs, struct htx *htx, */ static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count, char *fin) { + struct h3s *h3s = qcs->ctx; size_t total = 0; enum htx_blk_type btype; struct htx *htx; @@ -2678,7 +2690,7 @@ static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count, char break; case HTX_BLK_RES_SL: - /* start-line -> HEADERS h3 frame */ + /* start-line -> HEADERS h3 frame (FE side) */ ret = h3_resp_headers_send(qcs, htx); if (ret > 0) { total += ret; @@ -2759,7 +2771,7 @@ static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count, char #endif out: - if (eom && htx_is_empty(htx)) { + if (eom && htx_is_empty(htx) && !(h3s->flags & H3_SF_SENT_INTERIM)) { TRACE_USER("transcoding last HTX message", H3_EV_STRM_SEND, qcs->qcc->conn, qcs); *fin = 1; }