From 4d21cd315d09027ff01520f11526966a7ed805ff Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 27 Mar 2021 07:29:55 -0700 Subject: [PATCH] Support POST parameters in redbean server pages See #97 --- libc/str/memcasecmp.c | 37 ++ libc/str/str.h | 1 + net/http/escapeurl.c | 22 +- net/http/escapeurlfragment.c | 32 +- net/http/escapeurlparam.c | 35 +- net/http/escapeurlpath.c | 32 +- net/http/escapeurlpathsegment.c | 32 +- net/http/gethttpheader.gperf | 112 ++-- net/http/gethttpheader.inc | 211 +++---- net/http/gethttpheadername.c | 142 +++++ net/http/http.h | 126 +++-- net/http/http.mk | 1 + net/http/parsehttprequest.c | 28 +- test/net/http/escapeurlparam_test.c | 3 +- test/net/http/parsehttprequest_test.c | 53 +- tool/net/net.mk | 3 + tool/net/redbean-form.lua | 70 +++ tool/net/redbean.c | 764 ++++++++++++++------------ tool/net/redbean.html | 4 +- tool/net/redbean.lua | 31 +- 20 files changed, 1075 insertions(+), 664 deletions(-) create mode 100644 libc/str/memcasecmp.c create mode 100644 net/http/gethttpheadername.c create mode 100644 tool/net/redbean-form.lua diff --git a/libc/str/memcasecmp.c b/libc/str/memcasecmp.c new file mode 100644 index 00000000..a7ba7ac6 --- /dev/null +++ b/libc/str/memcasecmp.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +/** + * Compares memory case-insensitively. + * @return is <0, 0, or >0 based on uint8_t comparison + */ +int memcasecmp(const void *p, const void *q, size_t n) { + int c; + size_t i; + const unsigned char *a, *b; + if ((a = p) != (b = q)) { + for (i = 0; i < n; ++i) { + if ((c = kToLower[a[i]] - kToLower[b[i]])) { + return c; + } + } + } + return 0; +} diff --git a/libc/str/str.h b/libc/str/str.h index 21f584d5..090f469b 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -123,6 +123,7 @@ int wcscmp(const wchar_t *, const wchar_t *) strlenesque; int wcsncmp(const wchar_t *, const wchar_t *, size_t) strlenesque; int wmemcmp(const wchar_t *, const wchar_t *, size_t) strlenesque; int strcasecmp(const char *, const char *) strlenesque; +int memcasecmp(const void *, const void *, size_t) strlenesque; int strcasecmp16(const char16_t *, const char16_t *) strlenesque; int wcscasecmp(const wchar_t *, const wchar_t *) strlenesque; int strncasecmp(const char *, const char *, size_t) strlenesque; diff --git a/net/http/escapeurl.c b/net/http/escapeurl.c index 2d2b5efb..09dfddb1 100644 --- a/net/http/escapeurl.c +++ b/net/http/escapeurl.c @@ -37,21 +37,13 @@ struct EscapeResult EscapeUrl(const char *data, size_t size, struct EscapeResult r; p = r.data = xmalloc(size * 6 + 1); for (i = 0; i < size; ++i) { - switch (xlat[(c = data[i] & 0xff)]) { - case 0: - *p++ = c; - break; - case 1: - *p++ = '+'; - break; - case 2: - p[0] = '%'; - p[1] = "0123456789ABCDEF"[(c & 0xF0) >> 4]; - p[2] = "0123456789ABCDEF"[(c & 0x0F) >> 0]; - p += 3; - break; - default: - unreachable; + if (!xlat[(c = data[i] & 0xff)]) { + *p++ = c; + } else { + p[0] = '%'; + p[1] = "0123456789ABCDEF"[(c & 0xF0) >> 4]; + p[2] = "0123456789ABCDEF"[(c & 0x0F) >> 0]; + p += 3; } } r.size = p - r.data; diff --git a/net/http/escapeurlfragment.c b/net/http/escapeurlfragment.c index 1f3610ac..a03406b8 100644 --- a/net/http/escapeurlfragment.c +++ b/net/http/escapeurlfragment.c @@ -21,27 +21,27 @@ // url fragment dispatch // - 0 is -/?.~_@:!$&'()*+,;=0-9A-Za-z -// - 2 is everything else which needs uppercase hex %XX +// - 1 is everything else which needs uppercase hex %XX // note that '& can break html // note that '() can break css urls // note that unicode can still be wild static const char kEscapeUrlFragment[256] = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10 - 2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, // 0x30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, // 0x50 - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 2, // 0x70 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 0x50 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // 0x70 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0 }; /** diff --git a/net/http/escapeurlparam.c b/net/http/escapeurlparam.c index cbf42138..88b2ab67 100644 --- a/net/http/escapeurlparam.c +++ b/net/http/escapeurlparam.c @@ -21,26 +21,25 @@ // url query/form name/parameter dispatch // - 0 is -.*_0-9A-Za-z -// - 1 is ' ' which becomes '+' -// - 2 is everything else which needs uppercase hex %XX +// - 1 is everything else which needs uppercase hex %XX // note that unicode can still be wild static const char kEscapeUrlParam[256] = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10 - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 0, 0, 2, // 0x20 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, // 0x30 - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, // 0x50 - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, // 0x70 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, // 0x20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 0x50 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, // 0x70 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0 }; /** diff --git a/net/http/escapeurlpath.c b/net/http/escapeurlpath.c index ac23e6f1..55789eb4 100644 --- a/net/http/escapeurlpath.c +++ b/net/http/escapeurlpath.c @@ -21,27 +21,27 @@ // url path dispatch // - 0 is -.~_@:!$&'()*+,;=0-9A-Za-z/ -// - 2 is everything else which needs uppercase hex %XX +// - 1 is everything else which needs uppercase hex %XX // note that '& can break html // note that '() can break css urls // note that unicode can still be wild static const char kEscapeUrlPath[256] = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10 - 2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, // 0x30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, // 0x50 - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 2, // 0x70 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 0x50 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // 0x70 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0 }; /** diff --git a/net/http/escapeurlpathsegment.c b/net/http/escapeurlpathsegment.c index 1f431026..47667ec0 100644 --- a/net/http/escapeurlpathsegment.c +++ b/net/http/escapeurlpathsegment.c @@ -21,27 +21,27 @@ // url path segment dispatch // - 0 is -.~_@:!$&'()*+,;=0-9A-Za-z -// - 2 is everything else which needs uppercase hex %XX +// - 1 is everything else which needs uppercase hex %XX // note that '& can break html // note that '() can break css urls // note that unicode can still be wild static const char kEscapeUrlPathSegment[256] = { - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10 - 2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, // 0x20 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0x20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, // 0x30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, // 0x50 - 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 2, // 0x70 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 0x50 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // 0x70 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0 }; /** diff --git a/net/http/gethttpheader.gperf b/net/http/gethttpheader.gperf index 9993daec..c092bb4e 100644 --- a/net/http/gethttpheader.gperf +++ b/net/http/gethttpheader.gperf @@ -11,57 +11,61 @@ %define lookup-function-name LookupHttpHeader struct HttpHeaderSlot { char *name; char code; }; %% -Accept, kHttpAccept -Accept-Charset, kHttpAcceptCharset -Accept-Encoding, kHttpAcceptEncoding -Accept-Language, kHttpAcceptLanguage -Age, kHttpAge -Allow, kHttpAllow -Authorization, kHttpAuthorization -Cache-Control, kHttpCacheControl -Chunked, kHttpChunked -Close, kHttpClose -Connection, kHttpConnection -Content-Base, kHttpContentBase -Content-Encoding, kHttpContentEncoding -Content-Language, kHttpContentLanguage -Content-Length, kHttpContentLength -Content-Location, kHttpContentLocation -Content-Md5, kHttpContentMd5 -Content-Range, kHttpContentRange -Content-Type, kHttpContentType -Date, kHttpDate -ETag, kHttpEtag -Expires, kHttpExpires -From, kHttpFrom -Host, kHttpHost -If-Match, kHttpIfMatch -If-Modified-Since, kHttpIfModifiedSince -If-None-Match, kHttpIfNoneMatch -If-Range, kHttpIfRange -If-Unmodified-Since, kHttpIfUnmodifiedSince -Keep-Alive, kHttpKeepAlive -Max-Forwards, kHttpMaxForwards -Pragma, kHttpPragma -Proxy-Authenticate, kHttpProxyAuthenticate -Proxy-Authorization, kHttpProxyAuthorization -Proxy-Connection, kHttpProxyConnection -Range, kHttpRange -Referer, kHttpReferer -Transfer-Encoding, kHttpTransferEncoding -Upgrade, kHttpUpgrade -User-Agent, kHttpUserAgent -Via, kHttpVia -Location, kHttpLocation -Public, kHttpPublic -Retry-After, kHttpRetryAfter -Server, kHttpServer -Vary, kHttpVary -Warning, kHttpWarning -WWW-Authenticate, kHttpWwwAuthenticate -Last-Modified, kHttpLastModified -Cookie, kHttpCookie -Trailer, kHttpTrailer -TE, kHttpTe -DNT, kHttpDnt -Expect, kHttpExpect +Accept, kHttpAccept +Accept-Charset, kHttpAcceptCharset +Accept-Encoding, kHttpAcceptEncoding +Accept-Language, kHttpAcceptLanguage +Age, kHttpAge +Allow, kHttpAllow +Authorization, kHttpAuthorization +Cache-Control, kHttpCacheControl +Chunked, kHttpChunked +Close, kHttpClose +Connection, kHttpConnection +Content-Base, kHttpContentBase +Content-Encoding, kHttpContentEncoding +Content-Language, kHttpContentLanguage +Content-Length, kHttpContentLength +Content-Location, kHttpContentLocation +Content-Md5, kHttpContentMd5 +Content-Range, kHttpContentRange +Content-Type, kHttpContentType +Date, kHttpDate +ETag, kHttpEtag +Expires, kHttpExpires +From, kHttpFrom +Host, kHttpHost +If-Match, kHttpIfMatch +If-Modified-Since, kHttpIfModifiedSince +If-None-Match, kHttpIfNoneMatch +If-Range, kHttpIfRange +If-Unmodified-Since, kHttpIfUnmodifiedSince +Keep-Alive, kHttpKeepAlive +Max-Forwards, kHttpMaxForwards +Pragma, kHttpPragma +Proxy-Authenticate, kHttpProxyAuthenticate +Proxy-Authorization, kHttpProxyAuthorization +Proxy-Connection, kHttpProxyConnection +Range, kHttpRange +Referer, kHttpReferer +Transfer-Encoding, kHttpTransferEncoding +Upgrade, kHttpUpgrade +User-Agent, kHttpUserAgent +Via, kHttpVia +Location, kHttpLocation +Public, kHttpPublic +Retry-After, kHttpRetryAfter +Server, kHttpServer +Vary, kHttpVary +Warning, kHttpWarning +WWW-Authenticate, kHttpWwwAuthenticate +Last-Modified, kHttpLastModified +Cookie, kHttpCookie +Trailer, kHttpTrailer +TE, kHttpTe +DNT, kHttpDnt +Expect, kHttpExpect +Content-Disposition, kHttpContentDisposition +Content-Description, kHttpContentDescription +Origin, kHttpOrigin +Upgrade-Insecure-Requests, kHttpUpgradeInsecureRequests diff --git a/net/http/gethttpheader.inc b/net/http/gethttpheader.inc index c8f95e2a..dad490f2 100644 --- a/net/http/gethttpheader.inc +++ b/net/http/gethttpheader.inc @@ -37,12 +37,12 @@ #line 12 "gethttpheader.gperf" struct HttpHeaderSlot { char *name; char code; }; -#define TOTAL_KEYWORDS 54 +#define TOTAL_KEYWORDS 58 #define MIN_WORD_LENGTH 2 -#define MAX_WORD_LENGTH 19 +#define MAX_WORD_LENGTH 25 #define MIN_HASH_VALUE 2 -#define MAX_HASH_VALUE 89 -/* maximum key range = 88, duplicates = 0 */ +#define MAX_HASH_VALUE 97 +/* maximum key range = 96, duplicates = 0 */ #ifndef GPERF_DOWNCASE #define GPERF_DOWNCASE 1 @@ -101,32 +101,32 @@ hash (register const char *str, register size_t len) { static const unsigned char asso_values[] = { - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 30, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 5, 5, 30, 50, 0, - 35, 30, 0, 5, 90, 40, 0, 30, 0, 20, - 45, 90, 0, 5, 10, 5, 0, 15, 20, 25, - 90, 90, 90, 90, 90, 90, 90, 5, 5, 30, - 50, 0, 35, 30, 0, 5, 90, 40, 0, 30, - 0, 20, 45, 90, 0, 5, 10, 5, 0, 15, - 20, 25, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 90, 90 + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 30, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 5, 5, 30, 55, 0, + 35, 30, 0, 35, 98, 40, 0, 30, 0, 20, + 55, 98, 0, 5, 10, 5, 0, 5, 20, 30, + 98, 98, 98, 98, 98, 98, 98, 5, 5, 30, + 55, 0, 35, 30, 0, 35, 98, 40, 0, 30, + 0, 20, 55, 98, 0, 5, 10, 5, 0, 5, + 20, 30, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98 }; register unsigned int hval = len; @@ -159,132 +159,137 @@ LookupHttpHeader (register const char *str, register size_t len) { {""}, {""}, #line 65 "gethttpheader.gperf" - {"TE", kHttpTe}, + {"TE", kHttpTe}, #line 18 "gethttpheader.gperf" - {"Age", kHttpAge}, + {"Age", kHttpAge}, {""}, {""}, #line 58 "gethttpheader.gperf" - {"Server", kHttpServer}, + {"Server", kHttpServer}, #line 60 "gethttpheader.gperf" - {"Warning", kHttpWarning}, + {"Warning", kHttpWarning}, #line 54 "gethttpheader.gperf" - {"Via", kHttpVia}, + {"Via", kHttpVia}, {""}, #line 24 "gethttpheader.gperf" - {"Connection", kHttpConnection}, + {"Connection", kHttpConnection}, #line 56 "gethttpheader.gperf" - {"Public", kHttpPublic}, + {"Public", kHttpPublic}, #line 22 "gethttpheader.gperf" - {"Chunked", kHttpChunked}, + {"Chunked", kHttpChunked}, #line 66 "gethttpheader.gperf" - {"DNT", kHttpDnt}, + {"DNT", kHttpDnt}, #line 33 "gethttpheader.gperf" - {"Date", kHttpDate}, - {""}, {""}, -#line 64 "gethttpheader.gperf" - {"Trailer", kHttpTrailer}, - {""}, + {"Date", kHttpDate}, + {""}, {""}, {""}, {""}, #line 37 "gethttpheader.gperf" - {"Host", kHttpHost}, + {"Host", kHttpHost}, #line 53 "gethttpheader.gperf" - {"User-Agent", kHttpUserAgent}, + {"User-Agent", kHttpUserAgent}, #line 57 "gethttpheader.gperf" - {"Retry-After", kHttpRetryAfter}, + {"Retry-After", kHttpRetryAfter}, #line 51 "gethttpheader.gperf" - {"Transfer-Encoding", kHttpTransferEncoding}, + {"Transfer-Encoding", kHttpTransferEncoding}, {""}, #line 28 "gethttpheader.gperf" - {"Content-Length", kHttpContentLength}, + {"Content-Length", kHttpContentLength}, #line 19 "gethttpheader.gperf" - {"Allow", kHttpAllow}, + {"Allow", kHttpAllow}, #line 26 "gethttpheader.gperf" - {"Content-Encoding", kHttpContentEncoding}, + {"Content-Encoding", kHttpContentEncoding}, #line 25 "gethttpheader.gperf" - {"Content-Base", kHttpContentBase}, + {"Content-Base", kHttpContentBase}, #line 31 "gethttpheader.gperf" - {"Content-Range", kHttpContentRange}, -#line 59 "gethttpheader.gperf" - {"Vary", kHttpVary}, + {"Content-Range", kHttpContentRange}, +#line 69 "gethttpheader.gperf" + {"Content-Description", kHttpContentDescription}, #line 23 "gethttpheader.gperf" - {"Close", kHttpClose}, + {"Close", kHttpClose}, #line 27 "gethttpheader.gperf" - {"Content-Language", kHttpContentLanguage}, + {"Content-Language", kHttpContentLanguage}, {""}, #line 20 "gethttpheader.gperf" - {"Authorization", kHttpAuthorization}, - {""}, + {"Authorization", kHttpAuthorization}, +#line 59 "gethttpheader.gperf" + {"Vary", kHttpVary}, #line 49 "gethttpheader.gperf" - {"Range", kHttpRange}, + {"Range", kHttpRange}, #line 14 "gethttpheader.gperf" - {"Accept", kHttpAccept}, + {"Accept", kHttpAccept}, #line 52 "gethttpheader.gperf" - {"Upgrade", kHttpUpgrade}, + {"Upgrade", kHttpUpgrade}, #line 41 "gethttpheader.gperf" - {"If-Range", kHttpIfRange}, + {"If-Range", kHttpIfRange}, #line 34 "gethttpheader.gperf" - {"ETag", kHttpEtag}, + {"ETag", kHttpEtag}, {""}, #line 45 "gethttpheader.gperf" - {"Pragma", kHttpPragma}, + {"Pragma", kHttpPragma}, #line 50 "gethttpheader.gperf" - {"Referer", kHttpReferer}, + {"Referer", kHttpReferer}, #line 55 "gethttpheader.gperf" - {"Location", kHttpLocation}, + {"Location", kHttpLocation}, {""}, #line 17 "gethttpheader.gperf" - {"Accept-Language", kHttpAcceptLanguage}, + {"Accept-Language", kHttpAcceptLanguage}, #line 29 "gethttpheader.gperf" - {"Content-Location", kHttpContentLocation}, -#line 32 "gethttpheader.gperf" - {"Content-Type", kHttpContentType}, + {"Content-Location", kHttpContentLocation}, +#line 64 "gethttpheader.gperf" + {"Trailer", kHttpTrailer}, #line 40 "gethttpheader.gperf" - {"If-None-Match", kHttpIfNoneMatch}, + {"If-None-Match", kHttpIfNoneMatch}, #line 15 "gethttpheader.gperf" - {"Accept-Charset", kHttpAcceptCharset}, - {""}, -#line 67 "gethttpheader.gperf" - {"Expect", kHttpExpect}, - {""}, -#line 21 "gethttpheader.gperf" - {"Cache-Control", kHttpCacheControl}, -#line 36 "gethttpheader.gperf" - {"From", kHttpFrom}, -#line 43 "gethttpheader.gperf" - {"Keep-Alive", kHttpKeepAlive}, -#line 48 "gethttpheader.gperf" - {"Proxy-Connection", kHttpProxyConnection}, -#line 35 "gethttpheader.gperf" - {"Expires", kHttpExpires}, -#line 46 "gethttpheader.gperf" - {"Proxy-Authenticate", kHttpProxyAuthenticate}, -#line 47 "gethttpheader.gperf" - {"Proxy-Authorization", kHttpProxyAuthorization}, + {"Accept-Charset", kHttpAcceptCharset}, {""}, #line 61 "gethttpheader.gperf" - {"WWW-Authenticate", kHttpWwwAuthenticate}, + {"WWW-Authenticate", kHttpWwwAuthenticate}, +#line 32 "gethttpheader.gperf" + {"Content-Type", kHttpContentType}, +#line 21 "gethttpheader.gperf" + {"Cache-Control", kHttpCacheControl}, +#line 36 "gethttpheader.gperf" + {"From", kHttpFrom}, +#line 71 "gethttpheader.gperf" + {"Upgrade-Insecure-Requests", kHttpUpgradeInsecureRequests}, +#line 48 "gethttpheader.gperf" + {"Proxy-Connection", kHttpProxyConnection}, + {""}, +#line 46 "gethttpheader.gperf" + {"Proxy-Authenticate", kHttpProxyAuthenticate}, +#line 47 "gethttpheader.gperf" + {"Proxy-Authorization", kHttpProxyAuthorization}, + {""}, +#line 67 "gethttpheader.gperf" + {"Expect", kHttpExpect}, #line 44 "gethttpheader.gperf" - {"Max-Forwards", kHttpMaxForwards}, + {"Max-Forwards", kHttpMaxForwards}, #line 62 "gethttpheader.gperf" - {"Last-Modified", kHttpLastModified}, - {""}, {""}, + {"Last-Modified", kHttpLastModified}, +#line 68 "gethttpheader.gperf" + {"Content-Disposition", kHttpContentDisposition}, +#line 43 "gethttpheader.gperf" + {"Keep-Alive", kHttpKeepAlive}, #line 63 "gethttpheader.gperf" - {"Cookie", kHttpCookie}, + {"Cookie", kHttpCookie}, {""}, #line 38 "gethttpheader.gperf" - {"If-Match", kHttpIfMatch}, + {"If-Match", kHttpIfMatch}, {""}, {""}, -#line 30 "gethttpheader.gperf" - {"Content-Md5", kHttpContentMd5}, +#line 70 "gethttpheader.gperf" + {"Origin", kHttpOrigin}, {""}, {""}, {""}, #line 16 "gethttpheader.gperf" - {"Accept-Encoding", kHttpAcceptEncoding}, - {""}, + {"Accept-Encoding", kHttpAcceptEncoding}, +#line 30 "gethttpheader.gperf" + {"Content-Md5", kHttpContentMd5}, #line 39 "gethttpheader.gperf" - {"If-Modified-Since", kHttpIfModifiedSince}, + {"If-Modified-Since", kHttpIfModifiedSince}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, #line 42 "gethttpheader.gperf" - {"If-Unmodified-Since", kHttpIfUnmodifiedSince} + {"If-Unmodified-Since", kHttpIfUnmodifiedSince}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 35 "gethttpheader.gperf" + {"Expires", kHttpExpires} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) diff --git a/net/http/gethttpheadername.c b/net/http/gethttpheadername.c new file mode 100644 index 00000000..8f7212f7 --- /dev/null +++ b/net/http/gethttpheadername.c @@ -0,0 +1,142 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "net/http/http.h" + +const char *GetHttpHeaderName(int h) { + switch (h) { + case kHttpAccept: + return "Accept"; + case kHttpAcceptCharset: + return "Accept-Charset"; + case kHttpAcceptEncoding: + return "Accept-Encoding"; + case kHttpAcceptLanguage: + return "Accept-Language"; + case kHttpAge: + return "Age"; + case kHttpAllow: + return "Allow"; + case kHttpAuthorization: + return "Authorization"; + case kHttpCacheControl: + return "Cache-Control"; + case kHttpChunked: + return "Chunked"; + case kHttpClose: + return "Close"; + case kHttpConnection: + return "Connection"; + case kHttpContentBase: + return "Content-Base"; + case kHttpContentEncoding: + return "Content-Encoding"; + case kHttpContentLanguage: + return "Content-Language"; + case kHttpContentLength: + return "Content-Length"; + case kHttpContentLocation: + return "Content-Location"; + case kHttpContentMd5: + return "Content-Md5"; + case kHttpContentRange: + return "Content-Range"; + case kHttpContentType: + return "Content-Type"; + case kHttpDate: + return "Date"; + case kHttpEtag: + return "ETag"; + case kHttpExpires: + return "Expires"; + case kHttpFrom: + return "From"; + case kHttpHost: + return "Host"; + case kHttpIfMatch: + return "If-Match"; + case kHttpIfModifiedSince: + return "If-Modified-Since"; + case kHttpIfNoneMatch: + return "If-None-Match"; + case kHttpIfRange: + return "If-Range"; + case kHttpIfUnmodifiedSince: + return "If-Unmodified-Since"; + case kHttpKeepAlive: + return "Keep-Alive"; + case kHttpMaxForwards: + return "Max-Forwards"; + case kHttpPragma: + return "Pragma"; + case kHttpProxyAuthenticate: + return "Proxy-Authenticate"; + case kHttpProxyAuthorization: + return "Proxy-Authorization"; + case kHttpProxyConnection: + return "Proxy-Connection"; + case kHttpRange: + return "Range"; + case kHttpReferer: + return "Referer"; + case kHttpTransferEncoding: + return "Transfer-Encoding"; + case kHttpUpgrade: + return "Upgrade"; + case kHttpUserAgent: + return "User-Agent"; + case kHttpVia: + return "Via"; + case kHttpLocation: + return "Location"; + case kHttpPublic: + return "Public"; + case kHttpRetryAfter: + return "Retry-After"; + case kHttpServer: + return "Server"; + case kHttpVary: + return "Vary"; + case kHttpWarning: + return "Warning"; + case kHttpWwwAuthenticate: + return "WWW-Authenticate"; + case kHttpLastModified: + return "Last-Modified"; + case kHttpCookie: + return "Cookie"; + case kHttpTrailer: + return "Trailer"; + case kHttpTe: + return "TE"; + case kHttpDnt: + return "DNT"; + case kHttpExpect: + return "Expect"; + case kHttpContentDisposition: + return "Content-Disposition"; + case kHttpContentDescription: + return "Content-Description"; + case kHttpOrigin: + return "Origin"; + case kHttpUpgradeInsecureRequests: + return "Upgrade-Insecure-Requests"; + default: + return NULL; + } +} diff --git a/net/http/http.h b/net/http/http.h index 21a67555..f3cdafef 100644 --- a/net/http/http.h +++ b/net/http/http.h @@ -21,61 +21,65 @@ #define kHttpReport 15 #define kHttpUnlock 16 -#define kHttpAccept 0 -#define kHttpAcceptCharset 1 -#define kHttpAcceptEncoding 2 -#define kHttpAcceptLanguage 3 -#define kHttpAge 4 -#define kHttpAllow 5 -#define kHttpAuthorization 6 -#define kHttpCacheControl 7 -#define kHttpChunked 8 -#define kHttpClose 9 -#define kHttpConnection 10 -#define kHttpContentBase 11 -#define kHttpContentEncoding 12 -#define kHttpContentLanguage 13 -#define kHttpContentLength 14 -#define kHttpContentLocation 15 -#define kHttpContentMd5 16 -#define kHttpContentRange 17 -#define kHttpContentType 18 -#define kHttpDate 19 -#define kHttpEtag 20 -#define kHttpExpires 21 -#define kHttpFrom 22 -#define kHttpHost 23 -#define kHttpIfMatch 24 -#define kHttpIfModifiedSince 25 -#define kHttpIfNoneMatch 26 -#define kHttpIfRange 27 -#define kHttpIfUnmodifiedSince 28 -#define kHttpKeepAlive 29 -#define kHttpMaxForwards 30 -#define kHttpPragma 31 -#define kHttpProxyAuthenticate 32 -#define kHttpProxyAuthorization 33 -#define kHttpProxyConnection 34 -#define kHttpRange 35 -#define kHttpReferer 36 -#define kHttpTransferEncoding 37 -#define kHttpUpgrade 38 -#define kHttpUserAgent 39 -#define kHttpVia 40 -#define kHttpLocation 41 -#define kHttpPublic 42 -#define kHttpRetryAfter 43 -#define kHttpServer 44 -#define kHttpVary 45 -#define kHttpWarning 46 -#define kHttpWwwAuthenticate 47 -#define kHttpLastModified 48 -#define kHttpCookie 49 -#define kHttpTrailer 50 -#define kHttpTe 51 -#define kHttpDnt 52 -#define kHttpExpect 53 -#define kHttpHeadersMax 54 +#define kHttpAccept 0 +#define kHttpAcceptCharset 1 +#define kHttpAcceptEncoding 2 +#define kHttpAcceptLanguage 3 +#define kHttpAge 4 +#define kHttpAllow 5 +#define kHttpAuthorization 6 +#define kHttpCacheControl 7 +#define kHttpChunked 8 +#define kHttpClose 9 +#define kHttpConnection 10 +#define kHttpContentBase 11 +#define kHttpContentEncoding 12 +#define kHttpContentLanguage 13 +#define kHttpContentLength 14 +#define kHttpContentLocation 15 +#define kHttpContentMd5 16 +#define kHttpContentRange 17 +#define kHttpContentType 18 +#define kHttpDate 19 +#define kHttpEtag 20 +#define kHttpExpires 21 +#define kHttpFrom 22 +#define kHttpHost 23 +#define kHttpIfMatch 24 +#define kHttpIfModifiedSince 25 +#define kHttpIfNoneMatch 26 +#define kHttpIfRange 27 +#define kHttpIfUnmodifiedSince 28 +#define kHttpKeepAlive 29 +#define kHttpMaxForwards 30 +#define kHttpPragma 31 +#define kHttpProxyAuthenticate 32 +#define kHttpProxyAuthorization 33 +#define kHttpProxyConnection 34 +#define kHttpRange 35 +#define kHttpReferer 36 +#define kHttpTransferEncoding 37 +#define kHttpUpgrade 38 +#define kHttpUserAgent 39 +#define kHttpVia 40 +#define kHttpLocation 41 +#define kHttpPublic 42 +#define kHttpRetryAfter 43 +#define kHttpServer 44 +#define kHttpVary 45 +#define kHttpWarning 46 +#define kHttpWwwAuthenticate 47 +#define kHttpLastModified 48 +#define kHttpCookie 49 +#define kHttpTrailer 50 +#define kHttpTe 51 +#define kHttpDnt 52 +#define kHttpExpect 53 +#define kHttpContentDisposition 54 +#define kHttpContentDescription 55 +#define kHttpOrigin 56 +#define kHttpUpgradeInsecureRequests 57 +#define kHttpHeadersMax 58 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -85,12 +89,20 @@ struct HttpRequestSlice { }; struct HttpRequest { - int i, t, a, h; + int i, t, a; int method; + struct HttpRequestSlice k; struct HttpRequestSlice uri; struct HttpRequestSlice version; struct HttpRequestSlice scratch; struct HttpRequestSlice headers[kHttpHeadersMax]; + struct HttpRequestHeaders { + size_t n; + struct HttpRequestHeader { + struct HttpRequestSlice k; + struct HttpRequestSlice v; + } * p; + } xheaders; }; extern const char kHttpMethod[17][8]; @@ -98,6 +110,7 @@ extern const char kHttpMethod[17][8]; int GetHttpHeader(const char *, size_t); int GetHttpMethod(const char *, size_t); void InitHttpRequest(struct HttpRequest *); +void DestroyHttpRequest(struct HttpRequest *); int ParseHttpRequest(struct HttpRequest *, const char *, size_t); int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *, uint32_t *, bool, long double); @@ -107,6 +120,7 @@ bool ParseHttpRange(const char *, size_t, long, long *, long *); unsigned ParseHttpVersion(const char *, size_t); int64_t ParseHttpDateTime(const char *, size_t); const char *GetHttpReason(int); +const char *GetHttpHeaderName(int); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/net/http/http.mk b/net/http/http.mk index 163f0b8e..3fda5711 100644 --- a/net/http/http.mk +++ b/net/http/http.mk @@ -31,6 +31,7 @@ NET_HTTP_A_DIRECTDEPS = \ LIBC_FMT \ LIBC_INTRIN \ LIBC_LOG \ + LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ LIBC_SOCK \ diff --git a/net/http/parsehttprequest.c b/net/http/parsehttprequest.c index 2a6fcb30..185930ba 100644 --- a/net/http/parsehttprequest.c +++ b/net/http/parsehttprequest.c @@ -20,6 +20,7 @@ #include "libc/alg/arraylist.internal.h" #include "libc/limits.h" #include "libc/macros.internal.h" +#include "libc/mem/mem.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" @@ -37,11 +38,19 @@ void InitHttpRequest(struct HttpRequest *r) { memset(r, 0, sizeof(*r)); } +/** + * Destroys HTTP request parser. + */ +void DestroyHttpRequest(struct HttpRequest *r) { + free(r->xheaders.p); +} + /** * Parses HTTP request. */ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) { - int c; + int c, h; + struct HttpRequestHeader *x; for (n = MIN(n, LIMIT); r->i < n; ++r->i) { c = p[r->i] & 0xff; switch (r->t) { @@ -97,12 +106,12 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) { } else if (c == ' ' || c == '\t') { return ebadmsg(); /* RFC7230 § 3.2.4 */ } - r->a = r->i; + r->k.a = r->i; r->t = HKEY; break; case HKEY: if (c == ':') { - r->h = GetHttpHeader(p + r->a, r->i - r->a); + r->k.b = r->i; r->t = HSEP; } break; @@ -113,9 +122,16 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) { /* fallthrough */ case HVAL: if (c == '\r' || c == '\n') { - if (r->h != -1) { - r->headers[r->h].a = r->a; - r->headers[r->h].b = r->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 = r->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 = r->i; + r->xheaders.p = x; + ++r->xheaders.n; } r->t = c == '\r' ? CR1 : LF1; } diff --git a/test/net/http/escapeurlparam_test.c b/test/net/http/escapeurlparam_test.c index 3538c3b3..c7469c70 100644 --- a/test/net/http/escapeurlparam_test.c +++ b/test/net/http/escapeurlparam_test.c @@ -31,7 +31,8 @@ char *escapeparam(const char *s) { } TEST(escapeparam, test) { - EXPECT_STREQ("abc+%26%3C%3E%22%27%01%02", gc(escapeparam("abc &<>\"'\1\2"))); + EXPECT_STREQ("abc%20%26%3C%3E%22%27%01%02", + gc(escapeparam("abc &<>\"'\1\2"))); } TEST(escapeparam, testLargeGrowth) { diff --git a/test/net/http/parsehttprequest_test.c b/test/net/http/parsehttprequest_test.c index 3e8b99ad..1a96dc2e 100644 --- a/test/net/http/parsehttprequest_test.c +++ b/test/net/http/parsehttprequest_test.c @@ -41,19 +41,24 @@ static unsigned version(const char *m) { return ParseHttpVersion(m + req->version.a, req->version.b - req->version.a); } -TEST(ParseHttpRequest, testEmpty_tooShort) { +void SetUp(void) { InitHttpRequest(req); +} + +void TearDown(void) { + DestroyHttpRequest(req); +} + +TEST(ParseHttpRequest, testEmpty_tooShort) { EXPECT_EQ(0, ParseHttpRequest(req, "", 0)); } TEST(ParseHttpRequest, testTooShort) { - InitHttpRequest(req); EXPECT_EQ(0, ParseHttpRequest(req, "\r\n", 2)); } TEST(ParseHttpRequest, testNoHeaders) { static const char m[] = "GET /foo HTTP/1.0\r\n\r\n"; - InitHttpRequest(req); EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpGet, req->method); EXPECT_STREQ("/foo", gc(slice(m, req->uri))); @@ -66,7 +71,6 @@ POST /foo?bar%20hi HTTP/1.0\r\n\ Host: foo.example\r\n\ Content-Length: 0\r\n\ \r\n"; - InitHttpRequest(req); EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpPost, req->method); EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri))); @@ -78,7 +82,6 @@ Content-Length: 0\r\n\ TEST(ParseHttpRequest, testHttp101) { static const char m[] = "GET / HTTP/1.1\r\n\r\n"; - InitHttpRequest(req); EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpGet, req->method); EXPECT_STREQ("/", gc(slice(m, req->uri))); @@ -88,7 +91,6 @@ TEST(ParseHttpRequest, testHttp101) { TEST(ParseHttpRequest, testHttp100) { static const char m[] = "GET / HTTP/1.0\r\n\r\n"; - InitHttpRequest(req); EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpGet, req->method); EXPECT_STREQ("/", gc(slice(m, req->uri))); @@ -98,7 +100,6 @@ TEST(ParseHttpRequest, testHttp100) { TEST(ParseHttpRequest, testHttp009) { static const char m[] = "GET /\r\n\r\n"; - InitHttpRequest(req); EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpGet, req->method); EXPECT_STREQ("/", gc(slice(m, req->uri))); @@ -112,7 +113,6 @@ TEST(ParseHttpRequest, testLeadingLineFeeds_areIgnored) { GET /foo?bar%20hi HTTP/1.0\r\n\ User-Agent: hi\r\n\ \r\n"; - InitHttpRequest(req); EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri))); } @@ -123,7 +123,6 @@ GET /foo?bar%20hi HTTP/1.0\r\n\ User-Agent: hi\r\n\ there\r\n\ \r\n"; - InitHttpRequest(req); EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(EBADMSG, errno); } @@ -134,7 +133,6 @@ GET /foo?bar%20hi HTTP/1.0\r\n\ User-Agent: hi\r\n\ : hi\r\n\ \r\n"; - InitHttpRequest(req); EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(EBADMSG, errno); } @@ -146,7 +144,6 @@ Host: foo.example\n\ Content-Length: 0\n\ \n\ \n"; - InitHttpRequest(req); EXPECT_EQ(strlen(m) - 1, ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpPost, req->method); EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri))); @@ -155,3 +152,37 @@ Content-Length: 0\n\ EXPECT_STREQ("0", gc(slice(m, req->headers[kHttpContentLength]))); EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag]))); } + +TEST(ParseHttpRequest, testChromeMessage) { + 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"; + EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); + EXPECT_EQ(kHttpGet, req->method); + EXPECT_STREQ("/tool/net/redbean.png", gc(slice(m, req->uri))); + EXPECT_STREQ("HTTP/1.1", gc(slice(m, req->version))); + EXPECT_STREQ("10.10.10.124:8080", gc(slice(m, req->headers[kHttpHost]))); + EXPECT_STREQ("1", gc(slice(m, req->headers[kHttpDnt]))); + EXPECT_STREQ("", gc(slice(m, req->headers[kHttpExpect]))); + EXPECT_STREQ("", gc(slice(m, req->headers[kHttpContentLength]))); + EXPECT_STREQ("", gc(slice(m, req->headers[kHttpExpect]))); +} + +TEST(ParseHttpRequest, testExtendedHeaders) { + static const char m[] = "\ +GET /foo?bar%20hi HTTP/1.0\r\n\ +X-User-Agent: hi\r\n\ +\r\n"; + EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); + ASSERT_EQ(1, req->xheaders.n); + EXPECT_STREQ("X-User-Agent", gc(slice(m, req->xheaders.p[0].k))); + EXPECT_STREQ("hi", gc(slice(m, req->xheaders.p[0].v))); +} diff --git a/tool/net/net.mk b/tool/net/net.mk index 1ccbaad5..84280d70 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -67,6 +67,9 @@ o/$(MODE)/tool/net/redbean.com.dbg: \ o/$(MODE)/tool/net/redbean.css.zip.o \ o/$(MODE)/tool/net/redbean.html.zip.o \ o/$(MODE)/tool/net/redbean.lua.zip.o \ + o/$(MODE)/tool/net/redbean-form.lua.zip.o \ + o/$(MODE)/tool/net/.init.lua.zip.o \ + o/$(MODE)/tool/net/.reload.lua.zip.o \ o/$(MODE)/tool/net/net.pkg \ $(CRT) \ $(APE) diff --git a/tool/net/redbean-form.lua b/tool/net/redbean-form.lua new file mode 100644 index 00000000..5b8f0385 --- /dev/null +++ b/tool/net/redbean-form.lua @@ -0,0 +1,70 @@ +-- redbean post request handler demo + +local function main() + if GetMethod() ~= 'POST' then + ServeError(405) + SetHeader('Allow', 'POST') + return + end + SetStatus(200) + SetHeader('Content-Type', 'text/html; charset=utf-8') + Write('\n') + Write('redbean\n') + Write('

