In QuicSession::OnCanWrite, do not send stream data when PTO fires and handshake is not confirmed. Protected by FLAGS_quic_reloadable_flag_quic_donot_pto_stream_data_before_handshake_confirmed. PiperOrigin-RevId: 481377538
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc index 5c44172..565419d 100644 --- a/quiche/quic/core/quic_connection.cc +++ b/quiche/quic/core/quic_connection.cc
@@ -2438,8 +2438,7 @@ if (perspective_ == Perspective::IS_SERVER && version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) { - if (in_on_retransmission_time_out_ && - coalesced_packet_.NumberOfPackets() == 0u) { + if (in_probe_time_out_ && coalesced_packet_.NumberOfPackets() == 0u) { // PTO fires while handshake is not confirmed. Do not preempt handshake // data with stream data. QUIC_CODE_COUNT(quic_try_to_send_half_rtt_data_when_pto_fires); @@ -7088,15 +7087,15 @@ QuicConnection::ScopedRetransmissionTimeoutIndicator:: ScopedRetransmissionTimeoutIndicator(QuicConnection* connection) : connection_(connection) { - QUICHE_DCHECK(!connection_->in_on_retransmission_time_out_) + QUICHE_DCHECK(!connection_->in_probe_time_out_) << "ScopedRetransmissionTimeoutIndicator is not supposed to be nested"; - connection_->in_on_retransmission_time_out_ = true; + connection_->in_probe_time_out_ = true; } QuicConnection::ScopedRetransmissionTimeoutIndicator:: ~ScopedRetransmissionTimeoutIndicator() { - QUICHE_DCHECK(connection_->in_on_retransmission_time_out_); - connection_->in_on_retransmission_time_out_ = false; + QUICHE_DCHECK(connection_->in_probe_time_out_); + connection_->in_probe_time_out_ = false; } void QuicConnection::RestoreToLastValidatedPath(
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h index 36e9bbf..153c142 100644 --- a/quiche/quic/core/quic_connection.h +++ b/quiche/quic/core/quic_connection.h
@@ -1263,6 +1263,8 @@ context_.bug_listener.swap(bug_listener); } + bool in_probe_time_out() const { return in_probe_time_out_; } + // Ensures the network blackhole delay is longer than path degrading delay. static QuicTime::Delta CalculateNetworkBlackholeDelay( QuicTime::Delta blackhole_delay, QuicTime::Delta path_degrading_delay, @@ -1537,7 +1539,7 @@ QuicConnection* connection_; }; - // A class which sets and clears in_on_retransmission_time_out_ when entering + // A class which sets and clears in_probe_time_out_ when entering // and exiting OnRetransmissionTimeout, respectively. class QUIC_EXPORT_PRIVATE ScopedRetransmissionTimeoutIndicator { public: @@ -2244,7 +2246,7 @@ bool have_decrypted_first_one_rtt_packet_ = false; // True if we are currently processing OnRetransmissionTimeout. - bool in_on_retransmission_time_out_ = false; + bool in_probe_time_out_ = false; QuicPathValidator path_validator_;
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h index d54304f..d3079d5 100644 --- a/quiche/quic/core/quic_flags_list.h +++ b/quiche/quic/core/quic_flags_list.h
@@ -51,6 +51,8 @@ QUIC_FLAG(quic_reloadable_flag_quic_enable_disable_resumption, true) // If true, discard INITIAL packet if the key has been dropped. QUIC_FLAG(quic_reloadable_flag_quic_discard_initial_packet_with_key_dropped, true) +// If true, do not PTO new stream data before handshake confirmed. +QUIC_FLAG(quic_reloadable_flag_quic_donot_pto_stream_data_before_handshake_confirmed, true) // If true, do not issue a new connection ID that has been claimed by another connection. QUIC_FLAG(quic_reloadable_flag_quic_check_cid_collision_when_issue_new_cid, true) // If true, do not mark stream connection level write blocked if its write side has been closed.
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc index b6b0371..b4abf52 100644 --- a/quiche/quic/core/quic_session.cc +++ b/quiche/quic/core/quic_session.cc
@@ -639,6 +639,14 @@ if (control_frame_manager_.WillingToWrite()) { control_frame_manager_.OnCanWrite(); } + if (GetQuicReloadableFlag( + quic_donot_pto_stream_data_before_handshake_confirmed) && + version().UsesTls() && GetHandshakeState() != HANDSHAKE_CONFIRMED && + connection_->in_probe_time_out()) { + QUIC_CODE_COUNT(quic_donot_pto_stream_data_before_handshake_confirmed); + // Do not PTO stream data before handshake gets confirmed. + return; + } // TODO(b/147146815): this makes all datagrams go before stream data. We // should have a better priority scheme for this. if (!datagram_queue_.empty()) {
diff --git a/quiche/quic/core/quic_session_test.cc b/quiche/quic/core/quic_session_test.cc index 916ed1b..3e66183 100644 --- a/quiche/quic/core/quic_session_test.cc +++ b/quiche/quic/core/quic_session_test.cc
@@ -3128,6 +3128,40 @@ } } +TEST_P(QuicSessionTestServer, DonotPtoStreamDataBeforeHandshakeConfirmed) { + if (!session_.version().UsesTls()) { + return; + } + EXPECT_NE(HANDSHAKE_CONFIRMED, session_.GetHandshakeState()); + + TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); + EXPECT_FALSE(crypto_stream->HasBufferedCryptoFrames()); + std::string data(1350, 'a'); + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0)) + .WillOnce(Return(1000)); + crypto_stream->WriteCryptoData(ENCRYPTION_INITIAL, data); + ASSERT_TRUE(crypto_stream->HasBufferedCryptoFrames()); + + TestStream* stream = session_.CreateOutgoingBidirectionalStream(); + + session_.MarkConnectionLevelWriteBlocked(stream->id()); + // Buffered crypto data gets sent. + EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, _, _)) + .WillOnce(Return(350)); + if (GetQuicReloadableFlag( + quic_donot_pto_stream_data_before_handshake_confirmed)) { + // Verify stream data is not sent on PTO before handshake confirmed. + EXPECT_CALL(*stream, OnCanWrite()).Times(0); + } else { + EXPECT_CALL(*stream, OnCanWrite()); + } + + // Fire PTO. + QuicConnectionPeer::SetInProbeTimeOut(connection_, true); + session_.OnCanWrite(); + EXPECT_FALSE(crypto_stream->HasBufferedCryptoFrames()); +} + // A client test class that can be used when the automatic configuration is not // desired. class QuicSessionTestClientUnconfigured : public QuicSessionTestBase {
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc index eb28e2d..f7c6424 100644 --- a/quiche/quic/test_tools/quic_connection_peer.cc +++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -560,5 +560,11 @@ connection->FlushCoalescedPacket(); } +// static +void QuicConnectionPeer::SetInProbeTimeOut(QuicConnection* connection, + bool value) { + connection->in_probe_time_out_ = value; +} + } // namespace test } // namespace quic
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h index 169a33c..932ddba 100644 --- a/quiche/quic/test_tools/quic_connection_peer.h +++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -226,6 +226,8 @@ static void FlushCoalescedPacket(QuicConnection* connection); static QuicAlarm* GetMultiPortProbingAlarm(QuicConnection* connection); + + static void SetInProbeTimeOut(QuicConnection* connection, bool value); }; } // namespace test