MEDIUM: mux-h1: Support C-L/T-E header suppressions when sending messages

During the 2.9 dev cycle, to be able to support zero-copy data forwarding, a
change on the H1 mux was performed to ignore the headers modifications about
payload representation (Content-Length and Transfer-Encoding headers).

It appears there are some use-cases where it could be handy to change values
of these headers or just remove them. For instance, we can imagine to remove
these headers on a server response to force the old HTTP/1.0 close mode
behavior. So thaks to this patch, the rules are relaxed. It is now possible
to remove these headers. When this happens, the following rules are applied:

 * If "Content-Length" header is removed but a "Transfer-Encoding: chunked"
   header is found, no special processing is performed. The message remains
   chunked. However the close mode is not forced.

 * If "Transfer-Encoding" header is removed but a "Content-Length" header is
   found, no special processing is performed. The payload length must comply
   to the specified content length.

 * If one of them is removed and the other one is not found, a response is
   switch the close mode and a "Content-Length: 0" header is forced on a
   request.

With these rules, we fit the best to the user expectations.

This patch depends on the following commit:

  * MINOR: mux-h1: Add a flag to ignore the request payload

This patch should fix the issue #2536. It should be backported it to 2.9
with the commit above.
This commit is contained in:
Christopher Faulet 2024-05-16 17:37:13 +02:00
parent 1a2699d5f7
commit 2fc9e6fa39

View File

@ -2576,6 +2576,44 @@ static size_t h1_make_eoh(struct h1s *h1s, struct h1m *h1m, struct htx *htx, siz
b_slow_realign(&h1c->obuf, trash.area, b_data(&h1c->obuf));
outbuf = b_make(b_tail(&h1c->obuf), b_contig_space(&h1c->obuf), 0, 0);
/* Deal with removed "Content-Length" or "Transfer-Encoding" headers during analysis */
if (((h1m->flags & H1_MF_CLEN) && !(h1s->flags & H1S_F_HAVE_CLEN))||
((h1m->flags & H1_MF_CHNK) && !(h1s->flags & H1S_F_HAVE_CHNK))) {
TRACE_STATE("\"Content-Length\" or \"Transfer-Encoding\" header removed during analysis", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
if (h1s->flags & (H1S_F_HAVE_CLEN|H1S_F_HAVE_CHNK)) {
/* At least on header is present, we can continue */
if (!(h1s->flags & H1S_F_HAVE_CLEN)) {
h1m->curr_len = h1m->body_len = 0;
h1m->flags &= ~H1_MF_CLEN;
}
else /* h1s->flags & H1S_F_HAVE_CHNK */
h1m->flags &= ~(H1_MF_XFER_ENC|H1_MF_CHNK);
}
else {
/* Both headers are missing */
if (h1m->flags & H1_MF_RESP) {
/* It is a esponse: Switch to unknown xfer length */
h1m->flags &= ~(H1_MF_XFER_LEN|H1_MF_XFER_ENC|H1_MF_CLEN|H1_MF_CHNK);
h1s->flags &= ~(H1S_F_HAVE_CLEN|H1S_F_HAVE_CHNK);
TRACE_STATE("Switch response to unknown XFER length", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
}
else {
/* It is the request: Add "Content-Length: 0" header and skip payload */
struct ist n = ist("content-length");
if (h1c->px->options2 & (PR_O2_H1_ADJ_BUGCLI|PR_O2_H1_ADJ_BUGSRV))
h1_adjust_case_outgoing_hdr(h1s, h1m, &n);
if (!h1_format_htx_hdr(n, ist("0"), &outbuf))
goto full;
h1m->flags = (h1m->flags & ~(H1_MF_XFER_ENC|H1_MF_CHNK)) | H1_MF_CLEN;
h1s->flags = (h1s->flags & ~H1S_F_HAVE_CHNK) | (H1S_F_HAVE_CLEN|H1S_F_BODYLESS_REQ);
h1m->curr_len = h1m->body_len = 0;
TRACE_STATE("Set request content-length to 0 and skip payload", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
}
}
}
/* Deal with "Connection" header */
if (!(h1s->flags & H1S_F_HAVE_O_CONN)) {
if ((h1m->flags & (H1_MF_XFER_ENC|H1_MF_CLEN)) == (H1_MF_XFER_ENC|H1_MF_CLEN)) {
@ -2626,23 +2664,6 @@ static size_t h1_make_eoh(struct h1s *h1s, struct h1m *h1m, struct htx *htx, siz
h1s->flags |= H1S_F_HAVE_CHNK;
}
/* Deal with "Content-Length header */
if ((h1m->flags & H1_MF_CLEN) && !(h1s->flags & H1S_F_HAVE_CLEN)) {
char *end;
h1m->curr_len = h1m->body_len = htx->data + htx->extra - sz;
end = DISGUISE(ulltoa(h1m->body_len, trash.area, b_size(&trash)));
n = ist("content-length");
v = ist2(trash.area, end-trash.area);
if (h1c->px->options2 & (PR_O2_H1_ADJ_BUGCLI|PR_O2_H1_ADJ_BUGSRV))
h1_adjust_case_outgoing_hdr(h1s, h1m, &n);
if (!h1_format_htx_hdr(n, v, &outbuf))
goto full;
TRACE_STATE("add \"Content-Length: <LEN>\"", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
h1s->flags |= H1S_F_HAVE_CLEN;
}
/* Add the server name to a header (if requested) */
if (!(h1s->flags & H1S_F_HAVE_SRV_NAME) &&
!(h1m->flags & H1_MF_RESP) && isttest(h1c->px->server_id_hdr_name)) {