From: Christopher Faulet Date: Thu, 25 Jan 2024 14:00:10 +0000 (+0100) Subject: MEDIUM: mux-h1: Support zero-copy forwarding for chunks with an unknown size X-Git-Tag: v3.0-dev3~43 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91b77c1632;p=thirdparty%2Fhaproxy.git MEDIUM: mux-h1: Support zero-copy forwarding for chunks with an unknown size Till now, for chunked messages, the H1 mux used the size requested during the zero-copy forwarding negotiation as the chunk size. And till now, this was accurate because the requested size was indeed the chunk size on the producer side. But this will be a problem to implement the zero-copy forwarding on some applets because the content size is not known during the nego but only when it is produced. Thanks to previous patches, it is now possible to know the requested size is not exact and we are able to reserve a larger space to write the chunk size later, in h1_done_ff(), with some padding. --- diff --git a/src/mux_h1.c b/src/mux_h1.c index 6f6b95e060..96701d614e 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -4450,7 +4450,7 @@ static size_t h1_nego_ff(struct stconn *sc, struct buffer *input, size_t count, struct h1s *h1s = __sc_mux_strm(sc); struct h1c *h1c = h1s->h1c; struct h1m *h1m = (!(h1c->flags & H1C_F_IS_BACK) ? &h1s->res : &h1s->req); - size_t ret = 0; + size_t sz, offset = 0, ret = 0; TRACE_ENTER(H1_EV_STRM_SEND, h1c->conn, h1s, 0, (size_t[]){count}); @@ -4487,9 +4487,18 @@ static size_t h1_nego_ff(struct stconn *sc, struct buffer *input, size_t count, h1m->curr_len = count; } else { - /* XXX: Unsupported for now but will be added soon ! */ - h1s->sd->iobuf.flags |= IOBUF_FL_NO_FF; - goto out; + /* The producer does not know the chunk size, thus this will be emitted at the + * end, in done_ff(). So splicing cannot be used (see TODO below). + * We will reserve 12 bytes to handle at most 4Go chunk with all CRLFs ! + * (<8-bytes SIZE>) + */ + if (count > MAX_RANGE(unsigned int)) + count = MAX_RANGE(unsigned int); + offset = 12; + /* Add 2 more bytes to finish the previous chunk */ + if (h1m->state == H1_MSG_CHUNK_CRLF) + offset += 2; + goto no_splicing; } } } @@ -4514,6 +4523,7 @@ static size_t h1_nego_ff(struct stconn *sc, struct buffer *input, size_t count, TRACE_DEVEL("Unable to allocate pipe for splicing, fallback to buffer", H1_EV_STRM_SEND, h1c->conn, h1s); } + no_splicing: if (!h1_get_buf(h1c, &h1c->obuf)) { h1c->flags |= H1C_F_OUT_ALLOC; TRACE_STATE("waiting for opposite h1c obuf allocation", H1_EV_STRM_SEND|H1_EV_H1S_BLK, h1c->conn, h1s); @@ -4523,13 +4533,11 @@ static size_t h1_nego_ff(struct stconn *sc, struct buffer *input, size_t count, if (b_space_wraps(&h1c->obuf)) b_slow_realign(&h1c->obuf, trash.area, b_data(&h1c->obuf)); - h1s->sd->iobuf.buf = &h1c->obuf; - h1s->sd->iobuf.offset = 0; - h1s->sd->iobuf.data = 0; /* Cannot forward more than available room in output buffer */ - if (count > b_room(&h1c->obuf)) - count = b_room(&h1c->obuf); + sz = b_contig_space(&h1c->obuf) - offset; + if (count > sz) + count = sz; if (!count) { h1c->flags |= H1C_F_OUT_FULL; @@ -4538,6 +4546,10 @@ static size_t h1_nego_ff(struct stconn *sc, struct buffer *input, size_t count, goto out; } + h1s->sd->iobuf.buf = &h1c->obuf; + h1s->sd->iobuf.offset = offset; + h1s->sd->iobuf.data = 0; + /* forward remaining input data */ if (b_data(input)) { size_t xfer = count; @@ -4584,6 +4596,16 @@ static size_t h1_done_ff(struct stconn *sc) if (b_room(&h1c->obuf) == sd->iobuf.offset) h1c->flags |= H1C_F_OUT_FULL; + if (sd->iobuf.offset) { + b_add(&h1c->obuf, sd->iobuf.offset); + b_del(&h1c->obuf, sd->iobuf.offset); + h1_prepend_chunk_size(&h1c->obuf, sd->iobuf.data, sd->iobuf.offset - ((h1m->state == H1_MSG_CHUNK_CRLF) ? 4 : 2)); + h1_append_chunk_crlf(&h1c->obuf); + if (h1m->state == H1_MSG_CHUNK_CRLF) + h1_prepend_chunk_crlf(&h1c->obuf); + h1m->state = H1_MSG_CHUNK_SIZE; + } + total = sd->iobuf.data; sd->iobuf.buf = NULL; sd->iobuf.offset = 0;