Set a minimum number of chaos protection in new protector This simplifies testing whether chaos protection was performed since now we're guaranteed to get extra frames. This CL also adds a new e2e test for chaos protection v2, which test ensures that multi-packet chaos protection works as expected with kyber enabled. In particular, it ensures that retransmissions behave correctly. This change is only made in the new protector, which is behind a flag that is still enabled_blocked_by. Protected by FLAGS_quic_reloadable_flag_quic_enable_new_chaos_protector. PiperOrigin-RevId: 703637630
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc index 208fe89..a2dd544 100644 --- a/quiche/quic/core/http/end_to_end_test.cc +++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -6,6 +6,7 @@ #include <array> #include <cstddef> #include <cstdint> +#include <limits> #include <list> #include <memory> #include <optional> @@ -23,6 +24,7 @@ #include "quiche/quic/core/crypto/null_encrypter.h" #include "quiche/quic/core/crypto/quic_client_session_cache.h" #include "quiche/quic/core/frames/quic_blocked_frame.h" +#include "quiche/quic/core/frames/quic_crypto_frame.h" #include "quiche/quic/core/http/http_constants.h" #include "quiche/quic/core/http/quic_spdy_client_stream.h" #include "quiche/quic/core/http/quic_spdy_session.h" @@ -37,7 +39,10 @@ #include "quiche/quic/core/quic_dispatcher.h" #include "quiche/quic/core/quic_error_codes.h" #include "quiche/quic/core/quic_framer.h" +#include "quiche/quic/core/quic_interval.h" +#include "quiche/quic/core/quic_interval_set.h" #include "quiche/quic/core/quic_packet_creator.h" +#include "quiche/quic/core/quic_packet_number.h" #include "quiche/quic/core/quic_packet_writer.h" #include "quiche/quic/core/quic_packet_writer_wrapper.h" #include "quiche/quic/core/quic_packets.h" @@ -48,6 +53,7 @@ #include "quiche/quic/core/tls_client_handshaker.h" #include "quiche/quic/platform/api/quic_expect_bug.h" #include "quiche/quic/platform/api/quic_flags.h" +#include "quiche/quic/platform/api/quic_ip_address.h" #include "quiche/quic/platform/api/quic_logging.h" #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/quic/platform/api/quic_test.h" @@ -76,6 +82,7 @@ #include "quiche/quic/test_tools/quic_test_server.h" #include "quiche/quic/test_tools/quic_test_utils.h" #include "quiche/quic/test_tools/server_thread.h" +#include "quiche/quic/test_tools/simple_quic_framer.h" #include "quiche/quic/test_tools/web_transport_test_tools.h" #include "quiche/quic/tools/quic_backend_response.h" #include "quiche/quic/tools/quic_memory_cache_backend.h" @@ -289,6 +296,11 @@ std::make_unique<QuicClientSessionCache>(), GetParam().event_loop->Create(QuicDefaultClock::Get())); client->SetUserAgentID(kTestUserAgentId); + if (enable_kyber_in_client_) { + std::vector<uint16_t> client_supported_groups = { + SSL_GROUP_X25519_KYBER768_DRAFT00, SSL_GROUP_X25519}; + client->SetPreferredGroups(client_supported_groups); + } client->UseWriter(writer); if (!pre_shared_key_client_.empty()) { client->client()->SetPreSharedKey(pre_shared_key_client_); @@ -990,6 +1002,9 @@ CreateClientWithWriter(); } + void TestMultiPacketChaosProtection(int num_packets, bool drop_first_packet, + bool kyber = false); + quiche::test::ScopedEnvironmentForThreads environment_; bool initialized_; // If true, the Initialize() function will create |client_| and starts to @@ -1022,6 +1037,7 @@ int override_client_connection_id_length_ = -1; uint8_t expected_server_connection_id_length_; bool enable_web_transport_ = false; + bool enable_kyber_in_client_ = false; std::vector<std::string> received_webtransport_unidirectional_streams_; bool use_preferred_address_ = false; QuicSocketAddress server_preferred_address_; @@ -6557,35 +6573,233 @@ server_thread_->Resume(); } -// Testing packet writer that makes a copy of the first sent packets before -// sending them. Useful for tests that need access to sent packets. -class CopyingPacketWriter : public PacketDroppingTestWriter { +// Testing packet writer that parses initial packets and saves information +// relevant to chaos protection. +class ChaosPacketWriter : public PacketDroppingTestWriter { public: - explicit CopyingPacketWriter(int num_packets_to_copy) - : num_packets_to_copy_(num_packets_to_copy) {} + explicit ChaosPacketWriter(const ParsedQuicVersion& version, + bool drop_first_initial_packet) + : framer_({version}), + drop_next_initial_packet_(drop_first_initial_packet) { + framer_.framer()->SetInitialObfuscators(TestConnectionId()); + } + WriteResult WritePacket(const char* buffer, size_t buf_len, const QuicIpAddress& self_address, const QuicSocketAddress& peer_address, PerPacketOptions* options, const QuicPacketWriterParams& params) override { - if (num_packets_to_copy_ > 0) { - num_packets_to_copy_--; - packets_.push_back( - QuicEncryptedPacket(buffer, buf_len, /*owns_buffer=*/false).Clone()); + bool drop_packet = false; + QuicEncryptedPacket packet(buffer, buf_len); + if (framer_.ProcessPacket(packet)) { + if (framer_.header().form == IETF_QUIC_LONG_HEADER_PACKET && + framer_.header().long_packet_type == INITIAL) { + auto initial_packet = std::make_unique<InitialPacketContents>(); + for (const auto& frame : framer_.crypto_frames()) { + QuicInterval<QuicStreamOffset> interval( + frame->offset, frame->offset + frame->data_length); + initial_packet->crypto_data_intervals.Add(interval); + initial_packet->total_crypto_data_length += frame->data_length; + } + initial_packet->packet_number = + framer_.header().packet_number.ToUint64(); + initial_packet->num_crypto_frames = framer_.crypto_frames().size(); + initial_packet->num_padding_frames = framer_.padding_frames().size(); + initial_packet->num_ping_frames = framer_.ping_frames().size(); + if (drop_next_initial_packet_) { + drop_packet = true; + drop_next_initial_packet_ = false; + initial_packet->was_dropped = true; + } + initial_packets_.push_back(std::move(initial_packet)); + } + } + if (drop_packet) { + return WriteResult(WRITE_STATUS_OK, buf_len); } return PacketDroppingTestWriter::WritePacket(buffer, buf_len, self_address, peer_address, options, params); } - std::vector<std::unique_ptr<QuicEncryptedPacket>>& packets() { - return packets_; + struct InitialPacketContents { + uint64_t packet_number = std::numeric_limits<uint64_t>::max(); + int num_crypto_frames = 0; + int num_padding_frames = 0; + int num_ping_frames = 0; + bool was_dropped = false; + QuicByteCount total_crypto_data_length = 0; + QuicIntervalSet<QuicStreamOffset> crypto_data_intervals; + QuicByteCount min_crypto_offset() const { + return crypto_data_intervals.SpanningInterval().min(); + } + QuicByteCount max_crypto_data() const { + return crypto_data_intervals.SpanningInterval().max(); + } + }; + + const std::vector<std::unique_ptr<InitialPacketContents>>& initial_packets() { + return initial_packets_; } private: - int num_packets_to_copy_; - std::vector<std::unique_ptr<QuicEncryptedPacket>> packets_; + SimpleQuicFramer framer_; + std::vector<std::unique_ptr<InitialPacketContents>> initial_packets_; + bool drop_next_initial_packet_; }; +TEST_P(EndToEndTest, KyberChaosProtection) { + TestMultiPacketChaosProtection(/*num_packets=*/2, + /*drop_first_packet=*/false, + /*kyber=*/true); +} + +TEST_P(EndToEndTest, KyberChaosProtectionWithRetransmission) { + TestMultiPacketChaosProtection(/*num_packets=*/2, + /*drop_first_packet=*/true, + /*kyber=*/true); +} + +TEST_P(EndToEndTest, TwoPacketChaosProtection) { + TestMultiPacketChaosProtection(/*num_packets=*/2, + /*drop_first_packet=*/false); +} + +TEST_P(EndToEndTest, TwoPacketChaosProtectionWithRetransmission) { + TestMultiPacketChaosProtection(/*num_packets=*/2, + /*drop_first_packet=*/true); +} + +TEST_P(EndToEndTest, ThreePacketChaosProtection) { + TestMultiPacketChaosProtection(/*num_packets=*/3, + /*drop_first_packet=*/false); +} + +TEST_P(EndToEndTest, ThreePacketChaosProtectionWithRetransmission) { + TestMultiPacketChaosProtection(/*num_packets=*/3, + /*drop_first_packet=*/true); +} + +void EndToEndTest::TestMultiPacketChaosProtection(int num_packets, + bool drop_first_packet, + bool kyber) { + if (!version_.HasIetfQuicFrames()) { + ASSERT_TRUE(Initialize()); + return; + } + SetQuicReloadableFlag(quic_enable_new_chaos_protector, true); + // Setup test harness with a custom client writer. + connect_to_server_on_initialize_ = false; + int discard_length; + if (kyber) { + discard_length = 1216; + enable_kyber_in_client_ = true; + } else { + discard_length = 1000 * num_packets; + client_config_.SetDiscardLengthToSend(discard_length); + } + ASSERT_TRUE(Initialize()); + auto copying_writer = new ChaosPacketWriter(version_, drop_first_packet); + delete client_writer_; + client_writer_ = copying_writer; + client_.reset(CreateQuicClient(client_writer_, /*connect=*/false)); + client_->UseConnectionId(TestConnectionId()); + client_->Connect(); + MockableQuicClient* client = client_->client(); + QuicConnection* client_connection = GetClientConnection(); + client_writer_->Initialize( + QuicConnectionPeer::GetHelper(client_connection), + QuicConnectionPeer::GetAlarmFactory(client_connection), + std::make_unique<ClientDelegate>(client)); + EXPECT_TRUE(client->connected()); + // Make sure application data can be sent. + EXPECT_TRUE(SendSynchronousFooRequestAndCheckResponse()); + + // Make sure the first flight contains the entire client hello. + QuicIntervalSet<QuicStreamOffset> crypto_data_intervals; + int num_first_flight_packets = 0; + for (size_t i = 0; i < copying_writer->initial_packets().size(); ++i) { + if (copying_writer->initial_packets()[i]->crypto_data_intervals.Empty()) { + continue; + } + bool found = false; + for (const auto& interval : + copying_writer->initial_packets()[i]->crypto_data_intervals) { + if (!crypto_data_intervals.IsDisjoint(interval)) { + found = true; + } + crypto_data_intervals.Add(interval); + } + if (found) { + break; + } + num_first_flight_packets++; + } + EXPECT_EQ(num_first_flight_packets, num_packets); + EXPECT_EQ(crypto_data_intervals.Size(), 1u); + EXPECT_EQ(crypto_data_intervals.SpanningInterval().min(), 0u); + EXPECT_GT(crypto_data_intervals.SpanningInterval().max(), discard_length); + + ASSERT_GE(copying_writer->initial_packets().size(), 2u); + // First packet contains the start and end of the client hello. + auto& packet1 = copying_writer->initial_packets()[0]; + EXPECT_EQ(packet1->was_dropped, drop_first_packet); + EXPECT_EQ(packet1->packet_number, 1u); + EXPECT_GE(packet1->num_crypto_frames, 3u); + EXPECT_GE(packet1->num_ping_frames, 2u); + EXPECT_GE(packet1->num_padding_frames, 1u); + EXPECT_EQ(packet1->min_crypto_offset(), 0u); + EXPECT_GE(packet1->max_crypto_data(), discard_length); + EXPECT_GE(packet1->total_crypto_data_length, 500u); + // Subsequent packets contain the middle of the client hello. + auto& packet2 = copying_writer->initial_packets()[1]; + EXPECT_FALSE(packet2->was_dropped); + EXPECT_EQ(packet2->packet_number, 2u); + if (num_packets == 2) { + EXPECT_GE(packet2->num_crypto_frames, 3u); + EXPECT_GE(packet2->num_ping_frames, 2u); + } else { + EXPECT_GE(packet2->num_crypto_frames, 1u); + } + EXPECT_GT(packet2->min_crypto_offset(), 0u); + EXPECT_LT(packet2->max_crypto_data(), discard_length); + EXPECT_GE(packet2->total_crypto_data_length, 500u); + if (num_packets >= 3) { + ASSERT_GE(copying_writer->initial_packets().size(), 3u); + auto& packet3 = copying_writer->initial_packets()[2]; + EXPECT_FALSE(packet3->was_dropped); + EXPECT_EQ(packet3->packet_number, 3u); + EXPECT_GE(packet3->num_crypto_frames, 3u); + EXPECT_GE(packet3->num_ping_frames, 2u); + EXPECT_GE(packet3->num_padding_frames, 1u); + EXPECT_GT(packet3->min_crypto_offset(), 0u); + EXPECT_LT(packet3->max_crypto_data(), discard_length); + EXPECT_GE(packet3->total_crypto_data_length, 500u); + } + if (!drop_first_packet) { + return; + } + // Retransmission of the first packet contains the start and end of the client + // hello. This validates that the multiple crypto frames are retransmitted in + // the same packet, without the packet creator flushing between them. + bool found_retransmission = false; + for (size_t i = num_packets; i < copying_writer->initial_packets().size(); + ++i) { + // Iterate on subsequent packets until we find the one that contains the + // retransmission of the crypto frame that contains the start of the client + // hello. + auto& packet = copying_writer->initial_packets()[i]; + if (packet->num_crypto_frames == 0 || packet->min_crypto_offset() != 0) { + continue; + } + found_retransmission = true; + EXPECT_FALSE(packet->was_dropped); + EXPECT_GE(packet->num_crypto_frames, 2u); + EXPECT_GE(packet->max_crypto_data(), discard_length); + EXPECT_GE(packet->total_crypto_data_length, 500u); + } + EXPECT_TRUE(found_retransmission); +} + TEST_P(EndToEndTest, KeyUpdateInitiatedByClient) { if (!version_.UsesTls()) { // Key Update is only supported in TLS handshake.
diff --git a/quiche/quic/core/quic_chaos_protector.cc b/quiche/quic/core/quic_chaos_protector.cc index 0a1d3d1..8295730 100644 --- a/quiche/quic/core/quic_chaos_protector.cc +++ b/quiche/quic/core/quic_chaos_protector.cc
@@ -379,9 +379,12 @@ static_cast<int>(QuicFramer::GetMinCryptoFrameSize( crypto_buffer_offset_ + crypto_data_length_, crypto_data_length_)); // Pick a random number of CRYPTO frames to add. + constexpr uint64_t kMinAddedCryptoFrames = 2; constexpr uint64_t kMaxAddedCryptoFrames = 10; const uint64_t num_added_crypto_frames = - random_->InsecureRandUint64() % (kMaxAddedCryptoFrames + 1); + kMinAddedCryptoFrames + + random_->InsecureRandUint64() % + (kMaxAddedCryptoFrames + 1 - kMinAddedCryptoFrames); for (uint64_t i = 0; i < num_added_crypto_frames; i++) { if (remaining_padding_bytes_ < max_overhead_of_adding_a_crypto_frame) { break; @@ -430,10 +433,12 @@ if (remaining_padding_bytes_ == 0) { return; } + constexpr uint64_t kMinAddedPingFrames = 2; constexpr uint64_t kMaxAddedPingFrames = 10; - const uint64_t num_ping_frames = - random_->InsecureRandUint64() % - std::min<uint64_t>(kMaxAddedPingFrames, remaining_padding_bytes_); + const uint64_t num_ping_frames = std::min<uint64_t>( + kMinAddedPingFrames + random_->InsecureRandUint64() % + (kMaxAddedPingFrames + 1 - kMinAddedPingFrames), + remaining_padding_bytes_); for (uint64_t i = 0; i < num_ping_frames; i++) { frames_.push_back(QuicFrame(QuicPingFrame())); }
diff --git a/quiche/quic/core/quic_chaos_protector_test.cc b/quiche/quic/core/quic_chaos_protector_test.cc index fb87aa1..b090916 100644 --- a/quiche/quic/core/quic_chaos_protector_test.cc +++ b/quiche/quic/core/quic_chaos_protector_test.cc
@@ -434,41 +434,43 @@ TEST_P(QuicChaosProtectorTest, Main) { BuildEncryptAndParse(); - ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 6u); EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, 0u); EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, 1u); - ASSERT_EQ(validation_framer_.ping_frames().size(), 3u); - ASSERT_EQ(validation_framer_.padding_frames().size(), 7u); + EXPECT_EQ(validation_framer_.ping_frames().size(), 5u); + ASSERT_EQ(validation_framer_.padding_frames().size(), 9u); EXPECT_EQ(validation_framer_.padding_frames()[0].num_padding_bytes, 3); } TEST_P(QuicChaosProtectorTest, DifferentRandom) { random_.ResetBase(4); BuildEncryptAndParse(); - ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); - ASSERT_EQ(validation_framer_.ping_frames().size(), 4u); - ASSERT_EQ(validation_framer_.padding_frames().size(), 8u); + EXPECT_EQ(validation_framer_.crypto_frames().size(), 4u); + EXPECT_EQ(validation_framer_.ping_frames().size(), 6u); + EXPECT_EQ(validation_framer_.padding_frames().size(), 8u); } TEST_P(QuicChaosProtectorTest, RandomnessZero) { random_.ResetBase(0); BuildEncryptAndParse(); - ASSERT_EQ(validation_framer_.crypto_frames().size(), 1u); - EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 2u); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, 1); EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, - crypto_data_length_); - ASSERT_EQ(validation_framer_.ping_frames().size(), 0u); - ASSERT_EQ(validation_framer_.padding_frames().size(), 1u); + crypto_data_length_ - 1); + EXPECT_EQ(validation_framer_.crypto_frames()[1]->offset, crypto_offset_); + EXPECT_EQ(validation_framer_.crypto_frames()[1]->data_length, 1); + EXPECT_EQ(validation_framer_.ping_frames().size(), 2u); + EXPECT_EQ(validation_framer_.padding_frames().size(), 1u); } TEST_P(QuicChaosProtectorTest, Offset) { ResetOffset(123); BuildEncryptAndParse(); - ASSERT_EQ(validation_framer_.crypto_frames().size(), 4u); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 6u); EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, 1u); - ASSERT_EQ(validation_framer_.ping_frames().size(), 3u); - ASSERT_EQ(validation_framer_.padding_frames().size(), 7u); + EXPECT_EQ(validation_framer_.ping_frames().size(), 5u); + ASSERT_EQ(validation_framer_.padding_frames().size(), 8u); EXPECT_EQ(validation_framer_.padding_frames()[0].num_padding_bytes, 3); } @@ -476,12 +478,14 @@ ResetOffset(123); random_.ResetBase(0); BuildEncryptAndParse(); - ASSERT_EQ(validation_framer_.crypto_frames().size(), 1u); - EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_); + ASSERT_EQ(validation_framer_.crypto_frames().size(), 2u); + EXPECT_EQ(validation_framer_.crypto_frames()[0]->offset, crypto_offset_ + 1); EXPECT_EQ(validation_framer_.crypto_frames()[0]->data_length, - crypto_data_length_); - ASSERT_EQ(validation_framer_.ping_frames().size(), 0u); - ASSERT_EQ(validation_framer_.padding_frames().size(), 1u); + crypto_data_length_ - 1); + EXPECT_EQ(validation_framer_.crypto_frames()[1]->offset, crypto_offset_); + EXPECT_EQ(validation_framer_.crypto_frames()[1]->data_length, 1); + EXPECT_EQ(validation_framer_.ping_frames().size(), 2u); + EXPECT_EQ(validation_framer_.padding_frames().size(), 1u); } TEST_P(QuicChaosProtectorTest, ZeroRemainingBytesAfterSplit) { @@ -505,17 +509,17 @@ random_.ResetBase(38); BuildEncryptAndParse(); EXPECT_EQ(validation_framer_.crypto_frames().size(), 6u); - EXPECT_EQ(validation_framer_.ping_frames().size(), 8u); - EXPECT_EQ(validation_framer_.padding_frames().size(), 3u); + EXPECT_EQ(validation_framer_.ping_frames().size(), 4u); + EXPECT_EQ(validation_framer_.padding_frames().size(), 4u); } TEST_P(QuicChaosProtectorTest, ReorderedCryptoCryptoAndPadding) { input_frames_pattern_ = InputFramesPattern::kReorderedCryptoCryptoAndPadding; random_.ResetBase(38); BuildEncryptAndParse(); - EXPECT_EQ(validation_framer_.crypto_frames().size(), 7u); - EXPECT_EQ(validation_framer_.ping_frames().size(), 8u); - EXPECT_EQ(validation_framer_.padding_frames().size(), 3u); + EXPECT_EQ(validation_framer_.crypto_frames().size(), 6u); + EXPECT_EQ(validation_framer_.ping_frames().size(), 4u); + EXPECT_EQ(validation_framer_.padding_frames().size(), 4u); } TEST_P(QuicChaosProtectorTest, AckCryptoAndPadding) { @@ -523,8 +527,8 @@ random_.ResetBase(37); BuildEncryptAndParse(); EXPECT_EQ(validation_framer_.crypto_frames().size(), 3u); - EXPECT_EQ(validation_framer_.ping_frames().size(), 7u); - EXPECT_EQ(validation_framer_.padding_frames().size(), 2u); + EXPECT_EQ(validation_framer_.ping_frames().size(), 3u); + EXPECT_EQ(validation_framer_.padding_frames().size(), 4u); ASSERT_EQ(validation_framer_.ack_frames().size(), 1u); // Chaos protector does not insert padding before ACK, or recorder ACK frames. EXPECT_EQ(validation_framer_.frame_types()[0], ACK_FRAME);
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc index da3e87d..c9bc761 100644 --- a/quiche/quic/core/quic_connection_test.cc +++ b/quiche/quic/core/quic_connection_test.cc
@@ -10243,7 +10243,7 @@ EXPECT_EQ(connection_.max_packet_length(), writer_->last_packet_size()); // Verify packet process. - EXPECT_EQ(1u, writer_->crypto_frames().size()); + EXPECT_LE(1u, writer_->crypto_frames().size()); EXPECT_EQ(0u, writer_->stream_frames().size()); // Verify there is coalesced packet. EXPECT_NE(nullptr, writer_->coalesced_packet()); @@ -10706,7 +10706,7 @@ // RETRY. if (GetParam().ack_response == AckResponse::kImmediate) { EXPECT_EQ(2u, writer_->packets_write_attempts()); - EXPECT_EQ(1u, writer_->framer()->crypto_frames().size()); + EXPECT_LE(1u, writer_->framer()->crypto_frames().size()); } }
diff --git a/quiche/quic/core/quic_packet_creator_test.cc b/quiche/quic/core/quic_packet_creator_test.cc index f4e32e9..94baf89 100644 --- a/quiche/quic/core/quic_packet_creator_test.cc +++ b/quiche/quic/core/quic_packet_creator_test.cc
@@ -1412,9 +1412,9 @@ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _)); EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); if (enabled) { - EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)).Times(AtLeast(2)); + EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)).Times(AtLeast(3)); EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(AtLeast(2)); - EXPECT_CALL(framer_visitor_, OnPingFrame(_)).Times(AtLeast(1)); + EXPECT_CALL(framer_visitor_, OnPingFrame(_)).Times(AtLeast(2)); } else { EXPECT_CALL(framer_visitor_, OnCryptoFrame(_)).Times(1); EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(1);
diff --git a/quiche/quic/test_tools/quic_test_client.cc b/quiche/quic/test_tools/quic_test_client.cc index 1513064..4c011a6 100644 --- a/quiche/quic/test_tools/quic_test_client.cc +++ b/quiche/quic/test_tools/quic_test_client.cc
@@ -377,6 +377,11 @@ client_->SetUserAgentID(user_agent_id); } +void QuicTestClient::SetPreferredGroups( + const std::vector<uint16_t>& preferred_groups) { + client_->SetPreferredGroups(preferred_groups); +} + int64_t QuicTestClient::SendRequest(const std::string& uri) { quiche::HttpHeaderBlock headers; if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
diff --git a/quiche/quic/test_tools/quic_test_client.h b/quiche/quic/test_tools/quic_test_client.h index 5091e23..44c5210 100644 --- a/quiche/quic/test_tools/quic_test_client.h +++ b/quiche/quic/test_tools/quic_test_client.h
@@ -149,6 +149,9 @@ // Sets the |user_agent_id| of the |client_|. void SetUserAgentID(const std::string& user_agent_id); + // Sets the preferred TLS key exchange groups of the |client_|. + void SetPreferredGroups(const std::vector<uint16_t>& preferred_groups); + // Wraps data in a quic packet and sends it. int64_t SendData(const std::string& data, bool last_data); // As above, but |delegate| will be notified when |data| is ACKed.
diff --git a/quiche/quic/tools/quic_client_base.h b/quiche/quic/tools/quic_client_base.h index c707176..550e546 100644 --- a/quiche/quic/tools/quic_client_base.h +++ b/quiche/quic/tools/quic_client_base.h
@@ -190,6 +190,10 @@ crypto_config_.set_user_agent_id(user_agent_id); } + void SetPreferredGroups(const std::vector<uint16_t>& preferred_groups) { + crypto_config_.set_preferred_groups(preferred_groups); + } + void SetTlsSignatureAlgorithms(std::string signature_algorithms) { crypto_config_.set_tls_signature_algorithms( std::move(signature_algorithms));