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
23109
  BodyFormatter() : content_type_(Http::Headers::get().ContentTypeValues.Text) {}
22

            
23
  static absl::StatusOr<std::unique_ptr<BodyFormatter>>
24
  create(const envoy::config::core::v3::SubstitutionFormatString& config,
25
118
         Server::Configuration::GenericFactoryContext& context) {
26
118
    auto formatter_or_error =
27
118
        Formatter::SubstitutionFormatStringUtils::fromProtoConfig(config, context);
28
118
    RETURN_IF_NOT_OK_REF(formatter_or_error.status());
29
118
    return std::make_unique<BodyFormatter>(config, std::move(*formatter_or_error));
30
118
  }
31

            
32
  BodyFormatter(const envoy::config::core::v3::SubstitutionFormatString& config,
33
                Formatter::FormatterPtr&& formatter)
34
118
      : formatter_(std::move(formatter)),
35
118
        content_type_(
36
118
            !config.content_type().empty() ? config.content_type()
37
118
            : config.format_case() ==
38
116
                    envoy::config::core::v3::SubstitutionFormatString::FormatCase::kJsonFormat
39
116
                ? Http::Headers::get().ContentTypeValues.Json
40
118
                : Http::Headers::get().ContentTypeValues.Text) {}
41

            
42
  void format(const Http::RequestHeaderMap& request_headers,
43
              const Http::ResponseHeaderMap& response_headers,
44
              const Http::ResponseTrailerMap& response_trailers,
45
              const StreamInfo::StreamInfo& stream_info, std::string& body,
46
10226
              absl::string_view& content_type) const {
47
    // No specific formatter is provided and the default formatter %LOCAL_REPLY_BODY% will
48
    // be used. That means the body will be the same as the original body and we don't need
49
    // to format it.
50
10226
    if (formatter_ != nullptr) {
51
107
      body = formatter_->format({&request_headers, &response_headers, &response_trailers, body},
52
107
                                stream_info);
53
107
    }
54
10226
    content_type = content_type_;
55
10226
  }
56

            
57
private:
58
  const Formatter::FormatterPtr formatter_;
59
  const std::string content_type_;
