Change parameter list of QuicConnection::OnConnectionClosed upcall

Parameter list was the individual fields of the Connection Close frame.
Now, the QuicConnectionCloseFrame is passed up. This object contains
all the parameters of the frame.

gfe-relnote: N/A, not flag protected. This change just rearranges function parameters.
PiperOrigin-RevId: 254406883
Change-Id: I6c11b7d7a09d5e765f385d8d54a56c02687e7894
diff --git a/quic/core/http/quic_server_session_base.cc b/quic/core/http/quic_server_session_base.cc
index 8670e29..1055898 100644
--- a/quic/core/http/quic_server_session_base.cc
+++ b/quic/core/http/quic_server_session_base.cc
@@ -80,10 +80,10 @@
   }
 }
 
-void QuicServerSessionBase::OnConnectionClosed(QuicErrorCode error,
-                                               const std::string& error_details,
-                                               ConnectionCloseSource source) {
-  QuicSession::OnConnectionClosed(error, error_details, source);
+void QuicServerSessionBase::OnConnectionClosed(
+    const QuicConnectionCloseFrame& frame,
+    ConnectionCloseSource source) {
+  QuicSession::OnConnectionClosed(frame, source);
   // In the unlikely event we get a connection close while doing an asynchronous
   // crypto event, make sure we cancel the callback.
   if (crypto_stream_ != nullptr) {
diff --git a/quic/core/http/quic_server_session_base.h b/quic/core/http/quic_server_session_base.h
index 8f071f3..672bb0c 100644
--- a/quic/core/http/quic_server_session_base.h
+++ b/quic/core/http/quic_server_session_base.h
@@ -45,8 +45,7 @@
   QuicServerSessionBase& operator=(const QuicServerSessionBase&) = delete;
 
   // Override the base class to cancel any ongoing asychronous crypto.
-  void OnConnectionClosed(QuicErrorCode error,
-                          const std::string& error_details,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                           ConnectionCloseSource source) override;
 
   // Sends a server config update to the client, containing new bandwidth
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index c627487..348e5d3 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -388,12 +388,11 @@
                                                connection_close_behavior);
           })));
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
-  EXPECT_CALL(*session_, OnConnectionClosed(_, _, _))
-      .WillOnce(
-          Invoke([this](QuicErrorCode error, const std::string& error_details,
-                        ConnectionCloseSource source) {
-            session_->ReallyOnConnectionClosed(error, error_details, source);
-          }));
+  EXPECT_CALL(*session_, OnConnectionClosed(_, _))
+      .WillOnce(Invoke([this](const QuicConnectionCloseFrame& frame,
+                              ConnectionCloseSource source) {
+        session_->ReallyOnConnectionClosed(frame, source);
+      }));
   EXPECT_CALL(*session_, SendRstStream(_, _, _));
   EXPECT_CALL(*session_, SendRstStream(_, _, _));
 
@@ -1801,12 +1800,11 @@
                                                connection_close_behavior);
           })));
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
-  EXPECT_CALL(*session_, OnConnectionClosed(_, _, _))
-      .WillOnce(
-          Invoke([this](QuicErrorCode error, const std::string& error_details,
-                        ConnectionCloseSource source) {
-            session_->ReallyOnConnectionClosed(error, error_details, source);
-          }));
+  EXPECT_CALL(*session_, OnConnectionClosed(_, _))
+      .WillOnce(Invoke([this](const QuicConnectionCloseFrame& frame,
+                              ConnectionCloseSource source) {
+        session_->ReallyOnConnectionClosed(frame, source);
+      }));
   EXPECT_CALL(*session_, SendRstStream(_, _, _));
   EXPECT_CALL(*session_, SendRstStream(_, _, _));
   stream_->OnStreamFrame(frame);
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index fd1e5e8..485b4c9 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2755,7 +2755,10 @@
   FlushPackets();
   connected_ = false;
   DCHECK(visitor_ != nullptr);
-  visitor_->OnConnectionClosed(error, error_details, source);
+  // TODO(fkastenholz): When the IETF Transport Connection Close information
+  // gets plumbed in, expand this constructor to include that information.
+  QuicConnectionCloseFrame frame(error, error_details);
+  visitor_->OnConnectionClosed(frame, source);
   if (debug_visitor_ != nullptr) {
     debug_visitor_->OnConnectionClosed(error, error_details, source);
   }
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 0e7faff..947209e 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -124,8 +124,7 @@
 
   // Called when the connection is closed either locally by the framer, or
   // remotely by the peer.
