[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;
}