Skip to content

Commit

Permalink
SignedExchange: Add http_structured_header::ParseListOfLists()
Browse files Browse the repository at this point in the history
This will be used to parse Variants and Variant-Key headers.

Bug: 923229
Change-Id: I3ad3b8b18b4cd396bd01a79d06fffa6c04c86809
Reviewed-on: https://chromium-review.googlesource.com/c/1420509
Reviewed-by: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Chris Palmer <palmer@chromium.org>
Commit-Queue: Kunihiko Sakamoto <ksakamoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#628649}
  • Loading branch information
irori authored and Commit Bot committed Feb 4, 2019
1 parent 4d5a709 commit f815b4a
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 1 deletion.
32 changes: 32 additions & 0 deletions content/browser/web_package/http_structured_header.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ class StructuredHeaderParser {
return input_.empty();
}

// Parses a List of Lists ([SH] 4.2.4).
base::Optional<ListOfLists> ReadListOfLists() {
ListOfLists result;
while (true) {
std::vector<std::string> inner_list;
while (true) {
base::Optional<std::string> item = ReadItem();
if (!item)
return base::nullopt;
inner_list.push_back(*item);
SkipWhitespaces();
if (!ConsumeChar(';'))
break;
SkipWhitespaces();
}
result.push_back(std::move(inner_list));
SkipWhitespaces();
if (!ConsumeChar(','))
break;
SkipWhitespaces();
}
return result;
}

// Parses an Item ([SH] 4.2.7).
// Currently only limited types (non-negative integers, strings, tokens and
// byte sequences) are supported, and all types are returned as a string
Expand Down Expand Up @@ -287,5 +311,13 @@ base::Optional<ParameterisedList> ParseParameterisedList(
return base::nullopt;
}

base::Optional<ListOfLists> ParseListOfLists(const base::StringPiece& str) {
StructuredHeaderParser parser(str);
base::Optional<ListOfLists> list_of_lists = parser.ReadListOfLists();
if (list_of_lists && parser.FinishParsing())
return list_of_lists;
return base::nullopt;
}

} // namespace http_structured_header
} // namespace content
5 changes: 4 additions & 1 deletion content/browser/web_package/http_structured_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ struct CONTENT_EXPORT ParameterisedIdentifier {
~ParameterisedIdentifier();
};

typedef std::vector<ParameterisedIdentifier> ParameterisedList;
using ParameterisedList = std::vector<ParameterisedIdentifier>;
using ListOfLists = std::vector<std::vector<std::string>>;

CONTENT_EXPORT base::Optional<std::string> ParseItem(
const base::StringPiece& str);
CONTENT_EXPORT base::Optional<ParameterisedList> ParseParameterisedList(
const base::StringPiece& str);
CONTENT_EXPORT base::Optional<ListOfLists> ParseListOfLists(
const base::StringPiece& str);

} // namespace http_structured_header
} // namespace content
Expand Down
35 changes: 35 additions & 0 deletions content/browser/web_package/http_structured_header_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,41 @@ TEST(StructuredHeaderTest, ParseItem) {
}
}

TEST(StructuredHeaderTest, ParseListOfLists) {
struct TestCase {
const char* name;
const char* raw;
ListOfLists expected; // empty if parse error is expected
} cases[] = {
{"basic list of lists", "1;2, 42;43", {{"1", "2"}, {"42", "43"}}},
{"empty list of lists", "", {}},
{"single item list of lists", "42", {{"42"}}},
{"no whitespace list of lists", "1,42", {{"1"}, {"42"}}},
{"no inner whitespace list of lists",
"1;2, 42;43",
{{"1", "2"}, {"42", "43"}}},
{"extra whitespace list of lists", "1 , 42", {{"1"}, {"42"}}},
{"extra inner whitespace list of lists",
"1 ; 2,42 ; 43",
{{"1", "2"}, {"42", "43"}}},
{"trailing comma list of lists", "1;2, 42,", {}},
{"trailing semicolon list of lists", "1;2, 42;43;", {}},
{"leading comma list of lists", ",1;2, 42", {}},
{"leading semicolon list of lists", ";1;2, 42;43", {}},
{"empty item list of lists", "1,,42", {}},
{"empty inner item list of lists", "1;;2,42", {}},
};
for (const auto& c : cases) {
base::Optional<ListOfLists> result = ParseListOfLists(c.raw);
if (!c.expected.empty()) {
EXPECT_TRUE(result.has_value()) << c.name;
EXPECT_EQ(*result, c.expected) << c.name;
} else {
EXPECT_FALSE(result.has_value()) << c.name;
}
}
}

inline bool operator==(const ParameterisedIdentifier& lhs,
const ParameterisedIdentifier& rhs) {
return lhs.identifier == rhs.identifier && lhs.params == rhs.params;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Accept;text/plain;text/html, Accept-Encoding;gzip;br, Accept-Language;en;fr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
text/plain;gzip;en, "text/html;charset=utf8";gzip;en, "*/*";"identity";"*"
2 changes: 2 additions & 0 deletions content/test/fuzzer/http_structured_header_fuzzer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace http_structured_header {

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
base::StringPiece input(reinterpret_cast<const char*>(data), size);
ParseItem(input);
ParseListOfLists(input);
ParseParameterisedList(input);
return 0;
}
Expand Down

0 comments on commit f815b4a

Please sign in to comment.