Send HTTP/3 GOAWAY frame when closing connection.

Protected by FLAGS_quic_reloadable_flag_quic_send_goaway_with_connection_close.

PiperOrigin-RevId: 337371228
Change-Id: Ic810995552c911a4ef978208c397237f2848cc82
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 36a122e..2d883b0 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -1359,6 +1359,13 @@
   }
 }
 
+void QuicSpdySession::BeforeConnectionCloseSent() {
+  if (GetQuicReloadableFlag(quic_send_goaway_with_connection_close) &&
+      VersionUsesHttp3(transport_version()) && IsEncryptionEstablished()) {
+    SendHttp3GoAway();
+  }
+}
+
 void QuicSpdySession::OnCanCreateNewOutgoingStream(bool unidirectional) {
   if (unidirectional && VersionUsesHttp3(transport_version())) {
     MaybeInitializeHttp3UnidirectionalStreams();
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index bd9500c..2a6ca31 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -467,6 +467,9 @@
   // Initializes HTTP/3 unidirectional streams if not yet initialzed.
   virtual void MaybeInitializeHttp3UnidirectionalStreams();
 
+  // QuicConnectionVisitorInterface method.
+  void BeforeConnectionCloseSent() override;
+
  private:
   friend class test::QuicSpdySessionPeer;
 
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 345d1b5..76dba5c 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -23,6 +23,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
@@ -3061,6 +3062,63 @@
   session_.OnStreamFrame(data3);
 }
 
+TEST_P(QuicSpdySessionTestServer, Http3GoAwayWhenClosingConnection) {
+  if (!VersionUsesHttp3(transport_version())) {
+    return;
+  }
+
+  StrictMock<MockHttp3DebugVisitor> debug_visitor;
+  session_.set_debug_visitor(&debug_visitor);
+
+  EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_));
+  CompleteHandshake();
+
+  QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+
+  // Create stream by receiving some data (CreateIncomingStream() would not
+  // update the session's largest peer created stream ID).
+  const size_t headers_payload_length = 10;
+  std::unique_ptr<char[]> headers_buffer;
+  QuicByteCount headers_frame_header_length =
+      HttpEncoder::SerializeHeadersFrameHeader(headers_payload_length,
+                                               &headers_buffer);
+  absl::string_view headers_frame_header(headers_buffer.get(),
+                                         headers_frame_header_length);
+  EXPECT_CALL(debug_visitor,
+              OnHeadersFrameReceived(stream_id, headers_payload_length));
+  session_.OnStreamFrame(
+      QuicStreamFrame(stream_id, false, 0, headers_frame_header));
+
+  EXPECT_EQ(stream_id, QuicSessionPeer::GetLargestPeerCreatedStreamId(
+                           &session_, /*unidirectional = */ false));
+
+  if (GetQuicReloadableFlag(quic_send_goaway_with_connection_close)) {
+    if (GetQuicReloadableFlag(quic_fix_http3_goaway_stream_id)) {
+      // Stream with stream_id is already received and potentially processed,
+      // therefore a GOAWAY frame is sent with the next stream ID.
+      EXPECT_CALL(debug_visitor,
+                  OnGoAwayFrameSent(stream_id + QuicUtils::StreamIdDelta(
+                                                    transport_version())));
+    } else {
+      // GOAWAY frame stream id is incorrect, ignore.
+      EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_));
+    }
+  }
+
+  // Close connection.
+  EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _))
+      .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+  EXPECT_CALL(*connection_, CloseConnection(QUIC_NO_ERROR, _, _))
+      .WillOnce(
+          Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+  EXPECT_CALL(*connection_, SendConnectionClosePacket(QUIC_NO_ERROR, _))
+      .WillOnce(Invoke(connection_,
+                       &MockQuicConnection::ReallySendConnectionClosePacket));
+  connection_->CloseConnection(
+      QUIC_NO_ERROR, "closing connection",
+      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
 TEST_P(QuicSpdySessionTestClient, SendInitialMaxPushIdIfSet) {
   if (!VersionUsesHttp3(transport_version())) {
     return;
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 52992d0..4dec406 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -3787,6 +3787,11 @@
       packet_creator_.FlushAckFrame(frames);
     }
 
+    if (level == ENCRYPTION_FORWARD_SECURE &&
+        perspective_ == Perspective::IS_SERVER) {
+      visitor_->BeforeConnectionCloseSent();
+    }
+
     auto* frame =
         new QuicConnectionCloseFrame(transport_version(), error, details,
                                      framer_.current_received_frame_type());
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 3e74191..afcbe13 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -204,6 +204,11 @@
   // Called to generate an encrypter for the same key phase of the last
   // decrypter returned by AdvanceKeysAndCreateCurrentOneRttDecrypter().
   virtual std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() = 0;
+
+  // Called when connection is being closed right before a CONNECTION_CLOSE
+  // frame is serialized, but only on the server and only if forward secure
+  // encryption has already been established.
+  virtual void BeforeConnectionCloseSent() = 0;
 };
 
 // Interface which gets callbacks from the QuicConnection at interesting
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 178eb2f..62f4b8a 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1479,6 +1479,9 @@
   host.FromString("1.1.1.1");
   QuicSocketAddress self_address(host, 123);
   EXPECT_CALL(visitor_, AllowSelfAddressChange()).WillOnce(Return(false));
+  if (version().handshake_protocol == PROTOCOL_TLS1_3) {
+    EXPECT_CALL(visitor_, BeforeConnectionCloseSent());
+  }
   EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
   ProcessFramePacketWithAddresses(MakeCryptoFrame(), self_address, kPeerAddress,
                                   ENCRYPTION_INITIAL);
@@ -7401,6 +7404,9 @@
   frame1_.data_buffer = data->data();
   frame1_.data_length = data->length();
 
+  if (version().handshake_protocol == PROTOCOL_TLS1_3) {
+    EXPECT_CALL(visitor_, BeforeConnectionCloseSent());
+  }
   EXPECT_CALL(visitor_,
               OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   ForceProcessFramePacket(QuicFrame(frame1_));
@@ -7657,6 +7663,9 @@
   }
   set_perspective(Perspective::IS_SERVER);
 
+  if (version().handshake_protocol == PROTOCOL_TLS1_3) {
+    EXPECT_CALL(visitor_, BeforeConnectionCloseSent());
+  }
   EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
   const QuicErrorCode kQuicErrorCode = QUIC_INTERNAL_ERROR;
   connection_.CloseConnection(
@@ -9823,6 +9832,9 @@
   }
   set_perspective(Perspective::IS_SERVER);
   EXPECT_CALL(visitor_, OnHandshakeDoneReceived()).Times(0);
+  if (version().handshake_protocol == PROTOCOL_TLS1_3) {
+    EXPECT_CALL(visitor_, BeforeConnectionCloseSent());
+  }
   EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF))
       .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame));
   QuicFrames frames;
