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)