mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 13:52:28 +00:00
Make minor performance improvement to HTTP parser
redbean's message parser now takes 300 nanoseconds to parse a standard request sent by chrome, whereas it previous took 600 nanoseconds.
This commit is contained in:
parent
3c19b6e352
commit
52565e7af3
6 changed files with 140 additions and 43 deletions
|
@ -30,8 +30,11 @@ noinline size_t uint64toarray_radix10(uint64_t i, char a[hasatleast 21]) {
|
||||||
size_t j;
|
size_t j;
|
||||||
j = 0;
|
j = 0;
|
||||||
do {
|
do {
|
||||||
a[j++] = i % 10 + '0';
|
struct {
|
||||||
i /= 10;
|
uint64_t q, r;
|
||||||
|
} x = {i / 10, i % 10};
|
||||||
|
a[j++] = x.r + '0';
|
||||||
|
i = x.q;
|
||||||
} while (i > 0);
|
} while (i > 0);
|
||||||
a[j] = '\0';
|
a[j] = '\0';
|
||||||
reverse(a, j);
|
reverse(a, j);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* ANSI-C code produced by gperf version 3.1 */
|
/* ANSI-C code produced by gperf version 3.1 */
|
||||||
/* Command-line: gperf gethttpheader.gperf */
|
/* Command-line: gperf gethttpheader.gperf */
|
||||||
/* Computed positions: -k'3-4,10' */
|
/* Computed positions: -k'3-4,10' */
|
||||||
|
/* clang-format off */
|
||||||
|
|
||||||
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
|
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
|
||||||
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
|
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
|
||||||
|
@ -152,7 +153,7 @@ hash (register const char *str, register size_t len)
|
||||||
return hval;
|
return hval;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct HttpHeaderSlot *
|
static const struct HttpHeaderSlot *
|
||||||
LookupHttpHeader (register const char *str, register size_t len)
|
LookupHttpHeader (register const char *str, register size_t len)
|
||||||
{
|
{
|
||||||
static const struct HttpHeaderSlot wordlist[] =
|
static const struct HttpHeaderSlot wordlist[] =
|
||||||
|
|
|
@ -42,7 +42,11 @@ void InitHttpRequest(struct HttpRequest *r) {
|
||||||
* Destroys HTTP request parser.
|
* Destroys HTTP request parser.
|
||||||
*/
|
*/
|
||||||
void DestroyHttpRequest(struct HttpRequest *r) {
|
void DestroyHttpRequest(struct HttpRequest *r) {
|
||||||
|
if (r->xheaders.p) {
|
||||||
free(r->xheaders.p);
|
free(r->xheaders.p);
|
||||||
|
r->xheaders.p = NULL;
|
||||||
|
r->xheaders.n = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,6 +61,8 @@ void DestroyHttpRequest(struct HttpRequest *r) {
|
||||||
* All other things are permissive to the greatest extent possible.
|
* All other things are permissive to the greatest extent possible.
|
||||||
* Further functions are provided for the interpretation, validation,
|
* Further functions are provided for the interpretation, validation,
|
||||||
* and sanitization of various fields.
|
* and sanitization of various fields.
|
||||||
|
*
|
||||||
|
* @note we assume p points to a buffer that has >=SHRT_MAX bytes
|
||||||
*/
|
*/
|
||||||
int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
||||||
int c, h, i;
|
int c, h, i;
|
||||||
|
@ -72,6 +78,7 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
||||||
r->t = METHOD;
|
r->t = METHOD;
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case METHOD:
|
case METHOD:
|
||||||
|
for (;;) {
|
||||||
if (c == ' ') {
|
if (c == ' ') {
|
||||||
if ((r->method = GetHttpMethod(p + r->a, r->i - r->a)) != -1) {
|
if ((r->method = GetHttpMethod(p + r->a, r->i - r->a)) != -1) {
|
||||||
r->uri.a = r->i + 1;
|
r->uri.a = r->i + 1;
|
||||||
|
@ -79,9 +86,14 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
||||||
} else {
|
} else {
|
||||||
return ebadmsg();
|
return ebadmsg();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (++r->i == n) break;
|
||||||
|
c = p[r->i] & 0xff;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case URI:
|
case URI:
|
||||||
|
for (;;) {
|
||||||
if (c == ' ' || c == '\r' || c == '\n') {
|
if (c == ' ' || c == '\r' || c == '\n') {
|
||||||
if (r->i == r->uri.a) return ebadmsg();
|
if (r->i == r->uri.a) return ebadmsg();
|
||||||
r->uri.b = r->i;
|
r->uri.b = r->i;
|
||||||
|
@ -93,6 +105,10 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
||||||
} else {
|
} else {
|
||||||
r->t = LF1;
|
r->t = LF1;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (++r->i == n) break;
|
||||||
|
c = p[r->i] & 0xff;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case VERSION:
|
case VERSION:
|
||||||
|
@ -120,9 +136,14 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
||||||
r->t = HKEY;
|
r->t = HKEY;
|
||||||
break;
|
break;
|
||||||
case HKEY:
|
case HKEY:
|
||||||
|
for (;;) {
|
||||||
if (c == ':') {
|
if (c == ':') {
|
||||||
r->k.b = r->i;
|
r->k.b = r->i;
|
||||||
r->t = HSEP;
|
r->t = HSEP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (++r->i == n) break;
|
||||||
|
c = p[r->i] & 0xff;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HSEP:
|
case HSEP:
|
||||||
|
@ -131,14 +152,16 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
||||||
r->t = HVAL;
|
r->t = HVAL;
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case HVAL:
|
case HVAL:
|
||||||
|
for (;;) {
|
||||||
if (c == '\r' || c == '\n') {
|
if (c == '\r' || c == '\n') {
|
||||||
i = r->i;
|
i = r->i;
|
||||||
while (i > r->a && (p[i - 1] == ' ' || p[i - 1] == '\t')) --i;
|
while (i > r->a && (p[i - 1] == ' ' || p[i - 1] == '\t')) --i;
|
||||||
if ((h = GetHttpHeader(p + r->k.a, r->k.b - r->k.a)) != -1) {
|
if ((h = GetHttpHeader(p + r->k.a, r->k.b - r->k.a)) != -1) {
|
||||||
r->headers[h].a = r->a;
|
r->headers[h].a = r->a;
|
||||||
r->headers[h].b = i;
|
r->headers[h].b = i;
|
||||||
} else if ((x = realloc(r->xheaders.p, (r->xheaders.n + 1) *
|
} else if ((x = realloc(
|
||||||
sizeof(*r->xheaders.p)))) {
|
r->xheaders.p,
|
||||||
|
(r->xheaders.n + 1) * sizeof(*r->xheaders.p)))) {
|
||||||
x[r->xheaders.n].k = r->k;
|
x[r->xheaders.n].k = r->k;
|
||||||
x[r->xheaders.n].v.a = r->a;
|
x[r->xheaders.n].v.a = r->a;
|
||||||
x[r->xheaders.n].v.b = i;
|
x[r->xheaders.n].v.b = i;
|
||||||
|
@ -146,6 +169,10 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
|
||||||
++r->xheaders.n;
|
++r->xheaders.n;
|
||||||
}
|
}
|
||||||
r->t = c == '\r' ? CR1 : LF1;
|
r->t = c == '\r' ? CR1 : LF1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (++r->i == n) break;
|
||||||
|
c = p[r->i] & 0xff;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LF2:
|
case LF2:
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
#include "libc/fmt/itoa.h"
|
#include "libc/fmt/itoa.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
TEST(int64toarray_radix10, test) {
|
TEST(int64toarray_radix10, test) {
|
||||||
|
@ -64,3 +65,8 @@ TEST(uint128toarray_radix10, test) {
|
||||||
EXPECT_EQ(39, uint128toarray_radix10(INT128_MIN, buf));
|
EXPECT_EQ(39, uint128toarray_radix10(INT128_MIN, buf));
|
||||||
EXPECT_STREQ("170141183460469231731687303715884105728", buf);
|
EXPECT_STREQ("170141183460469231731687303715884105728", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BENCH(itoa64radix10, bench) {
|
||||||
|
char b[21];
|
||||||
|
EZBENCH2("itoa64radix10", donothing, uint64toarray_radix10(UINT64_MAX, b));
|
||||||
|
}
|
||||||
|
|
|
@ -18,10 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/bits.h"
|
#include "libc/bits/bits.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/log/check.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/gc.internal.h"
|
#include "libc/runtime/gc.internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
#include "net/http/http.h"
|
#include "net/http/http.h"
|
||||||
|
@ -199,3 +201,63 @@ User-Agent: \t hi there \t \r\n\
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("hi there", gc(slice(m, req->headers[kHttpUserAgent])));
|
EXPECT_STREQ("hi there", gc(slice(m, req->headers[kHttpUserAgent])));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DoTiniestHttpRequest(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
GET /\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpRequest(req);
|
||||||
|
ParseHttpRequest(req, m, sizeof(m));
|
||||||
|
DestroyHttpRequest(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoTinyHttpRequest(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
GET /\r\n\
|
||||||
|
Accept-Encoding: gzip\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpRequest(req);
|
||||||
|
ParseHttpRequest(req, m, sizeof(m));
|
||||||
|
DestroyHttpRequest(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoStandardChromeRequest(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
GET /tool/net/redbean.png HTTP/1.1\r\n\
|
||||||
|
Host: 10.10.10.124:8080\r\n\
|
||||||
|
Connection: keep-alive\r\n\
|
||||||
|
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36\r\n\
|
||||||
|
DNT: \t1 \r\n\
|
||||||
|
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\r\n\
|
||||||
|
Referer: http://10.10.10.124:8080/\r\n\
|
||||||
|
Accept-Encoding: gzip, deflate\r\n\
|
||||||
|
Accept-Language: en-US,en;q=0.9\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpRequest(req);
|
||||||
|
CHECK_EQ(sizeof(m) - 1, ParseHttpRequest(req, m, sizeof(m)));
|
||||||
|
DestroyHttpRequest(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoUnstandardChromeRequest(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
GET /tool/net/redbean.png HTTP/1.1\r\n\
|
||||||
|
X-Host: 10.10.10.124:8080\r\n\
|
||||||
|
X-Connection: keep-alive\r\n\
|
||||||
|
X-User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36\r\n\
|
||||||
|
X-DNT: \t1 \r\n\
|
||||||
|
X-Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\r\n\
|
||||||
|
X-Referer: http://10.10.10.124:8080/\r\n\
|
||||||
|
X-Accept-Encoding: gzip, deflate\r\n\
|
||||||
|
X-Accept-Language: en-US,en;q=0.9\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpRequest(req);
|
||||||
|
CHECK_EQ(sizeof(m) - 1, ParseHttpRequest(req, m, sizeof(m)));
|
||||||
|
DestroyHttpRequest(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCH(ParseHttpRequest, bench) {
|
||||||
|
EZBENCH2("DoTiniestHttpRequest", donothing, DoTiniestHttpRequest());
|
||||||
|
EZBENCH2("DoTinyHttpRequest", donothing, DoTinyHttpRequest());
|
||||||
|
EZBENCH2("DoStandardChromeRequest", donothing, DoStandardChromeRequest());
|
||||||
|
EZBENCH2("DoUnstandardChromeRequest", donothing, DoUnstandardChromeRequest());
|
||||||
|
}
|
||||||
|
|
|
@ -885,24 +885,22 @@ static struct Asset *LocateAssetZip(const char *path, size_t pathlen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct Asset *LocateAssetFile(const char *path, size_t pathlen) {
|
static struct Asset *LocateAssetFile(const char *path, size_t pathlen) {
|
||||||
|
char *p;
|
||||||
size_t i;
|
size_t i;
|
||||||
char *path2;
|
|
||||||
struct Asset *a;
|
struct Asset *a;
|
||||||
if (stagedirs.n) {
|
if (stagedirs.n) {
|
||||||
a = FreeLater(xcalloc(1, sizeof(struct Asset)));
|
a = FreeLater(xcalloc(1, sizeof(struct Asset)));
|
||||||
a->file = FreeLater(xmalloc(sizeof(struct File)));
|
a->file = FreeLater(xmalloc(sizeof(struct File)));
|
||||||
for (i = 0; i < stagedirs.n; ++i) {
|
for (i = 0; i < stagedirs.n; ++i) {
|
||||||
if (stat((a->file->path = FreeLater(xasprintf(
|
if (stat((a->file->path = p = FreeLater(xasprintf(
|
||||||
"%s%.*s", stagedirs.p[i], request.path.n, request.path.p))),
|
"%s%.*s", stagedirs.p[i], request.path.n, request.path.p))),
|
||||||
&a->file->st) != -1 &&
|
&a->file->st) != -1 &&
|
||||||
(S_ISREG(a->file->st.st_mode) ||
|
(S_ISREG(a->file->st.st_mode) ||
|
||||||
(S_ISDIR(a->file->st.st_mode) &&
|
(S_ISDIR(a->file->st.st_mode) &&
|
||||||
((stat((a->file->path = FreeLater(
|
((stat((a->file->path = FreeLater(xjoinpaths(p, "index.lua"))),
|
||||||
xasprintf("%s%s", a->file->path, "index.lua"))),
|
|
||||||
&a->file->st) != -1 &&
|
&a->file->st) != -1 &&
|
||||||
S_ISREG(a->file->st.st_mode)) ||
|
S_ISREG(a->file->st.st_mode)) ||
|
||||||
(stat((a->file->path = FreeLater(
|
(stat((a->file->path = FreeLater(xjoinpaths(p, "index.html"))),
|
||||||
xasprintf("%s%s", a->file->path, "index.html"))),
|
|
||||||
&a->file->st) != -1 &&
|
&a->file->st) != -1 &&
|
||||||
S_ISREG(a->file->st.st_mode)))))) {
|
S_ISREG(a->file->st.st_mode)))))) {
|
||||||
a->lastmodifiedstr = FormatUnixHttpDateTime(
|
a->lastmodifiedstr = FormatUnixHttpDateTime(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue