LCOV - code coverage report
Current view: top level - source/common/formatter - http_specific_formatter.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 127 272 46.7 %
Date: 2024-01-05 06:35:25 Functions: 15 37 40.5 %

          Line data    Source code
       1             : #include "source/common/formatter/http_specific_formatter.h"
       2             : 
       3             : #include "source/common/common/assert.h"
       4             : #include "source/common/common/empty_string.h"
       5             : #include "source/common/common/fmt.h"
       6             : #include "source/common/common/thread.h"
       7             : #include "source/common/common/utility.h"
       8             : #include "source/common/config/metadata.h"
       9             : #include "source/common/formatter/substitution_formatter.h"
      10             : #include "source/common/grpc/common.h"
      11             : #include "source/common/grpc/status.h"
      12             : #include "source/common/http/header_map_impl.h"
      13             : #include "source/common/http/utility.h"
      14             : #include "source/common/protobuf/message_validator_impl.h"
      15             : #include "source/common/protobuf/utility.h"
      16             : #include "source/common/runtime/runtime_features.h"
      17             : #include "source/common/stream_info/utility.h"
      18             : 
      19             : namespace Envoy {
      20             : namespace Formatter {
      21             : 
      22             : HttpFormatterContext::HttpFormatterContext(const Http::RequestHeaderMap* request_headers,
      23             :                                            const Http::ResponseHeaderMap* response_headers,
      24             :                                            const Http::ResponseTrailerMap* response_trailers,
      25             :                                            absl::string_view local_reply_body,
      26             :                                            AccessLog::AccessLogType log_type)
      27             :     : request_headers_(request_headers), response_headers_(response_headers),
      28             :       response_trailers_(response_trailers), local_reply_body_(local_reply_body),
      29        3113 :       log_type_(log_type) {}
      30             : 
      31        4110 : const Http::RequestHeaderMap& HttpFormatterContext::requestHeaders() const {
      32        4110 :   return request_headers_ != nullptr ? *request_headers_
      33        4110 :                                      : *Http::StaticEmptyHeaders::get().request_headers;
      34        4110 : }
      35         612 : const Http::ResponseHeaderMap& HttpFormatterContext::responseHeaders() const {
      36         612 :   return response_headers_ != nullptr ? *response_headers_
      37         612 :                                       : *Http::StaticEmptyHeaders::get().response_headers;
      38         612 : }
      39           0 : const Http::ResponseTrailerMap& HttpFormatterContext::responseTrailers() const {
      40           0 :   return response_trailers_ != nullptr ? *response_trailers_
      41           0 :                                        : *Http::StaticEmptyHeaders::get().response_trailers;
      42           0 : }
      43             : 
      44         382 : absl::string_view HttpFormatterContext::localReplyBody() const { return local_reply_body_; }
      45           0 : AccessLog::AccessLogType HttpFormatterContext::accessLogType() const { return log_type_; }
      46             : 
      47             : absl::optional<std::string>
      48             : LocalReplyBodyFormatter::formatWithContext(const HttpFormatterContext& context,
      49         382 :                                            const StreamInfo::StreamInfo&) const {
      50         382 :   return std::string(context.localReplyBody());
      51         382 : }
      52             : 
      53             : ProtobufWkt::Value
      54             : LocalReplyBodyFormatter::formatValueWithContext(const HttpFormatterContext& context,
      55           0 :                                                 const StreamInfo::StreamInfo&) const {
      56           0 :   return ValueUtil::stringValue(std::string(context.localReplyBody()));
      57           0 : }
      58             : 
      59             : absl::optional<std::string>
      60             : AccessLogTypeFormatter::formatWithContext(const HttpFormatterContext& context,
      61           0 :                                           const StreamInfo::StreamInfo&) const {
      62           0 :   return AccessLogType_Name(context.accessLogType());
      63           0 : }
      64             : 
      65             : ProtobufWkt::Value
      66             : AccessLogTypeFormatter::formatValueWithContext(const HttpFormatterContext& context,
      67           0 :                                                const StreamInfo::StreamInfo&) const {
      68           0 :   return ValueUtil::stringValue(AccessLogType_Name(context.accessLogType()));
      69           0 : }
      70             : 
      71             : HeaderFormatter::HeaderFormatter(const std::string& main_header,
      72             :                                  const std::string& alternative_header,
      73             :                                  absl::optional<size_t> max_length)
      74        1635 :     : main_header_(main_header), alternative_header_(alternative_header), max_length_(max_length) {}
      75             : 
      76        4722 : const Http::HeaderEntry* HeaderFormatter::findHeader(const Http::HeaderMap& headers) const {
      77        4722 :   const auto header = headers.get(main_header_);
      78             : 
      79        4722 :   if (header.empty() && !alternative_header_.get().empty()) {
      80         715 :     const auto alternate_header = headers.get(alternative_header_);
      81             :     // TODO(https://github.com/envoyproxy/envoy/issues/13454): Potentially log all header values.
      82         715 :     return alternate_header.empty() ? nullptr : alternate_header[0];
      83         715 :   }
      84             : 
      85        4007 :   return header.empty() ? nullptr : header[0];
      86        4722 : }
      87             : 
      88        4722 : absl::optional<std::string> HeaderFormatter::format(const Http::HeaderMap& headers) const {
      89        4722 :   const Http::HeaderEntry* header = findHeader(headers);
      90        4722 :   if (!header) {
      91        2514 :     return absl::nullopt;
      92        2514 :   }
      93             : 
      94        2208 :   std::string val = std::string(header->value().getStringView());
      95        2208 :   SubstitutionFormatUtils::truncate(val, max_length_);
      96        2208 :   return val;
      97        4722 : }
      98             : 
      99           0 : ProtobufWkt::Value HeaderFormatter::formatValue(const Http::HeaderMap& headers) const {
     100           0 :   const Http::HeaderEntry* header = findHeader(headers);
     101           0 :   if (!header) {
     102           0 :     return SubstitutionFormatUtils::unspecifiedValue();
     103           0 :   }
     104             : 
     105           0 :   std::string val = std::string(header->value().getStringView());
     106           0 :   SubstitutionFormatUtils::truncate(val, max_length_);
     107           0 :   return ValueUtil::stringValue(val);
     108           0 : }
     109             : 
     110             : ResponseHeaderFormatter::ResponseHeaderFormatter(const std::string& main_header,
     111             :                                                  const std::string& alternative_header,
     112             :                                                  absl::optional<size_t> max_length)
     113         171 :     : HeaderFormatter(main_header, alternative_header, max_length) {}
     114             : 
     115             : absl::optional<std::string>
     116             : ResponseHeaderFormatter::formatWithContext(const HttpFormatterContext& context,
     117         612 :                                            const StreamInfo::StreamInfo&) const {
     118         612 :   return HeaderFormatter::format(context.responseHeaders());
     119         612 : }
     120             : 
     121             : ProtobufWkt::Value
     122             : ResponseHeaderFormatter::formatValueWithContext(const HttpFormatterContext& context,
     123           0 :                                                 const StreamInfo::StreamInfo&) const {
     124           0 :   return HeaderFormatter::formatValue(context.responseHeaders());
     125           0 : }
     126             : 
     127             : RequestHeaderFormatter::RequestHeaderFormatter(const std::string& main_header,
     128             :                                                const std::string& alternative_header,
     129             :                                                absl::optional<size_t> max_length)
     130        1464 :     : HeaderFormatter(main_header, alternative_header, max_length) {}
     131             : 
     132             : absl::optional<std::string>
     133             : RequestHeaderFormatter::formatWithContext(const HttpFormatterContext& context,
     134        4110 :                                           const StreamInfo::StreamInfo&) const {
     135        4110 :   return HeaderFormatter::format(context.requestHeaders());
     136        4110 : }
     137             : 
     138             : ProtobufWkt::Value
     139             : RequestHeaderFormatter::formatValueWithContext(const HttpFormatterContext& context,
     140           0 :                                                const StreamInfo::StreamInfo&) const {
     141           0 :   return HeaderFormatter::formatValue(context.requestHeaders());
     142           0 : }
     143             : 
     144             : ResponseTrailerFormatter::ResponseTrailerFormatter(const std::string& main_header,
     145             :                                                    const std::string& alternative_header,
     146             :                                                    absl::optional<size_t> max_length)
     147           0 :     : HeaderFormatter(main_header, alternative_header, max_length) {}
     148             : 
     149             : absl::optional<std::string>
     150             : ResponseTrailerFormatter::formatWithContext(const HttpFormatterContext& context,
     151           0 :                                             const StreamInfo::StreamInfo&) const {
     152           0 :   return HeaderFormatter::format(context.responseTrailers());
     153           0 : }
     154             : 
     155             : ProtobufWkt::Value
     156             : ResponseTrailerFormatter::formatValueWithContext(const HttpFormatterContext& context,
     157           0 :                                                  const StreamInfo::StreamInfo&) const {
     158           0 :   return HeaderFormatter::formatValue(context.responseTrailers());
     159           0 : }
     160             : 
     161             : HeadersByteSizeFormatter::HeadersByteSizeFormatter(const HeaderType header_type)
     162           0 :     : header_type_(header_type) {}
     163             : 
     164             : uint64_t HeadersByteSizeFormatter::extractHeadersByteSize(
     165             :     const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers,
     166           0 :     const Http::ResponseTrailerMap& response_trailers) const {
     167           0 :   switch (header_type_) {
     168           0 :   case HeaderType::RequestHeaders:
     169           0 :     return request_headers.byteSize();
     170           0 :   case HeaderType::ResponseHeaders:
     171           0 :     return response_headers.byteSize();
     172           0 :   case HeaderType::ResponseTrailers:
     173           0 :     return response_trailers.byteSize();
     174           0 :   }
     175           0 :   PANIC_DUE_TO_CORRUPT_ENUM;
     176           0 : }
     177             : 
     178             : absl::optional<std::string>
     179             : HeadersByteSizeFormatter::formatWithContext(const HttpFormatterContext& context,
     180           0 :                                             const StreamInfo::StreamInfo&) const {
     181           0 :   return absl::StrCat(extractHeadersByteSize(context.requestHeaders(), context.responseHeaders(),
     182           0 :                                              context.responseTrailers()));
     183           0 : }
     184             : 
     185             : ProtobufWkt::Value
     186             : HeadersByteSizeFormatter::formatValueWithContext(const HttpFormatterContext& context,
     187           0 :                                                  const StreamInfo::StreamInfo&) const {
     188           0 :   return ValueUtil::numberValue(extractHeadersByteSize(
     189           0 :       context.requestHeaders(), context.responseHeaders(), context.responseTrailers()));
     190           0 : }
     191             : 
     192           0 : GrpcStatusFormatter::Format GrpcStatusFormatter::parseFormat(absl::string_view format) {
     193           0 :   if (format.empty() || format == "CAMEL_STRING") {
     194           0 :     return GrpcStatusFormatter::CamelString;
     195           0 :   }
     196             : 
     197           0 :   if (format == "SNAKE_STRING") {
     198           0 :     return GrpcStatusFormatter::SnakeString;
     199           0 :   }
     200           0 :   if (format == "NUMBER") {
     201           0 :     return GrpcStatusFormatter::Number;
     202           0 :   }
     203             : 
     204           0 :   throwEnvoyExceptionOrPanic(
     205           0 :       "GrpcStatusFormatter only supports CAMEL_STRING, SNAKE_STRING or NUMBER.");
     206           0 : }
     207             : 
     208             : GrpcStatusFormatter::GrpcStatusFormatter(const std::string& main_header,
     209             :                                          const std::string& alternative_header,
     210             :                                          absl::optional<size_t> max_length, Format format)
     211           0 :     : HeaderFormatter(main_header, alternative_header, max_length), format_(format) {}
     212             : 
     213             : absl::optional<std::string>
     214             : GrpcStatusFormatter::formatWithContext(const HttpFormatterContext& context,
     215           0 :                                        const StreamInfo::StreamInfo& info) const {
     216           0 :   if (Runtime::runtimeFeatureEnabled(
     217           0 :           "envoy.reloadable_features.validate_grpc_header_before_log_grpc_status")) {
     218           0 :     if (!Grpc::Common::isGrpcRequestHeaders(context.requestHeaders())) {
     219           0 :       return absl::nullopt;
     220           0 :     }
     221           0 :   }
     222           0 :   const auto grpc_status = Grpc::Common::getGrpcStatus(context.responseTrailers(),
     223           0 :                                                        context.responseHeaders(), info, true);
     224           0 :   if (!grpc_status.has_value()) {
     225           0 :     return absl::nullopt;
     226           0 :   }
     227           0 :   switch (format_) {
     228           0 :   case CamelString: {
     229           0 :     const auto grpc_status_message = Grpc::Utility::grpcStatusToString(grpc_status.value());
     230           0 :     if (grpc_status_message == EMPTY_STRING || grpc_status_message == "InvalidCode") {
     231           0 :       return std::to_string(grpc_status.value());
     232           0 :     }
     233           0 :     return grpc_status_message;
     234           0 :   }
     235           0 :   case SnakeString: {
     236           0 :     const auto grpc_status_message =
     237           0 :         absl::StatusCodeToString(static_cast<absl::StatusCode>(grpc_status.value()));
     238           0 :     if (grpc_status_message == EMPTY_STRING) {
     239           0 :       return std::to_string(grpc_status.value());
     240           0 :     }
     241           0 :     return grpc_status_message;
     242           0 :   }
     243           0 :   case Number: {
     244           0 :     return std::to_string(grpc_status.value());
     245           0 :   }
     246           0 :   }
     247           0 :   PANIC_DUE_TO_CORRUPT_ENUM;
     248           0 : }
     249             : 
     250             : ProtobufWkt::Value
     251             : GrpcStatusFormatter::formatValueWithContext(const HttpFormatterContext& context,
     252           0 :                                             const StreamInfo::StreamInfo& info) const {
     253           0 :   if (Runtime::runtimeFeatureEnabled(
     254           0 :           "envoy.reloadable_features.validate_grpc_header_before_log_grpc_status")) {
     255           0 :     if (!Grpc::Common::isGrpcRequestHeaders(context.requestHeaders())) {
     256           0 :       return SubstitutionFormatUtils::unspecifiedValue();
     257           0 :     }
     258           0 :   }
     259           0 :   const auto grpc_status = Grpc::Common::getGrpcStatus(context.responseTrailers(),
     260           0 :                                                        context.responseHeaders(), info, true);
     261           0 :   if (!grpc_status.has_value()) {
     262           0 :     return SubstitutionFormatUtils::unspecifiedValue();
     263           0 :   }
     264             : 
     265           0 :   switch (format_) {
     266           0 :   case CamelString: {
     267           0 :     const auto grpc_status_message = Grpc::Utility::grpcStatusToString(grpc_status.value());
     268           0 :     if (grpc_status_message == EMPTY_STRING || grpc_status_message == "InvalidCode") {
     269           0 :       return ValueUtil::stringValue(std::to_string(grpc_status.value()));
     270           0 :     }
     271           0 :     return ValueUtil::stringValue(grpc_status_message);
     272           0 :   }
     273           0 :   case SnakeString: {
     274           0 :     const auto grpc_status_message =
     275           0 :         absl::StatusCodeToString(static_cast<absl::StatusCode>(grpc_status.value()));
     276           0 :     if (grpc_status_message == EMPTY_STRING) {
     277           0 :       return ValueUtil::stringValue(std::to_string(grpc_status.value()));
     278           0 :     }
     279           0 :     return ValueUtil::stringValue(grpc_status_message);
     280           0 :   }
     281           0 :   case Number: {
     282           0 :     return ValueUtil::numberValue(grpc_status.value());
     283           0 :   }
     284           0 :   }
     285           0 :   PANIC_DUE_TO_CORRUPT_ENUM;
     286           0 : }
     287             : 
     288             : StreamInfoRequestHeaderFormatter::StreamInfoRequestHeaderFormatter(
     289             :     const std::string& main_header, const std::string& alternative_header,
     290             :     absl::optional<size_t> max_length)
     291           0 :     : HeaderFormatter(main_header, alternative_header, max_length) {}
     292             : 
     293             : absl::optional<std::string> StreamInfoRequestHeaderFormatter::formatWithContext(
     294           0 :     const HttpFormatterContext&, const StreamInfo::StreamInfo& stream_info) const {
     295           0 :   return HeaderFormatter::format(*stream_info.getRequestHeaders());
     296           0 : }
     297             : 
     298             : ProtobufWkt::Value StreamInfoRequestHeaderFormatter::formatValueWithContext(
     299           0 :     const HttpFormatterContext&, const StreamInfo::StreamInfo& stream_info) const {
     300           0 :   return HeaderFormatter::formatValue(*stream_info.getRequestHeaders());
     301           0 : }
     302             : 
     303             : const HttpBuiltInCommandParser::FormatterProviderLookupTbl&
     304        3998 : HttpBuiltInCommandParser::getKnownFormatters() {
     305        3998 :   CONSTRUCT_ON_FIRST_USE(
     306        3998 :       FormatterProviderLookupTbl,
     307        3998 :       {{"REQ",
     308        3998 :         {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
     309        3998 :          [](const std::string& format, absl::optional<size_t>& max_length) {
     310        3998 :            std::string main_header, alternative_header;
     311             : 
     312        3998 :            SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
     313             : 
     314        3998 :            return std::make_unique<RequestHeaderFormatter>(main_header, alternative_header,
     315        3998 :                                                            max_length);
     316        3998 :          }}},
     317        3998 :        {"RESP",
     318        3998 :         {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
     319        3998 :          [](const std::string& format, absl::optional<size_t>& max_length) {
     320        3998 :            std::string main_header, alternative_header;
     321             : 
     322        3998 :            SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
     323             : 
     324        3998 :            return std::make_unique<ResponseHeaderFormatter>(main_header, alternative_header,
     325        3998 :                                                             max_length);
     326        3998 :          }}},
     327        3998 :        {"TRAILER",
     328        3998 :         {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
     329        3998 :          [](const std::string& format, absl::optional<size_t>& max_length) {
     330        3998 :            std::string main_header, alternative_header;
     331             : 
     332        3998 :            SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
     333             : 
     334        3998 :            return std::make_unique<ResponseTrailerFormatter>(main_header, alternative_header,
     335        3998 :                                                              max_length);
     336        3998 :          }}},
     337        3998 :        {"LOCAL_REPLY_BODY",
     338        3998 :         {CommandSyntaxChecker::COMMAND_ONLY,
     339        3998 :          [](const std::string&, absl::optional<size_t>&) {
     340        3998 :            return std::make_unique<LocalReplyBodyFormatter>();
     341        3998 :          }}},
     342        3998 :        {"ACCESS_LOG_TYPE",
     343        3998 :         {CommandSyntaxChecker::COMMAND_ONLY,
     344        3998 :          [](const std::string&, absl::optional<size_t>&) {
     345        3998 :            return std::make_unique<AccessLogTypeFormatter>();
     346        3998 :          }}},
     347        3998 :        {"GRPC_STATUS",
     348        3998 :         {CommandSyntaxChecker::PARAMS_OPTIONAL,
     349        3998 :          [](const std::string& format, const absl::optional<size_t>&) {
     350        3998 :            return std::make_unique<GrpcStatusFormatter>("grpc-status", "", absl::optional<size_t>(),
     351        3998 :                                                         GrpcStatusFormatter::parseFormat(format));
     352        3998 :          }}},
     353        3998 :        {"GRPC_STATUS_NUMBER",
     354        3998 :         {CommandSyntaxChecker::COMMAND_ONLY,
     355        3998 :          [](const std::string&, const absl::optional<size_t>&) {
     356        3998 :            return std::make_unique<GrpcStatusFormatter>("grpc-status", "", absl::optional<size_t>(),
     357        3998 :                                                         GrpcStatusFormatter::Number);
     358        3998 :          }}},
     359        3998 :        {"REQUEST_HEADERS_BYTES",
     360        3998 :         {CommandSyntaxChecker::COMMAND_ONLY,
     361        3998 :          [](const std::string&, absl::optional<size_t>&) {
     362        3998 :            return std::make_unique<HeadersByteSizeFormatter>(
     363        3998 :                HeadersByteSizeFormatter::HeaderType::RequestHeaders);
     364        3998 :          }}},
     365        3998 :        {"RESPONSE_HEADERS_BYTES",
     366        3998 :         {CommandSyntaxChecker::COMMAND_ONLY,
     367        3998 :          [](const std::string&, absl::optional<size_t>&) {
     368        3998 :            return std::make_unique<HeadersByteSizeFormatter>(
     369        3998 :                HeadersByteSizeFormatter::HeaderType::ResponseHeaders);
     370        3998 :          }}},
     371        3998 :        {"RESPONSE_TRAILERS_BYTES",
     372        3998 :         {CommandSyntaxChecker::COMMAND_ONLY,
     373        3998 :          [](const std::string&, absl::optional<size_t>&) {
     374        3998 :            return std::make_unique<HeadersByteSizeFormatter>(
     375        3998 :                HeadersByteSizeFormatter::HeaderType::ResponseTrailers);
     376        3998 :          }}},
     377        3998 :        {"STREAM_INFO_REQ",
     378        3998 :         {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
     379        3998 :          [](const std::string& format, absl::optional<size_t>& max_length) {
     380        3998 :            std::string main_header, alternative_header;
     381        3998 :            SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
     382             : 
     383        3998 :            return std::make_unique<RequestHeaderFormatter>(main_header, alternative_header,
     384        3998 :                                                            max_length);
     385        3998 :          }}}});
     386        3998 : }
     387             : 
     388             : FormatterProviderPtr HttpBuiltInCommandParser::parse(const std::string& command,
     389             :                                                      const std::string& subcommand,
     390        3998 :                                                      absl::optional<size_t>& max_length) const {
     391        3998 :   const FormatterProviderLookupTbl& providers = getKnownFormatters();
     392             : 
     393        3998 :   auto it = providers.find(command);
     394             : 
     395        3998 :   if (it == providers.end()) {
     396        2050 :     return nullptr;
     397        2050 :   }
     398             : 
     399             :   // Check flags for the command.
     400        1948 :   CommandSyntaxChecker::verifySyntax((*it).second.first, command, subcommand, max_length);
     401             : 
     402             :   // Create a pointer to the formatter by calling a function
     403             :   // associated with formatter's name.
     404        1948 :   return (*it).second.second(subcommand, max_length);
     405        3998 : }
     406             : 
     407             : REGISTER_BUILT_IN_COMMAND_PARSER(HttpFormatterContext, HttpBuiltInCommandParser);
     408             : 
     409             : static const std::string DEFAULT_FORMAT =
     410             :     "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" "
     411             :     "%RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "
     412             :     "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "
     413             :     "\"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" "
     414             :     "\"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n";
     415             : 
     416         171 : FormatterPtr HttpSubstitutionFormatUtils::defaultSubstitutionFormatter() {
     417         171 :   return std::make_unique<Envoy::Formatter::FormatterImpl>(DEFAULT_FORMAT, false);
     418         171 : }
     419             : 
     420             : } // namespace Formatter
     421             : } // namespace Envoy

Generated by: LCOV version 1.15