1. Introduction
This section is non-normative.
This specification uses [WEB-TRANSPORT-HTTP3] to send data to and receive data from servers. It can be used like WebSockets but with support for multiple streams, unidirectional streams, out-of-order delivery, and reliable as well as unreliable transport.
Note: The API presented in this specification represents a preliminary proposal based on work-in-progress within the IETF WEBTRANS WG. Since the [WEB-TRANSPORT-HTTP3] specification is a work-in-progress, both the protocol and API are likely to change significantly going forward.
2. Conformance
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MUST and SHOULD are to be interpreted as described in [RFC2119] .
This specification defines conformance criteria that apply to a single product: the user agent that implements the interfaces that it contains.
Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)
Implementations that use ECMAScript to implement the APIs defined in this specification MUST implement them in a manner consistent with the ECMAScript Bindings defined in the Web IDL specification [WEBIDL] , as this specification uses that specification and terminology.
3. Protocol concepts
A WebTransport session is a session of WebTransport over HTTP/3. There may be multiple WebTransport sessions on one connection , when pooling is enabled.
WebTransport session has the following capabilities defined in [WEB-TRANSPORT-HTTP3] .
| capability | definition |
|---|---|
| send a datagram | [WEB-TRANSPORT-HTTP3] section 4.4 |
| receive a datagram | [WEB-TRANSPORT-HTTP3] section 4.4 |
| create an outgoing unidirectional stream | [WEB-TRANSPORT-HTTP3] section 4.1 |
| create a bidirectional stream | [WEB-TRANSPORT-HTTP3] section 4.2 |
| receive an incoming unidirectional stream | [WEB-TRANSPORT-HTTP3] section 4.1 |
| receive a bidirectional stream | [WEB-TRANSPORT-HTTP3] section 4.2 |
To establish a WebTransport session with an origin origin , follow [WEB-TRANSPORT-HTTP3] section 3.3 , with using origin , serialized and isomorphic encoded , as the Origin header of the request. When establishing a session, the client MUST NOT provide any credentials .
To terminate a WebTransport session session with an optional integer code and an optional byte sequence reason , follow [WEB-TRANSPORT-HTTP3] section 5 .
A WebTransport session session is terminated , with optionally an integer code and a byte sequence reason , when the HTTP/3 stream associated with the CONNECT request that initiated session is closed by the server, as described at [WEB-TRANSPORT-HTTP3] section 5 .
WebTransport stream is a concept for HTTP/3 stream on a WebTransport session .
A WebTransport stream is one of incoming unidirectional , outgoing unidirectional or bidirectional .
WebTransport stream has the following capabilities:
| capability | definition | incoming unidirectional | outgoing unidirectional | bidirectional |
|---|---|---|---|---|
| send bytes (potentially with FIN) | [QUIC] section 2.2 | No | Yes | Yes |
| receive bytes (potentially with FIN) | [QUIC] section 2.2 | Yes | No | Yes |
| send STOP_SENDING | [QUIC] section 3.5 | Yes | No | Yes |
| reset a WebTransport stream | [QUIC] section 19.4 | No | Yes | Yes |
WebTransport stream has the following signals:
| event | definition | incoming unidirectional | outgoing unidirectional | bidirectional |
|---|---|---|---|---|
| STOP_SENDING | [QUIC] section 3.5 | No | Yes | Yes |
| reset | [QUIC] section 19.4 | Yes | No | Yes |
4.
WebTransportDatagramDuplexStream
Interface
A
WebTransportDatagramDuplexStream
is
a
generic
duplex
stream.
[Exposed =(Window ,Worker ),SecureContext ]interface WebTransportDatagramDuplexStream {readonly attribute ReadableStream readable ;readonly attribute WritableStream writable ;readonly attribute unsigned long maxDatagramSize ;attribute double ?incomingMaxAge ;attribute double ?outgoingMaxAge ;attribute long incomingHighWaterMark ;attribute long outgoingHighWaterMark ; };
4.1. Internal slots
A
WebTransportDatagramDuplexStream
object
has
the
following
internal
slots.
| Internal Slot | Description ( non-normative ) |
|---|---|
[[Readable]]
|
A
ReadableStream
for
incoming
datagrams.
|
[[Writable]]
|
A
WritableStream
for
outgoing
datagrams.
|
[[IncomingDatagramsQueue]]
| A queue of pairs of an incoming datagram and a timestamp. |
[[IncomingDatagramsPullPromise]]
| A promise set by pullDatagrams , to wait for an incoming datagram. |
[[IncomingDatagramsHighWaterMark]]
| An integer representing the high water mark of the incoming datagrams. |
[[IncomingDatagramsExpirationDuration]]
|
A
double
value
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 integer representing the high water mark of the outgoing datagrams. |
[[OutgoingDatagramsExpirationDuration]]
|
A
double
value
representing
the
expiration
duration
for
outgoing
datagrams
(in
milliseconds),
or
null.
|
[[OutgoingMaxDatagramSize]]
| An integer representing the maximum size for an outgoing datagram. |
The
user
agent
MAY
update
[[OutgoingMaxDatagramSize]]
for
any
WebTransport
object
whose
[[State]]
is
either
"connecting"
or
"connected"
.
To
create
a
WebTransportDatagramDuplexStream
given
a
readable
,
and
a
writable
,
perform
the
following
steps.
-
Let stream be a new
WebTransportDatagramDuplexStream, with:-
[[Readable]] -
readable
-
[[Writable]] -
writable
-
[[IncomingDatagramsQueue]] -
an empty queue
-
[[IncomingDatagramsPullPromise]] -
null
-
[[IncomingDatagramsHighWaterMark]] -
an implementation-defined integer
-
[[IncomingDatagramsExpirationDuration]] -
null
-
[[OutgoingDatagramsQueue]] -
an empty queue
-
[[OutgoingDatagramsHighWaterMark]] -
an implementation-defined integer
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.
-
-
Return stream .
4.2. Attributes
-
readable, of type ReadableStream , readonly -
The getter steps are:
-
Return this .
[[Readable]].
-
-
writable, of type WritableStream , readonly -
The getter steps are:
-
Return this .
[[Writable]].
-
-
incomingMaxAge, of type double , nullable -
The getter steps are:
-
Return this .
[[IncomingDatagramsExpirationDuration]].
The setter steps are:
-
Let value be the given value.
-
If value is null or value > 0:
-
Set this .
[[IncomingDatagramsExpirationDuration]]to value .
-
-
-
maxDatagramSize, of type unsigned long , readonly -
The maximum size data that may be passed to
writable. The getter steps are to return this .[[OutgoingMaxDatagramSize]]. -
outgoingMaxAge, of type double , nullable -
The getter steps are:
-
Return this 's
[[OutgoingDatagramsExpirationDuration]].
The setter steps are:
-
Let value be the given value.
-
If value is null or value > 0:
-
Set this 's
[[OutgoingDatagramsExpirationDuration]]to value .
-
-
-
incomingHighWaterMark, of type long -
The getter steps are:
-
Return this .
[[IncomingDatagramsHighWaterMark]].
The setter steps are:
-
Let value be the given value.
-
If value ≥ 0:
-
Set this .
[[IncomingDatagramsHighWaterMark]]to value .
-
-
-
outgoingHighWaterMark, of type long -
The getter steps are:
-
Return this .
[[OutgoingDatagramsHighWaterMark]].
The setter steps are:
-
Let value be the given value.
-
If value ≥ 0:
-
Set this .
[[OutgoingDatagramsHighWaterMark]]to value .
-
-
4.3. Procedures
To
pullDatagrams
,
given
a
WebTransport
object
transport
,
run
these
steps:
-
Let datagrams be transport .
[[Datagrams]]. -
Assert: datagrams .
[[IncomingDatagramsPullPromise]]is null. -
Let queue be datagrams .
[[IncomingDatagramsQueue]]. -
If queue is empty, then:
-
Set datagrams .
[[IncomingDatagramsPullPromise]]to a new promise. -
Return datagrams .
[[IncomingDatagramsPullPromise]].
-
-
Let bytes and timestamp be the result of dequeuing queue .
-
Let chunk be a new
Uint8Arrayobject representing bytes . -
Enqueue chunk to transport .
[[Datagrams]].[[Readable]]. -
Return a promise resolved with undefined.
To
receiveDatagrams
,
given
a
WebTransport
object
transport
,
run
these
steps:
-
Let timestamp be a timestamp representing now.
-
Let queue be datagrams .
[[IncomingDatagramsQueue]]. -
Let duration be datagrams .
[[IncomingDatagramsExpirationDuration]]. -
If duration is null, then set duration to an implementation-defined value.
-
Let session be transport .
[[Session]]. -
While there are available incoming datagrams on session :
-
Let datagram be the result of receiving a datagram with session .
-
Let timestamp be a timestamp representing now.
-
Let chunk be a pair of datagram and timestamp .
-
Enqueue chunk to queue .
-
-
Let toBeRemoved be the length of queue minus datagrams .
[[IncomingDatagramsHighWaterMark]]. -
If toBeRemoved is positive, repeat dequeuing queue toBeRemoved times.
-
While queue is not empty:
-
If queue is not empty and datagrams .
[[IncomingDatagramsPullPromise]]is non-null, then:-
Let bytes and timestamp be the result of dequeuing queue .
-
Let promise be datagrams .
[[IncomingDatagramsPullPromise]]. -
Set datagrams .
[[IncomingDatagramsPullPromise]]to null. -
Queue a network task with transport to run the following steps:
-
Let chunk be a new
Uint8Arrayobject representing bytes . -
Enqueue chunk to datagrams .
[[Readable]]. -
Resolve promise with undefined.
-
-
The
user
agent
SHOULD
run
receiveDatagrams
for
any
WebTransport
object
whose
[[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:
-
Let timestamp be a timestamp representing now.
-
If data is not a
BufferSourceobject, then return a promise rejected with aTypeError. -
Let datagrams be transport .
[[Datagrams]]. -
If datagrams .
[[OutgoingMaxDatagramSize]]is less than data ’s [[ByteLength]], return a promise resolved with undefined. -
Let promise be a new promise.
-
Let bytes be a copy of bytes which data represents.
-
Let chunk be a tuple of bytes , timestamp and promise .
-
Enqueue chunk to datagrams .
[[OutgoingDatagramsQueue]]. -
If the length of datagrams .
[[OutgoingDatagramsQueue]]is less than datagrams .[[OutgoingDatagramsHighWaterMark]], then resolve promise with undefined. -
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
.
To
sendDatagrams
,
given
a
WebTransport
object
transport
,
run
these
steps:
-
Let queue be datagrams .
[[OutgoingDatagramsQueue]]. -
Let duration be datagrams .
[[OutgoingDatagramsExpirationDuration]]. -
If duration is null, then set duration to an implementation-defined value.
-
While queue is not empty:
-
Let bytes , timestamp and promise be queue ’s first element.
-
If more than duration milliseconds have passed since timestamp , then:
-
Remove the first element from queue .
-
Queue a network task with transport to resolve promise with undefined .
-
-
Otherwise, break this loop.
-
-
If transport .
[[State]]is not"connected", then return. -
Let maxSize be datagrams .
[[OutgoingMaxDatagramSize]]. -
While queue is not empty:
-
Let bytes , timestamp and promise be queue ’s first element.
-
If bytes ’s length ≤ maxSize :
-
If it is not possible to send bytes to the network immediately, then break this loop.
-
Send a datagram , with transport .
[[Session]]and bytes .
-
-
Remove the first element from queue .
-
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
[[OutgoingDatagramsExpirationDuration]]
,
and
they
can
be
discarded
in
the
same
manner
as
the
"connected"
case.
Once
the
transport’s
[[State]]
becomes
"connected"
,
it
will
start
sending
stored
datagrams.
5.
WebTransport
Interface
WebTransport
provides
an
API
to
the
HTTP/3
transport
functionality
defined
in
[WEB-TRANSPORT-HTTP3]
.
[Exposed =(Window ,Worker ),SecureContext ]interface {WebTransport (constructor USVString ,url optional WebTransportOptions = {});options Promise <WebTransportStats >getStats ();readonly attribute Promise <undefined >ready ;readonly attribute WebTransportReliabilityMode reliability ;readonly attribute Promise <WebTransportCloseInfo >closed ;undefined close (optional WebTransportCloseInfo = {});closeInfo readonly attribute WebTransportDatagramDuplexStream datagrams ;Promise <WebTransportBidirectionalStream >createBidirectionalStream (); /* a ReadableStream of WebTransportBidirectionalStream objects */readonly attribute ReadableStream incomingBidirectionalStreams ;Promise <WritableStream >createUnidirectionalStream (); /* a ReadableStream of ReceiveStreams */readonly attribute ReadableStream incomingUnidirectionalStreams ; };enum {WebTransportReliabilityMode ,"pending" ,"reliable-only" , };"supports-unreliable"
5.1. Internal slots
A
WebTransport
object
has
the
following
internal
slots.
| Internal Slot | Description ( non-normative ) |
|---|---|
[[SendStreams]]
|
An
ordered
set
of
SendStream
s
owned
by
this
WebTransport
.
|
[[ReceiveStreams]]
|
An
ordered
set
of
ReceiveStream
s
owned
by
this
WebTransport
.
|
[[IncomingBidirectionalStreams]]
|
A
ReadableStream
consisting
of
WebTransportBidirectionalStream
objects.
|
[[IncomingUnidirectionalStreams]]
|
A
ReadableStream
consisting
of
ReceiveStream
s.
|
[[State]]
|
An
enum
indicating
the
state
of
the
transport.
One
of
"connecting"
,
"connected"
,
"closed"
,
and
"failed"
.
|
[[Ready]]
| A promise fulfilled when the associated WebTransport session gets established , or rejected if the establishment process failed. |
[[Reliability]]
|
A
WebTransportReliabilityMode
indicating
whether
unreliable
(UDP)
transport
is
supported
or
whether
only
reliable
(TCP
fallback)
transport
is
used.
Returns
"pending"
until
a
connection
has
been
established.
|
[[Closed]]
|
A
promise
fulfilled
when
the
associated
WebTransport
object
is
closed
gracefully,
or
rejected
when
it
is
closed
abruptly
or
failed
on
initialization.
|
[[Datagrams]]
|
A
WebTransportDatagramDuplexStream
.
|
[[Session]]
|
A
WebTransport
session
for
this
WebTransport
object,
or
null.
|
5.2. Constructor
WebTransport()
constructor
is
invoked,
the
user
agent
MUST
run
the
following
steps:
-
Let parsedURL be the URL record resulting from parsing
url. -
If parsedURL is a failure, throw a
SyntaxErrorexception. -
If parsedURL scheme is not
https, throw aSyntaxErrorexception. -
If parsedURL fragment is not null, throw a
SyntaxErrorexception. -
Let allowPooling be
options'sallowPoolingif it exists, and false otherwise. -
Let dedicated be the negation of allowPooling .
-
Let serverCertificateHashes be
options'sserverCertificateHashesif it exists, and null otherwise. -
If dedicated is false and serverCertificateHashes is non-null, then throw a
TypeError. -
Let requireUnreliable be
options'srequireUnreliable. Let incomingDatagrams be a new
ReadableStream.-
Let outgoingDatagrams be a new
WritableStream. -
Let datagrams be the result of creating a
WebTransportDatagramDuplexStream, its readable set to incomingDatagrams and its writable set to outgoingDatagrams . -
Let transport be a newly constructed
WebTransportobject, with:-
[[SendStreams]] -
an empty ordered set
-
[[ReceiveStreams]] -
an empty ordered set
-
[[IncomingBidirectionalStreams]] -
a new
ReadableStream -
[[IncomingUnidirectionalStreams]] -
a new
ReadableStream -
[[State]] -
"connecting" -
[[Ready]] -
a new promise
-
[[Reliability]] "pending"
[[Closed]]-
a new promise
-
[[Datagrams]] -
datagrams
-
[[Session]] -
null
-
-
Let pullDatagramsAlgorithm be an action that runs pullDatagrams with transport .
-
Let writeDatagramsAlgorithm be an action that runs writeDatagrams with transport .
-
Set up incomingDatagrams with pullAlgorithm set to pullDatagramsAlgorithm , and highWaterMark set to 0.
-
Set up outgoingDatagrams with writeAlgorithm set to writeDatagramsAlgorithm .
-
Let pullBidirectionalStreamAlgorithm be an action that runs pullBidirectionalStream with transport .
-
Set up transport .
[[IncomingBidirectionalStreams]]with pullAlgorithm set to pullBidirectionalStreamAlgorithm , and highWaterMark set to 0. -
Let pullUnidirectionalStreamAlgorithm be an action that runs pullUnidirectionalStream with transport .
-
Set up transport .
[[IncomingUnidirectionalStreams]]with pullAlgorithm set to pullUnidirectionalStreamAlgorithm , and highWaterMark set to 0. -
Initialize WebTransport over HTTP with transport , parsedURL
and, dedicated , and requireUnreliable . -
Return transport .
WebTransport
object
transport
,
a
URL
record
url
,
-
Let client be transport ’s relevant settings object .
-
Let origin be client ’s origin .
-
Let request be a new request whose URL is url , client is client , policy container is client ’s policy container , destination is an empty string, and origin is origin .
-
If should request be blocked by Content Security Policy? with request returns "Blocked" , or if request should be blocked due to a bad port returns blocked , then abort the remaining steps and queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", then abort these steps. -
Let error be the result of creating a
WebTransportErrorwith"session". -
Cleanup transport with error , error and true.
-
-
Let networkPartitionKey be the result of determining the network partition key with transport ’s relevant settings object .
-
Run the remaining steps in parallel , but abort them whenever transport .
[[State]]becomes"closed"or"failed". -
Let newConnection be "
no" if dedicated is false; otherwise "yes-and-dedicated". -
Let connection be the result of obtaining a connection with networkPartitionKey , url , false, newConnection , and
withhttp3Onlyset to true.. -
If connection is failure, then abort the remaining steps and queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", then abort these steps. -
Let error be the result of creating a
WebTransportErrorwith"session". -
Cleanup transport with error .
-
-
Wait for connection to receive the first SETTINGS frame, and let settings be a dictionary that represents the SETTINGS frame.
-
If settings doesn’t contain SETTINGS_ENABLE_WEBTRANPORT with a value of 1, or it doesn’t contain H3_DATAGRAM with a value of 1, then abort the remaining steps and queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", then abort these steps. -
Let error be the result of creating a
WebTransportErrorwith"session". -
Cleanup transport with error .
-
-
Establish a WebTransport session with origin on connection .
Note: This step also contains the transport parameter exchange specified in [QUIC-DATAGRAM] .
-
If the previous step fails, abort the remaining steps and queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", then abort these steps. -
Let error be the result of creating a
WebTransportErrorwith"session". -
Cleanup transport with error .
-
-
Let session be the established WebTransport session .
-
Assert: maxDatagramSize is an integer.
-
Queue a network task with transport to run these steps:
-
If transport .
[[State]]is not"connecting":-
In parallel , terminate session .
-
Abort these steps.
-
-
Set transport .
[[State]]to"connected". -
Set transport .
[[Session]]to session . -
Set transport ’s
[[Reliability]]to"supports-unreliable".
-
WebTransport
object
transport
,
run
these
steps.
-
If transport .
[[State]]is"connecting", then return the result of performing the following steps upon fulfillment of transport .[[Ready]]:-
Return the result of pullBidirectionalStream with transport .
-
-
Let session be transport .
[[Session]]. -
Let p be a new promise.
-
Return p and run the remaining steps in parallel .
-
Wait until there is an available incoming bidirectional stream .
-
Let internalStream be the result of receiving a bidirectional stream .
-
Queue a network task with transport to run these steps:
-
Let stream be the result of creating a
WebTransportBidirectionalStreamwith internalStream and transport . -
Enqueue stream to transport .
[[IncomingBidirectionalStreams]]. -
Resolve p with undefined.
-
WebTransport
object
transport
,
run
these
steps.
-
If transport .
[[State]]is"connecting", then return the result of performing the following steps upon fulfillment of transport .[[Ready]]:-
Return the result of pullUnidirectionalStream with transport .
-
-
Let session be transport .
[[Session]]. -
Let p be a new promise.
-
Return p and run the remaining steps in parallel .
-
Wait until there is an available incoming unidirectional stream .
-
Let internalStream be the result of receiving an incoming unidirectional stream .
-
Queue a network task with transport to run these steps:
-
Let stream be the result of creating a
ReceiveStreamwith internalStream and transport . -
Enqueue stream to transport .
[[IncomingUnidirectionalStreams]]. -
Resolve p with undefined.
-
5.3. Attributes
-
ready, of type Promise< undefined >, readonly -
closed, of type Promise< WebTransportCloseInfo >, readonly -
On getting, it MUST return this 's
[[Closed]].This promise MUST be resolved when the transport is closed; an implementation SHOULD include error information in the
reasonandcloseCodefields ofWebTransportCloseInfo. -
datagrams, of type WebTransportDatagramDuplexStream , readonly -
A single duplex stream for sending and receiving datagrams over this session. The getter steps for the
datagramsattribute SHALL be:-
Return this 's
[[Datagrams]].
-
-
incomingBidirectionalStreams, of type ReadableStream , readonly -
Returns a
ReadableStreamofWebTransportBidirectionalStreams that have been received from the server. The getter steps for theincomingBidirectionalStreamsattribute SHALL be:-
Return this 's
[[IncomingBidirectionalStreams]].
-
-
incomingUnidirectionalStreams, of type ReadableStream , readonly -
A
ReadableStreamof unidirectional streams, each represented by aReceiveStream, that have been received from the server. The getter steps forincomingUnidirectionalStreamsare:-
Return this .
[[IncomingUnidirectionalStreams]].
-
-
reliability, of type WebTransportReliabilityMode , readonly Whether connection supports unreliable (over UDP) transport or only reliable (over TCP fallback) transport. Returns
"pending"until a connection has been established. The getter steps are to return this 's[[Reliability]].
5.4. Methods
-
close(closeInfo) -
Terminates the WebTransport session associated with the WebTransport object.
When close is called, the user agent MUST run the following steps:
-
Let transport be this .
-
If transport .
[[State]]is"closed"or"failed", then abort these steps. -
If transport .
[[State]]is"connecting":-
Let error be the result of creating a
WebTransportErrorwith"session". -
Cleanup transport with error .
-
Abort these steps.
-
-
Let session be transport .
[[Session]]. -
Let code be closeInfo .
closeCode. -
Let reason be closeInfo .
reason, UTF-8 encoded . -
If reason ’s length exceeds 1024, then set reason to a prefix of reason whose length is 1024.
-
In parallel , terminate session with code and reason .
Note: This also resets or sends STOP_SENDING WebTransport streams contained in transport .
[[SendStreams]]and[[ReceiveStreams]]. -
Cleanup transport with
AbortErrorand closeInfo .
-
-
getStats() -
Gathers stats for this
WebTransport's HTTP/3 connection and reports the result asynchronously.When getStats is called, the user agent MUST run the following steps:
-
Let transport be this .
-
Let p be a new promise.
-
If the URL scheme associated with transport is not
https, reject p withNotSupportedErrorand return p . -
Return p and continue the following steps in parallel .
-
Gather the stats from the underlying QUIC connection.
-
Wait for the stats to be ready.
-
Queue a network task with transport to run the following steps:
-
Let stats be a new
WebTransportStatsobject representing the gathered stats. -
Resolve p with stats .
-
-
-
-
createBidirectionalStream() -
Creates a
WebTransportBidirectionalStreamobject for an outgoing bidirectional stream. Note that the mere creation of a stream is not immediately visible to the peer until it is used to send data.When
createBidirectionalStreamis called, the user agent MUST run the following steps:-
Let transport be this .
-
If transport .
[[State]]is"closed"or"failed", return a new rejected promise with anInvalidStateError. -
Let p be a new promise.
-
Run the following steps in parallel , but abort them whenever transport ’s
[[State]]becomes"closed"or"failed", and instead queue a network task with transport to reject p with anInvalidStateError.-
Wait for transport .
[[State]]to be"connected". -
Let internalStream be the result of creating a bidirectional stream with transport .
[[Session]].Note: This operation may take time, for example when the stream ID is exhausted. [QUIC]
-
Queue a network task with transport to run the following steps:
-
If transport .
[[State]]is"closed"or"failed", reject p with anInvalidStateErrorand abort these steps. -
Let stream be the result of creating a
WebTransportBidirectionalStreamwith internalStream and transport . -
Resolve p with stream .
-
-
-
return p .
-
-
createUnidirectionalStream() -
Creates a
SendStreamfor an outgoing unidirectional stream. Note that the mere creation of a stream is not immediately visible to the server until it is used to send data.When
createUnidirectionalStream()method is called, the user agent MUST run the following steps:-
Let transport be this .
-
If transport .
[[State]]is"closed"or"failed", return a new rejected promise with anInvalidStateError. -
Let p be a new promise.
-
Run the following steps in parallel , but abort them whenever transport ’s
[[State]]becomes"closed"or"failed", and instead queue a network task with transport to reject p with anInvalidStateError.-
Wait for transport .
[[State]]to be"connected". -
Let internalStream be the result of creating an outgoing unidirectional stream with transport .
[[Session]].Note: This operation may take time, for example when the stream ID is exhausted. [QUIC]
-
Queue a network task with transport to run the following steps:
-
If transport .
[[State]]is"closed"or"failed", reject p with anInvalidStateErrorand abort these steps. -
Let stream be the result of creating a
SendStreamwith internalStream and transport . -
Resolve p with stream .
-
-
-
return p .
-
5.5. Procedures
WebTransport
transport
with
reason
,
error
and
a
boolean
abruptly
,
run
these
steps:
-
Let sendStreams be a copy of transport .
[[SendStreams]]. -
Let receiveStreams be a copy of transport .
[[ReceiveStreams]]. -
Let ready be transport .
[[Ready]]. -
Let closed be transport .
[[Closed]]. -
Let incomingBidirectionalStreams be transport .
[[IncomingBidirectionalStreams]]. -
Let incomingUnidirectionalStreams be transport .
[[IncomingUnidirectionalStreams]]. -
Set transport .
[[SendStreams]]to an empty set . -
Set transport .
[[ReceiveStreams]]to an empty set . -
If closeInfo is given, then set transport .
[[State]]to"closed". Otherwise, set transport .[[State]]to"failed". -
For each sendStream in sendStreams , error sendStream with error .
-
For each receiveStream in receiveStreams , error receiveStream with error .
Note: Script authors can inject code which runs in Promise resolution synchronously. Hence from here, do not touch transport as it may be mutated by scripts in an unpredictable way. This applies to logic calling this procedure, too.
-
If closeInfo is given, then:
-
Otherwise:
To
queue
a
network
task
with
a
WebTransport
transport
and
a
series
of
steps
steps
,
run
these
steps:
-
Queue a global task on the network task source with transport ’s relevant global object to run steps .
5.6. Session termination not initiated by the client
WebTransport
transport
is
terminated
with
optionally
code
and
reasonBytes
,
run
these
steps:
-
Let cleanly be a boolean representing whether the HTTP/3 stream associated with the CONNECT request that initiated transport .
[[Session]]is in the "Data Recvd" state. [QUIC] -
Queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", abort these steps. -
Let error be the result of creating a
WebTransportErrorwith"session". -
If cleanly is false, then cleanup transport with error , and abort these steps.
-
Let closeInfo be a new
WebTransportCloseInfo. -
If code is given, set closeInfo ’s
closeCodeto code . -
If reasonBytes is given, set closeInfo ’s
reasonto reasonBytes , UTF-8 decoded . -
Cleanup transport with error and closeInfo .
-
WebTransport
transport
gets
a
connection
error,
run
these
steps:
-
Queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", abort these steps. -
Let error be the result of creating a
WebTransportErrorwith"session". -
Cleanup transport with error , error and true.
-
5.7. Context cleanup steps
This
specification
defines
unloading
document
cleanup
steps
as
the
following
steps,
given
a
Document
document:
-
Let window be document ’s relevant global object .
-
For each
WebTransporttransport whose relevant global object is window :-
If transport .
[[State]]is"connected", set transport .[[State]]to"failed"and terminate transport .[[Session]]in parallel . -
If transport .
[[State]]is"connecting", set transport .[[State]]to"failed".
This needs to be done in workers too. See #127 and whatwg/html#6731 .
-
5.8. Garbage Collection
The
user
agent
MUST
NOT
garbage
collect
a
WebTransport
object
whose
[[State]]
is
either
"connecting"
or
"connected"
.
5.9. Configuration
dictionary {WebTransportHash DOMString ;algorithm BufferSource ; };value dictionary WebTransportOptions {;boolean allowPooling =false ;boolean requireUnreliable =false ;sequence <WebTransportHash >serverCertificateHashes ; };
WebTransportOptions
is
a
dictionary
of
parameters
that
determine
how
WebTransport
connection
is
established
and
used.
-
allowPooling, of type boolean , defaulting tofalse -
When set to true, the WebTransport connection can be pooled, that is, the network connection for the WebTransport session can be shared with other HTTP/3 sessions.
-
requireUnreliable, of type boolean , defaulting tofalse When set to true, the WebTransport connection cannot be established over HTTP/2 if an HTTP/3 connection is not possible.
-
serverCertificateHashes, of type sequence< WebTransportHash > -
This option is only supported for transports using dedicated connections. For transport protocols that do not support this feature, having this field non-empty SHALL result in a
NotSupportedErrorexception being thrown.If supported and non-empty, the user agent SHALL deem a server certificate trusted if and only if it can successfully verify a certificate hash against
serverCertificateHashesand satisfies custom certificate requirements . The user agent SHALL ignore any hash that uses an unknownalgorithm. If empty, the user agent SHALL use certificate verification procedures it would use for normal fetch operations.This cannot be used with
allowPooling.
-
Let cert be the input certificate, represented as a DER encoding of Certificate message defined in [RFC5280] .
-
Compute the SHA-256 hash of cert and return the computed value.
-
Let hashes be the input array of hashes.
-
Let referenceHash be the computed hash of the input certificate.
-
For every hash hash in hashes :
-
Return false.
The custom certificate requirements are as follows: the certificate MUST be an X.509v3 certificate as defined in [RFC5280] , the key used in the Subject Public Key field MUST be one of the allowed public key algorithms , the current time MUST be within the validity period of the certificate as defined in Section 4.1.2.5 of [RFC5280] and the total length of the validity period MUST NOT exceed two weeks. The user agent MAY impose additional implementation-defined requirements on the certificate.
The exact list of allowed public key algorithms used in the Subject Public Key Info field (and, as a consequence, in the TLS CertificateVerify message) is implementation-defined ; however, it MUST include ECDSA with the secp256r1 (NIST P-256) named group ( [RFC3279] , Section 2.3.5; [RFC8422] ) to provide an interoperable default. It MUST NOT contain RSA keys ( [RFC3279] , Section 2.3.1).
5.10.
WebTransportCloseInfo
Dictionary
The
WebTransportCloseInfo
dictionary
includes
information
relating
to
the
error
code
for
closing
a
WebTransport
.
This
information
is
used
to
set
the
error
code
and
reason
for
a
CONNECTION_CLOSE
frame.
dictionary WebTransportCloseInfo {unsigned long closeCode = 0;DOMString reason = ""; };
The dictionary SHALL have the following attributes:
-
closeCode, of type unsigned long , defaulting to0 -
The error code communicated to the peer.
-
reason, of type DOMString , defaulting to"" -
The reason for closing the
WebTransport.
5.11.
WebTransportStats
Dictionary
The
WebTransportStats
dictionary
includes
information
on
HTTP/3
connection
stats.
Now that quic-transport has been removed, this section needs to be revised. Some of those are safe to expose for HTTP/2 and HTTP/3 connections (like min-RTT), while most would either result in information disclosure or are impossible to define for pooled connections.
dictionary WebTransportStats {DOMHighResTimeStamp timestamp ;unsigned long long bytesSent ;unsigned long long packetsSent ;unsigned long long packetsLost ;unsigned long numOutgoingStreamsCreated ;unsigned long numIncomingStreamsCreated ;unsigned long long bytesReceived ;unsigned long long packetsReceived ;DOMHighResTimeStamp smoothedRtt ;DOMHighResTimeStamp rttVariation ;DOMHighResTimeStamp minRtt ;unsigned long numReceivedDatagramsDropped ; };
The dictionary SHALL have the following attributes:
-
timestamp, of type DOMHighResTimeStamp -
The
timestampfor when the stats are gathered, relative to the UNIX epoch (Jan 1, 1970, UTC). -
bytesSent, of type unsigned long long -
The number of bytes sent on the QUIC connection, including retransmissions. Does not include UDP or any other outer framing.
-
packetsSent, of type unsigned long long -
The number of packets sent on the QUIC connection, including those that are determined to have been lost.
-
packetsLost, of type unsigned long long -
The number of packets lost on the QUIC connection (does not monotonically increase, because packets that are declared lost can subsequently be received).
-
numOutgoingStreamsCreated, of type unsigned long -
The number of outgoing QUIC streams created on the QUIC connection.
-
numIncomingStreamsCreated, of type unsigned long -
The number of incoming QUIC streams created on the QUIC connection.
-
bytesReceived, of type unsigned long long -
The number of total bytes received on the QUIC connection, including duplicate data for streams. Does not include UDP or any other outer framing.
-
packetsReceived, of type unsigned long long -
The number of total packets received on the QUIC connection, including packets that were not processable.
-
smoothedRtt, of type DOMHighResTimeStamp -
The smoothed round-trip time (RTT) currently observed on the connection, as defined in [RFC9002] Section 5.3 .
-
rttVariation, of type DOMHighResTimeStamp -
The mean variation in round-trip time samples currently observed on the connection, as defined in [RFC9002] Section 5.3 .
-
minRtt, of type DOMHighResTimeStamp -
The minimum round-trip time observed on the entire connection.
-
numReceivedDatagramsDropped, of type unsigned long -
The number of datagrams that were dropped, due to too many datagrams buffered between calls to
datagrams'readable.
6.
SendStream
In
this
spec,
we
call
a
WritableStream
providing
outgoing
streaming
features
with
an
outgoing
unidirectional
or
bidirectional
WebTransport
stream
a
SendStream
.
Note:
SendStream
is
not
a
Web
IDL
type.
A
SendStream
is
always
created
by
the
create
procedure.
6.1. Internal Slots
A
SendStream
has
the
following
internal
slots.
| Internal Slot | Description ( non-normative ) |
|---|---|
[[InternalStream]]
| An outgoing unidirectional or bidirectional WebTransport stream . |
[[PendingOperation]]
| A promise representing a pending write or close operation, or null. |
[[Transport]]
|
A
WebTransport
which
owns
this
SendStream
.
|
Note:
These
internal
slots
need
to
be
attached
to
SendStream
(which
is
a
kind
of
WritableStream
)
conceptually,
not
physically.
For
example,
an
implementation
could
place
them
on
an
object
which
is
referenced
from
writeAlgorithm
,
closeAlgorithm
and
abortAlgorithm
in
the
create
procedure.
6.2. Procedures
To
create
a
SendStream
,
with
an
outgoing
unidirectional
or
bidirectional
WebTransport
stream
internalStream
and
a
WebTransport
transport
,
run
these
steps:
-
Let stream be a new
WritableStream, with:-
[[InternalStream]] -
internalStream
-
[[PendingOperation]] -
null
-
[[Transport]] -
transport
-
-
Let writeAlgorithm be an action that writes chunk to stream , given chunk .
-
Let closeAlgorithm be an action that closes stream .
-
Let abortAlgorithm be an action that aborts stream with reason , given reason .
-
Set up stream with writeAlgorithm set to writeAlgorithm , closeAlgorithm set to closeAlgorithm , abortAlgorithm set to abortAlgorithm .
-
Add the following steps to stream ’s [[controller]]'s [[signal]].
-
If stream .
[[PendingOperation]]is null, then abort these steps. -
Let reason be stream ’s [[controller]]'s [[signal]]'s abort reason .
-
Let abortPromise be the result of aborting stream with reason .
-
Upon fulfillment of abortPromise , reject promise with reason .
-
Let pendingOperation be stream .
[[PendingOperation]]. -
Set stream .
[[PendingOperation]]to null. -
Resolve pendingOperation with promise .
-
-
Append stream to transport .
[[SendStreams]]. -
Return stream .
SendStream
stream
,
run
these
steps:
-
Let transport be stream .
[[Transport]]. -
If chunk is not a
BufferSource, return a promise rejected with aTypeError. -
Let promise be a new promise.
-
Let bytes be a copy of the byte sequence which chunk represents.
-
Set stream .
[[PendingOperation]]to promise . -
Return promise and run the remaining steps in parallel .
-
Wait for transport .
[[Datagrams]]'s[[OutgoingDatagramsQueue]]to be empty.Note: This means outgoing datagrams are prioritized over stream data.
-
Send bytes on stream .
[[InternalStream]]and wait for the operation to complete. -
If the previous step failed, abort the remaining steps.
Note: We don’t reject promise here because we handle network errors elsewhere, and those steps error stream and reject the result of this write operation.
-
Queue a network task with transport to run these steps:
-
Set stream .
[[PendingOperation]]to null. -
Resolve promise with undefined.
-
Note:
The
user-agent
MAY
have
a
buffer
to
improve
the
transfer
performance.
Such
a
buffer
SHOULD
have
a
fixed
upper
limit,
to
carry
the
backpressure
information
to
the
user
of
SendStream
.
This
also
means
the
fulfillment
of
the
promise
returned
from
this
algorithm
(or,
WritableStreamDefaultWriter.write
)
does
NOT
necessarily
mean
that
the
chunk
is
acked
by
the
server
[QUIC]
.
It
may
just
mean
that
the
chunk
is
appended
to
the
buffer.
To
make
sure
that
the
chunk
arrives
at
the
server,
use
an
application-level
protocol.
SendStream
stream
,
run
these
steps:
-
Let transport be stream .
[[Transport]]. -
Let promise be a new promise.
-
Remove stream from transport .
[[SendStreams]]. -
Set stream .
[[PendingOperation]]to promise . -
Return promise and run the remaining steps in parallel .
-
Send FIN on stream .
[[InternalStream]]and wait for the operation to complete. -
Wait for stream .
[[InternalStream]]to enter the "Data Recvd" state. [QUIC] -
Queue a network task with transport to run these steps:
-
Set stream .
[[PendingOperation]]to null. -
Resolve promise with undefined.
-
SendStream
stream
with
reason
,
run
these
steps:
-
Let transport be stream .
[[Transport]]. -
Let promise be a new promise.
-
Let code be 0.
-
Remove stream from transport .
[[SendStreams]]. -
If reason is a
WebTransportErrorand reasons .[[StreamErrorCode]]is not null, then set code to reason .[[StreamErrorCode]]. -
If code < 0, then set code to 0.
-
If code > 255, then set code to 255.
Note: Valid values of code are from 0 to 255 inclusive. The code will be encoded to a number in [0x52e4a40fa8db, 0x52e4a40fa9e2] as decribed in [WEB-TRANSPORT-HTTP3] .
-
Return promise and run the remaining steps in parallel .
-
Reset stream .
[[InternalStream]]with code . -
Queue a network task with transport to resolve promise with undefined.
6.3. STOP_SENDING signal coming from the server
SendStream
stream
gets
a
STOP_SENDING
signal
from
the
server,
run
these
steps:
-
Let transport be stream .
[[Transport]]. -
Let code be the application protocol error code attached to the STOP_SENDING frame. [QUIC]
Note: Valid values of code are from 0 to 255 inclusive. The code has been decoded from a number in [0x52e4a40fa8db, 0x52e4a40fa9e2] as decribed in [WEB-TRANSPORT-HTTP3] .
-
Queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", abort these steps. -
Remove stream from transport .
[[SendStreams]]. -
Let error be the result of creating a
WebTransportErrorwith"stream". -
Set error .
[[StreamErrorCode]]to code . -
Error stream with error .
-
7.
ReceiveStream
In
this
spec,
we
call
a
ReadableStream
providing
incoming
streaming
features
with
an
incoming
unidirectional
or
bidirectional
WebTransport
stream
a
ReceiveStream
.
A
ReceiveStream
is
a
ReadableStream
of
Uint8Array
that
can
be
read
from,
to
consume
data
received
from
the
server.
ReceiveStream
is
a
readable
byte
stream
,
and
hence
it
allows
its
consumers
to
use
a
BYOB
reader
as
well
as
a
default
reader
.
Note:
ReceiveStream
is
not
a
Web
IDL
type.
A
ReceiveStream
is
always
created
by
the
create
procedure.
7.1. Internal Slots
A
ReceiveStream
has
the
following
internal
slots.
| Internal Slot | Description ( non-normative ) |
|---|---|
[[InternalStream]]
| An incoming unidirectional or bidirectional WebTransport stream . |
[[Transport]]
|
The
WebTransport
object
owning
this
ReceiveStream
.
|
Note:
These
internal
slots
need
to
be
attached
to
ReceiveStream
(which
is
a
kind
of
ReadableStream
)
conceptually,
not
physically.
For
example,
an
implementation
could
place
them
on
an
object
which
is
referenced
from
pullAlgorithm
and
cancelAlgorithm
in
the
create
procedure.
7.2. Procedures
To
create
a
ReceiveStream
,
with
an
incoming
unidirectional
or
bidirectional
WebTransport
stream
internalStream
and
a
WebTransport
transport
,
run
these
steps:
-
Let stream be a new
ReadableStream, with:-
[[InternalStream]] -
internalStream
-
[[Transport]] -
transport
-
-
Let pullAlgorithm be an action that pulls bytes from stream .
-
Let cancelAlgorithm be an action that cancels stream with reason , given reason .
-
Set up with byte reading support stream with pullAlgorithm set to pullAlgorithm and cancelAlgorithm set to cancelAlgorithm .
-
Append stream to transport .
[[ReceiveStreams]]. -
Return stream .
To
pull
bytes
from
a
ReceiveStream
stream
,
run
these
steps.
-
Let transport be stream .
[[Transport]]. -
Let internalStream be stream .
[[InternalStream]]. -
Let promise be a new promise.
-
Let buffer , offset , and maxBytes be null.
-
If stream ’s current BYOB request view for stream is not null:
-
Set offset to stream ’s current BYOB request view .[[ByteOffset]].
-
Set maxBytes to stream ’s current BYOB request view 's byte length .
-
Set buffer to stream ’s current BYOB request view 's underlying buffer .
-
-
Otherwise:
-
Set offset to 0.
-
Set maxBytes to an implementation-defined size.
-
Set buffer be a new
ArrayBufferwith maxBytes size. If allocating theArrayBufferfails, return a promise rejected with aRangeError.
-
-
Return promise and run the remaining steps in parallel .
-
Write the bytes that area read from internalStream into buffer with offset offset , up to maxBytes bytes. Wait until either at least one byte is read or FIN is received. Let read be the number of read bytes, and let hasReceivedFIN be whether FIN was accompanied. Note: The user-agent MAY have a buffer to improve the transfer performance. Such a buffer SHOULD have a fixed upper limit, to carry the backpressure information to the server.
Note: This operation may return before filling up all of bytes .
-
If the previous step failed, abort the remaining steps.
Note: We don’t reject promise here because we handle network errors elsewhere, and those steps error stream and reject the result of this read operation.
-
Queue a network task with transport to run these steps:
Note: If the buffer described above is available in the event loop where this procedure is running, the following steps may run immediately.
-
If read > 0:
-
Set view to a new
Uint8Arraywith buffer , offset and read . -
Enqueue view into stream .
-
-
If hasReceivedFIN is true:
-
Remove stream from transport .
[[ReceiveStreams]]. -
Close stream .
-
-
Resolve promise with undefined.
-
To
cancel
a
ReceiveStream
stream
with
reason
,
run
these
steps.
-
Let transport be stream .
[[Transport]]. -
Let internalStream be stream .
[[InternalStream]]. -
Let promise be a new promise.
-
Let code be 0.
-
If reason is a
WebTransportErrorand reasons .[[StreamErrorCode]]is not null, then set code to reason .[[StreamErrorCode]]. -
If code < 0, then set code to 0.
-
If code > 255, then set code to 255.
Note: Valid values of code are from 0 to 255 inclusive. The code will be encoded to a number in [0x52e4a40fa8db, 0x52e4a40fa9e2] as decribed in [WEB-TRANSPORT-HTTP3] .
-
Remove stream from transport .
[[SendStreams]]. -
Return promise and run the remaining steps in parallel .
-
Send STOP_SENDING with internalStream and code .
-
Queue a network task with transport to run these steps:
Note: If the buffer described above is available in the event loop where this procedure is running, the following steps may run immediately.
-
Remove stream from transport .
[[ReceiveStreams]]. -
Resolve promise with undefined.
-
7.3. Reset signal coming from the server
ReceiveStream
stream
gets
a
reset
signal
from
the
server,
run
these
steps:
-
Let transport be stream .
[[Transport]]. -
Let code be the application protocol error code attached to the RESET_STREAM frame. [QUIC]
Note: Valid values of code are from 0 to 255 inclusive. The code has been decoded from a number in [0x52e4a40fa8db, 0x52e4a40fa9e2] as decribed in [WEB-TRANSPORT-HTTP3] .
-
Queue a network task with transport to run these steps:
-
If transport .
[[State]]is"closed"or"failed", abort these steps. -
Remove stream from transport .
[[ReceiveStreams]]. -
Let error be the result of creating a
WebTransportErrorwith"stream". -
Set error .
[[StreamErrorCode]]to code . -
Error stream with error .
-
8.
Interface
WebTransportBidirectionalStream
[Exposed =(Window ,Worker ),SecureContext ]interface {WebTransportBidirectionalStream readonly attribute ReadableStream readable ;readonly attribute WritableStream writable ; };
8.1. Internal slots
A
WebTransportBidirectionalStream
has
the
following
internal
slots.
| Internal Slot | Description ( non-normative ) |
|---|---|
[[Readable]]
|
A
ReceiveStream
.
|
[[Writable]]
|
A
SendStream
.
|
[[Transport]]
|
The
WebTransport
object
owning
this
WebTransportBidirectionalStream
.
|
8.2. Attributes
-
readable, of type ReadableStream , readonly -
The getter steps are to return this 's
[[Readable]]. -
writable, of type WritableStream , readonly -
The getter steps are to return this 's
[[Writable]].
8.3. Procedures
WebTransportBidirectionalStream
with
a
bidirectional
WebTransport
stream
internalStream
and
a
WebTransport
object
transport
,
run
these
steps.
-
Let readable be the result of creating a
ReceiveStreamwith internalStream and transport . -
Let writable be the result of creating a
SendStreamwith internalStream and transport . -
Let stream be a new
WebTransportBidirectionalStream, with:-
[[Readable]] -
readable
-
[[Writable]] -
writable
-
[[Transport]] -
transport
-
-
Return stream .
9.
WebTransportError
Interface
WebTransportError
is
a
subclass
of
DOMException
that
represents
-
An error coming from the server or the network, or
-
A reason for a client-initiated abort operation.
[Exposed =(Window ,Worker ),SecureContext ]interface WebTransportError :DOMException {constructor (optional WebTransportErrorInit = {});init readonly attribute WebTransportErrorSource source ;readonly attribute octet ?streamErrorCode ; };dictionary { [WebTransportErrorInit Clamp ]octet ;streamErrorCode DOMString ; };message enum {WebTransportErrorSource ,"stream" , };"session"
9.1. Internal slots
A
WebTransportError
has
the
following
internal
slots.
| Internal Slot | Description ( non-normative ) |
|---|---|
[[Source]]
|
A
WebTransportErrorSource
indicating
the
source
of
this
error.
|
[[StreamErrorCode]]
| The application protocol error code for this error, or null. |
9.2. Constructor
The
new
WebTransportError(init)
constructor
steps
are:
-
Let error be this .
-
Let message be init .
messageif it exists, and""otherwise. -
Set up error with message and
"stream". -
Set error .
[[StreamErrorCode]]to init .streamErrorCodeif it exists.
9.3. Attributes
-
source, of type WebTransportErrorSource , readonly -
The getter steps are to return this 's
[[Source]]. -
streamErrorCode, of type octet , readonly, nullable -
The getter steps are to return this 's
[[StreamErrorCode]].
9.4. Procedures
To
create
a
WebTransportError
error
with
a
WebTransportErrorSource
source
,
run
these
steps:
-
Let message be an implementation-defined string.
-
Let error be a new
WebTransportError. -
Set up error with message and source .
To
set
up
a
WebTransportError
error
with
a
DOMString
message
and
a
WebTransportErrorSource
source
,
run
these
steps:
-
Set error ’s internal slots as:
-
[[StreamErrorCode]] -
null
-
[[Source]] -
source
-
-
Run new DOMException(message, name) constructor steps with error , message and
"WebTransportError".Note: This name does not have a mapping to a legacy code, so this 's
codeis 0.
10. Protocol Mappings
This section is non-normative.
This section describes the [QUIC] protocol behavior of methods defined in this specification, utilizing [WEB-TRANSPORT-HTTP3] .
| Method | QUIC Protocol Action |
|---|---|
writable
.
abort
(errorCode)
| send RESET_STREAM with errorCode |
writable
.
close
()
| send STREAM_FINAL |
writable
.getWriter().
write
()
| send STREAM |
writable
.getWriter().
close
()
| send STREAM_FINAL |
writable
.getWriter().
abort
(errorCode)
| send RESET_STREAM with errorCode |
readable
.
cancel
(errorCode)
| send STOP_SENDING with errorCode |
readable
.getReader().
read
()
| receive STREAM or STREAM_FINAL |
readable
.getReader().
cancel
(errorCode)
| send STOP_SENDING with errorCode |
11. Privacy and Security Considerations
This section is non-normative; it specifies no new behaviour, but instead summarizes information already present in other parts of the specification.
11.1. Confidentiality of Communications
The fact that communication is taking place cannot be hidden from adversaries that can observe the network, so this has to be regarded as public information.
All of the transport protocols described in this document use either TLS [RFC8446] or a semantically equivalent protocol, thus providing all of the security properties of TLS, including confidentiality and integrity of the traffic. WebTransport over HTTP uses the same certificate verification mechanism as outbound HTTP requests, thus relying on the same public key infrastructure for authentication of the remote server. In WebTransport, certificate verification errors are fatal; no interstitial allowing bypassing certificate validation is available.
11.2. State Persistence
WebTransport by itself does not create any new unique identifiers or new ways to persistently store state, nor does it automatically expose any of the existing persistent state to the server. For instance, none of the transports defined in this document automatically send cookies, support HTTP authentication or caching invalidation mechanisms. Since they use TLS, they may support TLS session tickets, which could be used by the server (though not by passive network observers) to correlate different connections from the same client. This is not specific to WebTransport by itself, but rather an inherent property of all TLS-based protocols; thus, this is out-of-scope for this specification.
11.3. Protocol Security
WebTransport imposes a set of requirements as described in [WEB-TRANSPORT-OVERVIEW] , including:
-
Ensuring that the remote server is aware that the WebTransport protocol is in use and confirming that the remote server is willing to use the WebTransport protocol. [WEB-TRANSPORT-HTTP3] uses a combination of ALPN [RFC7301] , an HTTP/3 setting, and a
:protocolpseudo-header to identify the WebTransport protocol. -
Allowing the server to filter connections based on the origin of the resource originating the transport session. The
Originheader field on the session establishment request carries this information.
Protocol security considerations related are described in the Security Considerations sections of [WEB-TRANSPORT-HTTP3] .
Networking APIs can be commonly used to scan the local network for available hosts, and thus be used for fingerprinting and other forms of attacks. WebTransport follows the WebSocket approach to this problem: the specific connection error is not returned until an endpoint is verified to be a WebTransport endpoint; thus, the Web application cannot distinguish between a non-existing endpoint and the endpoint that is not willing to accept connections from the Web.
11.4. Authentication using Certificate Hashes {#certificate-hashes}
Normally, a user agent authenticates a TLS connection between itself and a remote endpoint by verifying the validity of the TLS server certificate provided against the server name in the URL [RFC6125] . This is accomplished by chaining server certificates to one of the trust anchors maintained by the user agent; the trust anchors in question are responsible for authenticating the server names in the certificates. We will refer to this system as Web PKI.
This API provides web applications with a capability to connect to a remote network endpoint authenticated by a specific server certificate, rather than its server name. This mechanism enables connections to endpoints for which getting long-term certificates can be challenging, including hosts that are ephemeral in nature (e.g. short-lived virtual machines), or that are not publicly routable. Since this mechanism substitutes Web PKI-based authentication for an individual connection, we need to compare the security properties of both.
A remote server will be able to successfully perform a TLS handshake only if it posesses the private key corresponding to the public key of the certificate specified. The API identifies the certificates using their hashes. That is only secure as long as the cryptographic hash function used has second-preimage resistance. The only function defined in this document is SHA-256; the API provides a way to introduce new hash functions through allowing multiple algorithm-hash pairs to be specified.
It is important to note that Web PKI provides additional security mechanisms in addition to simply establishing a chain of trust for a server name. One of them is handling certificate revocation. In cases where the certificate used is ephemeral, such a mechanism is not necessary. In other cases, the Web application has to consider the mechanism by which the certificate hashes are provisioned; for instance, if the hash is provided as a cached HTTP resource, the cache needs to be invalidated if the corresponding certificate has been rotated due to compromise. Another security feature provided by the Web PKI are safeguards against certain issues with key generation, such as rejecting certificates with known weak keys; while this specification does not provide any specific guidance, browsers MAY reject those as a part of implementation-defined behavior.
Web PKI enforces an expiry period requirement on the certificates. This requirement limits the scope of potential key compromise; it also forces server operators to design systems that support and actively perform key rotation. For this reason, WebTransport imposes a similar expiry requirement; as the certificates are expected to be ephemeral or short-lived, the expiry period is limited to two weeks. The two weeks limit is a balance between setting the expiry limit as low as possible to minimize consequences of a key compromise, and maintaining it sufficiently high to accomodate for clock skew across devices, and to lower the costs of synchronizing certificates between the client and the server side.
The WebTransport API lets the application specify multiple certificate hashes at once, allowing the client to accept multiple certificates for a period in which a new certificate is being rolled out.
Unlike a similar mechanism in WebRTC, the server certificate hash API in WebTransport does not provide any means of authenticating the client; the fact that the client knows what the server certificate is or how to contact it is not sufficient. The application has to establish the identity of the client in-band if necessary.
12. Examples
12.1. Sending a buffer of datagrams
This section is non-normative.
Sending
a
buffer
of
datagrams
can
be
achieved
by
using
the
datagrams
'
writable
attribute.
In
the
following
example
datagrams
are
only
sent
if
the
transport
is
ready
to
send.
async function sendDatagrams( url, datagrams) { const wt= new WebTransport( url); const writer= wt. datagrams. writable. getWriter(); for ( const datagramof datagrams) { await writer. ready; writer. write( datagram). catch (() => {}); } }
12.2. Sending datagrams at a fixed rate
This section is non-normative.
Sending
datagrams
at
a
fixed
rate
regardless
if
the
transport
is
ready
to
send
can
be
achieved
by
simply
using
datagrams
'
writable
and
not
using
the
ready
attribute.
More
complex
scenarios
can
utilize
the
ready
attribute.
// Sends datagrams every 100 ms. async function sendFixedRate( url, createDatagram, ms= 100 ) { const wt= new WebTransport( url); await wt. ready; const writer= wt. datagrams. writable. getWriter(); const datagram= createDatagram(); setInterval(() => writer. write( datagram). catch (() => {}), ms); }
12.3. Receiving datagrams
This section is non-normative.
Datagrams
can
be
received
by
reading
from
the
transport.
datagrams
.
readable
attribute.
Null
values
may
indicate
that
packets
are
not
being
processed
quickly
enough.
async function receiveDatagrams( url) { const wt= new WebTransport( url); for await ( const datagramof wt. datagrams. readable) { // Process the datagram } }
12.4. Sending a stream
This section is non-normative.
Sending
data
as
a
one-way
stream
can
be
achieved
by
using
the
createUnidirectionalStream
function
and
the
resulting
stream’s
writer.
async function sendData( url, data) { const wt= new WebTransport( url); const writable= await wt. createUnidirectionalStream(); const writer= writable. getWriter(); await writer. write( data); await writer. close(); }
Encoding
can
also
be
done
through
pipes
from
a
ReadableStream
,
for
example
using
TextEncoderStream
.
async function sendText( url, readableStreamOfTextData) { const wt= new WebTransport( url); const writable= await wt. createUnidirectionalStream(); await readableStreamOfTextData. pipeThrough( new TextEncoderStream( "utf-8" )) . pipeTo( writable); }
12.5. Receiving incoming streams
This section is non-normative.
Reading
incoming
streams
can
be
achieved
by
iterating
over
the
incomingUnidirectionalStreams
attribute,
and
then
consuming
each
ReceiveStream
by
iterating
over
its
chunks.
async function receiveData( url, processTheData) { const wt= new WebTransport( url); for await ( const readableof wt. incomingUnidirectionalStreams) { // consume streams individually, reporting per-stream errors (( async () => { try { for await ( const chunkof readable) { processTheData( chunk); } } catch ( e) { console. error( e); } })()); } }
Decoding
can
also
be
done
through
pipes
to
new
WritableStreams,
for
example
using
TextDecoderStream
.
This
example
assumes
text
output
should
not
be
interleaved,
and
therefore
only
reads
one
stream
at
a
time.
async function receiveText( url, createWritableStreamForTextData) { const wt= new WebTransport( url); for await ( const readableof wt. incomingUnidirectionalStreams) { // consume sequentially to not interleave output, reporting per-stream errors try { await readable. pipeThrough( new TextDecoderStream( "utf-8" )) . pipeTo( createWritableStreamForTextData()); } catch ( e) { console. error( e); } } }
12.6. Complete example
This section is non-normative.
This example illustrates use of the closed and ready promises, opening of uni-directional and bi-directional streams by either the client or the server, and sending and receiving datagrams.
// Adds an entry to the event log on the page, optionally applying a specified // CSS class. let wt, streamNumber, datagramWriter; connect. onclick= async () => { try { const url= document. getElementById( 'url' ). value; wt= new WebTransport( url); addToEventLog( 'Initiating connection...' ); await wt. ready; addToEventLogaddToEventLog( ` ${ ( wt. reliability== "reliable-only" ) ? "TCP" : "UDP" } ` + `connection ready.` ); wt. closed. then(() => addToEventLog( 'Connection closed normally.' )) . catch (() => addToEventLog( 'Connection closed abruptly.' , 'error' )); streamNumber= 1 ; datagramWriter= wt. datagrams. writable. getWriter(); readDatagrams(); acceptUnidirectionalStreams(); document. forms. sending. elements. send. disabled= false ; document. getElementById( 'connect' ). disabled= true ; } catch ( e) { addToEventLog( `Connection failed. ${ e} ` , 'error' ); } } sendData. onclick= async () => { const form= document. forms. sending. elements; const rawData= sending. data. value; const data= new TextEncoder( 'utf-8' ). encode( rawData); try { switch ( form. sendtype. value) { case 'datagram' : { await datagramWriter. write( data); addToEventLog( `Sent datagram: ${ rawData} ` ); break ; } case 'unidi' : { const writable= await wt. createUnidirectionalStream(); const writer= writable. getWriter(); await writer. write( data); await writer. close(); addToEventLog( `Sent a unidirectional stream with data: ${ rawData} ` ); break ; } case 'bidi' : { const duplexStream= await wt. createBidirectionalStream(); const n= streamNumber++ ; readFromIncomingStream( duplexStream. readable, n); const writer= duplexStream. writable. getWriter(); await writer. write( data); await writer. close(); addToEventLog( `Sent bidirectional stream # ${ n} with data: ${ rawData} ` ); break ; } } } catch ( e) { addToEventLog( `Error while sending data: ${ e} ` , 'error' ); } } // Reads datagrams into the event log until EOF is reached. async function readDatagrams() { try { const decoder= new TextDecoderStream( 'utf-8' ); for await ( const dataof wt. datagrams. readable. pipeThrough( decoder)) { addToEventLog( `Datagram received: ${ data} ` ); } addToEventLog( 'Done reading datagrams!' ); } catch ( e) { addToEventLog( `Error while reading datagrams: ${ e} ` , 'error' ); } } async function acceptUnidirectionalStreams() { try { for await ( const readableof wt. incomingUnidirectionalStreams) { const number= streamNumber++ ; addToEventLog( `New incoming unidirectional stream # ${ number} ` ); readFromIncomingStream( readable, number); } addToEventLog( 'Done accepting unidirectional streams!' ); } catch ( e) { addToEventLog( `Error while accepting streams ${ e} ` , 'error' ); } } async function readFromIncomingStream( readable, number) { try { const decoder= new TextDecoderStream( 'utf-8' ); for await ( const chunkof readable. pipeThrough( decoder)) { addToEventLog( `Received data on stream # ${ number} : ${ chunk} ` ); } addToEventLog( `Stream # ${ number} closed` ); } catch ( e) { addToEventLog( `Error while reading from stream # ${ number} : ${ e} ` , 'error' ); addToEventLog( ` ${ e. message} ` ); } } function addToEventLog( text, severity= 'info' ) { const log= document. getElementById( 'event-log' ); const previous= log. lastElementChild; const entry= document. createElement( 'li' ); entry. innerText= text; entry. className= `log- ${ severity} ` ; log. appendChild( entry); // If the previous entry in the log was visible, scroll to the new element. if ( previous&& previous. getBoundingClientRect(). top< log. getBoundingClientRect(). bottom) { entry. scrollIntoView(); } }
13. Acknowledgements
The editors wish to thank the Working Group chairs and Team Contact, Jan-Ivar Bruaroey, Will Law and Yves Lafon, for their support.
The
WebTransport
interface
is
based
on
the
QuicTransport
interface
initially
described
in
the
W3C
ORTC
CG
,
and
has
been
adapted
for
use
in
this
specification.