Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,15 @@ Changes

* http: fixed URL parsing for HTTP/1.1 fully qualified URLs and connect requests containing IPv6 addresses.
* http: fixed bugs in datadog and squash filter's handling of responses with no bodies.
* http: reverting a behavioral change where upstream connect timeouts were temporarily treated differently from other connection failures. The change back to the original behavior can be temporarily reverted by setting `envoy.reloadable_features.treat_upstream_connect_timeout_as_connect_failure` to false.
* tls: fix detection of the upstream connection close event.

Removed Config or Runtime
-------------------------
*Normally occurs at the end of the* :ref:`deprecation period <deprecated>`

New Features
------------

Deprecated
----------
8 changes: 7 additions & 1 deletion source/common/router/upstream_request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "common/router/config_impl.h"
#include "common/router/debug_config.h"
#include "common/router/router.h"
#include "common/runtime/runtime_features.h"
#include "common/stream_info/uint32_accessor_impl.h"
#include "common/tracing/http_tracer_impl.h"

Expand Down Expand Up @@ -325,7 +326,12 @@ void UpstreamRequest::onPoolFailure(ConnectionPool::PoolFailureReason reason,
reset_reason = Http::StreamResetReason::ConnectionFailure;
break;
case ConnectionPool::PoolFailureReason::Timeout:
reset_reason = Http::StreamResetReason::LocalReset;
if (Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.treat_upstream_connect_timeout_as_connect_failure")) {
reset_reason = Http::StreamResetReason::ConnectionFailure;
} else {
reset_reason = Http::StreamResetReason::LocalReset;
}
}

// Mimic an upstream reset.
Expand Down
1 change: 1 addition & 0 deletions source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ constexpr const char* runtime_features[] = {
"envoy.reloadable_features.preserve_upstream_date",
"envoy.reloadable_features.stop_faking_paths",
"envoy.reloadable_features.strict_1xx_and_204_response_headers",
"envoy.reloadable_features.treat_upstream_connect_timeout_as_connect_failure",
};

// This is a section for officially sanctioned runtime features which are too
Expand Down
70 changes: 70 additions & 0 deletions test/common/router/router_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,76 @@ TEST_F(RouterTest, PoolFailureWithPriority) {
EXPECT_EQ(callbacks_.details_, "upstream_reset_before_response_started{connection failure}");
}

TEST_F(RouterTest, PoolFailureDueToConnectTimeout) {
ON_CALL(callbacks_.route_->route_entry_, priority())
.WillByDefault(Return(Upstream::ResourcePriority::High));
EXPECT_CALL(cm_, httpConnPoolForCluster(_, Upstream::ResourcePriority::High, _, &router_));
EXPECT_CALL(cm_.conn_pool_, newStream(_, _))
.WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks)
-> Http::ConnectionPool::Cancellable* {
callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::Timeout, "connect_timeout",
cm_.conn_pool_.host_);
return nullptr;
}));

Http::TestResponseHeaderMapImpl response_headers{
{":status", "503"}, {"content-length", "91"}, {"content-type", "text/plain"}};
EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false));
EXPECT_CALL(callbacks_, encodeData(_, true));
EXPECT_CALL(callbacks_.stream_info_,
setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure));
EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_))
.WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void {
EXPECT_EQ(host_address_, host->address());
}));

Http::TestRequestHeaderMapImpl headers;
HttpTestUtility::addDefaultHeaders(headers);
router_.decodeHeaders(headers, true);
EXPECT_TRUE(verifyHostUpstreamStats(0, 1));
// Pool failure, so upstream request was not initiated.
EXPECT_EQ(0U,
callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value());
EXPECT_EQ(callbacks_.details_,
"upstream_reset_before_response_started{connection failure,connect_timeout}");
}

TEST_F(RouterTest, PoolFailureDueToConnectTimeoutLegacy) {
TestScopedRuntime scoped_runtime;
Runtime::LoaderSingleton::getExisting()->mergeValues(
{{"envoy.reloadable_features.treat_upstream_connect_timeout_as_connect_failure", "false"}});
ON_CALL(callbacks_.route_->route_entry_, priority())
.WillByDefault(Return(Upstream::ResourcePriority::High));
EXPECT_CALL(cm_, httpConnPoolForCluster(_, Upstream::ResourcePriority::High, _, &router_));
EXPECT_CALL(cm_.conn_pool_, newStream(_, _))
.WillOnce(Invoke([&](Http::StreamDecoder&, Http::ConnectionPool::Callbacks& callbacks)
-> Http::ConnectionPool::Cancellable* {
callbacks.onPoolFailure(ConnectionPool::PoolFailureReason::Timeout, "connect_timeout",
cm_.conn_pool_.host_);
return nullptr;
}));

Http::TestResponseHeaderMapImpl response_headers{
{":status", "503"}, {"content-length", "84"}, {"content-type", "text/plain"}};
EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false));
EXPECT_CALL(callbacks_, encodeData(_, true));
EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::ResponseFlag::LocalReset));
EXPECT_CALL(callbacks_.stream_info_, onUpstreamHostSelected(_))
.WillOnce(Invoke([&](const Upstream::HostDescriptionConstSharedPtr host) -> void {
EXPECT_EQ(host_address_, host->address());
}));

Http::TestRequestHeaderMapImpl headers;
HttpTestUtility::addDefaultHeaders(headers);
router_.decodeHeaders(headers, true);
EXPECT_TRUE(verifyHostUpstreamStats(0, 1));
// Pool failure, so upstream request was not initiated.
EXPECT_EQ(0U,
callbacks_.route_->route_entry_.virtual_cluster_.stats().upstream_rq_total_.value());
EXPECT_EQ(callbacks_.details_,
"upstream_reset_before_response_started{local reset,connect_timeout}");
}

TEST_F(RouterTest, Http1Upstream) {
EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, absl::optional<Http::Protocol>(), _));
EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_));
Expand Down