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
|