gfe-relnote: In QUIC v48, support partial write of CRYPTO frames. Protected by existing gfe2_reloadable_flag_quic_enable_version_48.

PiperOrigin-RevId: 264373472
Change-Id: I285900280e459bbd70e519ec1e78dc2fa518e4c4
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index 1ee1f9d..0311191 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -166,7 +166,8 @@
 
   // Exit app-limited phase once a packet that was sent while the connection is
   // not app-limited is acknowledged.
-  if (is_app_limited_ && packet_number > end_of_app_limited_phase_) {
+  if (is_app_limited_ && end_of_app_limited_phase_.IsInitialized() &&
+      packet_number > end_of_app_limited_phase_) {
     is_app_limited_ = false;
   }
 
diff --git a/quic/core/quic_crypto_stream.cc b/quic/core/quic_crypto_stream.cc
index 395b490..9d70139 100644
--- a/quic/core/quic_crypto_stream.cc
+++ b/quic/core/quic_crypto_stream.cc
@@ -154,6 +154,7 @@
     QUIC_BUG << "Empty crypto data being written";
     return;
   }
+  const bool had_buffered_data = HasBufferedCryptoFrames();
   // Append |data| to the send buffer for this encryption level.
   struct iovec iov(QuicUtils::MakeIovec(data));
   QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
@@ -167,16 +168,16 @@
     CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW,
                                "Writing too much crypto handshake data");
   }
+  if (had_buffered_data) {
+    // Do not try to write if there is buffered data.
+    return;
+  }
 
   EncryptionLevel current_level = session()->connection()->encryption_level();
   session()->connection()->SetDefaultEncryptionLevel(level);
   size_t bytes_consumed =
       session()->connection()->SendCryptoData(level, data.length(), offset);
   session()->connection()->SetDefaultEncryptionLevel(current_level);
-  // Since CRYPTO frames aren't flow controlled, SendCryptoData should have sent
-  // all data we asked it to send.
-  DCHECK_EQ(bytes_consumed, data.length());
-
   send_buffer->OnStreamDataConsumed(bytes_consumed);
 }
 
@@ -412,6 +413,48 @@
   session()->connection()->SetDefaultEncryptionLevel(current_encryption_level);
 }
 
+void QuicCryptoStream::WriteBufferedCryptoFrames() {
+  QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(
+      session()->connection()->transport_version()))
+      << "Versions less than 47 don't use CRYPTO frames";
+  EncryptionLevel current_encryption_level =
+      session()->connection()->encryption_level();
+  for (EncryptionLevel level :
+       {ENCRYPTION_INITIAL, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+    QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+    const size_t data_length =
+        send_buffer->stream_offset() - send_buffer->stream_bytes_written();
+    if (data_length == 0) {
+      // No buffered data for this encryption level.
+      continue;
+    }
+    session()->connection()->SetDefaultEncryptionLevel(level);
+    size_t bytes_consumed = session()->connection()->SendCryptoData(
+        level, data_length, send_buffer->stream_bytes_written());
+    send_buffer->OnStreamDataConsumed(bytes_consumed);
+    if (bytes_consumed < data_length) {
+      // Connection is write blocked.
+      break;
+    }
+  }
+  session()->connection()->SetDefaultEncryptionLevel(current_encryption_level);
+}
+
+bool QuicCryptoStream::HasBufferedCryptoFrames() const {
+  QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(
+      session()->connection()->transport_version()))
+      << "Versions less than 47 don't use CRYPTO frames";
+  for (EncryptionLevel level :
+       {ENCRYPTION_INITIAL, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+    const QuicStreamSendBuffer& send_buffer = substreams_[level].send_buffer;
+    DCHECK_GE(send_buffer.stream_offset(), send_buffer.stream_bytes_written());
+    if (send_buffer.stream_offset() > send_buffer.stream_bytes_written()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool QuicCryptoStream::IsFrameOutstanding(EncryptionLevel level,
                                           size_t offset,
                                           size_t length) const {
diff --git a/quic/core/quic_crypto_stream.h b/quic/core/quic_crypto_stream.h
index 1b641b1..01523ee 100644
--- a/quic/core/quic_crypto_stream.h
+++ b/quic/core/quic_crypto_stream.h
@@ -130,6 +130,12 @@
   // encryption level, offset, and length in |crypto_frame|.
   void RetransmitData(QuicCryptoFrame* crypto_frame);
 
+  // Called to write buffered crypto frames.
+  void WriteBufferedCryptoFrames();
+
+  // Returns true if there is buffered crypto frames.
+  bool HasBufferedCryptoFrames() const;
+
   // Returns true if any portion of the data at encryption level |level|
   // starting at |offset| for |length| bytes is outstanding.
   bool IsFrameOutstanding(EncryptionLevel level,
diff --git a/quic/core/quic_crypto_stream_test.cc b/quic/core/quic_crypto_stream_test.cc
index 4117786..9d3bda5 100644
--- a/quic/core/quic_crypto_stream_test.cc
+++ b/quic/core/quic_crypto_stream_test.cc
@@ -23,6 +23,7 @@
 using testing::InSequence;
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
+using testing::Return;
 
 namespace quic {
 namespace test {
@@ -532,6 +533,43 @@
   }
 }
 
+TEST_F(QuicCryptoStreamTest, WriteBufferedCryptoFrames) {
+  if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+    return;
+  }
+  EXPECT_FALSE(stream_->HasBufferedCryptoFrames());
+  InSequence s;
+  // Send [0, 1350) in ENCRYPTION_INITIAL.
+  EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+  std::string data(1350, 'a');
+  // Only consumed 1000 bytes.
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0))
+      .WillOnce(Return(1000));
+  stream_->WriteCryptoData(ENCRYPTION_INITIAL, data);
+  EXPECT_TRUE(stream_->HasBufferedCryptoFrames());
+
+  // Send [1350, 2700) in ENCRYPTION_ZERO_RTT and verify no write is attempted
+  // because there is buffered data.
+  EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+  connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 350, 1000))
+      .WillOnce(Return(350));
+  // Partial write of ENCRYPTION_ZERO_RTT data.
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+      .WillOnce(Return(1000));
+  stream_->WriteBufferedCryptoFrames();
+  EXPECT_TRUE(stream_->HasBufferedCryptoFrames());
+  EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 350, 1000))
+      .WillOnce(Return(350));
+  stream_->WriteBufferedCryptoFrames();
+  EXPECT_FALSE(stream_->HasBufferedCryptoFrames());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index fd3d489..a335804 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -572,11 +572,24 @@
   size_t num_writes = flow_controller_.IsBlocked()
                           ? write_blocked_streams_.NumBlockedSpecialStreams()
                           : write_blocked_streams_.NumBlockedStreams();
