| #include "quiche/balsa/header_properties.h" | |
| #include <array> | |
| #include <cstdint> | |
| #include "absl/container/flat_hash_set.h" | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/common/quiche_text_utils.h" | |
| namespace quiche::header_properties { | |
| namespace { | |
| // The set of characters allowed in HTTP `token`s. See | |
| // https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.2 | |
| inline constexpr unsigned char kValidTokenCharList[] = { | |
| 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', | |
| 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', | |
| 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', | |
| 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', | |
| '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', | |
| '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~'}; | |
| using MultivaluedHeadersSet = | |
| absl::flat_hash_set<absl::string_view, StringPieceCaseHash, | |
| StringPieceCaseEqual>; | |
| MultivaluedHeadersSet* buildMultivaluedHeaders() { | |
| MultivaluedHeadersSet* multivalued_headers = new MultivaluedHeadersSet({ | |
| // clang-format off | |
| "accept", | |
| "accept-charset", | |
| "accept-encoding", | |
| "accept-language", | |
| "accept-ranges", | |
| // The follow four headers are all CORS standard headers | |
| "access-control-allow-headers", | |
| "access-control-allow-methods", | |
| "access-control-expose-headers", | |
| "access-control-request-headers", | |
| "allow", | |
| "cache-control", | |
| // IETF draft makes this have cache-control syntax | |
| "cdn-cache-control", | |
| "connection", | |
| "content-encoding", | |
| "content-language", | |
| "expect", | |
| "if-match", | |
| "if-none-match", | |
| // See RFC 5988 section 5 | |
| "link", | |
| "pragma", | |
| "proxy-authenticate", | |
| "te", | |
| // Used in the opening handshake of the WebSocket protocol. | |
| "sec-websocket-extensions", | |
| // Not mentioned in RFC 2616, but it can have multiple values. | |
| "set-cookie", | |
| "trailer", | |
| "transfer-encoding", | |
| "upgrade", | |
| "vary", | |
| "via", | |
| "warning", | |
| "www-authenticate", | |
| // De facto standard not in the RFCs | |
| "x-forwarded-for", | |
| // Internal Google usage gives this cache-control syntax | |
| "x-go" /**/ "ogle-cache-control", | |
| // clang-format on | |
| }); | |
| return multivalued_headers; | |
| } | |
| constexpr std::array<bool, 256> buildInvalidHeaderKeyCharLookupTable() { | |
| std::array<bool, 256> invalidCharTable{}; | |
| for (uint8_t c : kInvalidHeaderKeyCharList) { | |
| invalidCharTable[c] = true; | |
| } | |
| return invalidCharTable; | |
| } | |
| constexpr std::array<bool, 256> | |
| buildInvalidHeaderKeyCharLookupTableAllowDoubleQuote() { | |
| std::array<bool, 256> invalidCharTable{}; | |
| for (uint8_t c : kInvalidHeaderKeyCharListAllowDoubleQuote) { | |
| invalidCharTable[c] = true; | |
| } | |
| return invalidCharTable; | |
| } | |
| constexpr std::array<bool, 256> buildInvalidCharLookupTable() { | |
| std::array<bool, 256> invalidCharTable{}; | |
| for (uint8_t c : kInvalidHeaderCharList) { | |
| invalidCharTable[c] = true; | |
| } | |
| return invalidCharTable; | |
| } | |
| constexpr std::array<bool, 256> kAllTrueArray = { | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true, true, true, true, true, true, true, true, true, | |
| true, true, true, true}; | |
| constexpr std::array<bool, 256> buildInvalidPathCharLookupTable() { | |
| std::array<bool, 256> invalidCharTable = kAllTrueArray; | |
| for (uint8_t c : kValidPathCharList) { | |
| invalidCharTable[c] = false; | |
| } | |
| return invalidCharTable; | |
| } | |
| constexpr std::array<bool, 256> buildInvalidQueryCharLookupTable() { | |
| std::array<bool, 256> invalidCharTable = kAllTrueArray; | |
| for (uint8_t c : kValidQueryCharList) { | |
| invalidCharTable[c] = false; | |
| } | |
| return invalidCharTable; | |
| } | |
| constexpr std::array<bool, 256> buildValidTokenCharLookupTable() { | |
| std::array<bool, 256> validTokenCharTable{}; | |
| for (uint8_t c : kValidTokenCharList) { | |
| validTokenCharTable[c] = true; | |
| } | |
| return validTokenCharTable; | |
| } | |
| constexpr std::array<bool, 256> buildValidChunkExtensionValCharLookupTable() { | |
| std::array<bool, 256> validChunkExtValCharTable = kAllTrueArray; | |
| constexpr uint8_t US_ASCII_CONTROL_START = 0; | |
| constexpr uint8_t US_ASCII_CONTROL_END = 32; // exclusive | |
| // Valid chunk-ext-val characters are the entire range of visible US-ASCII | |
| // characters and all extended ASCII characters. Of the control characters, | |
| // only HTAB is allowed. There are some more precise rules for when DQUOTE | |
| // and backslash can appear in a quoted-string which we skip here. | |
| for (uint8_t c = US_ASCII_CONTROL_START; c < US_ASCII_CONTROL_END; ++c) { | |
| if (c == '\t') { | |
| continue; | |
| } | |
| validChunkExtValCharTable[c] = false; | |
| } | |
| // Also exclude the control character DEL. | |
| validChunkExtValCharTable[127] = false; | |
| return validChunkExtValCharTable; | |
| } | |
| } // anonymous namespace | |
| bool IsMultivaluedHeader(absl::string_view header) { | |
| static const MultivaluedHeadersSet* const multivalued_headers = | |
| buildMultivaluedHeaders(); | |
| return multivalued_headers->contains(header); | |
| } | |
| bool IsInvalidHeaderKeyChar(uint8_t c) { | |
| static constexpr std::array<bool, 256> invalidHeaderKeyCharTable = | |
| buildInvalidHeaderKeyCharLookupTable(); | |
| return invalidHeaderKeyCharTable[c]; | |
| } | |
| bool IsInvalidHeaderKeyCharAllowDoubleQuote(uint8_t c) { | |
| static constexpr std::array<bool, 256> invalidHeaderKeyCharTable = | |
| buildInvalidHeaderKeyCharLookupTableAllowDoubleQuote(); | |
| return invalidHeaderKeyCharTable[c]; | |
| } | |
| bool IsInvalidHeaderChar(uint8_t c) { | |
| static constexpr std::array<bool, 256> invalidCharTable = | |
| buildInvalidCharLookupTable(); | |
| return invalidCharTable[c]; | |
| } | |
| bool IsValidTokenChar(uint8_t c) { | |
| static constexpr std::array<bool, 256> validTokenCharTable = | |
| buildValidTokenCharLookupTable(); | |
| return validTokenCharTable[c]; | |
| } | |
| bool IsValidToken(absl::string_view value) { | |
| if (value.empty()) { | |
| return false; | |
| } | |
| for (const char c : value) { | |
| if (!IsValidTokenChar(static_cast<uint8_t>(c))) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool IsValidChunkExtension(absl::string_view value) { | |
| for (const char c : value) { | |
| if (!IsValidChunkExtensionValChar(static_cast<uint8_t>(c))) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool IsValidChunkExtensionValChar(uint8_t c) { | |
| static constexpr std::array<bool, 256> validChunkExtValCharTable = | |
| buildValidChunkExtensionValCharLookupTable(); | |
| return validChunkExtValCharTable[c]; | |
| } | |
| bool HasInvalidHeaderChars(absl::string_view value) { | |
| for (const char c : value) { | |
| if (IsInvalidHeaderChar(c)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool HasInvalidPathChar(absl::string_view value) { | |
| static constexpr std::array<bool, 256> invalidCharTable = | |
| buildInvalidPathCharLookupTable(); | |
| for (const char c : value) { | |
| if (invalidCharTable[c]) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool HasInvalidQueryChar(absl::string_view value) { | |
| static constexpr std::array<bool, 256> invalidCharTable = | |
| buildInvalidQueryCharLookupTable(); | |
| for (const char c : value) { | |
| if (invalidCharTable[c]) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| } // namespace quiche::header_properties |