Add QUIC_BUG_IF to QuicUtils::GetCryptoStreamId

In QUIC versions that use CRYPTO frames (instead of stream 1 frames) for
the crypto handshake, the concept of a "crypto stream ID" makes no
sense, so QuicUtils::GetCryptoStreamId should hit a QUIC_BUG_IF to
prevent its misuse.

gfe-relnote: Add QUIC_BUG_IF protected behind QuicVersionUsesCryptoFrames
PiperOrigin-RevId: 248463613
Change-Id: If6768658e9ffc058778b53a91f95839826602fbf
diff --git a/quic/test_tools/quic_server_session_base_peer.h b/quic/test_tools/quic_server_session_base_peer.h
index 30c9b22..d2a21a6 100644
--- a/quic/test_tools/quic_server_session_base_peer.h
+++ b/quic/test_tools/quic_server_session_base_peer.h
@@ -21,9 +21,11 @@
   static void SetCryptoStream(QuicServerSessionBase* s,
                               QuicCryptoServerStream* crypto_stream) {
     s->crypto_stream_.reset(crypto_stream);
-    s->RegisterStaticStream(
-        QuicUtils::GetCryptoStreamId(s->connection()->transport_version()),
-        crypto_stream);
+    if (!QuicVersionUsesCryptoFrames(s->connection()->transport_version())) {
+      s->RegisterStaticStream(
+          QuicUtils::GetCryptoStreamId(s->connection()->transport_version()),
+          crypto_stream);
+    }
   }
   static bool IsBandwidthResumptionEnabled(QuicServerSessionBase* s) {
     return s->bandwidth_resumption_enabled_;
diff --git a/quic/test_tools/quic_session_peer.cc b/quic/test_tools/quic_session_peer.cc
index 0683fd7..ed7043d 100644
--- a/quic/test_tools/quic_session_peer.cc
+++ b/quic/test_tools/quic_session_peer.cc
@@ -166,6 +166,20 @@
 }
 
 // static
+void QuicSessionPeer::RegisterStaticStream(QuicSession* session,
+                                           QuicStreamId id,
+                                           QuicStream* stream) {
+  return session->RegisterStaticStream(id, stream);
+}
+
+// static
+void QuicSessionPeer::RegisterStaticStreamNew(
+    QuicSession* session,
+    std::unique_ptr<QuicStream> stream) {
+  return session->RegisterStaticStreamNew(std::move(stream));
+}
+
+// static
 bool QuicSessionPeer::IsStreamClosed(QuicSession* session, QuicStreamId id) {
   DCHECK_NE(0u, id);
   return session->IsClosedStream(id);
diff --git a/quic/test_tools/quic_session_peer.h b/quic/test_tools/quic_session_peer.h
index 994a36c..3828981 100644
--- a/quic/test_tools/quic_session_peer.h
+++ b/quic/test_tools/quic_session_peer.h
@@ -64,6 +64,11 @@
       QuicSession* session);
   static void ActivateStream(QuicSession* session,
                              std::unique_ptr<QuicStream> stream);
+  static void RegisterStaticStream(QuicSession* session,
+                                   QuicStreamId stream_id,
+                                   QuicStream* stream);
+  static void RegisterStaticStreamNew(QuicSession* session,
+                                      std::unique_ptr<QuicStream> stream);
 
   // Discern the state of a stream.  Exactly one of these should be true at a
   // time for any stream id > 0 (other than the special streams 1 and 3).
