sess->t_handshake = 0;
sess->t_idle = 0;
+ /* A stream must have been registered for HTTP wait before attaching
+ * it to sedesc. See <qcs_wait_http_req> for more info.
+ */
+ BUG_ON_HOT(!LIST_INLIST(&qcs->el_opening));
+ LIST_DELETE(&qcs->el_opening);
+
return qcs->sd->sc;
}
+/* 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.
+ *
+ * This function should be called by the application protocol layer on request
+ * streams initialization.
+ */
+static inline void qcs_wait_http_req(struct qcs *qcs)
+{
+ struct qcc *qcc = qcs->qcc;
+
+ /* A stream cannot be registered several times. */
+ BUG_ON_HOT(tick_isset(qcs->start));
+ qcs->start = now_ms;
+
+ /* qcc.opening_list size is limited by flow-control so no custom
+ * restriction is needed here.
+ */
+ LIST_APPEND(&qcc->opening_list, &qcs->el_opening);
+}
+
#endif /* USE_QUIC */
#endif /* _HAPROXY_MUX_QUIC_H */
qcs->st = QC_SS_IDLE;
qcs->ctx = NULL;
+ /* App callback attach may register the stream for http-request wait.
+ * These fields must be initialed before.
+ */
+ LIST_INIT(&qcs->el_opening);
+ qcs->start = TICK_ETERNITY;
+
/* Allocate transport layer stream descriptor. Only needed for TX. */
if (!quic_stream_is_uni(id) || !quic_stream_is_remote(qcc, id)) {
struct quic_conn *qc = qcc->conn->handle.qc;
* it with global close_spread delay applied.
*/
- /* TODO implement specific timeouts
- * - http-requset for waiting on incomplete streams
- * - client-fin for graceful shutdown
- */
+ /* TODO implement client/server-fin timeout for graceful shutdown */
/* Frontend timeout management
* - detached streams with data left to send -> default timeout
+ * - stream waiting on incomplete request or no stream yet activated -> timeout http-request
* - idle after stream processing -> timeout http-keep-alive
*/
if (!conn_is_back(qcc->conn)) {
- int timeout;
-
if (qcc->nb_hreq) {
TRACE_DEVEL("one or more requests still in progress", QMUX_EV_QCC_WAKE, qcc->conn);
qcc->task->expire = tick_add_ifset(now_ms, qcc->timeout);
goto leave;
}
- /* Use http-request timeout if keep-alive timeout not set */
- timeout = tick_isset(px->timeout.httpka) ?
- px->timeout.httpka : px->timeout.httpreq;
+ if (!LIST_ISEMPTY(&qcc->opening_list) || unlikely(!qcc->largest_bidi_r)) {
+ int timeout = px->timeout.httpreq;
+ struct qcs *qcs = NULL;
+ int base_time;
- TRACE_DEVEL("at least one request achieved but none currently in progress", QMUX_EV_QCC_WAKE, qcc->conn);
- qcc->task->expire = tick_add_ifset(qcc->idle_start, timeout);
+ /* Use start time of first stream waiting on HTTP or
+ * qcc idle if no stream not yet used.
+ */
+ if (likely(!LIST_ISEMPTY(&qcc->opening_list)))
+ qcs = LIST_ELEM(qcc->opening_list.n, struct qcs *, el_opening);
+ base_time = qcs ? qcs->start : qcc->idle_start;
+
+ TRACE_DEVEL("waiting on http request", QMUX_EV_QCC_WAKE, qcc->conn, qcs);
+ qcc->task->expire = tick_add_ifset(base_time, timeout);
+ }
+ else {
+ /* Use http-request timeout if keep-alive timeout not set */
+ int timeout = tick_isset(px->timeout.httpka) ?
+ px->timeout.httpka : px->timeout.httpreq;
+
+ TRACE_DEVEL("at least one request achieved but none currently in progress", QMUX_EV_QCC_WAKE, qcc->conn);
+ qcc->task->expire = tick_add_ifset(qcc->idle_start, timeout);
+ }
}
/* fallback to default timeout if frontend specific undefined or for
}
}
+ if (qcc_may_expire(qcc) && !qcc->nb_hreq)
+ qcc_refresh_timeout(qcc);
+
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
return 0;
}
TRACE_DEVEL("receiving STOP_SENDING on stream", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs);
qcc_reset_stream(qcs, err);
+ if (qcc_may_expire(qcc) && !qcc->nb_hreq)
+ qcc_refresh_timeout(qcc);
+
out:
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
return 0;
qcc->task->expire = tick_add(now_ms, qcc->timeout);
}
qcc_reset_idle_start(qcc);
+ LIST_INIT(&qcc->opening_list);
if (!conn_is_back(conn)) {
if (!LIST_INLIST(&conn->stopping_list)) {