Map QuicErrorCodes to proper IETF QUIC Close Connection information.

THis CL maps a QuicErrorCode to either an IETF QUIC Application Connection
Close or Transport Connection Close. In the latter case, the mapping also
produces the correct IETF QUIC TransportErrorCode to use.

gfe-relnote: N/A code exercised only for IETF QUIC.
PiperOrigin-RevId: 261741702
Change-Id: I1e928046b18c86ff0a7cbcf013e0d9219c27d2dd
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 0c33604..a577bf1 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2683,12 +2683,23 @@
       !GetUpdatedAckFrame().ack_frame->packets.Empty()) {
     SendAck();
   }
-  QuicConnectionCloseFrame* frame =
-      new QuicConnectionCloseFrame(error, details);
-  // If version99/IETF QUIC set the close type. Default close type is Google
-  // QUIC.
+  QuicConnectionCloseFrame* frame;
   if (VersionHasIetfQuicFrames(transport_version())) {
-    frame->close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+    QuicErrorCodeToIetfMapping mapping =
+        QuicErrorCodeToTransportErrorCode(error);
+    if (mapping.is_transport_close_) {
+      // Maps to a transport close
+      // TODO(fkastenholz) need to change "0" to get the frame type currently
+      // being processed so that it can be inserted into the frame.
+      frame = new QuicConnectionCloseFrame(error, details,
+                                           mapping.transport_error_code_, 0);
+    } else {
+      // Maps to an application close.
+      frame = new QuicConnectionCloseFrame(error, details,
+                                           mapping.application_error_code_);
+    }
+  } else {
+    frame = new QuicConnectionCloseFrame(error, details);
   }
   packet_generator_.ConsumeRetransmittableControlFrame(QuicFrame(frame));
   packet_generator_.FlushAllQueuedFrames();
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 773db66..39d2760 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1437,11 +1437,27 @@
 
     header.packet_number = QuicPacketNumber(number);
 
-    QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY, "");
+    QuicErrorCode kQuicErrorCode = QUIC_PEER_GOING_AWAY;
+    // This QuicConnectionCloseFrame will default to being for a Google QUIC
+    // close. If doing IETF QUIC then set fields appropriately for CC/T or CC/A,
+    // depending on the mapping.
+    QuicConnectionCloseFrame qccf(kQuicErrorCode, "");
     if (VersionHasIetfQuicFrames(peer_framer_.transport_version())) {
-      // Default close-type is Google QUIC. If doing IETF QUIC then
-      // set close type to be IETF CC/T.
-      qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+      QuicErrorCodeToIetfMapping mapping =
+          QuicErrorCodeToTransportErrorCode(kQuicErrorCode);
+      if (mapping.is_transport_close_) {
+        // Maps to a transport close
+        qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+        qccf.transport_error_code = mapping.transport_error_code_;
+        // TODO(fkastenholz) need to change "0" to get the frame type currently
+        // being processed so that it can be inserted into the frame.
+        qccf.transport_close_frame_type = 0;
+      } else {
+        // Maps to an application close.
+        qccf.close_type = IETF_QUIC_APPLICATION_CONNECTION_CLOSE;
+        qccf.application_error_code = mapping.application_error_code_;
+      }
+      qccf.extracted_error_code = kQuicErrorCode;
     }
 
     QuicFrames frames;
@@ -1556,7 +1572,32 @@
     const std::vector<QuicConnectionCloseFrame>& connection_close_frames =
         writer_->connection_close_frames();
     ASSERT_EQ(1u, connection_close_frames.size());
