Add datatypes needed for IETF CONNECTION_CLOSE support.

This CL adds datatypes that are needed to support IETF QUIC's CONNECTION_CLOSE.
The types are
 - union QuicConnectionCloseErrorCode: a union that allows the frame's error
   code field to be accessed using the correct type (QuicErrorCode,
   QuicIetfTransportErrorCode, or uint16_t), and
 - enum QuicConnectionCloseType: indicates the type of connection close
   (Google-QUIC, IETF QUIC Connection Close/Application, or IETF QUIC
   Connection Close/Transport).

QuicConnectionCloseFrame is updated to include these fields.
QuicFramer is updated to set them to reasonable defaults/etc.
Code that refers to QuicConnectionCloseFrame::error_code has been
modified to get the error code value from the QuicErrorCode member
of the QuicConnectionCloseErrorCode union.

There are no logic or functional changes.  Additional CLs will add the necessary functionality.

gfe-relnote: Not flag protected, all code changes due to field name/etc changes.

ENUM_VALUE_OK=This value change is OK, it is the maximum value/limit of the enum

The design, including rationale and approximate plan for Cls is in
https://docs.google.com/document/d/1LB1Dhyw7tVLmFFEqmW6_xxnNiExNtlShJXrNU3CDM0M/edit?usp=sharing

PiperOrigin-RevId: 242647040
Change-Id: I3e45879777d83e7a8aeb2e19257b8bafcd0101bd
diff --git a/quic/core/frames/quic_connection_close_frame.cc b/quic/core/frames/quic_connection_close_frame.cc
index 55d356b..c4adf56 100644
--- a/quic/core/frames/quic_connection_close_frame.cc
+++ b/quic/core/frames/quic_connection_close_frame.cc
@@ -7,28 +7,74 @@
 namespace quic {
 
 QuicConnectionCloseFrame::QuicConnectionCloseFrame()
-    : error_code(QUIC_NO_ERROR), frame_type(0) {}
+    : close_type(UNINITIALIZED),
+      quic_error_code(QUIC_NO_ERROR),
+      extracted_error_code(QUIC_IETF_GQUIC_ERROR_MISSING),
+      transport_close_frame_type(0) {}
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame(QuicErrorCode error_code)
+    : close_type(UNINITIALIZED),
+      quic_error_code(error_code),
+      extracted_error_code(QUIC_IETF_GQUIC_ERROR_MISSING),
+      transport_close_frame_type(0) {}
 
 QuicConnectionCloseFrame::QuicConnectionCloseFrame(QuicErrorCode error_code,
                                                    std::string error_details)
-    : error_code(error_code),
+    : close_type(UNINITIALIZED),
+      quic_error_code(error_code),
+      extracted_error_code(QUIC_IETF_GQUIC_ERROR_MISSING),
       error_details(std::move(error_details)),
-      frame_type(0) {}
+      transport_close_frame_type(0) {}
 
 QuicConnectionCloseFrame::QuicConnectionCloseFrame(
-    QuicIetfTransportErrorCodes ietf_error_code,
+    QuicIetfTransportErrorCodes transport_error_code,
+    QuicErrorCode extracted_error_code,
     std::string error_details,
-    uint64_t frame_type)
-    : ietf_error_code(ietf_error_code),
+    uint64_t transport_close_frame_type)
+    : close_type(UNINITIALIZED),
+      transport_error_code(transport_error_code),
+      extracted_error_code(extracted_error_code),
       error_details(std::move(error_details)),