diff --git a/quic/test_tools/simple_session_notifier.cc b/quic/test_tools/simple_session_notifier.cc
index 9ae4d22..e72e0e1 100644
--- a/quic/test_tools/simple_session_notifier.cc
+++ b/quic/test_tools/simple_session_notifier.cc
@@ -72,7 +72,7 @@
                                                  QuicByteCount data_length,
                                                  bool fin) {
   StreamState& state = stream_map_.find(id)->second;
-  if (id == QuicUtils::GetCryptoStreamId(connection_->transport_version()) &&
+  if (QuicUtils::IsCryptoStreamId(connection_->transport_version(), id) &&
       data_length > 0) {
     crypto_bytes_transferred_[connection_->encryption_level()].Add(
         offset, offset + data_length);
@@ -127,8 +127,11 @@
 }
 
 void SimpleSessionNotifier::NeuterUnencryptedData() {
+  // TODO(nharper): Handle CRYPTO frame case.
+  if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+    return;
+  }
   for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) {
-    // TODO(nharper): Handle CRYPTO frame case.
     QuicStreamFrame stream_frame(
         QuicUtils::GetCryptoStreamId(connection_->transport_version()), false,
         interval.min(), interval.max() - interval.min());
@@ -146,7 +149,6 @@
     return;
   }
   // Write new data.
-  // TODO(nharper): Write CRYPTO frames.
   for (const auto& pair : stream_map_) {
     const auto& state = pair.second;
     if (!StreamHasBufferedData(pair.first)) {
@@ -320,8 +322,8 @@
     EncryptionLevel retransmission_encryption_level =
         connection_->encryption_level();
     EncryptionLevel current_encryption_level = connection_->encryption_level();
-    if (frame.stream_frame.stream_id ==
-        QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+    if (QuicUtils::IsCryptoStreamId(connection_->transport_version(),
+                                    frame.stream_frame.stream_id)) {
       for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
         if (retransmission.Intersects(crypto_bytes_transferred_[i])) {
           retransmission_encryption_level = static_cast<EncryptionLevel>(i);
@@ -339,8 +341,8 @@
       const bool can_bundle_fin =
           retransmit_fin &&
           (retransmission_offset + retransmission_length == state.bytes_sent);
-      if (frame.stream_frame.stream_id ==
-          QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+      if (QuicUtils::IsCryptoStreamId(connection_->transport_version(),
+                                      frame.stream_frame.stream_id)) {
         // Set appropriate encryption level for crypto stream.
         connection_->SetDefaultEncryptionLevel(retransmission_encryption_level);
       }
@@ -356,8 +358,8 @@
       if (can_bundle_fin) {
         retransmit_fin = !consumed.fin_consumed;
       }
-      if (frame.stream_frame.stream_id ==
-          QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+      if (QuicUtils::IsCryptoStreamId(connection_->transport_version(),
+                                      frame.stream_frame.stream_id)) {
         // Restore encryption level.
         connection_->SetDefaultEncryptionLevel(current_encryption_level);
       }
@@ -497,7 +499,36 @@
 }
 
 bool SimpleSessionNotifier::RetransmitLostCryptoData() {
-  // TODO(nharper): Handle CRYPTO frame case.
+  if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+    for (EncryptionLevel level :
+         {ENCRYPTION_INITIAL, ENCRYPTION_HANDSHAKE, ENCRYPTION_ZERO_RTT,
+          ENCRYPTION_FORWARD_SECURE}) {
+      auto& state = crypto_state_[level];
+      while (!state.pending_retransmissions.Empty()) {
+        connection_->SetTransmissionType(HANDSHAKE_RETRANSMISSION);
+        EncryptionLevel current_encryption_level =
+            connection_->encryption_level();
+        connection_->SetDefaultEncryptionLevel(level);
+        QuicIntervalSet<QuicStreamOffset> retransmission(
+            state.pending_retransmissions.begin()->min(),
+            state.pending_retransmissions.begin()->max());
+        retransmission.Intersection(crypto_bytes_transferred_[level]);
+        QuicStreamOffset retransmission_offset = retransmission.begin()->min();
+        QuicByteCount retransmission_length =
+            retransmission.begin()->max() - retransmission.begin()->min();
+        size_t bytes_consumed = connection_->SendCryptoData(
+            level, retransmission_length, retransmission_offset);
+        // Restore encryption level.
+        connection_->SetDefaultEncryptionLevel(current_encryption_level);
+        state.pending_retransmissions.Difference(
+            retransmission_offset, retransmission_offset + bytes_consumed);
+        if (bytes_consumed < retransmission_length) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
   if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
                                         connection_->transport_version()))) {
     return true;
diff --git a/quic/test_tools/simple_session_notifier_test.cc b/quic/test_tools/simple_session_notifier_test.cc
index 53712fd..e5298eb 100644
--- a/quic/test_tools/simple_session_notifier_test.cc
+++ b/quic/test_tools/simple_session_notifier_test.cc
@@ -4,10 +4,12 @@
 
 #include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
 
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
 
 using testing::_;
 using testing::InSequence;
@@ -126,6 +128,9 @@
 }
 
 TEST_F(SimpleSessionNotifierTest, NeuterUnencryptedData) {
+  if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+    return;
+  }
   InSequence s;
   // Send crypto data [0, 1024) in ENCRYPTION_INITIAL.
   connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
@@ -159,6 +164,9 @@
 }
 
 TEST_F(SimpleSessionNotifierTest, OnCanWrite) {
+  if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+    return;
+  }
   InSequence s;
   // Send crypto data [0, 1024) in ENCRYPTION_INITIAL.
   connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
@@ -221,6 +229,71 @@
   EXPECT_FALSE(notifier_.WillingToWrite());
 }
 
+TEST_F(SimpleSessionNotifierTest, OnCanWriteCryptoFrames) {
+  if (!QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+    return;
+  }
+  SimpleDataProducer producer;
+  connection_.SetDataProducer(&producer);
+  InSequence s;
+  // Send crypto data [0, 1024) in ENCRYPTION_INITIAL.
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+  EXPECT_CALL(connection_, SendCryptoData(ENCRYPTION_INITIAL, 1024, 0))
+      .WillOnce(Invoke(&connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  producer.SaveCryptoData(ENCRYPTION_INITIAL, 0, std::string(1024, 'a'));
+  producer.SaveCryptoData(ENCRYPTION_INITIAL, 500, std::string(524, 'a'));
+  notifier_.WriteCryptoData(ENCRYPTION_INITIAL, 1024, 0);
+  // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+  connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, QuicMakeUnique<NullEncrypter>(
+                                                    Perspective::IS_CLIENT));
+  EXPECT_CALL(connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1024, 0))
+      .WillOnce(Invoke(&connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  producer.SaveCryptoData(ENCRYPTION_ZERO_RTT, 0, std::string(1024, 'a'));
+  notifier_.WriteCryptoData(ENCRYPTION_ZERO_RTT, 1024, 0);
+  // Send stream 3 [0, 1024) and connection is blocked.
+  EXPECT_CALL(connection_, SendStreamData(3, 1024, 0, FIN))
+      .WillOnce(Return(QuicConsumedData(512, false)));
+  notifier_.WriteOrBufferData(3, 1024, FIN);
+  // Send stream 5 [0, 1024).
+  EXPECT_CALL(connection_, SendStreamData(5, _, _, _)).Times(0);
+  notifier_.WriteOrBufferData(5, 1024, NO_FIN);
+  // Reset stream 5 with error.
+  EXPECT_CALL(connection_, SendControlFrame(_)).Times(0);
+  notifier_.WriteOrBufferRstStream(5, QUIC_ERROR_PROCESSING_STREAM, 1024);
+
+  // Lost crypto data [500, 1500) and stream 3 [0, 512).
+  QuicCryptoFrame crypto_frame1(ENCRYPTION_INITIAL, 500, 524);
+  QuicCryptoFrame crypto_frame2(ENCRYPTION_ZERO_RTT, 0, 476);
+  QuicStreamFrame stream3_frame(3, false, 0, 512);
+  notifier_.OnFrameLost(QuicFrame(&crypto_frame1));
+  notifier_.OnFrameLost(QuicFrame(&crypto_frame2));
+  notifier_.OnFrameLost(QuicFrame(stream3_frame));
+
+  // Connection becomes writable.
+  // Lost crypto data gets retransmitted as [500, 1024) and [1024, 1500), as
+  // they are in different encryption levels.
+  EXPECT_CALL(connection_, SendCryptoData(ENCRYPTION_INITIAL, 524, 500))
+      .WillOnce(Invoke(&connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  EXPECT_CALL(connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 476, 0))
+      .WillOnce(Invoke(&connection_,
+                       &MockQuicConnection::QuicConnection_SendCryptoData));
+  // Lost stream 3 data gets retransmitted.
+  EXPECT_CALL(connection_, SendStreamData(3, 512, 0, NO_FIN))
+      .WillOnce(Return(QuicConsumedData(512, false)));
+  // Buffered control frames get sent.
+  EXPECT_CALL(connection_, SendControlFrame(_))
+      .WillOnce(Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+  // Buffered stream 3 data [512, 1024) gets sent.
+  EXPECT_CALL(connection_, SendStreamData(3, 512, 512, FIN))
+      .WillOnce(Return(QuicConsumedData(512, true)));
+  notifier_.OnCanWrite();
+  EXPECT_FALSE(notifier_.WillingToWrite());
+}
+
 TEST_F(SimpleSessionNotifierTest, RetransmitFrames) {
   InSequence s;
   // Send stream 3 data [0, 10) and fin.