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