Skip to content

Commit a34e7b3

Browse files
committed
quic: update to support client side priority
1 parent 10b3d25 commit a34e7b3

4 files changed

Lines changed: 61 additions & 25 deletions

File tree

doc/api/quic.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,10 @@ The current priority of the stream. Returns `null` if the session does not
882882
support priority (e.g. non-HTTP/3) or if the stream has been destroyed.
883883
Read only. Use [`stream.setPriority()`][] to change the priority.
884884

885+
On client-side HTTP/3 sessions, the value reflects what was set via
886+
[`stream.setPriority()`][]. On server-side HTTP/3 sessions, the value
887+
reflects the peer's requested priority (e.g., from `PRIORITY_UPDATE` frames).
888+
885889
### `stream.setPriority([options])`
886890

887891
<!-- YAML
@@ -895,8 +899,9 @@ added: REPLACEME
895899
interleaved with data from other streams of the same priority level.
896900
**Default:** `false`.
897901

898-
Sets the priority of the stream. Has no effect if the session does not
899-
support priority or if the stream has been destroyed.
902+
Sets the priority of the stream. Throws `ERR_INVALID_STATE` if the session
903+
does not support priority (e.g. non-HTTP/3). Has no effect if the stream
904+
has been destroyed.
900905

901906
### `stream.readable`
902907

src/quic/http3.cc

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ inline uint64_t ReadBE64(const uint8_t* buf) {
6666
(static_cast<uint64_t>(buf[5]) << 16) |
6767
(static_cast<uint64_t>(buf[6]) << 8) | static_cast<uint64_t>(buf[7]);
6868
}
69+
70+
// Serialize an nghttp3_pri into an RFC 9218 priority field value
71+
// (e.g., "u=3" or "u=0, i"). Returns the number of bytes written.
72+
// This is used only for setting the priority field of HTTP/3 streams on
73+
// the client side.
74+
inline size_t FormatPriority(char* buf, size_t buflen, const nghttp3_pri& pri) {
75+
int len;
76+
if (pri.inc) {
77+
len = snprintf(buf, buflen, "u=%d, i", pri.urgency);
78+
} else {
79+
len = snprintf(buf, buflen, "u=%d", pri.urgency);
80+
}
81+
return static_cast<size_t>(len);
82+
}
6983
} // namespace
7084

7185
struct Http3HeadersTraits {
@@ -506,17 +520,24 @@ class Http3ApplicationImpl final : public Session::Application {
506520
}
507521
if (session().is_server()) {
508522
nghttp3_conn_set_server_stream_priority(*this, stream.id(), &pri);
523+
} else {
524+
// The client API takes a serialized RFC 9218 priority field value
525+
// (e.g., "u=0, i") rather than an nghttp3_pri struct.
526+
char buf[8];
527+
size_t len = FormatPriority(buf, sizeof(buf), pri);
528+
nghttp3_conn_set_client_stream_priority(
529+
*this, stream.id(), reinterpret_cast<const uint8_t*>(buf), len);
509530
}
510-
// Client-side priority is set at request submission time via
511-
// nghttp3_conn_submit_request and is not typically changed
512-
// after the fact. The client API takes a serialized RFC 9218
513-
// field value rather than an nghttp3_pri struct.
514531
}
515532

516533
StreamPriorityResult GetStreamPriority(const Stream& stream) override {
517-
// nghttp3_conn_get_stream_priority is only available on the server side.
534+
// nghttp3_conn_get_stream_priority is only available on the server
535+
// side, where it reflects the peer's requested priority (e.g., from
536+
// PRIORITY_UPDATE frames). Client-side priority is tracked by the
537+
// Stream itself and returned directly from GetPriority in streams.cc.
518538
if (!session().is_server()) {
519-
return {StreamPriority::DEFAULT, StreamPriorityFlags::NON_INCREMENTAL};
539+
auto& stored = stream.stored_priority();
540+
return {stored.priority, stored.flags};
520541
}
521542
nghttp3_pri pri;
522543
if (nghttp3_conn_get_stream_priority(*this, &pri, stream.id()) == 0) {

src/quic/streams.cc

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -367,12 +367,14 @@ struct Stream::Impl {
367367
? StreamPriorityFlags::INCREMENTAL
368368
: StreamPriorityFlags::NON_INCREMENTAL;
369369

370-
if (stream->is_pending()) {
371-
stream->pending_priority_ = PendingPriority{
372-
.priority = priority,
373-
.flags = flags,
374-
};
375-
} else {
370+
// Always update the stored priority on the stream.
371+
stream->priority_ = StoredPriority{
372+
.priority = priority,
373+
.flags = flags,
374+
.pending = stream->is_pending(),
375+
};
376+
377+
if (!stream->is_pending()) {
376378
stream->session().application().SetStreamPriority(
377379
*stream, priority, flags);
378380
}
@@ -382,9 +384,15 @@ struct Stream::Impl {
382384
Stream* stream;
383385
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
384386

385-
if (stream->is_pending()) {
386-
uint32_t packed =
387-
(static_cast<uint32_t>(StreamPriority::DEFAULT) << 1) | 0;
387+
// On the client side, priority is always read from the stream's
388+
// stored value since the client is the one setting it. On the
389+
// server side, we delegate to the application which can read
390+
// the peer's requested priority (e.g., from PRIORITY_UPDATE
391+
// frames in HTTP/3).
392+
if (!stream->session().is_server()) {
393+
auto& pri = stream->priority_;
394+
uint32_t packed = (static_cast<uint32_t>(pri.priority) << 1) |
395+
(pri.flags == StreamPriorityFlags::INCREMENTAL ? 1 : 0);
388396
return args.GetReturnValue().Set(packed);
389397
}
390398

@@ -979,11 +987,10 @@ void Stream::NotifyStreamOpened(stream_id id) {
979987
CHECK_EQ(ngtcp2_conn_set_stream_user_data(this->session(), id, this), 0);
980988
maybe_pending_stream_.reset();
981989

982-
if (pending_priority_) {
983-
auto& priority = pending_priority_.value();
990+
if (priority_.pending) {
984991
session().application().SetStreamPriority(
985-
*this, priority.priority, priority.flags);
986-
pending_priority_ = std::nullopt;
992+
*this, priority_.priority, priority_.flags);
993+
priority_.pending = false;
987994
}
988995
if (!pending_headers_queue_.empty()) {
989996
if (!session().application().SupportsHeaders()) {

src/quic/streams.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,11 +355,14 @@ class Stream final : public AsyncWrap,
355355
error_code pending_close_read_code_ = 0;
356356
error_code pending_close_write_code_ = 0;
357357

358-
struct PendingPriority {
359-
StreamPriority priority;
360-
StreamPriorityFlags flags;
358+
struct StoredPriority {
359+
StreamPriority priority = StreamPriority::DEFAULT;
360+
StreamPriorityFlags flags = StreamPriorityFlags::NON_INCREMENTAL;
361+
bool pending = false;
361362
};
362-
std::optional<PendingPriority> pending_priority_ = std::nullopt;
363+
StoredPriority priority_;
364+
365+
const StoredPriority& stored_priority() const { return priority_; }
363366

364367
// The headers_ field holds a block of headers that have been received and
365368
// are being buffered for delivery to the JavaScript side.

0 commit comments

Comments
 (0)