MINOR: mux-quic: refresh timeout on frame decoding

Refresh the MUX connection timeout in frame parsing functions. This is
necessary as these Rx operation are completed directly from the
quic-conn layer outside of MUX I/O callback. Thus, the timeout should be
refreshed on this occasion.

Note that however on STREAM parsing refresh is only conducted when
receiving the current consecutive data offset.

Timeouts related function have been moved up in the source file to be
able to use them in qcc_decode_qcs().

This commit will be useful for http-request timeout. Indeed, a new
stream may be opened during qcc_decode_qcs() which should trigger this
timeout until a full header section is received and qcs instance is
attached to sedesc.
This commit is contained in:
Amaury Denoyelle 2022-08-02 15:57:16 +02:00
parent 8d818c6eab
commit 418ba21461
1 changed files with 74 additions and 72 deletions

View File

@ -261,6 +261,77 @@ static forceinline void qcc_rm_hreq(struct qcc *qcc)
qcc_reset_idle_start(qcc);
}
static inline int qcc_is_dead(const struct qcc *qcc)
{
/* Mux connection is considered dead if :
* - all stream-desc are detached AND
* = connection is on error OR
* = mux timeout has already fired or is unset
*/
if (!qcc->nb_sc && ((qcc->conn->flags & CO_FL_ERROR) || !qcc->task))
return 1;
return 0;
}
/* Return true if the mux timeout should be armed. */
static inline int qcc_may_expire(struct qcc *qcc)
{
return !qcc->nb_sc;
}
/* Refresh the timeout on <qcc> if needed depending on its state. */
static void qcc_refresh_timeout(struct qcc *qcc)
{
const struct proxy *px = qcc->proxy;
TRACE_ENTER(QMUX_EV_QCC_WAKE, qcc->conn);
if (!qcc->task)
goto leave;
/* Calculate the timeout. */
if (qcc_may_expire(qcc)) {
/* TODO implement specific timeouts
* - http-requset for waiting on incomplete streams
* - client-fin for graceful shutdown
*/
if (qcc->nb_hreq) {
/* detached streams left. */
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);
}
else if (!conn_is_back(qcc->conn) && qcc->largest_bidi_r > 0x00) {
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 none set. */
if (!tick_isset(qcc->task->expire)) {
TRACE_DEVEL("fallback to default timeout", QMUX_EV_QCC_WAKE, qcc->conn);
qcc->task->expire = tick_add_ifset(now_ms, qcc->timeout);
}
/* TODO if connection is idle on frontend and proxy is
* disabled, remove it with global close_spread delay applied.
*/
}
else {
TRACE_DEVEL("not eligible for timeout", QMUX_EV_QCC_WAKE, qcc->conn);
qcc->task->expire = TICK_ETERNITY;
}
/* Enqueue the timeout task. */
task_queue(qcc->task);
leave:
TRACE_LEAVE(QMUX_EV_QCS_NEW, qcc->conn);
}
/* Mark a stream as open if it was idle. This can be used on every
* successful emission/reception operation to update the stream state.
*/
@ -859,8 +930,10 @@ int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
if (qcs->flags & QC_SF_SIZE_KNOWN && !ncb_is_fragmented(&qcs->rx.ncbuf))
qcs_close_remote(qcs);
if (ncb_data(&qcs->rx.ncbuf, 0) && !(qcs->flags & QC_SF_DEM_FULL))
if (ncb_data(&qcs->rx.ncbuf, 0) && !(qcs->flags & QC_SF_DEM_FULL)) {
qcc_decode_qcs(qcc, qcs);
qcc_refresh_timeout(qcc);
}
if (qcs->flags & QC_SF_READ_ABORTED) {
/* TODO should send a STOP_SENDING */
@ -1032,77 +1105,6 @@ static void qcs_destroy(struct qcs *qcs)
TRACE_LEAVE(QMUX_EV_QCS_END, conn);
}
static inline int qcc_is_dead(const struct qcc *qcc)
{
/* Mux connection is considered dead if :
* - all stream-desc are detached AND
* = connection is on error OR
* = mux timeout has already fired or is unset
*/
if (!qcc->nb_sc && ((qcc->conn->flags & CO_FL_ERROR) || !qcc->task))
return 1;
return 0;
}
/* Return true if the mux timeout should be armed. */
static inline int qcc_may_expire(struct qcc *qcc)
{
return !qcc->nb_sc;
}
/* Refresh the timeout on <qcc> if needed depending on its state. */
static void qcc_refresh_timeout(struct qcc *qcc)
{
const struct proxy *px = qcc->proxy;
TRACE_ENTER(QMUX_EV_QCC_WAKE, qcc->conn);
if (!qcc->task)
goto leave;
/* Calculate the timeout. */
if (qcc_may_expire(qcc)) {
/* TODO implement specific timeouts
* - http-requset for waiting on incomplete streams
* - client-fin for graceful shutdown
*/
if (qcc->nb_hreq) {
/* detached streams left. */
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);
}
else if (!conn_is_back(qcc->conn) && qcc->largest_bidi_r > 0x00) {
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 none set. */
if (!tick_isset(qcc->task->expire)) {
TRACE_DEVEL("fallback to default timeout", QMUX_EV_QCC_WAKE, qcc->conn);
qcc->task->expire = tick_add_ifset(now_ms, qcc->timeout);
}
/* TODO if connection is idle on frontend and proxy is
* disabled, remove it with global close_spread delay applied.
*/
}
else {
TRACE_DEVEL("not eligible for timeout", QMUX_EV_QCC_WAKE, qcc->conn);
qcc->task->expire = TICK_ETERNITY;
}
/* Enqueue the timeout task. */
task_queue(qcc->task);
leave:
TRACE_LEAVE(QMUX_EV_QCS_NEW, qcc->conn);
}
/* Transfer as much as possible data on <qcs> from <in> to <out>. This is done
* in respect with available flow-control at stream and connection level.
*