|
From 9154bc92ed11c6de75573dec341b6a0ce68bd0eb Mon Sep 17 00:00:00 2001
|
|
From: Willy Tarreau <w@1wt.eu>
|
|
Date: Wed, 25 Nov 2015 20:17:27 +0100
|
|
Subject: [PATCH 07/10] BUG/MEDIUM: stream: fix half-closed timeout handling
|
|
|
|
client-fin and server-fin are bogus. They are applied on the write
|
|
side after a SHUTR was seen. The immediate effect is that sometimes
|
|
if a SHUTR was seen after a SHUTW on the same side, the timeout is
|
|
enabled again regardless of the fact that the output is already
|
|
closed. This results in the timeout event not to be processed and
|
|
a busy poll loop to happen until another timeout on the stream gets
|
|
rid of it. Note that haproxy continues its job during this, it's just
|
|
that it eats all the CPU trying to handle an event that it ignores.
|
|
|
|
An reproducible case consists in having a client stop reading data from
|
|
a server to ensure data remain in the response buffer, then the client
|
|
sends a shutdown(write). If abortonclose is enabled on haproxy, the
|
|
shutdown is passed to the server side and the server responds with a
|
|
SHUTR that cannot immediately be forwarded to the client since the
|
|
buffer is full. During this time the event is ignored and the task is
|
|
woken again in loops.
|
|
|
|
It is worth noting that the timeout handling since 1.5 is a bit fragile
|
|
and that it might be possible that other similar conditions still exist,
|
|
so the timeout handling should be audited regarding this issue.
|
|
|
|
Many thanks to BaiYang for providing detailed information showing the
|
|
problem in action.
|
|
|
|
This bug also affects 1.5 thus the fix must be backported.
|
|
(cherry picked from commit f25b3573d65fd2411c7537b7b0a4817b478df909)
|
|
[Note for 1.5, it's in session.c here]
|
|
(cherry picked from commit 44e86286159474a52dc74f80d3271504cc6f1550)
|
|
---
|
|
src/session.c | 16 ----------------
|
|
1 file changed, 16 deletions(-)
|
|
|
|
diff --git a/src/session.c b/src/session.c
|
|
index 7520a85..2b2ad78 100644
|
|
--- a/src/session.c
|
|
+++ b/src/session.c
|
|
@@ -2213,10 +2213,6 @@ struct task *process_session(struct task *t)
|
|
if (unlikely((s->req->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
|
|
(CF_AUTO_CLOSE|CF_SHUTR))) {
|
|
channel_shutw_now(s->req);
|
|
- if (tick_isset(s->fe->timeout.clientfin)) {
|
|
- s->rep->wto = s->fe->timeout.clientfin;
|
|
- s->rep->wex = tick_add(now_ms, s->rep->wto);
|
|
- }
|
|
}
|
|
|
|
/* shutdown(write) pending */
|
|
@@ -2241,10 +2237,6 @@ struct task *process_session(struct task *t)
|
|
if (s->req->prod->flags & SI_FL_NOHALF)
|
|
s->req->prod->flags |= SI_FL_NOLINGER;
|
|
si_shutr(s->req->prod);
|
|
- if (tick_isset(s->fe->timeout.clientfin)) {
|
|
- s->rep->wto = s->fe->timeout.clientfin;
|
|
- s->rep->wex = tick_add(now_ms, s->rep->wto);
|
|
- }
|
|
}
|
|
|
|
/* it's possible that an upper layer has requested a connection setup or abort.
|
|
@@ -2391,10 +2383,6 @@ struct task *process_session(struct task *t)
|
|
if (unlikely((s->rep->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
|
|
(CF_AUTO_CLOSE|CF_SHUTR))) {
|
|
channel_shutw_now(s->rep);
|
|
- if (tick_isset(s->be->timeout.serverfin)) {
|
|
- s->req->wto = s->be->timeout.serverfin;
|
|
- s->req->wex = tick_add(now_ms, s->req->wto);
|
|
- }
|
|
}
|
|
|
|
/* shutdown(write) pending */
|
|
@@ -2417,10 +2405,6 @@ struct task *process_session(struct task *t)
|
|
if (s->rep->prod->flags & SI_FL_NOHALF)
|
|
s->rep->prod->flags |= SI_FL_NOLINGER;
|
|
si_shutr(s->rep->prod);
|
|
- if (tick_isset(s->be->timeout.serverfin)) {
|
|
- s->req->wto = s->be->timeout.serverfin;
|
|
- s->req->wex = tick_add(now_ms, s->req->wto);
|
|
- }
|
|
}
|
|
|
|
if (s->req->prod->state == SI_ST_DIS || s->req->cons->state == SI_ST_DIS)
|
|
--
|
|
2.4.10
|
|
|