-      frame_type(frame_type) {}
+      transport_close_frame_type(transport_close_frame_type) {}
 
 std::ostream& operator<<(
     std::ostream& os,
     const QuicConnectionCloseFrame& connection_close_frame) {
-  os << "{ error_code: " << connection_close_frame.error_code
-     << ", error_details: '" << connection_close_frame.error_details
-     << "', frame_type: " << connection_close_frame.frame_type << "}\n";
+  os << "{ Close type: " << connection_close_frame.close_type
+     << ", error_code: "
+     << ((connection_close_frame.close_type ==
+          IETF_QUIC_TRANSPORT_CONNECTION_CLOSE)
+             ? connection_close_frame.transport_error_code
+             : ((connection_close_frame.close_type ==
+                 IETF_QUIC_APPLICATION_CONNECTION_CLOSE)
+                    ? connection_close_frame.application_error_code
+                    : connection_close_frame.quic_error_code))
+     << ", extracted_error_code: "
+     << connection_close_frame.extracted_error_code << ", error_details: '"
+     << connection_close_frame.error_details
+     << "', frame_type: " << connection_close_frame.transport_close_frame_type
+     << "}\n";
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicConnectionCloseType type) {
+  switch (type) {
+    case UNINITIALIZED:
+      os << "UNINITIALIZED";
+      break;
+    case GOOGLE_QUIC_CONNECTION_CLOSE:
+      os << "GOOGLE_QUIC_CONNECTION_CLOSE";
+      break;
+    case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE:
+      os << "IETF_QUIC_TRANSPORT_CONNECTION_CLOSE";
+      break;
+    case IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
+      os << "IETF_QUIC_APPLICATION_CONNECTION_CLOSE";
+      break;
+    default:
+      os << "Unknown: " << static_cast<int>(type);
+      break;
+  }
   return os;
 }
 
diff --git a/quic/core/frames/quic_connection_close_frame.h b/quic/core/frames/quic_connection_close_frame.h
index b4be56b..4d0cff7 100644
--- a/quic/core/frames/quic_connection_close_frame.h
+++ b/quic/core/frames/quic_connection_close_frame.h
@@ -14,10 +14,26 @@
 
 namespace quic {
 
+// There are three different forms of CONNECTION_CLOSE. UNINITIALIZED is
+// included explicitly for the QuicConnectionCloseFrame so that when the object
+// is constructed, a valid enumeration, indicating that the type is not known,
+// can be set.
+typedef enum QuicConnectionCloseType {
+  UNINITIALIZED = 0,
+  GOOGLE_QUIC_CONNECTION_CLOSE = 1,
+  IETF_QUIC_TRANSPORT_CONNECTION_CLOSE = 2,
+  IETF_QUIC_APPLICATION_CONNECTION_CLOSE = 3
+} QuicConnectionCloseType;
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+    std::ostream& os,
+    const QuicConnectionCloseType type);
+
 struct QUIC_EXPORT_PRIVATE QuicConnectionCloseFrame {
   QuicConnectionCloseFrame();
+  QuicConnectionCloseFrame(QuicErrorCode error_code);
   QuicConnectionCloseFrame(QuicErrorCode error_code, std::string error_details);
-  QuicConnectionCloseFrame(QuicIetfTransportErrorCodes ietf_error_code,
+  QuicConnectionCloseFrame(QuicIetfTransportErrorCodes transport_error_code,
+                           QuicErrorCode extracted_error_code,
                            std::string error_details,
                            uint64_t frame_type);
 
@@ -25,20 +41,38 @@
       std::ostream& os,
       const QuicConnectionCloseFrame& c);
 
-  // Set error_code or ietf_error_code based on the transport version
-  // currently in use.
+  // Indicates whether the received CONNECTION_CLOSE frame is a Google QUIC
+  // CONNECTION_CLOSE, IETF QUIC CONNECTION_CLOSE
+  QuicConnectionCloseType close_type;
+
+  // This is the error field in the frame.
+  // The CONNECTION_CLOSE frame reports a 16-bit error code:
+  // - The transport error code as reported in a CONNECTION_CLOSE/Transport
+  //   frame,
+  // - An opaque 16-bit code as reported in CONNECTION_CLOSE/Application frames,
+  // - A QuicErrorCode, which is used in Google QUIC.
   union {
-    // IETF QUIC has a different set of error codes. Include both
-    // code-sets.
-    QuicErrorCode error_code;
-    QuicIetfTransportErrorCodes ietf_error_code;
+    QuicIetfTransportErrorCodes transport_error_code;
+    uint16_t application_error_code;
+    QuicErrorCode quic_error_code;
   };
+
+  // This error code is extracted from, or added to, the "QuicErrorCode:
+  // QUIC_...(123)" text in the error_details. It provides fine-grained
+  // information as to the source of the error.
+  QuicErrorCode extracted_error_code;
+
+  // String with additional error details. In some circumstances, we will add a
+  // "QuicErrorCode: QUIC_...(123)" string indicating a more specific internal
+  // error code.
   std::string error_details;
 
+  // The frame type present in the IETF transport connection close frame.
+  // Not populated for the Google QUIC or application connection close frames.
   // Contains the type of frame that triggered the connection close. Made a
   // uint64, as opposed to the QuicIetfFrameType, to support possible
   // extensions as well as reporting invalid frame types received from the peer.
-  uint64_t frame_type;
+  uint64_t transport_close_frame_type;
 };
 
 }  // namespace quic