-    EXPECT_EQ(expected_code, connection_close_frames[0].quic_error_code);
+    if (!VersionHasIetfQuicFrames(version().transport_version)) {
+      EXPECT_EQ(expected_code, connection_close_frames[0].quic_error_code);
+      EXPECT_EQ(GOOGLE_QUIC_CONNECTION_CLOSE,
+                connection_close_frames[0].close_type);
+      return;
+    }
+
+    QuicErrorCodeToIetfMapping mapping =
+        QuicErrorCodeToTransportErrorCode(expected_code);
+
+    if (mapping.is_transport_close_) {
+      // This Google QUIC Error Code maps to a transport close,
+      EXPECT_EQ(IETF_QUIC_TRANSPORT_CONNECTION_CLOSE,
+                connection_close_frames[0].close_type);
+      EXPECT_EQ(mapping.transport_error_code_,
+                connection_close_frames[0].transport_error_code);
+      // TODO(fkastenholz): when the extracted error code CL lands,
+      // need to test that extracted==expected.
+    } else {
+      // This maps to an application close.
+      EXPECT_EQ(expected_code, connection_close_frames[0].quic_error_code);
+      EXPECT_EQ(IETF_QUIC_APPLICATION_CONNECTION_CLOSE,
+                connection_close_frames[0].close_type);
+      // TODO(fkastenholz): when the extracted error code CL lands,
+      // need to test that extracted==expected.
+    }
   }
 
   QuicConnectionId connection_id_;
@@ -1597,6 +1638,36 @@
                          QuicConnectionTest,
                          ::testing::ValuesIn(GetTestParams()));
 
+// These two tests ensure that the QuicErrorCode mapping works correctly.
+// Both tests expect to see a Google QUIC close if not running IETF QUIC.
+// If running IETF QUIC, the first will generate a transport connection
+// close, the second an application connection close.
+// The connection close codes for the two tests are manually chosen;
+// they are expected to always map to transport- and application-
+// closes, respectively. If that changes, mew codes should be chosen.
+TEST_P(QuicConnectionTest, CloseErrorCodeTestTransport) {
+  EXPECT_TRUE(connection_.connected());
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
+  connection_.CloseConnection(
+      IETF_QUIC_PROTOCOL_VIOLATION, "Should be transport close",
+      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION);
+}
+
+// Test that the IETF QUIC Error code mapping function works
+// properly for application connection close codes.
+TEST_P(QuicConnectionTest, CloseErrorCodeTestApplication) {
+  EXPECT_TRUE(connection_.connected());
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
+  connection_.CloseConnection(
+      QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE,
+      "Should be application close",
+      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE);
+}
+
 TEST_P(QuicConnectionTest, SelfAddressChangeAtClient) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
@@ -1942,8 +2013,13 @@
   TestConnectionCloseQuicErrorCode(QUIC_INTERNAL_ERROR);
   const std::vector<QuicConnectionCloseFrame>& connection_close_frames =
       writer_->connection_close_frames();
-  EXPECT_EQ("Packet written out of order.",
-            connection_close_frames[0].error_details);
+  if (VersionHasIetfQuicFrames(version().transport_version)) {
+    EXPECT_EQ("1:Packet written out of order.",
+              connection_close_frames[0].error_details);
+  } else {
+    EXPECT_EQ("Packet written out of order.",
+              connection_close_frames[0].error_details);
+  }
 }
 
 TEST_P(QuicConnectionTest, DiscardQueuedPacketsAfterConnectionClose) {
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index 102b60e..38572f1 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -124,4 +124,309 @@
   return os;
 }
 
