/* * Copyright (C) 2016 Canon Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted, provided that the following conditions * are required to be met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Canon Inc. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "FetchHeaders.h" #include "HTTPParsers.h" namespace WebCore { // https://fetch.spec.whatwg.org/#concept-headers-remove-privileged-no-cors-request-headers static void removePrivilegedNoCORSRequestHeaders(HTTPHeaderMap& headers) { headers.remove(HTTPHeaderName::Range); } static ExceptionOr 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 }; return true; } static ExceptionOr canWriteHeader(const String& name, const String& value, const String& combinedValue, FetchHeaders::Guard guard) { if (!isValidHTTPToken(name)) return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; 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 }; return true; } static ExceptionOr appendToHeaderMap(const String& name, const String& value, HTTPHeaderMap& headers, FetchHeaders::Guard guard) { 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); if (canWriteResult.hasException()) return canWriteResult.releaseException(); if (!canWriteResult.releaseReturnValue()) return {}; headers.set(name, combinedValue); // if (guard == FetchHeaders::Guard::RequestNoCors) // removePrivilegedNoCORSRequestHeaders(headers); return {}; } static ExceptionOr appendToHeaderMap(const HTTPHeaderMap::HTTPHeaderMapConstIterator::KeyValue& header, HTTPHeaderMap& headers, FetchHeaders::Guard guard) { String normalizedValue = stripLeadingAndTrailingHTTPSpaces(header.value); auto canWriteResult = canWriteHeader(header.key, normalizedValue, header.value, guard); if (canWriteResult.hasException()) return canWriteResult.releaseException(); if (!canWriteResult.releaseReturnValue()) return {}; if (header.keyAsHTTPHeaderName) headers.add(header.keyAsHTTPHeaderName.value(), header.value); else headers.add(header.key, header.value); return {}; } // https://fetch.spec.whatwg.org/#concept-headers-fill static ExceptionOr fillHeaderMap(HTTPHeaderMap& headers, const FetchHeaders::Init& headersInit, FetchHeaders::Guard guard) { if (std::holds_alternative>>(headersInit)) { auto& sequence = std::get>>(headersInit); for (auto& header : sequence) { if (header.size() != 2) return Exception { TypeError, "Header sub-sequence must contain exactly two items"_s }; auto result = appendToHeaderMap(header[0], header[1], headers, guard); if (result.hasException()) return result.releaseException(); } } else { auto& record = std::get>>(headersInit); for (auto& header : record) { auto result = appendToHeaderMap(header.key, header.value, headers, guard); if (result.hasException()) return result.releaseException(); } } return {}; } ExceptionOr> FetchHeaders::create(std::optional&& headersInit) { HTTPHeaderMap headers; if (headersInit) { auto result = fillHeaderMap(headers, *headersInit, Guard::None); if (result.hasException()) return result.releaseException(); } return adoptRef(*new FetchHeaders { Guard::None, WTFMove(headers) }); } ExceptionOr FetchHeaders::fill(const Init& headerInit) { return fillHeaderMap(m_headers, headerInit, m_guard); } ExceptionOr FetchHeaders::fill(const FetchHeaders& otherHeaders) { if (this->size() == 0) { HTTPHeaderMap headers; headers.commonHeaders().appendVector(otherHeaders.m_headers.commonHeaders()); headers.uncommonHeaders().appendVector(otherHeaders.m_headers.uncommonHeaders()); headers.getSetCookieHeaders().appendVector(otherHeaders.m_headers.getSetCookieHeaders()); setInternalHeaders(WTFMove(headers)); m_updateCounter++; return {}; } for (auto& header : otherHeaders.m_headers) { auto result = appendToHeaderMap(header, m_headers, m_guard); if (result.hasException()) return result.releaseException(); } return {}; } ExceptionOr FetchHeaders::append(const String& name, const String& value) { ++m_updateCounter; return appendToHeaderMap(name, value, m_headers, m_guard); } // https://fetch.spec.whatwg.org/#dom-headers-delete ExceptionOr FetchHeaders::remove(const String& name) { if (!isValidHTTPToken(name)) return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; if (m_guard == FetchHeaders::Guard::Immutable) return Exception { TypeError, "Headers object's guard is 'immutable'"_s }; if (m_guard == FetchHeaders::Guard::Request && isForbiddenHeaderName(name)) return {}; if (m_guard == FetchHeaders::Guard::RequestNoCors && !isNoCORSSafelistedRequestHeaderName(name) && !isPriviledgedNoCORSRequestHeaderName(name)) return {}; if (m_guard == FetchHeaders::Guard::Response && isForbiddenResponseHeaderName(name)) return {}; ++m_updateCounter; m_headers.remove(name); if (m_guard == FetchHeaders::Guard::RequestNoCors) removePrivilegedNoCORSRequestHeaders(m_headers); return {}; } ExceptionOr FetchHeaders::get(const String& name) const { if (!isValidHTTPToken(name)) return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; return m_headers.get(name); } ExceptionOr FetchHeaders::has(const String& name) const { if (!isValidHTTPToken(name)) return Exception { TypeError, makeString("Invalid header name: '", name, "'") }; return m_headers.contains(name); } ExceptionOr FetchHeaders::set(const String& name, const String& value) { String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); auto canWriteResult = canWriteHeader(name, normalizedValue, normalizedValue, m_guard); if (canWriteResult.hasException()) return canWriteResult.releaseException(); if (!canWriteResult.releaseReturnValue()) return {}; ++m_updateCounter; m_headers.set(name, normalizedValue); if (m_guard == FetchHeaders::Guard::RequestNoCors) removePrivilegedNoCORSRequestHeaders(m_headers); return {}; } void FetchHeaders::filterAndFill(const HTTPHeaderMap& headers, Guard guard) { for (auto& header : headers) { String normalizedValue = stripLeadingAndTrailingHTTPSpaces(header.value); auto canWriteResult = canWriteHeader(header.key, normalizedValue, header.value, guard); if (canWriteResult.hasException()) continue; if (!canWriteResult.releaseReturnValue()) continue; if (header.keyAsHTTPHeaderName) m_headers.add(header.keyAsHTTPHeaderName.value(), header.value); else m_headers.add(header.key, header.value); } } static NeverDestroyed setCookieLowercaseString(MAKE_STATIC_STRING_IMPL("set-cookie")); std::optional> 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.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 { WTFMove(key), WTFMove(cookie) }; } auto value = m_headers->m_headers.get(key); if (!value.isNull()) return KeyValuePair { WTFMove(key), WTFMove(value) }; } return std::nullopt; } FetchHeaders::Iterator::Iterator(FetchHeaders& headers) : m_headers(headers) { m_cookieIndex = 0; } } // namespace WebCore -only-component-css Unnamed repository; edit this file 'description' to name the repository.
aboutsummaryrefslogtreecommitdiff
AgeCommit message (Expand)AuthorFilesLines
2024-11-13[ci] release (beta) (#12405)astro@5.0.0-beta.8Gravatar Houston (Bot) 29-54/+125
2024-11-13[ci] release (#12422)astro@4.16.12Gravatar Houston (Bot) 28-54/+55
2024-11-13fix(routing): middleware in dev (#12420)Gravatar Emanuele Stoppa 6-9/+52
2024-11-12[ci] release (#12403)astro@4.16.11Gravatar Houston (Bot) 30-73/+68
2024-11-11fix(routing): emit error for forbidden rewrite (#12339)Gravatar Emanuele Stoppa 7-0/+88
2024-11-11[ci] formatGravatar Sarah Rainsberger 1-38/+38
2024-11-11update markdown config docs (#12382)Gravatar Sarah Rainsberger 1-4/+45
2024-11-08Actions middleware (#12373)Gravatar Ben Holmes 26-311/+483
2024-11-08Fix incorrect status code in dev server for action errors (#12401)Gravatar Ben Holmes 2-13/+21
2024-11-08fix(actions): better runtime check for invalid usages (#12402)Gravatar Emanuele Stoppa 8-5/+47
2024-11-08fix: error overlay message escape (#12305)Gravatar Florian Lefebvre 3-0/+28
2024-11-06[ci] release (beta) (#12370)create-astro@4.11.0-beta.0astro@5.0.0-beta.7@astrojs/svelte@6.0.0-beta.2Gravatar Houston (Bot) 31-55/+84
2024-11-06Add svelte changesetGravatar bluwy 5-39/+51
2024-11-06[ci] release (#12369)astro@4.16.10@astrojs/svelte@5.7.3Gravatar Houston (Bot) 34-81/+74
2024-11-06Update to Vite 6.0.0-beta.6 (#12323)Gravatar Bjorn Lu 15-100/+123
2024-11-06[ci] formatGravatar Bjorn Lu 2-18/+17
2024-11-06Add support for Svelte 5 @render syntax (#12390)Gravatar Bjorn Lu 3-0/+30
2024-11-06fix(deps): update all non-major dependencies (#12366)Gravatar renovate[bot] 29-533/+552