BUG/MEDIUM: mux-quic: fix EOI for request without payload

When a full message is received for a stream, MUX is responsible to set
EOI flag. This was done through rcv_buf stream callback by checking if
QCS HTX buffer contained the EOM flag.

This is not correct for HTTP without body. In this case, QCS HTX buffer
is never used. Only a local HTX buffer is used to transfer headers just
as stream endpoint is created. As such, EOI is never transmitted to the
upper layer.

If the transfer occur without any issue, this does not seem to cause any
problem. However, in case the transfer is aborted, the stream is never
released which cause a memory leak and prevent the process soft-stop.

To fix this, also check if EOM is put by application layer during
headers conversion. If true, this is transferred through a new argument
to qc_attach_sc() MUX function which is responsible to set the EOI flag.

This issue was reproduced using h2load with hundred of connections.
h2load is interrupted with a SIGINT which causes streams to never be
closed on haproxy side.

This should be backported up to 2.6.
This commit is contained in:
Amaury Denoyelle 2023-05-12 18:16:31 +02:00
parent 1a2faef92f
commit bf86d89ea6
3 changed files with 9 additions and 4 deletions

View File

@ -680,7 +680,7 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
htx_to_buf(htx, &htx_buf);
htx = NULL;
if (!qc_attach_sc(qcs, &htx_buf)) {
if (!qc_attach_sc(qcs, &htx_buf, fin)) {
h3c->err = H3_INTERNAL_ERROR;
len = -1;
goto out;

View File

@ -88,7 +88,7 @@ static ssize_t hq_interop_decode_qcs(struct qcs *qcs, struct buffer *b, int fin)
htx_add_endof(htx, HTX_BLK_EOH);
htx_to_buf(htx, &htx_buf);
if (!qc_attach_sc(qcs, &htx_buf))
if (!qc_attach_sc(qcs, &htx_buf, fin))
return -1;
b_free(&htx_buf);

View File

@ -639,7 +639,7 @@ static struct qcs *qcc_init_stream_remote(struct qcc *qcc, uint64_t id)
return NULL;
}
struct stconn *qc_attach_sc(struct qcs *qcs, struct buffer *buf)
struct stconn *qc_attach_sc(struct qcs *qcs, struct buffer *buf, char fin)
{
struct qcc *qcc = qcs->qcc;
struct session *sess = qcc->conn->owner;
@ -679,6 +679,11 @@ struct stconn *qc_attach_sc(struct qcs *qcs, struct buffer *buf)
BUG_ON_HOT(!LIST_INLIST(&qcs->el_opening));
LIST_DEL_INIT(&qcs->el_opening);
if (fin) {
TRACE_STATE("report end-of-input", QMUX_EV_STRM_RECV, qcc->conn, qcs);
se_fl_set(qcs->sd, SE_FL_EOI);
}
return qcs->sd->sc;
}
@ -2717,7 +2722,7 @@ static size_t qc_recv_buf(struct stconn *sc, struct buffer *buf,
else {
se_fl_clr(qcs->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
/* Set end-of-input if FIN received and all data extracted. */
/* Set end-of-input when full message properly received. */
if (fin) {
TRACE_STATE("report end-of-input", QMUX_EV_STRM_RECV, qcc->conn, qcs);
se_fl_set(qcs->sd, SE_FL_EOI);