]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: h3: encode htx headers to QPACK
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 24 Aug 2021 14:20:27 +0000 (16:20 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 23 Sep 2021 13:27:25 +0000 (15:27 +0200)
src/h3.c

index 194d57f2f48a56300b3e2a4e331190656151817d..a73b8c1eda3c6654392e302157163f80e0121994 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -26,6 +26,8 @@
 #include <haproxy/mux_quic.h>
 #include <haproxy/pool.h>
 #include <haproxy/qpack-dec.h>
+#include <haproxy/qpack-enc.h>
+#include <haproxy/quic_enc.h>
 #include <haproxy/stream.h>
 #include <haproxy/tools.h>
 #include <haproxy/xprt_quic.h>
@@ -435,6 +437,103 @@ static struct buffer *get_mux_next_tx_buf(struct qcs *qcs)
        return buf;
 }
 
+static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
+{
+       struct buffer outbuf;
+       struct buffer headers_buf = BUF_NULL;
+       struct buffer *res;
+       struct http_hdr list[global.tune.max_http_hdr];
+       struct htx_sl *sl;
+       struct htx_blk *blk;
+       enum htx_blk_type type;
+       int frame_length_size;  /* size in bytes of frame length varint field */
+       int ret = 0;
+       int hdr;
+       int status = 0;
+
+       sl = NULL;
+       hdr = 0;
+       for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
+               type = htx_get_blk_type(blk);
+
+               if (type == HTX_BLK_UNUSED)
+                       continue;
+
+               if (type == HTX_BLK_EOH)
+                       break;
+
+               if (type == HTX_BLK_RES_SL) {
+                       /* start-line -> HEADERS h3 frame */
+                       BUG_ON(sl);
+                       sl = htx_get_blk_ptr(htx, blk);
+                       /* TODO should be on h3 layer */
+                       status = sl->info.res.status;
+               }
+               else if (type == HTX_BLK_HDR) {
+                       list[hdr].n = htx_get_blk_name(htx, blk);
+                       list[hdr].v = htx_get_blk_value(htx, blk);
+                       hdr++;
+               }
+               else {
+                       ABORT_NOW();
+                       goto err;
+               }
+       }
+
+       BUG_ON(!sl);
+
+       list[hdr].n = ist("");
+
+       res = get_mux_next_tx_buf(qcs);
+
+       /* At least 5 bytes to store frame type + length as a varint max size */
+       if (b_room(res) < 5)
+               ABORT_NOW();
+
+       b_reset(&outbuf);
+       outbuf = b_make(b_tail(res), b_contig_space(res), 0, 0);
+       /* Start the headers after frame type + length */
+       headers_buf = b_make(b_head(res) + 5, b_size(res) - 5, 0, 0);
+
+       if (qpack_encode_field_section_line(&headers_buf))
+               ABORT_NOW();
+       if (qpack_encode_int_status(&headers_buf, status))
+               ABORT_NOW();
+
+       for (hdr = 0; hdr < sizeof(list) / sizeof(list[0]); ++hdr) {
+               if (isteq(list[hdr].n, ist("")))
+                       break;
+
+               if (qpack_encode_header(&headers_buf, list[hdr].n, list[hdr].v))
+                       ABORT_NOW();
+       }
+
+       /* Now that all headers are encoded, we are certain that res buffer is
+        * big enough
+        */
+       frame_length_size = quic_int_getsize(b_data(&headers_buf));
+       res->head += 4 - frame_length_size;
+       b_putchr(res, 0x01); /* h3 HEADERS frame type */
+       if (!b_quic_enc_int(res, b_data(&headers_buf)))
+               ABORT_NOW();
+       b_add(res, b_data(&headers_buf));
+
+       ret = 0;
+       blk = htx_get_head_blk(htx);
+       while (blk) {
+               type = htx_get_blk_type(blk);
+               ret += htx_get_blksz(blk);
+               blk = htx_remove_blk(htx, blk);
+               if (type == HTX_BLK_EOH)
+                       break;
+       }
+
+       return ret;
+
+ err:
+       return 0;
+}
+
 size_t h3_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t count, int flags)
 {
        size_t total = 0;
@@ -459,7 +558,15 @@ size_t h3_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t count, int
 
                switch (btype) {
                case HTX_BLK_RES_SL:
-                       /* TODO HEADERS h3 frame */
+                       /* start-line -> HEADERS h3 frame */
+                       ret = h3_resp_headers_send(qcs, htx);
+                       if (ret > 0) {
+                               total += ret;
+                               count -= ret;
+                               if (ret < bsize)
+                                       goto out;
+                       }
+                       break;
 
                case HTX_BLK_DATA:
                        /* TODO DATA h3 frame */