mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Organize Fetch Headers.h/Headers.cpp a bit
Generally just define things in the order they are declared (will make a change to use ByteString in this file a bit easier to follow). Also make a couple of free functions be class methods on Header / HeaderList.
This commit is contained in:
parent
556364fd76
commit
d70224ad2e
Notes:
github-actions[bot]
2025-11-26 14:16:37 +00:00
Author: https://github.com/trflynn89
Commit: d70224ad2e
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6933
7 changed files with 372 additions and 386 deletions
|
|
@ -101,7 +101,7 @@ GC::Ref<PolicyList> Policy::parse_a_responses_content_security_policies(GC::Heap
|
|||
|
||||
// 2. For each token returned by extracting header list values given Content-Security-Policy and response’s header
|
||||
// list:
|
||||
auto enforce_policy_tokens_or_failure = Fetch::Infrastructure::extract_header_list_values("Content-Security-Policy"sv.bytes(), response->header_list());
|
||||
auto enforce_policy_tokens_or_failure = response->header_list()->extract_header_list_values("Content-Security-Policy"sv.bytes());
|
||||
auto enforce_policy_tokens = enforce_policy_tokens_or_failure.has<Vector<ByteBuffer>>() ? enforce_policy_tokens_or_failure.get<Vector<ByteBuffer>>() : Vector<ByteBuffer> {};
|
||||
for (auto const& enforce_policy_token : enforce_policy_tokens) {
|
||||
// 1. Let policy be the result of parsing token, with a source of "header", and a disposition of "enforce".
|
||||
|
|
@ -115,7 +115,7 @@ GC::Ref<PolicyList> Policy::parse_a_responses_content_security_policies(GC::Heap
|
|||
|
||||
// 3. For each token returned by extracting header list values given Content-Security-Policy-Report-Only and
|
||||
// response’s header list:
|
||||
auto report_policy_tokens_or_failure = Fetch::Infrastructure::extract_header_list_values("Content-Security-Policy-Report-Only"sv.bytes(), response->header_list());
|
||||
auto report_policy_tokens_or_failure = response->header_list()->extract_header_list_values("Content-Security-Policy-Report-Only"sv.bytes());
|
||||
auto report_policy_tokens = report_policy_tokens_or_failure.has<Vector<ByteBuffer>>() ? report_policy_tokens_or_failure.get<Vector<ByteBuffer>>() : Vector<ByteBuffer> {};
|
||||
for (auto const& report_policy_token : report_policy_tokens) {
|
||||
// 1. Let policy be the result of parsing token, with a source of "header", and a disposition of "report".
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ GC::Ptr<PendingResponse> main_fetch(JS::Realm& realm, Infrastructure::FetchParam
|
|||
if (request->response_tainting() == Infrastructure::Request::ResponseTainting::CORS) {
|
||||
// 1. Let headerNames be the result of extracting header list values given
|
||||
// `Access-Control-Expose-Headers` and response’s header list.
|
||||
auto header_names_or_failure = Infrastructure::extract_header_list_values("Access-Control-Expose-Headers"sv.bytes(), response->header_list());
|
||||
auto header_names_or_failure = response->header_list()->extract_header_list_values("Access-Control-Expose-Headers"sv.bytes());
|
||||
auto header_names = header_names_or_failure.has<Vector<ByteBuffer>>() ? header_names_or_failure.get<Vector<ByteBuffer>>() : Vector<ByteBuffer> {};
|
||||
|
||||
// 2. If request’s credentials mode is not "include" and headerNames contains `*`, then set
|
||||
|
|
@ -2513,19 +2513,19 @@ GC::Ref<PendingResponse> cors_preflight_fetch(JS::Realm& realm, Infrastructure::
|
|||
// NOTE: The CORS check is done on request rather than preflight to ensure the correct credentials mode is used.
|
||||
if (cors_check(request, response) && Infrastructure::is_ok_status(response->status())) {
|
||||
// 1. Let methods be the result of extracting header list values given `Access-Control-Allow-Methods` and response’s header list.
|
||||
auto methods_or_failure = Infrastructure::extract_header_list_values("Access-Control-Allow-Methods"sv.bytes(), response->header_list());
|
||||
auto methods_or_failure = response->header_list()->extract_header_list_values("Access-Control-Allow-Methods"sv.bytes());
|
||||
|
||||
// 2. Let headerNames be the result of extracting header list values given `Access-Control-Allow-Headers` and
|
||||
// response’s header list.
|
||||
auto header_names_or_failure = Infrastructure::extract_header_list_values("Access-Control-Allow-Headers"sv.bytes(), response->header_list());
|
||||
auto header_names_or_failure = response->header_list()->extract_header_list_values("Access-Control-Allow-Headers"sv.bytes());
|
||||
|
||||
// 3. If either methods or headerNames is failure, return a network error.
|
||||
if (methods_or_failure.has<Infrastructure::ExtractHeaderParseFailure>()) {
|
||||
if (methods_or_failure.has<Infrastructure::HeaderList::ExtractHeaderParseFailure>()) {
|
||||
returned_pending_response->resolve(Infrastructure::Response::network_error(vm, "The Access-Control-Allow-Methods in the CORS-preflight response is syntactically invalid"_string));
|
||||
return;
|
||||
}
|
||||
|
||||
if (header_names_or_failure.has<Infrastructure::ExtractHeaderParseFailure>()) {
|
||||
if (header_names_or_failure.has<Infrastructure::HeaderList::ExtractHeaderParseFailure>()) {
|
||||
returned_pending_response->resolve(Infrastructure::Response::network_error(vm, "The Access-Control-Allow-Headers in the CORS-preflight response is syntactically invalid"_string));
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,27 +66,40 @@ Header Header::from_latin1_pair(StringView name, StringView value)
|
|||
};
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#extract-header-values
|
||||
Optional<Vector<ByteBuffer>> Header::extract_header_values() const
|
||||
{
|
||||
// FIXME: 1. If parsing header’s value, per the ABNF for header’s name, fails, then return failure.
|
||||
// FIXME: 2. Return one or more values resulting from parsing header’s value, per the ABNF for header’s name.
|
||||
|
||||
// For now we only parse some headers that are of the ABNF list form "#something"
|
||||
if (StringView { name }.is_one_of_ignoring_ascii_case(
|
||||
"Access-Control-Request-Headers"sv,
|
||||
"Access-Control-Expose-Headers"sv,
|
||||
"Access-Control-Allow-Headers"sv,
|
||||
"Access-Control-Allow-Methods"sv)
|
||||
&& !value.is_empty()) {
|
||||
auto split_values = StringView { value }.split_view(',');
|
||||
Vector<ByteBuffer> trimmed_values;
|
||||
|
||||
for (auto const& value : split_values) {
|
||||
auto trimmed_value = value.trim(" \t"sv);
|
||||
auto trimmed_value_as_byte_buffer = MUST(ByteBuffer::copy(trimmed_value.bytes()));
|
||||
trimmed_values.append(move(trimmed_value_as_byte_buffer));
|
||||
}
|
||||
|
||||
return trimmed_values;
|
||||
}
|
||||
|
||||
// This always ignores the ABNF rules for now and returns the header value as a single list item.
|
||||
return Vector { MUST(ByteBuffer::copy(value)) };
|
||||
}
|
||||
|
||||
GC::Ref<HeaderList> HeaderList::create(JS::VM& vm)
|
||||
{
|
||||
return vm.heap().allocate<HeaderList>();
|
||||
}
|
||||
|
||||
// Non-standard
|
||||
Vector<ByteBuffer> HeaderList::unique_names() const
|
||||
{
|
||||
Vector<ByteBuffer> header_names_set;
|
||||
HashTable<ReadonlyBytes, CaseInsensitiveBytesTraits<u8 const>> header_names_seen;
|
||||
|
||||
for (auto const& header : *this) {
|
||||
if (header_names_seen.contains(header.name))
|
||||
continue;
|
||||
header_names_seen.set(header.name);
|
||||
header_names_set.append(MUST(ByteBuffer::copy(header.name)));
|
||||
}
|
||||
|
||||
return header_names_set;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#header-list-contains
|
||||
bool HeaderList::contains(ReadonlyBytes name) const
|
||||
{
|
||||
|
|
@ -138,60 +151,6 @@ Optional<Vector<String>> HeaderList::get_decode_and_split(ReadonlyBytes name) co
|
|||
return get_decode_and_split_header_value(*value);
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#header-value-get-decode-and-split
|
||||
Optional<Vector<String>> get_decode_and_split_header_value(ReadonlyBytes value)
|
||||
{
|
||||
// To get, decode, and split a header value value, run these steps:
|
||||
|
||||
// 1. Let input be the result of isomorphic decoding value.
|
||||
auto input = Infra::isomorphic_decode(value);
|
||||
|
||||
// 2. Let position be a position variable for input, initially pointing at the start of input.
|
||||
auto lexer = GenericLexer { input };
|
||||
|
||||
// 3. Let values be a list of strings, initially « ».
|
||||
Vector<String> values;
|
||||
|
||||
// 4. Let temporaryValue be the empty string.
|
||||
StringBuilder temporary_value_builder;
|
||||
|
||||
// 5. While true:
|
||||
while (true) {
|
||||
// 1. Append the result of collecting a sequence of code points that are not U+0022 (") or U+002C (,) from input, given position, to temporaryValue.
|
||||
// NOTE: The result might be the empty string.
|
||||
temporary_value_builder.append(lexer.consume_until(is_any_of("\","sv)));
|
||||
|
||||
// 2. If position is not past the end of input and the code point at position within input is U+0022 ("):
|
||||
if (!lexer.is_eof() && lexer.peek() == '"') {
|
||||
// 1. Append the result of collecting an HTTP quoted string from input, given position, to temporaryValue.
|
||||
temporary_value_builder.append(collect_an_http_quoted_string(lexer));
|
||||
|
||||
// 2. If position is not past the end of input, then continue.
|
||||
if (!lexer.is_eof())
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. Remove all HTTP tab or space from the start and end of temporaryValue.
|
||||
auto temporary_value = MUST(String::from_utf8(temporary_value_builder.string_view().trim(HTTP_TAB_OR_SPACE, TrimMode::Both)));
|
||||
|
||||
// 4. Append temporaryValue to values.
|
||||
values.append(move(temporary_value));
|
||||
|
||||
// 5. Set temporaryValue to the empty string.
|
||||
temporary_value_builder.clear();
|
||||
|
||||
// 6. If position is past the end of input, then return values.
|
||||
if (lexer.is_eof())
|
||||
return values;
|
||||
|
||||
// 7. Assert: the code point at position within input is U+002C (,).
|
||||
VERIFY(lexer.peek() == ',');
|
||||
|
||||
// 8. Advance position by 1.
|
||||
lexer.ignore(1);
|
||||
}
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-header-list-append
|
||||
void HeaderList::append(Header header)
|
||||
{
|
||||
|
|
@ -323,15 +282,48 @@ Vector<Header> HeaderList::sort_and_combine() const
|
|||
return headers;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#extract-header-list-values
|
||||
Variant<Empty, Vector<ByteBuffer>, HeaderList::ExtractHeaderParseFailure> HeaderList::extract_header_list_values(ReadonlyBytes name) const
|
||||
{
|
||||
// 1. If list does not contain name, then return null.
|
||||
if (!contains(name))
|
||||
return {};
|
||||
|
||||
// FIXME: 2. If the ABNF for name allows a single header and list contains more than one, then return failure.
|
||||
// NOTE: If different error handling is needed, extract the desired header first.
|
||||
|
||||
// 3. Let values be an empty list.
|
||||
auto values = Vector<ByteBuffer> {};
|
||||
|
||||
// 4. For each header header list contains whose name is name:
|
||||
for (auto const& header : *this) {
|
||||
if (!StringView { header.name }.equals_ignoring_ascii_case(name))
|
||||
continue;
|
||||
|
||||
// 1. Let extract be the result of extracting header values from header.
|
||||
auto extract = header.extract_header_values();
|
||||
|
||||
// 2. If extract is failure, then return failure.
|
||||
if (!extract.has_value())
|
||||
return ExtractHeaderParseFailure {};
|
||||
|
||||
// 3. Append each value in extract, in order, to values.
|
||||
values.extend(extract.release_value());
|
||||
}
|
||||
|
||||
// 5. Return values.
|
||||
return values;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#header-list-extract-a-length
|
||||
HeaderList::ExtractLengthResult HeaderList::extract_length() const
|
||||
Variant<Empty, u64, HeaderList::ExtractLengthFailure> HeaderList::extract_length() const
|
||||
{
|
||||
// 1. Let values be the result of getting, decoding, and splitting `Content-Length` from headers.
|
||||
auto values = get_decode_and_split("Content-Length"sv.bytes());
|
||||
|
||||
// 2. If values is null, then return null.
|
||||
if (!values.has_value())
|
||||
return Empty {};
|
||||
return {};
|
||||
|
||||
// 3. Let candidateValue be null.
|
||||
Optional<String> candidate_value;
|
||||
|
|
@ -349,14 +341,13 @@ HeaderList::ExtractLengthResult HeaderList::extract_length() const
|
|||
}
|
||||
|
||||
// 5. If candidateValue is the empty string or has a code point that is not an ASCII digit, then return null.
|
||||
// NOTE: to_number does this for us.
|
||||
// 6. Return candidateValue, interpreted as decimal number.
|
||||
// NOTE: The spec doesn't say anything about trimming here, so we don't trim. If it contains a space, step 5 will cause us to return null.
|
||||
// FIXME: This will return an empty Optional if it cannot fit into a u64, is this correct?
|
||||
auto conversion_result = candidate_value.value().to_number<u64>(TrimWhitespace::No);
|
||||
if (!conversion_result.has_value())
|
||||
return Empty {};
|
||||
return ExtractLengthResult { conversion_result.release_value() };
|
||||
auto result = candidate_value->to_number<u64>(TrimWhitespace::No);
|
||||
if (!result.has_value())
|
||||
return {};
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
|
||||
|
|
@ -414,58 +405,20 @@ Optional<MimeSniff::MimeType> HeaderList::extract_mime_type() const
|
|||
return mime_type;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#legacy-extract-an-encoding
|
||||
StringView legacy_extract_an_encoding(Optional<MimeSniff::MimeType> const& mime_type, StringView fallback_encoding)
|
||||
// Non-standard
|
||||
Vector<ByteBuffer> HeaderList::unique_names() const
|
||||
{
|
||||
// 1. If mimeType is failure, then return fallbackEncoding.
|
||||
if (!mime_type.has_value())
|
||||
return fallback_encoding;
|
||||
|
||||
// 2. If mimeType["charset"] does not exist, then return fallbackEncoding.
|
||||
auto charset = mime_type->parameters().get("charset"sv);
|
||||
if (!charset.has_value())
|
||||
return fallback_encoding;
|
||||
|
||||
// 3. Let tentativeEncoding be the result of getting an encoding from mimeType["charset"].
|
||||
auto tentative_encoding = TextCodec::get_standardized_encoding(*charset);
|
||||
|
||||
// 4. If tentativeEncoding is failure, then return fallbackEncoding.
|
||||
if (!tentative_encoding.has_value())
|
||||
return fallback_encoding;
|
||||
|
||||
// 5. Return tentativeEncoding.
|
||||
return *tentative_encoding;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set
|
||||
OrderedHashTable<ByteBuffer> convert_header_names_to_a_sorted_lowercase_set(Span<ReadonlyBytes> header_names)
|
||||
{
|
||||
// To convert header names to a sorted-lowercase set, given a list of names headerNames, run these steps:
|
||||
|
||||
// 1. Let headerNamesSet be a new ordered set.
|
||||
Vector<ByteBuffer> header_names_set;
|
||||
HashTable<ReadonlyBytes, CaseInsensitiveBytesTraits<u8 const>> header_names_seen;
|
||||
|
||||
// 2. For each name of headerNames, append the result of byte-lowercasing name to headerNamesSet.
|
||||
for (auto name : header_names) {
|
||||
if (header_names_seen.contains(name))
|
||||
for (auto const& header : *this) {
|
||||
if (header_names_seen.contains(header.name))
|
||||
continue;
|
||||
auto bytes = MUST(ByteBuffer::copy(name));
|
||||
Infra::byte_lowercase(bytes);
|
||||
header_names_seen.set(name);
|
||||
header_names_set.append(move(bytes));
|
||||
header_names_seen.set(header.name);
|
||||
header_names_set.append(MUST(ByteBuffer::copy(header.name)));
|
||||
}
|
||||
|
||||
// 3. Return the result of sorting headerNamesSet in ascending order with byte less than.
|
||||
quick_sort(header_names_set, [](auto const& a, auto const& b) {
|
||||
return StringView { a } < StringView { b };
|
||||
});
|
||||
OrderedHashTable<ByteBuffer> ordered { header_names_set.size() };
|
||||
for (auto& name : header_names_set) {
|
||||
auto result = ordered.set(move(name));
|
||||
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
|
||||
}
|
||||
return ordered;
|
||||
return header_names_set;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#header-name
|
||||
|
|
@ -503,6 +456,268 @@ ByteBuffer normalize_header_value(ReadonlyBytes potential_value)
|
|||
return MUST(ByteBuffer::copy(trimmed.bytes()));
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-header-name
|
||||
bool is_forbidden_request_header(Header const& header)
|
||||
{
|
||||
// A header (name, value) is forbidden request-header if these steps return true:
|
||||
auto name = StringView { header.name };
|
||||
|
||||
// 1. If name is a byte-case-insensitive match for one of:
|
||||
// [...]
|
||||
// then return true.
|
||||
if (name.is_one_of_ignoring_ascii_case(
|
||||
"Accept-Charset"sv,
|
||||
"Accept-Encoding"sv,
|
||||
"Access-Control-Request-Headers"sv,
|
||||
"Access-Control-Request-Method"sv,
|
||||
"Connection"sv,
|
||||
"Content-Length"sv,
|
||||
"Cookie"sv,
|
||||
"Cookie2"sv,
|
||||
"Date"sv,
|
||||
"DNT"sv,
|
||||
"Expect"sv,
|
||||
"Host"sv,
|
||||
"Keep-Alive"sv,
|
||||
"Origin"sv,
|
||||
"Referer"sv,
|
||||
"Set-Cookie"sv,
|
||||
"TE"sv,
|
||||
"Trailer"sv,
|
||||
"Transfer-Encoding"sv,
|
||||
"Upgrade"sv,
|
||||
"Via"sv)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. If name when byte-lowercased starts with `proxy-` or `sec-`, then return true.
|
||||
if (name.starts_with("proxy-"sv, CaseSensitivity::CaseInsensitive)
|
||||
|| name.starts_with("sec-"sv, CaseSensitivity::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. If name is a byte-case-insensitive match for one of:
|
||||
// - `X-HTTP-Method`
|
||||
// - `X-HTTP-Method-Override`
|
||||
// - `X-Method-Override`
|
||||
// then:
|
||||
if (name.is_one_of_ignoring_ascii_case(
|
||||
"X-HTTP-Method"sv,
|
||||
"X-HTTP-Method-Override"sv,
|
||||
"X-Method-Override"sv)) {
|
||||
// 1. Let parsedValues be the result of getting, decoding, and splitting value.
|
||||
auto parsed_values = get_decode_and_split_header_value(header.value);
|
||||
|
||||
// 2. For each method of parsedValues: if the isomorphic encoding of method is a forbidden method, then return true.
|
||||
// Note: The values returned from get_decode_and_split_header_value have already been decoded.
|
||||
if (parsed_values.has_value() && any_of(*parsed_values, [](auto method) { return is_forbidden_method(method.bytes()); }))
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-response-header-name
|
||||
bool is_forbidden_response_header_name(ReadonlyBytes header_name)
|
||||
{
|
||||
// A forbidden response-header name is a header name that is a byte-case-insensitive match for one of:
|
||||
// - `Set-Cookie`
|
||||
// - `Set-Cookie2`
|
||||
return StringView { header_name }.is_one_of_ignoring_ascii_case(
|
||||
"Set-Cookie"sv,
|
||||
"Set-Cookie2"sv);
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#legacy-extract-an-encoding
|
||||
StringView legacy_extract_an_encoding(Optional<MimeSniff::MimeType> const& mime_type, StringView fallback_encoding)
|
||||
{
|
||||
// 1. If mimeType is failure, then return fallbackEncoding.
|
||||
if (!mime_type.has_value())
|
||||
return fallback_encoding;
|
||||
|
||||
// 2. If mimeType["charset"] does not exist, then return fallbackEncoding.
|
||||
auto charset = mime_type->parameters().get("charset"sv);
|
||||
if (!charset.has_value())
|
||||
return fallback_encoding;
|
||||
|
||||
// 3. Let tentativeEncoding be the result of getting an encoding from mimeType["charset"].
|
||||
auto tentative_encoding = TextCodec::get_standardized_encoding(*charset);
|
||||
|
||||
// 4. If tentativeEncoding is failure, then return fallbackEncoding.
|
||||
if (!tentative_encoding.has_value())
|
||||
return fallback_encoding;
|
||||
|
||||
// 5. Return tentativeEncoding.
|
||||
return *tentative_encoding;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#header-value-get-decode-and-split
|
||||
Optional<Vector<String>> get_decode_and_split_header_value(ReadonlyBytes value)
|
||||
{
|
||||
// To get, decode, and split a header value value, run these steps:
|
||||
|
||||
// 1. Let input be the result of isomorphic decoding value.
|
||||
auto input = Infra::isomorphic_decode(value);
|
||||
|
||||
// 2. Let position be a position variable for input, initially pointing at the start of input.
|
||||
GenericLexer lexer { input };
|
||||
|
||||
// 3. Let values be a list of strings, initially « ».
|
||||
Vector<String> values;
|
||||
|
||||
// 4. Let temporaryValue be the empty string.
|
||||
StringBuilder temporary_value_builder;
|
||||
|
||||
// 5. While true:
|
||||
while (true) {
|
||||
// 1. Append the result of collecting a sequence of code points that are not U+0022 (") or U+002C (,) from input, given position, to temporaryValue.
|
||||
// NOTE: The result might be the empty string.
|
||||
temporary_value_builder.append(lexer.consume_until(is_any_of("\","sv)));
|
||||
|
||||
// 2. If position is not past the end of input and the code point at position within input is U+0022 ("):
|
||||
if (!lexer.is_eof() && lexer.peek() == '"') {
|
||||
// 1. Append the result of collecting an HTTP quoted string from input, given position, to temporaryValue.
|
||||
temporary_value_builder.append(collect_an_http_quoted_string(lexer));
|
||||
|
||||
// 2. If position is not past the end of input, then continue.
|
||||
if (!lexer.is_eof())
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. Remove all HTTP tab or space from the start and end of temporaryValue.
|
||||
auto temporary_value = MUST(String::from_utf8(temporary_value_builder.string_view().trim(HTTP_TAB_OR_SPACE, TrimMode::Both)));
|
||||
|
||||
// 4. Append temporaryValue to values.
|
||||
values.append(move(temporary_value));
|
||||
|
||||
// 5. Set temporaryValue to the empty string.
|
||||
temporary_value_builder.clear();
|
||||
|
||||
// 6. If position is past the end of input, then return values.
|
||||
if (lexer.is_eof())
|
||||
return values;
|
||||
|
||||
// 7. Assert: the code point at position within input is U+002C (,).
|
||||
VERIFY(lexer.peek() == ',');
|
||||
|
||||
// 8. Advance position by 1.
|
||||
lexer.ignore(1);
|
||||
}
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set
|
||||
OrderedHashTable<ByteBuffer> convert_header_names_to_a_sorted_lowercase_set(Span<ReadonlyBytes> header_names)
|
||||
{
|
||||
// To convert header names to a sorted-lowercase set, given a list of names headerNames, run these steps:
|
||||
|
||||
// 1. Let headerNamesSet be a new ordered set.
|
||||
Vector<ByteBuffer> header_names_set;
|
||||
HashTable<ReadonlyBytes, CaseInsensitiveBytesTraits<u8 const>> header_names_seen;
|
||||
|
||||
// 2. For each name of headerNames, append the result of byte-lowercasing name to headerNamesSet.
|
||||
for (auto name : header_names) {
|
||||
if (header_names_seen.contains(name))
|
||||
continue;
|
||||
auto bytes = MUST(ByteBuffer::copy(name));
|
||||
Infra::byte_lowercase(bytes);
|
||||
header_names_seen.set(name);
|
||||
header_names_set.append(move(bytes));
|
||||
}
|
||||
|
||||
// 3. Return the result of sorting headerNamesSet in ascending order with byte less than.
|
||||
quick_sort(header_names_set, [](auto const& a, auto const& b) {
|
||||
return StringView { a } < StringView { b };
|
||||
});
|
||||
OrderedHashTable<ByteBuffer> ordered { header_names_set.size() };
|
||||
for (auto& name : header_names_set) {
|
||||
auto result = ordered.set(move(name));
|
||||
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
|
||||
}
|
||||
return ordered;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#build-a-content-range
|
||||
ByteString build_content_range(u64 const& range_start, u64 const& range_end, u64 const& full_length)
|
||||
{
|
||||
// 1. Let contentRange be `bytes `.
|
||||
// 2. Append rangeStart, serialized and isomorphic encoded, to contentRange.
|
||||
// 3. Append 0x2D (-) to contentRange.
|
||||
// 4. Append rangeEnd, serialized and isomorphic encoded to contentRange.
|
||||
// 5. Append 0x2F (/) to contentRange.
|
||||
// 6. Append fullLength, serialized and isomorphic encoded to contentRange.
|
||||
// 7. Return contentRange.
|
||||
return ByteString::formatted("bytes {}-{}/{}", String::number(range_start), String::number(range_end), String::number(full_length));
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#simple-range-header-value
|
||||
Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes const value, bool const allow_whitespace)
|
||||
{
|
||||
// 1. Let data be the isomorphic decoding of value.
|
||||
auto const data = Infra::isomorphic_decode(value);
|
||||
|
||||
// 2. If data does not start with "bytes", then return failure.
|
||||
if (!data.starts_with_bytes("bytes"sv))
|
||||
return {};
|
||||
|
||||
// 3. Let position be a position variable for data, initially pointing at the 5th code point of data.
|
||||
GenericLexer lexer { data };
|
||||
lexer.ignore(5);
|
||||
|
||||
// 4. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 5. If the code point at position within data is not U+003D (=), then return failure.
|
||||
// 6. Advance position by 1.
|
||||
if (!lexer.consume_specific('='))
|
||||
return {};
|
||||
|
||||
// 7. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 8. Let rangeStart be the result of collecting a sequence of code points that are ASCII digits, from data given position.
|
||||
auto range_start = lexer.consume_while(is_ascii_digit);
|
||||
|
||||
// 9. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the empty string; otherwise null.
|
||||
auto range_start_value = range_start.to_number<u64>();
|
||||
|
||||
// 10. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 11. If the code point at position within data is not U+002D (-), then return failure.
|
||||
// 12. Advance position by 1.
|
||||
if (!lexer.consume_specific('-'))
|
||||
return {};
|
||||
|
||||
// 13. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 14. Let rangeEnd be the result of collecting a sequence of code points that are ASCII digits, from data given position.
|
||||
auto range_end = lexer.consume_while(is_ascii_digit);
|
||||
|
||||
// 15. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd is not the empty string; otherwise null.
|
||||
auto range_end_value = range_end.to_number<u64>();
|
||||
|
||||
// 16. If position is not past the end of data, then return failure.
|
||||
if (!lexer.is_eof())
|
||||
return {};
|
||||
|
||||
// 17. If rangeEndValue and rangeStartValue are null, then return failure.
|
||||
if (!range_end_value.has_value() && !range_start_value.has_value())
|
||||
return {};
|
||||
|
||||
// 18. If rangeStartValue and rangeEndValue are numbers, and rangeStartValue is greater than rangeEndValue, then return failure.
|
||||
if (range_start_value.has_value() && range_end_value.has_value() && *range_start_value > *range_end_value)
|
||||
return {};
|
||||
|
||||
// 19. Return (rangeStartValue, rangeEndValue).
|
||||
return RangeHeaderValue { move(range_start_value), move(range_end_value) };
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#cors-safelisted-request-header
|
||||
bool is_cors_safelisted_request_header(Header const& header)
|
||||
{
|
||||
|
|
@ -690,237 +905,6 @@ bool is_no_cors_safelisted_request_header(Header const& header)
|
|||
return is_cors_safelisted_request_header(header);
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-header-name
|
||||
bool is_forbidden_request_header(Header const& header)
|
||||
{
|
||||
// A header (name, value) is forbidden request-header if these steps return true:
|
||||
auto name = StringView { header.name };
|
||||
|
||||
// 1. If name is a byte-case-insensitive match for one of:
|
||||
// [...]
|
||||
// then return true.
|
||||
if (name.is_one_of_ignoring_ascii_case(
|
||||
"Accept-Charset"sv,
|
||||
"Accept-Encoding"sv,
|
||||
"Access-Control-Request-Headers"sv,
|
||||
"Access-Control-Request-Method"sv,
|
||||
"Connection"sv,
|
||||
"Content-Length"sv,
|
||||
"Cookie"sv,
|
||||
"Cookie2"sv,
|
||||
"Date"sv,
|
||||
"DNT"sv,
|
||||
"Expect"sv,
|
||||
"Host"sv,
|
||||
"Keep-Alive"sv,
|
||||
"Origin"sv,
|
||||
"Referer"sv,
|
||||
"Set-Cookie"sv,
|
||||
"TE"sv,
|
||||
"Trailer"sv,
|
||||
"Transfer-Encoding"sv,
|
||||
"Upgrade"sv,
|
||||
"Via"sv)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. If name when byte-lowercased starts with `proxy-` or `sec-`, then return true.
|
||||
if (name.starts_with("proxy-"sv, CaseSensitivity::CaseInsensitive)
|
||||
|| name.starts_with("sec-"sv, CaseSensitivity::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. If name is a byte-case-insensitive match for one of:
|
||||
// - `X-HTTP-Method`
|
||||
// - `X-HTTP-Method-Override`
|
||||
// - `X-Method-Override`
|
||||
// then:
|
||||
if (name.is_one_of_ignoring_ascii_case(
|
||||
"X-HTTP-Method"sv,
|
||||
"X-HTTP-Method-Override"sv,
|
||||
"X-Method-Override"sv)) {
|
||||
// 1. Let parsedValues be the result of getting, decoding, and splitting value.
|
||||
auto parsed_values = get_decode_and_split_header_value(header.value);
|
||||
|
||||
// 2. For each method of parsedValues: if the isomorphic encoding of method is a forbidden method, then return true.
|
||||
// Note: The values returned from get_decode_and_split_header_value have already been decoded.
|
||||
if (parsed_values.has_value() && any_of(*parsed_values, [](auto method) { return is_forbidden_method(method.bytes()); }))
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-response-header-name
|
||||
bool is_forbidden_response_header_name(ReadonlyBytes header_name)
|
||||
{
|
||||
// A forbidden response-header name is a header name that is a byte-case-insensitive match for one of:
|
||||
// - `Set-Cookie`
|
||||
// - `Set-Cookie2`
|
||||
return StringView { header_name }.is_one_of_ignoring_ascii_case(
|
||||
"Set-Cookie"sv,
|
||||
"Set-Cookie2"sv);
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#request-body-header-name
|
||||
bool is_request_body_header_name(ReadonlyBytes header_name)
|
||||
{
|
||||
// A request-body-header name is a header name that is a byte-case-insensitive match for one of:
|
||||
// - `Content-Encoding`
|
||||
// - `Content-Language`
|
||||
// - `Content-Location`
|
||||
// - `Content-Type`
|
||||
return StringView { header_name }.is_one_of_ignoring_ascii_case(
|
||||
"Content-Encoding"sv,
|
||||
"Content-Language"sv,
|
||||
"Content-Location"sv,
|
||||
"Content-Type"sv);
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#extract-header-values
|
||||
Optional<Vector<ByteBuffer>> extract_header_values(Header const& header)
|
||||
{
|
||||
// FIXME: 1. If parsing header’s value, per the ABNF for header’s name, fails, then return failure.
|
||||
// FIXME: 2. Return one or more values resulting from parsing header’s value, per the ABNF for header’s name.
|
||||
|
||||
// For now we only parse some headers that are of the ABNF list form "#something"
|
||||
if (StringView { header.name }.is_one_of_ignoring_ascii_case(
|
||||
"Access-Control-Request-Headers"sv,
|
||||
"Access-Control-Expose-Headers"sv,
|
||||
"Access-Control-Allow-Headers"sv,
|
||||
"Access-Control-Allow-Methods"sv)
|
||||
&& !header.value.is_empty()) {
|
||||
auto split_values = StringView { header.value }.split_view(',');
|
||||
Vector<ByteBuffer> trimmed_values;
|
||||
|
||||
for (auto const& value : split_values) {
|
||||
auto trimmed_value = value.trim(" \t"sv);
|
||||
auto trimmed_value_as_byte_buffer = MUST(ByteBuffer::copy(trimmed_value.bytes()));
|
||||
trimmed_values.append(move(trimmed_value_as_byte_buffer));
|
||||
}
|
||||
|
||||
return trimmed_values;
|
||||
}
|
||||
|
||||
// This always ignores the ABNF rules for now and returns the header value as a single list item.
|
||||
return Vector { MUST(ByteBuffer::copy(header.value)) };
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#extract-header-list-values
|
||||
Variant<Vector<ByteBuffer>, ExtractHeaderParseFailure, Empty> extract_header_list_values(ReadonlyBytes name, HeaderList const& list)
|
||||
{
|
||||
// 1. If list does not contain name, then return null.
|
||||
if (!list.contains(name))
|
||||
return Empty {};
|
||||
|
||||
// FIXME: 2. If the ABNF for name allows a single header and list contains more than one, then return failure.
|
||||
// NOTE: If different error handling is needed, extract the desired header first.
|
||||
|
||||
// 3. Let values be an empty list.
|
||||
auto values = Vector<ByteBuffer> {};
|
||||
|
||||
// 4. For each header header list contains whose name is name:
|
||||
for (auto const& header : list) {
|
||||
if (!StringView { header.name }.equals_ignoring_ascii_case(name))
|
||||
continue;
|
||||
|
||||
// 1. Let extract be the result of extracting header values from header.
|
||||
auto extract = extract_header_values(header);
|
||||
|
||||
// 2. If extract is failure, then return failure.
|
||||
if (!extract.has_value())
|
||||
return ExtractHeaderParseFailure {};
|
||||
|
||||
// 3. Append each value in extract, in order, to values.
|
||||
values.extend(extract.release_value());
|
||||
}
|
||||
|
||||
// 5. Return values.
|
||||
return values;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#build-a-content-range
|
||||
ByteString build_content_range(u64 const& range_start, u64 const& range_end, u64 const& full_length)
|
||||
{
|
||||
// 1. Let contentRange be `bytes `.
|
||||
// 2. Append rangeStart, serialized and isomorphic encoded, to contentRange.
|
||||
// 3. Append 0x2D (-) to contentRange.
|
||||
// 4. Append rangeEnd, serialized and isomorphic encoded to contentRange.
|
||||
// 5. Append 0x2F (/) to contentRange.
|
||||
// 6. Append fullLength, serialized and isomorphic encoded to contentRange.
|
||||
// 7. Return contentRange.
|
||||
return ByteString::formatted("bytes {}-{}/{}", String::number(range_start), String::number(range_end), String::number(full_length));
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#simple-range-header-value
|
||||
Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes const value, bool const allow_whitespace)
|
||||
{
|
||||
// 1. Let data be the isomorphic decoding of value.
|
||||
auto const data = Infra::isomorphic_decode(value);
|
||||
|
||||
// 2. If data does not start with "bytes", then return failure.
|
||||
if (!data.starts_with_bytes("bytes"sv))
|
||||
return {};
|
||||
|
||||
// 3. Let position be a position variable for data, initially pointing at the 5th code point of data.
|
||||
auto lexer = GenericLexer { data };
|
||||
lexer.ignore(5);
|
||||
|
||||
// 4. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 5. If the code point at position within data is not U+003D (=), then return failure.
|
||||
// 6. Advance position by 1.
|
||||
if (!lexer.consume_specific('='))
|
||||
return {};
|
||||
|
||||
// 7. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 8. Let rangeStart be the result of collecting a sequence of code points that are ASCII digits, from data given position.
|
||||
auto range_start = lexer.consume_while(is_ascii_digit);
|
||||
|
||||
// 9. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the empty string; otherwise null.
|
||||
auto range_start_value = range_start.to_number<u64>();
|
||||
|
||||
// 10. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 11. If the code point at position within data is not U+002D (-), then return failure.
|
||||
// 12. Advance position by 1.
|
||||
if (!lexer.consume_specific('-'))
|
||||
return {};
|
||||
|
||||
// 13. If allowWhitespace is true, collect a sequence of code points that are HTTP tab or space, from data given position.
|
||||
if (allow_whitespace)
|
||||
lexer.consume_while(is_http_tab_or_space);
|
||||
|
||||
// 14. Let rangeEnd be the result of collecting a sequence of code points that are ASCII digits, from data given position.
|
||||
auto range_end = lexer.consume_while(is_ascii_digit);
|
||||
|
||||
// 15. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd is not the empty string; otherwise null.
|
||||
auto range_end_value = range_end.to_number<u64>();
|
||||
|
||||
// 16. If position is not past the end of data, then return failure.
|
||||
if (!lexer.is_eof())
|
||||
return {};
|
||||
|
||||
// 17. If rangeEndValue and rangeStartValue are null, then return failure.
|
||||
if (!range_end_value.has_value() && !range_start_value.has_value())
|
||||
return {};
|
||||
|
||||
// 18. If rangeStartValue and rangeEndValue are numbers, and rangeStartValue is greater than rangeEndValue, then return failure.
|
||||
if (range_start_value.has_value() && range_end_value.has_value() && *range_start_value > *range_end_value)
|
||||
return {};
|
||||
|
||||
// 19. Return (rangeStartValue, rangeEndValue).
|
||||
return RangeHeaderValue { move(range_start_value), move(range_end_value) };
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#default-user-agent-value
|
||||
ByteBuffer default_user_agent_value()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,12 +25,14 @@ namespace Web::Fetch::Infrastructure {
|
|||
// https://fetch.spec.whatwg.org/#concept-header
|
||||
// A header is a tuple that consists of a name (a header name) and value (a header value).
|
||||
struct WEB_API Header {
|
||||
ByteBuffer name;
|
||||
ByteBuffer value;
|
||||
|
||||
[[nodiscard]] static Header copy(Header const&);
|
||||
[[nodiscard]] static Header from_string_pair(StringView, StringView);
|
||||
[[nodiscard]] static Header from_latin1_pair(StringView, StringView);
|
||||
|
||||
[[nodiscard]] Optional<Vector<ByteBuffer>> extract_header_values() const;
|
||||
|
||||
ByteBuffer name;
|
||||
ByteBuffer value;
|
||||
};
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-header-list
|
||||
|
|
@ -42,13 +44,13 @@ class WEB_API HeaderList final
|
|||
GC_DECLARE_ALLOCATOR(HeaderList);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static GC::Ref<HeaderList> create(JS::VM&);
|
||||
|
||||
using Vector::begin;
|
||||
using Vector::clear;
|
||||
using Vector::end;
|
||||
using Vector::is_empty;
|
||||
|
||||
[[nodiscard]] static GC::Ref<HeaderList> create(JS::VM&);
|
||||
|
||||
[[nodiscard]] bool contains(ReadonlyBytes) const;
|
||||
[[nodiscard]] Optional<ByteBuffer> get(ReadonlyBytes) const;
|
||||
[[nodiscard]] Optional<Vector<String>> get_decode_and_split(ReadonlyBytes) const;
|
||||
|
|
@ -58,10 +60,11 @@ public:
|
|||
void combine(Header);
|
||||
[[nodiscard]] Vector<Header> sort_and_combine() const;
|
||||
|
||||
struct ExtractLengthFailure { };
|
||||
using ExtractLengthResult = Variant<u64, ExtractLengthFailure, Empty>;
|
||||
struct ExtractHeaderParseFailure { };
|
||||
[[nodiscard]] Variant<Empty, Vector<ByteBuffer>, ExtractHeaderParseFailure> extract_header_list_values(ReadonlyBytes) const;
|
||||
|
||||
[[nodiscard]] ExtractLengthResult extract_length() const;
|
||||
struct ExtractLengthFailure { };
|
||||
[[nodiscard]] Variant<Empty, u64, ExtractLengthFailure> extract_length() const;
|
||||
|
||||
[[nodiscard]] Optional<MimeSniff::MimeType> extract_mime_type() const;
|
||||
|
||||
|
|
@ -73,15 +76,20 @@ struct RangeHeaderValue {
|
|||
Optional<u64> end;
|
||||
};
|
||||
|
||||
struct ExtractHeaderParseFailure {
|
||||
};
|
||||
[[nodiscard]] bool is_header_name(ReadonlyBytes);
|
||||
[[nodiscard]] bool is_header_value(ReadonlyBytes);
|
||||
[[nodiscard]] ByteBuffer normalize_header_value(ReadonlyBytes);
|
||||
|
||||
[[nodiscard]] bool is_forbidden_request_header(Header const&);
|
||||
[[nodiscard]] bool is_forbidden_response_header_name(ReadonlyBytes);
|
||||
|
||||
[[nodiscard]] WEB_API StringView legacy_extract_an_encoding(Optional<MimeSniff::MimeType> const& mime_type, StringView fallback_encoding);
|
||||
[[nodiscard]] Optional<Vector<String>> get_decode_and_split_header_value(ReadonlyBytes);
|
||||
[[nodiscard]] OrderedHashTable<ByteBuffer> convert_header_names_to_a_sorted_lowercase_set(Span<ReadonlyBytes>);
|
||||
[[nodiscard]] bool is_header_name(ReadonlyBytes);
|
||||
[[nodiscard]] bool is_header_value(ReadonlyBytes);
|
||||
[[nodiscard]] ByteBuffer normalize_header_value(ReadonlyBytes);
|
||||
|
||||
[[nodiscard]] WEB_API ByteString build_content_range(u64 const& range_start, u64 const& range_end, u64 const& full_length);
|
||||
[[nodiscard]] WEB_API Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes, bool);
|
||||
|
||||
[[nodiscard]] bool is_cors_safelisted_request_header(Header const&);
|
||||
[[nodiscard]] bool is_cors_unsafe_request_header_byte(u8);
|
||||
[[nodiscard]] WEB_API OrderedHashTable<ByteBuffer> get_cors_unsafe_header_names(HeaderList const&);
|
||||
|
|
@ -90,13 +98,7 @@ struct ExtractHeaderParseFailure {
|
|||
[[nodiscard]] bool is_cors_safelisted_response_header_name(ReadonlyBytes, Span<ReadonlyBytes>);
|
||||
[[nodiscard]] bool is_no_cors_safelisted_request_header_name(ReadonlyBytes);
|
||||
[[nodiscard]] bool is_no_cors_safelisted_request_header(Header const&);
|
||||
[[nodiscard]] bool is_forbidden_request_header(Header const&);
|
||||
[[nodiscard]] bool is_forbidden_response_header_name(ReadonlyBytes);
|
||||
[[nodiscard]] bool is_request_body_header_name(ReadonlyBytes);
|
||||
[[nodiscard]] Optional<Vector<ByteBuffer>> extract_header_values(Header const&);
|
||||
[[nodiscard]] WEB_API Variant<Vector<ByteBuffer>, ExtractHeaderParseFailure, Empty> extract_header_list_values(ReadonlyBytes, HeaderList const&);
|
||||
[[nodiscard]] WEB_API ByteString build_content_range(u64 const& range_start, u64 const& range_end, u64 const& full_length);
|
||||
[[nodiscard]] WEB_API Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes, bool);
|
||||
|
||||
[[nodiscard]] WEB_API ByteBuffer default_user_agent_value();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ ErrorOr<Optional<URL::URL>> Response::location_url(Optional<String> const& reque
|
|||
return Optional<URL::URL> {};
|
||||
|
||||
// 2. Let location be the result of extracting header list values given `Location` and response’s header list.
|
||||
auto location_values_or_failure = extract_header_list_values("Location"sv.bytes(), m_header_list);
|
||||
if (location_values_or_failure.has<Infrastructure::ExtractHeaderParseFailure>() || location_values_or_failure.has<Empty>())
|
||||
auto location_values_or_failure = m_header_list->extract_header_list_values("Location"sv.bytes());
|
||||
if (location_values_or_failure.has<Infrastructure::HeaderList::ExtractHeaderParseFailure>() || location_values_or_failure.has<Empty>())
|
||||
return Optional<URL::URL> {};
|
||||
|
||||
auto const& location_values = location_values_or_failure.get<Vector<ByteBuffer>>();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Web::ReferrerPolicy {
|
|||
ReferrerPolicy parse_a_referrer_policy_from_a_referrer_policy_header(Fetch::Infrastructure::Response const& response)
|
||||
{
|
||||
// 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy` and response’s header list.
|
||||
auto policy_tokens_or_failure = Fetch::Infrastructure::extract_header_list_values("Referrer-Policy"sv.bytes(), response.header_list());
|
||||
auto policy_tokens_or_failure = response.header_list()->extract_header_list_values("Referrer-Policy"sv.bytes());
|
||||
auto policy_tokens = policy_tokens_or_failure.has<Vector<ByteBuffer>>() ? policy_tokens_or_failure.get<Vector<ByteBuffer>>() : Vector<ByteBuffer> {};
|
||||
|
||||
// 2. Let policy be the empty string.
|
||||
|
|
|
|||
|
|
@ -266,13 +266,13 @@ static void update(JS::VM& vm, GC::Ref<Job> job)
|
|||
|
||||
// 8. Let serviceWorkerAllowed be the result of extracting header list values given `Service-Worker-Allowed` and response’s header list.
|
||||
// Note: See the definition of the Service-Worker-Allowed header in Appendix B: Extended HTTP headers. https://w3c.github.io/ServiceWorker/#service-worker-allowed
|
||||
auto service_worker_allowed = Fetch::Infrastructure::extract_header_list_values("Service-Worker-Allowed"sv.bytes(), response->header_list());
|
||||
auto service_worker_allowed = response->header_list()->extract_header_list_values("Service-Worker-Allowed"sv.bytes());
|
||||
|
||||
// 9. Set policyContainer to the result of creating a policy container from a fetch response given response.
|
||||
// FIXME: CSP not implemented yet
|
||||
|
||||
// 10. If serviceWorkerAllowed is failure, then:
|
||||
if (service_worker_allowed.has<Fetch::Infrastructure::ExtractHeaderParseFailure>()) {
|
||||
if (service_worker_allowed.has<Fetch::Infrastructure::HeaderList::ExtractHeaderParseFailure>()) {
|
||||
// FIXME: Should we reject the job promise with a security error here?
|
||||
|
||||
// 1. Asynchronously complete these steps with a network error.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue