}
}
+/* returns true if the connection is allowed to expire, false otherwise. A
+ * connection may expire when:
+ * - it has no stream
+ * - it has data in the mux buffer
+ * - it has streams in the blocked list
+ * - it has streams in the fctl list
+ * - it has streams in the send list
+ * Otherwise it means some streams are waiting in the data layer and it should
+ * not expire.
+ */
+static inline int h2c_may_expire(const struct h2c *h2c)
+{
+ return eb_is_empty(&h2c->streams_by_id) ||
+ br_data(h2c->mbuf) ||
+ !LIST_ISEMPTY(&h2c->blocked_list) ||
+ !LIST_ISEMPTY(&h2c->fctl_list) ||
+ !LIST_ISEMPTY(&h2c->send_list) ||
+ !LIST_ISEMPTY(&h2c->sending_list);
+}
+
static __inline int
-h2c_is_dead(struct h2c *h2c)
+h2c_is_dead(const struct h2c *h2c)
{
if (eb_is_empty(&h2c->streams_by_id) && /* don't close if streams exist */
((h2c->conn->flags & CO_FL_ERROR) || /* errors close immediately */
h2_release_mbuf(h2c);
if (h2c->task) {
- h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
+ if (h2c_may_expire(h2c))
+ h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
+ else
+ h2c->task->expire = TICK_ETERNITY;
task_queue(h2c->task);
}
return t;
}
+ if (h2c && !h2c_may_expire(h2c)) {
+ /* we do still have streams but all of them are idle, waiting
+ * for the data layer, so we must not enforce the timeout here.
+ */
+ t->expire = TICK_ETERNITY;
+ return t;
+ }
+
task_destroy(t);
if (!h2c) {
h2_release(h2c);
}
else if (h2c->task) {
- h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
+ if (h2c_may_expire(h2c))
+ h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
+ else
+ h2c->task->expire = TICK_ETERNITY;
task_queue(h2c->task);
TRACE_DEVEL("leaving, refreshing connection's timeout", H2_EV_STRM_END, h2c->conn);
}