[MAJOR] replaced the client-side HTTP parser with a new one

The new parser uses an FSM to strictly follow RFC2616.
Headers are indexed and parsed only once they're all available.
That way, complex regexes make more sense.

HTTP processing is now performed in several phases by calling
multiple functions, making the code cleaner and easier to read.

Note that req[i]pass does not work anymore because it would
require that we mark a header to be ignored. What is really
needed is to have the ability to add an exception to a matching
(match xx except yy).

Several bugs have been fixed in appsession during the conversion
to the new FSM (method length and recovery on malloc errors).

The code does build and work with the debug examples, but is
not usable yet to connect to anything as it does not forward
the requests yet.
diff --git a/TODO b/TODO
index 30f923e..3013e18 100644
--- a/TODO
+++ b/TODO
@@ -166,3 +166,5 @@
   - check all copyrights
   - fix Makefile.bsd
   - separate inline functions to put them in files covered by GPL
+  - implement HTTP status 414 - request URI too long
+
diff --git a/doc/http-parsing.txt b/doc/http-parsing.txt
new file mode 100644
index 0000000..21c3fc5
--- /dev/null
+++ b/doc/http-parsing.txt
@@ -0,0 +1,214 @@
+--- Relevant portions of RFC2616 ---
+
+OCTET               = <any 8-bit sequence of data>
+CHAR                = <any US-ASCII character (octets 0 - 127)>
+UPALPHA             = <any US-ASCII uppercase letter "A".."Z">
+LOALPHA             = <any US-ASCII lowercase letter "a".."z">
+ALPHA               = UPALPHA | LOALPHA
+DIGIT               = <any US-ASCII digit "0".."9">
+CTL                 = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
+CR                  = <US-ASCII CR, carriage return (13)>
+LF                  = <US-ASCII LF, linefeed (10)>
+SP                  = <US-ASCII SP, space (32)>
+HT                  = <US-ASCII HT, horizontal-tab (9)>
+<">                 = <US-ASCII double-quote mark (34)>
+CRLF                = CR LF
+LWS                 = [CRLF] 1*( SP | HT )
+TEXT                = <any OCTET except CTLs, but including LWS>
+HEX                 = "A" | "B" | "C" | "D" | "E" | "F"
+                      | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
+separators          = "(" | ")" | "<" | ">" | "@"
+                    | "," | ";" | ":" | "\" | <">
+                    | "/" | "[" | "]" | "?" | "="
+                    | "{" | "}" | SP | HT
+token               = 1*<any CHAR except CTLs or separators>
+
+quoted-pair         = "\" CHAR
+ctext               = <any TEXT excluding "(" and ")">
+qdtext              = <any TEXT except <">>
+quoted-string       = ( <"> *(qdtext | quoted-pair ) <"> )
+comment             = "(" *( ctext | quoted-pair | comment ) ")"
+
+
+
+
+
+4 HTTP Message
+4.1 Message Types
+
+HTTP messages consist of requests from client to server and responses from
+server to client. Request (section 5) and Response (section 6) messages use the
+generic message format of RFC 822 [9] for transferring entities (the payload of
+the message). Both types of message consist of :
+
+  - a start-line
+  - zero or more header fields (also known as "headers")
+  - an empty line (i.e., a line with nothing preceding the CRLF) indicating the
+    end of the header fields
+  - and possibly a message-body.
+
+
+HTTP-message        = Request | Response
+
+start-line          = Request-Line | Status-Line
+generic-message     = start-line
+                      *(message-header CRLF)
+                      CRLF
+                      [ message-body ]
+
+In the interest of robustness, servers SHOULD ignore any empty line(s) received
+where a Request-Line is expected. In other words, if the server is reading the
+protocol stream at the beginning of a message and receives a CRLF first, it
+should ignore the CRLF.
+
+
+4.2 Message headers
+
+- Each header field consists of a name followed by a colon (":") and the field
+  value.
+- Field names are case-insensitive.
+- The field value MAY be preceded by any amount of LWS, though a single SP is
+  preferred.
+- Header fields can be extended over multiple lines by preceding each extra
+  line with at least one SP or HT.
+
+
+message-header      = field-name ":" [ field-value ]
+field-name          = token
+field-value         = *( field-content | LWS )
+field-content       = <the OCTETs making up the field-value and consisting of
+                       either *TEXT or combinations of token, separators, and
+                       quoted-string>
+
+
+The field-content does not include any leading or trailing LWS occurring before
+the first non-whitespace character of the field-value or after the last
+non-whitespace character of the field-value. Such leading or trailing LWS MAY
+be removed without changing the semantics of the field value. Any LWS that
+occurs between field-content MAY be replaced with a single SP before
+interpreting the field value or forwarding the message downstream.
+
+
+=> format des headers = 1*(CHAR & !ctl & !sep) ":" *(OCTET & (!ctl | LWS))
+=> les regex de matching de headers s'appliquent sur field-content, et peuvent
+   utiliser field-value comme espace de travail (mais de préférence après le
+   premier SP).
+
+(19.3) The line terminator for message-header fields is the sequence CRLF.
+However, we recommend that applications, when parsing such headers, recognize
+a single LF as a line terminator and ignore the leading CR.
+
+
+
+
+
+message-body    = entity-body
+                | <entity-body encoded as per Transfer-Encoding>
+
+
+
+5 Request
+
+Request         = Request-Line
+                  *(( general-header
+                    | request-header
+                    | entity-header ) CRLF)
+                  CRLF
+                  [ message-body ]
+
+
+
+5.1 Request line
+
+The elements are separated by SP characters. No CR or LF is allowed except in
+the final CRLF sequence.
+
+Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+
+(19.3) Clients SHOULD be tolerant in parsing the Status-Line and servers
+tolerant when parsing the Request-Line. In particular, they SHOULD accept any
+amount of SP or HT characters between fields, even though only a single SP is
+required.
+
+4.5 General headers
+Apply to MESSAGE.
+
+general-header  = Cache-Control
+                | Connection
+                | Date
+                | Pragma
+                | Trailer
+                | Transfer-Encoding
+                | Upgrade
+                | Via
+                | Warning
+
+General-header field names can be extended reliably only in combination with a
+change in the protocol version. However, new or experimental header fields may
+be given the semantics of general header fields if all parties in the
+communication recognize them to be general-header fields. Unrecognized header
+fields are treated as entity-header fields.
+
+
+
+
+5.3 Request Header Fields
+
+The request-header fields allow the client to pass additional information about
+the request, and about the client itself, to the server. These fields act as
+request modifiers, with semantics equivalent to the parameters on a programming
+language method invocation.
+
+request-header  = Accept
+                | Accept-Charset
+                | Accept-Encoding
+                | Accept-Language
+                | Authorization
+                | Expect
+                | From
+                | Host
+                | If-Match
+                | If-Modified-Since
+                | If-None-Match
+                | If-Range
+                | If-Unmodified-Since
+                | Max-Forwards
+                | Proxy-Authorization
+                | Range
+                | Referer
+                | TE
+                | User-Agent
+
+Request-header field names can be extended reliably only in combination with a
+change in the protocol version. However, new or experimental header fields MAY
+be given the semantics of request-header fields if all parties in the
+communication recognize them to be request-header fields. Unrecognized header
+fields are treated as entity-header fields.
+
+
+
+7.1 Entity header fields
+
+Entity-header fields define metainformation about the entity-body or, if no
+body is present, about the resource identified by the request. Some of this
+metainformation is OPTIONAL; some might be REQUIRED by portions of this
+specification.
+
+entity-header   = Allow
+                | Content-Encoding
+                | Content-Language
+                | Content-Length
+                | Content-Location
+                | Content-MD5
+                | Content-Range
+                | Content-Type
+                | Expires
+                | Last-Modified
+                | extension-header
+extension-header = message-header
+
+The extension-header mechanism allows additional entity-header fields to be
+defined without changing the protocol, but these fields cannot be assumed to be
+recognizable by the recipient. Unrecognized header fields SHOULD be ignored by
+the recipient and MUST be forwarded by transparent proxies.
+
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 0005bf7..beffcfe 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -27,6 +27,13 @@
 #include <types/session.h>
 #include <types/task.h>
 
+/*
+ * some macros used for the request parsing.
+ * from RFC2616:
+ *   CTL                 = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
+ */
+static inline int IS_CTL(const unsigned char x) { return (x < 32)||(x == 127);}
+
 
 int event_accept(int fd);
 int process_session(struct task *t);
@@ -39,6 +46,10 @@
 			int status, int msglen, const char *msg);
 
 int produce_content(struct session *s);
+void debug_hdr(const char *dir, struct session *t, const char *start, const char *end);
+void get_srv_from_appsession(struct session *t, const char *begin, const char *end);
+void apply_filters_to_session(struct session *t, struct buffer *req, struct hdr_exp *exp);
+void manage_client_side_cookies(struct session *t, struct buffer *req);
 
 #endif /* _PROTO_PROTO_HTTP_H */
 
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index 3cedc1e..6e6472e 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -51,6 +51,21 @@
 #define SV_STCLOSE	6
 
 
