]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: support hq-interop
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 12 Nov 2021 15:09:54 +0000 (16:09 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 18 Nov 2021 09:50:58 +0000 (10:50 +0100)
Implement a new app_ops layer for quic interop. This layer uses HTTP/0.9
on top of QUIC. Implementation is minimal, with the intent to be able to
pass interoperability test suite from
https://github.com/marten-seemann/quic-interop-runner.

It is instantiated if the negotiated ALPN is "hq-interop".

Makefile
include/haproxy/hq_interop.h [new file with mode: 0644]
src/hq_interop.c [new file with mode: 0644]
src/xprt_quic.c

index f5dfed238228e11f34b46a31ddbf0f79afadeb69..c7d6ee98d9a59387b7fc0b64bda61dffb5f01ac4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -594,7 +594,8 @@ endif
 ifneq ($(USE_QUIC),)
 OPTIONS_OBJS += src/quic_sock.o src/proto_quic.o src/xprt_quic.o src/quic_tls.o \
                 src/quic_frame.o src/quic_cc.o src/quic_cc_newreno.o src/mux_quic.o \
-                src/cbuf.o src/qpack-dec.o src/qpack-tbl.o src/h3.o src/qpack-enc.o
+                src/cbuf.o src/qpack-dec.o src/qpack-tbl.o src/h3.o src/qpack-enc.o \
+                src/hq_interop.o
 endif
 
 ifneq ($(USE_LUA),)
diff --git a/include/haproxy/hq_interop.h b/include/haproxy/hq_interop.h
new file mode 100644 (file)
index 0000000..eb6ebf6
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _HAPROXY_HQ_INTEROP_H_
+#define _HAPROXY_HQ_INTEROP_H_
+
+extern const struct qcc_app_ops hq_interop_ops;
+
+#endif /* _HAPROXY_HQ_INTEROP_H_ */
diff --git a/src/hq_interop.c b/src/hq_interop.c
new file mode 100644 (file)
index 0000000..0f878f0
--- /dev/null
@@ -0,0 +1,132 @@
+#include <haproxy/hq_interop.h>
+
+#include <import/ist.h>
+#include <haproxy/buf.h>
+#include <haproxy/connection.h>
+#include <haproxy/dynbuf.h>
+#include <haproxy/htx.h>
+#include <haproxy/http.h>
+#include <haproxy/mux_quic-t.h>
+#include <haproxy/stream.h>
+
+static int hq_interop_decode_qcs(struct qcs *qcs, void *ctx)
+{
+       struct buffer *rxbuf = &qcs->rx.buf;
+       struct htx *htx;
+       struct htx_sl *sl;
+       struct conn_stream *cs;
+       struct buffer htx_buf = BUF_NULL;
+       struct ist path;
+       char *ptr;
+
+       b_alloc(&htx_buf);
+       htx = htx_from_buf(&htx_buf);
+
+       /* skip method */
+       ptr = b_orig(rxbuf);
+       while (HTTP_IS_TOKEN(*ptr))
+               ++ptr;
+       BUG_ON(!HTTP_IS_SPHT(*ptr));
+       ++ptr;
+
+       /* extract path */
+       BUG_ON(HTTP_IS_LWS(*ptr));
+       path.ptr = ptr;
+       while (!HTTP_IS_LWS(*ptr))
+               ++ptr;
+       BUG_ON(!HTTP_IS_LWS(*ptr));
+       path.len = ptr - path.ptr;
+
+       sl = htx_add_stline(htx, HTX_BLK_REQ_SL, 0, ist("GET"), path, ist("HTTP/1.0"));
+       sl->flags |= HTX_SL_F_BODYLESS;
+       sl->info.req.meth = find_http_meth("GET", 3);
+
+       htx_add_endof(htx, HTX_BLK_EOH);
+       htx_to_buf(htx, &htx_buf);
+
+       cs = cs_new(qcs->qcc->conn, qcs->qcc->conn->target);
+       cs->ctx = qcs;
+       stream_create_from_cs(cs, &htx_buf);
+
+       b_del(rxbuf, b_data(rxbuf));
+       b_free(&htx_buf);
+
+       return 0;
+}
+
+static struct buffer *mux_get_buf(struct qcs *qcs)
+{
+       if (!b_size(&qcs->tx.buf))
+               b_alloc(&qcs->tx.buf);
+
+       return &qcs->tx.buf;
+}
+
+static size_t hq_interop_snd_buf(struct conn_stream *cs, struct buffer *buf,
+                                 size_t count, int flags)
+{
+       struct qcs *qcs = cs->ctx;
+       struct htx *htx;
+       enum htx_blk_type btype;
+       struct htx_blk *blk;
+       int32_t idx;
+       uint32_t bsize, fsize;
+       struct buffer *res, outbuf;
+       size_t total = 0;
+
+       htx = htx_from_buf(buf);
+       res = mux_get_buf(qcs);
+       outbuf = b_make(b_tail(res), b_contig_space(res), 0, 0);
+
+       while (count && !htx_is_empty(htx) && !(qcs->flags & QC_SF_BLK_MROOM)) {
+               /* Not implemented : QUIC on backend side */
+               idx = htx_get_head(htx);
+               blk = htx_get_blk(htx, idx);
+               btype = htx_get_blk_type(blk);
+               fsize = bsize = htx_get_blksz(blk);
+
+               BUG_ON(btype == HTX_BLK_REQ_SL);
+
+               switch (btype) {
+               case HTX_BLK_DATA:
+                       if (fsize > count)
+                               fsize = count;
+                       b_putblk(&outbuf, htx_get_blk_ptr(htx, blk), fsize);
+                       total += fsize;
+                       count -= fsize;
+
+                       if (fsize == bsize)
+                               htx_remove_blk(htx, blk);
+                       else
+                               htx_cut_data_blk(htx, blk, fsize);
+                       break;
+
+               /* only body is transfered on HTTP/0.9 */
+               case HTX_BLK_RES_SL:
+               case HTX_BLK_TLR:
+               case HTX_BLK_EOT:
+               default:
+                       htx_remove_blk(htx, blk);
+                       total += bsize;
+                       count -= bsize;
+                       break;
+               }
+       }
+
+       if ((htx->flags & HTX_FL_EOM) && htx_is_empty(htx))
+               qcs->flags |= QC_SF_FIN_STREAM;
+
+       b_add(res, b_data(&outbuf));
+
+       if (total) {
+               if (!(qcs->qcc->wait_event.events & SUB_RETRY_SEND))
+                       tasklet_wakeup(qcs->qcc->wait_event.tasklet);
+       }
+
+       return total;
+}
+
+const struct qcc_app_ops hq_interop_ops = {
+       .decode_qcs = hq_interop_decode_qcs,
+       .snd_buf    = hq_interop_snd_buf,
+};
index d77540a7b696978dcb9c2c3b01133b0528a4fb19..35afeaaf0e8e6827a0422a6ae25b42718631aa81 100644 (file)
@@ -36,6 +36,7 @@
 #include <haproxy/freq_ctr.h>
 #include <haproxy/global.h>
 #include <haproxy/h3.h>
+#include <haproxy/hq_interop.h>
 #include <haproxy/log.h>
 #include <haproxy/mux_quic.h>
 #include <haproxy/pipe.h>
@@ -1670,6 +1671,9 @@ static inline int qc_provide_cdata(struct quic_enc_level *el,
                if (!qc->qcc->app_ops->init(qc->qcc))
                        goto err;
        }
+       else if (alpn_len >= 10 && memcmp(alpn, "hq-interop", 10) == 0) {
+               qc->qcc->app_ops = &hq_interop_ops;
+       }
        else {
                /* TODO RFC9001 8.1. Protocol Negotiation
                 * must return no_application_protocol TLS alert