diff --git a/index.bs b/index.bs
index 46c69712..4ef3e190 100644
--- a/index.bs
+++ b/index.bs
@@ -332,15 +332,118 @@ 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 slot.
+
+
+
+
+ Internal Slot
+ | Description (non-normative)
+ |
+
+
+
+ `[[OutgoingDatagramsQueue]]`
+ | A queue of tuples of an outgoing datagram, a timestamp and a promise
+ which is resolved when the datagram is sent or discarded.
+ |
+
+
+
+ To create a
+ {{WebTransportDatagramsWritable}}, perform the following steps.
+
+ 1. Let |stream| be a [=new=] {{WebTransportDatagramsWritable}}, with:
+ : {{[[OutgoingDatagramsQueue]]}}
+ :: an empty queue
+ 1. Return |stream|.
+
+## Procedures ## {#datagram-writable-procedures}
+
+
+
+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.
+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 |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=] 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| 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:
+ 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 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
+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 WebTransportDatagramsWritable writable;
readonly attribute unsigned long maxDatagramSize;
attribute unrestricted double? incomingMaxAge;
@@ -350,7 +453,7 @@ interface WebTransportDatagramDuplexStream {
};
-## Internal slots ## {#datagramduplexstream-internal-slots}
+## Internal slots ## {#datagram-duplex-stream-internal-slots}
A {{WebTransportDatagramDuplexStream}} object has the following internal slots.
@@ -368,7 +471,7 @@ A {{WebTransportDatagramDuplexStream}} object has the following internal slots.
`[[Writable]]`
- | A {{WritableStream}} for outgoing datagrams.
+ | A default {{WebTransportDatagramsWritable}} for outgoing datagrams.
|
`[[IncomingDatagramsQueue]]`
@@ -388,11 +491,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
@@ -432,8 +530,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,6 +542,19 @@ 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}}.
+
## Attributes ## {#datagram-duplex-stream-attributes}
: readable
@@ -546,64 +655,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
@@ -779,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|.
|