Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/router/header_parser.cc
Line
Count
Source (jump to first uncovered line)
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
17
#include "absl/strings/str_cat.h"
18
#include "absl/strings/str_replace.h"
19
20
namespace Envoy {
21
namespace Router {
22
23
namespace {
24
25
absl::StatusOr<Formatter::FormatterPtr>
26
320k
parseHttpHeaderFormatter(const envoy::config::core::v3::HeaderValue& header_value) {
27
320k
  const std::string& key = header_value.key();
28
  // PGV constraints provide this guarantee.
29
320k
  ASSERT(!key.empty());
30
  // We reject :path/:authority rewriting, there is already a well defined mechanism to
31
  // perform this in the RouteAction, and doing this via request_headers_to_add
32
  // will cause us to have to worry about interaction with other aspects of the
33
  // RouteAction, e.g. prefix rewriting. We also reject other :-prefixed
34
  // headers, since it seems dangerous and there doesn't appear a use case.
35
  // Host is disallowed as it created confusing and inconsistent behaviors for
36
  // HTTP/1 and HTTP/2. It could arguably be allowed on the response path.
37
320k
  if (!Http::HeaderUtility::isModifiableHeader(key)) {
38
43
    return absl::InvalidArgumentError(":-prefixed or host headers may not be modified");
39
43
  }
40
41
  // UPSTREAM_METADATA and DYNAMIC_METADATA must be translated from JSON ["a", "b"] format to colon
42
  // format (a:b)
43
320k
  std::string final_header_value = HeaderParser::translateMetadataFormat(header_value.value());
44
  // Change PER_REQUEST_STATE to FILTER_STATE.
45
320k
  final_header_value = HeaderParser::translatePerRequestState(final_header_value);
46
47
  // Let the substitution formatter parse the final_header_value.
48
320k
  return std::make_unique<Envoy::Formatter::FormatterImpl>(final_header_value, true);
49
320k
}
50
51
} // namespace
52
53
HeadersToAddEntry::HeadersToAddEntry(const HeaderValueOption& header_value_option,
54
                                     absl::Status& creation_status)
55
    : original_value_(header_value_option.header().value()),
56
304k
      add_if_empty_(header_value_option.keep_empty_value()) {
57
58
304k
  if (header_value_option.has_append()) {
59
    // 'append' is set and ensure the 'append_action' value is equal to the default value.
60
30.9k
    if (header_value_option.append_action() != HeaderValueOption::APPEND_IF_EXISTS_OR_ADD) {
61
45
      creation_status =
62
45
          absl::InvalidArgumentError("Both append and append_action are set and it's not allowed");
63
45
      return;
64
45
    }
65
66
30.9k
    append_action_ = header_value_option.append().value()
67
30.9k
                         ? HeaderValueOption::APPEND_IF_EXISTS_OR_ADD
68
30.9k
                         : HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD;
69
273k
  } else {
70
273k
    append_action_ = header_value_option.append_action();
71
273k
  }
72
73
304k
  auto formatter_or_error = parseHttpHeaderFormatter(header_value_option.header());
74
304k
  SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
75
304k
  formatter_ = std::move(formatter_or_error.value());
76
304k
}
77
78
HeadersToAddEntry::HeadersToAddEntry(const HeaderValue& header_value,
79
                                     HeaderAppendAction append_action,
80
                                     absl::Status& creation_status)
81
16.2k
    : original_value_(header_value.value()), append_action_(append_action) {
82
16.2k
  auto formatter_or_error = parseHttpHeaderFormatter(header_value);
83
16.2k
  SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
84
16.2k
  formatter_ = std::move(formatter_or_error.value());
85
16.2k
}
86
87
absl::StatusOr<HeaderParserPtr>
88
79.5k
HeaderParser::configure(const Protobuf::RepeatedPtrField<HeaderValueOption>& headers_to_add) {
89
79.5k
  HeaderParserPtr header_parser(new HeaderParser());
90
304k
  for (const auto& header_value_option : headers_to_add) {
91
304k
    auto entry_or_error = HeadersToAddEntry::create(header_value_option);
92
304k
    RETURN_IF_NOT_OK_REF(entry_or_error.status());
93
304k
    header_parser->headers_to_add_.emplace_back(
94
304k
        Http::LowerCaseString(header_value_option.header().key()),
95
304k
        std::move(entry_or_error.value()));
96
304k
  }
97
98
79.4k
  return header_parser;
99
79.5k
}
100
101
absl::StatusOr<HeaderParserPtr> HeaderParser::configure(
102
    const Protobuf::RepeatedPtrField<envoy::config::core::v3::HeaderValue>& headers_to_add,
103
5.50k
    HeaderAppendAction append_action) {
104
5.50k
  HeaderParserPtr header_parser(new HeaderParser());
105
106
16.2k
  for (const auto& header_value : headers_to_add) {
107
16.2k
    auto entry_or_error = HeadersToAddEntry::create(header_value, append_action);
108
16.2k
    RETURN_IF_NOT_OK_REF(entry_or_error.status());
109
16.2k
    header_parser->headers_to_add_.emplace_back(Http::LowerCaseString(header_value.key()),
110
16.2k
                                                std::move(entry_or_error.value()));
111
16.2k
  }
112
113
5.50k
  return header_parser;
114
5.50k
}
115
116
absl::StatusOr<HeaderParserPtr>
117
HeaderParser::configure(const Protobuf::RepeatedPtrField<HeaderValueOption>& headers_to_add,
118
76.5k
                        const Protobuf::RepeatedPtrField<std::string>& headers_to_remove) {
119
76.5k
  auto parser_or_error = configure(headers_to_add);
120
76.5k
  RETURN_IF_NOT_OK_REF(parser_or_error.status());
121
76.4k
  HeaderParserPtr header_parser = std::move(parser_or_error.value());
122
123
76.4k
  for (const auto& header : headers_to_remove) {
124
    // We reject :-prefix (e.g. :path) removal here. This is dangerous, since other aspects of
125
    // request finalization assume their existence and they are needed for well-formedness in most
126
    // cases.
127
76.3k
    if (!Http::HeaderUtility::isRemovableHeader(header)) {
128
140
      return absl::InvalidArgumentError(":-prefixed or host headers may not be removed");
129
140
    }
130
76.2k
    header_parser->headers_to_remove_.emplace_back(header);
131
76.2k
  }
132
133
76.2k
  return header_parser;
134
76.4k
}
135
136
void HeaderParser::evaluateHeaders(Http::HeaderMap& headers,
137
                                   const Formatter::HttpFormatterContext& context,
138
5.37k
                                   const StreamInfo::StreamInfo& stream_info) const {
139
5.37k
  evaluateHeaders(headers, context, &stream_info);
140
5.37k
}
141
142
void HeaderParser::evaluateHeaders(Http::HeaderMap& headers,
143
                                   const Formatter::HttpFormatterContext& context,
144
37.2k
                                   const StreamInfo::StreamInfo* stream_info) const {
145
  // Removing headers in the headers_to_remove_ list first makes
146
  // remove-before-add the default behavior as expected by users.
147
37.2k
  for (const auto& header : headers_to_remove_) {
148
8.67k
    headers.remove(header);
149
8.67k
  }
150
151
  // Temporary storage to hold evaluated values of headers to add and replace. This is required
152
  // to execute all formatters using the original received headers.
153
  // Only after all the formatters produced the new values of the headers, the headers are set.
154
  // absl::InlinedVector is optimized for 4 headers. After that it behaves as normal std::vector.
155
  // It is assumed that most of the use cases will add or modify fairly small number of headers
156
  // (<=4). If this assumption changes, the number of inlined capacity should be increased.
157
  // header_formatter_speed_test.cc provides micro-benchmark for evaluating speed of adding and
158
  // replacing headers and should be used when modifying the code below to access the performance
159
  // impact of code changes.
160
37.2k
  absl::InlinedVector<std::pair<const Http::LowerCaseString&, const std::string>, 4> headers_to_add,
161
37.2k
      headers_to_overwrite;
162
  // value_buffer is used only when stream_info is a valid pointer and stores header value
163
  // created by a formatter. It is declared outside of 'for' loop for performance reason to avoid
164
  // stack allocation and unnecessary std::string's memory adjustments for each iteration. The
165
  // actual value of the header is accessed via 'value' variable which is initialized differently
166
  // depending whether stream_info and valid or nullptr. Based on performance tests implemented in
167
  // header_formatter_speed_test.cc this approach strikes the best balance between performance and
168
  // readability.
169
37.2k
  std::string value_buffer;
170
317k
  for (const auto& [key, entry] : headers_to_add_) {
171
317k
    absl::string_view value;
172
317k
    if (stream_info != nullptr) {
173
308k
      value_buffer = entry->formatter_->formatWithContext(context, *stream_info);
174
308k
      value = value_buffer;
175
308k
    } else {
176
8.88k
      value = entry->original_value_;
177
8.88k
    }
178
317k
    if (!value.empty() || entry->add_if_empty_) {
179
197k
      switch (entry->append_action_) {
180
0
        PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
181
140k
      case HeaderValueOption::APPEND_IF_EXISTS_OR_ADD:
182
140k
        headers_to_add.emplace_back(key, value);
183
140k
        break;
184
7.52k
      case HeaderValueOption::ADD_IF_ABSENT:
185
7.52k
        if (auto header_entry = headers.get(key); header_entry.empty()) {
186
7.18k
          headers_to_add.emplace_back(key, value);
187
7.18k
        }
188
7.52k
        break;
189
2.56k
      case HeaderValueOption::OVERWRITE_IF_EXISTS:
190
2.56k
        if (headers.get(key).empty()) {
191
2.12k
          break;
192
2.12k
        }
193
2.56k
        FALLTHRU;
194
47.6k
      case HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD:
195
47.6k
        headers_to_overwrite.emplace_back(key, value);
196
47.6k
        break;
197
197k
      }
198
197k
    }
199
317k
  }
200
201
  // First overwrite all headers which need to be overwritten.
202
47.6k
  for (const auto& header : headers_to_overwrite) {
203
47.6k
    headers.setReferenceKey(header.first, header.second);
204
47.6k
  }
205
206
  // Now add headers which should be added.
207
147k
  for (const auto& header : headers_to_add) {
208
147k
    headers.addReferenceKey(header.first, header.second);
209
147k
  }
210
37.2k
}
211
212
Http::HeaderTransforms HeaderParser::getHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
213
0
                                                         bool do_formatting) const {
214
0
  Http::HeaderTransforms transforms;
215
216
0
  for (const auto& [key, entry] : headers_to_add_) {
217
0
    if (do_formatting) {
218
0
      const std::string value = entry->formatter_->formatWithContext({}, stream_info);
219
0
      if (!value.empty() || entry->add_if_empty_) {
220
0
        switch (entry->append_action_) {
221
0
        case HeaderValueOption::APPEND_IF_EXISTS_OR_ADD:
222
0
          transforms.headers_to_append_or_add.push_back({key, value});
223
0
          break;
224
0
        case HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD:
225
0
          transforms.headers_to_overwrite_or_add.push_back({key, value});
226
0
          break;
227
0
        case HeaderValueOption::ADD_IF_ABSENT:
228
0
          transforms.headers_to_add_if_absent.push_back({key, value});
229
0
          break;
230
0
        default:
231
0
          break;
232
0
        }
233
0
      }
234
0
    } else {
235
0
      switch (entry->append_action_) {
236
0
      case HeaderValueOption::APPEND_IF_EXISTS_OR_ADD:
237
0
        transforms.headers_to_append_or_add.push_back({key, entry->original_value_});
238
0
        break;
239
0
      case HeaderValueOption::OVERWRITE_IF_EXISTS_OR_ADD:
240
0
        transforms.headers_to_overwrite_or_add.push_back({key, entry->original_value_});
241
0
        break;
242
0
      case HeaderValueOption::ADD_IF_ABSENT:
243
0
        transforms.headers_to_add_if_absent.push_back({key, entry->original_value_});
244
0
        break;
245
0
      default:
246
0
        break;
247
0
      }
248
0
    }
249
0
  }
250
251
0
  transforms.headers_to_remove = headers_to_remove_;
252
253
0
  return transforms;
254
0
}
255
256
} // namespace Router
257
} // namespace Envoy