[MEDIUM] allow a TCP frontend to switch to an HTTP backend

This patch allows a TCP frontend to switch to an HTTP backend.
During the switch, missing structures are automatically allocated.
The HTTP parser is enabled so that the backend first waits for a
full HTTP request.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 6f267be..eafb8cf 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4276,6 +4276,12 @@
   used (in case of a "listen" section) or, in case of a frontend, no server is
   used and a 503 service unavailable response is returned.
 
+  Note that it is possible to switch from a TCP frontend to an HTTP backend. In
+  this case, etiher the frontend has already checked that the protocol is HTTP,
+  and backend processing will immediately follow, or the backend will wait for
+  a complete HTTP request to get in. This feature is useful when a frontend
+  must decode several protocols on a unique port, one of them being HTTP.
+
   See also: "default_backend", "tcp-request", and section 7 about ACLs.
   
 
diff --git a/src/proto_http.c b/src/proto_http.c
index be31c48..229d71f 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1832,6 +1832,12 @@
 	struct redirect_rule *rule;
 	int cur_idx;
 
+	if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
+		/* we need more data */
+		buffer_write_dis(req);
+		return 0;
+	}
+
 	req->analysers &= ~an_bit;
 	req->analyse_exp = TICK_ETERNITY;
 
@@ -2092,6 +2098,12 @@
 	struct http_txn *txn = &s->txn;
 	struct http_msg *msg = &txn->req;
 
+	if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
+		/* we need more data */
+		buffer_write_dis(req);
+		return 0;
+	}
+
 	req->analysers &= ~an_bit;
 	req->analyse_exp = TICK_ETERNITY;
 
@@ -2450,6 +2462,12 @@
 	long long limit = s->be->url_param_post_limit;
 	struct hdr_ctx ctx;
 
+	if (unlikely(msg->msg_state != HTTP_MSG_BODY)) {
+		/* we need more data */
+		buffer_write_dis(req);
+		return 0;
+	}
+
 	/* We have to parse the HTTP request body to find any required data.
 	 * "balance url_param check_post" should have been the only way to get
 	 * into this. We were brought here after HTTP header analysis, so all
diff --git a/src/proxy.c b/src/proxy.c
index 2dfc93b..7f11a19 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -30,6 +30,7 @@
 #include <proto/client.h>
 #include <proto/backend.h>
 #include <proto/fd.h>
+#include <proto/hdr_idx.h>
 #include <proto/log.h>
 #include <proto/protocols.h>
 #include <proto/proto_tcp.h>
@@ -245,7 +246,8 @@
 		if ((curproxy->cap & cap)!=cap || strcmp(curproxy->id, name))
 			continue;
 
-		if (curproxy->mode != mode) {
+		if (curproxy->mode != mode &&
+		    !(curproxy->mode == PR_MODE_HTTP && mode == PR_MODE_TCP)) {
 			Alert("Unable to use proxy '%s' with wrong mode, required: %s, has: %s.\n", 
 				name, proxy_mode_str(mode), proxy_mode_str(curproxy->mode));
 			Alert("You may want to use 'mode %s'.\n", proxy_mode_str(mode));
@@ -654,6 +656,27 @@
 	if (be->options2 & PR_O2_RSPBUG_OK)
 		s->txn.rsp.err_pos = -1; /* let buggy responses pass */
 	s->flags |= SN_BE_ASSIGNED;
+
+	/* If the target backend requires HTTP processing, we have to allocate
+	 * a struct hdr_idx for it if we did not have one.
+	 */
+	if (unlikely(!s->txn.hdr_idx.v && (be->acl_requires & ACL_USE_L7_ANY))) {
+		if ((s->txn.hdr_idx.v = pool_alloc2(s->fe->hdr_idx_pool)) == NULL)
+			return 0; /* not enough memory */
+		s->txn.hdr_idx.size = MAX_HTTP_HDR;
+		hdr_idx_init(&s->txn.hdr_idx);
+	}
+
+	/* If we're switching from TCP mode to HTTP mode, we need to
+	 * enable several analysers on the backend.
+	 */
+	if (unlikely(s->fe->mode != PR_MODE_HTTP && s->be->mode == PR_MODE_HTTP)) {
+		/* We want to wait for a complete HTTP request and process the
+		 * backend parts.
+		 */
+		s->req->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_BE | AN_REQ_HTTP_INNER;
+	}
+
 	return 1;
 }