Skip to content

Commit

Permalink
auto-merge envoyproxy/envoy[main] into envoyproxy/envoy-openssl[main]
Browse files Browse the repository at this point in the history
* upstream/main:
  Handle encode metadata after recreated stream (#36427)
  mobile: Fix broken link (#36436)
  • Loading branch information
sync-envoy[bot] committed Oct 3, 2024
2 parents 95730f7 + 37b725d commit 316e292
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest {
HttpTestServerFactory.start(
HttpTestServerFactory.Type.HTTP1_WITHOUT_TLS,
0,
// http://go/mdn/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file
mapOf("Content-Type" to "application/x-ns-proxy-autoconfig"),
"""
function FindProxyForURL(url, host) {
Expand Down
10 changes: 10 additions & 0 deletions source/common/http/filter_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,10 @@ bool ActiveStreamDecoderFilter::recreateStream(const ResponseHeaderMap* headers)
return false;
}

parent_.state_.decoder_filter_chain_aborted_ = true;
parent_.state_.encoder_filter_chain_aborted_ = true;
parent_.state_.recreated_stream_ = true;

parent_.streamInfo().setResponseCodeDetails(
StreamInfo::ResponseCodeDetails::get().InternalRedirect);

Expand Down Expand Up @@ -1762,6 +1766,12 @@ void ActiveStreamEncoderFilter::drainSavedResponseMetadata() {
}

void ActiveStreamEncoderFilter::handleMetadataAfterHeadersCallback() {
if (parent_.state_.recreated_stream_) {
// The stream has been recreated. In this case, there's no reason to encode saved metadata.
getSavedResponseMetadata()->clear();
return;
}

// If we drain accumulated metadata, the iteration must start with the current filter.
const bool saved_state = iterate_from_current_filter_;
iterate_from_current_filter_ = true;
Expand Down
2 changes: 2 additions & 0 deletions source/common/http/filter_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,8 @@ class FilterManager : public ScopeTrackedObject,
bool decoder_filter_chain_aborted_{};
bool encoder_filter_chain_aborted_{};
bool saw_downstream_reset_{};
// True when the stream was recreated.
bool recreated_stream_{};

// The following 3 members are booleans rather than part of the space-saving bitfield as they
// are passed as arguments to functions expecting bools. Extend State using the bitfield
Expand Down
3 changes: 3 additions & 0 deletions test/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -798,13 +798,16 @@ envoy_cc_test(
"//source/common/http:header_map_lib",
"//source/extensions/filters/http/buffer:config",
"//test/integration/filters:add_body_filter_config_lib",
"//test/integration/filters:add_encode_metadata_filter_lib",
"//test/integration/filters:add_invalid_data_filter_lib",
"//test/integration/filters:assert_non_reentrant_filter_lib",
"//test/integration/filters:buffer_continue_filter_lib",
"//test/integration/filters:continue_after_local_reply_filter_lib",
"//test/integration/filters:continue_headers_only_inject_body",
"//test/integration/filters:encoder_decoder_buffer_filter_lib",
"//test/integration/filters:encoder_recreate_stream_filter_lib",
"//test/integration/filters:invalid_header_filter_lib",
"//test/integration/filters:local_reply_during_decoding_filter_lib",
"//test/integration/filters:local_reply_during_encoding_data_filter_lib",
"//test/integration/filters:local_reply_during_encoding_filter_lib",
"//test/integration/filters:local_reply_with_metadata_filter_lib",
Expand Down
80 changes: 80 additions & 0 deletions test/integration/filter_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1575,5 +1575,85 @@ TEST_P(FilterIntegrationTest, FilterAddsDataToHeaderOnlyRequestWithIndependentHa
testFilterAddsDataAndTrailersToHeaderOnlyRequest();
}

// Add metadata in the first filter before recreate the stream in the second filter,
// on response path.
TEST_P(FilterIntegrationTest, RecreateStreamAfterEncodeMetadata) {
// recreateStream is not supported in Upstream filter chain.
if (!testing_downstream_filter_) {
return;
}

prependFilter("{ name: add-metadata-encode-headers-filter }");
prependFilter("{ name: encoder-recreate-stream-filter }");
config_helper_.addConfigModifier(
[&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); });

initialize();

codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
waitForNextUpstreamRequest();
upstream_request_->encodeHeaders(default_response_headers_, true);

// Second upstream request is triggered by recreateStream.
FakeStreamPtr upstream_request_2;
// Wait for the next stream on the upstream connection.
ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_2));
// Wait for the stream to be completely received.
ASSERT_TRUE(upstream_request_2->waitForEndStream(*dispatcher_));
upstream_request_2->encodeHeaders(default_response_headers_, true);

// Wait for the response to be completely received.
ASSERT_TRUE(response->waitForEndStream());
ASSERT_TRUE(response->complete());

// Verify the metadata is received.
std::set<std::string> expected_metadata_keys = {"headers", "duplicate"};
EXPECT_EQ(response->metadataMap().size(), expected_metadata_keys.size());
for (const auto& key : expected_metadata_keys) {
// keys are the same as their corresponding values.
auto it = response->metadataMap().find(key);
ASSERT_FALSE(it == response->metadataMap().end()) << "key: " << key;
EXPECT_EQ(response->metadataMap().find(key)->second, key);
}
}

// Add metadata in the first filter on local reply path.
TEST_P(FilterIntegrationTest, EncodeMetadataOnLocalReply) {
// Local replies are not seen by upstream HTTP filters. add-metadata-encode-headers-filter will
// not be invoked if it is installed in upstream filter chain.
// Thus, this test is only applicable to downstream filter chain.
if (!testing_downstream_filter_) {
return;
}

prependFilter("{ name: local-reply-during-decode }");
prependFilter("{ name: add-metadata-encode-headers-filter }");

config_helper_.addConfigModifier(
[&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); });

initialize();

codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);

ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("500", response->headers().getStatusValue());

// Verify the metadata is received.
std::set<std::string> expected_metadata_keys = {"headers", "duplicate"};
EXPECT_EQ(response->metadataMap().size(), expected_metadata_keys.size());
for (const auto& key : expected_metadata_keys) {
// keys are the same as their corresponding values.
auto it = response->metadataMap().find(key);
ASSERT_FALSE(it == response->metadataMap().end()) << "key: " << key;
EXPECT_EQ(response->metadataMap().find(key)->second, key);
}
}

} // namespace
} // namespace Envoy
33 changes: 33 additions & 0 deletions test/integration/filters/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1013,3 +1013,36 @@ envoy_cc_test_library(
"//test/extensions/filters/http/common:empty_http_filter_config_lib",
],
)

envoy_cc_test_library(
name = "add_encode_metadata_filter_lib",
srcs = [
"add_encode_metadata_filter.cc",
],
deps = [
":common_lib",
"//envoy/event:timer_interface",
"//envoy/http:filter_interface",
"//envoy/registry",
"//envoy/server:filter_config_interface",
"//source/extensions/filters/http/common:pass_through_filter_lib",
"//test/extensions/filters/http/common:empty_http_filter_config_lib",
],
)

envoy_cc_test_library(
name = "encoder_recreate_stream_filter_lib",
srcs = [
"encoder_recreate_stream_filter.cc",
],
deps = [
":common_lib",
"//envoy/event:timer_interface",
"//envoy/http:filter_interface",
"//envoy/registry",
"//envoy/server:filter_config_interface",
"//source/common/router:string_accessor_lib",
"//source/extensions/filters/http/common:pass_through_filter_lib",
"//test/extensions/filters/http/common:empty_http_filter_config_lib",
],
)
41 changes: 41 additions & 0 deletions test/integration/filters/add_encode_metadata_filter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <chrono>
#include <string>

#include "envoy/event/timer.h"
#include "envoy/http/filter.h"
#include "envoy/registry/registry.h"
#include "envoy/server/filter_config.h"

#include "source/common/buffer/buffer_impl.h"
#include "source/extensions/filters/http/common/pass_through_filter.h"

#include "test/extensions/filters/http/common/empty_http_filter_config.h"
#include "test/integration/filters/common.h"

#include "gtest/gtest.h"

namespace Envoy {

// A filter add encoded metadata in encodeHeaders.
class AddEncodeMetadataFilter : public Http::PassThroughFilter {
public:
constexpr static char name[] = "add-metadata-encode-headers-filter";

Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override {
Http::MetadataMap metadata_map = {{"headers", "headers"}, {"duplicate", "duplicate"}};
Http::MetadataMapPtr metadata_map_ptr = std::make_unique<Http::MetadataMap>(metadata_map);
encoder_callbacks_->addEncodedMetadata(std::move(metadata_map_ptr));
return Http::FilterHeadersStatus::Continue;
}

Http::FilterDataStatus encodeData(Buffer::Instance&, bool) override {
return Http::FilterDataStatus::Continue;
}
};

constexpr char AddEncodeMetadataFilter::name[];
static Registry::RegisterFactory<SimpleFilterConfig<AddEncodeMetadataFilter>,
Server::Configuration::NamedHttpFilterConfigFactory>
register_;

} // namespace Envoy
55 changes: 55 additions & 0 deletions test/integration/filters/encoder_recreate_stream_filter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <chrono>
#include <string>

#include "envoy/event/timer.h"
#include "envoy/http/filter.h"
#include "envoy/registry/registry.h"
#include "envoy/server/filter_config.h"

#include "source/common/buffer/buffer_impl.h"
#include "source/common/router/string_accessor_impl.h"
#include "source/extensions/filters/http/common/pass_through_filter.h"

#include "test/extensions/filters/http/common/empty_http_filter_config.h"
#include "test/integration/filters/common.h"

#include "gtest/gtest.h"

namespace Envoy {

class EncoderRecreateStreamFilter : public Http::PassThroughFilter {
public:
constexpr static char name[] = "encoder-recreate-stream-filter";

Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override {
const auto* filter_state =
decoder_callbacks_->streamInfo().filterState()->getDataReadOnly<Router::StringAccessorImpl>(
"test_key");

if (filter_state != nullptr) {
return ::Envoy::Http::FilterHeadersStatus::Continue;
}

decoder_callbacks_->streamInfo().filterState()->setData(
"test_key", std::make_unique<Router::StringAccessorImpl>("test_value"),
StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::Request);

if (decoder_callbacks_->recreateStream(nullptr)) {
return ::Envoy::Http::FilterHeadersStatus::StopIteration;
}

return ::Envoy::Http::FilterHeadersStatus::Continue;
}

void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override {
decoder_callbacks_ = &callbacks;
}
};

// perform static registration
constexpr char EncoderRecreateStreamFilter::name[];
static Registry::RegisterFactory<SimpleFilterConfig<EncoderRecreateStreamFilter>,
Server::Configuration::NamedHttpFilterConfigFactory>
register_;

} // namespace Envoy

0 comments on commit 316e292

Please sign in to comment.