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),
diff --git a/quic/tools/quic_simple_server_session.cc b/quic/tools/quic_simple_server_session.cc index 7515936..8f7dcf8 100644 --- a/quic/tools/quic_simple_server_session.cc +++ b/quic/tools/quic_simple_server_session.cc
@@ -69,7 +69,7 @@ const std::string& request_url, const std::list<QuicBackendResponse::ServerPushInfo>& resources, QuicStreamId original_stream_id, - const spdy::SpdyStreamPrecedence& /*original_precedence*/, + const spdy::SpdyStreamPrecedence& original_precedence, const spdy::SpdyHeaderBlock& original_request_headers) { if (!server_push_enabled()) { return; @@ -82,9 +82,11 @@ QuicUtils::StreamIdDelta(connection()->transport_version()); SendPushPromise(original_stream_id, highest_promised_stream_id_, headers.Clone()); - promised_streams_.push_back( - PromisedStreamInfo(std::move(headers), highest_promised_stream_id_, - spdy::SpdyStreamPrecedence(resource.priority))); + promised_streams_.push_back(PromisedStreamInfo( + std::move(headers), highest_promised_stream_id_, + use_http2_priority_write_scheduler() + ? original_precedence + : spdy::SpdyStreamPrecedence(resource.priority))); } // Procese promised push request as many as possible.