From bb02ae695153af5162d2b2618a6789f32179b97b Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 9 Mar 2021 14:49:48 -0500 Subject: [PATCH 01/52] enable protocol integration test Signed-off-by: Dan Zhang --- .../integration/quic_http_integration_test.cc | 79 +++----------- .../quic_protocol_integration_test.cc | 17 ++- test/integration/BUILD | 6 + test/integration/fake_upstream.cc | 4 +- test/integration/filters/BUILD | 1 + test/integration/filters/pause_filter.cc | 45 ++++++-- test/integration/http_integration.cc | 86 ++++++++++++++- test/integration/http_integration.h | 9 ++ test/integration/http_protocol_integration.cc | 16 ++- test/integration/http_protocol_integration.h | 9 +- test/integration/protocol_integration_test.cc | 86 ++++++++++++--- test/integration/utility.cc | 103 ++++++++++++++++-- test/integration/utility.h | 12 ++ 13 files changed, 369 insertions(+), 104 deletions(-) diff --git a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc index 0ac6c7e34ed7..f884cef33c5e 100644 --- a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc +++ b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc @@ -9,7 +9,6 @@ #include "test/config/utility.h" #include "test/integration/http_integration.h" -#include "test/integration/ssl_utility.h" #include "test/test_common/test_runtime.h" #include "test/test_common/utility.h" @@ -56,51 +55,7 @@ void updateResource(AtomicFileUpdater& updater, double pressure) { updater.update(absl::StrCat(pressure)); } -std::unique_ptr -createQuicClientTransportSocketFactory(const Ssl::ClientSslTransportOptions& options, Api::Api& api, - const std::string& san_to_match) { - std::string yaml_plain = R"EOF( - common_tls_context: - validation_context: - trusted_ca: - filename: "{{ test_rundir }}/test/config/integration/certs/cacert.pem" -)EOF"; - envoy::extensions::transport_sockets::quic::v3::QuicUpstreamTransport - quic_transport_socket_config; - auto* tls_context = quic_transport_socket_config.mutable_upstream_tls_context(); - TestUtility::loadFromYaml(TestEnvironment::substitute(yaml_plain), *tls_context); - auto* common_context = tls_context->mutable_common_tls_context(); - - if (options.alpn_) { - common_context->add_alpn_protocols("h3"); - } - if (options.san_) { - common_context->mutable_validation_context()->add_match_subject_alt_names()->set_exact( - san_to_match); - } - for (const std::string& cipher_suite : options.cipher_suites_) { - common_context->mutable_tls_params()->add_cipher_suites(cipher_suite); - } - if (!options.sni_.empty()) { - tls_context->set_sni(options.sni_); - } - - common_context->mutable_tls_params()->set_tls_minimum_protocol_version(options.tls_version_); - common_context->mutable_tls_params()->set_tls_maximum_protocol_version(options.tls_version_); - - envoy::config::core::v3::TransportSocket message; - message.mutable_typed_config()->PackFrom(quic_transport_socket_config); - auto& config_factory = Config::Utility::getAndCheckFactory< - Server::Configuration::UpstreamTransportSocketConfigFactory>(message); - NiceMock mock_factory_ctx; - ON_CALL(mock_factory_ctx, api()).WillByDefault(testing::ReturnRef(api)); - return std::unique_ptr( - static_cast( - config_factory - .createTransportSocketFactory(quic_transport_socket_config, mock_factory_ctx) - .release())); -} - +// A test that sets up its own client connection with customized quic version and connection ID. class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVersionTest { public: QuicHttpIntegrationTest() @@ -143,9 +98,12 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers getNextConnectionId(), server_addr_, conn_helper_, alarm_factory_, quic::ParsedQuicVersionVector{supported_versions_[0]}, local_addr, *dispatcher_, nullptr); quic_connection_ = connection.get(); + ASSERT(quic_transport_socket_factory_ != nullptr); auto session = std::make_unique( - quic_config_, supported_versions_, std::move(connection), server_id_, crypto_config_.get(), - &push_promise_index_, *dispatcher_, 0); + quic_config_, supported_versions_, std::move(connection), + quic::QuicServerId{transport_socket_factory_->clientContextConfig().serverNameIndication(), + static_cast(server_addr_->ip()->port()), false}, + crypto_config_.get(), &push_promise_index_, *dispatcher_, 0); session->Initialize(); return session; } @@ -160,14 +118,11 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers absl::optional http2_options) override { IntegrationCodecClientPtr codec = HttpIntegrationTest::makeRawHttpConnection(std::move(conn), http2_options); - if (codec->disconnected()) { - // Connection may get closed during version negotiation or handshake. - ENVOY_LOG(error, "Fail to connect to server with error: {}", - codec->connection()->transportFailureReason()); - } else { + if (!codec->disconnected()) { codec->setCodecClientCallbacks(client_codec_callback_); + EXPECT_EQ(transport_socket_factory_->clientContextConfig().serverNameIndication(), + codec->connection()->requestedServerName()); } - EXPECT_EQ(server_id_.host(), codec->connection()->requestedServerName()); return codec; } @@ -241,14 +196,16 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers updateResource(file_updater_1_, 0); updateResource(file_updater_2_, 0); HttpIntegrationTest::initialize(); + // Latch quic_transport_socket_factory_ which is instantiated in initialize(). + transport_socket_factory_ = + static_cast(quic_transport_socket_factory_.get()); registerTestServerPorts({"http"}); + + ASSERT(&transport_socket_factory_->clientContextConfig()); + crypto_config_ = std::make_unique(std::make_unique( - stats_store_, - createQuicClientTransportSocketFactory( - Ssl::ClientSslTransportOptions().setAlpn(true).setSan(true), *api_, san_to_match_) - ->clientContextConfig(), - timeSystem())); + stats_store_, transport_socket_factory_->clientContextConfig(), timeSystem())); } void testMultipleQuicConnections() { @@ -297,8 +254,6 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers protected: quic::QuicConfig quic_config_; - quic::QuicServerId server_id_{"lyft.com", 443, false}; - std::string san_to_match_{"spiffe://lyft.com/backend-team"}; quic::QuicClientPushPromiseIndex push_promise_index_; quic::ParsedQuicVersionVector supported_versions_; std::unique_ptr crypto_config_; @@ -307,12 +262,12 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers CodecClientCallbacksForTest client_codec_callback_; Network::Address::InstanceConstSharedPtr server_addr_; EnvoyQuicClientConnection* quic_connection_{nullptr}; - bool set_reuse_port_{false}; const std::string injected_resource_filename_1_; const std::string injected_resource_filename_2_; AtomicFileUpdater file_updater_1_; AtomicFileUpdater file_updater_2_; std::list designated_connection_ids_; + Quic::QuicClientTransportSocketFactory* transport_socket_factory_{nullptr}; }; INSTANTIATE_TEST_SUITE_P(QuicHttpIntegrationTests, QuicHttpIntegrationTest, diff --git a/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc b/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc index 019f2007f282..c43c0410250d 100644 --- a/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc +++ b/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc @@ -2,11 +2,22 @@ namespace Envoy { -// We do not yet run QUIC downstream tests. -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DownstreamProtocolIntegrationTest); +// This will run with HTTP/3 downstream, and HTTP/2 upstream. +INSTANTIATE_TEST_SUITE_P(Protocols, DownstreamProtocolIntegrationTest, + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( + {Http::CodecClient::Type::HTTP3}, + {FakeHttpConnection::Type::HTTP1, FakeHttpConnection::Type::HTTP2})), + HttpProtocolIntegrationTest::protocolTestParamsToString); + +// This will run with HTTP/3 downstream, and HTTP/2 upstream. +INSTANTIATE_TEST_SUITE_P(DownstreamProtocols, ProtocolIntegrationTest, + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( + {Http::CodecClient::Type::HTTP3}, + {FakeHttpConnection::Type::HTTP1, FakeHttpConnection::Type::HTTP2})), + HttpProtocolIntegrationTest::protocolTestParamsToString); // This will run with HTTP/1 and HTTP/2 downstream, and HTTP/3 upstream. -INSTANTIATE_TEST_SUITE_P(Protocols, ProtocolIntegrationTest, +INSTANTIATE_TEST_SUITE_P(UpstreamProtocols, ProtocolIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecClient::Type::HTTP1, Http::CodecClient::Type::HTTP2}, {FakeHttpConnection::Type::HTTP3})), diff --git a/test/integration/BUILD b/test/integration/BUILD index 23667952cc21..5be22277c135 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -570,6 +570,7 @@ envoy_cc_test_library( "//test/test_common:registry_lib", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", ], ) @@ -635,14 +636,19 @@ envoy_cc_test_library( "//source/common/common:assert_lib", "//source/common/common:utility_lib", "//source/common/http:codec_client_lib", + "//source/common/http/http3:quic_client_connection_factory_lib", "//source/common/stats:isolated_store_lib", + "//source/extensions/quic_listeners/quiche:quic_transport_socket_factory_lib", "//test/common/upstream:utility_lib", "//test/mocks/event:event_mocks", + "//test/mocks/server:transport_socket_factory_context_mocks", "//test/mocks/upstream:cluster_info_mocks", + "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", "//test/test_common:printers_lib", "//test/test_common:simulated_time_system_lib", "//test/test_common:test_time_lib", + "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", ], ) diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 55a03439adf4..f6fbd30c2051 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -345,7 +345,9 @@ FakeHttpConnection::FakeHttpConnection( codec_ = std::unique_ptr( Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) - .createQuicServerConnection(shared_connection_.connection(), *this)); + .createQuicServerConnection(shared_connection_.connection(), *this, + max_request_headers_kb, max_request_headers_count, + headers_with_underscores_action)); } shared_connection_.connection().addReadFilter( Network::ReadFilterSharedPtr{new ReadFilter(*this)}); diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 1d78984323f3..0e4f379ac838 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -182,6 +182,7 @@ envoy_cc_test_library( "//include/envoy/registry", "//source/common/network:connection_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", + "//source/extensions/quic_listeners/quiche:quic_filter_manager_connection_lib", "//test/extensions/filters/http/common:empty_http_filter_config_lib", ], ) diff --git a/test/integration/filters/pause_filter.cc b/test/integration/filters/pause_filter.cc index b7f9aa1a3c36..221474e0fe77 100644 --- a/test/integration/filters/pause_filter.cc +++ b/test/integration/filters/pause_filter.cc @@ -5,6 +5,7 @@ #include "common/network/connection_impl.h" #include "extensions/filters/http/common/pass_through_filter.h" +#include "extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h" #include "test/extensions/filters/http/common/empty_http_filter_config.h" @@ -29,8 +30,9 @@ class TestPauseFilter : public Http::PassThroughFilter { number_of_decode_calls_ref_++; // If this is the second stream to decode headers and we're at high watermark. force low // watermark state - if (number_of_decode_calls_ref_ == 2 && connection()->aboveHighWatermark()) { - connection()->onWriteBufferLowWatermark(); + if (number_of_decode_calls_ref_ == 2 && + decoder_callbacks_->connection()->aboveHighWatermark()) { + mockConnectionBelowLowWatermark(); } } return PassThroughFilter::decodeData(buf, end_stream); @@ -42,19 +44,46 @@ class TestPauseFilter : public Http::PassThroughFilter { number_of_encode_calls_ref_++; // If this is the first stream to encode headers and we're not at high watermark, force high // watermark state. - if (number_of_encode_calls_ref_ == 1 && !connection()->aboveHighWatermark()) { - connection()->onWriteBufferHighWatermark(); + if (number_of_encode_calls_ref_ == 1 && + !decoder_callbacks_->connection()->aboveHighWatermark()) { + mockConnectionAboveHighWatermark(); } } return PassThroughFilter::encodeData(buf, end_stream); } - Network::ConnectionImpl* connection() { + void mockConnectionAboveHighWatermark() { // As long as we're doing horrible things let's do *all* the horrible things. - // Assert the connection we have is a ConnectionImpl and const cast it so we - // can force watermark changes. + // Assert the connection we have is a ConnectionImpl or QuicFilterManagerConnectionImpl and + // const cast it so we can force watermark changes. auto conn_impl = dynamic_cast(decoder_callbacks_->connection()); - return const_cast(conn_impl); + if (conn_impl != nullptr) { + const_cast(conn_impl)->onWriteBufferHighWatermark(); + return; + } + // If transport protocol is QUIC, simulate connection buffer above watermark differently. + auto quic_connection = const_cast( + dynamic_cast( + decoder_callbacks_->connection())); + quic_connection->write_buffer_watermark_simulation_.checkHighWatermark( + quic_connection->write_buffer_watermark_simulation_.highWatermark() + 1u); + } + + void mockConnectionBelowLowWatermark() { + // As long as we're doing horrible things let's do *all* the horrible things. + // Assert the connection we have is a ConnectionImpl or QuicFilterManagerConnectionImpl and + // const cast it so we can force watermark changes. + auto conn_impl = dynamic_cast(decoder_callbacks_->connection()); + if (conn_impl != nullptr) { + const_cast(conn_impl)->onWriteBufferHighWatermark(); + return; + } + // If transport protocol is QUIC, simulate connection buffer below watermark differently. + auto quic_connection = const_cast( + dynamic_cast( + decoder_callbacks_->connection())); + quic_connection->write_buffer_watermark_simulation_.checkLowWatermark( + quic_connection->write_buffer_watermark_simulation_.highWatermark() / 2 - 1u); } absl::Mutex& encode_lock_; diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 1c046889e8f1..84ccb870c2c7 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -11,6 +11,7 @@ #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/event/dispatcher.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" +#include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "envoy/http/header_map.h" #include "envoy/network/address.h" #include "envoy/registry/registry.h" @@ -20,6 +21,7 @@ #include "common/common/fmt.h" #include "common/common/thread_annotations.h" #include "common/http/headers.h" +#include "common/http/http3/quic_client_connection_factory.h" #include "common/network/socket_option_impl.h" #include "common/network/utility.h" #include "common/protobuf/utility.h" @@ -32,6 +34,7 @@ #include "test/common/upstream/utility.h" #include "test/integration/autonomous_upstream.h" +#include "test/integration/ssl_utility.h" #include "test/integration/test_host_predicate_config.h" #include "test/integration/utility.h" #include "test/mocks/upstream/cluster_info.h" @@ -40,8 +43,21 @@ #include "test/test_common/registry.h" #include "absl/time/time.h" +#include "base_integration_test.h" #include "gtest/gtest.h" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/core/quic_utils.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + namespace Envoy { namespace { @@ -72,6 +88,7 @@ IntegrationCodecClient::IntegrationCodecClient( CodecClient::Type type) : CodecClientProd(type, std::move(conn), host_description, dispatcher, random), dispatcher_(dispatcher), callbacks_(*this), codec_callbacks_(*this) { + std::cerr << "=========== IntegrationCodecClient add connection callback " << &callbacks_ << "\n"; connection_->addConnectionCallbacks(callbacks_); setCodecConnectionCallbacks(codec_callbacks_); dispatcher.run(Event::Dispatcher::RunType::Block); @@ -212,6 +229,23 @@ void IntegrationCodecClient::ConnectionCallbacks::onEvent(Network::ConnectionEve } } +Network::ClientConnectionPtr HttpIntegrationTest::makeClientConnectionWithOptions( + uint32_t port, const Network::ConnectionSocket::OptionsSharedPtr& options) { + if (downstream_protocol_ <= Http::CodecClient::Type::HTTP2) { + return BaseIntegrationTest::makeClientConnectionWithOptions(port, options); + } + // Setting socket options is not supported for HTTP3. + ASSERT(!options); + Network::Address::InstanceConstSharedPtr server_addr = Network::Utility::resolveUrl( + fmt::format("udp://{}:{}", Network::Test::getLoopbackAddressUrlString(version_), port)); + Network::Address::InstanceConstSharedPtr local_addr = + Network::Test::getCanonicalLoopbackAddress(version_); + return Config::Utility::getAndCheckFactoryByName( + Http::QuicCodecNames::get().Quiche) + .createQuicNetworkConnection(server_addr, local_addr, *quic_transport_socket_factory_, + stats_store_, *dispatcher_, timeSystem()); +} + IntegrationCodecClientPtr HttpIntegrationTest::makeHttpConnection(uint32_t port) { return makeHttpConnection(makeClientConnection(port)); } @@ -232,8 +266,16 @@ IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection( Upstream::HostDescriptionConstSharedPtr host_description{Upstream::makeTestHostDescription( cluster, fmt::format("tcp://{}:80", Network::Test::getLoopbackAddressUrlString(version_)), timeSystem())}; - return std::make_unique(*dispatcher_, random_, std::move(conn), - host_description, downstream_protocol_); + // This call may fail in QUICHE because of INVALID_VERSION. QUIC connection doesn't support + // in-connection version negotiation. + auto codec = std::make_unique(*dispatcher_, random_, std::move(conn), + host_description, downstream_protocol_); + if (downstream_protocol_ == Http::CodecClient::Type::HTTP3 && codec->disconnected()) { + // Connection may get closed during version negotiation or handshake. + ENVOY_LOG(error, "Fail to connect to server with error: {}", + codec->connection()->transportFailureReason()); + } + return codec; } IntegrationCodecClientPtr @@ -275,6 +317,44 @@ void HttpIntegrationTest::useAccessLog( HttpIntegrationTest::~HttpIntegrationTest() { cleanupUpstreamAndDownstream(); } +void HttpIntegrationTest::initialize() { + if (downstream_protocol_ != Http::CodecClient::Type::HTTP3) { + return BaseIntegrationTest::initialize(); + } + NiceMock mock_factory_ctx; + ON_CALL(mock_factory_ctx, api()).WillByDefault(testing::ReturnRef(*api_)); + + quic_transport_socket_factory_ = + IntegrationUtil::createQuicClientTransportSocketFactory(mock_factory_ctx, san_to_match_); + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + envoy::extensions::transport_sockets::quic::v3::QuicDownstreamTransport + quic_transport_socket_config; + auto tls_context = quic_transport_socket_config.mutable_downstream_tls_context(); + ConfigHelper::initializeTls(ConfigHelper::ServerSslOptions().setRsaCert(true).setTlsV13(true), + *tls_context->mutable_common_tls_context()); + for (auto& listener : *bootstrap.mutable_static_resources()->mutable_listeners()) { + if (listener.udp_listener_config().udp_listener_name() == "quiche_quic_listener") { + auto* filter_chain = listener.mutable_filter_chains(0); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->mutable_typed_config()->PackFrom(quic_transport_socket_config); + + listener.set_reuse_port(set_reuse_port_); + } + } + }); + + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + hcm.mutable_drain_timeout()->clear_seconds(); + hcm.mutable_drain_timeout()->set_nanos(500 * 1000 * 1000); + EXPECT_EQ(hcm.codec_type(), envoy::extensions::filters::network::http_connection_manager:: + v3::HttpConnectionManager::HTTP3); + }); + BaseIntegrationTest::initialize(); + registerTestServerPorts({"http"}); +} + void HttpIntegrationTest::setDownstreamProtocol(Http::CodecClient::Type downstream_protocol) { downstream_protocol_ = downstream_protocol; config_helper_.setClientCodec(typeToCodecType(downstream_protocol_)); @@ -497,7 +577,6 @@ void HttpIntegrationTest::testRouterNotFound() { void HttpIntegrationTest::testRouterNotFoundWithBody() { config_helper_.setDefaultHostAndRoute("foo.com", "/found"); initialize(); - BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( lookupPort("http"), "POST", "/notfound", "foo", downstream_protocol_, version_); ASSERT_TRUE(response->complete()); @@ -846,6 +925,7 @@ void HttpIntegrationTest::testEnvoyHandling100Continue(bool additional_continue_ ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); // Send the rest of the request. + std::cerr << "============== finish sendData"; codec_client_->sendData(*request_encoder_, 10, true); ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); // Verify the Expect header is stripped. diff --git a/test/integration/http_integration.h b/test/integration/http_integration.h index 5ee7de657997..791e7d7c7bc3 100644 --- a/test/integration/http_integration.h +++ b/test/integration/http_integration.h @@ -105,6 +105,8 @@ class HttpIntegrationTest : public BaseIntegrationTest { const std::string& config = ConfigHelper::httpProxyConfig()); ~HttpIntegrationTest() override; + void initialize() override; + protected: void useAccessLog(absl::string_view format = "", std::vector formatters = {}); @@ -114,6 +116,9 @@ class HttpIntegrationTest : public BaseIntegrationTest { virtual IntegrationCodecClientPtr makeRawHttpConnection( Network::ClientConnectionPtr&& conn, absl::optional http2_options); + // Makes a downstream network connection object based on client codec version. + Network::ClientConnectionPtr makeClientConnectionWithOptions( + uint32_t port, const Network::ConnectionSocket::OptionsSharedPtr& options) override; // Makes a http connection object with asserting a connected state. IntegrationCodecClientPtr makeHttpConnection(Network::ClientConnectionPtr&& conn); @@ -260,6 +265,10 @@ class HttpIntegrationTest : public BaseIntegrationTest { uint32_t max_request_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT}; std::string access_log_name_; testing::NiceMock random_; + + bool set_reuse_port_{false}; + std::string san_to_match_{"spiffe://lyft.com/backend-team"}; + Network::TransportSocketFactoryPtr quic_transport_socket_factory_; }; // Helper class for integration tests using raw HTTP/2 frames diff --git a/test/integration/http_protocol_integration.cc b/test/integration/http_protocol_integration.cc index 9420f33f1fb6..11c182fe70ff 100644 --- a/test/integration/http_protocol_integration.cc +++ b/test/integration/http_protocol_integration.cc @@ -30,12 +30,22 @@ absl::string_view upstreamToString(FakeHttpConnection::Type type) { return "UnknownUpstream"; } +absl::string_view downstreamToString(Http::CodecClient::Type type) { + switch (type) { + case Http::CodecClient::Type::HTTP1: + return "HttpDownstream_"; + case Http::CodecClient::Type::HTTP2: + return "Http2Downstream_"; + case Http::CodecClient::Type::HTTP3: + return "Http3Downstream_"; + } + return "UnknownDownstream"; +} + std::string HttpProtocolIntegrationTest::protocolTestParamsToString( const ::testing::TestParamInfo& params) { return absl::StrCat((params.param.version == Network::Address::IpVersion::v4 ? "IPv4_" : "IPv6_"), - (params.param.downstream_protocol == Http::CodecClient::Type::HTTP2 - ? "Http2Downstream_" - : "HttpDownstream_"), + downstreamToString(params.param.downstream_protocol), upstreamToString(params.param.upstream_protocol)); } diff --git a/test/integration/http_protocol_integration.h b/test/integration/http_protocol_integration.h index 6233e54865f1..911c12661642 100644 --- a/test/integration/http_protocol_integration.h +++ b/test/integration/http_protocol_integration.h @@ -31,9 +31,9 @@ class HttpProtocolIntegrationTest : public testing::TestWithParam& p); HttpProtocolIntegrationTest() - : HttpIntegrationTest(GetParam().downstream_protocol, GetParam().version) {} + : HttpIntegrationTest(GetParam().downstream_protocol, GetParam().version, + GetParam().downstream_protocol == Http::CodecClient::Type::HTTP3 + ? ConfigHelper::quicHttpProxyConfig() + : ConfigHelper::httpProxyConfig()) {} void SetUp() override { setDownstreamProtocol(GetParam().downstream_protocol); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index c8bdb3ed8010..b42610966004 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -59,6 +59,11 @@ void setDoNotValidateRouteConfig( return; \ } +#define EXCLUDE_DOWNSTREAM_HTTP3 \ + if (downstreamProtocol() == Http::CodecClient::Type::HTTP3) { \ + return; \ + } + TEST_P(ProtocolIntegrationTest, TrailerSupportHttp1) { config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); @@ -188,6 +193,10 @@ name: health_check // Verifies behavior for https://github.com/envoyproxy/envoy/pull/11248 TEST_P(ProtocolIntegrationTest, AddBodyToRequestAndWaitForIt) { + // QUICHE can't guarantee headers and FIN to be delivered together, so + // headers-only request can't be detected at L7 filters. + EXCLUDE_DOWNSTREAM_HTTP3; + // filters are prepended, so add them in reverse order config_helper_.addFilter(R"EOF( name: wait-for-whole-request-and-response-filter @@ -235,6 +244,11 @@ TEST_P(ProtocolIntegrationTest, AddBodyToResponseAndWaitForIt) { } TEST_P(ProtocolIntegrationTest, ContinueHeadersOnlyInjectBodyFilter) { + // Headers-only request is translated into headers and empty body by QUICHE + // because FIN bit in IETF QUIC stream is decoupled with http HEADERS frame. + // decodeHeaders() is always called with end_stream = false if QUICHE is + // using IETF version. + EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; config_helper_.addFilter(R"EOF( name: continue-headers-only-inject-body-filter @@ -312,7 +326,7 @@ name: add-trailers-filter } EXPECT_TRUE(response->complete()); EXPECT_EQ("503", response->headers().getStatusValue()); - if (downstream_protocol_ == Http::CodecClient::Type::HTTP2) { + if (downstream_protocol_ >= Http::CodecClient::Type::HTTP2) { EXPECT_EQ("encode", response->trailers()->getGrpcMessageValue()); } } @@ -370,6 +384,9 @@ TEST_P(DownstreamProtocolIntegrationTest, DownstreamRequestWithFaultyFilter) { } TEST_P(DownstreamProtocolIntegrationTest, FaultyFilterWithConnect) { + // TODO(danzh) re-enable after plumbing through http2 option + // "allow_connect". + EXCLUDE_DOWNSTREAM_HTTP3; // Faulty filter that removed host in a CONNECT request. config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -431,7 +448,6 @@ TEST_P(ProtocolIntegrationTest, LongHeaderValueWithSpaces) { // reads, which the buffer rounds up to about 20KB when allocating slices in // Buffer::OwnedImpl::reserve(). const std::string long_header_value_with_inner_lws = "v" + std::string(32 * 1024, ' ') + "v"; - initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); auto response = codec_client_->makeHeaderOnlyRequest( @@ -731,6 +747,10 @@ TEST_P(DownstreamProtocolIntegrationTest, RetryAttemptCountHeader) { // The retry priority will always target P1, which would otherwise never be hit due to P0 being // healthy. TEST_P(DownstreamProtocolIntegrationTest, RetryPriority) { + if (upstreamProtocol() == FakeHttpConnection::Type::HTTP2 && + downstreamProtocol() == Http::CodecClient::Type::HTTP3) { + return; + } const Upstream::HealthyLoad healthy_priority_load({0u, 100u}); const Upstream::DegradedLoad degraded_priority_load({0u, 100u}); NiceMock retry_priority(healthy_priority_load, @@ -940,7 +960,7 @@ TEST_P(DownstreamProtocolIntegrationTest, HittingDecoderFilterLimit) { // the 413-and-connection-close may be sent while the body is still being // sent, resulting in a write error and the connection being closed before the // response is read. - if (downstream_protocol_ == Http::CodecClient::Type::HTTP2) { + if (downstream_protocol_ >= Http::CodecClient::Type::HTTP2) { ASSERT_TRUE(response->complete()); } if (response->complete()) { @@ -1081,10 +1101,22 @@ TEST_P(ProtocolIntegrationTest, HeadersWithUnderscoresDropped) { EXPECT_EQ("200", response->headers().getStatusValue()); EXPECT_THAT(response->headers(), HeaderHasValueRef("bar_baz", "fooz")); Stats::Store& stats = test_server_->server().stats(); - std::string stat_name = (downstreamProtocol() == Http::CodecClient::Type::HTTP1) - ? "http1.dropped_headers_with_underscores" - : "http2.dropped_headers_with_underscores"; - EXPECT_EQ(1L, TestUtility::findCounter(stats, stat_name)->value()); + std::string stat_name; + switch (downstreamProtocol()) { + case Http::CodecClient::Type::HTTP1: + stat_name = "http1.dropped_headers_with_underscores"; + break; + case Http::CodecClient::Type::HTTP2: + stat_name = "http2.dropped_headers_with_underscores"; + break; + case Http::CodecClient::Type::HTTP3: + break; + default: + RELEASE_ASSERT(false, fmt::format("Unknown downstream protocol {}", downstream_protocol_)); + }; + if (downstream_protocol_ != Http::CodecClient::Type::HTTP3) { + EXPECT_EQ(1L, TestUtility::findCounter(stats, stat_name)->value()); + } } // Verify that by default headers with underscores in their names remain in both requests and @@ -1192,10 +1224,10 @@ TEST_P(ProtocolIntegrationTest, 304WithBody) { test_server_->waitForCounterGe("cluster.cluster_0.upstream_cx_protocol_error", 1); } - // Only for HTTP/2, where streams are ended with an explicit end-stream so we + // Only for HTTP/2 and Http/3, where streams are ended with an explicit end-stream so we // can differentiate between 304-with-advertised-but-absent-body and // 304-with-body, is there a protocol error on the active stream. - if (downstream_protocol_ == Http::CodecClient::Type::HTTP2 && + if (downstream_protocol_ >= Http::CodecClient::Type::HTTP2 && upstreamProtocol() >= FakeHttpConnection::Type::HTTP2) { response->waitForReset(); } @@ -1273,7 +1305,6 @@ TEST_P(ProtocolIntegrationTest, MissingStatus) { // Validate that lots of tiny cookies doesn't cause a DoS (single cookie header). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { initialize(); - codec_client_ = makeHttpConnection(lookupPort("http")); Http::TestRequestHeaderMapImpl request_headers{{":method", "POST"}, {":path", "/test/long/url"}, @@ -1346,6 +1377,8 @@ TEST_P(DownstreamProtocolIntegrationTest, InvalidContentLength) { } TEST_P(DownstreamProtocolIntegrationTest, InvalidContentLengthAllowed) { + // TODO(danzh) Add override_stream_error_on_invalid_http_message to http3 protocol options. + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addConfigModifier( [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1385,6 +1418,7 @@ TEST_P(DownstreamProtocolIntegrationTest, InvalidContentLengthAllowed) { } TEST_P(DownstreamProtocolIntegrationTest, MultipleContentLengths) { + initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); auto encoder_decoder = @@ -1407,6 +1441,8 @@ TEST_P(DownstreamProtocolIntegrationTest, MultipleContentLengths) { // TODO(PiotrSikora): move this HTTP/2 only variant to http2_integration_test.cc. TEST_P(DownstreamProtocolIntegrationTest, MultipleContentLengthsAllowed) { + // override_stream_error_on_invalid_http_message not supported yet. + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addConfigModifier( [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1416,6 +1452,7 @@ TEST_P(DownstreamProtocolIntegrationTest, MultipleContentLengthsAllowed) { }); initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "POST"}, @@ -1465,6 +1502,7 @@ name: local-reply-during-encode } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlRejected) { + EXCLUDE_DOWNSTREAM_HTTP3 // Send one 95 kB URL with limit 60 kB headers. testLargeRequestUrl(95, 60); } @@ -1475,6 +1513,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersRejected) { + EXCLUDE_DOWNSTREAM_HTTP3 // Send one 95 kB header with limit 60 kB and 100 headers. testLargeRequestHeaders(95, 1, 60, 100); } @@ -1485,6 +1524,8 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersRejected) { + // QUICHE doesn't limit number of headers. + EXCLUDE_DOWNSTREAM_HTTP3 // Send 101 empty headers with limit 60 kB and 100 headers. testLargeRequestHeaders(0, 101, 60, 80); } @@ -1495,6 +1536,8 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersRejected) { + // QUICHE doesn't limit number of headers. + EXCLUDE_DOWNSTREAM_HTTP3 // Default header (and trailer) count limit is 100. config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); @@ -1554,6 +1597,7 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersAccepted) { // This test uses an Http::HeaderMapImpl instead of an Http::TestHeaderMapImpl to avoid // time-consuming byte size validations that will cause this test to timeout. TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersTimeout) { + EXCLUDE_DOWNSTREAM_HTTP3 // Set timeout for 5 seconds, and ensure that a request with 10k+ headers can be sent. testManyRequestHeaders(std::chrono::milliseconds(5000)); } @@ -1564,6 +1608,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersRejected) { + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); testLargeRequestTrailers(66, 60); } @@ -1571,6 +1616,9 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersRejected) { // This test uses an Http::HeaderMapImpl instead of an Http::TestHeaderMapImpl to avoid // time-consuming byte size verification that will cause this test to timeout. TEST_P(DownstreamProtocolIntegrationTest, ManyTrailerHeaders) { + // Enable after setting QUICHE max_inbound_header_list_size_ from HCM + // config. + EXCLUDE_DOWNSTREAM_HTTP3 max_request_headers_kb_ = 96; max_request_headers_count_ = 20005; @@ -1618,9 +1666,9 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyTrailerHeaders) { // ------------------------------------------ // H1 H1 Envoy will reject (HTTP/1 codec behavior) // H1 H2 Envoy will reject (HTTP/1 codec behavior) -// H2 H1 Envoy will forward but backend will reject (HTTP/1 +// H2, H3 H1 Envoy will forward but backend will reject (HTTP/1 // codec behavior) -// H2 H2 Success +// H2, H3 H2 Success TEST_P(ProtocolIntegrationTest, LargeRequestMethod) { // There will be no upstream connections for HTTP/1 downstream, we need to // test the full mesh regardless. @@ -1643,7 +1691,7 @@ TEST_P(ProtocolIntegrationTest, LargeRequestMethod) { EXPECT_TRUE(response->complete()); EXPECT_EQ("400", response->headers().getStatusValue()); } else { - ASSERT(downstreamProtocol() == Http::CodecClient::Type::HTTP2); + ASSERT(downstreamProtocol() >= Http::CodecClient::Type::HTTP2); if (upstreamProtocol() == FakeHttpConnection::Type::HTTP1) { auto response = codec_client_->makeHeaderOnlyRequest(request_headers); ASSERT_TRUE( @@ -1663,6 +1711,9 @@ TEST_P(ProtocolIntegrationTest, LargeRequestMethod) { // Tests StopAllIterationAndBuffer. Verifies decode-headers-return-stop-all-filter calls decodeData // once after iteration is resumed. TEST_P(DownstreamProtocolIntegrationTest, TestDecodeHeadersReturnsStopAll) { + // Enable after setting QUICHE stream initial flow control window from http2 + // options. + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: call-decodedata-once-filter )EOF"); @@ -1714,6 +1765,7 @@ name: passthrough-filter // Tests StopAllIterationAndWatermark. decode-headers-return-stop-all-watermark-filter sets buffer // limit to 100. Verifies data pause when limit is reached, and resume after iteration continues. TEST_P(DownstreamProtocolIntegrationTest, TestDecodeHeadersReturnsStopAllWatermark) { + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: decode-headers-return-stop-all-filter )EOF"); @@ -1772,6 +1824,9 @@ name: passthrough-filter // Test two filters that return StopAllIterationAndBuffer back-to-back. TEST_P(DownstreamProtocolIntegrationTest, TestTwoFiltersDecodeHeadersReturnsStopAll) { + // TODO(danzh) Re-enable after codec buffer can be set according to http2 + // options. + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: decode-headers-return-stop-all-filter )EOF"); @@ -1820,6 +1875,8 @@ name: passthrough-filter // Tests encodeHeaders() returns StopAllIterationAndBuffer. TEST_P(DownstreamProtocolIntegrationTest, TestEncodeHeadersReturnsStopAll) { + // TODO(danzh) Re-enable after codec buffer can be set according to quic options. + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: encode-headers-return-stop-all-filter )EOF"); @@ -1852,6 +1909,7 @@ name: encode-headers-return-stop-all-filter // Tests encodeHeaders() returns StopAllIterationAndWatermark. TEST_P(DownstreamProtocolIntegrationTest, TestEncodeHeadersReturnsStopAllWatermark) { + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: encode-headers-return-stop-all-filter )EOF"); @@ -2138,6 +2196,8 @@ TEST_P(DownstreamProtocolIntegrationTest, ConnectStreamRejection) { {":method", "CONNECT"}, {":path", "/"}, {":authority", "host"}}); response->waitForReset(); + // TODO(danzh) plumb through stream_error_on_invalid_http_message. + EXCLUDE_DOWNSTREAM_HTTP3; EXPECT_FALSE(codec_client_->disconnected()); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 24b71b58ceff..d6b3bf5dd7a1 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -6,21 +6,27 @@ #include #include "envoy/event/dispatcher.h" +#include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "envoy/network/connection.h" #include "common/api/api_impl.h" #include "common/buffer/buffer_impl.h" #include "common/common/assert.h" #include "common/common/fmt.h" +#include "common/config/utility.h" #include "common/http/header_map_impl.h" #include "common/http/headers.h" +#include "common/http/http3/quic_client_connection_factory.h" +#include "common/http/http3/well_known_names.h" #include "common/network/utility.h" #include "common/upstream/upstream_impl.h" #include "test/common/upstream/utility.h" #include "test/mocks/common.h" +#include "test/mocks/server/transport_socket_factory_context.h" #include "test/mocks/stats/mocks.h" #include "test/mocks/upstream/cluster_info.h" +#include "test/test_common/environment.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" @@ -75,12 +81,71 @@ void BufferingStreamDecoder::onResetStream(Http::StreamResetReason, absl::string ADD_FAILURE(); } +struct ConnectionCallbacks : public Network::ConnectionCallbacks { + ConnectionCallbacks(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {} + + // Network::ConnectionCallbacks + void onEvent(Network::ConnectionEvent event) override { + if (event == Network::ConnectionEvent::Connected) { + connected_ = true; + dispatcher_.exit(); + } else if (event == Network::ConnectionEvent::RemoteClose) { + dispatcher_.exit(); + } else { + if (!connected_) { + // Before handshake gets established, any connection failure should exit the loop. I.e. a + // QUIC connection may fail of INVALID_VERSION if both this client doesn't support any of + // the versions the server advertised before handshake established. In this case the + // connection is closed locally and this is in a blocking event loop. + dispatcher_.exit(); + } + } + } + + void onAboveWriteBufferHighWatermark() override {} + void onBelowWriteBufferLowWatermark() override {} + + Event::Dispatcher& dispatcher_; + bool connected_{false}; +}; + +Network::TransportSocketFactoryPtr IntegrationUtil::createQuicClientTransportSocketFactory( + Server::Configuration::TransportSocketFactoryContext& context, + const std::string& san_to_match) { + std::string yaml_plain = R"EOF( + common_tls_context: + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/config/integration/certs/cacert.pem" +)EOF"; + envoy::extensions::transport_sockets::quic::v3::QuicUpstreamTransport + quic_transport_socket_config; + auto* tls_context = quic_transport_socket_config.mutable_upstream_tls_context(); + TestUtility::loadFromYaml(TestEnvironment::substitute(yaml_plain), *tls_context); + auto* common_context = tls_context->mutable_common_tls_context(); + + common_context->add_alpn_protocols("h3"); + common_context->mutable_validation_context()->add_match_subject_alt_names()->set_exact( + san_to_match); + tls_context->set_sni("lyft.com"); + + common_context->mutable_tls_params()->set_tls_minimum_protocol_version( + envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLS_AUTO); + common_context->mutable_tls_params()->set_tls_maximum_protocol_version( + envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLS_AUTO); + + envoy::config::core::v3::TransportSocket message; + message.mutable_typed_config()->PackFrom(quic_transport_socket_config); + auto& config_factory = Config::Utility::getAndCheckFactory< + Server::Configuration::UpstreamTransportSocketConfigFactory>(message); + return config_factory.createTransportSocketFactory(quic_transport_socket_config, context); +} + BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPtr& addr, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, const std::string& host, const std::string& content_type) { - NiceMock mock_stats_store; NiceMock random; Event::GlobalTimeSystem time_system; @@ -88,14 +153,36 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt Api::Impl api(Thread::threadFactoryForTest(), mock_stats_store, time_system, Filesystem::fileSystemForTest(), random_generator); Event::DispatcherPtr dispatcher(api.allocateDispatcher("test_thread")); + ConnectionCallbacks connection_callbacks(*dispatcher); std::shared_ptr cluster{new NiceMock()}; - Upstream::HostDescriptionConstSharedPtr host_description{ - Upstream::makeTestHostDescription(cluster, "tcp://127.0.0.1:80", time_system)}; - Http::CodecClientProd client( - type, - dispatcher->createClientConnection(addr, Network::Address::InstanceConstSharedPtr(), - Network::Test::createRawBufferSocket(), nullptr), - host_description, *dispatcher, random); + Upstream::HostDescriptionConstSharedPtr host_description{Upstream::makeTestHostDescription( + cluster, + fmt::format("{}://127.0.0.1:80", (type == Http::CodecClient::Type::HTTP3 ? "udp" : "tcp")), + time_system)}; + + NiceMock mock_factory_ctx; + ON_CALL(mock_factory_ctx, api()).WillByDefault(testing::ReturnRef(api)); + Network::TransportSocketFactoryPtr transport_socket_factory = + createQuicClientTransportSocketFactory(mock_factory_ctx, "spiffe://lyft.com/backend-team"); + Network::ClientConnectionPtr connection = + (type == Http::CodecClient::Type::HTTP3 + ? Config::Utility::getAndCheckFactoryByName( + Http::QuicCodecNames::get().Quiche) + .createQuicNetworkConnection(addr, Network::Address::InstanceConstSharedPtr(), + *transport_socket_factory, mock_stats_store, + *dispatcher, time_system) + : dispatcher->createClientConnection(addr, Network::Address::InstanceConstSharedPtr(), + Network::Test::createRawBufferSocket(), nullptr)); + if (type == Http::CodecClient::Type::HTTP3) { + connection->addConnectionCallbacks(connection_callbacks); + } + Http::CodecClientProd client(type, std::move(connection), host_description, *dispatcher, random); + + if (type == Http::CodecClient::Type::HTTP3) { + // Quic connection needs to finish handshake. + dispatcher->run(Event::Dispatcher::RunType::Block); + } + BufferingStreamDecoderPtr response(new BufferingStreamDecoder([&]() -> void { client.close(); dispatcher->exit(); diff --git a/test/integration/utility.h b/test/integration/utility.h index afe3df9a3de2..a9cf91c48126 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -9,6 +9,7 @@ #include "envoy/http/codec.h" #include "envoy/http/header_map.h" #include "envoy/network/filter.h" +#include "envoy/server/factory_context.h" #include "common/common/assert.h" #include "common/common/utility.h" @@ -184,6 +185,17 @@ class IntegrationUtil { const std::string& body, Http::CodecClient::Type type, Network::Address::IpVersion ip_version, const std::string& host = "host", const std::string& content_type = ""); + + /** + * Create transport socket factory for Quic client transport socket. + * @param context supplies the port to connect to on localhost. + * @param san_to_match configs |context| to match Subject Alternative Name during certificate + * verification. + * @return TransportSocketFactoryPtr the client transport socket factory. + */ + static Network::TransportSocketFactoryPtr createQuicClientTransportSocketFactory( + Server::Configuration::TransportSocketFactoryContext& context, + const std::string& san_to_match); }; // A set of connection callbacks which tracks connection state. From 09fc660cb9528d2c5f211a02d8b702b22406dd87 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 9 Mar 2021 15:12:39 -0500 Subject: [PATCH 02/52] visibility Signed-off-by: Dan Zhang --- source/extensions/quic_listeners/quiche/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD index da68122404ad..6596a2b2cade 100644 --- a/source/extensions/quic_listeners/quiche/BUILD +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -179,6 +179,7 @@ envoy_cc_library( srcs = ["quic_filter_manager_connection_impl.cc"], hdrs = ["quic_filter_manager_connection_impl.h"], tags = ["nofips"], +visibility = ["//test:__subpackages__"], deps = [ ":envoy_quic_connection_lib", ":envoy_quic_simulated_watermark_buffer_lib", @@ -387,6 +388,7 @@ envoy_cc_extension( "//source/server:__subpackages__", ], security_posture = "unknown", +visibility = ["//test:__subpackages__"], tags = ["nofips"], deps = [ "//include/envoy/network:transport_socket_interface", From 6ed3c94db342d30a9f68875507e3186937868317 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 10 Mar 2021 01:53:40 -0500 Subject: [PATCH 03/52] make protocol test pass Signed-off-by: Dan Zhang --- source/extensions/quic_listeners/quiche/BUILD | 4 ++-- .../quic_listeners/quiche/active_quic_listener.cc | 1 + .../quiche/envoy_quic_client_stream.cc | 14 +++++++++++++- .../quiche/envoy_quic_server_stream.cc | 12 +++++++++--- .../quic_listeners/quiche/envoy_quic_utils.cc | 8 ++++++++ .../quic_listeners/quiche/envoy_quic_utils.h | 5 ++++- .../quiche/platform/quiche_flags_impl.cc | 5 +++-- .../quiche/quic_filter_manager_connection_impl.h | 7 +++++++ test/integration/fake_upstream.cc | 4 +--- test/integration/protocol_integration_test.cc | 14 +++++++++++++- 10 files changed, 61 insertions(+), 13 deletions(-) diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD index 6596a2b2cade..850c7c2c0b5d 100644 --- a/source/extensions/quic_listeners/quiche/BUILD +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -179,7 +179,7 @@ envoy_cc_library( srcs = ["quic_filter_manager_connection_impl.cc"], hdrs = ["quic_filter_manager_connection_impl.h"], tags = ["nofips"], -visibility = ["//test:__subpackages__"], + visibility = ["//test:__subpackages__"], deps = [ ":envoy_quic_connection_lib", ":envoy_quic_simulated_watermark_buffer_lib", @@ -388,8 +388,8 @@ envoy_cc_extension( "//source/server:__subpackages__", ], security_posture = "unknown", -visibility = ["//test:__subpackages__"], tags = ["nofips"], + visibility = ["//test:__subpackages__"], deps = [ "//include/envoy/network:transport_socket_interface", "//include/envoy/server:transport_socket_config_interface", diff --git a/source/extensions/quic_listeners/quiche/active_quic_listener.cc b/source/extensions/quic_listeners/quiche/active_quic_listener.cc index 7a3843865246..805ce5a1cc62 100644 --- a/source/extensions/quic_listeners/quiche/active_quic_listener.cc +++ b/source/extensions/quic_listeners/quiche/active_quic_listener.cc @@ -39,6 +39,7 @@ ActiveQuicListener::ActiveQuicListener( &listener_config), dispatcher_(dispatcher), version_manager_(quic::CurrentSupportedVersions()), kernel_worker_routing_(kernel_worker_routing) { + SetQuicReloadableFlag(quic_single_ack_in_packet2, true); if (Runtime::LoaderSingleton::getExisting()) { enabled_.emplace(Runtime::FeatureFlag(enabled, Runtime::LoaderSingleton::get())); } diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_client_stream.cc b/source/extensions/quic_listeners/quiche/envoy_quic_client_stream.cc index d57840cda4e4..1664e68ca0ed 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_client_stream.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_client_stream.cc @@ -62,7 +62,19 @@ Http::Status EnvoyQuicClientStream::encodeHeaders(const Http::RequestHeaderMap& ? static_cast(this) : (dynamic_cast(session())->headers_stream()); const uint64_t bytes_to_send_old = writing_stream->BufferedDataBytes(); - WriteHeaders(envoyHeadersToSpdyHeaderBlock(headers), end_stream, nullptr); + auto spdy_headers = envoyHeadersToSpdyHeaderBlock(headers); + if (headers.Method() && headers.Method()->value() == "CONNECT") { + // It is a bytestream connect and should have :path and :protocol set accordingly + // As HTTP/1.1 does not require a path for CONNECT, we may have to add one + // if shifting codecs. For now, default to "/" - this can be made + // configurable if necessary. + // https://tools.ietf.org/html/draft-kinnear-httpbis-http2-transport-02 + spdy_headers[":protocol"] = Http::Headers::get().ProtocolValues.Bytestream; + if (!headers.Path()) { + spdy_headers[":path"] = "/"; + } + } + WriteHeaders(std::move(spdy_headers), end_stream, nullptr); local_end_stream_ = end_stream; const uint64_t bytes_to_send_new = writing_stream->BufferedDataBytes(); ASSERT(bytes_to_send_old <= bytes_to_send_new); diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_server_stream.cc b/source/extensions/quic_listeners/quiche/envoy_quic_server_stream.cc index 99dc7a70ddf0..602358d1dff8 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_server_stream.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_server_stream.cc @@ -26,6 +26,7 @@ #include "common/buffer/buffer_impl.h" #include "common/http/header_map_impl.h" #include "common/common/assert.h" +#include "common/http/header_utility.h" namespace Envoy { namespace Quic { @@ -160,9 +161,14 @@ void EnvoyQuicServerStream::OnInitialHeadersComplete(bool fin, size_t frame_len, if (fin) { end_stream_decoded_ = true; } - request_decoder_->decodeHeaders( - quicHeadersToEnvoyHeaders(header_list), - /*end_stream=*/fin); + std::unique_ptr headers = + quicHeadersToEnvoyHeaders(header_list); + if (!Http::HeaderUtility::authorityIsValid(headers->Host()->value().getStringView())) { + stream_delegate()->OnStreamError(quic::QUIC_HTTP_FRAME_ERROR, "Invalid headers"); + return; + } + request_decoder_->decodeHeaders(std::move(headers), + /*end_stream=*/fin); ConsumeHeaderList(); } diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc b/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc index abb70d9093f2..f0f658eb5585 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc @@ -4,6 +4,7 @@ #include "envoy/config/core/v3/base.pb.h" #include "common/network/socket_option_factory.h" +#include "common/network/utility.h" namespace Envoy { namespace Quic { @@ -118,6 +119,13 @@ Network::ConnectionSocketPtr createConnectionSocket(Network::Address::InstanceConstSharedPtr& peer_addr, Network::Address::InstanceConstSharedPtr& local_addr, const Network::ConnectionSocket::OptionsSharedPtr& options) { + if (local_addr == nullptr) { + if (peer_addr->ip()->ipv4() != nullptr) { + local_addr = Network::Utility::getCanonicalIpv4LoopbackAddress(); + } else { + local_addr = Network::Utility::getIpv6LoopbackAddress(); + } + } auto connection_socket = std::make_unique( Network::Socket::Type::Datagram, local_addr, peer_addr); connection_socket->addOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions()); diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_utils.h b/source/extensions/quic_listeners/quiche/envoy_quic_utils.h index 2e9c247d4722..ef68543df62d 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_utils.h +++ b/source/extensions/quic_listeners/quiche/envoy_quic_utils.h @@ -61,7 +61,10 @@ std::unique_ptr spdyHeaderBlockToEnvoyHeaders(const spdy::SpdyHeaderBlock& he for (auto entry : header_block) { // TODO(danzh): Avoid temporary strings and addCopy() with string_view. std::string key(entry.first); - headers->addCopy(Http::LowerCaseString(key), entry.second); + std::vector values = absl::StrSplit(entry.second, '\0'); + for (const absl::string_view& value : values) { + headers->addCopy(Http::LowerCaseString(key), value); + } } return headers; } diff --git a/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc b/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc index 78fd70d6cd9f..aad20c06f72c 100644 --- a/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc +++ b/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc @@ -45,7 +45,9 @@ FlagRegistry& FlagRegistry::getInstance() { return *instance; } -FlagRegistry::FlagRegistry() : flags_(makeFlagMap()) {} +FlagRegistry::FlagRegistry() : flags_(makeFlagMap()) { + FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2->setValue(true); +} void FlagRegistry::resetFlags() const { for (auto& kv : flags_) { @@ -134,7 +136,6 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_http2_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_http2_testonly_default_true, true) QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_true, true) - #undef QUIC_FLAG #define STRINGIFY(X) #X diff --git a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h index 6d07c2d07d0e..3e3eedba3bbd 100644 --- a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h +++ b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h @@ -12,6 +12,10 @@ #include "extensions/quic_listeners/quiche/envoy_quic_simulated_watermark_buffer.h" namespace Envoy { + +class TestPauseFilter; +class RandomPauseFilter; + namespace Quic { // Act as a Network::Connection to HCM and a FilterManager to FilterFactoryCb. @@ -123,6 +127,9 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase { EnvoyQuicConnection* quic_connection_{nullptr}; private: + friend class Envoy::RandomPauseFilter; + friend class Envoy::TestPauseFilter; + // Called when aggregated buffered bytes across all the streams exceeds high watermark. void onSendBufferHighWatermark(); // Called when aggregated buffered bytes across all the streams declines to low watermark. diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index f6fbd30c2051..55a03439adf4 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -345,9 +345,7 @@ FakeHttpConnection::FakeHttpConnection( codec_ = std::unique_ptr( Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) - .createQuicServerConnection(shared_connection_.connection(), *this, - max_request_headers_kb, max_request_headers_count, - headers_with_underscores_action)); + .createQuicServerConnection(shared_connection_.connection(), *this)); } shared_connection_.connection().addReadFilter( Network::ReadFilterSharedPtr{new ReadFilter(*this)}); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index b42610966004..ebe99bf39da2 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -442,6 +442,7 @@ TEST_P(DownstreamProtocolIntegrationTest, MissingHeadersLocalReply) { // Regression test for https://github.com/envoyproxy/envoy/issues/10270 TEST_P(ProtocolIntegrationTest, LongHeaderValueWithSpaces) { + EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Header with at least 20kb of spaces surrounded by non-whitespace characters to ensure that // dispatching is split across 2 dispatch calls. This threshold comes from Envoy preferring 16KB @@ -1077,6 +1078,8 @@ TEST_P(ProtocolIntegrationTest, MaxStreamDurationWithRetryPolicyWhenRetryUpstrea // Verify that headers with underscores in their names are dropped from client requests // but remain in upstream responses. TEST_P(ProtocolIntegrationTest, HeadersWithUnderscoresDropped) { + // TODO(danzh) treat underscore in headers according to the config. + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { @@ -1143,6 +1146,7 @@ TEST_P(ProtocolIntegrationTest, HeadersWithUnderscoresRemainByDefault) { // Verify that request with headers containing underscores is rejected when configured. TEST_P(DownstreamProtocolIntegrationTest, HeadersWithUnderscoresCauseRequestRejectedByDefault) { + EXCLUDE_DOWNSTREAM_HTTP3 useAccessLog("%RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS%"); config_helper_.addConfigModifier( [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& @@ -1304,6 +1308,7 @@ TEST_P(ProtocolIntegrationTest, MissingStatus) { // Validate that lots of tiny cookies doesn't cause a DoS (single cookie header). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { + EXCLUDE_DOWNSTREAM_HTTP3 initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); Http::TestRequestHeaderMapImpl request_headers{{":method", "POST"}, @@ -1325,6 +1330,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { // Validate that lots of tiny cookies doesn't cause a DoS (many cookie headers). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingMany) { + EXCLUDE_DOWNSTREAM_HTTP3 // Set header count limit to 2010. uint32_t max_count = 2010; config_helper_.addConfigModifier( @@ -1353,6 +1359,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingMany) { } TEST_P(DownstreamProtocolIntegrationTest, InvalidContentLength) { + EXCLUDE_DOWNSTREAM_HTTP3 initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -1418,7 +1425,7 @@ TEST_P(DownstreamProtocolIntegrationTest, InvalidContentLengthAllowed) { } TEST_P(DownstreamProtocolIntegrationTest, MultipleContentLengths) { - + EXCLUDE_DOWNSTREAM_HTTP3 initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); auto encoder_decoder = @@ -1508,6 +1515,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlAccepted) { + EXCLUDE_DOWNSTREAM_HTTP3 // Send one 95 kB URL with limit 96 kB headers. testLargeRequestUrl(95, 96); } @@ -1519,6 +1527,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersAccepted) { + EXCLUDE_DOWNSTREAM_HTTP3 // Send one 95 kB header with limit 96 kB and 100 headers. testLargeRequestHeaders(95, 1, 96, 100); } @@ -1603,6 +1612,7 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersTimeout) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersAccepted) { + EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); testLargeRequestTrailers(60, 96); } @@ -1673,6 +1683,7 @@ TEST_P(ProtocolIntegrationTest, LargeRequestMethod) { // There will be no upstream connections for HTTP/1 downstream, we need to // test the full mesh regardless. testing_upstream_intentionally_ = true; + EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // TODO(#14829) Rejected with QUIC_STREAM_EXCESSIVE_LOAD const std::string long_method = std::string(48 * 1024, 'a'); const Http::TestRequestHeaderMapImpl request_headers{{":method", long_method}, @@ -2133,6 +2144,7 @@ TEST_P(DownstreamProtocolIntegrationTest, BasicMaxStreamTimeoutLegacy) { // Make sure that invalid authority headers get blocked at or before the HCM. TEST_P(DownstreamProtocolIntegrationTest, InvalidAuthority) { + EXCLUDE_DOWNSTREAM_HTTP3 initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); From 186a69da5288327db9f49b330f9beb89c900332e Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Thu, 11 Mar 2021 00:18:38 -0500 Subject: [PATCH 04/52] add more test Signed-off-by: Dan Zhang --- .../quiche/active_quic_listener.cc | 2 + .../quiche/client_connection_factory_impl.cc | 3 ++ .../quiche/platform/quiche_flags_impl.cc | 5 +-- .../quic_filter_manager_connection_impl.h | 6 +-- .../quiche/envoy_quic_utils_test.cc | 15 ++++++- .../quic_listeners/quiche/integration/BUILD | 1 + test/integration/filters/BUILD | 13 ++++++ test/integration/filters/pause_filter.cc | 45 ++++--------------- test/integration/http_integration.cc | 22 +++------ test/integration/http_protocol_integration.h | 2 +- 10 files changed, 50 insertions(+), 64 deletions(-) diff --git a/source/extensions/quic_listeners/quiche/active_quic_listener.cc b/source/extensions/quic_listeners/quiche/active_quic_listener.cc index 805ce5a1cc62..78cd127ec9be 100644 --- a/source/extensions/quic_listeners/quiche/active_quic_listener.cc +++ b/source/extensions/quic_listeners/quiche/active_quic_listener.cc @@ -39,7 +39,9 @@ ActiveQuicListener::ActiveQuicListener( &listener_config), dispatcher_(dispatcher), version_manager_(quic::CurrentSupportedVersions()), kernel_worker_routing_(kernel_worker_routing) { + // This flag fix a QUICHE issue which may crashe Envoy during connection close. SetQuicReloadableFlag(quic_single_ack_in_packet2, true); + if (Runtime::LoaderSingleton::getExisting()) { enabled_.emplace(Runtime::FeatureFlag(enabled, Runtime::LoaderSingleton::get())); } diff --git a/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc b/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc index 9b8f40e34c73..4396e68dd044 100644 --- a/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc +++ b/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc @@ -11,6 +11,9 @@ QuicClientConnectionFactoryImpl::createQuicNetworkConnection( Network::Address::InstanceConstSharedPtr local_addr, Network::TransportSocketFactory& transport_socket_factory, Stats::Scope& stats_scope, Event::Dispatcher& dispatcher, TimeSource& time_source) { + // This flag fix a QUICHE issue which may crashe Envoy during connection close. + SetQuicReloadableFlag(quic_single_ack_in_packet2, true); + // TODO(#14829): reject config if anything but QuicClientTransportSocketConfigFactory configured. // raw buffer socket is configured. auto* quic_socket_factory = diff --git a/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc b/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc index aad20c06f72c..78fd70d6cd9f 100644 --- a/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc +++ b/source/extensions/quic_listeners/quiche/platform/quiche_flags_impl.cc @@ -45,9 +45,7 @@ FlagRegistry& FlagRegistry::getInstance() { return *instance; } -FlagRegistry::FlagRegistry() : flags_(makeFlagMap()) { - FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2->setValue(true); -} +FlagRegistry::FlagRegistry() : flags_(makeFlagMap()) {} void FlagRegistry::resetFlags() const { for (auto& kv : flags_) { @@ -136,6 +134,7 @@ QUIC_FLAG(FLAGS_quic_reloadable_flag_http2_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_reloadable_flag_http2_testonly_default_true, true) QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_false, false) QUIC_FLAG(FLAGS_quic_restart_flag_http2_testonly_default_true, true) + #undef QUIC_FLAG #define STRINGIFY(X) #X diff --git a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h index 3e3eedba3bbd..4be7a776f068 100644 --- a/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h +++ b/source/extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h @@ -13,8 +13,7 @@ namespace Envoy { -class TestPauseFilter; -class RandomPauseFilter; +class TestPauseFilterForQuic; namespace Quic { @@ -127,8 +126,7 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase { EnvoyQuicConnection* quic_connection_{nullptr}; private: - friend class Envoy::RandomPauseFilter; - friend class Envoy::TestPauseFilter; + friend class Envoy::TestPauseFilterForQuic; // Called when aggregated buffered bytes across all the streams exceeds high watermark. void onSendBufferHighWatermark(); diff --git a/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc b/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc index ad5dd5df4870..d8f5af4c4005 100644 --- a/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc +++ b/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc @@ -50,13 +50,24 @@ TEST(EnvoyQuicUtilsTest, HeadersConversion) { headers_block[":authority"] = "www.google.com"; headers_block[":path"] = "/index.hml"; headers_block[":scheme"] = "https"; + headers_block.AppendValueOrAddHeader("key", "value1"); + headers_block.AppendValueOrAddHeader("key", "value2"); auto envoy_headers = spdyHeaderBlockToEnvoyHeaders(headers_block); - EXPECT_EQ(headers_block.size(), envoy_headers->size()); + EXPECT_EQ(headers_block.size() + 1u, envoy_headers->size()); EXPECT_EQ("www.google.com", envoy_headers->getHostValue()); EXPECT_EQ("/index.hml", envoy_headers->getPathValue()); EXPECT_EQ("https", envoy_headers->getSchemeValue()); + EXPECT_EQ("value1", envoy_headers->get(Http::LowerCaseString("key"))[0]->value().getStringView()); + EXPECT_EQ("value2", envoy_headers->get(Http::LowerCaseString("key"))[1]->value().getStringView()); - quic::QuicHeaderList quic_headers = quic::test::AsHeaderList(headers_block); + quic::QuicHeaderList quic_headers; + quic_headers.OnHeaderBlockStart(); + quic_headers.OnHeader(":authority", "www.google.com"); + quic_headers.OnHeader(":path", "/index.hml"); + quic_headers.OnHeader(":scheme", "https"); + quic_headers.OnHeader("key", "value1"); + quic_headers.OnHeader("key", "value2"); + quic_headers.OnHeaderBlockEnd(0, 0); auto envoy_headers2 = quicHeadersToEnvoyHeaders(quic_headers); EXPECT_EQ(*envoy_headers, *envoy_headers2); } diff --git a/test/extensions/quic_listeners/quiche/integration/BUILD b/test/extensions/quic_listeners/quiche/integration/BUILD index 5086389b21dc..d6fec0d41872 100644 --- a/test/extensions/quic_listeners/quiche/integration/BUILD +++ b/test/extensions/quic_listeners/quiche/integration/BUILD @@ -27,6 +27,7 @@ envoy_cc_test( "//source/extensions/quic_listeners/quiche:quic_factory_lib", "//source/extensions/quic_listeners/quiche:quic_transport_socket_factory_lib", "//test/integration:protocol_integration_test_lib", + "//test/integration/filters:pause_filter_for_quic_lib", ], ) diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 0e4f379ac838..9ec65dd4d270 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -182,6 +182,19 @@ envoy_cc_test_library( "//include/envoy/registry", "//source/common/network:connection_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", + "//test/extensions/filters/http/common:empty_http_filter_config_lib", + ], +) + +envoy_cc_test_library( + name = "pause_filter_for_quic_lib", + srcs = [ + "pause_filter_for_quic.cc", + ], + deps = [ + "//include/envoy/http:filter_interface", + "//include/envoy/registry", + "//source/extensions/filters/http/common:pass_through_filter_lib", "//source/extensions/quic_listeners/quiche:quic_filter_manager_connection_lib", "//test/extensions/filters/http/common:empty_http_filter_config_lib", ], diff --git a/test/integration/filters/pause_filter.cc b/test/integration/filters/pause_filter.cc index 221474e0fe77..b7f9aa1a3c36 100644 --- a/test/integration/filters/pause_filter.cc +++ b/test/integration/filters/pause_filter.cc @@ -5,7 +5,6 @@ #include "common/network/connection_impl.h" #include "extensions/filters/http/common/pass_through_filter.h" -#include "extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h" #include "test/extensions/filters/http/common/empty_http_filter_config.h" @@ -30,9 +29,8 @@ class TestPauseFilter : public Http::PassThroughFilter { number_of_decode_calls_ref_++; // If this is the second stream to decode headers and we're at high watermark. force low // watermark state - if (number_of_decode_calls_ref_ == 2 && - decoder_callbacks_->connection()->aboveHighWatermark()) { - mockConnectionBelowLowWatermark(); + if (number_of_decode_calls_ref_ == 2 && connection()->aboveHighWatermark()) { + connection()->onWriteBufferLowWatermark(); } } return PassThroughFilter::decodeData(buf, end_stream); @@ -44,46 +42,19 @@ class TestPauseFilter : public Http::PassThroughFilter { number_of_encode_calls_ref_++; // If this is the first stream to encode headers and we're not at high watermark, force high // watermark state. - if (number_of_encode_calls_ref_ == 1 && - !decoder_callbacks_->connection()->aboveHighWatermark()) { - mockConnectionAboveHighWatermark(); + if (number_of_encode_calls_ref_ == 1 && !connection()->aboveHighWatermark()) { + connection()->onWriteBufferHighWatermark(); } } return PassThroughFilter::encodeData(buf, end_stream); } - void mockConnectionAboveHighWatermark() { + Network::ConnectionImpl* connection() { // As long as we're doing horrible things let's do *all* the horrible things. - // Assert the connection we have is a ConnectionImpl or QuicFilterManagerConnectionImpl and - // const cast it so we can force watermark changes. + // Assert the connection we have is a ConnectionImpl and const cast it so we + // can force watermark changes. auto conn_impl = dynamic_cast(decoder_callbacks_->connection()); - if (conn_impl != nullptr) { - const_cast(conn_impl)->onWriteBufferHighWatermark(); - return; - } - // If transport protocol is QUIC, simulate connection buffer above watermark differently. - auto quic_connection = const_cast( - dynamic_cast( - decoder_callbacks_->connection())); - quic_connection->write_buffer_watermark_simulation_.checkHighWatermark( - quic_connection->write_buffer_watermark_simulation_.highWatermark() + 1u); - } - - void mockConnectionBelowLowWatermark() { - // As long as we're doing horrible things let's do *all* the horrible things. - // Assert the connection we have is a ConnectionImpl or QuicFilterManagerConnectionImpl and - // const cast it so we can force watermark changes. - auto conn_impl = dynamic_cast(decoder_callbacks_->connection()); - if (conn_impl != nullptr) { - const_cast(conn_impl)->onWriteBufferHighWatermark(); - return; - } - // If transport protocol is QUIC, simulate connection buffer below watermark differently. - auto quic_connection = const_cast( - dynamic_cast( - decoder_callbacks_->connection())); - quic_connection->write_buffer_watermark_simulation_.checkLowWatermark( - quic_connection->write_buffer_watermark_simulation_.highWatermark() / 2 - 1u); + return const_cast(conn_impl); } absl::Mutex& encode_lock_; diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 84ccb870c2c7..f17cbc53edce 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -46,18 +46,6 @@ #include "base_integration_test.h" #include "gtest/gtest.h" -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - -#include "quiche/quic/core/quic_utils.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - namespace Envoy { namespace { @@ -88,7 +76,6 @@ IntegrationCodecClient::IntegrationCodecClient( CodecClient::Type type) : CodecClientProd(type, std::move(conn), host_description, dispatcher, random), dispatcher_(dispatcher), callbacks_(*this), codec_callbacks_(*this) { - std::cerr << "=========== IntegrationCodecClient add connection callback " << &callbacks_ << "\n"; connection_->addConnectionCallbacks(callbacks_); setCodecConnectionCallbacks(codec_callbacks_); dispatcher.run(Event::Dispatcher::RunType::Block); @@ -925,7 +912,6 @@ void HttpIntegrationTest::testEnvoyHandling100Continue(bool additional_continue_ ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); // Send the rest of the request. - std::cerr << "============== finish sendData"; codec_client_->sendData(*request_encoder_, 10, true); ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); // Verify the Expect header is stripped. @@ -1058,11 +1044,13 @@ void HttpIntegrationTest::testTwoRequests(bool network_backup) { // created while the socket appears to be in the high watermark state, and regression tests that // flow control will be corrected as the socket "becomes unblocked" if (network_backup) { - config_helper_.addFilter(R"EOF( - name: pause-filter + config_helper_.addFilter( + fmt::format(R"EOF( + name: pause-filter{} typed_config: "@type": type.googleapis.com/google.protobuf.Empty - )EOF"); + )EOF", + downstreamProtocol() == Http::CodecClient::Type::HTTP3 ? "-for-quic" : "")); } initialize(); diff --git a/test/integration/http_protocol_integration.h b/test/integration/http_protocol_integration.h index 911c12661642..f65b15455116 100644 --- a/test/integration/http_protocol_integration.h +++ b/test/integration/http_protocol_integration.h @@ -31,8 +31,8 @@ class HttpProtocolIntegrationTest : public testing::TestWithParam Date: Thu, 11 Mar 2021 00:19:23 -0500 Subject: [PATCH 05/52] quic_pause_filter Signed-off-by: Dan Zhang --- .../filters/pause_filter_for_quic.cc | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 test/integration/filters/pause_filter_for_quic.cc diff --git a/test/integration/filters/pause_filter_for_quic.cc b/test/integration/filters/pause_filter_for_quic.cc new file mode 100644 index 000000000000..90f95e3f337e --- /dev/null +++ b/test/integration/filters/pause_filter_for_quic.cc @@ -0,0 +1,91 @@ +#include + +#include "envoy/registry/registry.h" + +#include "extensions/filters/http/common/pass_through_filter.h" +#include "extensions/quic_listeners/quiche/quic_filter_manager_connection_impl.h" + +#include "test/extensions/filters/http/common/empty_http_filter_config.h" + +namespace Envoy { + +// This filter exists to synthetically test network backup by faking TCP +// connection back-up when an encode is finished, and unblocking it when the +// next stream starts to decode headers. +// Allows regression tests for https://github.com/envoyproxy/envoy/issues/4541 +class TestPauseFilterForQuic : public Http::PassThroughFilter { +public: + // Pass in a some global filter state to ensure the Network::Connection is + // blocked and unblocked exactly once. + TestPauseFilterForQuic(absl::Mutex& encode_lock, uint32_t& number_of_encode_calls_ref, + uint32_t& number_of_decode_calls_ref) + : encode_lock_(encode_lock), number_of_encode_calls_ref_(number_of_encode_calls_ref), + number_of_decode_calls_ref_(number_of_decode_calls_ref) {} + + Http::FilterDataStatus decodeData(Buffer::Instance& buf, bool end_stream) override { + if (end_stream) { + absl::WriterMutexLock m(&encode_lock_); + number_of_decode_calls_ref_++; + // If this is the second stream to decode headers and we're at high watermark. force low + // watermark state + if (number_of_decode_calls_ref_ == 2 && + decoder_callbacks_->connection()->aboveHighWatermark()) { + auto quic_connection = const_cast( + dynamic_cast( + decoder_callbacks_->connection())); + quic_connection->write_buffer_watermark_simulation_.checkHighWatermark( + quic_connection->write_buffer_watermark_simulation_.highWatermark() + 1u); + } + } + return PassThroughFilter::decodeData(buf, end_stream); + } + + Http::FilterDataStatus encodeData(Buffer::Instance& buf, bool end_stream) override { + if (end_stream) { + absl::WriterMutexLock m(&encode_lock_); + number_of_encode_calls_ref_++; + // If this is the first stream to encode headers and we're not at high watermark, force high + // watermark state. + if (number_of_encode_calls_ref_ == 1 && + !decoder_callbacks_->connection()->aboveHighWatermark()) { + auto quic_connection = const_cast( + dynamic_cast( + decoder_callbacks_->connection())); + quic_connection->write_buffer_watermark_simulation_.checkHighWatermark( + quic_connection->write_buffer_watermark_simulation_.highWatermark() + 1u); + } + } + return PassThroughFilter::encodeData(buf, end_stream); + } + + absl::Mutex& encode_lock_; + uint32_t& number_of_encode_calls_ref_; + uint32_t& number_of_decode_calls_ref_; +}; + +class TestPauseFilterConfigForQuic : public Extensions::HttpFilters::Common::EmptyHttpFilterConfig { +public: + TestPauseFilterConfigForQuic() : EmptyHttpFilterConfig("pause-filter-for-quic") {} + + Http::FilterFactoryCb createFilter(const std::string&, + Server::Configuration::FactoryContext&) override { + return [&](Http::FilterChainFactoryCallbacks& callbacks) -> void { + // ABSL_GUARDED_BY insists the lock be held when the guarded variables are passed by + // reference. + absl::WriterMutexLock m(&encode_lock_); + callbacks.addStreamFilter(std::make_shared<::Envoy::TestPauseFilterForQuic>( + encode_lock_, number_of_encode_calls_, number_of_decode_calls_)); + }; + } + + absl::Mutex encode_lock_; + uint32_t number_of_encode_calls_ ABSL_GUARDED_BY(encode_lock_) = 0; + uint32_t number_of_decode_calls_ ABSL_GUARDED_BY(encode_lock_) = 0; +}; + +// perform static registration +static Registry::RegisterFactory + register_; + +} // namespace Envoy From d5cbd0097e093ddc30c3bc8dcb00f34a30c72b72 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sat, 13 Mar 2021 00:22:10 -0500 Subject: [PATCH 06/52] address comments Signed-off-by: Dan Zhang --- source/common/http/utility.h | 1 + source/extensions/quic_listeners/quiche/BUILD | 2 +- .../quiche/active_quic_listener.cc | 2 +- .../quiche/client_connection_factory_impl.cc | 2 +- .../quic_listeners/quiche/envoy_quic_utils.cc | 6 +- test/integration/BUILD | 104 ++++++++---------- test/integration/ssl_utility.cc | 25 +++-- test/integration/ssl_utility.h | 12 +- test/integration/utility.cc | 22 +--- 9 files changed, 78 insertions(+), 98 deletions(-) diff --git a/source/common/http/utility.h b/source/common/http/utility.h index 526fb701a4fd..7d2a065bcb37 100644 --- a/source/common/http/utility.h +++ b/source/common/http/utility.h @@ -39,6 +39,7 @@ class AlpnNameValues { const std::string Http11 = "http/1.1"; const std::string Http2 = "h2"; const std::string Http2c = "h2c"; + const std::string Http3 = "h3"; }; using AlpnNames = ConstSingleton; diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD index 850c7c2c0b5d..488c58548936 100644 --- a/source/extensions/quic_listeners/quiche/BUILD +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -386,10 +386,10 @@ envoy_cc_extension( # Needed to verify that a quic specific configuration is used for quic transport socket. extra_visibility = [ "//source/server:__subpackages__", + "//test:__subpackages__", ], security_posture = "unknown", tags = ["nofips"], - visibility = ["//test:__subpackages__"], deps = [ "//include/envoy/network:transport_socket_interface", "//include/envoy/server:transport_socket_config_interface", diff --git a/source/extensions/quic_listeners/quiche/active_quic_listener.cc b/source/extensions/quic_listeners/quiche/active_quic_listener.cc index 78cd127ec9be..cbab473f6373 100644 --- a/source/extensions/quic_listeners/quiche/active_quic_listener.cc +++ b/source/extensions/quic_listeners/quiche/active_quic_listener.cc @@ -39,7 +39,7 @@ ActiveQuicListener::ActiveQuicListener( &listener_config), dispatcher_(dispatcher), version_manager_(quic::CurrentSupportedVersions()), kernel_worker_routing_(kernel_worker_routing) { - // This flag fix a QUICHE issue which may crashe Envoy during connection close. + // This flag fix a QUICHE issue which may crash Envoy during connection close. SetQuicReloadableFlag(quic_single_ack_in_packet2, true); if (Runtime::LoaderSingleton::getExisting()) { diff --git a/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc b/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc index 4396e68dd044..5a9a6154f02a 100644 --- a/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc +++ b/source/extensions/quic_listeners/quiche/client_connection_factory_impl.cc @@ -11,7 +11,7 @@ QuicClientConnectionFactoryImpl::createQuicNetworkConnection( Network::Address::InstanceConstSharedPtr local_addr, Network::TransportSocketFactory& transport_socket_factory, Stats::Scope& stats_scope, Event::Dispatcher& dispatcher, TimeSource& time_source) { - // This flag fix a QUICHE issue which may crashe Envoy during connection close. + // This flag fix a QUICHE issue which may crash Envoy during connection close. SetQuicReloadableFlag(quic_single_ack_in_packet2, true); // TODO(#14829): reject config if anything but QuicClientTransportSocketConfigFactory configured. diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc b/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc index f0f658eb5585..0533311978b4 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_utils.cc @@ -120,11 +120,7 @@ createConnectionSocket(Network::Address::InstanceConstSharedPtr& peer_addr, Network::Address::InstanceConstSharedPtr& local_addr, const Network::ConnectionSocket::OptionsSharedPtr& options) { if (local_addr == nullptr) { - if (peer_addr->ip()->ipv4() != nullptr) { - local_addr = Network::Utility::getCanonicalIpv4LoopbackAddress(); - } else { - local_addr = Network::Utility::getIpv6LoopbackAddress(); - } + local_addr = Network::Utility::getLocalAddress(peer_addr->ip()->version()); } auto connection_socket = std::make_unique( Network::Socket::Type::Datagram, local_addr, peer_addr); diff --git a/test/integration/BUILD b/test/integration/BUILD index 5be22277c135..afa7113d934c 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -619,39 +619,6 @@ envoy_cc_test_library( ], ) -envoy_cc_test_library( - name = "utility_lib", - srcs = [ - "utility.cc", - ], - hdrs = [ - "utility.h", - ], - deps = [ - "//include/envoy/api:api_interface", - "//include/envoy/http:codec_interface", - "//include/envoy/http:header_map_interface", - "//include/envoy/network:filter_interface", - "//source/common/api:api_lib", - "//source/common/common:assert_lib", - "//source/common/common:utility_lib", - "//source/common/http:codec_client_lib", - "//source/common/http/http3:quic_client_connection_factory_lib", - "//source/common/stats:isolated_store_lib", - "//source/extensions/quic_listeners/quiche:quic_transport_socket_factory_lib", - "//test/common/upstream:utility_lib", - "//test/mocks/event:event_mocks", - "//test/mocks/server:transport_socket_factory_context_mocks", - "//test/mocks/upstream:cluster_info_mocks", - "//test/test_common:environment_lib", - "//test/test_common:network_utility_lib", - "//test/test_common:printers_lib", - "//test/test_common:simulated_time_system_lib", - "//test/test_common:test_time_lib", - "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", - ], -) - envoy_cc_test_library( name = "integration_tcp_client_lib", srcs = [ @@ -731,7 +698,6 @@ envoy_cc_test_library( ":autonomous_upstream_lib", ":fake_upstream_lib", ":integration_tcp_client_lib", - ":server_lib", ":utility_lib", "//source/common/config:api_version_lib", "//source/common/config:version_converter_lib", @@ -757,26 +723,57 @@ envoy_cc_test_library( ) envoy_cc_test_library( - name = "server_lib", + name = "autonomous_upstream_lib", + srcs = [ + "autonomous_upstream.cc", + ], + hdrs = [ + "autonomous_upstream.h", + ], + deps = [ + ":fake_upstream_lib", + ], +) + +envoy_cc_test_library( + name = "utility_lib", srcs = [ "server.cc", + "ssl_utility.cc", + "utility.cc", ], hdrs = [ "server.h", + "ssl_utility.h", + "utility.h", ], + data = ["//test/common/runtime:filesystem_test_data"], deps = [ ":server_stats_interface", ":tcp_dump", - ":utility_lib", + "//include/envoy/api:api_interface", + "//include/envoy/http:codec_interface", + "//include/envoy/http:header_map_interface", + "//include/envoy/network:filter_interface", "//include/envoy/server:options_interface", "//include/envoy/server:process_context_interface", "//include/envoy/stats:stats_interface", + "//source/common/api:api_lib", "//source/common/common:assert_lib", "//source/common/common:lock_guard_lib", "//source/common/common:logger_lib", "//source/common/common:thread_lib", + "//source/common/common:utility_lib", + "//source/common/http:codec_client_lib", + "//source/common/http/http3:quic_client_connection_factory_lib", + "//source/common/json:json_loader_lib", + "//source/common/network:utility_lib", "//source/common/stats:allocator_lib", + "//source/common/stats:isolated_store_lib", "//source/common/thread_local:thread_local_lib", + "//source/extensions/quic_listeners/quiche:quic_transport_socket_factory_lib", + "//source/extensions/transport_sockets/tls:config", + "//source/extensions/transport_sockets/tls:context_lib", "//source/server:drain_manager_lib", "//source/server:hot_restart_nop_lib", "//source/server:listener_hooks_lib", @@ -784,46 +781,39 @@ envoy_cc_test_library( "//source/server:process_context_lib", "//source/server:server_lib", "//test/common/runtime:utility_lib", + "//test/common/upstream:utility_lib", + "//test/config:utility_lib", "//test/mocks:common_lib", + "//test/mocks/event:event_mocks", "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:transport_socket_factory_context_mocks", + "//test/mocks/upstream:cluster_info_mocks", "//test/test_common:environment_lib", + "//test/test_common:network_utility_lib", + "//test/test_common:printers_lib", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:test_time_lib", "//test/test_common:test_time_system_interface", "//test/test_common:utility_lib", "@com_google_absl//absl/synchronization", "@envoy_api//envoy/config/listener/v3:pkg_cc_proto", - ], -) - -envoy_cc_test_library( - name = "autonomous_upstream_lib", - srcs = [ - "autonomous_upstream.cc", - ], - hdrs = [ - "autonomous_upstream.h", - ], - deps = [ - ":fake_upstream_lib", + "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], ) envoy_cc_test_library( name = "integration_lib", - srcs = [ - "ssl_utility.cc", - ], hdrs = [ "integration.h", - "ssl_utility.h", ], - data = ["//test/common/runtime:filesystem_test_data"], deps = [ ":autonomous_upstream_lib", ":base_integration_test_lib", ":fake_upstream_lib", ":integration_stream_decoder_lib", ":integration_tcp_client_lib", - ":server_lib", + ":utility_lib", "//include/envoy/api:api_interface", "//include/envoy/buffer:buffer_interface", "//include/envoy/event:dispatcher_interface", @@ -866,8 +856,6 @@ envoy_cc_test_library( "//source/extensions/access_loggers/file:config", "//source/extensions/transport_sockets/raw_buffer:config", "//source/extensions/transport_sockets/tap:config", - "//source/extensions/transport_sockets/tls:config", - "//source/extensions/transport_sockets/tls:context_lib", "//source/server:connection_handler_lib", "//source/server:drain_manager_lib", "//source/server:hot_restart_nop_lib", @@ -879,7 +867,6 @@ envoy_cc_test_library( "//test/common/upstream:utility_lib", "//test/config:utility_lib", "//test/mocks/buffer:buffer_mocks", - "//test/mocks/server:transport_socket_factory_context_mocks", "//test/mocks/stats:stats_mocks", "//test/mocks/upstream:retry_priority_factory_mocks", "//test/mocks/upstream:retry_priority_mocks", @@ -890,7 +877,6 @@ envoy_cc_test_library( "//test/test_common:test_time_lib", "//test/test_common:test_time_system_interface", "//test/test_common:utility_lib", - "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], ) diff --git a/test/integration/ssl_utility.cc b/test/integration/ssl_utility.cc index aab3dd5d4adf..3141c1c0826f 100644 --- a/test/integration/ssl_utility.cc +++ b/test/integration/ssl_utility.cc @@ -1,7 +1,5 @@ #include "test/integration/ssl_utility.h" -#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" - #include "common/http/utility.h" #include "common/json/json_loader.h" #include "common/network/utility.h" @@ -23,9 +21,9 @@ using testing::ReturnRef; namespace Envoy { namespace Ssl { -Network::TransportSocketFactoryPtr -createClientSslTransportSocketFactory(const ClientSslTransportOptions& options, - ContextManager& context_manager, Api::Api& api) { +void initializeUpstreamTlsContextConfig( + const ClientSslTransportOptions& options, + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& tls_context) { std::string yaml_plain = R"EOF( common_tls_context: validation_context: @@ -50,17 +48,19 @@ createClientSslTransportSocketFactory(const ClientSslTransportOptions& options, )EOF"; } - envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; TestUtility::loadFromYaml(TestEnvironment::substitute(yaml_plain), tls_context); auto* common_context = tls_context.mutable_common_tls_context(); if (options.alpn_) { common_context->add_alpn_protocols(Http::Utility::AlpnNames::get().Http2); common_context->add_alpn_protocols(Http::Utility::AlpnNames::get().Http11); + common_context->add_alpn_protocols(Http::Utility::AlpnNames::get().Http3); } - if (options.san_) { - common_context->mutable_validation_context() - ->add_hidden_envoy_deprecated_verify_subject_alt_name("spiffe://lyft.com/backend-team"); + if (!options.san_.empty()) { + // common_context->mutable_validation_context() + // ->add_hidden_envoy_deprecated_verify_subject_alt_name(options.san_); + common_context->mutable_validation_context()->add_match_subject_alt_names()->set_exact( + options.san_); } for (const std::string& cipher_suite : options.cipher_suites_) { common_context->mutable_tls_params()->add_cipher_suites(cipher_suite); @@ -71,6 +71,13 @@ createClientSslTransportSocketFactory(const ClientSslTransportOptions& options, common_context->mutable_tls_params()->set_tls_minimum_protocol_version(options.tls_version_); common_context->mutable_tls_params()->set_tls_maximum_protocol_version(options.tls_version_); +} + +Network::TransportSocketFactoryPtr +createClientSslTransportSocketFactory(ClientSslTransportOptions& options, + ContextManager& context_manager, Api::Api& api) { + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; + initializeUpstreamTlsContextConfig(options.setSan("spiffe://lyft.com/backend-team"), tls_context); NiceMock mock_factory_ctx; ON_CALL(mock_factory_ctx, api()).WillByDefault(ReturnRef(api)); diff --git a/test/integration/ssl_utility.h b/test/integration/ssl_utility.h index afaf57eae00c..e908e90661e2 100644 --- a/test/integration/ssl_utility.h +++ b/test/integration/ssl_utility.h @@ -16,8 +16,8 @@ struct ClientSslTransportOptions { return *this; } - ClientSslTransportOptions& setSan(bool san) { - san_ = san; + ClientSslTransportOptions& setSan(absl::string_view san) { + san_ = std::string(san); return *this; } @@ -48,7 +48,7 @@ struct ClientSslTransportOptions { } bool alpn_{}; - bool san_{}; + std::string san_; bool client_ecdsa_cert_{}; std::vector cipher_suites_{}; std::string sigalgs_; @@ -57,8 +57,12 @@ struct ClientSslTransportOptions { envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLS_AUTO}; }; +void initializeUpstreamTlsContextConfig( + const ClientSslTransportOptions& options, + envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& tls_context); + Network::TransportSocketFactoryPtr -createClientSslTransportSocketFactory(const ClientSslTransportOptions& options, +createClientSslTransportSocketFactory(ClientSslTransportOptions& options, ContextManager& context_manager, Api::Api& api); Network::TransportSocketFactoryPtr createUpstreamSslContext(ContextManager& context_manager, diff --git a/test/integration/utility.cc b/test/integration/utility.cc index d6b3bf5dd7a1..ed83df1520f6 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -22,6 +22,7 @@ #include "common/upstream/upstream_impl.h" #include "test/common/upstream/utility.h" +#include "test/integration/ssl_utility.h" #include "test/mocks/common.h" #include "test/mocks/server/transport_socket_factory_context.h" #include "test/mocks/stats/mocks.h" @@ -112,27 +113,12 @@ struct ConnectionCallbacks : public Network::ConnectionCallbacks { Network::TransportSocketFactoryPtr IntegrationUtil::createQuicClientTransportSocketFactory( Server::Configuration::TransportSocketFactoryContext& context, const std::string& san_to_match) { - std::string yaml_plain = R"EOF( - common_tls_context: - validation_context: - trusted_ca: - filename: "{{ test_rundir }}/test/config/integration/certs/cacert.pem" -)EOF"; envoy::extensions::transport_sockets::quic::v3::QuicUpstreamTransport quic_transport_socket_config; auto* tls_context = quic_transport_socket_config.mutable_upstream_tls_context(); - TestUtility::loadFromYaml(TestEnvironment::substitute(yaml_plain), *tls_context); - auto* common_context = tls_context->mutable_common_tls_context(); - - common_context->add_alpn_protocols("h3"); - common_context->mutable_validation_context()->add_match_subject_alt_names()->set_exact( - san_to_match); - tls_context->set_sni("lyft.com"); - - common_context->mutable_tls_params()->set_tls_minimum_protocol_version( - envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLS_AUTO); - common_context->mutable_tls_params()->set_tls_maximum_protocol_version( - envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLS_AUTO); + initializeUpstreamTlsContextConfig( + Ssl::ClientSslTransportOptions().setAlpn(true).setSan(san_to_match).setSni("lyft.com"), + *tls_context); envoy::config::core::v3::TransportSocket message; message.mutable_typed_config()->PackFrom(quic_transport_socket_config); From cf083113b19f4531fbf65a3866591553331bad69 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Mon, 15 Mar 2021 14:02:50 -0400 Subject: [PATCH 07/52] fix const reference Signed-off-by: Dan Zhang --- test/integration/ssl_utility.cc | 8 ++++++-- test/integration/ssl_utility.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/integration/ssl_utility.cc b/test/integration/ssl_utility.cc index 3141c1c0826f..1f684a0608e5 100644 --- a/test/integration/ssl_utility.cc +++ b/test/integration/ssl_utility.cc @@ -74,10 +74,14 @@ void initializeUpstreamTlsContextConfig( } Network::TransportSocketFactoryPtr -createClientSslTransportSocketFactory(ClientSslTransportOptions& options, +createClientSslTransportSocketFactory(const ClientSslTransportOptions& options, ContextManager& context_manager, Api::Api& api) { +ClientSslTransportOptions options_with_san = options; + if (options.san_.empty()) { + options_with_san.setSan("spiffe://lyft.com/backend-team"); + } envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; - initializeUpstreamTlsContextConfig(options.setSan("spiffe://lyft.com/backend-team"), tls_context); + initializeUpstreamTlsContextConfig(options_with_san, tls_context); NiceMock mock_factory_ctx; ON_CALL(mock_factory_ctx, api()).WillByDefault(ReturnRef(api)); diff --git a/test/integration/ssl_utility.h b/test/integration/ssl_utility.h index e908e90661e2..5c4904080478 100644 --- a/test/integration/ssl_utility.h +++ b/test/integration/ssl_utility.h @@ -62,7 +62,7 @@ void initializeUpstreamTlsContextConfig( envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& tls_context); Network::TransportSocketFactoryPtr -createClientSslTransportSocketFactory(ClientSslTransportOptions& options, +createClientSslTransportSocketFactory(const ClientSslTransportOptions& options, ContextManager& context_manager, Api::Api& api); Network::TransportSocketFactoryPtr createUpstreamSslContext(ContextManager& context_manager, From 56827916386e7312bbedfc9aaebb611ddff08a2f Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 16 Mar 2021 17:26:37 -0400 Subject: [PATCH 08/52] fix in filters Signed-off-by: Dan Zhang --- test/integration/filters/add_body_filter.cc | 12 +++++++++++- test/integration/http_integration.cc | 1 + test/integration/protocol_integration_test.cc | 18 ++++++------------ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/test/integration/filters/add_body_filter.cc b/test/integration/filters/add_body_filter.cc index 18a11de9bd3f..00378476b2be 100644 --- a/test/integration/filters/add_body_filter.cc +++ b/test/integration/filters/add_body_filter.cc @@ -31,11 +31,21 @@ class AddBodyStreamFilter : public Http::PassThroughFilter { return Http::FilterHeadersStatus::Continue; } + Http::FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream) override { + // Ensure that decodeData is only called for HTTP/3 (where protocol is set at the + // connection level). In HTTP/3 the FIN arrives separately so we will get + // decodeData() with an empty body. + if (end_stream && decoder_callbacks_->connection()->streamInfo().protocol() && data.length() == 0u) { + data.add("body"); + } + return Http::FilterDataStatus::Continue; + } + Http::FilterDataStatus encodeData(Buffer::Instance& data, bool end_stream) override { // Ensure that encodeData is only called for HTTP/3 (where protocol is set at the // connection level). In HTTP/3 the FIN arrives separately so we will get // encodeData() with an empty body. - ASSERT(end_stream == false || decoder_callbacks_->connection()->streamInfo().protocol()); + ASSERT(!end_stream || decoder_callbacks_->connection()->streamInfo().protocol()); data.add("body"); return Http::FilterDataStatus::Continue; } diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 58a0d28b52b2..aa164ba44f8c 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -321,6 +321,7 @@ void HttpIntegrationTest::initialize() { *tls_context->mutable_common_tls_context()); for (auto& listener : *bootstrap.mutable_static_resources()->mutable_listeners()) { if (listener.udp_listener_config().udp_listener_name() == "quiche_quic_listener") { + // if (listener.udp_listener_config().listener_config().typed_config().type_url() == "type.googleapis.com/envoy.config.listener.v3.QuicProtocolOptions") { auto* filter_chain = listener.mutable_filter_chains(0); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->mutable_typed_config()->PackFrom(quic_transport_socket_config); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index d8a9907af63d..f46e589120d0 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -193,10 +193,6 @@ name: health_check // Verifies behavior for https://github.com/envoyproxy/envoy/pull/11248 TEST_P(ProtocolIntegrationTest, AddBodyToRequestAndWaitForIt) { - // QUICHE can't guarantee headers and FIN to be delivered together, so - // headers-only request can't be detected at L7 filters. - EXCLUDE_DOWNSTREAM_HTTP3; - // filters are prepended, so add them in reverse order config_helper_.addFilter(R"EOF( name: wait-for-whole-request-and-response-filter @@ -1114,6 +1110,7 @@ TEST_P(ProtocolIntegrationTest, HeadersWithUnderscoresDropped) { stat_name = "http2.dropped_headers_with_underscores"; break; case Http::CodecClient::Type::HTTP3: + // TODO(danzh) add stats for H3. break; default: RELEASE_ASSERT(false, fmt::format("Unknown downstream protocol {}", downstream_protocol_)); @@ -1362,6 +1359,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingMany) { } TEST_P(DownstreamProtocolIntegrationTest, InvalidContentLength) { + // TODO(danzh) Add content length validation. EXCLUDE_DOWNSTREAM_HTTP3 initialize(); @@ -1428,6 +1426,7 @@ TEST_P(DownstreamProtocolIntegrationTest, InvalidContentLengthAllowed) { } TEST_P(DownstreamProtocolIntegrationTest, MultipleContentLengths) { + // TODO(danzh) Add content length validation. EXCLUDE_DOWNSTREAM_HTTP3 initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -1462,7 +1461,6 @@ TEST_P(DownstreamProtocolIntegrationTest, MultipleContentLengthsAllowed) { }); initialize(); - codec_client_ = makeHttpConnection(lookupPort("http")); auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{{":method", "POST"}, @@ -1512,7 +1510,6 @@ name: local-reply-during-encode } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlRejected) { - EXCLUDE_DOWNSTREAM_HTTP3 // Send one 95 kB URL with limit 60 kB headers. testLargeRequestUrl(95, 60); } @@ -1525,7 +1522,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersRejected) { - EXCLUDE_DOWNSTREAM_HTTP3 // Send one 95 kB header with limit 60 kB and 100 headers. testLargeRequestHeaders(95, 1, 60, 100); } @@ -1731,8 +1727,7 @@ TEST_P(ProtocolIntegrationTest, LargeRequestMethod) { // Tests StopAllIterationAndBuffer. Verifies decode-headers-return-stop-all-filter calls decodeData // once after iteration is resumed. TEST_P(DownstreamProtocolIntegrationTest, TestDecodeHeadersReturnsStopAll) { - // Enable after setting QUICHE stream initial flow control window from http2 - // options. + // Enable after setting QUICHE stream initial flow control window from http3 options. EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: call-decodedata-once-filter @@ -1785,6 +1780,7 @@ name: passthrough-filter // Tests StopAllIterationAndWatermark. decode-headers-return-stop-all-watermark-filter sets buffer // limit to 100. Verifies data pause when limit is reached, and resume after iteration continues. TEST_P(DownstreamProtocolIntegrationTest, TestDecodeHeadersReturnsStopAllWatermark) { + // TODO(danzh) Re-enable after codec buffer can be set according to http3 options. EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: decode-headers-return-stop-all-filter @@ -1844,8 +1840,7 @@ name: passthrough-filter // Test two filters that return StopAllIterationAndBuffer back-to-back. TEST_P(DownstreamProtocolIntegrationTest, TestTwoFiltersDecodeHeadersReturnsStopAll) { - // TODO(danzh) Re-enable after codec buffer can be set according to http2 - // options. + // TODO(danzh) Re-enable after codec buffer can be set according to http3 options. EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: decode-headers-return-stop-all-filter @@ -2172,7 +2167,6 @@ TEST_P(DownstreamProtocolIntegrationTest, BasicMaxStreamTimeoutLegacy) { // Make sure that invalid authority headers get blocked at or before the HCM. TEST_P(DownstreamProtocolIntegrationTest, InvalidAuthority) { - EXCLUDE_DOWNSTREAM_HTTP3 initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); From 725d93e2345f2ec253dd277309bb4f81ad3ebd2b Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 16 Mar 2021 17:44:12 -0400 Subject: [PATCH 09/52] stop using udp_listener_name Signed-off-by: Dan Zhang --- test/integration/filters/add_body_filter.cc | 5 +++-- test/integration/http_integration.cc | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/integration/filters/add_body_filter.cc b/test/integration/filters/add_body_filter.cc index 00378476b2be..51faeb1e7b19 100644 --- a/test/integration/filters/add_body_filter.cc +++ b/test/integration/filters/add_body_filter.cc @@ -35,8 +35,9 @@ class AddBodyStreamFilter : public Http::PassThroughFilter { // Ensure that decodeData is only called for HTTP/3 (where protocol is set at the // connection level). In HTTP/3 the FIN arrives separately so we will get // decodeData() with an empty body. - if (end_stream && decoder_callbacks_->connection()->streamInfo().protocol() && data.length() == 0u) { - data.add("body"); + if (end_stream && decoder_callbacks_->connection()->streamInfo().protocol() && + data.length() == 0u) { + data.add("body"); } return Http::FilterDataStatus::Continue; } diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 8e0a3e5fbc16..162623e649c8 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -320,8 +320,8 @@ void HttpIntegrationTest::initialize() { ConfigHelper::initializeTls(ConfigHelper::ServerSslOptions().setRsaCert(true).setTlsV13(true), *tls_context->mutable_common_tls_context()); for (auto& listener : *bootstrap.mutable_static_resources()->mutable_listeners()) { - if (listener.udp_listener_config().udp_listener_name() == "quiche_quic_listener") { - // if (listener.udp_listener_config().listener_config().typed_config().type_url() == "type.googleapis.com/envoy.config.listener.v3.QuicProtocolOptions") { + if (listener.udp_listener_config().listener_config().typed_config().type_url() == + "type.googleapis.com/envoy.config.listener.v3.QuicProtocolOptions") { auto* filter_chain = listener.mutable_filter_chains(0); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->mutable_typed_config()->PackFrom(quic_transport_socket_config); From 354a32244695c90d53f7b8f30ef3b8046da5f021 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 16 Mar 2021 20:32:37 -0400 Subject: [PATCH 10/52] fix IPv6 test failure Signed-off-by: Dan Zhang --- test/integration/protocol_integration_test.cc | 1 + test/integration/utility.cc | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index db3e00c983bd..e5ae9db75003 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1309,6 +1309,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); Http::TestRequestHeaderMapImpl request_headers{{":method", "POST"}, {":path", "/test/long/url"}, diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 4d5307f95bfb..98a214635f42 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -18,6 +18,7 @@ #include "common/http/headers.h" #include "common/http/http3/quic_client_connection_factory.h" #include "common/http/http3/well_known_names.h" +#include "common/network/address_impl.h" #include "common/network/utility.h" #include "common/upstream/upstream_impl.h" @@ -158,8 +159,16 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt Http::QuicCodecNames::get().Quiche); persistent_info = connection_factory.createNetworkConnectionInfo( *dispatcher, *transport_socket_factory, mock_stats_store, time_system, addr); - connection = connection_factory.createQuicNetworkConnection( - *persistent_info, *dispatcher, addr, Network::Address::InstanceConstSharedPtr()); + + Network::Address::InstanceConstSharedPtr local_address; + if (addr->ip()->version() == Network::Address::IpVersion::v4) { + local_address = Network::Utility::getLocalAddress(Network::Address::IpVersion::v4); + } else { + // Docker only works with loopback v6 address. + local_address = std::make_shared("::1"); + } + connection = connection_factory.createQuicNetworkConnection(*persistent_info, *dispatcher, addr, + local_address); connection->addConnectionCallbacks(connection_callbacks); } else { connection = From 2b9462601842bee3b53a79c32af2f04d9a7b748c Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 16 Mar 2021 22:51:34 -0400 Subject: [PATCH 11/52] fix setSan Signed-off-by: Dan Zhang --- .../transport_sockets/tls/integration/ssl_integration_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc index 940d480899ae..4b2de9460e15 100644 --- a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc +++ b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc @@ -121,7 +121,7 @@ TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferHttp2) { TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferVerifySAN) { ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection(ClientSslTransportOptions().setSan(true)); + return makeSslClientConnection(ClientSslTransportOptions()); }; testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); checkStats(); @@ -130,7 +130,7 @@ TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferVerifySAN) { TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferHttp2VerifySAN) { setDownstreamProtocol(Http::CodecClient::Type::HTTP2); ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection(ClientSslTransportOptions().setAlpn(true).setSan(true)); + return makeSslClientConnection(ClientSslTransportOptions().setAlpn(true)); }; testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); checkStats(); From 77cb93746b78960dbe0e20d56b599b0d6de3c174 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 17 Mar 2021 12:45:49 -0400 Subject: [PATCH 12/52] fix asan and gcc Signed-off-by: Dan Zhang --- .../integration/quic_http_integration_test.cc | 15 +++++---------- test/integration/http_integration.h | 6 ++++-- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc index f884cef33c5e..904bd4c12ef3 100644 --- a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc +++ b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc @@ -26,6 +26,7 @@ #pragma GCC diagnostic pop #endif +#include "extensions/quic_listeners/quiche/client_connection_factory_impl.h" #include "extensions/quic_listeners/quiche/envoy_quic_client_session.h" #include "extensions/quic_listeners/quiche/envoy_quic_client_connection.h" #include "extensions/quic_listeners/quiche/envoy_quic_proof_verifier.h" @@ -98,12 +99,11 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers getNextConnectionId(), server_addr_, conn_helper_, alarm_factory_, quic::ParsedQuicVersionVector{supported_versions_[0]}, local_addr, *dispatcher_, nullptr); quic_connection_ = connection.get(); - ASSERT(quic_transport_socket_factory_ != nullptr); + ASSERT(quic_connection_persistent_info_ != nullptr); + auto& persistent_info = static_cast(*quic_connection_persistent_info_); auto session = std::make_unique( - quic_config_, supported_versions_, std::move(connection), - quic::QuicServerId{transport_socket_factory_->clientContextConfig().serverNameIndication(), - static_cast(server_addr_->ip()->port()), false}, - crypto_config_.get(), &push_promise_index_, *dispatcher_, 0); + quic_config_, supported_versions_, std::move(connection), persistent_info.server_id_, + persistent_info.crypto_config_.get(), &push_promise_index_, *dispatcher_, 0); session->Initialize(); return session; } @@ -202,10 +202,6 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers registerTestServerPorts({"http"}); ASSERT(&transport_socket_factory_->clientContextConfig()); - - crypto_config_ = - std::make_unique(std::make_unique( - stats_store_, transport_socket_factory_->clientContextConfig(), timeSystem())); } void testMultipleQuicConnections() { @@ -256,7 +252,6 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers quic::QuicConfig quic_config_; quic::QuicClientPushPromiseIndex push_promise_index_; quic::ParsedQuicVersionVector supported_versions_; - std::unique_ptr crypto_config_; EnvoyQuicConnectionHelper conn_helper_; EnvoyQuicAlarmFactory alarm_factory_; CodecClientCallbacksForTest client_codec_callback_; diff --git a/test/integration/http_integration.h b/test/integration/http_integration.h index 77c5a7ebd201..5ec5bb2e5dc0 100644 --- a/test/integration/http_integration.h +++ b/test/integration/http_integration.h @@ -249,6 +249,10 @@ class HttpIntegrationTest : public BaseIntegrationTest { // Prefix listener stat with IP:port, including IP version dependent loopback address. std::string listenerStatPrefix(const std::string& stat_name); + Network::TransportSocketFactoryPtr quic_transport_socket_factory_; + // Must outlive |codec_client_| because it may not close connection till the end of its life + // scope. + std::unique_ptr quic_connection_persistent_info_; // The client making requests to Envoy. IntegrationCodecClientPtr codec_client_; // A placeholder for the first upstream connection. @@ -268,8 +272,6 @@ class HttpIntegrationTest : public BaseIntegrationTest { bool set_reuse_port_{false}; std::string san_to_match_{"spiffe://lyft.com/backend-team"}; - std::unique_ptr quic_connection_persistent_info_; - Network::TransportSocketFactoryPtr quic_transport_socket_factory_; }; // Helper class for integration tests using raw HTTP/2 frames From 4e9e56ac9b7810705a362c24a07483694b4743a7 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 17 Mar 2021 17:28:12 -0400 Subject: [PATCH 13/52] test size to large Signed-off-by: Dan Zhang --- test/extensions/quic_listeners/quiche/integration/BUILD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/extensions/quic_listeners/quiche/integration/BUILD b/test/extensions/quic_listeners/quiche/integration/BUILD index 2cbb38cf9273..c3cab4a52f76 100644 --- a/test/extensions/quic_listeners/quiche/integration/BUILD +++ b/test/extensions/quic_listeners/quiche/integration/BUILD @@ -10,12 +10,12 @@ envoy_package() envoy_cc_test( name = "quic_protocol_integration_test", - size = "medium", + size = "large", srcs = [ "quic_protocol_integration_test.cc", ], data = ["//test/config/integration/certs"], - shard_count = 6, + shard_count = 8, tags = [ "fails_on_clang_cl", "fails_on_windows", From 88398b8c6d959ad0a930d8d9e2f73560e1c7426a Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 17 Mar 2021 20:51:22 -0400 Subject: [PATCH 14/52] test size to large Signed-off-by: Dan Zhang --- test/extensions/quic_listeners/quiche/integration/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/quic_listeners/quiche/integration/BUILD b/test/extensions/quic_listeners/quiche/integration/BUILD index c3cab4a52f76..824db7a326ea 100644 --- a/test/extensions/quic_listeners/quiche/integration/BUILD +++ b/test/extensions/quic_listeners/quiche/integration/BUILD @@ -33,7 +33,7 @@ envoy_cc_test( envoy_cc_test( name = "quic_http_integration_test", - size = "medium", + size = "large", srcs = ["quic_http_integration_test.cc"], data = ["//test/config/integration/certs"], # TODO(envoyproxy/windows-dev): Diagnose failure shown only on clang-cl build, see: From 5c1172342758438a00524b45be0bb1b1cc7d10eb Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Thu, 18 Mar 2021 12:57:54 -0400 Subject: [PATCH 15/52] move config setup to config helper Signed-off-by: Dan Zhang --- test/config/utility.cc | 22 +++++++++++++++- test/config/utility.h | 7 +++-- .../integration/quic_http_integration_test.cc | 12 --------- .../quic_protocol_integration_test.cc | 5 ++-- test/integration/http_integration.cc | 26 +------------------ test/integration/http_protocol_integration.h | 8 +++--- 6 files changed, 33 insertions(+), 47 deletions(-) diff --git a/test/config/utility.cc b/test/config/utility.cc index 2ce73d362eb1..b04182c8f284 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -145,7 +145,10 @@ name: "envoy.filters.listener.tls_inspector" )EOF"; } -std::string ConfigHelper::httpProxyConfig() { +std::string ConfigHelper::httpProxyConfig(bool downstream_use_quic) { + if (downstream_use_quic) { + return quicHttpProxyConfig(); + } return absl::StrCat(baseConfig(), fmt::format(R"EOF( filter_chains: filters: @@ -1050,6 +1053,23 @@ void ConfigHelper::addSslConfig(const ServerSslOptions& options) { filter_chain->mutable_transport_socket()->mutable_typed_config()->PackFrom(tls_context); } +void ConfigHelper::addQuicDownstreamTransportSocketConfig(bool reuse_port) { + envoy::extensions::transport_sockets::quic::v3::QuicDownstreamTransport + quic_transport_socket_config; + auto tls_context = quic_transport_socket_config.mutable_downstream_tls_context(); + ConfigHelper::initializeTls(ConfigHelper::ServerSslOptions().setRsaCert(true).setTlsV13(true), + *tls_context->mutable_common_tls_context()); + for (auto& listener : *bootstrap_.mutable_static_resources()->mutable_listeners()) { + if (listener.udp_listener_config().listener_config().typed_config().type_url() == + "type.googleapis.com/envoy.config.listener.v3.QuicProtocolOptions") { + auto* filter_chain = listener.mutable_filter_chains(0); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->mutable_typed_config()->PackFrom(quic_transport_socket_config); + listener.set_reuse_port(reuse_port); + } + } +} + bool ConfigHelper::setAccessLog( const std::string& filename, absl::string_view format, std::vector formatters) { diff --git a/test/config/utility.h b/test/config/utility.h index 1a748f913b3a..73efd034e87e 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -90,7 +90,7 @@ class ConfigHelper { // By default, this runs with an L7 proxy config, but config can be set to TCP_PROXY_CONFIG // to test L4 proxying. ConfigHelper(const Network::Address::IpVersion version, Api::Api& api, - const std::string& config = httpProxyConfig()); + const std::string& config = httpProxyConfig(false)); static void initializeTls(const ServerSslOptions& options, @@ -111,7 +111,7 @@ class ConfigHelper { // A basic configuration for L4 proxying. static std::string tcpProxyConfig(); // A basic configuration for L7 proxying. - static std::string httpProxyConfig(); + static std::string httpProxyConfig(bool downstream_use_quic = false); // A basic configuration for L7 proxying with QUIC transport. static std::string quicHttpProxyConfig(); // A string for a basic buffer filter, which can be used with addFilter() @@ -217,6 +217,9 @@ class ConfigHelper { void addSslConfig(const ServerSslOptions& options); void addSslConfig() { addSslConfig({}); } + // Add the default SSL configuration for QUIC downstream. + void addQuicDownstreamTransportSocketConfig(bool resuse_port); + // Set the HTTP access log for the first HCM (if present) to a given file. The default is // the platform's null device. bool setAccessLog(const std::string& filename, absl::string_view format = "", diff --git a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc index 904bd4c12ef3..7d3b7f70b5aa 100644 --- a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc +++ b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc @@ -137,18 +137,6 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers void initialize() override { config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - envoy::extensions::transport_sockets::quic::v3::QuicDownstreamTransport - quic_transport_socket_config; - auto tls_context = quic_transport_socket_config.mutable_downstream_tls_context(); - ConfigHelper::initializeTls(ConfigHelper::ServerSslOptions().setRsaCert(true).setTlsV13(true), - *tls_context->mutable_common_tls_context()); - auto* filter_chain = - bootstrap.mutable_static_resources()->mutable_listeners(0)->mutable_filter_chains(0); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->mutable_typed_config()->PackFrom(quic_transport_socket_config); - - bootstrap.mutable_static_resources()->mutable_listeners(0)->set_reuse_port(set_reuse_port_); - const std::string overload_config = fmt::format(R"EOF( refresh_interval: diff --git a/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc b/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc index af4940d989fa..4d94b6783cad 100644 --- a/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc +++ b/test/extensions/quic_listeners/quiche/integration/quic_protocol_integration_test.cc @@ -2,27 +2,26 @@ namespace Envoy { -// This will run with HTTP/3 downstream, and HTTP/2 upstream. +// These will run with HTTP/3 downstream, and Http and HTTP/2 upstream. INSTANTIATE_TEST_SUITE_P(Protocols, DownstreamProtocolIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecClient::Type::HTTP3}, {FakeHttpConnection::Type::HTTP1, FakeHttpConnection::Type::HTTP2})), HttpProtocolIntegrationTest::protocolTestParamsToString); -// This will run with HTTP/3 downstream, and HTTP/2 upstream. INSTANTIATE_TEST_SUITE_P(DownstreamProtocols, ProtocolIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecClient::Type::HTTP3}, {FakeHttpConnection::Type::HTTP1, FakeHttpConnection::Type::HTTP2})), HttpProtocolIntegrationTest::protocolTestParamsToString); +// These will run with HTTP/1 and HTTP/2 downstream, and HTTP/3 upstream. INSTANTIATE_TEST_SUITE_P(UpstreamProtocols, DownstreamProtocolIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecClient::Type::HTTP1, Http::CodecClient::Type::HTTP2}, {FakeHttpConnection::Type::HTTP3})), HttpProtocolIntegrationTest::protocolTestParamsToString); -// This will run with HTTP/1 and HTTP/2 downstream, and HTTP/3 upstream. INSTANTIATE_TEST_SUITE_P(UpstreamProtocols, ProtocolIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecClient::Type::HTTP1, Http::CodecClient::Type::HTTP2}, diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 162623e649c8..92bc8bcf5cfd 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -313,32 +313,8 @@ void HttpIntegrationTest::initialize() { quic_transport_socket_factory_ = IntegrationUtil::createQuicClientTransportSocketFactory(mock_factory_ctx, san_to_match_); - config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - envoy::extensions::transport_sockets::quic::v3::QuicDownstreamTransport - quic_transport_socket_config; - auto tls_context = quic_transport_socket_config.mutable_downstream_tls_context(); - ConfigHelper::initializeTls(ConfigHelper::ServerSslOptions().setRsaCert(true).setTlsV13(true), - *tls_context->mutable_common_tls_context()); - for (auto& listener : *bootstrap.mutable_static_resources()->mutable_listeners()) { - if (listener.udp_listener_config().listener_config().typed_config().type_url() == - "type.googleapis.com/envoy.config.listener.v3.QuicProtocolOptions") { - auto* filter_chain = listener.mutable_filter_chains(0); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->mutable_typed_config()->PackFrom(quic_transport_socket_config); - - listener.set_reuse_port(set_reuse_port_); - } - } - }); + config_helper_.addQuicDownstreamTransportSocketConfig(set_reuse_port_); - config_helper_.addConfigModifier( - [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) { - hcm.mutable_drain_timeout()->clear_seconds(); - hcm.mutable_drain_timeout()->set_nanos(500 * 1000 * 1000); - EXPECT_EQ(hcm.codec_type(), envoy::extensions::filters::network::http_connection_manager:: - v3::HttpConnectionManager::HTTP3); - }); BaseIntegrationTest::initialize(); registerTestServerPorts({"http"}); diff --git a/test/integration/http_protocol_integration.h b/test/integration/http_protocol_integration.h index f65b15455116..05a91eed1bb1 100644 --- a/test/integration/http_protocol_integration.h +++ b/test/integration/http_protocol_integration.h @@ -49,10 +49,10 @@ class HttpProtocolIntegrationTest : public testing::TestWithParam& p); HttpProtocolIntegrationTest() - : HttpIntegrationTest(GetParam().downstream_protocol, GetParam().version, - GetParam().downstream_protocol == Http::CodecClient::Type::HTTP3 - ? ConfigHelper::quicHttpProxyConfig() - : ConfigHelper::httpProxyConfig()) {} + : HttpIntegrationTest( + GetParam().downstream_protocol, GetParam().version, + ConfigHelper::httpProxyConfig(/*downstream_is_quic=*/GetParam().downstream_protocol == + Http::CodecClient::Type::HTTP3)) {} void SetUp() override { setDownstreamProtocol(GetParam().downstream_protocol); From 4c2b490f14ac5c5a3c0888d4e6fa6dd6b64e105b Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 19 Mar 2021 15:49:44 -0400 Subject: [PATCH 16/52] fix setSan Signed-off-by: Dan Zhang --- .../tls/integration/ssl_integration_test.cc | 4 ++-- test/integration/integration_admin_test.h | 2 +- test/integration/ssl_utility.cc | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc index 4b2de9460e15..b7a1fe7526a2 100644 --- a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc +++ b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc @@ -121,7 +121,7 @@ TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferHttp2) { TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferVerifySAN) { ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection(ClientSslTransportOptions()); + return makeSslClientConnection(ClientSslTransportOptions().setSan(san_to_match_)); }; testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); checkStats(); @@ -130,7 +130,7 @@ TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferVerifySAN) { TEST_P(SslIntegrationTest, RouterRequestAndResponseWithBodyNoBufferHttp2VerifySAN) { setDownstreamProtocol(Http::CodecClient::Type::HTTP2); ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { - return makeSslClientConnection(ClientSslTransportOptions().setAlpn(true)); + return makeSslClientConnection(ClientSslTransportOptions().setAlpn(true).setSan(san_to_match_)); }; testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); checkStats(); diff --git a/test/integration/integration_admin_test.h b/test/integration/integration_admin_test.h index a63649e7ed71..487ac3b3c63e 100644 --- a/test/integration/integration_admin_test.h +++ b/test/integration/integration_admin_test.h @@ -76,7 +76,7 @@ class IntegrationAdminTest : public HttpProtocolIntegrationTest { } // Validate that the stats JSON has expected histograms element. - EXPECT_EQ(expected_hist_count, histogram_count); + EXPECT_EQ(expected_hist_count, histogram_count) << stats_json; } }; diff --git a/test/integration/ssl_utility.cc b/test/integration/ssl_utility.cc index 40fe484fd7e8..0d86dcc7137e 100644 --- a/test/integration/ssl_utility.cc +++ b/test/integration/ssl_utility.cc @@ -76,12 +76,8 @@ void initializeUpstreamTlsContextConfig( Network::TransportSocketFactoryPtr createClientSslTransportSocketFactory(const ClientSslTransportOptions& options, ContextManager& context_manager, Api::Api& api) { - ClientSslTransportOptions options_with_san = options; - if (options.san_.empty()) { - options_with_san.setSan("spiffe://lyft.com/backend-team"); - } envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext tls_context; - initializeUpstreamTlsContextConfig(options_with_san, tls_context); + initializeUpstreamTlsContextConfig(options, tls_context); NiceMock mock_factory_ctx; ON_CALL(mock_factory_ctx, api()).WillByDefault(ReturnRef(api)); From 97292b268702e6a8bb69f29a6363db748198fd26 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sun, 21 Mar 2021 23:49:58 -0400 Subject: [PATCH 17/52] change send single request Signed-off-by: Dan Zhang --- test/integration/http_integration.cc | 2 +- test/integration/utility.cc | 106 +++++++++++++++------------ test/integration/utility.h | 4 +- 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 92bc8bcf5cfd..edb612f9b962 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -311,7 +311,7 @@ void HttpIntegrationTest::initialize() { ON_CALL(mock_factory_ctx, api()).WillByDefault(testing::ReturnRef(*api_)); quic_transport_socket_factory_ = - IntegrationUtil::createQuicClientTransportSocketFactory(mock_factory_ctx, san_to_match_); + IntegrationUtil::createQuicUpstreamTransportSocketFactory(mock_factory_ctx, san_to_match_); config_helper_.addQuicDownstreamTransportSocketConfig(set_reuse_port_); diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 98a214635f42..996d24b95cf1 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -111,7 +111,7 @@ struct ConnectionCallbacks : public Network::ConnectionCallbacks { bool connected_{false}; }; -Network::TransportSocketFactoryPtr IntegrationUtil::createQuicClientTransportSocketFactory( +Network::TransportSocketFactoryPtr IntegrationUtil::createQuicUpstreamTransportSocketFactory( Server::Configuration::TransportSocketFactoryContext& context, const std::string& san_to_match) { envoy::extensions::transport_sockets::quic::v3::QuicUpstreamTransport @@ -128,6 +128,37 @@ Network::TransportSocketFactoryPtr IntegrationUtil::createQuicClientTransportSoc return config_factory.createTransportSocketFactory(quic_transport_socket_config, context); } +BufferingStreamDecoderPtr +sendRequestAndWaitForResponse(Event::Dispatcher& dispatcher, const std::string& method, + const std::string& url, const std::string& body, + const std::string& host, const std::string& content_type, + Http::CodecClientProd& client) { + BufferingStreamDecoderPtr response(new BufferingStreamDecoder([&]() -> void { + client.close(); + dispatcher.exit(); + })); + Http::RequestEncoder& encoder = client.newStream(*response); + encoder.getStream().addCallbacks(*response); + + Http::TestRequestHeaderMapImpl headers; + headers.setMethod(method); + headers.setPath(url); + headers.setHost(host); + headers.setReferenceScheme(Http::Headers::get().SchemeValues.Http); + if (!content_type.empty()) { + headers.setContentType(content_type); + } + const auto status = encoder.encodeHeaders(headers, body.empty()); + ASSERT(status.ok()); + if (!body.empty()) { + Buffer::OwnedImpl body_buffer(body); + encoder.encodeData(body_buffer, true); + } + + dispatcher.run(Event::Dispatcher::RunType::Block); + return response; +} + BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPtr& addr, const std::string& method, const std::string& url, @@ -147,64 +178,43 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt fmt::format("{}://127.0.0.1:80", (type == Http::CodecClient::Type::HTTP3 ? "udp" : "tcp")), time_system)}; + if (type <= Http::CodecClient::Type::HTTP2) { + Http::CodecClientProd client( + type, + dispatcher->createClientConnection(addr, Network::Address::InstanceConstSharedPtr(), + Network::Test::createRawBufferSocket(), nullptr), + host_description, *dispatcher, random); + return sendRequestAndWaitForResponse(*dispatcher, method, url, body, host, content_type, + client); + } + NiceMock mock_factory_ctx; ON_CALL(mock_factory_ctx, api()).WillByDefault(testing::ReturnRef(api)); Network::TransportSocketFactoryPtr transport_socket_factory = - createQuicClientTransportSocketFactory(mock_factory_ctx, "spiffe://lyft.com/backend-team"); + createQuicUpstreamTransportSocketFactory(mock_factory_ctx, "spiffe://lyft.com/backend-team"); std::unique_ptr persistent_info; - Network::ClientConnectionPtr connection; - if (type == Http::CodecClient::Type::HTTP3) { - Http::QuicClientConnectionFactory& connection_factory = - Config::Utility::getAndCheckFactoryByName( - Http::QuicCodecNames::get().Quiche); - persistent_info = connection_factory.createNetworkConnectionInfo( - *dispatcher, *transport_socket_factory, mock_stats_store, time_system, addr); - - Network::Address::InstanceConstSharedPtr local_address; - if (addr->ip()->version() == Network::Address::IpVersion::v4) { - local_address = Network::Utility::getLocalAddress(Network::Address::IpVersion::v4); - } else { - // Docker only works with loopback v6 address. - local_address = std::make_shared("::1"); - } - connection = connection_factory.createQuicNetworkConnection(*persistent_info, *dispatcher, addr, - local_address); - connection->addConnectionCallbacks(connection_callbacks); + Http::QuicClientConnectionFactory& connection_factory = + Config::Utility::getAndCheckFactoryByName( + Http::QuicCodecNames::get().Quiche); + persistent_info = connection_factory.createNetworkConnectionInfo( + *dispatcher, *transport_socket_factory, mock_stats_store, time_system, addr); + + Network::Address::InstanceConstSharedPtr local_address; + if (addr->ip()->version() == Network::Address::IpVersion::v4) { + local_address = Network::Utility::getLocalAddress(Network::Address::IpVersion::v4); } else { - connection = - dispatcher->createClientConnection(addr, Network::Address::InstanceConstSharedPtr(), - Network::Test::createRawBufferSocket(), nullptr); + // Docker only works with loopback v6 address. + local_address = std::make_shared("::1"); } + Network::ClientConnectionPtr connection = connection_factory.createQuicNetworkConnection( + *persistent_info, *dispatcher, addr, local_address); + connection->addConnectionCallbacks(connection_callbacks); Http::CodecClientProd client(type, std::move(connection), host_description, *dispatcher, random); if (type == Http::CodecClient::Type::HTTP3) { // Quic connection needs to finish handshake. dispatcher->run(Event::Dispatcher::RunType::Block); } - - BufferingStreamDecoderPtr response(new BufferingStreamDecoder([&]() -> void { - client.close(); - dispatcher->exit(); - })); - Http::RequestEncoder& encoder = client.newStream(*response); - encoder.getStream().addCallbacks(*response); - - Http::TestRequestHeaderMapImpl headers; - headers.setMethod(method); - headers.setPath(url); - headers.setHost(host); - headers.setReferenceScheme(Http::Headers::get().SchemeValues.Http); - if (!content_type.empty()) { - headers.setContentType(content_type); - } - const auto status = encoder.encodeHeaders(headers, body.empty()); - ASSERT(status.ok()); - if (!body.empty()) { - Buffer::OwnedImpl body_buffer(body); - encoder.encodeData(body_buffer, true); - } - - dispatcher->run(Event::Dispatcher::RunType::Block); - return response; + return sendRequestAndWaitForResponse(*dispatcher, method, url, body, host, content_type, client); } BufferingStreamDecoderPtr diff --git a/test/integration/utility.h b/test/integration/utility.h index 3c82b61f7e61..9590685c8b0a 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -191,13 +191,13 @@ class IntegrationUtil { const std::string& content_type = ""); /** - * Create transport socket factory for Quic client transport socket. + * Create transport socket factory for Quic upstream transport socket. * @param context supplies the port to connect to on localhost. * @param san_to_match configs |context| to match Subject Alternative Name during certificate * verification. * @return TransportSocketFactoryPtr the client transport socket factory. */ - static Network::TransportSocketFactoryPtr createQuicClientTransportSocketFactory( + static Network::TransportSocketFactoryPtr createQuicUpstreamTransportSocketFactory( Server::Configuration::TransportSocketFactoryContext& context, const std::string& san_to_match); }; From 280fc25b4a78ad2fa41ff395bb03d41ceb4439de Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Mon, 22 Mar 2021 16:12:23 -0400 Subject: [PATCH 18/52] address comments Signed-off-by: Dan Zhang --- .../quiche/platform/quic_logging_impl.h | 2 -- test/config/utility.cc | 1 + .../quiche/envoy_quic_utils_test.cc | 3 ++ .../integration/quic_http_integration_test.cc | 5 --- test/integration/http_integration.cc | 13 +++++--- test/integration/protocol_integration_test.cc | 12 ++++++- test/integration/ssl_utility.cc | 2 -- test/integration/utility.cc | 31 +++++++++++-------- test/integration/utility.h | 5 ++- 9 files changed, 44 insertions(+), 30 deletions(-) diff --git a/source/extensions/quic_listeners/quiche/platform/quic_logging_impl.h b/source/extensions/quic_listeners/quiche/platform/quic_logging_impl.h index 905cc62bbf25..939226420b06 100644 --- a/source/extensions/quic_listeners/quiche/platform/quic_logging_impl.h +++ b/source/extensions/quic_listeners/quiche/platform/quic_logging_impl.h @@ -127,7 +127,6 @@ namespace quic { using QuicLogLevel = spdlog::level::level_enum; static const QuicLogLevel TRACE = spdlog::level::trace; -static const QuicLogLevel DEBUG = spdlog::level::debug; static const QuicLogLevel INFO = spdlog::level::info; static const QuicLogLevel WARNING = spdlog::level::warn; static const QuicLogLevel ERROR = spdlog::level::err; @@ -184,7 +183,6 @@ inline spdlog::logger& GetLogger() { #define QUICHE_IS_LOG_LEVEL_ENABLED_IMPL(severity) \ inline bool isLogLevelEnabled##severity() { return quic::severity >= GetLogger().level(); } QUICHE_IS_LOG_LEVEL_ENABLED_IMPL(TRACE) -QUICHE_IS_LOG_LEVEL_ENABLED_IMPL(DEBUG) QUICHE_IS_LOG_LEVEL_ENABLED_IMPL(INFO) QUICHE_IS_LOG_LEVEL_ENABLED_IMPL(WARNING) QUICHE_IS_LOG_LEVEL_ENABLED_IMPL(ERROR) diff --git a/test/config/utility.cc b/test/config/utility.cc index e1c8ef324f68..9f25cc985e08 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -1062,6 +1062,7 @@ void ConfigHelper::addQuicDownstreamTransportSocketConfig(bool reuse_port) { for (auto& listener : *bootstrap_.mutable_static_resources()->mutable_listeners()) { if (listener.udp_listener_config().listener_config().typed_config().type_url() == "type.googleapis.com/envoy.config.listener.v3.QuicProtocolOptions") { + ASSERT(listener.filter_chains_size() > 0); auto* filter_chain = listener.mutable_filter_chains(0); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->mutable_typed_config()->PackFrom(quic_transport_socket_config); diff --git a/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc b/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc index d8f5af4c4005..dade0cc4c340 100644 --- a/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc +++ b/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc @@ -50,9 +50,12 @@ TEST(EnvoyQuicUtilsTest, HeadersConversion) { headers_block[":authority"] = "www.google.com"; headers_block[":path"] = "/index.hml"; headers_block[":scheme"] = "https"; + // "value1" and "value2" should be coalesced into one header by QUICHE and splitted again while + // converting to Envoy headers.. headers_block.AppendValueOrAddHeader("key", "value1"); headers_block.AppendValueOrAddHeader("key", "value2"); auto envoy_headers = spdyHeaderBlockToEnvoyHeaders(headers_block); + // Envoy header block is 1 header larger because QUICHE header block does coalescing. EXPECT_EQ(headers_block.size() + 1u, envoy_headers->size()); EXPECT_EQ("www.google.com", envoy_headers->getHostValue()); EXPECT_EQ("/index.hml", envoy_headers->getPathValue()); diff --git a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc index 7d3b7f70b5aa..8b0ba7e96639 100644 --- a/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc +++ b/test/extensions/quic_listeners/quiche/integration/quic_http_integration_test.cc @@ -108,11 +108,6 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers return session; } - // This call may fail because of INVALID_VERSION, because QUIC connection doesn't support - // in-connection version negotiation. - // TODO(#8479) Propagate INVALID_VERSION error to caller and let caller to use server advertised - // version list to create a new connection with mutually supported version and make client codec - // again. IntegrationCodecClientPtr makeRawHttpConnection( Network::ClientConnectionPtr&& conn, absl::optional http2_options) override { diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index edb612f9b962..25a9bf65b7b5 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -258,6 +258,9 @@ IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection( host_description, downstream_protocol_); if (downstream_protocol_ == Http::CodecClient::Type::HTTP3 && codec->disconnected()) { // Connection may get closed during version negotiation or handshake. + // TODO(#8479) QUIC connection doesn't support in-connection version negotiationPropagate + // INVALID_VERSION error to caller and let caller to use server advertised version list to + // create a new connection with mutually supported version and make client codec again. ENVOY_LOG(error, "Fail to connect to server with error: {}", codec->connection()->transportFailureReason()); } @@ -307,12 +310,13 @@ void HttpIntegrationTest::initialize() { if (downstream_protocol_ != Http::CodecClient::Type::HTTP3) { return BaseIntegrationTest::initialize(); } - NiceMock mock_factory_ctx; - ON_CALL(mock_factory_ctx, api()).WillByDefault(testing::ReturnRef(*api_)); - + // Needs to be instantiated before base class calls initialize() which starts a QUIC listener + // according to the config. quic_transport_socket_factory_ = - IntegrationUtil::createQuicUpstreamTransportSocketFactory(mock_factory_ctx, san_to_match_); + IntegrationUtil::createQuicUpstreamTransportSocketFactory(*api_, san_to_match_); + // Needed to config QUIC transport socket factory, and needs to be added before base class calls + // initialize(). config_helper_.addQuicDownstreamTransportSocketConfig(set_reuse_port_); BaseIntegrationTest::initialize(); @@ -320,6 +324,7 @@ void HttpIntegrationTest::initialize() { Network::Address::InstanceConstSharedPtr server_addr = Network::Utility::resolveUrl(fmt::format( "udp://{}:{}", Network::Test::getLoopbackAddressUrlString(version_), lookupPort("http"))); + // Needs to outlive all QUIC connections. quic_connection_persistent_info_ = Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 3e88379c7550..4b8055873826 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -59,6 +59,7 @@ void setDoNotValidateRouteConfig( return; \ } +// TODO(#2557) fix all the failures. #define EXCLUDE_DOWNSTREAM_HTTP3 \ if (downstreamProtocol() == Http::CodecClient::Type::HTTP3) { \ return; \ @@ -746,6 +747,7 @@ TEST_P(DownstreamProtocolIntegrationTest, RetryAttemptCountHeader) { TEST_P(DownstreamProtocolIntegrationTest, RetryPriority) { if (upstreamProtocol() == FakeHttpConnection::Type::HTTP2 && downstreamProtocol() == Http::CodecClient::Type::HTTP3) { + // TODO(alyssawilk) investigate why this combination doesn't work. return; } EXCLUDE_UPSTREAM_HTTP3; @@ -1144,6 +1146,7 @@ TEST_P(ProtocolIntegrationTest, HeadersWithUnderscoresRemainByDefault) { // Verify that request with headers containing underscores is rejected when configured. TEST_P(DownstreamProtocolIntegrationTest, HeadersWithUnderscoresCauseRequestRejectedByDefault) { + // TODO(danzh) pass headers_with_underscores_action config into QUIC stream. EXCLUDE_DOWNSTREAM_HTTP3 useAccessLog("%RESPONSE_FLAGS% %RESPONSE_CODE_DETAILS%"); config_helper_.addConfigModifier( @@ -1306,6 +1309,7 @@ TEST_P(ProtocolIntegrationTest, MissingStatus) { // Validate that lots of tiny cookies doesn't cause a DoS (single cookie header). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { + // TODO(danzh) re-enable this test after quic headers size become configurable. EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; initialize(); @@ -1330,6 +1334,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { // Validate that lots of tiny cookies doesn't cause a DoS (many cookie headers). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingMany) { + // TODO(danzh) re-enable this test after quic headers size become configurable. EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Set header count limit to 2010. @@ -1542,6 +1547,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlAccepted) { + // TODO(danzh) re-enable this test after quic headers size become configurable. EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Send one 95 kB URL with limit 96 kB headers. @@ -1636,6 +1642,7 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersAccepted) { // This test uses an Http::HeaderMapImpl instead of an Http::TestHeaderMapImpl to avoid // time-consuming byte size validations that will cause this test to timeout. TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersTimeout) { + // TODO(danzh) re-enable this test after quic headers size become configurable. EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Set timeout for 5 seconds, and ensure that a request with 10k+ headers can be sent. @@ -1643,6 +1650,7 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersTimeout) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersAccepted) { + // TODO(danzh) re-enable this test after quic headers size become configurable. EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); @@ -1650,6 +1658,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersRejected) { + // TODO(danzh) investigate why it failed for H3. EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); @@ -1754,7 +1763,7 @@ TEST_P(ProtocolIntegrationTest, LargeRequestMethod) { // Tests StopAllIterationAndBuffer. Verifies decode-headers-return-stop-all-filter calls decodeData // once after iteration is resumed. TEST_P(DownstreamProtocolIntegrationTest, TestDecodeHeadersReturnsStopAll) { - // Enable after setting QUICHE stream initial flow control window from http3 options. + // TODO(danzh) Enable after setting QUICHE stream initial flow control window from http3 options. EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: call-decodedata-once-filter @@ -1961,6 +1970,7 @@ name: encode-headers-return-stop-all-filter // Tests encodeHeaders() returns StopAllIterationAndWatermark. TEST_P(DownstreamProtocolIntegrationTest, TestEncodeHeadersReturnsStopAllWatermark) { + // TODO(danzh) Re-enable after codec buffer can be set according to http3 options. EXCLUDE_DOWNSTREAM_HTTP3 config_helper_.addFilter(R"EOF( name: encode-headers-return-stop-all-filter diff --git a/test/integration/ssl_utility.cc b/test/integration/ssl_utility.cc index c46a226695fe..0afb4b6d80fb 100644 --- a/test/integration/ssl_utility.cc +++ b/test/integration/ssl_utility.cc @@ -65,8 +65,6 @@ void initializeUpstreamTlsContextConfig( common_context->add_alpn_protocols(Http::Utility::AlpnNames::get().Http3); } if (!options.san_.empty()) { - // common_context->mutable_validation_context() - // ->add_hidden_envoy_deprecated_verify_subject_alt_name(options.san_); common_context->mutable_validation_context()->add_match_subject_alt_names()->set_exact( options.san_); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 996d24b95cf1..4cb675a1f4db 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -83,15 +83,21 @@ void BufferingStreamDecoder::onResetStream(Http::StreamResetReason, absl::string ADD_FAILURE(); } -struct ConnectionCallbacks : public Network::ConnectionCallbacks { - ConnectionCallbacks(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {} +// A callback for a QUIC client connection to unblock the test after handshake succeeds. QUIC +// network connection initiates handshake and raises Connected event when it's done. Tests should +// proceed with sending requests afterwards. +class TestConnectionCallbacks : public Network::ConnectionCallbacks { +public: + TestConnectionCallbacks(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {} // Network::ConnectionCallbacks void onEvent(Network::ConnectionEvent event) override { if (event == Network::ConnectionEvent::Connected) { + // Handshake finished, unblock the test to continue. connected_ = true; dispatcher_.exit(); } else if (event == Network::ConnectionEvent::RemoteClose) { + // If the peer closes the connection, no need to wait anymore. dispatcher_.exit(); } else { if (!connected_) { @@ -107,13 +113,16 @@ struct ConnectionCallbacks : public Network::ConnectionCallbacks { void onAboveWriteBufferHighWatermark() override {} void onBelowWriteBufferLowWatermark() override {} +private: Event::Dispatcher& dispatcher_; bool connected_{false}; }; -Network::TransportSocketFactoryPtr IntegrationUtil::createQuicUpstreamTransportSocketFactory( - Server::Configuration::TransportSocketFactoryContext& context, - const std::string& san_to_match) { +Network::TransportSocketFactoryPtr +IntegrationUtil::createQuicUpstreamTransportSocketFactory(Api::Api& api, + const std::string& san_to_match) { + NiceMock context; + ON_CALL(context, api()).WillByDefault(testing::ReturnRef(api)); envoy::extensions::transport_sockets::quic::v3::QuicUpstreamTransport quic_transport_socket_config; auto* tls_context = quic_transport_socket_config.mutable_upstream_tls_context(); @@ -171,7 +180,7 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt Api::Impl api(Thread::threadFactoryForTest(), mock_stats_store, time_system, Filesystem::fileSystemForTest(), random_generator); Event::DispatcherPtr dispatcher(api.allocateDispatcher("test_thread")); - ConnectionCallbacks connection_callbacks(*dispatcher); + TestConnectionCallbacks connection_callbacks(*dispatcher); std::shared_ptr cluster{new NiceMock()}; Upstream::HostDescriptionConstSharedPtr host_description{Upstream::makeTestHostDescription( cluster, @@ -188,10 +197,8 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt client); } - NiceMock mock_factory_ctx; - ON_CALL(mock_factory_ctx, api()).WillByDefault(testing::ReturnRef(api)); Network::TransportSocketFactoryPtr transport_socket_factory = - createQuicUpstreamTransportSocketFactory(mock_factory_ctx, "spiffe://lyft.com/backend-team"); + createQuicUpstreamTransportSocketFactory(api, "spiffe://lyft.com/backend-team"); std::unique_ptr persistent_info; Http::QuicClientConnectionFactory& connection_factory = Config::Utility::getAndCheckFactoryByName( @@ -210,10 +217,8 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt *persistent_info, *dispatcher, addr, local_address); connection->addConnectionCallbacks(connection_callbacks); Http::CodecClientProd client(type, std::move(connection), host_description, *dispatcher, random); - if (type == Http::CodecClient::Type::HTTP3) { - // Quic connection needs to finish handshake. - dispatcher->run(Event::Dispatcher::RunType::Block); - } + // Quic connection needs to finish handshake. + dispatcher->run(Event::Dispatcher::RunType::Block); return sendRequestAndWaitForResponse(*dispatcher, method, url, body, host, content_type, client); } diff --git a/test/integration/utility.h b/test/integration/utility.h index 9590685c8b0a..d05a222821ba 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -197,9 +197,8 @@ class IntegrationUtil { * verification. * @return TransportSocketFactoryPtr the client transport socket factory. */ - static Network::TransportSocketFactoryPtr createQuicUpstreamTransportSocketFactory( - Server::Configuration::TransportSocketFactoryContext& context, - const std::string& san_to_match); + static Network::TransportSocketFactoryPtr + createQuicUpstreamTransportSocketFactory(Api::Api& api, const std::string& san_to_match); }; // A set of connection callbacks which tracks connection state. From 3c029786121ee6fedc87f8da39e9f4ae47a10d29 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Mon, 22 Mar 2021 18:33:08 -0400 Subject: [PATCH 19/52] fix comment Signed-off-by: Dan Zhang --- .../extensions/quic_listeners/quiche/envoy_quic_utils_test.cc | 2 +- test/integration/utility.cc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc b/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc index dade0cc4c340..4ac8561f39c0 100644 --- a/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc +++ b/test/extensions/quic_listeners/quiche/envoy_quic_utils_test.cc @@ -50,7 +50,7 @@ TEST(EnvoyQuicUtilsTest, HeadersConversion) { headers_block[":authority"] = "www.google.com"; headers_block[":path"] = "/index.hml"; headers_block[":scheme"] = "https"; - // "value1" and "value2" should be coalesced into one header by QUICHE and splitted again while + // "value1" and "value2" should be coalesced into one header by QUICHE and split again while // converting to Envoy headers.. headers_block.AppendValueOrAddHeader("key", "value1"); headers_block.AppendValueOrAddHeader("key", "value2"); diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 4cb675a1f4db..d461516e038b 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -93,7 +93,9 @@ class TestConnectionCallbacks : public Network::ConnectionCallbacks { // Network::ConnectionCallbacks void onEvent(Network::ConnectionEvent event) override { if (event == Network::ConnectionEvent::Connected) { - // Handshake finished, unblock the test to continue. + // Handshake finished, unblock the test to continue. This is needed because we call + // Dispatcher::run() with Block to wait for the handshake to finish before proceeding. + // TODO(danzh) find an alternative approach with behaviors more in parallel with SSL. connected_ = true; dispatcher_.exit(); } else if (event == Network::ConnectionEvent::RemoteClose) { From c31225b4fc7d0293efd9e94762c9abe76b9a78e4 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 23 Mar 2021 14:55:30 -0400 Subject: [PATCH 20/52] comment about \0 Signed-off-by: Dan Zhang --- source/extensions/quic_listeners/quiche/envoy_quic_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_utils.h b/source/extensions/quic_listeners/quiche/envoy_quic_utils.h index ef68543df62d..9d59e6b0e4e1 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_utils.h +++ b/source/extensions/quic_listeners/quiche/envoy_quic_utils.h @@ -61,6 +61,7 @@ std::unique_ptr spdyHeaderBlockToEnvoyHeaders(const spdy::SpdyHeaderBlock& he for (auto entry : header_block) { // TODO(danzh): Avoid temporary strings and addCopy() with string_view. std::string key(entry.first); + // QUICHE coalesces mulitple trailer values with the same key with '\0'. std::vector values = absl::StrSplit(entry.second, '\0'); for (const absl::string_view& value : values) { headers->addCopy(Http::LowerCaseString(key), value); From 2dd1ae5e3dae50441b23421d424c6924c95bb2ad Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 23 Mar 2021 18:01:12 -0400 Subject: [PATCH 21/52] fix typo Signed-off-by: Dan Zhang --- source/extensions/quic_listeners/quiche/envoy_quic_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_utils.h b/source/extensions/quic_listeners/quiche/envoy_quic_utils.h index 9d59e6b0e4e1..209b1b0c21ae 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_utils.h +++ b/source/extensions/quic_listeners/quiche/envoy_quic_utils.h @@ -61,7 +61,7 @@ std::unique_ptr spdyHeaderBlockToEnvoyHeaders(const spdy::SpdyHeaderBlock& he for (auto entry : header_block) { // TODO(danzh): Avoid temporary strings and addCopy() with string_view. std::string key(entry.first); - // QUICHE coalesces mulitple trailer values with the same key with '\0'. + // QUICHE coalesces multiple trailer values with the same key with '\0'. std::vector values = absl::StrSplit(entry.second, '\0'); for (const absl::string_view& value : values) { headers->addCopy(Http::LowerCaseString(key), value); From 60015bfcb1675e936080df411ca07d47d3c3e2c7 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 24 Mar 2021 01:16:49 -0400 Subject: [PATCH 22/52] plumb header size and underscore action Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 2 +- source/common/http/http3/quic_codec_factory.h | 8 ++++++-- .../filters/network/http_connection_manager/config.cc | 2 +- source/extensions/quic_listeners/quiche/codec_impl.cc | 10 +++++++--- source/extensions/quic_listeners/quiche/codec_impl.h | 6 ++++-- test/integration/fake_upstream.cc | 2 +- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index f6d457403cb7..cb9881a1ff77 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -185,7 +185,7 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne codec_ = std::unique_ptr( Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) - .createQuicClientConnection(*connection_, *this)); + .createQuicClientConnection(*connection_, *this, Http::DEFAULT_MAX_REQUEST_HEADERS_KB)); break; } } diff --git a/source/common/http/http3/quic_codec_factory.h b/source/common/http/http3/quic_codec_factory.h index 0b4a72404200..d4313b5304b3 100644 --- a/source/common/http/http3/quic_codec_factory.h +++ b/source/common/http/http3/quic_codec_factory.h @@ -5,6 +5,7 @@ #include "envoy/config/typed_config.h" #include "envoy/http/codec.h" #include "envoy/network/connection.h" +#include "envoy/config/core/v3/protocol.pb.h" namespace Envoy { namespace Http { @@ -15,7 +16,10 @@ class QuicHttpServerConnectionFactory : public Config::UntypedFactory { ~QuicHttpServerConnectionFactory() override = default; virtual std::unique_ptr - createQuicServerConnection(Network::Connection& connection, ConnectionCallbacks& callbacks) PURE; + createQuicServerConnection(Network::Connection& connection, ConnectionCallbacks& callbacks, + const uint32_t max_request_headers_kb, + envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction + headers_with_underscores_action) PURE; std::string category() const override { return "envoy.quic_client_codec"; } }; @@ -26,7 +30,7 @@ class QuicHttpClientConnectionFactory : public Config::UntypedFactory { ~QuicHttpClientConnectionFactory() override = default; virtual std::unique_ptr - createQuicClientConnection(Network::Connection& connection, ConnectionCallbacks& callbacks) PURE; + createQuicClientConnection(Network::Connection& connection, ConnectionCallbacks& callbacks, const uint32_t /*max_request_headers_kb*/) PURE; std::string category() const override { return "envoy.quic_server_codec"; } }; diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index 6475de5f7ce1..1949eae60da6 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -582,7 +582,7 @@ HttpConnectionManagerConfig::createCodec(Network::Connection& connection, return std::unique_ptr( Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) - .createQuicServerConnection(connection, callbacks)); + .createQuicServerConnection(connection, callbacks, maxRequestHeadersKb(), headersWithUnderscoresAction())); case CodecType::AUTO: return Http::ConnectionManagerUtility::autoCreateCodec( connection, data, callbacks, context_.scope(), context_.api().randomGenerator(), diff --git a/source/extensions/quic_listeners/quiche/codec_impl.cc b/source/extensions/quic_listeners/quiche/codec_impl.cc index 950d914c755b..6a064b2e745d 100644 --- a/source/extensions/quic_listeners/quiche/codec_impl.cc +++ b/source/extensions/quic_listeners/quiche/codec_impl.cc @@ -95,16 +95,20 @@ void QuicHttpClientConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWate std::unique_ptr QuicHttpClientConnectionFactoryImpl::createQuicClientConnection( - Network::Connection& connection, Http::ConnectionCallbacks& callbacks) { + Network::Connection& connection, Http::ConnectionCallbacks& callbacks, const uint32_t /*max_request_headers_kb*/) { return std::make_unique( dynamic_cast(connection), callbacks); } std::unique_ptr QuicHttpServerConnectionFactoryImpl::createQuicServerConnection( - Network::Connection& connection, Http::ConnectionCallbacks& callbacks) { + Network::Connection& connection, Http::ConnectionCallbacks& callbacks, const uint32_t /*max_request_headers_kb*/, + envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction + /*headers_with_underscores_action*/) { + auto& quic_session = dynamic_cast(connection); + // quic_session return std::make_unique( - dynamic_cast(connection), + quic_session, dynamic_cast(callbacks)); } diff --git a/source/extensions/quic_listeners/quiche/codec_impl.h b/source/extensions/quic_listeners/quiche/codec_impl.h index b13f85b3d341..43de15420f02 100644 --- a/source/extensions/quic_listeners/quiche/codec_impl.h +++ b/source/extensions/quic_listeners/quiche/codec_impl.h @@ -77,7 +77,7 @@ class QuicHttpClientConnectionFactoryImpl : public Http::QuicHttpClientConnectio public: std::unique_ptr createQuicClientConnection(Network::Connection& connection, - Http::ConnectionCallbacks& callbacks) override; + Http::ConnectionCallbacks& callbacks, const uint32_t max_request_headers_kb) override; std::string name() const override { return Http::QuicCodecNames::get().Quiche; } }; @@ -87,7 +87,9 @@ class QuicHttpServerConnectionFactoryImpl : public Http::QuicHttpServerConnectio public: std::unique_ptr createQuicServerConnection(Network::Connection& connection, - Http::ConnectionCallbacks& callbacks) override; + Http::ConnectionCallbacks& callbacks, const uint32_t max_request_headers_kb, + envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction + headers_with_underscores_action) override; std::string name() const override { return Http::QuicCodecNames::get().Quiche; } }; diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 2869c8c9c4f0..bcaf0145ac96 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -342,7 +342,7 @@ FakeHttpConnection::FakeHttpConnection( codec_ = std::unique_ptr( Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) - .createQuicServerConnection(shared_connection_.connection(), *this)); + .createQuicServerConnection(shared_connection_.connection(), *this, max_request_headers_kb, headers_with_underscores_action)); } shared_connection_.connection().addReadFilter( Network::ReadFilterSharedPtr{new ReadFilter(*this)}); From 71eb14c23438e983bf1baf4998f54729ebf407bd Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 24 Mar 2021 12:38:35 -0400 Subject: [PATCH 23/52] fail at QUICHE_CHECK Signed-off-by: Dan Zhang --- source/extensions/quic_listeners/quiche/codec_impl.cc | 4 ++-- .../quic_listeners/quiche/envoy_quic_client_session.cc | 2 +- .../quic_listeners/quiche/envoy_quic_server_session.cc | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/source/extensions/quic_listeners/quiche/codec_impl.cc b/source/extensions/quic_listeners/quiche/codec_impl.cc index 6a064b2e745d..43b008e5bf32 100644 --- a/source/extensions/quic_listeners/quiche/codec_impl.cc +++ b/source/extensions/quic_listeners/quiche/codec_impl.cc @@ -102,11 +102,11 @@ QuicHttpClientConnectionFactoryImpl::createQuicClientConnection( std::unique_ptr QuicHttpServerConnectionFactoryImpl::createQuicServerConnection( - Network::Connection& connection, Http::ConnectionCallbacks& callbacks, const uint32_t /*max_request_headers_kb*/, + Network::Connection& connection, Http::ConnectionCallbacks& callbacks, const uint32_t max_request_headers_kb, envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction /*headers_with_underscores_action*/) { auto& quic_session = dynamic_cast(connection); - // quic_session + quic_session.SetMaxInboundHeaderListSize(max_request_headers_kb * 1024); return std::make_unique( quic_session, dynamic_cast(callbacks)); diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_client_session.cc b/source/extensions/quic_listeners/quiche/envoy_quic_client_session.cc index c37d0ceb3dbf..fc0ca63f7560 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_client_session.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_client_session.cc @@ -16,7 +16,7 @@ EnvoyQuicClientSession::EnvoyQuicClientSession( crypto_config, push_promise_index), host_name_(server_id.host()) { // HTTP/3 header limits should be configurable, but for now hard-code to Envoy defaults. - set_max_inbound_header_list_size(Http::DEFAULT_MAX_REQUEST_HEADERS_KB * 1000); + SetMaxInboundHeaderListSize(Http::DEFAULT_MAX_REQUEST_HEADERS_KB * 1000); } EnvoyQuicClientSession::~EnvoyQuicClientSession() { diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc b/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc index bdfb2315b172..487f363da42f 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc @@ -20,8 +20,6 @@ EnvoyQuicServerSession::EnvoyQuicServerSession( crypto_config, compressed_certs_cache), QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), quic_connection_(std::move(connection)), listener_config_(listener_config) { - // HTTP/3 header limits should be configurable, but for now hard-code to Envoy defaults. - set_max_inbound_header_list_size(Http::DEFAULT_MAX_REQUEST_HEADERS_KB * 1000); } EnvoyQuicServerSession::~EnvoyQuicServerSession() { From 4c45c9feebf77782b9471835cc8faec86d73c4c1 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 24 Mar 2021 23:44:28 -0400 Subject: [PATCH 24/52] fix filter initialize order Signed-off-by: Dan Zhang --- source/common/http/http3/BUILD | 1 + source/common/http/http3/quic_codec_factory.h | 11 +++++----- .../network/http_connection_manager/config.cc | 3 ++- .../quic_listeners/quiche/codec_impl.cc | 14 +++++++----- .../quic_listeners/quiche/codec_impl.h | 10 ++++----- .../quiche/envoy_quic_server_session.cc | 17 ++++++++++---- .../quiche/envoy_quic_server_session.h | 3 +++ .../quiche/envoy_quic_dispatcher_test.cc | 2 ++ test/integration/fake_upstream.cc | 3 ++- test/integration/protocol_integration_test.cc | 22 ++++++++----------- 10 files changed, 51 insertions(+), 35 deletions(-) diff --git a/source/common/http/http3/BUILD b/source/common/http/http3/BUILD index 112ca100d629..8a2dd3fc89d8 100644 --- a/source/common/http/http3/BUILD +++ b/source/common/http/http3/BUILD @@ -28,6 +28,7 @@ envoy_cc_library( "//include/envoy/config:typed_config_interface", "//include/envoy/http:codec_interface", "//include/envoy/network:connection_interface", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/source/common/http/http3/quic_codec_factory.h b/source/common/http/http3/quic_codec_factory.h index d4313b5304b3..f477fdb25c54 100644 --- a/source/common/http/http3/quic_codec_factory.h +++ b/source/common/http/http3/quic_codec_factory.h @@ -2,10 +2,10 @@ #include +#include "envoy/config/core/v3/protocol.pb.h" #include "envoy/config/typed_config.h" #include "envoy/http/codec.h" #include "envoy/network/connection.h" -#include "envoy/config/core/v3/protocol.pb.h" namespace Envoy { namespace Http { @@ -15,9 +15,9 @@ class QuicHttpServerConnectionFactory : public Config::UntypedFactory { public: ~QuicHttpServerConnectionFactory() override = default; - virtual std::unique_ptr - createQuicServerConnection(Network::Connection& connection, ConnectionCallbacks& callbacks, - const uint32_t max_request_headers_kb, + virtual std::unique_ptr createQuicServerConnection( + Network::Connection& connection, ConnectionCallbacks& callbacks, + const uint32_t max_request_headers_kb, envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction headers_with_underscores_action) PURE; @@ -30,7 +30,8 @@ class QuicHttpClientConnectionFactory : public Config::UntypedFactory { ~QuicHttpClientConnectionFactory() override = default; virtual std::unique_ptr - createQuicClientConnection(Network::Connection& connection, ConnectionCallbacks& callbacks, const uint32_t /*max_request_headers_kb*/) PURE; + createQuicClientConnection(Network::Connection& connection, ConnectionCallbacks& callbacks, + const uint32_t /*max_request_headers_kb*/) PURE; std::string category() const override { return "envoy.quic_server_codec"; } }; diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index 1949eae60da6..f7147c656a97 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -582,7 +582,8 @@ HttpConnectionManagerConfig::createCodec(Network::Connection& connection, return std::unique_ptr( Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) - .createQuicServerConnection(connection, callbacks, maxRequestHeadersKb(), headersWithUnderscoresAction())); + .createQuicServerConnection(connection, callbacks, maxRequestHeadersKb(), + headersWithUnderscoresAction())); case CodecType::AUTO: return Http::ConnectionManagerUtility::autoCreateCodec( connection, data, callbacks, context_.scope(), context_.api().randomGenerator(), diff --git a/source/extensions/quic_listeners/quiche/codec_impl.cc b/source/extensions/quic_listeners/quiche/codec_impl.cc index 43b008e5bf32..3c4e4743837c 100644 --- a/source/extensions/quic_listeners/quiche/codec_impl.cc +++ b/source/extensions/quic_listeners/quiche/codec_impl.cc @@ -95,21 +95,23 @@ void QuicHttpClientConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWate std::unique_ptr QuicHttpClientConnectionFactoryImpl::createQuicClientConnection( - Network::Connection& connection, Http::ConnectionCallbacks& callbacks, const uint32_t /*max_request_headers_kb*/) { + Network::Connection& connection, Http::ConnectionCallbacks& callbacks, + const uint32_t /*max_request_headers_kb*/) { return std::make_unique( dynamic_cast(connection), callbacks); } std::unique_ptr QuicHttpServerConnectionFactoryImpl::createQuicServerConnection( - Network::Connection& connection, Http::ConnectionCallbacks& callbacks, const uint32_t max_request_headers_kb, - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction - /*headers_with_underscores_action*/) { + Network::Connection& connection, Http::ConnectionCallbacks& callbacks, + const uint32_t max_request_headers_kb, + envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction + /*headers_with_underscores_action*/) { auto& quic_session = dynamic_cast(connection); quic_session.SetMaxInboundHeaderListSize(max_request_headers_kb * 1024); + SetQuicFlag(FLAGS_quic_header_size_limit_counts_overhead, false); return std::make_unique( - quic_session, - dynamic_cast(callbacks)); + quic_session, dynamic_cast(callbacks)); } REGISTER_FACTORY(QuicHttpClientConnectionFactoryImpl, Http::QuicHttpClientConnectionFactory); diff --git a/source/extensions/quic_listeners/quiche/codec_impl.h b/source/extensions/quic_listeners/quiche/codec_impl.h index 43de15420f02..db83783d3488 100644 --- a/source/extensions/quic_listeners/quiche/codec_impl.h +++ b/source/extensions/quic_listeners/quiche/codec_impl.h @@ -76,8 +76,8 @@ class QuicHttpClientConnectionImpl : public QuicHttpConnectionImplBase, class QuicHttpClientConnectionFactoryImpl : public Http::QuicHttpClientConnectionFactory { public: std::unique_ptr - createQuicClientConnection(Network::Connection& connection, - Http::ConnectionCallbacks& callbacks, const uint32_t max_request_headers_kb) override; + createQuicClientConnection(Network::Connection& connection, Http::ConnectionCallbacks& callbacks, + const uint32_t max_request_headers_kb) override; std::string name() const override { return Http::QuicCodecNames::get().Quiche; } }; @@ -85,9 +85,9 @@ class QuicHttpClientConnectionFactoryImpl : public Http::QuicHttpClientConnectio // A factory to create QuicHttpServerConnection. class QuicHttpServerConnectionFactoryImpl : public Http::QuicHttpServerConnectionFactory { public: - std::unique_ptr - createQuicServerConnection(Network::Connection& connection, - Http::ConnectionCallbacks& callbacks, const uint32_t max_request_headers_kb, + std::unique_ptr createQuicServerConnection( + Network::Connection& connection, Http::ConnectionCallbacks& callbacks, + const uint32_t max_request_headers_kb, envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction headers_with_underscores_action) override; diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc b/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc index 487f363da42f..f9c5056179d3 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_server_session.cc @@ -19,8 +19,7 @@ EnvoyQuicServerSession::EnvoyQuicServerSession( : quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper, crypto_config, compressed_certs_cache), QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), - quic_connection_(std::move(connection)), listener_config_(listener_config) { -} + quic_connection_(std::move(connection)), listener_config_(listener_config) {} EnvoyQuicServerSession::~EnvoyQuicServerSession() { ASSERT(!quic_connection_->connected()); @@ -101,20 +100,30 @@ void EnvoyQuicServerSession::SetDefaultEncryptionLevel(quic::EncryptionLevel lev if (level != quic::ENCRYPTION_FORWARD_SECURE) { return; } - maybeCreateNetworkFilters(); + // maybeCreateNetworkFilters(); // This is only reached once, when handshake is done. raiseConnectionEvent(Network::ConnectionEvent::Connected); } +void EnvoyQuicServerSession::OnNewEncryptionKeyAvailable( + quic::EncryptionLevel level, std::unique_ptr encrypter) { + if (!filters_created_ && level == quic::ENCRYPTION_FORWARD_SECURE) { + // Instantiate filters before sending SETTINGS below. + maybeCreateNetworkFilters(); + } + quic::QuicServerSessionBase::OnNewEncryptionKeyAvailable(level, std::move(encrypter)); +} + bool EnvoyQuicServerSession::hasDataToWrite() { return HasDataToWrite(); } void EnvoyQuicServerSession::OnTlsHandshakeComplete() { quic::QuicServerSessionBase::OnTlsHandshakeComplete(); - maybeCreateNetworkFilters(); raiseConnectionEvent(Network::ConnectionEvent::Connected); } void EnvoyQuicServerSession::maybeCreateNetworkFilters() { + ASSERT(!filters_created_); + filters_created_ = true; auto proof_source_details = dynamic_cast(GetCryptoStream()->ProofSourceDetails()); ASSERT(proof_source_details != nullptr, diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_server_session.h b/source/extensions/quic_listeners/quiche/envoy_quic_server_session.h index 7a6f51b1db1c..80bca62d071b 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_server_session.h +++ b/source/extensions/quic_listeners/quiche/envoy_quic_server_session.h @@ -58,6 +58,8 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, void OnTlsHandshakeComplete() override; // quic::QuicSpdySession void SetDefaultEncryptionLevel(quic::EncryptionLevel level) override; + void OnNewEncryptionKeyAvailable(quic::EncryptionLevel level, + std::unique_ptr encrypter) override; using quic::QuicSession::PerformActionOnActiveStreams; @@ -86,6 +88,7 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, // These callbacks are owned by network filters and quic session should out live // them. Http::ServerConnectionCallbacks* http_connection_callbacks_{nullptr}; + bool filters_created_{false}; }; } // namespace Quic diff --git a/test/extensions/quic_listeners/quiche/envoy_quic_dispatcher_test.cc b/test/extensions/quic_listeners/quiche/envoy_quic_dispatcher_test.cc index f608c0d515ae..b535d0a8f8e2 100644 --- a/test/extensions/quic_listeners/quiche/envoy_quic_dispatcher_test.cc +++ b/test/extensions/quic_listeners/quiche/envoy_quic_dispatcher_test.cc @@ -170,6 +170,7 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, ASSERT(envoy_connection->addressProvider().localAddress() != nullptr); EXPECT_EQ(*listen_socket_->addressProvider().localAddress(), *envoy_connection->addressProvider().localAddress()); + EXPECT_EQ(64 * 1024, envoy_connection->max_inbound_header_list_size()); } void processValidChloPacketAndInitializeFilters(bool should_buffer) { @@ -197,6 +198,7 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, const std::vector& filter_factories) { EXPECT_EQ(1u, filter_factories.size()); Server::Configuration::FilterChainUtility::buildFilterChain(connection, filter_factories); + static_cast(connection).SetMaxInboundHeaderListSize(64 * 1024); return true; })); EXPECT_CALL(*read_filter, onNewConnection()) diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index bcaf0145ac96..579bcbe98edf 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -342,7 +342,8 @@ FakeHttpConnection::FakeHttpConnection( codec_ = std::unique_ptr( Config::Utility::getAndCheckFactoryByName( Http::QuicCodecNames::get().Quiche) - .createQuicServerConnection(shared_connection_.connection(), *this, max_request_headers_kb, headers_with_underscores_action)); + .createQuicServerConnection(shared_connection_.connection(), *this, + max_request_headers_kb, headers_with_underscores_action)); } shared_connection_.connection().addReadFilter( Network::ReadFilterSharedPtr{new ReadFilter(*this)}); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 4b8055873826..7ed11fd0e14a 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1309,9 +1309,16 @@ TEST_P(ProtocolIntegrationTest, MissingStatus) { // Validate that lots of tiny cookies doesn't cause a DoS (single cookie header). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { - // TODO(danzh) re-enable this test after quic headers size become configurable. - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; + if (downstream_protocol_ == Http::CodecClient::Type::HTTP3) { + // QUICHE Qpack splits concatinated cookies into crumbs to increase + // compression ratio. On the receiver side, the total size of these crumbs + // may be larger than coalesced cookie headers. Increase the max request + // header size to avoid QUIC_HEADERS_TOO_LARGE stream error. + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { hcm.mutable_max_request_headers_kb()->set_value(96); }); + } initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -1334,8 +1341,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { // Validate that lots of tiny cookies doesn't cause a DoS (many cookie headers). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingMany) { - // TODO(danzh) re-enable this test after quic headers size become configurable. - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Set header count limit to 2010. uint32_t max_count = 2010; @@ -1547,8 +1552,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlAccepted) { - // TODO(danzh) re-enable this test after quic headers size become configurable. - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Send one 95 kB URL with limit 96 kB headers. testLargeRequestUrl(95, 96); @@ -1560,7 +1563,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersAccepted) { - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Send one 95 kB header with limit 96 kB and 100 headers. testLargeRequestHeaders(95, 1, 96, 100); @@ -1643,7 +1645,6 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersAccepted) { // time-consuming byte size validations that will cause this test to timeout. TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersTimeout) { // TODO(danzh) re-enable this test after quic headers size become configurable. - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; // Set timeout for 5 seconds, and ensure that a request with 10k+ headers can be sent. testManyRequestHeaders(std::chrono::milliseconds(5000)); @@ -1651,7 +1652,6 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersTimeout) { TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersAccepted) { // TODO(danzh) re-enable this test after quic headers size become configurable. - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); testLargeRequestTrailers(60, 96); @@ -1659,7 +1659,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersAccepted) { TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersRejected) { // TODO(danzh) investigate why it failed for H3. - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); testLargeRequestTrailers(66, 60); @@ -1668,9 +1667,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersRejected) { // This test uses an Http::HeaderMapImpl instead of an Http::TestHeaderMapImpl to avoid // time-consuming byte size verification that will cause this test to timeout. TEST_P(DownstreamProtocolIntegrationTest, ManyTrailerHeaders) { - // Enable after setting QUICHE max_inbound_header_list_size_ from HCM - // config. - EXCLUDE_DOWNSTREAM_HTTP3 EXCLUDE_UPSTREAM_HTTP3; setMaxRequestHeadersKb(96); setMaxRequestHeadersCount(20005); From 852a39238c059686d6cc64395c7cee6b67c77d9b Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 9 Apr 2021 00:29:12 -0400 Subject: [PATCH 25/52] protocol test fail Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 17 +++++++++++++++-- source/common/http/codec_client.h | 4 ++-- source/common/quic/BUILD | 2 ++ source/common/quic/active_quic_listener.cc | 1 + .../quic/client_connection_factory_impl.cc | 2 +- source/common/quic/codec_impl.cc | 3 ++- source/common/quic/envoy_quic_client_session.cc | 5 +---- source/common/quic/envoy_quic_dispatcher.cc | 13 +++++++++++-- source/common/quic/envoy_quic_proof_source.cc | 12 +++--------- source/common/quic/envoy_quic_server_session.cc | 9 --------- source/common/quic/envoy_quic_server_session.h | 2 -- source/common/quic/envoy_quic_utils.cc | 17 +++++++++++++++++ source/common/quic/envoy_quic_utils.h | 6 ++++++ test/integration/BUILD | 2 +- test/integration/quic_http_integration_test.cc | 1 - 15 files changed, 62 insertions(+), 34 deletions(-) diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 9110962936cd..e328a05e55f9 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -34,6 +34,8 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, connection_->addConnectionCallbacks(*this); connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)}); + // Do not start handshake for H3 connection till it is initialized. + if (type_ != Type::HTTP3) { // In general, codecs are handed new not-yet-connected connections, but in the // case of ALPN, the codec may be handed an already connected connection. if (!connection_->connecting()) { @@ -43,6 +45,7 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, ENVOY_CONN_LOG(debug, "connecting", *connection_); connection_->connect(); } + } if (idle_timeout_) { idle_timer_ = dispatcher.createTimer([this]() -> void { onIdleTimeout(); }); @@ -168,7 +171,6 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator) : CodecClient(type, std::move(connection), host, dispatcher) { - switch (type) { case Type::HTTP1: { codec_ = std::make_unique( @@ -185,10 +187,21 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne } case Type::HTTP3: { #ifdef ENVOY_ENABLE_QUIC + auto& quic_session = dynamic_cast(*connection_); codec_ = std::make_unique( - dynamic_cast(*connection_), *this, + quic_session, *this, host->cluster().http3CodecStats(), host->cluster().http3Options(), Http::DEFAULT_MAX_REQUEST_HEADERS_KB); + // Initialize the session after max request header size is changed in above http client connection creation. + quic_session.Initialize(); + // The other two codecs have already connected in base class. +if (!connection_->connecting()) { + ASSERT(connection_->state() == Network::Connection::State::Open); + connected_ = true; + } else { + ENVOY_CONN_LOG(debug, "connecting", *connection_); + connection_->connect(); + } break; #else // Should be blocked by configuration checking at an earlier point. diff --git a/source/common/http/codec_client.h b/source/common/http/codec_client.h index d0c5e486f076..667be493c2f7 100644 --- a/source/common/http/codec_client.h +++ b/source/common/http/codec_client.h @@ -47,7 +47,7 @@ class CodecClientCallbacks { * This is an HTTP client that multiple stream management and underlying connection management * across multiple HTTP codec types. */ -class CodecClient : Logger::Loggable, +class CodecClient : protected Logger::Loggable, public Http::ConnectionCallbacks, public Network::ConnectionCallbacks, public Event::DeferredDeletable { @@ -177,6 +177,7 @@ class CodecClient : Logger::Loggable, ClientConnectionPtr codec_; Event::TimerPtr idle_timer_; const absl::optional idle_timeout_; + bool connected_{}; private: /** @@ -254,7 +255,6 @@ class CodecClient : Logger::Loggable, std::list active_requests_; Http::ConnectionCallbacks* codec_callbacks_{}; CodecClientCallbacks* codec_client_callbacks_{}; - bool connected_{}; bool remote_closed_{}; bool protocol_error_{false}; }; diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 506ee80b4294..5e08acb96436 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -354,7 +354,9 @@ envoy_cc_library( "//source/common/network:address_lib", "//source/common/network:listen_socket_lib", "//source/common/network:socket_option_factory_lib", + "//source/common/quic:quic_io_handle_wrapper_lib", "@com_googlesource_quiche//:quic_core_http_header_list_lib", + "//source/extensions/transport_sockets:well_known_names", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index ba987c33ae8a..cc02afd8413e 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -46,6 +46,7 @@ ActiveQuicListener::ActiveQuicListener( kernel_worker_routing_(kernel_worker_routing) { // This flag fix a QUICHE issue which may crash Envoy during connection close. SetQuicReloadableFlag(quic_single_ack_in_packet2, true); + SetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead, false); if (Runtime::LoaderSingleton::getExisting()) { enabled_.emplace(Runtime::FeatureFlag(enabled, Runtime::LoaderSingleton::get())); diff --git a/source/common/quic/client_connection_factory_impl.cc b/source/common/quic/client_connection_factory_impl.cc index 8034c74e4741..88a1304e4b40 100644 --- a/source/common/quic/client_connection_factory_impl.cc +++ b/source/common/quic/client_connection_factory_impl.cc @@ -49,11 +49,11 @@ createQuicNetworkConnection(Http::PersistentQuicInfo& info, Event::Dispatcher& d info_impl->alarm_factory_, quic::ParsedQuicVersionVector{info_impl->supported_versions_[0]}, local_addr, dispatcher, nullptr); auto& static_info = StaticInfo::get(); + SetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead, false); auto ret = std::make_unique( static_info.quic_config_, info_impl->supported_versions_, std::move(connection), info_impl->server_id_, info_impl->crypto_config_.get(), &static_info.push_promise_index_, dispatcher, 0); - ret->Initialize(); return ret; } diff --git a/source/common/quic/codec_impl.cc b/source/common/quic/codec_impl.cc index 84dca7c3abab..5cdae4dd8e96 100644 --- a/source/common/quic/codec_impl.cc +++ b/source/common/quic/codec_impl.cc @@ -69,11 +69,12 @@ QuicHttpClientConnectionImpl::QuicHttpClientConnectionImpl( EnvoyQuicClientSession& session, Http::ConnectionCallbacks& callbacks, Http::Http3::CodecStats& stats, const envoy::config::core::v3::Http3ProtocolOptions& http3_options, - const uint32_t /*max_request_headers_kb*/) + const uint32_t max_request_headers_kb) : QuicHttpConnectionImplBase(session, stats), quic_client_session_(session) { session.setCodecStats(stats); session.setHttp3Options(http3_options); session.setHttpConnectionCallbacks(callbacks); + session.set_max_inbound_header_list_size(max_request_headers_kb * 1024); } Http::RequestEncoder& diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index 7c969926fa1e..6eb743a80739 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -14,10 +14,7 @@ EnvoyQuicClientSession::EnvoyQuicClientSession( : QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), quic::QuicSpdyClientSession(config, supported_versions, connection.release(), server_id, crypto_config, push_promise_index), - host_name_(server_id.host()) { - // HTTP/3 header limits should be configurable, but for now hard-code to Envoy defaults. - set_max_inbound_header_list_size(Http::DEFAULT_MAX_REQUEST_HEADERS_KB * 1000); -} + host_name_(server_id.host()) {} EnvoyQuicClientSession::~EnvoyQuicClientSession() { ASSERT(!connection()->connected()); diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index 6a7caeba272d..90cd39259807 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -3,6 +3,7 @@ #include "common/http/utility.h" #include "common/quic/envoy_quic_server_connection.h" #include "common/quic/envoy_quic_server_session.h" +#include "common/quic/envoy_quic_utils.h" namespace Envoy { namespace Quic { @@ -48,8 +49,8 @@ void EnvoyQuicDispatcher::OnConnectionClosed(quic::QuicConnectionId connection_i std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( quic::QuicConnectionId server_connection_id, const quic::QuicSocketAddress& self_address, - const quic::QuicSocketAddress& peer_address, absl::string_view /*alpn*/, - const quic::ParsedQuicVersion& version, absl::string_view /*sni*/) { + const quic::QuicSocketAddress& peer_address, absl::string_view alpn, + const quic::ParsedQuicVersion& version, absl::string_view sni) { quic::QuicConfig quic_config = config(); // TODO(danzh) setup flow control window via config. quic_config.SetInitialStreamFlowControlWindowToSend( @@ -63,6 +64,14 @@ std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( quic_config, quic::ParsedQuicVersionVector{version}, std::move(quic_connection), this, session_helper(), crypto_config(), compressed_certs_cache(), dispatcher_, listener_config_.perConnectionBufferLimitBytes(), listener_config_); + + const Network::FilterChain* filter_chain = getFilterChain(listen_socket_.ioHandle(), listener_config_.filterChainManager(), self_address, peer_address, std::string(sni), alpn); + if (filter_chain != nullptr) { + const bool has_filter_initialized = + listener_config_.filterChainFactory().createNetworkFilterChain( + *quic_session, filter_chain->networkFilterFactories()); + ASSERT(has_filter_initialized); + } quic_session->Initialize(); // Filter chain can't be retrieved here as self address is unknown at this // point. diff --git a/source/common/quic/envoy_quic_proof_source.cc b/source/common/quic/envoy_quic_proof_source.cc index 71f1908854d9..8dc6bc6c7650 100644 --- a/source/common/quic/envoy_quic_proof_source.cc +++ b/source/common/quic/envoy_quic_proof_source.cc @@ -1,6 +1,7 @@ #include "common/quic/envoy_quic_proof_source.h" #include +#include #include "envoy/ssl/tls_certificate_config.h" @@ -83,16 +84,9 @@ EnvoyQuicProofSource::getTlsCertConfigAndFilterChain(const quic::QuicSocketAddre const quic::QuicSocketAddress& client_address, const std::string& hostname) { ENVOY_LOG(trace, "Getting cert chain for {}", hostname); - Network::ConnectionSocketImpl connection_socket( - std::make_unique(listen_socket_.ioHandle()), - quicAddressToEnvoyAddressInstance(server_address), - quicAddressToEnvoyAddressInstance(client_address)); - connection_socket.setDetectedTransportProtocol( - Extensions::TransportSockets::TransportProtocolNames::get().Quic); - connection_socket.setRequestedServerName(hostname); - connection_socket.setRequestedApplicationProtocols({"h2"}); const Network::FilterChain* filter_chain = - filter_chain_manager_.findFilterChain(connection_socket); + getFilterChain(listen_socket_.ioHandle(), filter_chain_manager_, server_address, client_address, hostname, "h3-29"); + if (filter_chain == nullptr) { listener_stats_.no_filter_chain_match_.inc(); return {absl::nullopt, absl::nullopt}; diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index af2375a675df..22da85f8e18e 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -111,15 +111,6 @@ void EnvoyQuicServerSession::SetDefaultEncryptionLevel(quic::EncryptionLevel lev raiseConnectionEvent(Network::ConnectionEvent::Connected); } -void EnvoyQuicServerSession::OnNewEncryptionKeyAvailable( - quic::EncryptionLevel level, std::unique_ptr encrypter) { - if (!filters_created_ && level == quic::ENCRYPTION_FORWARD_SECURE) { - // Instantiate filters before sending SETTINGS below. - maybeCreateNetworkFilters(); - } - quic::QuicServerSessionBase::OnNewEncryptionKeyAvailable(level, std::move(encrypter)); -} - bool EnvoyQuicServerSession::hasDataToWrite() { return HasDataToWrite(); } void EnvoyQuicServerSession::OnTlsHandshakeComplete() { diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index 8b14478c649f..d314517fdcca 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -59,8 +59,6 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, void OnTlsHandshakeComplete() override; // quic::QuicSpdySession void SetDefaultEncryptionLevel(quic::EncryptionLevel level) override; - void OnNewEncryptionKeyAvailable(quic::EncryptionLevel level, - std::unique_ptr encrypter) override; size_t WriteHeadersOnHeadersStream( quic::QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, const spdy::SpdyStreamPrecedence& precedence, diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 3da9c12702ea..0da1dbf6963b 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -3,6 +3,7 @@ #include "envoy/common/platform.h" #include "envoy/config/core/v3/base.pb.h" +#include "extensions/transport_sockets/well_known_names.h" #include "common/network/socket_option_factory.h" #include "common/network/utility.h" @@ -223,5 +224,21 @@ int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::strin return sign_alg; } +const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Network::FilterChainManager& filter_chain_manager, const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address, + const std::string& hostname, + std::string_view alpn) { + Network::ConnectionSocketImpl connection_socket( + std::make_unique(io_handle), + quicAddressToEnvoyAddressInstance(self_address), + quicAddressToEnvoyAddressInstance(peer_address)); + connection_socket.setDetectedTransportProtocol( + Extensions::TransportSockets::TransportProtocolNames::get().Quic); + connection_socket.setRequestedServerName(hostname); + connection_socket.setRequestedApplicationProtocols({alpn}); + return filter_chain_manager.findFilterChain(connection_socket); + +} + } // namespace Quic } // namespace Envoy diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index 8898bd1e4971..6d94bc8d83bc 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -7,6 +7,7 @@ #include "common/http/header_map_impl.h" #include "common/network/address_impl.h" #include "common/network/listen_socket_impl.h" +#include "common/quic/quic_io_handle_wrapper.h" #if defined(__GNUC__) #pragma GCC diagnostic push @@ -125,5 +126,10 @@ bssl::UniquePtr parseDERCertificate(const std::string& der_bytes, std::str // not supported, return 0 with error_details populated correspondingly. int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::string* error_details); +const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Network::FilterChainManager& filter_chain_manager, const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address, + const std::string& hostname, + std::string_view alpn); + } // namespace Quic } // namespace Envoy diff --git a/test/integration/BUILD b/test/integration/BUILD index 97e39f4d156c..53912cee1927 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1662,7 +1662,7 @@ envoy_cc_test( # selects. envoy_cc_test( name = "quic_protocol_integration_test", - size = "large", + size = "small", srcs = envoy_select_enable_http3([ "quic_protocol_integration_test.cc", ]), diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index afce0fb4af7f..f160a5beea5f 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -122,7 +122,6 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, public QuicMultiVers quic_config_, supported_versions_, std::move(connection), persistent_info.server_id_, persistent_info.crypto_config_.get(), &push_promise_index_, *dispatcher_, /*send_buffer_limit=*/2 * Http2::Utility::OptionsLimits::MIN_INITIAL_STREAM_WINDOW_SIZE); - session->Initialize(); return session; } From ec6a58c4439725247fb47826a30c301e77eb657c Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 9 Apr 2021 13:43:36 -0400 Subject: [PATCH 26/52] test pass Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 40 +++++++++---------- source/common/quic/BUILD | 2 +- source/common/quic/envoy_quic_dispatcher.cc | 12 +++--- source/common/quic/envoy_quic_proof_source.cc | 3 +- source/common/quic/envoy_quic_utils.cc | 22 +++++----- source/common/quic/envoy_quic_utils.h | 9 +++-- test/integration/BUILD | 2 +- test/integration/protocol_integration_test.cc | 2 +- .../quic_protocol_integration_test.cc | 8 ++-- 9 files changed, 52 insertions(+), 48 deletions(-) diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index e328a05e55f9..99d04e1e2cff 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -35,17 +35,17 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)}); // Do not start handshake for H3 connection till it is initialized. - if (type_ != Type::HTTP3) { - // In general, codecs are handed new not-yet-connected connections, but in the - // case of ALPN, the codec may be handed an already connected connection. - if (!connection_->connecting()) { - ASSERT(connection_->state() == Network::Connection::State::Open); - connected_ = true; - } else { - ENVOY_CONN_LOG(debug, "connecting", *connection_); - connection_->connect(); + if (type_ != Type::HTTP3) { + // In general, codecs are handed new not-yet-connected connections, but in the + // case of ALPN, the codec may be handed an already connected connection. + if (!connection_->connecting()) { + ASSERT(connection_->state() == Network::Connection::State::Open); + connected_ = true; + } else { + ENVOY_CONN_LOG(debug, "connecting", *connection_); + connection_->connect(); + } } - } if (idle_timeout_) { idle_timer_ = dispatcher.createTimer([this]() -> void { onIdleTimeout(); }); @@ -189,19 +189,19 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne #ifdef ENVOY_ENABLE_QUIC auto& quic_session = dynamic_cast(*connection_); codec_ = std::make_unique( - quic_session, *this, - host->cluster().http3CodecStats(), host->cluster().http3Options(), + quic_session, *this, host->cluster().http3CodecStats(), host->cluster().http3Options(), Http::DEFAULT_MAX_REQUEST_HEADERS_KB); - // Initialize the session after max request header size is changed in above http client connection creation. + // Initialize the session after max request header size is changed in above http client + // connection creation. quic_session.Initialize(); // The other two codecs have already connected in base class. -if (!connection_->connecting()) { - ASSERT(connection_->state() == Network::Connection::State::Open); - connected_ = true; - } else { - ENVOY_CONN_LOG(debug, "connecting", *connection_); - connection_->connect(); - } + if (!connection_->connecting()) { + ASSERT(connection_->state() == Network::Connection::State::Open); + connected_ = true; + } else { + ENVOY_CONN_LOG(debug, "connecting", *connection_); + connection_->connect(); + } break; #else // Should be blocked by configuration checking at an earlier point. diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 5e08acb96436..60aad187ed47 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -355,8 +355,8 @@ envoy_cc_library( "//source/common/network:listen_socket_lib", "//source/common/network:socket_option_factory_lib", "//source/common/quic:quic_io_handle_wrapper_lib", - "@com_googlesource_quiche//:quic_core_http_header_list_lib", "//source/extensions/transport_sockets:well_known_names", + "@com_googlesource_quiche//:quic_core_http_header_list_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index 90cd39259807..91ec785b9c9d 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -65,12 +65,14 @@ std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( session_helper(), crypto_config(), compressed_certs_cache(), dispatcher_, listener_config_.perConnectionBufferLimitBytes(), listener_config_); - const Network::FilterChain* filter_chain = getFilterChain(listen_socket_.ioHandle(), listener_config_.filterChainManager(), self_address, peer_address, std::string(sni), alpn); - if (filter_chain != nullptr) { + const Network::FilterChain* filter_chain = + getFilterChain(listen_socket_.ioHandle(), listener_config_.filterChainManager(), self_address, + peer_address, std::string(sni), alpn); + if (filter_chain != nullptr) { const bool has_filter_initialized = - listener_config_.filterChainFactory().createNetworkFilterChain( - *quic_session, filter_chain->networkFilterFactories()); - ASSERT(has_filter_initialized); + listener_config_.filterChainFactory().createNetworkFilterChain( + *quic_session, filter_chain->networkFilterFactories()); + ASSERT(has_filter_initialized); } quic_session->Initialize(); // Filter chain can't be retrieved here as self address is unknown at this diff --git a/source/common/quic/envoy_quic_proof_source.cc b/source/common/quic/envoy_quic_proof_source.cc index 8dc6bc6c7650..d7661beac92a 100644 --- a/source/common/quic/envoy_quic_proof_source.cc +++ b/source/common/quic/envoy_quic_proof_source.cc @@ -85,7 +85,8 @@ EnvoyQuicProofSource::getTlsCertConfigAndFilterChain(const quic::QuicSocketAddre const std::string& hostname) { ENVOY_LOG(trace, "Getting cert chain for {}", hostname); const Network::FilterChain* filter_chain = - getFilterChain(listen_socket_.ioHandle(), filter_chain_manager_, server_address, client_address, hostname, "h3-29"); + getFilterChain(listen_socket_.ioHandle(), filter_chain_manager_, server_address, + client_address, hostname, "h3-29"); if (filter_chain == nullptr) { listener_stats_.no_filter_chain_match_.inc(); diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 0da1dbf6963b..7ff744181c94 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -3,10 +3,11 @@ #include "envoy/common/platform.h" #include "envoy/config/core/v3/base.pb.h" -#include "extensions/transport_sockets/well_known_names.h" #include "common/network/socket_option_factory.h" #include "common/network/utility.h" +#include "extensions/transport_sockets/well_known_names.h" + namespace Envoy { namespace Quic { @@ -224,20 +225,19 @@ int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::strin return sign_alg; } -const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Network::FilterChainManager& filter_chain_manager, const quic::QuicSocketAddress& self_address, - const quic::QuicSocketAddress& peer_address, - const std::string& hostname, - std::string_view alpn) { - Network::ConnectionSocketImpl connection_socket( - std::make_unique(io_handle), - quicAddressToEnvoyAddressInstance(self_address), - quicAddressToEnvoyAddressInstance(peer_address)); +const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, + Network::FilterChainManager& filter_chain_manager, + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address, + const std::string& hostname, std::string_view alpn) { + Network::ConnectionSocketImpl connection_socket(std::make_unique(io_handle), + quicAddressToEnvoyAddressInstance(self_address), + quicAddressToEnvoyAddressInstance(peer_address)); connection_socket.setDetectedTransportProtocol( Extensions::TransportSockets::TransportProtocolNames::get().Quic); connection_socket.setRequestedServerName(hostname); connection_socket.setRequestedApplicationProtocols({alpn}); - return filter_chain_manager.findFilterChain(connection_socket); - + return filter_chain_manager.findFilterChain(connection_socket); } } // namespace Quic diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index 6d94bc8d83bc..6d8181b55884 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -126,10 +126,11 @@ bssl::UniquePtr parseDERCertificate(const std::string& der_bytes, std::str // not supported, return 0 with error_details populated correspondingly. int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::string* error_details); -const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Network::FilterChainManager& filter_chain_manager, const quic::QuicSocketAddress& self_address, - const quic::QuicSocketAddress& peer_address, - const std::string& hostname, - std::string_view alpn); +const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, + Network::FilterChainManager& filter_chain_manager, + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address, + const std::string& hostname, std::string_view alpn); } // namespace Quic } // namespace Envoy diff --git a/test/integration/BUILD b/test/integration/BUILD index 53912cee1927..97e39f4d156c 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1662,7 +1662,7 @@ envoy_cc_test( # selects. envoy_cc_test( name = "quic_protocol_integration_test", - size = "small", + size = "large", srcs = envoy_select_enable_http3([ "quic_protocol_integration_test.cc", ]), diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index eff7d0d50c96..29f13887fb74 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1585,7 +1585,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersAccepted) { - // TODO(alyssawilk) set upstream header size from config. + // TODO(alyssawilk) set upstream header size from config. EXCLUDE_UPSTREAM_HTTP3; // Send one 95 kB header with limit 96 kB and 100 headers. testLargeRequestHeaders(95, 1, 96, 100); diff --git a/test/integration/quic_protocol_integration_test.cc b/test/integration/quic_protocol_integration_test.cc index 4d94b6783cad..197eb3761c7f 100644 --- a/test/integration/quic_protocol_integration_test.cc +++ b/test/integration/quic_protocol_integration_test.cc @@ -2,13 +2,13 @@ namespace Envoy { -// These will run with HTTP/3 downstream, and Http and HTTP/2 upstream. -INSTANTIATE_TEST_SUITE_P(Protocols, DownstreamProtocolIntegrationTest, +// These will run with HTTP/3 downstream, and Http upstream. +INSTANTIATE_TEST_SUITE_P(DownstreamProtocols, DownstreamProtocolIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( - {Http::CodecClient::Type::HTTP3}, - {FakeHttpConnection::Type::HTTP1, FakeHttpConnection::Type::HTTP2})), + {Http::CodecClient::Type::HTTP3}, {FakeHttpConnection::Type::HTTP1})), HttpProtocolIntegrationTest::protocolTestParamsToString); +// These will run with HTTP/3 downstream, and Http and HTTP/2 upstream. INSTANTIATE_TEST_SUITE_P(DownstreamProtocols, ProtocolIntegrationTest, testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( {Http::CodecClient::Type::HTTP3}, From cf32e9dd7ed345a7052b60302f11feeb5ee34c04 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 9 Apr 2021 20:08:13 -0400 Subject: [PATCH 27/52] close connection during filter initialization Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 1 + .../common/quic/envoy_quic_client_session.cc | 1 + source/common/quic/envoy_quic_dispatcher.cc | 1 + .../common/quic/envoy_quic_server_session.cc | 20 +-- .../common/quic/envoy_quic_server_session.h | 3 - source/common/quic/envoy_quic_utils.cc | 2 + .../quic_filter_manager_connection_impl.cc | 6 + .../quic_filter_manager_connection_impl.h | 7 +- test/common/quic/active_quic_listener_test.cc | 15 +- .../common/quic/envoy_quic_dispatcher_test.cc | 128 +++++++++++++----- .../quic/envoy_quic_proof_source_test.cc | 4 +- .../quic/envoy_quic_server_session_test.cc | 25 +--- test/common/quic/test_utils.h | 5 + 13 files changed, 133 insertions(+), 85 deletions(-) diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 99d04e1e2cff..07bee8d2e965 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -171,6 +171,7 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator) : CodecClient(type, std::move(connection), host, dispatcher) { + switch (type) { case Type::HTTP1: { codec_ = std::make_unique( diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index 6eb743a80739..c064a6f31489 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -38,6 +38,7 @@ void EnvoyQuicClientSession::OnConnectionClosed(const quic::QuicConnectionCloseF void EnvoyQuicClientSession::Initialize() { quic::QuicSpdyClientSession::Initialize(); + initialized_ = true; quic_connection_->setEnvoyConnection(*this); } diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index 91ec785b9c9d..2746b2f437fe 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -68,6 +68,7 @@ std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( const Network::FilterChain* filter_chain = getFilterChain(listen_socket_.ioHandle(), listener_config_.filterChainManager(), self_address, peer_address, std::string(sni), alpn); + ASSERT(filter_chain); if (filter_chain != nullptr) { const bool has_filter_initialized = listener_config_.filterChainFactory().createNetworkFilterChain( diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index 22da85f8e18e..9be80394bed4 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -14,11 +14,11 @@ EnvoyQuicServerSession::EnvoyQuicServerSession( std::unique_ptr connection, quic::QuicSession::Visitor* visitor, quic::QuicCryptoServerStream::Helper* helper, const quic::QuicCryptoServerConfig* crypto_config, quic::QuicCompressedCertsCache* compressed_certs_cache, Event::Dispatcher& dispatcher, - uint32_t send_buffer_limit, Network::ListenerConfig& listener_config) + uint32_t send_buffer_limit, Network::ListenerConfig& /*listener_config*/) : quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper, crypto_config, compressed_certs_cache), QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), - quic_connection_(std::move(connection)), listener_config_(listener_config) {} + quic_connection_(std::move(connection)) {} EnvoyQuicServerSession::~EnvoyQuicServerSession() { ASSERT(!quic_connection_->connected()); @@ -86,6 +86,7 @@ void EnvoyQuicServerSession::OnConnectionClosed(const quic::QuicConnectionCloseF void EnvoyQuicServerSession::Initialize() { quic::QuicServerSessionBase::Initialize(); + initialized_ = true; quic_connection_->setEnvoyConnection(*this); } @@ -106,7 +107,6 @@ void EnvoyQuicServerSession::SetDefaultEncryptionLevel(quic::EncryptionLevel lev if (level != quic::ENCRYPTION_FORWARD_SECURE) { return; } - // maybeCreateNetworkFilters(); // This is only reached once, when handshake is done. raiseConnectionEvent(Network::ConnectionEvent::Connected); } @@ -118,20 +118,6 @@ void EnvoyQuicServerSession::OnTlsHandshakeComplete() { raiseConnectionEvent(Network::ConnectionEvent::Connected); } -void EnvoyQuicServerSession::maybeCreateNetworkFilters() { - ASSERT(!filters_created_); - filters_created_ = true; - auto proof_source_details = - dynamic_cast(GetCryptoStream()->ProofSourceDetails()); - ASSERT(proof_source_details != nullptr, - "ProofSource didn't provide ProofSource::Details. No filter chain will be installed."); - - const bool has_filter_initialized = - listener_config_.filterChainFactory().createNetworkFilterChain( - *this, proof_source_details->filterChain().networkFilterFactories()); - ASSERT(has_filter_initialized); -} - size_t EnvoyQuicServerSession::WriteHeadersOnHeadersStream( quic::QuicStreamId id, spdy::SpdyHeaderBlock headers, bool fin, const spdy::SpdyStreamPrecedence& precedence, diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index d314517fdcca..12f1d425a408 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -90,14 +90,11 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, private: void setUpRequestDecoder(EnvoyQuicServerStream& stream); - void maybeCreateNetworkFilters(); std::unique_ptr quic_connection_; - Network::ListenerConfig& listener_config_; // These callbacks are owned by network filters and quic session should out live // them. Http::ServerConnectionCallbacks* http_connection_callbacks_{nullptr}; - bool filters_created_{false}; envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction headers_with_underscores_action_; diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 7ff744181c94..8e0acf37fa28 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -230,6 +230,7 @@ const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address, const std::string& hostname, std::string_view alpn) { + std::cerr << "============ getFilterChain\n"; Network::ConnectionSocketImpl connection_socket(std::make_unique(io_handle), quicAddressToEnvoyAddressInstance(self_address), quicAddressToEnvoyAddressInstance(peer_address)); @@ -237,6 +238,7 @@ const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Extensions::TransportSockets::TransportProtocolNames::get().Quic); connection_socket.setRequestedServerName(hostname); connection_socket.setRequestedApplicationProtocols({alpn}); + std::cerr << "============ findFilterChain\n"; return filter_chain_manager.findFilterChain(connection_socket); } diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index fe91c978e2f0..362b9062010c 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -1,5 +1,6 @@ #include "common/quic/quic_filter_manager_connection_impl.h" +#include #include namespace Envoy { @@ -65,6 +66,11 @@ void QuicFilterManagerConnectionImpl::close(Network::ConnectionCloseType type) { // Already detached from quic connection. return; } + if (!initialized_) { + // Delay close till the first OnCanWrite() call. + delayed_close_state_ = DelayedCloseState::CloseAfterFlush; + return; + } const bool delayed_close_timeout_configured = delayed_close_timeout_.count() > 0; if (hasDataToWrite() && type != Network::ConnectionCloseType::NoFlush) { if (delayed_close_timeout_configured) { diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index e897042b6de7..51d6b46b72e4 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -73,13 +73,14 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, } Ssl::ConnectionInfoConstSharedPtr ssl() const override; Network::Connection::State state() const override { - if (quic_connection_ != nullptr && quic_connection_->connected()) { + if (!initialized_ || (quic_connection_ != nullptr && quic_connection_->connected())) { return Network::Connection::State::Open; } return Network::Connection::State::Closed; } bool connecting() const override { - if (quic_connection_ != nullptr && !quic_connection_->IsHandshakeComplete()) { + if (!initialized_ || + (quic_connection_ != nullptr && !quic_connection_->IsHandshakeComplete())) { return true; } return false; @@ -143,6 +144,8 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, absl::optional> codec_stats_; absl::optional> http3_options_; + // If false, do not call into quic_connection_. + bool initialized_{false}; private: friend class Envoy::TestPauseFilterForQuic; diff --git a/test/common/quic/active_quic_listener_test.cc b/test/common/quic/active_quic_listener_test.cc index 6ed403a415fd..d4cfe8a54231 100644 --- a/test/common/quic/active_quic_listener_test.cc +++ b/test/common/quic/active_quic_listener_test.cc @@ -126,7 +126,8 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { })); listener_factory_ = createQuicListenerFactory(yamlForQuicConfig()); - EXPECT_CALL(listener_config_, filterChainManager()).WillOnce(ReturnRef(filter_chain_manager_)); + EXPECT_CALL(listener_config_, filterChainManager()) + .WillRepeatedly(ReturnRef(filter_chain_manager_)); quic_listener_ = staticUniquePointerCast(listener_factory_->createActiveUdpListener( 0, connection_handler_, *dispatcher_, listener_config_)); @@ -155,9 +156,9 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { } void maybeConfigureMocks(int connection_count) { - if (quic_version_.UsesTls()) { - return; - } + EXPECT_CALL(filter_chain_manager_, findFilterChain(_)) + .Times(connection_count) + .WillRepeatedly(Return(filter_chain_)); EXPECT_CALL(listener_config_, filterChainFactory()).Times(connection_count); EXPECT_CALL(listener_config_.filter_chain_factory_, createNetworkFilterChain(_, _)) .Times(connection_count) @@ -167,8 +168,10 @@ class ActiveQuicListenerTest : public QuicMultiVersionTest { Server::Configuration::FilterChainUtility::buildFilterChain(connection, filter_factories); return true; })); - EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::Connected)) - .Times(connection_count); + if (!quic_version_.UsesTls()) { + EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::Connected)) + .Times(connection_count); + } EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::LocalClose)) .Times(connection_count); diff --git a/test/common/quic/envoy_quic_dispatcher_test.cc b/test/common/quic/envoy_quic_dispatcher_test.cc index a64b21ca886d..8260a8799f2e 100644 --- a/test/common/quic/envoy_quic_dispatcher_test.cc +++ b/test/common/quic/envoy_quic_dispatcher_test.cc @@ -109,22 +109,7 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, dispatcher_->run(Event::Dispatcher::RunType::NonBlock); } - void processValidChloPacketAndCheckStatus(bool should_buffer) { - quic::QuicSocketAddress peer_addr(version_ == Network::Address::IpVersion::v4 - ? quic::QuicIpAddress::Loopback4() - : quic::QuicIpAddress::Loopback6(), - 54321); - quic::QuicBufferedPacketStore* buffered_packets = - quic::test::QuicDispatcherPeer::GetBufferedPackets(&envoy_quic_dispatcher_); - if (!should_buffer) { - // Set QuicDispatcher::new_sessions_allowed_per_event_loop_ to - // |kNumSessionsToCreatePerLoopForTests| so that received CHLOs can be - // processed immediately. - envoy_quic_dispatcher_.ProcessBufferedChlos(kNumSessionsToCreatePerLoopForTests); - EXPECT_FALSE(buffered_packets->HasChlosBuffered()); - EXPECT_FALSE(buffered_packets->HasBufferedPackets(connection_id_)); - } - + void processValidChloPacket(const quic::QuicSocketAddress& peer_addr) { // Create a Quic Crypto or TLS1.3 CHLO packet. EnvoyQuicClock clock(*dispatcher_); Buffer::OwnedImpl payload = generateChloPacketToSend( @@ -142,7 +127,25 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, envoy_quic_dispatcher_.ProcessPacket( envoyIpAddressToQuicSocketAddress(listen_socket_->addressProvider().localAddress()->ip()), peer_addr, *received_packet); + } + void processValidChloPacketAndCheckStatus(bool should_buffer) { + quic::QuicSocketAddress peer_addr(version_ == Network::Address::IpVersion::v4 + ? quic::QuicIpAddress::Loopback4() + : quic::QuicIpAddress::Loopback6(), + 54321); + quic::QuicBufferedPacketStore* buffered_packets = + quic::test::QuicDispatcherPeer::GetBufferedPackets(&envoy_quic_dispatcher_); + if (!should_buffer) { + // Set QuicDispatcher::new_sessions_allowed_per_event_loop_ to + // |kNumSessionsToCreatePerLoopForTests| so that received CHLOs can be + // processed immediately. + envoy_quic_dispatcher_.ProcessBufferedChlos(kNumSessionsToCreatePerLoopForTests); + EXPECT_FALSE(buffered_packets->HasChlosBuffered()); + EXPECT_FALSE(buffered_packets->HasBufferedPackets(connection_id_)); + } + + processValidChloPacket(peer_addr); if (should_buffer) { // Incoming CHLO packet is buffered, because ProcessPacket() is called before // ProcessBufferedChlos(). @@ -190,6 +193,23 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, read_filter->callbacks_->connection().setConnectionStats( {read_total, read_current, write_total, write_current, nullptr, nullptr}); }}); + EXPECT_CALL(listener_config_, filterChainManager()).WillOnce(ReturnRef(filter_chain_manager)); + EXPECT_CALL(filter_chain_manager, findFilterChain(_)) + .WillOnce(Invoke([this](const Network::ConnectionSocket& socket) { + switch (GetParam().second) { + case QuicVersionType::GquicQuicCrypto: + EXPECT_EQ("", socket.requestedApplicationProtocols()[0]); + break; + case QuicVersionType::GquicTls: + EXPECT_EQ("h3-T051", socket.requestedApplicationProtocols()[0]); + break; + case QuicVersionType::Iquic: + EXPECT_EQ("h3-29", socket.requestedApplicationProtocols()[0]); + break; + } + EXPECT_EQ("test.example.org", socket.requestedServerName()); + return &proof_source_->filterChain(); + })); EXPECT_CALL(proof_source_->filterChain(), networkFilterFactories()) .WillOnce(ReturnRef(filter_factory)); EXPECT_CALL(listener_config_, filterChainFactory()); @@ -198,13 +218,17 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, const std::vector& filter_factories) { EXPECT_EQ(1u, filter_factories.size()); Server::Configuration::FilterChainUtility::buildFilterChain(connection, filter_factories); - static_cast(connection).SetMaxInboundHeaderListSize(64 * 1024); + dynamic_cast(connection) + .set_max_inbound_header_list_size(64 * 1024); return true; })); EXPECT_CALL(*read_filter, onNewConnection()) // Stop iteration to avoid calling getRead/WriteBuffer(). .WillOnce(Return(Network::FilterStatus::StopIteration)); - EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::Connected)); + if (!quicVersionUsesTls()) { + // 0-RTT is not supported in Quic TLS handshake yet. + EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::Connected)); + } processValidChloPacketAndCheckStatus(should_buffer); EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::LocalClose)); @@ -238,24 +262,64 @@ INSTANTIATE_TEST_SUITE_P(EnvoyQuicDispatcherTests, EnvoyQuicDispatcherTest, testing::ValuesIn(generateTestParam()), testParamsToString); TEST_P(EnvoyQuicDispatcherTest, CreateNewConnectionUponCHLO) { - if (quicVersionUsesTls()) { - // QUICHE doesn't support 0-RTT TLS1.3 handshake yet. - processValidChloPacketAndCheckStatus(false); - // Shutdown() to close the connection. - envoy_quic_dispatcher_.Shutdown(); - return; - } processValidChloPacketAndInitializeFilters(false); } -TEST_P(EnvoyQuicDispatcherTest, CreateNewConnectionUponBufferedCHLO) { - if (quicVersionUsesTls()) { - // QUICHE doesn't support 0-RTT TLS1.3 handshake yet. - processValidChloPacketAndCheckStatus(true); - // Shutdown() to close the connection. - envoy_quic_dispatcher_.Shutdown(); - return; +TEST_P(EnvoyQuicDispatcherTest, CloseConnectionDuringFilterInstallation) { + Network::MockFilterChainManager filter_chain_manager; + std::shared_ptr read_filter(new Network::MockReadFilter()); + Network::MockConnectionCallbacks network_connection_callbacks; + testing::StrictMock read_total; + testing::StrictMock read_current; + testing::StrictMock write_total; + testing::StrictMock write_current; + + std::vector filter_factory( + {[&](Network::FilterManager& filter_manager) { + filter_manager.addReadFilter(read_filter); + read_filter->callbacks_->connection().addConnectionCallbacks(network_connection_callbacks); + read_filter->callbacks_->connection().setConnectionStats( + {read_total, read_current, write_total, write_current, nullptr, nullptr}); + // This will not close connection right away, but after it processes the first packet. + read_filter->callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + }}); + EXPECT_CALL(listener_config_, filterChainManager()).WillOnce(ReturnRef(filter_chain_manager)); + EXPECT_CALL(filter_chain_manager, findFilterChain(_)) + .WillOnce(Return(&proof_source_->filterChain())); + EXPECT_CALL(proof_source_->filterChain(), networkFilterFactories()) + .WillOnce(ReturnRef(filter_factory)); + EXPECT_CALL(listener_config_, filterChainFactory()); + EXPECT_CALL(listener_config_.filter_chain_factory_, createNetworkFilterChain(_, _)) + .WillOnce(Invoke([](Network::Connection& connection, + const std::vector& filter_factories) { + EXPECT_EQ(1u, filter_factories.size()); + Server::Configuration::FilterChainUtility::buildFilterChain(connection, filter_factories); + return true; + })); + EXPECT_CALL(*read_filter, onNewConnection()) + // Stop iteration to avoid calling getRead/WriteBuffer(). + .WillOnce(Return(Network::FilterStatus::StopIteration)); + + if (!quicVersionUsesTls()) { + EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::Connected)); } + + EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::LocalClose)); + quic::QuicSocketAddress peer_addr(version_ == Network::Address::IpVersion::v4 + ? quic::QuicIpAddress::Loopback4() + : quic::QuicIpAddress::Loopback6(), + 54321); + // Set QuicDispatcher::new_sessions_allowed_per_event_loop_ to + // |kNumSessionsToCreatePerLoopForTests| so that received CHLOs can be + // processed immediately. + envoy_quic_dispatcher_.ProcessBufferedChlos(kNumSessionsToCreatePerLoopForTests); + + processValidChloPacket(peer_addr); + // Shutdown() to close the connection. + envoy_quic_dispatcher_.Shutdown(); +} + +TEST_P(EnvoyQuicDispatcherTest, CreateNewConnectionUponBufferedCHLO) { processValidChloPacketAndInitializeFilters(true); } diff --git a/test/common/quic/envoy_quic_proof_source_test.cc b/test/common/quic/envoy_quic_proof_source_test.cc index a837a686c293..f1ffb5691701 100644 --- a/test/common/quic/envoy_quic_proof_source_test.cc +++ b/test/common/quic/envoy_quic_proof_source_test.cc @@ -149,7 +149,7 @@ class EnvoyQuicProofSourceTest : public ::testing::Test { *connection_socket.addressProvider().remoteAddress()); EXPECT_EQ(Extensions::TransportSockets::TransportProtocolNames::get().Quic, connection_socket.detectedTransportProtocol()); - EXPECT_EQ("h2", connection_socket.requestedApplicationProtocols()[0]); + EXPECT_EQ("h3-29", connection_socket.requestedApplicationProtocols()[0]); return &filter_chain_; })); EXPECT_CALL(filter_chain_, transportSocketFactory()) @@ -225,7 +225,7 @@ TEST_F(EnvoyQuicProofSourceTest, GetProofFailNoCertConfig) { *connection_socket.addressProvider().remoteAddress()); EXPECT_EQ(Extensions::TransportSockets::TransportProtocolNames::get().Quic, connection_socket.detectedTransportProtocol()); - EXPECT_EQ("h2", connection_socket.requestedApplicationProtocols()[0]); + EXPECT_EQ("h3-29", connection_socket.requestedApplicationProtocols()[0]); return &filter_chain_; })); EXPECT_CALL(filter_chain_, transportSocketFactory()) diff --git a/test/common/quic/envoy_quic_server_session_test.cc b/test/common/quic/envoy_quic_server_session_test.cc index c2c93111a1dd..586bbdd76602 100644 --- a/test/common/quic/envoy_quic_server_session_test.cc +++ b/test/common/quic/envoy_quic_server_session_test.cc @@ -800,29 +800,8 @@ TEST_P(EnvoyQuicServerSessionTest, GoAway) { http_connection_->goAway(); } -TEST_P(EnvoyQuicServerSessionTest, InitializeFilterChain) { - read_filter_ = std::make_shared(); - Network::MockFilterChain filter_chain; - crypto_stream_->setProofSourceDetails( - std::make_unique(filter_chain)); - std::vector filter_factory{[this]( - Network::FilterManager& filter_manager) { - filter_manager.addReadFilter(read_filter_); - read_filter_->callbacks_->connection().addConnectionCallbacks(network_connection_callbacks_); - read_filter_->callbacks_->connection().setConnectionStats( - {read_total_, read_current_, write_total_, write_current_, nullptr, nullptr}); - }}; - EXPECT_CALL(filter_chain, networkFilterFactories()).WillOnce(ReturnRef(filter_factory)); - EXPECT_CALL(*read_filter_, onNewConnection()) - // Stop iteration to avoid calling getRead/WriteBuffer(). - .WillOnce(Return(Network::FilterStatus::StopIteration)); - EXPECT_CALL(listener_config_.filter_chain_factory_, createNetworkFilterChain(_, _)) - .WillOnce(Invoke([](Network::Connection& connection, - const std::vector& filter_factories) { - EXPECT_EQ(1u, filter_factories.size()); - Server::Configuration::FilterChainUtility::buildFilterChain(connection, filter_factories); - return true; - })); +TEST_P(EnvoyQuicServerSessionTest, ConnectedAfterHandshake) { + installReadFilter(); EXPECT_CALL(network_connection_callbacks_, onEvent(Network::ConnectionEvent::Connected)); if (!quic_version_[0].UsesTls()) { envoy_quic_session_.SetDefaultEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 4650eeb9f6dd..6e0571b58bf5 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -39,6 +39,11 @@ class MockEnvoyQuicSession : public quic::QuicSpdySession, public QuicFilterMana crypto_stream_ = std::make_unique(this); } + void Initialize() override { + quic::QuicSpdySession::Initialize(); + initialized_ = true; + } + // From QuicSession. MOCK_METHOD(quic::QuicSpdyStream*, CreateIncomingStream, (quic::QuicStreamId id)); MOCK_METHOD(quic::QuicSpdyStream*, CreateIncomingStream, (quic::PendingStream * pending)); From dd14732a5f47e0c3d3ed3bc5a2f02f502823e257 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sat, 10 Apr 2021 14:13:57 -0400 Subject: [PATCH 28/52] enable upstream test Signed-off-by: Dan Zhang --- test/integration/protocol_integration_test.cc | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 29f13887fb74..996050da87d5 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -775,11 +775,6 @@ TEST_P(DownstreamProtocolIntegrationTest, RetryAttemptCountHeader) { // The retry priority will always target P1, which would otherwise never be hit due to P0 being // healthy. TEST_P(DownstreamProtocolIntegrationTest, RetryPriority) { - if (upstreamProtocol() == FakeHttpConnection::Type::HTTP2 && - downstreamProtocol() == Http::CodecClient::Type::HTTP3) { - // TODO(alyssawilk) investigate why this combination doesn't work. - return; - } EXCLUDE_UPSTREAM_HTTP3; const Upstream::HealthyLoad healthy_priority_load({0u, 100u}); const Upstream::DegradedLoad degraded_priority_load({0u, 100u}); @@ -1330,9 +1325,7 @@ TEST_P(ProtocolIntegrationTest, MissingStatus) { // Validate that lots of tiny cookies doesn't cause a DoS (single cookie header). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { - // TODO(alyssawilk) set upstream header size from config. - EXCLUDE_UPSTREAM_HTTP3; - if (downstream_protocol_ == Http::CodecClient::Type::HTTP3) { + if (downstreamProtocol() == Http::CodecClient::Type::HTTP3) { // QUICHE Qpack splits concatinated cookies into crumbs to increase // compression ratio. On the receiver side, the total size of these crumbs // may be larger than coalesced cookie headers. Increase the max request @@ -1341,6 +1334,9 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& hcm) -> void { hcm.mutable_max_request_headers_kb()->set_value(96); }); } + if (upstreamProtocol() == FakeHttpConnection::Type::HTTP3) { + setMaxRequestHeadersKb(96); + } initialize(); codec_client_ = makeHttpConnection(lookupPort("http")); @@ -1363,8 +1359,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { // Validate that lots of tiny cookies doesn't cause a DoS (many cookie headers). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingMany) { - // TODO(alyssawilk) set upstream header size from config. - EXCLUDE_UPSTREAM_HTTP3; // Set header count limit to 2010. uint32_t max_count = 2010; config_helper_.addConfigModifier( @@ -1573,8 +1567,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestUrlAccepted) { - // TODO(alyssawilk) set upstream header size from config. - EXCLUDE_UPSTREAM_HTTP3; // Send one 95 kB URL with limit 96 kB headers. testLargeRequestUrl(95, 96); } @@ -1585,8 +1577,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersRejected) { } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersAccepted) { - // TODO(alyssawilk) set upstream header size from config. - EXCLUDE_UPSTREAM_HTTP3; // Send one 95 kB header with limit 96 kB and 100 headers. testLargeRequestHeaders(95, 1, 96, 100); } @@ -1604,11 +1594,9 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersRejected) { - // QUICHE doesn't limit number of headers. - EXCLUDE_DOWNSTREAM_HTTP3 - EXCLUDE_UPSTREAM_HTTP3; // CI asan use-after-free - // Default header (and trailer) count limit is 100. - config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); + EXCLUDE_DOWNSTREAM_HTTP3 // QUICHE doesn't limit number of headers. + // Default header (and trailer) count limit is 100. + config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); Http::TestRequestTrailerMapImpl request_trailers; for (int i = 0; i < 150; i++) { @@ -1634,7 +1622,6 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersRejected) { } TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersAccepted) { - EXCLUDE_UPSTREAM_HTTP3; // assert failure: validHeaderString // Set header (and trailer) count limit to 200. uint32_t max_count = 200; config_helper_.addConfigModifier( @@ -1667,22 +1654,16 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersAccepted) { // This test uses an Http::HeaderMapImpl instead of an Http::TestHeaderMapImpl to avoid // time-consuming byte size validations that will cause this test to timeout. TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersTimeout) { - // TODO(danzh) re-enable this test after quic headers size become configurable. - EXCLUDE_UPSTREAM_HTTP3; // Set timeout for 5 seconds, and ensure that a request with 10k+ headers can be sent. testManyRequestHeaders(std::chrono::milliseconds(5000)); } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersAccepted) { - // TODO(danzh) re-enable this test after quic headers size become configurable. - EXCLUDE_UPSTREAM_HTTP3; config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); testLargeRequestTrailers(60, 96); } TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersRejected) { - // TODO(danzh) investigate why it failed for H3. - EXCLUDE_UPSTREAM_HTTP3; config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); testLargeRequestTrailers(66, 60); } @@ -1690,7 +1671,6 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestTrailersRejected) { // This test uses an Http::HeaderMapImpl instead of an Http::TestHeaderMapImpl to avoid // time-consuming byte size verification that will cause this test to timeout. TEST_P(DownstreamProtocolIntegrationTest, ManyTrailerHeaders) { - EXCLUDE_UPSTREAM_HTTP3; setMaxRequestHeadersKb(96); setMaxRequestHeadersCount(20005); From 8730535e2edfd93a27b5d041fee4e04a96772e53 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sun, 11 Apr 2021 17:52:33 -0400 Subject: [PATCH 29/52] fix format Signed-off-by: Dan Zhang --- test/integration/protocol_integration_test.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 996050da87d5..5e43506e8542 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1326,7 +1326,7 @@ TEST_P(ProtocolIntegrationTest, MissingStatus) { // Validate that lots of tiny cookies doesn't cause a DoS (single cookie header). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { if (downstreamProtocol() == Http::CodecClient::Type::HTTP3) { - // QUICHE Qpack splits concatinated cookies into crumbs to increase + // QUICHE Qpack splits concatenated cookies into crumbs to increase // compression ratio. On the receiver side, the total size of these crumbs // may be larger than coalesced cookie headers. Increase the max request // header size to avoid QUIC_HEADERS_TOO_LARGE stream error. @@ -1595,8 +1595,8 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersAccepted) { TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersRejected) { EXCLUDE_DOWNSTREAM_HTTP3 // QUICHE doesn't limit number of headers. - // Default header (and trailer) count limit is 100. - config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); + // The default configured header (and trailer) count limit is 100. + config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); Http::TestRequestTrailerMapImpl request_trailers; for (int i = 0; i < 150; i++) { From f423074dc8daafddcc6a6267a45582adbd9c9357 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sun, 11 Apr 2021 19:56:26 -0400 Subject: [PATCH 30/52] format Signed-off-by: Dan Zhang --- test/integration/protocol_integration_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 5e43506e8542..2c45b8e42c62 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1594,7 +1594,8 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersRejected) { - EXCLUDE_DOWNSTREAM_HTTP3 // QUICHE doesn't limit number of headers. + // QUICHE doesn't limit number of headers. + EXCLUDE_DOWNSTREAM_HTTP3 // The default configured header (and trailer) count limit is 100. config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); From 87cd21282bee67eb2b02292a3ff2c9f610901997 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sun, 11 Apr 2021 23:00:22 -0400 Subject: [PATCH 31/52] use absl::string_view Signed-off-by: Dan Zhang --- source/common/quic/envoy_quic_utils.cc | 4 +--- source/common/quic/envoy_quic_utils.h | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 8e0acf37fa28..de5193681c45 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -229,8 +229,7 @@ const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Network::FilterChainManager& filter_chain_manager, const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address, - const std::string& hostname, std::string_view alpn) { - std::cerr << "============ getFilterChain\n"; + const std::string& hostname, absl::string_view alpn) { Network::ConnectionSocketImpl connection_socket(std::make_unique(io_handle), quicAddressToEnvoyAddressInstance(self_address), quicAddressToEnvoyAddressInstance(peer_address)); @@ -238,7 +237,6 @@ const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Extensions::TransportSockets::TransportProtocolNames::get().Quic); connection_socket.setRequestedServerName(hostname); connection_socket.setRequestedApplicationProtocols({alpn}); - std::cerr << "============ findFilterChain\n"; return filter_chain_manager.findFilterChain(connection_socket); } diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index 6d8181b55884..32ca615854b1 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -130,7 +130,7 @@ const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, Network::FilterChainManager& filter_chain_manager, const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address, - const std::string& hostname, std::string_view alpn); + const std::string& hostname, absl::string_view alpn); } // namespace Quic } // namespace Envoy From 6c48a1245264b57e2ceb19d9853d05e997de745f Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Mon, 12 Apr 2021 17:31:32 -0400 Subject: [PATCH 32/52] fix ClientCodec test Signed-off-by: Dan Zhang --- .../quic_filter_manager_connection_impl.h | 7 +- test/common/http/BUILD | 47 +++++++----- test/common/http/codec_client_test.cc | 76 +++++++++++++++++-- 3 files changed, 97 insertions(+), 33 deletions(-) diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 51d6b46b72e4..4abdda9c9d0d 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -79,11 +79,10 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, return Network::Connection::State::Closed; } bool connecting() const override { - if (!initialized_ || - (quic_connection_ != nullptr && !quic_connection_->IsHandshakeComplete())) { - return true; + if (initialized_ && (quic_connection_ == nullptr || quic_connection_->IsHandshakeComplete())) { + return false; } - return false; + return true; } void write(Buffer::Instance& /*data*/, bool /*end_stream*/) override { // All writes should be handled by Quic internally. diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 523d5aba4035..355367872cae 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -52,27 +52,32 @@ envoy_cc_test( envoy_cc_test( name = "codec_client_test", srcs = ["codec_client_test.cc"], - deps = [ - ":common_lib", - "//source/common/buffer:buffer_lib", - "//source/common/event:dispatcher_lib", - "//source/common/http:codec_client_lib", - "//source/common/http:exception_lib", - "//source/common/network:utility_lib", - "//source/common/stream_info:stream_info_lib", - "//source/common/upstream:upstream_includes", - "//source/common/upstream:upstream_lib", - "//test/common/upstream:utility_lib", - "//test/mocks:common_lib", - "//test/mocks/event:event_mocks", - "//test/mocks/http:http_mocks", - "//test/mocks/network:network_mocks", - "//test/mocks/ssl:ssl_mocks", - "//test/mocks/upstream:cluster_info_mocks", - "//test/test_common:environment_lib", - "//test/test_common:network_utility_lib", - "//test/test_common:utility_lib", - ], + deps = + envoy_select_enable_http3([ + "//test/common/quic:quic_test_utils_for_envoy_lib", + "//source/common/quic:envoy_quic_connection_helper_lib", + "//source/common/quic:envoy_quic_alarm_factory_lib", + ]) + [ + ":common_lib", + "//source/common/buffer:buffer_lib", + "//source/common/event:dispatcher_lib", + "//source/common/http:codec_client_lib", + "//source/common/http:exception_lib", + "//source/common/network:utility_lib", + "//source/common/stream_info:stream_info_lib", + "//source/common/upstream:upstream_includes", + "//source/common/upstream:upstream_lib", + "//test/common/upstream:utility_lib", + "//test/mocks:common_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/http:http_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/ssl:ssl_mocks", + "//test/mocks/upstream:cluster_info_mocks", + "//test/test_common:environment_lib", + "//test/test_common:network_utility_lib", + "//test/test_common:utility_lib", + ], ) envoy_proto_library( diff --git a/test/common/http/codec_client_test.cc b/test/common/http/codec_client_test.cc index 4f601a5a4561..5099d4ba57ac 100644 --- a/test/common/http/codec_client_test.cc +++ b/test/common/http/codec_client_test.cc @@ -9,6 +9,24 @@ #include "common/stream_info/stream_info_impl.h" #include "common/upstream/upstream_impl.h" +#ifdef ENVOY_ENABLE_QUIC +#include "common/quic/codec_impl.h" +#include "common/quic/envoy_quic_connection_helper.h" +#include "common/quic/envoy_quic_alarm_factory.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/test_tools/crypto_test_utils.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +#endif + #include "test/common/http/common.h" #include "test/common/upstream/utility.h" #include "test/mocks/common.h" @@ -62,7 +80,11 @@ class CodecClientTest : public Event::TestUsingSimulatedTime, public testing::Te ON_CALL(*connection_, streamInfo()).WillByDefault(ReturnRef(stream_info_)); } - ~CodecClientTest() override { EXPECT_EQ(0U, client_->numActiveRequests()); } + ~CodecClientTest() override { + if (client_ != nullptr) { + EXPECT_EQ(0U, client_->numActiveRequests()); + } + } Event::MockDispatcher dispatcher_; Network::MockClientConnection* connection_; @@ -77,20 +99,58 @@ class CodecClientTest : public Event::TestUsingSimulatedTime, public testing::Te NiceMock stream_info_; }; +#ifdef ENVOY_ENABLE_QUIC +class MockEnvoyQuicClientSession : public Quic::EnvoyQuicClientSession { +public: + MockEnvoyQuicClientSession(quic::ParsedQuicVersionVector& quic_version, + Quic::EnvoyQuicConnectionHelper& connection_helper, + Quic::EnvoyQuicAlarmFactory& alarm_factory, + quic::QuicCryptoClientConfig* crypto_config, + Event::Dispatcher& dispatcher) + : EnvoyQuicClientSession(quic::QuicConfig(), quic_version, + std::make_unique( + quic::test::TestConnectionId(), connection_helper, alarm_factory, + new testing::NiceMock(), + /*owns_writer=*/true, quic_version, dispatcher, + std::unique_ptr( + new testing::NiceMock())), + quic::QuicServerId("example.com", 443, false), crypto_config, + /*push_promise_index=*/nullptr, dispatcher, + /*send_buffer_limit=*/16 * 1024 * 2) {} + + ~MockEnvoyQuicClientSession() override {} + + // Network::ClientConnection + MOCK_METHOD(void, connect, ()); + MOCK_METHOD(bool, connecting, (), (const)); + MOCK_METHOD(void, addConnectionCallbacks, (Network::ConnectionCallbacks & cb)); + MOCK_METHOD(void, addReadFilter, (Network::ReadFilterSharedPtr filter)); + MOCK_METHOD(void, detectEarlyCloseWhenReadDisabled, (bool)); +}; + TEST_F(CodecClientTest, NotCallDetectEarlyCloseWhenReadDiabledUsingHttp3) { - auto connection = std::make_unique>(); + testing::NiceMock dispatcher; + quic::ParsedQuicVersionVector quic_version = quic::CurrentSupportedVersions(); + Quic::EnvoyQuicConnectionHelper connection_helper(dispatcher); + Quic::EnvoyQuicAlarmFactory alarm_factory(dispatcher, *connection_helper.GetClock()); + quic::QuicCryptoClientConfig crypto_config( + quic::test::crypto_test_utils::ProofVerifierForTesting()); + auto connection = std::make_unique( + quic_version, connection_helper, alarm_factory, &crypto_config, dispatcher); + testing::NiceMock random; - EXPECT_CALL(*connection, connecting()).WillOnce(Return(true)); EXPECT_CALL(*connection, detectEarlyCloseWhenReadDisabled(false)).Times(0); EXPECT_CALL(*connection, addConnectionCallbacks(_)).WillOnce(SaveArgAddress(&connection_cb_)); - EXPECT_CALL(*connection, connect()); EXPECT_CALL(*connection, addReadFilter(_)); - auto codec = new Http::MockClientConnection(); + EXPECT_CALL(*connection, connecting()).WillOnce(Return(true)); + EXPECT_CALL(*connection, connect()); - EXPECT_CALL(dispatcher_, createTimer_(_)); - client_ = std::make_unique(CodecClient::Type::HTTP3, std::move(connection), - codec, nullptr, host_, dispatcher_); + auto client = std::make_unique(CodecClient::Type::HTTP3, std::move(connection), + host_, dispatcher, random); + EXPECT_EQ(0U, client->numActiveRequests()); + client->close(); } +#endif TEST_F(CodecClientTest, BasicHeaderOnlyResponse) { initialize(); From 59322e6125ea546044b619ddfeb3c735a7638760 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Mon, 12 Apr 2021 23:50:40 -0400 Subject: [PATCH 33/52] fix asan Signed-off-by: Dan Zhang --- test/integration/fake_upstream.cc | 1 + test/integration/fake_upstream.h | 2 +- test/integration/protocol_integration_test.cc | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 7c47e739c3c0..626b0c92c280 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -589,6 +589,7 @@ void FakeUpstream::threadRoutine() { { absl::MutexLock lock(&lock_); new_connections_.clear(); + quic_connections_.clear(); consumed_connections_.clear(); } } diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index ce92bcb06f5e..04bccb733b66 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -805,12 +805,12 @@ class FakeUpstream : Logger::Loggable, Event::DispatcherPtr dispatcher_; Network::ConnectionHandlerPtr handler_; std::list new_connections_ ABSL_GUARDED_BY(lock_); - std::list quic_connections_ ABSL_GUARDED_BY(lock_); // When a QueuedConnectionWrapper is popped from new_connections_, ownership is transferred to // consumed_connections_. This allows later the Connection destruction (when the FakeUpstream is // deleted) on the same thread that allocated the connection. std::list consumed_connections_ ABSL_GUARDED_BY(lock_); + std::list quic_connections_ ABSL_GUARDED_BY(lock_); const FakeUpstreamConfig config_; bool read_disable_on_new_connection_; const bool enable_half_close_; diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 2c45b8e42c62..c2c1797c42f7 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1594,8 +1594,10 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersRejected) { - // QUICHE doesn't limit number of headers. - EXCLUDE_DOWNSTREAM_HTTP3 + if (downstreamProtocol() == Http::CodecClient::Type::HTTP3) { + // QUICHE doesn't limit number of headers. + return; + } // The default configured header (and trailer) count limit is 100. config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); From 669ef788f98d055f67b9ac95ed0e05270a7427a2 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 13 Apr 2021 00:36:17 -0400 Subject: [PATCH 34/52] remove unused include Signed-off-by: Dan Zhang --- source/common/quic/envoy_quic_proof_source.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/common/quic/envoy_quic_proof_source.cc b/source/common/quic/envoy_quic_proof_source.cc index d7661beac92a..95dafe7b4048 100644 --- a/source/common/quic/envoy_quic_proof_source.cc +++ b/source/common/quic/envoy_quic_proof_source.cc @@ -1,7 +1,6 @@ #include "common/quic/envoy_quic_proof_source.h" #include -#include #include "envoy/ssl/tls_certificate_config.h" From 0396a8f48656fb71af2b18b419e0743cfe861093 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 13 Apr 2021 13:54:57 -0400 Subject: [PATCH 35/52] fix clang-tidy Signed-off-by: Dan Zhang --- test/common/http/codec_client_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/http/codec_client_test.cc b/test/common/http/codec_client_test.cc index 5099d4ba57ac..91aee93592bb 100644 --- a/test/common/http/codec_client_test.cc +++ b/test/common/http/codec_client_test.cc @@ -118,7 +118,7 @@ class MockEnvoyQuicClientSession : public Quic::EnvoyQuicClientSession { /*push_promise_index=*/nullptr, dispatcher, /*send_buffer_limit=*/16 * 1024 * 2) {} - ~MockEnvoyQuicClientSession() override {} + ~MockEnvoyQuicClientSession() override = default; // Network::ClientConnection MOCK_METHOD(void, connect, ()); From fecd6ba7f0effae14b3d97535e52fc7ac7484bda Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Thu, 15 Apr 2021 13:00:00 -0400 Subject: [PATCH 36/52] split QuicFilterManagerConnection interface Signed-off-by: Dan Zhang --- source/common/quic/BUILD | 11 ++-- .../quic/envoy_quic_client_connection.cc | 12 ++-- .../quic/envoy_quic_client_connection.h | 17 +++++- .../common/quic/envoy_quic_client_session.cc | 17 ++++-- .../common/quic/envoy_quic_client_session.h | 2 + source/common/quic/envoy_quic_connection.cc | 17 +----- source/common/quic/envoy_quic_connection.h | 29 ++------- source/common/quic/envoy_quic_dispatcher.cc | 13 ++-- source/common/quic/envoy_quic_proof_source.cc | 5 +- .../quic/envoy_quic_server_connection.cc | 25 ++++---- .../quic/envoy_quic_server_connection.h | 17 +++++- .../common/quic/envoy_quic_server_session.cc | 15 ++++- .../common/quic/envoy_quic_server_session.h | 7 ++- source/common/quic/envoy_quic_utils.cc | 27 +++++---- source/common/quic/envoy_quic_utils.h | 12 ++-- .../quic_filter_manager_connection_impl.cc | 26 ++++---- .../quic_filter_manager_connection_impl.h | 36 +++++++---- .../quic/envoy_quic_server_session_test.cc | 28 +-------- .../quic/envoy_quic_server_stream_test.cc | 15 +++-- test/common/quic/test_utils.h | 60 +++++++++++++++++-- 20 files changed, 233 insertions(+), 158 deletions(-) diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 60aad187ed47..3025dd1ea5e3 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -192,6 +192,7 @@ envoy_cc_library( "//source/common/http/http3:codec_stats_lib", "//source/common/network:connection_base_lib", "//source/common/stream_info:stream_info_lib", + "@com_googlesource_quiche//:quic_core_connection_lib", ], ) @@ -208,6 +209,7 @@ envoy_cc_library( tags = ["nofips"], deps = [ ":envoy_quic_proof_source_lib", + ":envoy_quic_server_connection_lib", ":envoy_quic_stream_lib", ":envoy_quic_utils_lib", ":quic_filter_manager_connection_lib", @@ -260,12 +262,8 @@ envoy_cc_library( hdrs = ["envoy_quic_connection.h"], tags = ["nofips"], deps = [ - ":quic_io_handle_wrapper_lib", "//include/envoy/network:connection_interface", "//source/common/network:listen_socket_lib", - "//source/common/quic:envoy_quic_utils_lib", - "//source/extensions/transport_sockets:well_known_names", - "@com_googlesource_quiche//:quic_core_connection_lib", ], ) @@ -276,7 +274,11 @@ envoy_cc_library( tags = ["nofips"], deps = [ ":envoy_quic_connection_lib", + ":quic_io_handle_wrapper_lib", + "//source/common/quic:envoy_quic_utils_lib", + "//source/extensions/transport_sockets:well_known_names", "//source/server:connection_handler_lib", + "@com_googlesource_quiche//:quic_core_connection_lib", ], ) @@ -291,6 +293,7 @@ envoy_cc_library( "//include/envoy/event:dispatcher_interface", "//source/common/network:socket_option_factory_lib", "//source/common/network:udp_packet_writer_handler_lib", + "@com_googlesource_quiche//:quic_core_connection_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/source/common/quic/envoy_quic_client_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index 3b914b91f630..8cf3b422ca8c 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -42,12 +42,12 @@ EnvoyQuicClientConnection::EnvoyQuicClientConnection( quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, bool owns_writer, const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket) - : EnvoyQuicConnection(server_connection_id, quic::QuicSocketAddress(), - envoyIpAddressToQuicSocketAddress( - connection_socket->addressProvider().remoteAddress()->ip()), - helper, alarm_factory, writer, owns_writer, quic::Perspective::IS_CLIENT, - supported_versions, std::move(connection_socket)), - dispatcher_(dispatcher) {} + : quic::QuicConnection(server_connection_id, quic::QuicSocketAddress(), + envoyIpAddressToQuicSocketAddress( + connection_socket->addressProvider().remoteAddress()->ip()), + &helper, &alarm_factory, writer, owns_writer, + quic::Perspective::IS_CLIENT, supported_versions), + EnvoyQuicConnection(std::move(connection_socket)), dispatcher_(dispatcher) {} void EnvoyQuicClientConnection::processPacket( Network::Address::InstanceConstSharedPtr local_address, diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index 8fa3b18100e7..06e759a88f00 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -4,12 +4,27 @@ #include "common/network/utility.h" #include "common/quic/envoy_quic_connection.h" +#include "common/quic/envoy_quic_utils.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/core/quic_connection.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif namespace Envoy { namespace Quic { // A client QuicConnection instance managing its own file events. -class EnvoyQuicClientConnection : public EnvoyQuicConnection, public Network::UdpPacketProcessor { +class EnvoyQuicClientConnection : public quic::QuicConnection, + public EnvoyQuicConnection, + public Network::UdpPacketProcessor { public: // A connection socket will be created with given |local_addr|. If binding // port not provided in |local_addr|, pick up a random port. diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index c064a6f31489..6f0484c9535e 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -11,20 +11,21 @@ EnvoyQuicClientSession::EnvoyQuicClientSession( quic::QuicCryptoClientConfig* crypto_config, quic::QuicClientPushPromiseIndex* push_promise_index, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit) - : QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), + : QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, + send_buffer_limit), quic::QuicSpdyClientSession(config, supported_versions, connection.release(), server_id, crypto_config, push_promise_index), host_name_(server_id.host()) {} EnvoyQuicClientSession::~EnvoyQuicClientSession() { ASSERT(!connection()->connected()); - quic_connection_ = nullptr; + network_connection_ = nullptr; } absl::string_view EnvoyQuicClientSession::requestedServerName() const { return host_name_; } void EnvoyQuicClientSession::connect() { - dynamic_cast(quic_connection_)->setUpConnectionSocket(); + dynamic_cast(network_connection_)->setUpConnectionSocket(); // Start version negotiation and crypto handshake during which the connection may fail if server // doesn't support the one and only supported version. CryptoConnect(); @@ -39,7 +40,7 @@ void EnvoyQuicClientSession::OnConnectionClosed(const quic::QuicConnectionCloseF void EnvoyQuicClientSession::Initialize() { quic::QuicSpdyClientSession::Initialize(); initialized_ = true; - quic_connection_->setEnvoyConnection(*this); + network_connection_->setEnvoyConnection(*this); } void EnvoyQuicClientSession::OnCanWrite() { @@ -100,6 +101,14 @@ EnvoyQuicClientSession::CreateIncomingStream(quic::PendingStream* /*pending*/) { bool EnvoyQuicClientSession::hasDataToWrite() { return HasDataToWrite(); } +const quic::QuicConnection* EnvoyQuicClientSession::quicConnection() const { + return initialized_ ? connection() : nullptr; +} + +quic::QuicConnection* EnvoyQuicClientSession::quicConnection() { + return initialized_ ? connection() : nullptr; +} + void EnvoyQuicClientSession::OnTlsHandshakeComplete() { raiseConnectionEvent(Network::ConnectionEvent::Connected); } diff --git a/source/common/quic/envoy_quic_client_session.h b/source/common/quic/envoy_quic_client_session.h index 54398175b83c..eb4f4ddf8911 100644 --- a/source/common/quic/envoy_quic_client_session.h +++ b/source/common/quic/envoy_quic_client_session.h @@ -77,6 +77,8 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, // QuicFilterManagerConnectionImpl bool hasDataToWrite() override; + const quic::QuicConnection* quicConnection() const override; + quic::QuicConnection* quicConnection() override; private: // These callbacks are owned by network filters and quic session should outlive diff --git a/source/common/quic/envoy_quic_connection.cc b/source/common/quic/envoy_quic_connection.cc index 5a51ada8cc19..d3ee04603dd9 100644 --- a/source/common/quic/envoy_quic_connection.cc +++ b/source/common/quic/envoy_quic_connection.cc @@ -1,23 +1,10 @@ #include "common/quic/envoy_quic_connection.h" -#include "common/quic/envoy_quic_utils.h" - namespace Envoy { namespace Quic { -EnvoyQuicConnection::EnvoyQuicConnection(const quic::QuicConnectionId& server_connection_id, - quic::QuicSocketAddress initial_self_address, - quic::QuicSocketAddress initial_peer_address, - quic::QuicConnectionHelperInterface& helper, - quic::QuicAlarmFactory& alarm_factory, - quic::QuicPacketWriter* writer, bool owns_writer, - quic::Perspective perspective, - const quic::ParsedQuicVersionVector& supported_versions, - Network::ConnectionSocketPtr&& connection_socket) - : quic::QuicConnection(server_connection_id, initial_self_address, initial_peer_address, - &helper, &alarm_factory, writer, owns_writer, perspective, - supported_versions), - connection_socket_(std::move(connection_socket)) {} +EnvoyQuicConnection::EnvoyQuicConnection(Network::ConnectionSocketPtr&& connection_socket) + : connection_socket_(std::move(connection_socket)) {} EnvoyQuicConnection::~EnvoyQuicConnection() { connection_socket_->close(); } diff --git a/source/common/quic/envoy_quic_connection.h b/source/common/quic/envoy_quic_connection.h index d853252e777f..58c2065c17e7 100644 --- a/source/common/quic/envoy_quic_connection.h +++ b/source/common/quic/envoy_quic_connection.h @@ -1,40 +1,21 @@ #pragma once -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - -#include "quiche/quic/core/quic_connection.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - #include -#include "common/common/logger.h" #include "envoy/network/connection.h" +#include "common/common/logger.h" + namespace Envoy { namespace Quic { // Derived for network filter chain, stats and QoS. This is used on both client // and server side. -class EnvoyQuicConnection : public quic::QuicConnection, - protected Logger::Loggable { +class EnvoyQuicConnection : protected Logger::Loggable { public: - EnvoyQuicConnection(const quic::QuicConnectionId& server_connection_id, - quic::QuicSocketAddress initial_self_address, - quic::QuicSocketAddress initial_peer_address, - quic::QuicConnectionHelperInterface& helper, - quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, - bool owns_writer, quic::Perspective perspective, - const quic::ParsedQuicVersionVector& supported_versions, - Network::ConnectionSocketPtr&& connection_socket); + EnvoyQuicConnection(Network::ConnectionSocketPtr&& connection_socket); - ~EnvoyQuicConnection() override; + virtual ~EnvoyQuicConnection(); // Called by EnvoyQuicSession::setConnectionStats(). void setConnectionStats(const Network::Connection::ConnectionStats& stats) { diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index 2746b2f437fe..ff8774f5713e 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -57,18 +57,19 @@ std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( Http2::Utility::OptionsLimits::MIN_INITIAL_STREAM_WINDOW_SIZE); quic_config.SetInitialSessionFlowControlWindowToSend( 1.5 * Http2::Utility::OptionsLimits::MIN_INITIAL_STREAM_WINDOW_SIZE); + + Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket( + listen_socket_.ioHandle(), self_address, peer_address, std::string(sni), alpn); + const Network::FilterChain* filter_chain = + listener_config_.filterChainManager().findFilterChain(*connection_socket); + auto quic_connection = std::make_unique( server_connection_id, self_address, peer_address, *helper(), *alarm_factory(), writer(), - /*owns_writer=*/false, quic::ParsedQuicVersionVector{version}, listen_socket_); + /*owns_writer=*/false, quic::ParsedQuicVersionVector{version}, std::move(connection_socket)); auto quic_session = std::make_unique( quic_config, quic::ParsedQuicVersionVector{version}, std::move(quic_connection), this, session_helper(), crypto_config(), compressed_certs_cache(), dispatcher_, listener_config_.perConnectionBufferLimitBytes(), listener_config_); - - const Network::FilterChain* filter_chain = - getFilterChain(listen_socket_.ioHandle(), listener_config_.filterChainManager(), self_address, - peer_address, std::string(sni), alpn); - ASSERT(filter_chain); if (filter_chain != nullptr) { const bool has_filter_initialized = listener_config_.filterChainFactory().createNetworkFilterChain( diff --git a/source/common/quic/envoy_quic_proof_source.cc b/source/common/quic/envoy_quic_proof_source.cc index 95dafe7b4048..bcc4dd3524dc 100644 --- a/source/common/quic/envoy_quic_proof_source.cc +++ b/source/common/quic/envoy_quic_proof_source.cc @@ -83,9 +83,10 @@ EnvoyQuicProofSource::getTlsCertConfigAndFilterChain(const quic::QuicSocketAddre const quic::QuicSocketAddress& client_address, const std::string& hostname) { ENVOY_LOG(trace, "Getting cert chain for {}", hostname); + Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket( + listen_socket_.ioHandle(), server_address, client_address, hostname, "h3-29"); const Network::FilterChain* filter_chain = - getFilterChain(listen_socket_.ioHandle(), filter_chain_manager_, server_address, - client_address, hostname, "h3-29"); + filter_chain_manager_.findFilterChain(*connection_socket); if (filter_chain == nullptr) { listener_stats_.no_filter_chain_match_.inc(); diff --git a/source/common/quic/envoy_quic_server_connection.cc b/source/common/quic/envoy_quic_server_connection.cc index 290b60e0fbb1..d86e2b8c29bf 100644 --- a/source/common/quic/envoy_quic_server_connection.cc +++ b/source/common/quic/envoy_quic_server_connection.cc @@ -14,29 +14,26 @@ EnvoyQuicServerConnection::EnvoyQuicServerConnection( quic::QuicSocketAddress initial_self_address, quic::QuicSocketAddress initial_peer_address, quic::QuicConnectionHelperInterface& helper, quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, bool owns_writer, - const quic::ParsedQuicVersionVector& supported_versions, Network::Socket& listen_socket) - : EnvoyQuicConnection(server_connection_id, initial_self_address, initial_peer_address, helper, - alarm_factory, writer, owns_writer, quic::Perspective::IS_SERVER, - supported_versions, - std::make_unique( - // Wraps the real IoHandle instance so that if the connection socket - // gets closed, the real IoHandle won't be affected. - std::make_unique(listen_socket.ioHandle()), - nullptr, quicAddressToEnvoyAddressInstance(initial_peer_address))) {} + const quic::ParsedQuicVersionVector& supported_versions, + Network::ConnectionSocketPtr connection_socket) + : quic::QuicConnection(server_connection_id, initial_self_address, initial_peer_address, + &helper, &alarm_factory, writer, owns_writer, + quic::Perspective::IS_SERVER, supported_versions), + EnvoyQuicConnection(std::move(connection_socket)) {} bool EnvoyQuicServerConnection::OnPacketHeader(const quic::QuicPacketHeader& header) { - if (!EnvoyQuicConnection::OnPacketHeader(header)) { + quic::QuicSocketAddress old_self_address = self_address(); + if (!quic::QuicConnection::OnPacketHeader(header)) { return false; } - if (connectionSocket()->addressProvider().localAddress() != nullptr) { + if (old_self_address == self_address()) { return true; } + // Update local address if QUICHE has updated the self address. ASSERT(self_address().IsInitialized()); - // Self address should be initialized by now. connectionSocket()->addressProvider().setLocalAddress( quicAddressToEnvoyAddressInstance(self_address())); - connectionSocket()->setDetectedTransportProtocol( - Extensions::TransportSockets::TransportProtocolNames::get().Quic); + return true; } diff --git a/source/common/quic/envoy_quic_server_connection.h b/source/common/quic/envoy_quic_server_connection.h index 62903793d985..74f08f5b2d8f 100644 --- a/source/common/quic/envoy_quic_server_connection.h +++ b/source/common/quic/envoy_quic_server_connection.h @@ -3,13 +3,26 @@ #include "envoy/network/listener.h" #include "common/quic/envoy_quic_connection.h" +#include "common/quic/envoy_quic_utils.h" #include "server/connection_handler_impl.h" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/core/quic_connection.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + namespace Envoy { namespace Quic { -class EnvoyQuicServerConnection : public EnvoyQuicConnection { +class EnvoyQuicServerConnection : public quic::QuicConnection, public EnvoyQuicConnection { public: EnvoyQuicServerConnection(const quic::QuicConnectionId& server_connection_id, quic::QuicSocketAddress initial_self_address, @@ -18,7 +31,7 @@ class EnvoyQuicServerConnection : public EnvoyQuicConnection { quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, bool owns_writer, const quic::ParsedQuicVersionVector& supported_versions, - Network::Socket& listen_socket); + Network::ConnectionSocketPtr connection_socket); // EnvoyQuicConnection // Overridden to set connection_socket_ with initialized self address and retrieve filter chain. diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index 9be80394bed4..bfb8b44e7acc 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -11,18 +11,19 @@ namespace Quic { EnvoyQuicServerSession::EnvoyQuicServerSession( const quic::QuicConfig& config, const quic::ParsedQuicVersionVector& supported_versions, - std::unique_ptr connection, quic::QuicSession::Visitor* visitor, + std::unique_ptr connection, quic::QuicSession::Visitor* visitor, quic::QuicCryptoServerStream::Helper* helper, const quic::QuicCryptoServerConfig* crypto_config, quic::QuicCompressedCertsCache* compressed_certs_cache, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, Network::ListenerConfig& /*listener_config*/) : quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper, crypto_config, compressed_certs_cache), - QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), + QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, + send_buffer_limit), quic_connection_(std::move(connection)) {} EnvoyQuicServerSession::~EnvoyQuicServerSession() { ASSERT(!quic_connection_->connected()); - QuicFilterManagerConnectionImpl::quic_connection_ = nullptr; + QuicFilterManagerConnectionImpl::network_connection_ = nullptr; } absl::string_view EnvoyQuicServerSession::requestedServerName() const { @@ -113,6 +114,14 @@ void EnvoyQuicServerSession::SetDefaultEncryptionLevel(quic::EncryptionLevel lev bool EnvoyQuicServerSession::hasDataToWrite() { return HasDataToWrite(); } +const quic::QuicConnection* EnvoyQuicServerSession::quicConnection() const { + return initialized_ ? connection() : nullptr; +} + +quic::QuicConnection* EnvoyQuicServerSession::quicConnection() { + return initialized_ ? connection() : nullptr; +} + void EnvoyQuicServerSession::OnTlsHandshakeComplete() { quic::QuicServerSessionBase::OnTlsHandshakeComplete(); raiseConnectionEvent(Network::ConnectionEvent::Connected); diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index 12f1d425a408..f976e6791ce6 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -19,6 +19,7 @@ #include "common/quic/send_buffer_monitor.h" #include "common/quic/quic_filter_manager_connection_impl.h" +#include "common/quic/envoy_quic_server_connection.h" #include "common/quic/envoy_quic_server_stream.h" namespace Envoy { @@ -33,7 +34,7 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, public: EnvoyQuicServerSession(const quic::QuicConfig& config, const quic::ParsedQuicVersionVector& supported_versions, - std::unique_ptr connection, + std::unique_ptr connection, quic::QuicSession::Visitor* visitor, quic::QuicCryptoServerStreamBase::Helper* helper, const quic::QuicCryptoServerConfig* crypto_config, @@ -87,11 +88,13 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, // QuicFilterManagerConnectionImpl bool hasDataToWrite() override; + const quic::QuicConnection* quicConnection() const override; + quic::QuicConnection* quicConnection() override; private: void setUpRequestDecoder(EnvoyQuicServerStream& stream); - std::unique_ptr quic_connection_; + std::unique_ptr quic_connection_; // These callbacks are owned by network filters and quic session should out live // them. Http::ServerConnectionCallbacks* http_connection_callbacks_{nullptr}; diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index de5193681c45..8f9460db3e6c 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -1,5 +1,7 @@ #include "common/quic/envoy_quic_utils.h" +#include + #include "envoy/common/platform.h" #include "envoy/config/core/v3/base.pb.h" @@ -225,19 +227,20 @@ int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::strin return sign_alg; } -const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, - Network::FilterChainManager& filter_chain_manager, - const quic::QuicSocketAddress& self_address, - const quic::QuicSocketAddress& peer_address, - const std::string& hostname, absl::string_view alpn) { - Network::ConnectionSocketImpl connection_socket(std::make_unique(io_handle), - quicAddressToEnvoyAddressInstance(self_address), - quicAddressToEnvoyAddressInstance(peer_address)); - connection_socket.setDetectedTransportProtocol( +Network::ConnectionSocketPtr +createServerConnectionSocket(Network::IoHandle& io_handle, + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address, + const std::string& hostname, absl::string_view alpn) { + auto connection_socket = std::make_unique( + std::make_unique(io_handle), + quicAddressToEnvoyAddressInstance(self_address), + quicAddressToEnvoyAddressInstance(peer_address)); + connection_socket->setDetectedTransportProtocol( Extensions::TransportSockets::TransportProtocolNames::get().Quic); - connection_socket.setRequestedServerName(hostname); - connection_socket.setRequestedApplicationProtocols({alpn}); - return filter_chain_manager.findFilterChain(connection_socket); + connection_socket->setRequestedServerName(hostname); + connection_socket->setRequestedApplicationProtocols({alpn}); + return connection_socket; } } // namespace Quic diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index 32ca615854b1..bbbda38cb8ac 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -126,11 +126,13 @@ bssl::UniquePtr parseDERCertificate(const std::string& der_bytes, std::str // not supported, return 0 with error_details populated correspondingly. int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::string* error_details); -const Network::FilterChain* getFilterChain(Network::IoHandle& io_handle, - Network::FilterChainManager& filter_chain_manager, - const quic::QuicSocketAddress& self_address, - const quic::QuicSocketAddress& peer_address, - const std::string& hostname, absl::string_view alpn); +// Return a connection socket which can get information from io_handle, but not +// be able to modify it. +Network::ConnectionSocketPtr +createServerConnectionSocket(Network::IoHandle& io_handle, + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address, + const std::string& hostname, absl::string_view alpn); } // namespace Quic } // namespace Envoy diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index 362b9062010c..fe75fc7544ed 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -6,13 +6,13 @@ namespace Envoy { namespace Quic { -QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl(EnvoyQuicConnection& connection, - Event::Dispatcher& dispatcher, - uint32_t send_buffer_limit) +QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl( + EnvoyQuicConnection& connection, const quic::QuicConnectionId& connection_id, + Event::Dispatcher& dispatcher, uint32_t send_buffer_limit) // Using this for purpose other than logging is not safe. Because QUIC connection id can be // 18 bytes, so there might be collision when it's hashed to 8 bytes. - : Network::ConnectionImplBase(dispatcher, /*id=*/connection.connection_id().Hash()), - quic_connection_(&connection), filter_manager_(*this, *connection.connectionSocket()), + : Network::ConnectionImplBase(dispatcher, /*id=*/connection_id.Hash()), + network_connection_(&connection), filter_manager_(*this, *connection.connectionSocket()), stream_info_(dispatcher.timeSource(), connection.connectionSocket()->addressProviderSharedPtr()), write_buffer_watermark_simulation_( @@ -62,7 +62,7 @@ bool QuicFilterManagerConnectionImpl::aboveHighWatermark() const { } void QuicFilterManagerConnectionImpl::close(Network::ConnectionCloseType type) { - if (quic_connection_ == nullptr) { + if (quicConnection() == nullptr) { // Already detached from quic connection. return; } @@ -93,7 +93,7 @@ void QuicFilterManagerConnectionImpl::close(Network::ConnectionCloseType type) { } else if (hasDataToWrite()) { // Quic connection has unsent data but caller wants to close right away. ASSERT(type == Network::ConnectionCloseType::NoFlush); - quic_connection_->OnCanWrite(); + quicConnection()->OnCanWrite(); closeConnectionImmediately(); } else { // Quic connection doesn't have unsent data. It's up to the caller and @@ -112,7 +112,7 @@ void QuicFilterManagerConnectionImpl::close(Network::ConnectionCloseType type) { const Network::ConnectionSocket::OptionsSharedPtr& QuicFilterManagerConnectionImpl::socketOptions() const { - return quic_connection_->connectionSocket()->options(); + return network_connection_->connectionSocket()->options(); } Ssl::ConnectionInfoConstSharedPtr QuicFilterManagerConnectionImpl::ssl() const { @@ -158,21 +158,21 @@ void QuicFilterManagerConnectionImpl::onConnectionCloseEvent( const quic::QuicConnectionCloseFrame& frame, quic::ConnectionCloseSource source) { transport_failure_reason_ = absl::StrCat(quic::QuicErrorCodeToString(frame.quic_error_code), " with details: ", frame.error_details); - if (quic_connection_ != nullptr) { + if (network_connection_ != nullptr) { // Tell network callbacks about connection close if not detached yet. raiseConnectionEvent(source == quic::ConnectionCloseSource::FROM_PEER ? Network::ConnectionEvent::RemoteClose : Network::ConnectionEvent::LocalClose); - ASSERT(quic_connection_ != nullptr); - quic_connection_ = nullptr; + ASSERT(network_connection_ != nullptr); + network_connection_ = nullptr; } } void QuicFilterManagerConnectionImpl::closeConnectionImmediately() { - if (quic_connection_ == nullptr) { + if (quicConnection() == nullptr) { return; } - quic_connection_->CloseConnection(quic::QUIC_NO_ERROR, "Closed by application", + quicConnection()->CloseConnection(quic::QUIC_NO_ERROR, "Closed by application", quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 4abdda9c9d0d..b45af7545c43 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -5,6 +5,18 @@ #include "envoy/event/dispatcher.h" #include "envoy/network/connection.h" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/core/quic_connection.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #include "common/common/empty_string.h" #include "common/common/logger.h" #include "common/http/http3/codec_stats.h" @@ -24,8 +36,9 @@ namespace Quic { class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, public SendBufferMonitor { public: - QuicFilterManagerConnectionImpl(EnvoyQuicConnection& connection, Event::Dispatcher& dispatcher, - uint32_t send_buffer_limit); + QuicFilterManagerConnectionImpl(EnvoyQuicConnection& connection, + const quic::QuicConnectionId& connection_id, + Event::Dispatcher& dispatcher, uint32_t send_buffer_limit); ~QuicFilterManagerConnectionImpl() override = default; // Network::FilterManager @@ -56,10 +69,10 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, void detectEarlyCloseWhenReadDisabled(bool /*value*/) override {} bool readEnabled() const override { return true; } const Network::SocketAddressSetter& addressProvider() const override { - return quic_connection_->connectionSocket()->addressProvider(); + return network_connection_->connectionSocket()->addressProvider(); } Network::SocketAddressProviderSharedPtr addressProviderSharedPtr() const override { - return quic_connection_->connectionSocket()->addressProviderSharedPtr(); + return network_connection_->connectionSocket()->addressProviderSharedPtr(); } absl::optional unixSocketPeerCredentials() const override { @@ -69,17 +82,17 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, void setConnectionStats(const Network::Connection::ConnectionStats& stats) override { // TODO(danzh): populate stats. Network::ConnectionImplBase::setConnectionStats(stats); - quic_connection_->setConnectionStats(stats); + network_connection_->setConnectionStats(stats); } Ssl::ConnectionInfoConstSharedPtr ssl() const override; Network::Connection::State state() const override { - if (!initialized_ || (quic_connection_ != nullptr && quic_connection_->connected())) { + if (!initialized_ || (quicConnection() != nullptr && quicConnection()->connected())) { return Network::Connection::State::Open; } return Network::Connection::State::Closed; } bool connecting() const override { - if (initialized_ && (quic_connection_ == nullptr || quic_connection_->IsHandshakeComplete())) { + if (initialized_ && (quicConnection() == nullptr || quicConnection()->IsHandshakeComplete())) { return false; } return true; @@ -130,7 +143,7 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, } protected: - // Propagate connection close to network_connection_callbacks_. + // Propagate connection close to network_network_connection_callbacks_. void onConnectionCloseEvent(const quic::QuicConnectionCloseFrame& frame, quic::ConnectionCloseSource source); @@ -138,12 +151,15 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, virtual bool hasDataToWrite() PURE; - EnvoyQuicConnection* quic_connection_{nullptr}; + // Returns a QuicConnection interface if initialized_ is true, otherwise nullptr. + virtual const quic::QuicConnection* quicConnection() const = 0; + virtual quic::QuicConnection* quicConnection() = 0; + + EnvoyQuicConnection* network_connection_{nullptr}; absl::optional> codec_stats_; absl::optional> http3_options_; - // If false, do not call into quic_connection_. bool initialized_{false}; private: diff --git a/test/common/quic/envoy_quic_server_session_test.cc b/test/common/quic/envoy_quic_server_session_test.cc index 586bbdd76602..10123266288f 100644 --- a/test/common/quic/envoy_quic_server_session_test.cc +++ b/test/common/quic/envoy_quic_server_session_test.cc @@ -55,28 +55,6 @@ using testing::ReturnRef; namespace Envoy { namespace Quic { -class TestEnvoyQuicServerConnection : public EnvoyQuicServerConnection { -public: - TestEnvoyQuicServerConnection(quic::QuicConnectionHelperInterface& helper, - quic::QuicAlarmFactory& alarm_factory, - quic::QuicPacketWriter& writer, - const quic::ParsedQuicVersionVector& supported_versions, - Network::Socket& listen_socket) - : EnvoyQuicServerConnection(quic::test::TestConnectionId(), - quic::QuicSocketAddress(quic::QuicIpAddress::Any4(), 12345), - quic::QuicSocketAddress(quic::QuicIpAddress::Loopback4(), 12345), - helper, alarm_factory, &writer, /*owns_writer=*/false, - supported_versions, listen_socket) {} - - Network::Connection::ConnectionStats& connectionStats() const { - return EnvoyQuicConnection::connectionStats(); - } - - MOCK_METHOD(void, SendConnectionClosePacket, - (quic::QuicErrorCode, quic::QuicIetfTransportErrorCodes, const std::string&)); - MOCK_METHOD(bool, SendControlFrame, (const quic::QuicFrame& frame)); -}; - // Derive to have simpler priority mechanism. class TestEnvoyQuicServerSession : public EnvoyQuicServerSession { public: @@ -154,12 +132,12 @@ class EnvoyQuicServerSessionTest : public testing::TestWithParam { SetQuicReloadableFlag(quic_disable_version_draft_29, !GetParam()); return quic::ParsedVersionOfIndex(quic::CurrentSupportedVersions(), 0); }()), - quic_connection_(new TestEnvoyQuicServerConnection( + quic_connection_(new MockEnvoyQuicServerConnection( connection_helper_, alarm_factory_, writer_, quic_version_, *listener_config_.socket_)), crypto_config_(quic::QuicCryptoServerConfig::TESTING, quic::QuicRandom::GetInstance(), std::make_unique(), quic::KeyExchangeSource::Default()), envoy_quic_session_(quic_config_, quic_version_, - std::unique_ptr(quic_connection_), + std::unique_ptr(quic_connection_), /*visitor=*/nullptr, &crypto_stream_helper_, &crypto_config_, &compressed_certs_cache_, *dispatcher_, /*send_buffer_limit*/ quic::kDefaultFlowControlSendWindow * 1.5, @@ -270,7 +248,7 @@ class EnvoyQuicServerSessionTest : public testing::TestWithParam { quic::ParsedQuicVersionVector quic_version_; testing::NiceMock writer_; testing::NiceMock listener_config_; - TestEnvoyQuicServerConnection* quic_connection_; + MockEnvoyQuicServerConnection* quic_connection_; quic::QuicConfig quic_config_; quic::QuicCryptoServerConfig crypto_config_; testing::NiceMock crypto_stream_helper_; diff --git a/test/common/quic/envoy_quic_server_stream_test.cc b/test/common/quic/envoy_quic_server_stream_test.cc index 3ec95ee9e2c6..d9a5643f6f0a 100644 --- a/test/common/quic/envoy_quic_server_stream_test.cc +++ b/test/common/quic/envoy_quic_server_stream_test.cc @@ -50,11 +50,8 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { listener_stats_({ALL_LISTENER_STATS(POOL_COUNTER(listener_config_.listenerScope()), POOL_GAUGE(listener_config_.listenerScope()), POOL_HISTOGRAM(listener_config_.listenerScope()))}), - quic_connection_(quic::test::TestConnectionId(), - quic::QuicSocketAddress(quic::QuicIpAddress::Any6(), 123), - quic::QuicSocketAddress(quic::QuicIpAddress::Any6(), 12345), - connection_helper_, alarm_factory_, &writer_, - /*owns_writer=*/false, {quic_version_}, *listener_config_.socket_), + quic_connection_(connection_helper_, alarm_factory_, writer_, + quic::ParsedQuicVersionVector{quic_version_}, *listener_config_.socket_), quic_session_(quic_config_, {quic_version_}, &quic_connection_, *dispatcher_, quic_config_.GetInitialStreamFlowControlWindowToSend() * 2), stream_id_(VersionUsesHttp3(quic_version_.transport_version) ? 4u : 5u), @@ -111,6 +108,8 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { EXPECT_CALL(quic_session_, MaybeSendRstStreamFrame(_, _, _)).Times(testing::AtMost(1u)); EXPECT_CALL(quic_session_, MaybeSendStopSendingFrame(_, quic::QUIC_STREAM_NO_ERROR)) .Times(testing::AtMost(1u)); + EXPECT_CALL(quic_connection_, + SendConnectionClosePacket(_, quic::NO_IETF_QUIC_ERROR, "Closed by application")); quic_session_.close(Network::ConnectionCloseType::NoFlush); } } @@ -166,7 +165,7 @@ class EnvoyQuicServerStreamTest : public testing::TestWithParam { quic::QuicConfig quic_config_; testing::NiceMock listener_config_; Server::ListenerStats listener_stats_; - EnvoyQuicServerConnection quic_connection_; + testing::NiceMock quic_connection_; MockEnvoyQuicSession quic_session_; quic::QuicStreamId stream_id_; Http::Http3::CodecStats stats_; @@ -605,6 +604,8 @@ TEST_P(EnvoyQuicServerStreamTest, ConnectionCloseDuringEncoding) { receiveRequest(request_body_, true, request_body_.size() * 2); quic_stream_->encodeHeaders(response_headers_, /*end_stream=*/false); std::string response(16 * 1024 + 1, 'a'); + EXPECT_CALL(quic_connection_, + SendConnectionClosePacket(_, quic::NO_IETF_QUIC_ERROR, "Closed in WriteHeaders")); EXPECT_CALL(quic_session_, WritevData(_, _, _, _, _, _)) .Times(testing::AtLeast(1u)) .WillRepeatedly( @@ -645,6 +646,8 @@ TEST_P(EnvoyQuicServerStreamTest, ConnectionCloseDuringEncoding) { // onResetStream() callbacks. TEST_P(EnvoyQuicServerStreamTest, ConnectionCloseAfterEndStreamEncoded) { receiveRequest(request_body_, true, request_body_.size() * 2); + EXPECT_CALL(quic_connection_, + SendConnectionClosePacket(_, quic::NO_IETF_QUIC_ERROR, "Closed in WriteHeaders")); EXPECT_CALL(quic_session_, WritevData(_, _, _, _, _, _)) .WillOnce( Invoke([this](quic::QuicStreamId, size_t, quic::QuicStreamOffset, diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 6e0571b58bf5..dd2dae725c6d 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -1,5 +1,7 @@ #pragma once +#include "common/quic/envoy_quic_client_connection.h" +#include "common/quic/envoy_quic_server_connection.h" #include "common/quic/quic_filter_manager_connection_impl.h" #if defined(__GNUC__) @@ -28,14 +30,50 @@ namespace Envoy { namespace Quic { +class MockEnvoyQuicServerConnection : public EnvoyQuicServerConnection { +public: + MockEnvoyQuicServerConnection(quic::QuicConnectionHelperInterface& helper, + quic::QuicAlarmFactory& alarm_factory, + quic::QuicPacketWriter& writer, + const quic::ParsedQuicVersionVector& supported_versions, + Network::Socket& listen_socket) + : MockEnvoyQuicServerConnection( + helper, alarm_factory, writer, + quic::QuicSocketAddress(quic::QuicIpAddress::Any4(), 12345), + quic::QuicSocketAddress(quic::QuicIpAddress::Loopback4(), 12345), supported_versions, + listen_socket) {} + + MockEnvoyQuicServerConnection(quic::QuicConnectionHelperInterface& helper, + quic::QuicAlarmFactory& alarm_factory, + quic::QuicPacketWriter& writer, + quic::QuicSocketAddress self_address, + quic::QuicSocketAddress peer_address, + const quic::ParsedQuicVersionVector& supported_versions, + Network::Socket& listen_socket) + : EnvoyQuicServerConnection( + quic::test::TestConnectionId(), self_address, peer_address, helper, alarm_factory, + &writer, /*owns_writer=*/false, supported_versions, + createServerConnectionSocket(listen_socket.ioHandle(), self_address, peer_address, + "example.com", "h3-29")) {} + + Network::Connection::ConnectionStats& connectionStats() const { + return EnvoyQuicConnection::connectionStats(); + } + + MOCK_METHOD(void, SendConnectionClosePacket, + (quic::QuicErrorCode, quic::QuicIetfTransportErrorCodes, const std::string&)); + MOCK_METHOD(bool, SendControlFrame, (const quic::QuicFrame& frame)); +}; + class MockEnvoyQuicSession : public quic::QuicSpdySession, public QuicFilterManagerConnectionImpl { public: MockEnvoyQuicSession(const quic::QuicConfig& config, const quic::ParsedQuicVersionVector& supported_versions, - EnvoyQuicConnection* connection, Event::Dispatcher& dispatcher, + EnvoyQuicServerConnection* connection, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit) : quic::QuicSpdySession(connection, /*visitor=*/nullptr, config, supported_versions), - QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit) { + QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, + send_buffer_limit) { crypto_stream_ = std::make_unique(this); } @@ -75,6 +113,10 @@ class MockEnvoyQuicSession : public quic::QuicSpdySession, public QuicFilterMana protected: bool hasDataToWrite() override { return HasDataToWrite(); } + const quic::QuicConnection* quicConnection() const override { + return initialized_ ? connection() : nullptr; + } + quic::QuicConnection* quicConnection() override { return initialized_ ? connection() : nullptr; } private: std::unique_ptr crypto_stream_; @@ -85,14 +127,20 @@ class MockEnvoyQuicClientSession : public quic::QuicSpdyClientSession, public: MockEnvoyQuicClientSession(const quic::QuicConfig& config, const quic::ParsedQuicVersionVector& supported_versions, - EnvoyQuicConnection* connection, Event::Dispatcher& dispatcher, + EnvoyQuicClientConnection* connection, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit) : quic::QuicSpdyClientSession(config, supported_versions, connection, quic::QuicServerId("example.com", 443, false), &crypto_config_, nullptr), - QuicFilterManagerConnectionImpl(*connection, dispatcher, send_buffer_limit), + QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, + send_buffer_limit), crypto_config_(quic::test::crypto_test_utils::ProofVerifierForTesting()) {} + void Initialize() override { + quic::QuicSpdyClientSession::Initialize(); + initialized_ = true; + } + // From QuicSession. MOCK_METHOD(quic::QuicSpdyClientStream*, CreateIncomingStream, (quic::QuicStreamId id)); MOCK_METHOD(quic::QuicSpdyClientStream*, CreateIncomingStream, (quic::PendingStream * pending)); @@ -115,6 +163,10 @@ class MockEnvoyQuicClientSession : public quic::QuicSpdyClientSession, protected: bool hasDataToWrite() override { return HasDataToWrite(); } + const quic::QuicConnection* quicConnection() const override { + return initialized_ ? connection() : nullptr; + } + quic::QuicConnection* quicConnection() override { return initialized_ ? connection() : nullptr; } private: quic::QuicCryptoClientConfig crypto_config_; From 75e85ceefc06180ce82aaa3c1eaadc3b055a6bee Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 16 Apr 2021 00:40:40 -0400 Subject: [PATCH 37/52] add client codec connect() Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 33 +++++++------------ source/common/http/codec_client.h | 5 +++ source/common/http/conn_pool_base.h | 1 + source/common/quic/active_quic_listener.cc | 1 + source/common/upstream/health_checker_impl.cc | 2 ++ test/common/http/codec_client_test.cc | 25 +++++++------- test/integration/http_integration.cc | 3 +- test/integration/utility.cc | 2 ++ 8 files changed, 38 insertions(+), 34 deletions(-) diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 07bee8d2e965..6f2de24245fa 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -34,19 +34,6 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, connection_->addConnectionCallbacks(*this); connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)}); - // Do not start handshake for H3 connection till it is initialized. - if (type_ != Type::HTTP3) { - // In general, codecs are handed new not-yet-connected connections, but in the - // case of ALPN, the codec may be handed an already connected connection. - if (!connection_->connecting()) { - ASSERT(connection_->state() == Network::Connection::State::Open); - connected_ = true; - } else { - ENVOY_CONN_LOG(debug, "connecting", *connection_); - connection_->connect(); - } - } - if (idle_timeout_) { idle_timer_ = dispatcher.createTimer([this]() -> void { onIdleTimeout(); }); enableIdleTimer(); @@ -59,6 +46,18 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, CodecClient::~CodecClient() = default; +void CodecClient::connect() { + // In general, codecs are handed new not-yet-connected connections, but in the + // case of ALPN, the codec may be handed an already connected connection. + if (!connection_->connecting()) { + ASSERT(connection_->state() == Network::Connection::State::Open); + connected_ = true; + } else { + ENVOY_CONN_LOG(debug, "connecting", *connection_); + connection_->connect(); + } +} + void CodecClient::close() { connection_->close(Network::ConnectionCloseType::NoFlush); } void CodecClient::deleteRequest(ActiveRequest& request) { @@ -195,14 +194,6 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne // Initialize the session after max request header size is changed in above http client // connection creation. quic_session.Initialize(); - // The other two codecs have already connected in base class. - if (!connection_->connecting()) { - ASSERT(connection_->state() == Network::Connection::State::Open); - connected_ = true; - } else { - ENVOY_CONN_LOG(debug, "connecting", *connection_); - connection_->connect(); - } break; #else // Should be blocked by configuration checking at an earlier point. diff --git a/source/common/http/codec_client.h b/source/common/http/codec_client.h index 667be493c2f7..a9c30d726ae3 100644 --- a/source/common/http/codec_client.h +++ b/source/common/http/codec_client.h @@ -59,6 +59,11 @@ class CodecClient : protected Logger::Loggable, ~CodecClient() override; + /** + * Connect to the host. + */ + void connect(); + /** * Add a connection callback to the underlying network connection. */ diff --git a/source/common/http/conn_pool_base.h b/source/common/http/conn_pool_base.h index cde57ea4fb89..51c73271fdfe 100644 --- a/source/common/http/conn_pool_base.h +++ b/source/common/http/conn_pool_base.h @@ -112,6 +112,7 @@ class ActiveClient : public Envoy::ConnectionPool::ActiveClient { void initialize(Upstream::Host::CreateConnectionData& data, HttpConnPoolImplBase& parent) { real_host_description_ = data.host_description_; codec_client_ = parent.createCodecClient(data); + codec_client_->connect(); codec_client_->addConnectionCallbacks(*this); codec_client_->setConnectionStats( {parent_.host()->cluster().stats().upstream_cx_rx_bytes_total_, diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index cc02afd8413e..084db4b46ec1 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -46,6 +46,7 @@ ActiveQuicListener::ActiveQuicListener( kernel_worker_routing_(kernel_worker_routing) { // This flag fix a QUICHE issue which may crash Envoy during connection close. SetQuicReloadableFlag(quic_single_ack_in_packet2, true); + // Do not include 32-byte per-entry overhead while counting header size. SetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead, false); if (Runtime::LoaderSingleton::getExisting()) { diff --git a/source/common/upstream/health_checker_impl.cc b/source/common/upstream/health_checker_impl.cc index 7b64ed5fda85..3aa4d48705d4 100644 --- a/source/common/upstream/health_checker_impl.cc +++ b/source/common/upstream/health_checker_impl.cc @@ -258,6 +258,7 @@ void HttpHealthCheckerImpl::HttpActiveHealthCheckSession::onInterval() { host_->createHealthCheckConnection(parent_.dispatcher_, parent_.transportSocketOptions(), parent_.transportSocketMatchMetadata().get()); client_.reset(parent_.createCodecClient(conn)); + client_->connect(); client_->addConnectionCallbacks(connection_callback_impl_); client_->setCodecConnectionCallbacks(http_connection_callback_impl_); expect_reset_ = false; @@ -706,6 +707,7 @@ void GrpcHealthCheckerImpl::GrpcActiveHealthCheckSession::onInterval() { host_->createHealthCheckConnection(parent_.dispatcher_, parent_.transportSocketOptions(), parent_.transportSocketMatchMetadata().get()); client_ = parent_.createCodecClient(conn); + client_->connect(); client_->addConnectionCallbacks(connection_callback_impl_); client_->setCodecConnectionCallbacks(http_connection_callback_impl_); } diff --git a/test/common/http/codec_client_test.cc b/test/common/http/codec_client_test.cc index 91aee93592bb..f617dad09c3f 100644 --- a/test/common/http/codec_client_test.cc +++ b/test/common/http/codec_client_test.cc @@ -77,6 +77,7 @@ class CodecClientTest : public Event::TestUsingSimulatedTime, public testing::Te EXPECT_CALL(dispatcher_, createTimer_(_)); client_ = std::make_unique(CodecClient::Type::HTTP1, std::move(connection), codec_, nullptr, host_, dispatcher_); + client_->connect(); ON_CALL(*connection_, streamInfo()).WillByDefault(ReturnRef(stream_info_)); } @@ -107,22 +108,21 @@ class MockEnvoyQuicClientSession : public Quic::EnvoyQuicClientSession { Quic::EnvoyQuicAlarmFactory& alarm_factory, quic::QuicCryptoClientConfig* crypto_config, Event::Dispatcher& dispatcher) - : EnvoyQuicClientSession(quic::QuicConfig(), quic_version, - std::make_unique( - quic::test::TestConnectionId(), connection_helper, alarm_factory, - new testing::NiceMock(), - /*owns_writer=*/true, quic_version, dispatcher, - std::unique_ptr( - new testing::NiceMock())), - quic::QuicServerId("example.com", 443, false), crypto_config, - /*push_promise_index=*/nullptr, dispatcher, - /*send_buffer_limit=*/16 * 1024 * 2) {} + : EnvoyQuicClientSession( + quic::QuicConfig(), quic_version, + std::make_unique( + quic::test::TestConnectionId(), connection_helper, alarm_factory, + new testing::NiceMock(), + /*owns_writer=*/true, quic_version, dispatcher, + std::make_unique>()), + quic::QuicServerId("example.com", 443, false), crypto_config, + /*push_promise_index=*/nullptr, dispatcher, + /*send_buffer_limit=*/16 * 1024 * 2) {} ~MockEnvoyQuicClientSession() override = default; // Network::ClientConnection MOCK_METHOD(void, connect, ()); - MOCK_METHOD(bool, connecting, (), (const)); MOCK_METHOD(void, addConnectionCallbacks, (Network::ConnectionCallbacks & cb)); MOCK_METHOD(void, addReadFilter, (Network::ReadFilterSharedPtr filter)); MOCK_METHOD(void, detectEarlyCloseWhenReadDisabled, (bool)); @@ -142,11 +142,11 @@ TEST_F(CodecClientTest, NotCallDetectEarlyCloseWhenReadDiabledUsingHttp3) { EXPECT_CALL(*connection, detectEarlyCloseWhenReadDisabled(false)).Times(0); EXPECT_CALL(*connection, addConnectionCallbacks(_)).WillOnce(SaveArgAddress(&connection_cb_)); EXPECT_CALL(*connection, addReadFilter(_)); - EXPECT_CALL(*connection, connecting()).WillOnce(Return(true)); EXPECT_CALL(*connection, connect()); auto client = std::make_unique(CodecClient::Type::HTTP3, std::move(connection), host_, dispatcher, random); + client->connect(); EXPECT_EQ(0U, client->numActiveRequests()); client->close(); } @@ -372,6 +372,7 @@ class CodecNetworkTest : public Event::TestUsingSimulatedTime, client_ = std::make_unique(CodecClient::Type::HTTP1, std::move(client_connection), codec_, nullptr, host_, *dispatcher_); + client_->connect(); int expected_callbacks = 2; EXPECT_CALL(listener_callbacks_, onAccept_(_)) diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index f04b8366654f..c15b0f9b102d 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -81,7 +81,6 @@ IntegrationCodecClient::IntegrationCodecClient( dispatcher_(dispatcher), callbacks_(*this), codec_callbacks_(*this) { connection_->addConnectionCallbacks(callbacks_); setCodecConnectionCallbacks(codec_callbacks_); - dispatcher.run(Event::Dispatcher::RunType::Block); } void IntegrationCodecClient::flushWrite() { @@ -270,6 +269,8 @@ IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection( // in-connection version negotiation. auto codec = std::make_unique(*dispatcher_, random_, std::move(conn), host_description, downstream_protocol_); + codec->connect(); + dispatcher_->run(Event::Dispatcher::RunType::Block); if (downstream_protocol_ == Http::CodecClient::Type::HTTP3 && codec->disconnected()) { // Connection may get closed during version negotiation or handshake. // TODO(#8479) QUIC connection doesn't support in-connection version negotiationPropagate diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 37286f756112..b6e2c64f0bae 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -198,6 +198,7 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt dispatcher->createClientConnection(addr, Network::Address::InstanceConstSharedPtr(), Network::Test::createRawBufferSocket(), nullptr), host_description, *dispatcher, random); + client.connect(); return sendRequestAndWaitForResponse(*dispatcher, method, url, body, host, content_type, client); } @@ -220,6 +221,7 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt Quic::createQuicNetworkConnection(*persistent_info, *dispatcher, addr, local_address); connection->addConnectionCallbacks(connection_callbacks); Http::CodecClientProd client(type, std::move(connection), host_description, *dispatcher, random); + client.connect(); // Quic connection needs to finish handshake. dispatcher->run(Event::Dispatcher::RunType::Block); return sendRequestAndWaitForResponse(*dispatcher, method, url, body, host, content_type, client); From 1b9a3b20d64987a51b3797622e0eaa7ce6273940 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 16 Apr 2021 13:10:28 -0400 Subject: [PATCH 38/52] fix clang-tidy Signed-off-by: Dan Zhang --- test/common/quic/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 567e93adce0e..696ef67546c9 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -274,6 +274,8 @@ envoy_cc_test_library( external_deps = ["bazel_runfiles"], tags = ["nofips"], deps = [ + "//source/common/quic:envoy_quic_client_connection_lib", + "//source/common/quic:envoy_quic_server_connection_lib", "//source/common/quic:quic_filter_manager_connection_lib", "//test/test_common:environment_lib", "@com_googlesource_quiche//:quic_core_http_spdy_session_lib", From 836a6e88df7b95d26564819c98c3ff20ce53d5d4 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 16 Apr 2021 15:13:36 -0400 Subject: [PATCH 39/52] reverted misfired replacement Signed-off-by: Dan Zhang --- source/common/http/codec_client.h | 2 +- source/common/quic/quic_filter_manager_connection_impl.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/common/http/codec_client.h b/source/common/http/codec_client.h index a9c30d726ae3..814cb184307a 100644 --- a/source/common/http/codec_client.h +++ b/source/common/http/codec_client.h @@ -182,7 +182,6 @@ class CodecClient : protected Logger::Loggable, ClientConnectionPtr codec_; Event::TimerPtr idle_timer_; const absl::optional idle_timeout_; - bool connected_{}; private: /** @@ -260,6 +259,7 @@ class CodecClient : protected Logger::Loggable, std::list active_requests_; Http::ConnectionCallbacks* codec_callbacks_{}; CodecClientCallbacks* codec_client_callbacks_{}; + bool connected_{}; bool remote_closed_{}; bool protocol_error_{false}; }; diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index b45af7545c43..55832b411538 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -143,7 +143,7 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, } protected: - // Propagate connection close to network_network_connection_callbacks_. + // Propagate connection close to network_connection_callbacks_. void onConnectionCloseEvent(const quic::QuicConnectionCloseFrame& frame, quic::ConnectionCloseSource source); From a8297cbff285b3f91c62d3d274859fd80304fec4 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Mon, 19 Apr 2021 12:29:16 -0400 Subject: [PATCH 40/52] address comments Signed-off-by: Dan Zhang --- source/common/quic/BUILD | 12 ++++++------ source/common/quic/envoy_quic_client_connection.cc | 2 +- source/common/quic/envoy_quic_client_connection.h | 4 ++-- source/common/quic/envoy_quic_client_session.h | 1 + source/common/quic/envoy_quic_connection.cc | 14 -------------- source/common/quic/envoy_quic_proof_source.cc | 1 + source/common/quic/envoy_quic_server_connection.cc | 2 +- source/common/quic/envoy_quic_server_connection.h | 6 +++--- source/common/quic/envoy_quic_server_session.h | 1 + source/common/quic/envoy_quic_utils.h | 4 ++-- .../quic/quic_filter_manager_connection_impl.cc | 2 +- .../quic/quic_filter_manager_connection_impl.h | 6 +++--- source/common/quic/quic_network_connection.cc | 14 ++++++++++++++ ...quic_connection.h => quic_network_connection.h} | 10 +++++----- test/common/quic/envoy_quic_dispatcher_test.cc | 2 +- test/common/quic/test_utils.h | 2 +- test/integration/protocol_integration_test.cc | 6 ++---- 17 files changed, 45 insertions(+), 44 deletions(-) delete mode 100644 source/common/quic/envoy_quic_connection.cc create mode 100644 source/common/quic/quic_network_connection.cc rename source/common/quic/{envoy_quic_connection.h => quic_network_connection.h} (82%) diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 3025dd1ea5e3..dc593de0fa00 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -180,8 +180,8 @@ envoy_cc_library( hdrs = ["quic_filter_manager_connection_impl.h"], tags = ["nofips"], deps = [ - ":envoy_quic_connection_lib", ":envoy_quic_simulated_watermark_buffer_lib", + ":quic_network_connection_lib", ":send_buffer_monitor_lib", "//include/envoy/event:dispatcher_interface", "//include/envoy/network:connection_interface", @@ -257,9 +257,9 @@ envoy_cc_library( ) envoy_cc_library( - name = "envoy_quic_connection_lib", - srcs = ["envoy_quic_connection.cc"], - hdrs = ["envoy_quic_connection.h"], + name = "quic_network_connection_lib", + srcs = ["quic_network_connection.cc"], + hdrs = ["quic_network_connection.h"], tags = ["nofips"], deps = [ "//include/envoy/network:connection_interface", @@ -273,8 +273,8 @@ envoy_cc_library( hdrs = ["envoy_quic_server_connection.h"], tags = ["nofips"], deps = [ - ":envoy_quic_connection_lib", ":quic_io_handle_wrapper_lib", + ":quic_network_connection_lib", "//source/common/quic:envoy_quic_utils_lib", "//source/extensions/transport_sockets:well_known_names", "//source/server:connection_handler_lib", @@ -288,8 +288,8 @@ envoy_cc_library( hdrs = ["envoy_quic_client_connection.h"], tags = ["nofips"], deps = [ - ":envoy_quic_connection_lib", ":envoy_quic_packet_writer_lib", + ":quic_network_connection_lib", "//include/envoy/event:dispatcher_interface", "//source/common/network:socket_option_factory_lib", "//source/common/network:udp_packet_writer_handler_lib", diff --git a/source/common/quic/envoy_quic_client_connection.cc b/source/common/quic/envoy_quic_client_connection.cc index 8cf3b422ca8c..158fa4d2244b 100644 --- a/source/common/quic/envoy_quic_client_connection.cc +++ b/source/common/quic/envoy_quic_client_connection.cc @@ -47,7 +47,7 @@ EnvoyQuicClientConnection::EnvoyQuicClientConnection( connection_socket->addressProvider().remoteAddress()->ip()), &helper, &alarm_factory, writer, owns_writer, quic::Perspective::IS_CLIENT, supported_versions), - EnvoyQuicConnection(std::move(connection_socket)), dispatcher_(dispatcher) {} + QuicNetworkConnection(std::move(connection_socket)), dispatcher_(dispatcher) {} void EnvoyQuicClientConnection::processPacket( Network::Address::InstanceConstSharedPtr local_address, diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index 06e759a88f00..dc3587a3b05c 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -3,8 +3,8 @@ #include "envoy/event/dispatcher.h" #include "common/network/utility.h" -#include "common/quic/envoy_quic_connection.h" #include "common/quic/envoy_quic_utils.h" +#include "common/quic/quic_network_connection.h" #if defined(__GNUC__) #pragma GCC diagnostic push @@ -23,7 +23,7 @@ namespace Quic { // A client QuicConnection instance managing its own file events. class EnvoyQuicClientConnection : public quic::QuicConnection, - public EnvoyQuicConnection, + public QuicNetworkConnection, public Network::UdpPacketProcessor { public: // A connection socket will be created with given |local_addr|. If binding diff --git a/source/common/quic/envoy_quic_client_session.h b/source/common/quic/envoy_quic_client_session.h index eb4f4ddf8911..638d99ffcb80 100644 --- a/source/common/quic/envoy_quic_client_session.h +++ b/source/common/quic/envoy_quic_client_session.h @@ -77,6 +77,7 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, // QuicFilterManagerConnectionImpl bool hasDataToWrite() override; + // Used by base class to access quic connection after initialization. const quic::QuicConnection* quicConnection() const override; quic::QuicConnection* quicConnection() override; diff --git a/source/common/quic/envoy_quic_connection.cc b/source/common/quic/envoy_quic_connection.cc deleted file mode 100644 index d3ee04603dd9..000000000000 --- a/source/common/quic/envoy_quic_connection.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include "common/quic/envoy_quic_connection.h" - -namespace Envoy { -namespace Quic { - -EnvoyQuicConnection::EnvoyQuicConnection(Network::ConnectionSocketPtr&& connection_socket) - : connection_socket_(std::move(connection_socket)) {} - -EnvoyQuicConnection::~EnvoyQuicConnection() { connection_socket_->close(); } - -uint64_t EnvoyQuicConnection::id() const { return envoy_connection_->id(); } - -} // namespace Quic -} // namespace Envoy diff --git a/source/common/quic/envoy_quic_proof_source.cc b/source/common/quic/envoy_quic_proof_source.cc index bcc4dd3524dc..7e24f82d5d10 100644 --- a/source/common/quic/envoy_quic_proof_source.cc +++ b/source/common/quic/envoy_quic_proof_source.cc @@ -83,6 +83,7 @@ EnvoyQuicProofSource::getTlsCertConfigAndFilterChain(const quic::QuicSocketAddre const quic::QuicSocketAddress& client_address, const std::string& hostname) { ENVOY_LOG(trace, "Getting cert chain for {}", hostname); + // TODO(danzh) modify QUICHE to make quic session or ALPN accessible to avoid hard-coded ALPN. Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket( listen_socket_.ioHandle(), server_address, client_address, hostname, "h3-29"); const Network::FilterChain* filter_chain = diff --git a/source/common/quic/envoy_quic_server_connection.cc b/source/common/quic/envoy_quic_server_connection.cc index d86e2b8c29bf..b17a9a88663f 100644 --- a/source/common/quic/envoy_quic_server_connection.cc +++ b/source/common/quic/envoy_quic_server_connection.cc @@ -19,7 +19,7 @@ EnvoyQuicServerConnection::EnvoyQuicServerConnection( : quic::QuicConnection(server_connection_id, initial_self_address, initial_peer_address, &helper, &alarm_factory, writer, owns_writer, quic::Perspective::IS_SERVER, supported_versions), - EnvoyQuicConnection(std::move(connection_socket)) {} + QuicNetworkConnection(std::move(connection_socket)) {} bool EnvoyQuicServerConnection::OnPacketHeader(const quic::QuicPacketHeader& header) { quic::QuicSocketAddress old_self_address = self_address(); diff --git a/source/common/quic/envoy_quic_server_connection.h b/source/common/quic/envoy_quic_server_connection.h index 74f08f5b2d8f..04c1a4b8f80d 100644 --- a/source/common/quic/envoy_quic_server_connection.h +++ b/source/common/quic/envoy_quic_server_connection.h @@ -2,8 +2,8 @@ #include "envoy/network/listener.h" -#include "common/quic/envoy_quic_connection.h" #include "common/quic/envoy_quic_utils.h" +#include "common/quic/quic_network_connection.h" #include "server/connection_handler_impl.h" @@ -22,7 +22,7 @@ namespace Envoy { namespace Quic { -class EnvoyQuicServerConnection : public quic::QuicConnection, public EnvoyQuicConnection { +class EnvoyQuicServerConnection : public quic::QuicConnection, public QuicNetworkConnection { public: EnvoyQuicServerConnection(const quic::QuicConnectionId& server_connection_id, quic::QuicSocketAddress initial_self_address, @@ -33,7 +33,7 @@ class EnvoyQuicServerConnection : public quic::QuicConnection, public EnvoyQuicC const quic::ParsedQuicVersionVector& supported_versions, Network::ConnectionSocketPtr connection_socket); - // EnvoyQuicConnection + // QuicNetworkConnection // Overridden to set connection_socket_ with initialized self address and retrieve filter chain. bool OnPacketHeader(const quic::QuicPacketHeader& header) override; }; diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index f976e6791ce6..a3d0069de598 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -88,6 +88,7 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, // QuicFilterManagerConnectionImpl bool hasDataToWrite() override; + // Used by base class to access quic connection after initialization. const quic::QuicConnection* quicConnection() const override; quic::QuicConnection* quicConnection() override; diff --git a/source/common/quic/envoy_quic_utils.h b/source/common/quic/envoy_quic_utils.h index bbbda38cb8ac..f30c582b7d53 100644 --- a/source/common/quic/envoy_quic_utils.h +++ b/source/common/quic/envoy_quic_utils.h @@ -126,8 +126,8 @@ bssl::UniquePtr parseDERCertificate(const std::string& der_bytes, std::str // not supported, return 0 with error_details populated correspondingly. int deduceSignatureAlgorithmFromPublicKey(const EVP_PKEY* public_key, std::string* error_details); -// Return a connection socket which can get information from io_handle, but not -// be able to modify it. +// Return a connection socket which read and write via io_handle, but doesn't close it when the +// socket gets closed nor set options on the socket. Network::ConnectionSocketPtr createServerConnectionSocket(Network::IoHandle& io_handle, const quic::QuicSocketAddress& self_address, diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index fe75fc7544ed..c4e40623fb2c 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -7,7 +7,7 @@ namespace Envoy { namespace Quic { QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl( - EnvoyQuicConnection& connection, const quic::QuicConnectionId& connection_id, + QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit) // Using this for purpose other than logging is not safe. Because QUIC connection id can be // 18 bytes, so there might be collision when it's hashed to 8 bytes. diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 55832b411538..2d7d34f76beb 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -21,7 +21,7 @@ #include "common/common/logger.h" #include "common/http/http3/codec_stats.h" #include "common/network/connection_impl_base.h" -#include "common/quic/envoy_quic_connection.h" +#include "common/quic/quic_network_connection.h" #include "common/quic/envoy_quic_simulated_watermark_buffer.h" #include "common/quic/send_buffer_monitor.h" #include "common/stream_info/stream_info_impl.h" @@ -36,7 +36,7 @@ namespace Quic { class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, public SendBufferMonitor { public: - QuicFilterManagerConnectionImpl(EnvoyQuicConnection& connection, + QuicFilterManagerConnectionImpl(QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit); ~QuicFilterManagerConnectionImpl() override = default; @@ -155,7 +155,7 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, virtual const quic::QuicConnection* quicConnection() const = 0; virtual quic::QuicConnection* quicConnection() = 0; - EnvoyQuicConnection* network_connection_{nullptr}; + QuicNetworkConnection* network_connection_{nullptr}; absl::optional> codec_stats_; absl::optional> diff --git a/source/common/quic/quic_network_connection.cc b/source/common/quic/quic_network_connection.cc new file mode 100644 index 000000000000..577178c9cc32 --- /dev/null +++ b/source/common/quic/quic_network_connection.cc @@ -0,0 +1,14 @@ +#include "common/quic/quic_network_connection.h" + +namespace Envoy { +namespace Quic { + +QuicNetworkConnection::QuicNetworkConnection(Network::ConnectionSocketPtr&& connection_socket) + : connection_socket_(std::move(connection_socket)) {} + +QuicNetworkConnection::~QuicNetworkConnection() { connection_socket_->close(); } + +uint64_t QuicNetworkConnection::id() const { return envoy_connection_->id(); } + +} // namespace Quic +} // namespace Envoy diff --git a/source/common/quic/envoy_quic_connection.h b/source/common/quic/quic_network_connection.h similarity index 82% rename from source/common/quic/envoy_quic_connection.h rename to source/common/quic/quic_network_connection.h index 58c2065c17e7..66c51a7d5215 100644 --- a/source/common/quic/envoy_quic_connection.h +++ b/source/common/quic/quic_network_connection.h @@ -9,13 +9,13 @@ namespace Envoy { namespace Quic { -// Derived for network filter chain, stats and QoS. This is used on both client -// and server side. -class EnvoyQuicConnection : protected Logger::Loggable { +// A base class of both the client and server connections which keeps stats and +// connection socket. +class QuicNetworkConnection : protected Logger::Loggable { public: - EnvoyQuicConnection(Network::ConnectionSocketPtr&& connection_socket); + QuicNetworkConnection(Network::ConnectionSocketPtr&& connection_socket); - virtual ~EnvoyQuicConnection(); + virtual ~QuicNetworkConnection(); // Called by EnvoyQuicSession::setConnectionStats(). void setConnectionStats(const Network::Connection::ConnectionStats& stats) { diff --git a/test/common/quic/envoy_quic_dispatcher_test.cc b/test/common/quic/envoy_quic_dispatcher_test.cc index 8260a8799f2e..3117745cf6b5 100644 --- a/test/common/quic/envoy_quic_dispatcher_test.cc +++ b/test/common/quic/envoy_quic_dispatcher_test.cc @@ -226,7 +226,7 @@ class EnvoyQuicDispatcherTest : public QuicMultiVersionTest, // Stop iteration to avoid calling getRead/WriteBuffer(). .WillOnce(Return(Network::FilterStatus::StopIteration)); if (!quicVersionUsesTls()) { - // 0-RTT is not supported in Quic TLS handshake yet. + // The test utility can't generate 0-RTT packet for Quic TLS handshake yet. EXPECT_CALL(network_connection_callbacks, onEvent(Network::ConnectionEvent::Connected)); } diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index dd2dae725c6d..0da7ee95ba92 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -57,7 +57,7 @@ class MockEnvoyQuicServerConnection : public EnvoyQuicServerConnection { "example.com", "h3-29")) {} Network::Connection::ConnectionStats& connectionStats() const { - return EnvoyQuicConnection::connectionStats(); + return QuicNetworkConnection::connectionStats(); } MOCK_METHOD(void, SendConnectionClosePacket, diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 98a3b553a717..7be52fd227d5 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1594,10 +1594,8 @@ TEST_P(DownstreamProtocolIntegrationTest, ManyRequestHeadersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, ManyRequestTrailersRejected) { - if (downstreamProtocol() == Http::CodecClient::Type::HTTP3) { - // QUICHE doesn't limit number of headers. - return; - } + // QUICHE doesn't limit number of headers. + EXCLUDE_DOWNSTREAM_HTTP3 // The default configured header (and trailer) count limit is 100. config_helper_.addConfigModifier(setEnableDownstreamTrailersHttp1()); config_helper_.addConfigModifier(setEnableUpstreamTrailersHttp1()); From 885a7b950ea9f3322129f99c2bb017f7ca42d25b Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 23 Apr 2021 17:38:18 -0400 Subject: [PATCH 41/52] address comments Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 10 ++- source/common/http/codec_client.h | 14 ++-- source/common/http/conn_pool_base.h | 1 - source/common/quic/BUILD | 8 ++ .../quic/envoy_quic_client_connection.h | 13 +--- source/common/quic/envoy_quic_dispatcher.cc | 3 +- .../quic/envoy_quic_server_connection.h | 13 +--- .../common/quic/envoy_quic_server_session.cc | 2 +- .../common/quic/envoy_quic_server_session.h | 3 +- .../quic_filter_manager_connection_impl.h | 13 +--- source/common/quic/quic_includes_ignores.h | 31 ++++++++ source/common/upstream/health_checker_impl.cc | 2 - test/common/http/BUILD | 47 +++++------ test/common/http/codec_client_test.cc | 77 ++----------------- test/common/http/common.h | 1 + .../quic/envoy_quic_server_session_test.cc | 3 +- test/integration/http_integration.cc | 3 +- test/integration/utility.cc | 2 - 18 files changed, 98 insertions(+), 148 deletions(-) create mode 100644 source/common/quic/quic_includes_ignores.h diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 6f2de24245fa..18cb00a80c73 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -44,9 +44,15 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, connection_->noDelay(true); } -CodecClient::~CodecClient() = default; +CodecClient::~CodecClient() { + ASSERT(connect_called_, "CodecClient::connect() is not called through out the life time."); +} void CodecClient::connect() { +#ifndef NDEBUG + connect_called_ = true; +#endif + ASSERT(codec_ != nullptr); // In general, codecs are handed new not-yet-connected connections, but in the // case of ALPN, the codec may be handed an already connected connection. if (!connection_->connecting()) { @@ -170,7 +176,6 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator) : CodecClient(type, std::move(connection), host, dispatcher) { - switch (type) { case Type::HTTP1: { codec_ = std::make_unique( @@ -201,6 +206,7 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne #endif } } + connect(); } } // namespace Http diff --git a/source/common/http/codec_client.h b/source/common/http/codec_client.h index 814cb184307a..2d1c1e2ed238 100644 --- a/source/common/http/codec_client.h +++ b/source/common/http/codec_client.h @@ -59,11 +59,6 @@ class CodecClient : protected Logger::Loggable, ~CodecClient() override; - /** - * Connect to the host. - */ - void connect(); - /** * Add a connection callback to the underlying network connection. */ @@ -145,6 +140,12 @@ class CodecClient : protected Logger::Loggable, CodecClient(Type type, Network::ClientConnectionPtr&& connection, Upstream::HostDescriptionConstSharedPtr host, Event::Dispatcher& dispatcher); + /** + * Connect to the host. + * Needs to be called after codec_ is instantiated. + */ + void connect(); + // Http::ConnectionCallbacks void onGoAway(GoAwayErrorCode error_code) override { if (codec_callbacks_) { @@ -262,6 +263,9 @@ class CodecClient : protected Logger::Loggable, bool connected_{}; bool remote_closed_{}; bool protocol_error_{false}; +#ifndef NDEBUG + bool connect_called_{false}; +#endif }; using CodecClientPtr = std::unique_ptr; diff --git a/source/common/http/conn_pool_base.h b/source/common/http/conn_pool_base.h index 51c73271fdfe..cde57ea4fb89 100644 --- a/source/common/http/conn_pool_base.h +++ b/source/common/http/conn_pool_base.h @@ -112,7 +112,6 @@ class ActiveClient : public Envoy::ConnectionPool::ActiveClient { void initialize(Upstream::Host::CreateConnectionData& data, HttpConnPoolImplBase& parent) { real_host_description_ = data.host_description_; codec_client_ = parent.createCodecClient(data); - codec_client_->connect(); codec_client_->addConnectionCallbacks(*this); codec_client_->setConnectionStats( {parent_.host()->cluster().stats().upstream_cx_rx_bytes_total_, diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index dc593de0fa00..0962de6c3b8f 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -11,6 +11,11 @@ licenses(["notice"]) # Apache 2 # that are required to be selected into the build for http3 to work. envoy_package() +envoy_cc_library( + name = "quic_includes_ignores_lib", + hdrs = ["quic_includes_ignores.h"], +) + envoy_cc_library( name = "envoy_quic_alarm_lib", srcs = ["envoy_quic_alarm.cc"], @@ -181,6 +186,7 @@ envoy_cc_library( tags = ["nofips"], deps = [ ":envoy_quic_simulated_watermark_buffer_lib", + ":quic_includes_ignores_lib", ":quic_network_connection_lib", ":send_buffer_monitor_lib", "//include/envoy/event:dispatcher_interface", @@ -273,6 +279,7 @@ envoy_cc_library( hdrs = ["envoy_quic_server_connection.h"], tags = ["nofips"], deps = [ + ":quic_includes_ignores_lib", ":quic_io_handle_wrapper_lib", ":quic_network_connection_lib", "//source/common/quic:envoy_quic_utils_lib", @@ -289,6 +296,7 @@ envoy_cc_library( tags = ["nofips"], deps = [ ":envoy_quic_packet_writer_lib", + ":quic_includes_ignores_lib", ":quic_network_connection_lib", "//include/envoy/event:dispatcher_interface", "//source/common/network:socket_option_factory_lib", diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index dc3587a3b05c..cda240b01296 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -6,17 +6,8 @@ #include "common/quic/envoy_quic_utils.h" #include "common/quic/quic_network_connection.h" -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - -#include "quiche/quic/core/quic_connection.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif +#define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" +#include "common/quic/quic_includes_ignores.h" namespace Envoy { namespace Quic { diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index ff8774f5713e..862683019404 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -69,11 +69,12 @@ std::unique_ptr EnvoyQuicDispatcher::CreateQuicSession( auto quic_session = std::make_unique( quic_config, quic::ParsedQuicVersionVector{version}, std::move(quic_connection), this, session_helper(), crypto_config(), compressed_certs_cache(), dispatcher_, - listener_config_.perConnectionBufferLimitBytes(), listener_config_); + listener_config_.perConnectionBufferLimitBytes()); if (filter_chain != nullptr) { const bool has_filter_initialized = listener_config_.filterChainFactory().createNetworkFilterChain( *quic_session, filter_chain->networkFilterFactories()); + // QUIC listener must have HCM filter configured. Otherwise, stream creation later will fail. ASSERT(has_filter_initialized); } quic_session->Initialize(); diff --git a/source/common/quic/envoy_quic_server_connection.h b/source/common/quic/envoy_quic_server_connection.h index 04c1a4b8f80d..206633a11487 100644 --- a/source/common/quic/envoy_quic_server_connection.h +++ b/source/common/quic/envoy_quic_server_connection.h @@ -7,17 +7,8 @@ #include "server/connection_handler_impl.h" -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - -#include "quiche/quic/core/quic_connection.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif +#define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" +#include "common/quic/quic_includes_ignores.h" namespace Envoy { namespace Quic { diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index bfb8b44e7acc..44208d4fb3f3 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -14,7 +14,7 @@ EnvoyQuicServerSession::EnvoyQuicServerSession( std::unique_ptr connection, quic::QuicSession::Visitor* visitor, quic::QuicCryptoServerStream::Helper* helper, const quic::QuicCryptoServerConfig* crypto_config, quic::QuicCompressedCertsCache* compressed_certs_cache, Event::Dispatcher& dispatcher, - uint32_t send_buffer_limit, Network::ListenerConfig& /*listener_config*/) + uint32_t send_buffer_limit) : quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper, crypto_config, compressed_certs_cache), QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index a3d0069de598..1e4900e5632a 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -39,8 +39,7 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, quic::QuicCryptoServerStreamBase::Helper* helper, const quic::QuicCryptoServerConfig* crypto_config, quic::QuicCompressedCertsCache* compressed_certs_cache, - Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, - Network::ListenerConfig& listener_config); + Event::Dispatcher& dispatcher, uint32_t send_buffer_limit); ~EnvoyQuicServerSession() override; diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 2d7d34f76beb..f4d6811eb795 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -5,17 +5,8 @@ #include "envoy/event/dispatcher.h" #include "envoy/network/connection.h" -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - -#include "quiche/quic/core/quic_connection.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif +#define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" +#include "common/quic/quic_includes_ignores.h" #include "common/common/empty_string.h" #include "common/common/logger.h" diff --git a/source/common/quic/quic_includes_ignores.h b/source/common/quic/quic_includes_ignores.h new file mode 100644 index 000000000000..1a5a8a3b25e3 --- /dev/null +++ b/source/common/quic/quic_includes_ignores.h @@ -0,0 +1,31 @@ +#pragma once + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#ifdef QUICHE_INCLUDE_1 +#include QUICHE_INCLUDE_1 + +#endif +#ifdef QUICHE_INCLUDE_2 +#include QUICHE_INCLUDE_2 +#endif +#ifdef QUICHE_INCLUDE_3 +#include QUICHE_INCLUDE_3 +#endif +#ifdef QUICHE_INCLUDE_4 +#include QUICHE_INCLUDE_4 +#endif +#ifdef QUICHE_INCLUDE_5 +#include QUICHE_INCLUDE_5 +#endif +#ifdef QUICHE_INCLUDE_6 +#include QUICHE_INCLUDE_6 +#endif + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif diff --git a/source/common/upstream/health_checker_impl.cc b/source/common/upstream/health_checker_impl.cc index 3aa4d48705d4..7b64ed5fda85 100644 --- a/source/common/upstream/health_checker_impl.cc +++ b/source/common/upstream/health_checker_impl.cc @@ -258,7 +258,6 @@ void HttpHealthCheckerImpl::HttpActiveHealthCheckSession::onInterval() { host_->createHealthCheckConnection(parent_.dispatcher_, parent_.transportSocketOptions(), parent_.transportSocketMatchMetadata().get()); client_.reset(parent_.createCodecClient(conn)); - client_->connect(); client_->addConnectionCallbacks(connection_callback_impl_); client_->setCodecConnectionCallbacks(http_connection_callback_impl_); expect_reset_ = false; @@ -707,7 +706,6 @@ void GrpcHealthCheckerImpl::GrpcActiveHealthCheckSession::onInterval() { host_->createHealthCheckConnection(parent_.dispatcher_, parent_.transportSocketOptions(), parent_.transportSocketMatchMetadata().get()); client_ = parent_.createCodecClient(conn); - client_->connect(); client_->addConnectionCallbacks(connection_callback_impl_); client_->setCodecConnectionCallbacks(http_connection_callback_impl_); } diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 57ecb4b41c86..953c744753ca 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -52,32 +52,27 @@ envoy_cc_test( envoy_cc_test( name = "codec_client_test", srcs = ["codec_client_test.cc"], - deps = - envoy_select_enable_http3([ - "//test/common/quic:quic_test_utils_for_envoy_lib", - "//source/common/quic:envoy_quic_connection_helper_lib", - "//source/common/quic:envoy_quic_alarm_factory_lib", - ]) + [ - ":common_lib", - "//source/common/buffer:buffer_lib", - "//source/common/event:dispatcher_lib", - "//source/common/http:codec_client_lib", - "//source/common/http:exception_lib", - "//source/common/network:utility_lib", - "//source/common/stream_info:stream_info_lib", - "//source/common/upstream:upstream_includes", - "//source/common/upstream:upstream_lib", - "//test/common/upstream:utility_lib", - "//test/mocks:common_lib", - "//test/mocks/event:event_mocks", - "//test/mocks/http:http_mocks", - "//test/mocks/network:network_mocks", - "//test/mocks/ssl:ssl_mocks", - "//test/mocks/upstream:cluster_info_mocks", - "//test/test_common:environment_lib", - "//test/test_common:network_utility_lib", - "//test/test_common:utility_lib", - ], + deps = [ + ":common_lib", + "//source/common/buffer:buffer_lib", + "//source/common/event:dispatcher_lib", + "//source/common/http:codec_client_lib", + "//source/common/http:exception_lib", + "//source/common/network:utility_lib", + "//source/common/stream_info:stream_info_lib", + "//source/common/upstream:upstream_includes", + "//source/common/upstream:upstream_lib", + "//test/common/upstream:utility_lib", + "//test/mocks:common_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/http:http_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/ssl:ssl_mocks", + "//test/mocks/upstream:cluster_info_mocks", + "//test/test_common:environment_lib", + "//test/test_common:network_utility_lib", + "//test/test_common:utility_lib", + ], ) envoy_proto_library( diff --git a/test/common/http/codec_client_test.cc b/test/common/http/codec_client_test.cc index f617dad09c3f..4f601a5a4561 100644 --- a/test/common/http/codec_client_test.cc +++ b/test/common/http/codec_client_test.cc @@ -9,24 +9,6 @@ #include "common/stream_info/stream_info_impl.h" #include "common/upstream/upstream_impl.h" -#ifdef ENVOY_ENABLE_QUIC -#include "common/quic/codec_impl.h" -#include "common/quic/envoy_quic_connection_helper.h" -#include "common/quic/envoy_quic_alarm_factory.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - -#include "quiche/quic/test_tools/crypto_test_utils.h" - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif -#endif - #include "test/common/http/common.h" #include "test/common/upstream/utility.h" #include "test/mocks/common.h" @@ -77,15 +59,10 @@ class CodecClientTest : public Event::TestUsingSimulatedTime, public testing::Te EXPECT_CALL(dispatcher_, createTimer_(_)); client_ = std::make_unique(CodecClient::Type::HTTP1, std::move(connection), codec_, nullptr, host_, dispatcher_); - client_->connect(); ON_CALL(*connection_, streamInfo()).WillByDefault(ReturnRef(stream_info_)); } - ~CodecClientTest() override { - if (client_ != nullptr) { - EXPECT_EQ(0U, client_->numActiveRequests()); - } - } + ~CodecClientTest() override { EXPECT_EQ(0U, client_->numActiveRequests()); } Event::MockDispatcher dispatcher_; Network::MockClientConnection* connection_; @@ -100,57 +77,20 @@ class CodecClientTest : public Event::TestUsingSimulatedTime, public testing::Te NiceMock stream_info_; }; -#ifdef ENVOY_ENABLE_QUIC -class MockEnvoyQuicClientSession : public Quic::EnvoyQuicClientSession { -public: - MockEnvoyQuicClientSession(quic::ParsedQuicVersionVector& quic_version, - Quic::EnvoyQuicConnectionHelper& connection_helper, - Quic::EnvoyQuicAlarmFactory& alarm_factory, - quic::QuicCryptoClientConfig* crypto_config, - Event::Dispatcher& dispatcher) - : EnvoyQuicClientSession( - quic::QuicConfig(), quic_version, - std::make_unique( - quic::test::TestConnectionId(), connection_helper, alarm_factory, - new testing::NiceMock(), - /*owns_writer=*/true, quic_version, dispatcher, - std::make_unique>()), - quic::QuicServerId("example.com", 443, false), crypto_config, - /*push_promise_index=*/nullptr, dispatcher, - /*send_buffer_limit=*/16 * 1024 * 2) {} - - ~MockEnvoyQuicClientSession() override = default; - - // Network::ClientConnection - MOCK_METHOD(void, connect, ()); - MOCK_METHOD(void, addConnectionCallbacks, (Network::ConnectionCallbacks & cb)); - MOCK_METHOD(void, addReadFilter, (Network::ReadFilterSharedPtr filter)); - MOCK_METHOD(void, detectEarlyCloseWhenReadDisabled, (bool)); -}; - TEST_F(CodecClientTest, NotCallDetectEarlyCloseWhenReadDiabledUsingHttp3) { - testing::NiceMock dispatcher; - quic::ParsedQuicVersionVector quic_version = quic::CurrentSupportedVersions(); - Quic::EnvoyQuicConnectionHelper connection_helper(dispatcher); - Quic::EnvoyQuicAlarmFactory alarm_factory(dispatcher, *connection_helper.GetClock()); - quic::QuicCryptoClientConfig crypto_config( - quic::test::crypto_test_utils::ProofVerifierForTesting()); - auto connection = std::make_unique( - quic_version, connection_helper, alarm_factory, &crypto_config, dispatcher); - testing::NiceMock random; + auto connection = std::make_unique>(); + EXPECT_CALL(*connection, connecting()).WillOnce(Return(true)); EXPECT_CALL(*connection, detectEarlyCloseWhenReadDisabled(false)).Times(0); EXPECT_CALL(*connection, addConnectionCallbacks(_)).WillOnce(SaveArgAddress(&connection_cb_)); - EXPECT_CALL(*connection, addReadFilter(_)); EXPECT_CALL(*connection, connect()); + EXPECT_CALL(*connection, addReadFilter(_)); + auto codec = new Http::MockClientConnection(); - auto client = std::make_unique(CodecClient::Type::HTTP3, std::move(connection), - host_, dispatcher, random); - client->connect(); - EXPECT_EQ(0U, client->numActiveRequests()); - client->close(); + EXPECT_CALL(dispatcher_, createTimer_(_)); + client_ = std::make_unique(CodecClient::Type::HTTP3, std::move(connection), + codec, nullptr, host_, dispatcher_); } -#endif TEST_F(CodecClientTest, BasicHeaderOnlyResponse) { initialize(); @@ -372,7 +312,6 @@ class CodecNetworkTest : public Event::TestUsingSimulatedTime, client_ = std::make_unique(CodecClient::Type::HTTP1, std::move(client_connection), codec_, nullptr, host_, *dispatcher_); - client_->connect(); int expected_callbacks = 2; EXPECT_CALL(listener_callbacks_, onAccept_(_)) diff --git a/test/common/http/common.h b/test/common/http/common.h index c031767ef347..3a28e9b369b8 100644 --- a/test/common/http/common.h +++ b/test/common/http/common.h @@ -22,6 +22,7 @@ class CodecClientForTest : public Http::CodecClient { Upstream::HostDescriptionConstSharedPtr host, Event::Dispatcher& dispatcher) : CodecClient(type, std::move(connection), host, dispatcher), destroy_cb_(destroy_cb) { codec_.reset(codec); + connect(); } ~CodecClientForTest() override { if (destroy_cb_) { diff --git a/test/common/quic/envoy_quic_server_session_test.cc b/test/common/quic/envoy_quic_server_session_test.cc index 10123266288f..fe441dbc25cb 100644 --- a/test/common/quic/envoy_quic_server_session_test.cc +++ b/test/common/quic/envoy_quic_server_session_test.cc @@ -140,8 +140,7 @@ class EnvoyQuicServerSessionTest : public testing::TestWithParam { std::unique_ptr(quic_connection_), /*visitor=*/nullptr, &crypto_stream_helper_, &crypto_config_, &compressed_certs_cache_, *dispatcher_, - /*send_buffer_limit*/ quic::kDefaultFlowControlSendWindow * 1.5, - listener_config_), + /*send_buffer_limit*/ quic::kDefaultFlowControlSendWindow * 1.5), stats_({ALL_HTTP3_CODEC_STATS( POOL_COUNTER_PREFIX(listener_config_.listenerScope(), "http3."), POOL_GAUGE_PREFIX(listener_config_.listenerScope(), "http3."))}) { diff --git a/test/integration/http_integration.cc b/test/integration/http_integration.cc index 2cb713a45f92..8960e143407b 100644 --- a/test/integration/http_integration.cc +++ b/test/integration/http_integration.cc @@ -81,6 +81,7 @@ IntegrationCodecClient::IntegrationCodecClient( dispatcher_(dispatcher), callbacks_(*this), codec_callbacks_(*this) { connection_->addConnectionCallbacks(callbacks_); setCodecConnectionCallbacks(codec_callbacks_); + dispatcher.run(Event::Dispatcher::RunType::Block); } void IntegrationCodecClient::flushWrite() { @@ -269,8 +270,6 @@ IntegrationCodecClientPtr HttpIntegrationTest::makeRawHttpConnection( // in-connection version negotiation. auto codec = std::make_unique(*dispatcher_, random_, std::move(conn), host_description, downstream_protocol_); - codec->connect(); - dispatcher_->run(Event::Dispatcher::RunType::Block); if (downstream_protocol_ == Http::CodecClient::Type::HTTP3 && codec->disconnected()) { // Connection may get closed during version negotiation or handshake. // TODO(#8479) QUIC connection doesn't support in-connection version negotiationPropagate diff --git a/test/integration/utility.cc b/test/integration/utility.cc index b6e2c64f0bae..37286f756112 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -198,7 +198,6 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt dispatcher->createClientConnection(addr, Network::Address::InstanceConstSharedPtr(), Network::Test::createRawBufferSocket(), nullptr), host_description, *dispatcher, random); - client.connect(); return sendRequestAndWaitForResponse(*dispatcher, method, url, body, host, content_type, client); } @@ -221,7 +220,6 @@ IntegrationUtil::makeSingleRequest(const Network::Address::InstanceConstSharedPt Quic::createQuicNetworkConnection(*persistent_info, *dispatcher, addr, local_address); connection->addConnectionCallbacks(connection_callbacks); Http::CodecClientProd client(type, std::move(connection), host_description, *dispatcher, random); - client.connect(); // Quic connection needs to finish handshake. dispatcher->run(Event::Dispatcher::RunType::Block); return sendRequestAndWaitForResponse(*dispatcher, method, url, body, host, content_type, client); From 95561c4883354842b256cc8dc1030f75668e7478 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Fri, 23 Apr 2021 21:48:45 -0400 Subject: [PATCH 42/52] quic_header_size_limit_includes_overhead default to false Signed-off-by: Dan Zhang --- source/common/quic/active_quic_listener.cc | 2 -- source/common/quic/client_connection_factory_impl.cc | 1 - source/common/quic/platform/quiche_flags_impl.cc | 3 ++- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index 084db4b46ec1..ba987c33ae8a 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -46,8 +46,6 @@ ActiveQuicListener::ActiveQuicListener( kernel_worker_routing_(kernel_worker_routing) { // This flag fix a QUICHE issue which may crash Envoy during connection close. SetQuicReloadableFlag(quic_single_ack_in_packet2, true); - // Do not include 32-byte per-entry overhead while counting header size. - SetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead, false); if (Runtime::LoaderSingleton::getExisting()) { enabled_.emplace(Runtime::FeatureFlag(enabled, Runtime::LoaderSingleton::get())); diff --git a/source/common/quic/client_connection_factory_impl.cc b/source/common/quic/client_connection_factory_impl.cc index 88a1304e4b40..4fc23e597151 100644 --- a/source/common/quic/client_connection_factory_impl.cc +++ b/source/common/quic/client_connection_factory_impl.cc @@ -49,7 +49,6 @@ createQuicNetworkConnection(Http::PersistentQuicInfo& info, Event::Dispatcher& d info_impl->alarm_factory_, quic::ParsedQuicVersionVector{info_impl->supported_versions_[0]}, local_addr, dispatcher, nullptr); auto& static_info = StaticInfo::get(); - SetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead, false); auto ret = std::make_unique( static_info.quic_config_, info_impl->supported_versions_, std::move(connection), info_impl->server_id_, info_impl->crypto_config_.get(), &static_info.push_promise_index_, diff --git a/source/common/quic/platform/quiche_flags_impl.cc b/source/common/quic/platform/quiche_flags_impl.cc index 6475b71d92e7..641f31ae0b2f 100644 --- a/source/common/quic/platform/quiche_flags_impl.cc +++ b/source/common/quic/platform/quiche_flags_impl.cc @@ -33,7 +33,8 @@ absl::flat_hash_map makeFlagMap() { #define QUIC_PROTOCOL_FLAG(type, flag, ...) flags.emplace(FLAGS_##flag->name(), FLAGS_##flag); #include "quiche/quic/core/quic_protocol_flags_list.h" #undef QUIC_PROTOCOL_FLAG - + // Do not include 32-byte per-entry overhead while counting header size. + FLAGS_quic_header_size_limit_includes_overhead->setValue(false); return flags; } From 6eefb38799ce8084747bbffd3a5888f5c72f4de5 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sat, 24 Apr 2021 00:37:07 -0400 Subject: [PATCH 43/52] initialize flag registry Signed-off-by: Dan Zhang --- source/common/quic/active_quic_listener.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index ba987c33ae8a..6fe1af4b38d8 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -46,6 +46,9 @@ ActiveQuicListener::ActiveQuicListener( kernel_worker_routing_(kernel_worker_routing) { // This flag fix a QUICHE issue which may crash Envoy during connection close. SetQuicReloadableFlag(quic_single_ack_in_packet2, true); + // Do not include 32-byte per-entry overhead while counting header size. + quiche::FlagRegistry::getInstance(); + ASSERT(!GetQuicFlag(FLAGS_quic_header_size_limit_includes_overhead)); if (Runtime::LoaderSingleton::getExisting()) { enabled_.emplace(Runtime::FeatureFlag(enabled, Runtime::LoaderSingleton::get())); From 96aad88a373a471112f0dc0061796df4f0f5b3e5 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sat, 24 Apr 2021 00:49:18 -0400 Subject: [PATCH 44/52] register flags for client side Signed-off-by: Dan Zhang --- source/common/quic/client_connection_factory_impl.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/common/quic/client_connection_factory_impl.cc b/source/common/quic/client_connection_factory_impl.cc index 4fc23e597151..ffb6951237f9 100644 --- a/source/common/quic/client_connection_factory_impl.cc +++ b/source/common/quic/client_connection_factory_impl.cc @@ -22,7 +22,9 @@ PersistentQuicInfoImpl::PersistentQuicInfoImpl( static_cast(server_addr->ip()->port()), false}, crypto_config_( std::make_unique(std::make_unique( - stats_scope, getConfig(transport_socket_factory), time_source))) {} + stats_scope, getConfig(transport_socket_factory), time_source))) { + quiche::FlagRegistry::getInstance(); +} namespace { // TODO(alyssawilk, danzh2010): This is mutable static info that is required for the QUICHE code. From 6c3887989121f86367c50a426479377f0be806f2 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sun, 25 Apr 2021 16:03:13 -0400 Subject: [PATCH 45/52] NOLINT(namespace-envoy) Signed-off-by: Dan Zhang --- source/common/quic/quic_includes_ignores.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/common/quic/quic_includes_ignores.h b/source/common/quic/quic_includes_ignores.h index 1a5a8a3b25e3..42ef9b81fd3a 100644 --- a/source/common/quic/quic_includes_ignores.h +++ b/source/common/quic/quic_includes_ignores.h @@ -1,5 +1,8 @@ #pragma once +// NOLINT(namespace-envoy) +// A wrapper of QUICHE includes which supresses some compilation warnings thrown from QUICHE. + #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" From 7b29d70e0edb7a31b30c7a1a763d89fb133c967b Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sun, 25 Apr 2021 22:42:50 -0400 Subject: [PATCH 46/52] typo Signed-off-by: Dan Zhang --- source/common/quic/quic_includes_ignores.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/quic/quic_includes_ignores.h b/source/common/quic/quic_includes_ignores.h index 42ef9b81fd3a..fa7ff9d86eac 100644 --- a/source/common/quic/quic_includes_ignores.h +++ b/source/common/quic/quic_includes_ignores.h @@ -1,7 +1,7 @@ #pragma once // NOLINT(namespace-envoy) -// A wrapper of QUICHE includes which supresses some compilation warnings thrown from QUICHE. +// A wrapper of QUICHE includes which suppresses some compilation warnings thrown from QUICHE. #if defined(__GNUC__) #pragma GCC diagnostic push From b6e3ace93a1e997532cb9f77becceea9f34800c3 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Sun, 25 Apr 2021 23:33:50 -0400 Subject: [PATCH 47/52] connected_called_ NDEBUG Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 18cb00a80c73..439236eeba3c 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -45,7 +45,9 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, } CodecClient::~CodecClient() { +#ifndef NDEBUG ASSERT(connect_called_, "CodecClient::connect() is not called through out the life time."); +#endif } void CodecClient::connect() { From 5d77b161d04f3954ad99bf79d36a2e74c676dfc4 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Mon, 26 Apr 2021 16:08:24 -0400 Subject: [PATCH 48/52] remove DDEBUG Signed-off-by: Dan Zhang --- source/common/http/codec_client.cc | 4 ---- source/common/http/codec_client.h | 2 -- source/common/quic/envoy_quic_client_connection.h | 2 ++ source/common/quic/envoy_quic_server_connection.h | 2 ++ source/common/quic/quic_filter_manager_connection_impl.h | 2 ++ source/common/quic/quic_includes_ignores.h | 4 ++++ 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/source/common/http/codec_client.cc b/source/common/http/codec_client.cc index 439236eeba3c..7ea1916839b6 100644 --- a/source/common/http/codec_client.cc +++ b/source/common/http/codec_client.cc @@ -45,15 +45,11 @@ CodecClient::CodecClient(Type type, Network::ClientConnectionPtr&& connection, } CodecClient::~CodecClient() { -#ifndef NDEBUG ASSERT(connect_called_, "CodecClient::connect() is not called through out the life time."); -#endif } void CodecClient::connect() { -#ifndef NDEBUG connect_called_ = true; -#endif ASSERT(codec_ != nullptr); // In general, codecs are handed new not-yet-connected connections, but in the // case of ALPN, the codec may be handed an already connected connection. diff --git a/source/common/http/codec_client.h b/source/common/http/codec_client.h index 2d1c1e2ed238..07eb9724d607 100644 --- a/source/common/http/codec_client.h +++ b/source/common/http/codec_client.h @@ -263,9 +263,7 @@ class CodecClient : protected Logger::Loggable, bool connected_{}; bool remote_closed_{}; bool protocol_error_{false}; -#ifndef NDEBUG bool connect_called_{false}; -#endif }; using CodecClientPtr = std::unique_ptr; diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index cda240b01296..3f156c0816d4 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -6,6 +6,8 @@ #include "common/quic/envoy_quic_utils.h" #include "common/quic/quic_network_connection.h" +// Use QUICHE_INCLUDE_[1...N] to include needed QUICHE headers before including +// quic_includes_ignores.h #define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" #include "common/quic/quic_includes_ignores.h" diff --git a/source/common/quic/envoy_quic_server_connection.h b/source/common/quic/envoy_quic_server_connection.h index 206633a11487..009a9960079f 100644 --- a/source/common/quic/envoy_quic_server_connection.h +++ b/source/common/quic/envoy_quic_server_connection.h @@ -7,6 +7,8 @@ #include "server/connection_handler_impl.h" +// Use QUICHE_INCLUDE_[1...N] to include needed QUICHE headers before including +// quic_includes_ignores.h #define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" #include "common/quic/quic_includes_ignores.h" diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 9ba85efe47c3..2250c3be7a9a 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -5,6 +5,8 @@ #include "envoy/event/dispatcher.h" #include "envoy/network/connection.h" +// Use QUICHE_INCLUDE_[1...N] to include needed QUICHE headers before including +// quic_includes_ignores.h #define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" #include "common/quic/quic_includes_ignores.h" diff --git a/source/common/quic/quic_includes_ignores.h b/source/common/quic/quic_includes_ignores.h index fa7ff9d86eac..21e1a92bf6bf 100644 --- a/source/common/quic/quic_includes_ignores.h +++ b/source/common/quic/quic_includes_ignores.h @@ -2,6 +2,10 @@ // NOLINT(namespace-envoy) // A wrapper of QUICHE includes which suppresses some compilation warnings thrown from QUICHE. +// Usage: +// #define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" +// #define QUICHE_INCLUDE_2 "quiche/quic/core/quic_session.h" +// #include common/quic/quic_includes_ignores.h #if defined(__GNUC__) #pragma GCC diagnostic push From 1677acc4a9d729e483cb06aac8c1ff7b7ca399fc Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Tue, 27 Apr 2021 16:06:32 -0400 Subject: [PATCH 49/52] remove quic_includes_ignores Signed-off-by: Dan Zhang --- source/common/quic/BUILD | 8 ---- .../quic/envoy_quic_client_connection.h | 15 ++++++-- .../quic/envoy_quic_server_connection.h | 15 ++++++-- .../quic_filter_manager_connection_impl.h | 15 ++++++-- source/common/quic/quic_includes_ignores.h | 38 ------------------- 5 files changed, 33 insertions(+), 58 deletions(-) delete mode 100644 source/common/quic/quic_includes_ignores.h diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 28f57bec6276..9e4dae98192f 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -11,11 +11,6 @@ licenses(["notice"]) # Apache 2 # that are required to be selected into the build for http3 to work. envoy_package() -envoy_cc_library( - name = "quic_includes_ignores_lib", - hdrs = ["quic_includes_ignores.h"], -) - envoy_cc_library( name = "envoy_quic_alarm_lib", srcs = ["envoy_quic_alarm.cc"], @@ -186,7 +181,6 @@ envoy_cc_library( tags = ["nofips"], deps = [ ":envoy_quic_simulated_watermark_buffer_lib", - ":quic_includes_ignores_lib", ":quic_network_connection_lib", ":send_buffer_monitor_lib", "//include/envoy/event:dispatcher_interface", @@ -279,7 +273,6 @@ envoy_cc_library( hdrs = ["envoy_quic_server_connection.h"], tags = ["nofips"], deps = [ - ":quic_includes_ignores_lib", ":quic_io_handle_wrapper_lib", ":quic_network_connection_lib", "//source/common/quic:envoy_quic_utils_lib", @@ -296,7 +289,6 @@ envoy_cc_library( tags = ["nofips"], deps = [ ":envoy_quic_packet_writer_lib", - ":quic_includes_ignores_lib", ":quic_network_connection_lib", "//include/envoy/event:dispatcher_interface", "//source/common/network:socket_option_factory_lib", diff --git a/source/common/quic/envoy_quic_client_connection.h b/source/common/quic/envoy_quic_client_connection.h index 3f156c0816d4..dc3587a3b05c 100644 --- a/source/common/quic/envoy_quic_client_connection.h +++ b/source/common/quic/envoy_quic_client_connection.h @@ -6,10 +6,17 @@ #include "common/quic/envoy_quic_utils.h" #include "common/quic/quic_network_connection.h" -// Use QUICHE_INCLUDE_[1...N] to include needed QUICHE headers before including -// quic_includes_ignores.h -#define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" -#include "common/quic/quic_includes_ignores.h" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/core/quic_connection.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif namespace Envoy { namespace Quic { diff --git a/source/common/quic/envoy_quic_server_connection.h b/source/common/quic/envoy_quic_server_connection.h index 009a9960079f..04c1a4b8f80d 100644 --- a/source/common/quic/envoy_quic_server_connection.h +++ b/source/common/quic/envoy_quic_server_connection.h @@ -7,10 +7,17 @@ #include "server/connection_handler_impl.h" -// Use QUICHE_INCLUDE_[1...N] to include needed QUICHE headers before including -// quic_includes_ignores.h -#define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" -#include "common/quic/quic_includes_ignores.h" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/core/quic_connection.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif namespace Envoy { namespace Quic { diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 2250c3be7a9a..359dcb2a28e1 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -5,10 +5,17 @@ #include "envoy/event/dispatcher.h" #include "envoy/network/connection.h" -// Use QUICHE_INCLUDE_[1...N] to include needed QUICHE headers before including -// quic_includes_ignores.h -#define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" -#include "common/quic/quic_includes_ignores.h" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + +#include "quiche/quic/core/quic_connection.h" + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif #include "common/common/empty_string.h" #include "common/common/logger.h" diff --git a/source/common/quic/quic_includes_ignores.h b/source/common/quic/quic_includes_ignores.h deleted file mode 100644 index 21e1a92bf6bf..000000000000 --- a/source/common/quic/quic_includes_ignores.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -// NOLINT(namespace-envoy) -// A wrapper of QUICHE includes which suppresses some compilation warnings thrown from QUICHE. -// Usage: -// #define QUICHE_INCLUDE_1 "quiche/quic/core/quic_connection.h" -// #define QUICHE_INCLUDE_2 "quiche/quic/core/quic_session.h" -// #include common/quic/quic_includes_ignores.h - -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - -#ifdef QUICHE_INCLUDE_1 -#include QUICHE_INCLUDE_1 - -#endif -#ifdef QUICHE_INCLUDE_2 -#include QUICHE_INCLUDE_2 -#endif -#ifdef QUICHE_INCLUDE_3 -#include QUICHE_INCLUDE_3 -#endif -#ifdef QUICHE_INCLUDE_4 -#include QUICHE_INCLUDE_4 -#endif -#ifdef QUICHE_INCLUDE_5 -#include QUICHE_INCLUDE_5 -#endif -#ifdef QUICHE_INCLUDE_6 -#include QUICHE_INCLUDE_6 -#endif - -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif From 065190664c20be1821ace0dc4ece2e6aa7295b72 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 28 Apr 2021 00:48:26 -0400 Subject: [PATCH 50/52] delay close Signed-off-by: Dan Zhang --- source/common/quic/quic_filter_manager_connection_impl.cc | 8 ++++++-- source/common/quic/quic_filter_manager_connection_impl.h | 1 + test/common/quic/envoy_quic_dispatcher_test.cc | 2 -- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index 7477d21a71af..e47c27080d1d 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -62,13 +62,13 @@ bool QuicFilterManagerConnectionImpl::aboveHighWatermark() const { } void QuicFilterManagerConnectionImpl::close(Network::ConnectionCloseType type) { - if (quicConnection() == nullptr) { + if (network_connection_ == nullptr) { // Already detached from quic connection. return; } if (!initialized_) { // Delay close till the first OnCanWrite() call. - delayed_close_state_ = DelayedCloseState::CloseAfterFlush; + close_type_during_initialize_ = type; return; } const bool delayed_close_timeout_configured = delayed_close_timeout_.count() > 0; @@ -140,6 +140,10 @@ void QuicFilterManagerConnectionImpl::updateBytesBuffered(size_t old_buffered_by void QuicFilterManagerConnectionImpl::maybeApplyDelayClosePolicy() { if (!inDelayedClose()) { + if (close_type_during_initialize_.has_value()) { + close(close_type_during_initialize_.value()); + close_type_during_initialize_ = absl::nullopt; + } return; } if (hasDataToWrite() || delayed_close_state_ == DelayedCloseState::CloseAfterFlushAndWait) { diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 359dcb2a28e1..91cfd98b8e23 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -185,6 +185,7 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, // send buffer. EnvoyQuicSimulatedWatermarkBuffer write_buffer_watermark_simulation_; Buffer::OwnedImpl empty_buffer_; + absl::optional close_type_during_initialize_; }; } // namespace Quic diff --git a/test/common/quic/envoy_quic_dispatcher_test.cc b/test/common/quic/envoy_quic_dispatcher_test.cc index 3117745cf6b5..5b4f7b4b76b6 100644 --- a/test/common/quic/envoy_quic_dispatcher_test.cc +++ b/test/common/quic/envoy_quic_dispatcher_test.cc @@ -315,8 +315,6 @@ TEST_P(EnvoyQuicDispatcherTest, CloseConnectionDuringFilterInstallation) { envoy_quic_dispatcher_.ProcessBufferedChlos(kNumSessionsToCreatePerLoopForTests); processValidChloPacket(peer_addr); - // Shutdown() to close the connection. - envoy_quic_dispatcher_.Shutdown(); } TEST_P(EnvoyQuicDispatcherTest, CreateNewConnectionUponBufferedCHLO) { From 55fd7b324df0a71b17b819718d9c9232c0fbcb81 Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 28 Apr 2021 12:24:13 -0400 Subject: [PATCH 51/52] disable ManyLargeRequestHeadersAccepted Signed-off-by: Dan Zhang --- test/integration/protocol_integration_test.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 64f1d55dd42b..03cd14677936 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -1572,7 +1572,7 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersRejected) { } TEST_P(DownstreamProtocolIntegrationTest, VeryLargeRequestHeadersRejected) { - EXCLUDE_DOWNSTREAM_HTTP3 + EXCLUDE_DOWNSTREAM_HTTP3; EXCLUDE_UPSTREAM_HTTP3; // Send one very large 2048 kB (2 MB) header with limit 1024 kB (1 MB) and 100 headers. testLargeRequestHeaders(2048, 1, 1024, 100); @@ -1584,6 +1584,10 @@ TEST_P(DownstreamProtocolIntegrationTest, LargeRequestHeadersAccepted) { } TEST_P(DownstreamProtocolIntegrationTest, ManyLargeRequestHeadersAccepted) { + // Fail under TSAN. Quic blackhole detection fired and closed the connection with + // QUIC_TOO_MANY_RTOS while waiting for upstream finishing transferring the large header. Observed + // long event loop. + EXCLUDE_DOWNSTREAM_HTTP3; // Send 70 headers each of size 100 kB with limit 8192 kB (8 MB) and 100 headers. testLargeRequestHeaders(100, 70, 8192, 100); } From 874a4b0e14bccb97bc99a69aa7a6b5ec45af3b9a Mon Sep 17 00:00:00 2001 From: Dan Zhang Date: Wed, 28 Apr 2021 15:52:07 -0400 Subject: [PATCH 52/52] spell dict Signed-off-by: Dan Zhang --- tools/spelling/spelling_dictionary.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 52b0f16228c8..16b5fac73870 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -273,6 +273,7 @@ RPC RSA RST RTDS +RTOS RTTI RUNDIR RW