1
#include "source/common/router/header_parser.h"
2

            
3
#include <cctype>
4
#include <memory>
5
#include <regex>
6
#include <string>
7

            
8
#include "envoy/config/core/v3/base.pb.h"
9

            
10
#include "source/common/common/assert.h"
11
#include "source/common/formatter/substitution_formatter.h"
12
#include "source/common/http/header_utility.h"
13
#include "source/common/http/headers.h"
14
#include "source/common/json/json_loader.h"
15
#include "source/common/protobuf/utility.h"
16
#include "source/common/runtime/runtime_features.h"
17

            
18
#include "absl/strings/str_cat.h"
19
#include "absl/strings/str_replace.h"
20

            
21
namespace Envoy {
22
namespace Router {
23

            
24
namespace {
25

            
26
absl::StatusOr<Formatter::FormatterPtr>
27
parseHttpHeaderFormatter(const envoy::config::core::v3::HeaderValue& header_value,
28
3066
                         const Formatter::CommandParserPtrVector& command_parsers) {
29
3066
  const std::string& key = header_value.key();
30
  // PGV constraints provide this guarantee.
31
3066
  ASSERT(!key.empty());
32
  // We reject :path/:authority rewriting, there is already a well defined mechanism to
33
  // perform this in the RouteAction, and doing this via request_headers_to_add
34
  // will cause us to have to worry about interaction with other aspects of the
35
  // RouteAction, e.g. prefix rewriting. We also reject other :-prefixed
36
  // headers, since it seems dangerous and there doesn't appear a use case.
37
  // Host is disallowed as it created confusing and inconsistent behaviors for
38
  // HTTP/1 and HTTP/2. It could arguably be allowed on the response path.
39
3066
  if (!Http::HeaderUtility::isModifiableHeader(key)) {
40
11
    return absl::InvalidArgumentError(":-prefixed or host headers may not be modified");
41
11
  }
42

            
43
3055
  if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.remove_legacy_route_formatter")) {
44
    // UPSTREAM_METADATA and DYNAMIC_METADATA must be translated from JSON ["a", "b"] format to
45
    // colon format (a:b)
46
3055
    std::string final_header_value = HeaderParser::translateMetadataFormat(header_value.value());
47
    // Change PER_REQUEST_STATE to FILTER_STATE.
48
3055
    final_header_value = HeaderParser::translatePerRequestState(final_header_value);
49
    // Let the substitution formatter parse the final_header_value.
50
3055
    return Envoy::Formatter::FormatterImpl::create(final_header_value, true, command_parsers);
51
3055
  }
52

            
53
  // Let the substitution formatter parse the header_value.
54
  return Envoy::Formatter::FormatterImpl::create(header_value.value(), true, command_parsers);
55
3055
}
56

            
57
} // namespace
58

            
59
HeadersToAddEntry::HeadersToAddEntry(const HeaderValueOption& header_value_option,
60
                                     const Formatter::CommandParserPtrVector& command_parsers,
61
                                     absl::Status& creation_status)
62
2931
    : original_value_(header_value_option.header().value()),
63
2931
      add_if_empty_(header_value_option.keep_empty_value()) {
64

            
65
2931
  if (header_value_option.has_append()) {
66
    // 'append' is set and ensure the 'append_action' value is equal to the default value.
67
3
    if (header_value_option.append_action() != HeaderValueOption::APPEND_IF_EXISTS_OR_ADD) {
68
1
      creation_status =
69
1
          absl::InvalidArgumentError("Both append and append_action are set and it's not allowed");
70
1
      return;
71
1
    }
72

            
73
2
    append_action_ = header_value_option.append().value()
74
2
                         ? HeaderValueOption::APPEND_IF_EXISTS_OR_ADD
75
2
                         : HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD;
76
2928
  } else {
77
2928
    append_action_ = header_value_option.append_action();
78
2928
  }
79

            
80
2930
  auto formatter_or_error = parseHttpHeaderFormatter(header_value_option.header(), command_parsers);
81
2930
  SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
82
2877
  formatter_ = std::move(formatter_or_error.value());
83
2877
}
84

            
85
HeadersToAddEntry::HeadersToAddEntry(const HeaderValue& header_value,
86
                                     HeaderAppendAction append_action,
87
                                     const Formatter::CommandParserPtrVector& command_parsers,
88
                                     absl::Status& creation_status)
89
136
    : original_value_(header_value.value()), append_action_(append_action) {
90
136
  auto formatter_or_error = parseHttpHeaderFormatter(header_value, command_parsers);
91
136
  SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
92
136
  formatter_ = std::move(formatter_or_error.value());
93
136
}
94

            
95
absl::StatusOr<HeaderParserPtr>
96
4141
HeaderParser::configure(const Protobuf::RepeatedPtrField<HeaderValueOption>& headers_to_add) {
97
4141
  HeaderParserPtr header_parser(new HeaderParser());
98
4141
  header_parser->headers_to_add_.reserve(headers_to_add.size());
99
4407
  for (const auto& header_value_option : headers_to_add) {
100
2697
    auto entry_or_error = HeadersToAddEntry::create(header_value_option);
101
2697
    RETURN_IF_NOT_OK_REF(entry_or_error.status());
102
2643
    header_parser->headers_to_add_.emplace_back(
103
2643
        Http::LowerCaseString(header_value_option.header().key()),
104
2643
        std::move(entry_or_error.value()));
105
2643
  }
106

            
107
4087
  return header_parser;
108
4141
}
109

            
110
absl::StatusOr<HeaderParserPtr> HeaderParser::configure(
111
    const Protobuf::RepeatedPtrField<envoy::config::core::v3::HeaderValue>& headers_to_add,
112
2938
    HeaderAppendAction append_action) {
113
2938
  HeaderParserPtr header_parser(new HeaderParser());
114

            
115
2938
  header_parser->headers_to_add_.reserve(headers_to_add.size());
116
2965
  for (const auto& header_value : headers_to_add) {
117
136
    auto entry_or_error = HeadersToAddEntry::create(header_value, append_action);
118
136
    RETURN_IF_NOT_OK_REF(entry_or_error.status());
119
136
    header_parser->headers_to_add_.emplace_back(Http::LowerCaseString(header_value.key()),
120
136
                                                std::move(entry_or_error.value()));
121
136
  }
122

            
123
2938
  return header_parser;
124
2938
}
125

            
126
absl::StatusOr<HeaderParserPtr>
127
HeaderParser::configure(const Protobuf::RepeatedPtrField<HeaderValueOption>& headers_to_add,
128
1556
                        const Protobuf::RepeatedPtrField<std::string>& headers_to_remove) {
129
1556
  auto parser_or_error = configure(headers_to_add);
130
1556
  RETURN_IF_NOT_OK_REF(parser_or_error.status());
131
1548
  HeaderParserPtr header_parser = std::move(parser_or_error.value());
132

            
133
1548
  header_parser->headers_to_remove_.reserve(headers_to_remove.size());
134
1548
  for (const auto& header : headers_to_remove) {
135
    // We reject :-prefix (e.g. :path) removal here. This is dangerous, since other aspects of
136
    // request finalization assume their existence and they are needed for well-formedness in most
137
    // cases.
138
537
    if (!Http::HeaderUtility::isRemovableHeader(header)) {
139
7
      return absl::InvalidArgumentError(":-prefixed or host headers may not be removed");
140
7
    }
141
530
    header_parser->headers_to_remove_.emplace_back(header);
142
530
  }
143

            
144
1541
  return header_parser;
145
1548
}
146

            
147
void HeaderParser::evaluateHeaders(Http::HeaderMap& headers, const Formatter::Context& context,
148
259446
                                   const StreamInfo::StreamInfo& stream_info) const {
149
259446
  evaluateHeaders(headers, context, &stream_info);
150
259446
}
151

            
152
void HeaderParser::evaluateHeaders(Http::HeaderMap& headers, const Formatter::Context& context,
153
264515
                                   const StreamInfo::StreamInfo* stream_info) const {
154
  // Removing headers in the headers_to_remove_ list first makes
155
  // remove-before-add the default behavior as expected by users.
156
264515
  for (const auto& header : headers_to_remove_) {
157
294
    headers.remove(header);
158
294
  }
159

            
160
  // Temporary storage to hold evaluated values of headers to add and replace. This is required
161
  // to execute all formatters using the original received headers.
162
  // Only after all the formatters produced the new values of the headers, the headers are set.
163
  // absl::InlinedVector is optimized for 4 headers. After that it behaves as normal std::vector.
164
  // It is assumed that most of the use cases will add or modify fairly small number of headers
165
  // (<=4). If this assumption changes, the number of inlined capacity should be increased.
166
  // header_formatter_speed_test.cc provides micro-benchmark for evaluating speed of adding and
167
  // replacing headers and should be used when modifying the code below to access the performance
168
  // impact of code changes.
169
264515
  absl::InlinedVector<std::pair<const Http::LowerCaseString&, const std::string>, 4> headers_to_add,
170
264515
      headers_to_overwrite;
171
  // value_buffer is used only when stream_info is a valid pointer and stores header value
172
  // created by a formatter. It is declared outside of 'for' loop for performance reason to avoid
173
  // stack allocation and unnecessary std::string's memory adjustments for each iteration. The
174
  // actual value of the header is accessed via 'value' variable which is initialized differently
175
  // depending whether stream_info and valid or nullptr. Based on performance tests implemented in
176
  // header_formatter_speed_test.cc this approach strikes the best balance between performance and
177
  // readability.
178
264515
  std::string value_buffer;
179
264570
  for (const auto& [key, entry] : headers_to_add_) {
180
6222
    absl::string_view value;
181
6222
    if (stream_info != nullptr) {
182
6199
      value_buffer = entry->formatter_->format(context, *stream_info);
183
6199
      value = value_buffer;
184
6212
    } else {
185
23
      value = entry->original_value_;
186
23
    }
187
6222
    if (!value.empty() || entry->add_if_empty_) {
188
4680
      switch (entry->append_action_) {
189
        PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
190
4355
      case HeaderValueOption::APPEND_IF_EXISTS_OR_ADD:
191
4355
        headers_to_add.emplace_back(key, value);
192
4355
        break;
193
12
      case HeaderValueOption::ADD_IF_ABSENT:
194
12
        if (auto header_entry = headers.get(key); header_entry.empty()) {
195
7
          headers_to_add.emplace_back(key, value);
196
7
        }
197
12
        break;
198
2
      case HeaderValueOption::OVERWRITE_IF_EXISTS:
199
2
        if (headers.get(key).empty()) {
200
1
          break;
201
1
        }
202
1
        FALLTHRU;
203
312
      case HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD:
204
312
        headers_to_overwrite.emplace_back(key, value);
205
312
        break;
206
4680
      }
207
4680
    }
208
6222
  }
209

            
210
  // First overwrite all headers which need to be overwritten.
211
264522
  for (const auto& header : headers_to_overwrite) {
212
312
    headers.setReferenceKey(header.first, header.second);
213
312
  }
214

            
215
  // Now add headers which should be added.
216
264538
  for (const auto& header : headers_to_add) {
217
4362
    headers.addReferenceKey(header.first, header.second);
218
4362
  }
219
264515
}
220

            
221
Http::HeaderTransforms HeaderParser::getHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
222
72
                                                         bool do_formatting) const {
223
72
  Http::HeaderTransforms transforms;
224

            
225
91
  for (const auto& [key, entry] : headers_to_add_) {
226
91
    if (do_formatting) {
227
82
      const std::string value = entry->formatter_->format({}, stream_info);
228
82
      if (!value.empty() || entry->add_if_empty_) {
229
82
        switch (entry->append_action_) {
230
38
        case HeaderValueOption::APPEND_IF_EXISTS_OR_ADD:
231
38
          transforms.headers_to_append_or_add.push_back({key, value});
232
38
          break;
233
36
        case HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD:
234
36
          transforms.headers_to_overwrite_or_add.push_back({key, value});
235
36
          break;
236
7
        case HeaderValueOption::ADD_IF_ABSENT:
237
7
          transforms.headers_to_add_if_absent.push_back({key, value});
238
7
          break;
239
1
        default:
240
1
          break;
241
82
        }
242
82
      }
243
82
    } else {
244
9
      switch (entry->append_action_) {
245
2
      case HeaderValueOption::APPEND_IF_EXISTS_OR_ADD:
246
2
        transforms.headers_to_append_or_add.push_back({key, entry->original_value_});
247
2
        break;
248
5
      case HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD:
249
5
        transforms.headers_to_overwrite_or_add.push_back({key, entry->original_value_});
250
5
        break;
251
1
      case HeaderValueOption::ADD_IF_ABSENT:
252
1
        transforms.headers_to_add_if_absent.push_back({key, entry->original_value_});
253
1
        break;
254
1
      default:
255
1
        break;
256
9
      }
257
9
    }
258
91
  }
259

            
260
72
  transforms.headers_to_remove = headers_to_remove_;
261

            
262
72
  return transforms;
263
72
}
264

            
265
} // namespace Router
266
} // namespace Envoy