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) {