Add /statusz page to redbean plus other enhancements

redbean improvements:

- Explicitly disable corking
- Simulate Python regex API for Lua
- Send warmup requests in main process on startup
- Add Class-A granular IPv4 network classification
- Add /statusz page so you can monitor your redbean's health
- Fix regressions on OpenBSD/NetBSD caused by recent changes
- Plug Authorization header into Lua GetUser and GetPass APIs
- Recognize X-Forwarded-{For,Host} from local reverse proxies
- Add many additional functions to redbean Lua server page API
- Report resource usage of child processes on `/` listing page
- Introduce `-a` flag for logging child process resource usage
- Introduce `-t MILLIS` flag and `ProgramTimeout(ms)` init API
- Introduce `-H "Header: value"` flag and `ProgramHeader(k,v)` API

Cosmopolitan Libc improvements:

- Make strerror() simpler
- Make inet_pton() not depend on sscanf()
- Fix OpenExecutable() which broke .data section earlier
- Fix stdio in cases where it overflows kernel tty buffer
- Fix bugs in crash reporting w/o .com.dbg binary present
- Add polyfills for SO_LINGER, SO_RCVTIMEO, and SO_SNDTIMEO
- Polyfill TCP_CORK on BSD and XNU using TCP_NOPUSH magnums

New netcat clone in examples/nc.c:

While testing some of the failure conditions for redbean, I noticed that
BusyBox's `nc` command is pretty busted, if you use it as an interactive
tool, rather than having it be part of a pipeline. Unfortunately this'll
only work on UNIX since Windows doesn't let us poll on stdio and sockets
at the same time because I don't think they want tools like this running
on their platform. So if you want forbidden fruit, it's here so enjoy it
This commit is contained in:
Justine Tunney
2021-04-23 10:45:19 -07:00
parent 4effa23528
commit b107d2709f
163 changed files with 4425 additions and 2104 deletions

View File

@ -30,17 +30,14 @@
*/
TEST(strerror, e2big) {
if (IsTiny()) return;
EXPECT_STARTSWITH("E2BIG", strerror(E2BIG));
}
TEST(strerror, enosys) {
if (IsTiny()) return;
EXPECT_STARTSWITH("ENOSYS", strerror(ENOSYS));
}
TEST(strerror, einval) {
if (IsTiny()) return;
EXPECT_STARTSWITH("EINVAL", strerror(EINVAL));
}
@ -50,14 +47,9 @@ TEST(strerror, symbolizingTheseNumbersAsErrorsIsHeresyInUnixStyle) {
}
TEST(strerror, enotconn_orLinkerIsntUsingLocaleC_orCodeIsOutOfSync) {
if (IsTiny()) return;
EXPECT_STARTSWITH("ENOTCONN", strerror(ENOTCONN));
}
TEST(strerror, exfull_orLinkerIsntUsingLocaleC_orCodeIsOutOfSync) {
if (!IsTiny()) {
EXPECT_STARTSWITH("ETXTBSY", strerror(ETXTBSY));
} else {
EXPECT_STARTSWITH("EUNKNOWN", strerror(ETXTBSY));
}
EXPECT_STARTSWITH("ETXTBSY", strerror(ETXTBSY));
}

View File

