Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/v1.31] repo: Release v1.31.5 #37728

Merged
merged 4 commits into from
Dec 18, 2024
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
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.31.5-dev
1.31.5
6 changes: 6 additions & 0 deletions changelogs/1.29.12.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
date: December 18, 2024

bug_fixes:
- area: http/1
change: |
Fixes sending overload crashes when HTTP/1 request is reset.
9 changes: 9 additions & 0 deletions changelogs/1.30.9.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
date: December 18, 2024

bug_fixes:
- area: http/1
change: |
Fixes sending overload crashes when HTTP/1 request is reset.
- area: happy_eyeballs
change: |
Validate that ``additional_address`` are IP addresses instead of crashing when sorting.
26 changes: 11 additions & 15 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
date: Pending

behavior_changes:
# *Changes that are expected to cause an incompatibility if applicable; deployment changes are likely required*

minor_behavior_changes:
# *Changes that may cause incompatibilities for some users, but should not for most*
date: December 18, 2024

bug_fixes:
# *Changes expected to improve the state of the world and are unlikely to have negative effects*

removed_config_or_runtime:
# *Normally occurs at the end of the* :ref:`deprecation period <deprecated>`

new_features:

deprecated:
- area: http/1
change: |
Fixes sending overload crashes when HTTP/1 request is reset.
- area: happy_eyeballs
change: |
Validate that ``additional_address`` are IP addresses instead of crashing when sorting.
- area: balsa
change: |
Fix incorrect handling of non-101 1xx responses. This fix can be temporarily reverted by setting runtime guard
``envoy.reloadable_features.wait_for_first_byte_before_balsa_msg_done`` to false.
Binary file modified docs/inventories/v1.29/objects.inv
Binary file not shown.
Binary file modified docs/inventories/v1.30/objects.inv
Binary file not shown.
Binary file modified docs/inventories/v1.31/objects.inv
Binary file not shown.
6 changes: 3 additions & 3 deletions docs/versions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
"1.26": 1.26.8
"1.27": 1.27.7
"1.28": 1.28.7
"1.29": 1.29.11
"1.30": 1.30.8
"1.31": 1.31.3
"1.29": 1.29.12
"1.30": 1.30.9
"1.31": 1.31.4
5 changes: 4 additions & 1 deletion source/common/http/http1/balsa_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,10 @@ void BalsaParser::HeaderDone() {
void BalsaParser::ContinueHeaderDone() {}

void BalsaParser::MessageDone() {
if (status_ == ParserStatus::Error) {
if (status_ == ParserStatus::Error ||
// In the case of early 1xx, MessageDone() can be called twice in a row.
// The !first_byte_processed_ check is to make this function idempotent.
(wait_for_first_byte_before_msg_done_ && !first_byte_processed_)) {
return;
}
status_ = convertResult(connection_->onMessageComplete());
Expand Down
3 changes: 3 additions & 0 deletions source/common/http/http1/balsa_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class BalsaParser : public Parser, public quiche::BalsaVisitorInterface {
// Latched value of `envoy.reloadable_features.http1_balsa_delay_reset`.
const bool delay_reset_ =
Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http1_balsa_delay_reset");
// Latched value of `envoy.reloadable_features.wait_for_first_byte_before_balsa_msg_done`.
const bool wait_for_first_byte_before_msg_done_ = Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.wait_for_first_byte_before_balsa_msg_done");
};

} // namespace Http1
Expand Down
4 changes: 4 additions & 0 deletions source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1363,6 +1363,10 @@ Status ServerConnectionImpl::sendProtocolError(absl::string_view details) {
if (active_request_ == nullptr) {
RETURN_IF_ERROR(onMessageBeginImpl());
}

if (resetStreamCalled()) {
return okStatus();
}
ASSERT(active_request_);

active_request_->response_encoder_.setDetails(details);
Expand Down
1 change: 0 additions & 1 deletion source/common/http/http1/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,6 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
return *absl::get<RequestHeaderMapPtr>(headers_or_trailers_);
}
void allocHeaders(StatefulHeaderKeyFormatterPtr&& formatter) override {
ASSERT(nullptr == absl::get<RequestHeaderMapPtr>(headers_or_trailers_));
ASSERT(!processing_trailers_);
auto headers = RequestHeaderMapImpl::create(max_headers_kb_, max_headers_count_);
headers->setFormatter(std::move(formatter));
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 @@ -104,6 +104,7 @@ RUNTIME_GUARD(envoy_reloadable_features_use_typed_metadata_in_proxy_protocol_lis
RUNTIME_GUARD(envoy_reloadable_features_validate_connect);
RUNTIME_GUARD(envoy_reloadable_features_validate_grpc_header_before_log_grpc_status);
RUNTIME_GUARD(envoy_reloadable_features_validate_upstream_headers);
RUNTIME_GUARD(envoy_reloadable_features_wait_for_first_byte_before_balsa_msg_done);
RUNTIME_GUARD(envoy_reloadable_features_xdstp_path_avoid_colon_encoding);
RUNTIME_GUARD(envoy_restart_features_allow_client_socket_creation_failure);
RUNTIME_GUARD(envoy_restart_features_allow_slot_destroy_on_worker_threads);
Expand Down
12 changes: 9 additions & 3 deletions source/extensions/clusters/eds/eds.cc
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,15 @@ void EdsClusterImpl::BatchUpdateHelper::updateLocalityEndpoints(
if (!lb_endpoint.endpoint().additional_addresses().empty()) {
address_list.push_back(address);
for (const auto& additional_address : lb_endpoint.endpoint().additional_addresses()) {
address_list.emplace_back(
THROW_OR_RETURN_VALUE(parent_.resolveProtoAddress(additional_address.address()),
const Network::Address::InstanceConstSharedPtr));
Network::Address::InstanceConstSharedPtr address =
returnOrThrow(parent_.resolveProtoAddress(additional_address.address()));
address_list.emplace_back(address);
}
for (const Network::Address::InstanceConstSharedPtr& address : address_list) {
// All addresses must by IP addresses.
if (!address->ip()) {
throwEnvoyExceptionOrPanic("additional_addresses must be IP addresses.");
}
}
}

Expand Down
12 changes: 9 additions & 3 deletions source/extensions/clusters/static/static_cluster.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,15 @@ StaticClusterImpl::StaticClusterImpl(const envoy::config::cluster::v3::Cluster&
THROW_OR_RETURN_VALUE(resolveProtoAddress(lb_endpoint.endpoint().address()),
const Network::Address::InstanceConstSharedPtr));
for (const auto& additional_address : lb_endpoint.endpoint().additional_addresses()) {
address_list.emplace_back(
THROW_OR_RETURN_VALUE(resolveProtoAddress(additional_address.address()),
const Network::Address::InstanceConstSharedPtr));
Network::Address::InstanceConstSharedPtr address =
returnOrThrow(resolveProtoAddress(additional_address.address()));
address_list.emplace_back(address);
}
for (const Network::Address::InstanceConstSharedPtr& address : address_list) {
// All addresses must by IP addresses.
if (!address->ip()) {
throwEnvoyExceptionOrPanic("additional_addresses must be IP addresses.");
}
}
}
priority_state_manager_->registerHostForPriority(
Expand Down
33 changes: 33 additions & 0 deletions test/common/http/http1/codec_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2431,6 +2431,39 @@ TEST_P(Http1ServerConnectionImplTest, LoadShedPointCanCloseConnectionOnDispatchO
EXPECT_TRUE(isEnvoyOverloadError(status));
}

TEST_P(Http1ServerConnectionImplTest, LoadShedPointForAlreadyResetStream) {
InSequence sequence;

Server::MockLoadShedPoint mock_abort_dispatch;
EXPECT_CALL(overload_manager_, getLoadShedPoint(_)).WillOnce(Return(&mock_abort_dispatch));
initialize();

EXPECT_CALL(mock_abort_dispatch, shouldShedLoad()).WillOnce(Return(false));

NiceMock<MockRequestDecoder> decoder;
Http::ResponseEncoder* response_encoder = nullptr;
EXPECT_CALL(callbacks_, newStream(_, _))
.WillOnce(Invoke([&](ResponseEncoder& encoder, bool) -> RequestDecoder& {
response_encoder = &encoder;
return decoder;
}));

Buffer::OwnedImpl request_line_buffer("GET / HTTP/1.1\r\n");
auto status = codec_->dispatch(request_line_buffer);
EXPECT_EQ(0, request_line_buffer.length());

EXPECT_CALL(mock_abort_dispatch, shouldShedLoad()).WillRepeatedly(Return(true));
Buffer::OwnedImpl headers_buffer("final-header: value\r\n\r\n");

// The reset stream can be triggered in the middle of dispatching.
connection_.dispatcher_.post(
[&] { response_encoder->getStream().resetStream(StreamResetReason::LocalReset); });

status = codec_->dispatch(headers_buffer);
EXPECT_FALSE(status.ok());
EXPECT_TRUE(isEnvoyOverloadError(status));
}

TEST_P(Http1ServerConnectionImplTest, LoadShedPointCanCloseConnectionOnDispatchOfContinuingStream) {
Server::MockLoadShedPoint mock_abort_dispatch;
EXPECT_CALL(overload_manager_, getLoadShedPoint(_)).WillOnce(Return(&mock_abort_dispatch));
Expand Down
31 changes: 31 additions & 0 deletions test/common/upstream/cluster_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6409,6 +6409,37 @@ TEST_F(ClusterManagerImplTest, CheckAddressesList) {
ASSERT_EQ(hosts[0]->addressListOrNull()->size(), 2);
}

// Verify that non-IP additional addresses are rejected.
TEST_F(ClusterManagerImplTest, RejectNonIpAdditionalAddresses) {
const std::string bootstrap = R"EOF(
static_resources:
clusters:
- name: cluster_0
connect_timeout: 0.250s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: cluster_0
endpoints:
- lb_endpoints:
- endpoint:
additionalAddresses:
- address:
envoyInternalAddress:
server_listener_name: internal_address
address:
socket_address:
address: 127.0.0.1
port_value: 11001
)EOF";
try {
create(parseBootstrapFromV3Yaml(bootstrap));
FAIL() << "Invalid address was not rejected";
} catch (const EnvoyException& e) {
EXPECT_STREQ("additional_addresses must be IP addresses.", e.what());
}
}

TEST_F(ClusterManagerImplTest, CheckActiveStaticCluster) {
const std::string yaml = R"EOF(
static_resources:
Expand Down
30 changes: 30 additions & 0 deletions test/extensions/clusters/eds/eds_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,36 @@ TEST_F(EdsTest, DualStackEndpoint) {
EXPECT_NE(connection, connection_data.connection_.get());
}

// Verify that non-IP additional addresses are rejected.
TEST_F(EdsTest, RejectNonIpAdditionalAddresses) {
envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment;
cluster_load_assignment.set_cluster_name("fare");

// Add dual stack endpoint
auto* endpoints = cluster_load_assignment.add_endpoints();
auto* endpoint = endpoints->add_lb_endpoints();
endpoint->mutable_endpoint()->mutable_address()->mutable_socket_address()->set_address("::1");
endpoint->mutable_endpoint()->mutable_address()->mutable_socket_address()->set_port_value(80);
endpoint->mutable_endpoint()
->mutable_additional_addresses()
->Add()
->mutable_address()
->mutable_envoy_internal_address()
->set_server_listener_name("internal_address");

endpoint->mutable_load_balancing_weight()->set_value(30);

initialize();
const auto decoded_resources =
TestUtility::decodeResources({cluster_load_assignment}, "cluster_name");
try {
(void)eds_callbacks_->onConfigUpdate(decoded_resources.refvec_, "");
FAIL() << "Invalid address was not rejected";
} catch (const EnvoyException& e) {
EXPECT_STREQ("additional_addresses must be IP addresses.", e.what());
}
}

// Validate that onConfigUpdate() updates the endpoint metadata.
TEST_F(EdsTest, EndpointMetadata) {
envoy::config::endpoint::v3::ClusterLoadAssignment cluster_load_assignment;
Expand Down
84 changes: 84 additions & 0 deletions test/integration/protocol_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,90 @@ TEST_P(ProtocolIntegrationTest, EnvoyProxying104) {
testEnvoyProxying1xx(false, false, false, "104");
}

TEST_P(DownstreamProtocolIntegrationTest, EnvoyProxying102DelayBalsaReset) {
if (GetParam().http1_implementation != Http1ParserImpl::BalsaParser ||
GetParam().upstream_protocol != Http::CodecType::HTTP1 ||
GetParam().downstream_protocol != Http::CodecType::HTTP1) {
GTEST_SKIP() << "This test is only relevant for HTTP1 BalsaParser";
}
config_helper_.addConfigModifier(
[&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
hcm) -> void { hcm.set_proxy_100_continue(true); });
config_helper_.addRuntimeOverride(
"envoy.reloadable_features.wait_for_first_byte_before_balsa_msg_done", "false");
config_helper_.addRuntimeOverride("envoy.reloadable_features.http1_balsa_delay_reset", "true");
initialize();

codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "HEAD"},
{":path", "/dynamo/url"},
{":scheme", "http"},
{":authority", "sni.lyft.com"},
{"expect", "100-contINUE"}});
waitForNextUpstreamRequest();
upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}});
response->waitFor1xxHeaders();
upstream_request_->encodeHeaders(default_response_headers_, true);

EXPECT_FALSE(response->waitForEndStream());

cleanupUpstreamAndDownstream();
}

TEST_P(DownstreamProtocolIntegrationTest, EnvoyProxying102DelayBalsaResetWaitForFirstByte) {
if (GetParam().http1_implementation != Http1ParserImpl::BalsaParser ||
GetParam().upstream_protocol != Http::CodecType::HTTP1) {
GTEST_SKIP() << "This test is only relevant for HTTP1 upstream with BalsaParser";
}
config_helper_.addConfigModifier(
[&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
hcm) -> void { hcm.set_proxy_100_continue(true); });
config_helper_.addRuntimeOverride(
"envoy.reloadable_features.wait_for_first_byte_before_balsa_msg_done", "true");
config_helper_.addRuntimeOverride("envoy.reloadable_features.http1_balsa_delay_reset", "true");
initialize();

codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "HEAD"},
{":path", "/dynamo/url"},
{":scheme", "http"},
{":authority", "sni.lyft.com"},
{"expect", "100-contINUE"}});
waitForNextUpstreamRequest();
upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}});
response->waitFor1xxHeaders();
upstream_request_->encodeHeaders(default_response_headers_, true);
ASSERT_TRUE(response->waitForEndStream());
}

