Sent PRIORITY_UPDATE frame when priority changes.

gfe-relnote: n/a, change to QUIC v99-only code.  Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 292458752
Change-Id: I210de2980d1e79d9654ea16fd20ab663200fb2ed
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index 437e0bf..c75a268 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -229,7 +229,7 @@
 
   // Send PRIORITY_UPDATE frame and update |last_sent_urgency_| if
   // |last_sent_urgency_| is different from current priority.
-  void MaybeSendPriorityUpdateFrame();
+  void MaybeSendPriorityUpdateFrame() override;
 
  protected:
   // Called when the received headers are too large. By default this will
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index decba06..5d0a162 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -1372,6 +1372,54 @@
   EXPECT_TRUE(stream_->fin_sent());
 }
 
+TEST_P(QuicSpdyStreamTest, ChangePriority) {
+  if (!UsesHttp3()) {
+    return;
+  }
+
+  InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT);
+
+  // Two writes on the request stream: HEADERS frame header and payload.
+  EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _)).Times(2);
+  EXPECT_CALL(*stream_, WriteHeadersMock(false));
+  // 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(), _, _, _));
+  stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
+  testing::Mock::VerifyAndClearExpectations(session_.get());
+
+  // Another PRIORITY_UPDATE frame.
+  EXPECT_CALL(*session_, WritevData(send_control_stream,
+                                    send_control_stream->id(), _, _, _));
+  stream_->SetPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority));
+}
+
+TEST_P(QuicSpdyStreamTest, ChangePriorityBeforeWritingHeaders) {
+  if (!UsesHttp3()) {
+    return;
+  }
+
+  InitializeWithPerspective(kShouldProcessData, Perspective::IS_CLIENT);
+
+  // PRIORITY_UPDATE frame sent on the control stream as soon as SetPriority()
+  // is called, before HEADERS frame is sent.
+  auto send_control_stream =
+      QuicSpdySessionPeer::GetSendControlStream(session_.get());
+  EXPECT_CALL(*session_, WritevData(send_control_stream,
+                                    send_control_stream->id(), _, _, _));
+
+  stream_->SetPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority));
+  testing::Mock::VerifyAndClearExpectations(session_.get());
+
+  // Two writes on the request stream: HEADERS frame header and payload.
+  // PRIORITY_UPDATE frame is not sent this time, because one is already sent.
+  EXPECT_CALL(*session_, WritevData(stream_, stream_->id(), _, _, _)).Times(2);
+  EXPECT_CALL(*stream_, WriteHeadersMock(true));
+  stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/true, nullptr);
+}
+
 // 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) {
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index a440a13..bcdf0d9 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -562,6 +562,9 @@
 
 void QuicStream::SetPriority(const spdy::SpdyStreamPrecedence& precedence) {
   precedence_ = precedence;
+
+  MaybeSendPriorityUpdateFrame();
+
   session_->UpdateStreamPriority(id(), precedence);
 }
 
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index 42bdb5c..bd680f6 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -179,8 +179,14 @@
 
   const spdy::SpdyStreamPrecedence& precedence() const;
 
-  // Sets priority_ to priority.  This should only be called before bytes are
-  // written to the server.
+  // Send PRIORITY_UPDATE frame if application protocol supports it.
+  virtual void MaybeSendPriorityUpdateFrame() {}
+
+  // Sets |priority_| to priority.  This should only be called before bytes are
+  // written to the server.  For a server stream, this is called when a
+  // PRIORITY_UPDATE frame is received.  This calls
+  // MaybeSendPriorityUpdateFrame(), which for a client stream might send a
+  // PRIORITY_UPDATE frame.
   void SetPriority(const spdy::SpdyStreamPrecedence& precedence);
 
   // Returns true if this stream is still waiting for acks of sent data.