1
#include "source/common/http/header_mutation.h"
2

            
3
#include "source/common/common/matchers.h"
4
#include "source/common/router/header_parser.h"
5

            
6
namespace Envoy {
7
namespace Http {
8

            
9
namespace {
10

            
11
using HeaderAppendAction = envoy::config::core::v3::HeaderValueOption::HeaderAppendAction;
12
using HeaderValueOption = envoy::config::core::v3::HeaderValueOption;
13

            
14
// TODO(wbpcode): Inherit from Envoy::Router::HeadersToAddEntry to make sure the formatter
15
// has the same behavior as the router's formatter. We should try to find a more clean way
16
// to reuse the formatter after the router's formatter is completely removed.
17
class AppendMutation : public HeaderEvaluator, public Envoy::Router::HeadersToAddEntry {
18
public:
19
  AppendMutation(const HeaderValueOption& header_value_option,
20
                 const Formatter::CommandParserPtrVector& command_parsers,
21
                 absl::Status& creation_status)
22
234
      : HeadersToAddEntry(header_value_option, command_parsers, creation_status),
23
234
        header_name_(header_value_option.header().key()) {}
24

            
25
  void evaluateHeaders(Http::HeaderMap& headers, const Formatter::Context& context,
26
215
                       const StreamInfo::StreamInfo& stream_info) const override {
27
215
    const std::string value = formatter_->format(context, stream_info);
28

            
29
215
    if (!value.empty() || add_if_empty_) {
30
199
      switch (append_action_) {
31
        PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
32
126
      case HeaderValueOption::APPEND_IF_EXISTS_OR_ADD:
33
126
        headers.addReferenceKey(header_name_, value);
34
126
        return;
35
19
      case HeaderValueOption::ADD_IF_ABSENT: {
36
19
        auto header = headers.get(header_name_);
37
19
        if (!header.empty()) {
38
10
          return;
39
10
        }
40
9
        headers.addReferenceKey(header_name_, value);
41
9
        break;
42
19
      }
43
14
      case HeaderValueOption::OVERWRITE_IF_EXISTS:
44
14
        if (headers.get(header_name_).empty()) {
45
7
          return;
46
7
        }
47
7
        FALLTHRU;
48
47
      case HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD:
49
47
        headers.setReferenceKey(header_name_, value);
50
47
        break;
51
199
      }
52
199
    }
53
215
  }
54

            
55
private:
56
  Envoy::Http::LowerCaseString header_name_;
57
};
58

            
59
class RemoveMutation : public HeaderEvaluator {
60
public:
61
44
  RemoveMutation(const std::string& header_name) : header_name_(header_name) {}
62

            
63
  void evaluateHeaders(Http::HeaderMap& headers, const Formatter::Context&,
64
51
                       const StreamInfo::StreamInfo&) const override {
65
51
    headers.remove(header_name_);
66
51
  }
67

            
68
private:
69
  const Envoy::Http::LowerCaseString header_name_;
70
};
71

            
72
class RemoveOnMatchMutation : public HeaderEvaluator {
73
public:
74
  RemoveOnMatchMutation(const envoy::type::matcher::v3::StringMatcher& key_matcher,
75
                        Server::Configuration::CommonFactoryContext& context)
76
2
      : key_matcher_(key_matcher, context) {}
77

            
78
  void evaluateHeaders(Http::HeaderMap& headers, const Formatter::Context&,
79
2
                       const StreamInfo::StreamInfo&) const override {
80
17
    headers.removeIf([this](const Http::HeaderEntry& header) {
81
17
      return key_matcher_.match(header.key().getStringView());
82
17
    });
83
2
  }
84

            
85
private:
86
  const Matchers::StringMatcherImpl key_matcher_;
87
};
88

            
89
} // namespace
90

            
91
absl::StatusOr<std::unique_ptr<HeaderMutations>>
92
HeaderMutations::create(const ProtoHeaderMutatons& header_mutations,
93
                        Server::Configuration::CommonFactoryContext& context,
94
434
                        const Formatter::CommandParserPtrVector& command_parsers) {
95
434
  absl::Status creation_status = absl::OkStatus();
96
434
  auto ret = std::unique_ptr<HeaderMutations>(
97
434
      new HeaderMutations(header_mutations, context, command_parsers, creation_status));
98
434
  RETURN_IF_NOT_OK(creation_status);
99
434
  return ret;
100
434
}
101

            
102
HeaderMutations::HeaderMutations(const ProtoHeaderMutatons& header_mutations,
103
                                 Server::Configuration::CommonFactoryContext& context,
104
                                 const Formatter::CommandParserPtrVector& command_parsers,
105
434
                                 absl::Status& creation_status) {
106
434
  header_mutations_.reserve(header_mutations.size());
107
471
  for (const auto& mutation : header_mutations) {
108
280
    switch (mutation.action_case()) {
109
234
    case envoy::config::common::mutation_rules::v3::HeaderMutation::ActionCase::kAppend:
110
234
      header_mutations_.emplace_back(
111
234
          std::make_unique<AppendMutation>(mutation.append(), command_parsers, creation_status));
112
234
      if (!creation_status.ok()) {
113
        return;
114
      }
115
234
      break;
116
235
    case envoy::config::common::mutation_rules::v3::HeaderMutation::ActionCase::kRemove:
117
44
      header_mutations_.emplace_back(std::make_unique<RemoveMutation>(mutation.remove()));
118
44
      break;
119
2
    case envoy::config::common::mutation_rules::v3::HeaderMutation::ActionCase::kRemoveOnMatch:
120
2
      header_mutations_.emplace_back(std::make_unique<RemoveOnMatchMutation>(
121
2
          mutation.remove_on_match().key_matcher(), context));
122
2
      break;
123
    default:
124
      PANIC_DUE_TO_PROTO_UNSET;
125
280
    }
126
280
  }
127
434
}
128

            
129
void HeaderMutations::evaluateHeaders(Http::HeaderMap& headers, const Formatter::Context& context,
130
166
                                      const StreamInfo::StreamInfo& stream_info) const {
131
294
  for (const auto& mutation : header_mutations_) {
132
268
    mutation->evaluateHeaders(headers, context, stream_info);
133
268
  }
134
166
}
135

            
136
} // namespace Http
137
} // namespace Envoy