Skip to content
Closed
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
29 changes: 29 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,35 @@ date: Pending

behavior_changes:
# *Changes that are expected to cause an incompatibility if applicable; deployment changes are likely required*
- area: http
change: |
Remove the hop by hop TE header from downstream request headers. This change can be temporarily reverted
by setting ``envoy.reloadable_features.sanitize_te`` to false.
- area: http
change: |
Flip runtime flag ``envoy.reloadable_features.no_downgrade_to_canonical_name`` to true. Name downgrading in the
per filter config searching will be disabled by default. This behavior can be temporarily reverted by setting
the flag to false explicitly.
See doc :ref:`Http filter route specific config <arch_overview_http_filters_per_filter_config>` or
issue https://github.com/envoyproxy/envoy/issues/29461 for more specific detail and examples.
- area: listener
change: |
undeprecated runtime key ``overload.global_downstream_max_connections`` until :ref:`downstream connections monitor
<envoy_v3_api_msg_extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig>` extension becomes stable.
- area: stats dns_filter
change: |
Fixed tag extraction so that :ref:`stat_prefix <envoy_v3_api_msg_extensions.filters.udp.dns_filter.v3.DnsFilterConfig>`
is properly extracted. This changes the Prometheus name from
dns_filter_myprefix_local_a_record_answers{} to dns_filter_local_a_record_answers{envoy.dns_filter_prefix="myprefix"}.
- area: http2
change: |
Changes the default value of ``envoy.reloadable_features.http2_use_oghttp2`` to true. This changes the codec used for HTTP/2
requests and responses. This behavior can be reverted by setting the feature to false.
- area: http2
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Self-review -- This is the only note that the back-ported change addresses. I'm not sure if the other notes should be included here or not as part of the process.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah just the change from the original commit

change: |
Discard the ``Host`` header if the ``:authority`` header was received to bring Envoy into compliance with
https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 This behavioral change can be reverted by setting runtime flag
``envoy.reloadable_features.http2_discard_host_header`` to false.

minor_behavior_changes:
# *Changes that may cause incompatibilities for some users, but should not for most*
Expand Down
12 changes: 12 additions & 0 deletions source/common/http/http2/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2110,6 +2110,18 @@ int ServerConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& na
// For a server connection, we should never get push promise frames.
ASSERT(frame->hd.type == NGHTTP2_HEADERS);
ASSERT(frame->headers.cat == NGHTTP2_HCAT_REQUEST || frame->headers.cat == NGHTTP2_HCAT_HEADERS);
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
// Check if there is already the :authority header
const auto result = stream->headers().get(Http::Headers::get().Host);
if (!result.empty()) {
// Discard the host header value
return 0;
}
// Otherwise use host value as :authority
}
}
return saveHeader(frame, std::move(name), std::move(value));
}

Expand Down
2 changes: 2 additions & 0 deletions source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ RUNTIME_GUARD(envoy_reloadable_features_format_ports_as_numbers);
RUNTIME_GUARD(envoy_reloadable_features_handle_uppercase_scheme);
RUNTIME_GUARD(envoy_reloadable_features_http1_allow_codec_error_response_after_1xx_headers);
RUNTIME_GUARD(envoy_reloadable_features_http2_decode_metadata_with_quiche);
RUNTIME_GUARD(envoy_reloadable_features_http2_discard_host_header);
RUNTIME_GUARD(envoy_reloadable_features_http2_use_oghttp2);
RUNTIME_GUARD(envoy_reloadable_features_http2_validate_authority_with_quiche);
RUNTIME_GUARD(envoy_reloadable_features_http_allow_partial_urls_in_referer);
RUNTIME_GUARD(envoy_reloadable_features_http_ext_auth_failure_mode_allow_header_add);
Expand Down
6 changes: 3 additions & 3 deletions test/common/http/http2/http2_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ class Http2Frame {
ASSERT(size() >= HeaderSize);
setPayloadSize(size() - HeaderSize);
}
// Headers are directly encoded
void appendStaticHeader(StaticHeaderIndex index);
void appendHeaderWithoutIndexing(StaticHeaderIndex index, absl::string_view value);

private:
void buildHeader(Type type, uint32_t payload_size = 0, uint8_t flags = 0, uint32_t stream_id = 0);
Expand All @@ -277,9 +280,6 @@ class Http2Frame {
std::copy(data.begin(), data.end(), data_.begin() + 9);
}

