Skip to content

Commit

Permalink
Adds RouteEntry::requestHeaderTransforms. (envoyproxy#19621)
Browse files Browse the repository at this point in the history
* Adds RouteEntry::requestHeaderTransforms.

This new API works exactly the same as `ResponseEntry::responseHeaderTransforms` (see source/docs/response_header_transforms.md) except it gets transforms for request headers.

Signed-off-by: Ming Zhou <[email protected]>
  • Loading branch information
AdiJohn authored Jan 26, 2022
1 parent fcf2c55 commit fd3e837
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 146 deletions.
12 changes: 12 additions & 0 deletions envoy/router/router.h
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,18 @@ class RouteEntry : public ResponseEntry {
const StreamInfo::StreamInfo& stream_info,
bool insert_envoy_original_path) const PURE;

/**
* Returns the request header transforms that would be applied if finalizeRequestHeaders were
* called now. This is useful if you want to obtain request header transforms which was or will be
* applied through finalizeRequestHeaders call. Note: do not use unless you are sure that there
* will be no route modifications later in the filter chain.
* @param stream_info holds additional information about the request.
* @param do_formatting whether or not to evaluate configured transformations; if false, returns
* original values instead.
*/
virtual Http::HeaderTransforms requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting = true) const PURE;

/**
* @return const HashPolicy* the optional hash policy for the route.
*/
Expand Down
4 changes: 4 additions & 0 deletions source/common/http/async_client_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ class AsyncStreamImpl : public AsyncClient::Stream,
}
void finalizeRequestHeaders(Http::RequestHeaderMap&, const StreamInfo::StreamInfo&,
bool) const override {}
Http::HeaderTransforms requestHeaderTransforms(const StreamInfo::StreamInfo&,
bool) const override {
return {};
}
void finalizeResponseHeaders(Http::ResponseHeaderMap&,
const StreamInfo::StreamInfo&) const override {}
Http::HeaderTransforms responseHeaderTransforms(const StreamInfo::StreamInfo&,
Expand Down
106 changes: 64 additions & 42 deletions source/common/router/config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,24 @@ const envoy::config::route::v3::WeightedCluster::ClusterWeight& validateWeighted
return cluster;
}

// Returns a vector of header parsers, sorted by specificity. The `specificity_ascend` parameter
// specifies whether the returned parsers will be sorted from least specific to most specific
// (global connection manager level header parser, virtual host level header parser and finally
// route-level parser.) or the reverse.
absl::InlinedVector<const HeaderParser*, 3>
getHeaderParsers(const HeaderParser* global_route_config_header_parser,
const HeaderParser* vhost_header_parser, const HeaderParser* route_header_parser,
bool specificity_ascend) {
if (specificity_ascend) {
// Sorted from least to most specific: global connection manager level headers, virtual host
// level headers and finally route-level headers.
return {global_route_config_header_parser, vhost_header_parser, route_header_parser};
} else {
// Sorted from most to least specific.
return {route_header_parser, vhost_header_parser, global_route_config_header_parser};
}
}

} // namespace