+/* Possible states while parsing HTTP messages (request|response) */
+#define HTTP_PA_EMPTY      0    /* leading LF, before start line */
+#define HTTP_PA_START      1    /* inside start line */
+#define HTTP_PA_STRT_LF    2    /* LF after start line */
+#define HTTP_PA_HEADER     3    /* inside a header */
+#define HTTP_PA_HDR_LF     4    /* LF after a header */
+#define HTTP_PA_HDR_LWS    5    /* LWS after a header */
+#define HTTP_PA_LFLF       6    /* after double LF/CRLF at the end of headers */
+#define HTTP_PA_ERROR      7    /* syntax error in the message */
+#define HTTP_PA_CR_SKIP 0x10    /* ORed with other values when a CR was skipped */
+#define HTTP_PA_LF_EXP  0x20    /* ORed with other values when a CR is seen and
+				 * an LF is expected before entering the
+				 * designated state. */
+
+
 #endif /* _TYPES_PROTO_HTTP_H */
 
 /*
diff --git a/include/types/session.h b/include/types/session.h
index f5e9490..5a8710a 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -122,6 +122,7 @@
 	char **req_cap;				/* array of captured request headers (may be NULL) */
 	char **rsp_cap;				/* array of captured response headers (may be NULL) */
 	struct hdr_idx hdr_idx;                 /* array of header indexes (max: MAX_HTTP_HDR) */
