From: Willy Tarreau Date: Sun, 10 Aug 2008 20:55:22 +0000 (+0200) Subject: [MAJOR] kill CL_STINSPECT and CL_STHEADERS (step 1) X-Git-Tag: v1.3.16-rc1~209 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=67f0eead22212edad338b77adb8de1c1bf032a77;p=thirdparty%2Fhaproxy.git [MAJOR] kill CL_STINSPECT and CL_STHEADERS (step 1) This is a first attempt at separating data processing from the TCP state machine. Those two states have been replaced with flags in the session indicating what needs to be analyzed. The corresponding code is still called before and in lieu of TCP states. Next change should get rid of the specific SV_STANALYZE which is in fact a client state. Then next change should consist in making it possible to analyze TCP contents while being in CL_STDATA (or CL_STSHUT*). --- diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 713da49c1e..546564b989 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -32,12 +32,10 @@ */ /* different possible states for the client side */ -#define CL_STINSPECT 0 -#define CL_STHEADERS 1 -#define CL_STDATA 2 -#define CL_STSHUTR 3 -#define CL_STSHUTW 4 -#define CL_STCLOSE 5 +#define CL_STDATA 0 +#define CL_STSHUTR 1 +#define CL_STSHUTW 2 +#define CL_STCLOSE 3 /* different possible states for the server side */ #define SV_STIDLE 0 diff --git a/include/types/session.h b/include/types/session.h index 5bcd0d795d..c94401ae17 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -82,6 +82,18 @@ * and freed in session_free() ! */ +/* analysis flags */ +#define AN_REQ_INSPECT 0x00000001 /* inspect request contents */ +#define AN_REQ_HTTP_HDR 0x00000002 /* inspect HTTP request headers */ +#define AN_REQ_HTTP_BODY 0x00000004 /* inspect HTTP request body */ +#define AN_REQ_ANY (AN_REQ_INSPECT|AN_REQ_HTTP_HDR|AN_REQ_HTTP_BODY) + +#define AN_RTR_INSPECT 0x00000008 /* inspect response contents */ +#define AN_RTR_HTTP_HDR 0x00000010 /* inspect HTTP response headers */ +#define AN_RTR_HTTP_BODY 0x00000020 /* inspect HTTP response body */ +#define AN_RTR_ANY (AN_RTR_INSPECT|AN_RTR_HTTP_HDR|AN_RTR_HTTP_BODY) + + /* * Note: some session flags have dependencies : * - SN_DIRECT cannot exist without SN_ASSIGNED, because a server is @@ -105,6 +117,7 @@ struct session { int srv_state; /* state of the server side */ int conn_retries; /* number of connect retries left */ int flags; /* some flags describing the session */ + unsigned int analysis; /* bit field indicating remaining analysis to perform on data */ struct buffer *req; /* request buffer */ struct buffer *rep; /* response buffer */ struct sockaddr_storage cli_addr; /* the client address */ diff --git a/src/client.c b/src/client.c index e04a066ff2..cbe5f11983 100644 --- a/src/client.c +++ b/src/client.c @@ -106,10 +106,12 @@ int event_accept(int fd) { goto out_close; } + s->flags = 0; + s->analysis = 0; + /* if this session comes from a known monitoring system, we want to ignore * it as soon as possible, which means closing it immediately for TCP. */ - s->flags = 0; if (addr.ss_family == AF_INET && p->mon_mask.s_addr && (((struct sockaddr_in *)&addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr) { @@ -159,26 +161,19 @@ int event_accept(int fd) { * TCP mode there will be no header processing so any default * backend must be assigned if set. */ - if (p->mode == PR_MODE_HTTP) { - if (s->fe->tcp_req.inspect_delay) - s->cli_state = CL_STINSPECT; - else - s->cli_state = CL_STHEADERS; - } else { - /* We must assign any default backend now since - * there will be no header processing. - */ - if (p->mode == PR_MODE_TCP) { - if (p->defbe.be) - s->be = p->defbe.be; - s->flags |= SN_BE_ASSIGNED; - } - if (s->fe->tcp_req.inspect_delay) - s->cli_state = CL_STINSPECT; - else - s->cli_state = CL_STDATA; /* no HTTP headers for non-HTTP proxies */ + if (p->mode == PR_MODE_TCP) { + if (p->defbe.be) + s->be = p->defbe.be; + s->flags |= SN_BE_ASSIGNED; } + if (p->mode == PR_MODE_HTTP) + s->analysis |= AN_REQ_HTTP_HDR; + + if (s->fe->tcp_req.inspect_delay) + s->analysis |= AN_REQ_INSPECT; + + s->cli_state = CL_STDATA; s->srv_state = SV_STIDLE; s->req = s->rep = NULL; /* will be allocated later */ @@ -341,7 +336,7 @@ int event_accept(int fd) { if (p->mode == PR_MODE_HTTP) /* reserve some space for header rewriting */ s->req->rlim -= MAXREWRITE; - if (s->cli_state == CL_STDATA) + if (!(s->analysis & AN_REQ_ANY)) s->req->flags |= BF_MAY_CONNECT; /* don't wait to establish connection */ s->req->rto = s->fe->timeout.client; @@ -405,13 +400,15 @@ int event_accept(int fd) { } } - if (s->cli_state == CL_STHEADERS && s->fe->timeout.httpreq) { - s->txn.exp = tick_add(now_ms, s->fe->timeout.httpreq); - t->expire = tick_first(t->expire, s->txn.exp); - } - else if (s->cli_state == CL_STINSPECT && s->fe->tcp_req.inspect_delay) { - s->inspect_exp = tick_add(now_ms, s->fe->tcp_req.inspect_delay); - t->expire = tick_first(t->expire, s->inspect_exp); + if (s->analysis & AN_REQ_ANY) { + if (s->analysis & AN_REQ_INSPECT) { + s->inspect_exp = tick_add_ifset(now_ms, s->fe->tcp_req.inspect_delay); + t->expire = tick_first(t->expire, s->inspect_exp); + } + else if (s->analysis & AN_REQ_HTTP_HDR) { + s->txn.exp = tick_add_ifset(now_ms, s->fe->timeout.httpreq); + t->expire = tick_first(t->expire, s->txn.exp); + } } if (p->mode != PR_MODE_HEALTH) diff --git a/src/proto_http.c b/src/proto_http.c index 8a8bfffcab..83406b6104 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -365,8 +365,8 @@ const char http_is_ver_token[256] = { #ifdef DEBUG_FULL -static char *cli_stnames[6] = {"INS", "HDR", "DAT", "SHR", "SHW", "CLS" }; -static char *srv_stnames[8] = {"IDL", "ANA", "CON", "HDR", "DAT", "SHR", "SHW", "CLS" }; +static char *cli_stnames[4] = { "DAT", "SHR", "SHW", "CLS" }; +static char *srv_stnames[8] = { "IDL", "ANA", "CON", "HDR", "DAT", "SHR", "SHW", "CLS" }; #endif static void http_sess_log(struct session *s); @@ -676,10 +676,12 @@ void process_session(struct task *t, int *next) t->expire = tick_first(tick_first(s->req->rex, s->req->wex), tick_first(s->rep->rex, s->rep->wex)); t->expire = tick_first(t->expire, s->req->cex); - if (s->cli_state == CL_STHEADERS) - t->expire = tick_first(t->expire, s->txn.exp); - else if (s->cli_state == CL_STINSPECT) - t->expire = tick_first(t->expire, s->inspect_exp); + if (s->analysis & AN_REQ_ANY) { + if (s->analysis & AN_REQ_INSPECT) + t->expire = tick_first(t->expire, s->inspect_exp); + else if (s->analysis & AN_REQ_HTTP_HDR) + t->expire = tick_first(t->expire, s->txn.exp); + } /* restore t to its place in the task list */ task_queue(t); @@ -1549,7 +1551,7 @@ int process_cli(struct session *t) EV_FD_ISSET(t->cli_fd, DIR_RD), EV_FD_ISSET(t->cli_fd, DIR_WR), req->rex, rep->wex); - if (c == CL_STINSPECT) { + if (t->analysis & AN_REQ_INSPECT) { struct tcp_rule *rule; int partial; @@ -1564,6 +1566,7 @@ int process_cli(struct session *t) buffer_shutw_done(rep); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + t->analysis &= ~AN_REQ_ANY; t->fe->failed_req++; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_CLICL; @@ -1579,6 +1582,7 @@ int process_cli(struct session *t) buffer_shutw_done(rep); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + t->analysis &= ~AN_REQ_ANY; t->fe->failed_req++; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_CLITO; @@ -1623,6 +1627,7 @@ int process_cli(struct session *t) buffer_shutw_done(rep); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + t->analysis &= ~AN_REQ_ANY; t->fe->failed_req++; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_PRXCOND; @@ -1636,21 +1641,21 @@ int process_cli(struct session *t) } } - /* if we get there, it means we have no rule which matches, so - * we apply the default accept. + /* if we get there, it means we have no rule which matches, or + * we have an explicit accept, so we apply the default accept. */ req->rex = tick_add_ifset(now_ms, t->fe->timeout.client); - if (t->fe->mode == PR_MODE_HTTP) { - t->cli_state = CL_STHEADERS; + t->analysis &= ~AN_REQ_INSPECT; + if (t->analysis & AN_REQ_HTTP_HDR) t->txn.exp = tick_add_ifset(now_ms, t->fe->timeout.httpreq); - } else { - t->cli_state = CL_STDATA; + else req->flags |= BF_MAY_CONNECT | BF_MAY_FORWARD; - } + t->inspect_exp = TICK_ETERNITY; return 1; } - else if (c == CL_STHEADERS) { + + if (t->analysis & AN_REQ_HTTP_HDR) { /* * Now parse the partial (or complete) lines. * We will check the request syntax, and also join multi-line @@ -1732,6 +1737,7 @@ int process_cli(struct session *t) buffer_shutw_done(rep); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + t->analysis &= ~AN_REQ_ANY; t->fe->failed_req++; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_CLICL; @@ -1746,6 +1752,7 @@ int process_cli(struct session *t) /* read timeout : give up with an error message. */ txn->status = 408; client_retnclose(t, error_message(t, HTTP_ERR_408)); + t->analysis &= ~AN_REQ_ANY; t->fe->failed_req++; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_CLITO; @@ -1762,7 +1769,8 @@ int process_cli(struct session *t) */ req->rex = tick_add_ifset(now_ms, t->fe->timeout.client); } - return t->cli_state != CL_STHEADERS; + /* we don't need to resync if the state has not changed */ + return !(t->analysis & AN_REQ_HTTP_HDR); } @@ -1772,6 +1780,8 @@ int process_cli(struct session *t) * of each header's length, so we can parse them quickly. * ****************************************************************/ + t->analysis &= ~AN_REQ_HTTP_HDR; + /* ensure we keep this pointer to the beginning of the message */ msg->sol = req->data + msg->som; @@ -2334,6 +2344,7 @@ int process_cli(struct session *t) http_find_header2("Transfer-Encoding", 17, msg->sol, &txn->hdr_idx, &ctx); if (unlikely(ctx.idx && strncasecmp(ctx.line+ctx.val,"chunked",7)==0)) { t->srv_state = SV_STANALYZE; + t->analysis |= AN_REQ_HTTP_BODY; } else { ctx.idx = 0; http_find_header2("Content-Length", 14, msg->sol, &txn->hdr_idx, &ctx); @@ -2353,8 +2364,10 @@ int process_cli(struct session *t) if ( t->be->url_param_post_limit < hint ) hint = t->be->url_param_post_limit; /* now do we really need to buffer more data? */ - if ( len < hint ) + if ( len < hint ) { t->srv_state = SV_STANALYZE; + t->analysis |= AN_REQ_HTTP_BODY; + } /* else... There are no body bytes to wait for */ } } @@ -2366,9 +2379,7 @@ int process_cli(struct session *t) * could. Let's switch to the DATA state. * ************************************************************/ - t->cli_state = CL_STDATA; req->flags |= BF_MAY_CONNECT | BF_MAY_FORWARD; - req->rlim = req->data + BUFSIZE; /* no more rewrite needed */ t->logs.tv_request = now; @@ -2416,9 +2427,9 @@ int process_cli(struct session *t) if (!(t->flags & SN_FINST_MASK)) t->flags |= SN_FINST_R; return 1; - } - else if (c == CL_STDATA) { + + if (c == CL_STDATA) { process_data: /* FIXME: this error handling is partly buggy because we always report * a 'DATA' phase while we don't know if the server was in IDLE, CONN @@ -5239,6 +5250,7 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend) msg.len = sprintf(trash, HTTP_401_fmt, uri_auth->auth_realm); txn->status = 401; client_retnclose(t, &msg); + t->analysis &= ~AN_REQ_ANY; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_PRXCOND; if (!(t->flags & SN_FINST_MASK)) diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 8c89241671..f3938eaf67 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -412,6 +412,7 @@ int uxst_event_accept(int fd) { } s->flags = 0; + s->analysis = 0; if ((t = pool_alloc2(pool2_task)) == NULL) { Alert("out of memory in uxst_event_accept().\n");