]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-h1: Support C-L/T-E header suppressions when sending messages
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 16 May 2024 15:37:13 +0000 (17:37 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 17 May 2024 14:33:53 +0000 (16:33 +0200)
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.

src/mux_h1.c

index 46ceba439d44facd74f529c1e4d652f41444ee8c..6bdaf7187a1ac8d0078d47799fde331c2705596c 100644 (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)) {