From 0a7e957e44d708adf4f50bce8f69debec5e7752d Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Tue, 8 Oct 2024 16:30:34 -0400 Subject: [PATCH 1/4] Replace datagrams.writable with datagrams.createWritable() --- index.bs | 217 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 139 insertions(+), 78 deletions(-) diff --git a/index.bs b/index.bs index 46c69712..ecc32e39 100644 --- a/index.bs +++ b/index.bs @@ -332,15 +332,138 @@ A [=WebTransport stream=] has the following signals: -# `WebTransportDatagramDuplexStream` Interface # {#duplex-stream} +# `WebTransportDatagramsWritable` Interface # {#datagram-writable} + +A WebTransportDatagramsWritable is a {{WritableStream}} providing outgoing streaming +features to [=send a datagram | send datagrams=]. + +
+[Exposed=(Window,Worker), SecureContext, Transferable]
+interface WebTransportDatagramsWritable : WritableStream {
+};
+
+ +## Internal slots ## {#datagram-writable-internal-slots} + +A {{WebTransportDatagramsWritable}} object has the following internal slots. + + + + + + + + + + + + +
Internal Slot + Description (non-normative) +
`[[Writable]]` + A {{WritableStream}} for outgoing datagrams. +
`[[OutgoingDatagramsQueue]]` + A queue of tuples of an outgoing datagram, a timestamp and a promise + which is resolved when the datagram is sent or discarded. +
+ +The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport}} object whose +{{[[State]]}} is either `"connecting"` or `"connected"`. + + To create a + {{WebTransportDatagramsWritable}} given a + readable, and + a writable, + perform the following steps. + + 1. Let |stream| be a [=new=] {{WebTransportDatagramsWritable}}, with: + : {{WebTransportDatagramsWritable/[[Writable]]}} + :: |writable| + : {{[[OutgoingDatagramsQueue]]}} + :: an empty queue + : {{[[OutgoingDatagramsHighWaterMark]]}} + :: an [=implementation-defined=] value +
+

This implementation-defined value should be tuned to ensure decent throughput, without + jeopardizing the timeliness of transmitted data.

+
+ : {{[[OutgoingDatagramsExpirationDuration]]}} + :: null + : {{[[OutgoingMaxDatagramSize]]}} + :: an [=implementation-defined=] integer. + 1. Return |stream|. + +## Attributes ## {#datagram-writable-attributes} + +## Procedures ## {#datagram-writable-procedures} + +
+ +The writeDatagrams algorithm is given a |transport| as parameter and +|data| as input. It is defined by running the following steps: + +1. Let |timestamp| be a timestamp representing now. +1. If |data| is not a {{BufferSource}} object, then return [=a promise rejected with=] a {{TypeError}}. +1. Let |datagrams| be |transport|.{{[[Datagrams]]}}. +1. If |datagrams|.{{[[OutgoingMaxDatagramSize]]}} is less than |data|'s \[[ByteLength]], return + [=a promise resolved with=] undefined. +1. Let |promise| be a new promise. +1. Let |bytes| be a copy of bytes which |data| represents. +1. Let |chunk| be a tuple of |bytes|, |timestamp| and |promise|. +1. Enqueue |chunk| to |datagrams|.{{[[OutgoingDatagramsQueue]]}}. +1. If the length of |datagrams|.{{[[OutgoingDatagramsQueue]]}} is less than + |datagrams|.{{[[OutgoingDatagramsHighWaterMark]]}}, then [=resolve=] |promise| with undefined. +1. Return |promise|. + +Note: The associated {{WritableStream}} calls [=writeDatagrams=] only when all the promises that +have been returned by [=writeDatagrams=] have been resolved. Hence the timestamp and the expiration +duration work well only when the web developer pays attention to +{{WritableStreamDefaultWriter/ready|WritableStreamDefaultWriter.ready}}. + +
+ +
+ +To sendDatagrams, given a {{WebTransport}} object |transport|, run these steps: +1. Let |queue| be |datagrams|.{{[[OutgoingDatagramsQueue]]}}. +1. Let |duration| be |datagrams|.{{[[OutgoingDatagramsExpirationDuration]]}}. +1. If |duration| is null, then set |duration| to an [=implementation-defined=] value. +1. While |queue| is not empty: + 1. Let |bytes|, |timestamp| and |promise| be |queue|'s first element. + 1. If more than |duration| milliseconds have passed since |timestamp|, then: + 1. Remove the first element from |queue|. + 1. [=Queue a network task=] with |transport| to [=resolve=] |promise| with |undefined|. + 1. Otherwise, break this loop. +1. If |transport|.{{[[State]]}} is not `"connected"`, then return. +1. Let |maxSize| be |datagrams|.{{[[OutgoingMaxDatagramSize]]}}. +1. While |queue| is not empty: + 1. Let |bytes|, |timestamp| and |promise| be |queue|'s first element. + 1. If |bytes|'s length ≤ |maxSize|: + 1. If it is not possible to send |bytes| to the network immediately, then break this loop. + 1. [=session/Send a datagram=], with |transport|.{{[[Session]]}} and |bytes|. + 1. Remove the first element from |queue|. + 1. [=Queue a network task=] with |transport| to [=resolve=] |promise| with undefined. + +
+ +The user agent SHOULD run [=sendDatagrams=] for any {{WebTransport}} object whose +{{[[State]]}} is `"connecting"` or `"connected"` as soon as reasonably possible whenever the +algorithm can make progress. + +Note: Writing datagrams while the transport's {{[[State]]}} is `"connecting"` is allowed. The +datagrams are stored in {{[[OutgoingDatagramsQueue]]}}, and they can be discarded +in the same manner as when in the `"connected"` state. Once the transport's {{[[State]]}} becomes +`"connected"`, it will start sending the queued datagrams. + +# `WebTransportDatagramDuplexStream` Interface # {#datagram-duplex-stream} A WebTransportDatagramDuplexStream is a generic duplex stream.
 [Exposed=(Window,Worker), SecureContext]
 interface WebTransportDatagramDuplexStream {
+  WebTransportDatagramsWritable createWritable();
   readonly attribute ReadableStream readable;
-  readonly attribute WritableStream writable;
 
   readonly attribute unsigned long maxDatagramSize;
   attribute unrestricted double? incomingMaxAge;
@@ -350,7 +473,7 @@ interface WebTransportDatagramDuplexStream {
 };
 
-## Internal slots ## {#datagramduplexstream-internal-slots} +## Internal slots ## {#datagram-duplex-stream-internal-slots} A {{WebTransportDatagramDuplexStream}} object has the following internal slots. @@ -366,10 +489,6 @@ A {{WebTransportDatagramDuplexStream}} object has the following internal slots. `[[Readable]]` A {{ReadableStream}} for incoming datagrams. - - `[[Writable]]` - A {{WritableStream}} for outgoing datagrams. - `[[IncomingDatagramsQueue]]` A queue of pairs of an incoming datagram and a timestamp. @@ -388,11 +507,6 @@ A {{WebTransportDatagramDuplexStream}} object has the following internal slots. An {{unrestricted double}} representing the expiration duration for incoming datagrams (in milliseconds), or null. - - `[[OutgoingDatagramsQueue]]` - A queue of tuples of an outgoing datagram, a timestamp and a promise - which is resolved when the datagram is sent or discarded. - `[[OutgoingDatagramsHighWaterMark]]` An {{unrestricted double}} representing the @@ -422,8 +536,6 @@ The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport 1. Let |stream| be a [=new=] {{WebTransportDatagramDuplexStream}}, with: : {{WebTransportDatagramDuplexStream/[[Readable]]}} :: |readable| - : {{WebTransportDatagramDuplexStream/[[Writable]]}} - :: |writable| : {{[[IncomingDatagramsQueue]]}} :: an empty queue : {{[[IncomingDatagramsPullPromise]]}} @@ -432,8 +544,6 @@ The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport :: an [=implementation-defined=] value : {{[[IncomingDatagramsExpirationDuration]]}} :: null - : {{[[OutgoingDatagramsQueue]]}} - :: an empty queue : {{[[OutgoingDatagramsHighWaterMark]]}} :: an [=implementation-defined=] value
@@ -446,16 +556,25 @@ The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport :: an [=implementation-defined=] integer. 1. Return |stream|. +## Methods ## {#datagram-duplex-stream-methods} + +: createWritable() + +:: Creates a {{WebTransportDatagramsWritable}}. + + When `createWritable()` method is called, the user agent MUST + run the following steps: + 1. Let |transport| be {{WebTransport}} object associated with [=this=]. + 1. If |transport|.{{[[State]]}} is `"closed"` or `"failed"`, + [=throw=] an {{InvalidStateError}}. + 1. Return the result of [=WebTransportDatagramsWritable/creating=] a {{WebTransportDatagramsWritable}} with |transport|. + ## Attributes ## {#datagram-duplex-stream-attributes} : readable :: The getter steps are: 1. Return [=this=].{{WebTransportDatagramDuplexStream/[[Readable]]}}. -: writable -:: The getter steps are: - 1. Return [=this=].{{WebTransportDatagramDuplexStream/[[Writable]]}}. - : incomingMaxAge :: The getter steps are: 1. Return [=this=].{{[[IncomingDatagramsExpirationDuration]]}}. @@ -546,64 +665,6 @@ The user agent SHOULD run [=receiveDatagrams=] for any {{WebTransport}} object w {{[[State]]}} is `"connected"` as soon as reasonably possible whenever the algorithm can make progress. -
- -The writeDatagrams algorithm is given a |transport| as parameter and -|data| as input. It is defined by running the following steps: - -1. Let |timestamp| be a timestamp representing now. -1. If |data| is not a {{BufferSource}} object, then return [=a promise rejected with=] a {{TypeError}}. -1. Let |datagrams| be |transport|.{{[[Datagrams]]}}. -1. If |datagrams|.{{[[OutgoingMaxDatagramSize]]}} is less than |data|'s \[[ByteLength]], return - [=a promise resolved with=] undefined. -1. Let |promise| be a new promise. -1. Let |bytes| be a copy of bytes which |data| represents. -1. Let |chunk| be a tuple of |bytes|, |timestamp| and |promise|. -1. Enqueue |chunk| to |datagrams|.{{[[OutgoingDatagramsQueue]]}}. -1. If the length of |datagrams|.{{[[OutgoingDatagramsQueue]]}} is less than - |datagrams|.{{[[OutgoingDatagramsHighWaterMark]]}}, then [=resolve=] |promise| with undefined. -1. Return |promise|. - -Note: The associated {{WritableStream}} calls [=writeDatagrams=] only when all the promises that -have been returned by [=writeDatagrams=] have been resolved. Hence the timestamp and the expiration -duration work well only when the web developer pays attention to -{{WritableStreamDefaultWriter/ready|WritableStreamDefaultWriter.ready}}. - -
- -
- -To sendDatagrams, given a {{WebTransport}} object |transport|, run these steps: -1. Let |queue| be |datagrams|.{{[[OutgoingDatagramsQueue]]}}. -1. Let |duration| be |datagrams|.{{[[OutgoingDatagramsExpirationDuration]]}}. -1. If |duration| is null, then set |duration| to an [=implementation-defined=] value. -1. While |queue| is not empty: - 1. Let |bytes|, |timestamp| and |promise| be |queue|'s first element. - 1. If more than |duration| milliseconds have passed since |timestamp|, then: - 1. Remove the first element from |queue|. - 1. [=Queue a network task=] with |transport| to [=resolve=] |promise| with |undefined|. - 1. Otherwise, break this loop. -1. If |transport|.{{[[State]]}} is not `"connected"`, then return. -1. Let |maxSize| be |datagrams|.{{[[OutgoingMaxDatagramSize]]}}. -1. While |queue| is not empty: - 1. Let |bytes|, |timestamp| and |promise| be |queue|'s first element. - 1. If |bytes|'s length ≤ |maxSize|: - 1. If it is not possible to send |bytes| to the network immediately, then break this loop. - 1. [=session/Send a datagram=], with |transport|.{{[[Session]]}} and |bytes|. - 1. Remove the first element from |queue|. - 1. [=Queue a network task=] with |transport| to [=resolve=] |promise| with undefined. - -
- -The user agent SHOULD run [=sendDatagrams=] for any {{WebTransport}} object whose -{{[[State]]}} is `"connecting"` or `"connected"` as soon as reasonably possible whenever the -algorithm can make progress. - -Note: Writing datagrams while the transport's {{[[State]]}} is `"connecting"` is allowed. The -datagrams are stored in {{[[OutgoingDatagramsQueue]]}}, and they can be discarded -in the same manner as when in the `"connected"` state. Once the transport's {{[[State]]}} becomes -`"connected"`, it will start sending the queued datagrams. - # `WebTransport` Interface # {#web-transport} `WebTransport` provides an API to the underlying transport functionality From db89b5f17e58f146ebab5a767f28792809ac2d90 Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Wed, 20 Nov 2024 09:59:58 -0500 Subject: [PATCH 2/4] Restore the default writable for now --- index.bs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/index.bs b/index.bs index ecc32e39..0429192d 100644 --- a/index.bs +++ b/index.bs @@ -464,6 +464,7 @@ A WebTransportDatagramDuplexStream is a generic duplex stre interface WebTransportDatagramDuplexStream { WebTransportDatagramsWritable createWritable(); readonly attribute ReadableStream readable; + readonly attribute WritableStream writable; readonly attribute unsigned long maxDatagramSize; attribute unrestricted double? incomingMaxAge; @@ -489,6 +490,10 @@ A {{WebTransportDatagramDuplexStream}} object has the following internal slots. `[[Readable]]` A {{ReadableStream}} for incoming datagrams. + + `[[Writable]]` + A default {{WebTransportDatagramsWritable}} for outgoing datagrams. + `[[IncomingDatagramsQueue]]` A queue of pairs of an incoming datagram and a timestamp. @@ -536,6 +541,8 @@ The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport 1. Let |stream| be a [=new=] {{WebTransportDatagramDuplexStream}}, with: : {{WebTransportDatagramDuplexStream/[[Readable]]}} :: |readable| + : {{WebTransportDatagramDuplexStream/[[Writable]]}} + :: |writable| : {{[[IncomingDatagramsQueue]]}} :: an empty queue : {{[[IncomingDatagramsPullPromise]]}} @@ -575,6 +582,10 @@ The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport :: The getter steps are: 1. Return [=this=].{{WebTransportDatagramDuplexStream/[[Readable]]}}. +: writable +:: The getter steps are: + 1. Return [=this=].{{WebTransportDatagramDuplexStream/[[Writable]]}}. + : incomingMaxAge :: The getter steps are: 1. Return [=this=].{{[[IncomingDatagramsExpirationDuration]]}}. From d6302c2dc8e67826ec19108ac283ff20687f2e7d Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Wed, 4 Dec 2024 10:27:59 -0500 Subject: [PATCH 3/4] Make wt.datagrams.writable same type as from createWritable() --- index.bs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/index.bs b/index.bs index 0429192d..a20c75f6 100644 --- a/index.bs +++ b/index.bs @@ -345,7 +345,7 @@ interface WebTransportDatagramsWritable : WritableStream { ## Internal slots ## {#datagram-writable-internal-slots} -A {{WebTransportDatagramsWritable}} object has the following internal slots. +A {{WebTransportDatagramsWritable}} object has the following internal slot. @@ -355,10 +355,6 @@ A {{WebTransportDatagramsWritable}} object has the following internal slots. - -
`[[Writable]]` - A {{WritableStream}} for outgoing datagrams. -
`[[OutgoingDatagramsQueue]]` A queue of tuples of an outgoing datagram, a timestamp and a promise @@ -464,7 +460,7 @@ A WebTransportDatagramDuplexStream is a generic duplex stre interface WebTransportDatagramDuplexStream { WebTransportDatagramsWritable createWritable(); readonly attribute ReadableStream readable; - readonly attribute WritableStream writable; + readonly attribute WebTransportDatagramsWritable writable; readonly attribute unsigned long maxDatagramSize; attribute unrestricted double? incomingMaxAge; From b6c9b6bbbaeb715b9020c1db0ab97903a6af8f5c Mon Sep 17 00:00:00 2001 From: Jan-Ivar Bruaroey Date: Tue, 17 Dec 2024 09:45:06 -0500 Subject: [PATCH 4/4] Clean up algorithms from the split a bit more --- index.bs | 46 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/index.bs b/index.bs index a20c75f6..4ef3e190 100644 --- a/index.bs +++ b/index.bs @@ -363,39 +363,19 @@ A {{WebTransportDatagramsWritable}} object has the following internal slot.
-The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport}} object whose -{{[[State]]}} is either `"connecting"` or `"connected"`. - To create a - {{WebTransportDatagramsWritable}} given a - readable, and - a writable, - perform the following steps. + {{WebTransportDatagramsWritable}}, perform the following steps. 1. Let |stream| be a [=new=] {{WebTransportDatagramsWritable}}, with: - : {{WebTransportDatagramsWritable/[[Writable]]}} - :: |writable| : {{[[OutgoingDatagramsQueue]]}} :: an empty queue - : {{[[OutgoingDatagramsHighWaterMark]]}} - :: an [=implementation-defined=] value -
-

This implementation-defined value should be tuned to ensure decent throughput, without - jeopardizing the timeliness of transmitted data.

-
- : {{[[OutgoingDatagramsExpirationDuration]]}} - :: null - : {{[[OutgoingMaxDatagramSize]]}} - :: an [=implementation-defined=] integer. 1. Return |stream|. -## Attributes ## {#datagram-writable-attributes} - ## Procedures ## {#datagram-writable-procedures}
-The writeDatagrams algorithm is given a |transport| as parameter and +The writeDatagrams algorithm is given a |transport| and |writable| as parameters and |data| as input. It is defined by running the following steps: 1. Let |timestamp| be a timestamp representing now. @@ -406,22 +386,24 @@ The writeDatagrams algorithm is given a |transport| as parameter and 1. Let |promise| be a new promise. 1. Let |bytes| be a copy of bytes which |data| represents. 1. Let |chunk| be a tuple of |bytes|, |timestamp| and |promise|. -1. Enqueue |chunk| to |datagrams|.{{[[OutgoingDatagramsQueue]]}}. -1. If the length of |datagrams|.{{[[OutgoingDatagramsQueue]]}} is less than +1. Enqueue |chunk| to |writable|.{{[[OutgoingDatagramsQueue]]}}. +1. If the length of |writable|.{{[[OutgoingDatagramsQueue]]}} is less than |datagrams|.{{[[OutgoingDatagramsHighWaterMark]]}}, then [=resolve=] |promise| with undefined. 1. Return |promise|. Note: The associated {{WritableStream}} calls [=writeDatagrams=] only when all the promises that -have been returned by [=writeDatagrams=] have been resolved. Hence the timestamp and the expiration -duration work well only when the web developer pays attention to +have been returned by [=writeDatagrams=] for that stream have been resolved. Hence the +timestamp and the expiration duration work well only when the web developer pays attention to {{WritableStreamDefaultWriter/ready|WritableStreamDefaultWriter.ready}}.
-To sendDatagrams, given a {{WebTransport}} object |transport|, run these steps: -1. Let |queue| be |datagrams|.{{[[OutgoingDatagramsQueue]]}}. +To sendDatagrams, given a {{WebTransport}} object |transport| and a +{{WebTransportDatagramsWritable}} object |writable|, run these steps: +1. Let |queue| be |writable|.{{[[OutgoingDatagramsQueue]]}}. +1. Let |datagrams| be |transport|.{{[[Datagrams]]}}. 1. Let |duration| be |datagrams|.{{[[OutgoingDatagramsExpirationDuration]]}}. 1. If |duration| is null, then set |duration| to an [=implementation-defined=] value. 1. While |queue| is not empty: @@ -443,7 +425,8 @@ To sendDatagrams, given a {{WebTransport}} object |transport|, run th
The user agent SHOULD run [=sendDatagrams=] for any {{WebTransport}} object whose -{{[[State]]}} is `"connecting"` or `"connected"` as soon as reasonably possible whenever the +{{[[State]]}} is `"connecting"` or `"connected"` as soon as reasonably possible on each of +its associated {{WebTransportDatagramsWritable}} objects whenever the algorithm can make progress. Note: Writing datagrams while the transport's {{[[State]]}} is `"connecting"` is allowed. The @@ -570,7 +553,7 @@ The user agent MAY update {{[[OutgoingMaxDatagramSize]]}} for any {{WebTransport 1. Let |transport| be {{WebTransport}} object associated with [=this=]. 1. If |transport|.{{[[State]]}} is `"closed"` or `"failed"`, [=throw=] an {{InvalidStateError}}. - 1. Return the result of [=WebTransportDatagramsWritable/creating=] a {{WebTransportDatagramsWritable}} with |transport|. + 1. Return the result of [=WebTransportDatagramsWritable/creating=] a {{WebTransportDatagramsWritable}}. ## Attributes ## {#datagram-duplex-stream-attributes} @@ -847,7 +830,8 @@ agent MUST run the following steps: 1. Let |anticipatedConcurrentIncomingBidirectionalStreams| be {{WebTransport/constructor(url, options)/options}}'s {{WebTransportOptions/anticipatedConcurrentIncomingBidirectionalStreams}}. 1. Let |incomingDatagrams| be a [=new=] {{ReadableStream}}. -1. Let |outgoingDatagrams| be a [=new=] {{WritableStream}}. +1. Let |outgoingDatagrams| be a the result of [=WebTransportDatagramsWritable/creating=] a + {{WebTransportDatagramsWritable}} with |transport|. 1. Let |datagrams| be the result of [=WebTransportDatagramDuplexStream/creating=] a {{WebTransportDatagramDuplexStream}}, its [=WebTransportDatagramDuplexStream/create/readable=] set to |incomingDatagrams| and its [=WebTransportDatagramDuplexStream/create/writable=] set to |outgoingDatagrams|.