Commit 183a81b1 authored by Ian Clelland's avatar Ian Clelland Committed by Commit Bot

Update Structured Headers byte sequence syntax

Byte sequences, previously delimited by asterisks, have changed in
Structured Headers draft 15 to use ':' as the delimiter. This CL updates
the implementation to be in line with the latest draft, while keeping
backwards compatiblity with draft 9 for existing uses.

Bug: 1048755
Change-Id: I1e8684a016085573ab6815bb078c8f7f3a41870e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2073537
Commit-Queue: Ian Clelland <iclelland@chromium.org>
Reviewed-by: default avatarAsanka Herath <asanka@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746135}
parent 77ca1d91
...@@ -143,14 +143,21 @@ class StructuredHeaderParser { ...@@ -143,14 +143,21 @@ class StructuredHeaderParser {
case '"': case '"':
return ReadString(); return ReadString();
case '*': case '*':
return ReadByteSequence(); if (version_ == kDraft09)
return ReadByteSequence();
return ReadToken();
case ':':
if (version_ == kDraft15)
return ReadByteSequence();
return base::nullopt;
case '?': case '?':
return ReadBoolean(); return ReadBoolean();
default: default:
if (input_.front() == '-' || base::IsAsciiDigit(input_.front())) if (input_.front() == '-' || base::IsAsciiDigit(input_.front()))
return ReadNumber(); return ReadNumber();
else if (base::IsAsciiAlpha(input_.front()))
return ReadToken(); return ReadToken();
return base::nullopt;
} }
} }
...@@ -416,13 +423,14 @@ class StructuredHeaderParser { ...@@ -416,13 +423,14 @@ class StructuredHeaderParser {
// Parses a Byte Sequence ([SH09] 4.2.11, [SH15] 4.2.7). // Parses a Byte Sequence ([SH09] 4.2.11, [SH15] 4.2.7).
base::Optional<Item> ReadByteSequence() { base::Optional<Item> ReadByteSequence() {
if (!ConsumeChar('*')) { char delimiter = (version_ == kDraft09 ? '*' : ':');
LogParseError("ReadByteSequence", "'*'"); if (!ConsumeChar(delimiter)) {
LogParseError("ReadByteSequence", "delimiter");
return base::nullopt; return base::nullopt;
} }
size_t len = input_.find('*'); size_t len = input_.find(delimiter);
if (len == base::StringPiece::npos) { if (len == base::StringPiece::npos) {
DVLOG(1) << "ReadByteSequence: missing closing '*'"; DVLOG(1) << "ReadByteSequence: missing closing delimiter";
return base::nullopt; return base::nullopt;
} }
std::string base64(input_.substr(0, len)); std::string base64(input_.substr(0, len));
...@@ -435,7 +443,7 @@ class StructuredHeaderParser { ...@@ -435,7 +443,7 @@ class StructuredHeaderParser {
return base::nullopt; return base::nullopt;
} }
input_.remove_prefix(len); input_.remove_prefix(len);
ConsumeChar('*'); ConsumeChar(delimiter);
return Item(std::move(binary), Item::kByteSequenceType); return Item(std::move(binary), Item::kByteSequenceType);
} }
...@@ -470,8 +478,8 @@ class StructuredHeaderParser { ...@@ -470,8 +478,8 @@ class StructuredHeaderParser {
void LogParseError(const char* func, const char* expected) { void LogParseError(const char* func, const char* expected) {
DVLOG(1) << func << ": " << expected << " expected, got " DVLOG(1) << func << ": " << expected << " expected, got "
<< (input_.empty() ? "'" + input_.substr(0, 1).as_string() + "'" << (input_.empty() ? "EOS"
: "EOS"); : "'" + input_.substr(0, 1).as_string() + "'");
} }
base::StringPiece input_; base::StringPiece input_;
...@@ -540,10 +548,10 @@ class StructuredHeaderSerializer { ...@@ -540,10 +548,10 @@ class StructuredHeaderSerializer {
} }
if (value.is_byte_sequence()) { if (value.is_byte_sequence()) {
// Serializes a Byte Sequence ([SH15] 4.1.8). // Serializes a Byte Sequence ([SH15] 4.1.8).
output_ << "*"; output_ << ":";
output_ << base::Base64Encode( output_ << base::Base64Encode(
base::as_bytes(base::make_span(value.GetString()))); base::as_bytes(base::make_span(value.GetString())));
output_ << "*"; output_ << ":";
return true; return true;
} }
if (value.is_integer()) { if (value.is_integer()) {
......
...@@ -362,18 +362,18 @@ const struct ItemTestCase { ...@@ -362,18 +362,18 @@ const struct ItemTestCase {
{"spelled-out True boolean", "?True", base::nullopt}, {"spelled-out True boolean", "?True", base::nullopt},
{"spelled-out False boolean", "?False", base::nullopt}, {"spelled-out False boolean", "?False", base::nullopt},
// Byte Sequence // Byte Sequence
{"basic binary", "*aGVsbG8=*", Item("hello", Item::kByteSequenceType)}, {"basic binary", ":aGVsbG8=:", Item("hello", Item::kByteSequenceType)},
{"empty binary", "**", Item("", Item::kByteSequenceType)}, {"empty binary", "::", Item("", Item::kByteSequenceType)},
{"bad paddding", "*aGVsbG8*", Item("hello", Item::kByteSequenceType), {"bad paddding", ":aGVsbG8:", Item("hello", Item::kByteSequenceType),
"*aGVsbG8=*"}, ":aGVsbG8=:"},
{"bad end delimiter", "*aGVsbG8=", base::nullopt}, {"bad end delimiter", ":aGVsbG8=", base::nullopt},
{"extra whitespace", "*aGVsb G8=*", base::nullopt}, {"extra whitespace", ":aGVsb G8=:", base::nullopt},
{"extra chars", "*aGVsbG!8=*", base::nullopt}, {"extra chars", ":aGVsbG!8=:", base::nullopt},
{"suffix chars", "*aGVsbG8=!*", base::nullopt}, {"suffix chars", ":aGVsbG8=!:", base::nullopt},
{"non-zero pad bits", "*iZ==*", Item("\x89", Item::kByteSequenceType), {"non-zero pad bits", ":iZ==:", Item("\x89", Item::kByteSequenceType),
"*iQ==*"}, ":iQ==:"},
{"non-ASCII binary", "*/+Ah*", Item("\xFF\xE0!", Item::kByteSequenceType)}, {"non-ASCII binary", ":/+Ah:", Item("\xFF\xE0!", Item::kByteSequenceType)},
{"base64url binary", "*_-Ah*", base::nullopt}, {"base64url binary", ":_-Ah:", base::nullopt},
// String // String
{"basic string", "\"foo\"", Item("foo")}, {"basic string", "\"foo\"", Item("foo")},
{"empty string", "\"\"", Item("")}, {"empty string", "\"\"", Item("")},
...@@ -407,6 +407,28 @@ const struct ItemTestCase { ...@@ -407,6 +407,28 @@ const struct ItemTestCase {
{"c-style unicode escape in string", "\"\\u0061\"", base::nullopt}, {"c-style unicode escape in string", "\"\\u0061\"", base::nullopt},
}; };
const ItemTestCase sh09_item_test_cases[] = {
// Integer
{"large integer", "9223372036854775807", Integer(9223372036854775807L)},
{"large negative integer", "-9223372036854775807",
Integer(-9223372036854775807L)},
{"too large integer", "9223372036854775808", base::nullopt},
{"too large negative integer", "-9223372036854775808", base::nullopt},
// Byte Sequence
{"basic binary", "*aGVsbG8=*", Item("hello", Item::kByteSequenceType)},
{"empty binary", "**", Item("", Item::kByteSequenceType)},
{"bad paddding", "*aGVsbG8*", Item("hello", Item::kByteSequenceType),
"*aGVsbG8=*"},
{"bad end delimiter", "*aGVsbG8=", base::nullopt},
{"extra whitespace", "*aGVsb G8=*", base::nullopt},
{"extra chars", "*aGVsbG!8=*", base::nullopt},
{"suffix chars", "*aGVsbG8=!*", base::nullopt},
{"non-zero pad bits", "*iZ==*", Item("\x89", Item::kByteSequenceType),
"*iQ==*"},
{"non-ASCII binary", "*/+Ah*", Item("\xFF\xE0!", Item::kByteSequenceType)},
{"base64url binary", "*_-Ah*", base::nullopt},
};
// For Structured Headers Draft 15 // For Structured Headers Draft 15
const struct ParameterizedItemTestCase { const struct ParameterizedItemTestCase {
const char* name; const char* name;
...@@ -546,21 +568,23 @@ TEST(StructuredHeaderTest, ParseItem) { ...@@ -546,21 +568,23 @@ TEST(StructuredHeaderTest, ParseItem) {
} }
} }
// In Structured Headers Draft 9, integers can be larger than 1e15. This // Structured Headers Draft 9 parsing rules are different than Draft 15, and
// behaviour is exposed in the parser for SH09-specific lists, so test it // some strings which are considered invalid in SH15 should parse in SH09.
// through that interface. // The SH09 Item parser is not directly exposed, but can be used indirectly by
TEST(StructuredHeaderTest, SH09LargeInteger) { // calling the parser for SH09-specific lists.
base::Optional<ListOfLists> result = TEST(StructuredHeaderTest, ParseSH09Item) {
ParseListOfLists("9223372036854775807;-9223372036854775807"); for (const auto& c : sh09_item_test_cases) {
ASSERT_TRUE(result.has_value()); SCOPED_TRACE(c.name);
EXPECT_EQ(*result, (ListOfLists{{Integer(9223372036854775807L), base::Optional<ListOfLists> result = ParseListOfLists(c.raw);
Integer(-9223372036854775807L)}})); if (c.expected.has_value()) {
EXPECT_TRUE(result.has_value());
result = ParseListOfLists("9223372036854775808"); EXPECT_EQ(result->size(), 1UL);
EXPECT_FALSE(result.has_value()); EXPECT_EQ((*result)[0].size(), 1UL);
EXPECT_EQ((*result)[0][0], c.expected);
result = ParseListOfLists("-9223372036854775808"); } else {
EXPECT_FALSE(result.has_value()); EXPECT_FALSE(result.has_value());
}
}
} }
// In Structured Headers Draft 9, floats can have more than three fractional // In Structured Headers Draft 9, floats can have more than three fractional
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment