[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/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");