| #include "http2/adapter/header_validator.h" | 
 |  | 
 | #include "absl/strings/escaping.h" | 
 | #include "common/platform/api/quiche_logging.h" | 
 |  | 
 | namespace http2 { | 
 | namespace adapter { | 
 |  | 
 | namespace { | 
 |  | 
 | const absl::string_view kHttp2HeaderNameAllowedChars = | 
 |     "!#$%&\'*+-.0123456789" | 
 |     "^_`abcdefghijklmnopqrstuvwxyz|~"; | 
 |  | 
 | const absl::string_view kHttp2HeaderValueAllowedChars = | 
 |     "\t " | 
 |     "!\"#$%&'()*+,-./" | 
 |     "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" | 
 |     "abcdefghijklmnopqrstuvwxyz{|}~"; | 
 |  | 
 | const absl::string_view kHttp2StatusValueAllowedChars = "0123456789"; | 
 |  | 
 | // TODO(birenroy): Support websocket requests, which contain an extra | 
 | // `:protocol` pseudo-header. | 
 | bool ValidateRequestHeaders(const std::vector<std::string>& pseudo_headers) { | 
 |   static const std::vector<std::string>* kRequiredHeaders = | 
 |       new std::vector<std::string>( | 
 |           {":authority", ":method", ":path", ":scheme"}); | 
 |   return pseudo_headers == *kRequiredHeaders; | 
 | } | 
 |  | 
 | bool ValidateResponseHeaders(const std::vector<std::string>& pseudo_headers) { | 
 |   static const std::vector<std::string>* kRequiredHeaders = | 
 |       new std::vector<std::string>({":status"}); | 
 |   return pseudo_headers == *kRequiredHeaders; | 
 | } | 
 |  | 
 | bool ValidateResponseTrailers(const std::vector<std::string>& pseudo_headers) { | 
 |   return pseudo_headers.empty(); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | void HeaderValidator::StartHeaderBlock() { | 
 |   pseudo_headers_.clear(); | 
 |   status_.clear(); | 
 | } | 
 |  | 
 | HeaderValidator::HeaderStatus HeaderValidator::ValidateSingleHeader( | 
 |     absl::string_view key, absl::string_view value) { | 
 |   if (key.empty()) { | 
 |     return HEADER_NAME_EMPTY; | 
 |   } | 
 |   const absl::string_view validated_key = key[0] == ':' ? key.substr(1) : key; | 
 |   if (validated_key.find_first_not_of(kHttp2HeaderNameAllowedChars) != | 
 |       absl::string_view::npos) { | 
 |     QUICHE_VLOG(2) << "invalid chars in header name: [" | 
 |                    << absl::CEscape(validated_key) << "]"; | 
 |     return HEADER_NAME_INVALID_CHAR; | 
 |   } | 
 |   if (value.find_first_not_of(kHttp2HeaderValueAllowedChars) != | 
 |       absl::string_view::npos) { | 
 |     QUICHE_VLOG(2) << "invalid chars in header value: [" << absl::CEscape(value) | 
 |                    << "]"; | 
 |     return HEADER_VALUE_INVALID_CHAR; | 
 |   } | 
 |   if (key[0] == ':') { | 
 |     if (key == ":status") { | 
 |       if (value.size() != 3 || | 
 |           value.find_first_not_of(kHttp2StatusValueAllowedChars) != | 
 |               absl::string_view::npos) { | 
 |         QUICHE_VLOG(2) << "malformed status value: [" << absl::CEscape(value) | 
 |                        << "]"; | 
 |         return HEADER_VALUE_INVALID_CHAR; | 
 |       } | 
 |       status_ = std::string(value); | 
 |     } | 
 |     pseudo_headers_.push_back(std::string(key)); | 
 |   } | 
 |   return HEADER_OK; | 
 | } | 
 |  | 
 | // Returns true if all required pseudoheaders and no extra pseudoheaders are | 
 | // present for the given header type. | 
 | bool HeaderValidator::FinishHeaderBlock(HeaderType type) { | 
 |   std::sort(pseudo_headers_.begin(), pseudo_headers_.end()); | 
 |   switch (type) { | 
 |     case HeaderType::REQUEST: | 
 |       return ValidateRequestHeaders(pseudo_headers_); | 
 |     case HeaderType::RESPONSE_100: | 
 |     case HeaderType::RESPONSE: | 
 |       return ValidateResponseHeaders(pseudo_headers_); | 
 |     case HeaderType::RESPONSE_TRAILER: | 
 |       return ValidateResponseTrailers(pseudo_headers_); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | }  // namespace adapter | 
 | }  // namespace http2 |