1. 简介
本节为非规范性内容。
本规范使用 [WEB-TRANSPORT-HTTP3] 和 [WEB-TRANSPORT-HTTP2] 来向服务器发送数据和从服务器接收数据。 它可以像 WebSockets 一样使用,但支持多路流、单向流、乱序交付以及可靠和不可靠的传输。
注意: 本规范中提供的 API 代表了基于 IETF WEBTRANS WG 正在进行的工作的初步提案。 由于 [WEB-TRANSPORT-HTTP3] 和 [WEB-TRANSPORT-HTTP2] 规范仍在制定中,因此协议和 API 都可能会在未来发生重大变化。
2. 一致性
除了标记为非规范性的章节外,本规范中的所有编写指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。
关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、“MAY”和“OPTIONAL”应按照 [RFC2119] 和 [RFC8174] 中的描述进行解释,当且仅当它们全部以大写形式出现时,如此处所示。
本规范定义了一致性标准,该标准适用于单个产品:实现其中包含的接口的用户代理。
以算法或特定步骤表达的一致性要求可以通过任何方式实现,只要最终结果是等效的。(特别是,本规范中定义的算法旨在易于理解,而不是为了高性能。)
使用 ECMAScript 实现本规范中定义的 API 的实现必须以符合 Web IDL 规范 [WEBIDL] 中定义的 ECMAScript 绑定的方式实现它们,因为本规范使用了该规范及其术语。
3. 协议概念
WebTransport 有两个主要的协议概念:会话和流。每个 WebTransport 会话 可以包含多个 WebTransport 流。
这些不应与 协议名称 混淆,后者是应用层 API 构造。
3.1. WebTransport 会话
一个 WebTransport 会话 是指通过 HTTP/3 或 HTTP/2 底层连接建立的 WebTransport 会话。启用连接池时,一个连接上可能存在多个 WebTransport 会话。
根据 [WEB-TRANSPORT-OVERVIEW] 的定义,一个 WebTransport 会话具有以下能力:
要建立与源 origin 以及 protocols 数组的 WebTransport
会话,请按照 [WEB-TRANSPORT-OVERVIEW]
Section
4.1 中的说明,使用 origin,序列化和同构编码后的值作为请求的
`Origin
` 头部,并将同构编码后的
protocols 作为客户端希望服务器在此会话中使用的协议列表(按优先级排序),遵循 [WEB-TRANSPORT-OVERVIEW]
Section 3.1
中的描述。建立会话时,客户端不得提供任何凭据。生成的底层传输流称为会话的CONNECT 流。
要耗尽一个 WebTransport 会话 session,请按照 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中的说明进行操作。
当服务器请求优雅关闭 CONNECT 流时,WebTransport 会话 session 处于耗尽状态,如 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中所述。
要使用可选整数 code 和可选字节序列 reason 终止一个 WebTransport 会话 session,请按照 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中的说明进行操作。
当服务器关闭 CONNECT 流时,WebTransport 会话 session 处于已终止状态,并可选择包含整数 code 和字节序列 reason,如 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中所述。
3.2. WebTransport 流
WebTransport 流是 WebTransport 会话上可靠且按顺序排列的字节流的概念,如 [WEB-TRANSPORT-OVERVIEW] Section 4.3 中所述。
一个 WebTransport 流是入向单向、出向单向或双向中的一种。
一个 WebTransport 流具有以下能力:
能力 | 定义 | 入向单向 | 出向单向 | 双向 |
---|---|---|---|---|
发送 字节(可能带有 FIN) | [WEB-TRANSPORT-OVERVIEW] Section 4.3 | 否 | 是 | 是 |
接收 字节(可能带有 FIN) | [WEB-TRANSPORT-OVERVIEW] Section 4.3 | 是 | 否 | 是 |
发送 STOP_SENDING | [WEB-TRANSPORT-OVERVIEW] Section 4.3 | 是 | 否 | 是 |
重置 WebTransport 流 | [WEB-TRANSPORT-OVERVIEW] Section 4.3 | 否 | 是 | 是 |
一个 WebTransport 流具有以下信号:
事件 | 定义 | 入向单向 | 出向单向 | 双向 |
---|---|---|---|---|
STOP_SENDING | [WEB-TRANSPORT-OVERVIEW] Section 4.3 | 否 | 是 | 是 |
RESET_STREAM | [WEB-TRANSPORT-OVERVIEW] Section 4.3 | 是 | 否 | 是 |
流量控制 | [WEB-TRANSPORT-OVERVIEW] Section 4.3 | 否 | 是 | 是 |
4. WebTransportDatagramsWritable
接口
WebTransportDatagramsWritable
是一个
WritableStream
,为发送数据报提供出站流式传输功能。
[Exposed =(Window ,Worker ),SecureContext ,Transferable ]interface WebTransportDatagramsWritable :WritableStream {attribute WebTransportSendGroup ?sendGroup ;attribute long long sendOrder ; };
4.1. 内部插槽
一个 WebTransportDatagramsWritable
对象具有以下内部插槽。
内部插槽 | 描述 (非规范性) |
---|---|
[[OutgoingDatagramsQueue]]
| 一个由出站数据报、时间戳和 promise 组成的元组队列,该 promise 在数据报发送或丢弃时被解析。 |
[[Transport]]
| 拥有此 WebTransportDatagramsWritable 的一个
WebTransport 。
|
[[SendGroup]]
| 一个可选的 WebTransportSendGroup ,或者为 null。
|
[[SendOrder]]
| 一个可选的发送顺序号,默认为 0。 |
要创建一个
WebTransportDatagramsWritable
,给定一个
WebTransport
transport,一个
sendGroup,以及一个 sendOrder,请执行以下步骤。
-
设 stream 为一个 新的
WebTransportDatagramsWritable
,具有:[[OutgoingDatagramsQueue]]
-
一个空队列
[[Transport]]
-
transport
[[SendGroup]]
-
sendGroup
[[SendOrder]]
-
sendOrder
-
设 writeDatagramsAlgorithm 为一个运行 writeDatagrams,参数为 transport 和 stream 的操作。
-
设置 stream,其中 writeAlgorithm 设置为 writeDatagramsAlgorithm。
-
返回 stream。
4.2. 属性
sendGroup
, 类型为 WebTransportSendGroup, 可空-
getter 步骤:
-
返回 this 的
[[SendGroup]]
。
setter 步骤,给定 value:
-
如果 value 非空,且 value.
[[Transport]]
不是 this.[[Transport]]
,抛出一个InvalidStateError
。 -
设置 this.
[[SendGroup]]
为 value。
-
sendOrder
, 类型为 long long-
getter 步骤:
-
返回 this 的
[[SendOrder]]
。
setter 步骤,给定 value:
-
设置 this.
[[SendOrder]]
为 value。
-
4.3. 过程
writeDatagrams 算法给定一个 transport 和 writable 作为参数,以及 data 作为输入。它通过运行以下步骤来定义:
-
设 timestamp 为一个表示当前时间的 timestamp。
-
如果 data 不是一个
BufferSource
对象,则返回 一个 promise 被拒绝,并返回一个TypeError
。 -
设 datagrams 为 transport.
[[Datagrams]]
。 -
如果 datagrams.
[[OutgoingMaxDatagramSize]]
小于 data 的 [[ByteLength]],则返回 一个 promise 被解析,并返回 undefined。 -
设 promise 为一个新的 promise。
-
设 bytes 为 data 表示的字节的副本。
-
设 chunk 为一个元组,包含 bytes、timestamp 和 promise。
-
将 chunk 入队到 writable.
[[OutgoingDatagramsQueue]]
。 -
如果 writable.
[[OutgoingDatagramsQueue]]
的长度小于 datagrams.[[OutgoingDatagramsHighWaterMark]]
,则解析 promise 并返回 undefined。 -
返回 promise。
注意: 关联的 WritableStream
仅当该流的 writeDatagrams 返回的所有 promise 都已解析时,才会调用 writeDatagrams。因此,只有当
Web
开发人员关注 WritableStreamDefaultWriter.ready
时,时间戳和过期持续时间才能很好地工作。
要 sendDatagrams,给定一个
WebTransport
对象
transport 和一个 WebTransportDatagramsWritable
对象
writable,将一个网络任务入队,使用 transport 运行以下步骤:
-
设 queue 为 writable.
[[OutgoingDatagramsQueue]]
的副本。注意: 上述副本以及将网络任务排队以运行这些步骤都可以进行优化。
-
设 maxSize 为 transport.
[[Datagrams]]
.[[OutgoingMaxDatagramSize]]
。 -
设 duration 为 transport.
[[Datagrams]]
.[[OutgoingDatagramsExpirationDuration]]
。 -
如果 duration 为 null,则将 duration 设置为一个 实现定义的值。
-
并行运行以下步骤:
-
当 queue 不为空时:
-
如果 transport.
[[State]]
不是"connected"
,则返回。 -
当 queue 不为空时:
-
设 bytes、timestamp 和 promise 为 queue 的第一个元素。
-
如果 bytes 的长度 ≤ maxSize:
-
如果无法立即将 bytes 发送到网络,则跳出此循环。
-
发送一个数据报,使用 transport.
[[Session]]
和 bytes。
-
-
从 queue 中移除第一个元素。
-
-
对于任何 WebTransport
对象,用户代理必须:其
[[State]]
为 "connecting"
或
"connected"
,在与其关联的 WebTransportDatagramsWritable
对象的子集(由 发送顺序规则确定)上运行 sendDatagrams,并且只要算法能够取得进展,就应尽快执行此操作。
发送顺序规则是,通常,发送可能与先前排队的流和数据报的发送交错进行,以及尚未排队以通过此传输发送的流和数据报,但发送必须暂停,直到所有在具有相同
[[SendGroup]]
和更高
[[SendOrder]]
的流和数据报上排队等待发送的字节,这些流和数据报既没有 出错,也没有被 流量控制阻止,已经被发送。
注意: 允许在传输的 [[State]]
为 "connecting"
时写入数据报。数据报存储在
[[OutgoingDatagramsQueue]]
中,并且可以像在 "connected"
状态下一样丢弃它们。一旦传输的 [[State]]
变为 "connected"
,它将开始发送排队的数据报。
5. WebTransportDatagramDuplexStream
接口
一个 WebTransportDatagramDuplexStream
是一个通用的双工流。
[Exposed =(Window ,Worker ),SecureContext ]interface WebTransportDatagramDuplexStream {WebTransportDatagramsWritable createWritable (optional WebTransportSendOptions = {});
options readonly attribute ReadableStream readable ;readonly attribute unsigned long maxDatagramSize ;attribute unrestricted double ?incomingMaxAge ;attribute unrestricted double ?outgoingMaxAge ;attribute unrestricted double incomingHighWaterMark ;attribute unrestricted double outgoingHighWaterMark ; };
5.1. 内部插槽
一个 WebTransportDatagramDuplexStream
对象具有以下内部插槽。
内部插槽 | 描述 (非规范性) |
---|---|
[[Readable]]
| 一个用于传入数据报的
ReadableStream 。
|
[[Writables]]
| 一个 WebTransportDatagramsWritable 流的有序集合,最初为空。
|
[[IncomingDatagramsQueue]]
| 一个由传入数据报和时间戳对组成的队列。 |
[[IncomingDatagramsPullPromise]]
| 一个由 pullDatagrams 设置的 promise,用于等待传入数据报。 |
[[IncomingDatagramsHighWaterMark]]
| 一个表示传入数据报的高水位线的
unrestricted double 。
|
[[IncomingDatagramsExpirationDuration]]
| 一个表示传入数据报的过期持续时间(以毫秒为单位)的 unrestricted double ,或者为 null。
|
[[OutgoingDatagramsHighWaterMark]]
| 一个表示传出数据报的高水位线的
unrestricted double 。
|
[[OutgoingDatagramsExpirationDuration]]
| 一个 unrestricted double
值,表示传出数据报的过期持续时间(以毫秒为单位),或者为 null。
|
[[OutgoingMaxDatagramSize]]
|
一个表示传出数据报的最大大小的整数。
最大数据报大小取决于所使用的协议。在 HTTP/3 [WEB-TRANSPORT-HTTP3] 中,该值与路径
MTU
的估计值相关,该估计值会减少一些实现定义量以考虑任何开销。在 HTTP/2 [WEB-TRANSPORT-HTTP2] 中,没有等效的限制。
由于数据报的处理通常涉及将整个数据报保存在内存中,因此实现将对大小有限制。未来的协议扩展可以启用这些大小限制的信令,以用于所有协议变体。 |
对于任何 WebTransport
对象,用户代理可以更新
[[OutgoingMaxDatagramSize]]
,其
[[State]]
为 "connecting"
或
"connected"
。
要创建一个 WebTransportDatagramDuplexStream
,给定一个
readable,请执行以下步骤。
-
设 stream 为一个 新的
WebTransportDatagramDuplexStream
,具有:[[Readable]]
-
readable
[[Writables]]
-
一个空的 有序集合。
[[IncomingDatagramsQueue]]
-
一个空队列
[[IncomingDatagramsPullPromise]]
-
null
[[IncomingDatagramsHighWaterMark]]
-
一个实现定义的值
[[IncomingDatagramsExpirationDuration]]
-
null
[[OutgoingDatagramsHighWaterMark]]
-
一个实现定义的值
此实现定义的值应进行调整以确保良好的吞吐量,而不会危及传输数据的及时性。
[[OutgoingDatagramsExpirationDuration]]
-
null
[[OutgoingMaxDatagramSize]]
-
一个实现定义的整数。
-
返回 stream。
5.2. 方法
createWritable()
-
创建一个
WebTransportDatagramsWritable
。当调用
createWritable()
方法时,用户代理必须运行以下步骤:-
设 transport 为与 this 关联的
WebTransport
对象。 -
如果 transport.
[[State]]
为"closed"
或"failed"
,抛出一个InvalidStateError
。 -
返回使用 transport、sendGroup 和 sendOrder 创建一个
WebTransportDatagramsWritable
的结果。
-
5.3. 属性
readable
, 类型为 ReadableStream, readonly-
getter 步骤:
-
返回 this.
[[Readable]]
。
-
incomingMaxAge
, 类型为 unrestricted double, nullable-
getter 步骤:
setter 步骤,给定 value:
-
如果 value 为负数或 NaN,抛出一个
RangeError
。 -
如果 value 为
0
,设置 value 为 null。 -
设置 this.
[[IncomingDatagramsExpirationDuration]]
为 value。
maxDatagramSize
, 类型为 unsigned long, readonly-
可以传递给
WebTransportDatagramsWritable
的最大大小数据。 getter 步骤是返回 this.[[OutgoingMaxDatagramSize]]
。 outgoingMaxAge
, 类型为 unrestricted double, nullable-
getter 步骤:
setter 步骤,给定 value:
-
如果 value 为负数或 NaN,抛出一个
RangeError
。 -
如果 value 为
0
,设置 value 为 null。 -
设置 this.
[[OutgoingDatagramsExpirationDuration]]
为 value。
incomingHighWaterMark
, 类型为 unrestricted double-
getter 步骤:
setter 步骤,给定 value:
-
如果 value 为负数或 NaN,抛出一个
RangeError
。 -
如果 value <
1
,设置 value 为1
。 -
设置 this.
[[IncomingDatagramsHighWaterMark]]
为 value。
outgoingHighWaterMark
, 类型为 unrestricted double-
getter 步骤:
setter 步骤,给定 value:
-
如果 value 为负数或 NaN,抛出一个
RangeError
。 -
如果 value <
1
,设置 value 为1
。 -
设置 this.
[[OutgoingDatagramsHighWaterMark]]
为 value。
5.4. 过程
要 pullDatagrams,给定一个
WebTransport
对象
transport,运行以下步骤:
-
设 datagrams 为 transport.
[[Datagrams]]
。 -
断言:datagrams.
[[IncomingDatagramsPullPromise]]
为 null。 -
设 queue 为 datagrams.
[[IncomingDatagramsQueue]]
。 -
如果 queue 为空,则:
-
设置 datagrams.
[[IncomingDatagramsPullPromise]]
为一个新的 promise。 -
返回 datagrams.
[[IncomingDatagramsPullPromise]]
。
-
-
设 datagram 和 timestamp 为 出队 queue 的结果。
-
如果 datagrams.
[[Readable]]
的 当前 BYOB 请求视图不为 null,则:-
设 view 为 datagrams.
[[Readable]]
的 当前 BYOB 请求视图。 -
如果 view 的 字节长度小于 datagram 的大小,则返回 一个 promise 被拒绝,并返回一个
RangeError
。 -
设 elementSize 为 类型化数组构造函数表中为 view.[[TypedArrayName]] 指定的元素大小。如果 view 没有 [[TypedArrayName]] 内部插槽(即它是一个
DataView
),则设 elementSize 为 0。 -
如果 elementSize 不为 1,则返回 一个 promise 被拒绝,并返回一个
TypeError
。
-
-
从字节中拉取 datagram 到 datagrams.
[[Readable]]
。 -
返回 一个 promise 被解析,并返回 undefined。
要 receiveDatagrams,给定一个
WebTransport
对象
transport,运行以下步骤:
-
设 timestamp 为一个表示当前时间的 timestamp。
-
设 queue 为 datagrams.
[[IncomingDatagramsQueue]]
。 -
设 duration 为 datagrams.
[[IncomingDatagramsExpirationDuration]]
。 -
如果 duration 为 null,则将 duration 设置为一个 实现定义的值。
-
设 session 为 transport.
[[Session]]
。 -
当 session 上有 可用的传入数据报时:
-
设 toBeRemoved 为 queue 的长度减去 datagrams.
[[IncomingDatagramsHighWaterMark]]
。 -
当 queue 不为空时:
-
如果 queue 不为空,并且 datagrams.
[[IncomingDatagramsPullPromise]]
非 null,则:-
设 bytes 和 timestamp 为 出队 queue 的结果。
-
设 promise 为 datagrams.
[[IncomingDatagramsPullPromise]]
。 -
设置 datagrams.
[[IncomingDatagramsPullPromise]]
为 null。 -
将一个网络任务入队,使用 transport 运行以下步骤:
-
设 chunk 为一个表示 bytes 的新的
Uint8Array
对象。 -
入队 chunk 到 datagrams.
[[Readable]]
。 -
解析 promise 并返回 undefined。
-
-
对于任何 WebTransport
对象,用户代理都应该尽快运行 receiveDatagrams,其
[[State]]
为 "connected"
,只要算法能够取得进展。
6. WebTransport
接口
WebTransport
提供了一个 API,用于访问 [WEB-TRANSPORT-OVERVIEW] 中定义的底层传输功能。
[Exposed =(Window ,Worker ),SecureContext ]interface {
WebTransport (
constructor USVString ,
url optional WebTransportOptions = {});
options Promise <WebTransportConnectionStats >getStats (); [NewObject ]Promise <ArrayBuffer >exportKeyingMaterial (BufferSource ,
label optional BufferSource );
context readonly attribute Promise <undefined >ready ;readonly attribute WebTransportReliabilityMode reliability ;readonly attribute WebTransportCongestionControl congestionControl ; [EnforceRange ]attribute unsigned short ?anticipatedConcurrentIncomingUnidirectionalStreams ; [EnforceRange ]attribute unsigned short ?anticipatedConcurrentIncomingBidirectionalStreams ;readonly attribute DOMString protocol ;readonly attribute Promise <WebTransportCloseInfo >closed ;readonly attribute Promise <undefined >draining ;undefined close (optional WebTransportCloseInfo = {});
closeInfo readonly attribute WebTransportDatagramDuplexStream datagrams ;Promise <WebTransportBidirectionalStream >createBidirectionalStream (optional WebTransportSendStreamOptions = {}); /* a ReadableStream of WebTransportBidirectionalStream objects */
options readonly attribute ReadableStream incomingBidirectionalStreams ;Promise <WebTransportSendStream >createUnidirectionalStream (optional WebTransportSendStreamOptions = {}); /* a ReadableStream of WebTransportReceiveStream objects */
options readonly attribute ReadableStream incomingUnidirectionalStreams ;WebTransportSendGroup createSendGroup ();static readonly attribute boolean supportsReliableOnly ; };enum {
WebTransportReliabilityMode ,
"pending" ,
"reliable-only" , };
"supports-unreliable"
6.1. 内部插槽
一个 WebTransport
对象具有以下内部插槽。
内部插槽 | 描述 (非规范性) |
---|---|
[[SendStreams]]
| 此 WebTransport 拥有的一个
WebTransportSendStream
的有序集合。
|
[[ReceiveStreams]]
| 此 WebTransport 拥有的一个 WebTransportReceiveStream 的有序集合。
|
[[IncomingBidirectionalStreams]]
| 一个由 WebTransportBidirectionalStream 对象组成的
ReadableStream 。
|
[[IncomingUnidirectionalStreams]]
| 一个由 WebTransportReceiveStream 组成的 ReadableStream 。
|
[[State]]
| 一个枚举,指示传输的状态。为
"connecting" 、"connected" 、"draining" 、"closed"
和 "failed" 之一。
|
[[Ready]]
| 当关联的 WebTransport 会话 建立时,此 promise 会被实现;如果建立过程失败,则此 promise 会被拒绝。 |
[[Reliability]]
| 一个
WebTransportReliabilityMode ,指示第一跳是否支持不可靠的
(UDP) 传输,或者是否仅支持可靠的 (TCP 回退) 传输。在建立连接之前,返回 "pending" 。
|
[[CongestionControl]]
| 一个
WebTransportCongestionControl ,指示应用程序是否请求了针对吞吐量或低延迟优化的拥塞控制算法的偏好,并且该偏好是否已由用户代理满足;或者
"default" 。
|
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]
| 应用程序预计服务器将创建的并发打开的入向单向流的数量,或者为 null。 |
[[AnticipatedConcurrentIncomingBidirectionalStreams]]
| 应用程序预计服务器将创建的并发打开的双向流的数量,或者为 null。 |
[[Protocol]]
| 一个字符串,指示服务器选择的应用程序级协议(如果有)。最初为空字符串。 |
[[Closed]]
| 当关联的 WebTransport 对象正常关闭时,此 promise
会被实现;当其突然关闭或在初始化时失败时,此
promise 会被拒绝。
|
[[Draining]]
| 当关联的 WebTransport 会话 耗尽时,此 promise 会被实现。 |
[[Datagrams]]
| 一个 WebTransportDatagramDuplexStream 。
|
[[Session]]
| 此 WebTransport 对象的WebTransport
会话,或者为 null。
|
6.2. 构造函数
WebTransport()
构造函数时,用户代理必须运行以下步骤:
-
设 baseURL 为 this 的 相关设置对象的 API 基本 URL。
-
如果 parsedURL 失败,抛出一个
SyntaxError
异常。 -
如果 parsedURL scheme 不是
https
,抛出一个SyntaxError
异常。 -
如果 parsedURL fragment 不为 null,抛出一个
SyntaxError
异常。 -
设 allowPooling 为
options
的allowPooling
。 -
设 dedicated 为 allowPooling 的否定。
-
设 serverCertificateHashes 为
options
的serverCertificateHashes
(如果存在),否则为 null。 -
如果 dedicated 为 false 且 serverCertificateHashes 为非 null,则 抛出一个
NotSupportedError
异常。 -
设 requireUnreliable 为
options
的requireUnreliable
。 -
设 congestionControl 为
options
的congestionControl
。 -
如果 congestionControl 不是
"default"
,并且用户代理不支持任何针对 congestionControl 优化的拥塞控制算法(如 [RFC9002] Section 7 允许的那样),则将 congestionControl 设置为"default"
。 -
如果 protocols 中的任何值出现多次,或者不符合构成 WebTransport 协议定义的协商应用程序协议值的元素的条件,或者其同构编码长度为 0 或超过 512,则抛出一个
SyntaxError
异常。 [WEB-TRANSPORT-OVERVIEW] Section 3.1。 -
设 anticipatedConcurrentIncomingUnidirectionalStreams 为
options
的anticipatedConcurrentIncomingUnidirectionalStreams
。 -
设 anticipatedConcurrentIncomingBidirectionalStreams 为
options
的anticipatedConcurrentIncomingBidirectionalStreams
。 -
设 incomingDatagrams 为一个新的
ReadableStream
。 -
设 datagrams 为创建一个
WebTransportDatagramDuplexStream
的结果,其readable 设置为 incomingDatagrams。 -
设 transport 为新构造的
WebTransport
对象,具有:[[SendStreams]]
-
一个空的 有序集合
[[ReceiveStreams]]
-
一个空的 有序集合
[[IncomingBidirectionalStreams]]
-
一个新的
ReadableStream
[[IncomingUnidirectionalStreams]]
-
一个新的
ReadableStream
[[State]]
-
"connecting"
[[Ready]]
-
一个新的 promise
[[Reliability]]
-
"pending"
[[CongestionControl]]
-
congestionControl
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]
-
anticipatedConcurrentIncomingUnidirectionalStreams
[[AnticipatedConcurrentIncomingBidirectionalStreams]]
-
anticipatedConcurrentIncomingBidirectionalStreams
[[Protocol]]
-
一个空字符串
[[Closed]]
-
一个新的 promise
[[Draining]]
-
一个新的 promise
[[Datagrams]]
-
datagrams
[[Session]]
-
null
-
设 pullDatagramsAlgorithm 为一个运行 pullDatagrams,参数为 transport 的操作。
注意: 建议将 64kB 缓冲区用于数据报,因为有效的最大 WebTransport 数据报帧大小具有 QUIC 最大数据报帧大小的上限,建议为 64kB (参见 [QUIC-DATAGRAM] Section 3)。这将确保流不会因数据报大于缓冲区而出错。
-
设置字节读取支持 incomingDatagrams,其中 pullAlgorithm 设置为 pullDatagramsAlgorithm,并且 highWaterMark 设置为 0。
-
设 pullBidirectionalStreamAlgorithm 为一个运行 pullBidirectionalStream,参数为 transport 的操作。
-
设置 transport.
[[IncomingBidirectionalStreams]]
,其中 pullAlgorithm 设置为 pullBidirectionalStreamAlgorithm,并且 highWaterMark 设置为 0。 -
设 pullUnidirectionalStreamAlgorithm 为一个运行 pullUnidirectionalStream,参数为 transport 的操作。
-
设置 transport.
[[IncomingUnidirectionalStreams]]
,其中 pullAlgorithm 设置为 pullUnidirectionalStreamAlgorithm,并且 highWaterMark 设置为 0。 -
使用 HTTP 初始化 WebTransport,参数为 transport、parsedURL、dedicated、 requireUnreliable、congestionControl、protocols 和 serverCertificateHashes。
-
返回 transport。
WebTransport
对象
transport,一个 URL 记录 url,一个布尔值 dedicated,一个布尔值
requireUnreliable,一个 WebTransportCongestionControl
congestionControl,
一个 protocols 数组,以及一个
sequence<WebTransportHash
>
serverCertificateHashes,运行以下步骤。
-
设 client 为 transport 的 相关设置对象。
-
设 origin 为 client 的 源。
-
设 request 为一个新的 请求,其 URL 为 url,客户端为 client,策略容器为 client 的 策略容器,目标为一个空字符串, 源为 origin 且 重定向模式为 "error"。
-
如果 是否应阻止请求 与 request 一起返回 "Blocked",或者如果 request 应因错误端口而被阻止 返回 blocked,则中止剩余步骤并 将网络任务排队到 transport 以运行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
,则中止这些步骤。 -
设 error 为新 创建的
WebTransportError
,其source
为"session"
。 -
清理 transport,使用 error。
-
-
并行运行以下步骤 并行,但当 transport.
[[State]]
变为"closed"
或"failed"
时 中止:-
如果 dedicated 为 false,则设 newConnection 为 "
no
"; 否则为 "yes-and-dedicated
"。 -
设 connection 为 获取连接的结果,使用 networkPartitionKey、url、false、newConnection 和 requireUnreliable。如果用户代理 支持多个拥塞控制算法,请选择一个适合 congestionControl 的算法,用于在此 connection 上发送数据。当获取连接时,如果 指定了 serverCertificateHashes,而不是使用默认的证书验证 算法,如果证书满足自定义证书要求 并且如果 验证证书哈希针对 serverCertificateHashes 返回 true,则认为证书有效。如果任一条件不满足,则让 connection 失败。
-
如果 connection 失败,则中止剩余步骤并 将网络任务排队到 transport 以运行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
,则中止这些步骤。 -
设 error 为新 创建的
WebTransportError
,其source
为"session"
。 -
清理 transport,使用 error。
注意: 不会遵循重定向。由重定向引起的网络错误与其它网络错误有意地无法区分。在跨域上下文中,这将揭示通常会被 CORS 阻止的信息。在同源上下文中,这可能会鼓励应用程序滥用握手作为传递信息的向量。
-
-
等待 connection 接收第一个 SETTINGS 帧,并设 settings 为表示 SETTINGS 帧的字典。
-
如果 settings 不包含值为 1 的 SETTINGS_ENABLE_WEBTRANPORT,或者它不 包含值为 1 的 H3_DATAGRAM,则中止剩余步骤并 将网络任务排队到 transport 以运行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
,则中止这些步骤。 -
设 error 为新 创建的
WebTransportError
,其source
为"session"
。 -
清理 transport,使用 error。
-
-
建立一个 WebTransport 会话,使用 origin 和 protocols 在 connection 上。
注意: 此步骤还包含 [QUIC-DATAGRAM] 中指定的传输参数交换。
-
如果上一步失败,则中止剩余步骤并 将网络任务排队到 transport 以运行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
,则中止这些步骤。 -
设 error 为新 创建的
WebTransportError
,其source
为"session"
。 -
清理 transport,使用 error。
-
-
设 session 为已建立的 WebTransport 会话。
-
断言:maxDatagramSize 是一个整数。
-
将网络任务排队到 transport 以运行以下步骤:
-
如果 transport.
[[State]]
不是"connecting"
: -
设置 transport.
[[State]]
为"connected"
。 -
设置 transport.
[[Session]]
为 session。 -
设置 transport.
[[Protocol]]
为协商的应用程序的字符串值 协议(如果存在),遵循 [WEB-TRANSPORT-OVERVIEW] Section 3.1, 或者如果不存在,则为""
。 -
如果连接是 HTTP/3 连接,则设置 transport.
[[Reliability]]
为"supports-unreliable"
。 -
如果连接是 HTTP/2 连接 [WEB-TRANSPORT-HTTP2],则设置 transport 的
[[Reliability]]
为"reliable-only"
。
-
-
WebTransport
对象
transport,运行以下步骤。
-
如果 transport.
[[State]]
为"connecting"
,则返回在 transport.[[Ready]]
实现时执行以下步骤的结果:-
返回使用 transport pullBidirectionalStream 的结果。
-
-
如果 transport.
[[State]]
不是"connected"
,则返回一个新的 拒绝的 promise,其中包含 一个InvalidStateError
。 -
设 session 为 transport.
[[Session]]
。 -
设 p 为一个新的 promise。
-
并行运行以下步骤 并行:
-
等待直到存在一个 可用的传入双向流。
-
设 internalStream 为 接收双向流的结果。
-
将网络任务排队到 transport 以运行以下步骤:
-
设 stream 为创建一个
WebTransportBidirectionalStream
(使用 internalStream 和 transport)的结果。 -
Enqueue 将 stream 排入 transport 的
[[IncomingBidirectionalStreams]]
。 -
解析 p 为未定义。
-
-
-
返回 p。
WebTransport
对象
transport,运行以下步骤。
-
如果 transport.
[[State]]
为"connecting"
,则返回在 transport.[[Ready]]
实现时执行以下步骤的结果:-
返回使用 transport pullUnidirectionalStream 的结果。
-
-
如果 transport.
[[State]]
不是"connected"
,则返回一个新的 拒绝的 promise,其中包含 一个InvalidStateError
。 -
设 session 为 transport.
[[Session]]
。 -
设 p 为一个新的 promise。
-
并行运行以下步骤 并行:
-
等待直到存在一个 可用的传入单向流。
-
设 internalStream 为 接收传入单向流的结果。
-
将网络任务排队到 transport 以运行以下步骤:
-
设 stream 为创建一个
WebTransportReceiveStream
(使用 internalStream 和 transport)的结果。 -
Enqueue 将 stream 排入 transport 的
[[IncomingUnidirectionalStreams]]
。 -
解析 p 为未定义。
-
-
-
返回 p。
6.3. 属性
ready
, 类型为 Promise<undefined>, readonlyclosed
, 类型为 Promise<WebTransportCloseInfo>, readonly-
在获取时,它必须返回 this 的
[[Closed]]
。 draining
, 类型为 Promise<undefined>, readonly-
在获取时,它必须返回 this 的
[[Draining]]
。 datagrams
, 类型为 WebTransportDatagramDuplexStream, readonly-
用于通过此会话发送和接收数据报的单个双工流。
datagrams
属性的 getter 步骤应为:-
返回 this 的
[[Datagrams]]
。
-
incomingBidirectionalStreams
, 类型为 ReadableStream, readonly-
返回已从服务器接收的
ReadableStream
WebTransportBidirectionalStream
。注意: 传入流是否已具有数据将取决于服务器行为。
incomingBidirectionalStreams
属性的 getter 步骤应为: incomingUnidirectionalStreams
, 类型为 ReadableStream, readonly-
单向流的
ReadableStream
,每个流都由一个WebTransportReceiveStream
表示,这些流已从服务器接收。注意: 传入流是否已具有数据将取决于服务器行为。
incomingUnidirectionalStreams
的 getter 步骤为: reliability
, 类型为 WebTransportReliabilityMode, readonly-
连接是否支持不可靠(通过 UDP)传输或仅支持可靠(通过 TCP 回退)传输。 在建立连接之前,返回
"pending"
。 getter 步骤是返回 this 的[[Reliability]]
。 congestionControl
, 类型为 WebTransportCongestionControl, readonly-
应用程序的偏好(如果在构造函数中请求,并且用户代理满足),用于针对此连接上的发送进行吞吐量或低延迟优化的拥塞控制算法。如果请求了偏好但未满足,则该值为
"default"
getter 步骤是返回 this 的[[CongestionControl]]
。 supportsReliableOnly
, 类型为 boolean, readonly-
如果用户代理支持通过专门可靠的 WebTransport 会话 连接,则返回 true;否则返回 false。
anticipatedConcurrentIncomingUnidirectionalStreams
, 类型为 unsigned short, nullable-
可选地让应用程序指定它预计服务器创建的并发打开的 传入单向流的数量。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]
来减少未来的往返行程。getter 步骤是返回 this 的
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]
。给定 value,setter 步骤是将 this 的
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]
设置为 value。 anticipatedConcurrentIncomingBidirectionalStreams
, 类型为 unsigned short, nullable-
可选地让应用程序指定它预计服务器创建的并发打开的 双向流的数量。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑
[[AnticipatedConcurrentIncomingBidirectionalStreams]]
来减少未来的往返行程。getter 步骤是返回 this 的
[[AnticipatedConcurrentIncomingBidirectionalStreams]]
。给定 value,setter 步骤是将 this 的
[[AnticipatedConcurrentIncomingBidirectionalStreams]]
设置为 value。
注意: 设置 anticipatedConcurrentIncomingUnidirectionalStreams
或
anticipatedConcurrentIncomingBidirectionalStreams
不能保证
应用程序将接收到它预计的流数。
protocol
, 类型为 DOMString, readonly-
一旦建立了 WebTransport 会话,并且使用了
protocols
构造函数选项来提供非空数组,则返回服务器选择的应用程序级协议(如果有)。否则,返回一个空字符串。 getter 步骤是返回 this 的[[Protocol]]
。
6.4. 方法
关闭(closeInfo)
-
终止与 WebTransport 对象相关联的 WebTransport 会话。
当调用 close 时,用户代理必须执行以下步骤:
-
令 transport 为 this。
-
如果 transport.
[[State]]
为"closed"
或"failed"
,则中止这些步骤。 -
如果 transport.
[[State]]
为"connecting"
:-
令 error 为新创建的 异常
WebTransportError
,其source
为"session"
。 -
用 error 对 transport 进行 清理。
-
中止这些步骤。
-
-
令 session 为 transport.
[[Session]]
。 -
令 code 为 closeInfo.
closeCode
。 -
令 reasonString 为 closeInfo.
reason
的最大 代码单元前缀,其 长度不超过 UTF-8 编码前缀的 1024。 -
令 reason 为 reasonString,并进行 UTF-8 编码。
-
并行,用 code 和 reason 终止 session。
注意:这也会对 transport.
[[SendStreams]]
和[[ReceiveStreams]]
包含的 WebTransport 流进行 复位 或 发送 STOP_SENDING。 -
用
AbortError
和 closeInfo 对 transport 进行 清理。
-
getStats()
-
收集此
WebTransport
的 底层连接 的统计信息,并异步报告结果。当调用 getStats 时,用户代理必须执行以下步骤:
-
令 transport 为 this。
-
令 p 为一个新的 promise。
-
如果 transport.
[[State]]
为"failed"
,拒绝 p,并给出InvalidStateError
,然后中止这些步骤。 -
并行运行以下步骤:
-
如果 transport.
[[State]]
为"connecting"
,则等待状态发生变化。 -
如果 transport.
[[State]]
为"failed"
,则在 队列化一个网络任务 后中止这些步骤,该任务将 transport 用于 拒绝 p,并给出InvalidStateError
。 -
如果 transport.
[[State]]
为"closed"
,则在 队列化一个网络任务 后中止这些步骤,该任务将 transport 用于 解决 p,并返回连接最近可用的统计信息。具体收集时间点由实现自行定义。 -
收集来自 底层连接 的统计信息,包括数据报相关统计。
-
队列化一个网络任务,用 transport 运行以下步骤:
-
令 stats 为一个新的 new
WebTransportConnectionStats
对象,表示已收集的统计信息。 -
解决 p,并返回 stats。
-
-
-
返回 p。
-
exportKeyingMaterial(BufferSource label, optional BufferSource context)
-
从与此
WebTransport
唯一关联的 TLS 会话 的 TLS 密钥材料导出器 导出密钥材料, 用于其 底层连接。当调用
exportKeyingMaterial
时,用户代理必须执行以下步骤:-
令 transport 为 this。
-
令 labelLength 为 label.字节长度。
-
如果 labelLength 超过 255,返回一个 被拒绝的 promise,错误类型为
RangeError
。 -
令 contextLength 为 0。
-
如果提供了 context,则设置 contextLength 为 context.字节长度。
-
如果 contextLength 超过 255,返回一个 被拒绝的 promise,错误类型为
RangeError
。 -
令 p 为一个新的 promise。
-
并行运行以下步骤,但当 transport 的
[[State]]
变为"closed"
或"failed"
时中止,并 队列化一个网络任务,用 transport 拒绝 p,并给出InvalidStateError
:-
令 keyingMaterial 为导出 TLS 密钥材料的结果,定义见 [WEB-TRANSPORT-HTTP3] 第 4.7 节, 参数为 labelLength、label、contextLength,以及(如有)context。
-
-
返回 p。
-
createBidirectionalStream()
-
为一个传出的双向流创建
WebTransportBidirectionalStream
对象。注意,仅创建流并不会立即被对端感知,直到使用它进行数据发送为止。注意:服务器不会在数据发送之前获知该流的存在。
当调用
createBidirectionalStream
时,用户代理必须执行以下步骤:-
令 transport 为 this。
-
如果 transport.
[[State]]
为"closed"
或"failed"
, 返回一个新的 被拒绝的 promise,错误类型为InvalidStateError
。 -
令 waitUntilAvailable 为
options
的waitUntilAvailable
。 -
令 p 为一个新的 promise。
-
并行运行以下步骤,但当 transport 的
[[State]]
变为"closed"
或"failed"
时中止,并 队列化一个网络任务,用 transport 拒绝 p,并给出InvalidStateError
:-
令 streamId 为一个新的、对 transport.
[[Session]]
有效且唯一的流 ID,定义见 [QUIC] 第 19.11 节。如果由于资源耗尽无法马上获得, 且 waitUntilAvailable 为 true,则等待可用,否则为 false 时, 在 队列化一个网络任务 后中止这些步骤,该任务用 transport 拒绝 p ,并给出QuotaExceededError
。 -
令 internalStream 为 创建双向流 的结果, 参数为 transport.
[[Session]]
和 streamId。 -
队列化一个网络任务,用 transport 运行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
, 拒绝 p,错误类型为InvalidStateError
,然后中止这些步骤。 -
令 stream 为 创建的
WebTransportBidirectionalStream
,参数为 internalStream、transport、sendGroup、 sendOrder。 -
解决 p,并返回 stream。
-
-
-
返回 p。
-
createUnidirectionalStream()
-
为一个传出的单向流创建
WebTransportSendStream
。注意,仅创建流并不会立即被服务器感知,直到使用它进行数据发送为止。注意:服务器不会在数据发送之前获知该流的存在。
当调用
createUnidirectionalStream()
方法时,用户代理必须执行以下步骤:-
令 transport 为 this。
-
如果 transport.
[[State]]
为"closed"
或"failed"
, 返回一个新的 被拒绝的 promise,错误类型为InvalidStateError
。 -
令 waitUntilAvailable 为
options
的waitUntilAvailable
。 -
令 p 为一个新的 promise。
-
并行运行以下步骤,但当 transport 的
[[State]]
变为"closed"
或"failed"
时中止,并 队列化一个网络任务,用 transport 拒绝 p,并给出InvalidStateError
:-
令 streamId 为一个新的、对 transport.
[[Session]]
有效且唯一的流 ID,定义见 [QUIC] 第 19.11 节。如果由于资源耗尽无法马上获得, 且 waitUntilAvailable 为 true,则等待可用,否则为 false 时, 在 队列化一个网络任务 后中止这些步骤,该任务用 transport 拒绝 p ,并给出QuotaExceededError
。 -
令 internalStream 为 创建传出单向流 的结果, 参数为 transport.
[[Session]]
和 streamId。 -
队列化一个网络任务,用 transport 运行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
, 拒绝 p,错误类型为InvalidStateError
,然后中止这些步骤。 -
令 stream 为 创建的
WebTransportSendStream
,参数为 internalStream、transport、sendGroup、 sendOrder。 -
解决 p,并返回 stream。
-
-
-
返回 p。
-
createSendGroup()
-
创建一个
WebTransportSendGroup
。当调用
createSendGroup()
方法时,用户代理必须执行以下步骤:-
令 transport 为 this。
-
如果 transport.
[[State]]
为"closed"
或"failed"
, 抛出InvalidStateError
。 -
返回 创建的
WebTransportSendGroup
,参数为 transport。
-
6.5. 过程
WebTransport
transport,使用 error 和
可选的 closeInfo,运行以下步骤:
-
设 sendStreams 为 transport.
[[SendStreams]]
的副本。 -
设 receiveStreams 为 transport.
[[ReceiveStreams]]
的副本。 -
设 outgoingDatagramWritables 为 transport.
[[Datagrams]]
.[[Writables]]
。 -
设 incomingDatagrams 为 transport.
[[Datagrams]]
.[[Readable]]
。 -
设 ready 为 transport.
[[Ready]]
。 -
设 closed 为 transport.
[[Closed]]
。 -
设 incomingBidirectionalStreams 为 transport.
[[IncomingBidirectionalStreams]]
。 -
设 incomingUnidirectionalStreams 为 transport.
[[IncomingUnidirectionalStreams]]
。 -
将 transport.
[[SendStreams]]
设置为一个空的 集合。 -
将 transport.
[[ReceiveStreams]]
设置为一个空的 集合。 -
将 transport.
[[Datagrams]]
.[[OutgoingDatagramsQueue]]
设置为一个空的 队列。 -
将 transport.
[[Datagrams]]
.[[IncomingDatagramsQueue]]
设置为一个空的 队列。 -
如果给定了 closeInfo,则将 transport.
[[State]]
设置为"closed"
。 否则,将 transport.[[State]]
设置为"failed"
。 -
对于 sendStreams 中的每个 stream,运行以下步骤:
-
如果 stream.
[[PendingOperation]]
不为 null,则使用 error 拒绝 stream.[[PendingOperation]]
。 -
Error 使用 error 对 stream 进行错误处理。
-
-
对于 receiveStreams 中的每个 stream,使用 error 错误化 stream。
注意: 脚本作者可以注入代码,这些代码在 Promise 解析中同步运行。因此,从这里开始,不要接触 transport,因为它可能会被脚本以不可预测的方式修改。 这也适用于调用此过程的逻辑。
-
如果给定了 closeInfo,则:
-
否则:
-
Reject 使用 error 拒绝 closed。
-
将 closed.
[[PromiseIsHandled]]
设为 true。 -
Reject 使用 error 拒绝 ready。
-
将 ready.
[[PromiseIsHandled]]
设为 true。 -
Error 使用 error 错误处理 incomingBidirectionalStreams。
-
Error 使用 error 错误处理 incomingUnidirectionalStreams。
-
对 outgoingDatagramWritables 中的每个 writable,使用 error 错误处理该 writable。
-
Error 使用 error 错误处理 incomingDatagrams。
-
要与一个 WebTransport
transport 和一系列步骤 steps 将网络任务排队,运行以下步骤:
6.6. 并非由客户端发起的会话终止
WebTransport
transport 关联的 WebTransport 会话
使用可选的 code 和 reasonBytes 终止时,运行以下步骤:
-
将网络任务排队到 transport 以运行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
,中止这些步骤。 -
设 error 为新 创建的
WebTransportError
,其source
为"session"
。 -
设 closeInfo 为一个 新的
WebTransportCloseInfo
。 -
如果给定了 code,则将 closeInfo 的
closeCode
设置为 code。 -
如果给定了 reasonBytes,则将 closeInfo 的
reason
设置为 reasonBytes, UTF-8 解码。注意: 没有语言或方向 元数据可用于 reasonBytes。 First-strong 启发式方法可以用于 显示该值时的方向。
-
清理 transport,使用 error 和 closeInfo。
-
WebTransport
transport 的 底层连接出现连接错误时,
运行以下步骤:
6.7. 上下文清理步骤
本规范定义了上下文清理步骤,作为给定
WebTransport
transport 的以下步骤:
-
如果 transport.
[[State]]
为"connected"
,则:-
将 transport.
[[State]]
设置为"failed"
。 -
并行,终止 transport.
[[Session]]
。 -
将网络任务排队到 transport 以运行以下步骤:
-
设 error 为新 创建的
WebTransportError
,其source
为"session"
。 -
清理 transport,使用 error。
-
-
-
如果 transport.
[[State]]
为"connecting"
,则将 transport.[[State]]
设置为"failed"
。这也需要在 worker 中完成。参见 #127 和 whatwg/html#6731。
6.8. 垃圾回收
如果一个 WebTransport
对象的 [[State]]
为 "connecting"
,则如果
[[IncomingBidirectionalStreams]]
、
[[IncomingUnidirectionalStreams]]
、
任何
WebTransportReceiveStream
或 [[Datagrams]]
.[[Readable]]
是锁定的,或者如果
ready
、
draining
或 closed
promise 正在被观察,则不得进行垃圾回收。
如果一个 WebTransport
对象的 [[State]]
为 "connected"
,则如果
[[IncomingBidirectionalStreams]]
、
[[IncomingUnidirectionalStreams]]
、
任何
WebTransportReceiveStream
或 [[Datagrams]]
.[[Readable]]
是锁定的,或者如果
draining
或 closed
promise 正在被观察,则不得进行垃圾回收。
如果一个 WebTransport
对象的 [[State]]
为 "draining"
,则如果
[[IncomingBidirectionalStreams]]
、
[[IncomingUnidirectionalStreams]]
、
任何
WebTransportReceiveStream
或 [[Datagrams]]
.[[Readable]]
是锁定的,或者如果
closed
promise 正在被观察,则不得进行垃圾回收。
一个 WebTransport
对象,具有一个已建立的 WebTransport
会话,该会话具有排队要传输到网络的数据,包括
[[Datagrams]]
.[[OutgoingDatagramsQueue]]
中的数据报,
不得进行垃圾回收。
如果在 底层连接
仍然打开时,对 WebTransport
对象进行垃圾回收,则用户代理必须
终止
WebTransport 会话,使用应用程序错误代码
0
和应用程序错误消息 ""
。
6.9. 配置
dictionary {
WebTransportHash DOMString ;
algorithm BufferSource ; };
value dictionary WebTransportOptions {boolean allowPooling =false ;boolean requireUnreliable =false ;sequence <WebTransportHash >serverCertificateHashes ;WebTransportCongestionControl congestionControl = "default"; [EnforceRange ]unsigned short ?anticipatedConcurrentIncomingUnidirectionalStreams =null ; [EnforceRange ]unsigned short ?anticipatedConcurrentIncomingBidirectionalStreams =null ;sequence <DOMString >protocols = []; };enum {
WebTransportCongestionControl ,
"default" ,
"throughput" , };
"low-latency"
WebTransportOptions
是一个参数字典,用于确定如何建立和使用 WebTransport 会话。
allowPooling
, 类型为 boolean, 默认为false
-
如果设置为 true,则可以池化 WebTransport 会话,也就是说,可以与其他 WebTransport 会话共享其 底层连接。
requireUnreliable
, 类型为 boolean, 默认为false
-
如果设置为 true,则如果无法建立 HTTP/3 连接,则无法通过 HTTP/2 连接建立 WebTransport 会话。
serverCertificateHashes
, 类型为 sequence<WebTransportHash>-
此选项仅支持使用专用连接的传输。 对于不支持此功能的传输协议,如果此字段为非空,则应抛出一个
NotSupportedError
异常。如果支持且为非空,则用户代理应仅在可以成功地针对
serverCertificateHashes
验证证书哈希并满足 自定义证书要求时,才认为服务器证书是受信任的。用户代理应忽略任何使用未知algorithm
的哈希。 如果为空,则用户代理应使用它通常用于正常 fetch 操作的证书验证程序。这不能与
allowPooling
一起使用。 congestionControl
, 类型为 WebTransportCongestionControl, 默认为"default"
-
可选地指定应用程序首选的拥塞控制算法,该算法经过调整以用于在此连接上发送数据时的吞吐量或低延迟。这是对用户代理的提示。
anticipatedConcurrentIncomingUnidirectionalStreams
, 类型为 unsigned short, nullable, 默认为null
-
可选地让应用程序指定它预计服务器创建的并发打开的 传入单向流的数量。 用户代理最初必须允许来自服务器的至少 100 个传入单向流。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]
来减少往返行程。 anticipatedConcurrentIncomingBidirectionalStreams
, 类型为 unsigned short, nullable, 默认为null
-
可选地让应用程序指定它预计服务器创建的并发打开的 双向流的数量。 用户代理最初必须允许服务器创建至少 100 个双向流。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑
[[AnticipatedConcurrentIncomingBidirectionalStreams]]
来减少往返行程。 protocols
, 类型为 sequence<DOMString>, 默认为[]
-
一个可选提供的应用程序级协议名称数组。选择首选的应用程序协议并将其传达给客户端对于服务器是可选的。 如果未提供合适的协议,服务器可能会拒绝该请求。
-
设 cert 为 certificate,表示为 [RFC5280]中定义的 Certificate 消息的 DER 编码。
-
计算 cert 的 SHA-256 哈希并返回计算出的值。
-
设 referenceHash 为使用 certificate 计算证书哈希的结果。
-
对于 hashes 中的每个哈希 hash:
-
如果 hash.
value
不为 null 且 hash.algorithm
与 "sha-256" ASCII 大小写不敏感匹配:-
设 hashValue 为 hash.
value
表示的字节序列。 -
如果 hashValue 等于 referenceHash,则返回 true。
-
-
-
返回 false。
自定义证书要求custom certificate requirements如下:该证书必须是 [RFC5280]中定义的 X.509v3 证书, Subject Public Key 字段中使用的密钥必须是允许的公钥算法之一, 当前时间必须在证书的有效期内,如[RFC5280]的第 4.1.2.5 节中所定义, 且有效期的总长度不得超过两周。用户代理可以对证书施加额外的实现定义的要求。
Subject Public Key Info 字段(以及因此,在 TLS CertificateVerify 消息中)中使用的允许的公钥算法的确切列表是实现定义的;但是,它必须包括使用 secp256r1 (NIST P-256) 命名组的 ECDSA([RFC3279], 第 2.3.5 节;[RFC8422]) 以提供可互操作的默认值。它不得包含 RSA 密钥([RFC3279], 第 2.3.1 节)。
6.10.
WebTransportCloseInfo
字典
WebTransportCloseInfo
字典包含与关闭
WebTransport
的错误代码相关的信息。
此信息用于设置 CONNECTION_CLOSE 帧的错误代码和原因。
dictionary WebTransportCloseInfo {unsigned long closeCode = 0;USVString reason = ""; };
该字典应具有以下属性:
closeCode
, 类型为 unsigned long, 默认为0
-
传达给对等方的错误代码。
reason
, 类型为 USVString, 默认为""
-
关闭
WebTransport
的原因。
6.11. WebTransportSendOptions
字典
WebTransportSendOptions
是一个基本参数字典,它会影响
createUnidirectionalStream
、
createBidirectionalStream
以及
createWritable
方法的行为。
dictionary WebTransportSendOptions {WebTransportSendGroup ?sendGroup =null ;long long sendOrder = 0; };
该字典应具有以下属性:
sendGroup
, 类型为 WebTransportSendGroup, nullable, 默认为null
-
一个可选的
WebTransportSendGroup
,用于对创建的流进行分组,或为 null。 sendOrder
, 类型为 long long, 默认为0
-
一个发送顺序号,如果提供,则选择创建的流参与严格排序。 当前在严格排序的流上排队的字节将优先于当前在其他严格排序的流上排队的字节发送,这些流是使用较低的发送顺序号创建的。
如果未提供发送顺序号,则用户代理从中发送字节的顺序相对于其他流是实现定义的。但是,强烈建议用户代理在所有未被较低发送顺序号饿死的流之间公平地分配带宽。
注意: 这是发送方的数据优先级,不保证接收顺序。
6.12. WebTransportSendStreamOptions
字典
WebTransportSendStreamOptions
是一个参数字典,它会影响由
WebTransportSendStream
创建的
createUnidirectionalStream
和
createBidirectionalStream
的行为。
dictionary WebTransportSendStreamOptions :WebTransportSendOptions {boolean waitUntilAvailable =false ; };
该字典应具有以下属性:
waitUntilAvailable
, 类型为 boolean, 默认为false
-
如果为 true,则
createUnidirectionalStream
或createBidirectionalStream
调用返回的 promise 将不会解决, 直到底层连接具有足够的流量控制信用额度来创建流,或者连接达到无法再创建传出流的状态。如果为 false,则如果在调用时没有可用的流量控制窗口,则将拒绝该 promise。
6.13.
WebTransportConnectionStats
字典
WebTransportConnectionStats
字典包含有关WebTransport 会话的底层连接的 WebTransport 特定统计信息的
信息。
注意: 当使用池化时,池化在同一连接上的多个WebTransport 会话都接收相同的信息,即,该信息跨持有相同网络分区密钥的池化的会话公开。
注意: 任何不可用的统计信息都将从WebTransportConnectionStats
字典中缺失。
dictionary WebTransportConnectionStats {unsigned long long bytesSent = 0;unsigned long long packetsSent = 0;unsigned long long bytesLost = 0;unsigned long long packetsLost = 0;unsigned long long bytesReceived = 0;unsigned long long packetsReceived = 0;required DOMHighResTimeStamp smoothedRtt ;required DOMHighResTimeStamp rttVariation ;required DOMHighResTimeStamp minRtt ;required WebTransportDatagramStats ;
datagrams unsigned long long ?estimatedSendRate =null ;boolean atSendCapacity =false ; };
该字典应具有以下属性:
bytesSent
, 类型为 unsigned long long, 默认为0
-
在底层连接上发送的字节数,包括重传。 不包括 UDP 或任何其他外部帧。
packetsSent
, 类型为 unsigned long long, 默认为0
-
在底层连接上发送的数据包数,包括那些被确定为丢失的数据包。
bytesLost
, 类型为 unsigned long long, 默认为0
-
在底层连接上丢失的字节数(不会单调递增,因为随后可能会收到声明为丢失的数据包)。 不包括 UDP 或任何其他外部帧。
packetsLost
, 类型为 unsigned long long, 默认为0
-
在底层连接上丢失的数据包数(不会单调递增,因为随后可能会收到声明为丢失的数据包)。
bytesReceived
, 类型为 unsigned long long, 默认为0
-
在底层连接上收到的总字节数,包括流的重复数据。不包括 UDP 或任何其他外部帧。
packetsReceived
, 类型为 unsigned long long, 默认为0
-
在底层连接上收到的总数据包数,包括无法处理的数据包。
smoothedRtt
, 类型为 DOMHighResTimeStamprttVariation
, 类型为 DOMHighResTimeStampminRtt
, 类型为 DOMHighResTimeStamp-
在整个连接上观察到的最小往返时间。
estimatedSendRate
, 类型为 unsigned long long, nullable, 默认为null
-
用户代理将发送排队数据的估计速率,以每秒比特数为单位。 此速率适用于共享WebTransport 会话的所有流和数据报, 并且由拥塞控制算法计算(可能由
congestionControl
选择)。 此估计不包括任何帧开销,并且表示可以发送应用程序有效负载的速率。如果用户代理当前没有估计值,则成员必须为null
值。即使在先前的结果中不是null
,该成员也可以是null
。 atSendCapacity
, 类型为 boolean, 默认为false
-
值 false 表示
estimatedSendRate
可能受应用程序限制, 这意味着应用程序发送的数据明显少于拥塞控制器允许的数据。当应用程序受到限制时,拥塞控制器可能会对可用的网络容量产生较差的估计。值 true 表示应用程序正在以网络容量发送数据,并且
estimatedSendRate
反映了应用程序可用的网络容量。当atSendCapacity
为true
时,estimatedSendRate
反映了一个上限。 只要应用程序发送速率持续,estimatedSendRate
将适应网络状况。但是,允许estimatedSendRate
为null
,而atSendCapacity
为 true。
6.14.
WebTransportDatagramStats
字典
WebTransportDatagramStats
字典包含有关通过底层连接进行数据报传输的统计信息。
dictionary WebTransportDatagramStats {unsigned long long droppedIncoming = 0;unsigned long long expiredIncoming = 0;unsigned long long expiredOutgoing = 0;unsigned long long lostOutgoing = 0; };
该字典应具有以下属性:
droppedIncoming
, 类型为 unsigned long long, 默认为0
expiredIncoming
, 类型为 unsigned long long, 默认为0
-
由于传入数据报早于
incomingMaxAge
,因此在从datagrams
的readable
读取之前被丢弃的传入数据报的数量。 expiredOutgoing
, 类型为 unsigned long long, 默认为0
-
由于排队发送的数据报早于
outgoingMaxAge
,因此在能够发送之前被丢弃的排队发送的数据报的数量。 lostOutgoing
, 类型为 unsigned long long, 默认为0
7. 接口 WebTransportSendStream
WebTransportSendStream
是一个 WritableStream
,它使用传出单向或双向
WebTransport 流提供传出流功能。
它是一个 WritableStream
,类型为
Uint8Array
,可以写入数据以将数据发送到服务器。
[Exposed =(Window ,Worker ),SecureContext ,Transferable ]interface :
WebTransportSendStream WritableStream {attribute WebTransportSendGroup ?sendGroup ;attribute long long sendOrder ;Promise <WebTransportSendStreamStats >getStats ();WebTransportWriter getWriter (); };
WebTransportSendStream
始终由创建过程创建。
WebTransportSendStream
的传输步骤和传输接收步骤是那些
WritableStream
。
7.1. 属性
sendGroup
, 类型为 WebTransportSendGroup, nullable-
getter 步骤是:
给定value,setter 步骤是:
-
如果value为非空,并且value的
[[Transport]]
不是this的[[Transport]]
,抛出一个InvalidStateError
。 -
将this的
[[SendGroup]]
设置为value。
sendOrder
, 类型为 long long-
getter 步骤是:
给定value,setter 步骤是:
-
将this的
[[SendOrder]]
设置为value。
7.2. 方法
getStats()
-
收集特定于此
WebTransportSendStream
的性能的统计信息,并异步报告结果。调用 getStats 时,用户代理必须运行以下步骤:
-
设p为一个新的 promise。
-
并行运行以下步骤:
-
收集特定于此
WebTransportSendStream
的统计信息。 -
等待统计信息准备就绪。
-
使用transport将网络任务排队以运行以下步骤:
-
设stats为一个新的
WebTransportSendStreamStats
对象,表示收集的统计信息。 -
使用stats解析p。
-
-
-
返回p。
-
getWriter()
-
此方法必须以与从
WritableStream
继承的getWriter
相同的方式实现, 只是代替创建WritableStreamDefaultWriter
,它必须创建一个WebTransportWriter
,其中包含this。
7.3. 内部插槽
WebTransportSendStream
具有以下内部插槽。
内部插槽 | 描述(非规范) |
---|---|
[[InternalStream]]
| 一个传出单向或双向 WebTransport 流。 |
[[PendingOperation]]
| 表示挂起的写入或关闭操作的 promise,或 null。 |
[[Transport]]
| 一个WebTransport ,它拥有此WebTransportSendStream 。
|
[[SendGroup]]
| 一个可选的WebTransportSendGroup ,或 null。
|
[[SendOrder]]
| 一个可选的发送顺序号,默认为 0。 |
[[AtomicWriteRequests]]
| 一组有序集合的 promises,用于跟踪底层接收器排队处理的原子写入请求的子集。 |
[[BytesWritten]]
| 已写入流的字节数。 |
[[CommittedOffset]]
| 流中的一个偏移量,用于记录将传递给对等方的字节数,即使流被重置; 参见[RELIABLE-RESET]。 |
7.4. 过程
要创建一个
WebTransportSendStream
,带有一个传出单向或双向 WebTransport
流
internalStream,一个WebTransport
transport,sendGroup和一个sendOrder,运行以下步骤:
-
设stream为一个新的
WebTransportSendStream
,具有:[[InternalStream]]
-
internalStream
[[PendingOperation]]
-
null
[[Transport]]
-
transport
[[SendGroup]]
-
sendGroup
[[SendOrder]]
-
sendOrder
[[AtomicWriteRequests]]
-
一个空的 ordered set,其元素是 promise。
[[BytesWritten]]
-
0
[[CommittedOffset]]
-
0
-
设writeAlgorithm为一个动作,该动作将chunk写入到stream,给定chunk。
-
设closeAlgorithm为一个动作,该动作关闭stream。
-
设abortAlgorithm为一个动作,该动作使用reason中止stream,给定reason。
-
设置stream,其中writeAlgorithm设置为writeAlgorithm,closeAlgorithm设置为closeAlgorithm, abortAlgorithm设置为abortAlgorithm。
-
设abortSignal为stream的[[controller]].[[abortController]].[[signal]]。
-
将以下步骤添加到abortSignal。
-
设pendingOperation为stream的
[[PendingOperation]]
。 -
如果pendingOperation为 null,则中止这些步骤。
-
将stream的
[[PendingOperation]]
设置为 null。 -
设reason为abortSignal的中止原因。
-
设promise为中止流与reason的结果。
-
-
将stream附加到transport的
[[SendStreams]]
。 -
返回stream。
WebTransportSendStream
stream,运行以下步骤:
-
设transport为stream的
[[Transport]]
。 -
如果chunk不是一个
BufferSource
, 则返回一个被拒绝的 promise,并带有一个TypeError
。 -
设promise为一个新的 promise。
-
设bytes为字节序列(chunk表示)的副本。
-
将stream的
[[PendingOperation]]
设置为promise。 -
设inFlightWriteRequest为 stream的inFlightWriteRequest。
-
如果stream的
[[AtomicWriteRequests]]
包含inFlightWriteRequest,则设atomic为 true,否则为 false。 -
并行运行以下步骤:
-
如果atomic为 true,并且当前的流量控制窗口太小,无法完全发送bytes,则中止剩余步骤,并使用transport将网络任务排队,以运行以下子步骤:
-
将stream的
[[PendingOperation]]
设置为 null。 -
中止stream上的所有原子写入请求。
-
-
否则,在stream的
[[InternalStream]]
上发送bytes,并等待操作完成。 此发送可以与先前排队的流和数据报的发送交错,以及尚未排队以通过此传输发送的流和数据报。用户代理可以有一个缓冲区来提高传输性能。这样的缓冲区应该有一个固定的上限,以将反压信息传递给
WebTransportSendStream
的用户。此发送必须饿死 直到所有在具有相同
[[SendGroup]]
和更高[[SendOrder]]
的流上排队等待发送的字节,既不是出错,也没有被流量控制阻止,都已发送。我们并行访问stream的
[[SendOrder]]
。 用户代理应该响应发送期间这些值的实时更新,但细节是实现定义的。注意:重新传输的排序是实现定义的, 但强烈建议用户代理优先重新传输具有更高
[[SendOrder]]
值的 数据。用户代理应该在所有未被饿死的流之间公平地分配带宽。
注意:这里的公平性定义是实现定义的。
-
如果由于网络错误导致上一步失败,则中止剩余步骤。
注意:我们不在此处拒绝promise,因为我们在其他地方处理网络错误,并且这些步骤拒绝stream的
[[PendingOperation]]
。 -
否则,使用transport将网络任务排队,以运行以下步骤:
-
将stream的
[[PendingOperation]]
设置为 null。 -
将bytes的长度添加到stream的
[[BytesWritten]]
。 -
如果stream的
[[AtomicWriteRequests]]
包含inFlightWriteRequest,则删除inFlightWriteRequest。 -
使用 undefined解析promise。
-
-
-
返回promise。
注意:从此算法(或write(chunk)
)返回的 promise 的实现不一定意味着该块已通过服务器[QUIC]确认。它可能只是意味着该块已附加到缓冲区。为了确保该块到达服务器,服务器需要发送一个应用程序级别的确认消息。
WebTransportSendStream
stream,请执行以下步骤:
-
令 transport 为 stream.
[[Transport]]
。 -
令 promise 为一个新的 promise。
-
移除 stream 从 transport.
[[SendStreams]]
。 -
将 stream.
[[PendingOperation]]
设置为 promise。 -
并行运行以下步骤:
-
在 stream.
[[InternalStream]]
上发送 FIN,并等待操作完成。 -
等待 stream.
[[InternalStream]]
进入“所有数据已提交”状态。[QUIC] -
队列化一个网络任务,用 transport 执行以下步骤:
-
将 stream.
[[PendingOperation]]
设为 null。 -
解决 promise,返回 undefined。
-
-
-
返回 promise。
WebTransportSendStream
stream,并带 reason,请执行以下步骤:
-
令 transport 为 stream.
[[Transport]]
。 -
令 promise 为一个新的 promise。
-
令 code 为 0。
-
移除 stream 从 transport.
[[SendStreams]]
。 -
如果 reason 是
WebTransportError
并且 reason.[[StreamErrorCode]]
不为 null,则将 code 设为 reason.[[StreamErrorCode]]
。 -
如果 code < 0,则将 code 设为 0。
-
如果 code > 4294967295,则将 code 设为 4294967295。
-
令 committedOffset 为 stream.
[[CommittedOffset]]
。注意: code 的有效值为 0 到 4294967295(含)。如果 底层连接 使用 HTTP/3,则该 code 会被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 范围内的数字,详见 [WEB-TRANSPORT-HTTP3]。
-
并行运行以下步骤:
-
复位 stream.
[[InternalStream]]
,参数为 code 和 committedOffset。
-
-
返回 promise。
WebTransportSendStream
stream 上中止所有原子写请求,请执行以下步骤:
-
令 writeRequests 为 stream.writeRequests。
-
令 requestsToAbort 为 stream.
[[AtomicWriteRequests]]
。 -
如果 writeRequests 包含某个不在 requestsToAbort 中的 promise,则 出错 stream,错误类型为
AbortError
, 并中止这些步骤。 -
对于每个 promise 属于 requestsToAbort,拒绝该 promise,错误类型为
AbortError
。
7.5. 服务器发送的STOP_SENDING信号
WebTransportSendStream
stream 接收到来自服务器的
STOP_SENDING 信号时,请执行以下步骤:
-
让 transport 为 stream.
[[Transport]]
. -
让 code 为附加到STOP_SENDING帧的应用协议错误代码。 [QUIC]
注意: 有效的 code 值范围是 0 到 4294967295,包括边界值。如果底层连接使用的是HTTP/3,code 会被编码为一个范围为 [0x52e4a40fa8db, 0x52e5ac983162] 的数字,具体描述见 [WEB-TRANSPORT-HTTP3].
-
排队一个网络任务 使用 transport 执行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
,中止这些步骤。 -
移除 stream 从 transport.
[[SendStreams]]
. -
让 error 为一个新创建的 异常
WebTransportError
,其source
为"stream"
且streamErrorCode
为 code。 -
如果 stream.
[[PendingOperation]]
不为 null,拒绝 stream.[[PendingOperation]]
并附上 error。 -
错误 stream 和 error。
-
7.6. WebTransportSendStreamStats
字典
WebTransportSendStreamStats
字典
包括一个 WebTransportSendStream
的特定统计信息。
dictionary WebTransportSendStreamStats {unsigned long long bytesWritten = 0;unsigned long long bytesSent = 0;unsigned long long bytesAcknowledged = 0; };
字典应具有以下属性:
bytesWritten
, 类型为 unsigned long long, 默认值为0
-
应用程序已成功写入此
WebTransportSendStream
的总字节数。此数字只能增加。 bytesSent
, 类型为 unsigned long long, 默认值为0
-
指示应用程序写入此
WebTransportSendStream
的字节已至少发送一次的进度。此数字只能增加,并且始终小于或等于bytesWritten
。注意: 此值仅表示单个流上的应用数据发送进度,不包括任何网络开销。
bytesAcknowledged
, 类型为 unsigned long long, 默认值为0
-
指示应用程序写入此
WebTransportSendStream
的字节已发送并通过服务器的 QUIC ACK 机制确认接收的进度。只有连续的字节(直到但不包括第一个未确认的字节)被计入。此数字只能增加,并且始终小于或等于bytesSent
。注意: 如果连接是通过 HTTP/2,则此值将与
bytesSent
匹配。
8. 接口
WebTransportSendGroup
WebTransportSendGroup
是一个可选的组织对象,用于跟踪分布在许多个单独
(通常是 严格排序的)
WebTransportSendStream
上的数据传输。
WebTransportSendStream
可以在创建时或通过分配其 sendGroup
属性,被分组到最多一个
WebTransportSendGroup
中。默认情况下,它们是
未分组的。
用户代理在为发送 WebTransportSendStream
分配带宽时,
会将 WebTransportSendGroup
视为同等。
每个 WebTransportSendGroup
还会为评估 sendOrder
编号建立一个独立的数字空间。
[Exposed =(Window ,Worker ),SecureContext ]interface {
WebTransportSendGroup Promise <WebTransportSendStreamStats >getStats (); };
一个 WebTransportSendGroup
总是通过 创建过程生成。
8.1. 方法
getStats()
-
汇总
WebTransportSendStream
的统计数据,这些流在 分组到当前 sendGroup 下,并异步报告结果。当调用 getStats 时,用户代理必须执行以下步骤:
-
创建一个新的 p promise。
-
让 streams 是所有
WebTransportSendStream
,其[[SendGroup]]
是当前 sendGroup。 -
并行运行以下步骤:
-
收集 streams 中所有流的统计数据。
-
排队一个网络任务 使用 transport 执行以下步骤:
-
让 stats 为一个新的
WebTransportSendStreamStats
对象,表示聚合的统计数据。 -
解析 p 并返回 stats。
-
-
-
返回 p。
-
8.2. 内部槽
一个 WebTransportSendGroup
具有以下内部槽。
内部槽 | 描述 (非规范性) |
---|---|
[[Transport]]
| 拥有此 WebTransport
对象的 WebTransportSendGroup 。
|
8.3. 过程
要 创建一个
WebTransportSendGroup
,并绑定一个 WebTransport
transport,请执行以下步骤:
-
让 sendGroup 为一个新的
WebTransportSendGroup
,绑定:[[Transport]]
-
transport
-
返回 sendGroup。
9. 接口 WebTransportReceiveStream
一个 WebTransportReceiveStream
是一个 ReadableStream
,提供传入流功能,具有 传入单向 或 双向
WebTransport流。
它是一个 ReadableStream
的 Uint8Array
,可以读取以消费从服务器接收的数据。WebTransportReceiveStream
是一个 可读字节流,
因此允许其消费者使用 BYOB读取器 以及 默认读取器。
[Exposed =(Window ,Worker ),SecureContext ,Transferable ]interface :
WebTransportReceiveStream ReadableStream {Promise <WebTransportReceiveStreamStats >getStats (); };
一个 WebTransportReceiveStream
总是通过 创建过程生成。
WebTransportReceiveStream
的 传输步骤 和
接收传输步骤
与 ReadableStream 的步骤一致。
9.1. 方法
getStats()
-
收集此
WebTransportReceiveStream
性能的特定统计数据,并异步报告结果。当调用 getStats 时,用户代理必须执行以下步骤:
-
创建一个新的 p promise。
-
并行运行以下步骤:
-
收集此
WebTransportReceiveStream
的特定统计数据。 -
排队一个网络任务 使用 transport 执行以下步骤:
-
让 stats 为一个新的
WebTransportReceiveStreamStats
对象,表示收集到的统计数据。 -
解析 p 并返回 stats。
-
-
-
返回 p。
-
9.2. 内部槽
一个 WebTransportReceiveStream
具有以下内部槽。
内部槽 | 描述 (非规范性) |
---|---|
[[InternalStream]]
| 一个 传入单向 或 双向 WebTransport流。 |
[[Transport]]
| 拥有此 WebTransport
对象的 WebTransportReceiveStream 。
|
9.3. 过程
要 创建一个
WebTransportReceiveStream
,并绑定一个 传入单向 或 双向 WebTransport流
internalStream 和一个 WebTransport
transport,请执行以下步骤:
-
让 stream 为一个新的
WebTransportReceiveStream
,绑定:[[InternalStream]]
-
internalStream
[[Transport]]
-
transport
-
让 pullAlgorithm 为从 stream 拉取字节 的动作。
-
让 cancelAlgorithm 为取消 stream 的动作,并提供 reason。
-
使用字节读取支持设置 stream,将 pullAlgorithm 设置为 pullAlgorithm, 将 cancelAlgorithm 设置为 cancelAlgorithm。
-
将 stream 添加到 transport.
[[ReceiveStreams]]
。 -
返回 stream.
要 拉取字节从一个 WebTransportReceiveStream
stream,请执行以下步骤。
-
将 transport 设为 stream.
[[Transport]]
. -
将 internalStream 设为 stream.
[[InternalStream]]
. -
创建一个新的 promise.
-
将 buffer, offset, 和 maxBytes 初始化为 null.
-
如果 stream 的 当前 BYOB 请求视图 不为空:
-
设置 offset 为 stream 的 当前 BYOB 请求视图.[[ByteOffset]].
-
设置 maxBytes 为 stream 的 当前 BYOB 请求视图 的 字节长度.
-
设置 buffer 为 stream 的 当前 BYOB 请求视图 的 底层缓冲区.
-
-
否则:
-
将 offset 设置为 0.
-
设置 maxBytes 为一个 实现定义 的大小.
-
将 buffer 设置为一个新的
ArrayBuffer
,大小为 maxBytes. 如果分配失败,返回 一个被拒绝的 promise,错误为RangeError
.
-
-
并行运行以下步骤:
-
写入从 internalStream 读取的字节到 buffer,偏移量为 offset,最多 maxBytes 字节. 等待至少读取一个字节或接收 FIN. 设定 read 为读取字节数,设定 hasReceivedFIN 为是否接收到 FIN.
-
如果上述步骤失败,中止剩余步骤.
-
排队一个网络任务 使用 transport 执行以下步骤:
-
如果 read > 0:
-
设置 view 为一个新的
Uint8Array
,使用 buffer, offset, 和 read. -
放入队列 view 到 stream.
-
-
如果 hasReceivedFIN 为 true:
-
移除 stream 从 transport.
[[ReceiveStreams]]
. -
关闭 stream.
-
-
解析 promise 并返回 undefined.
-
-
-
返回 promise.
要 取消一个 WebTransportReceiveStream
stream 并提供 reason,请执行以下步骤。
-
将 transport 设置为 stream.
[[Transport]]
. -
将 internalStream 设置为 stream.
[[InternalStream]]
. -
创建一个新的 promise.
-
将 code 设置为 0.
-
如果 reason 是一个
WebTransportError
且 reason.[[StreamErrorCode]]
不为 null,则设置 code 为 reason.[[StreamErrorCode]]
. -
如果 code 小于 0,则将其设置为 0.
-
如果 code 大于 4294967295,则将其设置为 4294967295.
-
移除 stream 从 transport.
[[SendStreams]]
. -
并行运行以下步骤:
-
发送 STOP_SENDING 并提供 internalStream 和 code.
-
排队一个网络任务以执行以下步骤:
-
移除 stream 从 transport.
[[ReceiveStreams]]
. -
解析 promise 并返回 undefined.
-
-
-
返回 promise.
9.4. 来自服务器的重置信号
WebTransportReceiveStream
stream 接收到来自服务器的
RESET_STREAM 信号时,请执行以下步骤:
-
将 transport 设为 stream.
[[Transport]]
. -
将 code 设为附加到 RESET_STREAM_AT 或 WT_RESET_STREAM 帧的应用协议错误代码。 [RELIABLE-RESET] [WEB-TRANSPORT-HTTP2]
注意: 有效的 code 值范围是 0 到 4294967295,包括边界值。如果底层连接使用的是 HTTP/3,code 会被编码为一个范围为 [0x52e4a40fa8db, 0x52e5ac983162] 的数字,具体描述见 [WEB-TRANSPORT-HTTP3].
-
排队一个网络任务 使用 transport 执行以下步骤:
-
如果 transport.
[[State]]
为"closed"
或"failed"
,中止这些步骤。 -
移除 stream 从 transport.
[[ReceiveStreams]]
. -
将 error 设为一个新创建的 异常
WebTransportError
,其source
为"stream"
且streamErrorCode
为 code。 -
错误 stream 和 error。
-
9.5. WebTransportReceiveStreamStats
字典
WebTransportReceiveStreamStats
字典
包括一个 WebTransportReceiveStream
的特定统计信息。
dictionary WebTransportReceiveStreamStats {unsigned long long bytesReceived = 0;unsigned long long bytesRead = 0; };
字典应具有以下属性:
bytesReceived
, 类型为 unsigned long long, 默认值为0
-
指示服务器应用程序的字节进度,这些字节是针对此
WebTransportReceiveStream
到目前为止已接收的数量。只有连续的字节(直到但不包括第一个丢失的字节)被计入。此数字只能增加。注意: 此值仅表示单个流上的应用数据接收进度,不包括任何网络开销。
bytesRead
, 类型为 unsigned long long, 默认值为0
-
应用程序已成功读取此
WebTransportReceiveStream
的总字节数。此数字只能增加,并且始终小于或等于bytesReceived
。
10. 接口 WebTransportBidirectionalStream
[Exposed =(Window ,Worker ),SecureContext ]interface {
WebTransportBidirectionalStream readonly attribute WebTransportReceiveStream readable ;readonly attribute WebTransportSendStream writable ; };
10.1. 内部插槽
一个 WebTransportBidirectionalStream
具有以下内部槽。
内部槽 | 描述 (非规范性) |
---|---|
[[Readable]]
| 一个 WebTransportReceiveStream 。
|
[[Writable]]
| 一个 WebTransportSendStream 。
|
[[Transport]]
| 拥有此 WebTransport
对象的 WebTransportBidirectionalStream 。
|
10.2. 属性
readable
, 类型为 WebTransportReceiveStream, 只读-
获取器步骤是返回 this 的
[[Readable]]
。 writable
, 类型为 WebTransportSendStream, 只读-
获取器步骤是返回 this 的
[[Writable]]
。
10.3. 过程
WebTransportBidirectionalStream
,并绑定一个 双向
WebTransport流 internalStream、一个 WebTransport
对象 transport 和一个 sendOrder,请执行以下步骤:
-
将 readable 设为通过 创建一个
WebTransportReceiveStream
,并绑定 internalStream 和 transport 的结果。 -
将 writable 设为通过 创建一个
WebTransportSendStream
,并绑定 internalStream、transport 和 sendOrder 的结果。 -
将 stream 设为一个新的
WebTransportBidirectionalStream
,绑定以下内容:[[Readable]]
-
readable
[[Writable]]
-
writable
[[Transport]]
-
transport
-
返回 stream.
11.
WebTransportWriter
接口
WebTransportWriter
是 WritableStreamDefaultWriter
的子类,添加了两个方法。
一个 WebTransportWriter
总是通过 创建
过程生成。
[Exposed=*,SecureContext ]interface :
WebTransportWriter WritableStreamDefaultWriter {Promise <undefined >atomicWrite (optional any );
chunk undefined commit (); };
11.1. 方法
atomicWrite(chunk)
-
atomicWrite
方法会拒绝无法在发送时当前的流量控制窗口内完整发送的 chunk。此行为旨在满足对流量控制死锁敏感的特定事务应用程序 ([RFC9308] 第4.4节)。注意:
atomicWrite
可能在发送部分数据后仍然拒绝。虽然它在流量控制方面提供了原子性,但其他错误可能仍会发生。atomicWrite
无法防止数据在多个数据包之间拆分或与其他数据交错。只有发送者才知道如果atomicWrite
因缺乏可用流量控制信用而失败。注意: 原子写操作在排队到非原子写操作之后仍可能阻塞。如果原子写操作被拒绝,则此刻排队在其后的所有内容都将被拒绝。任何以这种方式被拒绝的非原子写操作将错误流。因此,建议应用程序始终等待原子写操作完成。
当调用
atomicWrite
时,用户代理必须执行以下步骤:-
让 p 为在
write(chunk)
上调用WritableStreamDefaultWriter
的结果,并传递 chunk。 -
附加 p 到 stream.
[[AtomicWriteRequests]]
。 -
返回对 p 的结果进行反应 (响应),并执行以下步骤:
-
如果 stream.
[[AtomicWriteRequests]]
包含 p, 移除 p。 -
如果 p 因原因 r 被拒绝,则返回 一个被拒绝的 promise,理由为 r。
-
返回 undefined。
-
-
commit()
-
commit
方法会更新流的[[CommittedOffset]]
,以匹配写入该流的字节数 ([[BytesWritten]]
)。 这确保这些字节将在写入操作被中止后可靠地传递给对端,导致流被重置。 此机制使用[RELIABLE-RESET]中描述的机制。注意: 这并不能保证在连接失败时进行传递,仅在流重置时保证传递。
当为 stream 调用
commit
时,用户代理必须执行以下步骤:-
将 transport 设为 stream.
[[Transport]]
。 -
将 stream.
[[CommittedOffset]]
设置为 stream.[[BytesWritten]]
的值。
-
11.2. 过程
要 创建一个
WebTransportWriter
,并绑定一个 WebTransportSendStream
stream,请执行以下步骤:
-
将 writer 设为一个新的
WebTransportWriter
。 -
运行 new WritableStreamDefaultWriter(stream) 构造步骤,传递 writer 作为 this,传递 stream 作为构造函数参数。
-
返回 writer。
12.
WebTransportError
接口
WebTransportError
是 DOMException
的子类,表示:
-
来自服务器或网络的错误,或
-
客户端发起的中止操作的原因。
[Exposed =(Window ,Worker ),Serializable ,SecureContext ]interface WebTransportError :DOMException {constructor (optional DOMString = "",
message optional WebTransportErrorOptions = {});
options readonly attribute WebTransportErrorSource source ;readonly attribute unsigned long ?streamErrorCode ; };dictionary {
WebTransportErrorOptions WebTransportErrorSource = "stream"; [
source Clamp ]unsigned long ?=
streamErrorCode null ; };enum {
WebTransportErrorSource ,
"stream" , };
"session"
12.1. 内部插槽
一个 WebTransportError
具有以下内部槽。
内部槽 | 描述 (非规范性) |
---|---|
[[Source]]
| 一个 WebTransportErrorSource
,指示此错误的来源。
|
[[StreamErrorCode]]
| 此错误的应用协议错误代码,或 null。 |
12.2. 构造函数
new WebTransportError(message, options)
构造步骤如下:
-
将 this 的 name 设置为
"WebTransportError"
。 -
将 this 的 message 设置为 message。
-
将 this 的内部槽设置如下:
[[Source]]
-
options.
source
[[StreamErrorCode]]
-
options.
streamErrorCode
12.3. 属性
source
, 类型为 WebTransportErrorSource, 只读-
获取器步骤是返回 this 的
[[Source]]
。 streamErrorCode
, 类型为 unsigned long, 只读,nullable-
获取器步骤是返回 this 的
[[StreamErrorCode]]
。
12.4. 序列化
WebTransportError
对象是 可序列化对象。
它们的 序列化步骤,给定 value 和 serialized,如下:
-
运行
DOMException
的 序列化步骤,给定 value 和 serialized。 -
将 serialized.
[[Source]]
设置为 value.[[Source]]
。 -
将 serialized.
[[StreamErrorCode]]
设置为 value.[[StreamErrorCode]]
。
它们的 反序列化步骤,给定 serialized 和 value,如下:
-
运行
DOMException
的 反序列化步骤,给定 serialized 和 value。 -
将 value.
[[Source]]
设置为 serialized.[[Source]]
。 -
将 value.
[[StreamErrorCode]]
设置为 serialized.[[StreamErrorCode]]
。
13. 协议映射
此部分为非规范性内容。
本节描述了本规范中定义的方法的底层协议行为,使用 [WEB-TRANSPORT-OVERVIEW]。 由于缓冲,因果关系可能不会立即显现。
WebTransport 协议行为 | API 效果 |
---|---|
会话 耗尽 | 等待 wt.draining
|
如果 底层连接 使用 HTTP/3,则适用于从 [WEB-TRANSPORT-HTTP3] 的以下协议行为。
在 WebTransportError
错误中的应用 streamErrorCode
转换为 httpErrorCode,反之亦然,如 [WEB-TRANSPORT-HTTP3]
第4.3节 中所述。
API 方法 | QUIC 协议行为 |
---|---|
writable .abort (error)
| 发送
RESET_STREAM_AT
带有 httpErrorCode 和与 [[CommittedOffset]]
对应的偏移量,加上任何流头;参见 [RELIABLE-RESET]
|
writable .close ()
| 发送 STREAM 并设置 FIN 位 |
writable .getWriter().write(chunk) ()
| 发送 STREAM |
readable .cancel (error)
| 发送 STOP_SENDING 带有 httpErrorCode |
注意:如 第3.2节 中所述 [QUIC], 接收 RESET_STREAM 帧或 RESET_STREAM_AT 帧 ([RELIABLE-RESET]) 不一定会向应用程序发出指示。接收重置可能会立即发出信号,打断流数据的传递,未使用的数据将被丢弃。然而,立即发出信号并非必需。
HTTP/3 协议行为 | API 效果 |
---|---|
会话 耗尽 | 等待 wt.draining
|
如果 底层连接 使用 HTTP/2,则适用于从 [WEB-TRANSPORT-HTTP2] 的以下协议行为。需要注意的是,与 HTTP/3 不同,流错误代码不需要转换为 HTTP 错误代码,反之亦然。
API 方法 | HTTP/2 协议行为 |
---|---|
writable .abort (error)
| 发送 WT_RESET_STREAM 带有错误 |
writable .close ()
| 发送 WT_STREAM 并设置 FIN 位 |
writable .getWriter().write ()
| 发送 WT_STREAM |
writable .getWriter().close ()
| 发送 WT_STREAM 并设置 FIN 位 |
writable .getWriter().abort (error)
| 发送 WT_RESET_STREAM 带有错误 |
readable .cancel (error)
| 发送 WT_STOP_SENDING 带有错误 |
readable .getReader().cancel (error)
| 发送 WT_STOP_SENDING 带有错误 |
wt.close (closeInfo)
| 终止会话并带有 closeInfo |
HTTP/2 协议行为 | API 效果 |
---|---|
接收 WT_STOP_SENDING 带有错误 | 错误 writable
并带有 streamErrorCode
|
接收 WT_STREAM | (等待
readable .getReader().read ()).value
|
接收 WT_STREAM 并设置 FIN 位 | (等待
readable .getReader().read ()).done
|
接收 WT_RESET_STREAM 带有错误 | 错误 readable
并带有 streamErrorCode
|
会话干净地 终止 并带有 closeInfo | (等待 wt.closed ).closeInfo,
并
错误 打开流
|
网络错误 | (等待 wt.closed )
拒绝,并且
错误 打开流
|
会话 耗尽 | 等待 wt.draining
|
14. 隐私与安全注意事项
此部分为非规范性内容;它未指定任何新的行为,而是总结了规范其他部分中已经存在的信息。
14.1. 通信机密性
通信正在进行的事实无法对能够观察网络的对手隐瞒,因此这必须被视为公开信息。
本文件中描述的所有传输协议都使用 TLS [RFC8446] 或语义上等效的协议,从而提供了 TLS 的所有安全属性,包括流量的机密性和完整性。基于 HTTP 的 WebTransport 使用与出站 HTTP 请求相同的证书验证机制,因此依赖于相同的公钥基础设施来验证远程服务器的身份。在 WebTransport 中,证书验证错误是致命的;没有允许绕过证书验证的中间页面。
14.2. 状态持久性
WebTransport 本身不会创建任何新的唯一标识符或持久存储状态的新方法,也不会自动向服务器公开任何现有的持久状态。例如,[WEB-TRANSPORT-HTTP3] 和[WEB-TRANSPORT-HTTP2] 都不发送 Cookies,也不支持 HTTP 身份验证或缓存失效机制。由于它们确实使用 TLS,它们继承了 TLS 的持久状态,例如 TLS 会话票据,这虽然对被动网络观察者不可见,但可能会被服务器用于关联来自同一客户端的不同连接。
14.3. 协议安全性
WebTransport 强加了一组要求,如 [WEB-TRANSPORT-OVERVIEW] 中所述,包括:
-
确保远程服务器知道正在使用 WebTransport 协议,并确认远程服务器愿意使用 WebTransport 协议。[WEB-TRANSPORT-HTTP3] 使用 ALPN [RFC7301]、 HTTP/3 设置和
:protocol
伪标头的组合来标识 WebTransport 协议。[WEB-TRANSPORT-HTTP2] 使用 ALPN、 HTTP/2 设置和:protocol
伪标头的组合来标识 WebTransport 协议。 -
允许服务器根据资源的来源过滤连接,该资源发起了传输会话。会话建立请求上的
Origin
标头字段携带此信息。
有关协议安全的注意事项在 [WEB-TRANSPORT-HTTP3] 和 [WEB-TRANSPORT-HTTP2] 的“安全注意事项”部分中进行了描述。
网络 API 通常可用于扫描本地网络以查找可用主机,因此可用于指纹识别和其他形式的攻击。WebTransport 遵循 WebSocket 方法 解决此问题: 直到端点被验证为 WebTransport 端点之前,具体的连接错误不会返回;因此,Web 应用程序无法区分不存在的端点和不愿意接受 Web 连接的端点。
14.4. 使用证书哈希进行身份验证
通常,用户代理通过验证所提供的 TLS 服务器证书的有效性与 URL 中的服务器名称 [RFC9525] 相比来对其自身与远程端点之间的 TLS 连接进行身份验证。这是通过将服务器证书链接到用户代理维护的信任锚之一来实现的;相关的信任锚负责对证书中的服务器名称进行身份验证。我们将此系统称为 Web PKI。
此 API 为 Web 应用程序提供了一种连接到远程网络端点的能力,该端点通过指定的服务器证书而不是其服务器名称进行身份验证。此机制使连接到难以获取长期证书的端点成为可能,包括那些本质上是短暂的主机(例如短期虚拟机),或不可公开路由的主机。由于此机制替代了基于 Web PKI 的单个连接身份验证,我们需要比较两者的安全属性。
只有在远程服务器拥有与指定证书的公钥相对应的私钥时,它才能成功执行 TLS 握手。API 使用其哈希值来标识证书。只要所使用的加密哈希函数具有第二原像抗性,这种方法才是安全的。本文档中唯一定义的函数是 SHA-256;API 提供了一种通过允许指定多个算法-哈希对来引入新哈希函数的方法。
需要注意的是,Web PKI 除了简单地为服务器名称建立信任链之外,还提供了额外的安全机制。其中之一是处理证书吊销。在使用的证书是短暂的情况下,这种机制没有必要。在其他情况下,Web 应用程序必须考虑证书哈希值的供应机制;例如,如果哈希值作为缓存的 HTTP 资源提供,则在相应证书因泄露而被轮换时需要使缓存失效。Web PKI 提供的另一个安全功能是针对密钥生成的某些问题的保障,例如拒绝具有已知弱密钥的证书;虽然本文档没有提供任何具体的指导,但浏览器可以作为实现定义的行为的一部分拒绝这些证书。
Web PKI 对证书实施了过期周期要求。此要求限制了潜在密钥泄露的范围;它还迫使服务器运营商设计支持并积极执行密钥轮换的系统。出于这个原因,WebTransport 对证书施加了类似的过期要求;由于证书预计是短暂或短期的,因此过期周期限制为两周。两周的限制是在尽可能低地设置过期限制以最大限度地减少密钥泄露的后果之间,以及保持其足够高以适应设备之间的时钟偏差,并降低客户端和服务器端同步证书的成本之间的平衡。
WebTransport API 允许应用程序一次指定多个证书哈希值,从而允许客户端在新证书推出期间接受多个证书。
与 WebRTC 中类似的机制不同,WebTransport 中的服务器证书哈希 API 不提供任何身份验证客户端的方法;客户端知道服务器证书是什么或如何联系它这一事实并不足够。应用程序如果需要,必须在带内建立客户端的身份。
14.5. 指纹识别与跟踪
此 API 使站点能够生成网络活动并密切观察此活动的效果。通过这种方式获取的信息可能是识别性的信息。
需要认识到其他 Web 平台 API(例如 fetch 和 [webrtc])提供了非常类似的网络功能。因此,添加 WebTransport 对隐私的不利影响微乎其微。本节中的注意事项同样适用于其他网络功能。
测量网络特性需要使用网络并测量该使用的效果,而这两者都由此 API 启用。WebTransport 为站点提供了向其选择的服务器生成网络活动并观察效果的能力。可以观察网络路径的稳定属性和网络使用的动态效果。
有关网络的信息可以通过服务器自己的网络堆栈直接获得,通过客户端消耗或传输数据的速率间接获得,或者作为由 API 提供的统计信息的一部分获得(参见 § 6.13 WebTransportConnectionStats Dictionary)。因此,对用户代理信息的限制并不是管理这些隐私风险的唯一机制。
14.5.1. 静态观察
站点可以观察用户代理与选定服务器之间的可用网络容量或往返时间(RTT)。当与其他跟踪矢量结合时,这些信息可能具有识别性。RTT 还可以揭示用户代理的物理位置,尤其是在可以从多个观察点进行多次测量的情况下。
尽管网络是共享的,但网络使用通常是零散的,这意味着站点通常可以观察未受争用或负载轻的网络路径的容量和往返时间。这些属性对于许多人来说是稳定的,因为他们的网络位置没有改变,网络瓶颈的位置--决定可用容量--可能靠近用户代理。
14.5.2. 共享网络
受争用的链接为站点提供了启用跨站点识别的机会,这可能会被用来执行未经授权的跟踪 [UNSANCTIONED-TRACKING]。 网络容量是有限的共享资源,因此用户代理同时访问不同站点可能会揭示每个站点呈现的身份之间的连接。
在一个站点上使用网络功能会减少其他站点可用的容量,可以使用网络 API 观察到这一点。网络使用和指标可能会动态变化,因此任何变化都可以实时观察到。这可能允许站点增加信心,即不同站点上的活动来自同一用户。
用户代理可以限制或降低对反馈机制(例如统计信息(§ 6.13 WebTransportConnectionStats Dictionary))的访问,针对不活跃或未获得焦点的站点(HTML § 6.6 Focus)。如所述,这并不能阻止服务器观察网络中的变化。
14.5.3. 池化会话
与共享网络场景类似,当会话在单个连接上池化时,一个会话的信息会受到另一个会话活动的影响。一个会话可以推断关于另一个会话活动的信息,例如另一个应用程序发送数据的速率。
使用共享连接已经允许服务器关联会话。使用 网络分区键 禁用池化,其中共享会话的使用可能启用不必要的跨站点识别。
15. 示例
15.1. 发送数据报缓冲区
此部分为非规范性内容。
可以通过使用
datagrams
'
createWritable
方法以及生成的流的 writer 来发送数据报缓冲区。在以下示例中,仅当传输准备好发送时才发送数据报。
async function sendDatagrams( url, datagrams) { const wt= new WebTransport( url); const writable= wt. datagrams. createWritable(); const writer= writable. getWriter(); for ( const bytesof datagrams) { await writer. ready; writer. write( bytes). catch (() => {}); } await writer. close(); }
15.2. 以固定速率发送数据报
此部分为非规范性内容。
无论传输是否准备好发送,通过简单使用
datagrams
'
createWritable
方法以及生成的流的 writer,而不等待 ready
属性即可以固定速率发送数据报。
// 每100毫秒发送数据报。 async function sendFixedRate( url, createDatagram, ms= 100 ) { const wt= new WebTransport( url); const writable= wt. datagrams. createWritable(); const writer= writable. getWriter(); const bytes= createDatagram(); setInterval(() => writer. write( bytes). catch (() => {}), ms); }
15.3. 接收数据报
此部分为非规范性内容。
可以通过从传输的
datagrams
.readable
属性中读取来接收数据报。空值可能表示未能快速处理数据包。
async function receiveDatagrams( url) { const wt= new WebTransport( url); for await ( const datagramof wt. datagrams. readable) { // 处理数据报 } }
15.4. 使用 BYOB 读取器接收数据报
此部分为非规范性内容。
由于 datagrams
是 可读字节流,您可以为其获取一个 BYOB
读取器,这允许更精确地控制缓冲区分配,以避免复制。此示例将数据报读取到一个 64kB 的内存缓冲区中。
const wt= new WebTransport( url); for await ( const datagramof wt. datagrams. readable) { const reader= datagram. getReader({ mode: "byob" }); let array_buffer= new ArrayBuffer( 65536 ); const buffer= await readInto( array_buffer); } async function readInto( buffer) { let offset= 0 ; while ( offset< buffer. byteLength) { const { value: view, done} = await reader. read( new Uint8Array( buffer, offset, buffer. byteLength- offset)); buffer= view. buffer; if ( done) { break ; } offset+= view. byteLength; } return buffer; }
15.5. 发送流
此部分为非规范性内容。
可以通过使用
createUnidirectionalStream
函数以及生成的流的 writer 来发送数据作为单向流。
接收时不会保留写入的块边界,因为字节可能会在传输线上合并。因此建议应用程序提供自己的框架。
async function sendData( url, ... data) { const wt= new WebTransport( url); const writable= await wt. createUnidirectionalStream(); const writer= writable. getWriter(); for ( const bytesof data) { await writer. ready; writer. write( bytes). catch (() => {}); } await writer. close(); }
流规范 不建议 等待 write() 的 Promise。
编码也可以通过管道从一个 ReadableStream
进行,例如使用 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); }
15.6. 接收传入流
此部分为非规范性内容。
可以通过遍历
incomingUnidirectionalStreams
属性,然后通过遍历其块来消费每个 WebTransportReceiveStream
来读取传入流。
分块由用户代理决定,而不是发送方。
async function receiveData( url, processTheData) { const wt= new WebTransport( url); for await ( const readableof wt. incomingUnidirectionalStreams) { // 使用 IFFE 单独消费流,报告每个流的错误 (( async () => { try { for await ( const bytesof readable) { processTheData( bytes); } } catch ( e) { console. error( e); } })()); } }
解码也可以通过管道传输到新的 WritableStreams,例如使用
TextDecoderStream
。
此示例假定文本输出不应交错,因此一次只读取一个流。
async function receiveText( url, createWritableStreamForTextData) { const wt= new WebTransport( url); for await ( const readableof wt. incomingUnidirectionalStreams) { // 顺序消费以避免输出交错,报告每个流的错误 try { await readable. pipeThrough( new TextDecoderStream( "utf-8" )) . pipeTo( createWritableStreamForTextData()); } catch ( e) { console. error( e); } } }
15.7. 使用 BYOB 读取器接收流
此部分为非规范性内容。
由于 WebTransportReceiveStream
是 可读字节流,您可以为其获取一个
BYOB
读取器,这允许更精确地控制缓冲区分配,以避免复制。此示例从
WebTransportReceiveStream
中将前 1024 字节读入单个内存缓冲区。
const wt= new WebTransport( url); const reader= wt. incomingUnidirectionalStreams. getReader(); const { value: recv_stream, done} = await reader. read(); const byob_reader= recv_stream. getReader({ mode: "byob" }); let array_buffer= new ArrayBuffer( 1024 ); const buffer= await readInto( array_buffer); async function readInto( buffer) { let offset= 0 ; while ( offset< buffer. byteLength) { const { value: view, done} = await reader. read( new Uint8Array( buffer, offset, buffer. byteLength- offset)); buffer= view. buffer; if ( done) { break ; } offset+= view. byteLength; } return buffer; }
15.8. 在流上发送事务性块
此部分为非规范性内容。
在单向流上发送事务性数据,仅当可以完全不阻塞 流控制 时,可以通过使用
getWriter
函数及其生成的 writer 来实现。
async function sendTransactionalData( wt, bytes) { const writable= await wt. createUnidirectionalStream(); const writer= writable. getWriter(); await writer. ready; try { await writer. atomicWrite( bytes); } catch ( e) { if ( e. name!= "AbortError" ) throw e; // 因避免阻塞流控制而被拒绝 // 只要没有待处理的非原子写入,可写流就保持无错误状态 } finally { writer. releaseLock(); } }
15.9. 使用服务器证书哈希
此部分为非规范性内容。
WebTransport 会话可以使用对提供给服务器的证书哈希的检查来覆盖客户端执行的默认信任评估。在下面的示例中,hashValue
是一个 BufferSource
,
包含 底层连接
应视为有效的服务器证书的 SHA-256 哈希。
const wt= new WebTransport( url, { serverCertificateHashes: [ { algorithm: "sha-256" , value: hashValue, } ] }); await wt. ready;
15.10. 完整示例
此部分为非规范性内容。
此示例说明了 closed 和 ready promise 的使用、客户端或服务器打开单向和双向流,以及发送和接收数据报。
曾经存在于传输的 datagrams
上的 writable
属性很容易通过如下方式进行 polyfill:
wt.datagrams.writable ||= wt.datagrams.createWritable();
// 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); wt. datagrams. writable||= wt. datagrams. createWritable(); addToEventLog( 'Initiating connection...' ); await wt. ready; addToEventLog( ` ${ ( 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 data= sending. data. value; const bytes= new TextEncoder( 'utf-8' ). encode( data); try { switch ( form. sendtype. value) { case 'datagram' : { await datagramWriter. ready; datagramWriter. write( bytes). catch (() => {}); addToEventLog( `Sent datagram: ${ data} ` ); break ; } case 'unidi' : { const writable= await wt. createUnidirectionalStream(); const writer= writable. getWriter(); writer. write( bytes). catch (() => {}); await writer. close(); addToEventLog( `Sent a unidirectional stream with data: ${ data} ` ); break ; } case 'bidi' : { const duplexStream= await wt. createBidirectionalStream(); const n= streamNumber++ ; readFromIncomingStream( duplexStream. readable, n); const writer= duplexStream. writable. getWriter(); writer. write( bytes). catch (() => {}); await writer. close(); addToEventLog( `Sent bidirectional stream # ${ n} with data: ${ data} ` ); 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 dataof readable. pipeThrough( decoder)) { addToEventLog( `Received data on stream # ${ number} : ${ data} ` ); } 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(); } }
16. 致谢
编辑们感谢工作组主席和团队联系人 Jan-Ivar Bruaroey、Will Law 和 Yves Lafon 的支持。
WebTransport
接口基于最初在 W3C ORTC CG 中描述的 QuicTransport
接口,并已为在本规范中使用而进行了调整。