FIN_WAIT state for too long when clients do not disconnect cleanly. This
problem is particularly common long connections such as RDP or WebSocket.
Note that this timeout can override "timeout tunnel" when a connection shuts
- down in one direction.
+ down in one direction. It is applied to idle HTTP/2 connections once a GOAWAY
+ frame was sent, often indicating an expectation that the connection quickly
+ ends.
This parameter is specific to frontends, but can be specified once for all in
"defaults" sections. By default it is not set, so half-closed connections
int32_t mfs; /* mux's max frame size */
int timeout; /* idle timeout duration in ticks */
+ int shut_timeout; /* idle timeout duration in ticks after GOAWAY was sent */
struct task *task; /* timeout management task */
struct eb_root streams_by_id; /* all active streams by their ID */
struct list send_list; /* list of blocked streams requesting to send */
goto fail;
- h2c->timeout = sess->fe->timeout.client;
+ h2c->shut_timeout = h2c->timeout = sess->fe->timeout.client;
+ if (tick_isset(sess->fe->timeout.clientfin))
+ h2c->shut_timeout = sess->fe->timeout.clientfin;
+
h2c->task = NULL;
if (tick_isset(h2c->timeout)) {
t = task_new(tid_bit);
if (h2c->task) {
if (eb_is_empty(&h2c->streams_by_id)) {
- h2c->task->expire = tick_add(now_ms, h2c->timeout);
+ h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
task_queue(h2c->task);
}
else
}
/* try to send but no need to insist */
+ h2c->last_sid = h2c->max_id;
if (h2c_send_goaway_error(h2c, NULL) <= 0)
h2c->flags |= H2_CF_GOAWAY_FAILED;
}
else if (h2c->task) {
if (eb_is_empty(&h2c->streams_by_id)) {
- h2c->task->expire = tick_add(now_ms, h2c->timeout);
+ h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
task_queue(h2c->task);
}
else