mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Add chunked transfer decoding to redbean
This commit is contained in:
parent
8d5f60a9cd
commit
58fb2fb3d3
13 changed files with 693 additions and 292 deletions
|
@ -81,7 +81,6 @@ TE, kHttpTe
|
|||
Trailer, kHttpTrailer
|
||||
Transfer-Encoding, kHttpTransferEncoding
|
||||
Upgrade, kHttpUpgrade
|
||||
Uri, kHttpUri
|
||||
Warning, kHttpWarning
|
||||
WWW-Authenticate, kHttpWwwAuthenticate
|
||||
Via, kHttpVia
|
||||
|
@ -93,3 +92,9 @@ Referrer-Policy, kHttpReferrerPolicy
|
|||
X-XSS-Protection, kHttpXXssProtection
|
||||
Accept-Ranges, kHttpAcceptRanges
|
||||
Set-Cookie, kHttpSetCookie
|
||||
Sec-CH-UA, kHttpSecChUa
|
||||
Sec-CH-UA-Mobile, kHttpSecChUaMobile
|
||||
Sec-Fetch-Site, kHttpSecFetchSite
|
||||
Sec-Fetch-Mode, kHttpSecFetchMode
|
||||
Sec-Fetch-User, kHttpSecFetchUser
|
||||
Sec-Fetch-Dest, kHttpSecFetchDest
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* ANSI-C code produced by gperf version 3.1 */
|
||||
/* Command-line: gperf gethttpheader.gperf */
|
||||
/* Computed positions: -k'1,10,22,$' */
|
||||
/* Computed positions: -k'5,10-11,22,$' */
|
||||
/* clang-format off */
|
||||
|
||||
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
|
||||
|
@ -38,12 +38,12 @@
|
|||
#line 12 "gethttpheader.gperf"
|
||||
struct thatispacked HttpHeaderSlot { char *name; char code; };
|
||||
|
||||
#define TOTAL_KEYWORDS 82
|
||||
#define TOTAL_KEYWORDS 87
|
||||
#define MIN_WORD_LENGTH 2
|
||||
#define MAX_WORD_LENGTH 32
|
||||
#define MIN_HASH_VALUE 6
|
||||
#define MAX_HASH_VALUE 133
|
||||
/* maximum key range = 128, duplicates = 0 */
|
||||
#define MIN_HASH_VALUE 3
|
||||
#define MAX_HASH_VALUE 198
|
||||
/* maximum key range = 196, duplicates = 0 */
|
||||
|
||||
#ifndef GPERF_DOWNCASE
|
||||
#define GPERF_DOWNCASE 1
|
||||
|
@ -102,32 +102,32 @@ hash (register const char *str, register size_t len)
|
|||
{
|
||||
static const unsigned char asso_values[] =
|
||||
{
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 20, 134, 134, 134, 134,
|
||||
134, 134, 134, 0, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 0, 134, 20, 95, 5,
|
||||
10, 70, 15, 30, 134, 5, 15, 20, 0, 75,
|
||||
40, 134, 35, 20, 0, 50, 25, 30, 0, 30,
|
||||
134, 134, 134, 134, 134, 134, 134, 0, 134, 20,
|
||||
95, 5, 10, 70, 15, 30, 134, 5, 15, 20,
|
||||
0, 75, 40, 134, 35, 20, 0, 50, 25, 30,
|
||||
0, 30, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||
134, 134, 134, 134, 134, 134
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 55, 199, 199, 199, 199,
|
||||
199, 199, 199, 20, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 15, 199, 30, 35, 10,
|
||||
25, 15, 0, 70, 199, 55, 25, 40, 0, 45,
|
||||
15, 20, 50, 0, 0, 5, 199, 0, 199, 20,
|
||||
199, 199, 199, 199, 199, 199, 199, 15, 199, 30,
|
||||
35, 10, 25, 15, 0, 70, 199, 55, 25, 40,
|
||||
0, 45, 15, 20, 50, 0, 0, 5, 199, 0,
|
||||
199, 20, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
|
||||
199, 199, 199, 199, 199, 199
|
||||
};
|
||||
register unsigned int hval = len;
|
||||
|
||||
|
@ -147,6 +147,8 @@ hash (register const char *str, register size_t len)
|
|||
case 13:
|
||||
case 12:
|
||||
case 11:
|
||||
hval += asso_values[(unsigned char)str[10]];
|
||||
/*FALLTHROUGH*/
|
||||
case 10:
|
||||
hval += asso_values[(unsigned char)str[9]];
|
||||
/*FALLTHROUGH*/
|
||||
|
@ -155,11 +157,11 @@ hash (register const char *str, register size_t len)
|
|||
case 7:
|
||||
case 6:
|
||||
case 5:
|
||||
hval += asso_values[(unsigned char)str[4]];
|
||||
/*FALLTHROUGH*/
|
||||
case 4:
|
||||
case 3:
|
||||
case 2:
|
||||
case 1:
|
||||
hval += asso_values[(unsigned char)str[0]];
|
||||
break;
|
||||
}
|
||||
return hval + asso_values[(unsigned char)str[len - 1]];
|
||||
|
@ -170,193 +172,220 @@ LookupHttpHeader (register const char *str, register size_t len)
|
|||
{
|
||||
static const struct thatispacked HttpHeaderSlot wordlist[] =
|
||||
{
|
||||
{""}, {""}, {""}, {""}, {""}, {""},
|
||||
#line 17 "gethttpheader.gperf"
|
||||
{"Accept", kHttpAccept},
|
||||
{""}, {""}, {""},
|
||||
#line 27 "gethttpheader.gperf"
|
||||
{"DNT", kHttpDnt},
|
||||
#line 14 "gethttpheader.gperf"
|
||||
{"Host", kHttpHost},
|
||||
#line 46 "gethttpheader.gperf"
|
||||
{"Allow", kHttpAllow},
|
||||
{""}, {""},
|
||||
#line 73 "gethttpheader.gperf"
|
||||
{"Location", kHttpLocation},
|
||||
{""}, {""}, {""},
|
||||
#line 80 "gethttpheader.gperf"
|
||||
{"TE", kHttpTe},
|
||||
#line 58 "gethttpheader.gperf"
|
||||
{"Age", kHttpAge},
|
||||
{""}, {""},
|
||||
#line 66 "gethttpheader.gperf"
|
||||
{"Expect", kHttpExpect},
|
||||
{""},
|
||||
#line 59 "gethttpheader.gperf"
|
||||
{"Authorization", kHttpAuthorization},
|
||||
#line 48 "gethttpheader.gperf"
|
||||
{"Accept-Charset", kHttpAcceptCharset},
|
||||
{""},
|
||||
#line 93 "gethttpheader.gperf"
|
||||
{"X-XSS-Protection", kHttpXXssProtection},
|
||||
#line 34 "gethttpheader.gperf"
|
||||
{"X-CSRF-Token", kHttpXCsrfToken},
|
||||
{""},
|
||||
#line 14 "gethttpheader.gperf"
|
||||
{"Host", kHttpHost},
|
||||
#line 18 "gethttpheader.gperf"
|
||||
{"Accept-Language", kHttpAcceptLanguage},
|
||||
#line 32 "gethttpheader.gperf"
|
||||
{"X-Forwarded-Host", kHttpXForwardedHost},
|
||||
{""},
|
||||
#line 73 "gethttpheader.gperf"
|
||||
{"Location", kHttpLocation},
|
||||
#line 72 "gethttpheader.gperf"
|
||||
{"Link", kHttpLink},
|
||||
#line 71 "gethttpheader.gperf"
|
||||
{"Keep-Alive", kHttpKeepAlive},
|
||||
#line 53 "gethttpheader.gperf"
|
||||
{"Access-Control-MaxAge", kHttpAccessControlMaxAge},
|
||||
#line 91 "gethttpheader.gperf"
|
||||
{"Alt-Svc", kHttpAltSvc},
|
||||
#line 87 "gethttpheader.gperf"
|
||||
#line 40 "gethttpheader.gperf"
|
||||
{"Date", kHttpDate},
|
||||
{""}, {""}, {""},
|
||||
#line 86 "gethttpheader.gperf"
|
||||
{"Via", kHttpVia},
|
||||
#line 35 "gethttpheader.gperf"
|
||||
{"Save-Data", kHttpSaveData},
|
||||
#line 45 "gethttpheader.gperf"
|
||||
{"ETag", kHttpEtag},
|
||||
#line 16 "gethttpheader.gperf"
|
||||
{"Connection", kHttpConnection},
|
||||
#line 26 "gethttpheader.gperf"
|
||||
{"Cookie", kHttpCookie},
|
||||
#line 42 "gethttpheader.gperf"
|
||||
{"Expires", kHttpExpires},
|
||||
#line 94 "gethttpheader.gperf"
|
||||
#line 17 "gethttpheader.gperf"
|
||||
{"Accept", kHttpAccept},
|
||||
{""},
|
||||
#line 67 "gethttpheader.gperf"
|
||||
{"If-Match", kHttpIfMatch},
|
||||
#line 39 "gethttpheader.gperf"
|
||||
{"Vary", kHttpVary},
|
||||
#line 36 "gethttpheader.gperf"
|
||||
{"Range", kHttpRange},
|
||||
#line 92 "gethttpheader.gperf"
|
||||
{"X-XSS-Protection", kHttpXXssProtection},
|
||||
{""},
|
||||
#line 50 "gethttpheader.gperf"
|
||||
{"Access-Control-Allow-Headers", kHttpAccessControlAllowHeaders},
|
||||
#line 55 "gethttpheader.gperf"
|
||||
{"Access-Control-RequestHeaders", kHttpAccessControlRequestHeaders},
|
||||
#line 57 "gethttpheader.gperf"
|
||||
{"Access-Control-Request-Methods", kHttpAccessControlRequestMethods},
|
||||
#line 53 "gethttpheader.gperf"
|
||||
{"Access-Control-MaxAge", kHttpAccessControlMaxAge},
|
||||
#line 83 "gethttpheader.gperf"
|
||||
{"Upgrade", kHttpUpgrade},
|
||||
#line 69 "gethttpheader.gperf"
|
||||
{"If-Range", kHttpIfRange},
|
||||
#line 37 "gethttpheader.gperf"
|
||||
{"Content-Length", kHttpContentLength},
|
||||
{""},
|
||||
#line 66 "gethttpheader.gperf"
|
||||
{"Expect", kHttpExpect},
|
||||
#line 90 "gethttpheader.gperf"
|
||||
{"Alt-Svc", kHttpAltSvc},
|
||||
{""},
|
||||
#line 61 "gethttpheader.gperf"
|
||||
{"Content-Description", kHttpContentDescription},
|
||||
{""},
|
||||
#line 85 "gethttpheader.gperf"
|
||||
{"WWW-Authenticate", kHttpWwwAuthenticate},
|
||||
#line 82 "gethttpheader.gperf"
|
||||
{"Transfer-Encoding", kHttpTransferEncoding},
|
||||
#line 93 "gethttpheader.gperf"
|
||||
{"Accept-Ranges", kHttpAcceptRanges},
|
||||
#line 29 "gethttpheader.gperf"
|
||||
{"From", kHttpFrom},
|
||||
#line 46 "gethttpheader.gperf"
|
||||
{"Allow", kHttpAllow},
|
||||
#line 31 "gethttpheader.gperf"
|
||||
{"X-Requested-With", kHttpXRequestedWith},
|
||||
#line 88 "gethttpheader.gperf"
|
||||
{"X-Frame-Options", kHttpXFrameOptions},
|
||||
#line 77 "gethttpheader.gperf"
|
||||
{"Proxy-Connection", kHttpProxyConnection},
|
||||
#line 60 "gethttpheader.gperf"
|
||||
{"Content-Base", kHttpContentBase},
|
||||
#line 47 "gethttpheader.gperf"
|
||||
{"Content-Range", kHttpContentRange},
|
||||
{""},
|
||||
#line 95 "gethttpheader.gperf"
|
||||
{"Set-Cookie", kHttpSetCookie},
|
||||
#line 24 "gethttpheader.gperf"
|
||||
{"Upgrade-Insecure-Requests", kHttpUpgradeInsecureRequests},
|
||||
#line 63 "gethttpheader.gperf"
|
||||
{"Content-Language", kHttpContentLanguage},
|
||||
#line 81 "gethttpheader.gperf"
|
||||
{"Trailer", kHttpTrailer},
|
||||
#line 69 "gethttpheader.gperf"
|
||||
{"If-Range", kHttpIfRange},
|
||||
#line 61 "gethttpheader.gperf"
|
||||
{"Content-Description", kHttpContentDescription},
|
||||
#line 36 "gethttpheader.gperf"
|
||||
{"Range", kHttpRange},
|
||||
#line 25 "gethttpheader.gperf"
|
||||
{"Pragma", kHttpPragma},
|
||||
#line 28 "gethttpheader.gperf"
|
||||
{"Sec-GPC", kHttpSecGpc},
|
||||
#line 15 "gethttpheader.gperf"
|
||||
{"Cache-Control", kHttpCacheControl},
|
||||
#line 55 "gethttpheader.gperf"
|
||||
{"Access-Control-RequestHeaders", kHttpAccessControlRequestHeaders},
|
||||
#line 57 "gethttpheader.gperf"
|
||||
{"Access-Control-Request-Methods", kHttpAccessControlRequestMethods},
|
||||
#line 86 "gethttpheader.gperf"
|
||||
{"WWW-Authenticate", kHttpWwwAuthenticate},
|
||||
{""},
|
||||
#line 67 "gethttpheader.gperf"
|
||||
{"If-Match", kHttpIfMatch},
|
||||
#line 37 "gethttpheader.gperf"
|
||||
{"Content-Length", kHttpContentLength},
|
||||
#line 22 "gethttpheader.gperf"
|
||||
{"X-Forwarded-For", kHttpXForwardedFor},
|
||||
#line 77 "gethttpheader.gperf"
|
||||
{"Proxy-Connection", kHttpProxyConnection},
|
||||
#line 30 "gethttpheader.gperf"
|
||||
{"If-Modified-Since", kHttpIfModifiedSince},
|
||||
#line 68 "gethttpheader.gperf"
|
||||
{"If-None-Match", kHttpIfNoneMatch},
|
||||
#line 39 "gethttpheader.gperf"
|
||||
{"Vary", kHttpVary},
|
||||
#line 95 "gethttpheader.gperf"
|
||||
{"Sec-CH-UA", kHttpSecChUa},
|
||||
#line 18 "gethttpheader.gperf"
|
||||
{"Accept-Language", kHttpAcceptLanguage},
|
||||
#line 54 "gethttpheader.gperf"
|
||||
{"Access-Control-Method", kHttpAccessControlMethod},
|
||||
#line 42 "gethttpheader.gperf"
|
||||
{"Expires", kHttpExpires},
|
||||
#line 75 "gethttpheader.gperf"
|
||||
{"Proxy-Authenticate", kHttpProxyAuthenticate},
|
||||
#line 72 "gethttpheader.gperf"
|
||||
{"Link", kHttpLink},
|
||||
#line 94 "gethttpheader.gperf"
|
||||
{"Set-Cookie", kHttpSetCookie},
|
||||
#line 25 "gethttpheader.gperf"
|
||||
{"Pragma", kHttpPragma},
|
||||
#line 49 "gethttpheader.gperf"
|
||||
{"Access-Control-Allow-Credentials", kHttpAccessControlAllowCredentials},
|
||||
{""},
|
||||
#line 56 "gethttpheader.gperf"
|
||||
{"Access-Control-Request-Method", kHttpAccessControlRequestMethod},
|
||||
#line 20 "gethttpheader.gperf"
|
||||
{"User-Agent", kHttpUserAgent},
|
||||
#line 41 "gethttpheader.gperf"
|
||||
{"Server", kHttpServer},
|
||||
#line 83 "gethttpheader.gperf"
|
||||
{"Upgrade", kHttpUpgrade},
|
||||
#line 50 "gethttpheader.gperf"
|
||||
{"Access-Control-Allow-Headers", kHttpAccessControlAllowHeaders},
|
||||
#line 70 "gethttpheader.gperf"
|
||||
{"If-Unmodified-Since", kHttpIfUnmodifiedSince},
|
||||
{""},
|
||||
#line 78 "gethttpheader.gperf"
|
||||
{"Public", kHttpPublic},
|
||||
#line 38 "gethttpheader.gperf"
|
||||
{"Content-Type", kHttpContentType},
|
||||
#line 51 "gethttpheader.gperf"
|
||||
{"Access-Control-Allow-Methods", kHttpAccessControlAllowMethods},
|
||||
#line 62 "gethttpheader.gperf"
|
||||
{"Content-Disposition", kHttpContentDisposition},
|
||||
{""}, {""},
|
||||
#line 49 "gethttpheader.gperf"
|
||||
{"Access-Control-Allow-Credentials", kHttpAccessControlAllowCredentials},
|
||||
#line 43 "gethttpheader.gperf"
|
||||
{"Content-Encoding", kHttpContentEncoding},
|
||||
#line 52 "gethttpheader.gperf"
|
||||
{"Access-Control-Allow-Origin", kHttpAccessControlAllowOrigin},
|
||||
#line 68 "gethttpheader.gperf"
|
||||
{"If-None-Match", kHttpIfNoneMatch},
|
||||
{""}, {""},
|
||||
#line 23 "gethttpheader.gperf"
|
||||
{"Origin", kHttpOrigin},
|
||||
#line 89 "gethttpheader.gperf"
|
||||
{"X-Content-Type-Options", kHttpXContentTypeOptions},
|
||||
{""},
|
||||
#line 35 "gethttpheader.gperf"
|
||||
{"Save-Data", kHttpSaveData},
|
||||
{""},
|
||||
#line 31 "gethttpheader.gperf"
|
||||
{"X-Requested-With", kHttpXRequestedWith},
|
||||
#line 81 "gethttpheader.gperf"
|
||||
{"Trailer", kHttpTrailer},
|
||||
{""},
|
||||
#line 76 "gethttpheader.gperf"
|
||||
{"Proxy-Authorization", kHttpProxyAuthorization},
|
||||
#line 89 "gethttpheader.gperf"
|
||||
{"X-Frame-Options", kHttpXFrameOptions},
|
||||
{""},
|
||||
#line 21 "gethttpheader.gperf"
|
||||
{"Referer", kHttpReferer},
|
||||
#line 75 "gethttpheader.gperf"
|
||||
{"Proxy-Authenticate", kHttpProxyAuthenticate},
|
||||
#line 45 "gethttpheader.gperf"
|
||||
{"ETag", kHttpEtag},
|
||||
{""},
|
||||
#line 23 "gethttpheader.gperf"
|
||||
{"Origin", kHttpOrigin},
|
||||
#line 90 "gethttpheader.gperf"
|
||||
{"X-Content-Type-Options", kHttpXContentTypeOptions},
|
||||
#line 71 "gethttpheader.gperf"
|
||||
{"Keep-Alive", kHttpKeepAlive},
|
||||
#line 26 "gethttpheader.gperf"
|
||||
{"Cookie", kHttpCookie},
|
||||
{""}, {""}, {""}, {""}, {""},
|
||||
#line 84 "gethttpheader.gperf"
|
||||
{"Uri", kHttpUri},
|
||||
{""}, {""},
|
||||
#line 79 "gethttpheader.gperf"
|
||||
{"Retry-After", kHttpRetryAfter},
|
||||
#line 74 "gethttpheader.gperf"
|
||||
{"Max-Forwards", kHttpMaxForwards},
|
||||
{""}, {""}, {""}, {""},
|
||||
#line 82 "gethttpheader.gperf"
|
||||
{"Transfer-Encoding", kHttpTransferEncoding},
|
||||
{""}, {""}, {""}, {""},
|
||||
#line 33 "gethttpheader.gperf"
|
||||
{"X-Forwarded-Proto", kHttpXForwardedProto},
|
||||
#line 27 "gethttpheader.gperf"
|
||||
{"DNT", kHttpDnt},
|
||||
{""},
|
||||
#line 24 "gethttpheader.gperf"
|
||||
{"Upgrade-Insecure-Requests", kHttpUpgradeInsecureRequests},
|
||||
{""},
|
||||
#line 52 "gethttpheader.gperf"
|
||||
{"Access-Control-Allow-Origin", kHttpAccessControlAllowOrigin},
|
||||
{""},
|
||||
#line 40 "gethttpheader.gperf"
|
||||
{"Date", kHttpDate},
|
||||
#line 19 "gethttpheader.gperf"
|
||||
{"Accept-Encoding", kHttpAcceptEncoding},
|
||||
#line 43 "gethttpheader.gperf"
|
||||
{"Content-Encoding", kHttpContentEncoding},
|
||||
#line 85 "gethttpheader.gperf"
|
||||
{"Warning", kHttpWarning},
|
||||
{""}, {""},
|
||||
#line 88 "gethttpheader.gperf"
|
||||
{"Strict-Transport-Security", kHttpStrictTransportSecurity},
|
||||
#line 64 "gethttpheader.gperf"
|
||||
{"Content-Location", kHttpContentLocation},
|
||||
{""}, {""}, {""}, {""},
|
||||
#line 54 "gethttpheader.gperf"
|
||||
{"Access-Control-Method", kHttpAccessControlMethod},
|
||||
{""}, {""}, {""},
|
||||
#line 92 "gethttpheader.gperf"
|
||||
{"Referrer-Policy", kHttpReferrerPolicy},
|
||||
{""}, {""}, {""},
|
||||
#line 56 "gethttpheader.gperf"
|
||||
{"Access-Control-Request-Method", kHttpAccessControlRequestMethod},
|
||||
{""},
|
||||
#line 48 "gethttpheader.gperf"
|
||||
{"Accept-Charset", kHttpAcceptCharset},
|
||||
{""},
|
||||
#line 65 "gethttpheader.gperf"
|
||||
{"Content-MD5", kHttpContentMd5},
|
||||
{""}, {""}, {""}, {""}, {""}, {""},
|
||||
{""},
|
||||
#line 15 "gethttpheader.gperf"
|
||||
{"Cache-Control", kHttpCacheControl},
|
||||
#line 62 "gethttpheader.gperf"
|
||||
{"Content-Disposition", kHttpContentDisposition},
|
||||
{""},
|
||||
#line 64 "gethttpheader.gperf"
|
||||
{"Content-Location", kHttpContentLocation},
|
||||
{""}, {""},
|
||||
#line 97 "gethttpheader.gperf"
|
||||
{"Sec-Fetch-Site", kHttpSecFetchSite},
|
||||
{""},
|
||||
#line 78 "gethttpheader.gperf"
|
||||
{"Public", kHttpPublic},
|
||||
#line 21 "gethttpheader.gperf"
|
||||
{"Referer", kHttpReferer},
|
||||
{""}, {""}, {""},
|
||||
#line 32 "gethttpheader.gperf"
|
||||
{"X-Forwarded-Host", kHttpXForwardedHost},
|
||||
{""}, {""}, {""}, {""}, {""},
|
||||
#line 30 "gethttpheader.gperf"
|
||||
{"If-Modified-Since", kHttpIfModifiedSince},
|
||||
{""}, {""},
|
||||
#line 19 "gethttpheader.gperf"
|
||||
{"Accept-Encoding", kHttpAcceptEncoding},
|
||||
{""},
|
||||
#line 74 "gethttpheader.gperf"
|
||||
{"Max-Forwards", kHttpMaxForwards},
|
||||
{""},
|
||||
#line 70 "gethttpheader.gperf"
|
||||
{"If-Unmodified-Since", kHttpIfUnmodifiedSince},
|
||||
{""}, {""},
|
||||
#line 34 "gethttpheader.gperf"
|
||||
{"X-CSRF-Token", kHttpXCsrfToken},
|
||||
#line 59 "gethttpheader.gperf"
|
||||
{"Authorization", kHttpAuthorization},
|
||||
#line 100 "gethttpheader.gperf"
|
||||
{"Sec-Fetch-Dest", kHttpSecFetchDest},
|
||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
||||
{""},
|
||||
#line 87 "gethttpheader.gperf"
|
||||
{"Strict-Transport-Security", kHttpStrictTransportSecurity},
|
||||
#line 79 "gethttpheader.gperf"
|
||||
{"Retry-After", kHttpRetryAfter},
|
||||
{""}, {""},
|
||||
#line 98 "gethttpheader.gperf"
|
||||
{"Sec-Fetch-Mode", kHttpSecFetchMode},
|
||||
#line 91 "gethttpheader.gperf"
|
||||
{"Referrer-Policy", kHttpReferrerPolicy},
|
||||
{""}, {""}, {""},
|
||||
#line 99 "gethttpheader.gperf"
|
||||
{"Sec-Fetch-User", kHttpSecFetchUser},
|
||||
{""},
|
||||
#line 96 "gethttpheader.gperf"
|
||||
{"Sec-CH-UA-Mobile", kHttpSecChUaMobile},
|
||||
{""}, {""}, {""}, {""}, {""},
|
||||
#line 33 "gethttpheader.gperf"
|
||||
{"X-Forwarded-Proto", kHttpXForwardedProto},
|
||||
{""}, {""},
|
||||
#line 22 "gethttpheader.gperf"
|
||||
{"X-Forwarded-For", kHttpXForwardedFor},
|
||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
||||
{""},
|
||||
#line 44 "gethttpheader.gperf"
|
||||
{"Last-Modified", kHttpLastModified}
|
||||
};
|
||||
|
|
|
@ -160,8 +160,6 @@ const char *GetHttpHeaderName(int h) {
|
|||
return "Transfer-Encoding";
|
||||
case kHttpUpgrade:
|
||||
return "Upgrade";
|
||||
case kHttpUri:
|
||||
return "Uri";
|
||||
case kHttpWarning:
|
||||
return "Warning";
|
||||
case kHttpWwwAuthenticate:
|
||||
|
@ -184,6 +182,18 @@ const char *GetHttpHeaderName(int h) {
|
|||
return "Accept-Ranges";
|
||||
case kHttpSetCookie:
|
||||
return "Set-Cookie";
|
||||
case kHttpSecChUa:
|
||||
return "Sec-CH-UA";
|
||||
case kHttpSecChUaMobile:
|
||||
return "Sec-CH-UA-Mobile";
|
||||
case kHttpSecFetchSite:
|
||||
return "Sec-Fetch-Site";
|
||||
case kHttpSecFetchMode:
|
||||
return "Sec-Fetch-Mode";
|
||||
case kHttpSecFetchUser:
|
||||
return "Sec-Fetch-User";
|
||||
case kHttpSecFetchDest:
|
||||
return "Sec-Fetch-Dest";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,31 @@
|
|||
#define kHttpReport 16
|
||||
#define kHttpUnlock 17
|
||||
|
||||
#define kHttpStateStart 0
|
||||
#define kHttpStateMethod 1
|
||||
#define kHttpStateUri 2
|
||||
#define kHttpStateVersion 3
|
||||
#define kHttpStateStatus 4
|
||||
#define kHttpStateMessage 5
|
||||
#define kHttpStateName 6
|
||||
#define kHttpStateColon 7
|
||||
#define kHttpStateValue 8
|
||||
#define kHttpStateCr 9
|
||||
#define kHttpStateLf1 10
|
||||
#define kHttpStateLf2 11
|
||||
|
||||
#define kHttpStateChunkStart 0
|
||||
#define kHttpStateChunkSize 1
|
||||
#define kHttpStateChunkExt 2
|
||||
#define kHttpStateChunkLf1 3
|
||||
#define kHttpStateChunk 4
|
||||
#define kHttpStateChunkCr2 5
|
||||
#define kHttpStateChunkLf2 6
|
||||
#define kHttpStateTrailerStart 7
|
||||
#define kHttpStateTrailer 8
|
||||
#define kHttpStateTrailerLf1 9
|
||||
#define kHttpStateTrailerLf2 10
|
||||
|
||||
#define kHttpHost 0
|
||||
#define kHttpCacheControl 1
|
||||
#define kHttpConnection 2
|
||||
|
@ -93,19 +118,24 @@
|
|||
#define kHttpTrailer 67
|
||||
#define kHttpTransferEncoding 68
|
||||
#define kHttpUpgrade 69
|
||||
#define kHttpUri 70
|
||||
#define kHttpWarning 71
|
||||
#define kHttpWwwAuthenticate 72
|
||||
#define kHttpVia 73
|
||||
#define kHttpStrictTransportSecurity 74
|
||||
#define kHttpXFrameOptions 75
|
||||
#define kHttpXContentTypeOptions 76
|
||||
#define kHttpAltSvc 77
|
||||
#define kHttpReferrerPolicy 78
|
||||
#define kHttpXXssProtection 79
|
||||
#define kHttpAcceptRanges 80
|
||||
#define kHttpSetCookie 81
|
||||
#define kHttpHeadersMax 82
|
||||
#define kHttpWarning 70
|
||||
#define kHttpWwwAuthenticate 71
|
||||
#define kHttpVia 72
|
||||
#define kHttpStrictTransportSecurity 73
|
||||
#define kHttpXFrameOptions 74
|
||||
#define kHttpXContentTypeOptions 75
|
||||
#define kHttpAltSvc 76
|
||||
#define kHttpReferrerPolicy 77
|
||||
#define kHttpXXssProtection 78
|
||||
#define kHttpAcceptRanges 79
|
||||
#define kHttpSetCookie 80
|
||||
#define kHttpSecChUa 81
|
||||
#define kHttpSecChUaMobile 82
|
||||
#define kHttpSecFetchSite 83
|
||||
#define kHttpSecFetchMode 84
|
||||
#define kHttpSecFetchUser 85
|
||||
#define kHttpSecFetchDest 86
|
||||
#define kHttpHeadersMax 87
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
@ -139,6 +169,13 @@ struct HttpMessage {
|
|||
struct HttpHeaders xheaders;
|
||||
};
|
||||
|
||||
struct HttpUnchunker {
|
||||
int t;
|
||||
size_t i;
|
||||
size_t j;
|
||||
ssize_t m;
|
||||
};
|
||||
|
||||
extern const char kHttpToken[256];
|
||||
extern const char kHttpMethod[18][8];
|
||||
extern const bool kHttpRepeatable[kHttpHeadersMax];
|
||||
|
@ -163,6 +200,7 @@ bool IsReasonablePath(const char *, size_t);
|
|||
int64_t ParseIp(const char *, size_t);
|
||||
int ParseForwarded(const char *, size_t, uint32_t *, uint16_t *);
|
||||
bool IsMimeType(const char *, size_t, const char *);
|
||||
ssize_t Unchunk(struct HttpUnchunker *, char *, size_t, size_t *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -73,7 +73,6 @@ const bool kHttpRepeatable[kHttpHeadersMax] = {
|
|||
[kHttpTrailer] = true,
|
||||
[kHttpTransferEncoding] = true,
|
||||
[kHttpUpgrade] = true,
|
||||
[kHttpUri] = true,
|
||||
[kHttpVary] = true,
|
||||
[kHttpVia] = true,
|
||||
[kHttpWarning] = true,
|
||||
|
|
|
@ -31,21 +31,6 @@
|
|||
|
||||
#define LIMIT (SHRT_MAX - 2)
|
||||
|
||||
enum HttpMessageState {
|
||||
START,
|
||||
METHOD,
|
||||
URI,
|
||||
VERSION,
|
||||
STATUS,
|
||||
MESSAGE,
|
||||
HKEY,
|
||||
HSEP,
|
||||
HVAL,
|
||||
CR1,
|
||||
LF1,
|
||||
LF2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes HTTP message parser.
|
||||
*/
|
||||
|
@ -78,8 +63,8 @@ void DestroyHttpMessage(struct HttpMessage *r) {
|
|||
*
|
||||
* This parser assumes ISO-8859-1 and guarantees no C0 or C1 control
|
||||
* codes are present in message fields, with the exception of tab.
|
||||
* Please note that fields like URI may use UTF-8 percent encoding. This
|
||||
* parser doesn't care if you choose ASA X3.4-1963 or MULTICS newlines.
|
||||
* Please note that fields like kHttpStateUri may use UTF-8 percent encoding.
|
||||
* This parser doesn't care if you choose ASA X3.4-1963 or MULTICS newlines.
|
||||
*
|
||||
* kHttpRepeatable defines which standard header fields are O(1) and
|
||||
* which ones may have comma entries spilled over into xheaders. For
|
||||
|
@ -105,20 +90,20 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
for (n = MIN(n, LIMIT); r->i < n; ++r->i) {
|
||||
c = p[r->i] & 0xff;
|
||||
switch (r->t) {
|
||||
case START:
|
||||
case kHttpStateStart:
|
||||
if (c == '\r' || c == '\n') break; /* RFC7230 § 3.5 */
|
||||
if (!kHttpToken[c]) return ebadmsg();
|
||||
r->t = r->type == kHttpRequest ? METHOD : VERSION;
|
||||
r->t = r->type == kHttpRequest ? kHttpStateMethod : kHttpStateVersion;
|
||||
r->a = r->i;
|
||||
break;
|
||||
case METHOD:
|
||||
case kHttpStateMethod:
|
||||
for (;;) {
|
||||
if (c == ' ') {
|
||||
r->method = GetHttpMethod(p + r->a, r->i - r->a);
|
||||
r->xmethod.a = r->a;
|
||||
r->xmethod.b = r->i;
|
||||
r->a = r->i + 1;
|
||||
r->t = URI;
|
||||
r->t = kHttpStateUri;
|
||||
break;
|
||||
} else if (!kHttpToken[c]) {
|
||||
return ebadmsg();
|
||||
|
@ -127,7 +112,7 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
c = p[r->i] & 0xff;
|
||||
}
|
||||
break;
|
||||
case URI:
|
||||
case kHttpStateUri:
|
||||
for (;;) {
|
||||
if (c == ' ' || c == '\r' || c == '\n') {
|
||||
if (r->i == r->a) return ebadmsg();
|
||||
|
@ -135,10 +120,10 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
r->uri.b = r->i;
|
||||
if (c == ' ') {
|
||||
r->a = r->i + 1;
|
||||
r->t = VERSION;
|
||||
r->t = kHttpStateVersion;
|
||||
} else {
|
||||
r->version = 9;
|
||||
r->t = c == '\r' ? CR1 : LF1;
|
||||
r->t = c == '\r' ? kHttpStateCr : kHttpStateLf1;
|
||||
}
|
||||
break;
|
||||
} else if (c < 0x20 || (0x7F <= c && c < 0xA0)) {
|
||||
|
@ -148,31 +133,31 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
c = p[r->i] & 0xff;
|
||||
}
|
||||
break;
|
||||
case VERSION:
|
||||
case kHttpStateVersion:
|
||||
if (c == ' ' || c == '\r' || c == '\n') {
|
||||
if (r->i - r->a == 8 &&
|
||||
(READ64BE(p + r->a) & 0xFFFFFFFFFF00FF00) == 0x485454502F002E00 &&
|
||||
isdigit(p[r->a + 5]) && isdigit(p[r->a + 7])) {
|
||||
r->version = (p[r->a + 5] - '0') * 10 + (p[r->a + 7] - '0');
|
||||
if (r->type == kHttpRequest) {
|
||||
r->t = c == '\r' ? CR1 : LF1;
|
||||
r->t = c == '\r' ? kHttpStateCr : kHttpStateLf1;
|
||||
} else {
|
||||
r->t = STATUS;
|
||||
r->t = kHttpStateStatus;
|
||||
}
|
||||
} else {
|
||||
return ebadmsg();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATUS:
|
||||
case kHttpStateStatus:
|
||||
for (;;) {
|
||||
if (c == ' ' || c == '\r' || c == '\n') {
|
||||
if (r->status < 100) return ebadmsg();
|
||||
if (c == ' ') {
|
||||
r->a = r->i + 1;
|
||||
r->t = MESSAGE;
|
||||
r->t = kHttpStateMessage;
|
||||
} else {
|
||||
r->t = c == '\r' ? CR1 : LF1;
|
||||
r->t = c == '\r' ? kHttpStateCr : kHttpStateLf1;
|
||||
}
|
||||
break;
|
||||
} else if ('0' <= c && c <= '9') {
|
||||
|
@ -186,12 +171,12 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
c = p[r->i] & 0xff;
|
||||
}
|
||||
break;
|
||||
case MESSAGE:
|
||||
case kHttpStateMessage:
|
||||
for (;;) {
|
||||
if (c == '\r' || c == '\n') {
|
||||
r->message.a = r->a;
|
||||
r->message.b = r->i;
|
||||
r->t = c == '\r' ? CR1 : LF1;
|
||||
r->t = c == '\r' ? kHttpStateCr : kHttpStateLf1;
|
||||
break;
|
||||
} else if (c < 0x20 || (0x7F <= c && c < 0xA0)) {
|
||||
return ebadmsg();
|
||||
|
@ -200,27 +185,31 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
c = p[r->i] & 0xff;
|
||||
}
|
||||
break;
|
||||
case CR1:
|
||||
case kHttpStateCr:
|
||||
if (c != '\n') return ebadmsg();
|
||||
r->t = LF1;
|
||||
r->t = kHttpStateLf1;
|
||||
break;
|
||||
case LF1:
|
||||
case kHttpStateLf1:
|
||||
if (c == '\r') {
|
||||
r->t = LF2;
|
||||
r->t = kHttpStateLf2;
|
||||
break;
|
||||
} else if (c == '\n') {
|
||||
return ++r->i;
|
||||
} else if (!kHttpToken[c]) {
|
||||
return ebadmsg(); /* RFC7230 § 3.2.4 */
|
||||
/*
|
||||
* 1. Forbid empty header name (RFC2616 §2.2)
|
||||
* 2. Forbid line folding (RFC7230 §3.2.4)
|
||||
*/
|
||||
return ebadmsg();
|
||||
}
|
||||
r->k.a = r->i;
|
||||
r->t = HKEY;
|
||||
r->t = kHttpStateName;
|
||||
break;
|
||||
case HKEY:
|
||||
case kHttpStateName:
|
||||
for (;;) {
|
||||
if (c == ':') {
|
||||
r->k.b = r->i;
|
||||
r->t = HSEP;
|
||||
r->t = kHttpStateColon;
|
||||
break;
|
||||
} else if (!kHttpToken[c]) {
|
||||
return ebadmsg();
|
||||
|
@ -229,12 +218,12 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
c = p[r->i] & 0xff;
|
||||
}
|
||||
break;
|
||||
case HSEP:
|
||||
case kHttpStateColon:
|
||||
if (c == ' ' || c == '\t') break;
|
||||
r->a = r->i;
|
||||
r->t = HVAL;
|
||||
r->t = kHttpStateValue;
|
||||
/* fallthrough */
|
||||
case HVAL:
|
||||
case kHttpStateValue:
|
||||
for (;;) {
|
||||
if (c == '\r' || c == '\n') {
|
||||
i = r->i;
|
||||
|
@ -252,7 +241,7 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
r->xheaders.p = x;
|
||||
++r->xheaders.n;
|
||||
}
|
||||
r->t = c == '\r' ? CR1 : LF1;
|
||||
r->t = c == '\r' ? kHttpStateCr : kHttpStateLf1;
|
||||
break;
|
||||
} else if ((c < 0x20 && c != '\t') || (0x7F <= c && c < 0xA0)) {
|
||||
return ebadmsg();
|
||||
|
@ -261,7 +250,7 @@ int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
|||
c = p[r->i] & 0xff;
|
||||
}
|
||||
break;
|
||||
case LF2:
|
||||
case kHttpStateLf2:
|
||||
if (c == '\n') {
|
||||
return ++r->i;
|
||||
}
|
||||
|
|
115
net/http/unchunk.c
Normal file
115
net/http/unchunk.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*-*- 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 "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "net/http/escape.h"
|
||||
#include "net/http/http.h"
|
||||
|
||||
/**
|
||||
* Removes chunk transfer encoding from message body in place.
|
||||
*
|
||||
* @param p is modified in place
|
||||
* @param l receives content length if not null
|
||||
* @return bytes processed, 0 if need more data, or -1 on failure
|
||||
*/
|
||||
ssize_t Unchunk(struct HttpUnchunker *u, char *p, size_t n, size_t *l) {
|
||||
int c, h;
|
||||
size_t s;
|
||||
while (u->i < n) {
|
||||
c = p[u->i++] & 255;
|
||||
switch (u->t) {
|
||||
case kHttpStateChunkStart:
|
||||
if ((u->m = kHexToInt[c]) == -1) return ebadmsg();
|
||||
u->t = kHttpStateChunkSize;
|
||||
break;
|
||||
case kHttpStateChunkSize:
|
||||
if ((h = kHexToInt[c]) != -1) {
|
||||
u->m *= 16;
|
||||
u->m += h;
|
||||
if (u->i + u->m >= n) return ebadmsg();
|
||||
break;
|
||||
}
|
||||
u->t = kHttpStateChunkExt;
|
||||
/* fallthrough */
|
||||
case kHttpStateChunkExt:
|
||||
if (c == '\r') {
|
||||
u->t = kHttpStateChunkLf1;
|
||||
break;
|
||||
} else if (c != '\n') {
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case kHttpStateChunkLf1:
|
||||
if (c != '\n') return ebadmsg();
|
||||
u->t = u->m ? kHttpStateChunk : kHttpStateTrailerStart;
|
||||
break;
|
||||
case kHttpStateChunk:
|
||||
p[u->j++] = c, --u->m;
|
||||
s = MIN(u->m, n - u->i);
|
||||
memmove(p + u->j, p + u->i, s);
|
||||
u->i += s;
|
||||
u->j += s;
|
||||
u->m -= s;
|
||||
if (!u->m) u->t = kHttpStateChunkCr2;
|
||||
break;
|
||||
case kHttpStateChunkCr2:
|
||||
if (c == '\r') {
|
||||
u->t = kHttpStateChunkLf2;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case kHttpStateChunkLf2:
|
||||
if (c != '\n') return ebadmsg();
|
||||
u->t = kHttpStateChunkStart;
|
||||
break;
|
||||
case kHttpStateTrailerStart:
|
||||
if (c == '\r') {
|
||||
u->t = kHttpStateTrailerLf2;
|
||||
break;
|
||||
} else if (c == '\n') {
|
||||
goto Finished;
|
||||
}
|
||||
u->t = kHttpStateTrailer;
|
||||
/* fallthrough */
|
||||
case kHttpStateTrailer:
|
||||
if (c == '\r') {
|
||||
u->t = kHttpStateTrailerLf1;
|
||||
break;
|
||||
} else if (c != '\n') {
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case kHttpStateTrailerLf1:
|
||||
if (c != '\n') return ebadmsg();
|
||||
u->t = kHttpStateTrailerStart;
|
||||
break;
|
||||
case kHttpStateTrailerLf2:
|
||||
if (c != '\n') return ebadmsg();
|
||||
Finished:
|
||||
if (l) *l = u->j;
|
||||
if (u->j < n) p[u->j] = 0;
|
||||
return u->i;
|
||||
break;
|
||||
default:
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -273,6 +273,15 @@ ACCEPT-encoding: bzip2\r\n\
|
|||
EXPECT_FALSE(HeaderHas(req, m, kHttpAcceptEncoding, "funzip", -1));
|
||||
}
|
||||
|
||||
TEST(HeaderHas, testEmptyHeaderName_isNotAllowed) {
|
||||
static const char m[] = "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
: boop\r\n\
|
||||
\r\n";
|
||||
InitHttpMessage(req, kHttpRequest);
|
||||
EXPECT_EQ(-1, ParseHttpMessage(req, m, strlen(m)));
|
||||
}
|
||||
|
||||
TEST(HeaderHas, testHeaderOnSameLIne) {
|
||||
static const char m[] = "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
|
|
164
test/net/http/unchunk_test.c
Normal file
164
test/net/http/unchunk_test.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*-*- 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 "libc/testlib/testlib.h"
|
||||
#include "net/http/http.h"
|
||||
|
||||
ssize_t EzUnchunk(char *p, size_t n, size_t *l) {
|
||||
struct HttpUnchunker u = {0};
|
||||
return Unchunk(&u, p, n, l);
|
||||
}
|
||||
|
||||
TEST(Unchunk, emptyInput_needsMoreData) {
|
||||
EXPECT_EQ(0, EzUnchunk(0, 0, 0));
|
||||
}
|
||||
|
||||
TEST(Unchunk, tooShort_needsMoreData) {
|
||||
char s[] = "0\r\n";
|
||||
EXPECT_EQ(0, EzUnchunk(s, strlen(s), 0));
|
||||
}
|
||||
|
||||
TEST(Unchunk, missingLength_needAtLeastOneSizeDigit) {
|
||||
char s[] = "\r\n\r\n";
|
||||
EXPECT_EQ(-1, EzUnchunk(s, strlen(s), 0));
|
||||
}
|
||||
|
||||
TEST(Unchunk, wrongLength_breaksExpectations) {
|
||||
char s[] = "3\r\nX\r\n0\r\n\r\n";
|
||||
EXPECT_EQ(-1, EzUnchunk(s, strlen(s), 0));
|
||||
}
|
||||
|
||||
TEST(Unchunk, maliciousLength_needsMoreData) {
|
||||
char s[] = "9\r\nX\r\n0\r\n\r\n";
|
||||
EXPECT_EQ(0, EzUnchunk(s, strlen(s), 0));
|
||||
}
|
||||
|
||||
TEST(Unchunk, overflowsAsCorrectLength_detectsOverflow) {
|
||||
char s[] = "10000000000000001\r\nX\r\n0\r\n\r\n";
|
||||
EXPECT_EQ(-1, EzUnchunk(s, strlen(s), 0));
|
||||
}
|
||||
|
||||
TEST(Unchunk, emptyContent) {
|
||||
size_t l;
|
||||
ssize_t n;
|
||||
char s[] = "0\r\n\r\n";
|
||||
EXPECT_EQ(5, (n = EzUnchunk(s, strlen(s), &l)));
|
||||
EXPECT_EQ(0, l);
|
||||
EXPECT_STREQ("", s);
|
||||
EXPECT_STREQ("", s + n);
|
||||
}
|
||||
|
||||
TEST(Unchunk, weirdlyEmptyContent) {
|
||||
size_t l;
|
||||
ssize_t n;
|
||||
char s[] = "00000000000\r\n\r\nextra";
|
||||
EXPECT_NE(-1, (n = EzUnchunk(s, strlen(s), &l)));
|
||||
EXPECT_EQ(0, l);
|
||||
EXPECT_STREQ("", s);
|
||||
EXPECT_STREQ("extra", s + n);
|
||||
}
|
||||
|
||||
TEST(Unchunk, smallest) {
|
||||
size_t l;
|
||||
ssize_t n;
|
||||
char s[] = "1\r\nX\r\n0\r\n\r\n";
|
||||
EXPECT_NE(-1, (n = EzUnchunk(s, strlen(s), &l)));
|
||||
EXPECT_EQ(1, l);
|
||||
EXPECT_STREQ("X", s);
|
||||
EXPECT_STREQ("", s + n);
|
||||
}
|
||||
|
||||
TEST(Unchunk, testBasicIdea) {
|
||||
size_t l;
|
||||
ssize_t n;
|
||||
char s[] = "\
|
||||
7\r\n\
|
||||
Mozilla\r\n\
|
||||
1e\r\n\
|
||||
DevelopersDevelopersDevelopers\r\n\
|
||||
7\r\n\
|
||||
Network\r\n\
|
||||
0\r\n\
|
||||
\r\n\
|
||||
extra guff";
|
||||
EXPECT_NE(-1, (n = EzUnchunk(s, strlen(s), &l)));
|
||||
EXPECT_EQ(0x7 + 0x1e + 0x7, l);
|
||||
EXPECT_STREQ("MozillaDevelopersDevelopersDevelopersNetwork", s);
|
||||
EXPECT_STREQ("extra guff", s + n);
|
||||
}
|
||||
|
||||
TEST(Unchunk, testTrailers_areIgnored) {
|
||||
size_t l;
|
||||
ssize_t n;
|
||||
char s[] = "\
|
||||
7\r\n\
|
||||
Mozilla\r\n\
|
||||
001E\r\n\
|
||||
DevelopersDevelopersDevelopers\r\n\
|
||||
7\r\n\
|
||||
Network\r\n\
|
||||
0\r\n\
|
||||
X-Lol: Cat\r\n\
|
||||
X-Cat: Lol\r\n\
|
||||
\r\n\
|
||||
extra guff";
|
||||
EXPECT_NE(-1, (n = EzUnchunk(s, strlen(s), &l)));
|
||||
EXPECT_EQ(0x7 + 0x1e + 0x7, l);
|
||||
EXPECT_STREQ("MozillaDevelopersDevelopersDevelopersNetwork", s);
|
||||
EXPECT_STREQ("extra guff", s + n);
|
||||
}
|
||||
|
||||
TEST(Unchunk, testChunkExtensions_areIgnored) {
|
||||
size_t l;
|
||||
ssize_t n;
|
||||
char s[] = "\
|
||||
7;hey=ho;yo\r\n\
|
||||
Mozilla\r\n\
|
||||
1e;hey=ho;yo\r\n\
|
||||
DevelopersDevelopersDevelopers\r\n\
|
||||
7;hey=ho;yo\r\n\
|
||||
Network\r\n\
|
||||
0\r\n\
|
||||
\r\n\
|
||||
extra guff";
|
||||
EXPECT_NE(-1, (n = EzUnchunk(s, strlen(s), &l)));
|
||||
EXPECT_EQ(0x7 + 0x1e + 0x7, l);
|
||||
EXPECT_STREQ("MozillaDevelopersDevelopersDevelopersNetwork", s);
|
||||
EXPECT_STREQ("extra guff", s + n);
|
||||
}
|
||||
|
||||
TEST(Unchunk, testNewlines_areFlexible) {
|
||||
size_t l;
|
||||
ssize_t n;
|
||||
char s[] = "\
|
||||
7\n\
|
||||
Mozilla\n\
|
||||
1e\n\
|
||||
DevelopersDevelopersDevelopers\n\
|
||||
7\n\
|
||||
Network\n\
|
||||
0\n\
|
||||
X-Lol: Cat\n\
|
||||
X-Cat: Lol\n\
|
||||
\n\
|
||||
extra guff";
|
||||
EXPECT_NE(-1, (n = EzUnchunk(s, strlen(s), &l)));
|
||||
EXPECT_EQ(0x7 + 0x1e + 0x7, l);
|
||||
EXPECT_STREQ("MozillaDevelopersDevelopersDevelopersNetwork", s);
|
||||
EXPECT_STREQ("extra guff", s + n);
|
||||
}
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
STATIC_YOINK("zip_uri_support");
|
||||
STATIC_YOINK("o/" MODE "/tool/net/redbean.com");
|
||||
|
||||
char testlib_enable_tmp_setup_teardown_once;
|
||||
int port;
|
||||
|
||||
|
|
3
tool/net/demo/printpayload.lua
Normal file
3
tool/net/demo/printpayload.lua
Normal file
|
@ -0,0 +1,3 @@
|
|||
SetStatus(200)
|
||||
SetHeader("Content-Type", GetHeader("Content-Type"))
|
||||
Write(GetPayload())
|
|
@ -129,6 +129,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
|
|||
tool/net/demo/redbean-xhr.lua \
|
||||
tool/net/demo/seekable.txt \
|
||||
tool/net/demo/virtualbean.html \
|
||||
tool/net/demo/printpayload.lua \
|
||||
tool/net/redbean.c \
|
||||
net/http/parsehttpmessage.c \
|
||||
net/http/parseurl.c \
|
||||
|
@ -144,7 +145,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
|
|||
@(cd o/$(MODE)/tool/net && ../../host/third_party/infozip/zip.com -qr redbean-demo.com .lua)
|
||||
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/hello.lua tool/net/demo/sql.lua
|
||||
@echo "<-- check out this lua server page" | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj $@ tool/net/demo/redbean.lua
|
||||
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua
|
||||
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua tool/net/demo/printpayload.lua
|
||||
@echo Uncompressed for HTTP Range requests | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj0 $@ tool/net/demo/seekable.txt
|
||||
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -q $@ tool/net/ tool/net/demo/ tool/net/demo/index.html tool/net/demo/redbean.css tool/net/redbean.c net/http/parsehttpmessage.c net/http/parseurl.c net/http/encodeurl.c test/net/http/parsehttpmessage_test.c test/net/http/parseurl_test.c
|
||||
@printf "<p>This is a live instance of <a href=https://justine.lol/redbean/>redbean</a>: a tiny multiplatform webserver that <a href=https://news.ycombinator.com/item?id=26271117>went viral</a> on hacker news a few months ago.\r\nSince then, we've added Lua dynamic serving, which also goes as fast as 1,000,000 requests per second on a core i9 (rather than a cheap virtual machine like this)\nin addition to SQLite and SSL. The text you're reading now is a PKZIP End Of Central Directory comment.\r\n<p>redbean aims to be production worthy across six operating systems, using a single executable file (this demo is hosted on FreeBSD 13). redbean has been enhanced to restore the APE header after startup.\r\nIt automatically generates this listing page based on your zip contents. If you use redbean as an application server / web development environment,\r\nthen you'll find other new and useful features like function call logging so you can get that sweet sweet microsecond scale latency." | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -z $@
|
||||
|
|
|
@ -432,6 +432,7 @@ static const char *certpath;
|
|||
static const char *launchbrowser;
|
||||
static struct pollfd *polls;
|
||||
static struct Strings loops;
|
||||
static size_t payloadlength;
|
||||
static size_t contentlength;
|
||||
static int64_t cacheseconds;
|
||||
static const char *serverheader;
|
||||
|
@ -3474,7 +3475,7 @@ static int LuaParseHttpDateTime(lua_State *L) {
|
|||
}
|
||||
|
||||
static int LuaGetPayload(lua_State *L) {
|
||||
lua_pushlstring(L, inbuf.p + hdrsize, msgsize - hdrsize);
|
||||
lua_pushlstring(L, inbuf.p + hdrsize, payloadlength);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -4712,11 +4713,6 @@ static char *HandleVersionNotSupported(void) {
|
|||
return ServeFailure(505, "HTTP Version Not Supported");
|
||||
}
|
||||
|
||||
static char *HandleTransferRefused(void) {
|
||||
LockInc(&shared->c.transfersrefused);
|
||||
return ServeFailure(501, "Not Implemented");
|
||||
}
|
||||
|
||||
static char *HandleConnectRefused(void) {
|
||||
LockInc(&shared->c.connectsrefused);
|
||||
return ServeFailure(501, "Not Implemented");
|
||||
|
@ -4738,6 +4734,11 @@ static char *HandlePayloadSlowloris(void) {
|
|||
return ServeFailure(408, "Request Timeout");
|
||||
}
|
||||
|
||||
static char *HandleTransferRefused(void) {
|
||||
LockInc(&shared->c.transfersrefused);
|
||||
return ServeFailure(501, "Not Implemented");
|
||||
}
|
||||
|
||||
static char *HandleMapFailed(struct Asset *a, int fd) {
|
||||
LockInc(&shared->c.mapfails);
|
||||
WARNF("mmap(%`'s) failed %s", a->file->path, strerror(errno));
|
||||
|
@ -4868,44 +4869,85 @@ static char *ServeServerOptions(void) {
|
|||
return p;
|
||||
}
|
||||
|
||||
char *SynchronizeStream(void) {
|
||||
static void SendContinueIfNeeded(void) {
|
||||
if (msg.version >= 11 && HeaderEqualCase(kHttpExpect, "100-continue")) {
|
||||
LockInc(&shared->c.continues);
|
||||
SendContinue();
|
||||
}
|
||||
}
|
||||
|
||||
static char *ReadMore(void) {
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
LockInc(&shared->c.frags);
|
||||
if (++frags == 64) return HandlePayloadSlowloris();
|
||||
if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
|
||||
if (!(got = rc)) return HandlePayloadDisconnect();
|
||||
amtread += got;
|
||||
} else if (errno == EINTR) {
|
||||
LockInc(&shared->c.readinterrupts);
|
||||
if (killed || ((meltdown || terminated) && nowl() - startread > 1)) {
|
||||
return HandlePayloadDrop();
|
||||
}
|
||||
} else {
|
||||
return HandlePayloadReadError();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *SynchronizeLength(void) {
|
||||
char *p;
|
||||
if (hdrsize + payloadlength > amtread) {
|
||||
if (hdrsize + payloadlength > inbuf.n) return HandleHugePayload();
|
||||
SendContinueIfNeeded();
|
||||
while (amtread < hdrsize + payloadlength) {
|
||||
if ((p = ReadMore())) return p;
|
||||
}
|
||||
}
|
||||
msgsize = hdrsize + payloadlength;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *SynchronizeChunked(void) {
|
||||
char *p;
|
||||
ssize_t transferlength;
|
||||
struct HttpUnchunker u = {0};
|
||||
SendContinueIfNeeded();
|
||||
while (!(transferlength = Unchunk(&u, inbuf.p + hdrsize, amtread - hdrsize,
|
||||
&payloadlength))) {
|
||||
if ((p = ReadMore())) return p;
|
||||
}
|
||||
if (transferlength == -1) return HandleHugePayload();
|
||||
msgsize = hdrsize + transferlength;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *SynchronizeStream(void) {
|
||||
int64_t cl;
|
||||
if (HasHeader(kHttpContentLength)) {
|
||||
if (HasHeader(kHttpTransferEncoding) &&
|
||||
!SlicesEqualCase("identity", 8, HeaderData(kHttpTransferEncoding),
|
||||
HeaderLength(kHttpTransferEncoding))) {
|
||||
if (SlicesEqualCase("chunked", 7, HeaderData(kHttpTransferEncoding),
|
||||
HeaderLength(kHttpTransferEncoding))) {
|
||||
return SynchronizeChunked();
|
||||
} else {
|
||||
return HandleTransferRefused();
|
||||
}
|
||||
} else if (HasHeader(kHttpContentLength)) {
|
||||
if ((cl = ParseContentLength(HeaderData(kHttpContentLength),
|
||||
HeaderLength(kHttpContentLength))) == -1) {
|
||||
HeaderLength(kHttpContentLength))) != -1) {
|
||||
payloadlength = cl;
|
||||
return SynchronizeLength();
|
||||
} else {
|
||||
return HandleBadContentLength();
|
||||
}
|
||||
} else if (msg.method == kHttpPost || msg.method == kHttpPut) {
|
||||
return HandleLengthRequired();
|
||||
} else {
|
||||
cl = 0;
|
||||
msgsize = hdrsize;
|
||||
payloadlength = 0;
|
||||
return NULL;
|
||||
}
|
||||
if (hdrsize + cl > amtread) {
|
||||
if (hdrsize + cl > inbuf.n) HandleHugePayload();
|
||||
if (msg.version >= 11 && HeaderEqualCase(kHttpExpect, "100-continue")) {
|
||||
LockInc(&shared->c.continues);
|
||||
SendContinue();
|
||||
}
|
||||
while (amtread < hdrsize + cl) {
|
||||
LockInc(&shared->c.frags);
|
||||
if (++frags == 64) HandlePayloadSlowloris();
|
||||
if ((rc = reader(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
|
||||
if (!(got = rc)) return HandlePayloadDisconnect();
|
||||
amtread += got;
|
||||
} else if (errno == EINTR) {
|
||||
LockInc(&shared->c.readinterrupts);
|
||||
if (killed || ((meltdown || terminated) && nowl() - startread > 1)) {
|
||||
return HandlePayloadDrop();
|
||||
}
|
||||
} else {
|
||||
return HandlePayloadReadError();
|
||||
}
|
||||
}
|
||||
}
|
||||
msgsize = hdrsize + cl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ParseRequestParameters(void) {
|
||||
|
@ -4936,12 +4978,6 @@ static void ParseRequestParameters(void) {
|
|||
url.scheme.n = 4;
|
||||
}
|
||||
}
|
||||
if (HasHeader(kHttpContentType) &&
|
||||
IsMimeType(HeaderData(kHttpContentType), HeaderLength(kHttpContentType),
|
||||
"application/x-www-form-urlencoded")) {
|
||||
FreeLater(ParseParams(inbuf.p + hdrsize, msgsize - hdrsize, &url.params));
|
||||
}
|
||||
FreeLater(url.params.p);
|
||||
}
|
||||
|
||||
static bool HasAtMostThisElement(int h, const char *s) {
|
||||
|
@ -4949,8 +4985,7 @@ static bool HasAtMostThisElement(int h, const char *s) {
|
|||
struct HttpHeader *x;
|
||||
if (HasHeader(h)) {
|
||||
n = strlen(s);
|
||||
if (!SlicesEqualCase(s, n, inbuf.p + msg.headers[h].a,
|
||||
msg.headers[h].b - msg.headers[h].a)) {
|
||||
if (!SlicesEqualCase(s, n, HeaderData(h), HeaderLength(h))) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < msg.xheaders.n; ++i) {
|
||||
|
@ -5000,7 +5035,7 @@ static char *HandleRequest(void) {
|
|||
return HandleVersionNotSupported();
|
||||
}
|
||||
if ((p = SynchronizeStream())) return p;
|
||||
if (logbodies) LogBody("received", inbuf.p + hdrsize, msgsize - hdrsize);
|
||||
if (logbodies) LogBody("received", inbuf.p + hdrsize, payloadlength);
|
||||
if (msg.version < 11 || HeaderEqualCase(kHttpConnection, "close")) {
|
||||
connectionclose = true;
|
||||
}
|
||||
|
@ -5014,9 +5049,6 @@ static char *HandleRequest(void) {
|
|||
if (!HasAtMostThisElement(kHttpExpect, "100-continue")) {
|
||||
return HandleExpectFailed();
|
||||
}
|
||||
if (!HasAtMostThisElement(kHttpTransferEncoding, "identity")) {
|
||||
return HandleTransferRefused();
|
||||
}
|
||||
ParseRequestParameters();
|
||||
if (!url.host.n || !url.path.n || url.path.p[0] != '/' ||
|
||||
!IsAcceptablePath(url.path.p, url.path.n) ||
|
||||
|
@ -5034,6 +5066,12 @@ static char *HandleRequest(void) {
|
|||
FreeLater(EncodeUrl(&url, 0)), HeaderLength(kHttpReferer),
|
||||
HeaderData(kHttpReferer), HeaderLength(kHttpUserAgent),
|
||||
HeaderData(kHttpUserAgent));
|
||||
if (HasHeader(kHttpContentType) &&
|
||||
IsMimeType(HeaderData(kHttpContentType), HeaderLength(kHttpContentType),
|
||||
"application/x-www-form-urlencoded")) {
|
||||
FreeLater(ParseParams(inbuf.p + hdrsize, payloadlength, &url.params));
|
||||
}
|
||||
FreeLater(url.params.p);
|
||||
#ifndef STATIC
|
||||
if (hasluaglobalhandler) return LuaOnHttpRequest();
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue