[MEDIUM] http: split request waiter from request processor

We want to split several steps in HTTP processing so that
we can call individual analysers depending on what processing
we want to perform. The first step consists in splitting the
part that waits for a request from the rest.
This commit is contained in:
Willy Tarreau 2009-07-07 10:14:51 +02:00
parent 571ec98baa
commit d787e6648c
5 changed files with 85 additions and 37 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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) ||

View File

@ -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

View File

@ -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))