diff options
author | 2022-12-02 07:40:22 -0800 | |
---|---|---|
committer | 2022-12-02 07:42:44 -0800 | |
commit | d84f79bcc16d4e748c8a9400ea1cdb03d7f963fb (patch) | |
tree | 876d0a65d75c0f6aec97038e0d9f89c09f915c3f /src/bun.js/bindings/webcore/FetchHeaders.cpp | |
parent | 917cbc8d5d0ea9446db66910be46b7fff4697304 (diff) | |
download | bun-d84f79bcc16d4e748c8a9400ea1cdb03d7f963fb.tar.gz bun-d84f79bcc16d4e748c8a9400ea1cdb03d7f963fb.tar.zst bun-d84f79bcc16d4e748c8a9400ea1cdb03d7f963fb.zip |
[fetch] Implement `Headers#getAll` and `Headers#getSetCookie()`
This matches Deno's behavior (get() combines, iterator preserves the order, set and append combine), but implements both the Cloudflare Workers `getAll()` and the potential standard `getSetCookie` function. The rationale for choosing both is to better support libraries which check for `getAll` and also because `getSetCookie` seems a little confusing (names are hard)
This also makes `.toJSON` and JSON.stringify return an array for `Set-Cookie`
Diffstat (limited to 'src/bun.js/bindings/webcore/FetchHeaders.cpp')
-rw-r--r-- | src/bun.js/bindings/webcore/FetchHeaders.cpp | 53 |
1 files changed, 52 insertions, 1 deletions
diff --git a/src/bun.js/bindings/webcore/FetchHeaders.cpp b/src/bun.js/bindings/webcore/FetchHeaders.cpp index 713b8a6ac..7834836c7 100644 --- a/src/bun.js/bindings/webcore/FetchHeaders.cpp +++ b/src/bun.js/bindings/webcore/FetchHeaders.cpp @@ -39,6 +39,18 @@ static void removePrivilegedNoCORSRequestHeaders(HTTPHeaderMap& headers) headers.remove(HTTPHeaderName::Range); } +static ExceptionOr<bool> canWriteHeader(const HTTPHeaderName name, const String& value, const String& combinedValue, FetchHeaders::Guard guard) +{ + ASSERT(value.isEmpty() || (!isHTTPSpace(value[0]) && !isHTTPSpace(value[value.length() - 1]))); + if (!isValidHTTPHeaderValue((value))) + return Exception { TypeError, makeString("Header '", name, "' has invalid value: '", value, "'") }; + if (guard == FetchHeaders::Guard::Immutable) + return Exception { TypeError, "Headers object's guard is 'immutable'"_s }; + if (guard == FetchHeaders::Guard::RequestNoCors && !combinedValue.isEmpty()) + return false; + return true; +} + static ExceptionOr<bool> canWriteHeader(const String& name, const String& value, const String& combinedValue, FetchHeaders::Guard guard) { if (!isValidHTTPToken(name)) @@ -61,6 +73,31 @@ static ExceptionOr<void> appendToHeaderMap(const String& name, const String& val { String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); String combinedValue = normalizedValue; + HTTPHeaderName headerName; + if (findHTTPHeaderName(name, headerName)) { + + if (headerName != HTTPHeaderName::SetCookie) { + if (headers.contains(headerName)) { + combinedValue = makeString(headers.get(headerName), ", ", normalizedValue); + } + } + + auto canWriteResult = canWriteHeader(headerName, normalizedValue, combinedValue, guard); + + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return {}; + + if (headerName != HTTPHeaderName::SetCookie) { + headers.set(headerName, combinedValue); + } else { + headers.add(headerName, normalizedValue); + } + + return {}; + } + if (headers.contains(name)) combinedValue = makeString(headers.get(name), ", ", normalizedValue); auto canWriteResult = canWriteHeader(name, normalizedValue, combinedValue, guard); @@ -222,28 +259,42 @@ void FetchHeaders::filterAndFill(const HTTPHeaderMap& headers, Guard guard) } } +static NeverDestroyed<const String> setCookieLowercaseString(MAKE_STATIC_STRING_IMPL("set-cookie")); + std::optional<KeyValuePair<String, String>> FetchHeaders::Iterator::next() { if (m_keys.isEmpty() || m_updateCounter != m_headers->m_updateCounter) { m_keys.resize(0); m_keys.reserveCapacity(m_headers->m_headers.size()); for (auto& header : m_headers->m_headers) - m_keys.uncheckedAppend(header.key.convertToASCIILowercase()); + m_keys.uncheckedAppend(header.asciiLowerCaseName()); std::sort(m_keys.begin(), m_keys.end(), WTF::codePointCompareLessThan); m_updateCounter = m_headers->m_updateCounter; + m_cookieIndex = 0; } + + auto& setCookieHeaders = m_headers->m_headers.getSetCookieHeaders(); + while (m_currentIndex < m_keys.size()) { auto key = m_keys[m_currentIndex++]; + + if (!setCookieHeaders.isEmpty() && key == setCookieLowercaseString) { + auto cookie = setCookieHeaders[m_cookieIndex++]; + return KeyValuePair<String, String> { WTFMove(key), WTFMove(cookie) }; + } + auto value = m_headers->m_headers.get(key); if (!value.isNull()) return KeyValuePair<String, String> { WTFMove(key), WTFMove(value) }; } + return std::nullopt; } FetchHeaders::Iterator::Iterator(FetchHeaders& headers) : m_headers(headers) { + m_cookieIndex = 0; } } // namespace WebCore |