Do not SendInResponseToPacket or return true in CanWrite when connection ID is required but missing in the packet creator.

Protected by FLAGS_quic_reloadable_flag_quic_do_not_write_when_no_client_cid_available.

PiperOrigin-RevId: 533120959
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index b68aad0..adb6e87 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -47,6 +47,7 @@
 #include "quiche/quic/platform/api/quic_logging.h"
 #include "quiche/quic/platform/api/quic_socket_address.h"
 #include "quiche/common/platform/api/quiche_flag_utils.h"
+#include "quiche/common/platform/api/quiche_testvalue.h"
 #include "quiche/common/quiche_text_utils.h"
 
 namespace quic {
@@ -2455,6 +2456,16 @@
     return;
   }
 
+  if (GetQuicReloadableFlag(quic_do_not_write_when_no_client_cid_available)) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_do_not_write_when_no_client_cid_available,
+                                 1, 3);
+    if (IsMissingDestinationConnectionID()) {
+      QUICHE_RELOADABLE_FLAG_COUNT_N(
+          quic_do_not_write_when_no_client_cid_available, 2, 3);
+      return;
+    }
+  }
+
   // If the writer is blocked, don't attempt to send packets now or in the send
   // alarm. When the writer unblocks, OnCanWrite() will be called for this
   // connection to send.
@@ -3186,14 +3197,18 @@
   }
 }
 
+bool QuicConnection::IsMissingDestinationConnectionID() const {
+  return peer_issued_cid_manager_ != nullptr &&
+         packet_creator_.GetDestinationConnectionId().IsEmpty();
+}
+
 bool QuicConnection::ShouldGeneratePacket(
     HasRetransmittableData retransmittable, IsHandshake handshake) {
   QUICHE_DCHECK(handshake != IS_HANDSHAKE ||
                 QuicVersionUsesCryptoFrames(transport_version()))
       << ENDPOINT
       << "Handshake in STREAM frames should not check ShouldGeneratePacket";
-  if (peer_issued_cid_manager_ != nullptr &&
-      packet_creator_.GetDestinationConnectionId().IsEmpty()) {
+  if (IsMissingDestinationConnectionID()) {
     QUICHE_DCHECK(version().HasIetfQuicFrames());
     QUIC_CODE_COUNT(quic_generate_packet_blocked_by_no_connection_id);
     QUIC_BUG_IF(quic_bug_90265_1, perspective_ == Perspective::IS_CLIENT);
@@ -3256,6 +3271,14 @@
     return false;
   }
 
+  if (GetQuicReloadableFlag(quic_do_not_write_when_no_client_cid_available)) {
+    if (IsMissingDestinationConnectionID()) {
+      QUIC_RELOADABLE_FLAG_COUNT_N(
+          quic_do_not_write_when_no_client_cid_available, 3, 3);
+      return false;
+    }
+  }
+
   if (version().CanSendCoalescedPackets() &&
       framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL) &&
       framer_.is_processing_packet()) {
@@ -6282,9 +6305,15 @@
               kMinNumOfActiveConnectionIds, client_connection_id, clock_,
               alarm_factory_, this, context());
     } else {
+      bool create_client_self_issued_cid_manager = true;
+      quiche::AdjustTestValue(
+          "quic::QuicConnection::create_cid_manager_when_set_client_cid",
+          &create_client_self_issued_cid_manager);
       // Note in Chromium client, set_client_connection_id is not called and
       // thus self_issued_cid_manager_ should be null.
-      self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager();
+      if (create_client_self_issued_cid_manager) {
+        self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager();
+      }
     }
   }
   QUIC_DLOG(INFO) << ENDPOINT << "setting client connection ID to "
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index fbfd952..f739644 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -725,6 +725,9 @@
       override;
   std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override;
 
+  // Whether destination connection ID is required but missing in the packet
+  // creator.
+  bool IsMissingDestinationConnectionID() const;
   // QuicPacketCreator::DelegateInterface
   bool ShouldGeneratePacket(HasRetransmittableData retransmittable,
                             IsHandshake handshake) override;
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h
index 5023083..7f6ba62 100644
--- a/quiche/quic/core/quic_flags_list.h
+++ b/quiche/quic/core/quic_flags_list.h
@@ -47,6 +47,8 @@
 QUIC_FLAG(quic_reloadable_flag_quic_discard_initial_packet_with_key_dropped, 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 write when client CID is requried but missing in the packet creator.
+QUIC_FLAG(quic_reloadable_flag_quic_do_not_write_when_no_client_cid_available, true)
 // If true, enable server retransmittable on wire PING.
 QUIC_FLAG(quic_reloadable_flag_quic_enable_server_on_wire_ping, true)
 // If true, flush pending frames as well as pending padding bytes on connection migration.