Skip to content

Commit 3ed9361

Browse files
committed
BUG/MEDIUM: mux-h2: try to wait for the peer to read the GOAWAY
When timeout http-keep-alive is very short (e.g. 10ms), it's possible sometimes for a client to face truncated responses due to an early close that happens while the system is still pushing the last data, colliding with the client's WINDOW_UPDATEs that trigger RSTs. Here we're trying to do better: first we send a GOAWAY on timeout, then we wait up to clientfin/client timeout for the peer to react so that we don't immediately close. This is sufficient to avoid truncation as soon as the timeout is more than a few hundred ms. It's not certain it should be backported, because it's a bit sensistive and might possibly fall into certain edge cases.
1 parent 75b302d commit 3ed9361

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

src/mux_h2.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,14 @@ static void h2c_update_timeout(struct h2c *h2c)
847847
h2c->task->expire = tick_add_ifset(now_ms, h2c->timeout);
848848
} else {
849849
/* no stream, no output data */
850-
if (!(h2c->flags & H2_CF_IS_BACK)) {
850+
if (h2c->flags & (H2_CF_GOAWAY_SENT|H2_CF_GOAWAY_FAILED)) {
851+
/* GOAWAY sent (or failed), closing in progress */
852+
int exp = tick_add_ifset(now_ms, h2c->shut_timeout);
853+
854+
h2c->task->expire = tick_first(h2c->task->expire, exp);
855+
is_idle_conn = 1;
856+
}
857+
else if (!(h2c->flags & H2_CF_IS_BACK)) {
851858
int to;
852859

853860
if (h2c->max_id > 0 && !b_data(&h2c->dbuf) &&
@@ -865,14 +872,6 @@ static void h2c_update_timeout(struct h2c *h2c)
865872
is_idle_conn = 1;
866873
}
867874

868-
if (h2c->flags & (H2_CF_GOAWAY_SENT|H2_CF_GOAWAY_FAILED)) {
869-
/* GOAWAY sent (or failed), closing in progress */
870-
int exp = tick_add_ifset(now_ms, h2c->shut_timeout);
871-
872-
h2c->task->expire = tick_first(h2c->task->expire, exp);
873-
is_idle_conn = 1;
874-
}
875-
876875
/* if a timeout above was not set, fall back to the default one */
877876
if (!tick_isset(h2c->task->expire))
878877
h2c->task->expire = tick_add_ifset(now_ms, h2c->timeout);
@@ -5036,6 +5035,20 @@ struct task *h2_timeout_task(struct task *t, void *context, unsigned int state)
50365035
conn_delete_from_tree(h2c->conn);
50375036

50385037
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
5038+
5039+
/* Try to gracefully close idle connections by sending a GOAWAY first,
5040+
* and then waiting for the fin timeout.
5041+
*/
5042+
if (!br_data(h2c->mbuf) && h2c_may_expire(h2c) &&
5043+
!(h2c->flags & (H2_CF_GOAWAY_SENT|H2_CF_GOAWAY_FAILED))) {
5044+
h2c_error(h2c, H2_ERR_NO_ERROR);
5045+
if (h2_send(h2c))
5046+
tasklet_wakeup(h2c->wait_event.tasklet);
5047+
t->expire = tick_add_ifset(now_ms, h2c->shut_timeout);
5048+
if (!tick_isset(t->expire))
5049+
t->expire = tick_add_ifset(now_ms, h2c->timeout);
5050+
return t;
5051+
}
50395052
}
50405053

50415054
do_leave:

0 commit comments

Comments
 (0)