[MEDIUM] reorganized request handling to prepare for content-switching
The filters are now iterated for FE, FI, BE.
Some grey areas remain :
- uri_auth has been propagated to the backend, but in fact it
should be checked at every level (fe, fi, be), depending
where it is declared, and before the filters.
- the HTTP method and URI should be stored and propagated everywhere
they are used. For this, we would need to first apply filters to
be aware of filter changes which affect them.
- there seems to be no need anymore for hdr_idx[0] being empty.
It may contain the start line, which will slightly improve
performance and make the code easier to read.
diff --git a/src/proto_http.c b/src/proto_http.c
index a51cbdb..171f440 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -264,6 +264,7 @@
*/
char *sol, *eol; /* Start Of Line, End Of Line */
+ struct proxy *rule_set;
eol = sol = req->data + t->hreq.eoh;
@@ -649,17 +650,8 @@
* Now, let's catch bad requests.
*/
- if (t->hreq.hdr_state == HTTP_PA_ERROR) {
- t->logs.status = 400;
- client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
- }
-
-
+ if (t->hreq.hdr_state == HTTP_PA_ERROR)
+ goto return_bad_req;
/*
* Now we quickly check if we have found a full request.
@@ -680,13 +672,7 @@
/* FIXME: check if hreq.hdr_state & mask < HTTP_PA_HEADER,
* and return Status 414 Request URI too long instead.
*/
- t->logs.status = 400;
- client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
+ goto return_bad_req;
}
/* 2: have we encountered a read error or a close ? */
@@ -738,15 +724,38 @@
/*
- * 1: the appsession cookie was looked up very early in 1.2,
- * so let's do the same now.
+ * 1: check if the URI matches the monitor_uri.
+ * We have to do this for every request which gets in, because
+ * the monitor-uri is defined by the frontend. To speed-up the
+ * test, we include the leading and trailing spaces in the
+ * comparison. This is generally not a problem because the
+ * monitor-uri is primarily used by external checkers which
+ * send pre-formatted requests too.
*/
- /* It needs to look into the URI */
- if (t->be->appsession_name) {
- sol = req->data + t->hreq.sor; /* start of the URI */
- eol = sol + t->hreq.hdr_idx.v[t->hreq.hdr_idx.v[0].next].len; /* end of the URI */
- get_srv_from_appsession(t, sol, eol);
+ t->hreq.start.str = req->data + t->hreq.sor; /* start of the REQURI */
+ t->hreq.start.len = t->hreq.hdr_idx.v[t->hreq.hdr_idx.v[0].next].len; /* end of the REQURI */
+
+ if ((t->fe->monitor_uri_len != 0) &&
+ (t->hreq.start.len >= t->fe->monitor_uri_len)) {
+ char *p = t->hreq.start.str;
+ int idx = 0;
+
+ /* skip the method so that we accept any method */
+ while (idx < t->hreq.start.len && p[idx] != ' ')
+ idx++;
+ p += idx;
+
+ if (t->hreq.start.len - idx >= t->fe->monitor_uri_len &&
+ !memcmp(p, t->fe->monitor_uri, t->fe->monitor_uri_len)) {
+ /*
+ * We have found the monitor URI
+ */
+ t->flags |= SN_MONITOR;
+ t->logs.status = 200;
+ client_retnclose(t, strlen(HTTP_200), HTTP_200);
+ goto return_prx_cond;
+ }
}
@@ -755,25 +764,60 @@
* As opposed to version 1.2, now they will be evaluated in the
* filters order and not in the header order. This means that
* each filter has to be validated among all headers.
+ *
+ * We can now check whether we want to switch to another
+ * backend, in which case we will re-check the backend's
+ * filters and various options. In order to support 3-level
+ * switching, here's how we should proceed :
+ *
+ * a) run fe->filters.
+ * if (switch) then switch ->fi and ->be to the new backend.
+ * b) run fi->filters.
+ * If there's another switch, then switch ->be to the new be.
+ * c) run be->filters
+ * There cannot be any switch from there, so ->be cannot be
+ * changed anymore.
+ *
+ * The response path will be able to apply either ->be, or
+ * ->be then ->fi, or ->be then ->fi then ->fe filters in order
+ * to match the reverse of the forward sequence.
*/
+ do {
+ if (t->fi == t->fe)
+ rule_set = t->fe;
+ else if (t->be == t->fi)
+ rule_set = t->fi;
+ else
+ rule_set = t->be;
- /* try headers filters */
- if (t->fi->req_exp != NULL)
- apply_filters_to_session(t, req, t->fi->req_exp);
+ /* try headers filters */
+ if (rule_set->req_exp != NULL)
+ apply_filters_to_session(t, req, rule_set->req_exp);
- /* has the request been denied ? */
- if (t->flags & SN_CLDENY) {
- /* no need to go further */
- t->logs.status = 403;
- t->logs.t_request = tv_diff(&t->logs.tv_accept, &now); /* let's log the request time */
- client_retnclose(t, t->fe->errmsg.len403, t->fe->errmsg.msg403);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
- }
+ /* has the request been denied ? */
+ if (t->flags & SN_CLDENY) {
+ /* no need to go further */
+ t->logs.status = 403;
+ /* let's log the request time */
+ t->logs.t_request = tv_diff(&t->logs.tv_accept, &now);
+ client_retnclose(t, t->fe->errmsg.len403, t->fe->errmsg.msg403);
+ goto return_prx_cond;
+ }
+
+ /* add request headers from the rule sets in the same order */
+ for (cur_hdr = 0; cur_hdr < rule_set->nb_reqadd; cur_hdr++) {
+ int len;
+
+ len = sprintf(trash, "%s\r\n", rule_set->req_add[cur_hdr]);
+ len = buffer_replace2(req, req->data + t->hreq.eoh,
+ req->data + t->hreq.eoh, trash, len);
+ t->hreq.eoh += len;
+
+ if (hdr_idx_add(len - 2, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0)
+ goto return_bad_req;
+ }
+ } while (rule_set != t->be); /* we loop only if t->be has changed */
/* Right now, we know that we have processed the entire headers
@@ -787,22 +831,35 @@
* check the method (needed for cookies).
*/
- t->hreq.start.str = req->data + t->hreq.hdr_idx.v[0].len; /* start of the REQURI */
+ t->hreq.start.str = req->data + t->hreq.sor; /* start of the REQURI */
t->hreq.start.len = t->hreq.hdr_idx.v[t->hreq.hdr_idx.v[0].next].len; /* end of the REQURI */
- sol = t->hreq.start.str;
- eol = sol + t->hreq.start.len;
-
- if (!memcmp(sol, "POST ", 5)) {
+ if ((t->hreq.start.len >= 5) &&
+ (t->hreq.start.str[4] == ' ' || t->hreq.start.str[4] == '\t') &&
+ (!memcmp(t->hreq.start.str, "POST", 4))) {
/* this is a POST request, which is not cacheable by default */
t->flags |= SN_POST;
}
/*
+ * 4: the appsession cookie was looked up very early in 1.2,
+ * so let's do the same now.
+ */
+
+ /* It needs to look into the URI */
+ if (t->be->appsession_name) {
+ get_srv_from_appsession(t,
+ t->hreq.start.str,
+ t->hreq.start.str + t->hreq.start.len);
+ }
+
+
+ /*
- * 4: Now we can work with the cookies.
+ * 5: Now we can work with the cookies.
* Note that doing so might move headers in the request, but
* the fields will stay coherent and the URI will not move.
+ * This should only be performed in the backend.
*/
if (!(t->flags & (SN_CLDENY|SN_CLTARPIT)))
manage_client_side_cookies(t, req);
@@ -811,49 +868,16 @@
/*
* FIXME:
* we could run the whole list of headers right here to extract
- * commonly used headers, as well as re-detect their real end.
+ * commonly used headers.
*/
-
- /*
- * 5: check if the URI matches the monitor_uri. To speed-up the
- * test, we include the leading and trailing spaces in the
- * comparison.
- */
- if ((t->be->monitor_uri_len != 0) &&
- (t->hreq.start.len >= t->be->monitor_uri_len)) {
- char *p = t->hreq.start.str;
- int idx = 0;
-
- /* skip the method so that we accept any method */
- while (idx < t->hreq.start.len && p[idx] != ' ')
- idx++;
- p += idx;
-
- if (t->hreq.start.len - idx >= t->be->monitor_uri_len &&
- !memcmp(p, t->be->monitor_uri, t->be->monitor_uri_len)) {
- /*
- * We have found the monitor URI
- */
- t->flags |= SN_MONITOR;
- t->logs.status = 200;
- client_retnclose(t, strlen(HTTP_200), HTTP_200);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
- }
- }
-
-
/*
* 6: check if the user tries to access a protected URI.
*/
- if (t->fi->uri_auth != NULL
- && t->hreq.start.len >= t->fi->uri_auth->uri_len + 4) { /* +4 for "GET /" */
+ if (t->be->uri_auth != NULL
+ && t->hreq.start.len >= t->be->uri_auth->uri_len + 4) { /* +4 for "GET /" */
if (!memcmp(t->hreq.start.str + 4,
- t->fi->uri_auth->uri_prefix, t->fi->uri_auth->uri_len)
+ t->be->uri_auth->uri_prefix, t->be->uri_auth->uri_len)
&& !memcmp(t->hreq.start.str, "GET ", 4)) {
struct user_auth *user;
int authenticated;
@@ -862,7 +886,7 @@
/* we are in front of a interceptable URI. Let's check
* if there's an authentication and if it's valid.
*/
- user = t->fi->uri_auth->users;
+ user = t->be->uri_auth->users;
if (!user) {
/* no user auth required, it's OK */
authenticated = 1;
@@ -908,14 +932,10 @@
/* no need to go further */
- msglen = sprintf(trash, HTTP_401_fmt, t->fi->uri_auth->auth_realm);
+ msglen = sprintf(trash, HTTP_401_fmt, t->be->uri_auth->auth_realm);
t->logs.status = 401;
client_retnclose(t, msglen, trash);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
+ goto return_prx_cond;
}
t->cli_state = CL_STSHUTR;
@@ -928,45 +948,9 @@
}
}
-
-// /*
-// * We will now have some data to append after the end of the
-// * request. Right now we do not have the end of request pointer
-// * anymore, so we have to look for it.
-// */
-//
-//#if READABLE_PARSER_VERSION
-// for (sol = req->data + t->hreq.sor;
-// (cur_idx = t->hreq.hdr_idx.v[cur_idx].next);
-// sol += t->hreq.hdr_idx.v[cur_idx].len + t->hreq.hdr_idx.v[cur_idx]cr + 1);
-//#else
-// sol = req->data + t->hreq.sor;
-// cur_idx = 0;
-//
-// while ((cur_idx = t->hreq.hdr_idx.v[cur_idx].next))
-// sol += t->hreq.hdr_idx.v[cur_idx].len +
-// t->hreq.hdr_idx.v[cur_idx].cr + 1;
-//#endif
/*
- * 7: add request headers
- * FIXME: this should move to a separate function.
- */
- for (cur_hdr = 0; cur_hdr < t->fi->nb_reqadd; cur_hdr++) {
- int len = sprintf(trash, "%s\r\n", t->fi->req_add[cur_hdr]);
- len = buffer_replace2(req, req->data + t->hreq.eoh,
- req->data + t->hreq.eoh, trash, len);
- t->hreq.eoh += len;
-
- if (hdr_idx_add(len - 2, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0) {
- t->hreq.hdr_state = HTTP_PA_ERROR;
- break;
- }
- }
-
-
- /*
- * 8: add X-Forwarded-For
+ * 7: add X-Forwarded-For : Should depend on the backend only.
*/
if (t->be->options & PR_O_FWDFOR) {
if (t->cli_addr.ss_family == AF_INET) {
@@ -979,16 +963,8 @@
req->data + t->hreq.eoh, trash, len);
t->hreq.eoh += len;
- if (hdr_idx_add(len - 2, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0) {
- t->hreq.hdr_state = HTTP_PA_ERROR;
- t->logs.status = 400;
- client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
- }
+ if (hdr_idx_add(len - 2, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0)
+ goto return_bad_req;
}
else if (t->cli_addr.ss_family == AF_INET6) {
int len;
@@ -1001,22 +977,14 @@
req->data + t->hreq.eoh, trash, len);
t->hreq.eoh += len;
- if (hdr_idx_add(len - 2, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0) {
- t->hreq.hdr_state = HTTP_PA_ERROR;
- t->logs.status = 400;
- client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
- }
+ if (hdr_idx_add(len - 2, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0)
+ goto return_bad_req;
}
}
/*
- * 9: add "Connection:"
+ * 8: add "Connection:"
*/
/* add a "connection: close" line if needed */
@@ -1026,29 +994,14 @@
req->data + t->hreq.eoh, "Connection: close\r\n", 19);
t->hreq.eoh += len;
- if (hdr_idx_add(17, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0) {
- t->hreq.hdr_state = HTTP_PA_ERROR;
- t->logs.status = 400;
- client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
- if (!(t->flags & SN_ERR_MASK))
- t->flags |= SN_ERR_PRXCOND;
- if (!(t->flags & SN_FINST_MASK))
- t->flags |= SN_FINST_R;
- return 1;
- }
+ if (hdr_idx_add(17, 1, &t->hreq.hdr_idx, t->hreq.hdr_idx.tail) < 0)
+ goto return_bad_req;
}
- /*
- * 10: We can now check whether we want to switch to another
- * backend, in which case we will re-check the backend's filters
- * and various options.
- *
- */
-
-
+
/*************************************************************
* OK, that's finished for the headers. We have done what we *
@@ -1089,8 +1042,6 @@
tv_delayfrom(&req->cex, &now,
t->be->contimeout ? t->be->contimeout : 0);
}
-
- goto process_data;
#if DEBUG_HTTP_PARSER
/* example: dump each line */
@@ -1121,6 +1072,19 @@
}
#endif
+ goto process_data;
+
+ return_bad_req: /* let's centralize all bad requests */
+ t->hreq.hdr_state = HTTP_PA_ERROR;
+ t->logs.status = 400;
+ client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400);
+ return_prx_cond:
+ if (!(t->flags & SN_ERR_MASK))
+ t->flags |= SN_ERR_PRXCOND;
+ if (!(t->flags & SN_FINST_MASK))
+ t->flags |= SN_FINST_R;
+ return 1;
+
}
else if (c == CL_STDATA) {
process_data: