2022-07-11 21:42:14 +01:00
|
|
|
|
/*
|
2025-11-26 14:13:23 -05:00
|
|
|
|
* Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
|
2023-02-10 22:02:18 +00:00
|
|
|
|
* Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
|
2026-02-28 18:36:44 +01:00
|
|
|
|
* Copyright (c) 2026, Shannon Booth <shannon@serenityos.org>
|
2022-07-11 21:42:14 +01:00
|
|
|
|
*
|
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
LibRegex: Add ECMAScriptRegex and migrate callers
Add `ECMAScriptRegex`, LibRegex's C++ facade for ECMAScript regexes.
The facade owns compilation, execution, captures, named groups, and
error translation for the Rust backend, which lets callers stop
depending on the legacy parser and matcher types directly. Use it in the
remaining non-LibJS callers: URLPattern, HTML input pattern handling,
and the places in LibHTTP that only needed token validation.
Where a full regex engine was unnecessary, replace those call sites with
direct character checks. Also update focused LibURL, LibHTTP, and WPT
coverage for the migrated callers and corrected surrogate handling.
2026-03-25 10:52:20 +01:00
|
|
|
|
#include <AK/AllOf.h>
|
|
|
|
|
|
#include <AK/AnyOf.h>
|
2022-07-11 21:42:14 +01:00
|
|
|
|
#include <AK/GenericLexer.h>
|
|
|
|
|
|
#include <AK/QuickSort.h>
|
2025-11-26 14:13:23 -05:00
|
|
|
|
#include <LibHTTP/HTTP.h>
|
|
|
|
|
|
#include <LibHTTP/Header.h>
|
|
|
|
|
|
#include <LibHTTP/Method.h>
|
|
|
|
|
|
#include <LibIPC/Decoder.h>
|
|
|
|
|
|
#include <LibIPC/Encoder.h>
|
2023-05-10 16:26:51 -04:00
|
|
|
|
#include <LibTextCodec/Decoder.h>
|
2025-11-24 12:20:51 -05:00
|
|
|
|
#include <LibTextCodec/Encoder.h>
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2025-11-26 14:13:23 -05:00
|
|
|
|
namespace HTTP {
|
2024-04-06 10:16:04 -07:00
|
|
|
|
|
2025-11-24 18:35:55 -05:00
|
|
|
|
Header Header::isomorphic_encode(StringView name, StringView value)
|
2024-10-22 11:47:22 +02:00
|
|
|
|
{
|
2025-11-26 14:13:23 -05:00
|
|
|
|
return { TextCodec::isomorphic_encode(name), TextCodec::isomorphic_encode(value) };
|
2022-10-24 09:16:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-15 19:39:29 +01:00
|
|
|
|
// https://www.rfc-editor.org/rfc/rfc9110.html#name-recipient-requirements
|
2026-02-28 18:36:44 +01:00
|
|
|
|
static Optional<Vector<ByteString>> extract_token_headers(ByteString const& value)
|
|
|
|
|
|
{
|
2026-03-15 19:39:29 +01:00
|
|
|
|
Vector<ByteString> result;
|
|
|
|
|
|
for (auto& part : value.split(',', SplitBehavior::Nothing)) {
|
|
|
|
|
|
auto trimmed = part.trim(HTTP_WHITESPACE, TrimMode::Both);
|
|
|
|
|
|
if (trimmed.is_empty())
|
|
|
|
|
|
continue;
|
|
|
|
|
|
if (!is_header_name(trimmed))
|
2026-02-28 18:36:44 +01:00
|
|
|
|
return {};
|
2026-03-15 19:39:29 +01:00
|
|
|
|
result.append(move(trimmed));
|
2026-02-28 18:36:44 +01:00
|
|
|
|
}
|
2026-03-15 19:39:29 +01:00
|
|
|
|
return result;
|
2026-02-28 18:36:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// https://fetch.spec.whatwg.org/#extract-header-values
|
2025-11-24 18:35:55 -05:00
|
|
|
|
Optional<Vector<ByteString>> Header::extract_header_values() const
|
2022-10-30 01:52:07 +00:00
|
|
|
|
{
|
2026-02-28 18:36:44 +01:00
|
|
|
|
// NB: There is some specification work to try and rework this function, see: https://github.com/whatwg/fetch/issues/814
|
|
|
|
|
|
|
|
|
|
|
|
// 1. If parsing header’s value, per the ABNF for header’s name, fails, then return failure.
|
|
|
|
|
|
// 2. Return one or more values resulting from parsing header’s value, per the ABNF for header’s name.
|
|
|
|
|
|
|
|
|
|
|
|
// ABNF taken from:
|
|
|
|
|
|
// * https://fetch.spec.whatwg.org/#http-new-header-syntax
|
|
|
|
|
|
// * https://httpwg.org/specs/rfc9110.html#field.accept-ranges
|
2022-10-30 01:52:07 +00:00
|
|
|
|
|
2026-02-28 18:36:44 +01:00
|
|
|
|
// Access-Control-Expose-Headers = #field-name (field-name = token)
|
|
|
|
|
|
// Access-Control-Allow-Headers = #field-name (field-name = token)
|
|
|
|
|
|
// Access-Control-Allow-Methods = #method (method = token)
|
2025-11-24 18:35:55 -05:00
|
|
|
|
if (name.is_one_of_ignoring_ascii_case(
|
2025-11-25 10:57:32 -05:00
|
|
|
|
"Access-Control-Expose-Headers"sv,
|
|
|
|
|
|
"Access-Control-Allow-Headers"sv,
|
2026-02-28 18:36:44 +01:00
|
|
|
|
"Access-Control-Allow-Methods"sv)) {
|
|
|
|
|
|
return extract_token_headers(value);
|
|
|
|
|
|
}
|
2025-11-25 10:57:32 -05:00
|
|
|
|
|
2026-02-28 18:36:44 +01:00
|
|
|
|
// Access-Control-Request-Headers = 1#field-name (field-name = token)
|
|
|
|
|
|
// Accept-Ranges = acceptable-ranges (acceptable-ranges = 1#range-unit, range-unit = token)
|
|
|
|
|
|
if (name.is_one_of_ignoring_ascii_case(
|
|
|
|
|
|
"Access-Control-Request-Headers"sv,
|
|
|
|
|
|
"Accept-Ranges"sv)) {
|
|
|
|
|
|
if (auto headers = extract_token_headers(value); headers.has_value()) {
|
|
|
|
|
|
if (headers->is_empty())
|
|
|
|
|
|
return {};
|
|
|
|
|
|
return headers;
|
|
|
|
|
|
}
|
|
|
|
|
|
return {};
|
2023-08-01 21:40:30 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:36:44 +01:00
|
|
|
|
// FIXME: What other headers should we handle here (or elsewhere?)
|
2025-11-24 18:35:55 -05:00
|
|
|
|
return Vector { value };
|
2025-11-25 10:57:32 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-11 21:42:14 +01:00
|
|
|
|
// https://fetch.spec.whatwg.org/#header-name
|
2025-11-24 18:35:55 -05:00
|
|
|
|
bool is_header_name(StringView header_name)
|
2022-07-11 21:42:14 +01:00
|
|
|
|
{
|
|
|
|
|
|
// A header name is a byte sequence that matches the field-name token production.
|
LibRegex: Add ECMAScriptRegex and migrate callers
Add `ECMAScriptRegex`, LibRegex's C++ facade for ECMAScript regexes.
The facade owns compilation, execution, captures, named groups, and
error translation for the Rust backend, which lets callers stop
depending on the legacy parser and matcher types directly. Use it in the
remaining non-LibJS callers: URLPattern, HTML input pattern handling,
and the places in LibHTTP that only needed token validation.
Where a full regex engine was unnecessary, replace those call sites with
direct character checks. Also update focused LibURL, LibHTTP, and WPT
coverage for the migrated callers and corrected surrogate handling.
2026-03-25 10:52:20 +01:00
|
|
|
|
return !header_name.is_empty() && all_of(header_name, is_http_token_code_point);
|
2022-07-11 21:42:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// https://fetch.spec.whatwg.org/#header-value
|
2025-11-24 18:35:55 -05:00
|
|
|
|
bool is_header_value(StringView header_value)
|
2022-07-11 21:42:14 +01:00
|
|
|
|
{
|
|
|
|
|
|
// A header value is a byte sequence that matches the following conditions:
|
|
|
|
|
|
// - Has no leading or trailing HTTP tab or space bytes.
|
|
|
|
|
|
// - Contains no 0x00 (NUL) or HTTP newline bytes.
|
|
|
|
|
|
if (header_value.is_empty())
|
|
|
|
|
|
return true;
|
2025-11-24 18:35:55 -05:00
|
|
|
|
|
2022-07-11 21:42:14 +01:00
|
|
|
|
auto first_byte = header_value[0];
|
2025-11-24 18:35:55 -05:00
|
|
|
|
auto last_byte = header_value[header_value.length() - 1];
|
|
|
|
|
|
|
|
|
|
|
|
if (is_http_tab_or_space(first_byte) || is_http_tab_or_space(last_byte))
|
2022-07-11 21:42:14 +01:00
|
|
|
|
return false;
|
2025-11-24 18:35:55 -05:00
|
|
|
|
|
2022-07-11 21:42:14 +01:00
|
|
|
|
return !any_of(header_value, [](auto byte) {
|
2025-11-24 18:35:55 -05:00
|
|
|
|
return byte == 0x00 || is_http_newline(byte);
|
2022-07-11 21:42:14 +01:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// https://fetch.spec.whatwg.org/#concept-header-value-normalize
|
2026-02-26 14:51:41 -05:00
|
|
|
|
StringView normalize_header_value(StringView potential_value)
|
2022-07-11 21:42:14 +01:00
|
|
|
|
{
|
2025-11-24 18:35:55 -05:00
|
|
|
|
// To normalize a byte sequence potentialValue, remove any leading and trailing HTTP whitespace bytes from
|
|
|
|
|
|
// potentialValue.
|
2022-07-11 21:42:14 +01:00
|
|
|
|
if (potential_value.is_empty())
|
2024-04-26 13:24:20 -04:00
|
|
|
|
return {};
|
2025-11-24 18:35:55 -05:00
|
|
|
|
return potential_value.trim(HTTP_WHITESPACE, TrimMode::Both);
|
2022-07-11 21:42:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// https://fetch.spec.whatwg.org/#forbidden-header-name
|
|
|
|
|
|
bool is_forbidden_request_header(Header const& header)
|
2022-07-11 21:42:14 +01:00
|
|
|
|
{
|
2025-11-24 18:35:55 -05:00
|
|
|
|
auto const& [name, value] = header;
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 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;
|
2022-07-11 21:42:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 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;
|
2022-12-07 18:16:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. If name is a byte-case-insensitive match for one of:
|
|
|
|
|
|
// - `X-HTTP-Method`
|
|
|
|
|
|
// - `X-HTTP-Method-Override`
|
|
|
|
|
|
// - `X-Method-Override`
|
|
|
|
|
|
// then:
|
2023-03-10 08:48:54 +01:00
|
|
|
|
if (name.is_one_of_ignoring_ascii_case(
|
2022-12-07 18:16:32 +00:00
|
|
|
|
"X-HTTP-Method"sv,
|
|
|
|
|
|
"X-HTTP-Method-Override"sv,
|
2024-08-04 11:53:52 +01:00
|
|
|
|
"X-Method-Override"sv)) {
|
2022-12-07 18:16:32 +00:00
|
|
|
|
// 1. Let parsedValues be the result of getting, decoding, and splitting value.
|
2025-11-24 18:35:55 -05:00
|
|
|
|
auto parsed_values = get_decode_and_split_header_value(value);
|
2022-12-07 18:16:32 +00:00
|
|
|
|
|
2022-12-07 18:29:17 +00:00
|
|
|
|
// 2. For each method of parsedValues: if the isomorphic encoding of method is a forbidden method, then return true.
|
2025-11-24 18:35:55 -05:00
|
|
|
|
// NB: The values returned from get_decode_and_split_header_value have already been decoded.
|
|
|
|
|
|
if (any_of(parsed_values, [](auto const& method) { return is_forbidden_method(method); }))
|
2022-12-07 18:16:32 +00:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. Return false.
|
|
|
|
|
|
return false;
|
2022-07-11 21:42:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// https://fetch.spec.whatwg.org/#forbidden-response-header-name
|
2025-11-24 18:35:55 -05:00
|
|
|
|
bool is_forbidden_response_header_name(StringView header_name)
|
2022-07-11 21:42:14 +01:00
|
|
|
|
{
|
|
|
|
|
|
// A forbidden response-header name is a header name that is a byte-case-insensitive match for one of:
|
|
|
|
|
|
// - `Set-Cookie`
|
|
|
|
|
|
// - `Set-Cookie2`
|
2025-11-24 18:35:55 -05:00
|
|
|
|
return header_name.is_one_of_ignoring_ascii_case(
|
2022-07-11 21:42:14 +01:00
|
|
|
|
"Set-Cookie"sv,
|
|
|
|
|
|
"Set-Cookie2"sv);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// https://fetch.spec.whatwg.org/#header-value-get-decode-and-split
|
2025-11-24 18:35:55 -05:00
|
|
|
|
Vector<String> get_decode_and_split_header_value(StringView value)
|
2022-10-25 23:02:47 +01:00
|
|
|
|
{
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 1. Let input be the result of isomorphic decoding value.
|
2025-11-24 12:20:51 -05:00
|
|
|
|
auto input = TextCodec::isomorphic_decode(value);
|
2023-08-01 22:00:28 +12:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 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) {
|
2025-11-24 18:35:55 -05:00
|
|
|
|
// 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.
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 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;
|
2023-08-01 22:00:28 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 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)));
|
2023-08-01 22:00:28 +12:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 4. Append temporaryValue to values.
|
|
|
|
|
|
values.append(move(temporary_value));
|
2022-10-25 23:02:47 +01:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 5. Set temporaryValue to the empty string.
|
|
|
|
|
|
temporary_value_builder.clear();
|
2022-10-25 23:02:47 +01:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 6. If position is past the end of input, then return values.
|
|
|
|
|
|
if (lexer.is_eof())
|
|
|
|
|
|
return values;
|
2022-10-25 23:02:47 +01:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 7. Assert: the code point at position within input is U+002C (,).
|
|
|
|
|
|
VERIFY(lexer.peek() == ',');
|
2022-10-25 23:02:47 +01:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 8. Advance position by 1.
|
|
|
|
|
|
lexer.ignore(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-10-25 23:02:47 +01:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set
|
2025-11-24 18:35:55 -05:00
|
|
|
|
Vector<ByteString> convert_header_names_to_a_sorted_lowercase_set(ReadonlySpan<ByteString> header_names)
|
2025-11-25 10:57:32 -05:00
|
|
|
|
{
|
|
|
|
|
|
// 1. Let headerNamesSet be a new ordered set.
|
2025-12-21 23:31:17 -06:00
|
|
|
|
HashTable<StringView, CaseInsensitiveASCIIStringTraits> header_names_seen;
|
2025-11-24 18:35:55 -05:00
|
|
|
|
Vector<ByteString> header_names_set;
|
2022-10-25 23:02:47 +01:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 2. For each name of headerNames, append the result of byte-lowercasing name to headerNamesSet.
|
2025-11-24 18:35:55 -05:00
|
|
|
|
for (auto const& name : header_names) {
|
2025-11-25 10:57:32 -05:00
|
|
|
|
if (header_names_seen.contains(name))
|
|
|
|
|
|
continue;
|
2025-11-24 18:35:55 -05:00
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
header_names_seen.set(name);
|
2025-11-24 18:35:55 -05:00
|
|
|
|
header_names_set.append(name.to_lowercase());
|
2022-10-25 23:02:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-25 10:57:32 -05:00
|
|
|
|
// 3. Return the result of sorting headerNamesSet in ascending order with byte less than.
|
2025-11-24 18:35:55 -05:00
|
|
|
|
quick_sort(header_names_set);
|
|
|
|
|
|
return header_names_set;
|
2022-10-25 23:02:47 +01:00
|
|
|
|
}
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:20:33 -06:00
|
|
|
|
// https://fetch.spec.whatwg.org/#build-a-content-range
|
2025-11-25 11:02:12 -05:00
|
|
|
|
ByteString build_content_range(u64 range_start, u64 range_end, u64 full_length)
|
2024-11-18 17:20:33 -06:00
|
|
|
|
{
|
|
|
|
|
|
// 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.
|
2025-11-25 11:02:12 -05:00
|
|
|
|
return ByteString::formatted("bytes {}-{}/{}", range_start, range_end, full_length);
|
2024-11-18 17:20:33 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-11 21:42:14 +01:00
|
|
|
|
// https://fetch.spec.whatwg.org/#simple-range-header-value
|
2025-11-26 14:13:23 -05:00
|
|
|
|
Optional<RangeHeaderValue> parse_single_range_header_value(StringView value, bool allow_whitespace)
|
2022-07-11 21:42:14 +01:00
|
|
|
|
{
|
|
|
|
|
|
// 1. Let data be the isomorphic decoding of value.
|
2025-11-26 14:13:23 -05:00
|
|
|
|
auto data = TextCodec::isomorphic_decode(value);
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 2. If data does not start with "bytes", then return failure.
|
|
|
|
|
|
if (!data.starts_with_bytes("bytes"sv))
|
2022-10-15 00:39:40 +02:00
|
|
|
|
return {};
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 3. Let position be a position variable for data, initially pointing at the 5th code point of data.
|
2025-11-25 10:57:32 -05:00
|
|
|
|
GenericLexer lexer { data };
|
2024-11-18 17:18:26 -06:00
|
|
|
|
lexer.ignore(5);
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 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.
|
2022-07-11 21:42:14 +01:00
|
|
|
|
auto range_start = lexer.consume_while(is_ascii_digit);
|
|
|
|
|
|
|
2025-11-26 14:13:23 -05:00
|
|
|
|
// 9. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the empty string;
|
|
|
|
|
|
// otherwise null.
|
2023-12-23 15:59:14 +13:00
|
|
|
|
auto range_start_value = range_start.to_number<u64>();
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 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.
|
2022-07-11 21:42:14 +01:00
|
|
|
|
if (!lexer.consume_specific('-'))
|
2022-10-15 00:39:40 +02:00
|
|
|
|
return {};
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 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.
|
2022-07-11 21:42:14 +01:00
|
|
|
|
auto range_end = lexer.consume_while(is_ascii_digit);
|
|
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 15. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd is not the empty string; otherwise null.
|
2023-12-23 15:59:14 +13:00
|
|
|
|
auto range_end_value = range_end.to_number<u64>();
|
2022-10-15 00:39:40 +02:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 16. If position is not past the end of data, then return failure.
|
2022-07-11 21:42:14 +01:00
|
|
|
|
if (!lexer.is_eof())
|
2022-10-15 00:39:40 +02:00
|
|
|
|
return {};
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 17. If rangeEndValue and rangeStartValue are null, then return failure.
|
2022-10-15 00:39:40 +02:00
|
|
|
|
if (!range_end_value.has_value() && !range_start_value.has_value())
|
|
|
|
|
|
return {};
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2025-11-26 14:13:23 -05:00
|
|
|
|
// 18. If rangeStartValue and rangeEndValue are numbers, and rangeStartValue is greater than rangeEndValue, then
|
|
|
|
|
|
// return failure.
|
2022-10-15 00:39:40 +02:00
|
|
|
|
if (range_start_value.has_value() && range_end_value.has_value() && *range_start_value > *range_end_value)
|
|
|
|
|
|
return {};
|
2022-07-11 21:42:14 +01:00
|
|
|
|
|
2024-11-18 17:18:26 -06:00
|
|
|
|
// 19. Return (rangeStartValue, rangeEndValue).
|
2025-11-26 14:13:23 -05:00
|
|
|
|
return RangeHeaderValue { range_start_value, range_end_value };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace IPC {
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
ErrorOr<void> encode(Encoder& encoder, HTTP::Header const& header)
|
|
|
|
|
|
{
|
|
|
|
|
|
TRY(encoder.encode(header.name));
|
|
|
|
|
|
TRY(encoder.encode(header.value));
|
|
|
|
|
|
return {};
|
2022-07-11 21:42:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-26 14:13:23 -05:00
|
|
|
|
template<>
|
|
|
|
|
|
ErrorOr<HTTP::Header> decode(Decoder& decoder)
|
2022-10-24 09:23:18 +01:00
|
|
|
|
{
|
2025-11-26 14:13:23 -05:00
|
|
|
|
auto name = TRY(decoder.decode<ByteString>());
|
|
|
|
|
|
auto value = TRY(decoder.decode<ByteString>());
|
|
|
|
|
|
return HTTP::Header { move(name), move(value) };
|
2022-10-24 09:23:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-11 21:42:14 +01:00
|
|
|
|
}
|