On QUIC MUX, <nb_hreq> counter serves to account for the currently
active requests in progress. While this number is non null, the
connection is considered active and cannot be released. The counter is
decremented only when the QCS instance is finally freed. This guarantees
that no truncated request could happen.
An issue happened on RESET_STREAM frame reception as the first frame for
a new QCS instance. In this case, <nb_hreq> is incremented. No progress
will occur as the upper stream layer is not instantiated, and QCS
instance will remains half-closed, which prevents its liberation.
The first issue is that it causes a leak of QCS during the connection
lifetime. Worst, this may prevent the whole connection closure even if
idle timeout as fired : this is because qcc_is_dead() checks <nb_hreq>
first before regarding transport layer status. Note that this only
happens if QUIC MUX timeout is not yet activated, which is the case if
qcc_io_cb() has not yet been executed.
This patch fixes this situation by improving RESET_STREAM receive
handler. Now, an extra check is performed to detect if the frame has
been received before the upper stream layer init. If true, QCS instance
is closed locally and immediately scheduled for purge, which ensures
that <nb_hreq> counter will be decremented as expected.
This must be backported up to 2.8.
/* Remove stream from recv_list if present. */
LIST_DEL_INIT(&qcs->el_recv);
+ /* Check if RESET_STREAM received before stream layer initialization.
+ * If true, prepare QCS purgeing immediately.
+ */
+ if (!qcs_sc(qcs) && !(qcs->flags & QC_SF_DETACH)) {
+ qcs_close_local(qcs);
+
+ BUG_ON(LIST_INLIST(&qcs->el_send));
+ TRACE_STATE("add stream in purg_list", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs);
+ LIST_APPEND(&qcs->qcc->purg_list, &qcs->el_send);
+ tasklet_wakeup(qcs->qcc->wait_event.tasklet);
+ }
+
out:
if (qcc->glitches != prev_glitches && !(qcc->flags & QC_CF_IS_BACK))
session_add_glitch_ctr(qcc->conn->owner, qcc->glitches - prev_glitches);