Allow QuicSession::WritevData() to write data at a specified encryption level.

gfe-relnote: protected by gfe2_reloadable_flag_quic_writevdata_at_level
PiperOrigin-RevId: 297697074
Change-Id: Ib4e5d860f22f506f36db76f4f8ee1c1e406f6a20
diff --git a/quic/core/quic_crypto_stream.cc b/quic/core/quic_crypto_stream.cc
index bf0106c..86603a9 100644
--- a/quic/core/quic_crypto_stream.cc
+++ b/quic/core/quic_crypto_stream.cc
@@ -10,10 +10,12 @@
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
 #include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_optional.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
 
 namespace quic {
@@ -36,7 +38,8 @@
       substreams_{{{this, ENCRYPTION_INITIAL},
                    {this, ENCRYPTION_HANDSHAKE},
                    {this, ENCRYPTION_ZERO_RTT},
-                   {this, ENCRYPTION_FORWARD_SECURE}}} {
+                   {this, ENCRYPTION_FORWARD_SECURE}}},
+      writevdata_at_level_(GetQuicReloadableFlag(quic_writevdata_at_level)) {
   // The crypto stream is exempt from connection level flow control.
   DisableConnectionFlowControlForThisStream();
 }
@@ -282,25 +285,32 @@
     pending.offset = retransmission.begin()->min();
     pending.length =
         retransmission.begin()->max() - retransmission.begin()->min();
-    EncryptionLevel current_encryption_level =
-        session()->connection()->encryption_level();
-    // Set appropriate encryption level.
-    session()->connection()->SetDefaultEncryptionLevel(
-        retransmission_encryption_level);
-    QuicConsumedData consumed =
-        stream_delegate()->WritevData(id(), pending.length, pending.offset,
-                                      NO_FIN, /*is_retransmission*/ true);
-    QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
-                  << " tries to retransmit stream data [" << pending.offset
-                  << ", " << pending.offset + pending.length
-                  << ") with encryption level: "
-                  << retransmission_encryption_level
-                  << ", consumed: " << consumed;
-    OnStreamFrameRetransmitted(pending.offset, consumed.bytes_consumed,
-                               consumed.fin_consumed);
-    // Restore encryption level.
-    session()->connection()->SetDefaultEncryptionLevel(
-        current_encryption_level);
+    QuicConsumedData consumed(0, false);
+    if (!writevdata_at_level_) {
+      EncryptionLevel current_encryption_level =
+          session()->connection()->encryption_level();
+      // Set appropriate encryption level.
+      session()->connection()->SetDefaultEncryptionLevel(
+          retransmission_encryption_level);
+      consumed = stream_delegate()->WritevData(
+          id(), pending.length, pending.offset, NO_FIN,
+          /*is_retransmission*/ true, QuicheNullOpt);
+      QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
+                    << " tries to retransmit stream data [" << pending.offset
+                    << ", " << pending.offset + pending.length
+                    << ") with encryption level: "
+                    << retransmission_encryption_level
+                    << ", consumed: " << consumed;
+      OnStreamFrameRetransmitted(pending.offset, consumed.bytes_consumed,
+                                 consumed.fin_consumed);
+      // Restore encryption level.
+      session()->connection()->SetDefaultEncryptionLevel(
+          current_encryption_level);
+    } else {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_writevdata_at_level, 1, 2);
+      consumed = RetransmitStreamDataAtLevel(pending.offset, pending.length,
+                                             retransmission_encryption_level);
+    }
     if (consumed.bytes_consumed < pending.length) {
       // The connection is write blocked.
       break;
@@ -328,22 +338,29 @@
   for (const auto& interval : retransmission) {
     QuicStreamOffset retransmission_offset = interval.min();
     QuicByteCount retransmission_length = interval.max() - interval.min();
-    // Set appropriate encryption level.
-    session()->connection()->SetDefaultEncryptionLevel(send_encryption_level);
-    QuicConsumedData consumed = stream_delegate()->WritevData(
-        id(), retransmission_length, retransmission_offset, NO_FIN,
-        /*is_retransmission*/ true);
-    QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
-                  << " is forced to retransmit stream data ["
-                  << retransmission_offset << ", "
-                  << retransmission_offset + retransmission_length
-                  << "), with encryption level: " << send_encryption_level
-                  << ", consumed: " << consumed;
-    OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed,
-                               consumed.fin_consumed);
-    // Restore encryption level.
-    session()->connection()->SetDefaultEncryptionLevel(
-        current_encryption_level);
+    QuicConsumedData consumed(0, false);
+    if (!writevdata_at_level_) {
+      // Set appropriate encryption level.
+      session()->connection()->SetDefaultEncryptionLevel(send_encryption_level);
+      consumed = stream_delegate()->WritevData(
+          id(), retransmission_length, retransmission_offset, NO_FIN,
+          /*is_retransmission*/ true, QuicheNullOpt);
+      QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
+                    << " is forced to retransmit stream data ["
+                    << retransmission_offset << ", "
+                    << retransmission_offset + retransmission_length
+                    << "), with encryption level: " << send_encryption_level
+                    << ", consumed: " << consumed;
+      OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed,
+                                 consumed.fin_consumed);
+      // Restore encryption level.
+      session()->connection()->SetDefaultEncryptionLevel(
+          current_encryption_level);
+    } else {
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_writevdata_at_level, 2, 2);
+      consumed = RetransmitStreamDataAtLevel(
+          retransmission_offset, retransmission_length, send_encryption_level);
+    }
     if (consumed.bytes_consumed < retransmission_length) {
       // The connection is write blocked.
       return false;
@@ -353,6 +370,26 @@
   return true;
 }
 
+QuicConsumedData QuicCryptoStream::RetransmitStreamDataAtLevel(
+    QuicStreamOffset retransmission_offset,
+    QuicByteCount retransmission_length,
+    EncryptionLevel encryption_level) {
+  DCHECK(writevdata_at_level_);
+  const auto consumed = stream_delegate()->WritevData(
+      id(), retransmission_length, retransmission_offset, NO_FIN,
+      /*is_retransmission*/ true, encryption_level);
+  QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
+                << " is forced to retransmit stream data ["
+                << retransmission_offset << ", "
+                << retransmission_offset + retransmission_length
+                << "), with encryption level: " << encryption_level
+                << ", consumed: " << consumed;
+  OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed,
+                             consumed.fin_consumed);
+
+  return consumed;
+}
+
 uint64_t QuicCryptoStream::crypto_bytes_read() const {
   if (!QuicVersionUsesCryptoFrames(session()->transport_version())) {
     return stream_bytes_read();