Skip to content

Commit

Permalink
http: make custom inline headers bootstrap configurable (#17330)
Browse files Browse the repository at this point in the history
Signed-off-by: wbpcode <[email protected]>
  • Loading branch information
wbpcode authored Jul 27, 2021
1 parent 57bfbda commit 6ace04d
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 6 deletions.
49 changes: 48 additions & 1 deletion api/envoy/config/bootstrap/v3/bootstrap.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// <config_overview_bootstrap>` for more detail.

// Bootstrap :ref:`configuration overview <config_overview_bootstrap>`.
// [#next-free-field: 32]
// [#next-free-field: 33]
message Bootstrap {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.bootstrap.v2.Bootstrap";
Expand Down Expand Up @@ -322,6 +322,13 @@ message Bootstrap {
// field.
// [#not-implemented-hide:]
map<string, core.v3.TypedExtensionConfig> certificate_provider_instances = 25;

// Specifies a set of headers that need to be registered as inline header. This configuration
// allows users to customize the inline headers on-demand at Envoy startup without modifying
// Envoy's source code.
//
// Note that the 'set-cookie' header cannot be registered as inline header.
repeated CustomInlineHeader inline_headers = 32;
}

// Administration interface :ref:`operations documentation
Expand Down Expand Up @@ -595,3 +602,43 @@ message LayeredRuntime {
// such that later layers in the list overlay earlier entries.
repeated RuntimeLayer layers = 1;
}

// Used to specify the header that needs to be registered as an inline header.
//
// If request or response contain multiple headers with the same name and the header
// name is registered as an inline header. Then multiple headers will be folded
// into one, and multiple header values will be concatenated by a suitable delimiter.
// The delimiter is generally a comma.
//
// For example, if 'foo' is registered as an inline header, and the headers contains
// the following two headers:
//
// .. code-block:: text
//
// foo: bar
// foo: eep
//
// Then they will eventually be folded into:
//
// .. code-block:: text
//
// foo: bar, eep
//
// Inline headers provide O(1) search performance, but each inline header imposes
// an additional memory overhead on all instances of the corresponding type of
// HeaderMap or TrailerMap.
message CustomInlineHeader {
enum InlineHeaderType {
REQUEST_HEADER = 0;
REQUEST_TRAILER = 1;
RESPONSE_HEADER = 2;
RESPONSE_TRAILER = 3;
}

// The name of the header that is expected to be set as the inline header.
string inline_header_name = 1
[(validate.rules).string = {min_len: 1 well_known_regex: HTTP_HEADER_NAME strict: false}];

// The type of the header that is expected to be set as the inline header.
InlineHeaderType inline_header_type = 2 [(validate.rules).enum = {defined_only: true}];
}
52 changes: 51 additions & 1 deletion api/envoy/config/bootstrap/v4alpha/bootstrap.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Removed Config or Runtime

New Features
------------
* bootstrap: added :ref:`inline_headers <envoy_v3_api_field_config.bootstrap.v3.Bootstrap.inline_headers>` in the bootstrap to make custom inline headers bootstrap configurable.
* http: added :ref:`string_match <envoy_v3_api_field_config.route.v3.HeaderMatcher.string_match>` in the header matcher.
* http: added support for :ref:`max_requests_per_connection <envoy_v3_api_field_config.core.v3.HttpProtocolOptions.max_requests_per_connection>` for both upstream and downstream connections.

Expand Down
49 changes: 48 additions & 1 deletion generated_api_shadow/envoy/config/bootstrap/v3/bootstrap.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions source/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ envoy_cc_library(
"//source/common/grpc:context_lib",
"//source/common/http:codes_lib",
"//source/common/http:context_lib",
"//source/common/http:headers_lib",
"//source/common/init:manager_lib",
"//source/common/local_info:local_info_lib",
"//source/common/memory:heap_shrinker_lib",
Expand Down
47 changes: 45 additions & 2 deletions source/server/server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "source/common/config/version_converter.h"
#include "source/common/config/xds_resource.h"
#include "source/common/http/codes.h"
#include "source/common/http/headers.h"
#include "source/common/local_info/local_info_impl.h"
#include "source/common/memory/stats.h"
#include "source/common/network/address_impl.h"
Expand Down Expand Up @@ -295,6 +296,46 @@ void loadBootstrap(absl::optional<uint32_t> bootstrap_version,
throw EnvoyException(fmt::format("Unknown bootstrap version {}.", *bootstrap_version));
}
}

bool canBeRegisteredAsInlineHeader(const Http::LowerCaseString& header_name) {
// 'set-cookie' cannot currently be registered as an inline header.
if (header_name == Http::Headers::get().SetCookie) {
return false;
}
return true;
}

void registerCustomInlineHeadersFromBootstrap(
const envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
for (const auto& inline_header : bootstrap.inline_headers()) {
const Http::LowerCaseString lower_case_name(inline_header.inline_header_name());
if (!canBeRegisteredAsInlineHeader(lower_case_name)) {
throw EnvoyException(fmt::format("Header {} cannot be registered as an inline header.",
inline_header.inline_header_name()));
}
switch (inline_header.inline_header_type()) {
case envoy::config::bootstrap::v3::CustomInlineHeader::REQUEST_HEADER:
Http::CustomInlineHeaderRegistry::registerInlineHeader<
Http::RequestHeaderMap::header_map_type>(lower_case_name);
break;
case envoy::config::bootstrap::v3::CustomInlineHeader::REQUEST_TRAILER:
Http::CustomInlineHeaderRegistry::registerInlineHeader<
Http::RequestTrailerMap::header_map_type>(lower_case_name);
break;
case envoy::config::bootstrap::v3::CustomInlineHeader::RESPONSE_HEADER:
Http::CustomInlineHeaderRegistry::registerInlineHeader<
Http::ResponseHeaderMap::header_map_type>(lower_case_name);
break;
case envoy::config::bootstrap::v3::CustomInlineHeader::RESPONSE_TRAILER:
Http::CustomInlineHeaderRegistry::registerInlineHeader<
Http::ResponseTrailerMap::header_map_type>(lower_case_name);
break;
default:
NOT_IMPLEMENTED_GCOVR_EXCL_LINE;
}
}
}

} // namespace

void InstanceUtil::loadBootstrapConfig(envoy::config::bootstrap::v3::Bootstrap& bootstrap,
Expand Down Expand Up @@ -356,8 +397,10 @@ void InstanceImpl::initialize(const Options& options,
// setPrefix has a release assert verifying that setPrefix() is not called after prefix()
ThreadSafeSingleton<Http::PrefixValue>::get().setPrefix(bootstrap_.header_prefix().c_str());
}
// TODO(mattklein123): Custom O(1) headers can be registered at this point for creating/finalizing
// any header maps.

// Register Custom O(1) headers from bootstrap.
registerCustomInlineHeadersFromBootstrap(bootstrap_);

ENVOY_LOG(info, "HTTP header map info:");
for (const auto& info : Http::HeaderMapImplUtility::getAllHeaderMapImplInfo()) {
ENVOY_LOG(info, " {}: {} bytes: {}", info.name_, info.size_,
Expand Down
Loading

0 comments on commit 6ace04d

Please sign in to comment.