When the H1 connection is in DRAINING state, h1_process() parses the input
data using a stream rxbuf, which limits the amount of data consumed per call
to a buffer minus the reserve. But the input buffer may contain more data
than that. If no more data are received, nothing calls h1_process() again:
the connection only subscribes for reads. The remaining data, the end of the
request payload and possibly the next pipelined request, are then left
unprocessed until the client timeout strikes, which kills the connection and
the pipelined request is lost.
Now, when data are still pending in the input buffer after a parsing pass in
DRAINING state, the parsing is retried.
This was introduced by commit
077906da1 ("MAJOR: mux-h1: Drain requests on
client side before shut a stream down") in 3.0. It must be backported as far
as 3.0.
if (h1s->sess->t_idle == -1)
h1s->sess->t_idle = ns_to_ms(now_ns - h1s->sess->accept_ts) - h1s->sess->t_handshake;
+ retry_parsing:
/* Get the stream rxbuf */
buf = h1_get_rxbuf(h1s);
if (!buf) {
TRACE_ERROR("parsing error detected", H1_EV_H1C_WAKE|H1_EV_H1C_ERR);
}
else {
+ /* When draining, if input data remain to parse, retry the parsing now the rxbuf was emptied */
+ if (h1c->state == H1_CS_DRAINING && (h1s->flags & H1S_F_RX_CONGESTED) && b_data(&h1c->ibuf))
+ goto retry_parsing;
+
TRACE_STATE("Incomplete message, subscribing", H1_EV_RX_DATA|H1_EV_H1C_BLK|H1_EV_H1C_WAKE, h1c->conn, h1s);
h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
}