In QUIC, use the actual error code to serialize connection close if the connection is closed silently.

Protected by FLAGS_quic_reloadable_flag_quic_fix_dispatcher_sent_error_code.

PiperOrigin-RevId: 363717315
Change-Id: I8178e5c7e48ebefcdf2939b8554716e5be09b152
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 936aa9f..585aab7 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -4373,7 +4373,11 @@
 
   client_.reset(CreateQuicClient(client_writer_));
   EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
-  EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_FAILED));
+  if (GetQuicReloadableFlag(quic_fix_dispatcher_sent_error_code)) {
+    EXPECT_THAT(client_->connection_error(), IsError(QUIC_PACKET_WRITE_ERROR));
+  } else {
+    EXPECT_THAT(client_->connection_error(), IsError(QUIC_HANDSHAKE_FAILED));
+  }
 }
 
 // Regression test for b/116200989.
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index 73d636a..0c50d29 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -803,7 +803,9 @@
 
 void QuicDispatcher::CleanUpSession(QuicConnectionId server_connection_id,
                                     QuicConnection* connection,
-                                    ConnectionCloseSource /*source*/) {
+                                    QuicErrorCode error,
+                                    const std::string& error_details,
+                                    ConnectionCloseSource source) {
   write_blocked_list_.erase(connection);
   QuicTimeWaitListManager::TimeWaitAction action =
       QuicTimeWaitListManager::SEND_STATELESS_RESET;
@@ -812,6 +814,14 @@
     action = QuicTimeWaitListManager::SEND_CONNECTION_CLOSE_PACKETS;
   } else {
     if (!connection->IsHandshakeComplete()) {
+      const bool fix_dispatcher_sent_error_code =
+          GetQuicReloadableFlag(quic_fix_dispatcher_sent_error_code) &&
+          source == ConnectionCloseSource::FROM_SELF;
+      // TODO(fayang): Do not serialize connection close packet if the
+      // connection is closed by the client.
+      if (fix_dispatcher_sent_error_code) {
+        QUIC_RELOADABLE_FLAG_COUNT(quic_fix_dispatcher_sent_error_code);
+      }
       if (!connection->version().HasIetfInvariantHeader()) {
         QUIC_CODE_COUNT(gquic_add_to_time_wait_list_with_handshake_failed);
       } else {
@@ -820,20 +830,22 @@
       if (support_multiple_cid_per_connection_) {
         QUIC_RESTART_FLAG_COUNT_N(
             quic_dispatcher_support_multiple_cid_per_connection_v2, 1, 2);
-        // This serializes a connection close termination packet with error code
-        // QUIC_HANDSHAKE_FAILED and adds the connection to the time wait list.
+        // This serializes a connection close termination packet and adds the
+        // connection to the time wait list.
         StatelessConnectionTerminator terminator(
             server_connection_id, connection->version(), helper_.get(),
             time_wait_list_manager_.get());
         terminator.CloseConnection(
-            QUIC_HANDSHAKE_FAILED,
-            "Connection is closed by server before handshake confirmed",
+            fix_dispatcher_sent_error_code ? error : QUIC_HANDSHAKE_FAILED,
+            fix_dispatcher_sent_error_code
+                ? error_details
+                : "Connection is closed by server before handshake confirmed",
             connection->version().HasIetfInvariantHeader(),
             connection->GetActiveServerConnectionIds());
       } else {
         action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS;
-        // This serializes a connection close termination packet with error code
-        // QUIC_HANDSHAKE_FAILED and adds the connection to the time wait list.
+        // This serializes a connection close termination packet and adds the
+        // connection to the time wait list.
         StatelesslyTerminateConnection(
             connection->connection_id(),
             connection->version().HasIetfInvariantHeader()
@@ -841,8 +853,11 @@
                 : GOOGLE_QUIC_PACKET,
             /*version_flag=*/true,
             connection->version().HasLengthPrefixedConnectionIds(),
-            connection->version(), QUIC_HANDSHAKE_FAILED,
-            "Connection is closed by server before handshake confirmed",
+            connection->version(),
+            fix_dispatcher_sent_error_code ? error : QUIC_HANDSHAKE_FAILED,
+            fix_dispatcher_sent_error_code
+                ? error_details
+                : "Connection is closed by server before handshake confirmed",
             // Although it is our intention to send termination packets, the
             // |action| argument is not used by this call to
             // StatelesslyTerminateConnection().
@@ -1027,7 +1042,7 @@
       }
       closed_ref_counted_session_list_.push_back(std::move(it->second));
     }
-    CleanUpSession(it->first, connection, source);
+    CleanUpSession(it->first, connection, error, error_details, source);
     if (support_multiple_cid_per_connection_) {
       QUIC_RESTART_FLAG_COUNT_N(
           quic_dispatcher_support_multiple_cid_per_connection_v2, 1, 2);
@@ -1065,7 +1080,7 @@
       }
       closed_session_list_.push_back(std::move(it->second));
     }
-    CleanUpSession(it->first, connection, source);
+    CleanUpSession(it->first, connection, error, error_details, source);
     session_map_.erase(it);
   }
 }
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index 4245a5d..b8a02fb 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -305,6 +305,8 @@
   // from the map after that.
   void CleanUpSession(QuicConnectionId server_connection_id,
                       QuicConnection* connection,
+                      QuicErrorCode error,
+                      const std::string& error_details,
                       ConnectionCloseSource source);
 
   // Called to terminate a connection statelessly. Depending on |format|, either
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 30820a0..b1a92dc 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -43,6 +43,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_dispatcher_sent_error_code, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_key_update_on_first_packet, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_stateless_reset, false)