Close connection if trying to send GOAWAY before encryption gets established.
Protected by FLAGS_quic_reloadable_flag_quic_encrypted_goaway.
PiperOrigin-RevId: 346378578
Change-Id: I35923b59ee19f33056405ba07cfb82cdeb91e34e
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 7f7deb2..a86aefa 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -692,15 +692,25 @@
if (perspective() == Perspective::IS_SERVER &&
frame.stream_count >= QuicUtils::GetMaxStreamCount()) {
DCHECK_EQ(frame.stream_count, QuicUtils::GetMaxStreamCount());
- SendHttp3GoAway();
+ SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "stream count too large");
}
return true;
}
-void QuicSpdySession::SendHttp3GoAway() {
+void QuicSpdySession::SendHttp3GoAway(QuicErrorCode error_code,
+ const std::string& reason) {
DCHECK_EQ(perspective(), Perspective::IS_SERVER);
DCHECK(VersionUsesHttp3(transport_version()));
-
+ if (GetQuicReloadableFlag(quic_encrypted_goaway)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_encrypted_goaway, 2, 2);
+ if (!IsEncryptionEstablished()) {
+ QUIC_CODE_COUNT(quic_h3_goaway_before_encryption_established);
+ connection()->CloseConnection(
+ error_code, reason,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ }
QuicStreamId stream_id;
if (goaway_with_max_stream_id_) {
@@ -747,7 +757,7 @@
void QuicSpdySession::SendHttp3Shutdown() {
if (goaway_with_max_stream_id_) {
- SendHttp3GoAway();
+ SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Server shutdown");
return;
}
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 6e7ccb8..4ab4cfd 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -230,8 +230,9 @@
// Write GOAWAY frame with maximum stream ID on the control stream. Called to
// initite graceful connection shutdown. Do not use smaller stream ID, in
// case client does not implement retry on GOAWAY. Do not send GOAWAY if one
- // has already been sent.
- void SendHttp3GoAway();
+ // has already been sent. Send connection close with |error_code| and |reason|
+ // before encryption gets established.
+ void SendHttp3GoAway(QuicErrorCode error_code, const std::string& reason);
// Same as SendHttp3GoAway(). TODO(bnc): remove when
// gfe2_reloadable_flag_quic_goaway_with_max_stream_id flag is deprecated.
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 9f1a80b..3606949 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -24,6 +24,7 @@
#include "net/third_party/quiche/src/quic/core/quic_config.h"
#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.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"
@@ -1101,6 +1102,21 @@
EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId));
}
+TEST_P(QuicSpdySessionTestServer, SendGoAwayWithoutEncryption) {
+ SetQuicReloadableFlag(quic_encrypted_goaway, true);
+ if (VersionHasIetfQuicFrames(transport_version())) {
+ // HTTP/3 GOAWAY has different semantic and thus has its own test.
+ return;
+ }
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_PEER_GOING_AWAY, "Going Away.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_FALSE(session_.goaway_sent());
+}
+
TEST_P(QuicSpdySessionTestServer, SendHttp3GoAway) {
if (!VersionUsesHttp3(transport_version())) {
return;
@@ -1121,7 +1137,7 @@
// retried on a different connection.
EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0));
}
- session_.SendHttp3GoAway();
+ session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway");
EXPECT_TRUE(session_.goaway_sent());
// New incoming stream is not reset.
@@ -1132,7 +1148,20 @@
// No more GOAWAY frames are sent because they could not convey new
// information to the client.
- session_.SendHttp3GoAway();
+ session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway");
+}
+
+TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayWithoutEncryption) {
+ SetQuicReloadableFlag(quic_encrypted_goaway, true);
+ if (!VersionUsesHttp3(transport_version())) {
+ return;
+ }
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_PEER_GOING_AWAY, "Goaway",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway");
+ EXPECT_FALSE(session_.goaway_sent());
}
TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayAfterStreamIsCreated) {
@@ -1159,12 +1188,12 @@
// starting with stream ID = 4 can be retried on a different connection.
EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 4));
}
- session_.SendHttp3GoAway();
+ session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway");
EXPECT_TRUE(session_.goaway_sent());
// No more GOAWAY frames are sent because they could not convey new
// information to the client.
- session_.SendHttp3GoAway();
+ session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway");
}
TEST_P(QuicSpdySessionTestServer, SendHttp3Shutdown) {
@@ -1212,7 +1241,7 @@
session_.SendHttp3Shutdown();
EXPECT_TRUE(session_.goaway_sent());
- session_.SendHttp3GoAway();
+ session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway");
const QuicStreamId kTestStreamId =
GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0);
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 908aa12..4f435a6 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -36,6 +36,7 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_server_on_wire_ping, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_token_based_address_validation, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_extract_x509_subject_using_certificate_view, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_willing_and_able_to_write2, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_goaway_with_max_stream_id, false)
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 73370d3..b2d59c5 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -931,6 +931,16 @@
const std::string& reason) {
// GOAWAY frame is not supported in IETF QUIC.
DCHECK(!VersionHasIetfQuicFrames(transport_version()));
+ if (GetQuicReloadableFlag(quic_encrypted_goaway)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_encrypted_goaway, 1, 2);
+ if (!IsEncryptionEstablished()) {
+ QUIC_CODE_COUNT(quic_goaway_before_encryption_established);
+ connection_->CloseConnection(
+ error_code, reason,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ }
if (transport_goaway_sent_) {
return;
}
diff --git a/quic/test_tools/quic_test_server.cc b/quic/test_tools/quic_test_server.cc
index c69ec29..886d434 100644
--- a/quic/test_tools/quic_test_server.cc
+++ b/quic/test_tools/quic_test_server.cc
@@ -229,7 +229,7 @@
void ImmediateGoAwaySession::OnStreamFrame(const QuicStreamFrame& frame) {
if (VersionUsesHttp3(transport_version())) {
- SendHttp3GoAway();
+ SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "");
} else {
SendGoAway(QUIC_PEER_GOING_AWAY, "");
}
@@ -253,7 +253,7 @@
std::move(encrypter));
if (VersionUsesHttp3(transport_version())) {
if (IsEncryptionEstablished() && !goaway_sent()) {
- SendHttp3GoAway();
+ SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "");
}
}
}