TEST_P(DownstreamProtocolIntegrationTest, EnvoyProxying102NoDelayBalsaReset) {
if (GetParam().http1_implementation != Http1ParserImpl::BalsaParser ||
GetParam().upstream_protocol != Http::CodecType::HTTP1) {
GTEST_SKIP() << "This test is only relevant for HTTP1 upstream with BalsaParser";
}
config_helper_.addConfigModifier(
[&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
hcm) -> void { hcm.set_proxy_100_continue(true); });
config_helper_.addRuntimeOverride("envoy.reloadable_features.http1_balsa_delay_reset", "false");
initialize();

codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "HEAD"},
{":path", "/dynamo/url"},
{":scheme", "http"},
{":authority", "sni.lyft.com"},
{"expect", "100-contINUE"}});

waitForNextUpstreamRequest();
upstream_request_->encode1xxHeaders(Http::TestResponseHeaderMapImpl{{":status", "102"}});
response->waitFor1xxHeaders();
upstream_request_->encodeHeaders(default_response_headers_, true);
ASSERT_TRUE(response->waitForEndStream());
}

TEST_P(ProtocolIntegrationTest, TwoRequests) { testTwoRequests(); }

TEST_P(ProtocolIntegrationTest, TwoRequestsWithForcedBackup) { testTwoRequests(true); }
Expand Down