[MAJOR] get rid of the SV_STHEADERS state

The HTTP response code has been moved to a specific function
called "process_response" and the SV_STHEADERS state has been
removed and replaced with the flag AN_RTR_HTTP_HDR.
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index e349f19..fc1e01d 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -62,6 +62,7 @@
 int process_cli(struct session *t);
 int process_srv(struct session *t);
 int process_request(struct session *t);
+int process_response(struct session *t);
 
 void client_retnclose(struct session *s, const struct chunk *msg);
 void client_return(struct session *s, const struct chunk *msg);
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index d592b71..441b8dc 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -40,11 +40,10 @@
 /* different possible states for the server side */
 #define SV_STIDLE	0
 #define SV_STCONN	1
-#define SV_STHEADERS	2
-#define SV_STDATA	3
-#define SV_STSHUTR	4
-#define SV_STSHUTW	5
-#define SV_STCLOSE	6
+#define SV_STDATA	2
+#define SV_STSHUTR	3
+#define SV_STSHUTW	4
+#define SV_STCLOSE	5
 
 /*
  * Transaction flags moved from session
diff --git a/src/proto_http.c b/src/proto_http.c
index e323b1d..a027504 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -366,7 +366,7 @@
 
 #ifdef DEBUG_FULL
 static char *cli_stnames[4] = { "DAT", "SHR", "SHW", "CLS" };
-static char *srv_stnames[7] = { "IDL", "CON", "HDR", "DAT", "SHR", "SHW", "CLS" };
+static char *srv_stnames[6] = { "IDL", "CON", "DAT", "SHR", "SHW", "CLS" };
 #endif
 
 static void http_sess_log(struct session *s);
@@ -669,6 +669,10 @@
 		//fprintf(stderr,"before_srv:cli=%d, srv=%d, resync=%d\n", s->cli_state, s->srv_state, fsm_resync);
 		fsm_resync |= process_srv(s);
 
+		//fprintf(stderr,"before_rep:cli=%d, srv=%d, resync=%d\n", s->cli_state, s->srv_state, fsm_resync);
+		if (s->analysis & AN_RTR_ANY)
+			fsm_resync |= process_response(s);
+
 		//fprintf(stderr,"endof_loop:cli=%d, srv=%d, resync=%d\n", s->cli_state, s->srv_state, fsm_resync);
 	} while (fsm_resync);
 
@@ -2504,1041 +2508,1071 @@
 	return fsm_resync;
 }
 
-/*
- * manages the client FSM and its socket. BTW, it also tries to handle the
- * cookie. It returns 1 if a state has changed (and a resync may be needed),
- * 0 else.
+/* This function performs all the processing enabled for the current response.
+ * It returns 1 if it changes its state and it believes that another function
+ * must be updated, otherwise zero. It might make sense to explode it into
+ * several other functions.
  */
