gfe-relnote: Let GFE be able to use HTTP2(tree-style) priority write scheduler in QUIC and enable it via a connection option H2PR. Protected by gfe2_reloadable_flag_quic_use_http2_priority_write_scheduler.
PiperOrigin-RevId: 260938733
Change-Id: I6d3f6c325a07b17bdfd8c416add8327b8d54be8a
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 12afbd1..81f9730 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -197,6 +197,9 @@
const QuicTag kMTUH = TAG('M', 'T', 'U', 'H'); // High-target MTU discovery.
const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery.
+// Enable Priority scheme experiment.
+const QuicTag kH2PR = TAG('H', '2', 'P', 'R'); // HTTP2 priorities.
+
// Proof types (i.e. certificate types)
// NOTE: although it would be silly to do so, specifying both kX509 and kX59R
// is allowed and is equivalent to specifying only kX509.
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 2542aca..8bdc22f 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -343,6 +343,9 @@
GetQuicReloadableFlag(quic_enable_pcc3)) {
copt.push_back(kTPCC);
}
+ // TODO(fayang): Move this to GetTestParams when other priority connection
+ // opts are supported.
+ copt.push_back(kH2PR);
client_config_.SetConnectionOptionsToSend(copt);
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 0454ad2..7eeb8c3 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -170,8 +170,8 @@
void OnHeaders(SpdyStreamId stream_id,
bool has_priority,
int weight,
- SpdyStreamId /*parent_stream_id*/,
- bool /*exclusive*/,
+ SpdyStreamId parent_stream_id,
+ bool exclusive,
bool fin,
bool /*end*/) override {
if (!session_->IsConnected()) {
@@ -184,6 +184,15 @@
return;
}
+ if (session_->use_http2_priority_write_scheduler()) {
+ session_->OnHeaders(
+ stream_id, has_priority,
+ spdy::SpdyStreamPrecedence(parent_stream_id, weight, exclusive), fin);
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_http2_priority_write_scheduler, 1,
+ 3);
+ return;
+ }
+
SpdyPriority priority =
has_priority ? Http2WeightToSpdy3Priority(weight) : 0;
session_->OnHeaders(stream_id, has_priority,
@@ -213,9 +222,9 @@
void OnContinuation(SpdyStreamId /*stream_id*/, bool /*end*/) override {}
void OnPriority(SpdyStreamId stream_id,
- SpdyStreamId /*parent_id*/,
+ SpdyStreamId parent_id,
int weight,
- bool /*exclusive*/) override {
+ bool exclusive) override {
if (session_->connection()->transport_version() <= QUIC_VERSION_39) {
CloseConnection("SPDY PRIORITY frame received.",
QUIC_INVALID_HEADERS_STREAM_DATA);
@@ -224,8 +233,13 @@
if (!session_->IsConnected()) {
return;
}
- // TODO (wangyix): implement real HTTP/2 weights and dependencies instead of
- // converting to SpdyPriority.
+ if (session_->use_http2_priority_write_scheduler()) {
+ session_->OnPriority(
+ stream_id, spdy::SpdyStreamPrecedence(parent_id, weight, exclusive));
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_http2_priority_write_scheduler, 2,
+ 3);
+ return;
+ }
SpdyPriority priority = Http2WeightToSpdy3Priority(weight);
session_->OnPriority(stream_id, spdy::SpdyStreamPrecedence(priority));
}
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 964ddc1..1dbaf44 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -88,7 +88,8 @@
control_frame_manager_(this),
last_message_id_(0),
closed_streams_clean_up_alarm_(nullptr),
- supported_versions_(supported_versions) {
+ supported_versions_(supported_versions),
+ use_http2_priority_write_scheduler_(false) {
closed_streams_clean_up_alarm_ =
QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm(
new ClosedStreamsCleanUpDelegate(this)));
@@ -993,6 +994,13 @@
if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFWA)) {
AdjustInitialFlowControlWindows(1024 * 1024);
}
+ // Enable HTTP2 (tree-style) priority write scheduler.
+ if (GetQuicReloadableFlag(quic_use_http2_priority_write_scheduler) &&
+ ContainsQuicTag(config_.ReceivedConnectionOptions(), kH2PR) &&
+ !VersionHasIetfQuicFrames(connection_->transport_version())) {
+ use_http2_priority_write_scheduler_ =
+ write_blocked_streams_.UseHttp2PriorityScheduler();
+ }
}
config_.SetStatelessResetTokenToSend(GetStatelessResetToken());
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index ba5592a..d6f8a60 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -434,6 +434,10 @@
return connection_->transport_version();
}
+ bool use_http2_priority_write_scheduler() const {
+ return use_http2_priority_write_scheduler_;
+ }
+
protected:
using StreamMap = QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>;
@@ -721,6 +725,10 @@
// Supported version list used by the crypto handshake only. Please note, this
// list may be a superset of the connection framer's supported versions.
ParsedQuicVersionVector supported_versions_;
+
+ // If true, write_blocked_streams_ uses HTTP2 (tree-style) priority write
+ // scheduler.
+ bool use_http2_priority_write_scheduler_;
};
} // namespace quic
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index dce8ec6..267208f 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -917,6 +917,88 @@
session_.OnCanWrite();
}
+TEST_P(QuicSessionTestServer, Http2Priority) {
+ if (VersionHasIetfQuicFrames(GetParam().transport_version)) {
+ return;
+ }
+ SetQuicReloadableFlag(quic_use_http2_priority_write_scheduler, true);
+ QuicTagVector copt;
+ copt.push_back(kH2PR);
+ QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+ session_.OnConfigNegotiated();
+ ASSERT_TRUE(session_.use_http2_priority_write_scheduler());
+
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.set_writev_consumes_all_data(true);
+ /*
+ 0
+ /|\
+ 2 4 6
+ */
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+
+ // Verify streams are scheduled round robin.
+ InSequence s;
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream4, OnCanWrite());
+ EXPECT_CALL(*stream6, OnCanWrite());
+ session_.OnCanWrite();
+
+ /*
+ 0
+ |
+ 4
+ / \
+ 2 6
+ */
+ // Update stream 4's priority.
+ stream4->SetPriority(
+ spdy::SpdyStreamPrecedence(0, spdy::kHttp2DefaultStreamWeight, true));
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ EXPECT_CALL(*stream2, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_CALL(*stream6, OnCanWrite());
+ session_.OnCanWrite();
+
+ /*
+ 0
+ |
+ 6
+ |
+ 4
+ |
+ 2
+ */
+ // Update stream 6's priority.
+ stream6->SetPriority(
+ spdy::SpdyStreamPrecedence(0, spdy::kHttp2DefaultStreamWeight, true));
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+
+ EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() {
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ }));
+ EXPECT_CALL(*stream6, OnCanWrite());
+ EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_CALL(*stream2, OnCanWrite());
+ session_.OnCanWrite();
+}
+
TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) {
// Encryption needs to be established before data can be sent.
CryptoHandshakeMessage msg;
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index a44be4e..e8d6a01 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -236,7 +236,12 @@
: sequencer_(std::move(sequencer)),
id_(id),
session_(session),
- precedence_(spdy::SpdyStreamPrecedence(kDefaultPriority)),
+ precedence_(
+ session->use_http2_priority_write_scheduler()
+ ? spdy::SpdyStreamPrecedence(0,
+ spdy::kHttp2DefaultStreamWeight,
+ false)
+ : spdy::SpdyStreamPrecedence(kDefaultPriority)),
stream_bytes_read_(stream_bytes_read),
stream_error_(QUIC_STREAM_NO_ERROR),
connection_error_(QUIC_NO_ERROR),