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