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();