From: Amaury Denoyelle Date: Fri, 30 May 2025 09:48:00 +0000 (+0200) Subject: MINOR: h3: complete HTTP/3 request scheme encoding X-Git-Tag: v3.3-dev2~46 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=235e818fa19f6015e0a57a5ebb2cad1037919ab7;p=thirdparty%2Fhaproxy.git MINOR: h3: complete HTTP/3 request scheme encoding Previously, scheme was always set to https when transcoding an HTX start-line into a HTTP/3 request. Change this so this conversion is now fully compliant. If no scheme is specified by the client, which is what happens most of the time with HTTP/1, https is set for the HTTP/3 request. Else, reuse the scheme requested by the client. If either https or http is set, qpack_encode_scheme will encode it using entry from QPACK static table. Else, a full literal field line with name ref is used instead as the scheme value is specified as-is. --- diff --git a/include/haproxy/qpack-enc.h b/include/haproxy/qpack-enc.h index 10ad1d415..29136191b 100644 --- a/include/haproxy/qpack-enc.h +++ b/include/haproxy/qpack-enc.h @@ -9,7 +9,7 @@ struct buffer; int qpack_encode_field_section_line(struct buffer *out); int qpack_encode_int_status(struct buffer *out, unsigned int status); int qpack_encode_method(struct buffer *out, enum http_meth_t meth, struct ist other); -int qpack_encode_scheme(struct buffer *out); +int qpack_encode_scheme(struct buffer *out, const struct ist scheme); int qpack_encode_path(struct buffer *out, const struct ist path); int qpack_encode_header(struct buffer *out, const struct ist n, const struct ist v); diff --git a/src/h3.c b/src/h3.c index 1ec2ae440..2eb9360ca 100644 --- a/src/h3.c +++ b/src/h3.c @@ -1707,7 +1707,7 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx) enum htx_blk_type type; struct htx_blk *blk; struct htx_sl *sl; - struct ist meth; + struct ist meth, uri, scheme = IST_NULL, auth = IST_NULL; int frame_length_size; /* size in bytes of frame length varint field */ int ret, err, hdr; @@ -1726,6 +1726,7 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx) BUG_ON_HOT(sl); /* Only one start-line expected */ sl = htx_get_blk_ptr(htx, blk); meth = htx_sl_req_meth(sl); + uri = htx_sl_req_uri(sl); break; case HTX_BLK_HDR: @@ -1762,7 +1763,35 @@ static int h3_req_headers_send(struct qcs *qcs, struct htx *htx) if (qpack_encode_method(&headers_buf, sl->info.req.meth, meth)) goto err; - if (qpack_encode_scheme(&headers_buf)) + if (uri.ptr[0] != '/' && uri.ptr[0] != '*') { + int len = 1; + + /* the URI seems to start with a scheme */ + while (len < uri.len && uri.ptr[len] != ':') + len++; + + if (len + 2 < uri.len && uri.ptr[len + 1] == '/' && uri.ptr[len + 2] == '/') { + /* make the uri start at the authority now */ + scheme = ist2(uri.ptr, len); + uri = istadv(uri, len + 3); + + /* find the auth part of the URI */ + auth = ist2(uri.ptr, 0); + while (auth.len < uri.len && auth.ptr[auth.len] != '/') + auth.len++; + + uri = istadv(uri, auth.len); + } + } + + if (!istlen(scheme)) { + if ((sl->flags & (HTX_SL_F_HAS_SCHM|HTX_SL_F_SCHM_HTTP)) == (HTX_SL_F_HAS_SCHM|HTX_SL_F_SCHM_HTTP)) + scheme = ist("http"); + else + scheme = ist("https"); + } + + if (qpack_encode_scheme(&headers_buf, scheme)) goto err; if (qpack_encode_path(&headers_buf, ist('/'))) diff --git a/src/qpack-enc.c b/src/qpack-enc.c index 4aebc0172..ba03b7042 100644 --- a/src/qpack-enc.c +++ b/src/qpack-enc.c @@ -172,17 +172,34 @@ int qpack_encode_method(struct buffer *out, enum http_meth_t meth, struct ist ot return 0; } -/* Encode pseudo-header scheme defined to https on buffer. - * - * Returns 0 on success else non-zero. - */ -int qpack_encode_scheme(struct buffer *out) +/* Returns 0 on success else non-zero. */ +int qpack_encode_scheme(struct buffer *out, const struct ist scheme) { - if (b_room(out) < 2) - return 1; + int sz; + + if (unlikely(!isteq(scheme, ist("https"))) && !isteq(scheme, ist("http"))) { + sz = 2 + qpack_get_prefix_int_size(istlen(scheme), 7) + istlen(scheme); + if (b_room(out) < sz) + return 1; + + /* literal field line with name ref */ + qpack_encode_prefix_integer(out, 23, 4, 0x50); + qpack_encode_prefix_integer(out, istlen(scheme), 7, 0); + for (size_t i = 0; i < istlen(scheme); ++i) + b_putchr(out, istptr(scheme)[i]); + } + else { + int idx = 23; + + if (unlikely(!isteq(scheme, ist("http")))) + idx = 22; + if (b_room(out) < 2) + return 1; + + /* :scheme: http[s] */ + qpack_encode_prefix_integer(out, idx, 6, 0xc0); + } - /* :scheme: https */ - qpack_encode_prefix_integer(out, 23, 6, 0xc0); return 0; }