[MAJOR] finished replacement of the client-side HTTP parser with a new one

The code is working again, but not as clean as it could be.
Many blocks should still move to dedicated functions. req->h
must be removed everywhere and updated everytime needed.

A few functions or macros should take care of the headers
during header insertion/deletion/change.
diff --git a/src/proto_http.c b/src/proto_http.c
index ea46fbd..bb4ebc3 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -367,13 +367,6 @@
 				    (!(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 */
@@ -547,17 +540,6 @@
 				}
 
 
-				/* 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.
 				 */
@@ -768,81 +750,156 @@
 		 */
 
 
-		/* try headers regexps */
+		/* try headers filters */
 		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);
-
+		/* has the request been denied ? */
+		if (t->flags & SN_CLDENY) {
+			/* no need to go further */
+			t->logs.status = 403;
+			t->logs.t_request = tv_diff(&t->logs.tv_accept, &now); /* let's log the request time */
+			client_retnclose(t, t->fe->errmsg.len403, t->fe->errmsg.msg403);
+			if (!(t->flags & SN_ERR_MASK))
+				t->flags |= SN_ERR_PRXCOND;
+			if (!(t->flags & SN_FINST_MASK))
+				t->flags |= SN_FINST_R;
+			return 1;
+		}
+		
 
+		/* Right now, we know that we have processed the entire headers
+		 * and that unwanted requests have been filtered out. We can do
+		 * whatever we want with the remaining request.
+		 */
 
-#if TEST
-		/* example: dump each line */
 
-		fprintf(stderr, "t->flags=0x%08x\n", t->flags & (SN_CLALLOW|SN_CLDENY|SN_CLTARPIT));
+		/*
+		 * 3: save a pointer to the first line as the request, and
+		 * check the method (needed for cookies).
+		 */
+		req->h = req->data + t->hdr_idx.v[0].len;            /* start of the REQURI */
+		ptr = req->h + t->hdr_idx.v[t->hdr_idx.v[0].next].len; /* end of the REQURI */
 
-		fprintf(stderr, "req->h=%d\n", req->h - req->data);
-		req->h = req->data + t->hdr_idx.v[0].len;
-		cur_hdr = 0;
+		t->req_line.str = req->h;
+		t->req_line.len = ptr - req->h;
 
-		cur_idx = t->hdr_idx.v[0].next;
-		cur_hdr = 1;
+		if (!memcmp(req->h, "POST ", 5)) {
+			/* this is a POST request, which is not cacheable by default */
+			t->flags |= SN_POST;
+		}
+		    
 
-		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);
+		/*
+		 * 4: Now we can work with the cookies.
+		 * Note that doing so might move headers in the request, but
+		 * the fields will stay coherent and the URI will not move.
+		 */
+		if (!(t->flags & (SN_CLDENY|SN_CLTARPIT)))
+			manage_client_side_cookies(t, req);
 
-			req->h = ptr;
-			cur_idx = t->hdr_idx.v[cur_idx].next;
-			cur_hdr++;
-		}
-#endif
 