-  virtual void OnConnectionClosed(QuicErrorCode error,
-                                  const std::string& error_details,
+  virtual void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                                   ConnectionCloseSource source) = 0;
 
   // Called when the connection failed to write because the socket was blocked.
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index f78e0c2..5201b5a 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -902,6 +902,17 @@
 }
 
 class QuicConnectionTest : public QuicTestWithParam<TestParams> {
+ public:
+  // For tests that do silent connection closes, no such packet is generated. In
+  // order to verify the contents of the OnConnectionClosed upcall, EXPECTs
+  // should invoke this method, saving the frame, and then the test can verify
+  // the contents.
+  void SaveConnectionCloseFrame(const QuicConnectionCloseFrame& frame,
+                                ConnectionCloseSource /*source*/) {
+    saved_connection_close_frame_ = frame;
+    connection_close_frame_count_++;
+  }
+
  protected:
   QuicConnectionTest()
       : connection_id_(TestConnectionId()),
@@ -936,7 +947,8 @@
         crypto_frame_(ENCRYPTION_INITIAL, 0, QuicStringPiece(data1)),
         packet_number_length_(PACKET_4BYTE_PACKET_NUMBER),
         connection_id_included_(CONNECTION_ID_PRESENT),
-        notifier_(&connection_) {
+        notifier_(&connection_),
+        connection_close_frame_count_(0) {
     SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
     connection_.set_defer_send_in_response_to_packets(GetParam().ack_response ==
                                                       AckResponse::kDefer);
@@ -1484,15 +1496,19 @@
 
   void TriggerConnectionClose() {
     // Send an erroneous packet to close the connection.
-    EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
-                                             ConnectionCloseSource::FROM_SELF));
+    EXPECT_CALL(visitor_,
+                OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+        .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
+
     EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
     // Triggers a connection by receiving ACK of unsent packet.
     QuicAckFrame frame = InitAckFrame(10000);
     ProcessAckPacket(1, &frame);
-
     EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
                  nullptr);
+    EXPECT_EQ(1, connection_close_frame_count_);
+    EXPECT_EQ(QUIC_INVALID_ACK_DATA,
+              saved_connection_close_frame_.quic_error_code);
   }
 
   void BlockOnNextWrite() {
@@ -1541,6 +1557,16 @@
            p.version == AllSupportedVersions()[0] && p.no_stop_waiting;
   }
 
+  void TestConnectionCloseQuicErrorCode(QuicErrorCode expected_code) {
+    // Not strictly needed for this test, but is commonly done.
+    EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
+                 nullptr);
+    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);
+  }
+
   QuicConnectionId connection_id_;
   QuicFramer framer_;
 
@@ -1569,6 +1595,9 @@
   QuicConnectionIdIncluded connection_id_included_;
 
   SimpleSessionNotifier notifier_;
+
+  QuicConnectionCloseFrame saved_connection_close_frame_;
+  int connection_close_frame_count_;
 };
 
 // Run all end to end tests with all supported versions.
@@ -1631,9 +1660,10 @@
   host.FromString("1.1.1.1");
   QuicSocketAddress self_address(host, 123);
   EXPECT_CALL(visitor_, AllowSelfAddressChange()).WillOnce(Return(false));
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_ERROR_MIGRATING_ADDRESS, _, _));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
   ProcessFramePacketWithAddresses(frame, self_address, kPeerAddress);
   EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_ERROR_MIGRATING_ADDRESS);
 }
 
 TEST_P(QuicConnectionTest, AllowSelfAddressChangeToMappedIpv4AddressAtServer) {
@@ -1912,12 +1942,16 @@
   connection_.SendConnectivityProbingPacket(writer_.get(),
                                             connection_.peer_address());
 
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INTERNAL_ERROR,
-                                           "Packet written out of order.",
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_QUIC_BUG(connection_.OnCanWrite(),
                   "Attempt to write packet:1 after:2");
   EXPECT_FALSE(connection_.connected());
+  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);
 }
 
 TEST_P(QuicConnectionTest, DiscardQueuedPacketsAfterConnectionClose) {
@@ -1925,7 +1959,7 @@
   {
     InSequence seq;
     EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
-    EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1);
+    EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(1);
   }
 
   set_perspective(Perspective::IS_CLIENT);
@@ -2478,17 +2512,11 @@
   // Process an unencrypted packet from the non-crypto stream.
   frame1_.stream_id = 3;
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_QUIC_PEER_BUG(ProcessDataPacketAtLevel(1, false, ENCRYPTION_INITIAL),
                        "");
-  EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
-               nullptr);
-  const std::vector<QuicConnectionCloseFrame>& connection_close_frames =
-      writer_->connection_close_frames();
-  ASSERT_EQ(1u, connection_close_frames.size());
-  EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA,
-            connection_close_frames[0].quic_error_code);
+  TestConnectionCloseQuicErrorCode(QUIC_UNENCRYPTED_STREAM_DATA);
 }
 
 TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
@@ -2729,10 +2757,13 @@
   QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 7);
   if (!GetParam().no_stop_waiting) {
     EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
-    EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, _,
-                                             ConnectionCloseSource::FROM_SELF));
+    EXPECT_CALL(visitor_,
+                OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   }
   ProcessStopWaitingPacket(InitStopWaitingFrame(1));
+  if (!GetParam().no_stop_waiting) {
+    TestConnectionCloseQuicErrorCode(QUIC_INVALID_STOP_WAITING_DATA);
+  }
 }
 
 TEST_P(QuicConnectionTest, TooManySentPackets) {
@@ -2750,12 +2781,12 @@
   // Ack packet 1, which leaves more than the limit outstanding.
   EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
   EXPECT_CALL(visitor_,
-              OnConnectionClosed(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, _,
-                                 ConnectionCloseSource::FROM_SELF));
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
 
   // Nack the first packet and ack the rest, leaving a huge gap.
   QuicAckFrame frame1 = ConstructAckFrame(num_packets, 1);
   ProcessAckPacket(&frame1);
+  TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS);
 }
 
 TEST_P(QuicConnectionTest, LargestObservedLower) {
@@ -2775,22 +2806,26 @@
     EXPECT_CALL(visitor_, OnCanWrite());
   } else {
     // Now change it to 1, and it should cause a connection error.
-    EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
-                                             ConnectionCloseSource::FROM_SELF));
+    EXPECT_CALL(visitor_,
+                OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
     EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
   }
   ProcessAckPacket(&frame1);
+  if (!GetQuicReloadableFlag(quic_tolerate_reneging)) {
+    TestConnectionCloseQuicErrorCode(QUIC_INVALID_ACK_DATA);
+  }
 }
 
 TEST_P(QuicConnectionTest, AckUnsentData) {
   // Ack a packet which has not been sent.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   QuicAckFrame frame = InitAckFrame(1);
   EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
   ProcessAckPacket(&frame);
+  TestConnectionCloseQuicErrorCode(QUIC_INVALID_ACK_DATA);
 }
 
 TEST_P(QuicConnectionTest, BasicSending) {
@@ -3685,8 +3720,11 @@
 TEST_P(QuicConnectionTest, DoNotAddToWriteBlockedListAfterDisconnect) {
   writer_->SetBatchMode(true);
   EXPECT_TRUE(connection_.connected());
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  // Have to explicitly grab the OnConnectionClosed frame and check
+  // its parameters because this is a silent connection close and the
+  // frame is not also transmitted to the peer.
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
 
   EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0);
 
@@ -3698,6 +3736,9 @@
     EXPECT_FALSE(connection_.connected());
     writer_->SetWriteBlocked();
   }
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_PEER_GOING_AWAY,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, AddToWriteBlockedListIfBlockedOnFlushPackets) {
@@ -4364,8 +4405,8 @@
       QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
   EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
 
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   // Simulate the timeout alarm firing.
   clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1));
   connection_.GetTimeoutAlarm()->Fire();
@@ -4379,6 +4420,7 @@
   EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, IdleTimeoutAfterFirstSentPacket) {
@@ -4407,7 +4449,7 @@
 
   // Simulate the timeout alarm firing, the connection should not be closed as
   // a new packet has been sent.
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0);
   QuicTime::Delta delay = initial_ddl - clock_.ApproximateNow();
   clock_.AdvanceTime(delay);
   connection_.GetTimeoutAlarm()->Fire();
@@ -4418,8 +4460,8 @@
 
   // Simulate the timeout alarm firing again, the connection now should be
   // closed.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   clock_.AdvanceTime(new_ddl - clock_.ApproximateNow());
   connection_.GetTimeoutAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
@@ -4430,6 +4472,7 @@
   EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, IdleTimeoutAfterSendTwoPackets) {
@@ -4460,8 +4503,8 @@
   EXPECT_EQ(QuicPacketNumber(2u), last_packet);
 
   // Simulate the timeout alarm firing, the connection will be closed.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   clock_.AdvanceTime(initial_ddl - clock_.ApproximateNow());
   connection_.GetTimeoutAlarm()->Fire();
 
@@ -4473,6 +4516,7 @@
   EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, HandshakeTimeout) {
@@ -4504,8 +4548,8 @@
 
   clock_.AdvanceTime(timeout - QuicTime::Delta::FromSeconds(2));
 
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_HANDSHAKE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   // Simulate the timeout alarm firing.
   connection_.GetTimeoutAlarm()->Fire();
 
@@ -4516,6 +4560,7 @@
   EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
   EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+  TestConnectionCloseQuicErrorCode(QUIC_HANDSHAKE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, PingAfterSend) {
@@ -4935,7 +4980,7 @@
                        nullptr);
   EXPECT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
 
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
   connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason",
                               ConnectionCloseBehavior::SILENT_CLOSE);
   EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
@@ -4980,14 +5025,15 @@
             connection_.GetTimeoutAlarm()->deadline());
 
   // This time, we should time out.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   clock_.AdvanceTime(five_ms);
   EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow());
   connection_.GetTimeoutAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
   EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfterRetransmission) {
@@ -5054,8 +5100,8 @@
             connection_.GetTimeoutAlarm()->deadline().ToDebuggingValue());
 
   // This time, we should time out.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   clock_.AdvanceTime(final_timeout - clock_.Now());
   EXPECT_EQ(connection_.GetTimeoutAlarm()->deadline(), clock_.Now());
@@ -5063,6 +5109,7 @@
   connection_.GetTimeoutAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
   EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, NewTimeoutAfterSendSilentClose) {
@@ -5123,13 +5170,20 @@
             connection_.GetTimeoutAlarm()->deadline());
 
   // This time, we should time out.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  // This results in a SILENT_CLOSE, so the writer will not be invoked