-  if (num_writes == 0 && !control_frame_manager_.WillingToWrite()) {
+  if (num_writes == 0 && !control_frame_manager_.WillingToWrite() &&
+      (!QuicVersionUsesCryptoFrames(connection_->transport_version()) ||
+       !GetCryptoStream()->HasBufferedCryptoFrames())) {
     return;
   }
 
   QuicConnection::ScopedPacketFlusher flusher(connection_);
+  if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+    QuicCryptoStream* crypto_stream = GetMutableCryptoStream();
+    if (crypto_stream->HasBufferedCryptoFrames()) {
+      crypto_stream->WriteBufferedCryptoFrames();
+    }
+    if (crypto_stream->HasBufferedCryptoFrames()) {
+      // Cannot finish writing buffered crypto frames, connection is write
+      // blocked.
+      return;
+    }
+  }
   if (control_frame_manager_.WillingToWrite()) {
     control_frame_manager_.OnCanWrite();
   }
@@ -626,6 +639,10 @@
   // 3) If the crypto or headers streams are blocked, or
   // 4) connection is not flow control blocked and there are write blocked
   // streams.
+  if (QuicVersionUsesCryptoFrames(connection_->transport_version()) &&
+      HasPendingHandshake()) {
+    return true;
+  }
   return control_frame_manager_.WillingToWrite() ||
          !streams_with_pending_retransmission_.empty() ||
          write_blocked_streams_.HasWriteBlockedSpecialStream() ||
@@ -635,9 +652,8 @@
 
 bool QuicSession::HasPendingHandshake() const {
   if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
-    // Writing CRYPTO frames is not subject to flow control, so there can't be
-    // pending data to write, only pending retransmissions.
-    return GetCryptoStream()->HasPendingCryptoRetransmission();
+    return GetCryptoStream()->HasPendingCryptoRetransmission() ||
+           GetCryptoStream()->HasBufferedCryptoFrames();
   }
   return QuicContainsKey(
              streams_with_pending_retransmission_,
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 3a6db56..09d2bbb 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -2692,6 +2692,31 @@
   EXPECT_TRUE(stream->write_side_closed());
 }
 
+TEST_P(QuicSessionTestServer, WriteBufferedCryptoFrames) {
+  if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+    return;
+  }
+  std::string data(1350, 'a');
+  TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+  // Only consumed 1000 bytes.
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0))
+      .WillOnce(Return(1000));
+  crypto_stream->WriteCryptoData(ENCRYPTION_INITIAL, data);
+  EXPECT_TRUE(session_.HasPendingHandshake());
+  EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+  EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+  crypto_stream->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 350, 1000))
+      .WillOnce(Return(350));
+  EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+      .WillOnce(Return(1350));
+  session_.OnCanWrite();
+  EXPECT_FALSE(session_.HasPendingHandshake());
+  EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic