diff --git a/doc/configuration.txt b/doc/configuration.txt index 2dab50aac..69dfbe7bb 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1482,6 +1482,7 @@ option dontlognull (*) X X X - option forceclose (*) X X X X -- keyword -------------------------- defaults - frontend - listen -- backend - option forwardfor X X X X +option http-buffer-request (*) X X X X option http-ignore-probes (*) X X X - option http-keep-alive (*) X X X X option http-no-delay (*) X X X X @@ -4453,6 +4454,28 @@ option forwardfor [ except ] [ header ] [ if-none ] "option forceclose", "option http-keep-alive" +option http-buffer-request +no option http-buffer-request + Enable or disable waiting for whole HTTP request body before proceeding + May be used in sections : defaults | frontend | listen | backend + yes | yes | yes | yes + Arguments : none + + It is sometimes desirable to wait for the body of an HTTP request before + taking a decision. This is what is being done by "balance url_param" for + example. The first use case is to buffer requests from slow clients before + connecting to the server. Another use case consists in taking the routing + decision based on the request body's contents. This option placed in a + frontend or backend forces the HTTP processing to wait until either the whole + body is received, or the request buffer is full, or the first chunk is + complete in case of chunked encoding. It can have undesired side effects with + some applications abusing HTTP by expecting unbufferred transmissions between + the frontend and the backend, so this should definitely not be used by + default. + + See also : "option http-no-delay" + + option http-ignore-probes no option http-ignore-probes Enable or disable logging of null connections and request timeouts @@ -4578,6 +4601,8 @@ no option http-no-delay usage and CPU usage, which may significantly lower performance in high latency environments. + See also : "option http-buffer-request" + option http-pretend-keepalive no option http-pretend-keepalive diff --git a/include/types/proxy.h b/include/types/proxy.h index 34501f249..826a0f2b6 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -83,7 +83,8 @@ enum pr_mode { #define PR_O_FWDFOR 0x00000100 /* conditionally insert x-forwarded-for with client address */ #define PR_O_IGNORE_PRB 0x00000200 /* ignore empty requests (aborts and timeouts) */ #define PR_O_NULLNOLOG 0x00000400 /* a connect without request will not be logged */ -/* unused: 0x0800, 0x1000 */ +#define PR_O_WREQ_BODY 0x00000800 /* always wait for the HTTP request body */ +/* unused: 0x1000 */ #define PR_O_FF_ALWAYS 0x00002000 /* always set x-forwarded-for */ #define PR_O_PERSIST 0x00004000 /* server persistence stays effective even when server is down */ #define PR_O_LOGASAP 0x00008000 /* log as soon as possible, without waiting for the stream to complete */ diff --git a/src/cfgparse.c b/src/cfgparse.c index e8004c628..8578bc517 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -154,6 +154,7 @@ static const struct cfg_opt cfg_opts[] = { "contstats", PR_O_CONTSTATS, PR_CAP_FE, 0, 0 }, { "dontlognull", PR_O_NULLNOLOG, PR_CAP_FE, 0, 0 }, { "http_proxy", PR_O_HTTP_PROXY, PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP }, + { "http-buffer-request", PR_O_WREQ_BODY, PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP }, { "http-ignore-probes", PR_O_IGNORE_PRB, PR_CAP_FE, 0, PR_MODE_HTTP }, { "prefer-last-server", PR_O_PREF_LAST, PR_CAP_BE, 0, PR_MODE_HTTP }, { "logasap", PR_O_LOGASAP, PR_CAP_FE, 0, 0 }, diff --git a/src/proto_http.c b/src/proto_http.c index dc599815a..6bd89bc22 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3133,6 +3133,11 @@ int http_wait_for_request(struct stream *s, struct channel *req, int an_bit) ((sess->fe->options & PR_O_HTTP_MODE) != (s->be->options & PR_O_HTTP_MODE))) http_adjust_conn_mode(s, txn, msg); + /* we may have to wait for the request's body */ + if ((s->be->options & PR_O_WREQ_BODY) && + (msg->body_len || (msg->flags & HTTP_MSGF_TE_CHNK))) + req->analysers |= AN_REQ_HTTP_BODY; + /* end of job, return OK */ req->analysers &= ~an_bit; req->analyse_exp = TICK_ETERNITY; @@ -3288,7 +3293,7 @@ int http_handle_stats(struct stream *s, struct channel *req) } /* Was the status page requested with a POST ? */ - if (unlikely(txn->meth == HTTP_METH_POST && txn->req.body_len > 0)) { + if (unlikely(txn->meth == HTTP_METH_POST && txn->req.body_len > 0 && msg->msg_state < HTTP_MSG_BODY)) { if (appctx->ctx.stats.flags & STAT_ADMIN) { /* we'll need the request body, possibly after sending 100-continue */ req->analysers |= AN_REQ_HTTP_BODY; diff --git a/src/proxy.c b/src/proxy.c index 12e8aa333..ed3b58262 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -940,6 +940,13 @@ int stream_set_backend(struct stream *s, struct proxy *be) if (be->options2 & PR_O2_INDEPSTR) s->si[1].flags |= SI_FL_INDEP_STR; + /* We want to enable the backend-specific analysers except those which + * were already run as part of the frontend/listener. Note that it would + * be more reliable to store the list of analysers that have been run, + * but what we do here is OK for now. + */ + s->req.analysers |= be->be_req_ana & ~strm_li(s)->analysers; + /* If the target backend requires HTTP processing, we have to allocate * the HTTP transaction and hdr_idx if we did not have one. */ @@ -971,6 +978,11 @@ int stream_set_backend(struct stream *s, struct proxy *be) if (be->mode == PR_MODE_HTTP && (be->lbprm.algo & (BE_LB_KIND | BE_LB_PARM)) == (BE_LB_KIND_HI | BE_LB_HASH_PRM)) s->txn->req.flags |= HTTP_MSGF_WAIT_CONN; + + /* we may request to parse a request body */ + if ((be->options & PR_O_WREQ_BODY) && + (s->txn->req.body_len || (s->txn->req.flags & HTTP_MSGF_TE_CHNK))) + s->req.analysers |= AN_REQ_HTTP_BODY; } s->flags |= SF_BE_ASSIGNED; @@ -979,13 +991,6 @@ int stream_set_backend(struct stream *s, struct proxy *be) s->res.flags |= CF_NEVER_WAIT; } - /* We want to enable the backend-specific analysers except those which - * were already run as part of the frontend/listener. Note that it would - * be more reliable to store the list of analysers that have been run, - * but what we do here is OK for now. - */ - s->req.analysers |= be->be_req_ana & ~strm_li(s)->analysers; - return 1; }