+  // and will not save the frame. Grab the frame from OnConnectionClosed
+  // directly.
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
+
   clock_.AdvanceTime(five_ms);
   EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow());
   connection_.GetTimeoutAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
   EXPECT_FALSE(connection_.connected());
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_NETWORK_IDLE_TIMEOUT,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseAndTLP) {
@@ -5178,14 +5232,15 @@
   connection_.GetRetransmissionAlarm()->Fire();
 
   // This time, we should time out and send a connection close due to the TLP.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   clock_.AdvanceTime(connection_.GetTimeoutAlarm()->deadline() -
                      clock_.ApproximateNow() + five_ms);
   connection_.GetTimeoutAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
   EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseWithOpenStreams) {
@@ -5232,14 +5287,15 @@
       .WillRepeatedly(Return(true));
 
   // This time, we should time out and send a connection close due to the TLP.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   clock_.AdvanceTime(connection_.GetTimeoutAlarm()->deadline() -
                      clock_.ApproximateNow() + five_ms);
   connection_.GetTimeoutAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
   EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfterReceive) {
@@ -5282,14 +5338,15 @@
             connection_.GetTimeoutAlarm()->deadline());
 
   // This time, we should time out.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   clock_.AdvanceTime(five_ms);
   EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow());
   connection_.GetTimeoutAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
   EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfterReceiveNotSendWhenUnacked) {
@@ -5339,8 +5396,8 @@
 
   // Now, send packets while advancing the time and verify that the connection
   // eventually times out.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
   for (int i = 0; i < 100 && connection_.connected(); ++i) {
     QUIC_LOG(INFO) << "sending data packet";
@@ -5352,6 +5409,7 @@
   }
   EXPECT_FALSE(connection_.connected());
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+  TestConnectionCloseQuicErrorCode(QUIC_NETWORK_IDLE_TIMEOUT);
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfter5ClientRTOs) {
@@ -5380,12 +5438,13 @@
   EXPECT_EQ(2u, connection_.sent_packet_manager().GetConsecutiveTlpCount());
   EXPECT_EQ(4u, connection_.sent_packet_manager().GetConsecutiveRtoCount());
   // This time, we should time out.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_TOO_MANY_RTOS, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
   connection_.GetRetransmissionAlarm()->Fire();
   EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
   EXPECT_FALSE(connection_.connected());
+  TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS);
 }
 
 TEST_P(QuicConnectionTest, SendScheduler) {
@@ -5404,7 +5463,7 @@
   // Test that the connection does not crash when it fails to send the first
   // packet at which point self_address_ might be uninitialized.
   QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT);
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1);
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(1);
   std::unique_ptr<QuicPacket> packet =
       ConstructDataPacket(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
   QuicPacketCreatorPeer::SetPacketNumber(creator_, 1);
@@ -6485,16 +6544,19 @@
 TEST_P(QuicConnectionTest, NoAckSentForClose) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   ProcessPacket(1);
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
-                                           ConnectionCloseSource::FROM_PEER));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_PEER))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
   ProcessClosePacket(2);
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_PEER_GOING_AWAY,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, SendWhenDisconnected) {
   EXPECT_TRUE(connection_.connected());
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason",
                               ConnectionCloseBehavior::SILENT_CLOSE);
   EXPECT_FALSE(connection_.connected());
@@ -6505,6 +6567,9 @@
       .Times(0);
   connection_.SendPacket(ENCRYPTION_INITIAL, 1, std::move(packet),
                          HAS_RETRANSMITTABLE_DATA, false, false);
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_PEER_GOING_AWAY,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, SendConnectivityProbingWhenDisconnected) {
@@ -6514,8 +6579,8 @@
   }
 
   EXPECT_TRUE(connection_.connected());
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason",
                               ConnectionCloseBehavior::SILENT_CLOSE);
   EXPECT_FALSE(connection_.connected());
@@ -6528,6 +6593,9 @@
                       writer_.get(), connection_.peer_address()),
                   "Not sending connectivity probing packet as connection is "
                   "disconnected.");
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_PEER_GOING_AWAY,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, WriteBlockedAfterClientSendsConnectivityProbe) {
@@ -6570,7 +6638,7 @@
 
   // Connection should not be closed if a connectivity probe is failed to be
   // sent.
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0);
 
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
       .Times(0);
@@ -6585,7 +6653,7 @@
   writer_->SetShouldWriteFail();
   // Connection should not be closed if a connectivity probe is failed to be
   // sent.
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0);
 
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
       .Times(0);
@@ -6604,9 +6672,11 @@
       framer_.BuildPublicResetPacket(header));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*packet, QuicTime::Zero()));
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PUBLIC_RESET, _,
-                                           ConnectionCloseSource::FROM_PEER));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_PEER))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_PUBLIC_RESET, saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, IetfStatelessReset) {
@@ -6624,9 +6694,11 @@
                                                 kTestStatelessResetToken));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*packet, QuicTime::Zero()));
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PUBLIC_RESET, _,
-                                           ConnectionCloseSource::FROM_PEER));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_PEER))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_PUBLIC_RESET, saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, GoAway) {
@@ -6668,7 +6740,7 @@
 
 TEST_P(QuicConnectionTest, ZeroBytePacket) {
   // Don't close the connection for zero byte packets.
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0);
   QuicReceivedPacket encrypted(nullptr, 0, QuicTime::Zero());
   connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, encrypted);
 }
