diff --git a/libc/fmt/itoa64radix10.greg.c b/libc/fmt/itoa64radix10.greg.c index 182bc5dcf..380411487 100644 --- a/libc/fmt/itoa64radix10.greg.c +++ b/libc/fmt/itoa64radix10.greg.c @@ -30,8 +30,11 @@ noinline size_t uint64toarray_radix10(uint64_t i, char a[hasatleast 21]) { size_t j; j = 0; do { - a[j++] = i % 10 + '0'; - i /= 10; + struct { + uint64_t q, r; + } x = {i / 10, i % 10}; + a[j++] = x.r + '0'; + i = x.q; } while (i > 0); a[j] = '\0'; reverse(a, j); diff --git a/net/http/gethttpheader.inc b/net/http/gethttpheader.inc index dad490f20..0c4affada 100644 --- a/net/http/gethttpheader.inc +++ b/net/http/gethttpheader.inc @@ -1,6 +1,7 @@ /* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf gethttpheader.gperf */ /* Computed positions: -k'3-4,10' */ +/* clang-format off */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ @@ -152,7 +153,7 @@ hash (register const char *str, register size_t len) return hval; } -const struct HttpHeaderSlot * +static const struct HttpHeaderSlot * LookupHttpHeader (register const char *str, register size_t len) { static const struct HttpHeaderSlot wordlist[] = diff --git a/net/http/parsehttprequest.c b/net/http/parsehttprequest.c index 9bb842c9e..d0a8d9587 100644 --- a/net/http/parsehttprequest.c +++ b/net/http/parsehttprequest.c @@ -42,7 +42,11 @@ void InitHttpRequest(struct HttpRequest *r) { * Destroys HTTP request parser. */ void DestroyHttpRequest(struct HttpRequest *r) { - free(r->xheaders.p); + if (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. * Further functions are provided for the interpretation, validation, * 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 c, h, i; @@ -72,27 +78,37 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) { r->t = METHOD; /* fallthrough */ case METHOD: - if (c == ' ') { - if ((r->method = GetHttpMethod(p + r->a, r->i - r->a)) != -1) { - r->uri.a = r->i + 1; - r->t = URI; - } else { - return ebadmsg(); + for (;;) { + if (c == ' ') { + if ((r->method = GetHttpMethod(p + r->a, r->i - r->a)) != -1) { + r->uri.a = r->i + 1; + r->t = URI; + } else { + return ebadmsg(); + } + break; } + if (++r->i == n) break; + c = p[r->i] & 0xff; } break; case URI: - if (c == ' ' || c == '\r' || c == '\n') { - if (r->i == r->uri.a) return ebadmsg(); - r->uri.b = r->i; - if (c == ' ') { - r->version.a = r->i + 1; - r->t = VERSION; - } else if (c == '\r') { - r->t = CR1; - } else { - r->t = LF1; + for (;;) { + if (c == ' ' || c == '\r' || c == '\n') { + if (r->i == r->uri.a) return ebadmsg(); + r->uri.b = r->i; + if (c == ' ') { + r->version.a = r->i + 1; + r->t = VERSION; + } else if (c == '\r') { + r->t = CR1; + } else { + r->t = LF1; + } + break; } + if (++r->i == n) break; + c = p[r->i] & 0xff; } break; case VERSION: @@ -120,9 +136,14 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) { r->t = HKEY; break; case HKEY: - if (c == ':') { - r->k.b = r->i; - r->t = HSEP; + for (;;) { + if (c == ':') { + r->k.b = r->i; + r->t = HSEP; + break; + } + if (++r->i == n) break; + c = p[r->i] & 0xff; } break; case HSEP: @@ -131,21 +152,27 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) { r->t = HVAL; /* fallthrough */ case HVAL: - if (c == '\r' || c == '\n') { - i = r->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) { - r->headers[h].a = r->a; - r->headers[h].b = i; - } else if ((x = realloc(r->xheaders.p, (r->xheaders.n + 1) * - sizeof(*r->xheaders.p)))) { - x[r->xheaders.n].k = r->k; - x[r->xheaders.n].v.a = r->a; - x[r->xheaders.n].v.b = i; - r->xheaders.p = x; - ++r->xheaders.n; + for (;;) { + if (c == '\r' || c == '\n') { + i = r->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) { + r->headers[h].a = r->a; + r->headers[h].b = i; + } else if ((x = realloc( + r->xheaders.p, + (r->xheaders.n + 1) * sizeof(*r->xheaders.p)))) { + x[r->xheaders.n].k = r->k; + x[r->xheaders.n].v.a = r->a; + x[r->xheaders.n].v.b = i; + r->xheaders.p = x; + ++r->xheaders.n; + } + r->t = c == '\r' ? CR1 : LF1; + break; } - r->t = c == '\r' ? CR1 : LF1; + if (++r->i == n) break; + c = p[r->i] & 0xff; } break; case LF2: diff --git a/test/libc/fmt/itoa64radix10_test.c b/test/libc/fmt/itoa64radix10_test.c index 870a64e9a..970e87ead 100644 --- a/test/libc/fmt/itoa64radix10_test.c +++ b/test/libc/fmt/itoa64radix10_test.c @@ -19,6 +19,7 @@ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/limits.h" +#include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" TEST(int64toarray_radix10, test) { @@ -64,3 +65,8 @@ TEST(uint128toarray_radix10, test) { EXPECT_EQ(39, uint128toarray_radix10(INT128_MIN, buf)); EXPECT_STREQ("170141183460469231731687303715884105728", buf); } + +BENCH(itoa64radix10, bench) { + char b[21]; + EZBENCH2("itoa64radix10", donothing, uint64toarray_radix10(UINT64_MAX, b)); +} diff --git a/test/net/http/parsehttprequest_test.c b/test/net/http/parsehttprequest_test.c index 292d97d12..5047fa829 100644 --- a/test/net/http/parsehttprequest_test.c +++ b/test/net/http/parsehttprequest_test.c @@ -18,10 +18,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/errno.h" +#include "libc/log/check.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/x/x.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_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()); +} diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 3cf0d8cb4..dfdf40aeb 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -885,24 +885,22 @@ static struct Asset *LocateAssetZip(const char *path, size_t pathlen) { } static struct Asset *LocateAssetFile(const char *path, size_t pathlen) { + char *p; size_t i; - char *path2; struct Asset *a; if (stagedirs.n) { a = FreeLater(xcalloc(1, sizeof(struct Asset))); a->file = FreeLater(xmalloc(sizeof(struct File))); 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))), &a->file->st) != -1 && (S_ISREG(a->file->st.st_mode) || (S_ISDIR(a->file->st.st_mode) && - ((stat((a->file->path = FreeLater( - xasprintf("%s%s", a->file->path, "index.lua"))), + ((stat((a->file->path = FreeLater(xjoinpaths(p, "index.lua"))), &a->file->st) != -1 && S_ISREG(a->file->st.st_mode)) || - (stat((a->file->path = FreeLater( - xasprintf("%s%s", a->file->path, "index.html"))), + (stat((a->file->path = FreeLater(xjoinpaths(p, "index.html"))), &a->file->st) != -1 && S_ISREG(a->file->st.st_mode)))))) { a->lastmodifiedstr = FormatUnixHttpDateTime(