Send Goaway after receiving too large stream count in QuicStreamsBlocked frame. gfe-relnote: protected by gfe2_reloadable_flag_quic_enable_version_draft_25_v3 PiperOrigin-RevId: 302554974 Change-Id: Ib3394652f651c172e6fde8355fac7f5cff0aa4df
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc index 26bd5d0..2b1b457 100644 --- a/quic/core/http/quic_spdy_session.cc +++ b/quic/core/http/quic_spdy_session.cc
@@ -657,6 +657,22 @@ http3_goaway_received_ = true; } +bool QuicSpdySession::OnStreamsBlockedFrame( + const QuicStreamsBlockedFrame& frame) { + if (!QuicSession::OnStreamsBlockedFrame(frame)) { + return false; + } + + // The peer asked for stream space more than this implementation has. Send + // goaway. + if (perspective() == Perspective::IS_SERVER && + frame.stream_count >= QuicUtils::GetMaxStreamCount()) { + DCHECK_EQ(frame.stream_count, QuicUtils::GetMaxStreamCount()); + SendHttp3GoAway(); + } + return true; +} + void QuicSpdySession::SendHttp3GoAway() { DCHECK_EQ(perspective(), Perspective::IS_SERVER); DCHECK(VersionUsesHttp3(transport_version()));
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h index 631c036..bd1438b 100644 --- a/quic/core/http/quic_spdy_session.h +++ b/quic/core/http/quic_spdy_session.h
@@ -217,6 +217,9 @@ // the client side. virtual void OnHttp3GoAway(QuicStreamId stream_id); + // Send GOAWAY if the peer is blocked on the implementation max. + bool OnStreamsBlockedFrame(const QuicStreamsBlockedFrame& frame) override; + // Write the GOAWAY |frame| on control stream. void SendHttp3GoAway();
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index fb6ebfc..ac56ce9 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -13,6 +13,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_streams_blocked_frame.h" #include "net/third_party/quiche/src/quic/core/http/http_constants.h" #include "net/third_party/quiche/src/quic/core/http/http_encoder.h" #include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h" @@ -676,6 +677,37 @@ EXPECT_TRUE(session_.WillingAndAbleToWrite()); } +TEST_P(QuicSpdySessionTestServer, TooLargeStreamBlocked) { + // STREAMS_BLOCKED frame is IETF QUIC only. + if (!VersionUsesHttp3(transport_version())) { + return; + } + + StrictMock<MockHttp3DebugVisitor> debug_visitor; + session_.set_debug_visitor(&debug_visitor); + connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + MockPacketWriter* writer = static_cast<MockPacketWriter*>( + QuicConnectionPeer::GetWriter(session_.connection())); + EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)) + .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); + if (connection_->version().HasHandshakeDone()) { + EXPECT_CALL(*connection_, SendControlFrame(_)); + } + + CryptoHandshakeMessage message; + EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); + session_.GetMutableCryptoStream()->OnHandshakeMessage(message); + + // Simualte the situation where the incoming stream count is at its limit and + // the peer is blocked. + QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams( + static_cast<QuicSession*>(&session_), QuicUtils::GetMaxStreamCount()); + QuicStreamsBlockedFrame frame; + frame.stream_count = QuicUtils::GetMaxStreamCount(); + EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); + session_.OnStreamsBlockedFrame(frame); +} + TEST_P(QuicSpdySessionTestServer, TestBatchedWrites) { session_.set_writev_consumes_all_data(true); TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();