-int process_cli(struct session *t)
+int process_response(struct session *t)
 {
-	int s = t->srv_state;
-	int c = t->cli_state;
+	struct http_txn *txn = &t->txn;
 	struct buffer *req = t->req;
 	struct buffer *rep = t->rep;
 
-	DPRINTF(stderr,"[%u] process_cli: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x\n",
+	DPRINTF(stderr,"[%u] process_rep: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x analysis=%02x\n",
 		now_ms,
 		cli_stnames[t->cli_state], srv_stnames[t->srv_state],
-		EV_FD_ISSET(t->cli_fd, DIR_RD), EV_FD_ISSET(t->cli_fd, DIR_WR),
-		req->rex, rep->wex,
-		req->flags, rep->flags);
+		EV_FD_ISSET(t->srv_fd, DIR_RD), EV_FD_ISSET(t->srv_fd, DIR_WR),
+		req->rex, rep->wex, req->flags, rep->flags, t->analysis);
 
-	/* if no analysis remains, it's time to forward the connection */
-	if (!(t->analysis & AN_REQ_ANY) && !(req->flags & (BF_MAY_CONNECT|BF_MAY_FORWARD)))
-		req->flags |= BF_MAY_CONNECT | BF_MAY_FORWARD;
+	if (t->analysis & AN_RTR_HTTP_HDR) { /* receiving server headers */
+		/*
+		 * Now parse the partial (or complete) lines.
+		 * We will check the response syntax, and also join multi-line
+		 * headers. An index of all the lines will be elaborated while
+		 * parsing.
+		 *
+		 * For the parsing, we use a 28 states FSM.
+		 *
+		 * Here is the information we currently have :
+		 *   rep->data + req->som  = beginning of response
+		 *   rep->data + req->eoh  = end of processed headers / start of current one
+		 *   rep->data + req->eol  = end of current header or line (LF or CRLF)
+		 *   rep->lr = first non-visited byte
+		 *   rep->r  = end of data
+		 */
 
-	if (c == CL_STDATA || c == CL_STSHUTR || c == CL_STSHUTW) {
-		/* read or write error */
-		if (rep->flags & BF_WRITE_ERROR || req->flags & BF_READ_ERROR) {
-			buffer_shutr_done(req);
-			buffer_shutw_done(rep);
-			fd_delete(t->cli_fd);
-			t->cli_state = CL_STCLOSE;
-			if (!(t->flags & SN_ERR_MASK))
-				t->flags |= SN_ERR_CLICL;
-			if (!(t->flags & SN_FINST_MASK)) {
-				if (t->analysis & AN_REQ_ANY)
-					t->flags |= SN_FINST_R;
-				else if (t->pend_pos)
-					t->flags |= SN_FINST_Q;
-				else if (s == SV_STCONN)
-					t->flags |= SN_FINST_C;
-				else
-					t->flags |= SN_FINST_D;
-			}
-			return 1;
-		}
-		/* last read, or end of server write */
-		else if (!(req->flags & BF_SHUTR_STATUS) &&   /* already done */
-			 req->flags & (BF_READ_NULL | BF_SHUTW_STATUS)) {
-			buffer_shutr_done(req);
-			if (!(rep->flags & BF_SHUTW_STATUS)) {
-				EV_FD_CLR(t->cli_fd, DIR_RD);
-				t->cli_state = CL_STSHUTR;
-			} else {
-				/* output was already closed */
-				fd_delete(t->cli_fd);
-				t->cli_state = CL_STCLOSE;
-			}
-			return 1;
-		}	
-		/* last server read and buffer empty */
-		else if (!(rep->flags & BF_SHUTW_STATUS) &&   /* already done */
-			 rep->l == 0 && rep->flags & BF_SHUTR_STATUS && !(t->flags & SN_SELF_GEN)) {
-			buffer_shutw_done(rep);
-			if (!(req->flags & BF_SHUTR_STATUS)) {
-				EV_FD_CLR(t->cli_fd, DIR_WR);
-				shutdown(t->cli_fd, SHUT_WR);
-				/* We must ensure that the read part is still alive when switching to shutw */
-				/* FIXME: is this still true ? */
-				EV_FD_SET(t->cli_fd, DIR_RD);
-				req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
-				t->cli_state = CL_STSHUTW;
-			} else {
-				fd_delete(t->cli_fd);
-				t->cli_state = CL_STCLOSE;
+		int cur_idx;
+		struct http_msg *msg = &txn->rsp;
+		struct proxy *cur_proxy;
+
+		if (likely(rep->lr < rep->r))
+			http_msg_analyzer(rep, msg, &txn->hdr_idx);
+
+		/* 1: we might have to print this header in debug mode */
+		if (unlikely((global.mode & MODE_DEBUG) &&
+			     (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) &&
+			     (msg->msg_state == HTTP_MSG_BODY || msg->msg_state == HTTP_MSG_ERROR))) {
+			char *eol, *sol;
+
+			sol = rep->data + msg->som;
+			eol = sol + msg->sl.rq.l;
+			debug_hdr("srvrep", t, sol, eol);
+
+			sol += hdr_idx_first_pos(&txn->hdr_idx);
+			cur_idx = hdr_idx_first_idx(&txn->hdr_idx);
+
+			while (cur_idx) {
+				eol = sol + txn->hdr_idx.v[cur_idx].len;
+				debug_hdr("srvhdr", t, sol, eol);
+				sol = eol + txn->hdr_idx.v[cur_idx].cr + 1;
+				cur_idx = txn->hdr_idx.v[cur_idx].next;
 			}
-			return 1;
 		}
-		/* read timeout */
-		else if (tick_is_expired(req->rex, now_ms)) {
-			buffer_shutr_done(req);
-			req->flags |= BF_READ_TIMEOUT;
-			if (!(rep->flags & BF_SHUTW_STATUS)) {
-				EV_FD_CLR(t->cli_fd, DIR_RD);
-				t->cli_state = CL_STSHUTR;
-			} else {
-				/* output was already closed */
-				fd_delete(t->cli_fd);
-				t->cli_state = CL_STCLOSE;
-			}
-			if (!(t->flags & SN_ERR_MASK))
-				t->flags |= SN_ERR_CLITO;
-			if (!(t->flags & SN_FINST_MASK)) {
-				if (t->analysis & AN_REQ_ANY)
-					t->flags |= SN_FINST_R;
-				else if (t->pend_pos)
-					t->flags |= SN_FINST_Q;
-				else if (s == SV_STCONN)
-					t->flags |= SN_FINST_C;
-				else
-					t->flags |= SN_FINST_D;
-			}
-			return 1;
-		}	
-		/* write timeout */
-		else if (tick_is_expired(rep->wex, now_ms)) {
-			buffer_shutw_done(rep);
-			rep->flags |= BF_WRITE_TIMEOUT;
-			if (!(req->flags & BF_SHUTR_STATUS)) {
-				EV_FD_CLR(t->cli_fd, DIR_WR);
-				shutdown(t->cli_fd, SHUT_WR);
-				/* We must ensure that the read part is still alive when switching to shutw */
-				/* FIXME: is this still true ? */
-				EV_FD_SET(t->cli_fd, DIR_RD);
-				req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
-				t->cli_state = CL_STSHUTW;
-			} else {
-				fd_delete(t->cli_fd);
-				t->cli_state = CL_STCLOSE;
-			}
 
-			if (!(t->flags & SN_ERR_MASK))
-				t->flags |= SN_ERR_CLITO;
-			if (!(t->flags & SN_FINST_MASK)) {
-				if (t->analysis & AN_REQ_ANY)
-					t->flags |= SN_FINST_R;
-				else if (t->pend_pos)
-					t->flags |= SN_FINST_Q;
-				else if (s == SV_STCONN)
-					t->flags |= SN_FINST_C;
-				else
-					t->flags |= SN_FINST_D;
-			}
-			return 1;
+
+		if ((rep->l < rep->rlim - rep->data) && !tick_isset(rep->rex)) {
+			EV_FD_COND_S(t->srv_fd, DIR_RD);
+			/* fd in DIR_RD was disabled, perhaps because of a previous buffer
+			 * full. We cannot loop here since stream_sock_read will disable it only if
+			 * rep->l == rlim-data
+			 */
+			rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
 		}
 
-		/* manage read timeout */
-		if (!(req->flags & BF_SHUTR_STATUS)) {
-			if (req->l >= req->rlim - req->data) {
-				/* no room to read more data */
-				if (EV_FD_COND_C(t->cli_fd, DIR_RD)) {
-					/* stop reading until we get some space */
-					req->rex = TICK_ETERNITY;
+
+		/*
+		 * Now we quickly check if we have found a full valid response.
+		 * If not so, we check the FD and buffer states before leaving.
+		 * A full response is indicated by the fact that we have seen
+		 * the double LF/CRLF, so the state is HTTP_MSG_BODY. Invalid
+		 * responses are checked first.
+		 *
+		 * Depending on whether the client is still there or not, we
+		 * may send an error response back or not. Note that normally
+		 * we should only check for HTTP status there, and check I/O
+		 * errors somewhere else.
+		 */
+
+		if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
+
+			/* Invalid response, or read error or write error */
+			if (unlikely((msg->msg_state == HTTP_MSG_ERROR) ||
+			             (req->flags & BF_WRITE_ERROR) ||
+			             (rep->flags & BF_READ_ERROR))) {
+				buffer_shutr_done(rep);
+				buffer_shutw_done(req);
+				fd_delete(t->srv_fd);
+				if (t->srv) {
+					t->srv->cur_sess--;
+					t->srv->failed_resp++;
+					sess_change_server(t, NULL);
 				}
-			} else {
-				EV_FD_COND_S(t->cli_fd, DIR_RD);
-				req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
+				t->be->failed_resp++;
+				t->srv_state = SV_STCLOSE;
+				t->analysis &= ~AN_RTR_ANY;
+				rep->flags |= BF_MAY_FORWARD;
+				txn->status = 502;
+				client_return(t, error_message(t, HTTP_ERR_502));
+				if (!(t->flags & SN_ERR_MASK))
+					t->flags |= SN_ERR_SRVCL;
+				if (!(t->flags & SN_FINST_MASK))
+					t->flags |= SN_FINST_H;
+				/* We used to have a free connection slot. Since we'll never use it,
+				 * we have to inform the server that it may be used by another session.
+				 */
+				if (t->srv && may_dequeue_tasks(t->srv, t->be))
+					process_srv_queue(t->srv);
+
+				return 1;
 			}
-		}
 
-		/* manage write timeout */
-		if (!(rep->flags & BF_SHUTW_STATUS)) {
-			/* first, we may have to produce data (eg: stats).
-			 * right now, this is limited to the SHUTR state.
+			/* end of client write or end of server read.
+			 * since we are in header mode, if there's no space left for headers, we
+			 * won't be able to free more later, so the session will never terminate.
 			 */
-			if (req->flags & BF_SHUTR_STATUS && t->flags & SN_SELF_GEN) {
-				produce_content(t);
-				if (rep->l == 0) {
-					buffer_shutw_done(rep);
-					fd_delete(t->cli_fd);
-					t->cli_state = CL_STCLOSE;
-					return 1;
-				}
+			else if (unlikely(rep->flags & (BF_READ_NULL | BF_SHUTW_STATUS) ||
+			                  rep->l >= rep->rlim - rep->data)) {
+				EV_FD_CLR(t->srv_fd, DIR_RD);
+				buffer_shutr_done(rep);
+				t->srv_state = SV_STSHUTR;
+				t->analysis &= ~AN_RTR_ANY;
+				//fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
+				return 1;
 			}
 
-			/* we don't enable client write if the buffer is empty, nor if the server has to analyze it */
-			if ((rep->l == 0) || !(rep->flags & BF_MAY_FORWARD)) {
-				if (EV_FD_COND_C(t->cli_fd, DIR_WR)) {
-					/* stop writing */
-					rep->wex = TICK_ETERNITY;
-				}
-			} else {
-				/* buffer not empty */
-				if (!tick_isset(rep->wex)) {
-					EV_FD_COND_S(t->cli_fd, DIR_WR);
-					/* restart writing */
-					rep->wex = tick_add_ifset(now_ms, t->fe->timeout.client);
-					if (!(req->flags & BF_SHUTR_STATUS) && tick_isset(rep->wex) && tick_isset(req->rex)) {
-						/* FIXME: to prevent the client from expiring read timeouts during writes,
-						 * we refresh it, except if it was already infinite. */
-						req->rex = rep->wex;
-					}
+			/* read timeout : return a 504 to the client. */
+			else if (unlikely(EV_FD_ISSET(t->srv_fd, DIR_RD) &&
+			                  tick_is_expired(rep->rex, now_ms))) {
+				buffer_shutr_done(rep);
+				buffer_shutw_done(req);
+				fd_delete(t->srv_fd);
+				if (t->srv) {
+					t->srv->cur_sess--;
+					t->srv->failed_resp++;
+					sess_change_server(t, NULL);
 				}
+				t->be->failed_resp++;
+				t->srv_state = SV_STCLOSE;
+				t->analysis &= ~AN_RTR_ANY;
+				rep->flags |= BF_MAY_FORWARD;
+				txn->status = 504;
+				client_return(t, error_message(t, HTTP_ERR_504));
+				if (!(t->flags & SN_ERR_MASK))
+					t->flags |= SN_ERR_SRVTO;
+				if (!(t->flags & SN_FINST_MASK))
+					t->flags |= SN_FINST_H;
+				/* We used to have a free connection slot. Since we'll never use it,
+				 * we have to inform the server that it may be used by another session.
+				 */
+				if (t->srv && may_dequeue_tasks(t->srv, t->be))
+					process_srv_queue(t->srv);
+				return 1;
 			}
-		}
-		return 0; /* other cases change nothing */
-	}
-	else { /* CL_STCLOSE: nothing to do */
-		if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
-			int len;
-			len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->be->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
-			write(1, trash, len);
-		}
-		return 0;
-	}
-	return 0;
-}
 
+			/* last client read and buffer empty */
+			/* FIXME!!! here, we don't want to switch to SHUTW if the
+			 * client shuts read too early, because we may still have
+			 * some work to do on the headers.
+			 * The side-effect is that if the client completely closes its
+			 * connection during SV_STHEADER, the connection to the server
+			 * is kept until a response comes back or the timeout is reached.
+			 * This sometimes causes fast loops when the request buffer is
+			 * full, so we still perform the transition right now. It will
+			 * make sense later anyway.
+			 */
+			else if (unlikely(req->flags & BF_SHUTR_STATUS && (req->l == 0))) {
 
-/*
- * manages the server FSM and its socket. It returns 1 if a state has changed
- * (and a resync may be needed), 0 else.
- */
-int process_srv(struct session *t)
-{
-	int s = t->srv_state;
-	struct http_txn *txn = &t->txn;
-	struct buffer *req = t->req;
-	struct buffer *rep = t->rep;
-	int conn_err;
+				EV_FD_CLR(t->srv_fd, DIR_WR);
+				buffer_shutw_done(req);
 
-	DPRINTF(stderr,"[%u] process_srv: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x\n",
-		now_ms,
-		cli_stnames[t->cli_state], srv_stnames[t->srv_state],
-		EV_FD_ISSET(t->srv_fd, DIR_RD), EV_FD_ISSET(t->srv_fd, DIR_WR),
-		rep->rex, req->wex,
-		req->flags, rep->flags);
+				/* We must ensure that the read part is still
+				 * alive when switching to shutw */
+				EV_FD_SET(t->srv_fd, DIR_RD);
+				rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
 
-	if (s == SV_STIDLE) {
-		if ((rep->flags & BF_SHUTW_STATUS) ||
-			 ((req->flags & BF_SHUTR_STATUS) &&
-			  (req->l == 0 || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
-			req->cex = TICK_ETERNITY;
-			if (t->pend_pos)
-				t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-			/* note that this must not return any error because it would be able to
-			 * overwrite the client_retnclose() output.
-			 */
-			if (txn->flags & TX_CLTARPIT)
-				srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, NULL);
-			else
-				srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, NULL);
+				shutdown(t->srv_fd, SHUT_WR);
+				t->srv_state = SV_STSHUTW;
+				t->analysis &= ~AN_RTR_ANY;
+				return 1;
+			}
 
-			return 1;
-		}
-		else if (req->flags & BF_MAY_CONNECT) {
-			/* the client allows the server to connect */
-			if (txn->flags & TX_CLTARPIT) {
-				/* This connection is being tarpitted. The CLIENT side has
-				 * already set the connect expiration date to the right
-				 * timeout. We just have to check that it has not expired.
-				 */
-				if (!tick_is_expired(req->cex, now_ms))
-					return 0;
+			/* write timeout */
+			/* FIXME!!! here, we don't want to switch to SHUTW if the
+			 * client shuts read too early, because we may still have
+			 * some work to do on the headers.
+			 */
+			else if (unlikely(EV_FD_ISSET(t->srv_fd, DIR_WR) &&
+					  tick_is_expired(req->wex, now_ms))) {
+				EV_FD_CLR(t->srv_fd, DIR_WR);
+				buffer_shutw_done(req);
+				shutdown(t->srv_fd, SHUT_WR);
+				/* We must ensure that the read part is still alive
+				 * when switching to shutw */
+				EV_FD_SET(t->srv_fd, DIR_RD);
+				rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
 
-				/* We will set the queue timer to the time spent, just for
-				 * logging purposes. We fake a 500 server error, so that the
-				 * attacker will not suspect his connection has been tarpitted.
-				 * It will not cause trouble to the logs because we can exclude
-				 * the tarpitted connections by filtering on the 'PT' status flags.
-				 */
-				req->cex = TICK_ETERNITY;
-				t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-				srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T,
-						   500, error_message(t, HTTP_ERR_500));
+				t->srv_state = SV_STSHUTW;
+				t->analysis &= ~AN_RTR_ANY;
+				if (!(t->flags & SN_ERR_MASK))
+					t->flags |= SN_ERR_SRVTO;
+				if (!(t->flags & SN_FINST_MASK))
+					t->flags |= SN_FINST_H;
 				return 1;
 			}
 
-			/* Right now, we will need to create a connection to the server.
-			 * We might already have tried, and got a connection pending, in
-			 * which case we will not do anything till it's pending. It's up
-			 * to any other session to release it and wake us up again.
+			/*
+			 * And now the non-error cases.
 			 */
-			if (t->pend_pos) {
-				if (!tick_is_expired(req->cex, now_ms)) {
-					return 0;
-				} else {
-					/* we've been waiting too long here */
-					req->cex = TICK_ETERNITY;
-					t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-					srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q,
-							   503, error_message(t, HTTP_ERR_503));
-					if (t->srv)
-						t->srv->failed_conns++;
-					t->be->failed_conns++;
-					return 1;
+
+			/* Data remaining in the request buffer.
+			 * This happens during the first pass here, and during
+			 * long posts.
+			 */
+			else if (likely(req->l)) {
+				if (!tick_isset(req->wex)) {
+					EV_FD_COND_S(t->srv_fd, DIR_WR);
+					/* restart writing */
+					req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
+					if (tick_isset(req->wex)) {
+						/* FIXME: to prevent the server from expiring read timeouts during writes,
+						 * we refresh it. */
+						rep->rex = req->wex;
+					}
 				}
 			}
 
-			do {
-				/* first, get a connection */
-				if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
-					t->flags |= SN_REDIRECTABLE;
+			/* nothing left in the request buffer */
+			else {
+				if (EV_FD_COND_C(t->srv_fd, DIR_WR)) {
+					/* stop writing */
+					req->wex = TICK_ETERNITY;
+				}
+			}
 
-				if (srv_redispatch_connect(t))
-					return t->srv_state != SV_STIDLE;
+			/* return 0 if nothing changed */
+			return !(t->analysis & AN_RTR_ANY);
+		}
 
-				if ((t->flags & SN_REDIRECTABLE) && t->srv && t->srv->rdr_len) {
-					/* Server supporting redirection and it is possible.
-					 * Invalid requests are reported as such. It concerns all
-					 * the largest ones.
-					 */
-					struct chunk rdr;
-					char *path;
-					int len;
 
-					/* 1: create the response header */
-					rdr.len = strlen(HTTP_302);
-					rdr.str = trash;
-					memcpy(rdr.str, HTTP_302, rdr.len);
+		/*****************************************************************
+		 * More interesting part now : we know that we have a complete   *
+		 * response which at least looks like HTTP. We have an indicator *
+		 * of each header's length, so we can parse them quickly.        *
+		 ****************************************************************/
 
-					/* 2: add the server's prefix */
-					if (rdr.len + t->srv->rdr_len > sizeof(trash))
-						goto cancel_redir;
+		/* ensure we keep this pointer to the beginning of the message */
+		msg->sol = rep->data + msg->som;
 
-					memcpy(rdr.str + rdr.len, t->srv->rdr_pfx, t->srv->rdr_len);
-					rdr.len += t->srv->rdr_len;
+		/*
+		 * 1: get the status code and check for cacheability.
+		 */
 
-					/* 3: add the request URI */
-					path = http_get_path(txn);
-					if (!path)
-						goto cancel_redir;
-					len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
-					if (rdr.len + len > sizeof(trash) - 4) /* 4 for CRLF-CRLF */
-						goto cancel_redir;
+		t->logs.logwait &= ~LW_RESP;
+		txn->status = strl2ui(rep->data + msg->sl.st.c, msg->sl.st.c_l);
 
-					memcpy(rdr.str + rdr.len, path, len);
-					rdr.len += len;
-					memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
-					rdr.len += 4;
+		switch (txn->status) {
+		case 200:
+		case 203:
+		case 206:
+		case 300:
+		case 301:
+		case 410:
+			/* RFC2616 @13.4:
+			 *   "A response received with a status code of
+			 *    200, 203, 206, 300, 301 or 410 MAY be stored
+			 *    by a cache (...) unless a cache-control
+			 *    directive prohibits caching."
+			 *   
+			 * RFC2616 @9.5: POST method :
+			 *   "Responses to this method are not cacheable,
+			 *    unless the response includes appropriate
+			 *    Cache-Control or Expires header fields."
+			 */
+			if (likely(txn->meth != HTTP_METH_POST) &&
+			    (t->be->options & (PR_O_CHK_CACHE|PR_O_COOK_NOC)))
+				txn->flags |= TX_CACHEABLE | TX_CACHE_COOK;
+			break;
+		default:
+			break;
+		}
 
-					srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr);
-					/* FIXME: we should increase a counter of redirects per server and per backend. */
-					if (t->srv)
-						t->srv->cum_sess++;
-					return 1;
-				cancel_redir:
-					txn->status = 400;
-					t->fe->failed_req++;
-					srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C,
-							   400, error_message(t, HTTP_ERR_400));
+		/*
+		 * 2: we may need to capture headers
+		 */
+		if (unlikely((t->logs.logwait & LW_RSPHDR) && t->fe->rsp_cap))
+			capture_headers(rep->data + msg->som, &txn->hdr_idx,
+					txn->rsp.cap, t->fe->rsp_cap);
+
+		/*
+		 * 3: we will have to evaluate the filters.
+		 * As opposed to version 1.2, now they will be evaluated in the
+		 * filters order and not in the header order. This means that
+		 * each filter has to be validated among all headers.
+		 *
+		 * Filters are tried with ->be first, then with ->fe if it is
+		 * different from ->be.
+		 */
+
+		t->flags &= ~SN_CONN_CLOSED; /* prepare for inspection */
+
+		cur_proxy = t->be;
+		while (1) {
+			struct proxy *rule_set = cur_proxy;
+
+			/* try headers filters */
+			if (rule_set->rsp_exp != NULL) {
+				if (apply_filters_to_response(t, rep, rule_set->rsp_exp) < 0) {
+				return_bad_resp:
+					if (t->srv) {
+						t->srv->cur_sess--;
+						t->srv->failed_resp++;
+						sess_change_server(t, NULL);
+					}
+					cur_proxy->failed_resp++;
+				return_srv_prx_502:
+					buffer_shutr_done(rep);
+					buffer_shutw_done(req);
+					fd_delete(t->srv_fd);
+					t->srv_state = SV_STCLOSE;
+					t->analysis &= ~AN_RTR_ANY;
+					rep->flags |= BF_MAY_FORWARD;
+					txn->status = 502;
+					client_return(t, error_message(t, HTTP_ERR_502));
+					if (!(t->flags & SN_ERR_MASK))
+						t->flags |= SN_ERR_PRXCOND;
+					if (!(t->flags & SN_FINST_MASK))
+						t->flags |= SN_FINST_H;
+					/* We used to have a free connection slot. Since we'll never use it,
+					 * we have to inform the server that it may be used by another session.
+					 */
+					if (t->srv && may_dequeue_tasks(t->srv, cur_proxy))
+						process_srv_queue(t->srv);
 					return 1;
 				}
+			}
 
-				/* try to (re-)connect to the server, and fail if we expire the
-				 * number of retries.
-				 */
-				if (srv_retryable_connect(t)) {
-					t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-					return t->srv_state != SV_STIDLE;
-				}
-			} while (1);
-		}
-	}
-	else if (s == SV_STCONN) { /* connection in progress */
-		if ((rep->flags & BF_SHUTW_STATUS) ||
-		    ((req->flags & BF_SHUTR_STATUS) &&
-		     ((req->l == 0 && !(req->flags & BF_WRITE_STATUS)) ||
-		      t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
-			req->cex = TICK_ETERNITY;
-			if (!(t->flags & SN_CONN_TAR)) {
-				/* if we are in turn-around, we have already closed the FD */
-				fd_delete(t->srv_fd);
+			/* has the response been denied ? */
+			if (txn->flags & TX_SVDENY) {
 				if (t->srv) {
 					t->srv->cur_sess--;
+					t->srv->failed_secu++;
 					sess_change_server(t, NULL);
 				}
+				cur_proxy->denied_resp++;
+				goto return_srv_prx_502;
 			}
 
-			/* note that this must not return any error because it would be able to
-			 * overwrite the client_retnclose() output.
-			 */
-			srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL);
-			return 1;
-		}
-		if (!(req->flags & BF_WRITE_STATUS) && !tick_is_expired(req->cex, now_ms)) {
-			return 0; /* nothing changed */
-		}
-		else if (!(req->flags & BF_WRITE_STATUS) || (req->flags & BF_WRITE_ERROR)) {
-			/* timeout, asynchronous connect error or first write error */
-			//fprintf(stderr,"2: c=%d, s=%d\n", c, s);
-
-			if (t->flags & SN_CONN_TAR) {
-				/* We are doing a turn-around waiting for a new connection attempt. */
-				if (!tick_is_expired(req->cex, now_ms))
-					return 0;
-				t->flags &= ~SN_CONN_TAR;
-			}
-			else {
-				fd_delete(t->srv_fd);
-				if (t->srv) {
-					t->srv->cur_sess--;
-					sess_change_server(t, NULL);
-				}
+			/* We might have to check for "Connection:" */
+			if (((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
+			    !(t->flags & SN_CONN_CLOSED)) {
+				char *cur_ptr, *cur_end, *cur_next;
+				int cur_idx, old_idx, delta, val;
+				struct hdr_idx_elem *cur_hdr;
 
-				if (!(req->flags & BF_WRITE_STATUS))
-					conn_err = SN_ERR_SRVTO; // it was a connect timeout.
-				else
-					conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error.
+				cur_next = rep->data + txn->rsp.som + hdr_idx_first_pos(&txn->hdr_idx);
+				old_idx = 0;
 
-				/* ensure that we have enough retries left */
-				if (srv_count_retry_down(t, conn_err))
-					return 1;
+				while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
+					cur_hdr  = &txn->hdr_idx.v[cur_idx];
+					cur_ptr  = cur_next;
+					cur_end  = cur_ptr + cur_hdr->len;
+					cur_next = cur_end + cur_hdr->cr + 1;
 
-				if (req->flags & BF_WRITE_ERROR) {
-					/* we encountered an immediate connection error, and we
-					 * will have to retry connecting to the same server, most
-					 * likely leading to the same result. To avoid this, we
-					 * fake a connection timeout to retry after a turn-around
-					 * time of 1 second. We will wait in the previous if block.
-					 */
-					t->flags |= SN_CONN_TAR;
-					req->cex = tick_add(now_ms, MS_TO_TICKS(1000));
-					return 0;
+					val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
+					if (val) {
+						/* 3 possibilities :
+						 * - we have already set Connection: close,
+						 *   so we remove this line.
+						 * - we have not yet set Connection: close,
+						 *   but this line indicates close. We leave
+						 *   it untouched and set the flag.
+						 * - we have not yet set Connection: close,
+						 *   and this line indicates non-close. We
+						 *   replace it.
+						 */
+						if (t->flags & SN_CONN_CLOSED) {
+							delta = buffer_replace2(rep, cur_ptr, cur_next, NULL, 0);
+							txn->rsp.eoh += delta;
+							cur_next += delta;
+							txn->hdr_idx.v[old_idx].next = cur_hdr->next;
+							txn->hdr_idx.used--;
+							cur_hdr->len = 0;
+						} else {
+							if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
+								delta = buffer_replace2(rep, cur_ptr + val, cur_end,
+											"close", 5);
+								cur_next += delta;
+								cur_hdr->len += delta;
+								txn->rsp.eoh += delta;
+							}
+							t->flags |= SN_CONN_CLOSED;
+						}
+					}
+					old_idx = cur_idx;
 				}
 			}
 
-			if (t->srv && t->conn_retries == 0 && t->be->options & PR_O_REDISP) {
-				/* We're on our last chance, and the REDISP option was specified.
-				 * We will ignore cookie and force to balance or use the dispatcher.
-				 */
-				/* let's try to offer this slot to anybody */
-				if (may_dequeue_tasks(t->srv, t->be))
-					process_srv_queue(t->srv);
-
-				/* it's left to the dispatcher to choose a server */
-				t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
-				t->prev_srv = t->srv;
-
-				/* first, get a connection */
-				if (srv_redispatch_connect(t))
-					return t->srv_state != SV_STCONN;
-			} else {
-				if (t->srv)
-					t->srv->retries++;
-				t->be->retries++;
+			/* add response headers from the rule sets in the same order */
+			for (cur_idx = 0; cur_idx < rule_set->nb_rspadd; cur_idx++) {
+				if (unlikely(http_header_add_tail(rep, &txn->rsp, &txn->hdr_idx,
+								  rule_set->rsp_add[cur_idx])) < 0)
+					goto return_bad_resp;
 			}
 
-			do {
-				/* Now we will try to either reconnect to the same server or
-				 * connect to another server. If the connection gets queued
-				 * because all servers are saturated, then we will go back to
-				 * the SV_STIDLE state.
-				 */
-				if (srv_retryable_connect(t)) {
-					t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
-					return t->srv_state != SV_STCONN;
-				}
-
-				/* we need to redispatch the connection to another server */
-				if (srv_redispatch_connect(t))
-					return t->srv_state != SV_STCONN;
-			} while (1);
+			/* check whether we're already working on the frontend */
+			if (cur_proxy == t->fe)
+				break;
+			cur_proxy = t->fe;
 		}
-		else { /* no error or write 0 */
-			t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now);
 
-			//fprintf(stderr,"3: c=%d, s=%d\n", c, s);
-			if (req->l == 0) /* nothing to write */ {
-				EV_FD_CLR(t->srv_fd, DIR_WR);
-				req->wex = TICK_ETERNITY;
-			} else  /* need the right to write */ {
-				EV_FD_SET(t->srv_fd, DIR_WR);
-				req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
-				if (tick_isset(req->wex)) {
-					/* FIXME: to prevent the server from expiring read timeouts during writes,
-					 * we refresh it. */
-					rep->rex = req->wex;
-				}
-			}
+		/*
+		 * 4: check for server cookie.
+		 */
+		if (t->be->cookie_name || t->be->appsession_name || t->be->capture_name
+		    || (t->be->options & PR_O_CHK_CACHE))
+			manage_server_side_cookies(t, rep);
 
-			if (t->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
-				EV_FD_SET(t->srv_fd, DIR_RD);
-				rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
-				t->srv_state = SV_STDATA;
-				rep->flags |= BF_MAY_FORWARD;
-				rep->rlim = rep->data + BUFSIZE; /* no rewrite needed */
 
-				/* if the user wants to log as soon as possible, without counting
-				   bytes from the server, then this is the right moment. */
-				if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
-					t->logs.t_close = t->logs.t_connect; /* to get a valid end date */
-					tcp_sess_log(t);
-				}
-#ifdef CONFIG_HAP_TCPSPLICE
-				if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
-					/* TCP splicing supported by both FE and BE */
-					tcp_splice_splicefd(t->cli_fd, t->srv_fd, 0);
-				}
-#endif
-			}
-			else {
-				t->srv_state = SV_STHEADERS;
-				rep->rlim = rep->data + BUFSIZE - MAXREWRITE; /* rewrite needed */
-				t->txn.rsp.msg_state = HTTP_MSG_RPBEFORE;
-				/* reset hdr_idx which was already initialized by the request.
-				 * right now, the http parser does it.
-				 * hdr_idx_init(&t->txn.hdr_idx);
-				 */
-			}
-			req->cex = TICK_ETERNITY;
-			return 1;
-		}
-	}
-	else if (s == SV_STHEADERS) { /* receiving server headers */
 		/*
-		 * Now parse the partial (or complete) lines.
-		 * We will check the response syntax, and also join multi-line
-		 * headers. An index of all the lines will be elaborated while
-		 * parsing.
-		 *
-		 * For the parsing, we use a 28 states FSM.
-		 *
-		 * Here is the information we currently have :
-		 *   rep->data + req->som  = beginning of response
-		 *   rep->data + req->eoh  = end of processed headers / start of current one
-		 *   rep->data + req->eol  = end of current header or line (LF or CRLF)
-		 *   rep->lr = first non-visited byte
-		 *   rep->r  = end of data
+		 * 5: check for cache-control or pragma headers if required.
 		 */
+		if ((t->be->options & (PR_O_COOK_NOC | PR_O_CHK_CACHE)) != 0)
+			check_response_for_cacheability(t, rep);
 
-		int cur_idx;
-		struct http_msg *msg = &txn->rsp;
-		struct proxy *cur_proxy;
+		/*
+		 * 6: add server cookie in the response if needed
+		 */
+		if ((t->srv) && !(t->flags & SN_DIRECT) && (t->be->options & PR_O_COOK_INS) &&
+		    (!(t->be->options & PR_O_COOK_POST) || (txn->meth == HTTP_METH_POST))) {
+			int len;
 
-		if (likely(rep->lr < rep->r))
-			http_msg_analyzer(rep, msg, &txn->hdr_idx);
+			/* the server is known, it's not the one the client requested, we have to
+			 * insert a set-cookie here, except if we want to insert only on POST
+			 * requests and this one isn't. Note that servers which don't have cookies
+			 * (eg: some backup servers) will return a full cookie removal request.
+			 */
+			len = sprintf(trash, "Set-Cookie: %s=%s; path=/",
+				      t->be->cookie_name,
+				      t->srv->cookie ? t->srv->cookie : "; Expires=Thu, 01-Jan-1970 00:00:01 GMT");
 
-		/* 1: we might have to print this header in debug mode */
-		if (unlikely((global.mode & MODE_DEBUG) &&
-			     (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) &&
-			     (msg->msg_state == HTTP_MSG_BODY || msg->msg_state == HTTP_MSG_ERROR))) {
-			char *eol, *sol;
+			if (t->be->cookie_domain)
+				len += sprintf(trash+len, "; domain=%s", t->be->cookie_domain);
 
-			sol = rep->data + msg->som;
-			eol = sol + msg->sl.rq.l;
-			debug_hdr("srvrep", t, sol, eol);
+			if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
+							   trash, len)) < 0)
+				goto return_bad_resp;
+			txn->flags |= TX_SCK_INSERTED;
 
-			sol += hdr_idx_first_pos(&txn->hdr_idx);
-			cur_idx = hdr_idx_first_idx(&txn->hdr_idx);
+			/* Here, we will tell an eventual cache on the client side that we don't
+			 * want it to cache this reply because HTTP/1.0 caches also cache cookies !
+			 * Some caches understand the correct form: 'no-cache="set-cookie"', but
+			 * others don't (eg: apache <= 1.3.26). So we use 'private' instead.
+			 */
+			if ((t->be->options & PR_O_COOK_NOC) && (txn->flags & TX_CACHEABLE)) {
 
-			while (cur_idx) {
-				eol = sol + txn->hdr_idx.v[cur_idx].len;
-				debug_hdr("srvhdr", t, sol, eol);
-				sol = eol + txn->hdr_idx.v[cur_idx].cr + 1;
-				cur_idx = txn->hdr_idx.v[cur_idx].next;
+				txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
+
+				if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
+								   "Cache-control: private", 22)) < 0)
+					goto return_bad_resp;
 			}
 		}
 
 
-		if ((rep->l < rep->rlim - rep->data) && !tick_isset(rep->rex)) {
-			EV_FD_COND_S(t->srv_fd, DIR_RD);
-			/* fd in DIR_RD was disabled, perhaps because of a previous buffer
-			 * full. We cannot loop here since stream_sock_read will disable it only if
-			 * rep->l == rlim-data
+		/*
+		 * 7: check if result will be cacheable with a cookie.
+		 * We'll block the response if security checks have caught
+		 * nasty things such as a cacheable cookie.
+		 */
+		if (((txn->flags & (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) ==
+		     (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) &&
+		    (t->be->options & PR_O_CHK_CACHE)) {
+
+			/* we're in presence of a cacheable response containing
+			 * a set-cookie header. We'll block it as requested by
+			 * the 'checkcache' option, and send an alert.
 			 */
-			rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
-		}
+			if (t->srv) {
+				t->srv->cur_sess--;
+				t->srv->failed_secu++;
+				sess_change_server(t, NULL);
+			}
+			t->be->denied_resp++;
 
+			Alert("Blocking cacheable cookie in response from instance %s, server %s.\n",
+			      t->be->id, t->srv?t->srv->id:"<dispatch>");
+			send_log(t->be, LOG_ALERT,
+				 "Blocking cacheable cookie in response from instance %s, server %s.\n",
+				 t->be->id, t->srv?t->srv->id:"<dispatch>");
+			goto return_srv_prx_502;
+		}
 
 		/*
-		 * Now we quickly check if we have found a full valid response.
-		 * If not so, we check the FD and buffer states before leaving.
-		 * A full response is indicated by the fact that we have seen
-		 * the double LF/CRLF, so the state is HTTP_MSG_BODY. Invalid
-		 * responses are checked first.
-		 *
-		 * Depending on whether the client is still there or not, we
-		 * may send an error response back or not. Note that normally
-		 * we should only check for HTTP status there, and check I/O
-		 * errors somewhere else.
+		 * 8: add "Connection: close" if needed and not yet set.
+		 * Note that we do not need to add it in case of HTTP/1.0.
 		 */
+		if (!(t->flags & SN_CONN_CLOSED) &&
+		    ((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO))) {
+			if ((unlikely(msg->sl.st.v_l != 8) ||
+			     unlikely(req->data[msg->som + 7] != '0')) &&
+			    unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
+							   "Connection: close", 17)) < 0)
+				goto return_bad_resp;
+			t->flags |= SN_CONN_CLOSED;
+		}
 
-		if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
 
-			/* Invalid response, or read error or write error */
-			if (unlikely((msg->msg_state == HTTP_MSG_ERROR) ||
-			             (req->flags & BF_WRITE_ERROR) ||
-			             (rep->flags & BF_READ_ERROR))) {
-				buffer_shutr_done(rep);
-				buffer_shutw_done(req);
-				fd_delete(t->srv_fd);
-				if (t->srv) {
-					t->srv->cur_sess--;
-					t->srv->failed_resp++;
-					sess_change_server(t, NULL);
-				}
-				t->be->failed_resp++;
-				t->srv_state = SV_STCLOSE;
-				rep->flags |= BF_MAY_FORWARD;
-				txn->status = 502;
-				client_return(t, error_message(t, HTTP_ERR_502));
-				if (!(t->flags & SN_ERR_MASK))
-					t->flags |= SN_ERR_SRVCL;
-				if (!(t->flags & SN_FINST_MASK))
-					t->flags |= SN_FINST_H;
-				/* We used to have a free connection slot. Since we'll never use it,
-				 * we have to inform the server that it may be used by another session.
-				 */
-				if (t->srv && may_dequeue_tasks(t->srv, t->be))
-					process_srv_queue(t->srv);
+		/*************************************************************
+		 * OK, that's finished for the headers. We have done what we *
+		 * could. Let's switch to the DATA state.                    *
+		 ************************************************************/
 
-				return 1;
-			}
+		t->srv_state = SV_STDATA;
+		t->analysis &= ~AN_RTR_ANY;
+		rep->flags |= BF_MAY_FORWARD;
+		rep->rlim = rep->data + BUFSIZE; /* no more rewrite needed */
+		t->logs.t_data = tv_ms_elapsed(&t->logs.tv_accept, &now);
 
-			/* end of client write or end of server read.
-			 * since we are in header mode, if there's no space left for headers, we
-			 * won't be able to free more later, so the session will never terminate.
-			 */
-			else if (unlikely(rep->flags & (BF_READ_NULL | BF_SHUTW_STATUS) ||
-			                  rep->l >= rep->rlim - rep->data)) {
-				EV_FD_CLR(t->srv_fd, DIR_RD);
-				buffer_shutr_done(rep);
-				t->srv_state = SV_STSHUTR;
-				//fprintf(stderr,"%p:%s(%d), c=%d, s=%d\n", t, __FUNCTION__, __LINE__, t->cli_state, t->cli_state);
-				return 1;
-			}
+		/* client connection already closed or option 'forceclose' required :
+		 * we close the server's outgoing connection right now.
+		 */
+		if ((req->l == 0) &&
+		    (req->flags & BF_SHUTR_STATUS || t->be->options & PR_O_FORCE_CLO)) {
+			EV_FD_CLR(t->srv_fd, DIR_WR);
+			buffer_shutw_done(req);
 
-			/* read timeout : return a 504 to the client. */
-			else if (unlikely(EV_FD_ISSET(t->srv_fd, DIR_RD) &&
-			                  tick_is_expired(rep->rex, now_ms))) {
-				buffer_shutr_done(rep);
-				buffer_shutw_done(req);
-				fd_delete(t->srv_fd);
-				if (t->srv) {
-					t->srv->cur_sess--;
-					t->srv->failed_resp++;
-					sess_change_server(t, NULL);
-				}
-				t->be->failed_resp++;
-				t->srv_state = SV_STCLOSE;
-				rep->flags |= BF_MAY_FORWARD;
-				txn->status = 504;
-				client_return(t, error_message(t, HTTP_ERR_504));
-				if (!(t->flags & SN_ERR_MASK))
-					t->flags |= SN_ERR_SRVTO;
-				if (!(t->flags & SN_FINST_MASK))
-					t->flags |= SN_FINST_H;
-				/* We used to have a free connection slot. Since we'll never use it,
-				 * we have to inform the server that it may be used by another session.
-				 */
-				if (t->srv && may_dequeue_tasks(t->srv, t->be))
-					process_srv_queue(t->srv);
-				return 1;
-			}
+			/* We must ensure that the read part is still alive when switching
+			 * to shutw */
+			EV_FD_SET(t->srv_fd, DIR_RD);
+			rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
 
-			/* last client read and buffer empty */
-			/* FIXME!!! here, we don't want to switch to SHUTW if the
-			 * client shuts read too early, because we may still have
-			 * some work to do on the headers.
-			 * The side-effect is that if the client completely closes its
-			 * connection during SV_STHEADER, the connection to the server
-			 * is kept until a response comes back or the timeout is reached.
-			 * This sometimes causes fast loops when the request buffer is
-			 * full, so we still perform the transition right now. It will
-			 * make sense later anyway.
-			 */
-			else if (unlikely(req->flags & BF_SHUTR_STATUS && (req->l == 0))) {
+			shutdown(t->srv_fd, SHUT_WR);
+			t->srv_state = SV_STSHUTW;
+			t->analysis &= ~AN_RTR_ANY;
+		}
 
-				EV_FD_CLR(t->srv_fd, DIR_WR);
-				buffer_shutw_done(req);
+#ifdef CONFIG_HAP_TCPSPLICE
+		if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
+			/* TCP splicing supported by both FE and BE */
+			tcp_splice_splicefd(t->cli_fd, t->srv_fd, 0);
+		}
+#endif
+		/* if the user wants to log as soon as possible, without counting
+		 * bytes from the server, then this is the right moment. We have
+		 * to temporarily assign bytes_out to log what we currently have.
+		 */
+		if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
+			t->logs.t_close = t->logs.t_data; /* to get a valid end date */
+			t->logs.bytes_out = txn->rsp.eoh;
+			if (t->fe->to_log & LW_REQ)
+				http_sess_log(t);
+			else
+				tcp_sess_log(t);
+			t->logs.bytes_out = 0;
+		}
 
-				/* We must ensure that the read part is still
-				 * alive when switching to shutw */
-				EV_FD_SET(t->srv_fd, DIR_RD);
-				rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
-
-				shutdown(t->srv_fd, SHUT_WR);
-				t->srv_state = SV_STSHUTW;
-				return 1;
-			}
+		/* Note: we must not try to cheat by jumping directly to DATA,
+		 * otherwise we would not let the client side wake up.
+		 */
 
-			/* write timeout */
-			/* FIXME!!! here, we don't want to switch to SHUTW if the
-			 * client shuts read too early, because we may still have
-			 * some work to do on the headers.
-			 */
-			else if (unlikely(EV_FD_ISSET(t->srv_fd, DIR_WR) &&
-					  tick_is_expired(req->wex, now_ms))) {
-				EV_FD_CLR(t->srv_fd, DIR_WR);
-				buffer_shutw_done(req);
-				shutdown(t->srv_fd, SHUT_WR);
-				/* We must ensure that the read part is still alive
-				 * when switching to shutw */
-				EV_FD_SET(t->srv_fd, DIR_RD);
-				rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+		return 1;
+	}
+	return 0;
+}
 
-				t->srv_state = SV_STSHUTW;
-				if (!(t->flags & SN_ERR_MASK))
-					t->flags |= SN_ERR_SRVTO;
-				if (!(t->flags & SN_FINST_MASK))
-					t->flags |= SN_FINST_H;
-				return 1;
-			}
+/*
+ * manages the client FSM and its socket. BTW, it also tries to handle the
+ * cookie. It returns 1 if a state has changed (and a resync may be needed),
+ * 0 else.
+ */
+int process_cli(struct session *t)
+{
+	int s = t->srv_state;
+	int c = t->cli_state;
+	struct buffer *req = t->req;
+	struct buffer *rep = t->rep;
 
-			/*
-			 * And now the non-error cases.
-			 */
+	DPRINTF(stderr,"[%u] process_cli: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x\n",
+		now_ms,
+		cli_stnames[t->cli_state], srv_stnames[t->srv_state],
+		EV_FD_ISSET(t->cli_fd, DIR_RD), EV_FD_ISSET(t->cli_fd, DIR_WR),
+		req->rex, rep->wex,
+		req->flags, rep->flags);
 
-			/* Data remaining in the request buffer.
-			 * This happens during the first pass here, and during
-			 * long posts.
-			 */
-			else if (likely(req->l)) {
-				if (!tick_isset(req->wex)) {
-					EV_FD_COND_S(t->srv_fd, DIR_WR);
-					/* restart writing */
-					req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
-					if (tick_isset(req->wex)) {
-						/* FIXME: to prevent the server from expiring read timeouts during writes,
-						 * we refresh it. */
-						rep->rex = req->wex;
-					}
-				}
-			}
+	/* if no analysis remains, it's time to forward the connection */
+	if (!(t->analysis & AN_REQ_ANY) && !(req->flags & (BF_MAY_CONNECT|BF_MAY_FORWARD)))
+		req->flags |= BF_MAY_CONNECT | BF_MAY_FORWARD;
 
-			/* nothing left in the request buffer */
-			else {
-				if (EV_FD_COND_C(t->srv_fd, DIR_WR)) {
-					/* stop writing */
-					req->wex = TICK_ETERNITY;
-				}
+	if (c == CL_STDATA || c == CL_STSHUTR || c == CL_STSHUTW) {
+		/* read or write error */
+		if (rep->flags & BF_WRITE_ERROR || req->flags & BF_READ_ERROR) {
+			buffer_shutr_done(req);
+			buffer_shutw_done(rep);
+			fd_delete(t->cli_fd);
+			t->cli_state = CL_STCLOSE;
+			if (!(t->flags & SN_ERR_MASK))
+				t->flags |= SN_ERR_CLICL;
+			if (!(t->flags & SN_FINST_MASK)) {
+				if (t->analysis & AN_REQ_ANY)
+					t->flags |= SN_FINST_R;
+				else if (t->pend_pos)
+					t->flags |= SN_FINST_Q;
+				else if (s == SV_STCONN)
+					t->flags |= SN_FINST_C;
+				else
+					t->flags |= SN_FINST_D;
 			}
-
-			return t->srv_state != SV_STHEADERS;
+			return 1;
 		}
-
-
-		/*****************************************************************
-		 * More interesting part now : we know that we have a complete   *
-		 * response which at least looks like HTTP. We have an indicator *
-		 * of each header's length, so we can parse them quickly.        *
-		 ****************************************************************/
-
-		/* ensure we keep this pointer to the beginning of the message */
-		msg->sol = rep->data + msg->som;
-
-		/*
-		 * 1: get the status code and check for cacheability.
-		 */
-
-		t->logs.logwait &= ~LW_RESP;
-		txn->status = strl2ui(rep->data + msg->sl.st.c, msg->sl.st.c_l);
-
-		switch (txn->status) {
-		case 200:
-		case 203:
-		case 206:
-		case 300:
-		case 301:
-		case 410:
-			/* RFC2616 @13.4:
-			 *   "A response received with a status code of
-			 *    200, 203, 206, 300, 301 or 410 MAY be stored
-			 *    by a cache (...) unless a cache-control
-			 *    directive prohibits caching."
-			 *   
-			 * RFC2616 @9.5: POST method :
-			 *   "Responses to this method are not cacheable,
-			 *    unless the response includes appropriate
-			 *    Cache-Control or Expires header fields."
-			 */
-			if (likely(txn->meth != HTTP_METH_POST) &&
-			    (t->be->options & (PR_O_CHK_CACHE|PR_O_COOK_NOC)))
-				txn->flags |= TX_CACHEABLE | TX_CACHE_COOK;
-			break;
-		default:
-			break;
+		/* last read, or end of server write */
+		else if (!(req->flags & BF_SHUTR_STATUS) &&   /* already done */
+			 req->flags & (BF_READ_NULL | BF_SHUTW_STATUS)) {
+			buffer_shutr_done(req);
+			if (!(rep->flags & BF_SHUTW_STATUS)) {
+				EV_FD_CLR(t->cli_fd, DIR_RD);
+				t->cli_state = CL_STSHUTR;
+			} else {
+				/* output was already closed */
+				fd_delete(t->cli_fd);
+				t->cli_state = CL_STCLOSE;
+			}
+			return 1;
+		}	
+		/* last server read and buffer empty */
+		else if (!(rep->flags & BF_SHUTW_STATUS) &&   /* already done */
+			 rep->l == 0 && rep->flags & BF_SHUTR_STATUS && !(t->flags & SN_SELF_GEN)) {
+			buffer_shutw_done(rep);
+			if (!(req->flags & BF_SHUTR_STATUS)) {
+				EV_FD_CLR(t->cli_fd, DIR_WR);
+				shutdown(t->cli_fd, SHUT_WR);
+				/* We must ensure that the read part is still alive when switching to shutw */
+				/* FIXME: is this still true ? */
+				EV_FD_SET(t->cli_fd, DIR_RD);
+				req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
+				t->cli_state = CL_STSHUTW;
+			} else {
+				fd_delete(t->cli_fd);
+				t->cli_state = CL_STCLOSE;
+			}
+			return 1;
 		}
-
-		/*
-		 * 2: we may need to capture headers
-		 */
-		if (unlikely((t->logs.logwait & LW_RSPHDR) && t->fe->rsp_cap))
-			capture_headers(rep->data + msg->som, &txn->hdr_idx,
-					txn->rsp.cap, t->fe->rsp_cap);
-
-		/*
-		 * 3: we will have to evaluate the filters.
-		 * As opposed to version 1.2, now they will be evaluated in the
-		 * filters order and not in the header order. This means that
-		 * each filter has to be validated among all headers.
-		 *
-		 * Filters are tried with ->be first, then with ->fe if it is
-		 * different from ->be.
-		 */
-
-		t->flags &= ~SN_CONN_CLOSED; /* prepare for inspection */
-
-		cur_proxy = t->be;
-		while (1) {
-			struct proxy *rule_set = cur_proxy;
+		/* read timeout */
+		else if (tick_is_expired(req->rex, now_ms)) {
+			buffer_shutr_done(req);
+			req->flags |= BF_READ_TIMEOUT;
+			if (!(rep->flags & BF_SHUTW_STATUS)) {
+				EV_FD_CLR(t->cli_fd, DIR_RD);
+				t->cli_state = CL_STSHUTR;
+			} else {
+				/* output was already closed */
+				fd_delete(t->cli_fd);
+				t->cli_state = CL_STCLOSE;
+			}
+			if (!(t->flags & SN_ERR_MASK))
+				t->flags |= SN_ERR_CLITO;
+			if (!(t->flags & SN_FINST_MASK)) {
+				if (t->analysis & AN_REQ_ANY)
+					t->flags |= SN_FINST_R;
+				else if (t->pend_pos)
+					t->flags |= SN_FINST_Q;
+				else if (s == SV_STCONN)
+					t->flags |= SN_FINST_C;
+				else
+					t->flags |= SN_FINST_D;
+			}
+			return 1;
+		}	
+		/* write timeout */
+		else if (tick_is_expired(rep->wex, now_ms)) {
+			buffer_shutw_done(rep);
+			rep->flags |= BF_WRITE_TIMEOUT;
+			if (!(req->flags & BF_SHUTR_STATUS)) {
+				EV_FD_CLR(t->cli_fd, DIR_WR);
+				shutdown(t->cli_fd, SHUT_WR);
+				/* We must ensure that the read part is still alive when switching to shutw */
+				/* FIXME: is this still true ? */
+				EV_FD_SET(t->cli_fd, DIR_RD);
+				req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
+				t->cli_state = CL_STSHUTW;
+			} else {
+				fd_delete(t->cli_fd);
+				t->cli_state = CL_STCLOSE;
+			}
 
-			/* try headers filters */
-			if (rule_set->rsp_exp != NULL) {
-				if (apply_filters_to_response(t, rep, rule_set->rsp_exp) < 0) {
-				return_bad_resp:
-					if (t->srv) {
-						t->srv->cur_sess--;
-						t->srv->failed_resp++;
-						sess_change_server(t, NULL);
-					}
-					cur_proxy->failed_resp++;
-				return_srv_prx_502:
-					buffer_shutr_done(rep);
-					buffer_shutw_done(req);
-					fd_delete(t->srv_fd);
-					t->srv_state = SV_STCLOSE;
-					rep->flags |= BF_MAY_FORWARD;
-					txn->status = 502;
-					client_return(t, error_message(t, HTTP_ERR_502));
-					if (!(t->flags & SN_ERR_MASK))
-						t->flags |= SN_ERR_PRXCOND;
-					if (!(t->flags & SN_FINST_MASK))
-						t->flags |= SN_FINST_H;
-					/* We used to have a free connection slot. Since we'll never use it,
-					 * we have to inform the server that it may be used by another session.
-					 */
-					if (t->srv && may_dequeue_tasks(t->srv, cur_proxy))
-						process_srv_queue(t->srv);
+			if (!(t->flags & SN_ERR_MASK))
+				t->flags |= SN_ERR_CLITO;
+			if (!(t->flags & SN_FINST_MASK)) {
+				if (t->analysis & AN_REQ_ANY)
+					t->flags |= SN_FINST_R;
+				else if (t->pend_pos)
+					t->flags |= SN_FINST_Q;
+				else if (s == SV_STCONN)
+					t->flags |= SN_FINST_C;
+				else
+					t->flags |= SN_FINST_D;
+			}
+			return 1;
+		}
+
+		/* manage read timeout */
+		if (!(req->flags & BF_SHUTR_STATUS)) {
+			if (req->l >= req->rlim - req->data) {
+				/* no room to read more data */
+				if (EV_FD_COND_C(t->cli_fd, DIR_RD)) {
+					/* stop reading until we get some space */
+					req->rex = TICK_ETERNITY;
+				}
+			} else {
+				EV_FD_COND_S(t->cli_fd, DIR_RD);
+				req->rex = tick_add_ifset(now_ms, t->fe->timeout.client);
+			}
+		}
+
+		/* manage write timeout */
+		if (!(rep->flags & BF_SHUTW_STATUS)) {
+			/* first, we may have to produce data (eg: stats).
+			 * right now, this is limited to the SHUTR state.
+			 */
+			if (req->flags & BF_SHUTR_STATUS && t->flags & SN_SELF_GEN) {
+				produce_content(t);
+				if (rep->l == 0) {
+					buffer_shutw_done(rep);
+					fd_delete(t->cli_fd);
+					t->cli_state = CL_STCLOSE;
 					return 1;
 				}
 			}
 
-			/* has the response been denied ? */
-			if (txn->flags & TX_SVDENY) {
-				if (t->srv) {
-					t->srv->cur_sess--;
-					t->srv->failed_secu++;
-					sess_change_server(t, NULL);
+			/* we don't enable client write if the buffer is empty, nor if the server has to analyze it */
+			if ((rep->l == 0) || !(rep->flags & BF_MAY_FORWARD)) {
+				if (EV_FD_COND_C(t->cli_fd, DIR_WR)) {
+					/* stop writing */
+					rep->wex = TICK_ETERNITY;
 				}
-				cur_proxy->denied_resp++;
-				goto return_srv_prx_502;
+			} else {
+				/* buffer not empty */
+				if (!tick_isset(rep->wex)) {
+					EV_FD_COND_S(t->cli_fd, DIR_WR);
+					/* restart writing */
+					rep->wex = tick_add_ifset(now_ms, t->fe->timeout.client);
+					if (!(req->flags & BF_SHUTR_STATUS) && tick_isset(rep->wex) && tick_isset(req->rex)) {
+						/* FIXME: to prevent the client from expiring read timeouts during writes,
+						 * we refresh it, except if it was already infinite. */
+						req->rex = rep->wex;
+					}
+				}
 			}
+		}
+		return 0; /* other cases change nothing */
+	}
+	else { /* CL_STCLOSE: nothing to do */
+		if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
+			int len;
+			len = sprintf(trash, "%08x:%s.clicls[%04x:%04x]\n", t->uniq_id, t->be->id, (unsigned short)t->cli_fd, (unsigned short)t->srv_fd);
+			write(1, trash, len);
+		}
+		return 0;
+	}
+	return 0;
+}
 
-			/* We might have to check for "Connection:" */
-			if (((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
-			    !(t->flags & SN_CONN_CLOSED)) {
-				char *cur_ptr, *cur_end, *cur_next;
-				int cur_idx, old_idx, delta, val;
-				struct hdr_idx_elem *cur_hdr;
 
-				cur_next = rep->data + txn->rsp.som + hdr_idx_first_pos(&txn->hdr_idx);
-				old_idx = 0;
+/*
+ * manages the server FSM and its socket. It returns 1 if a state has changed
+ * (and a resync may be needed), 0 else.
+ */
+int process_srv(struct session *t)
+{
+	int s = t->srv_state;
+	struct http_txn *txn = &t->txn;
+	struct buffer *req = t->req;
+	struct buffer *rep = t->rep;
+	int conn_err;
 
-				while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
-					cur_hdr  = &txn->hdr_idx.v[cur_idx];
-					cur_ptr  = cur_next;
-					cur_end  = cur_ptr + cur_hdr->len;
-					cur_next = cur_end + cur_hdr->cr + 1;
+	DPRINTF(stderr,"[%u] process_srv: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%u,%u req=%08x rep=%08x\n",
+		now_ms,
+		cli_stnames[t->cli_state], srv_stnames[t->srv_state],
+		EV_FD_ISSET(t->srv_fd, DIR_RD), EV_FD_ISSET(t->srv_fd, DIR_WR),
+		rep->rex, req->wex,
+		req->flags, rep->flags);
 
-					val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
-					if (val) {
-						/* 3 possibilities :
-						 * - we have already set Connection: close,
-						 *   so we remove this line.
-						 * - we have not yet set Connection: close,
-						 *   but this line indicates close. We leave
-						 *   it untouched and set the flag.
-						 * - we have not yet set Connection: close,
-						 *   and this line indicates non-close. We
-						 *   replace it.
-						 */
-						if (t->flags & SN_CONN_CLOSED) {
-							delta = buffer_replace2(rep, cur_ptr, cur_next, NULL, 0);
-							txn->rsp.eoh += delta;
-							cur_next += delta;
-							txn->hdr_idx.v[old_idx].next = cur_hdr->next;
-							txn->hdr_idx.used--;
-							cur_hdr->len = 0;
-						} else {
-							if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
-								delta = buffer_replace2(rep, cur_ptr + val, cur_end,
-											"close", 5);
-								cur_next += delta;
-								cur_hdr->len += delta;
-								txn->rsp.eoh += delta;
-							}
-							t->flags |= SN_CONN_CLOSED;
-						}
-					}
-					old_idx = cur_idx;
-				}
+	if (s == SV_STIDLE) {
+		if ((rep->flags & BF_SHUTW_STATUS) ||
+			 ((req->flags & BF_SHUTR_STATUS) &&
+			  (req->l == 0 || t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
+			req->cex = TICK_ETERNITY;
+			if (t->pend_pos)
+				t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+			/* note that this must not return any error because it would be able to
+			 * overwrite the client_retnclose() output.
+			 */
+			if (txn->flags & TX_CLTARPIT)
+				srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, NULL);
+			else
+				srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, NULL);
+
+			return 1;
+		}
+		else if (req->flags & BF_MAY_CONNECT) {
+			/* the client allows the server to connect */
+			if (txn->flags & TX_CLTARPIT) {
+				/* This connection is being tarpitted. The CLIENT side has
+				 * already set the connect expiration date to the right
+				 * timeout. We just have to check that it has not expired.
+				 */
+				if (!tick_is_expired(req->cex, now_ms))
+					return 0;
+
+				/* We will set the queue timer to the time spent, just for
+				 * logging purposes. We fake a 500 server error, so that the
+				 * attacker will not suspect his connection has been tarpitted.
+				 * It will not cause trouble to the logs because we can exclude
+				 * the tarpitted connections by filtering on the 'PT' status flags.
+				 */
+				req->cex = TICK_ETERNITY;
+				t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+				srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T,
+						   500, error_message(t, HTTP_ERR_500));
+				return 1;
 			}
 
-			/* add response headers from the rule sets in the same order */
-			for (cur_idx = 0; cur_idx < rule_set->nb_rspadd; cur_idx++) {
-				if (unlikely(http_header_add_tail(rep, &txn->rsp, &txn->hdr_idx,
-								  rule_set->rsp_add[cur_idx])) < 0)
-					goto return_bad_resp;
+			/* Right now, we will need to create a connection to the server.
+			 * We might already have tried, and got a connection pending, in
+			 * which case we will not do anything till it's pending. It's up
+			 * to any other session to release it and wake us up again.
+			 */
+			if (t->pend_pos) {
+				if (!tick_is_expired(req->cex, now_ms)) {
+					return 0;
+				} else {
+					/* we've been waiting too long here */
+					req->cex = TICK_ETERNITY;
+					t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+					srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q,
+							   503, error_message(t, HTTP_ERR_503));
+					if (t->srv)
+						t->srv->failed_conns++;
+					t->be->failed_conns++;
+					return 1;
+				}
 			}
 
-			/* check whether we're already working on the frontend */
-			if (cur_proxy == t->fe)
-				break;
-			cur_proxy = t->fe;
-		}
+			do {
+				/* first, get a connection */
+				if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
+					t->flags |= SN_REDIRECTABLE;
 
-		/*
-		 * 4: check for server cookie.
-		 */
-		if (t->be->cookie_name || t->be->appsession_name || t->be->capture_name
-		    || (t->be->options & PR_O_CHK_CACHE))
-			manage_server_side_cookies(t, rep);
+				if (srv_redispatch_connect(t))
+					return t->srv_state != SV_STIDLE;
 
+				if ((t->flags & SN_REDIRECTABLE) && t->srv && t->srv->rdr_len) {
+					/* Server supporting redirection and it is possible.
+					 * Invalid requests are reported as such. It concerns all
+					 * the largest ones.
+					 */
+					struct chunk rdr;
+					char *path;
+					int len;
 
-		/*
-		 * 5: check for cache-control or pragma headers if required.
-		 */
-		if ((t->be->options & (PR_O_COOK_NOC | PR_O_CHK_CACHE)) != 0)
-			check_response_for_cacheability(t, rep);
+					/* 1: create the response header */
+					rdr.len = strlen(HTTP_302);
+					rdr.str = trash;
+					memcpy(rdr.str, HTTP_302, rdr.len);
 
-		/*
-		 * 6: add server cookie in the response if needed
-		 */
-		if ((t->srv) && !(t->flags & SN_DIRECT) && (t->be->options & PR_O_COOK_INS) &&
-		    (!(t->be->options & PR_O_COOK_POST) || (txn->meth == HTTP_METH_POST))) {
-			int len;
+					/* 2: add the server's prefix */
+					if (rdr.len + t->srv->rdr_len > sizeof(trash))
+						goto cancel_redir;
 
-			/* the server is known, it's not the one the client requested, we have to
-			 * insert a set-cookie here, except if we want to insert only on POST
-			 * requests and this one isn't. Note that servers which don't have cookies
-			 * (eg: some backup servers) will return a full cookie removal request.
-			 */
-			len = sprintf(trash, "Set-Cookie: %s=%s; path=/",
-				      t->be->cookie_name,
-				      t->srv->cookie ? t->srv->cookie : "; Expires=Thu, 01-Jan-1970 00:00:01 GMT");
+					memcpy(rdr.str + rdr.len, t->srv->rdr_pfx, t->srv->rdr_len);
+					rdr.len += t->srv->rdr_len;
+
+					/* 3: add the request URI */
+					path = http_get_path(txn);
+					if (!path)
+						goto cancel_redir;
+					len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
+					if (rdr.len + len > sizeof(trash) - 4) /* 4 for CRLF-CRLF */
+						goto cancel_redir;
 
-			if (t->be->cookie_domain)
-				len += sprintf(trash+len, "; domain=%s", t->be->cookie_domain);
+					memcpy(rdr.str + rdr.len, path, len);
+					rdr.len += len;
+					memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
+					rdr.len += 4;
 
-			if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
-							   trash, len)) < 0)
-				goto return_bad_resp;
-			txn->flags |= TX_SCK_INSERTED;
+					srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr);
+					/* FIXME: we should increase a counter of redirects per server and per backend. */
+					if (t->srv)
+						t->srv->cum_sess++;
+					return 1;
+				cancel_redir:
+					txn->status = 400;
+					t->fe->failed_req++;
+					srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C,
+							   400, error_message(t, HTTP_ERR_400));
+					return 1;
+				}
 
-			/* Here, we will tell an eventual cache on the client side that we don't
-			 * want it to cache this reply because HTTP/1.0 caches also cache cookies !
-			 * Some caches understand the correct form: 'no-cache="set-cookie"', but
-			 * others don't (eg: apache <= 1.3.26). So we use 'private' instead.
-			 */
-			if ((t->be->options & PR_O_COOK_NOC) && (txn->flags & TX_CACHEABLE)) {
+				/* try to (re-)connect to the server, and fail if we expire the
+				 * number of retries.
+				 */
+				if (srv_retryable_connect(t)) {
+					t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+					return t->srv_state != SV_STIDLE;
+				}
+			} while (1);
+		}
+	}
+	else if (s == SV_STCONN) { /* connection in progress */
+		if ((rep->flags & BF_SHUTW_STATUS) ||
+		    ((req->flags & BF_SHUTR_STATUS) &&
+		     ((req->l == 0 && !(req->flags & BF_WRITE_STATUS)) ||
+		      t->be->options & PR_O_ABRT_CLOSE))) { /* give up */
+			req->cex = TICK_ETERNITY;
+			if (!(t->flags & SN_CONN_TAR)) {
+				/* if we are in turn-around, we have already closed the FD */
+				fd_delete(t->srv_fd);
+				if (t->srv) {
+					t->srv->cur_sess--;
+					sess_change_server(t, NULL);
+				}
+			}
 
-				txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
+			/* note that this must not return any error because it would be able to
+			 * overwrite the client_retnclose() output.
+			 */
+			srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL);
+			return 1;
+		}
+		if (!(req->flags & BF_WRITE_STATUS) && !tick_is_expired(req->cex, now_ms)) {
+			return 0; /* nothing changed */
+		}
+		else if (!(req->flags & BF_WRITE_STATUS) || (req->flags & BF_WRITE_ERROR)) {
+			/* timeout, asynchronous connect error or first write error */
+			//fprintf(stderr,"2: c=%d, s=%d\n", c, s);
 
-				if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
-								   "Cache-control: private", 22)) < 0)
-					goto return_bad_resp;
+			if (t->flags & SN_CONN_TAR) {
+				/* We are doing a turn-around waiting for a new connection attempt. */
+				if (!tick_is_expired(req->cex, now_ms))
+					return 0;
+				t->flags &= ~SN_CONN_TAR;
 			}
-		}
+			else {
+				fd_delete(t->srv_fd);
+				if (t->srv) {
+					t->srv->cur_sess--;
+					sess_change_server(t, NULL);
+				}
 
+				if (!(req->flags & BF_WRITE_STATUS))
+					conn_err = SN_ERR_SRVTO; // it was a connect timeout.
+				else
+					conn_err = SN_ERR_SRVCL; // it was an asynchronous connect error.
 
-		/*
-		 * 7: check if result will be cacheable with a cookie.
-		 * We'll block the response if security checks have caught
-		 * nasty things such as a cacheable cookie.
-		 */
-		if (((txn->flags & (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) ==
-		     (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) &&
-		    (t->be->options & PR_O_CHK_CACHE)) {
+				/* ensure that we have enough retries left */
+				if (srv_count_retry_down(t, conn_err))
+					return 1;
 
-			/* we're in presence of a cacheable response containing
-			 * a set-cookie header. We'll block it as requested by
-			 * the 'checkcache' option, and send an alert.
-			 */
-			if (t->srv) {
-				t->srv->cur_sess--;
-				t->srv->failed_secu++;
-				sess_change_server(t, NULL);
+				if (req->flags & BF_WRITE_ERROR) {
+					/* we encountered an immediate connection error, and we
+					 * will have to retry connecting to the same server, most
+					 * likely leading to the same result. To avoid this, we
+					 * fake a connection timeout to retry after a turn-around
+					 * time of 1 second. We will wait in the previous if block.
+					 */
+					t->flags |= SN_CONN_TAR;
+					req->cex = tick_add(now_ms, MS_TO_TICKS(1000));
+					return 0;
+				}
 			}
-			t->be->denied_resp++;
-
-			Alert("Blocking cacheable cookie in response from instance %s, server %s.\n",
-			      t->be->id, t->srv?t->srv->id:"<dispatch>");
-			send_log(t->be, LOG_ALERT,
-				 "Blocking cacheable cookie in response from instance %s, server %s.\n",
-				 t->be->id, t->srv?t->srv->id:"<dispatch>");
-			goto return_srv_prx_502;
-		}
 
-		/*
-		 * 8: add "Connection: close" if needed and not yet set.
-		 * Note that we do not need to add it in case of HTTP/1.0.
-		 */
-		if (!(t->flags & SN_CONN_CLOSED) &&
-		    ((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO))) {
-			if ((unlikely(msg->sl.st.v_l != 8) ||
-			     unlikely(req->data[msg->som + 7] != '0')) &&
-			    unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
-							   "Connection: close", 17)) < 0)
-				goto return_bad_resp;
-			t->flags |= SN_CONN_CLOSED;
-		}
+			if (t->srv && t->conn_retries == 0 && t->be->options & PR_O_REDISP) {
+				/* We're on our last chance, and the REDISP option was specified.
+				 * We will ignore cookie and force to balance or use the dispatcher.
+				 */
+				/* let's try to offer this slot to anybody */
+				if (may_dequeue_tasks(t->srv, t->be))
+					process_srv_queue(t->srv);
 
+				/* it's left to the dispatcher to choose a server */
+				t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
+				t->prev_srv = t->srv;
 
-		/*************************************************************
-		 * OK, that's finished for the headers. We have done what we *
-		 * could. Let's switch to the DATA state.                    *
-		 ************************************************************/
+				/* first, get a connection */
+				if (srv_redispatch_connect(t))
+					return t->srv_state != SV_STCONN;
+			} else {
+				if (t->srv)
+					t->srv->retries++;
+				t->be->retries++;
+			}
 
-		t->srv_state = SV_STDATA;
-		rep->flags |= BF_MAY_FORWARD;
-		rep->rlim = rep->data + BUFSIZE; /* no more rewrite needed */
-		t->logs.t_data = tv_ms_elapsed(&t->logs.tv_accept, &now);
+			do {
+				/* Now we will try to either reconnect to the same server or
+				 * connect to another server. If the connection gets queued
+				 * because all servers are saturated, then we will go back to
+				 * the SV_STIDLE state.
+				 */
+				if (srv_retryable_connect(t)) {
+					t->logs.t_queue = tv_ms_elapsed(&t->logs.tv_accept, &now);
+					return t->srv_state != SV_STCONN;
+				}
 
-		/* client connection already closed or option 'forceclose' required :
-		 * we close the server's outgoing connection right now.
-		 */
-		if ((req->l == 0) &&
-		    (req->flags & BF_SHUTR_STATUS || t->be->options & PR_O_FORCE_CLO)) {
-			EV_FD_CLR(t->srv_fd, DIR_WR);
-			buffer_shutw_done(req);
+				/* we need to redispatch the connection to another server */
+				if (srv_redispatch_connect(t))
+					return t->srv_state != SV_STCONN;
+			} while (1);
+		}
+		else { /* no error or write 0 */
+			t->logs.t_connect = tv_ms_elapsed(&t->logs.tv_accept, &now);
 
-			/* We must ensure that the read part is still alive when switching
-			 * to shutw */
-			EV_FD_SET(t->srv_fd, DIR_RD);
-			rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+			//fprintf(stderr,"3: c=%d, s=%d\n", c, s);
+			if (req->l == 0) /* nothing to write */ {
+				EV_FD_CLR(t->srv_fd, DIR_WR);
+				req->wex = TICK_ETERNITY;
+			} else  /* need the right to write */ {
+				EV_FD_SET(t->srv_fd, DIR_WR);
+				req->wex = tick_add_ifset(now_ms, t->be->timeout.server);
+				if (tick_isset(req->wex)) {
+					/* FIXME: to prevent the server from expiring read timeouts during writes,
+					 * we refresh it. */
+					rep->rex = req->wex;
+				}
+			}
 
-			shutdown(t->srv_fd, SHUT_WR);
-			t->srv_state = SV_STSHUTW;
-		}
+			if (t->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
+				EV_FD_SET(t->srv_fd, DIR_RD);
+				rep->rex = tick_add_ifset(now_ms, t->be->timeout.server);
+				t->srv_state = SV_STDATA;
+				rep->flags |= BF_MAY_FORWARD;
+				rep->rlim = rep->data + BUFSIZE; /* no rewrite needed */
 
+				/* if the user wants to log as soon as possible, without counting
+				   bytes from the server, then this is the right moment. */
+				if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
+					t->logs.t_close = t->logs.t_connect; /* to get a valid end date */
+					tcp_sess_log(t);
+				}
 #ifdef CONFIG_HAP_TCPSPLICE
-		if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
-			/* TCP splicing supported by both FE and BE */
-			tcp_splice_splicefd(t->cli_fd, t->srv_fd, 0);
-		}
+				if ((t->fe->options & t->be->options) & PR_O_TCPSPLICE) {
+					/* TCP splicing supported by both FE and BE */
+					tcp_splice_splicefd(t->cli_fd, t->srv_fd, 0);
+				}
 #endif
-		/* if the user wants to log as soon as possible, without counting
-		 * bytes from the server, then this is the right moment. We have
-		 * to temporarily assign bytes_out to log what we currently have.
-		 */
-		if (t->fe->to_log && !(t->logs.logwait & LW_BYTES)) {
-			t->logs.t_close = t->logs.t_data; /* to get a valid end date */
-			t->logs.bytes_out = txn->rsp.eoh;
-			if (t->fe->to_log & LW_REQ)
-				http_sess_log(t);
-			else
-				tcp_sess_log(t);
-			t->logs.bytes_out = 0;
+			}
+			else {
+				t->srv_state = SV_STDATA;
+				t->analysis |= AN_RTR_HTTP_HDR;
+				rep->rlim = rep->data + BUFSIZE - MAXREWRITE; /* rewrite needed */
+				t->txn.rsp.msg_state = HTTP_MSG_RPBEFORE;
+				/* reset hdr_idx which was already initialized by the request.
+				 * right now, the http parser does it.
+				 * hdr_idx_init(&t->txn.hdr_idx);
+				 */
+			}
+			req->cex = TICK_ETERNITY;
+			return 1;
 		}
-
-		/* Note: we must not try to cheat by jumping directly to DATA,
-		 * otherwise we would not let the client side wake up.
-		 */
-
-		return 1;
 	}
 	else if (s == SV_STDATA) {
 		/* read or write error */