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: 258400699
Change-Id: I5b28b882bf89162cfbe7ab56f34cbe0555ed475e
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_client_base.cc b/quic/tools/quic_client_base.cc
index b03f7cc..e53911d 100644
--- a/quic/tools/quic_client_base.cc
+++ b/quic/tools/quic_client_base.cc
@@ -85,6 +85,10 @@
break;
}
}
+ if (max_allowed_push_id_ > 0 &&
+ dynamic_cast<QuicSpdyClientSession*>(session()))
+ static_cast<QuicSpdyClientSession*>(session())->set_max_allowed_push_id(
+ max_allowed_push_id_);
return session()->connection()->connected();
}
diff --git a/quic/tools/quic_client_base.h b/quic/tools/quic_client_base.h
index fb15b86..a54c621 100644
--- a/quic/tools/quic_client_base.h
+++ b/quic/tools/quic_client_base.h
@@ -211,6 +211,9 @@
crypto_config_.set_pre_shared_key(key);
}
+ // Set the max promise id for the client session.
+ void set_max_allowed_push_id(QuicStreamId max) { max_allowed_push_id_ = max; }
+
protected:
// TODO(rch): Move GetNumSentClientHellosFromSession and
// GetNumReceivedServerConfigUpdatesFromSession into a new/better
@@ -329,6 +332,9 @@
// The network helper used to create sockets and manage the event loop.
// Not owned by this class.
std::unique_ptr<NetworkHelper> network_helper_;
+
+ // The max promise id to set on the client session when created.
+ QuicStreamId max_allowed_push_id_;
};
} // namespace quic