@@ -6697,18 +6769,20 @@
           AllSupportedVersions()));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*encrypted, QuicTime::Zero()));
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_VERSION, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
   EXPECT_FALSE(connection_.connected());
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_INVALID_VERSION,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, BadVersionNegotiation) {
   // Send a version negotiation packet with the version the client started with.
   // It should be rejected.
-  EXPECT_CALL(visitor_,
-              OnConnectionClosed(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, _,
-                                 ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   std::unique_ptr<QuicEncryptedPacket> encrypted(
       QuicFramer::BuildVersionNegotiationPacket(
           connection_id_, EmptyQuicConnectionId(),
@@ -6717,6 +6791,9 @@
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*encrypted, QuicTime::Zero()));
   connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_INVALID_VERSION_NEGOTIATION_PACKET,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, CheckSendStats) {
@@ -6813,14 +6890,17 @@
       peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(1),
                                   *packet, buffer, kMaxOutgoingPacketSize);
 
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
-                                           ConnectionCloseSource::FROM_PEER));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_PEER))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
   connection_.ProcessUdpPacket(
       kSelfAddress, kPeerAddress,
       QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_PEER_GOING_AWAY,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, SelectMutualVersion) {
@@ -7037,14 +7117,16 @@
     return;
   }
 
-  EXPECT_CALL(visitor_,
-              OnConnectionClosed(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA,
-                                 _, ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   struct iovec iov;
   MakeIOVector("", &iov);
   EXPECT_QUIC_BUG(connection_.SaveAndSendStreamData(3, &iov, 1, 0, 0, FIN),
                   "Cannot send stream data with level: ENCRYPTION_INITIAL");
   EXPECT_FALSE(connection_.connected());
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, SetRetransmissionAlarmForCryptoPacket) {
@@ -7420,7 +7502,7 @@
   // Verifies that multiple calls to CloseConnection do not
   // result in multiple attempts to close the connection - it will be marked as
   // disconnected after the first call.
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1);
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(1);
   connection_.CloseConnection(QUIC_NO_ERROR, "no reason",
                               ConnectionCloseBehavior::SILENT_CLOSE);
   connection_.CloseConnection(QUIC_NO_ERROR, "no reason",
@@ -7441,9 +7523,10 @@
   frame1_.data_buffer = data->data();
   frame1_.data_length = data->length();
 
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_MAYBE_CORRUPTED_MEMORY, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   ForceProcessFramePacket(QuicFrame(frame1_));
+  TestConnectionCloseQuicErrorCode(QUIC_MAYBE_CORRUPTED_MEMORY);
 }
 
 TEST_P(QuicConnectionTest, ClientReceivesRejOnNonCryptoStream) {
@@ -7457,28 +7540,29 @@
   frame1_.data_buffer = data->data();
   frame1_.data_length = data->length();
 
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_MAYBE_CORRUPTED_MEMORY, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   ForceProcessFramePacket(QuicFrame(frame1_));
+  TestConnectionCloseQuicErrorCode(QUIC_MAYBE_CORRUPTED_MEMORY);
 }
 
 TEST_P(QuicConnectionTest, CloseConnectionOnPacketTooLarge) {
   SimulateNextPacketTooLarge();
   // A connection close packet is sent
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _,
-                                           ConnectionCloseSource::FROM_SELF))
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
       .Times(1);
   connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+  TestConnectionCloseQuicErrorCode(QUIC_PACKET_WRITE_ERROR);
 }
 
 TEST_P(QuicConnectionTest, AlwaysGetPacketTooLarge) {
   // Test even we always get packet too large, we do not infinitely try to send
   // close packet.
   AlwaysGetPacketTooLarge();
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _,
-                                           ConnectionCloseSource::FROM_SELF))
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
       .Times(1);
   connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+  TestConnectionCloseQuicErrorCode(QUIC_PACKET_WRITE_ERROR);
 }
 
 // Verify that if connection has no outstanding data, it notifies the send
@@ -7588,22 +7672,24 @@
   EXPECT_TRUE(ack_alarm->IsSet());
   connection_.GetAckAlarm()->Fire();
   // Simulate data packet causes write error.
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _, _));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
   SimulateNextPacketTooLarge();
   connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
   EXPECT_EQ(1u, writer_->frame_count());
   EXPECT_FALSE(writer_->connection_close_frames().empty());
   // Ack frame is not bundled in connection close packet.
   EXPECT_TRUE(writer_->ack_frames().empty());
+  TestConnectionCloseQuicErrorCode(QUIC_PACKET_WRITE_ERROR);
 }
 
 // Regression test for b/63620844.
 TEST_P(QuicConnectionTest, FailedToWriteHandshakePacket) {
   SimulateNextPacketTooLarge();
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _,
-                                           ConnectionCloseSource::FROM_SELF))
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
       .Times(1);
+
   connection_.SendCryptoStreamData();
+  TestConnectionCloseQuicErrorCode(QUIC_PACKET_WRITE_ERROR);
 }
 
 TEST_P(QuicConnectionTest, MaxPacingRate) {
@@ -7927,13 +8013,16 @@
 
 TEST_P(QuicConnectionTest, WriteBlockedWithInvalidAck) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _, _));