const std::string& OriginalConnectPort::key() {
Expand Down Expand Up @@ -645,17 +663,10 @@ const std::string& RouteEntryImplBase::clusterName() const { return cluster_name
void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers,
const StreamInfo::StreamInfo& stream_info,
bool insert_envoy_original_path) const {
if (!vhost_.globalRouteConfig().mostSpecificHeaderMutationsWins()) {
// Append user-specified request headers from most to least specific: route-level headers,
// virtual host level headers and finally global connection manager level headers.
request_headers_parser_->evaluateHeaders(headers, stream_info);
vhost_.requestHeaderParser().evaluateHeaders(headers, stream_info);
vhost_.globalRouteConfig().requestHeaderParser().evaluateHeaders(headers, stream_info);
} else {
// Most specific mutations take precedence.
vhost_.globalRouteConfig().requestHeaderParser().evaluateHeaders(headers, stream_info);
vhost_.requestHeaderParser().evaluateHeaders(headers, stream_info);
request_headers_parser_->evaluateHeaders(headers, stream_info);
for (const HeaderParser* header_parser : getRequestHeaderParsers(
/*specificity_ascend=*/vhost_.globalRouteConfig().mostSpecificHeaderMutationsWins())) {
// Later evaluated header parser wins.
header_parser->evaluateHeaders(headers, stream_info);
}

// Restore the port if this was a CONNECT request.
Expand Down Expand Up @@ -698,48 +709,51 @@ void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers,

void RouteEntryImplBase::finalizeResponseHeaders(Http::ResponseHeaderMap& headers,
const StreamInfo::StreamInfo& stream_info) const {
if (!vhost_.globalRouteConfig().mostSpecificHeaderMutationsWins()) {
// Append user-specified request headers from most to least specific: route-level headers,
// virtual host level headers and finally global connection manager level headers.
response_headers_parser_->evaluateHeaders(headers, stream_info);
vhost_.responseHeaderParser().evaluateHeaders(headers, stream_info);
vhost_.globalRouteConfig().responseHeaderParser().evaluateHeaders(headers, stream_info);
} else {
// Most specific mutations take precedence.
vhost_.globalRouteConfig().responseHeaderParser().evaluateHeaders(headers, stream_info);
vhost_.responseHeaderParser().evaluateHeaders(headers, stream_info);
response_headers_parser_->evaluateHeaders(headers, stream_info);
for (const HeaderParser* header_parser : getResponseHeaderParsers(
/*specificity_ascend=*/vhost_.globalRouteConfig().mostSpecificHeaderMutationsWins())) {
// Later evaluated header parser wins.
header_parser->evaluateHeaders(headers, stream_info);
}
}

Http::HeaderTransforms
RouteEntryImplBase::responseHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting) const {
Http::HeaderTransforms transforms;
if (!vhost_.globalRouteConfig().mostSpecificHeaderMutationsWins()) {
// Append user-specified request headers from most to least specific: route-level headers,
// virtual host level headers and finally global connection manager level headers.
mergeTransforms(transforms,
response_headers_parser_->getHeaderTransforms(stream_info, do_formatting));
mergeTransforms(transforms,
vhost_.responseHeaderParser().getHeaderTransforms(stream_info, do_formatting));
mergeTransforms(transforms,
vhost_.globalRouteConfig().responseHeaderParser().getHeaderTransforms(
stream_info, do_formatting));
} else {
// Most specific mutations (route-level) take precedence by being applied
// last: if a header is specified at all levels, the last one applied wins.
mergeTransforms(transforms,
vhost_.globalRouteConfig().responseHeaderParser().getHeaderTransforms(
stream_info, do_formatting));
mergeTransforms(transforms,
vhost_.responseHeaderParser().getHeaderTransforms(stream_info, do_formatting));
mergeTransforms(transforms,
response_headers_parser_->getHeaderTransforms(stream_info, do_formatting));
for (const HeaderParser* header_parser : getResponseHeaderParsers(
/*specificity_ascend=*/vhost_.globalRouteConfig().mostSpecificHeaderMutationsWins())) {
// Later evaluated header parser wins.
mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
}
return transforms;
}

Http::HeaderTransforms
RouteEntryImplBase::requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting) const {
Http::HeaderTransforms transforms;
for (const HeaderParser* header_parser : getRequestHeaderParsers(
/*specificity_ascend=*/vhost_.globalRouteConfig().mostSpecificHeaderMutationsWins())) {
// Later evaluated header parser wins.
mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
}
return transforms;
}

absl::InlinedVector<const HeaderParser*, 3>
RouteEntryImplBase::getRequestHeaderParsers(bool specificity_ascend) const {
return getHeaderParsers(&vhost_.globalRouteConfig().requestHeaderParser(),
&vhost_.requestHeaderParser(), request_headers_parser_.get(),
specificity_ascend);
}

absl::InlinedVector<const HeaderParser*, 3>
RouteEntryImplBase::getResponseHeaderParsers(bool specificity_ascend) const {
return getHeaderParsers(&vhost_.globalRouteConfig().responseHeaderParser(),
&vhost_.responseHeaderParser(), response_headers_parser_.get(),
specificity_ascend);
}

absl::optional<RouteEntryImplBase::RuntimeData>
RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& route_match) {
absl::optional<RuntimeData> runtime;
Expand Down Expand Up @@ -1196,6 +1210,14 @@ RouteEntryImplBase::WeightedClusterEntry::WeightedClusterEntry(
}
}

Http::HeaderTransforms RouteEntryImplBase::WeightedClusterEntry::requestHeaderTransforms(
const StreamInfo::StreamInfo& stream_info, bool do_formatting) const {
auto transforms = request_headers_parser_->getHeaderTransforms(stream_info, do_formatting);
mergeTransforms(transforms,
DynamicRouteEntry::requestHeaderTransforms(stream_info, do_formatting));
return transforms;
}

Http::HeaderTransforms RouteEntryImplBase::WeightedClusterEntry::responseHeaderTransforms(
const StreamInfo::StreamInfo& stream_info, bool do_formatting) const {
auto transforms = response_headers_parser_->getHeaderTransforms(stream_info, do_formatting);
Expand Down
30 changes: 30 additions & 0 deletions source/common/router/config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,8 @@ class RouteEntryImplBase : public RouteEntry,
void finalizeRequestHeaders(Http::RequestHeaderMap& headers,
const StreamInfo::StreamInfo& stream_info,
bool insert_envoy_original_path) const override;
Http::HeaderTransforms requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting = true) const override;
void finalizeResponseHeaders(Http::ResponseHeaderMap& headers,
const StreamInfo::StreamInfo& stream_info) const override;
Http::HeaderTransforms responseHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
Expand Down Expand Up @@ -672,6 +674,10 @@ class RouteEntryImplBase : public RouteEntry,
bool insert_envoy_original_path) const override {
return parent_->finalizeRequestHeaders(headers, stream_info, insert_envoy_original_path);
}
Http::HeaderTransforms requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting = true) const override {
return parent_->requestHeaderTransforms(stream_info, do_formatting);
}
void finalizeResponseHeaders(Http::ResponseHeaderMap& headers,
const StreamInfo::StreamInfo& stream_info) const override {
return parent_->finalizeResponseHeaders(headers, stream_info);
Expand Down Expand Up @@ -810,6 +816,8 @@ class RouteEntryImplBase : public RouteEntry,
}
DynamicRouteEntry::finalizeRequestHeaders(headers, stream_info, insert_envoy_original_path);
}
Http::HeaderTransforms requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting = true) const override;
void finalizeResponseHeaders(Http::ResponseHeaderMap& headers,
const StreamInfo::StreamInfo& stream_info) const override {
response_headers_parser_->evaluateHeaders(headers, stream_info);
Expand Down Expand Up @@ -844,6 +852,28 @@ class RouteEntryImplBase : public RouteEntry,

using WeightedClusterEntrySharedPtr = std::shared_ptr<WeightedClusterEntry>;

/**
* Returns a vector of request header parsers which applied or will apply header transformations
* to the request in this route.
* @param specificity_ascend specifies whether the returned parsers will be sorted from least
* specific to most specific (global connection manager level header parser, virtual host
* level header parser and finally route-level parser.) or the reverse.
* @return a vector of request header parsers.
*/
absl::InlinedVector<const HeaderParser*, 3>
getRequestHeaderParsers(bool specificity_ascend) const;

/**
* Returns a vector of response header parsers which applied or will apply header transformations
* to the response in this route.
* @param specificity_ascend specifies whether the returned parsers will be sorted from least
* specific to most specific (global connection manager level header parser, virtual host
* level header parser and finally route-level parser.) or the reverse.
* @return a vector of request header parsers.
*/
absl::InlinedVector<const HeaderParser*, 3>
getResponseHeaderParsers(bool specificity_ascend) const;

absl::optional<RuntimeData> loadRuntimeData(const envoy::config::route::v3::RouteMatch& route);

static std::multimap<std::string, std::string>
Expand Down
6 changes: 6 additions & 0 deletions source/common/router/delegating_route_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ void DelegatingRouteEntry::finalizeRequestHeaders(Http::RequestHeaderMap& header
insert_envoy_original_path);
}