diff --git a/quic/core/frames/quic_frames_test.cc b/quic/core/frames/quic_frames_test.cc
index 986b3c5..9c67af4 100644
--- a/quic/core/frames/quic_frames_test.cc
+++ b/quic/core/frames/quic_frames_test.cc
@@ -133,12 +133,14 @@
 
 TEST_F(QuicFramesTest, ConnectionCloseFrameToString) {
   QuicConnectionCloseFrame frame;
-  frame.error_code = QUIC_NETWORK_IDLE_TIMEOUT;
+  frame.quic_error_code = QUIC_NETWORK_IDLE_TIMEOUT;
   frame.error_details = "No recent network activity.";
   std::ostringstream stream;
   stream << frame;
   EXPECT_EQ(
-      "{ error_code: 25, error_details: 'No recent network activity.', "
+      "{ Close type: UNINITIALIZED, error_code: 25, extracted_error_code: 122, "
+      "error_details: 'No recent "
+      "network activity.', "
       "frame_type: 0"
       "}\n",
       stream.str());
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index c6f939e..822b269 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -1321,15 +1321,16 @@
     debug_visitor_->OnConnectionCloseFrame(frame);
   }
   QUIC_DLOG(INFO) << ENDPOINT << "Received ConnectionClose for connection: "
-                  << connection_id()
-                  << ", with error: " << QuicErrorCodeToString(frame.error_code)
-                  << " (" << frame.error_details << ")";
-  if (frame.error_code == QUIC_BAD_MULTIPATH_FLAG) {
+                  << connection_id() << ", with error: "
+                  << QuicErrorCodeToString(frame.quic_error_code) << " ("
+                  << frame.error_details << ")";
+  if (frame.close_type == GOOGLE_QUIC_CONNECTION_CLOSE &&
+      frame.quic_error_code == QUIC_BAD_MULTIPATH_FLAG) {
     QUIC_LOG_FIRST_N(ERROR, 10) << "Unexpected QUIC_BAD_MULTIPATH_FLAG error."
                                 << " last_received_header: " << last_header_
                                 << " encryption_level: " << encryption_level_;
   }
-  TearDownLocalConnectionState(frame.error_code, frame.error_details,
+  TearDownLocalConnectionState(frame.quic_error_code, frame.error_details,
                                ConnectionCloseSource::FROM_PEER);
   return connected_;
 }
@@ -3016,9 +3017,8 @@
       !GetUpdatedAckFrame().ack_frame->packets.Empty()) {
     SendAck();
   }
-  QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
-  frame->error_code = error;
-  frame->error_details = details;
+  QuicConnectionCloseFrame* frame =
+      new QuicConnectionCloseFrame(error, details);
   packet_generator_.AddControlFrame(QuicFrame(frame));
   packet_generator_.FlushAllQueuedFrames();
 }
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index f5a840a..b36f888 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1305,8 +1305,7 @@
       header.destination_connection_id_included = CONNECTION_ID_ABSENT;
     }
 
-    QuicConnectionCloseFrame qccf;
-    qccf.error_code = QUIC_PEER_GOING_AWAY;
+    QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY);
 
     QuicFrames frames;
     frames.push_back(QuicFrame(&qccf));
@@ -2368,7 +2367,7 @@
       writer_->connection_close_frames();
   EXPECT_EQ(1u, connection_close_frames.size());
   EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA,
-            connection_close_frames[0].error_code);
+            connection_close_frames[0].quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
@@ -7006,8 +7005,7 @@
   header.packet_number = QuicPacketNumber(1);
   header.version_flag = false;
 
