MINOR: quic: support hq-interop
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".
This commit is contained in:
parent
71e588c8a7
commit
154bc7f864
3
Makefile
3
Makefile
|
@ -594,7 +594,8 @@ endif
|
||||||
ifneq ($(USE_QUIC),)
|
ifneq ($(USE_QUIC),)
|
||||||
OPTIONS_OBJS += src/quic_sock.o src/proto_quic.o src/xprt_quic.o src/quic_tls.o \
|
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/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
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_LUA),)
|
ifneq ($(USE_LUA),)
|
||||||
|
|
|
@ -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_ */
|
|
@ -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,
|
||||||
|
};
|
|
@ -36,6 +36,7 @@
|
||||||
#include <haproxy/freq_ctr.h>
|
#include <haproxy/freq_ctr.h>
|
||||||
#include <haproxy/global.h>
|
#include <haproxy/global.h>
|
||||||
#include <haproxy/h3.h>
|
#include <haproxy/h3.h>
|
||||||
|
#include <haproxy/hq_interop.h>
|
||||||
#include <haproxy/log.h>
|
#include <haproxy/log.h>
|
||||||
#include <haproxy/mux_quic.h>
|
#include <haproxy/mux_quic.h>
|
||||||
#include <haproxy/pipe.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))
|
if (!qc->qcc->app_ops->init(qc->qcc))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
else if (alpn_len >= 10 && memcmp(alpn, "hq-interop", 10) == 0) {
|
||||||
|
qc->qcc->app_ops = &hq_interop_ops;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
/* TODO RFC9001 8.1. Protocol Negotiation
|
/* TODO RFC9001 8.1. Protocol Negotiation
|
||||||
* must return no_application_protocol TLS alert
|
* must return no_application_protocol TLS alert
|
||||||
|
|
Loading…
Reference in New Issue