Do not send PRIORITY_UPDATE frame if it does not change priority. gfe-relnote: n/a, change to QUIC v99-only code. Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99. PiperOrigin-RevId: 292388791 Change-Id: I8e146591be22f57026f89231f847080299506ef6
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc index d69299e..b393ee3 100644 --- a/quic/core/http/quic_spdy_stream.cc +++ b/quic/core/http/quic_spdy_stream.cc
@@ -202,7 +202,8 @@ decoder_(http_decoder_visitor_.get()), sequencer_offset_(0), is_decoder_processing_input_(false), - ack_listener_(nullptr) { + ack_listener_(nullptr), + last_sent_urgency_(kDefaultUrgency) { DCHECK_EQ(session()->connection(), spdy_session->connection()); DCHECK_EQ(transport_version(), spdy_session->transport_version()); DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id)); @@ -238,7 +239,8 @@ decoder_(http_decoder_visitor_.get()), sequencer_offset_(sequencer()->NumBytesConsumed()), is_decoder_processing_input_(false), - ack_listener_(nullptr) { + ack_listener_(nullptr), + last_sent_urgency_(kDefaultUrgency) { DCHECK_EQ(session()->connection(), spdy_session->connection()); DCHECK_EQ(transport_version(), spdy_session->transport_version()); DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id())); @@ -591,6 +593,10 @@ // Value between 0 and 7, inclusive. Lower value means higher priority. int urgency = precedence().spdy3_priority(); + if (last_sent_urgency_ == urgency) { + return; + } + last_sent_urgency_ = urgency; PriorityUpdateFrame priority_update; priority_update.prioritized_element_type = REQUEST_STREAM;
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h index e6dbb50..437e0bf 100644 --- a/quic/core/http/quic_spdy_stream.h +++ b/quic/core/http/quic_spdy_stream.h
@@ -43,6 +43,12 @@ : public QuicStream, public QpackDecodedHeadersAccumulator::Visitor { public: + // The default value of urgency when using the priority extension defined at + // https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html#default. + // This is not the default priority of a stream, rather the default priority a + // server thinks a stream has until it receives a PRIORITY_UPDATE frame. + static const int kDefaultUrgency = 1; + // Visitor receives callbacks from the stream. class QUIC_EXPORT_PRIVATE Visitor { public: @@ -334,6 +340,10 @@ // Offset of unacked frame headers. QuicIntervalSet<QuicStreamOffset> unacked_frame_headers_offsets_; + + // Urgency value sent in the last PRIORITY_UPDATE frame, or default urgency + // defined by the spec if no PRIORITY_UPDATE frame has been sent. + int last_sent_urgency_; }; } // namespace quic
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc index 1e9bbff..decba06 100644 --- a/quic/core/http/quic_spdy_stream_test.cc +++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -1339,6 +1339,39 @@ EXPECT_TRUE(stream_->fin_sent()); } +TEST_P(QuicSpdyStreamTest, DoNotSendPriorityUpdateWithDefaultUrgency) { + 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); + + stream_->SetPriority( + spdy::SpdyStreamPrecedence(QuicSpdyStream::kDefaultUrgency)); + + // No PRIORITY_UPDATE frames on the control stream. + auto send_control_stream = + QuicSpdySessionPeer::GetSendControlStream(session_.get()); + EXPECT_CALL(*session_, WritevData(send_control_stream, + send_control_stream->id(), _, _, _)) + .Times(0); + + // 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) {