+		/*
+		 * FIXME:
+		 * we could run the whole list of headers right here to extract
+		 * commonly used headers, as well as re-detect their real end.
+		 */
 
 
-		/**************** 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;
+		/*
+		 * 5: check if the URI matches the monitor_uri. To speed-up the
+		 * test, we include the leading and trailing spaces in the
+		 * comparison.
+		 */
+		if ((t->be->monitor_uri_len != 0) &&
+		    (t->req_line.len >= t->be->monitor_uri_len)) {
+			char *p = t->req_line.str;
+			int idx = 0;
 
+			/* skip the method so that we accept any method */
+			while (idx < t->req_line.len && p[idx] != ' ')
+				idx++;
+			p += idx;
+			
+			if (t->req_line.len - idx >= t->be->monitor_uri_len &&
+			    !memcmp(p, t->be->monitor_uri, t->be->monitor_uri_len)) {
+				/*
+				 * We have found the monitor URI
+				 */
+				t->flags |= SN_MONITOR;
+				t->logs.status = 200;
+				client_retnclose(t, strlen(HTTP_200), HTTP_200);
+				if (!(t->flags & SN_ERR_MASK))
+					t->flags |= SN_ERR_PRXCOND;
+				if (!(t->flags & SN_FINST_MASK))
+					t->flags |= SN_FINST_R;
+				return 1;
+			}
+		}
 
 
+		/*
+		 * 6: check if the user tries to access a protected URI.
+		 */
+		if (t->fi->uri_auth != NULL
+		    && t->req_line.len >= t->fi->uri_auth->uri_len + 4) {   /* +4 for "GET /" */
+			if (!memcmp(t->req_line.str + 4,
+				    t->fi->uri_auth->uri_prefix, t->fi->uri_auth->uri_len)
+			    && !memcmp(t->req_line.str, "GET ", 4)) {
+				struct user_auth *user;
+				int authenticated;
+				char *h;
 
+				/* we are in front of a interceptable URI. Let's check
+				 * if there's an authentication and if it's valid.
+				 */
+				user = t->fi->uri_auth->users;
+				if (!user) {
+					/* no user auth required, it's OK */
+					authenticated = 1;
+				} else {
+					authenticated = 0;
 
-#if 0
+					/* a user list is defined, we have to check.
+					 * skip 21 chars for "Authorization: Basic ".
+					 */
 
+					/* FIXME: this should move to an earlier place */
+					cur_idx = 0;
+					h = req->data + t->hdr_idx.v[0].len;
+					while ((cur_idx = t->hdr_idx.v[cur_idx].next)) {
+						int len = t->hdr_idx.v[cur_idx].len;
+						if (len > 14 &&
+						    !strncasecmp("Authorization:", h, 14)) {
+							t->auth_hdr.str = h;
+							t->auth_hdr.len = len;
+							break;
+						}
+						h += len + t->hdr_idx.v[cur_idx].cr + 1;
+					}
 
 
-			char *ptr;
-			char *request_line = NULL;
-	
-			if (ptr == req->h) { /* empty line, end of headers */
-				int line, len;
+					if (t->auth_hdr.len < 21 || memcmp(t->auth_hdr.str + 14, " Basic ", 7))
+						user = NULL;
+					
+					while (user) {
+						if ((t->auth_hdr.len == user->user_len + 21)
+						    && !memcmp(t->auth_hdr.str+21, user->user_pwd, user->user_len)) {
+							authenticated = 1;
+							break;
+						}
+						user = user->next;
+					}
+				}
 
-				/* we can only get here after an end of headers */
-				/* we'll have something else to do here : add new headers ... */
+				if (!authenticated) {
+					int msglen;
 
-				if (t->flags & SN_CLDENY) {
 					/* no need to go further */
-					t->logs.status = 403;
-					t->logs.t_request = tv_diff(&t->logs.tv_accept, &now); /* let's log the request time */
-					client_retnclose(t, t->fe->errmsg.len403, t->fe->errmsg.msg403);
+
+					msglen = sprintf(trash, HTTP_401_fmt, t->fi->uri_auth->auth_realm);
+					t->logs.status = 401;
+					client_retnclose(t, msglen, trash);
 					if (!(t->flags & SN_ERR_MASK))
 						t->flags |= SN_ERR_PRXCOND;
 					if (!(t->flags & SN_FINST_MASK))
@@ -850,200 +907,152 @@
 					return 1;
 				}
 
-				/* Right now, we know that we have processed the entire headers
-				 * and that unwanted requests have been filtered out. We can do
-				 * whatever we want.
-				 */
-
-
-				/* check if the URI matches the monitor_uri. To speed-up the
-				 * test, we include the leading and trailing spaces in the
-				 * comparison.
-				 */
-				if ((t->be->monitor_uri_len != 0) &&
-				    (t->req_line.len >= t->be->monitor_uri_len)) {
-					char *p = t->req_line.str;
-					int idx = 0;
-
-					/* skip the method so that we accept any method */
-					while (idx < t->req_line.len && p[idx] != ' ')
-						idx++;
-					p += idx;
+				t->cli_state = CL_STSHUTR;
+				req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
+				t->logs.t_request = tv_diff(&t->logs.tv_accept, &now);
+				t->data_source = DATA_SRC_STATS;
+				t->data_state  = DATA_ST_INIT;
+				produce_content(t);
+				return 1;
+			}
+		}
 
-					if (t->req_line.len - idx >= t->be->monitor_uri_len &&
-					    !memcmp(p, t->be->monitor_uri, t->be->monitor_uri_len)) {
-						/*
-						 * We have found the monitor URI
-						 */
-						t->flags |= SN_MONITOR;
-						t->logs.status = 200;
-						client_retnclose(t, strlen(HTTP_200), HTTP_200);
-						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 (t->fi->uri_auth != NULL
-				    && t->req_line.len >= t->fi->uri_auth->uri_len + 4) {   /* +4 for "GET /" */
-					if (!memcmp(t->req_line.str + 4,
-						    t->fi->uri_auth->uri_prefix, t->fi->uri_auth->uri_len)
-					    && !memcmp(t->req_line.str, "GET ", 4)) {
-						struct user_auth *user;
-						int authenticated;
+		/*
+		 * We will now have some data to append after the end of the
+		 * request. Right now we do not have the end of request pointer
+		 * anymore, so we have to look for it.
+		 */
 
-						/* we are in front of a interceptable URI. Let's check
-						 * if there's an authentication and if it's valid.
-						 */
-						user = t->fi->uri_auth->users;
-						if (!user) {
-							/* no user auth required, it's OK */
-							authenticated = 1;
-						} else {
-							authenticated = 0;
+#if READABLE_PARSER_VERSION
+		for (req->h = req->data + t->hdr_idx.v[cur_idx=0].len;
+		     (cur_idx = t->hdr_idx.v[cur_idx].next);
+		     req->h  += t->hdr_idx.v[cur_idx].len + t->hdr_idx.v[cur_idx]cr + 1);
+#else
+		req->h = req->data;
+		cur_idx = 0;
 
-							/* a user list is defined, we have to check.
-							 * skip 21 chars for "Authorization: Basic ".
-							 */
-							if (t->auth_hdr.len < 21 || memcmp(t->auth_hdr.str + 14, " Basic ", 7))
-								user = NULL;
+		while ((req->h += t->hdr_idx.v[cur_idx].len,
+			cur_idx = t->hdr_idx.v[cur_idx].next))
+			req->h  += t->hdr_idx.v[cur_idx].cr + 1;
+#endif
 
-							while (user) {
-								if ((t->auth_hdr.len == user->user_len + 21)
-								    && !memcmp(t->auth_hdr.str+21, user->user_pwd, user->user_len)) {
-									authenticated = 1;
-									break;
-								}
-								user = user->next;
-							}
-						}
+		/* now req->h points to the last LF/CRLF */
 
-						if (!authenticated) {
-							int msglen;
+		/*
+		 * 7: add request headers
+		 * FIXME: this should move to a separate function.
+		 */
+		for (cur_hdr = 0; cur_hdr < t->fi->nb_reqadd; cur_hdr++) {
+			int len = sprintf(trash, "%s\r\n", t->fi->req_add[cur_hdr]);
+			buffer_replace2(req, req->h, req->h, trash, len);
+		}
 
-							/* no need to go further */
 
-							msglen = sprintf(trash, HTTP_401_fmt, t->fi->uri_auth->auth_realm);
-							t->logs.status = 401;
-							client_retnclose(t, msglen, trash);
-							if (!(t->flags & SN_ERR_MASK))
-								t->flags |= SN_ERR_PRXCOND;
-							if (!(t->flags & SN_FINST_MASK))
-								t->flags |= SN_FINST_R;
-							return 1;
-						}
+		/*
+		 * 8: add X-Forwarded-For
+		 */
+		if (t->be->options & PR_O_FWDFOR) {
+			if (t->cli_addr.ss_family == AF_INET) {
+				int len;
+				unsigned char *pn;
+				pn = (unsigned char *)&((struct sockaddr_in *)&t->cli_addr)->sin_addr;
+				len = sprintf(trash, "X-Forwarded-For: %d.%d.%d.%d\r\n",
+					      pn[0], pn[1], pn[2], pn[3]);
+				buffer_replace2(req, req->h, req->h, trash, len);
+			}
+			else if (t->cli_addr.ss_family == AF_INET6) {
+				int len;
+				char pn[INET6_ADDRSTRLEN];
+				inet_ntop(AF_INET6,
+					  (const void *)&((struct sockaddr_in6 *)(&t->cli_addr))->sin6_addr,
+					  pn, sizeof(pn));
+				len = sprintf(trash, "X-Forwarded-For: %s\r\n", pn);
+				buffer_replace2(req, req->h, req->h, trash, len);
+			}
+		}
 
-						t->cli_state = CL_STSHUTR;
-						req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
-						t->logs.t_request = tv_diff(&t->logs.tv_accept, &now);
-						t->data_source = DATA_SRC_STATS;
-						t->data_state  = DATA_ST_INIT;
-						produce_content(t);
-						return 1;
-					}
-				}
 
+		/*
+		 * 9: add "Connection:"
+		 */
 
-				for (line = 0; line < t->fi->nb_reqadd; line++) {
-					len = sprintf(trash, "%s\r\n", t->fi->req_add[line]);
-					buffer_replace2(req, req->h, req->h, trash, len);
-				}
+		/* add a "connection: close" line if needed */
+		if (t->fe->options & PR_O_HTTP_CLOSE)
+			buffer_replace2(req, req->h, req->h, "Connection: close\r\n", 19);
 
-				if (t->be->options & PR_O_FWDFOR) {
-					if (t->cli_addr.ss_family == AF_INET) {
-						unsigned char *pn;
-						pn = (unsigned char *)&((struct sockaddr_in *)&t->cli_addr)->sin_addr;
-						len = sprintf(trash, "X-Forwarded-For: %d.%d.%d.%d\r\n",
-							      pn[0], pn[1], pn[2], pn[3]);
-						buffer_replace2(req, req->h, req->h, trash, len);
-					}
-					else if (t->cli_addr.ss_family == AF_INET6) {
-						char pn[INET6_ADDRSTRLEN];
-						inet_ntop(AF_INET6,
-							  (const void *)&((struct sockaddr_in6 *)(&t->cli_addr))->sin6_addr,
-							  pn, sizeof(pn));
-						len = sprintf(trash, "X-Forwarded-For: %s\r\n", pn);
-						buffer_replace2(req, req->h, req->h, trash, len);
-					}
-				}
 
-				/* add a "connection: close" line if needed */
-				if (t->fe->options & PR_O_HTTP_CLOSE)
-					buffer_replace2(req, req->h, req->h, "Connection: close\r\n", 19);
+		/*************************************************************
+		 * OK, that's finished for the headers. We have done what we *
+		 * could. Let's switch to the DATA state.                    *
+		 ************************************************************/
 
-				if (!memcmp(req->data, "POST ", 5)) {
-					/* this is a POST request, which is not cacheable by default */
-					t->flags |= SN_POST;
-				}
-		    
-				t->cli_state = CL_STDATA;
-				req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
+		t->cli_state = CL_STDATA;
+		req->rlim = req->data + BUFSIZE; /* no more rewrite needed */
 
-				t->logs.t_request = tv_diff(&t->logs.tv_accept, &now);
-				/* FIXME: we'll set the client in a wait state while we try to
-				 * connect to the server. Is this really needed ? wouldn't it be
-				 * better to release the maximum of system buffers instead ?
-				 * The solution is to enable the FD but set its time-out to
-				 * eternity as long as the server-side does not enable data xfer.
-				 * CL_STDATA also has to take care of this, which is done below.
-				 */
-				//MY_FD_CLR(t->cli_fd, StaticReadEvent);
-				//tv_eternity(&req->rex);
+		t->logs.t_request = tv_diff(&t->logs.tv_accept, &now);
 
-				/* FIXME: if we break here (as up to 1.1.23), having the client
-				 * shutdown its connection can lead to an abort further.
-				 * it's better to either return 1 or even jump directly to the
-				 * data state which will save one schedule.
-				 */
-				//break;
 
-				if (!t->fe->clitimeout ||
-				    (t->srv_state < SV_STDATA && t->be->srvtimeout))
-					/* If the client has no timeout, or if the server is not ready yet,
-					 * and we know for sure that it can expire, then it's cleaner to
-					 * disable the timeout on the client side so that too low values
-					 * cannot make the sessions abort too early.
-					 *
-					 * FIXME-20050705: the server needs a way to re-enable this time-out
-					 * when it switches its state, otherwise a client can stay connected
-					 * indefinitely. This now seems to be OK.
-					 */
-					tv_eternity(&req->rex);
+		if (!t->fe->clitimeout ||
+		    (t->srv_state < SV_STDATA && t->be->srvtimeout)) {
+			/* If the client has no timeout, or if the server is not ready yet,
+			 * and we know for sure that it can expire, then it's cleaner to
+			 * disable the timeout on the client side so that too low values
+			 * cannot make the sessions abort too early.
+			 *
+			 * FIXME-20050705: the server needs a way to re-enable this time-out
+			 * when it switches its state, otherwise a client can stay connected
+			 * indefinitely. This now seems to be OK.
+			 */
+			tv_eternity(&req->rex);
+		}
 
 
-				/* When a connection is tarpitted, we use the queue timeout for the
-				 * tarpit delay, which currently happens to be the server's connect
-				 * timeout. If unset, then set it to zero because we really want it
-				 * to expire at one moment.
-				 */
-				if (t->flags & SN_CLTARPIT) {
-					t->req->l = 0;
-					/* flush the request so that we can drop the connection early
-					 * if the client closes first.
-					 */
-					tv_delayfrom(&req->cex, &now,
-						     t->be->contimeout ? t->be->contimeout : 0);
-				}
+		/* When a connection is tarpitted, we use the queue timeout for the
+		 * tarpit delay, which currently happens to be the server's connect
+		 * timeout. If unset, then set it to zero because we really want it
+		 * to expire at one moment.
+		 */
+		if (t->flags & SN_CLTARPIT) {
+			t->req->l = 0;
+			/* flush the request so that we can drop the connection early
+			 * if the client closes first.
+			 */
+			tv_delayfrom(&req->cex, &now,
+				     t->be->contimeout ? t->be->contimeout : 0);
+		}
+		
+		goto process_data;
 
-				goto process_data;
-			}
+#if DEBUG_HTTP_PARSER
+		/* 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
 
-
-
-
-		/****************************************************************/
-
-
-
 	}
 	else if (c == CL_STDATA) {
 	process_data:
@@ -3011,7 +3020,7 @@
 
 
 
-	/* Iterate through the headers in the inner loop.
+	/* Iterate through the headers.
 	 * we start with the start line.
 	 */
 	old_idx = cur_idx = 0;