-  QuicConnectionCloseFrame qccf;
-  qccf.error_code = QUIC_PEER_GOING_AWAY;
+  QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY);
 
   QuicFrames frames;
   frames.push_back(QuicFrame(frame1_));
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index 27ff2b8..8024394 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -136,9 +136,8 @@
   void CloseConnection(QuicErrorCode error_code,
                        const std::string& error_details,
                        bool ietf_quic) {
-    QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame;
-    frame->error_code = error_code;
-    frame->error_details = error_details;
+    QuicConnectionCloseFrame* frame =
+        new QuicConnectionCloseFrame(error_code, error_details);
     if (!creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)) {
       QUIC_BUG << "Unable to add frame to an empty packet";
       delete frame;
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index 215f527..a5bef73 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -156,6 +156,7 @@
     RETURN_STRING_LITERAL(QUIC_MAX_STREAM_ID_ERROR);
     RETURN_STRING_LITERAL(QUIC_HTTP_DECODER_ERROR);
     RETURN_STRING_LITERAL(QUIC_STALE_CONNECTION_CANCELLED);
+    RETURN_STRING_LITERAL(QUIC_IETF_GQUIC_ERROR_MISSING);
 
     RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
     // Intentionally have no default case, so we'll break the build
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index d3446a8..bbdb13f 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -328,8 +328,14 @@
   // Connection from stale host needs to be cancelled.
   QUIC_STALE_CONNECTION_CANCELLED = 121,
 
+  // A pseudo error, used as an extended error reason code in the error_details
+  // of IETF-QUIC CONNECTION_CLOSE frames. It is used in
+  // OnConnectionClosed upcalls to indicate that extended error information was
+  // not available in a received CONNECTION_CLOSE frame.
+  QUIC_IETF_GQUIC_ERROR_MISSING = 122,
+
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 122,
+  QUIC_LAST_ERROR = 123,
 };
 // QuicErrorCodes is encoded as a single octet on-the-wire.
 static_assert(static_cast<int>(QUIC_LAST_ERROR) <=
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index f170cbc..4fd6936 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -590,7 +590,7 @@
   if (version == QUIC_VERSION_99) {
     return QuicDataWriter::GetVarInt62Len(
                TruncatedErrorStringSize(frame.error_details)) +
-           QuicDataWriter::GetVarInt62Len(frame.frame_type) +
+           QuicDataWriter::GetVarInt62Len(frame.transport_close_frame_type) +
            kQuicFrameTypeSize + kQuicIetfQuicErrorCodeSize;
   }
   return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize;
@@ -2905,7 +2905,12 @@
         }
         case IETF_CONNECTION_CLOSE: {
           QuicConnectionCloseFrame frame;
-          if (!ProcessIetfConnectionCloseFrame(reader, &frame)) {
+          if (!ProcessIetfConnectionCloseFrame(
+                  reader,
+                  ((frame_type == IETF_CONNECTION_CLOSE)
+                       ? IETF_QUIC_TRANSPORT_CONNECTION_CLOSE
+                       : IETF_QUIC_APPLICATION_CONNECTION_CLOSE),
+                  &frame)) {
             return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA);
           }
           if (!visitor_->OnConnectionCloseFrame(frame)) {
@@ -3724,6 +3729,8 @@
 bool QuicFramer::ProcessConnectionCloseFrame(QuicDataReader* reader,
                                              QuicConnectionCloseFrame* frame) {
   uint32_t error_code;
+  frame->close_type = GOOGLE_QUIC_CONNECTION_CLOSE;
+
   if (!reader->ReadUInt32(&error_code)) {
     set_detailed_error("Unable to read connection close error code.");
     return false;
@@ -3734,7 +3741,7 @@
     error_code = QUIC_LAST_ERROR;
   }
 
-  frame->error_code = static_cast<QuicErrorCode>(error_code);
+  frame->quic_error_code = static_cast<QuicErrorCode>(error_code);
 
   QuicStringPiece error_details;
   if (!reader->ReadStringPiece16(&error_details)) {
@@ -5068,7 +5075,7 @@
   if (version_.transport_version == QUIC_VERSION_99) {
     return AppendIetfConnectionCloseFrame(frame, writer);
   }
-  uint32_t error_code = static_cast<uint32_t>(frame.error_code);
+  uint32_t error_code = static_cast<uint32_t>(frame.quic_error_code);
   if (!writer->WriteUInt32(error_code)) {
     return false;
   }
@@ -5180,11 +5187,12 @@
 bool QuicFramer::AppendIetfConnectionCloseFrame(
     const QuicConnectionCloseFrame& frame,
     QuicDataWriter* writer) {
-  if (!writer->WriteUInt16(static_cast<const uint16_t>(frame.error_code))) {
+  if (!writer->WriteUInt16(frame.application_error_code)) {
     set_detailed_error("Can not write connection close frame error code");
     return false;
   }
-  if (!writer->WriteVarInt62(frame.frame_type)) {
+
+  if (!writer->WriteVarInt62(frame.transport_close_frame_type)) {
     set_detailed_error("Writing frame type failed.");
     return false;
   }
@@ -5215,15 +5223,17 @@
 
 bool QuicFramer::ProcessIetfConnectionCloseFrame(
     QuicDataReader* reader,
+    QuicConnectionCloseType type,
     QuicConnectionCloseFrame* frame) {
+  frame->close_type = type;
   uint16_t code;
   if (!reader->ReadUInt16(&code)) {
     set_detailed_error("Unable to read connection close error code.");
     return false;
   }
-  frame->ietf_error_code = static_cast<QuicIetfTransportErrorCodes>(code);
+  frame->transport_error_code = static_cast<QuicIetfTransportErrorCodes>(code);
 
-  if (!reader->ReadVarInt62(&frame->frame_type)) {
+  if (!reader->ReadVarInt62(&frame->transport_close_frame_type)) {
     set_detailed_error("Unable to read connection close frame type.");
     return false;
   }
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index b431f14..84773b4 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -774,6 +774,7 @@
                               uint8_t frame_type,
                               QuicStreamFrame* frame);
   bool ProcessIetfConnectionCloseFrame(QuicDataReader* reader,
+                                       QuicConnectionCloseType type,
                                        QuicConnectionCloseFrame* frame);
   bool ProcessApplicationCloseFrame(QuicDataReader* reader,
                                     QuicApplicationCloseFrame* frame);
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 09b2023..dc6e231 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -4391,11 +4391,12 @@
       PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
 
   EXPECT_EQ(0u, visitor_.stream_frames_.size());
-
-  EXPECT_EQ(0x11, visitor_.connection_close_frame_.error_code);
+  EXPECT_EQ(0x11, static_cast<unsigned>(
+                      visitor_.connection_close_frame_.quic_error_code));
   EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details);
   if (framer_.transport_version() == QUIC_VERSION_99) {
-    EXPECT_EQ(0x1234u, visitor_.connection_close_frame_.frame_type);
+    EXPECT_EQ(0x1234u,
+              visitor_.connection_close_frame_.transport_close_frame_type);
   }
 
   ASSERT_EQ(0u, visitor_.ack_frames_.size());
@@ -7279,11 +7280,11 @@
 
   QuicConnectionCloseFrame close_frame;
   if (framer_.transport_version() == QUIC_VERSION_99) {
-    close_frame.ietf_error_code =
+    close_frame.transport_error_code =
         static_cast<QuicIetfTransportErrorCodes>(0x11);
-    close_frame.frame_type = 0x05;
+    close_frame.transport_close_frame_type = 0x05;
   } else {
-    close_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
+    close_frame.quic_error_code = static_cast<QuicErrorCode>(0x05060708);
   }
   close_frame.error_details = "because I can";
 
@@ -7406,10 +7407,10 @@
 
   QuicConnectionCloseFrame close_frame;
   if (framer_.transport_version() == QUIC_VERSION_99) {
-    close_frame.ietf_error_code = PROTOCOL_VIOLATION;  // value is 0x0a
-    EXPECT_EQ(0u, close_frame.frame_type);
+    close_frame.transport_error_code = PROTOCOL_VIOLATION;  // value is 0x0a
+    EXPECT_EQ(0u, close_frame.transport_close_frame_type);
   } else {
-    close_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
+    close_frame.quic_error_code = static_cast<QuicErrorCode>(0x05060708);
   }
   close_frame.error_details = std::string(2048, 'A');
   QuicFrames frames = {QuicFrame(&close_frame)};
diff --git a/quic/core/quic_ietf_framer_test.cc b/quic/core/quic_ietf_framer_test.cc
index c1ada6b..9c30e9c 100644
--- a/quic/core/quic_ietf_framer_test.cc
+++ b/quic/core/quic_ietf_framer_test.cc
@@ -798,9 +798,9 @@
   // empty string,
   std::string test_string = "Ich Bin Ein Jelly Donut?";
   QuicConnectionCloseFrame sent_frame;
-  sent_frame.error_code = static_cast<QuicErrorCode>(0);
+  sent_frame.quic_error_code = static_cast<QuicErrorCode>(0);
   sent_frame.error_details = test_string;
-  sent_frame.frame_type = 123;
+  sent_frame.transport_close_frame_type = 123;
   // write the frame to the packet buffer.
   EXPECT_TRUE(QuicFramerPeer::AppendIetfConnectionCloseFrame(
       &framer_, sent_frame, &writer));
@@ -814,11 +814,11 @@
   // a QuicConnectionCloseFrame to hold the results.
   QuicConnectionCloseFrame sink_frame;
 
-  EXPECT_TRUE(QuicFramerPeer::ProcessIetfConnectionCloseFrame(&framer_, &reader,
-                                                              &sink_frame));
+  EXPECT_TRUE(QuicFramerPeer::ProcessIetfConnectionCloseFrame(
+      &framer_, &reader, IETF_QUIC_TRANSPORT_CONNECTION_CLOSE, &sink_frame));
 
   // Now check that received == sent
-  EXPECT_EQ(sink_frame.error_code, static_cast<QuicErrorCode>(0));
+  EXPECT_EQ(sink_frame.quic_error_code, static_cast<QuicErrorCode>(0));
   EXPECT_EQ(sink_frame.error_details, test_string);
 }
 
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index aa2f6f7..f6d4f5f 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -559,9 +559,7 @@
 }
 
 TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) {
-  QuicConnectionCloseFrame frame;
-  frame.error_code = QUIC_NO_ERROR;
-  frame.error_details = "error";
+  QuicConnectionCloseFrame frame(QUIC_NO_ERROR, "error");
 
   QuicFrames frames;
   frames.push_back(QuicFrame(&frame));
diff --git a/quic/core/quic_packet_generator_test.cc b/quic/core/quic_packet_generator_test.cc
index 09abb31..cb22fd5 100644
--- a/quic/core/quic_packet_generator_test.cc
+++ b/quic/core/quic_packet_generator_test.cc
@@ -1345,11 +1345,10 @@
 // Regression test for b/31486443.
 TEST_F(QuicPacketGeneratorTest, ConnectionCloseFrameLargerThanPacketSize) {
   delegate_.SetCanWriteAnything();
-  QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
-  frame->error_code = QUIC_PACKET_WRITE_ERROR;
   char buf[2000] = {};
   QuicStringPiece error_details(buf, 2000);
-  frame->error_details = std::string(error_details);
+  QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(
+      QUIC_PACKET_WRITE_ERROR, std::string(error_details));
   generator_.AddControlFrame(QuicFrame(frame), /*bundle_ack=*/false);
   EXPECT_TRUE(generator_.HasQueuedFrames());
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
diff --git a/quic/core/quic_trace_visitor.cc b/quic/core/quic_trace_visitor.cc
index 728bab4..2c13b56 100644
--- a/quic/core/quic_trace_visitor.cc
+++ b/quic/core/quic_trace_visitor.cc
@@ -152,7 +152,7 @@
       frame_record->set_frame_type(quic_trace::CONNECTION_CLOSE);
 
       quic_trace::CloseInfo* info = frame_record->mutable_close_info();
-      info->set_error_code(frame.connection_close_frame->error_code);
+      info->set_error_code(frame.connection_close_frame->quic_error_code);
       info->set_reason_phrase(frame.connection_close_frame->error_details);
       break;
     }
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index 16bf52d..6a3af07 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -50,4 +50,53 @@
 MessageResult::MessageResult(MessageStatus status, QuicMessageId message_id)
     : status(status), message_id(message_id) {}
 
+std::ostream& operator<<(std::ostream& os,
+                         const QuicIetfTransportErrorCodes& c) {
+  if (c >= 0xff00) {
+    os << "Private value: " << c;
+    return os;
+  }
+
+  switch (c) {
+    case NO_IETF_QUIC_ERROR:
+      os << "NO_IETF_QUIC_ERROR";
+      break;
+    case INTERNAL_ERROR:
+      os << "INTERNAL_ERROR";
+      break;
+    case SERVER_BUSY_ERROR:
+      os << "SERVER_BUSY_ERROR";
+      break;
+    case FLOW_CONTROL_ERROR:
+      os << "FLOW_CONTROL_ERROR";
+      break;
+    case STREAM_LIMIT_ERROR:
+      os << "STREAM_LIMIT_ERROR";
+      break;
+    case STREAM_STATE_ERROR:
+      os << "STREAM_STATE_ERROR";
+      break;
+    case FINAL_SIZE_ERROR:
+      os << "FINAL_SIZE_ERROR";
+      break;
+    case FRAME_ENCODING_ERROR:
+      os << "FRAME_ENCODING_ERROR";
+      break;
+    case TRANSPORT_PARAMETER_ERROR:
+      os << "TRANSPORT_PARAMETER_ERROR";
+      break;
+    case VERSION_NEGOTIATION_ERROR:
+      os << "VERSION_NEGOTIATION_ERROR";
+      break;
+    case PROTOCOL_VIOLATION:
+      os << "PROTOCOL_VIOLATION";
+      break;
+    case INVALID_MIGRATION:
+      os << "INVALID_MIGRATION";
+      break;
+      // No default -- compiler will catch any adds/changes then.
+  }
+  return os;
+}
+
 }  // namespace quic
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 54d1628..aeedbb5 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/core/quic_packet_number.h"
 #include "net/third_party/quiche/src/quic/core/quic_time.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
@@ -246,10 +247,10 @@
   IETF_RETIRE_CONNECTION_ID = 0x19,
   IETF_PATH_CHALLENGE = 0x1a,
   IETF_PATH_RESPONSE = 0x1b,
+  // Both of the following are "Connection Close" frames,
+  // the first signals transport-layer errors, the second application-layer
+  // errors.
   IETF_CONNECTION_CLOSE = 0x1c,
-  // 0x1d reserved, a flag setting for IETF_CONNECTION_CLOSE
-  // TODO(fkastenholz): IETF_APPLICATION_CLOSE disappears in the next version of
-  // QUIC. It is retained temporarily
   IETF_APPLICATION_CLOSE = 0x1d,
 
   // MESSAGE frame type is not yet determined, use 0x2x temporarily to give
@@ -483,16 +484,18 @@
   INTERNAL_ERROR = 0x1,
   SERVER_BUSY_ERROR = 0x2,
   FLOW_CONTROL_ERROR = 0x3,
-  STREAM_ID_ERROR = 0x4,
+  STREAM_LIMIT_ERROR = 0x4,
   STREAM_STATE_ERROR = 0x5,
-  FINAL_OFFSET_ERROR = 0x6,
+  FINAL_SIZE_ERROR = 0x6,
   FRAME_ENCODING_ERROR = 0x7,
   TRANSPORT_PARAMETER_ERROR = 0x8,
   VERSION_NEGOTIATION_ERROR = 0x9,
   PROTOCOL_VIOLATION = 0xA,
   INVALID_MIGRATION = 0xC,
-  FRAME_ERROR_base = 0x100,  // add frame type to this base
 };
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+    std::ostream& os,
+    const QuicIetfTransportErrorCodes& c);
 
 // Please note, this value cannot used directly for packet serialization.
 enum QuicLongHeaderType : uint8_t {
diff --git a/quic/test_tools/quic_framer_peer.cc b/quic/test_tools/quic_framer_peer.cc
index 5f8068e..9b7da0b 100644
--- a/quic/test_tools/quic_framer_peer.cc
+++ b/quic/test_tools/quic_framer_peer.cc
@@ -112,8 +112,9 @@
 bool QuicFramerPeer::ProcessIetfConnectionCloseFrame(
     QuicFramer* framer,
     QuicDataReader* reader,
+    QuicConnectionCloseType type,
     QuicConnectionCloseFrame* frame) {
-  return framer->ProcessIetfConnectionCloseFrame(reader, frame);
+  return framer->ProcessIetfConnectionCloseFrame(reader, type, frame);
 }
 
 // static
diff --git a/quic/test_tools/quic_framer_peer.h b/quic/test_tools/quic_framer_peer.h
index e5c5e00..2db5af0 100644
--- a/quic/test_tools/quic_framer_peer.h
+++ b/quic/test_tools/quic_framer_peer.h
@@ -61,6 +61,7 @@
       QuicDataWriter* writer);
   static bool ProcessIetfConnectionCloseFrame(QuicFramer* framer,
                                               QuicDataReader* reader,
+                                              QuicConnectionCloseType type,
                                               QuicConnectionCloseFrame* frame);
   static bool ProcessApplicationCloseFrame(QuicFramer* framer,
                                            QuicDataReader* reader,