From: Amaury Denoyelle Date: Mon, 16 Jun 2025 13:38:26 +0000 (+0200) Subject: MINOR: h3: encode request headers X-Git-Tag: v3.3-dev2~48 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f5342e0a9633155b9d4cd8972929e59e2fdb35b0;p=thirdparty%2Fhaproxy.git MINOR: h3: encode request headers Implement encoding of HTTP/3 request headers during HTX->H3 conversion on the backend side. This simply relies on h3_encode_header(). Special check is implemented to ensure that connection-specific headers are ignored. An HTTP/3 endpoint must never generate them, or the peer will consider the message as malformed. --- diff --git a/src/h3.c b/src/h3.c index ccf6eae7a..de236ba4d 100644 --- a/src/h3.c +++ b/src/h3.c @@ -1700,13 +1700,51 @@ static int h3_encode_header(struct buffer *buf, */ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx) { + struct http_hdr list[global.tune.max_http_hdr * 2]; struct buffer outbuf; struct buffer headers_buf = BUF_NULL; struct buffer *res; - struct htx_blk *blk; enum htx_blk_type type; + struct htx_blk *blk; + struct htx_sl *sl; int frame_length_size; /* size in bytes of frame length varint field */ - int ret, err; + int ret, err, hdr; + + hdr = 0; + sl = NULL; + for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) { + type = htx_get_blk_type(blk); + + switch (type) { + case HTX_BLK_EOH: + goto end_loop; + case HTX_BLK_UNUSED: + break; + + case HTX_BLK_REQ_SL: + BUG_ON_HOT(sl); /* Only one start-line expected */ + sl = htx_get_blk_ptr(htx, blk); + break; + + case HTX_BLK_HDR: + if (unlikely(hdr >= sizeof(list) / sizeof(list[0]) - 1)) + goto err; + + list[hdr].n = htx_get_blk_name(htx, blk); + list[hdr].v = htx_get_blk_value(htx, blk); + + ++hdr; + break; + + default: + break; + } + } + + end_loop: + BUG_ON_HOT(!sl); /* start-line must be present. */ + /* marker for end of headers */ + list[hdr].n = ist(""); res = qcc_get_stream_txbuf(qcs, &err, 0); BUG_ON(!res); @@ -1734,6 +1772,39 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx) if (h3_encode_header(&headers_buf, ist(":authority"), ist("127.0.0.1:20443"))) goto err; + /* Encode every parsed headers, stop at empty one. */ + for (hdr = 0; hdr < sizeof(list) / sizeof(list[0]); ++hdr) { + if (isteq(list[hdr].n, ist(""))) + break; + + /* RFC 9114 4.2. HTTP Fields + * + * An intermediary transforming an HTTP/1.x message to HTTP/3 + * MUST remove connection-specific header fields as discussed in + * Section 7.6.1 of [HTTP], or their messages will be treated by + * other HTTP/3 endpoints as malformed. + */ + if (isteq(list[hdr].n, ist("connection")) || + isteq(list[hdr].n, ist("proxy-connection")) || + isteq(list[hdr].n, ist("keep-alive")) || + isteq(list[hdr].n, ist("upgrade")) || + isteq(list[hdr].n, ist("transfer-encoding"))) { + continue; + } + else if (isteq(list[hdr].n, ist("te"))) { + /* "te" may only be sent with "trailers" if this value + * is present, otherwise it must be deleted. + */ + const struct ist v = istist(list[hdr].v, ist("trailers")); + if (!isttest(v) || (v.len > 8 && v.ptr[8] != ',')) + continue; + list[hdr].v = ist("trailers"); + } + + if (h3_encode_header(&headers_buf, list[hdr].n, list[hdr].v)) + goto err; + } + /* Now that all headers are encoded, we are certain that res buffer is * big enough */