Expose the number of available peer-issued CIDs This allows Chromium code to record the number of available CIDs whenever it attempts to migration. This is going to be useful to validate: 1. That the CID capacity experiment is working 2. The ideal number of spare CID GFEs should issue See https://crrev.com/c/8483300. PiperOrigin-RevId: 991740308
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc index 841df24..2ffa025 100644 --- a/quiche/quic/core/quic_connection.cc +++ b/quiche/quic/core/quic_connection.cc
@@ -4413,6 +4413,12 @@ peer_issued_cid_manager_->HasUnusedConnectionId()); } +size_t QuicConnection::NumUnusedPeerIssuedConnectionIds() const { + return peer_issued_cid_manager_ == nullptr + ? 0 + : peer_issued_cid_manager_->NumUnusedConnectionIds(); +} + void QuicConnection::OnRetransmissionAlarm() { QUICHE_DCHECK(connected()); ScopedRetransmissionTimeoutIndicator indicator(this);
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h index d59463d..442df61 100644 --- a/quiche/quic/core/quic_connection.h +++ b/quiche/quic/core/quic_connection.h
@@ -1364,6 +1364,9 @@ bool HasUnusedConnectionId() const; + // Returns the number of unused peer-issued connection IDs. + size_t NumUnusedPeerIssuedConnectionIds() const; + QuicPathValidationContext* GetPathValidationContext() const; // TODO(martinduke): Delete this deprecated method once non-QUICHE callers are
diff --git a/quiche/quic/core/quic_connection_id_manager.h b/quiche/quic/core/quic_connection_id_manager.h index a466fb1..5276eca 100644 --- a/quiche/quic/core/quic_connection_id_manager.h +++ b/quiche/quic/core/quic_connection_id_manager.h
@@ -75,6 +75,10 @@ return !unused_connection_id_data_.empty(); } + size_t NumUnusedConnectionIds() const { + return unused_connection_id_data_.size(); + } + // Returns the data associated with an unused connection Id. After the call, // the Id is marked as used. Returns nullptr if there is no unused connection // Id.
diff --git a/quiche/quic/core/quic_connection_id_manager_test.cc b/quiche/quic/core/quic_connection_id_manager_test.cc index 03af309..9f6a2da 100644 --- a/quiche/quic/core/quic_connection_id_manager_test.cc +++ b/quiche/quic/core/quic_connection_id_manager_test.cc
@@ -110,6 +110,49 @@ bool duplicate_frame_ = false; }; +TEST_F(QuicPeerIssuedConnectionIdManagerTest, NumUnusedConnectionIds) { + EXPECT_EQ(0u, peer_issued_cid_manager_.NumUnusedConnectionIds()); + EXPECT_FALSE(peer_issued_cid_manager_.HasUnusedConnectionId()); + + // Receives CID #1 from peer. + QuicNewConnectionIdFrame frame1; + frame1.connection_id = TestConnectionId(1); + frame1.sequence_number = 1u; + frame1.retire_prior_to = 0u; + frame1.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame1.connection_id); + ASSERT_THAT(peer_issued_cid_manager_.OnNewConnectionIdFrame( + frame1, &error_details_, &duplicate_frame_), + IsQuicNoError()); + EXPECT_EQ(1u, peer_issued_cid_manager_.NumUnusedConnectionIds()); + EXPECT_TRUE(peer_issued_cid_manager_.HasUnusedConnectionId()); + + // Receives CID #2 from peer. + peer_issued_cid_manager_.set_active_connection_id_limit(3); + QuicNewConnectionIdFrame frame2; + frame2.connection_id = TestConnectionId(2); + frame2.sequence_number = 2u; + frame2.retire_prior_to = 0u; + frame2.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame2.connection_id); + ASSERT_THAT(peer_issued_cid_manager_.OnNewConnectionIdFrame( + frame2, &error_details_, &duplicate_frame_), + IsQuicNoError()); + EXPECT_EQ(2u, peer_issued_cid_manager_.NumUnusedConnectionIds()); + + // Consume one unused connection ID. + const QuicConnectionIdData* cid_data = + peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); + ASSERT_THAT(cid_data, testing::NotNull()); + EXPECT_EQ(1u, peer_issued_cid_manager_.NumUnusedConnectionIds()); + + // Consume the remaining unused connection ID. + cid_data = peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(); + ASSERT_THAT(cid_data, testing::NotNull()); + EXPECT_EQ(0u, peer_issued_cid_manager_.NumUnusedConnectionIds()); + EXPECT_FALSE(peer_issued_cid_manager_.HasUnusedConnectionId()); +} + TEST_F(QuicPeerIssuedConnectionIdManagerTest, ConnectionIdSequenceWhenMigrationSucceed) { {
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc index 7c710c9..72258ed 100644 --- a/quiche/quic/core/quic_connection_test.cc +++ b/quiche/quic/core/quic_connection_test.cc
@@ -13642,6 +13642,39 @@ } } +TEST_P(QuicConnectionTest, NumUnusedPeerIssuedConnectionIds) { + if (!version().IsIetfQuic()) { + EXPECT_EQ(0u, connection_.NumUnusedPeerIssuedConnectionIds()); + return; + } + EXPECT_EQ(0u, connection_.NumUnusedPeerIssuedConnectionIds()); + + connection_.CreateConnectionIdManager(); + EXPECT_EQ(0u, connection_.NumUnusedPeerIssuedConnectionIds()); + + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + connection_.OnHandshakeComplete(); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); + + QuicNewConnectionIdFrame frame; + frame.connection_id = TestConnectionId(1234); + ASSERT_NE(frame.connection_id, connection_.connection_id()); + frame.stateless_reset_token = + QuicUtils::GenerateStatelessResetToken(frame.connection_id); + frame.retire_prior_to = 0u; + frame.sequence_number = 1u; + EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame)); + EXPECT_EQ(1u, connection_.NumUnusedPeerIssuedConnectionIds()); + + const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Any4(), 12345); + TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT); + EXPECT_TRUE(connection_.MigratePath(kNewSelfAddress, + connection_.peer_address(), &new_writer, + /*owns_writer=*/false)); + EXPECT_EQ(0u, connection_.NumUnusedPeerIssuedConnectionIds()); +} + TEST_P(QuicConnectionTest, MigrateToNewPathDuringProbing) { if (!VersionIsIetfQuic(connection_.version().transport_version)) { return;