[MAJOR] kill CL_STINSPECT and CL_STHEADERS (step 1)

This is a first attempt at separating data processing from the
TCP state machine. Those two states have been replaced with flags
in the session indicating what needs to be analyzed. The corresponding
code is still called before and in lieu of TCP states.

Next change should get rid of the specific SV_STANALYZE which is in
fact a client state.

Then next change should consist in making it possible to analyze
TCP contents while being in CL_STDATA (or CL_STSHUT*).
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index 713da49..546564b 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -32,12 +32,10 @@
  */
 
 /* different possible states for the client side */
-#define CL_STINSPECT	0
-#define CL_STHEADERS	1
-#define CL_STDATA	2
-#define CL_STSHUTR	3
-#define CL_STSHUTW	4
-#define CL_STCLOSE	5
+#define CL_STDATA	0
+#define CL_STSHUTR	1
+#define CL_STSHUTW	2
+#define CL_STCLOSE	3
 
 /* different possible states for the server side */
 #define SV_STIDLE	0
diff --git a/include/types/session.h b/include/types/session.h
index 5bcd0d7..c94401a 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -82,6 +82,18 @@
  * and freed in session_free() !
  */
 
+/* analysis flags */
+#define AN_REQ_INSPECT          0x00000001  /* inspect request contents */
+#define AN_REQ_HTTP_HDR         0x00000002  /* inspect HTTP request headers */
+#define AN_REQ_HTTP_BODY        0x00000004  /* inspect HTTP request body */
+#define AN_REQ_ANY              (AN_REQ_INSPECT|AN_REQ_HTTP_HDR|AN_REQ_HTTP_BODY)
+
+#define AN_RTR_INSPECT          0x00000008  /* inspect response contents */
+#define AN_RTR_HTTP_HDR         0x00000010  /* inspect HTTP response headers */
+#define AN_RTR_HTTP_BODY        0x00000020  /* inspect HTTP response body */
+#define AN_RTR_ANY              (AN_RTR_INSPECT|AN_RTR_HTTP_HDR|AN_RTR_HTTP_BODY)
+
+
 /*
  * Note: some session flags have dependencies :
  *  - SN_DIRECT cannot exist without SN_ASSIGNED, because a server is
@@ -105,6 +117,7 @@
 	int srv_state;				/* state of the server side */
 	int conn_retries;			/* number of connect retries left */
 	int flags;				/* some flags describing the session */
+	unsigned int analysis;			/* bit field indicating remaining analysis to perform on data */
 	struct buffer *req;			/* request buffer */
 	struct buffer *rep;			/* response buffer */
 	struct sockaddr_storage cli_addr;	/* the client address */
diff --git a/src/client.c b/src/client.c
index e04a066..cbe5f11 100644
--- a/src/client.c
+++ b/src/client.c
@@ -106,10 +106,12 @@
 			goto out_close;
 		}
 
+		s->flags = 0;
+		s->analysis = 0;
+
 		/* if this session comes from a known monitoring system, we want to ignore
 		 * it as soon as possible, which means closing it immediately for TCP.
 		 */
