1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:08:11 +00:00

LibWeb/Fetch: Add support for parsing a single range header value

This is a change in the Fetch spec.

See:
- 2d080d7
- a18fb3e
This commit is contained in:
Linus Groh 2022-10-15 00:39:40 +02:00
parent b8e0cec20e
commit 60e9790a40
2 changed files with 39 additions and 27 deletions

View file

@ -425,8 +425,16 @@ bool is_cors_safelisted_request_header(Header const& header)
}
// `range`
else if (name.equals_ignoring_case("range"sv)) {
// If value is not a simple range header value, then return false.
if (!is_simple_range_header_value(value))
// 1. Let rangeValue be the result of parsing a single range header value given value.
auto range_value = parse_single_range_header_value(value);
// 2. If rangeValue is failure, then return false.
if (!range_value.has_value())
return false;
// 3. If rangeValue[0] is null, then return false.
// NOTE: As web browsers have historically not emitted ranges such as `bytes=-500` this algorithm does not safelist them.
if (!range_value->start.has_value())
return false;
}
// Otherwise
@ -616,16 +624,14 @@ bool is_request_body_header_name(ReadonlyBytes header_name)
// TODO: https://fetch.spec.whatwg.org/#extract-header-list-values
// https://fetch.spec.whatwg.org/#simple-range-header-value
bool is_simple_range_header_value(ReadonlyBytes value)
Optional<RangeHeaderValue> parse_single_range_header_value(ReadonlyBytes value)
{
// To determine if a byte sequence value is a simple range header value, perform the following steps. They return a boolean.
// 1. Let data be the isomorphic decoding of value.
auto data = StringView { value };
// 2. If data does not start with "bytes=", then return false.
// 2. If data does not start with "bytes=", then return failure.
if (!data.starts_with("bytes="sv))
return false;
return {};
// 3. Let position be a position variable for data, initially pointing at the 6th code point of data.
auto lexer = GenericLexer { data };
@ -634,34 +640,35 @@ bool is_simple_range_header_value(ReadonlyBytes value)
// 4. 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);
// FIXME: I believe the case of an empty rangeStart string (`bytes=-`) is not handled,
// and would later fail 'interpreted as decimal number'.
if (range_start.is_empty())
return false;
// 5. Let rangeStartValue be rangeStart, interpreted as decimal number, if rangeStart is not the empty string; otherwise null.
auto range_start_value = range_start.to_uint<u64>();
// 5. If the code point at position within data is not U+002D (-), then return false.
// 6. Advance position by 1.
// 6. If the code point at position within data is not U+002D (-), then return failure.
// 7. Advance position by 1.
if (!lexer.consume_specific('-'))
return false;
return {};
// 7. Let rangeEnd be the result of collecting a sequence of code points that are ASCII digits, from data given position.
// 8. 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);
// 8. If position is not past the end of data, then return false.
// 9. Let rangeEndValue be rangeEnd, interpreted as decimal number, if rangeEnd is not the empty string; otherwise null.
auto range_end_value = range_end.to_uint<u64>();
// 10. If position is not past the end of data, then return failure.
if (!lexer.is_eof())
return false;
return {};
// 9. If rangeEnds length is 0, then return true.
// NOTE: The range end can be omitted, e.g., `bytes=0-` is valid.
if (range_end.is_empty())
return true;
// 11. If rangeEndValue and rangeStartValue are null, then return failure.
if (!range_end_value.has_value() && !range_start_value.has_value())
return {};
// 10. If rangeStart, interpreted as decimal number, is greater than rangeEnd, interpreted as decimal number, then return false.
if (*range_start.to_uint<u64>() > *range_end.to_uint<u64>())
return false;
// 12. 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 {};
// 11. Return true.
return true;
// 13. Return (rangeStartValue, rangeEndValue).
// NOTE: The range end or start can be omitted, e.g., `bytes=0-` or `bytes=-500` are valid ranges.
return RangeHeaderValue { move(range_start_value), move(range_end_value) };
}
}