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));