From 8d818c6eabf71f45d5cd46e136b60bcb4dde50d9 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Tue, 2 Aug 2022 11:32:45 +0200 Subject: [PATCH] MINOR: h3: support HTTP request framing state Store the current step of HTTP message in h3s stream. This reports if we are in the parsing of headers, content or trailers section. A new enum h3s_st_req is defined for this. This field is stored in h3s struct but only used for request stream. It is left undefined for other streams (control or QPACK streams). h3_is_frame_valid() has been extended to take into account this state information. A connection error H3_FRAME_UNEXPECTED is reported if an invalid frame according to the current state is received; for example a DATA frame at the beginning of a stream. --- include/haproxy/h3.h | 8 ++++++++ src/h3.c | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/haproxy/h3.h b/include/haproxy/h3.h index 5953da614..504e9bfed 100644 --- a/include/haproxy/h3.h +++ b/include/haproxy/h3.h @@ -101,6 +101,14 @@ enum h3s_t { H3S_T_UNKNOWN }; +/* State for request streams */ +enum h3s_st_req { + H3S_ST_REQ_BEFORE = 0, /* initial state */ + H3S_ST_REQ_HEADERS, /* header section received */ + H3S_ST_REQ_DATA, /* first DATA frame for content received */ + H3S_ST_REQ_TRAILERS, /* trailer section received */ +}; + extern const struct qcc_app_ops h3_ops; size_t h3_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, int flags); diff --git a/src/h3.c b/src/h3.c index bda99cd2c..f00281e8d 100644 --- a/src/h3.c +++ b/src/h3.c @@ -138,6 +138,7 @@ struct h3s { struct h3c *h3c; enum h3s_t type; + enum h3s_st_req st_req; /* only used for request streams */ int demux_frame_len; int demux_frame_type; @@ -284,8 +285,11 @@ static int h3_is_frame_valid(struct h3c *h3c, struct qcs *qcs, uint64_t ftype) switch (ftype) { case H3_FT_DATA: + return h3s->type != H3S_T_CTRL && (h3s->st_req == H3S_ST_REQ_HEADERS || + h3s->st_req == H3S_ST_REQ_DATA); + case H3_FT_HEADERS: - return h3s->type != H3S_T_CTRL; + return h3s->type != H3S_T_CTRL && h3s->st_req != H3S_ST_REQ_TRAILERS; case H3_FT_CANCEL_PUSH: case H3_FT_GOAWAY: @@ -344,6 +348,8 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf, TRACE_ENTER(H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs); + /* TODO support trailer parsing in this function */ + /* TODO support buffer wrapping */ BUG_ON(b_head(buf) + len >= b_wrap(buf)); ret = qpack_decode_fs((const unsigned char *)b_head(buf), len, tmp, @@ -673,6 +679,7 @@ static ssize_t h3_decode_qcs(struct qcs *qcs, struct buffer *b, int fin) ret = h3_data_to_htx(qcs, b, flen, last_stream_frame); /* TODO handle error reporting. Stream closure required. */ if (ret < 0) { ABORT_NOW(); } + h3s->st_req = H3S_ST_REQ_DATA; break; case H3_FT_HEADERS: ret = h3_headers_to_htx(qcs, b, flen, last_stream_frame); @@ -684,6 +691,8 @@ static ssize_t h3_decode_qcs(struct qcs *qcs, struct buffer *b, int fin) qcc_emit_cc_app(qcs->qcc, h3c->err, 1); return -1; } + h3s->st_req = (h3s->st_req == H3S_ST_REQ_BEFORE) ? + H3S_ST_REQ_HEADERS : H3S_ST_REQ_TRAILERS; break; case H3_FT_CANCEL_PUSH: case H3_FT_PUSH_PROMISE: @@ -1070,6 +1079,7 @@ static int h3_attach(struct qcs *qcs, void *conn_ctx) if (quic_stream_is_bidi(qcs->id)) { h3s->type = H3S_T_REQ; + h3s->st_req = H3S_ST_REQ_BEFORE; } else { /* stream type must be decoded for unidirectional streams */