+QuicErrorCodeToIetfMapping QuicErrorCodeToTransportErrorCode(
+    QuicErrorCode error) {
+  switch (error) {
+    // TODO(fkastenholz): Currently, all QuicError codes will map
+    // to application error codes and the original Google QUIC error
+    // code. This will change over time as we go through all calls to
+    // CloseConnection() and see whether the call is a Transport or an
+    // Application close and what the translated code should be.
+    case QUIC_NO_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_NO_ERROR)}};
+    case QUIC_INTERNAL_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_INTERNAL_ERROR)}};
+    case QUIC_STREAM_DATA_AFTER_TERMINATION:
+      return {true,
+              {static_cast<uint64_t>(QUIC_STREAM_DATA_AFTER_TERMINATION)}};
+    case QUIC_INVALID_PACKET_HEADER:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_PACKET_HEADER)}};
+    case QUIC_INVALID_FRAME_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_FRAME_DATA)}};
+    case QUIC_MISSING_PAYLOAD:
+      return {true, {static_cast<uint64_t>(QUIC_MISSING_PAYLOAD)}};
+    case QUIC_INVALID_FEC_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_FEC_DATA)}};
+    case QUIC_INVALID_STREAM_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_STREAM_DATA)}};
+    case QUIC_OVERLAPPING_STREAM_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_OVERLAPPING_STREAM_DATA)}};
+    case QUIC_UNENCRYPTED_STREAM_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_UNENCRYPTED_STREAM_DATA)}};
+    case QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA)}};
+    case QUIC_MAYBE_CORRUPTED_MEMORY:
+      return {true, {static_cast<uint64_t>(QUIC_MAYBE_CORRUPTED_MEMORY)}};
+    case QUIC_UNENCRYPTED_FEC_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_UNENCRYPTED_FEC_DATA)}};
+    case QUIC_INVALID_RST_STREAM_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_RST_STREAM_DATA)}};
+    case QUIC_INVALID_CONNECTION_CLOSE_DATA:
+      return {true,
+              {static_cast<uint64_t>(QUIC_INVALID_CONNECTION_CLOSE_DATA)}};
+    case QUIC_INVALID_GOAWAY_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_GOAWAY_DATA)}};
+    case QUIC_INVALID_WINDOW_UPDATE_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_WINDOW_UPDATE_DATA)}};
+    case QUIC_INVALID_BLOCKED_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_BLOCKED_DATA)}};
+    case QUIC_INVALID_STOP_WAITING_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_STOP_WAITING_DATA)}};
+    case QUIC_INVALID_PATH_CLOSE_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_PATH_CLOSE_DATA)}};
+    case QUIC_INVALID_ACK_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_ACK_DATA)}};
+    case QUIC_INVALID_MESSAGE_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_MESSAGE_DATA)}};
+    case QUIC_INVALID_VERSION_NEGOTIATION_PACKET:
+      return {true,
+              {static_cast<uint64_t>(QUIC_INVALID_VERSION_NEGOTIATION_PACKET)}};
+    case QUIC_INVALID_PUBLIC_RST_PACKET:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_PUBLIC_RST_PACKET)}};
+    case QUIC_DECRYPTION_FAILURE:
+      return {true, {static_cast<uint64_t>(QUIC_DECRYPTION_FAILURE)}};
+    case QUIC_ENCRYPTION_FAILURE:
+      return {true, {static_cast<uint64_t>(QUIC_ENCRYPTION_FAILURE)}};
+    case QUIC_PACKET_TOO_LARGE:
+      return {true, {static_cast<uint64_t>(QUIC_PACKET_TOO_LARGE)}};
+    case QUIC_PEER_GOING_AWAY:
+      return {true, {static_cast<uint64_t>(QUIC_PEER_GOING_AWAY)}};
+    case QUIC_INVALID_STREAM_ID:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_STREAM_ID)}};
+    case QUIC_INVALID_PRIORITY:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_PRIORITY)}};
+    case QUIC_TOO_MANY_OPEN_STREAMS:
+      return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_OPEN_STREAMS)}};
+    case QUIC_TOO_MANY_AVAILABLE_STREAMS:
+      return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_AVAILABLE_STREAMS)}};
+    case QUIC_PUBLIC_RESET:
+      return {true, {static_cast<uint64_t>(QUIC_PUBLIC_RESET)}};
+    case QUIC_INVALID_VERSION:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_VERSION)}};
+    case QUIC_INVALID_HEADER_ID:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_HEADER_ID)}};
+    case QUIC_INVALID_NEGOTIATED_VALUE:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_NEGOTIATED_VALUE)}};
+    case QUIC_DECOMPRESSION_FAILURE:
+      return {true, {static_cast<uint64_t>(QUIC_DECOMPRESSION_FAILURE)}};
+    case QUIC_NETWORK_IDLE_TIMEOUT:
+      return {true, {static_cast<uint64_t>(QUIC_NETWORK_IDLE_TIMEOUT)}};
+    case QUIC_HANDSHAKE_TIMEOUT:
+      return {true, {static_cast<uint64_t>(QUIC_HANDSHAKE_TIMEOUT)}};
+    case QUIC_ERROR_MIGRATING_ADDRESS:
+      return {true, {static_cast<uint64_t>(QUIC_ERROR_MIGRATING_ADDRESS)}};
+    case QUIC_ERROR_MIGRATING_PORT:
+      return {true, {static_cast<uint64_t>(QUIC_ERROR_MIGRATING_PORT)}};
+    case QUIC_PACKET_WRITE_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_PACKET_WRITE_ERROR)}};
+    case QUIC_PACKET_READ_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_PACKET_READ_ERROR)}};
+    case QUIC_EMPTY_STREAM_FRAME_NO_FIN:
+      return {true, {static_cast<uint64_t>(QUIC_EMPTY_STREAM_FRAME_NO_FIN)}};
+    case QUIC_INVALID_HEADERS_STREAM_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_HEADERS_STREAM_DATA)}};
+    case QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE:
+      return {
+          true,
+          {static_cast<uint64_t>(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE)}};
+    case QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA:
+      return {
+          true,
+          {static_cast<uint64_t>(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA)}};
+    case QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA:
+      return {true,
+              {static_cast<uint64_t>(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA)}};
+    case QUIC_FLOW_CONTROL_INVALID_WINDOW:
+      return {true, {static_cast<uint64_t>(QUIC_FLOW_CONTROL_INVALID_WINDOW)}};
+    case QUIC_CONNECTION_IP_POOLED:
+      return {true, {static_cast<uint64_t>(QUIC_CONNECTION_IP_POOLED)}};
+    case QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS:
+      return {true,
+              {static_cast<uint64_t>(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS)}};
+    case QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS:
+      return {
+          true,
+          {static_cast<uint64_t>(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS)}};
+    case QUIC_CONNECTION_CANCELLED:
+      return {true, {static_cast<uint64_t>(QUIC_CONNECTION_CANCELLED)}};
+    case QUIC_BAD_PACKET_LOSS_RATE:
+      return {true, {static_cast<uint64_t>(QUIC_BAD_PACKET_LOSS_RATE)}};
+    case QUIC_PUBLIC_RESETS_POST_HANDSHAKE:
+      return {true, {static_cast<uint64_t>(QUIC_PUBLIC_RESETS_POST_HANDSHAKE)}};
+    case QUIC_FAILED_TO_SERIALIZE_PACKET:
+      return {true, {static_cast<uint64_t>(QUIC_FAILED_TO_SERIALIZE_PACKET)}};
+    case QUIC_TOO_MANY_RTOS:
+      return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_RTOS)}};
+    case QUIC_HANDSHAKE_FAILED:
+      return {true, {static_cast<uint64_t>(QUIC_HANDSHAKE_FAILED)}};
+    case QUIC_CRYPTO_TAGS_OUT_OF_ORDER:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_TAGS_OUT_OF_ORDER)}};
+    case QUIC_CRYPTO_TOO_MANY_ENTRIES:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_TOO_MANY_ENTRIES)}};
+    case QUIC_CRYPTO_INVALID_VALUE_LENGTH:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_INVALID_VALUE_LENGTH)}};
+    case QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)}};
+    case QUIC_INVALID_CRYPTO_MESSAGE_TYPE:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_CRYPTO_MESSAGE_TYPE)}};
+    case QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER:
+      return {true,
+              {static_cast<uint64_t>(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER)}};
+    case QUIC_INVALID_CHANNEL_ID_SIGNATURE:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_CHANNEL_ID_SIGNATURE)}};
+    case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+      return {true,
+              {static_cast<uint64_t>(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND)}};
+    case QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP:
+      return {
+          true,
+          {static_cast<uint64_t>(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP)}};
+    case QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND:
+      return {true,
+              {static_cast<uint64_t>(QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND)}};
+    case QUIC_UNSUPPORTED_PROOF_DEMAND:
+      return {true, {static_cast<uint64_t>(QUIC_UNSUPPORTED_PROOF_DEMAND)}};
+    case QUIC_CRYPTO_INTERNAL_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_INTERNAL_ERROR)}};
+    case QUIC_CRYPTO_VERSION_NOT_SUPPORTED:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_VERSION_NOT_SUPPORTED)}};
+    case QUIC_CRYPTO_NO_SUPPORT:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_NO_SUPPORT)}};
+    case QUIC_CRYPTO_TOO_MANY_REJECTS:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_TOO_MANY_REJECTS)}};
+    case QUIC_PROOF_INVALID:
+      return {true, {static_cast<uint64_t>(QUIC_PROOF_INVALID)}};
+    case QUIC_CRYPTO_DUPLICATE_TAG:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_DUPLICATE_TAG)}};
+    case QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT:
+      return {true,
+              {static_cast<uint64_t>(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT)}};
+    case QUIC_CRYPTO_SERVER_CONFIG_EXPIRED:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_SERVER_CONFIG_EXPIRED)}};
+    case QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED:
+      return {true,
+              {static_cast<uint64_t>(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED)}};
+    case QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO)}};
+    case QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE)}};
+    case QUIC_CRYPTO_CHLO_TOO_LARGE:
+      return {true, {static_cast<uint64_t>(QUIC_CRYPTO_CHLO_TOO_LARGE)}};
+    case QUIC_VERSION_NEGOTIATION_MISMATCH:
+      return {true, {static_cast<uint64_t>(QUIC_VERSION_NEGOTIATION_MISMATCH)}};
+    case QUIC_BAD_MULTIPATH_FLAG:
+      return {true, {static_cast<uint64_t>(QUIC_BAD_MULTIPATH_FLAG)}};
+    case QUIC_MULTIPATH_PATH_DOES_NOT_EXIST:
+      return {true,
+              {static_cast<uint64_t>(QUIC_MULTIPATH_PATH_DOES_NOT_EXIST)}};
+    case QUIC_MULTIPATH_PATH_NOT_ACTIVE:
+      return {true, {static_cast<uint64_t>(QUIC_MULTIPATH_PATH_NOT_ACTIVE)}};
+    case QUIC_IP_ADDRESS_CHANGED:
+      return {true, {static_cast<uint64_t>(QUIC_IP_ADDRESS_CHANGED)}};
+    case QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS)}};
+    case QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES:
+      return {
+          true,
+          {static_cast<uint64_t>(QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES)}};
+    case QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK:
+      return {
+          true,
+          {static_cast<uint64_t>(QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK)}};
+    case QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM)}};
+    case QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG)}};
+    case QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR:
+      return {
+          true,
+          {static_cast<uint64_t>(QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR)}};
+    case QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED)}};
+    case QUIC_TOO_MANY_STREAM_DATA_INTERVALS:
+      return {true,
+              {static_cast<uint64_t>(QUIC_TOO_MANY_STREAM_DATA_INTERVALS)}};
+    case QUIC_STREAM_SEQUENCER_INVALID_STATE:
+      return {true,
+              {static_cast<uint64_t>(QUIC_STREAM_SEQUENCER_INVALID_STATE)}};
+    case QUIC_TOO_MANY_SESSIONS_ON_SERVER:
+      return {true, {static_cast<uint64_t>(QUIC_TOO_MANY_SESSIONS_ON_SERVER)}};
+    case QUIC_STREAM_LENGTH_OVERFLOW:
+      return {true, {static_cast<uint64_t>(QUIC_STREAM_LENGTH_OVERFLOW)}};
+    case QUIC_INVALID_MAX_DATA_FRAME_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_MAX_DATA_FRAME_DATA)}};
+    case QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA:
+      return {true,
+              {static_cast<uint64_t>(QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA)}};
+    case QUIC_MAX_STREAMS_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_MAX_STREAMS_DATA)}};
+    case QUIC_STREAMS_BLOCKED_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_STREAMS_BLOCKED_DATA)}};
+    case QUIC_INVALID_STREAM_BLOCKED_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_STREAM_BLOCKED_DATA)}};
+    case QUIC_INVALID_NEW_CONNECTION_ID_DATA:
+      return {true,
+              {static_cast<uint64_t>(QUIC_INVALID_NEW_CONNECTION_ID_DATA)}};
+    case QUIC_INVALID_STOP_SENDING_FRAME_DATA:
+      return {true,
+              {static_cast<uint64_t>(QUIC_INVALID_STOP_SENDING_FRAME_DATA)}};
+    case QUIC_INVALID_PATH_CHALLENGE_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_PATH_CHALLENGE_DATA)}};
+    case QUIC_INVALID_PATH_RESPONSE_DATA:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_PATH_RESPONSE_DATA)}};
+    case IETF_QUIC_PROTOCOL_VIOLATION:
+      return {true, {static_cast<uint64_t>(IETF_QUIC_PROTOCOL_VIOLATION)}};
+    case QUIC_INVALID_NEW_TOKEN:
+      return {true, {static_cast<uint64_t>(QUIC_INVALID_NEW_TOKEN)}};
+    case QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM)}};
+    case QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM)}};
+    case QUIC_INVALID_RETIRE_CONNECTION_ID_DATA:
+      return {true,
+              {static_cast<uint64_t>(QUIC_INVALID_RETIRE_CONNECTION_ID_DATA)}};
+    case QUIC_STREAMS_BLOCKED_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_STREAMS_BLOCKED_ERROR)}};
+    case QUIC_MAX_STREAMS_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_MAX_STREAMS_ERROR)}};
+    case QUIC_HTTP_DECODER_ERROR:
+      return {true, {static_cast<uint64_t>(QUIC_HTTP_DECODER_ERROR)}};
+    case QUIC_STALE_CONNECTION_CANCELLED:
+      return {true, {static_cast<uint64_t>(QUIC_STALE_CONNECTION_CANCELLED)}};
+    case QUIC_IETF_GQUIC_ERROR_MISSING:
+      return {true, {static_cast<uint64_t>(QUIC_IETF_GQUIC_ERROR_MISSING)}};
+    case QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM:
+      return {true,
+              {static_cast<uint64_t>(
+                  QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM)}};
+    case QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES:
+      return {true,
+              {static_cast<uint64_t>(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES)}};
+    case QUIC_LAST_ERROR:
+      return {false, {static_cast<uint64_t>(QUIC_LAST_ERROR)}};
+  }
+  // If it's an unknown code, indicate it's an application error code.
+  return {false, {NO_IETF_QUIC_ERROR}};
+}
+
 }  // namespace quic
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index c3f70e7..8a5434e 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -523,6 +523,21 @@
     std::ostream& os,
     const QuicIetfTransportErrorCodes& c);
 
+// Returns the mapping of the QuicErrorCode to an IETF TransportErrorCode. If
+// first element of the pair is false, it means that an IETF Application Close
+// should be done instead.
+
+struct QuicErrorCodeToIetfMapping {
+  bool is_transport_close_;
+  union {
+    uint64_t application_error_code_;
+    QuicIetfTransportErrorCodes transport_error_code_;
+  };
+};
+
+QUIC_EXPORT_PRIVATE QuicErrorCodeToIetfMapping
+QuicErrorCodeToTransportErrorCode(QuicErrorCode error);
+
 // Please note, this value cannot used directly for packet serialization.
 enum QuicLongHeaderType : uint8_t {
   VERSION_NEGOTIATION,