Http::HeaderTransforms
DelegatingRouteEntry::requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting) const {
return base_route_->routeEntry()->requestHeaderTransforms(stream_info, do_formatting);
}

const Http::HashPolicy* DelegatingRouteEntry::hashPolicy() const {
return base_route_->routeEntry()->hashPolicy();
}
Expand Down
3 changes: 3 additions & 0 deletions source/common/router/delegating_route_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class DelegatingRouteEntry : public Router::RouteEntry {
void finalizeRequestHeaders(Http::RequestHeaderMap& headers,
const StreamInfo::StreamInfo& stream_info,
bool insert_envoy_original_path) const override;
Http::HeaderTransforms requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
bool do_formatting = true) const override;

const Http::HashPolicy* hashPolicy() const override;
const HedgePolicy& hedgePolicy() const override;
Upstream::ResourcePriority priority() const override;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
## `ResponseEntry::responseHeaderTransforms` API
## `RouteEntry::requestHeaderTransforms` and `ResponseEntry::responseHeaderTransforms` APIs

### Overview

`ResponseEntry::responseHeaderTransforms` allows filters to obtain response
header transformations that would be applied by
`ResponseEntry::finalizeResponseHeaders`.
`RouteEntry::requestHeaderTransforms`
and `ResponseEntry::responseHeaderTransforms` allows filters to obtain request
and response header transformations that would be applied by
`RouteEntry::finalizeRequestHeaders`
and `ResponseEntry::finalizeResponseHeaders` respectively.

If you do not have full knowledge that there will be no further route
modifications later in the filter chain, you should not use this API.
modifications later in the filter chain, you should not use these APIs.

### Usage
### Example usage

To obtain the response header transformations at request time:

Expand Down
Loading

0 comments on commit fd3e837

Please sign in to comment.