@@ -11008,6 +11020,9 @@
   EXPECT_TRUE(connection_.connected());
   EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
 
+  if (version().handshake_protocol == PROTOCOL_TLS1_3) {
+    EXPECT_CALL(visitor_, BeforeConnectionCloseSent());
+  }
   EXPECT_CALL(visitor_,
               OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF));
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 3c04c77..e22bd23 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -140,6 +140,7 @@
   std::unique_ptr<QuicDecrypter> AdvanceKeysAndCreateCurrentOneRttDecrypter()
       override;
   std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override;
+  void BeforeConnectionCloseSent() override {}
 
   // QuicStreamFrameDataProducer
   WriteStreamDataResult WriteStreamData(QuicStreamId id,
diff --git a/quic/test_tools/quic_session_peer.h b/quic/test_tools/quic_session_peer.h
index 9a6fdef..fdcb443 100644
--- a/quic/test_tools/quic_session_peer.h
+++ b/quic/test_tools/quic_session_peer.h
@@ -82,6 +82,10 @@
   static void SetPerspective(QuicSession* session, Perspective perspective);
   static size_t GetNumOpenDynamicStreams(QuicSession* session);
   static size_t GetNumDrainingStreams(QuicSession* session);
+  static QuicStreamId GetLargestPeerCreatedStreamId(QuicSession* session,
+                                                    bool unidirectional) {
+    return session->GetLargestPeerCreatedStreamId(unidirectional);
+  }
 };
 
 }  // namespace test
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 3539628..a55f9a8 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -589,6 +589,7 @@
               CreateCurrentOneRttEncrypter,
               (),
               (override));
+  MOCK_METHOD(void, BeforeConnectionCloseSent, (), (override));
 };
 
 class MockQuicConnectionHelper : public QuicConnectionHelperInterface {
@@ -733,6 +734,11 @@
     QuicConnection::CloseConnection(error, details, connection_close_behavior);
   }
 
+  void ReallySendConnectionClosePacket(QuicErrorCode error,
+                                       const std::string& details) {
+    QuicConnection::SendConnectionClosePacket(error, details);
+  }
+
   void ReallyProcessUdpPacket(const QuicSocketAddress& self_address,
                               const QuicSocketAddress& peer_address,
                               const QuicReceivedPacket& packet) {
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h
index 7968900..7f5bef2 100644
--- a/quic/test_tools/simulator/quic_endpoint.h
+++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -101,6 +101,7 @@
   std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override {
     return nullptr;
   }
+  void BeforeConnectionCloseSent() override {}
 
   // End QuicConnectionVisitorInterface implementation.