From 0017dbec4e58d11353dfd855033e6cd3fbcb55a2 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 2 Mar 2024 21:42:57 -0800 Subject: [PATCH] Fixes #9180 (#9212) Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- packages/bun-usockets/src/context.c | 18 --------- packages/bun-uws/src/HttpParser.h | 12 +++--- .../bindings/webcore/HTTPHeaderField.cpp | 11 +++++- src/bun.js/bindings/webcore/HTTPHeaderField.h | 7 +++- src/bun.js/bindings/webcore/HTTPParsers.cpp | 11 ++++++ test/js/bun/http/bun-serve-headers.test.ts | 38 +++++++++++++++++++ 6 files changed, 70 insertions(+), 27 deletions(-) create mode 100644 test/js/bun/http/bun-serve-headers.test.ts diff --git a/packages/bun-usockets/src/context.c b/packages/bun-usockets/src/context.c index 54a98b377f..eea45424cb 100644 --- a/packages/bun-usockets/src/context.c +++ b/packages/bun-usockets/src/context.c @@ -223,17 +223,8 @@ struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t * struct us_socket_context_t *context = us_calloc(1, sizeof(struct us_socket_context_t) + context_ext_size); context->loop = loop; - context->head_sockets = 0; - context->head_listen_sockets = 0; - context->iterator = 0; - context->next = 0; context->is_low_prio = default_is_low_prio_handler; - /* Begin at 0 */ - context->timestamp = 0; - context->long_timestamp = 0; - context->global_tick = 0; - us_internal_loop_link(loop, context); /* If we are called from within SSL code, SSL code will make further changes to us */ @@ -253,17 +244,8 @@ struct us_socket_context_t *us_create_bun_socket_context(int ssl, struct us_loop struct us_socket_context_t *context = us_calloc(1, sizeof(struct us_socket_context_t) + context_ext_size); context->loop = loop; - context->head_sockets = 0; - context->head_listen_sockets = 0; - context->iterator = 0; - context->next = 0; context->is_low_prio = default_is_low_prio_handler; - /* Begin at 0 */ - context->timestamp = 0; - context->long_timestamp = 0; - context->global_tick = 0; - us_internal_loop_link(loop, context); /* If we are called from within SSL code, SSL code will make further changes to us */ diff --git a/packages/bun-uws/src/HttpParser.h b/packages/bun-uws/src/HttpParser.h index 75eedd5c7c..876ab85079 100644 --- a/packages/bun-uws/src/HttpParser.h +++ b/packages/bun-uws/src/HttpParser.h @@ -262,15 +262,15 @@ namespace uWS hasMore(x, 'z'); } - static inline void *consumeFieldName(char *p) { + static inline void *consumeFieldName(unsigned char *p) { //for (; true; p += 8) { //uint64_t word; //memcpy(&word, p, sizeof(uint64_t)); //if (notFieldNameWord(word)) { - while (isFieldNameByte(*(unsigned char *)p)) { - *(p++) |= 0x20; - } - return (void *)p; + while (isFieldNameByte(*(unsigned char *)p)) { + *(p++) |= p[0] <= 'Z' ? 32 : 0; + } + return (void *)p; //} //word |= 0x2020202020202020ull; //memcpy(p, &word, sizeof(uint64_t)); @@ -386,7 +386,7 @@ namespace uWS { /* Lower case and consume the field name */ preliminaryKey = postPaddedBuffer; - postPaddedBuffer = (char *)consumeFieldName(postPaddedBuffer); + postPaddedBuffer = (char *)consumeFieldName(reinterpret_cast(postPaddedBuffer)); headers->key = std::string_view(preliminaryKey, (size_t)(postPaddedBuffer - preliminaryKey)); /* We should not accept whitespace between key and colon, so colon must foloow immediately */ diff --git a/src/bun.js/bindings/webcore/HTTPHeaderField.cpp b/src/bun.js/bindings/webcore/HTTPHeaderField.cpp index bdcd3e3be7..189d1c3794 100644 --- a/src/bun.js/bindings/webcore/HTTPHeaderField.cpp +++ b/src/bun.js/bindings/webcore/HTTPHeaderField.cpp @@ -31,6 +31,15 @@ namespace WebCore { namespace RFC7230 { bool isTokenCharacter(UChar c) +{ + return c < 0x80 && isTokenCharacter(static_cast(c)); +} +bool isDelimiter(UChar c) +{ + return c < 0x80 && isDelimiter(static_cast(c)); +} + +bool isTokenCharacter(LChar c) { return isASCIIAlpha(c) || isASCIIDigit(c) || c == '!' || c == '#' || c == '$' @@ -40,7 +49,7 @@ bool isTokenCharacter(UChar c) || c == '`' || c == '|' || c == '~'; } -bool isDelimiter(UChar c) +bool isDelimiter(LChar c) { return c == '(' || c == ')' || c == ',' || c == '/' || c == ':' || c == ';' diff --git a/src/bun.js/bindings/webcore/HTTPHeaderField.h b/src/bun.js/bindings/webcore/HTTPHeaderField.h index c5fbc1b436..37a3624711 100644 --- a/src/bun.js/bindings/webcore/HTTPHeaderField.h +++ b/src/bun.js/bindings/webcore/HTTPHeaderField.h @@ -43,7 +43,8 @@ private: HTTPHeaderField(String&& name, String&& value) : m_name(WTFMove(name)) , m_value(WTFMove(value)) - { } + { + } String m_name; String m_value; }; @@ -68,12 +69,14 @@ std::optional HTTPHeaderField::decode(Decoder& decoder) if (!value) return std::nullopt; - return {{ WTFMove(*name), WTFMove(*value) }}; + return { { WTFMove(*name), WTFMove(*value) } }; } namespace RFC7230 { bool isTokenCharacter(UChar); bool isWhitespace(UChar); +bool isTokenCharacter(LChar); +bool isWhitespace(LChar); bool isCommentText(UChar); bool isQuotedPairSecondOctet(UChar); bool isDelimiter(UChar); diff --git a/src/bun.js/bindings/webcore/HTTPParsers.cpp b/src/bun.js/bindings/webcore/HTTPParsers.cpp index 3974fbc6b7..ac45dd26cc 100644 --- a/src/bun.js/bindings/webcore/HTTPParsers.cpp +++ b/src/bun.js/bindings/webcore/HTTPParsers.cpp @@ -197,6 +197,17 @@ bool isValidHTTPToken(StringView value) { if (value.isEmpty()) return false; + + if (value.is8Bit()) { + const LChar* characters = value.characters8(); + const LChar* end = characters + value.length(); + while (characters < end) { + if (!RFC7230::isTokenCharacter(*characters++)) + return false; + } + return true; + } + for (UChar c : value.codeUnits()) { if (!RFC7230::isTokenCharacter(c)) return false; diff --git a/test/js/bun/http/bun-serve-headers.test.ts b/test/js/bun/http/bun-serve-headers.test.ts new file mode 100644 index 0000000000..a9f032b037 --- /dev/null +++ b/test/js/bun/http/bun-serve-headers.test.ts @@ -0,0 +1,38 @@ +import { test, expect } from "bun:test"; + +// https://github.com/oven-sh/bun/issues/9180 +test("weird headers", async () => { + const server = Bun.serve({ + port: 0, + development: false, + fetch(req) { + const headers = new Headers(); + req.headers.forEach((value, key) => { + headers.append(key, value); + }); + + return new Response("OK", { + headers, + }); + }, + }); + + try { + for (let i = 0; i < 255; i++) { + const headers = new Headers(); + const name = "X-" + String.fromCharCode(i); + try { + headers.set(name, "1"); + } catch { + continue; + } + + const res = await fetch(server.url, { + headers, + }); + expect(res.headers.get(name)).toBe("1"); + } + } finally { + server.stop(true); + } +});