Close QUIC connection when QuicSpdySession receives server push stream.
Since we don't send out MAX_PUSH_ID, we should never receive server push stream.
Protected by quic_reloadable_flag_quic_decline_server_push_stream.
PiperOrigin-RevId: 387622986
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index dc773ae..56b0db3 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -470,6 +470,9 @@
h2_deframer_.set_visitor(spdy_framer_visitor_.get());
h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get());
spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
+ if (decline_server_push_stream_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_decline_server_push_stream);
+ }
}
QuicSpdySession::~QuicSpdySession() {
@@ -1358,6 +1361,11 @@
return receive_control_stream_;
}
case kServerPushStream: { // Push Stream.
+ if (decline_server_push_stream_) {
+ CloseConnectionWithDetails(QUIC_HTTP_RECEIVE_SERVER_PUSH,
+ "Received server push stream");
+ return nullptr;
+ }
QuicSpdyStream* stream = CreateIncomingStream(pending);
return stream;
}
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 8085dd8..78c132a 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -678,6 +678,10 @@
// Limited to kMaxUnassociatedWebTransportStreams; when the list is full,
// oldest streams are evicated first.
std::list<BufferedWebTransportStream> buffered_streams_;
+
+ // Latched value of flag_quic_decline_server_push_stream.
+ const bool decline_server_push_stream_ =
+ GetQuicReloadableFlag(quic_decline_server_push_stream);
};
} // namespace quic
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index be475b8..5a98432 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -2007,25 +2007,33 @@
std::string frame_type1 = absl::HexStringToBytes("01");
QuicStreamId stream_id1 =
GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0);
- session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false,
- /* offset = */ 0, frame_type1));
+ if (!GetQuicReloadableFlag(quic_decline_server_push_stream)) {
+ session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false,
+ /* offset = */ 0, frame_type1));
- EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- QuicStream* stream = session_.GetOrCreateStream(stream_id1);
- EXPECT_EQ(1u, QuicStreamPeer::bytes_consumed(stream));
- EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
+ QuicStream* stream = session_.GetOrCreateStream(stream_id1);
+ EXPECT_EQ(1u, QuicStreamPeer::bytes_consumed(stream));
+ EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed());
- // The same stream type can be encoded differently.
- std::string frame_type2 = absl::HexStringToBytes("80000001");
- QuicStreamId stream_id2 =
- GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1);
- session_.OnStreamFrame(QuicStreamFrame(stream_id2, /* fin = */ false,
- /* offset = */ 0, frame_type2));
+ // The same stream type can be encoded differently.
+ std::string frame_type2 = absl::HexStringToBytes("80000001");
+ QuicStreamId stream_id2 =
+ GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1);
+ session_.OnStreamFrame(QuicStreamFrame(stream_id2, /* fin = */ false,
+ /* offset = */ 0, frame_type2));
- EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- stream = session_.GetOrCreateStream(stream_id2);
- EXPECT_EQ(4u, QuicStreamPeer::bytes_consumed(stream));
- EXPECT_EQ(5u, session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
+ stream = session_.GetOrCreateStream(stream_id2);
+ EXPECT_EQ(4u, QuicStreamPeer::bytes_consumed(stream));
+ EXPECT_EQ(5u, session_.flow_controller()->bytes_consumed());
+ } else {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _))
+ .Times(1);
+ session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false,
+ /* offset = */ 0, frame_type1));
+ }
}
TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) {
@@ -2053,10 +2061,17 @@
session_.OnStreamFrame(data2);
EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- session_.OnStreamFrame(data1);
- EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
- QuicStream* stream = session_.GetOrCreateStream(stream_id);
- EXPECT_EQ(3u, stream->highest_received_byte_offset());
+ if (!GetQuicReloadableFlag(quic_decline_server_push_stream)) {
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_));
+ QuicStream* stream = session_.GetOrCreateStream(stream_id);
+ EXPECT_EQ(3u, stream->highest_received_byte_offset());
+ } else {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_HTTP_RECEIVE_SERVER_PUSH, _, _))
+ .Times(1);
+ session_.OnStreamFrame(data1);
+ }
}
TEST_P(QuicSpdySessionTestServer, OnStreamFrameLost) {
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index e82e7cb..c3fcb88 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -238,6 +238,7 @@
RETURN_STRING_LITERAL(QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS);
RETURN_STRING_LITERAL(QUIC_HTTP_RECEIVE_SPDY_SETTING);
RETURN_STRING_LITERAL(QUIC_HTTP_RECEIVE_SPDY_FRAME);
+ RETURN_STRING_LITERAL(QUIC_HTTP_RECEIVE_SERVER_PUSH);
RETURN_STRING_LITERAL(QUIC_HPACK_INDEX_VARINT_ERROR);
RETURN_STRING_LITERAL(QUIC_HPACK_NAME_LENGTH_VARINT_ERROR);
RETURN_STRING_LITERAL(QUIC_HPACK_VALUE_LENGTH_VARINT_ERROR);
@@ -678,6 +679,9 @@
case QUIC_HTTP_STREAM_LIMIT_TOO_LOW:
return {false, static_cast<uint64_t>(
QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)};
+ case QUIC_HTTP_RECEIVE_SERVER_PUSH:
+ return {false, static_cast<uint64_t>(
+ QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)};
case QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH:
return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::SETTINGS_ERROR)};
case QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH:
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index 9fa422f..53a8810 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -510,6 +510,9 @@
QUIC_HTTP_RECEIVE_SPDY_SETTING = 169,
// HTTP/3 session received an HTTP/2 only frame.
QUIC_HTTP_RECEIVE_SPDY_FRAME = 171,
+ // HTTP/3 session received SERVER_PUSH stream, which is an error because
+ // PUSH_PROMISE is not accepted.
+ QUIC_HTTP_RECEIVE_SERVER_PUSH = 205,
// HPACK header block decoding errors.
// Index varint beyond implementation limit.
@@ -597,7 +600,7 @@
QUIC_TLS_CERTIFICATE_REQUIRED = 202,
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 205,
+ QUIC_LAST_ERROR = 206,
};
// QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
// or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 0aa0ccc..7218487 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -89,6 +89,8 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_discard_packets_with_invalid_cid, true)
// If true, quic dispatcher supports one connection to use multiple connection IDs.
QUIC_FLAG(FLAGS_quic_restart_flag_quic_dispatcher_support_multiple_cid_per_connection_v2, true)
+// If true, receiving server push stream will trigger QUIC connection close.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_decline_server_push_stream, false)
// If true, require handshake confirmation for QUIC connections, functionally disabling 0-rtt handshakes.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false)
// If true, reset per packet state before processing undecryptable packets.