Write PRIORITY_UPDATE frame. gfe-relnote: n/a, change to QUIC v99-only code. Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99. PiperOrigin-RevId: 289127305 Change-Id: Ic515e3159362430055178010ee1c45e1a8e2c604
diff --git a/quic/core/http/quic_send_control_stream.cc b/quic/core/http/quic_send_control_stream.cc index 2da2118..eda0729 100644 --- a/quic/core/http/quic_send_control_stream.cc +++ b/quic/core/http/quic_send_control_stream.cc
@@ -72,6 +72,19 @@ settings_sent_ = true; } +void QuicSendControlStream::WritePriorityUpdate( + const PriorityUpdateFrame& priority_update) { + QuicConnection::ScopedPacketFlusher flusher(session()->connection()); + MaybeSendSettingsFrame(); + std::unique_ptr<char[]> buffer; + QuicByteCount frame_length = + HttpEncoder::SerializePriorityUpdateFrame(priority_update, &buffer); + QUIC_DVLOG(1) << "Control Stream " << id() << " is writing " + << priority_update; + WriteOrBufferData(quiche::QuicheStringPiece(buffer.get(), frame_length), + false, nullptr); +} + void QuicSendControlStream::SendMaxPushIdFrame(PushId max_push_id) { QuicConnection::ScopedPacketFlusher flusher(session()->connection());
diff --git a/quic/core/http/quic_send_control_stream.h b/quic/core/http/quic_send_control_stream.h index f8c9515..0095317 100644 --- a/quic/core/http/quic_send_control_stream.h +++ b/quic/core/http/quic_send_control_stream.h
@@ -38,10 +38,16 @@ // first frame sent on this stream. void MaybeSendSettingsFrame(); - // Construct a MAX_PUSH_ID frame and send it on this stream. + // Send a MAX_PUSH_ID frame on this stream, and a SETTINGS frame beforehand if + // one has not been already sent. void SendMaxPushIdFrame(PushId max_push_id); - // Serialize a GOAWAY frame from |stream_id| and send it on this stream. + // Send a PRIORITY_UPDATE frame on this stream, and a SETTINGS frame + // beforehand if one has not been already sent. + void WritePriorityUpdate(const PriorityUpdateFrame& priority_update); + + // Send a GOAWAY frame on this stream, and a SETTINGS frame beforehand if one + // has not been already sent. void SendGoAway(QuicStreamId stream_id); // The send control stream is write unidirectional, so this method should
diff --git a/quic/core/http/quic_send_control_stream_test.cc b/quic/core/http/quic_send_control_stream_test.cc index 5da6448..7a03ac3 100644 --- a/quic/core/http/quic_send_control_stream_test.cc +++ b/quic/core/http/quic_send_control_stream_test.cc
@@ -152,11 +152,26 @@ EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)); send_control_stream_->MaybeSendSettingsFrame(); - // No data should be written the sencond time MaybeSendSettingsFrame() is + // No data should be written the second time MaybeSendSettingsFrame() is // called. send_control_stream_->MaybeSendSettingsFrame(); } +// Send stream type and SETTINGS frame if WritePriorityUpdate() is called first. +TEST_P(QuicSendControlStreamTest, WritePriorityBeforeSettings) { + Initialize(); + testing::InSequence s; + + // The first write will trigger the control stream to write stream type and a + // SETTINGS frame before the PRIORITY_UPDATE frame. + EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)).Times(3); + PriorityUpdateFrame frame; + send_control_stream_->WritePriorityUpdate(frame); + + EXPECT_CALL(session_, WritevData(send_control_stream_, _, _, _, _)); + send_control_stream_->WritePriorityUpdate(frame); +} + TEST_P(QuicSendControlStreamTest, ResetControlStream) { Initialize(); QuicRstStreamFrame rst_frame(kInvalidControlFrameId,
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc index 209e46f..5df724c 100644 --- a/quic/core/http/quic_spdy_session.cc +++ b/quic/core/http/quic_spdy_session.cc
@@ -531,6 +531,13 @@ return frame.size(); } +void QuicSpdySession::WriteHttp3PriorityUpdate( + const PriorityUpdateFrame& priority_update) { + DCHECK(VersionUsesHttp3(transport_version())); + + send_control_stream_->WritePriorityUpdate(priority_update); +} + void QuicSpdySession::OnHttp3GoAway(QuicStreamId stream_id) { DCHECK_EQ(perspective(), Perspective::IS_CLIENT); if (!QuicUtils::IsBidirectionalStreamId(stream_id) ||
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h index b0a83e9..9f7349f 100644 --- a/quic/core/http/quic_spdy_session.h +++ b/quic/core/http/quic_spdy_session.h
@@ -141,14 +141,17 @@ const spdy::SpdyStreamPrecedence& precedence, QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener); - // Writes a PRIORITY frame the to peer. Returns the size in bytes of the - // resulting PRIORITY frame for QUIC_VERSION_43 and above. Otherwise, does + // Writes an HTTP/2 PRIORITY frame the to peer. Returns the size in bytes of + // the resulting PRIORITY frame for QUIC_VERSION_43 and above. Otherwise, does // nothing and returns 0. size_t WritePriority(QuicStreamId id, QuicStreamId parent_stream_id, int weight, bool exclusive); + // Writes an HTTP/3 PRIORITY_UPDATE frame to the peer. + void WriteHttp3PriorityUpdate(const PriorityUpdateFrame& priority_update); + // Process received HTTP/3 GOAWAY frame. This method should only be called on // the client side. virtual void OnHttp3GoAway(QuicStreamId stream_id);
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc index 819caef..195f92b 100644 --- a/quic/core/http/quic_spdy_stream.cc +++ b/quic/core/http/quic_spdy_stream.cc
@@ -1037,7 +1037,16 @@ std::move(ack_listener)); } - // TODO(b/147306124): Send PRIORITY_UPDATE frame. + if (!priority_sent_) { + PriorityUpdateFrame priority_update; + priority_update.prioritized_element_type = REQUEST_STREAM; + priority_update.prioritized_element_id = id(); + // Value between 0 and 7, inclusive. Lower value means higher priority. + int urgency = precedence().spdy3_priority(); + priority_update.priority_field_value = quiche::QuicheStrCat("u=", urgency); + spdy_session_->WriteHttp3PriorityUpdate(priority_update); + priority_sent_ = true; + } // Encode header list. QuicByteCount encoder_stream_sent_byte_count;
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc index 1a01015..164b119 100644 --- a/quic/core/http/quic_spdy_stream_test.cc +++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -228,19 +228,14 @@ EXPECT_CALL(*connection_, OnCanWrite()); } if (UsesHttp3()) { - // In this case, TestStream::WriteHeadersImpl() does not prevent writes. - // Six writes include priority for headers, headers frame header, headers - // frame, priority of trailers, trailing headers frame header, and - // trailers. - auto send_control_stream = - QuicSpdySessionPeer::GetSendControlStream(session_.get()); - // The control stream will write 3 times, including stream type, settings - // frame and max push id, priority for headers. + // The control stream will write the stream type and SETTINGS frame. int num_control_stream_writes = 2; if (session_->perspective() == Perspective::IS_CLIENT) { // The control stream also writes the max push id frame. num_control_stream_writes++; } + auto send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); EXPECT_CALL(*session_, WritevData(send_control_stream, send_control_stream->id(), _, _, _)) .Times(num_control_stream_writes); @@ -1264,8 +1259,15 @@ if (UsesHttp3()) { // In this case, TestStream::WriteHeadersImpl() does not prevent writes. + // Four writes on the request stream: HEADERS frame header and payload both + // for headers and trailers. EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _)) - .Times(AtLeast(1)); + .Times(4); + // PRIORITY_UPDATE frame on the control stream. + auto send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_CALL(*session_, WritevData(send_control_stream, + send_control_stream->id(), _, _, _)); } // Write the initial headers, without a FIN. @@ -1280,6 +1282,34 @@ EXPECT_TRUE(stream_->fin_sent()); } +TEST_P(QuicSpdyStreamTest, SendPriorityUpdate) { + if (!UsesHttp3()) { + return; + } + + InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT); + + // Four writes on the request stream: HEADERS frame header and payload both + // for headers and trailers. + EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _)).Times(4); + // PRIORITY_UPDATE frame on the control stream. + auto send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_CALL(*session_, WritevData(send_control_stream, + send_control_stream->id(), _, _, _)); + + // Write the initial headers, without a FIN. + EXPECT_CALL(*stream_, WriteHeadersMock(false)); + stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr); + + // Writing trailers implicitly sends a FIN. + SpdyHeaderBlock trailers; + trailers["trailer key"] = "trailer value"; + EXPECT_CALL(*stream_, WriteHeadersMock(true)); + stream_->WriteTrailers(std::move(trailers), nullptr); + EXPECT_TRUE(stream_->fin_sent()); +} + // Test that when writing trailers, the trailers that are actually sent to the // peer contain the final offset field indicating last byte of data. TEST_P(QuicSpdyStreamTest, WritingTrailersFinalOffset) { @@ -1287,8 +1317,14 @@ if (UsesHttp3()) { // In this case, TestStream::WriteHeadersImpl() does not prevent writes. + // HEADERS frame header and payload on the request stream. EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _)) - .Times(AtLeast(1)); + .Times(2); + // PRIORITY_UPDATE frame on the control stream. + auto send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_CALL(*session_, WritevData(send_control_stream, + send_control_stream->id(), _, _, _)); } // Write the initial headers. @@ -1333,6 +1369,13 @@ // also written on the stream in case of IETF QUIC. EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _)) .Times(AtLeast(1)); + if (UsesHttp3()) { + // PRIORITY_UPDATE frame. + auto send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_CALL(*session_, WritevData(send_control_stream, + send_control_stream->id(), _, _, _)); + } // Write the initial headers. EXPECT_CALL(*stream_, WriteHeadersMock(false));
diff --git a/quic/tools/quic_simple_server_session_test.cc b/quic/tools/quic_simple_server_session_test.cc index 4e3a50c..f416757 100644 --- a/quic/tools/quic_simple_server_session_test.cc +++ b/quic/tools/quic_simple_server_session_test.cc
@@ -38,6 +38,7 @@ #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" using testing::_; +using testing::AnyNumber; using testing::AtLeast; using testing::InSequence; using testing::Invoke; @@ -623,6 +624,14 @@ /*is_static=*/true, spdy::SpdyStreamPrecedence(QuicStream::kDefaultPriority)); } + if (VersionUsesHttp3(transport_version())) { + // Ignore writes on the control stream. + auto send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_CALL(*connection_, + SendStreamData(send_control_stream->id(), _, _, NO_FIN)) + .Times(AnyNumber()); + } } // Given |num_resources|, create this number of fake push resources and push