-
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _))
+      .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   BlockOnNextWrite();
   connection_.SendStreamDataWithString(5, "foo", 0, FIN);
   // This causes connection to be closed because packet 1 has not been sent yet.
   QuicAckFrame frame = InitAckFrame(1);
   ProcessAckPacket(1, &frame);
+  EXPECT_EQ(1, connection_close_frame_count_);
+  EXPECT_EQ(QUIC_INVALID_ACK_DATA,
+            saved_connection_close_frame_.quic_error_code);
 }
 
 TEST_P(QuicConnectionTest, SendMessage) {
@@ -8208,10 +8297,11 @@
   // Received ACK for packets 2 and 3 in wrong packet number space.
   QuicAckFrame invalid_ack =
       InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(4)}});
-  EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
-                                           ConnectionCloseSource::FROM_SELF));
+  EXPECT_CALL(visitor_,
+              OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   ProcessFramePacketAtLevel(300, QuicFrame(&invalid_ack), ENCRYPTION_INITIAL);
+  TestConnectionCloseQuicErrorCode(QUIC_INVALID_ACK_DATA);
 }
 
 TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) {
@@ -8404,7 +8494,7 @@
   // of scope, a delayed ACK is pending, and ACK alarm should not be scheduled
   // because connection is disconnected.
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-  EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
   EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
   std::unique_ptr<QuicConnectionCloseFrame> connection_close_frame(
       new QuicConnectionCloseFrame(QUIC_INTERNAL_ERROR));
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index 3b0c92b..b38654f 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -72,9 +72,8 @@
 
   ~TestQuicSpdyServerSession() override { delete connection(); }
 
-  MOCK_METHOD3(OnConnectionClosed,
-               void(QuicErrorCode error,
-                    const std::string& error_details,
+  MOCK_METHOD2(OnConnectionClosed,
+               void(const QuicConnectionCloseFrame& frame,
                     ConnectionCloseSource source));
   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream* pending));
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 19b219d..0671b8e 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -419,23 +419,22 @@
   }
 }
 
