mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-16 15:56:15 -04:00
Performance tweak to evhttp_parse_request_line.
Method parsing has been tweaked out to use a lookup table instead of multiple branching. In our testing it has increased performance by a fair bit.
This commit is contained in:
parent
18f24ef4d5
commit
aee1a97da4
135
http.c
135
http.c
@ -1470,6 +1470,8 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
|
|||||||
char *version;
|
char *version;
|
||||||
const char *hostname;
|
const char *hostname;
|
||||||
const char *scheme;
|
const char *scheme;
|
||||||
|
size_t method_len;
|
||||||
|
enum evhttp_cmd_type type;
|
||||||
|
|
||||||
/* Parse the request line */
|
/* Parse the request line */
|
||||||
method = strsep(&line, " ");
|
method = strsep(&line, " ");
|
||||||
@ -1482,30 +1484,121 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
|
|||||||
if (line != NULL)
|
if (line != NULL)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
|
method_len = (uri - method) - 1;
|
||||||
|
type = _EVHTTP_REQ_UNKNOWN;
|
||||||
|
|
||||||
/* First line */
|
/* First line */
|
||||||
if (strcmp(method, "GET") == 0) {
|
switch (method_len) {
|
||||||
req->type = EVHTTP_REQ_GET;
|
case 3:
|
||||||
} else if (strcmp(method, "POST") == 0) {
|
/* The length of the method string is 3, meaning it can only be one of two methods: GET or PUT */
|
||||||
req->type = EVHTTP_REQ_POST;
|
|
||||||
} else if (strcmp(method, "HEAD") == 0) {
|
/* Since both GET and PUT share the same character 'T' at the end,
|
||||||
req->type = EVHTTP_REQ_HEAD;
|
* if the string doesn't have 'T', we can immediately determine this
|
||||||
} else if (strcmp(method, "PUT") == 0) {
|
* is an invalid HTTP method */
|
||||||
req->type = EVHTTP_REQ_PUT;
|
|
||||||
} else if (strcmp(method, "DELETE") == 0) {
|
if (method[2] != 'T') {
|
||||||
req->type = EVHTTP_REQ_DELETE;
|
break;
|
||||||
} else if (strcmp(method, "OPTIONS") == 0) {
|
}
|
||||||
req->type = EVHTTP_REQ_OPTIONS;
|
|
||||||
} else if (strcmp(method, "TRACE") == 0) {
|
switch (*method) {
|
||||||
req->type = EVHTTP_REQ_TRACE;
|
case 'G':
|
||||||
} else if (strcmp(method, "PATCH") == 0) {
|
/* This first byte is 'G', so make sure the next byte is
|
||||||
req->type = EVHTTP_REQ_PATCH;
|
* 'E', if it isn't then this isn't a valid method */
|
||||||
} else {
|
|
||||||
req->type = _EVHTTP_REQ_UNKNOWN;
|
if (method[1] == 'E') {
|
||||||
event_debug(("%s: bad method %s on request %p from %s",
|
type = EVHTTP_REQ_GET;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
/* First byte is P, check second byte for 'U', if not,
|
||||||
|
* we know it's an invalid method */
|
||||||
|
if (method[1] == 'U') {
|
||||||
|
type = EVHTTP_REQ_PUT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
/* The method length is 4 bytes, leaving only the methods "POST" and "HEAD" */
|
||||||
|
switch (*method) {
|
||||||
|
case 'P':
|
||||||
|
if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
|
||||||
|
type = EVHTTP_REQ_POST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
if (method[3] == 'D' && method[2] == 'A' && method[1] == 'E') {
|
||||||
|
type = EVHTTP_REQ_HEAD;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
/* Method length is 5 bytes, which can only encompass PATCH and TRACE */
|
||||||
|
switch (*method) {
|
||||||
|
case 'P':
|
||||||
|
if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
|
||||||
|
type = EVHTTP_REQ_PATCH;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
if (method[4] == 'E' && method[3] == 'C' && method[2] == 'A' && method[1] == 'R') {
|
||||||
|
type = EVHTTP_REQ_TRACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
/* Method length is 6, only valid method 6 bytes in length is DELEte */
|
||||||
|
|
||||||
|
/* If the first byte isn't 'D' then it's invalid */
|
||||||
|
if (*method != 'D') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' && method[2] == 'L' && method[1] == 'E') {
|
||||||
|
type = EVHTTP_REQ_DELETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
/* Method length is 7, only valid methods are "OPTIONS" and "CONNECT" */
|
||||||
|
switch (*method) {
|
||||||
|
case 'O':
|
||||||
|
if (method[6] == 'S' && method[5] == 'N' && method[4] == 'O' &&
|
||||||
|
method[3] == 'I' && method[2] == 'T' && method[1] == 'P') {
|
||||||
|
type = EVHTTP_REQ_OPTIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
if (method[6] == 'T' && method[5] == 'C' && method[4] == 'E' &&
|
||||||
|
method[3] == 'N' && method[2] == 'N' && method[1] == 'O') {
|
||||||
|
type = EVHTTP_REQ_CONNECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} /* switch */
|
||||||
|
|
||||||
|
if (type == _EVHTTP_REQ_UNKNOWN) {
|
||||||
|
event_debug(("%s: bad method %s on request %p from %s",
|
||||||
__func__, method, req, req->remote_host));
|
__func__, method, req, req->remote_host));
|
||||||
/* No error yet; we'll give a better error later when
|
/* No error yet; we'll give a better error later when
|
||||||
* we see that req->type is unsupported. */
|
* we see that req->type is unsupported. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req->type = type;
|
||||||
|
|
||||||
if (evhttp_parse_http_version(version, req) < 0)
|
if (evhttp_parse_http_version(version, req) < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user