60
};
61

            
62
using BodyFormatterPtr = std::unique_ptr<BodyFormatter>;
63
using HeaderParserPtr = std::unique_ptr<Envoy::Router::HeaderParser>;
64

            
65
class ResponseMapper {
66
public:
67
  static absl::StatusOr<std::unique_ptr<ResponseMapper>>
68
  create(const envoy::extensions::filters::network::http_connection_manager::v3::ResponseMapper&
69
             config,
70
155
         Server::Configuration::FactoryContext& context) {
71
155
    absl::Status creation_status = absl::OkStatus();
72
155
    auto ret = std::make_unique<ResponseMapper>(config, context, creation_status);
73
155
    RETURN_IF_NOT_OK(creation_status);
74
155
    return ret;
75
155
  }
76

            
77
  ResponseMapper(
78
      const envoy::extensions::filters::network::http_connection_manager::v3::ResponseMapper&
79
          config,
80
      Server::Configuration::FactoryContext& context, absl::Status& creation_status)
81
155
      : filter_(AccessLog::FilterFactory::fromProto(config.filter(), context)) {
82
155
    if (config.has_status_code()) {
83
150
      status_code_ = static_cast<Http::Code>(config.status_code().value());
84
150
    }
85
155
    if (config.has_body()) {
86
41
      auto body_or_error =
87
41
          Config::DataSource::read(config.body(), true, context.serverFactoryContext().api());
88
41
      SET_AND_RETURN_IF_NOT_OK(body_or_error.status(), creation_status);
89
41
      body_ = *body_or_error;
90
41
    }
91

            
92
155
    if (config.has_body_format_override()) {
93
15
      auto formatter_or_error = BodyFormatter::create(config.body_format_override(), context);
94
15
      SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
95
15
      body_formatter_ = std::move(*formatter_or_error);
96
15
    }
97

            
98
155
    auto parser_or_error = Envoy::Router::HeaderParser::configure(config.headers_to_add());
99
155
    SET_AND_RETURN_IF_NOT_OK(parser_or_error.status(), creation_status);
100
155
    header_parser_ = std::move(*parser_or_error);
101
155
  }
102

            
103
  bool matchAndRewrite(const Http::RequestHeaderMap& request_headers,
104
                       Http::ResponseHeaderMap& response_headers,
105
                       const Http::ResponseTrailerMap& response_trailers,
106
                       StreamInfo::StreamInfo& stream_info, Http::Code& code, std::string& body,
107
147
                       BodyFormatter*& final_formatter) const {
108
    // If not matched, just bail out.
109
147
    if (filter_ == nullptr ||
110
147
        !filter_->evaluate({&request_headers, &response_headers, &response_trailers},
111
147
                           stream_info)) {
112
69
      return false;
113
69
    }
114

            
115
78
    if (body_.has_value()) {
116
41
      body = body_.value();
117
41
    }
118

            
119
78
    header_parser_->evaluateHeaders(response_headers, {&request_headers, &response_headers},
120
78
                                    stream_info);
121

            
122
78
    if (status_code_.has_value() && code != status_code_.value()) {
123
73
      code = status_code_.value();
124
73
      response_headers.setStatus(std::to_string(enumToInt(code)));
125
73
      stream_info.setResponseCode(static_cast<uint32_t>(code));
126
73
    }
127

            
128
78
    if (body_formatter_) {
129
15
      final_formatter = body_formatter_.get();
130
15
    }
131
78
    return true;
132
147
  }
133

            
134
private:
135
  const AccessLog::FilterPtr filter_;
136
  absl::optional<Http::Code> status_code_;
137
  absl::optional<std::string> body_;
138
  HeaderParserPtr header_parser_;
139
  BodyFormatterPtr body_formatter_;
140
};
141

            
142
using ResponseMapperPtr = std::unique_ptr<ResponseMapper>;
143

            
144
class LocalReplyImpl : public LocalReply {
145
public:
146
13233
  LocalReplyImpl() : body_formatter_(std::make_unique<BodyFormatter>()) {}
147

            
148
  static absl::StatusOr<std::unique_ptr<LocalReplyImpl>>
149
  create(const envoy::extensions::filters::network::http_connection_manager::v3::LocalReplyConfig&
150
             config,
151
9979
         Server::Configuration::FactoryContext& context) {
152
9979
    absl::Status creation_status = absl::OkStatus();
153
9979
    auto ret = std::make_unique<LocalReplyImpl>(config, context, creation_status);
154
9979
    RETURN_IF_NOT_OK(creation_status);
155
9979
    return ret;
156
9979
  }
157

            
158
  LocalReplyImpl(
159
      const envoy::extensions::filters::network::http_connection_manager::v3::LocalReplyConfig&
160
          config,
161
9979
      Server::Configuration::FactoryContext& context, absl::Status& creation_status) {
162
9979
    if (!config.has_body_format()) {
163
9876
      body_formatter_ = std::make_unique<BodyFormatter>();
164
9953
    } else {
165
103
      auto formatter_or_error = BodyFormatter::create(config.body_format(), context);
166
103
      SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
167
103
      body_formatter_ = std::move(*formatter_or_error);
168
103
    }
169

            
170
9979
    mappers_.reserve(config.mappers().size());
171
10027
    for (const auto& mapper : config.mappers()) {
172
155
      auto mapper_or_error = ResponseMapper::create(mapper, context);
173
155
      SET_AND_RETURN_IF_NOT_OK(mapper_or_error.status(), creation_status);
174
155
      mappers_.emplace_back(std::move(*mapper_or_error));
175
155
    }
176
9979
  }
177

            
178
  void rewrite(const Http::RequestHeaderMap* request_headers,
179
               Http::ResponseHeaderMap& response_headers, StreamInfo::StreamInfo& stream_info,
180
               Http::Code& code, std::string& body,
181
10226
               absl::string_view& content_type) const override {
182
    // Set response code to stream_info and response_headers due to:
183
    // 1) StatusCode filter is using response_code from stream_info,
184
    // 2) %RESP(:status)% is from Status() in response_headers.
185
10226
    response_headers.setStatus(std::to_string(enumToInt(code)));
186
10226
    stream_info.setResponseCode(static_cast<uint32_t>(code));
187

            
188
10226
    if (request_headers == nullptr) {
189
1352
      request_headers = Http::StaticEmptyHeaders::get().request_headers.get();
190
1352
    }
191

            
192
10226
    BodyFormatter* final_formatter{};
193
10258
    for (const auto& mapper : mappers_) {
194
147
      if (mapper->matchAndRewrite(*request_headers, response_headers,
195
147
                                  *Http::StaticEmptyHeaders::get().response_trailers, stream_info,
196
147
                                  code, body, final_formatter)) {
197
78
        break;
198
78
      }
199
147
    }
200

            
201
10226
    if (!final_formatter) {
202
10211
      final_formatter = body_formatter_.get();
203
10211
    }
204
10226
    return final_formatter->format(*request_headers, response_headers,
205
10226
                                   *Http::StaticEmptyHeaders::get().response_trailers, stream_info,
206
10226
                                   body, content_type);
207
10226
  }
208

            
209
private:
210
  std::vector<ResponseMapperPtr> mappers_;
211
  BodyFormatterPtr body_formatter_;
212
};
213

            
214
13233
LocalReplyPtr Factory::createDefault() { return std::make_unique<LocalReplyImpl>(); }
215

            
216
absl::StatusOr<LocalReplyPtr> Factory::create(
217
    const envoy::extensions::filters::network::http_connection_manager::v3::LocalReplyConfig&
218
        config,
219
9979
    Server::Configuration::FactoryContext& context) {
220
9979
  return LocalReplyImpl::create(config, context);
221
9979
}
222

            
223
} // namespace LocalReply
224
} // namespace Envoy