Use static char maps in HeaderValidator::ValidateSingleHeader(). When validating header names/values against allowed characters, switching to static char maps may help with performance. This CL is otherwise not a functional change. PiperOrigin-RevId: 425681702
diff --git a/http2/adapter/header_validator.cc b/http2/adapter/header_validator.cc index dd605f4..470b9f7 100644 --- a/http2/adapter/header_validator.cc +++ b/http2/adapter/header_validator.cc
@@ -1,5 +1,7 @@ #include "http2/adapter/header_validator.h" +#include <array> + #include "absl/strings/escaping.h" #include "absl/strings/numbers.h" #include "common/platform/api/quiche_logging.h" @@ -25,26 +27,51 @@ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~%!$&'()[" "]*+,;=:"; -// Returns whether `authority` contains only characters from the `host` ABNF -// from RFC 3986 section 3.2.2. -bool IsValidAuthority(absl::string_view authority) { - static const bool* valid_chars = []() { - using ValidCharArray = bool[256]; - bool* chars = new ValidCharArray; - memset(chars, 0, sizeof(ValidCharArray)); - for (char c : kValidAuthorityChars) { - chars[static_cast<uint8_t>(c)] = true; - } - return chars; - }(); - for (char c : authority) { - if (!valid_chars[static_cast<uint8_t>(c)]) { +using CharMap = std::array<bool, 256>; + +CharMap BuildValidCharMap(absl::string_view valid_chars) { + CharMap map; + map.fill(false); + for (char c : valid_chars) { + map[c] = true; + } + return map; +} + +bool AllCharsInMap(absl::string_view str, const CharMap& map) { + for (char c : str) { + if (!map[c]) { return false; } } return true; } +// Returns whether `authority` contains only characters from the `host` ABNF +// from RFC 3986 section 3.2.2. +bool IsValidAuthority(absl::string_view authority) { + static const CharMap valid_chars = BuildValidCharMap(kValidAuthorityChars); + return AllCharsInMap(authority, valid_chars); +} + +bool IsValidHeaderName(absl::string_view name) { + static const CharMap valid_chars = + BuildValidCharMap(kHttp2HeaderNameAllowedChars); + return AllCharsInMap(name, valid_chars); +} + +bool IsValidHeaderValue(absl::string_view value) { + static const CharMap valid_chars = + BuildValidCharMap(kHttp2HeaderValueAllowedChars); + return AllCharsInMap(value, valid_chars); +} + +bool IsValidStatus(absl::string_view status) { + static const CharMap valid_chars = + BuildValidCharMap(kHttp2StatusValueAllowedChars); + return AllCharsInMap(status, valid_chars); +} + bool ValidateRequestHeaders(const std::vector<std::string>& pseudo_headers, absl::string_view method, absl::string_view path, bool allow_connect) { @@ -112,23 +139,19 @@ return HEADER_FIELD_TOO_LONG; } const absl::string_view validated_key = key[0] == ':' ? key.substr(1) : key; - if (validated_key.find_first_not_of(kHttp2HeaderNameAllowedChars) != - absl::string_view::npos) { + if (!IsValidHeaderName(validated_key)) { QUICHE_VLOG(2) << "invalid chars in header name: [" << absl::CEscape(validated_key) << "]"; return HEADER_FIELD_INVALID; } - if (value.find_first_not_of(kHttp2HeaderValueAllowedChars) != - absl::string_view::npos) { + if (!IsValidHeaderValue(value)) { QUICHE_VLOG(2) << "invalid chars in header value: [" << absl::CEscape(value) << "]"; return HEADER_FIELD_INVALID; } if (key[0] == ':') { if (key == ":status") { - if (value.size() != 3 || - value.find_first_not_of(kHttp2StatusValueAllowedChars) != - absl::string_view::npos) { + if (value.size() != 3 || !IsValidStatus(value)) { QUICHE_VLOG(2) << "malformed status value: [" << absl::CEscape(value) << "]"; return HEADER_FIELD_INVALID;