// static// Parses Fetch's "single range header value" for the "bytes" range
// unit. Optional HTTP tab/space around '=' and '-' is accepted only when
// `allow_whitespace` is true. At least one side of the range must contain
// digits.
bool HttpUtil::ParseFetchSingleRange(std::string_view value,nit: range_header_value
// Range unit must be "bytes".// Fetch requires the range unit to be exactly "bytes".
auto skip_ws = [&]() {nit: skip_optional_whitespace
auto consume_char = [&](char c) {Rename lambda parameter as `delimiter` & consume_char with `consume_delimiter`
Also add supporting comment
bool overflow = false;nit: number_overflowed
and comment : // Fetch parses range numbers as decimal numbers, but HttpByteRange stores them as int64_t, so overflow is treated as parse failure here.
auto collect_digits = [&]() -> std::optional<int64_t> {nit: parse_decimal_number
Add a supporting comment above this method
if (!base::StringToInt64(value.substr(start, pos - start), &parsed)) {`ParseInt64` makes the intent much clearer
if (!range_start) {
*range = HttpByteRange::Suffix(*range_end);
} else if (range_end) {
*range = HttpByteRange::Bounded(*range_start, *range_end);
} else {
*range = HttpByteRange::RightUnbounded(*range_start);
}
// Reject syntactically valid but invalid ranges.
return range->IsValid();
}Do not update range out parameter unless it is validated.
if (range is valid)
then only update range out parameter
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
if (!range_start) {
*range = HttpByteRange::Suffix(*range_end);
} else if (range_end) {
*range = HttpByteRange::Bounded(*range_start, *range_end);
} else {
*range = HttpByteRange::RightUnbounded(*range_start);
}
// Reject syntactically valid but invalid ranges.
return range->IsValid();
}Do not update range out parameter unless it is validated.
if (range is valid)
then only update range out parameter
Instead, the better way would be to remove out parameter and use
optional<HttpByteRange> return type.
if invalid return nullopt
// Parses Fetch's "single range header value" for the "bytes" range
// unit. Optional HTTP tab/space around '=' and '-' is accepted only when
// `allow_whitespace` is true. At least one side of the range must contain
// digits.
Updated the comment
bool HttpUtil::ParseFetchSingleRange(std::string_view value,Jatin Mittalnit: range_header_value
Renamed value to range_header_value.
HttpByteRange* range) {Jatin Mittalnit: range_out
This is no longer applicable after switching to the std::optional<HttpByteRange> return type, since the range parameter has been removed.
// Fetch requires the range unit to be exactly "bytes".
Done
auto skip_ws = [&]() {Jatin Mittalnit: skip_optional_whitespace
Renamed in the next patchset.
Rename lambda parameter as `delimiter` & consume_char with `consume_delimiter`
Also add supporting comment
both renamed in the next patchset.
nit: number_overflowed
and comment : // Fetch parses range numbers as decimal numbers, but HttpByteRange stores them as int64_t, so overflow is treated as parse failure here.
Done
nit: parse_decimal_number
Add a supporting comment above this method
Done
if (!base::StringToInt64(value.substr(start, pos - start), &parsed)) {`ParseInt64` makes the intent much clearer
Switched to net::ParseInt64(...) in the next patchset.
if (!range_start) {
*range = HttpByteRange::Suffix(*range_end);
} else if (range_end) {
*range = HttpByteRange::Bounded(*range_start, *range_end);
} else {
*range = HttpByteRange::RightUnbounded(*range_start);
}
// Reject syntactically valid but invalid ranges.
return range->IsValid();
}Mayur PatilDo not update range out parameter unless it is validated.
if (range is valid)
then only update range out parameter
Instead, the better way would be to remove out parameter and use
optional<HttpByteRange> return type.
if invalid return nullopt
Good point. Switched to a std::optional<HttpByteRange> return type and return std::nullopt on failure, which removes the out parameter and avoids partial updates.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
HttpByteRange range;| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
allow_whitespace && pos < range_header_value.size() &&allow_whitespace is checked everytime inside while loop
add a condition
```
if (allow_whitespace)
return
```
allow_whitespace && pos < range_header_value.size() &&```
// Helper: Check if we can still read from the input
auto in_bounds = [&]() {
return pos < range_header_value.size();
};
```
introduce this helper, it will make it more readable
if (pos >= range_header_value.size() ||```
if (!in_bounds()) {
return false;
}
if (range_header_value[pos] != delimiter) {
return false;
}
```
if (pos != range_header_value.size() || number_overflowed ||
(!range_start && !range_end)) {`(in_bounds() || number_overflowed || (!range_start && !range_end))`
HttpByteRange candidate_range;nit:
```
std::optional<HttpByteRange> candidate_range;
```
and accordingly update the if statement
```
if (!candidate_range->IsValid()) {
return std::nullopt;
}
```
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
constexpr std::string_view kBytesUnit = "bytes";"bytes" is used in multiple places in the file. Can you make this field reusable?
Move it to an nameless namespace and use it throughout the file?
// Verifies that optional HTTP tab/space around '=' and '-' is accepted
// only when `allow_whitespace` is true, and rejected otherwise.
TEST(HttpUtilTest, ParseFetchSingleRangeAcceptsWhitespaceWhenAllowed) {
static constexpr const char* kCases[] = {
"bytes =5-10", "bytes= 5-10", "bytes=5 -10",
"bytes=5- 10", "bytes=5\t-\t10",
};
for (const char* value : kCases) {
SCOPED_TRACE(value);
// Spec-allowed whitespace is accepted only when enabled.
std::optional<HttpByteRange> range =
HttpUtil::ParseFetchSingleRange(value, /*allow_whitespace=*/true);
ASSERT_TRUE(range.has_value());
EXPECT_EQ(5, range->first_byte_position());
EXPECT_EQ(10, range->last_byte_position());
EXPECT_FALSE(
HttpUtil::ParseFetchSingleRange(value, /*allow_whitespace=*/false)
.has_value());
}
}The test coverage is good, but consider adding two more test cases:
1. Boundary value testing: Add a test for edge cases like bytes=0- (zero-start open-ended) and bytes=-1 (single-byte suffix). These are valid but at the boundaries of typical usage.
2. Strict mode positive cases: The existing ParseFetchSingleRangeAcceptsWhitespaceWhenAllowed test verifies that allow_whitespace=false rejects whitespace, but doesn't verify that valid ranges without whitespace still parse successfully in strict mode. Add a test that confirms bytes=5-10 , bytes=5- , and bytes=-5 work with allow_whitespace=false .
// Reuses the multi-range error code; surfaces to Fetch as a TypeError.// Range header is invalid or malformed. Fail the request.
if (pos != range_header_value.size() || number_overflowed ||Overflow: The spec says "interpreted as decimal number" which is conceptually
unbounded. So `bytes=4-999999999999999999999999` should parse successfully —
the serving layer would clamp rangeEnd to fullLength-1 later. Currently int64_t
overflow → std::nullopt → network error.
Possible fix: If rangeStart parsed OK but rangeEnd overflows, saturate to a
right-unbounded range (equivalent to `bytes=4-`), since clamping would produce
the same result anyway.
if (!range.IsValid()) {
return std::nullopt;`bytes=-0`: Tracing the spec algorithm — rangeStart="" → null, rangeEnd="0" → 0.
Neither "both null" nor "start > end" conditions trigger, so it returns (null, 0).
The spec parser accepts it. Currently rejected via HttpByteRange::IsValid() inside
the parser, conflating parsing with semantic validity.
Possible fix: Move the IsValid() check to the caller (BlobURLLoader), so the
parser is purely syntactic. The caller can then reject "last 0 bytes" as a
serving-level decision.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
"bytes" is used in multiple places in the file. Can you make this field reusable?
Move it to an nameless namespace and use it throughout the file?
Done, hoisted `kBytesUnit` into the anonymous namespace and reused it across the range-parsing helpers to avoid duplicating the `"bytes"` literal.
```
// Helper: Check if we can still read from the input
auto in_bounds = [&]() {
return pos < range_header_value.size();
};
```introduce this helper, it will make it more readable
Done
allow_whitespace is checked everytime inside while loop
add a condition
```
if (allow_whitespace)
return
```
Done
```
if (!in_bounds()) {
return false;
}
if (range_header_value[pos] != delimiter) {
return false;
}
```
updated this to use `in_bounds()`
if (pos != range_header_value.size() || number_overflowed ||Overflow: The spec says "interpreted as decimal number" which is conceptually
unbounded. So `bytes=4-999999999999999999999999` should parse successfully —
the serving layer would clamp rangeEnd to fullLength-1 later. Currently int64_t
overflow → std::nullopt → network error.
Possible fix: If rangeStart parsed OK but rangeEnd overflows, saturate to a
right-unbounded range (equivalent to `bytes=4-`), since clamping would produce
the same result anyway.
Done. I updated the parser to saturate overflowing values to `int64_t::max()` rather than rejecting them. This preserves the spec behavior for all range forms (`bytes=4-999...`, `bytes=-999...`, and `bytes=999...-`) while allowing the existing `HttpByteRange::ComputeBounds()` logic to perform the appropriate clamping or rejection. Added `ParseFetchSingleRangeSaturatesOverflow` coverage for these cases as well.
if (pos != range_header_value.size() || number_overflowed ||
(!range_start && !range_end)) {`(in_bounds() || number_overflowed || (!range_start && !range_end))`
Done
nit:
```
std::optional<HttpByteRange> candidate_range;
```
and accordingly update the if statement
```
if (!candidate_range->IsValid()) {
return std::nullopt;
}
```
changed `candidate_range` to `std::optional<HttpByteRange>` and updated the validity check to use `!candidate_range->IsValid()`.
if (!range.IsValid()) {
return std::nullopt;`bytes=-0`: Tracing the spec algorithm — rangeStart="" → null, rangeEnd="0" → 0.
Neither "both null" nor "start > end" conditions trigger, so it returns (null, 0).
The spec parser accepts it. Currently rejected via HttpByteRange::IsValid() inside
the parser, conflating parsing with semantic validity.
Possible fix: Move the IsValid() check to the caller (BlobURLLoader), so the
parser is purely syntactic. The caller can then reject "last 0 bytes" as a
serving-level decision.
Separated parsing from validation. The parser now follows the Fetch spec and accepts bytes=-0 syntactically, while BlobURLLoader rejects semantically invalid ranges during request handling. This keeps parsing spec-compliant and validation at the serving layer.
// Verifies that optional HTTP tab/space around '=' and '-' is accepted
// only when `allow_whitespace` is true, and rejected otherwise.
TEST(HttpUtilTest, ParseFetchSingleRangeAcceptsWhitespaceWhenAllowed) {
static constexpr const char* kCases[] = {
"bytes =5-10", "bytes= 5-10", "bytes=5 -10",
"bytes=5- 10", "bytes=5\t-\t10",
};
for (const char* value : kCases) {
SCOPED_TRACE(value);
// Spec-allowed whitespace is accepted only when enabled.
std::optional<HttpByteRange> range =
HttpUtil::ParseFetchSingleRange(value, /*allow_whitespace=*/true);
ASSERT_TRUE(range.has_value());
EXPECT_EQ(5, range->first_byte_position());
EXPECT_EQ(10, range->last_byte_position());
EXPECT_FALSE(
HttpUtil::ParseFetchSingleRange(value, /*allow_whitespace=*/false)
.has_value());
}
}The test coverage is good, but consider adding two more test cases:
1. Boundary value testing: Add a test for edge cases like bytes=0- (zero-start open-ended) and bytes=-1 (single-byte suffix). These are valid but at the boundaries of typical usage.
2. Strict mode positive cases: The existing ParseFetchSingleRangeAcceptsWhitespaceWhenAllowed test verifies that allow_whitespace=false rejects whitespace, but doesn't verify that valid ranges without whitespace still parse successfully in strict mode. Add a test that confirms bytes=5-10 , bytes=5- , and bytes=-5 work with allow_whitespace=false .
Done. Added ParseFetchSingleRangeAcceptsBoundaryValues covering
bytes=0- and bytes=-1 ,
and ParseFetchSingleRangeAcceptsValidRangesInStrictMode which confirms
bytes=5-10, bytes=5-, and bytes=-5 all parse with
allow_whitespace=false.
// Reuses the multi-range error code; surfaces to Fetch as a TypeError.// Range header is invalid or malformed. Fail the request.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +1 |
bool allow_whitespace);Are you going to add a use of this function with `allow_whitespace` set to false?
std::optional<HttpByteRange> HttpUtil::ParseFetchSingleRange(It's good to add a fuzzer when adding a new parser. This is very easy with the `FUZZ_TEST` macro: see https://chromium.googlesource.com/chromium/src/+/HEAD/testing/libfuzzer/getting_started.md
size_t pos = kBytesUnit.size();Nit: you can make a string parser more obviously correct by having a `std::string_view` variable that contains the parts of the string you haven't yet parsed, and removing chunks from the strong with the `remove_prefix()` method as you consume them. If you use high-level methods like `find_first_not_of()` you can avoid subscripting, and bounds checks can be replaced with `!remaining_input.empty()` checks. See https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/uri_template/uri_template.cc;drc=362dbaf3c1048a93929fff003e784d537f3ebfbc;bpv=1;bpt=1;l=167?gsn=Expand&gs=KYTHE%3A%2F%2Fkythe%3A%2F%2Fchromium.googlesource.com%2Fcodesearch%2Fchromium%2Fsrc%2F%2Fmain%3Flang%3Dc%252B%252B%3Fpath%3Dnet%2Fthird_party%2Furi_template%2Furi_template.cc%23jarBFsW_57seV6_jeOna5zFFWAA5nzNowIAXLd5OK5Afor an example of this technique.
This parser is simple enough that you don't need to do this, unless you want to.
std::optional<HttpByteRange> candidate_range;
if (!range_start) {
candidate_range = HttpByteRange::Suffix(*range_end);
} else if (range_end) {
candidate_range = HttpByteRange::Bounded(*range_start, *range_end);
} else {
candidate_range = HttpByteRange::RightUnbounded(*range_start);
}
return candidate_range;Why not do:
```suggestion
if (!range_start) {
return HttpByteRange::Suffix(*range_end);
}
if (!range_end) {
return HttpByteRange::RightUnbounded(*range_start);
}
return HttpByteRange::Bounded(*range_start, *range_end);
```
?
"bytes=5-10 ",Also good to test that we reject multiple ranges, and that extra '=' or '-' are rejected:
```
"bytes=0-10,15-20",
"bytes==0-5",
"bytes=0--5",
"bytes="0-5-",
"bytes="-0-5",
```
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
bool allow_whitespace);Are you going to add a use of this function with `allow_whitespace` set to false?
I am planning to use allow_whitespace=false for the Fetch CORS-safelisted Range header check. The Fetch spec explicitly defines parsing a single range header with an allowWhitespace boolean ("To parse a single range header value from a byte sequence value and a boolean allowWhitespace, run these steps:"), so this parser is intended to be reused for that validation.
Today, the CORS Range check in services/network/public/cpp/cors/cors.cc is implemented on top of the lenient ParseRangeHeader, with additional validation for LWS/comma handling, single-range enforcement, and suffix-range exclusion. The strict parser is intended to replace this hand-rolled validation.
Since this affects observable CORS behavior, I'd prefer to make that migration in a dedicated follow-up CL with appropriate CORS/WPT coverage rather than expand the scope of this parser CL.
Spec links:
CORS-safelisted request-header (the range case, allowWhitespace = false):
https://fetch.spec.whatwg.org/#cors-safelisted-request-header
The shared parser this CL implements (parse a single range header value,
takes an allowWhitespace boolean):
https://fetch.spec.whatwg.org/#simple-range-header-value
std::optional<HttpByteRange> HttpUtil::ParseFetchSingleRange(It's good to add a fuzzer when adding a new parser. This is very easy with the `FUZZ_TEST` macro: see https://chromium.googlesource.com/chromium/src/+/HEAD/testing/libfuzzer/getting_started.md
Done. Added a `FUZZ_TEST` for `ParseFetchSingleRange` that fuzzes both the input string and the `allow_whitespace` flag, ensuring the parser never crashes. Also registered it in `net/BUILD.gn` under the `net_unittests` fuzztests list.
size_t pos = kBytesUnit.size();Nit: you can make a string parser more obviously correct by having a `std::string_view` variable that contains the parts of the string you haven't yet parsed, and removing chunks from the strong with the `remove_prefix()` method as you consume them. If you use high-level methods like `find_first_not_of()` you can avoid subscripting, and bounds checks can be replaced with `!remaining_input.empty()` checks. See https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/uri_template/uri_template.cc;drc=362dbaf3c1048a93929fff003e784d537f3ebfbc;bpv=1;bpt=1;l=167?gsn=Expand&gs=KYTHE%3A%2F%2Fkythe%3A%2F%2Fchromium.googlesource.com%2Fcodesearch%2Fchromium%2Fsrc%2F%2Fmain%3Flang%3Dc%252B%252B%3Fpath%3Dnet%2Fthird_party%2Furi_template%2Furi_template.cc%23jarBFsW_57seV6_jeOna5zFFWAA5nzNowIAXLd5OK5Afor an example of this technique.
This parser is simple enough that you don't need to do this, unless you want to.
Thank you for the suggestion and for sharing the parsing pattern. I'll leave the implementation as-is for this CL, since, as you noted, the parser is simple enough that it doesn't require this approach. The current implementation keeps all accesses bounds-checked via the in_bounds() helper, and it is covered by both the unit tests and the new fuzzer, so I'd prefer to avoid refactoring the already-reviewed parsing logic for a stylistic change
std::optional<HttpByteRange> candidate_range;
if (!range_start) {
candidate_range = HttpByteRange::Suffix(*range_end);
} else if (range_end) {
candidate_range = HttpByteRange::Bounded(*range_start, *range_end);
} else {
candidate_range = HttpByteRange::RightUnbounded(*range_start);
}
return candidate_range;Why not do:
```suggestion
if (!range_start) {
return HttpByteRange::Suffix(*range_end);
}
if (!range_end) {
return HttpByteRange::RightUnbounded(*range_start);
}
return HttpByteRange::Bounded(*range_start, *range_end);
```
?
Good suggestion. Updated to use early returns and dropped the temporary `std::optional`.
"bytes=5-10 ",Also good to test that we reject multiple ranges, and that extra '=' or '-' are rejected:
```
"bytes=0-10,15-20",
"bytes==0-5",
"bytes=0--5",
"bytes="0-5-",
"bytes="-0-5",
```
Done. Added test cases for multiple ranges (`bytes=0-10,15-20`) and invalid extra `=`/`-` delimiters (`bytes==0-5`, `bytes=0--5`, `bytes=0-5-`, and `bytes=-0-5`).
| Code-Review | +1 |
third_party/blink/renderer/platform/runtime_enabled_features.json5 LGTM
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
I just noticed there's 500 people in CC? That doesn't seem right... did an agent do that?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
I just noticed there's 500 people in CC? That doesn't seem right... did an agent do that?
Yes, Mike. That happened during a rebase. I noticed it afterward and tried to clean it up, but I couldn't find an option to remove all of the CCs at once.
I'll take extra care going forward to ensure it doesn't happen again.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
// Kill switch for Fetch-compliant Range header validation on blob: URLs.Sorry, I mistakenly assumed the cost was in Blink. Since the feature is not in Blink, it is wasteful (and an encapsulation violation) to define it here, and you should define i in storage/browser/blob/features.h instead.
// Kill switch for Fetch-compliant Range header validation on blob: URLs.Sorry, I mistakenly assumed the cost was in Blink. Since the feature is not in Blink, it is wasteful (and an encapsulation violation) to define it here, and you should define i in storage/browser/blob/features.h instead.
Done, I've updated it. Thanks for the clarification, and sorry for the oversight. I'm still getting familiar with the repository structure, and I'll make sure to pay closer attention to this in the future.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |