@@ -14,7 +14,7 @@ import assert from 'node:assert';
1414import * as fixtures from '../common/fixtures.mjs' ;
1515const { readKey } = fixtures ;
1616
17- const { deepStrictEqual, strictEqual } = assert ;
17+ const { deepStrictEqual, strictEqual, rejects } = assert ;
1818
1919if ( ! hasQuic ) {
2020 skip ( 'QUIC is not enabled' ) ;
@@ -39,6 +39,7 @@ const decoder = new TextDecoder();
3939 const pri = stream . priority ;
4040 strictEqual ( typeof pri , 'object' ) ;
4141 strictEqual ( typeof pri . level , 'string' ) ;
42+ strictEqual ( typeof pri . sendOrder , 'number' ) ;
4243 strictEqual ( typeof pri . incremental , 'boolean' ) ;
4344 } , 4 ) ;
4445 } ) , {
@@ -75,7 +76,7 @@ const decoder = new TextDecoder();
7576 } ) ;
7677
7778 // Priority reflects what was set at creation.
78- deepStrictEqual ( stream1 . priority , { level : 'high' , incremental : false } ) ;
79+ deepStrictEqual ( stream1 . priority , { level : 'high' , sendOrder : 0 , incremental : false } ) ;
7980
8081 // Priority 'low' + incremental at creation.
8182 const stream2 = await clientSession . createBidirectionalStream ( {
@@ -91,7 +92,7 @@ const decoder = new TextDecoder();
9192 strictEqual ( headers [ ':status' ] , '200' ) ;
9293 } ) ,
9394 } ) ;
94- deepStrictEqual ( stream2 . priority , { level : 'low' , incremental : true } ) ;
95+ deepStrictEqual ( stream2 . priority , { level : 'low' , sendOrder : 7 , incremental : true } ) ;
9596
9697 // Default priority at creation.
9798 const stream3 = await clientSession . createBidirectionalStream ( {
@@ -105,7 +106,7 @@ const decoder = new TextDecoder();
105106 strictEqual ( headers [ ':status' ] , '200' ) ;
106107 } ) ,
107108 } ) ;
108- deepStrictEqual ( stream3 . priority , { level : 'default' , incremental : false } ) ;
109+ deepStrictEqual ( stream3 . priority , { level : 'default' , sendOrder : 3 , incremental : false } ) ;
109110
110111 // setPriority after creation.
111112 const stream4 = await clientSession . createBidirectionalStream ( {
@@ -120,19 +121,19 @@ const decoder = new TextDecoder();
120121 } ) ,
121122 } ) ;
122123 // Default priority initially.
123- deepStrictEqual ( stream4 . priority , { level : 'default' , incremental : false } ) ;
124+ deepStrictEqual ( stream4 . priority , { level : 'default' , sendOrder : 3 , incremental : false } ) ;
124125
125126 // Change to high.
126127 stream4 . setPriority ( { level : 'high' } ) ;
127- deepStrictEqual ( stream4 . priority , { level : 'high' , incremental : false } ) ;
128+ deepStrictEqual ( stream4 . priority , { level : 'high' , sendOrder : 0 , incremental : false } ) ;
128129
129130 // Change to incremental.
130131 stream4 . setPriority ( { level : 'low' , incremental : true } ) ;
131- deepStrictEqual ( stream4 . priority , { level : 'low' , incremental : true } ) ;
132+ deepStrictEqual ( stream4 . priority , { level : 'low' , sendOrder : 7 , incremental : true } ) ;
132133
133134 // Back to default.
134135 stream4 . setPriority ( { level : 'default' , incremental : false } ) ;
135- deepStrictEqual ( stream4 . priority , { level : 'default' , incremental : false } ) ;
136+ deepStrictEqual ( stream4 . priority , { level : 'default' , sendOrder : 3 , incremental : false } ) ;
136137
137138 // Read all bodies.
138139 const allBodies = await Promise . all ( [
@@ -177,7 +178,7 @@ const decoder = new TextDecoder();
177178
178179 // The server's priority getter should reflect the
179180 // client's PRIORITY_UPDATE (high, incremental).
180- deepStrictEqual ( stream . priority , { level : 'high' , incremental : true } ) ;
181+ deepStrictEqual ( stream . priority , { level : 'high' , sendOrder : 0 , incremental : true } ) ;
181182 serverSawHighPriority . resolve ( ) ;
182183
183184 await stream . closed ;
@@ -221,14 +222,14 @@ const decoder = new TextDecoder();
221222 strictEqual ( headers [ ':status' ] , '200' ) ;
222223 } ) ,
223224 } ) ;
224- deepStrictEqual ( stream . priority , { level : 'default' , incremental : false } ) ;
225+ deepStrictEqual ( stream . priority , { level : 'default' , sendOrder : 3 , incremental : false } ) ;
225226
226227 // Change priority — this sends a PRIORITY_UPDATE frame on the
227228 // control stream. The body data was already provided at creation
228229 // but the PRIORITY_UPDATE travels on the control stream which
229230 // nghttp3 prioritizes over bidi streams.
230231 stream . setPriority ( { level : 'high' , incremental : true } ) ;
231- deepStrictEqual ( stream . priority , { level : 'high' , incremental : true } ) ;
232+ deepStrictEqual ( stream . priority , { level : 'high' , sendOrder : 0 , incremental : true } ) ;
232233
233234 // Read the response.
234235 const body = await bytes ( stream ) ;
@@ -241,3 +242,75 @@ const decoder = new TextDecoder();
241242 await clientSession . close ( ) ;
242243 await serverEndpoint . close ( ) ;
243244}
245+
246+ // Test: sendOrder option and validation.
247+ {
248+ const serverEndpoint = await listen ( mustCall ( async ( ss ) => {
249+ ss . onstream = mustCall ( ( stream ) => {
250+ deepStrictEqual ( stream . priority , { level : 'high' , sendOrder : 1 , incremental : false } ) ;
251+ } ) ;
252+ } ) , {
253+ sni : { '*' : { keys : [ key ] , certs : [ cert ] } } ,
254+ onheaders : mustCall ( function ( headers ) {
255+ this . sendHeaders ( { ':status' : '200' } ) ;
256+ this . writer . writeSync ( encoder . encode ( 'ok' ) ) ;
257+ this . writer . endSync ( ) ;
258+ } ) ,
259+ } ) ;
260+
261+ const clientSession = await connect ( serverEndpoint . address , {
262+ servername : 'localhost' ,
263+ verifyPeer : 'manual' ,
264+ } ) ;
265+ await clientSession . opened ;
266+
267+ // Set sendOrder at creation time.
268+ const stream1 = await clientSession . createBidirectionalStream ( {
269+ headers : {
270+ ':method' : 'GET' ,
271+ ':path' : '/' ,
272+ ':scheme' : 'https' ,
273+ ':authority' : 'localhost' ,
274+ } ,
275+ sendOrder : 1 ,
276+ incremental : false ,
277+ } ) ;
278+ deepStrictEqual ( stream1 . priority , { level : 'high' , sendOrder : 1 , incremental : false } ) ;
279+
280+ // Update sendOrder via setPriority.
281+ stream1 . setPriority ( { sendOrder : 6 , incremental : true } ) ;
282+ deepStrictEqual ( stream1 . priority , { level : 'low' , sendOrder : 6 , incremental : true } ) ;
283+
284+ // sendOrder accepts any integer per W3C long long semantics;
285+ // values outside [0, 7] are clamped to the QUIC urgency range.
286+ const streamNeg = await clientSession . createBidirectionalStream ( { sendOrder : - 100 } ) ;
287+ // Clamped to 0 (highest urgency)
288+ deepStrictEqual ( streamNeg . priority , { level : 'high' , sendOrder : 0 , incremental : false } ) ;
289+
290+ const streamLarge = await clientSession . createBidirectionalStream ( { sendOrder : 999 } ) ;
291+ // Clamped to 7 (lowest urgency)
292+ deepStrictEqual ( streamLarge . priority , { level : 'low' , sendOrder : 7 , incremental : false } ) ;
293+
294+ // Non-integer values are still rejected.
295+ await rejects (
296+ clientSession . createBidirectionalStream ( { sendOrder : 1.5 } ) ,
297+ { code : 'ERR_INVALID_ARG_TYPE' }
298+ ) ;
299+ await rejects (
300+ clientSession . createBidirectionalStream ( { sendOrder : '3' } ) ,
301+ { code : 'ERR_INVALID_ARG_TYPE' }
302+ ) ;
303+
304+ // setPriority also accepts and clamps out-of-range integers.
305+ stream1 . setPriority ( { sendOrder : - 5 } ) ;
306+ deepStrictEqual ( stream1 . priority , { level : 'high' , sendOrder : 0 , incremental : false } ) ;
307+ stream1 . setPriority ( { sendOrder : 100 } ) ;
308+ deepStrictEqual ( stream1 . priority , { level : 'low' , sendOrder : 7 , incremental : false } ) ;
309+
310+ const body = await bytes ( stream1 ) ;
311+ strictEqual ( decoder . decode ( body ) , 'ok' ) ;
312+
313+ await Promise . all ( [ stream1 . closed ] ) ;
314+ await clientSession . close ( ) ;
315+ await serverEndpoint . close ( ) ;
316+ }
0 commit comments