From: Willy Tarreau Date: Sun, 12 Jul 2009 07:47:04 +0000 (+0200) Subject: [MEDIUM] allow a TCP frontend to switch to an HTTP backend X-Git-Tag: v1.4-dev1~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=51aecc76f8dea77a151982875322c0f606d4f856;p=thirdparty%2Fhaproxy.git [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 6f267be58d..eafb8cfeb6 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4276,6 +4276,12 @@ use_backend unless 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 be31c48801..229d71f48f 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -1832,6 +1832,12 @@ int http_process_req_common(struct session *s, struct buffer *req, int an_bit, s 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 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit) 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 @@ int http_process_request_body(struct session *s, struct buffer *req, int an_bit) 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 2dfc93b3b1..7f11a19b35 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -245,7 +246,8 @@ struct proxy *findproxy(const char *name, int mode, int cap) { 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 @@ int session_set_backend(struct session *s, struct proxy *be) 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; }