OPTIM/MINOR: session: abort if possible before connecting to the backend

Depending on the path that led to sess_update_stream_int(), it's
possible that we had a read error on the frontend, but that we haven't
checked if we may abort the connection.

This was seen in particular the following setup: tcp mode, with
abortonclose set, frontend using ssl. If the ssl connection had a first
successful read, but the second read failed, we would stil try to open a
connection to the backend, although we had enough information to close
the connection early.

sess_update_stream_int() had some logic to handle that case in the
SI_ST_QUE and SI_ST_TAR, but that was missing in the SI_ST_ASS case.

This patches addresses the issue by verifying the state of the req
channel (and the abortonclose option) right before opening the
connection to the backend, so we have the opportunity to close the
connection there, and factorizes the shared SI_ST_{QUE,TAR,ASS} code.
This commit is contained in:
Frederik Deweerdt 2016-04-07 09:01:04 -07:00 committed by Willy Tarreau
parent bb137a8af7
commit 6cd8d13c05

View File

@ -774,6 +774,14 @@ static void sess_establish(struct stream *s)
req->wex = TICK_ETERNITY;
}
/* Check if the connection request is in such a state that it can be aborted. */
static int check_req_may_abort(struct channel *req, struct stream *s)
{
return ((req->flags & (CF_READ_ERROR)) ||
((req->flags & CF_SHUTW_NOW) && /* empty and client aborted */
(channel_is_empty(req) || s->be->options & PR_O_ABRT_CLOSE)));
}
/* Update back stream interface status for input states SI_ST_ASS, SI_ST_QUE,
* SI_ST_TAR. Other input states are simply ignored.
* Possible output states are SI_ST_CLO, SI_ST_TAR, SI_ST_ASS, SI_ST_REQ, SI_ST_CON
@ -798,6 +806,14 @@ static void sess_update_stream_int(struct stream *s)
/* Server assigned to connection request, we have to try to connect now */
int conn_err;
/* Before we try to initiate the connection, see if the
* request may be aborted instead.
*/
if (check_req_may_abort(req, s)) {
si->err_type |= SI_ET_CONN_ABRT;
goto abort_connection;
}
conn_err = connect_server(s);
srv = objt_server(s->target);
@ -893,19 +909,10 @@ static void sess_update_stream_int(struct stream *s)
}
/* Connection remains in queue, check if we have to abort it */
if ((req->flags & (CF_READ_ERROR)) ||
((req->flags & CF_SHUTW_NOW) && /* empty and client aborted */
(channel_is_empty(req) || s->be->options & PR_O_ABRT_CLOSE))) {
/* give up */
si->exp = TICK_ETERNITY;
if (check_req_may_abort(req, s)) {
s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
si_shutr(si);
si_shutw(si);
si->err_type |= SI_ET_QUEUE_ABRT;
si->state = SI_ST_CLO;
if (s->srv_error)
s->srv_error(s, si);
return;
goto abort_connection;
}
/* Nothing changed */
@ -913,18 +920,9 @@ static void sess_update_stream_int(struct stream *s)
}
else if (si->state == SI_ST_TAR) {
/* Connection request might be aborted */
if ((req->flags & (CF_READ_ERROR)) ||
((req->flags & CF_SHUTW_NOW) && /* empty and client aborted */
(channel_is_empty(req) || s->be->options & PR_O_ABRT_CLOSE))) {
/* give up */
si->exp = TICK_ETERNITY;
si_shutr(si);
si_shutw(si);
if (check_req_may_abort(req, s)) {
si->err_type |= SI_ET_CONN_ABRT;
si->state = SI_ST_CLO;
if (s->srv_error)
s->srv_error(s, si);
return;
goto abort_connection;
}
if (!(si->flags & SI_FL_EXP))
@ -942,6 +940,17 @@ static void sess_update_stream_int(struct stream *s)
si->state = SI_ST_REQ;
return;
}
return;
abort_connection:
/* give up */
si->exp = TICK_ETERNITY;
si_shutr(si);
si_shutw(si);
si->state = SI_ST_CLO;
if (s->srv_error)
s->srv_error(s, si);
return;
}
/* Set correct stream termination flags in case no analyser has done it. It