|  | #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_); | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace adapter | 
|  | }  // namespace http2 |