MEDIUM: mux-h1: Don't emit any payload for bodyless responses
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).
diff --git a/src/mux_h1.c b/src/mux_h1.c
index 277220b..7b99f1a 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -1690,7 +1690,12 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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;