]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: h3: complete HTTP/3 request scheme encoding
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 30 May 2025 09:48:00 +0000 (11:48 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 16 Jun 2025 16:11:09 +0000 (18:11 +0200)
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.

include/haproxy/qpack-enc.h
src/h3.c
src/qpack-enc.c

index 10ad1d415e052035b23cb0eddb9beff374156cb3..29136191b8bcb347ecef8eec070d6dc78e5f581d 100644 (file)
@@ -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);
 
index 1ec2ae44060c30963c19e6e54e2cc77aaadc2649..2eb9360ca67cb6af8a959007cf624474364d680d 100644 (file)
--- 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('/')))
index 4aebc0172b81ed2a413728e212e1f1f226b7b538..ba03b7042d40963e707fc992c92e5937d3c3b41c 100644 (file)
@@ -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 <out> 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;
 }