LCOV - code coverage report
Current view: top level - source/common/http - header_utility.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 19 23 82.6 %
Date: 2024-01-05 06:35:25 Functions: 3 5 60.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <vector>
       4             : 
       5             : #include "envoy/common/matchers.h"
       6             : #include "envoy/common/regex.h"
       7             : #include "envoy/config/core/v3/protocol.pb.h"
       8             : #include "envoy/config/route/v3/route_components.pb.h"
       9             : #include "envoy/http/header_map.h"
      10             : #include "envoy/http/header_validator.h"
      11             : #include "envoy/http/protocol.h"
      12             : #include "envoy/type/v3/range.pb.h"
      13             : 
      14             : #include "source/common/http/status.h"
      15             : #include "source/common/protobuf/protobuf.h"
      16             : 
      17             : namespace Envoy {
      18             : namespace Http {
      19             : 
      20             : /**
      21             :  * Classes and methods for manipulating and checking HTTP headers.
      22             :  */
      23             : class HeaderUtility {
      24             : public:
      25             :   enum class HeaderMatchType {
      26             :     Value,
      27             :     Regex,
      28             :     Range,
      29             :     Present,
      30             :     Prefix,
      31             :     Suffix,
      32             :     Contains,
      33             :     StringMatch
      34             :   };
      35             : 
      36             :   /**
      37             :    * Get all header values as a single string. Multiple headers are concatenated with ','.
      38             :    */
      39             :   class GetAllOfHeaderAsStringResult {
      40             :   public:
      41             :     // The ultimate result of the concatenation. If absl::nullopt, no header values were found.
      42             :     // If the final string required a string allocation, the memory is held in
      43             :     // backingString(). This allows zero allocation in the common case of a single header
      44             :     // value.
      45          27 :     absl::optional<absl::string_view> result() const {
      46             :       // This is safe for move/copy of this class as the backing string will be moved or copied.
      47             :       // Otherwise result_ is valid. The assert verifies that both are empty or only 1 is set.
      48          27 :       ASSERT((!result_.has_value() && result_backing_string_.empty()) ||
      49          27 :              (result_.has_value() ^ !result_backing_string_.empty()));
      50          27 :       return !result_backing_string_.empty() ? result_backing_string_ : result_;
      51          27 :     }
      52             : 
      53           0 :     const std::string& backingString() const { return result_backing_string_; }
      54             : 
      55             :   private:
      56             :     absl::optional<absl::string_view> result_;
      57             :     // Valid only if result_ relies on memory allocation that must live beyond the call. See above.
      58             :     std::string result_backing_string_;
      59             : 
      60             :     friend class HeaderUtility;
      61             :   };
      62             :   static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap::GetResult& header,
      63             :                                                              absl::string_view separator = ",");
      64             :   static GetAllOfHeaderAsStringResult getAllOfHeaderAsString(const HeaderMap& headers,
      65             :                                                              const Http::LowerCaseString& key,
      66             :                                                              absl::string_view separator = ",");
      67             : 
      68             :   // A HeaderData specifies one of exact value or regex or range element
      69             :   // to match in a request's header, specified in the header_match_type_ member.
      70             :   // It is the runtime equivalent of the HeaderMatchSpecifier proto in RDS API.
      71             :   struct HeaderData : public HeaderMatcher {
      72             :     HeaderData(const envoy::config::route::v3::HeaderMatcher& config);
      73             : 
      74             :     const LowerCaseString name_;
      75             :     HeaderMatchType header_match_type_;
      76             :     std::string value_;
      77             :     Regex::CompiledMatcherPtr regex_;
      78             :     envoy::type::v3::Int64Range range_;
      79             :     Matchers::StringMatcherPtr string_match_;
      80             :     const bool invert_match_;
      81             :     const bool treat_missing_as_empty_;
      82             :     bool present_;
      83             : 
      84             :     // HeaderMatcher
      85           0 :     bool matchesHeaders(const HeaderMap& headers) const override {
      86           0 :       return HeaderUtility::matchHeaders(headers, *this);
      87           0 :     };
      88             :   };
      89             : 
      90             :   using HeaderDataPtr = std::unique_ptr<HeaderData>;
      91             : 
      92             :   /**
      93             :    * Build a vector of HeaderDataPtr given input config.
      94             :    */
      95             :   static std::vector<HeaderUtility::HeaderDataPtr> buildHeaderDataVector(
      96        1664 :       const Protobuf::RepeatedPtrField<envoy::config::route::v3::HeaderMatcher>& header_matchers) {
      97        1664 :     std::vector<HeaderUtility::HeaderDataPtr> ret;
      98        1664 :     for (const auto& header_matcher : header_matchers) {
      99          68 :       ret.emplace_back(std::make_unique<HeaderUtility::HeaderData>(header_matcher));
     100          68 :     }
     101        1664 :     return ret;
     102        1664 :   }
     103             : 
     104             :   /**
     105             :    * Build a vector of HeaderMatcherSharedPtr given input config.
     106             :    */
     107             :   static std::vector<Http::HeaderMatcherSharedPtr> buildHeaderMatcherVector(
     108         176 :       const Protobuf::RepeatedPtrField<envoy::config::route::v3::HeaderMatcher>& header_matchers) {
     109         176 :     std::vector<Http::HeaderMatcherSharedPtr> ret;
     110         176 :     for (const auto& header_matcher : header_matchers) {
     111           4 :       ret.emplace_back(std::make_shared<HeaderUtility::HeaderData>(header_matcher));
     112           4 :     }
     113         176 :     return ret;
     114         176 :   }
     115             : 
     116             :   /**
     117             :    * See if the headers specified in the config are present in a request.
     118             :    * @param request_headers supplies the headers from the request.
     119             :    * @param config_headers supplies the list of configured header conditions on which to match.
     120             :    * @return bool true if all the headers (and values) in the config_headers are found in the
     121             :    *         request_headers. If no config_headers are specified, returns true.
     122             :    */
     123             :   static bool matchHeaders(const HeaderMap& request_headers,
     124             :                            const std::vector<HeaderDataPtr>& config_headers);
     125             : 
     126             :   static bool matchHeaders(const HeaderMap& request_headers, const HeaderData& config_header);
     127             : 
     128             :   /**
     129             :    * Validates that a header value is valid, according to RFC 7230, section 3.2.
     130             :    * http://tools.ietf.org/html/rfc7230#section-3.2
     131             :    * @return bool true if the header values are valid, according to the aforementioned RFC.
     132             :    */
     133             :   static bool headerValueIsValid(const absl::string_view header_value);
     134             : 
     135             :   /**
     136             :    * Validates that a header name is valid, according to RFC 7230, section 3.2.
     137             :    * http://tools.ietf.org/html/rfc7230#section-3.2
     138             :    * @return bool true if the header name is valid, according to the aforementioned RFC.
     139             :    */
     140             :   static bool headerNameIsValid(const absl::string_view header_key);
     141             : 
     142             :   /**
     143             :    * Checks if header name contains underscore characters.
     144             :    * Underscore character is allowed in header names by the RFC-7230 and this check is implemented
     145             :    * as a security measure due to systems that treat '_' and '-' as interchangeable. Envoy by
     146             :    * default allows headers with underscore characters.
     147             :    * @return bool true if header name contains underscore characters.
     148             :    */
     149             :   static bool headerNameContainsUnderscore(const absl::string_view header_name);
     150             : 
     151             :   /**
     152             :    * Validates that the characters in the authority are valid.
     153             :    * @return bool true if the header values are valid, false otherwise.
     154             :    */
     155             :   static bool authorityIsValid(const absl::string_view authority_value);
     156             : 
     157             :   /**
     158             :    * @brief return if the 1xx should be handled by the [encode|decode]1xx calls.
     159             :    */
     160             :   static bool isSpecial1xx(const ResponseHeaderMap& response_headers);
     161             : 
     162             :   /**
     163             :    * @brief a helper function to determine if the headers represent a CONNECT request.
     164             :    */
     165             :   static bool isConnect(const RequestHeaderMap& headers);
     166             : 
     167             :   /**
     168             :    * @brief a helper function to determine if the headers represent a CONNECT-UDP request.
     169             :    */
     170             :   static bool isConnectUdpRequest(const RequestHeaderMap& headers);
     171             : 
     172             :   /**
     173             :    * @brief a helper function to determine if the headers represent a CONNECT-UDP response.
     174             :    */
     175             :   static bool isConnectUdpResponse(const ResponseHeaderMap& headers);
     176             : 
     177             :   /**
     178             :    * @brief a helper function to determine if the headers represent an accepted CONNECT response.
     179             :    */
     180             :   static bool isConnectResponse(const RequestHeaderMap* request_headers,
     181             :                                 const ResponseHeaderMap& response_headers);
     182             : 
     183             :   /**
     184             :    * @brief Rewrites the authority header field by parsing the path using the default CONNECT-UDP
     185             :    * URI template. Returns true if the parsing was successful, otherwise returns false.
     186             :    */
     187             :   static bool rewriteAuthorityForConnectUdp(RequestHeaderMap& headers);
     188             : 
     189             : #ifdef ENVOY_ENABLE_HTTP_DATAGRAMS
     190             :   /**
     191             :    * @brief Returns true if the Capsule-Protocol header field (RFC 9297) is set to true. If the
     192             :    * header field is included multiple times, returns false as per RFC 9297.
     193             :    */
     194             :   static bool isCapsuleProtocol(const RequestOrResponseHeaderMap& headers);
     195             : #endif
     196             : 
     197             :   static bool requestShouldHaveNoBody(const RequestHeaderMap& headers);
     198             : 
     199             :   /**
     200             :    * @brief a helper function to determine if the headers represent an envoy internal request
     201             :    */
     202             :   static bool isEnvoyInternalRequest(const RequestHeaderMap& headers);
     203             : 
     204             :   /**
     205             :    * Determines if request headers pass Envoy validity checks.
     206             :    * @param headers to validate
     207             :    * @return details of the error if an error is present, otherwise absl::nullopt
     208             :    */
     209             :   static absl::optional<std::reference_wrapper<const absl::string_view>>
     210             :   requestHeadersValid(const RequestHeaderMap& headers);
     211             : 
     212             :   /**
     213             :    * Determines if the response should be framed by Connection: Close based on protocol
     214             :    * and headers.
     215             :    * @param protocol the protocol of the request
     216             :    * @param headers the request or response headers
     217             :    * @return if the response should be framed by Connection: Close
     218             :    */
     219             :   static bool shouldCloseConnection(Http::Protocol protocol,
     220             :                                     const RequestOrResponseHeaderMap& headers);
     221             : 
     222             :   /**
     223             :    * @brief Remove the trailing host dot from host/authority header.
     224             :    */
     225             :   static void stripTrailingHostDot(RequestHeaderMap& headers);
     226             : 
     227             :   /**
     228             :    * @return bool true if the provided host has a port, false otherwise.
     229             :    */
     230             :   static bool hostHasPort(absl::string_view host);
     231             : 
     232             :   /**
     233             :    * @brief Remove the port part from host/authority header if it is equal to provided port.
     234             :    * @return absl::optional<uint32_t> containing the port, if removed, else absl::nullopt.
     235             :    * If port is not passed, port part from host/authority header is removed.
     236             :    */
     237             :   static absl::optional<uint32_t> stripPortFromHost(RequestHeaderMap& headers,
     238             :                                                     absl::optional<uint32_t> listener_port);
     239             : 
     240             :   /**
     241             :    * @brief Return the index of the port, or npos if the host has no port
     242             :    *
     243             :    * Note this does not do validity checks on the port, it just finds the
     244             :    * trailing : which is not a part of an IP address.
     245             :    */
     246             :   static absl::string_view::size_type getPortStart(absl::string_view host);
     247             : 
     248             :   /* Does a common header check ensuring required request headers are present.
     249             :    * Required request headers include :method header, :path for non-CONNECT requests, and
     250             :    * host/authority for HTTP/1.1 or CONNECT requests.
     251             :    * @return Status containing the result. If failed, message includes details on which header was
     252             :    * missing.
     253             :    */
     254             :   static Http::Status checkRequiredRequestHeaders(const Http::RequestHeaderMap& headers);
     255             : 
     256             :   /* Does a common header check ensuring required response headers are present.
     257             :    * Current required response headers only includes :status.
     258             :    * @return Status containing the result. If failed, message includes details on which header was
     259             :    * missing.
     260             :    */
     261             :   static Http::Status checkRequiredResponseHeaders(const Http::ResponseHeaderMap& headers);
     262             : 
     263             :   /* Does a common header check ensuring that header keys and values are valid and do not contain
     264             :    * forbidden characters (e.g. valid HTTP header keys/values should never contain embedded NULLs
     265             :    * or new lines.)
     266             :    * @return Status containing the result. If failed, message includes details on which header key
     267             :    * or value was invalid.
     268             :    */
     269             :   static Http::Status checkValidRequestHeaders(const Http::RequestHeaderMap& headers);
     270             : 
     271             :   /**
     272             :    * Returns true if a header may be safely removed without causing additional
     273             :    * problems. Effectively, header names beginning with ":" and the "host" header
     274             :    * may not be removed.
     275             :    */
     276             :   static bool isRemovableHeader(absl::string_view header);
     277             : 
     278             :   /**
     279             :    * Returns true if a header may be safely modified without causing additional
     280             :    * problems. Currently header names beginning with ":" and the "host" header
     281             :    * may not be modified.
     282             :    */
     283             :   static bool isModifiableHeader(absl::string_view header);
     284             : 
     285             :   enum class HeaderValidationResult {
     286             :     ACCEPT = 0,
     287             :     DROP,
     288             :     REJECT,
     289             :   };
     290             : 
     291             :   /**
     292             :    * Check if the given header_name has underscore.
     293             :    * Return HeaderValidationResult and populate the given counters based on
     294             :    * headers_with_underscores_action.
     295             :    */
     296             :   static HeaderValidationResult checkHeaderNameForUnderscores(
     297             :       absl::string_view header_name,
     298             :       envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
     299             :           headers_with_underscores_action,
     300             :       HeaderValidatorStats& stats);
     301             : 
     302             :   /**
     303             :    * Check if header_value represents a valid value for HTTP content-length header.
     304             :    * Return HeaderValidationResult and populate content_length_output if the value is valid,
     305             :    * otherwise populate should_close_connection according to
     306             :    * override_stream_error_on_invalid_http_message.
     307             :    */
     308             :   static HeaderValidationResult
     309             :   validateContentLength(absl::string_view header_value,
     310             :                         bool override_stream_error_on_invalid_http_message,
     311             :                         bool& should_close_connection, size_t& content_length_output);
     312             : 
     313             :   /**
     314             :    * Parse a comma-separated header string to the individual tokens. Discard empty tokens
     315             :    * and whitespace. Return a vector of the comma-separated tokens.
     316             :    */
     317             :   static std::vector<absl::string_view> parseCommaDelimitedHeader(absl::string_view header_value);
     318             : 
     319             :   /**
     320             :    * Return the part of attribute before first ';'-sign. For example,
     321             :    * "foo;bar=1" would return "foo".
     322             :    */
     323             :   static absl::string_view getSemicolonDelimitedAttribute(absl::string_view value);
     324             : 
     325             :   /**
     326             :    * Return a new AcceptEncoding header string vector.
     327             :    */
     328             :   static std::string addEncodingToAcceptEncoding(absl::string_view accept_encoding_header,
     329             :                                                  absl::string_view encoding);
     330             : 
     331             :   /**
     332             :    * Return `true` if the request is a standard HTTP CONNECT.
     333             :    * HTTP/1 RFC: https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.6
     334             :    * HTTP/2 RFC: https://datatracker.ietf.org/doc/html/rfc9113#section-8.5
     335             :    */
     336             :   static bool isStandardConnectRequest(const Http::RequestHeaderMap& headers);
     337             : 
     338             :   /**
     339             :    * Return `true` if the request is an extended HTTP/2 CONNECT.
     340             :    * according to https://datatracker.ietf.org/doc/html/rfc8441#section-4
     341             :    */
     342             :   static bool isExtendedH2ConnectRequest(const Http::RequestHeaderMap& headers);
     343             : 
     344             :   /**
     345             :    * Return true if the given header name is a pseudo header.
     346             :    */
     347             :   static bool isPseudoHeader(absl::string_view header_name);
     348             : };
     349             : 
     350             : } // namespace Http
     351             : } // namespace Envoy

Generated by: LCOV version 1.15