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; }