Skip to content

Commit

Permalink
mobile: Add an option to set the UDP socket send and receive buffer s…
Browse files Browse the repository at this point in the history
…izes (#35190)

This PR adds an option in the `EngineBuilder` to set the UDP socket send
buffer size. By default it will use the same value as the one set in
Chromium:
https://source.chromium.org/chromium/chromium/src/+/main:net/quic/quic_session_pool.cc;l=790-793;drc=7f04a8e033c23dede6beae129cd212e6d4473d72

This PR also updates the code to only apply the default receive buffer
size to UDP and not TCP given that the code in
https://source.chromium.org/chromium/chromium/src/+/main:net/quic/quic_session_pool.cc;l=766;drc=7f04a8e033c23dede6beae129cd212e6d4473d72
only applies the socket option to UDP only.

Risk Level: low
Testing: unit tests
Docs Changes: n/a
Release Notes: n/a
Platform Specific Features: mobile

---------

Signed-off-by: Fredy Wijaya <[email protected]>
  • Loading branch information
fredyw authored Jul 25, 2024
1 parent d4aa4bd commit ddaa2c3
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 17 deletions.
41 changes: 27 additions & 14 deletions mobile/library/cc/engine_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,13 @@ EngineBuilder& EngineBuilder::setUseGroIfAvailable(bool use_gro_if_available) {
return *this;
}

EngineBuilder& EngineBuilder::setSocketReceiveBufferSize(int32_t size) {
socket_receive_buffer_size_ = size;
EngineBuilder& EngineBuilder::setUdpSocketReceiveBufferSize(int32_t size) {
udp_socket_receive_buffer_size_ = size;
return *this;
}

EngineBuilder& EngineBuilder::setUdpSocketSendBufferSize(int32_t size) {
udp_socket_send_buffer_size_ = size;
return *this;
}

Expand Down Expand Up @@ -721,19 +726,27 @@ std::unique_ptr<envoy::config::bootstrap::v3::Bootstrap> EngineBuilder::generate
["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"]
.PackFrom(alpn_options);

// Set the upstream connections socket receive buffer size. The operating system defaults are
// usually too small for QUIC.
// NOTE: An H3 cluster can also establish H2 connections (for example, if the H3 connection is
// marked as broken in the ConnectivityGrid). This option would apply to all connections in the
// cluster, meaning H2 TCP connections buffer size would also be set to 1MB. On the platforms
// we've tested, IPPROTO_UDP cannot be used as a level for the SO_RCVBUF option.
envoy::config::core::v3::SocketOption* rcv_buf_sock_opt =
// Set the upstream connections UDP socket receive buffer size. The operating system defaults
// are usually too small for QUIC.
envoy::config::core::v3::SocketOption* udp_rcv_buf_sock_opt =
base_cluster->mutable_upstream_bind_config()->add_socket_options();
udp_rcv_buf_sock_opt->set_name(SO_RCVBUF);
udp_rcv_buf_sock_opt->set_level(SOL_SOCKET);
udp_rcv_buf_sock_opt->set_int_value(udp_socket_receive_buffer_size_);
// Only apply the socket option to the datagram socket.
udp_rcv_buf_sock_opt->mutable_type()->mutable_datagram();
udp_rcv_buf_sock_opt->set_description(
absl::StrCat("UDP SO_RCVBUF = ", udp_socket_receive_buffer_size_, " bytes"));

envoy::config::core::v3::SocketOption* udp_snd_buf_sock_opt =
base_cluster->mutable_upstream_bind_config()->add_socket_options();
rcv_buf_sock_opt->set_name(SO_RCVBUF);
rcv_buf_sock_opt->set_level(SOL_SOCKET);
rcv_buf_sock_opt->set_int_value(socket_receive_buffer_size_);
rcv_buf_sock_opt->set_description(
absl::StrCat("SO_RCVBUF = ", socket_receive_buffer_size_, " bytes"));
udp_snd_buf_sock_opt->set_name(SO_SNDBUF);
udp_snd_buf_sock_opt->set_level(SOL_SOCKET);
udp_snd_buf_sock_opt->set_int_value(udp_socket_send_buffer_size_);
// Only apply the socket option to the datagram socket.
udp_snd_buf_sock_opt->mutable_type()->mutable_datagram();
udp_snd_buf_sock_opt->set_description(
absl::StrCat("UDP SO_SNDBUF = ", udp_socket_send_buffer_size_, " bytes"));
// Set the network service type on iOS, if supplied.
#if defined(__APPLE__)
if (ios_network_service_type_ > 0) {
Expand Down
9 changes: 7 additions & 2 deletions mobile/library/cc/engine_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ class EngineBuilder {
EngineBuilder& enableDrainPostDnsRefresh(bool drain_post_dns_refresh_on);
// Sets whether to use GRO for upstream UDP sockets (QUIC/HTTP3).
EngineBuilder& setUseGroIfAvailable(bool use_gro_if_available);
EngineBuilder& setSocketReceiveBufferSize(int32_t size);
EngineBuilder& setUdpSocketReceiveBufferSize(int32_t size);
EngineBuilder& setUdpSocketSendBufferSize(int32_t size);
EngineBuilder& enforceTrustChainVerification(bool trust_chain_verification_on);
EngineBuilder& setUpstreamTlsSni(std::string sni);
EngineBuilder& enablePlatformCertificatesValidation(bool platform_certificates_validation_on);
Expand Down Expand Up @@ -182,7 +183,11 @@ class EngineBuilder {

// This is the same value Cronet uses for QUIC:
// https://source.chromium.org/chromium/chromium/src/+/main:net/quic/quic_context.h;drc=ccfe61524368c94b138ddf96ae8121d7eb7096cf;l=87
int32_t socket_receive_buffer_size_ = 1024 * 1024; // 1MB
int32_t udp_socket_receive_buffer_size_ = 1024 * 1024; // 1MB
// This is the same value Cronet uses for QUIC:
// https://source.chromium.org/chromium/chromium/src/+/main:net/quic/quic_session_pool.cc;l=790-793;drc=7f04a8e033c23dede6beae129cd212e6d4473d72
// https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/quic/core/quic_constants.h;l=43-47;drc=34ad7f3844f882baf3d31a6bc6e300acaa0e3fc8
int32_t udp_socket_send_buffer_size_ = 1452 * 20;
};

using EngineBuilderSharedPtr = std::shared_ptr<EngineBuilder>;
Expand Down
34 changes: 33 additions & 1 deletion mobile/test/cc/unit/envoy_config_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ TEST(TestConfig, DisableHttp3) {
}

#ifdef ENVOY_ENABLE_QUIC
TEST(TestConfig, SocketReceiveBufferSize) {
TEST(TestConfig, UdpSocketReceiveBufferSize) {
EngineBuilder engine_builder;
engine_builder.enableHttp3(true);

Expand All @@ -358,8 +358,40 @@ TEST(TestConfig, SocketReceiveBufferSize) {
// When using an H3 cluster, the UDP receive buffer size option should always be set.
ASSERT_THAT(rcv_buf_option, NotNull());
EXPECT_EQ(rcv_buf_option->level(), SOL_SOCKET);
EXPECT_TRUE(rcv_buf_option->type().has_datagram());
EXPECT_EQ(rcv_buf_option->int_value(), 1024 * 1024 /* 1 MB */);
}

TEST(TestConfig, UdpSocketSendBufferSize) {
EngineBuilder engine_builder;
engine_builder.enableHttp3(true);

std::unique_ptr<Bootstrap> bootstrap = engine_builder.generateBootstrap();
Cluster const* base_cluster = nullptr;
for (const Cluster& cluster : bootstrap->static_resources().clusters()) {
if (cluster.name() == "base") {
base_cluster = &cluster;
break;
}
}

// The base H3 cluster should always be found.
ASSERT_THAT(base_cluster, NotNull());

SocketOption const* snd_buf_option = nullptr;
for (const SocketOption& sock_opt : base_cluster->upstream_bind_config().socket_options()) {
if (sock_opt.name() == SO_SNDBUF) {
snd_buf_option = &sock_opt;
break;
}
}

// When using an H3 cluster, the UDP send buffer size option should always be set.
ASSERT_THAT(snd_buf_option, NotNull());
EXPECT_EQ(snd_buf_option->level(), SOL_SOCKET);
EXPECT_TRUE(snd_buf_option->type().has_datagram());
EXPECT_EQ(snd_buf_option->int_value(), 1452 * 20);
}
#endif

TEST(TestConfig, EnablePlatformCertificatesValidation) {
Expand Down

0 comments on commit ddaa2c3

Please sign in to comment.