/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 |