+	int hdr_state;                          /* where we are in the current header parsing */
 	struct chunk req_line;			/* points to first line */
 	struct chunk auth_hdr;			/* points to 'Authorization:' header */
 	struct {
diff --git a/src/client.c b/src/client.c
index b867830..0ce2392 100644
--- a/src/client.c
+++ b/src/client.c
@@ -161,6 +161,7 @@
 
 		s->cli_state = (p->mode == PR_MODE_HTTP) ?  CL_STHEADERS : CL_STDATA; /* no HTTP headers for non-HTTP proxies */
 		s->srv_state = SV_STIDLE;
+		s->hdr_state = HTTP_PA_EMPTY; /* at the very beginning of the request */
 		s->req = s->rep = NULL; /* will be allocated later */
 
 		s->cli_fd = cfd;
diff --git a/src/proto_http.c b/src/proto_http.c
index 124594b..ea46fbd 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -44,12 +44,16 @@
 #include <proto/buffers.h>
 #include <proto/fd.h>
 #include <proto/log.h>
+#include <proto/hdr_idx.h>
 #include <proto/proto_http.h>
 #include <proto/queue.h>
 #include <proto/session.h>
 #include <proto/task.h>
 
 
+#define DEBUG_PARSE_NO_SPEEDUP
+#undef DEBUG_PARSE_NO_SPEEDUP
+
 /* This is used by remote monitoring */
 const char *HTTP_200 =
 	"HTTP/1.0 200 OK\r\n"
@@ -218,9 +222,10 @@
 	int c = t->cli_state;
 	struct buffer *req = t->req;
 	struct buffer *rep = t->rep;
-	int method_checked = 0;
-	appsess *asession_temp = NULL;
-	appsess local_asession;
+	int delete_header = 0;
+
+	int cur_hdr, cur_idx;
+	char *ptr;
 
 #ifdef DEBUG_FULL
 	fprintf(stderr,"process_cli: c=%s s=%s set(r,w)=%d,%d exp(r,w)=%d.%d,%d.%d\n",
@@ -234,46 +239,602 @@
 	//MY_FD_ISSET(t->srv_fd, StaticReadEvent), MY_FD_ISSET(t->srv_fd, StaticWriteEvent)
 	//);
 	if (c == CL_STHEADERS) {
-		/* now parse the partial (or complete) headers */
-		while (req->lr < req->r) { /* this loop only sees one header at each iteration */
+		/*
+		 * Now parse the partial (or complete) lines.
+		 * We will check the request syntax, and also join multi-line
+		 * headers. An index of all the lines will be elaborated while
+		 * parsing.
+		 *
+		 * For the parsing, we use a 10 states FSM.
+		 *
+		 * RFC2616 requires that both LF and CRLF are recognized as
+		 * line breaks, but that any other combination is an error.
+		 * To avoid duplicating all the states above to check for CR,
+		 * we use a special bit HTTP_PA_LF_EXP that we 'OR' with the
+		 * state we will switch to if the LF is seen, so that we know
+		 * whether there's a pending CR or not. We can check it
+		 * globally since all CR followed by anything but LF are
+		 * errors. Each state is entered with the first character is
+		 * has to process at req->lr. We also have HTTP_PA_CR_SKIP
+		 * indicating that a CR has been seen on current line and
+		 * skipped.
+		 *
+		 * Here is the information we currently have :
+		 *   req->h  = beginning of current line or header
+		 *   req->lr = first non-visited byte
+		 *   req->r  = end of data
+		 */
+
+		while (req->lr < req->r) {
 			char *ptr;
-			int delete_header;
-			char *request_line = NULL;
-	
-			ptr = req->lr;
+			int parse;
 
-			/* look for the end of the current header */
-			while (ptr < req->r && *ptr != '\n' && *ptr != '\r')
-				ptr++;
-	    
-			if (ptr == req->h) { /* empty line, end of headers */
-				int line, len;
+#ifdef DEBUG_FULL
+			fprintf(stderr, "WHL: hdr_st=0x%02x, hdr_used=%d hdr_tail=%d hdr_last=%d, h=%p, lr=%p, r=%p\n",
+				t->hdr_state, t->hdr_idx.used, t->hdr_idx.tail, t->hdr_idx.last, req->h, req->lr, req->r);
+#endif
 
-				/*
-				 * first, let's check that it's not a leading empty line, in
-				 * which case we'll ignore and remove it (according to RFC2616).
+			if (t->hdr_state & HTTP_PA_LF_EXP) {
+				if (*req->lr != '\n') {
+					t->hdr_state = HTTP_PA_ERROR;
+					break;
+				}
+				t->hdr_state &= ~HTTP_PA_LF_EXP;
+			}
+
+			parse = t->hdr_state & ~HTTP_PA_CR_SKIP;;
+
+			if (parse == HTTP_PA_EMPTY) {
+				/* leading empty lines */
+
+				if (*req->lr == '\n') {
+					req->lr ++;
+					t->hdr_state = HTTP_PA_EMPTY;
+					continue;
+				}
+				else if (*req->lr == '\r') {
+					req->lr ++;
+					t->hdr_state = HTTP_PA_EMPTY | HTTP_PA_CR_SKIP | HTTP_PA_LF_EXP;
+					continue;
+				}				
+
+#if PARSE_PRESERVE_EMPTY_LINES
+				/* only skip empty leading lines, don't remove them */
+				t->hdr_idx.v[0].len = req->lr - req->h;
+#else
+				/* remove empty leading lines, as recommended by
+				 * RFC2616. This takes a lot of time because we
+				 * must move all the buffer backwards, but this
+				 * is rarely needed. The method above will be
+				 * cleaner when we'll be able to start sending
+				 * the request from any place in the buffer.
 				 */
-				if (req->h == req->data) {
-					/* to get a complete header line, we need the ending \r\n, \n\r, \r or \n too */
-					if (ptr > req->r - 2) {
-						/* this is a partial header, let's wait for more to come */
-						req->lr = ptr;
-						break;
+				buffer_replace2(req, req->h, req->lr, NULL, 0);
+#endif
+				req->h = req->lr;
+
+				t->hdr_state = HTTP_PA_START;
+#ifndef DEBUG_PARSE_NO_SPEEDUP
+				/* we know that we still have one char available */
+				goto parse_start;
+#else
+				continue;
+#endif
+
+			} else if (parse == HTTP_PA_START) {
+			parse_start:
+				/* Start line */
+				while (req->lr < req->r && !IS_CTL(*req->lr))
+					req->lr++;
+				if (req->lr == req->r)
+					break;
+				/* we have a CTL char */
+				if (*req->lr == '\r') {
+					req->lr++;
+					t->hdr_state = HTTP_PA_STRT_LF | HTTP_PA_CR_SKIP | HTTP_PA_LF_EXP;
+					continue;
+				}
+				else if (*req->lr == '\n') {
+					t->hdr_state = HTTP_PA_STRT_LF;
+#ifndef DEBUG_PARSE_NO_SPEEDUP
+					/* we know that we still have one char available */
+					goto parse_strt_lf;
+#else
+					continue;
+#endif
+				}
+				t->hdr_state = HTTP_PA_ERROR;
+				break;
+
+			} else if (parse == HTTP_PA_STRT_LF) {
+			parse_strt_lf:
+				/* The LF validating the request line */
+
+				ptr = req->lr;
+				if (t->hdr_state & HTTP_PA_CR_SKIP)
+					ptr--; /* Get back to the CR */
+
+				/* We have the complete start line between
+				 * req->h and ptr (excluded). lr points to
+				 * the LF.
+				 */
+
+				/* FIXME: insert a REQUESTURI hook here. */
+
+
+				/* 1: we might have to print this header */
+				if ((global.mode & MODE_DEBUG) &&
+				    (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))
+					debug_hdr("clireq", t, req->h, ptr);
+
+
+				/* 1: save a pointer to the first line as the request */
+				if (t->req_line.len < 0) {
+					t->req_line.str = req->h;
+					t->req_line.len = ptr - req->h;
+				}
+
+				/* 2: maybe we have to copy the original REQURI for the logs ? */
+				if (t->logs.logwait & LW_REQ) {
+					/* we have a complete HTTP request that we must log */
+					if ((t->logs.uri = pool_alloc(requri)) != NULL) {
+						int urilen = ptr - req->h;
+
+						if (urilen >= REQURI_LEN)
+							urilen = REQURI_LEN - 1;
+						memcpy(t->logs.uri, req->h, urilen);
+						t->logs.uri[urilen] = 0;
+
+						if (!(t->logs.logwait &= ~LW_REQ))
+							sess_log(t);
+					} else {
+						Alert("HTTP logging : out of memory.\n");
 					}
+				}
+
+				/* 3: reference this line as the start line */
+				if (hdr_idx_add(ptr - req->h, req->lr - ptr,
+						&t->hdr_idx, t->hdr_idx.tail) < 0) {
+					t->hdr_state = HTTP_PA_ERROR;
+					break;
+				}
 
-					/* now we know that *ptr is either \r or \n,
-					 * and that there are at least 1 char after it.
+				req->lr++;
+				req->h = req->lr;
+				/* in fact, a state is missing here, we should
+				 * be able to distinguish between an empty line
+				 * and a header.
+				 */
+				t->hdr_state = HTTP_PA_HEADER;
+				continue;
+
+			} else if (parse == HTTP_PA_HEADER) {
+			parse_inside_hdr:
+				/* Inside a non-empty header */
+
+				delete_header = 0;
+				while (req->lr < req->r && !IS_CTL(*req->lr))
+					req->lr++;
+				if (req->lr == req->r)
+					break;
+
+				/* we have a CTL char */
+				if (*req->lr == '\r') {
+					t->hdr_state = HTTP_PA_HDR_LF | HTTP_PA_CR_SKIP | HTTP_PA_LF_EXP;
+					req->lr++;
+					continue;
+				}
+				else if (*req->lr == '\n') {
+					t->hdr_state = HTTP_PA_HDR_LF;
+#ifndef DEBUG_PARSE_NO_SPEEDUP
+					goto parse_hdr_lf;
+#else
+					continue;
+#endif
+				}
+				t->hdr_state = HTTP_PA_ERROR;
+				break;
+
+			} else if (parse == HTTP_PA_HDR_LF) {
+			parse_hdr_lf:
+				/* The LF validating last header, but it
+				 * may also be an LWS, in which case we will
+				 * need more data to know if we can close this
+				 * header or not. However, we must check right
+				 * now if this LF/CRLF closes an empty line, in
+				 * which case it means the end of the request.
+				 */
+				ptr = req->lr;
+				if (t->hdr_state & HTTP_PA_CR_SKIP)
+					ptr--; /* Get back to the CR */
+
+				if (ptr == req->h) {
+					/* We have found the end of the headers.
+					 * req->h points to the ending LF/CRLF,
+					 * and req->lr points to the first byte
+					 * after the LF, so it is easy to append
+					 * anything there.
 					 */
-					if ((ptr[0] == ptr[1]) || (ptr[1] != '\r' && ptr[1] != '\n'))
-						req->lr = ptr + 1; /* \r\r, \n\n, \r[^\n], \n[^\r] */
-					else
-						req->lr = ptr + 2; /* \r\n or \n\r */
-					/* ignore empty leading lines */
-					buffer_replace2(req, req->h, req->lr, NULL, 0);
-					req->h = req->lr;
+					t->hdr_state = HTTP_PA_LFLF;
+#ifndef DEBUG_PARSE_NO_SPEEDUP
+					goto parse_lflf;
+#else
+					continue;
+#endif
+					//req->lr++;
+					//break;
+				}
+
+				if (req->lr + 1 >= req->r) /* LF, ?? */
+					break;
+				req->lr++;
+
+				/* Right now, we *know* that there is one char
+				 * available at req->lr.
+				 */
+
+				if (*req->lr == ' ' || *req->lr == '\t') {
+					/* We have an LWS, we will replace the
+					 * CR and LF with spaces as RFC2616
+					 * allows it. <lr> now points to the
+					 * first space char of the LWS part.
+					 */
+					for (;ptr < req->lr; ptr++)
+						*ptr = ' ';
+
+					t->hdr_state = HTTP_PA_HDR_LWS;
+#ifndef DEBUG_PARSE_NO_SPEEDUP
+					goto parse_hdr_lws;
+#else
 					continue;
+#endif
+				}
+
+				/**********************************************
+				 * We now have one complete header between    *
+				 * req->h and ptr, with a possible CR at ptr, *
+				 * everything ending before req->lr. Some very*
+				 * early processing can be applied.           *
+				 **********************************************/
+
+				/*
+				 * FIXME: insert a REQHEADER hook here.
+				 * For instance, we could check the header's
+				 * syntax such as forbidding the leading space
+				 * in the first header (Apache also has the same problem)
+				 */
+
+
+				/* 1: we might have to print this header */
+				if ((global.mode & MODE_DEBUG) &&
+				    (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))
+					debug_hdr("clihdr", t, req->h, ptr);
+
+
+				/* 2: maybe we have to copy this header for the logs ? */
+				if (t->logs.logwait & LW_REQHDR) {
+					/* FIXME: we must *search* the value after the ':' and not
+					 * consider that it's necessary after one single space.*/
+					struct cap_hdr *h;
+					int len;
+					for (h = t->fi->req_cap; h; h = h->next) {
+						if ((h->namelen + 2 <= ptr - req->h) &&
+						    (req->h[h->namelen] == ':') &&
+						    (strncasecmp(req->h, h->name, h->namelen) == 0)) {
+							if (t->req_cap[h->index] == NULL)
+								t->req_cap[h->index] = pool_alloc_from(h->pool, h->len + 1);
+
+							if (t->req_cap[h->index] == NULL) {
+								Alert("HTTP capture : out of memory.\n");
+								continue;
+							}
+							
+							len = ptr - (req->h + h->namelen + 2);
+							if (len > h->len)
+								len = h->len;
+							
+							memcpy(t->req_cap[h->index], req->h + h->namelen + 2, len);
+							t->req_cap[h->index][len]=0;
+						}
+					}
+				}
+
+
+				/* 3: We might need to remove "connection:" */
+				if (!delete_header && (t->fe->options & PR_O_HTTP_CLOSE)
+				    && (strncasecmp(req->h, "Connection:", 11) == 0)) {
+					delete_header = 1;
+				}
+
+
+				/* 4: we might also need the 'Authorization:' header */
+				if (!delete_header &&
+				    t->auth_hdr.len < 0 && t->fi->uri_auth != NULL &&
+				    ptr > req->h + 14 && !strncasecmp("Authorization:", req->h, 14)) {
+					t->auth_hdr.str = req->h;
+					t->auth_hdr.len = ptr - req->h;
 				}
 
+
+
+
+				/* OK, that's enough processing for the first step.
+				 * Now either we index this header or we remove it.
+				 */
+
+				if (!delete_header) {
+					/* we insert it into the index */
+					if (hdr_idx_add(ptr - req->h, req->lr - ptr - 1,
+							&t->hdr_idx, t->hdr_idx.tail) < 0) {
+						t->hdr_state = HTTP_PA_ERROR;
+						break;
+					}
+				} else {
+					/* we remove it */
+					delete_header = 0;
+					buffer_replace2(req, req->h, req->lr, NULL, 0);
+					/* WARNING: ptr is not valid anymore, since the
+					 * header may have been deleted or truncated ! */
+				}
+				
+				/* In any case, we set the next header pointer
+				 * to the next line.
+				 */
+				req->h = req->lr;
+
+#ifndef DEBUG_PARSE_NO_SPEEDUP
+				/*
+				 * We know that at least one character remains.
+				 * It is interesting to directly branch to the
+				 * matching state.
+				 */
+				ptr = req->lr;
+				if (IS_CTL(*req->lr)) {
+					if (*ptr == '\r') {
+						req->lr++;
+						t->hdr_state = HTTP_PA_LFLF | HTTP_PA_LF_EXP;
+						continue;
+					}
+					else if (*ptr == '\n') {
+						t->hdr_state = HTTP_PA_LFLF;
+						goto parse_lflf;
+					}
+					else {
+						t->hdr_state = HTTP_PA_ERROR;
+						break;
+					}
+				}
+				t->hdr_state = HTTP_PA_HEADER;
+				goto parse_inside_hdr;
+#else
+				t->hdr_state = HTTP_PA_HEADER;
+				continue;
+#endif
+
+			} else if (parse == HTTP_PA_HDR_LWS) {
+			parse_hdr_lws:
+				/* Inside an LWS. We just replace tabs with
+				 * spaces and fall back to the HEADER state
+				 * at the first non-space character
+				 */
+
+				while (req->lr < req->r) {
+					if (*req->lr == '\t')
+						*req->lr = ' ';
+					else if (*req->lr != ' ') {
+						t->hdr_state = HTTP_PA_HEADER;
+#ifndef DEBUG_PARSE_NO_SPEEDUP
+						goto parse_inside_hdr;
+#else
+						break;
+#endif
+					}
+					req->lr++;
+				}
+				continue;
+
+			} else if (parse == HTTP_PA_LFLF) {
+			parse_lflf:
+				req->lr ++;
+				/* req->h points to either CR or CRLF, and
+				 * req->lr points to 1 char after LF.
+				 */
+
+				/*
+				 * FIXME: insert a hook here for the end of the headers
+				 */
+				break;
+			} else if (parse == HTTP_PA_ERROR) {
+				break;
+			}
+
+		} /* end of the "while(req->lr < req->r)" loop */
+
+#ifdef DEBUG_FULL
+		fprintf(stderr, "END: hdr_st=0x%02x, hdr_used=%d hdr_tail=%d hdr_last=%d, h=%p, lr=%p, r=%p\n",
+			t->hdr_state, t->hdr_idx.used, t->hdr_idx.tail, t->hdr_idx.last, req->h, req->lr, req->r);
+#endif
+
+		/*
+		 * Now, let's catch bad requests.
+		 */
+
+		if (t->hdr_state == HTTP_PA_ERROR) {
+			t->logs.status = 400;
+			client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
+			if (!(t->flags & SN_ERR_MASK))
+				t->flags |= SN_ERR_PRXCOND;
+			if (!(t->flags & SN_FINST_MASK))
+				t->flags |= SN_FINST_R;
+			return 1;
+		}
+
+
+
+		/*
+		 * Now we quickly check if we have found a full request.
+		 * If not so, we check the FD and buffer states before leaving.
+		 * A full request is indicated by the fact that we have seen
+		 * the double LF/CRLF, so the state is HTTP_PA_LFLF.
+		 *
+		 */
+
+		if (t->hdr_state != HTTP_PA_LFLF) {	/* Request not complete yet */
+
+			/* 1: 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. We
+			 *    must terminate it now.
+			 */
+			if (req->l >= req->rlim - req->data) {
+				/* FIXME: check if hdr_state & mask < HTTP_PA_HEADER,
+				 * and return Status 414 Request URI too long instead.
+				 */
+				t->logs.status = 400;
+				client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
+				if (!(t->flags & SN_ERR_MASK))
+					t->flags |= SN_ERR_PRXCOND;
+				if (!(t->flags & SN_FINST_MASK))
+					t->flags |= SN_FINST_R;
+				return 1;
+			}
+
+			/* 2: have we encountered a read error or a close ? */
+			else if (req->flags & (BF_READ_ERROR | BF_READ_NULL)) {
+				/* read error, or last read : give up.  */
+				tv_eternity(&req->rex);
+				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))
+					t->flags |= SN_FINST_R;
+				return 1;
+			}
+
+			/* 3: has the read timeout expired ? */
+			else if (tv_cmp2_ms(&req->rex, &now) <= 0) {
+				/* read timeout : give up with an error message. */
+				t->logs.status = 408;
+				client_retnclose(t, t->fe->errmsg.len408, t->fe->errmsg.msg408);
+				if (!(t->flags & SN_ERR_MASK))
+					t->flags |= SN_ERR_CLITO;
+				if (!(t->flags & SN_FINST_MASK))
+					t->flags |= SN_FINST_R;
+				return 1;
+			}
+
+			/* 4: do we need to re-enable the read socket ? */
+			else if (! MY_FD_ISSET(t->cli_fd, StaticReadEvent)) {
+				/* fd in StaticReadEvent was disabled, perhaps because of a previous buffer
+				 * full. We cannot loop here since stream_sock_read will disable it only if
+				 * req->l == rlim-data
+				 */
+				MY_FD_SET(t->cli_fd, StaticReadEvent);
+				if (t->fe->clitimeout)
+					tv_delayfrom(&req->rex, &now, t->fe->clitimeout);
+				else
+					tv_eternity(&req->rex);
+			}
+			return t->cli_state != CL_STHEADERS;
+		}
+
+
+		/****************************************************************
+		 * More interesting part now : we know that we have a complete  *
+		 * request which at least looks like HTTP. We have an indicator *
+		 * of each header's length, so we can parse them quickly.       *
+		 ****************************************************************/
+
+
+		/*
+		 * 1: the appsession cookie was looked up very early in 1.2,
+		 * so let's do the same now.
+		 */
+
+		/* It needs to look into the URI */
+		if (t->be->appsession_name) {
+			req->h = req->data + t->hdr_idx.v[0].len;            /* start of the URI */
+			ptr = req->h + t->hdr_idx.v[t->hdr_idx.v[0].next].len; /* end of the URI */
+			get_srv_from_appsession(t, req->h, ptr);
+		}
+
+
+		/*
+		 * 2: 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.
+		 */
+
+
+		/* try headers regexps */
+		if (t->fi->req_exp != NULL)
+			apply_filters_to_session(t, req, t->fi->req_exp);
+
+		/*
+		 * 3: Now we can work with the cookies.
+		 */
+
+		if (!(t->flags & (SN_CLDENY|SN_CLTARPIT)))
+			manage_client_side_cookies(t, req);
+
+
+
+#if TEST
+		/* example: dump each line */
+
+		fprintf(stderr, "t->flags=0x%08x\n", t->flags & (SN_CLALLOW|SN_CLDENY|SN_CLTARPIT));
+
+		fprintf(stderr, "req->h=%d\n", req->h - req->data);
+		req->h = req->data + t->hdr_idx.v[0].len;
+		cur_hdr = 0;
+
+		cur_idx = t->hdr_idx.v[0].next;
+		cur_hdr = 1;
+
+		while (cur_hdr < t->hdr_idx.used) {
+			ptr = req->h + t->hdr_idx.v[cur_idx].len + t->hdr_idx.v[cur_idx].cr + 1;
+			fprintf(stderr, "lr=%d r=%d hdr=%d idx=%d adr=%d..%d len=%d cr=%d data:\n",
+				req->lr - req->data, req->r - req->data,
+				cur_hdr, cur_idx,
+				req->h - req->data,
+				req->h - req->data + t->hdr_idx.v[cur_idx].len + t->hdr_idx.v[cur_idx].cr,
+				t->hdr_idx.v[cur_idx].len,
+				t->hdr_idx.v[cur_idx].cr);
+			write(2, req->h, ptr - req->h);
+
+			req->h = ptr;
+			cur_idx = t->hdr_idx.v[cur_idx].next;
+			cur_hdr++;
+		}
+#endif
+
+
+
+		/**************** debugging ***************/
+		t->logs.status = 400;
+		client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
+		if (!(t->flags & SN_ERR_MASK))
+			t->flags |= SN_ERR_PRXCOND;
+		if (!(t->flags & SN_FINST_MASK))
+			t->flags |= SN_FINST_R;
+		return 1;
+
+
+
+
+
+#if 0
+
+
+
+			char *ptr;
+			char *request_line = NULL;
+	
+			if (ptr == req->h) { /* empty line, end of headers */
+				int line, len;
+
 				/* we can only get here after an end of headers */
 				/* we'll have something else to do here : add new headers ... */
 
@@ -468,595 +1029,21 @@
 
 				goto process_data;
 			}
-
-			/* To get a complete header line, we need the ending \r\n, \n\r,
-			 * \r or \n, possibly followed by a white space or tab indicating
-			 * that the header goes on next line. */
-			if (ptr > req->r - 3) {
-				/* this is a partial header, let's wait for more to come */
-				req->lr = ptr;
-				break;
-			}
-
-			/* now we know that *ptr is either \r or \n,
-			 * and that there are at least 1 char after it.
-			 */
-			if ((ptr[0] == ptr[1]) || (ptr[1] != '\r' && ptr[1] != '\n'))
-				req->lr = ptr + 1; /* \r\r, \n\n, \r[^\n], \n[^\r] */
-			else
-				req->lr = ptr + 2; /* \r\n or \n\r */
-
-			/* Now, try to detect multi-line headers. From RFC 2616 :
-			 * HTTP/1.1 header field values can be folded onto multiple lines if the
-			 * continuation line begins with a space or horizontal tab. All linear
-			 * white space, including folding, has the same semantics as SP. A
-			 * recipient MAY replace any linear white space with a single SP before
-			 * interpreting the field value or forwarding the message downstream.
-			 *
-			 *     LWS            = [CRLF] 1*( SP | HT )
-			 */
-			if (req->lr < req->r &&
-			    (*req->lr == ' ' || *req->lr == '\t')) {
-				/* we are allowed to replace the \r\n with spaces */
-				while (ptr < req->lr)
-					*ptr++ = ' ';
-				/* now look for end of LWS */
-				do {
-					req->lr++;
-				} while (req->lr < req->r && (*req->lr == ' ' || *req->lr == '\t'));
-				
-				/* continue processing on the same header */
-				continue;
-			}
-
-			/*
-			 * now we know that we have a full header ; we can do whatever
-			 * we want with these pointers :
-			 *   req->h  = beginning of header
-			 *   ptr     = end of header (first \r or \n)
-			 *   req->lr = beginning of next line (next rep->h)
-			 *   req->r  = end of data (not used at this stage)
-			 */
-
-			if (!method_checked && (t->be->appsession_name != NULL) &&
-			    ((memcmp(req->h, "GET ", 4) == 0) || (memcmp(req->h, "POST ", 4) == 0)) &&
-			    ((request_line = memchr(req->h, ';', req->lr - req->h)) != NULL)) {
-
-				/* skip ; */
-				request_line++;
-
-				/* look if we have a jsessionid */
-
-				if (strncasecmp(request_line, t->be->appsession_name, t->be->appsession_name_len) == 0) {
-
-					/* skip jsessionid= */
-					request_line += t->be->appsession_name_len + 1;
-		
-					/* First try if we allready have an appsession */
-					asession_temp = &local_asession;
-		
-					if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
-						Alert("Not enough memory process_cli():asession_temp->sessid:calloc().\n");
-						send_log(t->be, LOG_ALERT, "Not enough Memory process_cli():asession_temp->sessid:calloc().\n");
-						return 0;
-					}
-
-					/* Copy the sessionid */
-					memcpy(asession_temp->sessid, request_line, t->be->appsession_len);
-					asession_temp->sessid[t->be->appsession_len] = 0;
-					asession_temp->serverid = NULL;
-
-					/* only do insert, if lookup fails */
-					if (chtbl_lookup(&(t->be->htbl_proxy), (void *)&asession_temp)) {
-						if ((asession_temp = pool_alloc(appsess)) == NULL) {
-							Alert("Not enough memory process_cli():asession:calloc().\n");
-							send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
-							return 0;
-						}
-						asession_temp->sessid = local_asession.sessid;
-						asession_temp->serverid = local_asession.serverid;
-						chtbl_insert(&(t->be->htbl_proxy), (void *) asession_temp);
-					} /* end if (chtbl_lookup()) */
-					else {
-						/*free wasted memory;*/
-						pool_free_to(apools.sessid, local_asession.sessid);
-					}
-
-					tv_delayfrom(&asession_temp->expire, &now, t->be->appsession_timeout);
-					asession_temp->request_count++;
-		
-#if defined(DEBUG_HASH)
-					print_table(&(t->proxy->htbl_proxy));
-#endif
-
-					if (asession_temp->serverid == NULL) {
-						Alert("Found Application Session without matching server.\n");
-					} else {
-						struct server *srv = t->be->srv;
-						while (srv) {
-							if (strcmp(srv->id, asession_temp->serverid) == 0) {
-								if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
-		                /* we found the server and it's usable */
-									t->flags &= ~SN_CK_MASK;
-									t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
-									t->srv = srv;
-									break;
-								} else {
-									t->flags &= ~SN_CK_MASK;
-									t->flags |= SN_CK_DOWN;
-								}
-							} /* end if (strcmp()) */
-							srv = srv->next;
-						}/* end while(srv) */
-					}/* end else of if (asession_temp->serverid == NULL) */
-				}/* end if (strncasecmp(request_line,t->proxy->appsession_name,apssesion_name_len) == 0) */
-				else {
-					//fprintf(stderr,">>>>>>>>>>>>>>>>>>>>>>NO SESSION\n");
-				}
-				method_checked = 1;
-			} /* end if (!method_checked ...) */
-			else{
-				//printf("No Methode-Header with Session-String\n");
-			}
-	    
-			if (t->logs.logwait & LW_REQ) {
-				/* we have a complete HTTP request that we must log */
-				int urilen;
-
-				if ((t->logs.uri = pool_alloc(requri)) == NULL) {
-					Alert("HTTP logging : out of memory.\n");
-					t->logs.status = 500;
-					client_retnclose(t, t->fe->errmsg.len500, t->fe->errmsg.msg500);
-					if (!(t->flags & SN_ERR_MASK))
-						t->flags |= SN_ERR_PRXCOND;
-					if (!(t->flags & SN_FINST_MASK))
-						t->flags |= SN_FINST_R;
-					return 1;
-				}
-		
-				urilen = ptr - req->h;
-				if (urilen >= REQURI_LEN)
-					urilen = REQURI_LEN - 1;
-				memcpy(t->logs.uri, req->h, urilen);
-				t->logs.uri[urilen] = 0;
-
-				if (!(t->logs.logwait &= ~LW_REQ))
-					sess_log(t);
-			}
-			else if (t->logs.logwait & LW_REQHDR) {
-				struct cap_hdr *h;
-				int len;
-				for (h = t->fi->req_cap; h; h = h->next) {
-					if ((h->namelen + 2 <= ptr - req->h) &&
-					    (req->h[h->namelen] == ':') &&
-					    (strncasecmp(req->h, h->name, h->namelen) == 0)) {
-
-						if (t->req_cap[h->index] == NULL)
-							t->req_cap[h->index] = pool_alloc_from(h->pool, h->len + 1);
-
-						len = ptr - (req->h + h->namelen + 2);
-						if (len > h->len)
-							len = h->len;
-
-						memcpy(t->req_cap[h->index], req->h + h->namelen + 2, len);
-						t->req_cap[h->index][len]=0;
-					}
-				}
-		
-			}
-
-			delete_header = 0;
-
-			if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
-				int len, max;
-				len = sprintf(trash, "%08x:%s.clihdr[%04x:%04x]: ", t->uniq_id, t->be->id, (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
-				max = ptr - req->h;
-				UBOUND(max, sizeof(trash) - len - 1);
-				len += strlcpy2(trash + len, req->h, max + 1);
-				trash[len++] = '\n';
-				write(1, trash, len);
-			}
-
-
-			/* remove "connection: " if needed */
-			if (!delete_header && (t->fe->options & PR_O_HTTP_CLOSE)
-			    && (strncasecmp(req->h, "Connection: ", 12) == 0)) {
-				delete_header = 1;
-			}
-
-			/* try headers regexps */
-			if (!delete_header && t->fi->req_exp != NULL
-			    && !(t->flags & SN_CLDENY)) {
-				struct hdr_exp *exp;
-				char term;
-		
-				term = *ptr;
-				*ptr = '\0';
-				exp = t->fi->req_exp;
-				do {
-					if (regexec(exp->preg, req->h, MAX_MATCH, pmatch, 0) == 0) {
-						switch (exp->action) {
-						case ACT_ALLOW:
-							if (!(t->flags & (SN_CLDENY | SN_CLTARPIT)))
-								t->flags |= SN_CLALLOW;
-							break;
-						case ACT_REPLACE:
-							if (!(t->flags & (SN_CLDENY | SN_CLTARPIT))) {
-								int len = exp_replace(trash, req->h, exp->replace, pmatch);
-								ptr += buffer_replace2(req, req->h, ptr, trash, len);
-							}
-							break;
-						case ACT_REMOVE:
-							if (!(t->flags & (SN_CLDENY | SN_CLTARPIT)))
-								delete_header = 1;
-							break;
-						case ACT_DENY:
-							if (!(t->flags & (SN_CLALLOW | SN_CLTARPIT)))
-								t->flags |= SN_CLDENY;
-							break;
-						case ACT_TARPIT:
-							if (!(t->flags & (SN_CLALLOW | SN_CLDENY)))
-								t->flags |= SN_CLTARPIT;
-							break;
-						case ACT_PASS: /* we simply don't deny this one */
-							break;
-						}
-						break;
-					}
-				} while ((exp = exp->next) != NULL);
-				*ptr = term; /* restore the string terminator */
-			}
-	    
-			/* Now look for cookies. Conforming to RFC2109, we have to support
-			 * attributes whose name begin with a '$', and associate them with
-			 * the right cookie, if we want to delete this cookie.
-			 * So there are 3 cases for each cookie read :
-			 * 1) it's a special attribute, beginning with a '$' : ignore it.
-			 * 2) it's a server id cookie that we *MAY* want to delete : save
-			 *    some pointers on it (last semi-colon, beginning of cookie...)
-			 * 3) it's an application cookie : we *MAY* have to delete a previous
-			 *    "special" cookie.
-			 * At the end of loop, if a "special" cookie remains, we may have to
-			 * remove it. If no application cookie persists in the header, we
-			 * *MUST* delete it
-			 */
-			if (!delete_header &&
-			    (t->be->cookie_name != NULL || t->be->appsession_name !=NULL || t->fi->capture_name != NULL)
-			    && !(t->flags & (SN_CLDENY|SN_CLTARPIT)) && (ptr >= req->h + 8)
-			    && (strncasecmp(req->h, "Cookie: ", 8) == 0)) {
-				char *p1, *p2, *p3, *p4;
-				char *del_colon, *del_cookie, *colon;
-				int app_cookies;
 
-				p1 = req->h + 8; /* first char after 'Cookie: ' */
-				colon = p1;
-				/* del_cookie == NULL => nothing to be deleted */
-				del_colon = del_cookie = NULL;
-				app_cookies = 0;
-		
-				while (p1 < ptr) {
-					/* skip spaces and colons, but keep an eye on these ones */
-					while (p1 < ptr) {
-						if (*p1 == ';' || *p1 == ',')
-							colon = p1;
-						else if (!isspace((int)*p1))
-							break;
-						p1++;
-					}
-		    
-					if (p1 == ptr)
-						break;
-		    
-					/* p1 is at the beginning of the cookie name */
-					p2 = p1;
-					while (p2 < ptr && *p2 != '=')
-						p2++;
-		    
-					if (p2 == ptr)
-						break;
 
-					p3 = p2 + 1; /* skips the '=' sign */
-					if (p3 == ptr)
-						break;
-		    
-					p4 = p3;
-					while (p4 < ptr && !isspace((int)*p4) && *p4 != ';' && *p4 != ',')
-						p4++;
-		    
-					/* here, we have the cookie name between p1 and p2,
-					 * and its value between p3 and p4.
-					 * we can process it :
-					 *
-					 * Cookie: NAME=VALUE;
-					 * |      ||   ||    |
-					 * |      ||   ||    +--> p4
-					 * |      ||   |+-------> p3
-					 * |      ||   +--------> p2
-					 * |      |+------------> p1
-					 * |      +-------------> colon
-					 * +--------------------> req->h
-					 */
-		    
-					if (*p1 == '$') {
-						/* skip this one */
-					}
-					else {
-						/* first, let's see if we want to capture it */
-						if (t->fi->capture_name != NULL &&
-						    t->logs.cli_cookie == NULL &&
-						    (p4 - p1 >= t->fi->capture_namelen) &&
-						    memcmp(p1, t->fi->capture_name, t->fi->capture_namelen) == 0) {
-							int log_len = p4 - p1;
+			/*******************************************/
 
-							if ((t->logs.cli_cookie = pool_alloc(capture)) == NULL) {
-								Alert("HTTP logging : out of memory.\n");
-							} else {
-								if (log_len > t->fi->capture_len)
-									log_len = t->fi->capture_len;
-								memcpy(t->logs.cli_cookie, p1, log_len);
-								t->logs.cli_cookie[log_len] = 0;
-							}
-						}
 
-						if ((p2 - p1 == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
-						    (memcmp(p1, t->be->cookie_name, p2 - p1) == 0)) {
-							/* Cool... it's the right one */
-							struct server *srv = t->be->srv;
-							char *delim;
-
-							/* if we're in cookie prefix mode, we'll search the delimitor so that we
-							 * have the server ID betweek p3 and delim, and the original cookie between
-							 * delim+1 and p4. Otherwise, delim==p4 :
-							 *
-							 * Cookie: NAME=SRV~VALUE;
-							 * |      ||   ||  |     |
-							 * |      ||   ||  |     +--> p4
-							 * |      ||   ||  +--------> delim
-							 * |      ||   |+-----------> p3
-							 * |      ||   +------------> p2
-							 * |      |+----------------> p1
-							 * |      +-----------------> colon
-							 * +------------------------> req->h
-							 */
-
-							if (t->be->options & PR_O_COOK_PFX) {
-								for (delim = p3; delim < p4; delim++)
-									if (*delim == COOKIE_DELIM)
-										break;
-							}
-							else
-								delim = p4;
-
-
-							/* Here, we'll look for the first running server which supports the cookie.
-							 * This allows to share a same cookie between several servers, for example
-							 * to dedicate backup servers to specific servers only.
-							 * However, to prevent clients from sticking to cookie-less backup server
-							 * when they have incidentely learned an empty cookie, we simply ignore
-							 * empty cookies and mark them as invalid.
-							 */
-							if (delim == p3)
-								srv = NULL;
-
-							while (srv) {
-								if ((srv->cklen == delim - p3) && !memcmp(p3, srv->cookie, delim - p3)) {
-									if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
-										/* we found the server and it's usable */
-										t->flags &= ~SN_CK_MASK;
-										t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
-										t->srv = srv;
-										break;
-									} else {
-										/* we found a server, but it's down */
-										t->flags &= ~SN_CK_MASK;
-										t->flags |= SN_CK_DOWN;
-									}
-								}
-								srv = srv->next;
-							}
-
-							if (!srv && !(t->flags & SN_CK_DOWN)) {
-								/* no server matched this cookie */
-								t->flags &= ~SN_CK_MASK;
-								t->flags |= SN_CK_INVALID;
-							}
-
-							/* depending on the cookie mode, we may have to either :
-							 * - delete the complete cookie if we're in insert+indirect mode, so that
-							 *   the server never sees it ;
-							 * - remove the server id from the cookie value, and tag the cookie as an
-							 *   application cookie so that it does not get accidentely removed later,
-							 *   if we're in cookie prefix mode
-							 */
-							if ((t->be->options & PR_O_COOK_PFX) && (delim != p4)) {
-								buffer_replace2(req, p3, delim + 1, NULL, 0);
-								p4  -= (delim + 1 - p3);
-								ptr -= (delim + 1 - p3);
-								del_cookie = del_colon = NULL;
-								app_cookies++;	/* protect the header from deletion */
-							}
-							else if (del_cookie == NULL &&
-								 (t->be->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) {
-								del_cookie = p1;
-								del_colon = colon;
-							}
-						} else {
-							/* now we know that we must keep this cookie since it's
-							 * not ours. But if we wanted to delete our cookie
-							 * earlier, we cannot remove the complete header, but we
-							 * can remove the previous block itself.
-							 */
-							app_cookies++;
-			    
-							if (del_cookie != NULL) {
-								buffer_replace2(req, del_cookie, p1, NULL, 0);
-								p4  -= (p1 - del_cookie);
-								ptr -= (p1 - del_cookie);
-								del_cookie = del_colon = NULL;
-							}
-						}
-			
-						if ((t->be->appsession_name != NULL) &&
-						    (memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
-							/* first, let's see if the cookie is our appcookie*/
-			    
-							/* Cool... it's the right one */
-
-							asession_temp = &local_asession;
-			  
-							if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
-								Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
-								send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
-								return 0;
-							}
-			  
-							memcpy(asession_temp->sessid, p3, t->be->appsession_len);
-							asession_temp->sessid[t->be->appsession_len] = 0;
-							asession_temp->serverid = NULL;
-			    
-							/* only do insert, if lookup fails */
-							if (chtbl_lookup(&(t->be->htbl_proxy), (void *) &asession_temp) != 0) {
-								if ((asession_temp = pool_alloc(appsess)) == NULL) {
-									Alert("Not enough memory process_cli():asession:calloc().\n");
-									send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
-									return 0;
-								}
-				
-								asession_temp->sessid = local_asession.sessid;
-								asession_temp->serverid = local_asession.serverid;
-								chtbl_insert(&(t->be->htbl_proxy), (void *) asession_temp);
-							} else {
-								/* free wasted memory */
-								pool_free_to(apools.sessid, local_asession.sessid);
-							}
-			    
-							if (asession_temp->serverid == NULL) {
-								Alert("Found Application Session without matching server.\n");
-							} else {
-								struct server *srv = t->be->srv;
-								while (srv) {
-									if (strcmp(srv->id, asession_temp->serverid) == 0) {
-										if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
-											/* we found the server and it's usable */
-											t->flags &= ~SN_CK_MASK;
-											t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
-											t->srv = srv;
-											break;
-										} else {
-											t->flags &= ~SN_CK_MASK;
-											t->flags |= SN_CK_DOWN;
-										}
-									}
-									srv = srv->next;
-								}/* end while(srv) */
-							}/* end else if server == NULL */
-			    
-							tv_delayfrom(&asession_temp->expire, &now, t->be->appsession_timeout);
-						}/* end if ((t->proxy->appsession_name != NULL) ... */
-					}
-
-					/* we'll have to look for another cookie ... */
-					p1 = p4;
-				} /* while (p1 < ptr) */
-
-				/* There's no more cookie on this line.
-				 * We may have marked the last one(s) for deletion.
-				 * We must do this now in two ways :
-				 *  - if there is no app cookie, we simply delete the header ;
-				 *  - if there are app cookies, we must delete the end of the
-				 *    string properly, including the colon/semi-colon before
-				 *    the cookie name.
-				 */
-				if (del_cookie != NULL) {
-					if (app_cookies) {
-						buffer_replace2(req, del_colon, ptr, NULL, 0);
-						/* WARNING! <ptr> becomes invalid for now. If some code
-						 * below needs to rely on it before the end of the global
-						 * header loop, we need to correct it with this code :
-						 */
-						ptr = del_colon;
-					}
-					else
-						delete_header = 1;
-				}
-			} /* end of cookie processing on this header */
-
-			/* let's look if we have to delete this header */
-			if (delete_header && !(t->flags & (SN_CLDENY|SN_CLTARPIT))) {
-				buffer_replace2(req, req->h, req->lr, NULL, 0);
-				/* WARNING: ptr is not valid anymore, since the header may have
-				 * been deleted or truncated ! */
-			} else {
-				/* try to catch the first line as the request */
-				if (t->req_line.len < 0) {
-					t->req_line.str = req->h;
-					t->req_line.len = ptr - req->h;
-				}
-
-				/* We might also need the 'Authorization: ' header */
-				if (t->auth_hdr.len < 0 &&
-				    t->fi->uri_auth != NULL &&
-				    ptr > req->h + 15 &&
-				    !strncasecmp("Authorization: ", req->h, 15)) {
-					t->auth_hdr.str = req->h;
-					t->auth_hdr.len = ptr - req->h;
-				}
-			}
 
-			req->h = req->lr;
-		} /* while (req->lr < req->r) */
+#endif
 
-		/* end of header processing (even if incomplete) */
 
-		if ((req->l < req->rlim - req->data) && ! MY_FD_ISSET(t->cli_fd, StaticReadEvent)) {
-			/* fd in StaticReadEvent was disabled, perhaps because of a previous buffer
-			 * full. We cannot loop here since stream_sock_read will disable it only if
-			 * req->l == rlim-data
-			 */
-			MY_FD_SET(t->cli_fd, StaticReadEvent);
-			if (t->fe->clitimeout)
-				tv_delayfrom(&req->rex, &now, t->fe->clitimeout);
-			else
-				tv_eternity(&req->rex);
-		}
 
-		/* 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->l >= req->rlim - req->data) {
-			t->logs.status = 400;
-			client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
-			if (!(t->flags & SN_ERR_MASK))
-				t->flags |= SN_ERR_PRXCOND;
-			if (!(t->flags & SN_FINST_MASK))
-				t->flags |= SN_FINST_R;
-			return 1;
-		}
-		else if (req->flags & (BF_READ_ERROR | BF_READ_NULL)) {
-			/* read error, or last read : give up.  */
-			tv_eternity(&req->rex);
-			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))
-				t->flags |= SN_FINST_R;
-			return 1;
-		}
-		else if (tv_cmp2_ms(&req->rex, &now) <= 0) {
 
-			/* read timeout : give up with an error message.
-			 */
-			t->logs.status = 408;
-			client_retnclose(t, t->fe->errmsg.len408, t->fe->errmsg.msg408);
-			if (!(t->flags & SN_ERR_MASK))
-				t->flags |= SN_ERR_CLITO;
-			if (!(t->flags & SN_FINST_MASK))
-				t->flags |= SN_FINST_R;
-			return 1;
-		}
+		/****************************************************************/
+
 
