]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-h1: Don't emit any payload for bodyless responses
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 2 Dec 2020 15:13:22 +0000 (16:13 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 28 Jan 2021 15:37:14 +0000 (16:37 +0100)
Some responses must not contain data. Reponses to HEAD requests and 204/304
xresponses. But there is no warranty that this will be really respected by
the senders or even if it is possible. For instance, the method may be
rewritten by an http-request rule (HEAD->GET). Thus, it is not really
possible to always strip the payload from the response at the receive
stage. And the response may be emitted by an applet or an internal service
not strictly following the spec. All that to say that we may be prepared to
handle payload for bodyless responses on the sending path.

So, thanks to previous patches, it is now possible to know on the sending
path if a response must be bodyless or not. So, for such responses, no
payload is emitted, all HTX blocks after the EOH are silently removed
(including the trailers).

src/mux_h1.c

index 277220b2e765e01f34b8676bbd0e08766082af2b..7b99f1a79c14de45e48496b95ed9aa3db1ef0882 100644 (file)
@@ -1690,7 +1690,12 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                    htx_nbblks(chn_htx) == 1 &&
                    htx_get_blk_type(blk) == HTX_BLK_DATA &&
                    htx_get_blk_value(chn_htx, blk).len == count) {
-                       void *old_area = h1c->obuf.area;
+                       void *old_area;
+
+                       if ((h1m->flags & H1_MF_RESP) && (h1s->flags & H1S_F_BODYLESS_RESP)) {
+                               TRACE_PROTO("Skip data for bodyless response", H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, chn_htx);
+                               goto skip_zero_copy;
+                       }
 
                        TRACE_PROTO("sending message data (zero-copy)", H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, chn_htx, (size_t[]){count});
                        if (h1m->state == H1_MSG_DATA && chn_htx->flags & HTX_FL_EOM) {
@@ -1698,6 +1703,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                last_data = 1;
                        }
 
+                       old_area = h1c->obuf.area;
                        h1c->obuf.area = buf->area;
                        h1c->obuf.head = sizeof(struct htx) + blk->addr;
                        h1c->obuf.data = count;
@@ -1722,7 +1728,6 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                }
                        }
 
-                       total += count;
                        if (h1m->state == H1_MSG_DATA)
                                TRACE_PROTO((!(h1m->flags & H1_MF_RESP) ? "H1 request payload data xferred" : "H1 response payload data xferred"),
                                            H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, 0, (size_t[]){count});
@@ -1730,6 +1735,8 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                TRACE_PROTO((!(h1m->flags & H1_MF_RESP) ? "H1 request tunneled data xferred" : "H1 response tunneled data xferred"),
                                            H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, 0, (size_t[]){count});
 
+                 skip_zero_copy:
+                       total += count;
                        if (last_data) {
                                h1m->state = H1_MSG_DONE;
                                if (h1s->h1c->flags & H1C_F_WAIT_OUTPUT) {
@@ -1878,7 +1885,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                if ((h1s->meth != HTTP_METH_CONNECT &&
                                     (h1m->flags & (H1_MF_VER_11|H1_MF_RESP|H1_MF_CLEN|H1_MF_CHNK|H1_MF_XFER_LEN)) ==
                                     (H1_MF_VER_11|H1_MF_XFER_LEN)) ||
-                                   (h1s->status >= 200 && h1s->status != 204 && h1s->status != 304 && h1s->meth != HTTP_METH_HEAD &&
+                                   (h1s->status >= 200 && !(h1s->flags & H1S_F_BODYLESS_RESP) &&
                                     !(h1s->meth == HTTP_METH_CONNECT && h1s->status >= 200 && h1s->status < 300) &&
                                     (h1m->flags & (H1_MF_VER_11|H1_MF_RESP|H1_MF_CLEN|H1_MF_CHNK|H1_MF_XFER_LEN)) ==
                                     (H1_MF_VER_11|H1_MF_RESP|H1_MF_XFER_LEN))) {
@@ -1935,12 +1942,6 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                        h1s->flags &= ~H1S_F_HAVE_O_CONN;
                                        TRACE_STATE("1xx response xferred", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
                                }
-                               else if ((h1m->flags & H1_MF_RESP) &&  h1s->meth == HTTP_METH_HEAD) {
-                                       if (!chunk_memcat(&tmp, "\r\n", 2))
-                                               goto full;
-                                       TRACE_STATE("HEAD response processed", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
-                                       goto done;
-                               }
                                else {
                                        /* EOM flag is set and it is the last block */
                                        if (htx_is_unique_blk(chn_htx, blk) && (chn_htx->flags & HTX_FL_EOM)) {
@@ -1969,6 +1970,11 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                else if (type != HTX_BLK_DATA)
                                        goto error;
 
+                               if (h1m->state == H1_MSG_DATA && (h1m->flags & H1_MF_RESP) && (h1s->flags & H1S_F_BODYLESS_RESP)) {
+                                       TRACE_PROTO("Skip data for bodyless response", H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, chn_htx);
+                                       break;
+                               }
+
                                TRACE_PROTO("sending message data", H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, chn_htx, (size_t[]){sz});
 
                                /* It is the last block of this message. After this one,
@@ -2035,6 +2041,11 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
                                if (!(h1m->flags & H1_MF_CHNK))
                                        break;
 
+                               if ((h1m->flags & H1_MF_RESP) && (h1s->flags & H1S_F_BODYLESS_RESP)) {
+                                       TRACE_PROTO("Skip trailers for bodyless response", H1_EV_TX_DATA|H1_EV_TX_BODY, h1c->conn, h1s, chn_htx);
+                                       break;
+                               }
+
                                if (type == HTX_BLK_EOT) {
                                        if (!chunk_memcat(&tmp, "\r\n", 2))
                                                goto full;