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

Do not 503 on Upgrade: h2c instead remove the header and ignore. #7981

Merged
merged 38 commits into from
Sep 4, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a27ef3f
Do not 503 on Upgrade: h2c instead remove the header and ignore.
jplevyak Aug 20, 2019
36cd5ca
Add release note.
jplevyak Aug 20, 2019
66243c3
Address comments.
jplevyak Aug 20, 2019
b516bd7
Address comments.
jplevyak Aug 21, 2019
215e3aa
Address comments.
jplevyak Aug 21, 2019
b563518
Address comments.
jplevyak Aug 21, 2019
b5ea6c6
Address comments.
jplevyak Aug 21, 2019
34632d5
Address comments.
jplevyak Aug 21, 2019
92726a5
Address comments.
jplevyak Aug 21, 2019
a82cc9d
Address comments.
jplevyak Aug 21, 2019
30ef294
Address comments.
jplevyak Aug 21, 2019
9d3a216
Address comments.
jplevyak Aug 21, 2019
1e1ab46
Address comments.
jplevyak Aug 21, 2019
11dbe74
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Aug 21, 2019
ad6c698
Address comments.
jplevyak Aug 22, 2019
51e0372
Address comments.
jplevyak Aug 22, 2019
069b642
Address comments.
jplevyak Aug 22, 2019
a2ac07f
Address comments.
jplevyak Aug 22, 2019
756224b
Address comments.
jplevyak Aug 22, 2019
5d0a1a6
Address comments.
jplevyak Aug 22, 2019
561650a
Fix clang-tidy error.
jplevyak Aug 22, 2019
427b0aa
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Aug 26, 2019
cecc97f
Address comments.
jplevyak Aug 26, 2019
53134ef
Rewritten to use <algorithm> as requested.
jplevyak Aug 27, 2019
7378c2e
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Aug 27, 2019
150f6fd
Fix test.
jplevyak Aug 27, 2019
bba47af
Address comments.
jplevyak Aug 27, 2019
f6c0797
Address comments.
jplevyak Aug 27, 2019
84a2f7b
Address comments.
jplevyak Aug 27, 2019
b79eb9c
Address comments.
jplevyak Aug 27, 2019
46b6993
Remove unnecessary include.
jplevyak Aug 27, 2019
973aa7f
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Aug 28, 2019
a2e7210
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Aug 28, 2019
e230448
Address comments.
jplevyak Aug 29, 2019
f97827f
Format
jplevyak Aug 29, 2019
9663c31
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Aug 30, 2019
73a2b9c
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Sep 4, 2019
af9d672
Merge remote-tracking branch 'envoyproxy/master' into ignore-upgrade-h2c
jplevyak Sep 4, 2019
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
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Version history
* header to metadata: added :ref:`PROTOBUF_VALUE <envoy_api_enum_value_config.filter.http.header_to_metadata.v2.Config.ValueType.PROTOBUF_VALUE>` and :ref:`ValueEncode <envoy_api_enum_config.filter.http.header_to_metadata.v2.Config.ValueEncode>` to support protobuf Value and Base64 encoding.
* http: added the ability to reject HTTP/1.1 requests with invalid HTTP header values, using the runtime feature `envoy.reloadable_features.strict_header_validation`.
* http: added the ability to :ref:`merge adjacent slashes<envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.merge_slashes>` in the path.
* http: ignore h2c upgrades for HTTP/1.1 requests which are not currently supported.
jplevyak marked this conversation as resolved.
Show resolved Hide resolved
* listeners: added :ref:`continue_on_listener_filters_timeout <envoy_api_field_Listener.continue_on_listener_filters_timeout>` to configure whether a listener will still create a connection when listener filters time out.
* listeners: added :ref:`HTTP inspector listener filter <config_listener_filters_http_inspector>`.
* redis: added :ref:`read_policy <envoy_api_field_config.filter.network.redis_proxy.v2.RedisProxy.ConnPoolSettings.read_policy>` to allow reading from redis replicas for Redis Cluster deployments.
Expand Down
41 changes: 27 additions & 14 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(HeaderMapPtr&& headers,
Server::OverloadActionState::Active) {
// In this one special case, do not create the filter chain. If there is a risk of memory
// overload it is more important to avoid unnecessary allocation than to create the filters.
state_.created_filter_chain_ = true;
state_.overloaded_do_not_create_filter_chain_ = true;
connection_manager_.stats_.named_.downstream_rq_overload_close_.inc();
sendLocalReply(Grpc::Common::hasGrpcContentType(*request_headers_),
Http::Code::ServiceUnavailable, "envoy overloaded", nullptr, is_head_request_,
Expand Down Expand Up @@ -758,13 +758,15 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(HeaderMapPtr&& headers,
cached_route_.value().get());
}

const bool upgrade_rejected = createFilterChain() == false;
const auto create_result = createFilterChain();

// TODO if there are no filters when starting a filter iteration, the connection manager
// should return 404. The current returns no response if there is no router filter.
if (protocol == Protocol::Http11 && hasCachedRoute()) {
if (upgrade_rejected) {
if (create_result == CreateFilterChainResult::UpgradeRejected ||
create_result == CreateFilterChainResult::Overloaded) {
// Do not allow upgrades if the route does not support it.
// NB: includes OVERLOADED and failed h2c upgrades.
connection_manager_.stats_.named_.downstream_rq_ws_on_non_ws_route_.inc();
sendLocalReply(Grpc::Common::hasGrpcContentType(*request_headers_), Code::Forbidden, "",
nullptr, is_head_request_, absl::nullopt,
Expand Down Expand Up @@ -1243,9 +1245,7 @@ void ConnectionManagerImpl::ActiveStream::sendLocalReply(
ASSERT(response_headers_ == nullptr);
// For early error handling, do a best-effort attempt to create a filter chain
// to ensure access logging.
if (!state_.created_filter_chain_) {
createFilterChain();
}
createFilterChain();
stream_info_.setResponseCodeDetails(details);
Utility::sendLocalReply(
is_grpc_request,
Expand Down Expand Up @@ -1724,11 +1724,15 @@ void ConnectionManagerImpl::ActiveStream::setBufferLimit(uint32_t new_limit) {
}
}

bool ConnectionManagerImpl::ActiveStream::createFilterChain() {
ConnectionManagerImpl::ActiveStream::CreateFilterChainResult
ConnectionManagerImpl::ActiveStream::createFilterChain() {
if (state_.created_filter_chain_) {
return false;
return CreateFilterChainResult::AlreadyCreated;
}
bool upgrade_rejected = false;
if (state_.overloaded_do_not_create_filter_chain_) {
return CreateFilterChainResult::Overloaded;
}
auto result = CreateFilterChainResult::Created;
auto upgrade = request_headers_ ? request_headers_->Upgrade() : nullptr;
state_.created_filter_chain_ = true;
if (upgrade != nullptr) {
Expand All @@ -1745,16 +1749,25 @@ bool ConnectionManagerImpl::ActiveStream::createFilterChain() {
state_.successful_upgrade_ = true;
connection_manager_.stats_.named_.downstream_cx_upgrades_total_.inc();
connection_manager_.stats_.named_.downstream_cx_upgrades_active_.inc();
return true;
return CreateFilterChainResult::Upgraded;
} else {
upgrade_rejected = true;
// Fall through to the default filter chain. The function calling this
// will send a local reply indicating that the upgrade failed.
// Ignore h2c upgrade requests until we support them.
// See https://github.com/envoyproxy/envoy/issues/7161 for details.
if (absl::EqualsIgnoreCase(request_headers_->Upgrade()->value().getStringView(),
jplevyak marked this conversation as resolved.
Show resolved Hide resolved
Http::Headers::get().UpgradeValues.H2c)) {
request_headers_->removeUpgrade();
result = CreateFilterChainResult::UpgradeIgnored;
// Fall through to the default filter chain.
} else {
result = CreateFilterChainResult::UpgradeRejected;
// Fall through to the default filter chain. The function calling this
// will send a local reply indicating that the upgrade failed.
}
}
}

connection_manager_.config_.filterFactory().createFilterChain(*this);
return !upgrade_rejected;
return result;
}

void ConnectionManagerImpl::ActiveStreamFilterBase::commonContinue() {
Expand Down
13 changes: 11 additions & 2 deletions source/common/http/conn_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
State()
: remote_complete_(false), local_complete_(false), codec_saw_local_complete_(false),
saw_connection_close_(false), successful_upgrade_(false), created_filter_chain_(false),
is_internally_created_(false) {}
overloaded_do_not_create_filter_chain_(false), is_internally_created_(false) {}

uint32_t filter_call_state_{0};
// The following 3 members are booleans rather than part of the space-saving bitfield as they
Expand All @@ -548,6 +548,7 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
bool saw_connection_close_ : 1;
bool successful_upgrade_ : 1;
bool created_filter_chain_ : 1;
bool overloaded_do_not_create_filter_chain_ : 1;
jplevyak marked this conversation as resolved.
Show resolved Hide resolved

// True if this stream is internally created. Currently only used for
// internal redirects or other streams created via recreateStream().
Expand All @@ -561,7 +562,15 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
// Possibly increases buffer_limit_ to the value of limit.
void setBufferLimit(uint32_t limit);
// Set up the Encoder/Decoder filter chain.
bool createFilterChain();
enum class CreateFilterChainResult {
jplevyak marked this conversation as resolved.
Show resolved Hide resolved
Overloaded, // Filter chain not created.
AlreadyCreated, // Filter chain not created (again).
UpgradeRejected, // Filter chain not created.
UpgradeIgnored, // Filter chain created (e.g. http1 for Upgrade: h2c).
Created, // Filter chain created, no Upgrade header.
Upgraded, // Upgraded filter chain created.
};
CreateFilterChainResult createFilterChain();
// Per-stream idle timeout callback.
void onIdleTimeout();
// Reset per-stream idle timer.
Expand Down
1 change: 1 addition & 0 deletions source/common/http/headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ class HeaderValues {
} ConnectionValues;

struct {
const std::string H2c{"h2c"};
const std::string WebSocket{"websocket"};
} UpgradeValues;

Expand Down
27 changes: 27 additions & 0 deletions test/common/http/conn_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2108,6 +2108,33 @@ TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) {
conn_manager_->onData(fake_input, false);
}

TEST_F(HttpConnectionManagerImplTest, IgnoreUpgradeH2c) {
setup(false, "");
StreamDecoder* decoder = nullptr;
HeaderMap* header_map = nullptr;

NiceMock<MockStreamEncoder> encoder;
EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> void {
decoder = &conn_manager_->newStream(encoder);
HeaderMapPtr headers{new TestHeaderMapImpl{{":authority", "host"},
{":method", "GET"},
{":path", "/"},
{"connection", "Upgrade"},
{"upgrade", "h2c"}}};
header_map = headers.get();
decoder->decodeHeaders(std::move(headers), true);
data.drain(4);
}));

Buffer::OwnedImpl fake_input("1234");
conn_manager_->onData(fake_input, false);

// The h2c header should be removed.
EXPECT_TRUE(header_map->Upgrade() == nullptr);

EXPECT_EQ(0U, stats_.named_.downstream_rq_ws_on_non_ws_route_.value());
}

TEST_F(HttpConnectionManagerImplTest, DrainClose) {
setup(true, "");

Expand Down