QuicStreamPriority refactor
Create QuicStreamPriority class in a dedicated build target, move parsing and
serializing functions and their tests together. Consolidate constants like
minimum, maximum and default values, and structured header dictionary keys "u"
and "i", and re-use them in both the parsing and serializing methods (previously
ParsePriorityFieldValue() had these values hardcoded).
https://github.com/google/quiche/issues/25
PiperOrigin-RevId: 494755193
diff --git a/build/source_list.bzl b/build/source_list.bzl
index 4d627c1..76eb89b 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -328,6 +328,7 @@
"quic/core/quic_stream.h",
"quic/core/quic_stream_frame_data_producer.h",
"quic/core/quic_stream_id_manager.h",
+ "quic/core/quic_stream_priority.h",
"quic/core/quic_stream_send_buffer.h",
"quic/core/quic_stream_sequencer.h",
"quic/core/quic_stream_sequencer_buffer.h",
@@ -650,6 +651,7 @@
"quic/core/quic_socket_address_coder.cc",
"quic/core/quic_stream.cc",
"quic/core/quic_stream_id_manager.cc",
+ "quic/core/quic_stream_priority.cc",
"quic/core/quic_stream_send_buffer.cc",
"quic/core/quic_stream_sequencer.cc",
"quic/core/quic_stream_sequencer_buffer.cc",
@@ -1226,6 +1228,7 @@
"quic/core/quic_session_test.cc",
"quic/core/quic_socket_address_coder_test.cc",
"quic/core/quic_stream_id_manager_test.cc",
+ "quic/core/quic_stream_priority_test.cc",
"quic/core/quic_stream_send_buffer_test.cc",
"quic/core/quic_stream_sequencer_buffer_test.cc",
"quic/core/quic_stream_sequencer_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 82132ae..78333b6 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -328,6 +328,7 @@
"src/quiche/quic/core/quic_stream.h",
"src/quiche/quic/core/quic_stream_frame_data_producer.h",
"src/quiche/quic/core/quic_stream_id_manager.h",
+ "src/quiche/quic/core/quic_stream_priority.h",
"src/quiche/quic/core/quic_stream_send_buffer.h",
"src/quiche/quic/core/quic_stream_sequencer.h",
"src/quiche/quic/core/quic_stream_sequencer_buffer.h",
@@ -650,6 +651,7 @@
"src/quiche/quic/core/quic_socket_address_coder.cc",
"src/quiche/quic/core/quic_stream.cc",
"src/quiche/quic/core/quic_stream_id_manager.cc",
+ "src/quiche/quic/core/quic_stream_priority.cc",
"src/quiche/quic/core/quic_stream_send_buffer.cc",
"src/quiche/quic/core/quic_stream_sequencer.cc",
"src/quiche/quic/core/quic_stream_sequencer_buffer.cc",
@@ -1226,6 +1228,7 @@
"src/quiche/quic/core/quic_session_test.cc",
"src/quiche/quic/core/quic_socket_address_coder_test.cc",
"src/quiche/quic/core/quic_stream_id_manager_test.cc",
+ "src/quiche/quic/core/quic_stream_priority_test.cc",
"src/quiche/quic/core/quic_stream_send_buffer_test.cc",
"src/quiche/quic/core/quic_stream_sequencer_buffer_test.cc",
"src/quiche/quic/core/quic_stream_sequencer_test.cc",
diff --git a/build/source_list.json b/build/source_list.json
index 3ded441..3811e8b 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -327,6 +327,7 @@
"quiche/quic/core/quic_stream.h",
"quiche/quic/core/quic_stream_frame_data_producer.h",
"quiche/quic/core/quic_stream_id_manager.h",
+ "quiche/quic/core/quic_stream_priority.h",
"quiche/quic/core/quic_stream_send_buffer.h",
"quiche/quic/core/quic_stream_sequencer.h",
"quiche/quic/core/quic_stream_sequencer_buffer.h",
@@ -649,6 +650,7 @@
"quiche/quic/core/quic_socket_address_coder.cc",
"quiche/quic/core/quic_stream.cc",
"quiche/quic/core/quic_stream_id_manager.cc",
+ "quiche/quic/core/quic_stream_priority.cc",
"quiche/quic/core/quic_stream_send_buffer.cc",
"quiche/quic/core/quic_stream_sequencer.cc",
"quiche/quic/core/quic_stream_sequencer_buffer.cc",
@@ -1225,6 +1227,7 @@
"quiche/quic/core/quic_session_test.cc",
"quiche/quic/core/quic_socket_address_coder_test.cc",
"quiche/quic/core/quic_stream_id_manager_test.cc",
+ "quiche/quic/core/quic_stream_priority_test.cc",
"quiche/quic/core/quic_stream_send_buffer_test.cc",
"quiche/quic/core/quic_stream_sequencer_buffer_test.cc",
"quiche/quic/core/quic_stream_sequencer_test.cc",
diff --git a/quiche/quic/core/http/quic_receive_control_stream.cc b/quiche/quic/core/http/quic_receive_control_stream.cc
index a35a925..b2b56d8 100644
--- a/quiche/quic/core/http/quic_receive_control_stream.cc
+++ b/quiche/quic/core/http/quic_receive_control_stream.cc
@@ -12,11 +12,11 @@
#include "quiche/quic/core/http/http_constants.h"
#include "quiche/quic/core/http/http_decoder.h"
#include "quiche/quic/core/http/quic_spdy_session.h"
+#include "quiche/quic/core/quic_stream_priority.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/common/quiche_text_utils.h"
-#include "quiche/common/structured_headers.h"
namespace quic {
@@ -149,8 +149,8 @@
}
const QuicStreamId stream_id = frame.prioritized_element_id;
- return spdy_session_->OnPriorityUpdateForRequestStream(stream_id,
- result.urgency);
+ return spdy_session_->OnPriorityUpdateForRequestStream(
+ stream_id, result.priority.urgency);
}
for (absl::string_view key_value :
@@ -225,45 +225,6 @@
return true;
}
-QuicReceiveControlStream::ParsePriorityFieldValueResult
-QuicReceiveControlStream::ParsePriorityFieldValue(
- absl::string_view priority_field_value) {
- // Default values
- int urgency = 3;
- bool incremental = false;
-
- absl::optional<quiche::structured_headers::Dictionary> parsed_dictionary =
- quiche::structured_headers::ParseDictionary(priority_field_value);
- if (!parsed_dictionary.has_value()) {
- return {false, urgency, incremental};
- }
-
- for (const auto& [name, value] : *parsed_dictionary) {
- if (value.member_is_inner_list) {
- continue;
- }
-
- const std::vector<quiche::structured_headers::ParameterizedItem>& member =
- value.member;
- if (member.size() != 1) {
- continue;
- }
-
- const quiche::structured_headers::Item item = member[0].item;
- if (name == "u" && item.is_integer()) {
- int parsed_urgency = item.GetInteger();
- // Ignore out-of-range values.
- if (parsed_urgency >= 0 && parsed_urgency <= 7) {
- urgency = parsed_urgency;
- }
- } else if (name == "i" && item.is_boolean()) {
- incremental = item.GetBoolean();
- }
- }
-
- return {true, urgency, incremental};
-}
-
bool QuicReceiveControlStream::OnUnknownFrameEnd() {
// Ignore unknown frame types.
return true;
diff --git a/quiche/quic/core/http/quic_receive_control_stream.h b/quiche/quic/core/http/quic_receive_control_stream.h
index e5eeb01..71d0bf2 100644
--- a/quiche/quic/core/http/quic_receive_control_stream.h
+++ b/quiche/quic/core/http/quic_receive_control_stream.h
@@ -20,13 +20,6 @@
: public QuicStream,
public HttpDecoder::Visitor {
public:
- // Return type of ParsePriorityFieldValue().
- struct ParsePriorityFieldValueResult {
- bool success;
- int urgency;
- bool incremental;
- };
-
explicit QuicReceiveControlStream(PendingStream* pending,
QuicSpdySession* spdy_session);
QuicReceiveControlStream(const QuicReceiveControlStream&) = delete;
@@ -67,10 +60,6 @@
QuicSpdySession* spdy_session() { return spdy_session_; }
- // Parses the Priority Field Value field of a PRIORITY_UPDATE frame.
- static ParsePriorityFieldValueResult ParsePriorityFieldValue(
- absl::string_view priority_field_value);
-
private:
// Called when a frame of allowed type is received. Returns true if the frame
// is allowed in this position. Returns false and resets the stream
diff --git a/quiche/quic/core/http/quic_receive_control_stream_test.cc b/quiche/quic/core/http/quic_receive_control_stream_test.cc
index 9181ba6..af7e5d4 100644
--- a/quiche/quic/core/http/quic_receive_control_stream_test.cc
+++ b/quiche/quic/core/http/quic_receive_control_stream_test.cc
@@ -456,71 +456,6 @@
/* offset = */ 1, unknown_frame));
}
-TEST(ParsePriorityFieldValueTest, ParsePriorityFieldValue) {
- // Default values
- QuicReceiveControlStream::ParsePriorityFieldValueResult result =
- QuicReceiveControlStream::ParsePriorityFieldValue("");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(3, result.urgency);
- EXPECT_FALSE(result.incremental);
-
- result = QuicReceiveControlStream::ParsePriorityFieldValue("i=?1");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(3, result.urgency);
- EXPECT_TRUE(result.incremental);
-
- result = QuicReceiveControlStream::ParsePriorityFieldValue("u=5");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(5, result.urgency);
- EXPECT_FALSE(result.incremental);
-
- result = QuicReceiveControlStream::ParsePriorityFieldValue("u=5, i");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(5, result.urgency);
- EXPECT_TRUE(result.incremental);
-
- result = QuicReceiveControlStream::ParsePriorityFieldValue("i, u=1");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(1, result.urgency);
- EXPECT_TRUE(result.incremental);
-
- // Duplicate values are allowed.
- result =
- QuicReceiveControlStream::ParsePriorityFieldValue("u=5, i=?1, i=?0, u=2");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(2, result.urgency);
- EXPECT_FALSE(result.incremental);
-
- // Unknown parameters MUST be ignored.
- result = QuicReceiveControlStream::ParsePriorityFieldValue("a=42, u=4, i=?0");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(4, result.urgency);
- EXPECT_FALSE(result.incremental);
-
- // Out-of-range values MUST be ignored.
- result = QuicReceiveControlStream::ParsePriorityFieldValue("u=-2, i");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(3, result.urgency);
- EXPECT_TRUE(result.incremental);
-
- // Values of unexpected types MUST be ignored.
- result =
- QuicReceiveControlStream::ParsePriorityFieldValue("u=4.2, i=\"foo\"");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(3, result.urgency);
- EXPECT_FALSE(result.incremental);
-
- // Values of the right type but different names are ignored.
- result = QuicReceiveControlStream::ParsePriorityFieldValue("a=4, b=?1");
- EXPECT_TRUE(result.success);
- EXPECT_EQ(3, result.urgency);
- EXPECT_FALSE(result.incremental);
-
- // Cannot be parsed as structured headers.
- result = QuicReceiveControlStream::ParsePriorityFieldValue("000");
- EXPECT_FALSE(result.success);
-}
-
} // namespace
} // namespace test
} // namespace quic
diff --git a/quiche/quic/core/http/quic_send_control_stream.cc b/quiche/quic/core/http/quic_send_control_stream.cc
index fbde32c..d3c2ef2 100644
--- a/quiche/quic/core/http/quic_send_control_stream.cc
+++ b/quiche/quic/core/http/quic_send_control_stream.cc
@@ -16,18 +16,10 @@
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/platform/api/quic_logging.h"
-#include "quiche/common/structured_headers.h"
namespace quic {
namespace {
-// See https://httpwg.org/specs/rfc9218.html for Priority Field Value format.
-constexpr absl::string_view kUrgencyKey = "u";
-constexpr absl::string_view kIncrementalKey = "i";
-constexpr int kMinimumUrgency = 0;
-constexpr int kMaximumUrgency = 7;
-constexpr bool kDefaultIncremental = false;
-
} // anonymous namespace
QuicSendControlStream::QuicSendControlStream(QuicStreamId id,
@@ -94,12 +86,12 @@
}
void QuicSendControlStream::WritePriorityUpdate(QuicStreamId stream_id,
- int urgency, bool incremental) {
+ QuicStreamPriority priority) {
QuicConnection::ScopedPacketFlusher flusher(session()->connection());
MaybeSendSettingsFrame();
const std::string priority_field_value =
- SerializePriorityFieldValue(urgency, incremental);
+ SerializePriorityFieldValue(priority);
PriorityUpdateFrame priority_update_frame{stream_id, priority_field_value};
if (spdy_session_->debug_visitor()) {
spdy_session_->debug_visitor()->OnPriorityUpdateFrameSent(
@@ -126,30 +118,4 @@
WriteOrBufferData(HttpEncoder::SerializeGoAwayFrame(frame), false, nullptr);
}
-std::string QuicSendControlStream::SerializePriorityFieldValue(
- int urgency, bool incremental) {
- quiche::structured_headers::Dictionary dictionary;
-
- if (urgency != QuicStream::kDefaultUrgency && urgency >= kMinimumUrgency &&
- urgency <= kMaximumUrgency) {
- dictionary[kUrgencyKey] = quiche::structured_headers::ParameterizedMember(
- quiche::structured_headers::Item(static_cast<int64_t>(urgency)), {});
- }
-
- if (incremental != kDefaultIncremental) {
- dictionary[kIncrementalKey] =
- quiche::structured_headers::ParameterizedMember(
- quiche::structured_headers::Item(incremental), {});
- }
-
- absl::optional<std::string> priority_field_value =
- quiche::structured_headers::SerializeDictionary(dictionary);
- if (!priority_field_value.has_value()) {
- QUICHE_BUG(priority_field_value_serialization_failed);
- return "";
- }
-
- return *priority_field_value;
-}
-
} // namespace quic
diff --git a/quiche/quic/core/http/quic_send_control_stream.h b/quiche/quic/core/http/quic_send_control_stream.h
index c76aaf9..6d0745c 100644
--- a/quiche/quic/core/http/quic_send_control_stream.h
+++ b/quiche/quic/core/http/quic_send_control_stream.h
@@ -7,6 +7,7 @@
#include "quiche/quic/core/http/http_encoder.h"
#include "quiche/quic/core/quic_stream.h"
+#include "quiche/quic/core/quic_stream_priority.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_export.h"
#include "quiche/quic/platform/api/quic_logging.h"
@@ -39,8 +40,7 @@
// Send a PRIORITY_UPDATE frame on this stream, and a SETTINGS frame
// beforehand if one has not been already sent.
- void WritePriorityUpdate(QuicStreamId stream_id, int urgency,
- bool incremental);
+ void WritePriorityUpdate(QuicStreamId stream_id, QuicStreamPriority priority);
// Send a GOAWAY frame on this stream, and a SETTINGS frame beforehand if one
// has not been already sent.
@@ -50,9 +50,6 @@
// never be called.
void OnDataAvailable() override { QUICHE_NOTREACHED(); }
- // Serialize the Priority Field Value for a PRIORITY_UPDATE frame.
- static std::string SerializePriorityFieldValue(int urgency, bool incremental);
-
private:
// Track if a settings frame is already sent.
bool settings_sent_;
diff --git a/quiche/quic/core/http/quic_send_control_stream_test.cc b/quiche/quic/core/http/quic_send_control_stream_test.cc
index 1627e78..20e96c6 100644
--- a/quiche/quic/core/http/quic_send_control_stream_test.cc
+++ b/quiche/quic/core/http/quic_send_control_stream_test.cc
@@ -252,13 +252,15 @@
EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _))
.Times(4);
send_control_stream_->WritePriorityUpdate(
- /* stream_id = */ 0, /* urgency = */ 3, /* incremental = */ false);
+ /* stream_id = */ 0,
+ QuicStreamPriority{/* urgency = */ 3, /* incremental = */ false});
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&session_));
EXPECT_CALL(session_, WritevData(send_control_stream_->id(), _, _, _, _, _));
send_control_stream_->WritePriorityUpdate(
- /* stream_id = */ 0, /* urgency = */ 3, /* incremental = */ false);
+ /* stream_id = */ 0,
+ QuicStreamPriority{/* urgency = */ 3, /* incremental = */ false});
}
TEST_P(QuicSendControlStreamTest, CloseControlStream) {
@@ -294,23 +296,6 @@
send_control_stream_->SendGoAway(stream_id);
}
-TEST(SerializePriorityFieldValueTest, SerializePriorityFieldValue) {
- // Default value is omitted.
- EXPECT_EQ("", QuicSendControlStream::SerializePriorityFieldValue(
- /* urgency = */ 3, /* incremental = */ false));
- EXPECT_EQ("u=5", QuicSendControlStream::SerializePriorityFieldValue(
- /* urgency = */ 5, /* incremental = */ false));
- EXPECT_EQ("i", QuicSendControlStream::SerializePriorityFieldValue(
- /* urgency = */ 3, /* incremental = */ true));
- EXPECT_EQ("u=0, i", QuicSendControlStream::SerializePriorityFieldValue(
- /* urgency = */ 0, /* incremental = */ true));
- // Out-of-bound value is ignored.
- EXPECT_EQ("", QuicSendControlStream::SerializePriorityFieldValue(
- /* urgency = */ -2, /* incremental = */ false));
- EXPECT_EQ("i", QuicSendControlStream::SerializePriorityFieldValue(
- /* urgency = */ 9, /* incremental = */ true));
-}
-
} // namespace
} // namespace test
} // namespace quic
diff --git a/quiche/quic/core/http/quic_spdy_session.cc b/quiche/quic/core/http/quic_spdy_session.cc
index 8827e01..ae83de6 100644
--- a/quiche/quic/core/http/quic_spdy_session.cc
+++ b/quiche/quic/core/http/quic_spdy_session.cc
@@ -706,10 +706,10 @@
}
void QuicSpdySession::WriteHttp3PriorityUpdate(QuicStreamId stream_id,
- int urgency, bool incremental) {
+ QuicStreamPriority priority) {
QUICHE_DCHECK(VersionUsesHttp3(transport_version()));
- send_control_stream_->WritePriorityUpdate(stream_id, urgency, incremental);
+ send_control_stream_->WritePriorityUpdate(stream_id, priority);
}
void QuicSpdySession::OnHttp3GoAway(uint64_t id) {
diff --git a/quiche/quic/core/http/quic_spdy_session.h b/quiche/quic/core/http/quic_spdy_session.h
index 0cb3f6b..60ff11a 100644
--- a/quiche/quic/core/http/quic_spdy_session.h
+++ b/quiche/quic/core/http/quic_spdy_session.h
@@ -26,6 +26,7 @@
#include "quiche/quic/core/qpack/qpack_receive_stream.h"
#include "quiche/quic/core/qpack/qpack_send_stream.h"
#include "quiche/quic/core/quic_session.h"
+#include "quiche/quic/core/quic_stream_priority.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
@@ -213,8 +214,8 @@
int weight, bool exclusive);
// Writes an HTTP/3 PRIORITY_UPDATE frame to the peer.
- void WriteHttp3PriorityUpdate(QuicStreamId stream_id, int urgency,
- bool incremental);
+ void WriteHttp3PriorityUpdate(QuicStreamId stream_id,
+ QuicStreamPriority priority);
// Process received HTTP/3 GOAWAY frame. When sent from server to client,
// |id| is a stream ID. When sent from client to server, |id| is a push ID.
diff --git a/quiche/quic/core/http/quic_spdy_session_test.cc b/quiche/quic/core/http/quic_spdy_session_test.cc
index a90e276..7410728 100644
--- a/quiche/quic/core/http/quic_spdy_session_test.cc
+++ b/quiche/quic/core/http/quic_spdy_session_test.cc
@@ -29,6 +29,7 @@
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_stream.h"
+#include "quiche/quic/core/quic_stream_priority.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/core/quic_versions.h"
@@ -2219,7 +2220,7 @@
// PRIORITY_UPDATE frame arrives after stream creation.
TestStream* stream1 = session_.CreateIncomingStream(stream_id1);
- EXPECT_EQ(QuicStream::kDefaultUrgency,
+ EXPECT_EQ(QuicStreamPriority::kDefaultUrgency,
stream1->precedence().spdy3_priority());
EXPECT_CALL(debug_visitor, OnPriorityUpdateFrameReceived(priority_update1));
session_.OnStreamFrame(data3);
diff --git a/quiche/quic/core/http/quic_spdy_stream.cc b/quiche/quic/core/http/quic_spdy_stream.cc
index b1f9db5..4cb89a8 100644
--- a/quiche/quic/core/http/quic_spdy_stream.cc
+++ b/quiche/quic/core/http/quic_spdy_stream.cc
@@ -190,8 +190,7 @@
HttpDecoderOptionsForBidiStream(spdy_session)),
sequencer_offset_(0),
is_decoder_processing_input_(false),
- ack_listener_(nullptr),
- last_sent_urgency_(kDefaultUrgency) {
+ ack_listener_(nullptr) {
QUICHE_DCHECK_EQ(session()->connection(), spdy_session->connection());
QUICHE_DCHECK_EQ(transport_version(), spdy_session->transport_version());
QUICHE_DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id));
@@ -225,8 +224,7 @@
decoder_(http_decoder_visitor_.get()),
sequencer_offset_(sequencer()->NumBytesConsumed()),
is_decoder_processing_input_(false),
- ack_listener_(nullptr),
- last_sent_urgency_(kDefaultUrgency) {
+ ack_listener_(nullptr) {
QUICHE_DCHECK_EQ(session()->connection(), spdy_session->connection());
QUICHE_DCHECK_EQ(transport_version(), spdy_session->transport_version());
QUICHE_DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id()));
@@ -588,14 +586,15 @@
}
// Value between 0 and 7, inclusive. Lower value means higher priority.
- const int urgency = precedence().spdy3_priority();
- if (last_sent_urgency_ == urgency) {
+ const uint8_t urgency = precedence().spdy3_priority();
+ const QuicStreamPriority priority{urgency,
+ QuicStreamPriority::kDefaultIncremental};
+ if (last_sent_priority_ == priority) {
return;
}
- last_sent_urgency_ = urgency;
+ last_sent_priority_ = priority;
- spdy_session_->WriteHttp3PriorityUpdate(id(), urgency,
- /* incremental = */ false);
+ spdy_session_->WriteHttp3PriorityUpdate(id(), priority);
}
void QuicSpdyStream::OnHeadersTooLarge() { Reset(QUIC_HEADERS_TOO_LARGE); }
diff --git a/quiche/quic/core/http/quic_spdy_stream.h b/quiche/quic/core/http/quic_spdy_stream.h
index a4ca50e..450e154 100644
--- a/quiche/quic/core/http/quic_spdy_stream.h
+++ b/quiche/quic/core/http/quic_spdy_stream.h
@@ -29,6 +29,7 @@
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_stream.h"
+#include "quiche/quic/core/quic_stream_priority.h"
#include "quiche/quic/core/quic_stream_sequencer.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/web_transport_interface.h"
@@ -225,8 +226,8 @@
QuicSpdySession* spdy_session() const { return spdy_session_; }
- // Send PRIORITY_UPDATE frame and update |last_sent_urgency_| if
- // |last_sent_urgency_| is different from current priority.
+ // Send PRIORITY_UPDATE frame and update |last_sent_priority_| if
+ // |last_sent_priority_| is different from current priority.
void MaybeSendPriorityUpdateFrame() override;
// Returns the WebTransport session owned by this stream, if one exists.
@@ -470,9 +471,9 @@
// 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_;
+ // Priority parameters sent in the last PRIORITY_UPDATE frame, or default
+ // values defined by RFC9218 if no PRIORITY_UPDATE frame has been sent.
+ QuicStreamPriority last_sent_priority_;
// If this stream is a WebTransport extended CONNECT stream, contains the
// WebTransport session associated with this stream.
diff --git a/quiche/quic/core/quic_stream.cc b/quiche/quic/core/quic_stream.cc
index 29a6d04..9a121cb 100644
--- a/quiche/quic/core/quic_stream.cc
+++ b/quiche/quic/core/quic_stream.cc
@@ -13,6 +13,7 @@
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_flow_controller.h"
#include "quiche/quic/core/quic_session.h"
+#include "quiche/quic/core/quic_stream_priority.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/core/quic_versions.h"
@@ -111,9 +112,6 @@
// static
const SpdyPriority QuicStream::kDefaultPriority;
-// static
-const int QuicStream::kDefaultUrgency;
-
PendingStream::PendingStream(QuicStreamId id, QuicSession* session)
: id_(id),
version_(session->version()),
@@ -1431,7 +1429,7 @@
const QuicSession* session) {
return spdy::SpdyStreamPrecedence(
VersionUsesHttp3(session->transport_version())
- ? kDefaultUrgency
+ ? QuicStreamPriority::kDefaultUrgency
: QuicStream::kDefaultPriority);
}
diff --git a/quiche/quic/core/quic_stream.h b/quiche/quic/core/quic_stream.h
index 39c895d..f8aff9a 100644
--- a/quiche/quic/core/quic_stream.h
+++ b/quiche/quic/core/quic_stream.h
@@ -161,10 +161,6 @@
virtual ~QuicStream();
- // Default priority for IETF QUIC, defined by the priority extension at
- // https://httpwg.org/http-extensions/draft-ietf-httpbis-priority.html#urgency.
- static const int kDefaultUrgency = 3;
-
// QuicStreamSequencer::StreamInterface implementation.
QuicStreamId id() const override { return id_; }
ParsedQuicVersion version() const override;
diff --git a/quiche/quic/core/quic_stream_priority.cc b/quiche/quic/core/quic_stream_priority.cc
new file mode 100644
index 0000000..377866a
--- /dev/null
+++ b/quiche/quic/core/quic_stream_priority.cc
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/quic_stream_priority.h"
+
+#include "quiche/common/platform/api/quiche_bug_tracker.h"
+#include "quiche/common/structured_headers.h"
+
+namespace quic {
+
+std::string SerializePriorityFieldValue(QuicStreamPriority priority) {
+ quiche::structured_headers::Dictionary dictionary;
+
+ if (priority.urgency != QuicStreamPriority::kDefaultUrgency &&
+ priority.urgency >= QuicStreamPriority::kMinimumUrgency &&
+ priority.urgency <= QuicStreamPriority::kMaximumUrgency) {
+ dictionary[QuicStreamPriority::kUrgencyKey] =
+ quiche::structured_headers::ParameterizedMember(
+ quiche::structured_headers::Item(
+ static_cast<int64_t>(priority.urgency)),
+ {});
+ }
+
+ if (priority.incremental != QuicStreamPriority::kDefaultIncremental) {
+ dictionary[QuicStreamPriority::kIncrementalKey] =
+ quiche::structured_headers::ParameterizedMember(
+ quiche::structured_headers::Item(priority.incremental), {});
+ }
+
+ absl::optional<std::string> priority_field_value =
+ quiche::structured_headers::SerializeDictionary(dictionary);
+ if (!priority_field_value.has_value()) {
+ QUICHE_BUG(priority_field_value_serialization_failed);
+ return "";
+ }
+
+ return *priority_field_value;
+}
+
+ParsePriorityFieldValueResult ParsePriorityFieldValue(
+ absl::string_view priority_field_value) {
+ absl::optional<quiche::structured_headers::Dictionary> parsed_dictionary =
+ quiche::structured_headers::ParseDictionary(priority_field_value);
+ if (!parsed_dictionary.has_value()) {
+ return {false, {}};
+ }
+
+ uint8_t urgency = QuicStreamPriority::kDefaultUrgency;
+ bool incremental = QuicStreamPriority::kDefaultIncremental;
+
+ for (const auto& [name, value] : *parsed_dictionary) {
+ if (value.member_is_inner_list) {
+ continue;
+ }
+
+ const std::vector<quiche::structured_headers::ParameterizedItem>& member =
+ value.member;
+ if (member.size() != 1) {
+ continue;
+ }
+
+ const quiche::structured_headers::Item item = member[0].item;
+ if (name == QuicStreamPriority::kUrgencyKey && item.is_integer()) {
+ int parsed_urgency = item.GetInteger();
+ // Ignore out-of-range values.
+ if (parsed_urgency >= QuicStreamPriority::kMinimumUrgency &&
+ parsed_urgency <= QuicStreamPriority::kMaximumUrgency) {
+ urgency = parsed_urgency;
+ }
+ } else if (name == QuicStreamPriority::kIncrementalKey &&
+ item.is_boolean()) {
+ incremental = item.GetBoolean();
+ }
+ }
+
+ return {true, {urgency, incremental}};
+}
+
+} // namespace quic
diff --git a/quiche/quic/core/quic_stream_priority.h b/quiche/quic/core/quic_stream_priority.h
new file mode 100644
index 0000000..33f1183
--- /dev/null
+++ b/quiche/quic/core/quic_stream_priority.h
@@ -0,0 +1,58 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_PRIORITY_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_PRIORITY_H_
+
+#include <cstdint>
+#include <string>
+#include <tuple>
+
+#include "absl/strings/string_view.h"
+#include "quiche/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Class to hold urgency and incremental values defined by
+// https://httpwg.org/specs/rfc9218.html.
+struct QUICHE_EXPORT QuicStreamPriority {
+ static constexpr int kMinimumUrgency = 0;
+ static constexpr int kMaximumUrgency = 7;
+ static constexpr int kDefaultUrgency = 3;
+ static constexpr bool kDefaultIncremental = false;
+
+ // Parameter names for Priority Field Value.
+ static constexpr absl::string_view kUrgencyKey = "u";
+ static constexpr absl::string_view kIncrementalKey = "i";
+
+ uint8_t urgency = kDefaultUrgency;
+ bool incremental = kDefaultIncremental;
+
+ bool operator==(const QuicStreamPriority& other) const {
+ return std::tie(urgency, incremental) ==
+ std::tie(other.urgency, other.incremental);
+ }
+
+ bool operator!=(const QuicStreamPriority& other) const {
+ return !operator==(other);
+ }
+};
+
+// Serializes the Priority Field Value for a PRIORITY_UPDATE frame.
+QUICHE_EXPORT std::string SerializePriorityFieldValue(
+ QuicStreamPriority priority);
+
+// Return type of ParsePriorityFieldValue().
+struct QUICHE_EXPORT ParsePriorityFieldValueResult {
+ bool success;
+ QuicStreamPriority priority;
+};
+
+// Parses the Priority Field Value field of a PRIORITY_UPDATE frame.
+QUICHE_EXPORT ParsePriorityFieldValueResult
+ParsePriorityFieldValue(absl::string_view priority_field_value);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_PRIORITY_H_
diff --git a/quiche/quic/core/quic_stream_priority_test.cc b/quiche/quic/core/quic_stream_priority_test.cc
new file mode 100644
index 0000000..50b6662
--- /dev/null
+++ b/quiche/quic/core/quic_stream_priority_test.cc
@@ -0,0 +1,107 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/quic_stream_priority.h"
+
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace quic::test {
+
+TEST(QuicStreamPriority, DefaultConstructed) {
+ QuicStreamPriority priority;
+
+ EXPECT_EQ(QuicStreamPriority::kDefaultUrgency, priority.urgency);
+ EXPECT_EQ(QuicStreamPriority::kDefaultIncremental, priority.incremental);
+}
+
+TEST(QuicStreamPriority, Equals) {
+ EXPECT_EQ((QuicStreamPriority()),
+ (QuicStreamPriority{QuicStreamPriority::kDefaultUrgency,
+ QuicStreamPriority::kDefaultIncremental}));
+ EXPECT_EQ((QuicStreamPriority{5, true}), (QuicStreamPriority{5, true}));
+ EXPECT_EQ((QuicStreamPriority{2, false}), (QuicStreamPriority{2, false}));
+
+ EXPECT_NE((QuicStreamPriority{1, true}), (QuicStreamPriority{3, true}));
+ EXPECT_NE((QuicStreamPriority{4, false}), (QuicStreamPriority{4, true}));
+ EXPECT_NE((QuicStreamPriority{6, true}), (QuicStreamPriority{2, false}));
+}
+
+TEST(SerializePriorityFieldValueTest, SerializePriorityFieldValue) {
+ // Default value is omitted.
+ EXPECT_EQ("", SerializePriorityFieldValue(
+ {/* urgency = */ 3, /* incremental = */ false}));
+ EXPECT_EQ("u=5", SerializePriorityFieldValue(
+ {/* urgency = */ 5, /* incremental = */ false}));
+ EXPECT_EQ("i", SerializePriorityFieldValue(
+ {/* urgency = */ 3, /* incremental = */ true}));
+ EXPECT_EQ("u=0, i", SerializePriorityFieldValue(
+ {/* urgency = */ 0, /* incremental = */ true}));
+ // Out-of-bound value is ignored.
+ EXPECT_EQ("i", SerializePriorityFieldValue(
+ {/* urgency = */ 9, /* incremental = */ true}));
+}
+
+TEST(ParsePriorityFieldValueTest, ParsePriorityFieldValue) {
+ // Default values
+ ParsePriorityFieldValueResult result = ParsePriorityFieldValue("");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(3, result.priority.urgency);
+ EXPECT_FALSE(result.priority.incremental);
+
+ result = ParsePriorityFieldValue("i=?1");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(3, result.priority.urgency);
+ EXPECT_TRUE(result.priority.incremental);
+
+ result = ParsePriorityFieldValue("u=5");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(5, result.priority.urgency);
+ EXPECT_FALSE(result.priority.incremental);
+
+ result = ParsePriorityFieldValue("u=5, i");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(5, result.priority.urgency);
+ EXPECT_TRUE(result.priority.incremental);
+
+ result = ParsePriorityFieldValue("i, u=1");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(1, result.priority.urgency);
+ EXPECT_TRUE(result.priority.incremental);
+
+ // Duplicate values are allowed.
+ result = ParsePriorityFieldValue("u=5, i=?1, i=?0, u=2");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(2, result.priority.urgency);
+ EXPECT_FALSE(result.priority.incremental);
+
+ // Unknown parameters MUST be ignored.
+ result = ParsePriorityFieldValue("a=42, u=4, i=?0");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(4, result.priority.urgency);
+ EXPECT_FALSE(result.priority.incremental);
+
+ // Out-of-range values MUST be ignored.
+ result = ParsePriorityFieldValue("u=-2, i");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(3, result.priority.urgency);
+ EXPECT_TRUE(result.priority.incremental);
+
+ // Values of unexpected types MUST be ignored.
+ result = ParsePriorityFieldValue("u=4.2, i=\"foo\"");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(3, result.priority.urgency);
+ EXPECT_FALSE(result.priority.incremental);
+
+ // Values of the right type but different names are ignored.
+ result = ParsePriorityFieldValue("a=4, b=?1");
+ EXPECT_TRUE(result.success);
+ EXPECT_EQ(3, result.priority.urgency);
+ EXPECT_FALSE(result.priority.incremental);
+
+ // Cannot be parsed as structured headers.
+ result = ParsePriorityFieldValue("000");
+ EXPECT_FALSE(result.success);
+}
+
+} // namespace quic::test