]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: h3: handle interim response properly on FE side
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 11 Jul 2025 12:25:30 +0000 (14:25 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 15 Jul 2025 16:39:23 +0000 (18:39 +0200)
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, <fin> 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 <fin> to be reported. Thus, <fin> 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.

src/h3.c

index 2adda3d21297ef54da31d1e3b85b1e80b4c84f79..f3267db01b85ebe29d68cac08b80ad332d68b1b6 100644 (file)
--- 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;
        }