-		return t->cli_state != CL_STHEADERS;
+
 	}
 	else if (c == CL_STDATA) {
 	process_data:
@@ -1801,15 +1788,8 @@
 
 			delete_header = 0;
 
-			if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
-				int len, max;
-				len = sprintf(trash, "%08x:%s.srvhdr[%04x:%04x]: ", t->uniq_id, t->be->id, (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
-				max = ptr - rep->h;
-				UBOUND(max, sizeof(trash) - len - 1);
-				len += strlcpy2(trash + len, rep->h, max + 1);
-				trash[len++] = '\n';
-				write(1, trash, len);
-			}
+			if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))
+				debug_hdr("srvhdr", t, rep->h, ptr);
 
 			/* remove "connection: " if needed */
 			if (!delete_header && (t->fe->options & PR_O_HTTP_CLOSE)
@@ -2898,7 +2878,563 @@
 }
 
 
+
+/*
+ * Apply all the req filters <exp> to all headers in buffer <req> of session <t>
+ */
+
+void apply_filters_to_session(struct session *t, struct buffer *req, struct hdr_exp *exp)
+{
+	/* iterate through the filters in the outer loop */
+	while (exp && !(t->flags & (SN_CLDENY|SN_CLTARPIT))) {
+		char term;
+		char *cur_ptr, *cur_end, *cur_next;
+		int cur_idx, old_idx, abort_filt;
+		
+				
+		/*
+		 * The interleaving of transformations and verdicts
+		 * makes it difficult to decide to continue or stop
+		 * the evaluation.
+		 */
+
+		if ((t->flags & SN_CLALLOW) &&
+		    (exp->action == ACT_ALLOW || exp->action == ACT_DENY ||
+		     exp->action == ACT_TARPIT || exp->action == ACT_PASS)) {
+			exp = exp->next;
+			continue;
+		}
+
+		/* Iterate through the headers in the inner loop.
+		 * we start with the start line.
+		 */
+		old_idx = cur_idx = 0;
+		cur_next = req->data + t->hdr_idx.v[0].len;
+		abort_filt = 0;
+
+		while (!abort_filt && (cur_idx = t->hdr_idx.v[cur_idx].next)) {
+			struct hdr_idx_elem *cur_hdr = &t->hdr_idx.v[cur_idx];
+			cur_ptr = cur_next;
+			cur_end = cur_ptr + cur_hdr->len;
+			cur_next = cur_end + cur_hdr->cr + 1;
+
+			/* Now we have one header between cur_ptr and cur_end,
+			 * and the next header starts at cur_next.
+			 */
+
+			/* The annoying part is that pattern matching needs
+			 * that we modify the contents to null-terminate all
+			 * strings before testing them.
+			 */
+
+			term = *cur_end;
+			*cur_end = '\0';
+
+			if (regexec(exp->preg, cur_ptr, MAX_MATCH, pmatch, 0) == 0) {
+				switch (exp->action) {
+				case ACT_ALLOW:
+					if (!(t->flags & (SN_CLDENY | SN_CLTARPIT))) {
+						t->flags |= SN_CLALLOW;
+						abort_filt = 1;
+					}
+					break;
+				case ACT_REPLACE:
+					if (!(t->flags & (SN_CLDENY | SN_CLTARPIT))) {
+						int len, delta;
+						len = exp_replace(trash, cur_ptr, exp->replace, pmatch);
+						delta = buffer_replace2(req, cur_ptr, cur_end, trash, len);
+						cur_end += delta;
+						cur_next += delta;
+						cur_hdr->len += delta;
+					}
+					break;
+				case ACT_REMOVE:
+					if (!(t->flags & (SN_CLDENY | SN_CLTARPIT))) {
+						int delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
+						cur_next += delta;
+
+						/* FIXME: this should be a separate function */
+						t->hdr_idx.v[old_idx].next = cur_hdr->next;
+						t->hdr_idx.used--;
+						cur_hdr->len = 0;
+
+						cur_end = NULL; /* null-term has been rewritten */
+					}
+					break;
+				case ACT_DENY:
+					if (!(t->flags & (SN_CLALLOW | SN_CLTARPIT))) {
+						t->flags |= SN_CLDENY;
+						abort_filt = 1;
+					}
+					break;
+				case ACT_TARPIT:
+					if (!(t->flags & (SN_CLALLOW | SN_CLDENY))) {
+						t->flags |= SN_CLTARPIT;
+						abort_filt = 1;
+					}
+					break;
+					//case ACT_PASS: /* FIXME: broken as of now. We should mark the header as "ignored". */
+					//	break;
+				}
+			}
+			if (cur_end)
+				*cur_end = term; /* restore the string terminator */
+
+			/* keep the link from this header to next one */
+			old_idx = cur_idx;
+		}
+		exp = exp->next;
+	}
+}
+
+
+
+/*
+ * Manager client-side cookie
+ */
+void manage_client_side_cookies(struct session *t, struct buffer *req)
+{
+	char *p1, *p2, *p3, *p4;
+	char *del_colon, *del_cookie, *colon;
+	int app_cookies;
+
+	appsess *asession_temp = NULL;
+	appsess local_asession;
+
+	char *cur_ptr, *cur_end, *cur_next;
+	int cur_idx, old_idx, abort_filt;
+
+	if (t->be->cookie_name == NULL &&
+	    t->be->appsession_name ==NULL &&
+	    t->fi->capture_name != NULL)
+		return;
+
+
+
+	/* Iterate through the headers in the inner loop.
+	 * we start with the start line.
+	 */
+	old_idx = cur_idx = 0;
+	cur_next = req->data + t->hdr_idx.v[0].len;
+	abort_filt = 0;
+
+	while ((cur_idx = t->hdr_idx.v[cur_idx].next)) {
+		struct hdr_idx_elem *cur_hdr;
+
+		cur_hdr  = &t->hdr_idx.v[cur_idx];
+		cur_ptr  = cur_next;
+		cur_end  = cur_ptr + cur_hdr->len;
+		cur_next = cur_end + cur_hdr->cr + 1;
+
+		/* We have one full header between cur_ptr and cur_end, and the
+		 * next header starts at cur_next. We're only interested in
+		 * "Cookie:" headers.
+		 */
+
+		if ((cur_end - cur_ptr <= 7) ||
+		    (strncasecmp(cur_ptr, "Cookie:", 7) != 0)) {
+			old_idx = cur_idx;
+			continue;
+		}
+
+		/* Now look for cookies. Conforming to RFC2109, we have to support
+		 * attributes whose name begin with a '$', and associate them with
+		 * the right cookie, if we want to delete this cookie.
+		 * So there are 3 cases for each cookie read :
+		 * 1) it's a special attribute, beginning with a '$' : ignore it.
+		 * 2) it's a server id cookie that we *MAY* want to delete : save
+		 *    some pointers on it (last semi-colon, beginning of cookie...)
+		 * 3) it's an application cookie : we *MAY* have to delete a previous
+		 *    "special" cookie.
+		 * At the end of loop, if a "special" cookie remains, we may have to
+		 * remove it. If no application cookie persists in the header, we
+		 * *MUST* delete it
+		 */
+
+
+		p1 = cur_ptr + 7; /* first char after 'Cookie:' */
+		if (isspace((int)*p1)) /* try to get the first space with it */
+		    p1++;
+
+		colon = p1;
+		/* del_cookie == NULL => nothing to be deleted */
+		del_colon = del_cookie = NULL;
+		app_cookies = 0;
+		
+		while (p1 < cur_end) {
+			/* skip spaces and colons, but keep an eye on these ones */
+			while (p1 < cur_end) {
+				if (*p1 == ';' || *p1 == ',')
+					colon = p1;
+				else if (!isspace((int)*p1))
+					break;
+				p1++;
+			}
+
+			if (p1 == cur_end)
+				break;
+		    
+			/* p1 is at the beginning of the cookie name */
+			p2 = p1;
+			while (p2 < cur_end && *p2 != '=')
+				p2++;
+
+			if (p2 == cur_end)
+				break;
+
+			p3 = p2 + 1; /* skips the '=' sign */
+			if (p3 == cur_end)
+				break;
+		    
+			p4 = p3;
+			while (p4 < cur_end && !isspace((int)*p4) && *p4 != ';' && *p4 != ',')
+				p4++;
+
+			/* here, we have the cookie name between p1 and p2,
+			 * and its value between p3 and p4.
+			 * we can process it :
+			 *
+			 * Cookie: NAME=VALUE;
+			 * |      ||   ||    |
+			 * |      ||   ||    +--> p4
+			 * |      ||   |+-------> p3
+			 * |      ||   +--------> p2
+			 * |      |+------------> p1
+			 * |      +-------------> colon
+			 * +--------------------> cur_ptr
+			 */
+		    
+			if (*p1 == '$') {
+				/* skip this one */
+			}
+			else {
+				/* first, let's see if we want to capture it */
+				if (t->fi->capture_name != NULL &&
+				    t->logs.cli_cookie == NULL &&
+				    (p4 - p1 >= t->fi->capture_namelen) &&
+				    memcmp(p1, t->fi->capture_name, t->fi->capture_namelen) == 0) {
+					int log_len = p4 - p1;
+
+					if ((t->logs.cli_cookie = pool_alloc(capture)) == NULL) {
+						Alert("HTTP logging : out of memory.\n");
+					} else {
+						if (log_len > t->fi->capture_len)
+							log_len = t->fi->capture_len;
+						memcpy(t->logs.cli_cookie, p1, log_len);
+						t->logs.cli_cookie[log_len] = 0;
+					}
+				}
+
+				if ((p2 - p1 == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
+				    (memcmp(p1, t->be->cookie_name, p2 - p1) == 0)) {
+					/* Cool... it's the right one */
+					struct server *srv = t->be->srv;
+					char *delim;
+
+					/* if we're in cookie prefix mode, we'll search the delimitor so that we
+					 * have the server ID betweek p3 and delim, and the original cookie between
+					 * delim+1 and p4. Otherwise, delim==p4 :
+					 *
+					 * Cookie: NAME=SRV~VALUE;
+					 * |      ||   ||  |     |
+					 * |      ||   ||  |     +--> p4
+					 * |      ||   ||  +--------> delim
+					 * |      ||   |+-----------> p3
+					 * |      ||   +------------> p2
+					 * |      |+----------------> p1
+					 * |      +-----------------> colon
+					 * +------------------------> cur_ptr
+					 */
+
+					if (t->be->options & PR_O_COOK_PFX) {
+						for (delim = p3; delim < p4; delim++)
+							if (*delim == COOKIE_DELIM)
+								break;
+					}
+					else
+						delim = p4;
+
+
+					/* Here, we'll look for the first running server which supports the cookie.
+					 * This allows to share a same cookie between several servers, for example
+					 * to dedicate backup servers to specific servers only.
+					 * However, to prevent clients from sticking to cookie-less backup server
+					 * when they have incidentely learned an empty cookie, we simply ignore
+					 * empty cookies and mark them as invalid.
+					 */
+					if (delim == p3)
+						srv = NULL;
+
+					while (srv) {
+						if ((srv->cklen == delim - p3) && !memcmp(p3, srv->cookie, delim - p3)) {
+							if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
+								/* we found the server and it's usable */
+								t->flags &= ~SN_CK_MASK;
+								t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
+								t->srv = srv;
+								break;
+							} else {
+								/* we found a server, but it's down */
+								t->flags &= ~SN_CK_MASK;
+								t->flags |= SN_CK_DOWN;
+							}
+						}
+						srv = srv->next;
+					}
+
+					if (!srv && !(t->flags & SN_CK_DOWN)) {
+						/* no server matched this cookie */
+						t->flags &= ~SN_CK_MASK;
+						t->flags |= SN_CK_INVALID;
+					}
+
+					/* depending on the cookie mode, we may have to either :
+					 * - delete the complete cookie if we're in insert+indirect mode, so that
+					 *   the server never sees it ;
+					 * - remove the server id from the cookie value, and tag the cookie as an
+					 *   application cookie so that it does not get accidentely removed later,
+					 *   if we're in cookie prefix mode
+					 */
+					if ((t->be->options & PR_O_COOK_PFX) && (delim != p4)) {
+						int delta; /* negative */
+
+						delta = buffer_replace2(req, p3, delim + 1, NULL, 0);
+						p4  += delta;
+						cur_end += delta;
+						cur_next += delta;
+						cur_hdr->len += delta;
+
+						del_cookie = del_colon = NULL;
+						app_cookies++;	/* protect the header from deletion */
+					}
+					else if (del_cookie == NULL &&
+						 (t->be->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) {
+						del_cookie = p1;
+						del_colon = colon;
+					}
+				} else {
+					/* now we know that we must keep this cookie since it's
+					 * not ours. But if we wanted to delete our cookie
+					 * earlier, we cannot remove the complete header, but we
+					 * can remove the previous block itself.
+					 */
+					app_cookies++;
+
+					if (del_cookie != NULL) {
+						int delta; /* negative */
+
+						delta = buffer_replace2(req, del_cookie, p1, NULL, 0);
+						p4  += delta;
+						cur_end += delta;
+						cur_next += delta;
+						cur_hdr->len += delta;
+						del_cookie = del_colon = NULL;
+					}
+				}
+
+				if ((t->be->appsession_name != NULL) &&
+				    (memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
+					/* first, let's see if the cookie is our appcookie*/
+			    
+					/* Cool... it's the right one */
+
+					asession_temp = &local_asession;
+			  
+					if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
+						Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
+						send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
+						return;
+					}
+
+					memcpy(asession_temp->sessid, p3, t->be->appsession_len);
+					asession_temp->sessid[t->be->appsession_len] = 0;
+					asession_temp->serverid = NULL;
+			    
+					/* only do insert, if lookup fails */
+					if (chtbl_lookup(&(t->be->htbl_proxy), (void *) &asession_temp) != 0) {
+						if ((asession_temp = pool_alloc(appsess)) == NULL) {
+							/* free previously allocated memory */
+							pool_free_to(apools.sessid, local_asession.sessid);
+							Alert("Not enough memory process_cli():asession:calloc().\n");
+							send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
+							return;
+						}
+
+						asession_temp->sessid = local_asession.sessid;
+						asession_temp->serverid = local_asession.serverid;
+						chtbl_insert(&(t->be->htbl_proxy), (void *) asession_temp);
+					} else {
+						/* free previously allocated memory */
+						pool_free_to(apools.sessid, local_asession.sessid);
+					}
+			    
+					if (asession_temp->serverid == NULL) {
+						Alert("Found Application Session without matching server.\n");
+					} else {
+						struct server *srv = t->be->srv;
+						while (srv) {
+							if (strcmp(srv->id, asession_temp->serverid) == 0) {
+								if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
+									/* we found the server and it's usable */
+									t->flags &= ~SN_CK_MASK;
+									t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
+									t->srv = srv;
+									break;
+								} else {
+									t->flags &= ~SN_CK_MASK;
+									t->flags |= SN_CK_DOWN;
+								}
+							}
+							srv = srv->next;
+						}/* end while(srv) */
+					}/* end else if server == NULL */
+
+					tv_delayfrom(&asession_temp->expire, &now, t->be->appsession_timeout);
+				}/* end if ((t->proxy->appsession_name != NULL) ... */
+			}
+
+			/* we'll have to look for another cookie ... */
+			p1 = p4;
+		} /* while (p1 < cur_end) */
+
+		/* There's no more cookie on this line.
+		 * We may have marked the last one(s) for deletion.
+		 * We must do this now in two ways :
+		 *  - if there is no app cookie, we simply delete the header ;
+		 *  - if there are app cookies, we must delete the end of the
+		 *    string properly, including the colon/semi-colon before
+		 *    the cookie name.
+		 */
+		if (del_cookie != NULL) {
+			int delta;
+			if (app_cookies) {
+				delta = buffer_replace2(req, del_colon, cur_end, NULL, 0);
+				cur_end = del_colon;
+				cur_next += delta;
+				cur_hdr->len += delta;
+			} else {
+				delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
+				cur_next += delta;
+
+				/* FIXME: this should be a separate function */
+				t->hdr_idx.v[old_idx].next = cur_hdr->next;
+				t->hdr_idx.used--;
+				cur_hdr->len = 0;
+			}
+		}
+
+		/* keep the link from this header to next one */
+		old_idx = cur_idx;
+	} /* end of cookie processing on this header */
+}
+
+
+
+/*
+ * Try to retrieve a known appsession in the URI, then the associated server.
+ * If the server is found, it's assigned to the session.
+ */
+
+void get_srv_from_appsession(struct session *t, const char *begin, const char *end)
+{
+	appsess *asession_temp = NULL;
+	appsess local_asession;
+	char *request_line;
+
+	if (t->be->appsession_name == NULL ||
+	    (memcmp(begin, "GET ", 4) != 0 && memcmp(begin, "POST ", 5) != 0) ||
+	    (request_line = memchr(begin, ';', end - begin)) == NULL ||
+	    ((1 + t->be->appsession_name_len + 1 + t->be->appsession_len) > (end - request_line)))
+		return;
+
+	/* skip ';' */
+	request_line++;
+
+	/* look if we have a jsessionid */
+	if (strncasecmp(request_line, t->be->appsession_name, t->be->appsession_name_len) != 0)
+		return;
+
+	/* skip jsessionid= */
+	request_line += t->be->appsession_name_len + 1;
+	
+	/* First try if we already have an appsession */
+	asession_temp = &local_asession;
+	
+	if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
+		Alert("Not enough memory process_cli():asession_temp->sessid:calloc().\n");
+		send_log(t->be, LOG_ALERT, "Not enough Memory process_cli():asession_temp->sessid:calloc().\n");
+		return;
+	}
+	
+	/* Copy the sessionid */
+	memcpy(asession_temp->sessid, request_line, t->be->appsession_len);
+	asession_temp->sessid[t->be->appsession_len] = 0;
+	asession_temp->serverid = NULL;
+	
+	/* only do insert, if lookup fails */
+	if (chtbl_lookup(&(t->be->htbl_proxy), (void *)&asession_temp)) {
+		if ((asession_temp = pool_alloc(appsess)) == NULL) {
+			/* free previously allocated memory */
+			pool_free_to(apools.sessid, local_asession.sessid);
+			Alert("Not enough memory process_cli():asession:calloc().\n");
+			send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
+			return;
+		}
+		asession_temp->sessid = local_asession.sessid;
+		asession_temp->serverid = local_asession.serverid;
+		chtbl_insert(&(t->be->htbl_proxy), (void *) asession_temp);
+	}
+	else {
+		/* free previously allocated memory */
+		pool_free_to(apools.sessid, local_asession.sessid);
+	}
+	
+	tv_delayfrom(&asession_temp->expire, &now, t->be->appsession_timeout);
+	asession_temp->request_count++;
+	
+#if defined(DEBUG_HASH)
+	print_table(&(t->proxy->htbl_proxy));
+#endif
+	if (asession_temp->serverid == NULL) {
+		Alert("Found Application Session without matching server.\n");
+	} else {
+		struct server *srv = t->be->srv;
+		while (srv) {
+			if (strcmp(srv->id, asession_temp->serverid) == 0) {
+				if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
+					/* we found the server and it's usable */
+					t->flags &= ~SN_CK_MASK;
+					t->flags |= SN_CK_VALID | SN_DIRECT | SN_ASSIGNED;
+					t->srv = srv;
+					break;
+				} else {
+					t->flags &= ~SN_CK_MASK;
+					t->flags |= SN_CK_DOWN;
+				}
+			}
+			srv = srv->next;
+		}
+	}
+}
+
+
 /*
+ * Print a debug line with a header
+ */
+void debug_hdr(const char *dir, struct session *t, const char *start, const char *end)
+{
+	int len, max;
+	len = sprintf(trash, "%08x:%s.%s[%04x:%04x]: ", t->uniq_id, t->be->id,
+		      dir, (unsigned  short)t->cli_fd, (unsigned short)t->srv_fd);
+	max = end - start;
+	UBOUND(max, sizeof(trash) - len - 1);
+	len += strlcpy2(trash + len, start, max + 1);
+	trash[len++] = '\n';
+	write(1, trash, len);
+}
+
+
+/*
  * Local variables:
  *  c-indent-level: 8
  *  c-basic-offset: 8