diff --git a/include/types/session.h b/include/types/session.h index 8b87be21a..1177924c5 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -47,7 +47,7 @@ #define SN_BE_ASSIGNED 0x00000008 /* a backend was assigned. Conns are accounted. */ #define SN_CONN_CLOSED 0x00000010 /* "Connection: close" was present or added */ #define SN_MONITOR 0x00000020 /* this session comes from a monitoring system */ -/* unused: 0x00000040 */ +#define SN_CURR_SESS 0x00000040 /* a connection is currently being counted on the server */ #define SN_FRT_ADDR_SET 0x00000080 /* set if the frontend address has been filled */ #define SN_REDISP 0x00000100 /* set if this session was redispatched from one server to another */ #define SN_CONN_TAR 0x00000200 /* set if this session is turning around before reconnecting */ diff --git a/src/backend.c b/src/backend.c index de0537a19..0d6fef12f 100644 --- a/src/backend.c +++ b/src/backend.c @@ -1820,6 +1820,7 @@ int connect_server(struct session *s) s->req->cons->state = SI_ST_CON; if (s->srv) { + s->flags |= SN_CURR_SESS; s->srv->cur_sess++; if (s->srv->cur_sess > s->srv->cur_sess_max) s->srv->cur_sess_max = s->srv->cur_sess; diff --git a/src/proto_http.c b/src/proto_http.c index 6f2dab3cf..fe59841a6 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -963,8 +963,8 @@ void process_session(struct task *t, int *next) * upper by setting flags into the buffers. Note that the side towards * the client cannot have connect (hence retryable) errors. */ - if (unlikely(s->si[0].state == SI_ST_EST)) { - if (s->si[0].flags & SI_FL_ERR) { + if (s->si[0].state == SI_ST_EST) { + if (unlikely(s->si[0].flags & SI_FL_ERR)) { s->si[0].state = SI_ST_CLO; fd_delete(s->si[0].fd); stream_int_report_error(&s->si[0]); @@ -972,10 +972,13 @@ void process_session(struct task *t, int *next) } if (s->si[1].state == SI_ST_EST) { - if (s->si[1].flags & SI_FL_ERR) { + if (unlikely(s->si[1].flags & SI_FL_ERR)) { s->si[1].state = SI_ST_CLO; fd_delete(s->si[1].fd); stream_int_report_error(&s->si[1]); + s->be->failed_resp++; + if (s->srv) + s->srv->failed_resp++; } } else if (s->si[1].state != SI_ST_INI && s->si[1].state != SI_ST_CLO) { @@ -1044,35 +1047,33 @@ void process_session(struct task *t, int *next) s->req->cons->shutw(s->req->cons); } + if (unlikely((s->req->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW)) { + /* write closed on server side, let's forward it to the client */ + buffer_shutr_now(s->req); + s->req->prod->shutr(s->req->prod); + } + if (unlikely((s->rep->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) == (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)) || unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTW_NOW)) == BF_SHUTW_NOW)) { buffer_shutw(s->rep); s->rep->cons->shutw(s->rep->cons); } + if (unlikely((s->rep->flags & (BF_SHUTW|BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTW)) { + /* write closed on client side, let's forward it to the server */ + buffer_shutr_now(s->rep); + s->rep->prod->shutr(s->rep->prod); + } + /* 3: When a server-side connection is released, we have to * count it and check for pending connections on this server. - * FIXME: the test below is not accurate. An audit is needed - * to find all uncaught transitions. We need a way to ensure - * that shutdowns called right after connect() after TAR will - * correctly be caught for instance. In fact we need a way to - * track when the connection is assigned to the server. */ - if (unlikely(s->req->cons->state == SI_ST_CLO && - (s->req->cons->prev_state == SI_ST_EST || s->req->cons->prev_state == SI_ST_CON))) { - /* Count server-side errors (but not timeouts). */ - if (s->req->flags & BF_WRITE_ERROR) { - s->be->failed_resp++; - if (s->srv) - s->srv->failed_resp++; - } - - if (s->srv) { - s->srv->cur_sess--; - sess_change_server(s, NULL); - if (may_dequeue_tasks(s->srv, s->be)) - process_srv_queue(s->srv); - } + if (unlikely(s->req->cons->state == SI_ST_CLO && s->srv && (s->flags & SN_CURR_SESS))) { + s->flags &= ~SN_CURR_SESS; + s->srv->cur_sess--; + sess_change_server(s, NULL); + if (may_dequeue_tasks(s->srv, s->be)) + process_srv_queue(s->srv); } /* Dirty trick: force one first pass everywhere */ @@ -1153,7 +1154,8 @@ void process_session(struct task *t, int *next) * count it and check for pending connections on this server. */ if (s->req->cons->state == SI_ST_CLO) { - if (s->srv) { + if (s->srv && (s->flags & SN_CURR_SESS)) { + s->flags &= ~SN_CURR_SESS; s->srv->cur_sess--; sess_change_server(s, NULL); if (may_dequeue_tasks(s->srv, s->be)) @@ -3734,7 +3736,8 @@ int sess_update_st_con_tcp(struct session *s, struct stream_interface *si) si->state = SI_ST_CER; fd_delete(si->fd); - if (s->srv) { + if (s->srv && (s->flags & SN_CURR_SESS)) { + s->flags &= ~SN_CURR_SESS; s->srv->cur_sess--; sess_change_server(s, NULL); si->err_loc = s->srv; @@ -3759,7 +3762,8 @@ int sess_update_st_con_tcp(struct session *s, struct stream_interface *si) /* give up */ req->wex = TICK_ETERNITY; fd_delete(si->fd); - if (s->srv) { + if (s->srv && (s->flags & SN_CURR_SESS)) { + s->flags &= ~SN_CURR_SESS; s->srv->cur_sess--; sess_change_server(s, NULL); } diff --git a/src/session.c b/src/session.c index ceef0b7df..fb7de4a3b 100644 --- a/src/session.c +++ b/src/session.c @@ -37,8 +37,13 @@ void session_free(struct session *s) if (s->pend_pos) pendconn_free(s->pend_pos); - if (s->srv) /* there may be requests left pending in queue */ + if (s->srv) { /* there may be requests left pending in queue */ + if (s->flags & SN_CURR_SESS) { + s->flags &= ~SN_CURR_SESS; + s->srv->cur_sess--; + } process_srv_queue(s->srv); + } if (unlikely(s->srv_conn)) { /* the session still has a reserved slot on a server, but * it should normally be only the same as the one above,