Skip to content

Commit

Permalink
quic: batch packet testing (envoyproxy#36061)
Browse files Browse the repository at this point in the history
Adding test infra to allow a quic test client to talk to the quic test
upstream
Adding an e2e test of reading when more than 32 packets are in the
kernel buffer

Risk Level: low
Testing: yes
Docs Changes: n/a
Release Notes: n/a

---------

Signed-off-by: Alyssa Wilk <[email protected]>
  • Loading branch information
alyssawilk authored Sep 16, 2024
1 parent b10deb6 commit d6a8361
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 5 deletions.
56 changes: 56 additions & 0 deletions test/integration/quic_protocol_integration_test.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,63 @@
#include "source/common/network/socket_option_impl.h"

#include "test/integration/protocol_integration_test.h"

namespace Envoy {

// Test that the quiche code can handle packets getting batched together, i.e.
// that it will re-register to read even without incoming packets.
TEST_P(DownstreamProtocolIntegrationTest, BatchedPackets) {
if (downstreamProtocol() != Http::CodecType::HTTP3) {
return; // Testing H3 client talking to H3 upstream only.
}
setUpstreamProtocol(Http::CodecType::HTTP3);
initialize();

// Set up the transport factory so the codec client will have credentials to
// talk to the upstream.
quic_transport_socket_factory_ = IntegrationUtil::createQuicUpstreamTransportSocketFactory(
*api_, stats_store_, context_manager_, thread_local_, san_to_match_,
true /*connect to upstream*/);

// Connect directly to the upstream.
int upstream_port = fake_upstreams_[0]->localAddress()->ip()->port();
// Make sure the client receive buffer can handle all the packets without loss.
auto options = std::make_shared<Network::Socket::Options>();
options->emplace_back(std::make_shared<Network::SocketOptionImpl>(
envoy::config::core::v3::SocketOption::STATE_PREBIND,
ENVOY_MAKE_SOCKET_OPTION_NAME(SOL_SOCKET, SO_RCVBUF), 1024 * 100));

codec_client_ = makeHttpConnection(makeClientConnectionWithOptions(upstream_port, options));

// Send a request and a response that can not be handled in one read.
auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
waitForNextUpstreamRequest();
upstream_request_->encodeHeaders(default_response_headers_, false);
// Send more than the 32 packets. Generally all packets will be read in one pass.
// All packet are sent here where the client is not looping, so can not read. The upstream is
// then deadlocked, guaranteeing all packets are sent to the kernel before the client performs
// any reads. Manual testing confirms they're consistently read at once. There are no guarantees
// of this, but given the test uses loopback sockets it's likely to continue to be the case.
upstream_request_->encodeData(1024 * 35, true);

// Now deadlock the upstream so it can not do anything - no acks, no
// retransmissions.
absl::Notification unblock_upstream;
absl::Notification upstream_blocked;
fake_upstreams_[0]->runOnDispatcherThread([&] {
upstream_blocked.Notify();
unblock_upstream.WaitForNotification();
});
upstream_blocked.WaitForNotification();

// Make sure all the packets are read by the client.
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
// Unblock the upstream.
unblock_upstream.Notify();
ASSERT_TRUE(fake_upstream_connection_->close());
}

// These will run with HTTP/3 downstream, and Http upstream.
INSTANTIATE_TEST_SUITE_P(DownstreamProtocols, DownstreamProtocolIntegrationTest,
testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams(
Expand Down
12 changes: 11 additions & 1 deletion test/integration/ssl_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,18 @@ namespace Ssl {

void initializeUpstreamTlsContextConfig(
const ClientSslTransportOptions& options,
envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& tls_context) {
envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& tls_context,
bool connect_to_upstream) {
const std::string rundir = TestEnvironment::runfilesDirectory();
if (connect_to_upstream) {
tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_trusted_ca()
->set_filename(rundir + "/test/config/integration/certs/upstreamcacert.pem");
tls_context.set_sni("foo.lyft.com");
return;
}

tls_context.mutable_common_tls_context()
->mutable_validation_context()
->mutable_trusted_ca()
Expand Down
4 changes: 3 additions & 1 deletion test/integration/ssl_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ struct ClientSslTransportOptions {

void initializeUpstreamTlsContextConfig(
const ClientSslTransportOptions& options,
envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& tls_context);
envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext& tls_context,
// By default, clients connect to Envoy. Allow configuring to connect to upstreams.
bool connect_to_upstream = false);

Network::UpstreamTransportSocketFactoryPtr
createClientSslTransportSocketFactory(const ClientSslTransportOptions& options,
Expand Down
6 changes: 4 additions & 2 deletions test/integration/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ Network::UpstreamTransportSocketFactoryPtr
IntegrationUtil::createQuicUpstreamTransportSocketFactory(Api::Api& api, Stats::Store& store,
Ssl::ContextManager& context_manager,
ThreadLocal::Instance& threadlocal,
const std::string& san_to_match) {
const std::string& san_to_match,
bool connect_to_upstreams) {
NiceMock<Server::Configuration::MockTransportSocketFactoryContext> context;
ON_CALL(context.server_context_, api()).WillByDefault(testing::ReturnRef(api));
ON_CALL(context, statsScope()).WillByDefault(testing::ReturnRef(*store.rootScope()));
Expand All @@ -155,10 +156,11 @@ IntegrationUtil::createQuicUpstreamTransportSocketFactory(Api::Api& api, Stats::
#ifdef ENVOY_ENABLE_YAML
initializeUpstreamTlsContextConfig(
Ssl::ClientSslTransportOptions().setAlpn(true).setSan(san_to_match).setSni("lyft.com"),
*tls_context);
*tls_context, connect_to_upstreams);
#else
UNREFERENCED_PARAMETER(tls_context);
UNREFERENCED_PARAMETER(san_to_match);
UNREFERENCED_PARAMETER(connect_to_upstreams);
RELEASE_ASSERT(0, "unsupported");
#endif // ENVOY_ENABLE_YAML

Expand Down
4 changes: 3 additions & 1 deletion test/integration/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ class IntegrationUtil {
*/
static Network::UpstreamTransportSocketFactoryPtr createQuicUpstreamTransportSocketFactory(
Api::Api& api, Stats::Store& store, Ssl::ContextManager& context_manager,
ThreadLocal::Instance& threadlocal, const std::string& san_to_match);
ThreadLocal::Instance& threadlocal, const std::string& san_to_match,
// Allow configuring TLS to talk to upstreams instead of Envoy
bool connect_to_fake_upstreams = false);

static Http::HeaderValidatorFactoryPtr makeHeaderValidationFactory(
const ::envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig&
Expand Down

0 comments on commit d6a8361

Please sign in to comment.