gfe-relnote: Implement sending the MAX_PUSH_ID frame when the client sets a non-zero maximum push id.
PiperOrigin-RevId: 263372703
Change-Id: If68ed7819648fdd7bd5ec968592d04f88467f5d2
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 14dd32c..d6c8486 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -280,7 +280,9 @@
}
client->UseConnectionIdLength(override_server_connection_id_length_);
client->UseClientConnectionIdLength(override_client_connection_id_length_);
- client->client()->set_max_allowed_push_id(kMaxQuicStreamId);
+ if (support_server_push_) {
+ client->client()->set_max_allowed_push_id(kMaxQuicStreamId);
+ }
client->Connect();
return client;
}
@@ -4114,6 +4116,29 @@
EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error());
}
+TEST_P(EndToEndTest, TestMaxPushId) {
+ // Has to be before version test, see EndToEndTest::TearDown()
+ ASSERT_TRUE(Initialize());
+ if (!VersionHasIetfQuicFrames(negotiated_version_.transport_version)) {
+ // Only runs for IETF QUIC.
+ return;
+ }
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ static_cast<QuicSpdySession*>(client_->client()->session())
+ ->set_max_allowed_push_id(kMaxQuicStreamId);
+
+ client_->SendSynchronousRequest("/foo");
+
+ EXPECT_EQ(kMaxQuicStreamId,
+ static_cast<QuicSpdySession*>(client_->client()->session())
+ ->max_allowed_push_id());
+
+ EXPECT_EQ(
+ kMaxQuicStreamId,
+ static_cast<QuicSpdySession*>(GetServerSession())->max_allowed_push_id());
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/http/quic_headers_stream_test.cc b/quic/core/http/quic_headers_stream_test.cc
index e3e03dd..2035d47 100644
--- a/quic/core/http/quic_headers_stream_test.cc
+++ b/quic/core/http/quic_headers_stream_test.cc
@@ -397,6 +397,8 @@
}
TEST_P(QuicHeadersStreamTest, WritePushPromises) {
+ session_.set_max_allowed_push_id(kMaxQuicStreamId);
+
for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
stream_id += next_stream_id_) {
QuicStreamId promised_stream_id = NextPromisedStreamId();
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
index f899e78..1808a92 100644
--- a/quic/core/http/quic_receive_control_stream.cc
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -52,7 +52,13 @@
return false;
}
- bool OnMaxPushIdFrame(const MaxPushIdFrame& /*frame*/) override {
+ bool OnMaxPushIdFrame(const MaxPushIdFrame& frame) override {
+ if (stream_->session()->perspective() == Perspective::IS_SERVER) {
+ QuicSpdySession* spdy_session =
+ static_cast<QuicSpdySession*>(stream_->session());
+ spdy_session->set_max_allowed_push_id(frame.push_id);
+ return true;
+ }
CloseConnectionOnWrongFrame("Max Push Id");
return false;
}
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc
index 53ff3ef..e033b61 100644
--- a/quic/core/http/quic_send_control_stream.cc
+++ b/quic/core/http/quic_send_control_stream.cc
@@ -65,4 +65,15 @@
nullptr);
}
+void QuicSendControlStream::SendMaxPushIdFrame(PushId max_push_id) {
+ QuicConnection::ScopedPacketFlusher flusher(session()->connection());
+
+ MaxPushIdFrame frame;
+ frame.push_id = max_push_id;
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount frame_length = encoder_.SerializeMaxPushIdFrame(frame, &buffer);
+ WriteOrBufferData(QuicStringPiece(buffer.get(), frame_length),
+ /*fin = */ false, nullptr);
+}
+
} // namespace quic
diff --git a/quic/core/http/quic_send_control_stream.h b/quic/core/http/quic_send_control_stream.h
index 566bcc8..b38ae95 100644
--- a/quic/core/http/quic_send_control_stream.h
+++ b/quic/core/http/quic_send_control_stream.h
@@ -34,6 +34,9 @@
// stream. Settings frame must be the first frame sent on this stream.
void SendSettingsFrame();
+ // Construct a MAX_PUSH_ID frame and send it on this stream.
+ void SendMaxPushIdFrame(PushId max_push_id);
+
// Send |Priority| on this stream. It must be sent after settings.
void WritePriority(const PriorityFrame& priority);
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc
index d5cdb28..a2a9aae 100644
--- a/quic/core/http/quic_spdy_client_session_base.cc
+++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -24,8 +24,7 @@
: QuicSpdySession(connection, nullptr, config, supported_versions),
push_promise_index_(push_promise_index),
largest_promised_stream_id_(
- QuicUtils::GetInvalidStreamId(connection->transport_version())),
- max_allowed_push_id_(0) {}
+ QuicUtils::GetInvalidStreamId(connection->transport_version())) {}
QuicSpdyClientSessionBase::~QuicSpdyClientSessionBase() {
// all promised streams for this session
@@ -43,6 +42,10 @@
void QuicSpdyClientSessionBase::OnCryptoHandshakeEvent(
CryptoHandshakeEvent event) {
QuicSpdySession::OnCryptoHandshakeEvent(event);
+ if (event == HANDSHAKE_CONFIRMED && max_allowed_push_id() > 0 &&
+ VersionHasIetfQuicFrames(connection()->transport_version())) {
+ SendMaxPushId(max_allowed_push_id());
+ }
}
void QuicSpdyClientSessionBase::OnInitialHeadersComplete(
@@ -193,6 +196,8 @@
push_promise_index_->promised_by_url()->erase(promised->url());
// Since promised_by_id_ contains the unique_ptr, this will destroy
// promised.
+ // ToDo: Consider implementing logic to send a new MAX_PUSH_ID frame to allow
+ // another stream to be promised.
promised_by_id_.erase(promised->id());
if (!VersionUsesQpack(connection()->transport_version())) {
headers_stream()->MaybeReleaseSequencerBuffer();
@@ -223,9 +228,4 @@
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 98b8589..aec5e75 100644
--- a/quic/core/http/quic_spdy_client_session_base.h
+++ b/quic/core/http/quic_spdy_client_session_base.h
@@ -111,10 +111,6 @@
// 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;
@@ -138,7 +134,6 @@
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_session.cc b/quic/core/http/quic_spdy_session.cc
index 90560ad..e6ec954 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -320,7 +320,8 @@
frame_len_(0),
supports_push_promise_(perspective() == Perspective::IS_CLIENT),
spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
- spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
+ spdy_framer_visitor_(new SpdyFramerVisitor(this)),
+ max_allowed_push_id_(0) {
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());
@@ -514,6 +515,13 @@
return;
}
+ if (VersionHasIetfQuicFrames(connection()->transport_version()) &&
+ promised_stream_id > max_allowed_push_id()) {
+ QUIC_BUG
+ << "Server shouldn't send push id higher than client's MAX_PUSH_ID.";
+ return;
+ }
+
if (!VersionHasStreamType(connection()->transport_version())) {
SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
std::move(headers));
@@ -916,4 +924,25 @@
}
}
+void QuicSpdySession::set_max_allowed_push_id(
+ QuicStreamId max_allowed_push_id) {
+ if (VersionHasIetfQuicFrames(connection()->transport_version()) &&
+ perspective() == Perspective::IS_SERVER &&
+ max_allowed_push_id > max_allowed_push_id_) {
+ OnCanCreateNewOutgoingStream(true);
+ }
+
+ max_allowed_push_id_ = max_allowed_push_id;
+
+ if (VersionHasIetfQuicFrames(connection()->transport_version()) &&
+ perspective() == Perspective::IS_CLIENT && IsHandshakeConfirmed()) {
+ SendMaxPushId(max_allowed_push_id);
+ }
+}
+
+void QuicSpdySession::SendMaxPushId(QuicStreamId max_allowed_push_id) {
+ DCHECK(VersionHasStreamType(connection()->transport_version()));
+ send_control_stream_->SendMaxPushIdFrame(max_allowed_push_id);
+}
+
} // namespace quic
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 68becb6..ce8fff0 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -186,6 +186,10 @@
// those streams are not initialized yet.
void OnCanCreateNewOutgoingStream(bool unidirectional) override;
+ void set_max_allowed_push_id(QuicStreamId max_allowed_push_id);
+
+ QuicStreamId max_allowed_push_id() { return max_allowed_push_id_; }
+
protected:
// Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and
// CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to
@@ -252,6 +256,11 @@
// Initializes HTTP/3 unidirectional streams if not yet initialzed.
virtual void MaybeInitializeHttp3UnidirectionalStreams();
+ void set_max_uncompressed_header_bytes(
+ size_t set_max_uncompressed_header_bytes);
+
+ void SendMaxPushId(QuicStreamId max_allowed_push_id);
+
private:
friend class test::QuicSpdySessionPeer;
@@ -308,6 +317,7 @@
// TODO(renjietang): Replace these two members with actual QPACK send streams.
NoopQpackStreamSenderDelegate encoder_stream_sender_delegate_;
NoopQpackStreamSenderDelegate decoder_stream_sender_delegate_;
+ QuicStreamId max_allowed_push_id_;
};
} // namespace quic
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 57ced3a..cbfff08 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -575,6 +575,8 @@
return false;
}
+ bool IsHandshakeConfirmed() { return is_handshake_confirmed_; }
+
private:
friend class test::QuicSessionPeer;
diff --git a/quic/tools/quic_simple_server_session.cc b/quic/tools/quic_simple_server_session.cc
index cf2c569..d06cd4b 100644
--- a/quic/tools/quic_simple_server_session.cc
+++ b/quic/tools/quic_simple_server_session.cc
@@ -76,6 +76,10 @@
request_url, resource, original_request_headers);
highest_promised_stream_id_ +=
QuicUtils::StreamIdDelta(connection()->transport_version());
+ if (VersionHasIetfQuicFrames(connection()->transport_version()) &&
+ highest_promised_stream_id_ > max_allowed_push_id()) {
+ return;
+ }
SendPushPromise(original_stream_id, highest_promised_stream_id_,
headers.Clone());
promised_streams_.push_back(PromisedStreamInfo(
diff --git a/quic/tools/quic_simple_server_session_test.cc b/quic/tools/quic_simple_server_session_test.cc
index 7fbce71..10661ba 100644
--- a/quic/tools/quic_simple_server_session_test.cc
+++ b/quic/tools/quic_simple_server_session_test.cc
@@ -711,6 +711,7 @@
// opened and send push response.
TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) {
MaybeConsumeHeadersStreamData();
+ session_->set_max_allowed_push_id(kMaxQuicStreamId);
size_t num_resources = kMaxStreamsForTest + 5;
PromisePushResources(num_resources);
EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
@@ -721,6 +722,7 @@
TEST_P(QuicSimpleServerSessionServerPushTest,
HandlePromisedPushRequestsAfterStreamDraining) {
MaybeConsumeHeadersStreamData();
+ session_->set_max_allowed_push_id(kMaxQuicStreamId);
size_t num_resources = kMaxStreamsForTest + 1;
QuicByteCount data_frame_header_length = PromisePushResources(num_resources);
QuicStreamId next_out_going_stream_id;
@@ -789,6 +791,7 @@
TEST_P(QuicSimpleServerSessionServerPushTest,
ResetPromisedStreamToCancelServerPush) {
MaybeConsumeHeadersStreamData();
+ session_->set_max_allowed_push_id(kMaxQuicStreamId);
// Having two extra resources to be send later. One of them will be reset, so
// when opened stream become close, only one will become open.
@@ -877,6 +880,7 @@
TEST_P(QuicSimpleServerSessionServerPushTest,
CloseStreamToHandleMorePromisedStream) {
MaybeConsumeHeadersStreamData();
+ session_->set_max_allowed_push_id(kMaxQuicStreamId);
size_t num_resources = kMaxStreamsForTest + 1;
if (VersionHasIetfQuicFrames(transport_version())) {
// V99 will send out a stream-id-blocked frame when the we desired to exceed