[MEDIUM] stream_interface: added a DISconnected state between CON/EST and CLO
There were rare situations where it was not easy to detect that a failed
session attempt had occurred and needed some server cleanup. In particular,
client aborts sometimes lead to session leaks on the server side.
A new state "SI_ST_DIS" (disconnected) has been introduced for this. When
a session has been closed at a stream interface but the server cleanup has
not occurred, this state is entered instead of CLO. The cleanup is then
performed there and the state goes to CLO.
A new diagram has been added to show possible stream_interface state
transitions that can occur in a stream-sock. It makes debugging easier.
diff --git a/src/proto_http.c b/src/proto_http.c
index a3a7817..df905e2 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -700,6 +700,8 @@
si->ob->flags |= BF_WRITE_ERROR;
s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
+
+ /* no session was ever accounted for this server */
si->state = SI_ST_CLO;
return;
}
@@ -963,7 +965,7 @@
*/
if (s->si[0].state == SI_ST_EST) {
if (unlikely(s->si[0].flags & SI_FL_ERR)) {
- s->si[0].state = SI_ST_CLO;
+ s->si[0].state = SI_ST_DIS;
fd_delete(s->si[0].fd);
stream_int_report_error(&s->si[0]);
}
@@ -971,7 +973,7 @@
if (s->si[1].state == SI_ST_EST) {
if (unlikely(s->si[1].flags & SI_FL_ERR)) {
- s->si[1].state = SI_ST_CLO;
+ s->si[1].state = SI_ST_DIS;
fd_delete(s->si[1].fd);
stream_int_report_error(&s->si[1]);
/////////// FIXME: the following must move somewhere else
@@ -980,7 +982,7 @@
s->srv->failed_resp++;
}
}
- else if (s->si[1].state != SI_ST_INI && s->si[1].state != SI_ST_CLO) {
+ else if (s->si[1].state >= SI_ST_QUE && s->si[1].state <= SI_ST_CON) {
/* Maybe we were trying to establish a connection on the server side ? */
if (s->si[1].state == SI_ST_CON) {
if (unlikely(!sess_update_st_con_tcp(s, &s->si[1])))
@@ -1010,8 +1012,8 @@
/////// FIXME: do that later
/* FIXME: we might have got errors above, and we should process them below */
- if (s->si[1].state == SI_ST_CLO && s->si[1].prev_state != SI_ST_CLO &&
- s->si[1].err_type != SI_ET_NONE)
+ if ((s->si[1].state == SI_ST_DIS || s->si[1].state == SI_ST_CLO) &&
+ s->si[1].prev_state != SI_ST_CLO && s->si[1].err_type != SI_ET_NONE)
return_srv_error(s, s->si[1].err_type);
@@ -1078,14 +1080,29 @@
/* 3: When a server-side connection is released, we have to
* count it and check for pending connections on this server.
*/
- 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);
+ if (unlikely(s->req->cons->state == SI_ST_DIS)) {
+ s->req->cons->state = SI_ST_CLO;
+ if (s->srv) {
+ if (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);
+ }
}
+ /* nothing special to be done on client side */
+ if (unlikely(s->req->prod->state == SI_ST_DIS))
+ s->req->prod->state = SI_ST_CLO;
+
+ /*
+ * Note: all transient states (REQ, CER, DIS) have been eliminated at
+ * this point.
+ */
+
+
/* Dirty trick: force one first pass everywhere */
rqf_cli = rqf_srv = ~s->req->flags;
rpf_cli = rpf_srv = ~s->rep->flags;
@@ -1163,7 +1180,8 @@
/* When a server-side connection is released, we have to
* count it and check for pending connections on this server.
*/
- if (s->req->cons->state == SI_ST_CLO) {
+ if (s->req->cons->state == SI_ST_DIS) {
+ s->req->cons->state = SI_ST_CLO;
if (s->srv) {
if (s->flags & SN_CURR_SESS) {
s->flags &= ~SN_CURR_SESS;
@@ -3722,7 +3740,7 @@
/* This function is called with (si->state == SI_ST_CON) meaning that a
* connection was attempted and that the file descriptor is already allocated.
* We must check for establishment, error and abort. Possible output states
- * are SI_ST_EST (established), SI_ST_CER (error), SI_ST_CLO (abort), and
+ * are SI_ST_EST (established), SI_ST_CER (error), SI_ST_DIS (abort), and
* SI_ST_CON (no change). The function returns 0 if it switches to SI_ST_CER,
* otherwise 1.
*/
@@ -3770,7 +3788,7 @@
fd_delete(si->fd);
buffer_shutw(req);
buffer_shutr(rep);
- si->state = SI_ST_CLO;
+ si->state = SI_ST_DIS;
si->err_type |= SI_ET_CONN_ABRT;
si->err_loc = s->srv;
return 1;