When a fixed-length message completes while curr_len is still non-zero,
fewer bytes than announced were sent and the peer keeps waiting, so
reusing the connection desynchronizes the next request (request
smuggling). In h1s_destroy(), when either the request or the response
still expects data, flag the connection on error, unless the message is
legitimately bodyless. It is then closed instead of being returned to
the idle pool.
This can be reproduced with an H2 frontend, that will send less data
than announced before sending an end of stream, to an H1 backend that
replies before it received the whole request.
This should be backported up to 2.8.
Many thanks to truff@projet7.org for reporting the issue and providing
an initial fix.
H1C_F_IN_SALLOC|H1C_F_IN_SMAYALLOC|
H1C_F_CO_MSG_MORE|H1C_F_CO_STREAMER);
+ /*
+ * If we reached the end of the stream and sent less bits than
+ * announced in Content-Length, we have little choice but to
+ * close the connection, as the peer will expect more.
+ */
+ if (((h1s->req.flags & H1_MF_CLEN) && h1s->req.curr_len && !(h1s->flags & H1S_F_BODYLESS_REQ)) ||
+ ((h1s->res.flags & H1_MF_CLEN) && h1s->res.curr_len && !(h1s->flags & H1S_F_BODYLESS_RESP)))
+ h1c->flags |= H1C_F_ERROR;
+
if (!(h1c->flags & (H1C_F_EOS|H1C_F_ERR_PENDING|H1C_F_ERROR|H1C_F_ABRT_PENDING|H1C_F_ABRTED)) && /* No error/read0/abort */
h1_is_alive(h1c) && /* still alive */
(h1s->flags & H1S_F_WANT_KAL) && /* K/A possible */