Skip to content

Commit d06b41c

Browse files
authoredFeb 9, 2021
http3: adding upstream API hooks (envoyproxy#14839)
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 envoyproxy#14829 Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
1 parent 47ad8ee commit d06b41c

File tree

14 files changed

+149
-8
lines changed

14 files changed

+149
-8
lines changed
 

‎api/envoy/config/core/v3/protocol.proto

+8
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,11 @@ message GrpcProtocolOptions {
389389

390390
Http2ProtocolOptions http2_protocol_options = 1;
391391
}
392+
393+
// [#not-implemented-hide:]
394+
//
395+
// A message which allows using HTTP/3 as an upstream protocol.
396+
//
397+
// Eventually this will include configuration for tuning HTTP/3.
398+
message Http3ProtocolOptions {
399+
}

‎api/envoy/config/core/v4alpha/protocol.proto

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto

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

6969
config.core.v3.Http2ProtocolOptions http2_protocol_options = 2;
70+
71+
// [#not-implemented-hide:]
72+
config.core.v3.Http3ProtocolOptions http3_protocol_options = 3;
7073
}
7174
}
7275

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

7881
config.core.v3.Http2ProtocolOptions http2_protocol_options = 2;
82+
83+
// [#not-implemented-hide:]
84+
config.core.v3.Http3ProtocolOptions http3_protocol_options = 3;
7985
}
8086

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

‎api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎generated_api_shadow/envoy/config/core/v3/protocol.proto

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎generated_api_shadow/envoy/config/core/v4alpha/protocol.proto

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎include/envoy/upstream/upstream.h

+2
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,8 @@ class ClusterInfo {
706706
// If USE_ALPN and HTTP2 are true, the upstream protocol will be negotiated using ALPN.
707707
// If ALPN is attempted but not supported by the upstream HTTP/1.1 is used.
708708
static const uint64_t USE_ALPN = 0x8;
709+
// Whether the upstream supports HTTP3. This is used when creating connection pools.
710+
static const uint64_t HTTP3 = 0x10;
709711
};
710712

711713
virtual ~ClusterInfo() = default;

‎source/common/upstream/cluster_manager_impl.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -1490,9 +1490,9 @@ Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool(
14901490
const Network::ConnectionSocket::OptionsSharedPtr& options,
14911491
const Network::TransportSocketOptionsSharedPtr& transport_socket_options,
14921492
ClusterConnectivityState& state) {
1493-
if (protocols.size() == 2 &&
1494-
((protocols[0] == Http::Protocol::Http2 && protocols[1] == Http::Protocol::Http11) ||
1495-
(protocols[1] == Http::Protocol::Http2 && protocols[0] == Http::Protocol::Http11))) {
1493+
if (protocols.size() == 2) {
1494+
ASSERT((protocols[0] == Http::Protocol::Http2 && protocols[1] == Http::Protocol::Http11) ||
1495+
(protocols[1] == Http::Protocol::Http2 && protocols[0] == Http::Protocol::Http11));
14961496
return std::make_unique<Http::HttpConnPoolImplMixed>(dispatcher, api_.randomGenerator(), host,
14971497
priority, options,
14981498
transport_socket_options, state);

‎source/common/upstream/upstream_impl.cc

+8
Original file line numberDiff line numberDiff line change
@@ -895,8 +895,12 @@ ClusterInfoImpl::upstreamHttpProtocol(absl::optional<Http::Protocol> downstream_
895895
features_ & Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL) {
896896
return {downstream_protocol.value()};
897897
} else if (features_ & Upstream::ClusterInfo::Features::USE_ALPN) {
898+
ASSERT(!(features_ & Upstream::ClusterInfo::Features::HTTP3));
898899
return {Http::Protocol::Http2, Http::Protocol::Http11};
899900
} else {
901+
if (features_ & Upstream::ClusterInfo::Features::HTTP3) {
902+
return {Http::Protocol::Http3};
903+
}
900904
return {(features_ & Upstream::ClusterInfo::Features::HTTP2) ? Http::Protocol::Http2
901905
: Http::Protocol::Http11};
902906
}
@@ -929,6 +933,10 @@ ClusterImplBase::ClusterImplBase(
929933
fmt::format("ALPN configured for cluster {} which has a non-ALPN transport socket: {}",
930934
cluster.name(), cluster.DebugString()));
931935
}
936+
if ((info_->features() & ClusterInfoImpl::Features::HTTP3)) {
937+
throw EnvoyException(
938+
fmt::format("HTTP3 not yet supported: {}", cluster.name(), cluster.DebugString()));
939+
}
932940

933941
// Create the default (empty) priority set before registering callbacks to
934942
// avoid getting an update the first time it is accessed.

‎source/extensions/upstreams/http/config.cc

+20-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ getHttp2Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOption
4141
return options.explicit_http_config().http2_protocol_options();
4242
}
4343

44+
absl::optional<envoy::config::core::v3::Http3ProtocolOptions>
45+
getHttp3Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
46+
if (options.has_use_downstream_protocol_config() &&
47+
options.use_downstream_protocol_config().has_http3_protocol_options()) {
48+
return options.use_downstream_protocol_config().http3_protocol_options();
49+
}
50+
if (options.has_explicit_http_config() &&
51+
options.explicit_http_config().has_http3_protocol_options()) {
52+
return options.explicit_http_config().http3_protocol_options();
53+
}
54+
return {};
55+
}
56+
4457
} // namespace
4558