-		s->flags = 0;
 		if (addr.ss_family == AF_INET &&
 		    p->mon_mask.s_addr &&
 		    (((struct sockaddr_in *)&addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr) {
@@ -159,26 +161,19 @@
 		 * TCP mode there will be no header processing so any default
 		 * backend must be assigned if set.
 		 */
-		if (p->mode == PR_MODE_HTTP) {
-			if (s->fe->tcp_req.inspect_delay)
-				s->cli_state = CL_STINSPECT;
-			else
-				s->cli_state = CL_STHEADERS;
-		} else {
-			/* We must assign any default backend now since
-			 * there will be no header processing.
-			 */
-			if (p->mode == PR_MODE_TCP) {
-				if (p->defbe.be)
-					s->be = p->defbe.be;
-				s->flags |= SN_BE_ASSIGNED;
-			}
-			if (s->fe->tcp_req.inspect_delay)
-				s->cli_state = CL_STINSPECT;
-			else
-				s->cli_state = CL_STDATA; /* no HTTP headers for non-HTTP proxies */
+		if (p->mode == PR_MODE_TCP) {
+			if (p->defbe.be)
+				s->be = p->defbe.be;
+			s->flags |= SN_BE_ASSIGNED;
 		}
 
+		if (p->mode == PR_MODE_HTTP)
+			s->analysis |= AN_REQ_HTTP_HDR;
+
+		if (s->fe->tcp_req.inspect_delay)
+			s->analysis |= AN_REQ_INSPECT;
+
+		s->cli_state = CL_STDATA;
 		s->srv_state = SV_STIDLE;
 		s->req = s->rep = NULL; /* will be allocated later */
 
@@ -341,7 +336,7 @@
 		if (p->mode == PR_MODE_HTTP) /* reserve some space for header rewriting */
 			s->req->rlim -= MAXREWRITE;
 
-		if (s->cli_state == CL_STDATA)
+		if (!(s->analysis & AN_REQ_ANY))
 			s->req->flags |= BF_MAY_CONNECT;  /* don't wait to establish connection */
 
 		s->req->rto = s->fe->timeout.client;
@@ -405,13 +400,15 @@
 			}
 		}
 
-		if (s->cli_state == CL_STHEADERS && s->fe->timeout.httpreq) {
-			s->txn.exp = tick_add(now_ms, s->fe->timeout.httpreq);
-			t->expire = tick_first(t->expire, s->txn.exp);
-		}
-		else if (s->cli_state == CL_STINSPECT && s->fe->tcp_req.inspect_delay) {
-			s->inspect_exp = tick_add(now_ms, s->fe->tcp_req.inspect_delay);
-			t->expire = tick_first(t->expire, s->inspect_exp);
+		if (s->analysis & AN_REQ_ANY) {
+			if (s->analysis & AN_REQ_INSPECT) {
+				s->inspect_exp = tick_add_ifset(now_ms, s->fe->tcp_req.inspect_delay);
+				t->expire = tick_first(t->expire, s->inspect_exp);
+			}
+			else if (s->analysis & AN_REQ_HTTP_HDR) {
+				s->txn.exp = tick_add_ifset(now_ms, s->fe->timeout.httpreq);
+				t->expire = tick_first(t->expire, s->txn.exp);
+			}
 		}
 
 		if (p->mode != PR_MODE_HEALTH)
diff --git a/src/proto_http.c b/src/proto_http.c
index 8a8bfff..83406b6 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -365,8 +365,8 @@
 
 
 #ifdef DEBUG_FULL
-static char *cli_stnames[6] = {"INS", "HDR", "DAT", "SHR", "SHW", "CLS" };
-static char *srv_stnames[8] = {"IDL", "ANA", "CON", "HDR", "DAT", "SHR", "SHW", "CLS" };
+static char *cli_stnames[4] = { "DAT", "SHR", "SHW", "CLS" };
+static char *srv_stnames[8] = { "IDL", "ANA", "CON", "HDR", "DAT", "SHR", "SHW", "CLS" };
 #endif
 
 static void http_sess_log(struct session *s);
@@ -676,10 +676,12 @@
 		t->expire = tick_first(tick_first(s->req->rex, s->req->wex),
 				       tick_first(s->rep->rex, s->rep->wex));
 		t->expire = tick_first(t->expire, s->req->cex);
-		if (s->cli_state == CL_STHEADERS)
-			t->expire = tick_first(t->expire, s->txn.exp);
-		else if (s->cli_state == CL_STINSPECT)
-			t->expire = tick_first(t->expire, s->inspect_exp);
+		if (s->analysis & AN_REQ_ANY) {
+			if (s->analysis & AN_REQ_INSPECT)
+				t->expire = tick_first(t->expire, s->inspect_exp);
+			else if (s->analysis & AN_REQ_HTTP_HDR)
+				t->expire = tick_first(t->expire, s->txn.exp);
+		}
 
 		/* restore t to its place in the task list */
 		task_queue(t);
@@ -1549,7 +1551,7 @@
 		EV_FD_ISSET(t->cli_fd, DIR_RD), EV_FD_ISSET(t->cli_fd, DIR_WR),
 		req->rex, rep->wex);
 
-	if (c == CL_STINSPECT) {
+	if (t->analysis & AN_REQ_INSPECT) {
 		struct tcp_rule *rule;
 		int partial;
 
@@ -1564,6 +1566,7 @@
 			buffer_shutw_done(rep);
 			fd_delete(t->cli_fd);
 			t->cli_state = CL_STCLOSE;
+			t->analysis &= ~AN_REQ_ANY;
 			t->fe->failed_req++;
 			if (!(t->flags & SN_ERR_MASK))
 				t->flags |= SN_ERR_CLICL;
@@ -1579,6 +1582,7 @@
 			buffer_shutw_done(rep);
 			fd_delete(t->cli_fd);
 			t->cli_state = CL_STCLOSE;
+			t->analysis &= ~AN_REQ_ANY;
 			t->fe->failed_req++;
 			if (!(t->flags & SN_ERR_MASK))
 				t->flags |= SN_ERR_CLITO;
@@ -1623,6 +1627,7 @@
 					buffer_shutw_done(rep);
 					fd_delete(t->cli_fd);
 					t->cli_state = CL_STCLOSE;
+					t->analysis &= ~AN_REQ_ANY;
 					t->fe->failed_req++;
 					if (!(t->flags & SN_ERR_MASK))
 						t->flags |= SN_ERR_PRXCOND;
@@ -1636,21 +1641,21 @@
 			}
 		}
 		
-		/* if we get there, it means we have no rule which matches, so
-		 * we apply the default accept.
+		/* if we get there, it means we have no rule which matches, or
+		 * we have an explicit accept, so we apply the default accept.
 		 */
 		req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
-		if (t->fe->mode == PR_MODE_HTTP) {
-			t->cli_state = CL_STHEADERS;
+		t->analysis &= ~AN_REQ_INSPECT;
+		if (t->analysis & AN_REQ_HTTP_HDR)
 			t->txn.exp = tick_add_ifset(now_ms, t->fe->timeout.httpreq);
-		} else {
-			t->cli_state = CL_STDATA;
+		else
 			req->flags |= BF_MAY_CONNECT | BF_MAY_FORWARD;
-		}
+
 		t->inspect_exp = TICK_ETERNITY;
 		return 1;
 	}
-	else if (c == CL_STHEADERS) {
+
+	if (t->analysis & AN_REQ_HTTP_HDR) {
 		/*
 		 * Now parse the partial (or complete) lines.
 		 * We will check the request syntax, and also join multi-line
@@ -1732,6 +1737,7 @@
 				buffer_shutw_done(rep);
 				fd_delete(t->cli_fd);
 				t->cli_state = CL_STCLOSE;
+				t->analysis &= ~AN_REQ_ANY;
 				t->fe->failed_req++;
 				if (!(t->flags & SN_ERR_MASK))
 					t->flags |= SN_ERR_CLICL;
@@ -1746,6 +1752,7 @@
 				/* read timeout : give up with an error message. */
 				txn->status = 408;
 				client_retnclose(t, error_message(t, HTTP_ERR_408));
+				t->analysis &= ~AN_REQ_ANY;
 				t->fe->failed_req++;
 				if (!(t->flags & SN_ERR_MASK))
 					t->flags |= SN_ERR_CLITO;
@@ -1762,7 +1769,8 @@
 				 */
 				req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
 			}
-			return t->cli_state != CL_STHEADERS;
+			/* we don't need to resync if the state has not changed */
+			return !(t->analysis & AN_REQ_HTTP_HDR);
 		}
 
 
@@ -1772,6 +1780,8 @@
 		 * of each header's length, so we can parse them quickly.       *
 		 ****************************************************************/
 
+		t->analysis &= ~AN_REQ_HTTP_HDR;
+
 		/* ensure we keep this pointer to the beginning of the message */
 		msg->sol = req->data + msg->som;
 
@@ -2334,6 +2344,7 @@
 				http_find_header2("Transfer-Encoding", 17, msg->sol, &txn->hdr_idx, &ctx);
 				if (unlikely(ctx.idx && strncasecmp(ctx.line+ctx.val,"chunked",7)==0)) {
 					t->srv_state = SV_STANALYZE;
+					t->analysis |= AN_REQ_HTTP_BODY;
 				} else {
 					ctx.idx = 0;
 					http_find_header2("Content-Length", 14, msg->sol, &txn->hdr_idx, &ctx);
@@ -2353,8 +2364,10 @@
 					if ( t->be->url_param_post_limit < hint )
 						hint = t->be->url_param_post_limit;
 					/* now do we really need to buffer more data? */
-					if ( len < hint )
+					if ( len < hint ) {
 						t->srv_state = SV_STANALYZE;
+						t->analysis |= AN_REQ_HTTP_BODY;
+					}
 					/* else... There are no body bytes to wait for */
 				}
 			}
