diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 175eb0479..8620bf45b 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -61,6 +61,7 @@ int event_accept(int fd); int process_cli(struct session *t); int process_srv_data(struct session *t); int process_srv_conn(struct session *t); +int http_wait_for_request(struct session *s, struct buffer *req); int http_process_request(struct session *t, struct buffer *req); int http_process_tarpit(struct session *s, struct buffer *req); int http_process_request_body(struct session *s, struct buffer *req); diff --git a/include/types/buffers.h b/include/types/buffers.h index faad019b2..a895477d1 100644 --- a/include/types/buffers.h +++ b/include/types/buffers.h @@ -111,6 +111,7 @@ #define AN_REQ_HTTP_TARPIT 0x00000008 /* wait for end of HTTP tarpit */ #define AN_RTR_HTTP_HDR 0x00000010 /* inspect HTTP response headers */ #define AN_REQ_UNIX_STATS 0x00000020 /* process unix stats socket request */ +#define AN_REQ_WAIT_HTTP 0x00000040 /* wait for an HTTP request */ /* describes a chunk of string */ struct chunk { diff --git a/src/cfgparse.c b/src/cfgparse.c index ce6b266b8..3852c459e 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -3777,7 +3777,7 @@ int check_config_validity() listener->handler = process_session; if (curproxy->mode == PR_MODE_HTTP) - listener->analysers |= AN_REQ_HTTP_HDR; + listener->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_HDR; /* smart accept mode is automatic in HTTP mode */ if ((curproxy->options2 & PR_O2_SMARTACC) || diff --git a/src/proto_http.c b/src/proto_http.c index 77092e024..7d43770ff 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -1502,29 +1502,14 @@ void http_msg_analyzer(struct buffer *buf, struct http_msg *msg, struct hdr_idx return; } -/* This function performs all the processing enabled for the current request. - * It returns 1 if the processing can continue on next analysers, or zero if it - * needs more data, encounters an error, or wants to immediately abort the - * request. It relies on buffers flags, and updates s->req->analysers. Its - * behaviour is rather simple: - * - all enabled analysers are called in turn from the lower to the higher - * bit. - * - the analyser must check for errors and timeouts, and react as expected. - * It does not have to close anything upon error, the caller will. - * - if the analyser does not have enough data, it must return 0without calling - * other ones. It should also probably do a buffer_write_dis() to ensure - * that unprocessed data will not be forwarded. But that probably depends on - * the protocol. - * - if an analyser has enough data, it just has to pass on to the next - * analyser without using buffer_write_dis() (enabled by default). - * - if an analyser thinks it has no added value anymore staying here, it must - * reset its bit from the analysers flags in order not to be called anymore. - * - * In the future, analysers should be able to indicate that they want to be - * called after XXX bytes have been received (or transfered), and the min of - * all's wishes will be used to ring back (unless a special condition occurs). +/* This stream analyser waits for a complete HTTP request. It returns 1 if the + * processing can continue on next analysers, or zero if it either needs more + * data or wants to immediately abort the request (eg: timeout, error, ...). It + * is tied to AN_REQ_WAIT_HTTP and may may remove itself from s->req->analysers + * when it has nothing left to do, and may remove any analyser when it wants to + * abort. */ -int http_process_request(struct session *s, struct buffer *req) +int http_wait_for_request(struct session *s, struct buffer *req) { /* * We will parse the partial (or complete) lines. @@ -1540,12 +1525,16 @@ int http_process_request(struct session *s, struct buffer *req) * req->data + req->eol = end of current header or line (LF or CRLF) * req->lr = first non-visited byte * req->r = end of data + * + * At end of parsing, we may perform a capture of the error (if any), and + * we will set a few fields (msg->sol, txn->meth, sn->flags/SN_REDIRECTABLE). + * We also check for monitor-uri, logging, HTTP/0.9 to 1.0 conversion, and + * finally headers capture. */ int cur_idx; struct http_txn *txn = &s->txn; struct http_msg *msg = &txn->req; - struct proxy *cur_proxy; DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n", now_ms, __FUNCTION__, @@ -1669,19 +1658,17 @@ int http_process_request(struct session *s, struct buffer *req) return 0; } + /* OK now we have a complete HTTP request with indexed headers. Let's + * complete the request parsing by setting a few fields we will need + * later. + */ - /**************************************************************** - * More interesting part now : we know that we have a complete * - * request which at least looks like HTTP. We have an indicator * - * of each header's length, so we can parse them quickly. * - ****************************************************************/ - - if (msg->err_pos >= 0) + /* Maybe we found in invalid header name while we were configured not + * to block on that, so we have to capture it now. + */ + if (unlikely(msg->err_pos >= 0)) http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe); - req->analysers &= ~AN_REQ_HTTP_HDR; - req->analyse_exp = TICK_ETERNITY; - /* ensure we keep this pointer to the beginning of the message */ msg->sol = req->data + msg->som; @@ -1708,13 +1695,12 @@ int http_process_request(struct session *s, struct buffer *req) * We have found the monitor URI */ struct acl_cond *cond; - cur_proxy = s->fe; s->flags |= SN_MONITOR; /* Check if we want to fail this monitor request or not */ - list_for_each_entry(cond, &cur_proxy->mon_fail_cond, list) { - int ret = acl_exec_cond(cond, cur_proxy, s, txn, ACL_DIR_REQ); + list_for_each_entry(cond, &s->fe->mon_fail_cond, list) { + int ret = acl_exec_cond(cond, s->fe, s, txn, ACL_DIR_REQ); ret = acl_pass(ret); if (cond->pol == ACL_COND_UNLESS) @@ -1793,6 +1779,60 @@ int http_process_request(struct session *s, struct buffer *req) capture_headers(req->data + msg->som, &txn->hdr_idx, txn->req.cap, s->fe->req_cap); + /* end of job, return OK */ + req->analysers &= ~AN_REQ_WAIT_HTTP; + req->analyse_exp = TICK_ETERNITY; + return 1; + + return_bad_req: + /* We centralize bad requests processing here */ + if (unlikely(msg->msg_state == HTTP_MSG_ERROR) || msg->err_pos >= 0) { + /* we detected a parsing error. We want to archive this request + * in the dedicated proxy area for later troubleshooting. + */ + http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe); + } + + txn->req.msg_state = HTTP_MSG_ERROR; + txn->status = 400; + stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400)); + s->fe->failed_req++; + + return_prx_cond: + if (!(s->flags & SN_ERR_MASK)) + s->flags |= SN_ERR_PRXCOND; + if (!(s->flags & SN_FINST_MASK)) + s->flags |= SN_FINST_R; + + req->analysers = 0; + req->analyse_exp = TICK_ETERNITY; + return 0; +} + +/* This function performs all the processing enabled for the current request. + * It returns 1 if the processing can continue on next analysers, or zero if it + * needs more data, encounters an error, or wants to immediately abort the + * request. It relies on buffers flags, and updates s->req->analysers. + */ +int http_process_request(struct session *s, struct buffer *req) +{ + int cur_idx; + struct http_txn *txn = &s->txn; + struct http_msg *msg = &txn->req; + struct proxy *cur_proxy; + + req->analysers &= ~AN_REQ_HTTP_HDR; + req->analyse_exp = TICK_ETERNITY; + + DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n", + now_ms, __FUNCTION__, + s, + req, + req->rex, req->wex, + req->flags, + req->l, + req->analysers); + /* * 6: we will have to evaluate the filters. * As opposed to version 1.2, now they will be evaluated in the diff --git a/src/session.c b/src/session.c index d3f3710c9..0f82639b8 100644 --- a/src/session.c +++ b/src/session.c @@ -730,6 +730,12 @@ resync_stream_interface: break; } + if (s->req->analysers & AN_REQ_WAIT_HTTP) { + last_ana |= AN_REQ_WAIT_HTTP; + if (!http_wait_for_request(s, s->req)) + break; + } + if (s->req->analysers & AN_REQ_HTTP_HDR) { last_ana |= AN_REQ_HTTP_HDR; if (!http_process_request(s, s->req))