4659
uint64_t ProtocolOptionsConfigImpl::parseFeatures(const envoy::config::cluster::v3::Cluster& config,
@@ -50,13 +63,15 @@ uint64_t ProtocolOptionsConfigImpl::parseFeatures(const envoy::config::cluster::
5063
if (options.use_http2_) {
5164
features |= Upstream::ClusterInfo::Features::HTTP2;
5265
}
66+
if (options.use_http3_) {
67+
features |= Upstream::ClusterInfo::Features::HTTP3;
68+
}
5369
if (options.use_downstream_protocol_) {
5470
features |= Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL;
5571
}
5672
if (options.use_alpn_) {
5773
features |= Upstream::ClusterInfo::Features::USE_ALPN;
5874
}
59-
6075
if (config.close_connections_on_host_health_failure()) {
6176
features |= Upstream::ClusterInfo::Features::CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE;
6277
}
@@ -67,12 +82,16 @@ ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl(
6782
const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options)
6883
: http1_settings_(Envoy::Http::Utility::parseHttp1Settings(getHttpOptions(options))),
6984
http2_options_(Http2::Utility::initializeAndValidateOptions(getHttp2Options(options))),
85+
http3_options_(getHttp3Options(options)),
7086
common_http_protocol_options_(options.common_http_protocol_options()),
7187
upstream_http_protocol_options_(
7288
options.has_upstream_http_protocol_options()
7389
? absl::make_optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>(
7490
options.upstream_http_protocol_options())
7591
: absl::nullopt) {
92+
if (http3_options_.has_value()) {
93+
use_http3_ = true;
94+
}
7695
if (options.has_explicit_http_config() &&
7796
options.explicit_http_config().has_http2_protocol_options()) {
7897
use_http2_ = true;

‎source/extensions/upstreams/http/config.h

+2
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@ class ProtocolOptionsConfigImpl : public Upstream::ProtocolOptionsConfig {
4141

4242
const Envoy::Http::Http1Settings http1_settings_;
4343
const envoy::config::core::v3::Http2ProtocolOptions http2_options_;
44+
absl::optional<envoy::config::core::v3::Http3ProtocolOptions> http3_options_{};
4445
const envoy::config::core::v3::HttpProtocolOptions common_http_protocol_options_;
4546
const absl::optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>
4647
upstream_http_protocol_options_;
4748

4849
bool use_downstream_protocol_{};
4950
bool use_http2_{};
51+
bool use_http3_{};
5052
bool use_alpn_{};
5153
};
5254

‎test/common/upstream/upstream_impl_test.cc

+50
Original file line numberDiff line numberDiff line change
@@ -3008,6 +3008,8 @@ TEST_F(ClusterInfoImplTest, UseDownstreamHttpProtocol) {
30083008
cluster->info()->upstreamHttpProtocol({Http::Protocol::Http11})[0]);
30093009
EXPECT_EQ(Http::Protocol::Http2,
30103010
cluster->info()->upstreamHttpProtocol({Http::Protocol::Http2})[0]);
3011+
EXPECT_EQ(Http::Protocol::Http3,
3012+
cluster->info()->upstreamHttpProtocol({Http::Protocol::Http3})[0]);
30113013
}
30123014

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

3054+
TEST_F(ClusterInfoImplTest, Http3) {
3055+
const std::string yaml = R"EOF(
3056+
name: name
3057+
connect_timeout: 0.25s
3058+
type: STRICT_DNS
3059+
lb_policy: MAGLEV
3060+
load_assignment:
3061+
endpoints:
3062+
- lb_endpoints:
3063+
- endpoint:
3064+
address:
3065+
socket_address:
3066+
address: foo.bar.com
3067+
port_value: 443
3068+
)EOF";
3069+
3070+
BazFactory baz_factory;
3071+
Registry::InjectFactory<ClusterTypedMetadataFactory> registered_factory(baz_factory);
3072+
auto cluster1 = makeCluster(yaml);
3073+
ASSERT_TRUE(cluster1->info()->idleTimeout().has_value());
3074+
EXPECT_EQ(std::chrono::hours(1), cluster1->info()->idleTimeout().value());
3075+
3076+
const std::string explicit_http3 = R"EOF(
3077+
typed_extension_protocol_options:
3078+
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
3079+
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
3080+
explicit_http_config:
3081+
http3_protocol_options: {}
3082+
)EOF";
3083+
3084+
const std::string downstream_http3 = R"EOF(
3085+
typed_extension_protocol_options:
3086+
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
3087+
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
3088+
common_http_protocol_options:
3089+
idle_timeout: 1s
3090+
)EOF";
3091+
3092+
{
3093+
EXPECT_THROW_WITH_REGEX(makeCluster(yaml + explicit_http3), EnvoyException,
3094+
"HTTP3 not yet supported: name.*");
3095+
}
3096+
{
3097+
EXPECT_THROW_WITH_REGEX(makeCluster(yaml + explicit_http3), EnvoyException,
3098+
"HTTP3 not yet supported: name.*");
3099+
}
3100+
}
3101+
30523102
// Validate empty singleton for HostsPerLocalityImpl.
30533103
TEST(HostsPerLocalityImpl, Empty) {
30543104
EXPECT_FALSE(HostsPerLocalityImpl::empty()->hasLocalLocality());

0 commit comments

Comments
 (0)
Please sign in to comment.