@@ -2366,9 +2379,7 @@
 		 * could. Let's switch to the DATA state.                    *
 		 ************************************************************/
 
-		t->cli_state = CL_STDATA;
 		req->flags |= BF_MAY_CONNECT | BF_MAY_FORWARD;
-
 		req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
 
 		t->logs.tv_request = now;
@@ -2416,9 +2427,9 @@
 		if (!(t->flags & SN_FINST_MASK))
 			t->flags |= SN_FINST_R;
 		return 1;
-
 	}
-	else if (c == CL_STDATA) {
+
+	if (c == CL_STDATA) {
 	process_data:
 		/* FIXME: this error handling is partly buggy because we always report
 		 * a 'DATA' phase while we don't know if the server was in IDLE, CONN
@@ -5239,6 +5250,7 @@
 		msg.len = sprintf(trash, HTTP_401_fmt, uri_auth->auth_realm);
 		txn->status = 401;
 		client_retnclose(t, &msg);
+		t->analysis &= ~AN_REQ_ANY;
 		if (!(t->flags & SN_ERR_MASK))
 			t->flags |= SN_ERR_PRXCOND;
 		if (!(t->flags & SN_FINST_MASK))
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 8c89241..f3938ea 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -412,6 +412,7 @@
 		}
 
 		s->flags = 0;
+		s->analysis = 0;
 
 		if ((t = pool_alloc2(pool2_task)) == NULL) {
 			Alert("out of memory in uxst_event_accept().\n");