diff --git a/test/integration/tcp_tunneling_integration_test.cc b/test/integration/tcp_tunneling_integration_test.cc index 85a61ce6d693..9c0292ecbac1 100644 --- a/test/integration/tcp_tunneling_integration_test.cc +++ b/test/integration/tcp_tunneling_integration_test.cc @@ -2112,8 +2112,9 @@ TEST_P(TcpTunnelingIntegrationTest, UpstreamDisconnectBeforeResponseReceived) { tcp_client_->close(); } -TEST_P(TcpTunnelingIntegrationTest, - ConnectionAttemptRetryOnUpstreamConnectionCloseBeforeResponseHeadersNoBackoffOptions) { +TEST_P( + TcpTunnelingIntegrationTest, + ConnectionAttemptRetryOnUpstreamConnectionCloseBeforeResponseHeadersNoBackoffOptionsRetryOnDifferentEventLoop) { const std::string access_log_filename = TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { @@ -2178,6 +2179,85 @@ TEST_P(TcpTunnelingIntegrationTest, EXPECT_THAT(waitForAccessLog(access_log_filename), testing::HasSubstr(expected_log)); } +TEST_P( + TcpTunnelingIntegrationTest, + ConnectionAttemptRetryOnUpstreamConnectionCloseBeforeResponseHeadersNoBackoffOptionsRetryOnSameEventLoop) { + const std::string access_log_filename = + TestEnvironment::temporaryPath(TestUtility::uniqueFilename()); + config_helper_.addRuntimeOverride( + "envoy.reloadable_features.tcp_proxy_retry_on_different_event_loop", "false"); + config_helper_.addConfigModifier([&](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { + envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy proxy_config; + proxy_config.set_stat_prefix("tcp_stats"); + proxy_config.set_cluster("cluster_0"); + proxy_config.mutable_tunneling_config()->set_hostname("foo.lyft.com:80"); + proxy_config.mutable_max_connect_attempts()->set_value(2); + + envoy::extensions::access_loggers::file::v3::FileAccessLog access_log_config; + access_log_config.mutable_log_format()->mutable_text_format_source()->set_inline_string( + "%UPSTREAM_REQUEST_ATTEMPT_COUNT% %RESPONSE_FLAGS%\n"); + access_log_config.set_path(access_log_filename); + proxy_config.add_access_log()->mutable_typed_config()->PackFrom(access_log_config); + + auto* listeners = bootstrap.mutable_static_resources()->mutable_listeners(); + for (auto& listener : *listeners) { + if (listener.name() != "tcp_proxy") { + continue; + } + auto* filter_chain = listener.mutable_filter_chains(0); + auto* filter = filter_chain->mutable_filters(0); + filter->mutable_typed_config()->PackFrom(proxy_config); + break; + } + }); + initialize(); + + // Start a connection, and verify the upgrade headers are received upstream. + tcp_client_ = makeTcpConnection(lookupPort("tcp_proxy")); + + // Send some data straight away. + ASSERT_TRUE(tcp_client_->write("hello", false)); + + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); + + // Close the upstream connection before sending response headers. + ASSERT_TRUE(fake_upstream_connection_->close()); + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + + if (upstreamProtocol() == Http::CodecType::HTTP2) { + // The connection is not fully closed yet, so the retry will be on the same connection. + tcp_client_->close(); + const std::string expected_log = + "2 " + std::string(StreamInfo::ResponseFlagUtils::UPSTREAM_CONNECTION_FAILURE) + "," + + std::string(StreamInfo::ResponseFlagUtils::UPSTREAM_RETRY_LIMIT_EXCEEDED); + EXPECT_THAT(waitForAccessLog(access_log_filename), testing::HasSubstr(expected_log)); + return; + } + + // Retry to create a new stream on new connection and not the closed one. + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForHeadersComplete()); + + upstream_request_->encodeHeaders(default_response_headers_, false); + ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); + + tcp_client_->close(); + if (upstreamProtocol() == Http::CodecType::HTTP1) { + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + } else { + ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); + // If the upstream now sends 'end stream' the connection is fully closed. + upstream_request_->encodeData(0, true); + } + + const std::string expected_log = + "2 " + std::string(StreamInfo::ResponseFlagUtils::UPSTREAM_CONNECTION_FAILURE); + EXPECT_THAT(waitForAccessLog(access_log_filename), testing::HasSubstr(expected_log)); +} + TEST_P(TcpTunnelingIntegrationTest, ConnectionAttemptRetryOnUpstreamConnectionCloseBeforeResponseHeadersWithBackoffOptions) { const std::string access_log_filename = diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 28e814b790c3..3a926837fdbe 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -17,7 +17,6 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common/network/dns_resolver:91.4" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts "source/common/quic:93.3" "source/common/signal:87.2" # Death tests don't report LCOV -"source/common/tcp_proxy:96.5" "source/common/thread:0.0" # Death tests don't report LCOV "source/common/tls:95.5" "source/common/tls/cert_validator:94.7"