LCOV - code coverage report
Current view: top level - source/common/local_reply - local_reply.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 30 71 42.3 %
Date: 2024-01-05 06:35:25 Functions: 8 10 80.0 %

          Line data    Source code
       1             : #include "source/common/local_reply/local_reply.h"
       2             : 
       3             : #include <string>
       4             : #include <vector>
       5             : 
       6             : #include "envoy/api/api.h"
       7             : 
       8             : #include "source/common/access_log/access_log_impl.h"
       9             : #include "source/common/common/enum_to_int.h"
      10             : #include "source/common/config/datasource.h"
      11             : #include "source/common/formatter/substitution_format_string.h"
      12             : #include "source/common/formatter/substitution_formatter.h"
      13             : #include "source/common/http/header_map_impl.h"
      14             : #include "source/common/router/header_parser.h"
      15             : 
      16             : namespace Envoy {
      17             : namespace LocalReply {
      18             : 
      19             : class BodyFormatter {
      20             : public:
      21             :   BodyFormatter()
      22             :       : formatter_(std::make_unique<Envoy::Formatter::FormatterImpl>("%LOCAL_REPLY_BODY%", false)),
      23         309 :         content_type_(Http::Headers::get().ContentTypeValues.Text) {}
      24             : 
      25             :   BodyFormatter(const envoy::config::core::v3::SubstitutionFormatString& config,
      26             :                 Server::Configuration::GenericFactoryContext& context)
      27             :       : formatter_(Formatter::SubstitutionFormatStringUtils::fromProtoConfig(config, context)),
      28             :         content_type_(
      29             :             !config.content_type().empty() ? config.content_type()
      30             :             : config.format_case() ==
      31             :                     envoy::config::core::v3::SubstitutionFormatString::FormatCase::kJsonFormat
      32             :                 ? Http::Headers::get().ContentTypeValues.Json
      33           1 :                 : Http::Headers::get().ContentTypeValues.Text) {}
      34             : 
      35             :   void format(const Http::RequestHeaderMap& request_headers,
      36             :               const Http::ResponseHeaderMap& response_headers,
      37             :               const Http::ResponseTrailerMap& response_trailers,
      38             :               const StreamInfo::StreamInfo& stream_info, std::string& body,
      39         383 :               absl::string_view& content_type) const {
      40         383 :     body = formatter_->formatWithContext(
      41         383 :         {&request_headers, &response_headers, &response_trailers, body}, stream_info);
      42         383 :     content_type = content_type_;
      43         383 :   }
      44             : 
      45             : private:
      46             :   const Formatter::FormatterPtr formatter_;
      47             :   const std::string content_type_;
      48             : };
      49             : 
      50             : using BodyFormatterPtr = std::unique_ptr<BodyFormatter>;
      51             : using HeaderParserPtr = std::unique_ptr<Envoy::Router::HeaderParser>;
      52             : 
      53             : class ResponseMapper {
      54             : public:
      55             :   ResponseMapper(
      56             :       const envoy::extensions::filters::network::http_connection_manager::v3::ResponseMapper&
      57             :           config,
      58             :       Server::Configuration::FactoryContext& context)
      59           0 :       : filter_(AccessLog::FilterFactory::fromProto(config.filter(), context)) {
      60           0 :     if (config.has_status_code()) {
      61           0 :       status_code_ = static_cast<Http::Code>(config.status_code().value());
      62           0 :     }
      63           0 :     if (config.has_body()) {
      64           0 :       body_ = Config::DataSource::read(config.body(), true, context.serverFactoryContext().api());
      65           0 :     }
      66             : 
      67           0 :     if (config.has_body_format_override()) {
      68           0 :       body_formatter_ = std::make_unique<BodyFormatter>(config.body_format_override(), context);
      69           0 :     }
      70             : 
      71           0 :     header_parser_ = Envoy::Router::HeaderParser::configure(config.headers_to_add());
      72           0 :   }
      73             : 
      74             :   bool matchAndRewrite(const Http::RequestHeaderMap& request_headers,
      75             :                        Http::ResponseHeaderMap& response_headers,
      76             :                        const Http::ResponseTrailerMap& response_trailers,
      77             :                        StreamInfo::StreamInfo& stream_info, Http::Code& code, std::string& body,
      78           0 :                        BodyFormatter*& final_formatter) const {
      79             :     // If not matched, just bail out.
      80           0 :     if (filter_ == nullptr ||
      81           0 :         !filter_->evaluate({&request_headers, &response_headers, &response_trailers},
      82           0 :                            stream_info)) {
      83           0 :       return false;
      84           0 :     }
      85             : 
      86           0 :     if (body_.has_value()) {
      87           0 :       body = body_.value();
      88           0 :     }
      89             : 
      90           0 :     header_parser_->evaluateHeaders(response_headers, {&request_headers, &response_headers},
      91           0 :                                     stream_info);
      92             : 
      93           0 :     if (status_code_.has_value() && code != status_code_.value()) {
      94           0 :       code = status_code_.value();
      95           0 :       response_headers.setStatus(std::to_string(enumToInt(code)));
      96           0 :       stream_info.setResponseCode(static_cast<uint32_t>(code));
      97           0 :     }
      98             : 
      99           0 :     if (body_formatter_) {
     100           0 :       final_formatter = body_formatter_.get();
     101           0 :     }
     102           0 :     return true;
     103           0 :   }
     104             : 
     105             : private:
     106             :   const AccessLog::FilterPtr filter_;
     107             :   absl::optional<Http::Code> status_code_;
     108             :   absl::optional<std::string> body_;
     109             :   HeaderParserPtr header_parser_;
     110             :   BodyFormatterPtr body_formatter_;
     111             : };
     112             : 
     113             : using ResponseMapperPtr = std::unique_ptr<ResponseMapper>;
     114             : 
     115             : class LocalReplyImpl : public LocalReply {
     116             : public:
     117         170 :   LocalReplyImpl() : body_formatter_(std::make_unique<BodyFormatter>()) {}
     118             : 
     119             :   LocalReplyImpl(
     120             :       const envoy::extensions::filters::network::http_connection_manager::v3::LocalReplyConfig&
     121             :           config,
     122             :       Server::Configuration::FactoryContext& context)
     123             :       : body_formatter_(config.has_body_format()
     124             :                             ? std::make_unique<BodyFormatter>(config.body_format(), context)
     125         140 :                             : std::make_unique<BodyFormatter>()) {
     126         140 :     for (const auto& mapper : config.mappers()) {
     127           0 :       mappers_.emplace_back(std::make_unique<ResponseMapper>(mapper, context));
     128           0 :     }
     129         140 :   }
     130             : 
     131             :   void rewrite(const Http::RequestHeaderMap* request_headers,
     132             :                Http::ResponseHeaderMap& response_headers, StreamInfo::StreamInfo& stream_info,
     133             :                Http::Code& code, std::string& body,
     134         383 :                absl::string_view& content_type) const override {
     135             :     // Set response code to stream_info and response_headers due to:
     136             :     // 1) StatusCode filter is using response_code from stream_info,
     137             :     // 2) %RESP(:status)% is from Status() in response_headers.
     138         383 :     response_headers.setStatus(std::to_string(enumToInt(code)));
     139         383 :     stream_info.setResponseCode(static_cast<uint32_t>(code));
     140             : 
     141         383 :     if (request_headers == nullptr) {
     142          81 :       request_headers = Http::StaticEmptyHeaders::get().request_headers.get();
     143          81 :     }
     144             : 
     145         383 :     BodyFormatter* final_formatter{};
     146         383 :     for (const auto& mapper : mappers_) {
     147           0 :       if (mapper->matchAndRewrite(*request_headers, response_headers,
     148           0 :                                   *Http::StaticEmptyHeaders::get().response_trailers, stream_info,
     149           0 :                                   code, body, final_formatter)) {
     150           0 :         break;
     151           0 :       }
     152           0 :     }
     153             : 
     154         383 :     if (!final_formatter) {
     155         383 :       final_formatter = body_formatter_.get();
     156         383 :     }
     157         383 :     return final_formatter->format(*request_headers, response_headers,
     158         383 :                                    *Http::StaticEmptyHeaders::get().response_trailers, stream_info,
     159         383 :                                    body, content_type);
     160         383 :   }
     161             : 
     162             : private:
     163             :   std::list<ResponseMapperPtr> mappers_;
     164             :   const BodyFormatterPtr body_formatter_;
     165             : };
     166             : 
     167         170 : LocalReplyPtr Factory::createDefault() { return std::make_unique<LocalReplyImpl>(); }
     168             : 
     169             : LocalReplyPtr Factory::create(
     170             :     const envoy::extensions::filters::network::http_connection_manager::v3::LocalReplyConfig&
     171             :         config,
     172         140 :     Server::Configuration::FactoryContext& context) {
     173         140 :   return std::make_unique<LocalReplyImpl>(config, context);
     174         140 : }
     175             : 
     176             : } // namespace LocalReply
     177             : } // namespace Envoy

Generated by: LCOV version 1.15