From 53b6c74d06593cf39279fc9cbea4eee7f7908e69 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 17 Dec 2006 13:37:46 +0100 Subject: [PATCH] [MEDIUM] check the HTTP method after all filters have been applied The HTTP method is now checked and saved into hreq.meth. It will be usable at several places instead of those dirty string comparisons. --- include/types/session.h | 13 +++++++ src/proto_http.c | 84 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/include/types/session.h b/include/types/session.h index 4d837162b8..a1530193a6 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -99,6 +99,18 @@ #define SN_SELF_GEN 0x02000000 /* the proxy generates data for the client (eg: stats) */ #define SN_CLTARPIT 0x04000000 /* the session is tarpitted (anti-dos) */ +typedef enum { + HTTP_METH_NONE = 0, + HTTP_METH_OPTIONS, + HTTP_METH_GET, + HTTP_METH_HEAD, + HTTP_METH_POST, + HTTP_METH_PUT, + HTTP_METH_DELETE, + HTTP_METH_TRACE, + HTTP_METH_CONNECT, + HTTP_METH_OTHER, +} http_meth_t; /* WARNING: if new fields are added, they must be initialized in event_accept() */ struct session { @@ -122,6 +134,7 @@ struct session { char **rsp_cap; /* array of captured response headers (may be NULL) */ struct { int hdr_state; /* where we are in the current header parsing */ + http_meth_t meth; /* HTTP method */ int sor, eoh; /* Start Of Request and End Of Headers, relative to buffer */ struct hdr_idx hdr_idx; /* array of header indexes (max: MAX_HTTP_HDR) */ struct chunk start; /* points to first line, called "start line" in RFC2616 */ diff --git a/src/proto_http.c b/src/proto_http.c index 4dae1a8e41..e6e4ca7a0d 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -86,6 +86,41 @@ const char *HTTP_401_fmt = "

401 Unauthorized

\nYou need a valid user and password to access this content.\n\n"; +/* + * We have 26 list of methods (1 per first letter), each of which can have + * up to 3 entries (2 valid, 1 null). + */ +struct http_method_desc { + http_meth_t meth; + int len; + const char text[8]; +}; + +static struct http_method_desc http_methods[26][3] = { + ['C' - 'A'] = { + [0] = { .meth = HTTP_METH_CONNECT , .len=7, .text="CONNECT" }, + }, + ['D' - 'A'] = { + [0] = { .meth = HTTP_METH_DELETE , .len=6, .text="DELETE" }, + }, + ['G' - 'A'] = { + [0] = { .meth = HTTP_METH_GET , .len=3, .text="GET" }, + }, + ['H' - 'A'] = { + [0] = { .meth = HTTP_METH_HEAD , .len=4, .text="HEAD" }, + }, + ['P' - 'A'] = { + [0] = { .meth = HTTP_METH_POST , .len=4, .text="POST" }, + [1] = { .meth = HTTP_METH_PUT , .len=3, .text="PUT" }, + }, + ['T' - 'A'] = { + [0] = { .meth = HTTP_METH_TRACE , .len=5, .text="TRACE" }, + }, + /* rest is empty like this : + * [1] = { .meth = HTTP_METH_NONE , .len=0, .text="" }, + */ +}; + #ifdef DEBUG_FULL static char *cli_stnames[5] = {"HDR", "DAT", "SHR", "SHW", "CLS" }; static char *srv_stnames[7] = {"IDL", "CON", "HDR", "DAT", "SHR", "SHW", "CLS" }; @@ -148,6 +183,37 @@ void srv_close_with_err(struct session *t, int err, int finst, } +/* + * returns HTTP_METH_NONE if there is nothing valid to read (empty or non-text + * string), HTTP_METH_OTHER for unknown methods, or the identified method. + */ +static http_meth_t find_http_meth(const char *str, const int len) +{ + unsigned char m; + struct http_method_desc *h; + + m = ((unsigned)*str - 'A'); + + if (m < 26) { + int l; + for (h = http_methods[m]; (l = (h->len)) > 0; h++) { + if (len <= l) + continue; + + if (str[l] != ' ' && str[l] != '\t') + continue; + + if (memcmp(str, h->text, l) == 0) { + return h->meth; + } + }; + return HTTP_METH_OTHER; + } + return HTTP_METH_NONE; + +} + + /* Processes the client and server jobs of a session task, then * puts it back to the wait queue in a clean state, or * cleans up its resources if it must be deleted. Returns @@ -839,13 +905,25 @@ int process_cli(struct session *t) * to match the reverse of the forward sequence. */ + 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 */ + + t->hreq.meth = find_http_meth(t->hreq.start.str, t->hreq.start.len); + do { rule_set = t->be; /* try headers filters */ - if (rule_set->req_exp != NULL) + if (rule_set->req_exp != NULL) { apply_filters_to_session(t, req, rule_set->req_exp); + /* the start line might have been modified */ + t->hreq.start.len = t->hreq.hdr_idx.v[t->hreq.hdr_idx.v[0].next].len; + t->hreq.meth = find_http_meth(t->hreq.start.str, t->hreq.start.len); + + t->hreq.meth = find_http_meth(t->hreq.start.str, t->hreq.start.len); + } + /* has the request been denied ? */ if (t->flags & SN_CLDENY) { /* no need to go further */ @@ -887,9 +965,7 @@ int process_cli(struct session *t) 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->hreq.start.len >= 5) && - (t->hreq.start.str[4] == ' ' || t->hreq.start.str[4] == '\t') && - (!memcmp(t->hreq.start.str, "POST", 4))) { + if (t->hreq.meth == HTTP_METH_POST) { /* this is a POST request, which is not cacheable by default */ t->flags |= SN_POST; }