gfe-relnote: Add support for sending MAX_PUSH_ID, defaulting to zero and close connection if we receive a push ID higher than the max. Protected by the existing disabled and blocked quic_enable_version_99 reloadable flag. PiperOrigin-RevId: 258759749 Change-Id: I5a10b4145931f22ec359e8b96c36cc267de2306a
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc index ba5a9cc..f27411a 100644 --- a/quic/core/http/quic_spdy_client_session_base.cc +++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -24,7 +24,8 @@ : QuicSpdySession(connection, nullptr, config, supported_versions), push_promise_index_(push_promise_index), largest_promised_stream_id_( - QuicUtils::GetInvalidStreamId(connection->transport_version())) {} + QuicUtils::GetInvalidStreamId(connection->transport_version())), + max_allowed_push_id_(0) {} QuicSpdyClientSessionBase::~QuicSpdyClientSessionBase() { // all promised streams for this session @@ -88,6 +89,14 @@ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return; } + + if (VersionHasIetfQuicFrames(connection()->transport_version()) && + promised_stream_id > max_allowed_push_id()) { + connection()->CloseConnection( + QUIC_INVALID_STREAM_ID, + "Received push stream id higher than MAX_PUSH_ID.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + } largest_promised_stream_id_ = promised_stream_id; QuicSpdyStream* stream = GetSpdyDataStream(stream_id); @@ -209,4 +218,9 @@ return !HasActiveRequestStreams() && promised_by_id_.empty(); } +void QuicSpdyClientSessionBase::set_max_allowed_push_id( + QuicStreamId max_allowed_push_id) { + max_allowed_push_id_ = max_allowed_push_id; +} + } // namespace quic
diff --git a/quic/core/http/quic_spdy_client_session_base.h b/quic/core/http/quic_spdy_client_session_base.h index aec5e75..98b8589 100644 --- a/quic/core/http/quic_spdy_client_session_base.h +++ b/quic/core/http/quic_spdy_client_session_base.h
@@ -111,6 +111,10 @@ // Returns true if there are no active requests and no promised streams. bool ShouldReleaseHeadersStreamSequencerBuffer() override; + void set_max_allowed_push_id(QuicStreamId max_allowed_push_id); + + QuicStreamId max_allowed_push_id() { return max_allowed_push_id_; } + size_t get_max_promises() const { return max_open_incoming_unidirectional_streams() * kMaxPromisedStreamsMultiplier; @@ -134,6 +138,7 @@ QuicClientPushPromiseIndex* push_promise_index_; QuicPromisedByIdMap promised_by_id_; QuicStreamId largest_promised_stream_id_; + QuicStreamId max_allowed_push_id_; }; } // namespace quic
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc index 716ae42..dfa3031 100644 --- a/quic/core/http/quic_spdy_client_session_test.cc +++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -610,6 +610,38 @@ QuicHeaderList()); } +TEST_P(QuicSpdyClientSessionTest, PushPromiseStreamIdTooHigh) { + // Initialize crypto before the client session will create a stream. + CompleteCryptoHandshake(); + QuicStreamId stream_id = + QuicSessionPeer::GetNextOutgoingBidirectionalStreamId(session_.get()); + QuicSessionPeer::ActivateStream( + session_.get(), QuicMakeUnique<QuicSpdyClientStream>( + stream_id, session_.get(), BIDIRECTIONAL)); + + session_->set_max_allowed_push_id(GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 10)); + if (VersionHasIetfQuicFrames(connection_->transport_version())) { + // TODO(b/136295430) Use PushId to represent Push IDs instead of + // QuicStreamId. + EXPECT_CALL( + *connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Received push stream id higher than MAX_PUSH_ID.", _)); + } + auto promise_id = GetNthServerInitiatedUnidirectionalStreamId( + connection_->transport_version(), 11); + auto headers = QuicHeaderList(); + headers.OnHeaderBlockStart(); + headers.OnHeader(":path", "/bar"); + headers.OnHeader(":authority", "www.google.com"); + headers.OnHeader(":version", "HTTP/1.1"); + headers.OnHeader(":method", "GET"); + headers.OnHeader(":scheme", "https"); + headers.OnHeaderBlockEnd(0, 0); + session_->OnPromiseHeaderList(stream_id, promise_id, 0, headers); +} + TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeadersAlreadyClosed) { // Initialize crypto before the client session will create a stream. CompleteCryptoHandshake();
diff --git a/quic/tools/quic_spdy_client_base.cc b/quic/tools/quic_spdy_client_base.cc index 06abc96..c44c677 100644 --- a/quic/tools/quic_spdy_client_base.cc +++ b/quic/tools/quic_spdy_client_base.cc
@@ -60,6 +60,9 @@ void QuicSpdyClientBase::InitializeSession() { client_session()->Initialize(); client_session()->CryptoConnect(); + if (max_allowed_push_id_ > 0) { + client_session()->set_max_allowed_push_id(max_allowed_push_id_); + } } void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) {
diff --git a/quic/tools/quic_spdy_client_base.h b/quic/tools/quic_spdy_client_base.h index 87fc664..dc1811d 100644 --- a/quic/tools/quic_spdy_client_base.h +++ b/quic/tools/quic_spdy_client_base.h
@@ -136,6 +136,9 @@ } bool drop_response_body() const { return drop_response_body_; } + // Set the max promise id for the client session. + void set_max_allowed_push_id(QuicStreamId max) { max_allowed_push_id_ = max; } + protected: int GetNumSentClientHellosFromSession() override; int GetNumReceivedServerConfigUpdatesFromSession() override; @@ -209,6 +212,9 @@ std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_; bool drop_response_body_ = false; + + // The max promise id to set on the client session when created. + QuicStreamId max_allowed_push_id_; }; } // namespace quic