[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_cli(struct session *t);
int process_srv_data(struct session *t); int process_srv_data(struct session *t);
int process_srv_conn(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_request(struct session *t, struct buffer *req);
int http_process_tarpit(struct session *s, struct buffer *req); int http_process_tarpit(struct session *s, struct buffer *req);
int http_process_request_body(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_REQ_HTTP_TARPIT 0x00000008 /* wait for end of HTTP tarpit */
#define AN_RTR_HTTP_HDR 0x00000010 /* inspect HTTP response headers */ #define AN_RTR_HTTP_HDR 0x00000010 /* inspect HTTP response headers */
#define AN_REQ_UNIX_STATS 0x00000020 /* process unix stats socket request */ #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 */ /* describes a chunk of string */
struct chunk { struct chunk {

View File

@ -3777,7 +3777,7 @@ int check_config_validity()
listener->handler = process_session; listener->handler = process_session;
if (curproxy->mode == PR_MODE_HTTP) 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 */ /* smart accept mode is automatic in HTTP mode */
if ((curproxy->options2 & PR_O2_SMARTACC) || 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; return;
} }
/* This function performs all the processing enabled for the current request. /* This stream analyser waits for a complete HTTP request. It returns 1 if the
* It returns 1 if the processing can continue on next analysers, or zero if it * processing can continue on next analysers, or zero if it either needs more
* needs more data, encounters an error, or wants to immediately abort the * data or wants to immediately abort the request (eg: timeout, error, ...). It
* request. It relies on buffers flags, and updates s->req->analysers. Its * is tied to AN_REQ_WAIT_HTTP and may may remove itself from s->req->analysers
* behaviour is rather simple: * when it has nothing left to do, and may remove any analyser when it wants to
* - all enabled analysers are called in turn from the lower to the higher * abort.
* 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).
*/ */
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. * 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->data + req->eol = end of current header or line (LF or CRLF)
* req->lr = first non-visited byte * req->lr = first non-visited byte
* req->r = end of data * 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; int cur_idx;
struct http_txn *txn = &s->txn; struct http_txn *txn = &s->txn;
struct http_msg *msg = &txn->req; 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", DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
now_ms, __FUNCTION__, now_ms, __FUNCTION__,
@ -1669,19 +1658,17 @@ int http_process_request(struct session *s, struct buffer *req)
return 0; 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.
*/
/**************************************************************** /* Maybe we found in invalid header name while we were configured not
* More interesting part now : we know that we have a complete * * to block on that, so we have to capture it now.
* request which at least looks like HTTP. We have an indicator * */
* of each header's length, so we can parse them quickly. * if (unlikely(msg->err_pos >= 0))
****************************************************************/
if (msg->err_pos >= 0)
http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe); 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 */ /* ensure we keep this pointer to the beginning of the message */
msg->sol = req->data + msg->som; 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 * We have found the monitor URI
*/ */
struct acl_cond *cond; struct acl_cond *cond;
cur_proxy = s->fe;
s->flags |= SN_MONITOR; s->flags |= SN_MONITOR;
/* Check if we want to fail this monitor request or not */ /* Check if we want to fail this monitor request or not */
list_for_each_entry(cond, &cur_proxy->mon_fail_cond, list) { list_for_each_entry(cond, &s->fe->mon_fail_cond, list) {
int ret = acl_exec_cond(cond, cur_proxy, s, txn, ACL_DIR_REQ); int ret = acl_exec_cond(cond, s->fe, s, txn, ACL_DIR_REQ);
ret = acl_pass(ret); ret = acl_pass(ret);
if (cond->pol == ACL_COND_UNLESS) 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, capture_headers(req->data + msg->som, &txn->hdr_idx,
txn->req.cap, s->fe->req_cap); 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. * 6: we will have to evaluate the filters.
* As opposed to version 1.2, now they will be evaluated in the * As opposed to version 1.2, now they will be evaluated in the

View File

@ -730,6 +730,12 @@ resync_stream_interface:
break; 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) { if (s->req->analysers & AN_REQ_HTTP_HDR) {
last_ana |= AN_REQ_HTTP_HDR; last_ana |= AN_REQ_HTTP_HDR;
if (!http_process_request(s, s->req)) if (!http_process_request(s, s->req))