int qcc_install_app_ops(struct qcc *qcc);
-/* Register <qcs> stream for http-request timeout. If the stream is not yet
- * attached in the configured delay, qcc timeout task will be triggered. This
- * means the full header section was not received in time.
+/* Flags <qcs> as a request stream. The connection will be considered as active
+ * until all request streams are closed or on inactivity timeout. On the
+ * frontend side, http-request timeout will be applied on the stream to ensure
+ * headers are received in time.
*
* This function should be called by the application protocol layer on request
* streams initialization.
{
struct qcc *qcc = qcs->qcc;
+ /* For frontend connections, register the stream in QCC opening_list.
+ * This is necessary for http-request timeout.
+ */
if (!conn_is_back(qcc->conn)) {
/* A stream cannot be registered several times. */
BUG_ON_HOT(tick_isset(qcs->start));
*/
LIST_APPEND(&qcc->opening_list, &qcs->el_opening);
}
+
+ /* Ensure flag is only set once per stream to avoid nb_hreq counter wrapping. */
+ BUG_ON_HOT(qcs->flags & QC_SF_HREQ_RECV);
+ qcs->flags |= QC_SF_HREQ_RECV;
+ ++qcc->nb_hreq;
}
void qcc_show_quic(struct qcc *qcc);
sedesc_free(qcs->sd);
qcs->sd = NULL;
+ if (qcs->flags & QC_SF_HREQ_RECV) {
+ BUG_ON(!qcc->nb_hreq);
+ --qcc->nb_hreq;
+ }
+
/* Release app-layer context. */
if (qcs->ctx && qcc->app_ops->detach)
qcc->app_ops->detach(qcs);
{
BUG_ON(!qcc->nb_sc); /* Ensure sc count is always valid (ie >=0). */
--qcc->nb_sc;
-
- /* Reset qcc idle start for http-keep-alive timeout. Timeout will be
- * refreshed after this on stream detach.
- */
- if (!conn_is_back(qcc->conn) && qcc_may_expire(qcc) && !qcc->nb_hreq)
- qcc_reset_idle_start(qcc);
-}
-
-/* Decrement <qcc> hreq. */
-static forceinline void qcc_rm_hreq(struct qcc *qcc)
-{
- BUG_ON(!qcc->nb_hreq); /* Ensure http req count is always valid (ie >=0). */
- --qcc->nb_hreq;
-
- /* Reset qcc idle start for http-keep-alive timeout. Timeout will be
- * refreshed after this on I/O handler.
- */
- if (!conn_is_back(qcc->conn) && qcc_may_expire(qcc) && !qcc->nb_hreq)
- qcc_reset_idle_start(qcc);
}
static inline int qcc_is_dead(const struct qcc *qcc)
{
- /* Maintain connection if stream endpoints are still active. */
- if (qcc->nb_sc)
+ /* Maintain connection if there is still request streams active. */
+ if (qcc->nb_hreq)
return 0;
/* Connection considered dead if either :
* - remote error detected at transport level
* - error detected locally
* - MUX timeout expired
- * - app layer shut and all transfers done (FE side only - used for stream.max-total)
+ * - app layer shut (FE side only - used for stream.max-total)
* - new stream initiating definitely blocked (BE side only - used for H3 GOAWAY reception)
*/
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL_DONE) ||
!qcc->task ||
- (!conn_is_back(qcc->conn) && !qcc->nb_hreq && qcc->app_st == QCC_APP_ST_SHUT) ||
- (conn_is_back(qcc->conn) && !qcc->nb_hreq && (qcc->flags & QC_CF_CONN_SHUT))) {
+ (!conn_is_back(qcc->conn) && qcc->app_st == QCC_APP_ST_SHUT) ||
+ (conn_is_back(qcc->conn) && (qcc->flags & QC_CF_CONN_SHUT))) {
return 1;
}
if (quic_stream_is_bidi(qcs->id)) {
qcs->st = (qcs->st == QC_SS_HREM) ? QC_SS_CLO : QC_SS_HLOC;
-
- if (qcs->flags & QC_SF_HREQ_RECV)
- qcc_rm_hreq(qcs->qcc);
}
else {
/* Only local uni streams are valid for this operation. */
return -1;
}
- /* QC_SF_HREQ_RECV must be set once for a stream. Else, nb_hreq counter
- * will be incorrect for the connection.
- */
- BUG_ON_HOT(qcs->flags & QC_SF_HREQ_RECV);
- qcs->flags |= QC_SF_HREQ_RECV;
+ /* QCS must be identified as request stream prior to stconn instantiation. */
+ BUG_ON(!(qcs->flags & QC_SF_HREQ_RECV));
++qcc->nb_sc;
- ++qcc->nb_hreq;
++qcc->tot_sc;
/* TODO duplicated from mux_h2 */
qcs_free(qcs);
+ /* Rearm http-keep-alive timeout when last request stream is freed. */
+ if (!conn_is_back(qcc->conn) && qcc_may_expire(qcc) && !qcc->nb_hreq) {
+ qcc_reset_idle_start(qcc);
+ qcc_refresh_timeout(qcc);
+ }
+
TRACE_LEAVE(QMUX_EV_QCS_END, conn);
}
* shutdown should occurs. For all other cases, an immediate close
* seems legitimate.
*/
- if (qcc_is_dead(qcc)) {
- TRACE_STATE("releasing dead connection", QMUX_EV_QCC_WAKE, qcc->conn);
- qcc_app_shutdown(qcc);
- qcc_release(qcc);
- }
+ TRACE_STATE("releasing dead connection", QMUX_EV_QCC_WAKE, qcc->conn);
+ qcc_app_shutdown(qcc);
+ qcc_release(qcc);
out:
TRACE_LEAVE(QMUX_EV_QCC_WAKE);