POST Request HTML Form Handler Demo

\n') + + Write('

') + firstname = GetParam('firstname') + lastname = GetParam('lastname') + if firstname and lastname then + Write('Hello ') + Write(EscapeHtml(firstname)) + Write(' ') + Write(EscapeHtml(lastname)) + Write('!
') + Write('Thank you for using redbean.') + end + + Write('

\n') + + Write('
Params\n') + Write('
\n') + Write('
\n') + params = GetParams() + for i = 1,#params do + Write('
') + Write(EscapeHtml(params[i][1])) + Write('\n') + if params[i][2] then + Write('
') + Write(EscapeHtml(params[i][2])) + Write('\n') + end + end + Write('
\n') + + Write('
Headers\n') + Write('
\n') + Write('
\n') + for k,v in pairs(GetHeaders()) do + Write('
') + Write(EscapeHtml(k)) + Write('\n') + Write('
') + Write(EscapeHtml(v)) + Write('\n') + end + Write('
\n') + + Write('
Payload\n') + Write('

') + Write(EscapeHtml(GetPayload())) + Write('\n') + + Write('

\n') + + Write('

') + Write('Click here ') + Write('to return to the previous page.\n') +end + +main() diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 70000941..7e023bdb 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/bits/bswap.h" +#include "libc/bits/popcnt.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/struct/iovec.h" @@ -34,6 +35,8 @@ #include "libc/macros.internal.h" #include "libc/math.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/crc32.h" #include "libc/rand/rand.h" #include "libc/runtime/runtime.h" @@ -80,7 +83,7 @@ #include "third_party/zlib/zlib.h" #define USAGE \ - " [-hvdsm] [-p PORT]\n\ + " [-hvdsm] [-p PORT] [-- SCRIPTARGS...]\n\ \n\ DESCRIPTION\n\ \n\ @@ -106,11 +109,11 @@ FLAGS\n\ \n\ FEATURES\n\ \n\ + - Lua v5.4\n\ - HTTP v0.9\n\ - HTTP v1.0\n\ - HTTP v1.1\n\ - - Lua v5.4.x\n\ - - Gzip Content-Encoding\n\ + - Content-Encoding\n\ - Range / Content-Range\n\ - Last-Modified / If-Modified-Since\n\ \n\ @@ -235,7 +238,7 @@ struct Buffer { char *p; }; -struct UriParser { +struct Parser { int i; int c; const char *data; @@ -252,6 +255,12 @@ struct Params { } * p; }; +struct Request { + struct Buffer path; + struct Params params; + struct Buffer fragment; +}; + static struct Freelist { size_t n; void **p; @@ -304,11 +313,11 @@ static int statuscode; static unsigned httpversion; static uint32_t clientaddrsize; -static void *zmap; static lua_State *L; static void *content; -static uint8_t *zbase; -static uint8_t *zcdir; +static uint8_t *zmap; +static uint8_t *zdir; +static size_t hdrsize; static size_t msgsize; static size_t amtread; static size_t zmapsize; @@ -321,13 +330,10 @@ static int64_t cacheseconds; static uint8_t gzip_footer[8]; static const char *serverheader; -static struct Buffer body; static struct Buffer inbuf; static struct Buffer hdrbuf; static struct Buffer outbuf; -static struct Buffer uripath; -static struct Params uriparams; -static struct Buffer urifragment; +static struct Request request; static long double nowish; static long double startread; @@ -336,7 +342,7 @@ static long double startconnection; static struct sockaddr_in serveraddr; static struct sockaddr_in clientaddr; -static struct HttpRequest req; +static struct HttpRequest msg; static char currentdate[32]; static char clientaddrstr[32]; static char serveraddrstr[32]; @@ -378,7 +384,7 @@ static void OnHup(void) { } } -static int ComparePaths(const char *a, size_t n, const char *b, size_t m) { +static int CompareSlices(const char *a, size_t n, const char *b, size_t m) { int c; if ((c = memcmp(a, b, MIN(n, m)))) return c; if (n < m) return -1; @@ -386,13 +392,21 @@ static int ComparePaths(const char *a, size_t n, const char *b, size_t m) { return 0; } +static int CompareSlicesCase(const char *a, size_t n, const char *b, size_t m) { + int c; + if ((c = memcasecmp(a, b, MIN(n, m)))) return c; + if (n < m) return -1; + if (n > m) return +1; + return 0; +} + static long FindRedirect(const char *path, size_t n) { int c, m, l, r, z; l = 0; r = redirects.n - 1; while (l <= r) { m = (l + r) >> 1; - c = ComparePaths(redirects.p[m].path, redirects.p[m].pathlen, path, n); + c = CompareSlices(redirects.p[m].path, redirects.p[m].pathlen, path, n); if (c < 0) { l = m + 1; } else if (c > 0) { @@ -419,8 +433,8 @@ static void AddRedirect(const char *arg) { i = redirects.n; redirects.p = xrealloc(redirects.p, (i + 1) * sizeof(*redirects.p)); for (j = i; j > 0; --j) { - if (ComparePaths(r.path, r.pathlen, redirects.p[j - 1].path, - redirects.p[j - 1].pathlen) < 0) { + if (CompareSlices(r.path, r.pathlen, redirects.p[j - 1].path, + redirects.p[j - 1].pathlen) < 0) { redirects.p[j] = redirects.p[j - 1]; } else { break; @@ -657,19 +671,23 @@ static uint32_t Hash(const void *data, size_t size) { } static bool HasHeader(int h) { - return req.headers[h].b > req.headers[h].a; + return msg.headers[h].b > msg.headers[h].a; } -static int CompareHeaderValue(int h, const char *s) { - return strncmp(s, inbuf.p + req.headers[h].a, - req.headers[h].b - req.headers[h].a); +static int CompareHeader(int h, const char *s) { + return CompareSlices(s, strlen(s), inbuf.p + msg.headers[h].a, + msg.headers[h].b - msg.headers[h].a); +} + +static bool HeaderEquals(int h, const char *s) { + return !CompareHeader(h, s); } static bool ClientAcceptsGzip(void) { return httpversion >= 100 && - !!memmem(inbuf.p + req.headers[kHttpAcceptEncoding].a, - req.headers[kHttpAcceptEncoding].b - - req.headers[kHttpAcceptEncoding].a, + !!memmem(inbuf.p + msg.headers[kHttpAcceptEncoding].a, + msg.headers[kHttpAcceptEncoding].b - + msg.headers[kHttpAcceptEncoding].a, "gzip", 4); } @@ -710,16 +728,16 @@ static int64_t GetLastModifiedZip(const uint8_t *cfile) { } static bool IsCompressed(struct Asset *a) { - return ZIP_LFILE_COMPRESSIONMETHOD(zbase + a->lf) == kZipCompressionDeflate; + return ZIP_LFILE_COMPRESSIONMETHOD(zmap + a->lf) == kZipCompressionDeflate; } static bool IsNotModified(struct Asset *a) { if (httpversion < 100) return false; if (!HasHeader(kHttpIfModifiedSince)) return false; return a->lastmodified >= - ParseHttpDateTime(inbuf.p + req.headers[kHttpIfModifiedSince].a, - req.headers[kHttpIfModifiedSince].b - - req.headers[kHttpIfModifiedSince].a); + ParseHttpDateTime(inbuf.p + msg.headers[kHttpIfModifiedSince].a, + msg.headers[kHttpIfModifiedSince].b - + msg.headers[kHttpIfModifiedSince].a); } static char *FormatUnixHttpDateTime(char *s, int64_t t) { @@ -729,86 +747,43 @@ static char *FormatUnixHttpDateTime(char *s, int64_t t) { return s; } -static void FreeAssetsIndex(struct Asset *p, size_t n) { - int i; - if (p) { - for (i = 0; i < n; ++i) { - free(p[i].lastmodifiedstr); - } - free(p); - } -} - -static bool IndexAssets(const uint8_t *base, const uint8_t *cdir) { - bool ok; +static void IndexAssets(void) { int64_t lm; struct Asset *p; uint32_t i, n, m, cf, step, hash; - DCHECK_GE(HASH_LOAD_FACTOR, 2); - n = ZIP_CDIR_RECORDS(cdir); + CHECK_GE(HASH_LOAD_FACTOR, 2); + n = ZIP_CDIR_RECORDS(zdir); m = roundup2pow(MAX(1, n) * HASH_LOAD_FACTOR); - p = calloc(m, sizeof(struct Asset)); - ok = ZIP_CDIR_MAGIC(cdir) == kZipCdirHdrMagic; - if (p && ok) { - for (cf = ZIP_CDIR_OFFSET(cdir); n--; cf += ZIP_CFILE_HDRSIZE(base + cf)) { - if (ZIP_CFILE_MAGIC(base + cf) == kZipCfileHdrMagic) { - hash = Hash(ZIP_CFILE_NAME(base + cf), ZIP_CFILE_NAMESIZE(base + cf)); - step = 0; - do { - i = (hash + (step * (step + 1)) >> 1) & (m - 1); - ++step; - } while (p[i].hash); - lm = GetLastModifiedZip(base + cf); - p[i].hash = hash; - p[i].lf = ZIP_CFILE_OFFSET(base + cf); - p[i].lastmodified = lm; - p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm); - } else { - WARNF("corrupt zip central directory entry"); - ok = false; - break; - } - } - } else { - WARNF("corrupt zip central directory"); + p = xcalloc(m, sizeof(struct Asset)); + CHECK_EQ(kZipCdirHdrMagic, ZIP_CDIR_MAGIC(zdir)); + for (cf = ZIP_CDIR_OFFSET(zdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) { + CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf)); + hash = Hash(ZIP_CFILE_NAME(zmap + cf), ZIP_CFILE_NAMESIZE(zmap + cf)); + step = 0; + do { + i = (hash + (step * (step + 1)) >> 1) & (m - 1); + ++step; + } while (p[i].hash); + lm = GetLastModifiedZip(zmap + cf); + p[i].hash = hash; + p[i].lf = ZIP_CFILE_OFFSET(zmap + cf); + p[i].lastmodified = lm; + p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm); } - if (ok) { - FreeAssetsIndex(assets.p, assets.n); - assets.p = p; - assets.n = m; - } else { - FreeAssetsIndex(p, m); - } - return ok; + assets.p = p; + assets.n = m; } -static bool OpenZip(const char *path) { +static void OpenZip(const char *path) { int fd; - bool ok; - void *map; struct stat st; - const uint8_t *cdir; - if (zmap) { - LOGIFNEG1(munmap(zmap, zmapsize)); - } - fd = -1; - map = MAP_FAILED; - if ((fd = open(path, O_RDONLY)) != -1 && fstat(fd, &st) != -1 && st.st_size && - (map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != - MAP_FAILED && - (cdir = zipfindcentraldir(map, st.st_size)) && IndexAssets(map, cdir)) { - ok = true; - zmap = map; - zbase = map; - zcdir = cdir; - map = MAP_FAILED; - zmapsize = st.st_size; - } else { - ok = false; - } - if (map != MAP_FAILED) LOGIFNEG1(munmap(map, st.st_size)); - if (fd != -1) LOGIFNEG1(close(fd)); - return ok; + CHECK_NE(-1, (fd = open(path, O_RDONLY))); + CHECK_NE(-1, fstat(fd, &st)); + CHECK((zmapsize = st.st_size)); + CHECK_NE(MAP_FAILED, + (zmap = mmap(NULL, zmapsize, PROT_READ, MAP_SHARED, fd, 0))); + CHECK_NOTNULL((zdir = zipfindcentraldir(zmap, zmapsize))); + close(fd); } static struct Asset *FindAsset(const char *path, size_t pathlen) { @@ -818,8 +793,8 @@ static struct Asset *FindAsset(const char *path, size_t pathlen) { i = (hash + (step * (step + 1)) >> 1) & (assets.n - 1); if (!assets.p[i].hash) return NULL; if (hash == assets.p[i].hash && - pathlen == ZIP_LFILE_NAMESIZE(zbase + assets.p[i].lf) && - memcmp(path, ZIP_LFILE_NAME(zbase + assets.p[i].lf), pathlen) == 0) { + pathlen == ZIP_LFILE_NAMESIZE(zmap + assets.p[i].lf) && + memcmp(path, ZIP_LFILE_NAME(zmap + assets.p[i].lf), pathlen) == 0) { return &assets.p[i]; } } @@ -853,135 +828,140 @@ static void *FreeLater(void *p) { static void CollectGarbage(void) { size_t i; - for (i = 0; i < freelist.n; ++i) { - free(freelist.p[i]); - } + for (i = 0; i < freelist.n; ++i) free(freelist.p[i]); freelist.n = 0; + free(request.params.p); + DestroyHttpRequest(&msg); } -static void EmitUriParamKey(struct UriParser *u) { - uriparams.p = xrealloc(uriparams.p, ++uriparams.n * sizeof(*uriparams.p)); - uriparams.p[uriparams.n - 1].key.p = u->q; - uriparams.p[uriparams.n - 1].key.n = u->p - u->q; +static void EmitParamKey(struct Parser *u, struct Params *h) { + h->p = xrealloc(h->p, ++h->n * sizeof(*h->p)); + h->p[h->n - 1].key.p = u->q; + h->p[h->n - 1].key.n = u->p - u->q; u->q = u->p; } -static void EmitUriParamVal(struct UriParser *u, bool t) { +static void EmitParamVal(struct Parser *u, struct Params *h, bool t) { if (!t) { if (u->p > u->q) { - EmitUriParamKey(u); - uriparams.p[uriparams.n - 1].val.p = NULL; - uriparams.p[uriparams.n - 1].val.n = SIZE_MAX; + EmitParamKey(u, h); + h->p[h->n - 1].val.p = NULL; + h->p[h->n - 1].val.n = SIZE_MAX; } } else { - uriparams.p[uriparams.n - 1].val.p = u->q; - uriparams.p[uriparams.n - 1].val.n = u->p - u->q; + h->p[h->n - 1].val.p = u->q; + h->p[h->n - 1].val.n = u->p - u->q; u->q = u->p; } } -static void ParseUriEscape(struct UriParser *u) { +static void ParseEscape(struct Parser *u) { int a, b; - a = u->i < u->size ? u->data[u->i++] : 0; - b = u->i < u->size ? u->data[u->i++] : 0; + a = u->i < u->size ? u->data[u->i++] & 0xff : 0; + b = u->i < u->size ? u->data[u->i++] & 0xff : 0; *u->p++ = kHexToInt[a] << 4 | kHexToInt[b]; } -static void ParseUriPath(struct UriParser *u) { - if (u->c == '/') { - while (u->i < u->size) { - u->c = u->data[u->i++]; - if (u->c == '#' || u->c == '?') { +static void ParsePath(struct Parser *u, struct Buffer *h) { + while (u->i < u->size) { + u->c = u->data[u->i++] & 0xff; + if (u->c == '#' || u->c == '?') { + break; + } else if (u->c != '%') { + *u->p++ = u->c; + } else { + ParseEscape(u); + } + } + h->p = u->q; + h->n = u->p - u->q; + u->q = u->p; +} + +static void ParseParams(struct Parser *u, struct Params *h, bool isform) { + bool t = false; + while (u->i < u->size) { + switch ((u->c = u->data[u->i++] & 0xff)) { + default: + *u->p++ = u->c; break; - } else if (u->c != '%') { - *u->p++ = u->c; - } else { - ParseUriEscape(u); - } - } - } - uripath.p = u->q; - uripath.n = u->p - u->q; - u->q = u->p; -} - -static void ParseUriParams(struct UriParser *u) { - bool t; - uriparams.n = 0; - uriparams.p = 0; - if (u->c == '?') { - t = false; - while (u->i < u->size) { - switch ((u->c = u->data[u->i++])) { - default: - *u->p++ = u->c; - break; - case '+': - *u->p++ = ' '; - break; - case '%': - ParseUriEscape(u); - break; - case '&': - EmitUriParamVal(u, t); - t = false; - break; - case '=': - if (!t) { - if (u->p > u->q) { - EmitUriParamKey(u); - t = true; - } - } else { - *u->p++ = '='; + case '+': + *u->p++ = isform ? ' ' : u->c; + break; + case '%': + ParseEscape(u); + break; + case '&': + EmitParamVal(u, h, t); + t = false; + break; + case '=': + if (!t) { + if (u->p > u->q) { + EmitParamKey(u, h); + t = true; } - break; - case '#': - goto EndOfParams; - } + } else { + *u->p++ = '='; + } + break; + case '#': + goto EndOfParams; } - EndOfParams: - EmitUriParamVal(u, t); - FreeLater(uriparams.p); } +EndOfParams: + EmitParamVal(u, h, t); } -static void ParseUriFragment(struct UriParser *u) { - if (u->c == '#') { - while (u->i < u->size) { - u->c = u->data[u->i++]; - if (u->c != '%') { - *u->p++ = u->c; - } else { - ParseUriEscape(u); - } +static void ParseFragment(struct Parser *u, struct Buffer *h) { + while (u->i < u->size) { + u->c = u->data[u->i++] & 0xff; + if (u->c != '%') { + *u->p++ = u->c; + } else { + ParseEscape(u); } } - urifragment.p = u->q; - urifragment.n = u->p - u->q; + h->p = u->q; + h->n = u->p - u->q; u->q = u->p; } +static bool IsForbiddenPath(struct Buffer *b) { + return !!memmem(b->p, b->n, "/.", 2); +} + static bool ParseRequestUri(void) { - struct UriParser u; + struct Parser u; u.i = 0; u.c = '/'; - u.data = inbuf.p + req.uri.a; - u.size = req.uri.b - req.uri.a; + u.data = inbuf.p + msg.uri.a; + u.size = msg.uri.b - msg.uri.a; + memset(&request, 0, sizeof(request)); if (!u.size || *u.data != '/') return false; u.q = u.p = FreeLater(xmalloc(u.size)); - ParseUriPath(&u); - ParseUriParams(&u); - ParseUriFragment(&u); - return u.i == u.size; + if (u.c == '/') ParsePath(&u, &request.path); + if (u.c == '?') ParseParams(&u, &request.params, false); + if (u.c == '#') ParseFragment(&u, &request.fragment); + return u.i == u.size && !IsForbiddenPath(&request.path); +} + +static void ParseFormParams(void) { + struct Parser u; + u.i = 0; + u.c = 0; + u.data = inbuf.p + hdrsize; + u.size = msgsize - hdrsize; + u.q = u.p = FreeLater(xmalloc(u.size)); + ParseParams(&u, &request.params, true); } static void *AddRange(char *content, long start, long length) { intptr_t mend, mstart; if (!__builtin_add_overflow((intptr_t)content, start, &mstart) || !__builtin_add_overflow(mstart, length, &mend) || - ((intptr_t)zbase <= mstart && mstart <= (intptr_t)zbase + zmapsize) || - ((intptr_t)zbase <= mend && mend <= (intptr_t)zbase + zmapsize)) { + ((intptr_t)zmap <= mstart && mstart <= (intptr_t)zmap + zmapsize) || + ((intptr_t)zmap <= mend && mend <= (intptr_t)zmap + zmapsize)) { return (void *)mstart; } else { abort(); @@ -991,8 +971,8 @@ static void *AddRange(char *content, long start, long length) { static bool IsConnectionClose(void) { int n; char *p; - p = inbuf.p + req.headers[kHttpConnection].a; - n = req.headers[kHttpConnection].b - req.headers[kHttpConnection].a; + p = inbuf.p + msg.headers[kHttpConnection].a; + n = msg.headers[kHttpConnection].b - msg.headers[kHttpConnection].a; return n == 5 && memcmp(p, "close", 5) == 0; } @@ -1003,7 +983,7 @@ static char *AppendCrlf(char *p) { } static bool MustNotIncludeMessageBody(void) { /* RFC2616 § 4.4 */ - return req.method == kHttpHead || (100 <= statuscode && statuscode <= 199) || + return msg.method == kHttpHead || (100 <= statuscode && statuscode <= 199) || statuscode == 204 || statuscode == 304; } @@ -1043,8 +1023,8 @@ static char *ServeError(int code, const char *reason) { content = FreeLater(xmalloc(reasonlen + 3)); contentlength = reasonlen + 2; stpcpy(stpcpy(content, reason), "\r\n"); - WARNF("%s %s %`'.*s %d %s", clientaddrstr, kHttpMethod[req.method], uripath.n, - uripath.p, code, reason); + WARNF("%s %s %`'.*s %d %s", clientaddrstr, kHttpMethod[msg.method], + request.path.n, request.path.p, code, reason); return p; } @@ -1142,13 +1122,13 @@ static void *Deflate(const void *data, size_t size, size_t *out_size) { static void *LoadAsset(struct Asset *a, size_t *out_size) { size_t size; uint8_t *data; - size = ZIP_LFILE_UNCOMPRESSEDSIZE(zbase + a->lf); + size = ZIP_LFILE_UNCOMPRESSEDSIZE(zmap + a->lf); data = xmalloc(size + 1); - if (ZIP_LFILE_COMPRESSIONMETHOD(zbase + a->lf) == kZipCompressionDeflate) { - CHECK(Inflate(data, size, ZIP_LFILE_CONTENT(zbase + a->lf), - ZIP_LFILE_COMPRESSEDSIZE(zbase + a->lf))); + if (ZIP_LFILE_COMPRESSIONMETHOD(zmap + a->lf) == kZipCompressionDeflate) { + CHECK(Inflate(data, size, ZIP_LFILE_CONTENT(zmap + a->lf), + ZIP_LFILE_COMPRESSEDSIZE(zmap + a->lf))); } else { - memcpy(data, ZIP_LFILE_CONTENT(zbase + a->lf), size); + memcpy(data, ZIP_LFILE_CONTENT(zmap + a->lf), size); } data[size] = '\0'; if (out_size) *out_size = size; @@ -1172,32 +1152,32 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { char *p; long rangestart, rangelength; if (IsNotModified(a)) { - DEBUGF("%s %s %`'.*s not modified", clientaddrstr, kHttpMethod[req.method], + DEBUGF("%s %s %`'.*s not modified", clientaddrstr, kHttpMethod[msg.method], pathlen, path); p = SetStatus(304, "Not Modified"); } else { - content = ZIP_LFILE_CONTENT(zbase + a->lf); - contentlength = ZIP_LFILE_COMPRESSEDSIZE(zbase + a->lf); + content = ZIP_LFILE_CONTENT(zmap + a->lf); + contentlength = ZIP_LFILE_COMPRESSEDSIZE(zmap + a->lf); if (IsCompressed(a)) { if (ClientAcceptsGzip()) { gzipped = true; - memcpy(gzip_footer + 0, zbase + a->lf + kZipLfileOffsetCrc32, 4); - memcpy(gzip_footer + 4, zbase + a->lf + kZipLfileOffsetUncompressedsize, + memcpy(gzip_footer + 0, zmap + a->lf + kZipLfileOffsetCrc32, 4); + memcpy(gzip_footer + 4, zmap + a->lf + kZipLfileOffsetUncompressedsize, 4); p = SetStatus(200, "OK"); p = AppendHeader(p, "Content-Encoding", "gzip"); } else { CHECK(Inflate( (content = - FreeLater(xmalloc(ZIP_LFILE_UNCOMPRESSEDSIZE(zbase + a->lf)))), - (contentlength = ZIP_LFILE_UNCOMPRESSEDSIZE(zbase + a->lf)), - ZIP_LFILE_CONTENT(zbase + a->lf), - ZIP_LFILE_COMPRESSEDSIZE(zbase + a->lf))); + FreeLater(xmalloc(ZIP_LFILE_UNCOMPRESSEDSIZE(zmap + a->lf)))), + (contentlength = ZIP_LFILE_UNCOMPRESSEDSIZE(zmap + a->lf)), + ZIP_LFILE_CONTENT(zmap + a->lf), + ZIP_LFILE_COMPRESSEDSIZE(zmap + a->lf))); p = SetStatus(200, "OK"); } } else if (httpversion >= 101 && HasHeader(kHttpRange)) { - if (ParseHttpRange(inbuf.p + req.headers[kHttpRange].a, - req.headers[kHttpRange].b - req.headers[kHttpRange].a, + if (ParseHttpRange(inbuf.p + msg.headers[kHttpRange].a, + msg.headers[kHttpRange].b - msg.headers[kHttpRange].a, contentlength, &rangestart, &rangelength)) { p = SetStatus(206, "Partial Content"); p = AppendContentRange(p, rangestart, rangelength, contentlength); @@ -1205,9 +1185,9 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { contentlength = rangelength; } else { WARNF("%s %s %`'.*s bad range %`'.*s", clientaddrstr, - kHttpMethod[req.method], pathlen, path, - req.headers[kHttpRange].b - req.headers[kHttpRange].a, - inbuf.p + req.headers[kHttpRange].a); + kHttpMethod[msg.method], pathlen, path, + msg.headers[kHttpRange].b - msg.headers[kHttpRange].a, + inbuf.p + msg.headers[kHttpRange].a); p = SetStatus(416, "Range Not Satisfiable"); p = AppendContentRange(p, rangestart, rangelength, contentlength); content = ""; @@ -1253,6 +1233,22 @@ static bool IsValidFieldContent(const char *s, size_t n) { return true; } +static int LuaIsValidFieldName(lua_State *L) { + size_t size; + const char *data; + data = luaL_checklstring(L, 1, &size); + lua_pushboolean(L, IsValidFieldName(data, size)); + return 1; +} + +static int LuaIsValidFieldContent(lua_State *L) { + size_t size; + const char *data; + data = luaL_checklstring(L, 1, &size); + lua_pushboolean(L, IsValidFieldContent(data, size)); + return 1; +} + static int LuaServeAsset(lua_State *L) { size_t pathlen; struct Asset *a; @@ -1265,7 +1261,7 @@ static int LuaServeAsset(lua_State *L) { return 0; } -static int LuaServeError(lua_State *L) { +static int LuaRespond(lua_State *L, char *respond(int, const char *)) { int code; size_t reasonlen; const char *reason; @@ -1281,10 +1277,18 @@ static int LuaServeError(lua_State *L) { return luaL_argerror(L, 2, "invalid"); } } - luaheaderp = ServeError(code, reason); + luaheaderp = respond(code, reason); return 0; } +static int LuaSetStatus(lua_State *L) { + return LuaRespond(L, SetStatus); +} + +static int LuaServeError(lua_State *L) { + return LuaRespond(L, ServeError); +} + static int LuaLoadAsset(lua_State *L) { char *data; struct Asset *a; @@ -1300,26 +1304,6 @@ static int LuaLoadAsset(lua_State *L) { return 1; } -static int LuaSetStatus(lua_State *L) { - int code; - size_t reasonlen; - const char *reason; - code = luaL_checkinteger(L, 1); - if (!(100 <= code && code <= 999)) { - return luaL_argerror(L, 1, "bad status code"); - } - if (lua_isnoneornil(L, 2)) { - reason = GetHttpReason(code); - } else { - reason = lua_tolstring(L, 2, &reasonlen); - if (reasonlen > 128 || !IsValidFieldContent(reason, reasonlen)) { - return luaL_argerror(L, 2, "invalid"); - } - } - luaheaderp = SetStatus(code, reason); - return 0; -} - static int LuaGetDate(lua_State *L) { lua_pushinteger(L, nowish); return 1; @@ -1331,23 +1315,22 @@ static int LuaGetVersion(lua_State *L) { } static int LuaGetMethod(lua_State *L) { - checkedmethod = true; - lua_pushstring(L, kHttpMethod[req.method]); + lua_pushstring(L, kHttpMethod[msg.method]); return 1; } static int LuaGetPath(lua_State *L) { - lua_pushlstring(L, uripath.p, uripath.n); + lua_pushlstring(L, request.path.p, request.path.n); return 1; } static int LuaGetFragment(lua_State *L) { - lua_pushlstring(L, urifragment.p, urifragment.n); + lua_pushlstring(L, request.fragment.p, request.fragment.n); return 1; } static int LuaGetUri(lua_State *L) { - lua_pushlstring(L, inbuf.p + req.uri.a, req.uri.b - req.uri.a); + lua_pushlstring(L, inbuf.p + msg.uri.a, msg.uri.b - msg.uri.a); return 1; } @@ -1379,23 +1362,53 @@ static int LuaGetServerAddr(lua_State *L) { return 1; } +static int LuaGetPayload(lua_State *L) { + lua_pushlstring(L, inbuf.p + hdrsize, msgsize - hdrsize); + return 1; +} + static int LuaGetHeader(lua_State *L) { int h; - size_t keylen, vallen; const char *key, *val; + size_t i, keylen, vallen; key = luaL_checklstring(L, 1, &keylen); if ((h = GetHttpHeader(key, keylen)) != -1) { - val = inbuf.p + req.headers[h].a; - vallen = req.headers[h].b - req.headers[h].a; - if (vallen && IsValidFieldContent(val, vallen)) { - lua_pushlstring(L, val, vallen); - } else { - lua_pushnil(L); - } + val = inbuf.p + msg.headers[h].a; + vallen = msg.headers[h].b - msg.headers[h].a; + lua_pushlstring(L, val, vallen); return 1; - } else { - return luaL_argerror(L, 1, "unsupported"); } + for (i = 0; i < msg.xheaders.n; ++i) { + if (!CompareSlicesCase(key, keylen, inbuf.p + msg.xheaders.p[i].k.a, + msg.xheaders.p[i].k.b - msg.xheaders.p[i].k.a)) { + lua_pushlstring(L, inbuf.p + msg.xheaders.p[i].v.a, + msg.xheaders.p[i].v.b - msg.xheaders.p[i].v.a); + return 1; + } + } + lua_pushstring(L, ""); + return 1; +} + +static int LuaGetHeaders(lua_State *L) { + size_t i; + lua_newtable(L); + for (i = 0; i < kHttpHeadersMax; ++i) { + if (msg.headers[i].b - msg.headers[i].a) { + lua_pushlstring(L, inbuf.p + msg.headers[i].a, + msg.headers[i].b - msg.headers[i].a); + lua_setfield(L, -2, GetHttpHeaderName(i)); + } + } + for (i = 0; i < msg.xheaders.n; ++i) { + lua_pushlstring(L, inbuf.p + msg.xheaders.p[i].v.a, + msg.xheaders.p[i].v.b - msg.xheaders.p[i].v.a); + lua_setfield( + L, -2, + FreeLater(strndup(inbuf.p + msg.xheaders.p[i].k.a, + msg.xheaders.p[i].k.b - msg.xheaders.p[i].k.a))); + } + return 1; } static int LuaSetHeader(lua_State *L) { @@ -1453,9 +1466,9 @@ static int LuaHasParam(lua_State *L) { const char *key; size_t i, keylen; key = luaL_checklstring(L, 1, &keylen); - for (i = 0; i < uriparams.n; ++i) { - if (uriparams.p[i].key.n == keylen && - !memcmp(uriparams.p[i].key.p, key, keylen)) { + for (i = 0; i < request.params.n; ++i) { + if (request.params.p[i].key.n == keylen && + !memcmp(request.params.p[i].key.p, key, keylen)) { lua_pushboolean(L, true); return 1; } @@ -1468,11 +1481,11 @@ static int LuaGetParam(lua_State *L) { const char *key; size_t i, keylen; key = luaL_checklstring(L, 1, &keylen); - for (i = 0; i < uriparams.n; ++i) { - if (uriparams.p[i].key.n == keylen && - !memcmp(uriparams.p[i].key.p, key, keylen)) { - if (uriparams.p[i].val.n == SIZE_MAX) break; - lua_pushlstring(L, uriparams.p[i].val.p, uriparams.p[i].val.n); + for (i = 0; i < request.params.n; ++i) { + if (request.params.p[i].key.n == keylen && + !memcmp(request.params.p[i].key.p, key, keylen)) { + if (request.params.p[i].val.n == SIZE_MAX) break; + lua_pushlstring(L, request.params.p[i].val.p, request.params.p[i].val.n); return 1; } } @@ -1483,12 +1496,12 @@ static int LuaGetParam(lua_State *L) { static int LuaGetParams(lua_State *L) { size_t i; lua_newtable(L); - for (i = 0; i < uriparams.n; ++i) { + for (i = 0; i < request.params.n; ++i) { lua_newtable(L); - lua_pushlstring(L, uriparams.p[i].key.p, uriparams.p[i].key.n); + lua_pushlstring(L, request.params.p[i].key.p, request.params.p[i].key.n); lua_seti(L, -2, 1); - if (uriparams.p[i].val.n != SIZE_MAX) { - lua_pushlstring(L, uriparams.p[i].val.p, uriparams.p[i].val.n); + if (request.params.p[i].val.n != SIZE_MAX) { + lua_pushlstring(L, request.params.p[i].val.p, request.params.p[i].val.n); lua_seti(L, -2, 2); } lua_seti(L, -2, i + 1); @@ -1543,6 +1556,31 @@ static int LuaEscapeLiteral(lua_State *L) { return LuaEscaper(L, EscapeJsStringLiteral); } +static int LuaPopcnt(lua_State *L) { + lua_pushinteger(L, popcnt(luaL_checkinteger(L, 1))); + return 1; +} + +static int LuaBsr(lua_State *L) { + long x; + if ((x = luaL_checkinteger(L, 1))) { + lua_pushinteger(L, bsr(x)); + return 1; + } else { + return luaL_argerror(L, 1, "zero"); + } +} + +static int LuaBsf(lua_State *L) { + long x; + if ((x = luaL_checkinteger(L, 1))) { + lua_pushinteger(L, bsf(x)); + return 1; + } else { + return luaL_argerror(L, 1, "zero"); + } +} + static void LuaRun(const char *path) { struct Asset *a; const char *code; @@ -1558,34 +1596,51 @@ static void LuaRun(const char *path) { } static const luaL_Reg kLuaFuncs[] = { - {"EscapeFragment", LuaEscapeFragment}, // - {"EscapeHtml", LuaEscapeHtml}, // - {"EscapeLiteral", LuaEscapeLiteral}, // - {"EscapeParam", LuaEscapeParam}, // - {"EscapePath", LuaEscapePath}, // - {"EscapeSegment", LuaEscapeSegment}, // - {"FormatDate", LuaFormatDate}, // - {"GetClientAddr", LuaGetClientAddr}, // - {"GetDate", LuaGetDate}, // - {"GetFragment", LuaGetFragment}, // - {"GetHeader", LuaGetHeader}, // - {"GetMethod", LuaGetMethod}, // - {"GetParam", LuaGetParam}, // - {"GetParams", LuaGetParams}, // - {"GetPath", LuaGetPath}, // - {"GetServerAddr", LuaGetServerAddr}, // - {"GetUri", LuaGetUri}, // - {"GetVersion", LuaGetVersion}, // - {"HasParam", LuaHasParam}, // - {"LoadAsset", LuaLoadAsset}, // - {"ParseDate", LuaParseDate}, // - {"ServeAsset", LuaServeAsset}, // - {"ServeError", LuaServeError}, // - {"SetHeader", LuaSetHeader}, // - {"SetStatus", LuaSetStatus}, // - {"Write", LuaWrite}, // + {"EscapeFragment", LuaEscapeFragment}, // + {"EscapeHtml", LuaEscapeHtml}, // + {"EscapeLiteral", LuaEscapeLiteral}, // + {"EscapeParam", LuaEscapeParam}, // + {"EscapePath", LuaEscapePath}, // + {"EscapeSegment", LuaEscapeSegment}, // + {"FormatDate", LuaFormatDate}, // + {"GetClientAddr", LuaGetClientAddr}, // + {"GetDate", LuaGetDate}, // + {"GetFragment", LuaGetFragment}, // + {"GetHeader", LuaGetHeader}, // + {"GetHeaders", LuaGetHeaders}, // + {"GetMethod", LuaGetMethod}, // + {"GetParam", LuaGetParam}, // + {"GetParams", LuaGetParams}, // + {"GetPath", LuaGetPath}, // + {"GetPayload", LuaGetPayload}, // + {"GetServerAddr", LuaGetServerAddr}, // + {"GetUri", LuaGetUri}, // + {"GetVersion", LuaGetVersion}, // + {"HasParam", LuaHasParam}, // + {"IsValidFieldContent", LuaIsValidFieldContent}, // + {"IsValidFieldName", LuaIsValidFieldName}, // + {"LoadAsset", LuaLoadAsset}, // + {"ParseDate", LuaParseDate}, // + {"ServeAsset", LuaServeAsset}, // + {"ServeError", LuaServeError}, // + {"SetHeader", LuaSetHeader}, // + {"SetStatus", LuaSetStatus}, // + {"Write", LuaWrite}, // + {"bsf", LuaBsf}, // + {"bsr", LuaBsr}, // + {"popcnt", LuaPopcnt}, // }; +static void LuaSetArgv(void) { + size_t i; + lua_newtable(L); + for (i = optind; i < __argc; ++i) { + lua_pushstring(L, __argv[i]); + lua_seti(L, -2, i - optind + 1); + } + lua_setglobal(L, "argv"); +} + static void LuaInit(void) { size_t i; L = luaL_newstate(); @@ -1594,6 +1649,7 @@ static void LuaInit(void) { lua_pushcfunction(L, kLuaFuncs[i].func); lua_setglobal(L, kLuaFuncs[i].name); } + LuaSetArgv(); LuaRun("/tool/net/.init.lua"); } @@ -1605,25 +1661,23 @@ static char *ServeLua(struct Asset *a) { char *p; outbuf.n = 0; luaheaderp = NULL; - checkedmethod = false; if (luaL_dostring(L, FreeLater(LoadAsset(a, NULL))) == LUA_OK) { - if (!checkedmethod && req.method != kHttpGet && req.method != kHttpHead) { - return ServeError(405, "Method Not Allowed"); - } if (!(p = luaheaderp)) { p = SetStatus(200, "OK"); p = AppendContentType(p, "text/html"); } - if (istext && outbuf.n >= 100 && ClientAcceptsGzip()) { - gzipped = true; - p = AppendHeader(p, "Content-Encoding", "gzip"); - p = AppendHeader(p, "Vary", "Accept-Encoding"); - WRITE32LE(gzip_footer + 0, crc32_z(0, outbuf.p, outbuf.n)); - WRITE32LE(gzip_footer + 4, outbuf.n); - content = FreeLater(Deflate(outbuf.p, outbuf.n, &contentlength)); - } else { - content = outbuf.p; - contentlength = outbuf.n; + if (outbuf.n) { + if (istext && outbuf.n >= 100 && ClientAcceptsGzip()) { + gzipped = true; + p = AppendHeader(p, "Content-Encoding", "gzip"); + p = AppendHeader(p, "Vary", "Accept-Encoding"); + WRITE32LE(gzip_footer + 0, crc32_z(0, outbuf.p, outbuf.n)); + WRITE32LE(gzip_footer + 4, outbuf.n); + content = FreeLater(Deflate(outbuf.p, outbuf.n, &contentlength)); + } else { + content = outbuf.p; + contentlength = outbuf.n; + } } return p; } else { @@ -1635,9 +1689,9 @@ static char *ServeLua(struct Asset *a) { } static bool IsLua(struct Asset *a) { - return ZIP_LFILE_NAMESIZE(zbase + a->lf) >= 4 && - !memcmp(ZIP_LFILE_NAME(zbase + a->lf) + - ZIP_LFILE_NAMESIZE(zbase + a->lf) - 4, + return ZIP_LFILE_NAMESIZE(zmap + a->lf) >= 4 && + !memcmp(ZIP_LFILE_NAME(zmap + a->lf) + + ZIP_LFILE_NAMESIZE(zmap + a->lf) - 4, ".lua", 4); } @@ -1645,7 +1699,7 @@ static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) { char *p; if (IsLua(a)) { p = ServeLua(a); - } else if (req.method == kHttpGet || req.method == kHttpHead) { + } else if (msg.method == kHttpGet || msg.method == kHttpHead) { p = ServeAsset(a, path, pathlen); p = AppendHeader(p, "X-Content-Type-Options", "nosniff"); } else { @@ -1659,36 +1713,48 @@ static char *HandleRedirect(struct Redirect *r) { struct Asset *a; if ((a = LocateAsset(r->location, strlen(r->location)))) { DEBUGF("%s %s %`'.*s rewritten %`'s", clientaddrstr, - kHttpMethod[req.method], uripath.n, uripath.p, r->location); + kHttpMethod[msg.method], request.path.n, request.path.p, + r->location); p = HandleAsset(a, r->location, strlen(r->location)); } else if (httpversion == 9) { p = ServeError(505, "HTTP Version Not Supported"); } else { DEBUGF("%s %s %`'.*s redirecting %`'s", clientaddrstr, - kHttpMethod[req.method], uripath.n, uripath.p, r->location); + kHttpMethod[msg.method], request.path.n, request.path.p, + r->location); p = SetStatus(307, "Temporary Redirect"); p = AppendHeader(p, "Location", r->location); } return p; } -static ssize_t SendString(const char *s) { +static void LogMessage(const char *d, const char *s, size_t n) { + if (!logmessages) return; + while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; + LOGF("%s %s %,ld byte message\n%.*s", clientaddrstr, d, n, n, s); +} + +static ssize_t SendMessageString(const char *s) { + size_t n; ssize_t rc; + n = strlen(s); + LogMessage("sending", s, n); + return 0; for (;;) { - if ((rc = write(client, s, strlen(s))) != -1 || errno != EINTR) { + if ((rc = write(client, s, n)) != -1 || errno != EINTR) { return rc; } } } static ssize_t SendContinue(void) { - return SendString("\ + return SendMessageString("\ HTTP/1.1 100 Continue\r\n\ \r\n"); } static ssize_t SendTimeout(void) { - return SendString("\ + return SendMessageString("\ HTTP/1.1 408 Request Timeout\r\n\ Connection: close\r\n\ Content-Length: 0\r\n\ @@ -1696,7 +1762,7 @@ Content-Length: 0\r\n\ } static ssize_t SendServiceUnavailable(void) { - return SendString("\ + return SendMessageString("\ HTTP/1.1 503 Service Unavailable\r\n\ Connection: close\r\n\ Content-Length: 0\r\n\ @@ -1705,7 +1771,7 @@ Content-Length: 0\r\n\ static void LogClose(const char *reason) { if (amtread) { - WARNF("%s %s with %,ld bytes unprocessed", clientaddrstr, reason, amtread); + WARNF("%s %s with %,ld bytes unprocessed", clientaddrstr, reason); } else { DEBUGF("%s %s", clientaddrstr, reason); } @@ -1719,42 +1785,41 @@ static const char *DescribeClose(void) { return "destroyed"; } -static char *HandleMessage(size_t hdrlen) { +static char *HandleMessage(void) { long r; ssize_t cl, rc; struct Asset *a; size_t got, need; httpversion = - ParseHttpVersion(inbuf.p + req.version.a, req.version.b - req.version.a); + ParseHttpVersion(inbuf.p + msg.version.a, msg.version.b - msg.version.a); if (httpversion > 101) { return ServeError(505, "HTTP Version Not Supported"); } - if (req.method > kHttpPost || + if (msg.method > kHttpPost || (HasHeader(kHttpTransferEncoding) && - CompareHeaderValue(kHttpTransferEncoding, "identity"))) { + !HeaderEquals(kHttpTransferEncoding, "identity"))) { return ServeError(501, "Not Implemented"); } - if (HasHeader(kHttpExpect) && - CompareHeaderValue(kHttpExpect, "100-continue")) { + if (HasHeader(kHttpExpect) && !HeaderEquals(kHttpExpect, "100-continue")) { return ServeError(417, "Expectation Failed"); } - if ((cl = ParseContentLength(inbuf.p + req.headers[kHttpContentLength].a, - req.headers[kHttpContentLength].b - - req.headers[kHttpContentLength].a)) == -1) { + if ((cl = ParseContentLength(inbuf.p + msg.headers[kHttpContentLength].a, + msg.headers[kHttpContentLength].b - + msg.headers[kHttpContentLength].a)) == -1) { if (HasHeader(kHttpContentLength)) { return ServeError(400, "Bad Request"); - } else if (req.method != kHttpGet && req.method != kHttpHead && - req.method != kHttpOptions) { + } else if (msg.method != kHttpGet && msg.method != kHttpHead && + msg.method != kHttpOptions) { return ServeError(411, "Length Required"); } else { cl = 0; } } - need = hdrlen + cl; /* synchronization is possible */ + need = hdrsize + cl; /* synchronization is possible */ if (need > inbuf.n) { return ServeError(413, "Payload Too Large"); } - if (!CompareHeaderValue(kHttpExpect, "100-continue") && httpversion >= 101) { + if (HeaderEquals(kHttpExpect, "100-continue") && httpversion >= 101) { SendContinue(); } while (amtread < need) { @@ -1786,18 +1851,21 @@ static char *HandleMessage(size_t hdrlen) { connectionclose = true; } if (!ParseRequestUri()) { - WARNF("%s could not parse request uri %`'.*s", clientaddrstr, - req.uri.b - req.uri.a, inbuf.p + req.uri.a); + WARNF("%s could not parse request request %`'.*s", clientaddrstr, + msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a); connectionclose = true; return ServeError(400, "Bad Request"); } + if (HeaderEquals(kHttpContentType, "application/x-www-form-urlencoded")) { + ParseFormParams(); + } VERBOSEF("%s %s %`'.*s referrer %`'.*s", clientaddrstr, - kHttpMethod[req.method], uripath.n, uripath.p, - req.headers[kHttpReferer].b - req.headers[kHttpReferer].a, - inbuf.p + req.headers[kHttpReferer].a); - if ((a = LocateAsset(uripath.p, uripath.n))) { - return HandleAsset(a, uripath.p, uripath.n); - } else if ((r = FindRedirect(uripath.p, uripath.n)) != -1) { + kHttpMethod[msg.method], request.path.n, request.path.p, + msg.headers[kHttpReferer].b - msg.headers[kHttpReferer].a, + inbuf.p + msg.headers[kHttpReferer].a); + if ((a = LocateAsset(request.path.p, request.path.n))) { + return HandleAsset(a, request.path.p, request.path.n); + } else if ((r = FindRedirect(request.path.p, request.path.n)) != -1) { return HandleRedirect(redirects.p + r); } else { return ServeError(404, "Not Found"); @@ -1810,10 +1878,11 @@ static bool HandleRequest(void) { int iovlen; struct iovec iov[4]; long actualcontentlength; - msgsize = 0; - if ((rc = ParseHttpRequest(&req, inbuf.p, amtread)) != -1) { + if ((rc = ParseHttpRequest(&msg, inbuf.p, amtread)) != -1) { if (!rc) return false; - p = HandleMessage(rc); + hdrsize = rc; + LogMessage("received", inbuf.p, hdrsize); + p = HandleMessage(); } else { httpversion = 101; p = ServeError(400, "Bad Request"); @@ -1844,10 +1913,7 @@ static bool HandleRequest(void) { p = AppendContentLength(p, actualcontentlength); p = AppendCrlf(p); CHECK_LE(p - hdrbuf.p, hdrbuf.n); - if (logmessages) { - LOGF("%s sending %,d byte message\n%.*s", clientaddrstr, p - hdrbuf.p, - p - hdrbuf.p - 4, hdrbuf.p); - } + LogMessage("sending", hdrbuf.p, p - hdrbuf.p); iov[0].iov_base = hdrbuf.p; iov[0].iov_len = p - hdrbuf.p; iovlen = 1; @@ -1878,11 +1944,12 @@ static bool HandleRequest(void) { static void InitRequest(void) { frags = 0; + msgsize = 0; content = NULL; gzipped = false; branded = false; contentlength = 0; - InitHttpRequest(&req); + InitHttpRequest(&msg); } static void ProcessRequests(void) { @@ -1893,7 +1960,7 @@ static void ProcessRequests(void) { InitRequest(); startread = nowl(); for (;;) { - if (!req.i && amtread && HandleRequest()) break; + if (!msg.i && amtread && HandleRequest()) break; if ((rc = read(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) { startrequest = now = nowl(); if (now - nowish > 1) UpdateCurrentDate(now); @@ -1941,7 +2008,8 @@ static void ProcessRequests(void) { static void ProcessConnection(void) { int pid; clientaddrsize = sizeof(clientaddr); - if ((client = accept(server, &clientaddr, &clientaddrsize)) != -1) { + if ((client = accept4(server, &clientaddr, &clientaddrsize, SOCK_CLOEXEC)) != + -1) { startconnection = nowl(); if (uniprocess) { pid = -1; @@ -1986,7 +2054,8 @@ void RedBean(void) { if (IsWindows()) uniprocess = true; if (daemonize) Daemonize(); gmtoff = GetGmtOffset(); - CHECK(OpenZip((const char *)getauxval(AT_EXECFN))); + OpenZip((const char *)getauxval(AT_EXECFN)); + IndexAssets(); xsigaction(SIGINT, OnInt, 0, 0, 0); xsigaction(SIGHUP, OnHup, 0, 0, 0); xsigaction(SIGTERM, OnTerm, 0, 0, 0); @@ -1998,7 +2067,8 @@ void RedBean(void) { if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) { heartless = true; } - CHECK_NE(-1, (server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); + CHECK_NE(-1, + (server = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP))); TuneServerSocket(); if (bind(server, &serveraddr, sizeof(serveraddr)) == -1) { if (errno == EADDRINUSE) { diff --git a/tool/net/redbean.html b/tool/net/redbean.html index b25f922e..c7f6fe5b 100644 --- a/tool/net/redbean.html +++ b/tool/net/redbean.html @@ -1,8 +1,8 @@ redbean - - + +

redbean
diff --git a/tool/net/redbean.lua b/tool/net/redbean.lua index 91b8ce75..df8b62d2 100644 --- a/tool/net/redbean.lua +++ b/tool/net/redbean.lua @@ -1,8 +1,7 @@ -- redbean lua server page demo local function main() - -- This check is optional. - -- We do this by default if you don't call GetMethod(). + -- This check is pedantic but might be good to have. if GetMethod() ~= 'GET' and GetMethod() ~= 'HEAD' then ServeError(405) SetHeader('Allow', 'GET, HEAD') @@ -40,9 +39,35 @@ local function main() Write('

\n') Write('none
\n') Write('ProTip: Try clicking here!\n') end + + -- Access redbean command line arguments. + -- These are the ones that come *after* the redbean server arguments. + Write('

command line arguments

\n') + if #argv > 0 then + Write('\n') + else + Write('

none\n') + end + + Write('

post request html form demo

\n') + Write('
\n') + Write('\n') + Write('\n') + Write('
\n') + Write('\n') + Write('\n') + Write('
\n') + Write('\n') + Write('
\n') end main()