1
#include "source/extensions/http/custom_response/local_response_policy/local_response_policy.h"
2

            
3
#include "envoy/stream_info/filter_state.h"
4

            
5
#include "source/common/common/enum_to_int.h"
6
#include "source/common/config/datasource.h"
7
#include "source/common/formatter/substitution_format_string.h"
8
#include "source/common/formatter/substitution_formatter.h"
9
#include "source/common/http/header_map_impl.h"
10
#include "source/common/http/message_impl.h"
11
#include "source/common/http/utility.h"
12
#include "source/common/router/header_parser.h"
13
#include "source/extensions/filters/http/custom_response/custom_response_filter.h"
14
#include "source/server/generic_factory_context.h"
15

            
16
namespace Envoy {
17
namespace Extensions {
18
namespace Http {
19
namespace CustomResponse {
20
LocalResponsePolicy::LocalResponsePolicy(
21
    const envoy::extensions::http::custom_response::local_response_policy::v3::LocalResponsePolicy&
22
        config,
23
    Server::Configuration::ServerFactoryContext& context)
24
149
    : local_body_{config.has_body()
25
149
                      ? absl::optional<std::string>(THROW_OR_RETURN_VALUE(
26
149
                            Config::DataSource::read(config.body(), true, context.api()),
27
149
                            std::string))
28
149
                      : absl::optional<std::string>{}},
29
149
      status_code_{config.has_status_code()
30
149
                       ? absl::optional<Envoy::Http::Code>(
31
148
                             static_cast<Envoy::Http::Code>(config.status_code().value()))
32
149
                       : absl::optional<Envoy::Http::Code>{}},
33
149
      header_parser_(THROW_OR_RETURN_VALUE(
34
          Envoy::Router::HeaderParser::configure(config.response_headers_to_add()),
35
149
          Router::HeaderParserPtr)) {
36

            
37
  // TODO(wbpcode): these is a potential bug of message validation. The validation visitor
38
  // of server context should not be used here directly. But this is bug is not introduced
39
  // by this PR and will be fixed in the future.
40
149
  Server::GenericFactoryContextImpl generic_context(context, context.messageValidationVisitor());
41
149
  if (config.has_body_format()) {
42
6
    formatter_ = THROW_OR_RETURN_VALUE(Formatter::SubstitutionFormatStringUtils::fromProtoConfig(
43
6
                                           config.body_format(), generic_context),
44
6
                                       Formatter::FormatterPtr);
45
6
  }
46
149
}
47

            
48
// TODO(pradeepcrao): investigate if this code can be made common with
49
// Envoy::LocalReply::BodyFormatter for consistent behavior.
50
void LocalResponsePolicy::formatBody(const Envoy::Http::RequestHeaderMap& request_headers,
51
                                     const Envoy::Http::ResponseHeaderMap& response_headers,
52
                                     const StreamInfo::StreamInfo& stream_info,
53
44
                                     std::string& body) const {
54
44
  if (local_body_.has_value()) {
55
44
    body = local_body_.value();
56
44
  }
57

            
58
44
  if (formatter_) {
59
6
    body = formatter_->format({&request_headers, &response_headers, nullptr, body}, stream_info);
60
6
  }
61
44
}
62

            
63
Envoy::Http::FilterHeadersStatus LocalResponsePolicy::encodeHeaders(
64
    Envoy::Http::ResponseHeaderMap& headers, bool,
65
44
    Extensions::HttpFilters::CustomResponse::CustomResponseFilter& custom_response_filter) const {
66
44
  auto encoder_callbacks = custom_response_filter.encoderCallbacks();
67
44
  ENVOY_BUG(encoder_callbacks->streamInfo().filterState()->getDataReadOnly<Policy>(
68
44
                "envoy.filters.http.custom_response") == nullptr,
69
44
            "Filter State should not be set when using the LocalResponse policy.");
70
  // Handle local body
71
44
  std::string body;
72
44
  Envoy::Http::Code code = getStatusCodeForLocalReply(headers);
73
44
  formatBody(encoder_callbacks->streamInfo().getRequestHeaders() == nullptr
74
44
                 ? *Envoy::Http::StaticEmptyHeaders::get().request_headers
75
44
                 : *encoder_callbacks->streamInfo().getRequestHeaders(),
76
44
             headers, encoder_callbacks->streamInfo(), body);
77

            
78
44
  const auto mutate_headers = [this, encoder_callbacks](Envoy::Http::ResponseHeaderMap& headers) {
79
42
    header_parser_->evaluateHeaders(headers, encoder_callbacks->streamInfo());
80
42
  };
81
44
  encoder_callbacks->sendLocalReply(code, body, mutate_headers, absl::nullopt, "");
82
44
  return Envoy::Http::FilterHeadersStatus::StopIteration;
83
44
}
84

            
85
Envoy::Http::Code LocalResponsePolicy::getStatusCodeForLocalReply(
86
44
    const Envoy::Http::ResponseHeaderMap& response_headers) const {
87
44
  Envoy::Http::Code code = Envoy::Http::Code::InternalServerError;
88
44
  if (status_code_.has_value()) {
89
43
    code = *status_code_;
90
43
  } else if (auto current_code = Envoy::Http::Utility::getResponseStatusOrNullopt(response_headers);
91
1
             current_code.has_value()) {
92
1
    code = static_cast<Envoy::Http::Code>(*current_code);
93
1
  }
94
44
  return code;
95
44
}
96
} // namespace CustomResponse
97
} // namespace Http
98
} // namespace Extensions
99
} // namespace Envoy