@ -23,18 +23,46 @@
#include "libc/testlib/testlib.h"
TEST(inet_pton, testLocalhost) {
uint32_t addr;
ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", &addr));
EXPECT_EQ(htonl(INADDR_LOOPBACK), addr);
uint8_t addr[4] = {255, 255, 255, 255};
EXPECT_EQ(1, inet_pton(AF_INET, "127.0.0.1", &addr));
EXPECT_EQ(127, addr[0]);
EXPECT_EQ(0, addr[1]);
EXPECT_EQ(0, addr[2]);
EXPECT_EQ(1, addr[3]);
}
TEST(inet_pton, testBadAddresses) {
uint32_t addr;
ASSERT_EQ(0, inet_pton(AF_INET, "127.0.0", &addr));
ASSERT_EQ(0, inet_pton(AF_INET, "256.0.0.1", &addr));
TEST(inet_pton, testShortAddress_doesntFillFullValue) {
uint8_t addr[4] = {255, 255, 255, 255};
EXPECT_EQ(0, inet_pton(AF_INET, "127.0.0", &addr));
EXPECT_EQ(127, addr[0]);
EXPECT_EQ(0, addr[1]);
EXPECT_EQ(0, addr[2]);
EXPECT_EQ(255, addr[3]);
}
TEST(inet_pton, testBadFamily) {
uint32_t addr = 666;
ASSERT_EQ(-1, inet_pton(666, "127.0.0.1", &addr));
TEST(inet_pton, testOverflow_stopsParsing) {
uint8_t addr[4] = {255, 255, 255, 255};
EXPECT_EQ(0, inet_pton(AF_INET, "0.300.0", &addr));
EXPECT_EQ(0, addr[0]);
EXPECT_EQ(255, addr[1]);
EXPECT_EQ(255, addr[2]);
EXPECT_EQ(255, addr[3]);
}
TEST(inet_pton, testBadChar_stopsParsing) {
uint8_t addr[4] = {255, 255, 255, 255};
EXPECT_EQ(0, inet_pton(AF_INET, "127-.0.0", &addr));
EXPECT_EQ(127, addr[0]);
EXPECT_EQ(255, addr[1]);
EXPECT_EQ(255, addr[2]);
EXPECT_EQ(255, addr[3]);
}
TEST(inet_pton, testBadFamily_returnsNegAndChangesNothing) {
uint8_t addr[4] = {255, 255, 255, 255};
EXPECT_EQ(-1, inet_pton(666, "127.0.0.1", &addr));
EXPECT_EQ(255, addr[0]);
EXPECT_EQ(255, addr[1]);
EXPECT_EQ(255, addr[2]);
EXPECT_EQ(255, addr[3]);
}

View File

@ -40,12 +40,12 @@ TEST(crc32c, test) {
strlen(hyperion) - strlen(FANATICS)));
}
FIXTURE(crc32c, pure_) {
*(void **)(&crc32c) = (void *)crc32c_pure;
}
FIXTURE(crc32c, sse42_) {
if (X86_HAVE(SSE4_2)) {
*(void **)(&crc32c) = (void *)crc32c_sse42;
}
TEST(crc32c_pure, test) {
EXPECT_EQ(0, crc32c_pure(0, "", 0));
EXPECT_EQ(crc32c_pure(0, "hello", 5), crc32c_pure(0, "hello", 5));
EXPECT_EQ(0xe3069283, crc32c_pure(0, "123456789", 9));
EXPECT_EQ(0x6d6eefba, crc32c_pure(0, hyperion, strlen(hyperion)));
EXPECT_EQ(0x6d6eefba, crc32c_pure(crc32c_pure(0, FANATICS, strlen(FANATICS)),
hyperion + strlen(FANATICS),
strlen(hyperion) - strlen(FANATICS)));
}

View File

@ -16,10 +16,11 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/errno.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
#include "net/http/escape.h"
size_t n;
@ -27,6 +28,30 @@ TEST(DecodeLatin1, test) {
EXPECT_STREQ("", DecodeLatin1(NULL, 0, 0));
EXPECT_STREQ("¥atta", DecodeLatin1("\245atta", -1, &n));
EXPECT_EQ(6, n);
EXPECT_STREQ("\245atta", EncodeLatin1("¥atta", -1, &n, 0));
EXPECT_EQ(5, n);
}
TEST(DecodeLatin1, testAbleToImposeCharacterRestrictions) {
errno = 0;
EXPECT_EQ(0, EncodeLatin1("\200atta", -1, &n, kControlC1));
EXPECT_EQ(0, n);
EXPECT_EQ(EILSEQ, errno);
errno = 0;
EXPECT_EQ(0, EncodeLatin1("\002atta", -1, &n, kControlC0));
EXPECT_EQ(0, n);
EXPECT_EQ(EILSEQ, errno);
}
TEST(EncodeLatin1, roundTrip) {
int i;
char b[256];
for (i = 0; i < 256; ++i) b[i] = i;
char *utf8 = DecodeLatin1(b, 256, &n);
EXPECT_EQ(384, n);
char *lat1 = EncodeLatin1(utf8, n, &n, 0);
ASSERT_EQ(256, n);
EXPECT_EQ(0, memcmp(b, lat1, 256));
}
TEST(DecodeLatin1, testOom_returnsNullAndSetsSizeToZero) {
@ -38,4 +63,6 @@ TEST(DecodeLatin1, testOom_returnsNullAndSetsSizeToZero) {
BENCH(DecodeLatin1, bench) {
EZBENCH2("DecodeLatin1", donothing,
DecodeLatin1(kHyperion, kHyperionSize, 0));
EZBENCH2("EncodeLatin1", donothing,
EncodeLatin1(kHyperion, kHyperionSize, 0, 0));
}

View File

@ -23,7 +23,7 @@
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "net/http/base64.h"
#include "net/http/escape.h"
size_t i, n, m;
char *p, *q, b[32];

View File

@ -22,7 +22,7 @@
#include "libc/stdio/stdio.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
#include "net/http/escape.h"
char *p;
size_t n;

View File

@ -24,10 +24,11 @@
#include "net/http/escape.h"
char *escapehtml(const char *s) {
struct EscapeResult r;
r = EscapeHtml(s, strlen(s));
ASSERT_EQ(strlen(r.data), r.size);
return r.data;
char *p;
size_t n;
p = EscapeHtml(s, strlen(s), &n);
ASSERT_EQ(strlen(p), n);
return p;
}
TEST(escapehtml, test) {
@ -49,5 +50,5 @@ TEST(escapehtml, testAstralPlanes_doesNothing) {
BENCH(escapehtml, bench) {
EZBENCH2("escapehtml", donothing,
free(EscapeHtml(kHyperion, kHyperionSize).data));
free(EscapeHtml(kHyperion, kHyperionSize, 0)));
}

View File

@ -24,10 +24,11 @@
#include "net/http/escape.h"
char *escapejs(const char *s) {
struct EscapeResult r;
r = EscapeJsStringLiteral(s, strlen(s));
ASSERT_EQ(strlen(r.data), r.size);
return r.data;
char *p;
size_t n;
p = EscapeJsStringLiteral(s, strlen(s), &n);
ASSERT_EQ(strlen(p), n);
return p;
}
TEST(EscapeJsStringLiteral, test) {
@ -53,27 +54,29 @@ TEST(EscapeJsStringLiteral, testBrokenUnicode_sparesInnocentCharacters) {
void makefile1(void) {
FILE *f;
struct EscapeResult r;
r = EscapeJsStringLiteral(kHyperion, kHyperionSize);
char *p;
size_t n;
p = EscapeJsStringLiteral(kHyperion, kHyperionSize, &n);
f = fopen("/tmp/a", "wb");
fwrite(r.data, r.size, 1, f);
fwrite(p, n, 1, f);
fclose(f);
free(r.data);
free(p);
}
void makefile2(void) {
int fd;
struct EscapeResult r;
r = EscapeJsStringLiteral(kHyperion, kHyperionSize);
char *p;
size_t n;
p = EscapeJsStringLiteral(kHyperion, kHyperionSize, &n);
fd = creat("/tmp/a", 0644);
write(fd, r.data, r.size);
write(fd, p, n);
close(fd);
free(r.data);
free(p);
}
BENCH(EscapeJsStringLiteral, bench) {
EZBENCH2("escapejs", donothing,
free(EscapeJsStringLiteral(kHyperion, kHyperionSize).data));
free(EscapeJsStringLiteral(kHyperion, kHyperionSize, 0)));
EZBENCH2("makefile1", donothing, makefile1());
EZBENCH2("makefile2", donothing, makefile2());
}

View File

@ -24,10 +24,11 @@
#include "net/http/escape.h"
char *escapeparam(const char *s) {
struct EscapeResult r;
r = EscapeParam(s, -1);
ASSERT_EQ(strlen(r.data), r.size);
return r.data;
char *p;
size_t n;
p = EscapeParam(s, -1, &n);
ASSERT_EQ(strlen(p), n);
return p;
}
TEST(EscapeParam, test) {
@ -49,5 +50,5 @@ TEST(EscapeParam, testAstralPlanes_usesUtf8HexEncoding) {
BENCH(EscapeParam, bench) {
EZBENCH2("EscapeParam", donothing,
free(EscapeParam(kHyperion, kHyperionSize).data));
free(EscapeParam(kHyperion, kHyperionSize, 0)));
}

View File

@ -0,0 +1,51 @@
/*-*- 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/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "net/http/escape.h"
TEST(HasControlCodes, test) {
EXPECT_FALSE(
HasControlCodes(kHyperion, kHyperionSize, kControlC0 | kControlC1));
EXPECT_TRUE(HasControlCodes("hi\1", -1, kControlC0));
EXPECT_FALSE(HasControlCodes("hi\1", -1, kControlC1));
EXPECT_FALSE(HasControlCodes("hi there", -1, 0));
EXPECT_TRUE(HasControlCodes("hi\tthere", -1, kControlWs));
}
TEST(HasControlCodes, testDoesUtf8) {
EXPECT_FALSE(HasControlCodes(u8"", -1, kControlC0 | kControlC1));
EXPECT_FALSE(HasControlCodes("\304\200", -1, kControlC0 | kControlC1));
EXPECT_TRUE(HasControlCodes("\300\200", -1, kControlC0 | kControlC1));
EXPECT_FALSE(HasControlCodes("\300\200", -1, kControlC1));
EXPECT_TRUE(HasControlCodes("\302\202", -1, kControlC0 | kControlC1));
EXPECT_TRUE(HasControlCodes("\302\202", -1, kControlC1));
EXPECT_FALSE(HasControlCodes("\302\202", -1, kControlC0));
}
TEST(HasControlCodes, testHasLatin1FallbackBehavior) {
EXPECT_TRUE(HasControlCodes("\202", -1, kControlWs | kControlC1));
EXPECT_FALSE(HasControlCodes("\202", -1, kControlC0));
}
BENCH(HasControlCodes, bench) {
EZBENCH2("HasControlCodes", donothing,
HasControlCodes(kHyperion, kHyperionSize, kControlWs));
}

View File

@ -21,7 +21,7 @@
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
#include "net/http/escape.h"
TEST(IndentLines, testEmpty) {
char *p;

View File

@ -89,8 +89,41 @@ TEST(ParseIp, test) {
EXPECT_EQ(-1, ParseIp("hello..example", -1));
}
TEST(ParseForwarded, test) {
uint32_t ip = 7;
uint16_t port = 7;
EXPECT_EQ(-1, ParseForwarded("", -1, &ip, &port));
EXPECT_EQ(-1, ParseForwarded("0.0.0.0", -1, &ip, &port));
EXPECT_EQ(-1, ParseForwarded("8.8.8.8", -1, &ip, &port));
EXPECT_EQ(-1, ParseForwarded("[::1]:123", -1, &ip, &port));
EXPECT_EQ(7, ip);
EXPECT_EQ(7, port);
EXPECT_EQ(0, ParseForwarded("0.0.0.1:123", -1, &ip, &port));
EXPECT_EQ(0x00000001, ip);
EXPECT_EQ(123, port);
EXPECT_EQ(0, ParseForwarded("1.2.3.4:123", -1, &ip, &port));
EXPECT_EQ(0x01020304, ip);
EXPECT_EQ(123, port);
EXPECT_EQ(0, ParseForwarded("128.2.3.4:123", -1, &ip, &port));
EXPECT_EQ(0x80020304, ip);
EXPECT_EQ(123, port);
EXPECT_EQ(0, ParseForwarded("255.255.255.255:123", -1, &ip, &port));
EXPECT_EQ(0xFFFFFFFF, ip);
EXPECT_EQ(123, port);
EXPECT_EQ(0, ParseForwarded("203.0.113.0:123", -1, &ip, &port));
EXPECT_EQ(0xcb007100, ip);
EXPECT_EQ(123, port);
EXPECT_EQ(0, ParseForwarded("203.0.113.42:31337", -1, &ip, &port));
EXPECT_EQ(-1, ParseForwarded("...:123", -1, &ip, &port));
EXPECT_EQ(-1, ParseForwarded("203.0.113.0:123123123", -1, &ip, &port));
}
BENCH(IsAcceptableHost, bench) {
uint32_t ip;
uint16_t port;
EZBENCH2("IsAcceptableHost 127.0.0.1", donothing,
IsAcceptableHost("127.0.0.1", 9));
EZBENCH2("IsAcceptablePort 80", donothing, IsAcceptablePort("80", 2));
EZBENCH2("ParseForwarded 80", donothing,
ParseForwarded("203.0.113.42:31337", 20, &ip, &port));
}

View File

@ -23,10 +23,12 @@
#include "net/http/http.h"
TEST(IsAcceptablePath, test) {
EXPECT_TRUE(IsAcceptablePath("*", 1));
EXPECT_TRUE(IsAcceptablePath("/", 1));
EXPECT_TRUE(IsAcceptablePath("index.html", 10));
EXPECT_TRUE(IsAcceptablePath("/index.html", 11));
EXPECT_TRUE(IsAcceptablePath("/index.html", -1));
EXPECT_TRUE(IsAcceptablePath("/redbean.png", -1));
}
TEST(IsAcceptablePath, testEmptyString_allowedIfYouLikeImplicitLeadingSlash) {
@ -88,5 +90,6 @@ TEST(IsAcceptablePath, testOverlongSlashDot_isDetected) {
}
BENCH(IsAcceptablePath, bench) {
EZBENCH2("IsAcceptablePath", donothing, IsAcceptablePath("*", 1));
EZBENCH2("IsAcceptablePath", donothing, IsAcceptablePath("/index.html", 11));
}

View File

@ -0,0 +1,91 @@
/*-*- 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/ezbench.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
TEST(IsReasonablePath, test) {
EXPECT_TRUE(IsReasonablePath("/", 1));
EXPECT_TRUE(IsReasonablePath("index.html", 10));
EXPECT_TRUE(IsReasonablePath("/index.html", 11));
EXPECT_TRUE(IsReasonablePath("/index.html", -1));
EXPECT_TRUE(IsReasonablePath("/redbean.png", -1));
}
TEST(IsReasonablePath, testEmptyString_allowedIfYouLikeImplicitLeadingSlash) {
EXPECT_TRUE(IsReasonablePath(0, 0));
EXPECT_TRUE(IsReasonablePath(0, -1));
EXPECT_TRUE(IsReasonablePath("", 0));
}
TEST(IsReasonablePath, testHiddenFiles_areAllowed) {
EXPECT_TRUE(IsReasonablePath("/.index.html", 12));
EXPECT_TRUE(IsReasonablePath("/x/.index.html", 14));
}
TEST(IsReasonablePath, testDoubleSlash_isAllowed) {
EXPECT_TRUE(IsReasonablePath("//", 2));
EXPECT_TRUE(IsReasonablePath("foo//", 5));
EXPECT_TRUE(IsReasonablePath("/foo//", 6));
EXPECT_TRUE(IsReasonablePath("/foo//bar", 9));
}
TEST(IsReasonablePath, testNoncanonicalDirectories_areForbidden) {
EXPECT_FALSE(IsReasonablePath(".", 1));
EXPECT_FALSE(IsReasonablePath("..", 2));
EXPECT_FALSE(IsReasonablePath("/.", 2));
EXPECT_FALSE(IsReasonablePath("/..", 3));
EXPECT_FALSE(IsReasonablePath("./", 2));
EXPECT_FALSE(IsReasonablePath("../", 3));
EXPECT_FALSE(IsReasonablePath("/./", 3));
EXPECT_FALSE(IsReasonablePath("/../", 4));
EXPECT_FALSE(IsReasonablePath("x/.", 3));
EXPECT_FALSE(IsReasonablePath("x/..", 4));
EXPECT_FALSE(IsReasonablePath("x/./", 4));
EXPECT_FALSE(IsReasonablePath("x/../", 5));
EXPECT_FALSE(IsReasonablePath("/x/./", 5));
EXPECT_FALSE(IsReasonablePath("/x/../", 6));
}
TEST(IsReasonablePath, testNoncanonicalWindowsDirs_areForbidden) {
EXPECT_FALSE(IsReasonablePath(".", 1));
EXPECT_FALSE(IsReasonablePath("..", 2));
EXPECT_FALSE(IsReasonablePath("\\.", 2));
EXPECT_FALSE(IsReasonablePath("\\..", 3));
EXPECT_FALSE(IsReasonablePath(".\\", 2));
EXPECT_FALSE(IsReasonablePath("..\\", 3));
EXPECT_FALSE(IsReasonablePath("\\.\\", 3));
EXPECT_FALSE(IsReasonablePath("\\..\\", 4));
EXPECT_FALSE(IsReasonablePath("x\\.", 3));
EXPECT_FALSE(IsReasonablePath("x\\..", 4));
EXPECT_FALSE(IsReasonablePath("x\\.\\", 4));
EXPECT_FALSE(IsReasonablePath("x\\..\\", 5));
EXPECT_FALSE(IsReasonablePath("\\x\\.\\", 5));
EXPECT_FALSE(IsReasonablePath("\\x\\..\\", 6));
}
TEST(IsReasonablePath, testOverlongSlashDot_isDetected) {
EXPECT_FALSE(IsReasonablePath("/\300\256", 3)); /* /. */
EXPECT_TRUE(IsReasonablePath("/\300\257", 3)); /* // */
EXPECT_FALSE(IsReasonablePath("\300\256\300\256", 4)); /* .. */
}
BENCH(IsReasonablePath, bench) {
EZBENCH2("IsReasonablePath", donothing, IsReasonablePath("/index.html", 11));
}

View File

@ -242,7 +242,7 @@ Accept: text/plain\r\n\
EXPECT_STREQ("text/plain", gc(slice(m, req->xheaders.p[0].v)));
}
TEST(HeaderHasSubstring, testHeaderSpansMultipleLines) {
TEST(HeaderHas, testHeaderSpansMultipleLines) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
Accept-Encoding: deflate\r\n\
@ -250,20 +250,20 @@ ACCEPT-ENCODING: gzip\r\n\
ACCEPT-encoding: bzip2\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "gzip", -1));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "deflate", -1));
EXPECT_FALSE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "funzip", -1));
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "gzip", -1));
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "deflate", -1));
EXPECT_FALSE(HeaderHas(req, m, kHttpAcceptEncoding, "funzip", -1));
}
TEST(HeaderHasSubstring, testHeaderOnSameLIne) {
TEST(HeaderHas, testHeaderOnSameLIne) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
Accept-Encoding: deflate, gzip, bzip2\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "gzip", -1));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "deflate", -1));
EXPECT_FALSE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "funzip", -1));
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "gzip", -1));
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "deflate", -1));
EXPECT_FALSE(HeaderHas(req, m, kHttpAcceptEncoding, "funzip", -1));
}
TEST(ParseHttpRequest, testHeaderValuesWithWhitespace_getsTrimmed) {
@ -273,6 +273,7 @@ User-Agent: \t hi there \t \r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("hi there", gc(slice(m, req->headers[kHttpUserAgent])));
EXPECT_STREQ("*", gc(slice(m, req->uri)));
}
TEST(ParseHttpRequest, testAbsentHost_setsSliceToZero) {
@ -373,7 +374,7 @@ BENCH(ParseHttpRequest, bench) {
EZBENCH2("DoUnstandardChromeRequest", donothing, DoUnstandardChromeRequest());
}
BENCH(HeaderHasSubstring, bench) {
BENCH(HeaderHas, bench) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
X-In-Your-Way-A: a\r\n\
@ -384,12 +385,12 @@ ACCEPT-ENCODING: gzip\r\n\
ACCEPT-encoding: bzip2\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EZBENCH2("HeaderHasSubstring text/plain", donothing,
HeaderHasSubstring(req, m, kHttpAccept, "text/plain", 7));
EZBENCH2("HeaderHasSubstring deflate", donothing,
HeaderHasSubstring(req, m, kHttpAcceptEncoding, "deflate", 7));
EZBENCH2("HeaderHasSubstring gzip", donothing,
HeaderHasSubstring(req, m, kHttpAcceptEncoding, "gzip", 4));
EZBENCH2("HeaderHas text/plain", donothing,
HeaderHas(req, m, kHttpAccept, "text/plain", 7));
EZBENCH2("HeaderHas deflate", donothing,
HeaderHas(req, m, kHttpAcceptEncoding, "deflate", 7));
EZBENCH2("HeaderHas gzip", donothing,
HeaderHas(req, m, kHttpAcceptEncoding, "gzip", 4));
EZBENCH2("IsMimeType", donothing,
IsMimeType("text/plain; charset=utf-8", -1, "text/plain"));
}

View File

@ -22,6 +22,7 @@
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
#include "net/http/url.h"
TEST(ParseUrl, testEmpty) {
@ -160,8 +161,7 @@ TEST(ParseUrl, testUrl) {
ASSERT_EQ('b', h.user.p[0]);
ASSERT_EQ(1, h.pass.n);
ASSERT_EQ('B', h.pass.p[0]);
ASSERT_EQ(1, h.host.n);
ASSERT_EQ('c', h.host.p[0]);
ASSERT_STREQ("c", gc(strndup(h.host.p, h.host.n)));
ASSERT_EQ(1, h.port.n);
ASSERT_EQ('C', h.port.p[0]);
ASSERT_EQ(2, h.path.n);
@ -380,6 +380,16 @@ TEST(ParseHost, testObviouslyIllegalIpLiteral_getsTreatedAsRegName) {
ASSERT_STREQ("//vf.%3A%3A1%00", gc(EncodeUrl(&h, 0)));
}
TEST(ParseHost, testUnclosedIpv6_doesntSetPort) {
struct Url h = {0};
gc(ParseHost("2001:db8:cafe::17", -1, &h));
gc(h.params.p);
ASSERT_STREQ("2001:db8:cafe::17", gc(strndup(h.host.p, h.host.n)));
ASSERT_EQ(0, h.port.n);
ASSERT_EQ(0, h.port.p);
ASSERT_STREQ("//2001%3Adb8%3Acafe%3A%3A17", gc(EncodeUrl(&h, 0)));
}
TEST(EncodeUrl, testHostPortPlacedInHostField_ungoodIdea) {
struct Url h = {0};
h.host.n = strlen("foo.example:80");
@ -696,6 +706,8 @@ BENCH(ParseUrl, bench) {
free(ParseUrl("a://b@c/?zd#f", -1, &h));
free(h.params.p);
}));
EZBENCH2("ParseHost", donothing, free(ParseHost("127.0.0.1:34832", 15, &h)));
EZBENCH2("ParseIp", donothing, ParseIp("127.0.0.1", 9));
}
BENCH(EncodeUrl, bench) {

View File

@ -0,0 +1,40 @@
/*-*- 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/runtime/gc.internal.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "net/http/escape.h"
TEST(Underlong, test) {
size_t n;
EXPECT_BINEQ(u"e e", gc(Underlong("e\300\200e", -1, &n)));
EXPECT_EQ(3, n);
}
TEST(Underlong, testNormalText) {
size_t n;
EXPECT_STREQ(kHyperion, gc(Underlong(kHyperion, kHyperionSize, &n)));
EXPECT_EQ(kHyperionSize, n);
}
BENCH(Underlong, bench) {
EZBENCH2("Underlong", donothing,
free(Underlong(kHyperion, kHyperionSize, 0)));
}

View File

@ -16,24 +16,34 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/runtime/gc.internal.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
TEST(VisualizeControlCodes, test) {
EXPECT_STREQ("hello", VisualizeControlCodes("hello", -1, 0));
EXPECT_STREQ("hello\r\n", VisualizeControlCodes("hello\r\n", -1, 0));
EXPECT_STREQ("hello␁␂␡", VisualizeControlCodes("hello\1\2\177", -1, 0));
EXPECT_STREQ("hello\\u0085", VisualizeControlCodes("hello\302\205", -1, 0));
EXPECT_STREQ("hello", gc(VisualizeControlCodes("hello", -1, 0)));
EXPECT_STREQ("hello\r\n", gc(VisualizeControlCodes("hello\r\n", -1, 0)));
EXPECT_STREQ("hello␁␂␡", gc(VisualizeControlCodes("hello\1\2\177", -1, 0)));
EXPECT_STREQ("hello\\u0085",
gc(VisualizeControlCodes("hello\302\205", -1, 0)));
}
TEST(VisualizeControlCodes, testOom_returnsNullAndSetsSizeToZero) {
size_t n = 31337;
EXPECT_EQ(NULL, VisualizeControlCodes("hello", 0x1000000000000, &n));
EXPECT_EQ(NULL, gc(VisualizeControlCodes("hello", 0x1000000000000, &n)));
EXPECT_EQ(0, n);
}
TEST(VisualizeControlCodes, testWeirdHttp) {
size_t n = 31337;
char *p, B[] = "\0GET /redbean.lua\n\n";
ASSERT_NE(0, (p = gc(VisualizeControlCodes(B, sizeof(B), &n))));
EXPECT_STREQ("\"␀GET /redbean.lua\\n\\n␀\"", gc(xasprintf("%`'.*s", n, p)));
}
BENCH(VisualizeControlCodes, bench) {
EZBENCH2("VisualizeControlCodes", donothing,
free(VisualizeControlCodes(kHyperion, kHyperionSize, 0)));