Move IsInvalidHeaderKeyChar() from balsa_frame.cc anonymous namespace to header_properties. This is for consistency with IsInvalidHeaderChar(). Also, add tests for IsInvalidHeaderKeyChar(), and extend tests for IsInvalidHeaderChar(). PiperOrigin-RevId: 446704033
diff --git a/quiche/common/balsa/balsa_frame.cc b/quiche/common/balsa/balsa_frame.cc index b148a42..cb7db65 100644 --- a/quiche/common/balsa/balsa_frame.cc +++ b/quiche/common/balsa/balsa_frame.cc
@@ -34,22 +34,6 @@ constexpr absl::string_view kIdentity = "identity"; constexpr absl::string_view kTransferEncoding = "transfer-encoding"; -std::array<bool, 256> buildInvalidHeaderKeyCharLookupTable() { - std::array<bool, 256> invalidCharTable; - invalidCharTable.fill(false); - for (uint8_t c : BalsaFrame::kInvalidHeaderKeyCharList) { - invalidCharTable[c] = true; - } - return invalidCharTable; -} - -inline bool IsInvalidHeaderKeyChar(uint8_t c) { - static const std::array<bool, 256> invalidHeaderKeyCharTable = - buildInvalidHeaderKeyCharLookupTable(); - - return invalidHeaderKeyCharTable[c]; -} - } // namespace void BalsaFrame::Reset() { @@ -408,7 +392,7 @@ break; } - if (IsInvalidHeaderKeyChar(*current)) { + if (header_properties::IsInvalidHeaderKeyChar(*current)) { // Generally invalid characters were found earlier. HandleError(is_trailer ? BalsaFrameEnums::INVALID_TRAILER_NAME_CHARACTER
diff --git a/quiche/common/balsa/balsa_frame.h b/quiche/common/balsa/balsa_frame.h index 5b54272..2711ac0 100644 --- a/quiche/common/balsa/balsa_frame.h +++ b/quiche/common/balsa/balsa_frame.h
@@ -34,17 +34,6 @@ typedef BalsaHeaders::HeaderLines HeaderLines; typedef BalsaHeaders::HeaderTokenList HeaderTokenList; - // Only applied in strict mode. - // Control characters, including \t, \n, \r, as well as space and - // (),/;<=>?@[\]{} and \x7f (see - // https://tools.ietf.org/html/rfc7230#section-3.2.6). - static constexpr char kInvalidHeaderKeyCharList[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, - 0x1E, 0x1F, ' ', '(', ')', ',', '/', ';', '<', '=', - '>', '?', '@', '[', '\\', ']', '{', '}', 0x7f}; - enum class InvalidCharsLevel { kOff, kWarning, kError }; // TODO(fenix): get rid of the 'kValidTerm*' stuff by using the 'since last
diff --git a/quiche/common/balsa/header_properties.cc b/quiche/common/balsa/header_properties.cc index 6722239..561fe7a 100644 --- a/quiche/common/balsa/header_properties.cc +++ b/quiche/common/balsa/header_properties.cc
@@ -59,6 +59,15 @@ }); } +std::array<bool, 256> buildInvalidHeaderKeyCharLookupTable() { + std::array<bool, 256> invalidCharTable; + invalidCharTable.fill(false); + for (uint8_t c : kInvalidHeaderKeyCharList) { + invalidCharTable[c] = true; + } + return invalidCharTable; +} + std::array<bool, 256> buildInvalidCharLookupTable() { std::array<bool, 256> invalidCharTable; invalidCharTable.fill(false); @@ -76,6 +85,13 @@ return multivalued_headers->contains(header); } +bool IsInvalidHeaderKeyChar(uint8_t c) { + static const std::array<bool, 256> invalidHeaderKeyCharTable = + buildInvalidHeaderKeyCharLookupTable(); + + return invalidHeaderKeyCharTable[c]; +} + bool IsInvalidHeaderChar(uint8_t c) { static const std::array<bool, 256> invalidCharTable = buildInvalidCharLookupTable();
diff --git a/quiche/common/balsa/header_properties.h b/quiche/common/balsa/header_properties.h index 6b43763..2539a90 100644 --- a/quiche/common/balsa/header_properties.h +++ b/quiche/common/balsa/header_properties.h
@@ -18,6 +18,17 @@ // be perfectly reliable in practice. QUICHE_EXPORT_PRIVATE bool IsMultivaluedHeader(absl::string_view header); +// An array of characters that are invalid in HTTP header field names. +// These are control characters, including \t, \n, \r, as well as space and +// (),/;<=>?@[\]{} and \x7f (see +// https://tools.ietf.org/html/rfc7230#section-3.2.6). +inline constexpr char kInvalidHeaderKeyCharList[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, ' ', '(', ')', ',', '/', ';', '<', '=', + '>', '?', '@', '[', '\\', ']', '{', '}', 0x7f}; + // An array of characters that are invalid in HTTP header field values, // according to RFC 7230 Section 3.2. Valid low characters not in this array // are \t (0x09), \n (0x0A), and \r (0x0D). @@ -27,6 +38,8 @@ 0x0C, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x7F}; +// Returns true if the given `c` is invalid in a header field name. +QUICHE_EXPORT_PRIVATE bool IsInvalidHeaderKeyChar(uint8_t c); // Returns true if the given `c` is invalid in a header field or the `value` has // invalid characters. QUICHE_EXPORT_PRIVATE bool IsInvalidHeaderChar(uint8_t c);
diff --git a/quiche/common/balsa/header_properties_test.cc b/quiche/common/balsa/header_properties_test.cc index ffb6b83..c9dfc6d 100644 --- a/quiche/common/balsa/header_properties_test.cc +++ b/quiche/common/balsa/header_properties_test.cc
@@ -18,17 +18,48 @@ EXPECT_FALSE(IsMultivaluedHeader("Content-Length")); } +TEST(HeaderPropertiesTest, IsInvalidHeaderKeyChar) { + EXPECT_TRUE(IsInvalidHeaderKeyChar(0x00)); + EXPECT_TRUE(IsInvalidHeaderKeyChar(0x06)); + EXPECT_TRUE(IsInvalidHeaderKeyChar(0x09)); + EXPECT_TRUE(IsInvalidHeaderKeyChar(0x1F)); + EXPECT_TRUE(IsInvalidHeaderKeyChar(0x7F)); + EXPECT_TRUE(IsInvalidHeaderKeyChar(' ')); + EXPECT_TRUE(IsInvalidHeaderKeyChar('\t')); + EXPECT_TRUE(IsInvalidHeaderKeyChar('\r')); + EXPECT_TRUE(IsInvalidHeaderKeyChar('\n')); + + EXPECT_FALSE(IsInvalidHeaderKeyChar('a')); + EXPECT_FALSE(IsInvalidHeaderKeyChar('B')); + EXPECT_FALSE(IsInvalidHeaderKeyChar('7')); + EXPECT_FALSE(IsInvalidHeaderKeyChar(0x42)); + EXPECT_FALSE(IsInvalidHeaderChar(0x7D)); +} + TEST(HeaderPropertiesTest, IsInvalidHeaderChar) { EXPECT_TRUE(IsInvalidHeaderChar(0x00)); EXPECT_TRUE(IsInvalidHeaderChar(0x06)); EXPECT_TRUE(IsInvalidHeaderChar(0x1F)); EXPECT_TRUE(IsInvalidHeaderChar(0x7F)); + EXPECT_FALSE(IsInvalidHeaderChar(0x09)); EXPECT_FALSE(IsInvalidHeaderChar(' ')); EXPECT_FALSE(IsInvalidHeaderChar('\t')); EXPECT_FALSE(IsInvalidHeaderChar('\r')); EXPECT_FALSE(IsInvalidHeaderChar('\n')); + EXPECT_FALSE(IsInvalidHeaderChar('a')); + EXPECT_FALSE(IsInvalidHeaderChar('B')); + EXPECT_FALSE(IsInvalidHeaderChar('7')); EXPECT_FALSE(IsInvalidHeaderChar(0x42)); + EXPECT_FALSE(IsInvalidHeaderChar(0x7D)); +} + +TEST(HeaderPropertiesTest, KeyMoreRestrictiveThanValue) { + for (int c = 0; c < 255; ++c) { + if (IsInvalidHeaderChar(c)) { + EXPECT_TRUE(IsInvalidHeaderKeyChar(c)) << c; + } + } } TEST(HeaderPropertiesTest, HasInvalidHeaderChars) {