-void QuicSession::OnConnectionClosed(QuicErrorCode error,
-                                     const std::string& error_details,
+void QuicSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                                      ConnectionCloseSource source) {
   DCHECK(!connection_->connected());
   if (perspective() == Perspective::IS_SERVER) {
-    RecordConnectionCloseAtServer(error, source);
+    RecordConnectionCloseAtServer(frame.quic_error_code, source);
   }
 
   if (error_ == QUIC_NO_ERROR) {
-    error_ = error;
+    error_ = frame.quic_error_code;
   }
 
   if (!eliminate_static_stream_map_) {
     while (!dynamic_stream_map_.empty()) {
       DynamicStreamMap::iterator it = dynamic_stream_map_.begin();
       QuicStreamId id = it->first;
-      it->second->OnConnectionClosed(error, source);
+      it->second->OnConnectionClosed(frame.quic_error_code, source);
       // The stream should call CloseStream as part of OnConnectionClosed.
       if (dynamic_stream_map_.find(id) != dynamic_stream_map_.end()) {
         QUIC_BUG << ENDPOINT << "Stream " << id
@@ -454,7 +453,7 @@
     }
     for (const auto& it : non_static_streams) {
       QuicStreamId id = it.first;
-      it.second->OnConnectionClosed(error, source);
+      it.second->OnConnectionClosed(frame.quic_error_code, source);
       if (dynamic_stream_map_.find(id) != dynamic_stream_map_.end()) {
         QUIC_BUG << ENDPOINT << "Stream " << id
                  << " failed to close under OnConnectionClosed";
@@ -473,8 +472,9 @@
   closed_streams_clean_up_alarm_->Cancel();
 
   if (visitor_) {
-    visitor_->OnConnectionClosed(connection_->connection_id(), error,
-                                 error_details, source);
+    visitor_->OnConnectionClosed(connection_->connection_id(),
+                                 frame.quic_error_code, frame.error_details,
+                                 source);
   }
 }
 
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 1efde28..5a0e9dd 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -105,8 +105,7 @@
   void OnMessageReceived(QuicStringPiece message) override;
   void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
   void OnBlockedFrame(const QuicBlockedFrame& frame) override;
-  void OnConnectionClosed(QuicErrorCode error,
-                          const std::string& error_details,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                           ConnectionCloseSource source) override;
   void OnWriteBlocked() override;
   void OnSuccessfulVersionNegotiation(
diff --git a/quic/quartc/quartc_endpoint.cc b/quic/quartc/quartc_endpoint.cc
index faee615..7d01ce8 100644
--- a/quic/quartc/quartc_endpoint.cc
+++ b/quic/quartc/quartc_endpoint.cc
@@ -100,11 +100,11 @@
                                        latest_rtt);
 }
 
-void QuartcClientEndpoint::OnConnectionClosed(QuicErrorCode error_code,
-                                              const std::string& error_details,
-                                              ConnectionCloseSource source) {
+void QuartcClientEndpoint::OnConnectionClosed(
+    const QuicConnectionCloseFrame& frame,
+    ConnectionCloseSource source) {
   // First, see if we can restart the session with a mutually-supported version.
-  if (error_code == QUIC_INVALID_VERSION && session_ &&
+  if (frame.quic_error_code == QUIC_INVALID_VERSION && session_ &&
       session_->connection() &&
       !session_->connection()->server_supported_versions().empty()) {
     for (const auto& client_version :
@@ -122,7 +122,7 @@
 
   // Permanent version negotiation errors are forwarded to the |delegate_|,
   // along with all other errors.
-  delegate_->OnConnectionClosed(error_code, error_details, source);
+  delegate_->OnConnectionClosed(frame, source);
 }
 
 void QuartcClientEndpoint::OnMessageReceived(QuicStringPiece message) {
diff --git a/quic/quartc/quartc_endpoint.h b/quic/quartc/quartc_endpoint.h
index df6f056..51f35ce 100644
--- a/quic/quartc/quartc_endpoint.h
+++ b/quic/quartc/quartc_endpoint.h
@@ -83,8 +83,7 @@
   void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
                                  QuicBandwidth pacing_rate,
                                  QuicTime::Delta latest_rtt) override;
-  void OnConnectionClosed(QuicErrorCode error_code,
-                          const std::string& error_details,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                           ConnectionCloseSource source) override;
   void OnMessageReceived(QuicStringPiece message) override;
   void OnMessageSent(int64_t datagram_id) override;
diff --git a/quic/quartc/quartc_fakes.h b/quic/quartc/quartc_fakes.h
index 8b86064..91ea9a9 100644
--- a/quic/quartc/quartc_fakes.h
+++ b/quic/quartc/quartc_fakes.h
@@ -43,8 +43,7 @@
   }
 
   // Called when connection closes locally, or remotely by peer.
-  void OnConnectionClosed(QuicErrorCode /*error_code*/,
-                          const std::string& /*error_details*/,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& /*frame*/,
                           ConnectionCloseSource /*source*/) override {
     connected_ = false;
   }
diff --git a/quic/quartc/quartc_session.cc b/quic/quartc/quartc_session.cc
index e332613..93f32bb 100644
--- a/quic/quartc/quartc_session.cc
+++ b/quic/quartc/quartc_session.cc
@@ -206,12 +206,11 @@
   return GetNumOpenDynamicStreams() > 0;
 }
 
-void QuartcSession::OnConnectionClosed(QuicErrorCode error,
-                                       const std::string& error_details,
+void QuartcSession::OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                                        ConnectionCloseSource source) {
-  QuicSession::OnConnectionClosed(error, error_details, source);
+  QuicSession::OnConnectionClosed(frame, source);
   DCHECK(session_delegate_);
-  session_delegate_->OnConnectionClosed(error, error_details, source);
+  session_delegate_->OnConnectionClosed(frame, source);
 }
 
 void QuartcSession::CloseConnection(const std::string& details) {
diff --git a/quic/quartc/quartc_session.h b/quic/quartc/quartc_session.h
index 6cd1a20..6e7ab6c 100644
--- a/quic/quartc/quartc_session.h
+++ b/quic/quartc/quartc_session.h
@@ -81,8 +81,7 @@
   void OnCanWrite() override;
   bool SendProbingData() override;
 
-  void OnConnectionClosed(QuicErrorCode error,
-                          const std::string& error_details,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                           ConnectionCloseSource source) override;
 
   // QuartcSession methods.
@@ -134,8 +133,7 @@
 
     // Called when the connection is closed. This means all of the streams will
     // be closed and no new streams can be created.
-    virtual void OnConnectionClosed(QuicErrorCode error_code,
-                                    const std::string& error_details,
+    virtual void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                                     ConnectionCloseSource source) = 0;
 
     // Called when message (sent as SendMessage) is received.
diff --git a/quic/quartc/test/quartc_peer.cc b/quic/quartc/test/quartc_peer.cc
index 0a24187..d5e0d90 100644
--- a/quic/quartc/test/quartc_peer.cc
+++ b/quic/quartc/test/quartc_peer.cc
@@ -90,11 +90,9 @@
   }
 }
 
-void QuartcPeer::OnConnectionClosed(QuicErrorCode error_code,
-                                    const std::string& error_details,
+void QuartcPeer::OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                                     ConnectionCloseSource /*source*/) {
-  QUIC_LOG(INFO) << "Connection closed, error=" << error_code
-                 << ", details=" << error_details;
+  QUIC_LOG(INFO) << "Connection closed, frame=" << frame;
   SetEnabled(false);
 }
 
diff --git a/quic/quartc/test/quartc_peer.h b/quic/quartc/test/quartc_peer.h
index 2eab393..828b1e2 100644
--- a/quic/quartc/test/quartc_peer.h
+++ b/quic/quartc/test/quartc_peer.h
@@ -80,8 +80,7 @@
   void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
                                  QuicBandwidth pacing_rate,
                                  QuicTime::Delta latest_rtt) override;
-  void OnConnectionClosed(QuicErrorCode error_code,
-                          const std::string& error_details,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                           ConnectionCloseSource source) override;
   void OnMessageReceived(QuicStringPiece message) override;
   void OnMessageSent(int64_t /*datagram_id*/) override {}
diff --git a/quic/quartc/test/quic_trace_interceptor.cc b/quic/quartc/test/quic_trace_interceptor.cc
index dc10c38..9a9c638 100644
--- a/quic/quartc/test/quic_trace_interceptor.cc
+++ b/quic/quartc/test/quic_trace_interceptor.cc
@@ -53,10 +53,10 @@
                                        latest_rtt);
 }
 
-void QuicTraceInterceptor::OnConnectionClosed(QuicErrorCode error_code,
-                                              const std::string& error_details,
-                                              ConnectionCloseSource source) {
-  delegate_->OnConnectionClosed(error_code, error_details, source);
+void QuicTraceInterceptor::OnConnectionClosed(
+    const QuicConnectionCloseFrame& frame,
+    ConnectionCloseSource source) {
+  delegate_->OnConnectionClosed(frame, source);
 }
 
 void QuicTraceInterceptor::OnMessageReceived(QuicStringPiece message) {
diff --git a/quic/quartc/test/quic_trace_interceptor.h b/quic/quartc/test/quic_trace_interceptor.h
index 263b596..df283af 100644
--- a/quic/quartc/test/quic_trace_interceptor.h
+++ b/quic/quartc/test/quic_trace_interceptor.h
@@ -34,8 +34,7 @@
   void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
                                  QuicBandwidth pacing_rate,
                                  QuicTime::Delta latest_rtt) override;
-  void OnConnectionClosed(QuicErrorCode error_code,
-                          const std::string& error_details,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& frame,
                           ConnectionCloseSource source) override;
   void OnMessageReceived(QuicStringPiece message) override;
   void OnMessageSent(int64_t datagram_id) override;
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 5d168cc..221dd6f 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -360,9 +360,8 @@
   MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
   MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
   MOCK_METHOD1(OnMessageReceived, void(QuicStringPiece message));
-  MOCK_METHOD3(OnConnectionClosed,
-               void(QuicErrorCode error,
-                    const std::string& error_details,
+  MOCK_METHOD2(OnConnectionClosed,
+               void(const QuicConnectionCloseFrame& frame,
                     ConnectionCloseSource source));
   MOCK_METHOD0(OnWriteBlocked, void());
   MOCK_METHOD0(OnCanWrite, void());
@@ -596,9 +595,8 @@
   const QuicCryptoStream* GetCryptoStream() const override;
   void SetCryptoStream(QuicCryptoStream* crypto_stream);
 
-  MOCK_METHOD3(OnConnectionClosed,
-               void(QuicErrorCode error,
-                    const std::string& error_details,
+  MOCK_METHOD2(OnConnectionClosed,
+               void(const QuicConnectionCloseFrame& frame,
                     ConnectionCloseSource source));
   MOCK_METHOD1(CreateIncomingStream, QuicStream*(QuicStreamId id));
   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream* stream));
@@ -673,16 +671,14 @@
   const QuicCryptoStream* GetCryptoStream() const override;
   void SetCryptoStream(QuicCryptoStream* crypto_stream);
 
-  void ReallyOnConnectionClosed(QuicErrorCode error,
-                                const std::string& error_details,
+  void ReallyOnConnectionClosed(const QuicConnectionCloseFrame& frame,
                                 ConnectionCloseSource source) {
-    QuicSession::OnConnectionClosed(error, error_details, source);
+    QuicSession::OnConnectionClosed(frame, source);
   }
 
   // From QuicSession.
-  MOCK_METHOD3(OnConnectionClosed,
-               void(QuicErrorCode error,
-                    const std::string& error_details,
+  MOCK_METHOD2(OnConnectionClosed,
+               void(const QuicConnectionCloseFrame& frame,
                     ConnectionCloseSource source));
   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream* stream));
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h
index 62f19a1..7481ddc 100644
--- a/quic/test_tools/simulator/quic_endpoint.h
+++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -92,8 +92,7 @@
   void OnRstStream(const QuicRstStreamFrame& /*frame*/) override {}
   void OnGoAway(const QuicGoAwayFrame& /*frame*/) override {}
   void OnMessageReceived(QuicStringPiece /*message*/) override {}
-  void OnConnectionClosed(QuicErrorCode /*error*/,
-                          const std::string& /*error_details*/,
+  void OnConnectionClosed(const QuicConnectionCloseFrame& /*frame*/,
                           ConnectionCloseSource /*source*/) override {}
   void OnWriteBlocked() override {}
   void OnSuccessfulVersionNegotiation(
diff --git a/quic/tools/quic_simple_server_stream_test.cc b/quic/tools/quic_simple_server_stream_test.cc
index de2968f..7bac18e 100644
--- a/quic/tools/quic_simple_server_stream_test.cc
+++ b/quic/tools/quic_simple_server_stream_test.cc
@@ -117,9 +117,8 @@
       delete;
   ~MockQuicSimpleServerSession() override = default;
 
-  MOCK_METHOD3(OnConnectionClosed,
-               void(QuicErrorCode error,
-                    const std::string& error_details,
+  MOCK_METHOD2(OnConnectionClosed,
+               void(const QuicConnectionCloseFrame& frame,
                     ConnectionCloseSource source));
   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
   MOCK_METHOD5(WritevData,