Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions libc/str/isutf8.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ static const char kUtf8Dispatch[] = {
1, 1, 1, 1, 1, 1, 1, 1, // 0320
1, 1, 1, 1, 1, 1, 1, 1, // 0330
2, 3, 3, 3, 3, 3, 3, 3, // 0340 utf8-3
3, 3, 3, 3, 3, 3, 3, 3, // 0350
4, 5, 5, 5, 5, 0, 0, 0, // 0360 utf8-4
3, 3, 3, 3, 3, 4, 3, 3, // 0350
5, 6, 6, 6, 7, 0, 0, 0, // 0360 utf8-4
0, 0, 0, 0, 0, 0, 0, 0, // 0370
};

Expand All @@ -45,6 +45,8 @@ static const char kUtf8Dispatch[] = {
* - Incorrect sequencing of 0300 (FIRST) and 0200 (CONT) chars
* - Thompson-Pike varint sequence not encodable as UTF-16
* - Overlong UTF-8 encoding
* - any UTF-16 surrogate code points
* - last character in a multi-byte UTF-8 sequence exceeds the valid limit
*
* @param size if -1 implies strlen
*/
Expand Down Expand Up @@ -95,6 +97,7 @@ bool32 isutf8(const void *data, size_t size) {
}
// fallthrough
case 3:
case3:
if (p + 2 <= e && //
(p[0] & 0300) == 0200 && //
(p[1] & 0300) == 0200) { //
Expand All @@ -104,11 +107,17 @@ bool32 isutf8(const void *data, size_t size) {
return false; // missing cont
}
case 4:
if (p < e && (*p & 040)) {
return false; // utf-16 surrogate
}
goto case3;
case 5:
if (p < e && (*p & 0377) < 0220) {
return false; // overlong
}
// fallthrough
case 5:
case 6:
case6:
if (p + 3 <= e && //
(((uint32_t)(p[+2] & 0377) << 030 | //
(uint32_t)(p[+1] & 0377) << 020 | //
Expand All @@ -120,6 +129,11 @@ bool32 isutf8(const void *data, size_t size) {
} else {
return false; // missing cont
}
case 7:
if (p < e && (*p & 0x3F) > 0xF) {
return false; // over limit
}
goto case6;
default:
__builtin_unreachable();
}
Expand Down
1 change: 1 addition & 0 deletions net/http/gethttpheader.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,4 @@ CF-Visitor, kHttpCfVisitor
CF-Connecting-IP, kHttpCfConnectingIp
CF-IPCountry, kHttpCfIpcountry
CDN-Loop, kHttpCdnLoop
Sec-WebSocket-Key, kHttpWebSocketKey
7 changes: 5 additions & 2 deletions net/http/gethttpheader.inc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#line 12 "gethttpheader.gperf"
struct thatispacked HttpHeaderSlot { char *name; char code; };

#define TOTAL_KEYWORDS 93
#define TOTAL_KEYWORDS 94
#define MIN_WORD_LENGTH 2
#define MAX_WORD_LENGTH 32
#define MIN_HASH_VALUE 3
Expand Down Expand Up @@ -387,7 +387,10 @@ LookupHttpHeader (register const char *str, register size_t len)
#line 87 "gethttpheader.gperf"
{"Strict-Transport-Security", kHttpStrictTransportSecurity},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""},
{""}, {""},
#line 107 "gethttpheader.gperf"
{"Sec-WebSocket-Key", kHttpWebSocketKey},
{""}, {""},
#line 22 "gethttpheader.gperf"
{"X-Forwarded-For", kHttpXForwardedFor},
{""},
Expand Down
2 changes: 2 additions & 0 deletions net/http/gethttpheadername.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ const char *GetHttpHeaderName(int h) {
return "CDN-Loop";
case kHttpSecChUaPlatform:
return "Sec-CH-UA-Platform";
case kHttpWebSocketKey:
return "Sec-WebSocket-Key";
default:
return NULL;
}
Expand Down
3 changes: 2 additions & 1 deletion net/http/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@
#define kHttpCfIpcountry 90
#define kHttpSecChUaPlatform 91
#define kHttpCdnLoop 92
#define kHttpHeadersMax 93
#define kHttpWebSocketKey 93
#define kHttpHeadersMax 94

COSMOPOLITAN_C_START_

Expand Down
14 changes: 10 additions & 4 deletions test/libc/str/isutf8_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,16 @@ TEST(isutf8, good) {
}

TEST(isutf8, bad) {
ASSERT_FALSE(isutf8("\300\200", -1)); // overlong nul
ASSERT_FALSE(isutf8("\200\300", -1)); // latin1 c1 control code
ASSERT_FALSE(isutf8("\300\300", -1)); // missing continuation
ASSERT_FALSE(isutf8("\377\200\200\200\200", -1)); // thompson-pike varint
ASSERT_FALSE(isutf8("\300\200", -1)); // overlong nul
ASSERT_FALSE(isutf8("\200\300", -1)); // latin1 c1 control code
ASSERT_FALSE(isutf8("\300\300", -1)); // missing continuation
ASSERT_FALSE(isutf8("\377\200\200\200\200", -1)); // thompson-pike varint
ASSERT_FALSE(isutf8("\355\240\200", -1)); // single utf-16 surrogate (high)
ASSERT_FALSE(isutf8("\355\277\277", -1)); // single utf-16 surrogate (low)
ASSERT_FALSE(isutf8("\355\240\200\355\260\200", -1)); // paired utf-16 surrogates (range begin)
ASSERT_FALSE(isutf8("\355\257\277\355\277\277", -1)); // paired utf-16 surrogates (range end)
ASSERT_FALSE(isutf8("\364\220\200\200", -1)); // boundary condition
ASSERT_FALSE(isutf8("\220", -1)); // boundary condition
}

TEST(isutf8, oob) {
Expand Down
6 changes: 6 additions & 0 deletions test/tool/net/BUILD.mk
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,17 @@ o/$(MODE)/test/tool/net/%.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)


o/$(MODE)/tool/net/tester/.init.lua.zip.o: private \
ZIPOBJ_FLAGS += \
-B

.PRECIOUS: o/$(MODE)/test/tool/net/redbean-tester
o/$(MODE)/test/tool/net/redbean-tester.dbg: \
$(TOOL_NET_DEPS) \
o/$(MODE)/tool/net/redbean.o \
$(TOOL_NET_REDBEAN_LUA_MODULES) \
o/$(MODE)/tool/net/tester/.init.lua.zip.o \
o/$(MODE)/tool/net/demo/seekable.txt.zip.o \
o/$(MODE)/tool/net/net.pkg \
$(CRT) \
Expand Down
42 changes: 42 additions & 0 deletions test/tool/net/redbean_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,45 @@ Z\n",
EXPECT_NE(-1, wait(0));
EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
}

TEST(redbean, testWebSockets) {
if (IsWindows())
return;
char portbuf[16];
int pid, pipefds[2];
sigset_t chldmask, savemask;
sigaddset(&chldmask, SIGCHLD);
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask));
ASSERT_NE(-1, pipe(pipefds));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
setpgrp();
close(0);
open("/dev/null", O_RDWR);
close(pipefds[0]);
dup2(pipefds[1], 1);
sigprocmask(SIG_SETMASK, &savemask, NULL);
execv("bin/redbean-tester",
(char *const[]){"bin/redbean-tester", "-vvszXp0", "-l127.0.0.1", // "-L/tmp/redbean-tester.log",
__strace > 0 ? "--strace" : 0, 0});
_exit(127);
}
EXPECT_NE(-1, close(pipefds[1]));
EXPECT_NE(-1, read(pipefds[0], portbuf, sizeof(portbuf)));
port = atoi(portbuf);
EXPECT_TRUE(Matches("HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
"Date: .*\r\n"
"Server: redbean/.*\r\n"
"\r\n",
gc(SendHttpRequest("GET /ws HTTP/1.1\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n"))));
EXPECT_EQ(0, close(pipefds[0]));
EXPECT_NE(-1, kill(pid, SIGTERM));
EXPECT_NE(-1, wait(0));
EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
}
Loading