Skip to content

Commit

Permalink
http3: adding upstream API hooks (#14839)
Browse files Browse the repository at this point in the history
Only adding explicit (hard-configured, or downstream-initiated) HTTP/3. Getting Auto for UDP/TCP is going to take substantially more work. HTTP/3 config will be rejected initially to keep this PR simple as possible.

Risk Level: Low (unused, hidden)
Testing: new unit tests
Docs Changes: n/a
Release Notes: n/a
Part of #14829
Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
  • Loading branch information
alyssawilk authored Feb 9, 2021
1 parent 47ad8ee commit d06b41c
Show file tree
Hide file tree
Showing 14 changed files with 149 additions and 8 deletions.
8 changes: 8 additions & 0 deletions api/envoy/config/core/v3/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,11 @@ message GrpcProtocolOptions {

Http2ProtocolOptions http2_protocol_options = 1;
}

// [#not-implemented-hide:]
//
// A message which allows using HTTP/3 as an upstream protocol.
//
// Eventually this will include configuration for tuning HTTP/3.
message Http3ProtocolOptions {
}
10 changes: 10 additions & 0 deletions api/envoy/config/core/v4alpha/protocol.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// .... [further cluster config]
// [#next-free-field: 6]
message HttpProtocolOptions {
// If this is used, the cluster will only operate on one of the possible upstream protocols (HTTP/1.1, HTTP/2).
// If this is used, the cluster will only operate on one of the possible upstream protocols.
// Note that HTTP/2 should generally be used for upstream clusters doing gRPC.
message ExplicitHttpConfig {
oneof protocol_config {
Expand All @@ -67,6 +67,9 @@ message HttpProtocolOptions {
config.core.v3.Http1ProtocolOptions http_protocol_options = 1;

config.core.v3.Http2ProtocolOptions http2_protocol_options = 2;

// [#not-implemented-hide:]
config.core.v3.Http3ProtocolOptions http3_protocol_options = 3;
}
}

Expand All @@ -76,6 +79,9 @@ message HttpProtocolOptions {
config.core.v3.Http1ProtocolOptions http_protocol_options = 1;

config.core.v3.Http2ProtocolOptions http2_protocol_options = 2;

// [#not-implemented-hide:]
config.core.v3.Http3ProtocolOptions http3_protocol_options = 3;
}

// If this is used, the cluster can use either HTTP/1 or HTTP/2, and will use whichever
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions generated_api_shadow/envoy/config/core/v3/protocol.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions generated_api_shadow/envoy/config/core/v4alpha/protocol.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions include/envoy/upstream/upstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,8 @@ class ClusterInfo {
// If USE_ALPN and HTTP2 are true, the upstream protocol will be negotiated using ALPN.
// If ALPN is attempted but not supported by the upstream HTTP/1.1 is used.
static const uint64_t USE_ALPN = 0x8;
// Whether the upstream supports HTTP3. This is used when creating connection pools.
static const uint64_t HTTP3 = 0x10;
};

virtual ~ClusterInfo() = default;
Expand Down
6 changes: 3 additions & 3 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1490,9 +1490,9 @@ Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool(
const Network::ConnectionSocket::OptionsSharedPtr& options,
const Network::TransportSocketOptionsSharedPtr& transport_socket_options,
ClusterConnectivityState& state) {
if (protocols.size() == 2 &&
((protocols[0] == Http::Protocol::Http2 && protocols[1] == Http::Protocol::Http11) ||
(protocols[1] == Http::Protocol::Http2 && protocols[0] == Http::Protocol::Http11))) {
if (protocols.size() == 2) {
ASSERT((protocols[0] == Http::Protocol::Http2 && protocols[1] == Http::Protocol::Http11) ||
(protocols[1] == Http::Protocol::Http2 && protocols[0] == Http::Protocol::Http11));
return std::make_unique<Http::HttpConnPoolImplMixed>(dispatcher, api_.randomGenerator(), host,
priority, options,
transport_socket_options, state);
Expand Down
8 changes: 8 additions & 0 deletions source/common/upstream/upstream_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -895,8 +895,12 @@ ClusterInfoImpl::upstreamHttpProtocol(absl::optional<Http::Protocol> downstream_
features_ & Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL) {
return {downstream_protocol.value()};
} else if (features_ & Upstream::ClusterInfo::Features::USE_ALPN) {
ASSERT(!(features_ & Upstream::ClusterInfo::Features::HTTP3));
return {Http::Protocol::Http2, Http::Protocol::Http11};
} else {
if (features_ & Upstream::ClusterInfo::Features::HTTP3) {
return {Http::Protocol::Http3};
}
return {(features_ & Upstream::ClusterInfo::Features::HTTP2) ? Http::Protocol::Http2
: Http::Protocol::Http11};
}
Expand Down Expand Up @@ -929,6 +933,10 @@ ClusterImplBase::ClusterImplBase(
fmt::format("ALPN configured for cluster {} which has a non-ALPN transport socket: {}",
cluster.name(), cluster.DebugString()));
}
if ((info_->features() & ClusterInfoImpl::Features::HTTP3)) {
throw EnvoyException(
fmt::format("HTTP3 not yet supported: {}", cluster.name(), cluster.DebugString()));
}

// Create the default (empty) priority set before registering callbacks to
// avoid getting an update the first time it is accessed.
Expand Down
21 changes: 20 additions & 1 deletion source/extensions/upstreams/http/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ getHttp2Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOption
return options.explicit_http_config().http2_protocol_options();
}

absl::optional<envoy::config::core::v3::Http3ProtocolOptions>
getHttp3Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
if (options.has_use_downstream_protocol_config() &&
options.use_downstream_protocol_config().has_http3_protocol_options()) {
return options.use_downstream_protocol_config().http3_protocol_options();
}
if (options.has_explicit_http_config() &&
options.explicit_http_config().has_http3_protocol_options()) {
return options.explicit_http_config().http3_protocol_options();
}
return {};
}

} // namespace

uint64_t ProtocolOptionsConfigImpl::parseFeatures(const envoy::config::cluster::v3::Cluster& config,
Expand All @@ -50,13 +63,15 @@ uint64_t ProtocolOptionsConfigImpl::parseFeatures(const envoy::config::cluster::
if (options.use_http2_) {
features |= Upstream::ClusterInfo::Features::HTTP2;
}
if (options.use_http3_) {
features |= Upstream::ClusterInfo::Features::HTTP3;
}
if (options.use_downstream_protocol_) {
features |= Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL;
}
if (options.use_alpn_) {
features |= Upstream::ClusterInfo::Features::USE_ALPN;
}

if (config.close_connections_on_host_health_failure()) {
features |= Upstream::ClusterInfo::Features::CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE;
}
Expand All @@ -67,12 +82,16 @@ ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl(
const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options)
: http1_settings_(Envoy::Http::Utility::parseHttp1Settings(getHttpOptions(options))),
http2_options_(Http2::Utility::initializeAndValidateOptions(getHttp2Options(options))),
http3_options_(getHttp3Options(options)),
common_http_protocol_options_(options.common_http_protocol_options()),
upstream_http_protocol_options_(
options.has_upstream_http_protocol_options()
? absl::make_optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>(
options.upstream_http_protocol_options())
: absl::nullopt) {
if (http3_options_.has_value()) {
use_http3_ = true;
}
if (options.has_explicit_http_config() &&
options.explicit_http_config().has_http2_protocol_options()) {
use_http2_ = true;
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/upstreams/http/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ class ProtocolOptionsConfigImpl : public Upstream::ProtocolOptionsConfig {

const Envoy::Http::Http1Settings http1_settings_;
const envoy::config::core::v3::Http2ProtocolOptions http2_options_;
absl::optional<envoy::config::core::v3::Http3ProtocolOptions> http3_options_{};
const envoy::config::core::v3::HttpProtocolOptions common_http_protocol_options_;
const absl::optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>
upstream_http_protocol_options_;

bool use_downstream_protocol_{};
bool use_http2_{};
bool use_http3_{};
bool use_alpn_{};
};

Expand Down
50 changes: 50 additions & 0 deletions test/common/upstream/upstream_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3008,6 +3008,8 @@ TEST_F(ClusterInfoImplTest, UseDownstreamHttpProtocol) {
cluster->info()->upstreamHttpProtocol({Http::Protocol::Http11})[0]);
EXPECT_EQ(Http::Protocol::Http2,
cluster->info()->upstreamHttpProtocol({Http::Protocol::Http2})[0]);
EXPECT_EQ(Http::Protocol::Http3,
cluster->info()->upstreamHttpProtocol({Http::Protocol::Http3})[0]);
}

TEST_F(ClusterInfoImplTest, UpstreamHttp2Protocol) {
Expand Down Expand Up @@ -3049,6 +3051,54 @@ TEST_F(ClusterInfoImplTest, UpstreamHttp11Protocol) {
cluster->info()->upstreamHttpProtocol({Http::Protocol::Http2})[0]);
}

TEST_F(ClusterInfoImplTest, Http3) {
const std::string yaml = R"EOF(
name: name
connect_timeout: 0.25s
type: STRICT_DNS
lb_policy: MAGLEV
load_assignment:
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: foo.bar.com
port_value: 443
)EOF";

BazFactory baz_factory;
Registry::InjectFactory<ClusterTypedMetadataFactory> registered_factory(baz_factory);
auto cluster1 = makeCluster(yaml);
ASSERT_TRUE(cluster1->info()->idleTimeout().has_value());
EXPECT_EQ(std::chrono::hours(1), cluster1->info()->idleTimeout().value());

const std::string explicit_http3 = R"EOF(
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http3_protocol_options: {}
)EOF";

const std::string downstream_http3 = R"EOF(
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
common_http_protocol_options:
idle_timeout: 1s
)EOF";

{
EXPECT_THROW_WITH_REGEX(makeCluster(yaml + explicit_http3), EnvoyException,
"HTTP3 not yet supported: name.*");
}
{
EXPECT_THROW_WITH_REGEX(makeCluster(yaml + explicit_http3), EnvoyException,
"HTTP3 not yet supported: name.*");
}
}

// Validate empty singleton for HostsPerLocalityImpl.
TEST(HostsPerLocalityImpl, Empty) {
EXPECT_FALSE(HostsPerLocalityImpl::empty()->hasLocalLocality());
Expand Down

0 comments on commit d06b41c

Please sign in to comment.