// Headers are directly encoded
void appendStaticHeader(StaticHeaderIndex index);
void appendHeaderWithoutIndexing(StaticHeaderIndex index, absl::string_view value);
void appendEmptyHeader();

DataContainer data_;
Expand Down
81 changes: 81 additions & 0 deletions test/integration/multiplexed_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,87 @@ TEST_P(Http2FrameIntegrationTest, AccessLogOfWireBytesIfResponseSizeGreaterThanW
tcp_client_->close();
}

TEST_P(Http2FrameIntegrationTest, HostDifferentFromAuthority) {
beginSession();

uint32_t request_idx = 0;
auto request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(request_idx),
"one.example.com", "/path", {{"host", "two.example.com"}});
sendFrame(request);

waitForNextUpstreamRequest();
EXPECT_EQ(upstream_request_->headers().getHostValue(), "one.example.com");
upstream_request_->encodeHeaders(default_response_headers_, true);
auto frame = readFrame();
EXPECT_EQ(Http2Frame::Type::Headers, frame.type());
EXPECT_EQ(Http2Frame::ResponseStatus::Ok, frame.responseStatus());
tcp_client_->close();
}

TEST_P(Http2FrameIntegrationTest, HostSameAsAuthority) {
beginSession();

uint32_t request_idx = 0;
auto request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(request_idx),
"one.example.com", "/path", {{"host", "one.example.com"}});
sendFrame(request);

waitForNextUpstreamRequest();
EXPECT_EQ(upstream_request_->headers().getHostValue(), "one.example.com");
upstream_request_->encodeHeaders(default_response_headers_, true);
auto frame = readFrame();
EXPECT_EQ(Http2Frame::Type::Headers, frame.type());
EXPECT_EQ(Http2Frame::ResponseStatus::Ok, frame.responseStatus());
tcp_client_->close();
}

TEST_P(Http2FrameIntegrationTest, HostConcatenatedWithAuthorityWithOverride) {
config_helper_.addRuntimeOverride("envoy.reloadable_features.http2_discard_host_header", "false");
beginSession();

uint32_t request_idx = 0;
auto request = Http2Frame::makeRequest(Http2Frame::makeClientStreamId(request_idx),
"one.example.com", "/path", {{"host", "two.example.com"}});
sendFrame(request);

waitForNextUpstreamRequest();
EXPECT_EQ(upstream_request_->headers().getHostValue(), "one.example.com,two.example.com");
upstream_request_->encodeHeaders(default_response_headers_, true);
auto frame = readFrame();
EXPECT_EQ(Http2Frame::Type::Headers, frame.type());
EXPECT_EQ(Http2Frame::ResponseStatus::Ok, frame.responseStatus());
tcp_client_->close();
}

// All HTTP/2 static headers must be before non-static headers.
// Verify that codecs validate this.
TEST_P(Http2FrameIntegrationTest, HostBeforeAuthorityIsRejected) {
#ifdef ENVOY_ENABLE_UHV
// TODO(yanavlasov): fix this check for oghttp2 in UHV mode.
if (GetParam().http2_implementation == Http2Impl::Oghttp2) {
return;
}
#endif
beginSession();

Http2Frame request = Http2Frame::makeEmptyHeadersFrame(Http2Frame::makeClientStreamId(0),
Http2Frame::HeadersFlags::EndHeaders);
request.appendStaticHeader(Http2Frame::StaticHeaderIndex::MethodPost);
request.appendStaticHeader(Http2Frame::StaticHeaderIndex::SchemeHttps);
request.appendHeaderWithoutIndexing(Http2Frame::StaticHeaderIndex::Path, "/path");
// Add the `host` header before `:authority`
request.appendHeaderWithoutIndexing({"host", "two.example.com"});
request.appendHeaderWithoutIndexing(Http2Frame::StaticHeaderIndex::Authority, "one.example.com");
request.adjustPayloadSize();

sendFrame(request);

// By default codec treats stream errors as protocol errors and closes the connection.
tcp_client_->waitForDisconnect();
tcp_client_->close();
EXPECT_EQ(1, test_server_->counter("http.config_test.downstream_cx_protocol_error")->value());
}

TEST_P(Http2FrameIntegrationTest, MultipleHeaderOnlyRequests) {
const int kRequestsSentPerIOCycle = 20;
autonomous_upstream_ = true;
Expand Down