1. 简介
本节为非规范性内容。
本规范使用 [WEB-TRANSPORT-OVERVIEW] 向服务器发送和接收数据。它的用法类似于 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 会话具有以下能力:
WebTransport 会话 session 处于排空状态时,表示CONNECT 流 已由服务器请求优雅关闭,如 [WEB-TRANSPORT-OVERVIEW] 第4.1节所述。
要使用可选的整数code和可选的字节序列reason来终止 WebTransport 会话 session,请遵循 [WEB-TRANSPORT-OVERVIEW] 第4.1节。
当CONNECT 流被服务器关闭时,WebTransport 会话 session被终止(可选择 一个整数code和字节序列reason) 如 [WEB-TRANSPORT-OVERVIEW] 第4.1节所述。
3.2. WebTransport 流
WebTransport 流是用于 WebTransport 会话 上可靠有序字节流的一个概念,详见 [WEB-TRANSPORT-OVERVIEW] 第4.3节。
WebTransport 流分为入站单向、 出站单向或双向。
这包括原子写入,
原子写入的目的在于避免流控死锁,
而不是保证数据同时被传递。
依赖基于消息协议,且需要在发送端与接收端之间 保持消息边界的应用,需要添加帧封装层来 便于重组成消息。
一个WebTransport 流具有以下能力:
| 能力 | 定义 | 入站单向 | 出站单向 | 双向 |
|---|---|---|---|---|
| 发送 字节(可能带 FIN) | [WEB-TRANSPORT-OVERVIEW] 第 4.3 节 | 否 | 是 | 是 |
| 接收 字节(可能带 FIN) | [WEB-TRANSPORT-OVERVIEW] 第 4.3 节 | 是 | 否 | 是 |
| 中止接收 在WebTransport 流上 | [WEB-TRANSPORT-OVERVIEW] 第 4.3 节 | 是 | 否 | 是 |
| 中止发送 在WebTransport 流上 | [WEB-TRANSPORT-OVERVIEW] 第 4.3 节 | 否 | 是 | 是 |
一个WebTransport 流具有以下信号:
| 事件 | 定义 | 入站单向 | 出站单向 | 双向 |
|---|---|---|---|---|
| 接收已中止 | [WEB-TRANSPORT-OVERVIEW] 第 4.3 节 | 否 | 是 | 是 |
| 发送已中止 | [WEB-TRANSPORT-OVERVIEW] 第 4.3 节 | 是 | 否 | 是 |
| 流控 | [WEB-TRANSPORT-OVERVIEW] 第 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]]
| WebTransport
拥有的这个 WebTransportDatagramsWritable。
|
[[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.[[OutgoingMaxBufferedDatagrams]],则解析 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,
并且只要算法能取得进展,应尽快这么做。
注:
当传输的 [[State]]
为 "connecting" 时允许写入数据报。
这些数据报会被存储在 [[OutgoingDatagramsQueue]],
并且可以和 "connected" 状态下一样被丢弃。
一旦传输的 [[State]]
变为 "connected",就会开始发送队列中的数据报。
发送顺序规则
指的是,整体发送操作可以与之前排队的流和数据报的发送交错进行,
也可以和还未排队但即将通过本传输发送的流和数据报交错进行,
但前提是,只有当所有具有同一
[[SendGroup]]
且更高
[[SendOrder]]
,并且既未出错,也未被流控阻塞的字节全部发送后,
当前发送操作才能继续。
注: 默认的 null sendGroup 也是有效的 sendGroup。
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 unsigned long incomingMaxBufferedDatagrams ;attribute unsigned long outgoingMaxBufferedDatagrams ; };
5.1. 内部插槽
| 内部槽 | 描述(非规范性) |
|---|---|
[[Transport]]
| 拥有此 WebTransport
的 WebTransportDatagramDuplexStream。
|
[[Readable]]
| 用于接收数据报的 ReadableStream。
|
[[ReadableType]]
| 用于接收数据报的 ReadableStreamType。
|
[[Writables]]
| 一个有序集合的 WebTransportDatagramsWritable
流,初始为空。
|
[[IncomingDatagramsQueue]]
| 一个队列,包含每个接收数据报和其时间戳的对。 |
[[IncomingDatagramsPullPromise]]
| 通过 pullDatagrams 设置的一个 promise,用于等待接收数据报。 |
[[IncomingMaxBufferedDatagrams]]
| 一个 unsigned long,
用于表示接收队列的长度(以数据报数量计),超出后队首的数据报会被丢弃。
|
[[IncomingDatagramsExpirationDuration]]
| 一个 unrestricted double,
表示接收数据报的过期时长(毫秒),或为 null。
|
[[OutgoingMaxBufferedDatagrams]]
| 一个 unsigned long,
表示底层 sink 的发送队列长度(以数据报数量计),超出后将产生背压。
|
[[OutgoingDatagramsExpirationDuration]]
| 一个 unrestricted double
,表示发送数据报的过期时长(毫秒),或为 null。
|
[[OutgoingMaxDatagramSize]]
|
一个整数,表示单个发送数据报的最大尺寸。
最大数据报大小取决于所用协议。
在 HTTP/3 [WEB-TRANSPORT-HTTP3] 中,该值与路径 MTU 的估算有关,
并会因实现定义的开销而减小。
在 HTTP/2 [WEB-TRANSPORT-HTTP2] 中没有类似限制。
由于处理数据报时通常需要在内存中暂存整个数据报, 实现会有限制其大小的机制。 将来协议扩展可能支持全部协议变体下大小限制的信号。 |
用户代理可以随时更新任何 [[OutgoingMaxDatagramSize]]
,只要对应的 WebTransport
对象其
[[State]]
为 "connecting" 或 "connected"。
WebTransportDatagramDuplexStream
,给定 WebTransport
transport、readable 和 readableType,执行以下步骤:
-
令 stream 是一个 新建
WebTransportDatagramDuplexStream,其属性:[[Transport]]-
transport
[[Readable]]-
readable
[[ReadableType]]-
readableType
[[Writables]]-
一个空的 有序集合。
[[IncomingDatagramsQueue]]-
一个空队列
[[IncomingDatagramsPullPromise]]-
null
[[IncomingMaxBufferedDatagrams]]-
一个由实现定义的值
[[IncomingDatagramsExpirationDuration]]-
null
[[OutgoingMaxBufferedDatagrams]]-
一个由实现定义的值
此实现定义的值应调优以保证传输吞吐量合理,同时不影响传输数据的时效性。
[[OutgoingDatagramsExpirationDuration]]-
null
[[OutgoingMaxDatagramSize]]-
一个由实现定义的整数。
-
返回 stream。
5.2. 方法
createWritable()-
创建一个
WebTransportDatagramsWritable。当调用createWritable()方法时,用户代理必须执行以下步骤:-
令 transport 为与 this 关联的
WebTransport对象。 -
如果 transport.
[[State]]为"closed"或"failed", 抛出一个InvalidStateError。 -
如果 sendGroup 不为 null 且 sendGroup.
[[Transport]]不等于 this.[[Transport]], 抛出 一个InvalidStateError。 -
返回创建一个
WebTransportDatagramsWritable的结果,参数为 transport、sendGroup 和 sendOrder。
-
5.3. 属性
readable, 类型为 ReadableStream,只读-
getter 步骤如下:
-
返回 this.
[[Readable]]。
-
incomingMaxAge, 类型为 unrestricted double,可为 null-
getter 步骤如下:
setter 步骤,给定 value:
-
如果 value 为负数或 NaN,抛出 一个
RangeError。 -
如果 value 为
0,将 value 设为 null。 -
将 this.
[[IncomingDatagramsExpirationDuration]]设为 value。
maxDatagramSize, 类型为 unsigned long,只读-
可传递至
WebTransportDatagramsWritable的数据的最大字节数。 getter 步骤是返回 this.[[OutgoingMaxDatagramSize]]。 outgoingMaxAge, 类型为 unrestricted double,可为 null-
getter 步骤如下:
setter 步骤,给定 value:
-
如果 value 为负数或 NaN,抛出 一个
RangeError。 -
如果 value 为
0,将 value 设为 null。 -
将 this.
[[OutgoingDatagramsExpirationDuration]]设为 value。
incomingMaxBufferedDatagrams, 类型为 unsigned long-
getter 步骤如下:
setter 步骤,给定 value:
-
如果 value 小于
1,将 value 设为1。 -
将 this.
[[IncomingMaxBufferedDatagrams]]设为 value。
outgoingMaxBufferedDatagrams, 类型为 unsigned long-
getter 步骤如下:
setter 步骤,给定 value:
-
如果 value 小于
1,将 value 设为1。 -
将 this.
[[OutgoingMaxBufferedDatagrams]]设为 value。
5.4. 过程
要拉取数据报,给定
WebTransport
对象 transport,执行以下步骤:
-
令 datagrams 为 transport.
[[Datagrams]]。 -
断言:datagrams.
[[IncomingDatagramsPullPromise]]为 null。 -
令 queue 为 datagrams.
[[IncomingDatagramsQueue]]。 -
如果 queue 为空,则:
-
将 datagrams.
[[IncomingDatagramsPullPromise]]设为新的 promise。 -
返回 datagrams.
[[IncomingDatagramsPullPromise]]。
-
-
令 datagram 和 timestamp 为 出队 queue 的结果。
-
如果 datagrams.
[[ReadableType]]为"bytes",则:-
如果 datagrams.
[[Readable]]的 当前 BYOB 请求 view 不为 null,则:-
令 view 为 datagrams.
[[Readable]]的 当前 BYOB 请求 view。 -
如果 view 的 字节长度 小于 datagram 的大小,则返回 一个被拒绝的 promise,原因是
RangeError。 -
令 elementSize 为 typed array 构造表 中 view.[[TypedArrayName]] 对应的元素字节数。 如果 view 没有 [[TypedArrayName]] 内部槽 (即是
DataView), 则 elementSize 设为 0。 -
如果 elementSize 不为 1,返回 一个被拒绝的 promise,原因是
TypeError。
-
-
从字节拉取 datagram 到 datagrams.
[[Readable]]。
-
-
否则:
-
令 chunk 为一个新的
Uint8Array对象,表示 datagram。 -
入队 chunk 到 transport.
[[Datagrams]].[[Readable]]。
-
-
返回 一个已解决的 promise,值为 undefined。
要 接收数据报,
给定 WebTransport
对象 transport,按以下步骤执行:
-
令 timestamp 为当前时间的时间戳。
-
令 queue 等于 datagrams.
[[IncomingDatagramsQueue]]。 -
令 duration 等于 datagrams.
[[IncomingDatagramsExpirationDuration]]。 -
如果 duration 为 null,则将 duration 设置为一个 由实现定义的值。
-
令 session 等于 transport.
[[Session]]。 -
只要 session 上有 可用传入数据报:
-
令 toBeRemoved 为 queue 的长度减去 datagrams.
[[IncomingMaxBufferedDatagrams]]。 -
只要 queue 非空:
-
如果 queue 非空且 datagrams.
[[IncomingDatagramsPullPromise]]非 null,则:-
令 bytes 和 timestamp 等于 出队 queue 的结果。
-
令 promise 等于 datagrams.
[[IncomingDatagramsPullPromise]]。 -
设置 datagrams.
[[IncomingDatagramsPullPromise]]为 null。 -
队列一个网络任务, 参数为 transport,任务内容如下:
-
令 chunk 为表示 bytes 的新
Uint8Array对象。 -
入队 chunk 到 datagrams.
[[Readable]]。 -
解决 promise,值为 undefined。
-
-
只要算法能取得进展,用户代理应尽快为任何 WebTransport
对象
(其 [[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 <Uint8Array >exportKeyingMaterial (BufferSource ,label optional BufferSource );context readonly attribute Promise <undefined >ready ;readonly attribute WebTransportReliabilityMode reliability ;readonly attribute WebTransportCongestionControl congestionControl ;attribute [EnforceRange ]unsigned short ?anticipatedConcurrentIncomingUnidirectionalStreams ;attribute [EnforceRange ]unsigned short ?anticipatedConcurrentIncomingBidirectionalStreams ; [SameObject ]readonly attribute Headers ?responseHeaders ;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]]
| 一个 有序集合,
由本 WebTransportSendStream
拥有。
|
[[ReceiveStreams]]
| 一个 有序集合,
由本 WebTransportReceiveStream
拥有。
|
[[IncomingBidirectionalStreams]]
| 一个 ReadableStream
,由 WebTransportBidirectionalStream
对象组成。
|
[[IncomingUnidirectionalStreams]]
| 一个 ReadableStream
,由 WebTransportReceiveStream
组成。
|
[[State]]
| 用于指示传输状态的枚举值,可能为 "connecting"、
"connected"、"draining"、"closed"、
"failed" 之一。
|
[[Ready]]
| 在相关 WebTransport 会话 建立 后 fulfill, 如果 建立过程失败则 reject。 |
[[Reliability]]
| 一个 WebTransportReliabilityMode
,指示首跳是否支持不可靠(UDP)传输或仅支持可靠(TCP)传输。连接建立前返回 "pending"。
|
[[CongestionControl]]
| 一个 WebTransportCongestionControl
,用于指示应用是否请求并被用户代理满足了优化吞吐量或低延迟的拥塞控制算法,或 "default"。
|
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]
| 应用预期服务器创建的并发打开的 入向单向流数量,或为 null。 |
[[AnticipatedConcurrentIncomingBidirectionalStreams]]
| 应用预期服务器创建的并发打开的 双向流数量,或为 null。 |
[[ResponseHeaders]]
| 一个 Headers
对象或 null。初始值为 null。
|
[[Protocol]]
| 一个字符串,指示服务器选择的应用层协议(如有)。初始值为空字符串。 |
[[Closed]]
| 在相关 WebTransport
对象
正常关闭时 fulfill,异常或初始化失败时 reject。
|
[[Draining]]
| 在相关 WebTransport 会话 开始 draining 时 fulfill。 |
[[Datagrams]]
| 一个 WebTransportDatagramDuplexStream。
|
[[Session]]
| 用于此 WebTransport
对象的
WebTransport 会话,或 null。
|
[[NewConnection]]
| "no" 或 "yes-and-dedicated"。
|
[[RequireUnreliable]]
| 一个布尔值,表示是否需要 UDP。 |
6.2. 构造函数
WebTransport()
构造函数时,用户代理必须运行下列步骤:
-
令 baseURL 为 this 的 相关设置对象的 API 基础 URL。
-
如果 url 为失败,抛出一个
SyntaxError异常。 -
如果 url 的 scheme 不是
https,抛出一个SyntaxError异常。 -
如果 url 的 fragment 不为 null,抛出一个
SyntaxError异常。 -
如果
options的allowPooling为 true,则 newConnection 为 "no";否则为 "yes-and-dedicated"。 -
令 serverCertificateHashes 为
options的serverCertificateHashes。 -
如果 newConnection 为 "
no" 且 serverCertificateHashes 非空,则 抛出NotSupportedError异常。 -
令 requireUnreliable 为
options的requireUnreliable。 -
令 congestionControl 为
options的congestionControl。 -
如果 congestionControl 不为
"default",且用户代理不支持任何针对 congestionControl 优化的拥塞控制算法(允许采用 [RFC9002] 第 7 节), 则将 congestionControl 置为"default"。 -
如果 protocols 中的任一值出现多次,或不符合 WebTransport 协议定义的协商应用协议项要求,或者其等同编码长度为 0 或超出 512,则抛出一个
SyntaxError异常。 [WEB-TRANSPORT-OVERVIEW] 第 3.1 节。 -
令 anticipatedConcurrentIncomingUnidirectionalStreams 为
options的anticipatedConcurrentIncomingUnidirectionalStreams。 -
令 anticipatedConcurrentIncomingBidirectionalStreams 为
options的anticipatedConcurrentIncomingBidirectionalStreams。 -
令 datagramsReadableType 为
options的datagramsReadableType。 -
令 incomingDatagrams 为一个新的
ReadableStream。 -
令 transport 为新构造的
WebTransport对象,具备如下属性:[[SendStreams]]-
空的有序集合
[[ReceiveStreams]]-
空的有序集合
[[IncomingBidirectionalStreams]][[IncomingUnidirectionalStreams]][[State]]-
"connecting" [[Ready]]-
新的 promise
[[Reliability]]-
"pending"
[[CongestionControl]]-
congestionControl
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]-
anticipatedConcurrentIncomingUnidirectionalStreams
[[AnticipatedConcurrentIncomingBidirectionalStreams]]-
anticipatedConcurrentIncomingBidirectionalStreams
[[ResponseHeaders]]-
null
[[Protocol]]-
空字符串
[[Closed]]-
新的 promise
[[Draining]]-
新的 promise
[[Datagrams]]-
undefined
[[Session]]-
null
[[NewConnection]]-
newConnection
[[RequireUnreliable]]-
requireUnreliable
-
将 transport.
[[Datagrams]]设为 创建WebTransportDatagramDuplexStream的结果, 参数为 transport、incomingDatagrams 和 datagramsReadableType。 -
令 pullDatagramsAlgorithm 为运行 pullDatagrams 并传入 transport 的动作。
注: 推荐和 datagrams 一起使用 64 KiB 缓冲区,因为 WebTransport 有效的最大 datagram 帧大小受 QUIC 最大 datagram 帧大小的上限限制, 推荐值为 64 KiB(见 [QUIC-DATAGRAM] 第 3 节)。 这将确保由于 datagram 超过缓冲区而导致的流错误不会发生。
-
如果 datagramsReadableType 为
"bytes",使用字节读取支持初始化 incomingDatagrams,pullAlgorithm 设为 pullDatagramsAlgorithm,highWaterMark 设为 0。否则,初始化 incomingDatagrams, pullAlgorithm 设为 pullDatagramsAlgorithm, highWaterMark 设为 0。 -
令 pullBidirectionalStreamAlgorithm 为运行 pullBidirectionalStream 并传入 transport 的动作。
-
初始化 transport.
[[IncomingBidirectionalStreams]], pullAlgorithm 设为 pullBidirectionalStreamAlgorithm, highWaterMark 设为 0。 -
令 pullUnidirectionalStreamAlgorithm 为运行 pullUnidirectionalStream 并传入 transport 的动作。
-
初始化 transport.
[[IncomingUnidirectionalStreams]], pullAlgorithm 设为 pullUnidirectionalStreamAlgorithm, highWaterMark 设为 0。 -
令 client 为 transport 的 相关设置对象。
-
令 origin 为 client 的 origin。
-
令 request 为新建的 请求(request),其 URL 为 url, client 为 client,service-workers 模式为"
none", referrer为"no-referrer",mode为"webtransport", credentials mode为"omit", cache mode为"no-store", policy container 为 client 的 策略容器, destination 为空字符串, origin为 origin, WebTransport-hash 列表为 serverCertificateHashes, redirect mode为 "error"。注: 不会跟随重定向。重定向导致的网络错误与其他网络错误无法区分。有意如此:在跨域上下文中,这可以避免暴露通常会被 CORS 阻止的信息;在同源上下文中,避免应用滥用握手作为信息通道。
-
将 request 的 method 设置为 "
CONNECT",并将该方法相关联的:protocol伪首部(pseudo-header) 设为"webtransport"。 -
令 requestHeaders 为新建的
Headers对象,其 header list 是 request 的 header list, guard 为 "request"。 -
对于 headers 的每个 header list 中的 header:
注: 头部会受 Fetch forbidden request-header 限制。
-
如果 protocols 非空,设置结构化字段值, 即在 request 的 header list 中, 设置 (
WT-Available-Protocols, 一个 结构化头部列表,其成员为 结构化头部字符串 项,按照 protocols 顺序)。 -
Fetch request,useParallelQueue 设为 true, processResponse 设为以下步骤,参数为 response:
-
处理 WebTransport fetch 响应,参数为 response 和 transport。
-
-
返回 transport。
-
令 transport 为与 request 关联的
WebTransport对象。 -
令 url 为 request 的 当前 URL。
-
令 newConnection 为 transport 的
[[NewConnection]]。 -
令 requireUnreliable 为 transport 的
[[RequireUnreliable]]。 -
令 webTransportHashes 为 request 的 WebTransport-hash 列表 中的值。
-
令 connection 为使用 networkPartitionKey、url、false、newConnection、 requireUnreliable 和 webTransportHashes 调用 obtaining a connection 的结果。
-
如果 connection 为失败,则返回失败。
-
等待 connection 接收第一个 SETTINGS 帧,令 settings 为表示该 SETTINGS 帧的字典。
-
如果 settings 不包含
SETTINGS_ENABLE_CONNECT_PROTOCOL(0x08,HTTP/2 参见 Section 3 的 [RFC8441];HTTP/3 参见 Section 3 的 [RFC9220])且其值不为 1,则返回失败。 -
如果 settings 未指示服务器支持 WebTransport,则返回失败。 [WEB-TRANSPORT-OVERVIEW] Section 4.1。
-
在 HTTP/3 上,支持需要
SETTINGS_WT_MAX_SESSIONS的值大于 0,且需要SETTINGS_H3_DATAGRAM的值为 1。[WEB-TRANSPORT-HTTP3] Section 3.1。 -
在 HTTP/2 上,潜在支持已由上文的
SETTINGS_ENABLE_CONNECT_PROTOCOL指示。[WEB-TRANSPORT-HTTP2] Section 3.1。
注:
SETTINGS_WT_MAX_SESSIONS在 IETF 中仍在变动,可能会改回SETTINGS_ENABLE_WEBTRANSPORT。 -
-
返回 connection。
WebTransport
transport,运行以下步骤:
-
如果 response 是 网络错误,则中止剩余步骤并对 transport 排列一个网络任务以运行下列步骤:
-
如果 transport 的
[[State]]为"closed"或"failed",则中止这些步骤。 -
令 error 为新创建的一个 WebTransportError,其
source为"session"。 -
对 transport 执行 Cleanup,参数为 error。
-
-
令 connection 为与 response 关联的底层连接。
-
遵循 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中的任何限制,在 connection 上 建立 一个 WebTransport 会话,使用服务器的 response, 并令 session 为所得的 WebTransport 会话。 由此产生的底层传输流称为该会话的 CONNECT 流。
注: 此步骤也完成了在 [QUIC-DATAGRAM] 中指定的传输参数交换。
-
如果前一步失败,则中止剩余步骤并对 transport 排列一个网络任务以运行下列步骤:
-
如果 transport 的
[[State]]为"closed"或"failed",则中止这些步骤。 -
令 error 为新创建的一个 WebTransportError,其
source为"session"。 -
对 transport 执行 Cleanup,参数为 error。
-
-
如果用户代理支持多个拥塞控制算法,则为此 connection 上的数据发送选择一个适合 transport 的
[[CongestionControl]]。 -
对 transport 排列一个网络任务以运行下列步骤:
-
断言:this 的
[[Datagrams]]的[[OutgoingMaxDatagramSize]]为整数。 -
如果 transport 的
[[State]]不是"connecting": -
将 transport 的
[[State]]设为"connected"。 -
将 transport 的
[[Session]]设为 session。 -
令 responseHeaders 为 response 的 header list 的副本。
-
从 responseHeaders 中 删除 "
wt-protocol"。 -
将 transport 的
[[ResponseHeaders]]设为一个新的Headers对象, 其 header list 为 responseHeaders,且 guard 为 "immutable"。 -
将 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。
-
并行运行下列步骤:
-
等待直到 session 中有一个 可用的入向双向流。
-
令 internalStream 为从 session 接收一个双向流 的结果。
-
对 transport 排列一个网络任务以运行下列步骤:
-
令 stream 为使用 internalStream 和 transport 创建的 WebTransportBidirectionalStream 对象的结果。
-
将 stream 入队到 transport 的
[[IncomingBidirectionalStreams]]。 -
Resolve p,值为 undefined。
-
-
-
返回 p。
WebTransport
对象 transport,运行以下步骤。
-
如果 transport 的
[[State]]为"connecting",则在 transport 的[[Ready]]被满足后返回执行下列步骤的结果:-
以 transport 为参数再次调用并返回 pullUnidirectionalStream 的结果。
-
-
如果 transport 的
[[State]]不是"connected",则返回一个新的被拒绝的 promise,原因为InvalidStateError。 -
令 session 为 transport 的
[[Session]]。 -
令 p 为一个新的 promise。
-
并行运行下列步骤:
-
等待直到 session 中有一个 可用的入向单向流。
-
令 internalStream 为从 session 接收一个入向单向流 的结果。
-
对 transport 排列一个网络任务以运行下列步骤:
-
令 stream 为使用 internalStream 和 transport 创建的 WebTransportReceiveStream 的结果。
-
将 stream 入队到 transport 的
[[IncomingUnidirectionalStreams]]。 -
Resolve p,值为 undefined。
-
-
-
返回 p。
6.3. 属性
ready,类型为 Promise<undefined>,只读closed,类型为 Promise<WebTransportCloseInfo>,只读-
读取时,必须返回 this 的
[[Closed]]。 draining,类型为 Promise<undefined>,只读-
读取时,必须返回 this 的
[[Draining]]。 datagrams,类型为 WebTransportDatagramDuplexStream, 只读-
用于在本会话中收发数据报的单个双工流。
datagrams属性的 getter 步骤如下:-
返回 this 的
[[Datagrams]]。
-
incomingBidirectionalStreams, 类型为 ReadableStream,只读-
返回由服务器接收的
ReadableStream,其元素为WebTransportBidirectionalStream。注:传入流是否已有数据,取决于服务器行为。
incomingBidirectionalStreams属性的 getter 步骤如下: incomingUnidirectionalStreams, 类型为 ReadableStream,只读-
一个单向流的
ReadableStream, 每个流由一个WebTransportReceiveStream表示, 它们都来自服务器。注:传入流是否已有数据,取决于服务器行为。
incomingUnidirectionalStreams的 getter 步骤如下: reliability,类型为 WebTransportReliabilityMode, 只读-
表示连接是否支持不可靠(基于 UDP)传输或只支持可靠(TCP 回退)传输。 在未建立连接前返回
"pending"。 getter 步骤为返回 this 的[[Reliability]]。 congestionControl, 类型为 WebTransportCongestionControl, 只读-
应用层的偏好,如果在构造函数中指定并被用户代理满足,则该连接发送数据时使用针对吞吐量或低延迟优化的拥塞控制算法。如果请求了偏好但未被满足,则值为
"default"。 getter 步骤为返回 this 的[[CongestionControl]]。 supportsReliableOnly, 类型为 boolean,只读-
当用户代理支持仅通过可靠 WebTransport 会话和 连接 时返回 true,否则为 false。
anticipatedConcurrentIncomingUnidirectionalStreams, 类型为 unsigned short,可为 null-
允许应用可选地声明其预期服务器将创建的并发 传入单向流的数量。 如果非 null,用户代理在与服务器协商时应尝试将
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]纳入考虑,从而减少未来往返次数。getter 步骤为返回 this 的
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]。setter 步骤,给定 value,是将 this 的
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]设置为 value。 anticipatedConcurrentIncomingBidirectionalStreams, 类型为 unsigned short,可为 null-
允许应用可选地声明其预期服务器将创建的并发 双向流的数量。 如果非 null,用户代理在与服务器协商时应尝试将
[[AnticipatedConcurrentIncomingBidirectionalStreams]]纳入考虑,从而减少未来的往返次数。getter 步骤为返回 this 的
[[AnticipatedConcurrentIncomingBidirectionalStreams]]。setter 步骤,给定 value,是将 this 的
[[AnticipatedConcurrentIncomingBidirectionalStreams]]设置为 value。
注:设置 anticipatedConcurrentIncomingUnidirectionalStreams
或
anticipatedConcurrentIncomingBidirectionalStreams
不能保证应用会收到预期数量的流。
responseHeaders, 类型为 Headers,只读,可为 null-
一旦建立了 WebTransport 会话, 保存由服务器设置的应用层响应头(如果有)。初始值为 null。 获取器步骤是返回 this 的
[[ResponseHeaders]]。 protocol, 类型为 DOMString,只读-
一旦建立了 WebTransport 会话 且构造函数选项中的
protocols提供了非空数组,则返回服务器选择的应用层协议(如果有)。否则返回空字符串。 获取器步骤是返回 this 的[[Protocol]]。
6.4. 方法
close(closeInfo)-
终止与该 WebTransport 会话(与此 WebTransport 对象关联)。
当调用 close 时,用户代理必须运行以下步骤:
-
令 transport 为 this.
-
如果 transport.
[[State]]为"closed"或"failed",则中止这些步骤。 -
如果 transport.
[[State]]为"connecting":-
令 error 为新创建的 WebTransportError,其
source为"session"。 -
对 transport 执行 Cleanup,参数为 error。
-
中止这些步骤。
-
-
令 session 为 transport.
[[Session]]。 -
令 code 为 closeInfo.
closeCode。 -
令 reasonString 为 closeInfo.
reason的最大代码单元前缀,使得该前缀经 UTF-8 编码 后的 长度 不超过 1024。 -
令 reason 为对 reasonString 进行 UTF-8 编码 的结果。
-
并行地,使用 code 和 reason 对 session 执行 terminate。
注: 这也会中止 WebTransport 流(包含在 transport.
[[SendStreams]]和[[ReceiveStreams]]中) 的发送或接收。 -
对 transport 执行 Cleanup,参数为
AbortError和 closeInfo。
-
getStats()-
收集该
WebTransport的 底层连接 的统计信息,并异步报告结果。当调用 getStats 时,用户代理必须运行以下步骤:
-
令 transport 为 this。
-
令 p 为一个新的 promise。
-
如果 transport.
[[State]]为"failed",则用InvalidStateError拒绝 p 并中止这些步骤。 -
并行地运行下列步骤:
-
如果 transport.
[[State]]为"connecting",则等待其改变。 -
如果 transport.
[[State]]为"failed",则在对 transport 排列一个网络任务以 拒绝 p(原因为InvalidStateError) 之后中止这些步骤。 -
如果 transport.
[[State]]为"closed",则在对 transport 排列一个网络任务以 解决 p(返回连接的最近可用统计信息)之后中止这些步骤。 何时收集这些统计信息为 实现定义。 -
令 gatheredStats 为特定于 底层连接 的统计信息的 列表, 用以准确填充
WebTransportConnectionStats和WebTransportDatagramStats的字典成员。 -
对 transport 排列一个网络任务以运行下列步骤:
-
令 stats 为新建的
WebTransportConnectionStats对象。 -
令 datagramStats 为新建的
WebTransportDatagramStats对象。 -
将 stats["
datagrams"] 设为 datagramStats。 -
对于 stats 与 datagramStats 中用户代理希望公开的每个字典成员 member,将该 member 设置为 gatheredStats 中对应的条目。
-
用 stats 解决 p(Resolve p)。
-
-
-
返回 p。
-
exportKeyingMaterial(BufferSource label, optional BufferSource context)-
为与此
WebTransport的 底层连接 唯一关联的 TLS 会话,导出来自 TLS Keying Material Exporter 的密钥材料。当调用
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-OVERVIEW] 第 4.1 节)获得的
Uint8Array, 参数为 labelLength, label, contextLength,以及如有则包括 context。
-
-
返回 p。
-
createBidirectionalStream()-
为一个外发的双向流创建一个
WebTransportBidirectionalStream对象。 注意,流的创建本身在未用于发送数据前并不会立即对对端可见。注: 服务器在未收到数据前并不会感知该流的存在,这是预期行为。
WhencreateBidirectionalStreamis called, the user agent MUST run the following steps:-
如果 this.
[[State]]为"closed"或"failed",则返回一个被拒绝的 promise(原因为InvalidStateError)。 -
如果 sendGroup 非 null,且 sendGroup.
[[Transport]]不是 this,则抛出一个InvalidStateError。 -
令 waitUntilAvailable 为
options的waitUntilAvailable。 -
令 p 为一个新的 promise。
-
令 transport 为 this。
-
并行地运行下列步骤,但当 transport 的
[[State]]变为"closed"或"failed"时中止,并改为对 transport 排列一个网络任务以 拒绝 p(原因为InvalidStateError):-
令 streamId 为一个对 transport.
[[Session]]有效且唯一的新流 ID,如 [QUIC] 第 19.11 节 所定义。若因耗尽而无法立即获得,则:若 waitUntilAvailable 为 true,则等待可用;若为 false, 则在对 transport 排列一个网络任务以 拒绝 p(原因为QuotaExceededError, 其 requested 与 quota 均为 null)之后中止这些步骤。 -
令 internalStream 为使用 transport.
[[Session]]和 streamId 创建双向流的结果(参见 session 创建双向流)。 -
对 transport 排列一个网络任务以运行下列步骤:
-
如果 transport.
[[State]]为"closed"或"failed",则以InvalidStateError拒绝 p 并中止这些步骤。 -
令 stream 为使用 internalStream、transport、 sendGroup 与 sendOrder 创建的
WebTransportBidirectionalStream。 -
用 stream 解决 p。
-
-
-
返回 p。
-
createUnidirectionalStream()-
为一个外发的单向流创建一个
WebTransportSendStream。 注意,流的创建本身在未用于发送数据前并不会立即对服务器可见。注: 在未发送数据之前,服务器不会感知该流的存在,这是预期行为。
WhencreateUnidirectionalStream()method is called, the user agent MUST run the following steps:-
如果 this.
[[State]]为"closed"或"failed",则返回一个被拒绝的 promise(原因为InvalidStateError)。 -
如果 sendGroup 非 null,且 sendGroup.
[[Transport]]不是 this,则抛出一个InvalidStateError。 -
令 waitUntilAvailable 为
options的waitUntilAvailable。 -
令 p 为一个新的 promise。
-
令 transport 为 this。
-
并行地运行下列步骤,但当 transport 的
[[State]]变为"closed"或"failed"时中止,并改为对 transport 排列一个网络任务以 拒绝 p(原因为InvalidStateError):-
令 streamId 为一个对 transport.
[[Session]]有效且唯一的新流 ID,如 [QUIC] 第 19.11 节 所定义。若因耗尽而无法立即获得,则:若 waitUntilAvailable 为 true,则等待可用;若为 false, 则在对 transport 排列一个网络任务以 拒绝 p(原因为QuotaExceededError)之后中止这些步骤。 -
令 internalStream 为使用 transport.
[[Session]]和 streamId 创建的外发单向流的结果(参见 session 创建外发单向流)。 -
对 transport 排列一个网络任务以运行下列步骤:
-
如果 transport.
[[State]]为"closed"或"failed",则以InvalidStateError拒绝 p 并中止这些步骤。 -
令 stream 为使用 internalStream、transport、 sendGroup 与 sendOrder 创建的
WebTransportSendStream。 -
用 stream 解决 p。
-
-
-
return p.
-
createSendGroup()-
创建一个
WebTransportSendGroup。当调用createSendGroup()方法时,用户代理必须运行以下步骤:-
如果 this.
[[State]]为"closed"或"failed",则抛出一个InvalidStateError。 -
返回对 创建 一个
WebTransportSendGroup的结果,参数为 this。
-
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,拒绝 stream.[[PendingOperation]],原因为 error。 -
使流出错 stream, 原因为 error。
-
-
对于 receiveStreams 中每一个 stream, 使 stream 出错, 原因为 error。
注:脚本作者可以注入代码在 Promise 解析时同步运行。因此,从此处开始,不要再触碰 transport,因为它可能会被脚本不可预测地更改。 调用此过程的逻辑同样适用。
-
如果给定 closeInfo,则:
-
否则:
-
拒绝 closed,原因为 error。
-
将 closed.
[[PromiseIsHandled]]设为 true。 -
拒绝 ready,原因为 error。
-
将 ready.
[[PromiseIsHandled]]设为 true。 -
使 incomingBidirectionalStreams 出错,原因为 error。
-
使 incomingUnidirectionalStreams 出错,原因为 error。
-
对于 outgoingDatagramWritables 中的每一个 writable,使其出错,原因为 error。
-
使 incomingDatagrams 出错,原因为 error。
-
要排队一个网络任务,给定 WebTransport
transport 和一系列步骤 steps,请按以下步骤执行:
6.6. 并非由客户端发起的会话终止
WebTransport
transport 关联的 WebTransport session
被 terminated 时,可选地带有
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 的 底层连接 发生连接错误时,
运行以下步骤:
-
为 transport 排队一个网络任务并运行以下步骤:
-
如果 transport.
[[State]]为"closed"或"failed",中止这些步骤。 -
令 error 为新创建的 WebTransportError,其
source为"session"。 -
清理 transport,传入 error。
-
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
session 且有待发送到网络的数据排队(包括 [[Datagrams]].[[OutgoingDatagramsQueue]]
中的数据报)时,禁止被垃圾回收。
如果 WebTransport
对象在 底层连接
仍然开启的情况下被垃圾回收,用户代理必须以 Application Error Code 为 0 且 Application Error Message 为 ""
终止
WebTransport session。
6.9. 配置
dictionary {WebTransportHash required DOMString ;algorithm required BufferSource ; };value dictionary WebTransportOptions {boolean allowPooling =false ;boolean requireUnreliable =false ;HeadersInit headers = {};sequence <WebTransportHash >serverCertificateHashes = [];WebTransportCongestionControl congestionControl = "default"; [EnforceRange ]unsigned short ?anticipatedConcurrentIncomingUnidirectionalStreams =null ; [EnforceRange ]unsigned short ?anticipatedConcurrentIncomingBidirectionalStreams =null ;sequence <DOMString >protocols = [];ReadableStreamType datagramsReadableType ; };enum {WebTransportCongestionControl ,"default" ,"throughput" , };"low-latency"
WebTransportOptions 是一个字典,包含用于控制 WebTransport 会话的建立与使用的参数。
allowPooling, 类型为 boolean,默认值为false-
若设为 true,则 WebTransport 会话允许池化。即其 底层连接 可与其他 WebTransport 会话共享。
requireUnreliable, 类型为 boolean,默认值为false-
若设为 true,则若无法建立 HTTP/3 连接,WebTransport 会话不能通过 HTTP/2 连接建立。
headers, 类型为 HeadersInit,默认值为{}-
可选项。可以是
Headers对象、对象字面量、或二维数组, 用于设置 请求的 header list,用于 建立 WebTransport 会话时。 serverCertificateHashes, 类型为 sequence<WebTransportHash>,默认值为[]-
仅支持专用连接的传输协议。 不支持该功能的传输层,若该字段非空,则必须抛出
NotSupportedError异常。若支持且非空,用户代理只在可成功 验证证书哈希与
serverCertificateHashes匹配且符合 自定义证书要求时认定证书可信。未知algorithm的哈希将被忽略。 若 为空,用户代理采用正常 fetch 流程的证书验证方式。不可与
allowPooling一起使用。 congestionControl, 类型为 WebTransportCongestionControl, 默认值为"default"-
可选项,指明应用对拥塞控制算法更偏向吞吐量还是低延迟,发送数据时应采用何种算法。 这是对用户代理的优化建议。
anticipatedConcurrentIncomingUnidirectionalStreams, 类型为 unsigned short,可为 null,默认值为null-
可选项,允许应用指定预期服务器将并发创建多少 入向单向流。 用户代理最初必须允许至少 100 个 入向单向流来自服务器。 若不为 null,用户代理应尝试通过与服务器协商
[[AnticipatedConcurrentIncomingUnidirectionalStreams]]以减少往返。 anticipatedConcurrentIncomingBidirectionalStreams, 类型为 unsigned short,可为 null,默认值为null-
可选项,允许应用指定预期服务器将并发创建多少 双向流。 用户代理最初必须允许服务器创建至少 100 个 双向流。 若不为 null,用户代理应尝试通过与服务器协商
[[AnticipatedConcurrentIncomingBidirectionalStreams]]以减少往返。 protocols, 类型为 sequence<DOMString>,默认值为[]-
可选,提供一个应用层 协议名称数组。服务器可以选择性地选定优先应用协议并通知客户端。 若未提供合适协议,服务器可能会拒绝请求。
datagramsReadableType, 类型为 ReadableStreamType-
可选,指明应用偏好用于接收数据报的 可读字节流。否则使用默认 可读流。
注: 虽然 可读流 与数据报语义兼容, 可读字节流则并不兼容。数据报是区分的消息(每个消息为零或多个字节),可以无序到达, 与无分隔的字节序列不同。空的数据报会丢失,且
min会丢失消息边界。
-
令 cert 为 certificate,其表示为 [RFC5280] 中定义的证书消息的 DER 编码。
-
计算 cert 的 SHA-256 哈希并返回计算得到的值。
-
令 certificate 为 certificate chain 中的第一个证书(叶子证书)。
-
令 referenceHash 为使用 certificate计算证书哈希的结果。
-
对于 hashes 中的每个哈希 hash:
-
若 hash.
value非 null 且 hash.algorithm与 "sha-256" 为ASCII 不区分大小写匹配:-
令 hashValue 为 hash.
value所表示的字节序列。 -
若 hashValue 等于 referenceHash,则返回 true。
-
-
-
返回 false。
自定义证书要求如下:证书必须是 [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 会话时设置错误码和原因的信息。
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 ;unsigned long long bytesSentOverhead ;unsigned long long bytesAcknowledged ;unsigned long long packetsSent ;unsigned long long bytesLost ;unsigned long long packetsLost ;unsigned long long bytesReceived ;unsigned long long packetsReceived ;DOMHighResTimeStamp smoothedRtt ;DOMHighResTimeStamp rttVariation ;DOMHighResTimeStamp minRtt ;required WebTransportDatagramStats ;datagrams unsigned long long ?estimatedSendRate =null ;boolean atSendCapacity =false ; };
该字典应具有以下属性:
bytesSent, 类型为 unsigned long long-
在 底层连接上发送的负载字节数,不包括任何帧开销或重传。
bytesSentOverhead, 类型为 unsigned long longbytesAcknowledged, 类型为 unsigned long long-
通过 QUIC 的 ACK 机制,在 底层连接上被服务器确认收到的负载字节数。不包括任何帧开销。
注: 通常比
bytesSent落后,但因数据包丢失可能永久小于后者。 packetsSent, 类型为 unsigned long long-
在 底层连接上发送的总包数(包括已判定丢失的包)。
bytesLost, 类型为 unsigned long long-
在 底层连接上丢失的字节数(可能非单调递增,因为丢失判定的数据包可能随后收到)。 不包括 UDP 或其他外层帧开销。
packetsLost, 类型为 unsigned long long-
在 底层连接上丢失的数据包数(同样可能非单调递增,因为丢失判定的数据包可能随后收到)。
bytesReceived, 类型为 unsigned long long-
在 底层连接上接收到的总字节数,包括流的重复数据。不包括 UDP 或其他外层帧。
packetsReceived, 类型为 unsigned long long-
在 底层连接上接收到的总包数,包括无法处理的包。
smoothedRtt, 类型为 DOMHighResTimeStamprttVariation, 类型为 DOMHighResTimeStampminRtt, 类型为 DOMHighResTimeStampestimatedSendRate, 类型为 unsigned long long,可为 null,默认值为null-
用户代理队列数据的预计发送速率(单位:比特每秒)。 该速率适用于所有共享 WebTransport 会话的流和数据报, 由拥塞控制算法(可能由
congestionControl选择)计算。 此估算不包括任何帧开销,表示应用负载的数据发送速率。若用户代理当前没有估算,此成员必须为null。 该成员即使之前非null,后续结果中也可能变为null。 atSendCapacity, 类型为 boolean,默认值为false-
若值为 false,表示
estimatedSendRate可能受应用限制, 即应用发送的数据量远小于拥塞控制允许的量。在应用受限情形下,拥塞控制器可能无法准确估算可用网络容量。若值为 true,表示应用以网络容量发送数据,
estimatedSendRate反映应用可用的网络容量。当atSendCapacity为true,estimatedSendRate反映了网络上限。 只要应用发送速率持续,estimatedSendRate将随网络状况调整。不过,estimatedSendRate即使在atSendCapacity为 true 时也允许为null。
6.14.
WebTransportDatagramStats 字典
WebTransportDatagramStats 字典包括
有关 底层连接
上数据报传输的统计信息。
dictionary WebTransportDatagramStats {unsigned long long droppedIncoming ;unsigned long long expiredIncoming ;unsigned long long expiredOutgoing ;unsigned long long lostOutgoing ; };
该字典应具有以下属性:
droppedIncoming, 类型为 unsigned long longexpiredIncoming, 类型为 unsigned long long-
由于在被从
datagrams的readable读取前已超过incomingMaxAge而被丢弃的入站数据报数量。 expiredOutgoing, 类型为 unsigned long long-
排队等待发送的数据报,由于在能够发送前已超过
outgoingMaxAge而被丢弃的数量。 lostOutgoing, 类型为 unsigned long long
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,可为 null-
获取器步骤如下:
-
返回 this 的
[[SendGroup]]。
设置器步骤,给定 value,如下:
-
若 value 非 null 且 value.
[[Transport]]不是 this.[[Transport]], 抛出InvalidStateError。 -
将 this 的
[[SendGroup]]设置为 value。
-
sendOrder,类型为 long long-
获取器步骤如下:
-
返回 this 的
[[SendOrder]]。
设置器步骤,给定 value,如下:
-
将 this 的
[[SendOrder]]设置为 value。
-
7.2. 方法
getStats()-
收集此
WebTransportSendStream性能相关的统计信息, 并以异步方式返回结果。调用 getStats 时,用户代理必须执行以下步骤:
-
令 p 为一个新的 promise。
-
按如下并行执行步骤:
-
令 gatheredStats 为特定于 this
WebTransportSendStream的统计信息 列表, 用于准确填充WebTransportSendStreamStats的 字典成员。 -
排入网络任务队列,以 transport 运行以下步骤:
-
-
返回 p。
-
getWriter()-
为该 流 创建一个
WebTransportWriter。当调用
getWriter时,返回 创建的WebTransportWriter, 参数为 this。
7.3. 内部插槽
WebTransportSendStream
拥有以下内部槽。
| 内部槽 | 说明(非规范性) |
|---|---|
[[InternalStream]]
| 一个出站单向或双向 WebTransport 流。 |
[[PendingOperation]]
| 表示挂起写入或关闭操作的 promise,或为 null。 |
[[Transport]]
| 拥有该 WebTransport
的
WebTransportSendStream。
|
[[SendGroup]]
| 一个可选的 WebTransportSendGroup,
或 null。
|
[[SendOrder]]
| 可选的发送顺序号,默认值为 0。 |
[[AtomicWriteRequests]]
| promise 的有序集合, 用于追踪排队提交至底层 sink 并要求原子操作的写入请求子集。 |
[[InsideSynchronousAtomicWrite]]
| 布尔值,仅在执行 atomicWrite
方法的同步部分时为 true。
|
[[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]]-
一个空的 有序 promise 集合
[[InsideSynchronousAtomicWrite]]-
false
[[BytesWritten]]-
0
[[CommittedOffset]]-
0
-
令 writeAlgorithm 为一个动作,该动作 写入 chunk 到 stream(参数为 chunk 时)。
-
令 closeAlgorithm 为一个动作,该动作 关闭 stream。
-
令 abortAlgorithm 为一个动作,该动作 终止 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 为 终止 stream ,参数为 reason 的结果。
-
在 promise fulfilled 时,拒绝 pendingOperation,原因为 reason。
-
-
追加 stream 到 transport.
[[SendStreams]]。 -
返回 stream。
WebTransportSendStream
stream,执行如下步骤:
-
令 transport 为 stream.
[[Transport]]。 -
如果 chunk 不是
BufferSource, 返回一个被拒绝的 promise,异常为TypeError。 -
令 promise 为一个新的 promise。
-
令 bytes 为 chunk 所表示的字节序列的副本。
-
将 stream.
[[PendingOperation]]设为 promise。 -
令 inFlightWriteRequest 为 stream.inFlightWriteRequest。
-
令 atomic 为 true,若 stream.
[[AtomicWriteRequests]]包含 inFlightWriteRequest 或 stream.[[InsideSynchronousAtomicWrite]]为 true,否则为 false。 -
按如下并行执行步骤:
-
若 atomic 为 true,且当前流控窗口过小,无法将 bytes 一次性发出,则中止后续步骤,排入网络任务队列,transport 执行以下子步骤:
-
将 stream.
[[PendingOperation]]设为 null。 -
在 stream 上 中止所有原子写入请求。
-
-
否则,发送 bytes 到 stream.
[[InternalStream]]并等待操作完成。 该发送 必须遵循发送顺序规则。用户代理可拥有缓冲区以提升传输性能。此类缓冲区应有固定上限,以将背压信息传递给
WebTransportSendStream的使用者。此处可并行访问 stream.
[[SendOrder]]。 用户代理应支持发送过程中该值的实时更新, 具体细节为实现自定义。注: 重传的排序实现自定义, 但强烈建议用户代理优先对
[[SendOrder]]值更大的数据进行重传。用户代理应当为所有未被饿死的流公平分配带宽。
注: 此处公平的定义实现自定义。
-
如果上一步因网络错误失败,终止剩余步骤。
注: 此处不会 reject promise,因为网络错误会在其他地方处理,届时会 reject stream.
[[PendingOperation]]。 -
否则,排入网络任务队列,transport 执行以下步骤:
-
将 stream.
[[PendingOperation]]设为 null。 -
将 bytes 的长度加到 stream.
[[BytesWritten]]。 -
如果 stream.
[[AtomicWriteRequests]]包含 inFlightWriteRequest,则移除 inFlightWriteRequest。 -
解决 promise,值为 undefined。
-
-
-
返回 promise。
注: 此算法(或 write(chunk))
返回的 promise 被 fulfill 不代表
该 chunk 已被服务器
[QUIC] 确认(acked)。
它可能仅意味着该 chunk 被附加到了缓冲区。要确保 chunk 到达服务器,服务端必须再发送应用层确认消息。
WebTransportSendStream
stream,执行以下步骤:
-
令 transport 为 stream.
[[Transport]]。 -
令 promise 为新建的 promise。
-
移除 stream 从 transport.
[[SendStreams]]中。 -
将 stream.
[[PendingOperation]]设为 promise。 -
并行执行以下步骤:
-
发送 FIN 到 stream.
[[InternalStream]], 并等待操作完成。 -
等待 stream.
[[InternalStream]]进入 "all data committed" 状态。[QUIC] -
队列网络任务,传入 transport,执行:
-
将 stream.
[[PendingOperation]]设为 null。 -
resolve 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,则置为 0。
-
如果 code > 4294967295,则置为 4294967295。
-
令 committedOffset 为 stream.
[[CommittedOffset]]。注意: code 的有效取值为 0 到 4294967295(含)。如果 底层连接 使用 HTTP/3,code 会按 [WEB-TRANSPORT-HTTP3] 描述编码到 [0x52e4a40fa8db, 0x52e5ac983162] 区间。
-
并行执行以下步骤:
-
中止发送 stream.
[[InternalStream]],传入 code 和 committedOffset。
-
-
返回 promise。
WebTransportSendStream
stream 上终止所有 atomic write
请求,执行如下步骤:
-
令 writeRequests 为 stream.writeRequests。
-
令 requestsToAbort 为 stream.
[[AtomicWriteRequests]]。 -
如果 writeRequests 包含 任意不属于 requestsToAbort 的 promise,则 使 stream 出错,错误类型为
AbortError, 并终止这些步骤。 -
依次 对 requestsToAbort 中每个 promise, reject promise,异常为
AbortError。
7.5. 接收来自服务器的中止信号
WebTransportSendStream
stream 关联的 WebTransport 流
从服务器收到
接收已中止 信号时,执行以下步骤:
-
令 transport 为 stream.
[[Transport]]。 -
令 code 为附加在 接收已中止 信号上的应用协议错误码。
注: code 的有效值为 0 到 4294967295(含)。 若 底层连接 使用的是 HTTP/3,则错误码会被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 区间的数, 详见 [WEB-TRANSPORT-HTTP3]。
-
排入网络任务队列,用 transport 执行以下步骤:
-
如果 transport.
[[State]]为"closed"或"failed",终止这些步骤。 -
移除 stream 出 transport.
[[SendStreams]]。 -
令 error 为新 创建 的
WebTransportError, 其中source为"stream",streamErrorCode为 code。 -
若 stream.
[[PendingOperation]]不为 null,则 reject stream.[[PendingOperation]],原因为 error。 -
使 stream 出错,错误值为 error。
-
7.6. WebTransportSendStreamStats 字典
WebTransportSendStreamStats 字典
包括一个 WebTransportSendStream
的特定统计信息。
dictionary WebTransportSendStreamStats {unsigned long long bytesWritten ;unsigned long long bytesSent ;unsigned long long bytesAcknowledged ; };
该字典应具有以下属性:
bytesWritten, 类型为 unsigned long long-
应用已经成功写入此
WebTransportSendStream的总字节数。该数值只会递增。 bytesSent, 类型为 unsigned long long-
对应用写入此
WebTransportSendStream的字节有多少至少发送过一次的进度指标。该数值只会递增,且始终小于等于bytesWritten。注意: 此为仅本流应用层数据的发送进度,不包含任何网络开销。
bytesAcknowledged, 类型为 unsigned long long-
对应用写入此
WebTransportSendStream的字节已被服务端通过 QUIC 的 ACK 机制确认接收的进度指标。仅统计第一个未确认字节之前的所有连续字节。该数值只会递增,且始终小于等于bytesSent。注意: 当连接为 HTTP/2 时,该值会与
bytesSent保持一致。
8. 接口
WebTransportSendGroup
WebTransportSendGroup
是一个可选的组织对象,用于跟踪分布在许多个单独
(通常是 严格排序的)
WebTransportSendStream
上的数据传输。
WebTransportSendStream
和
WebTransportDatagramsWritable,
在创建或通过其 sendGroup 属性赋值后,
都可以在任意时刻被归组
到至多一个 WebTransportSendGroup
或 空 sendGroup。
默认情况下,
WebTransportSendStream
和 WebTransportDatagramsWritable
被分配到 空 sendGroup。
用户代理在分配带宽以发送 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]]等于 this。 -
按如下并行执行:
-
令 gatheredStats 为 streams 中所有流的聚合统计 列表, 用于准确填充
WebTransportSendStreamStats的 字典成员。 -
排入网络任务队列,用 transport 执行以下步骤:
-
-
返回 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 reader 以及 默认 reader。
[Exposed =(Window ,Worker ),SecureContext ,Transferable ]interface :WebTransportReceiveStream ReadableStream {Promise <WebTransportReceiveStreamStats >getStats (); };
一个 WebTransportReceiveStream
总是通过 创建过程生成。
WebTransportReceiveStream
的 传输步骤 和
接收传输步骤
与 ReadableStream 的步骤一致。
9.1. 方法
getStats()-
收集此
WebTransportReceiveStream性能相关的统计信息,并以异步方式返回结果。调用 getStats 时,用户代理必须执行以下步骤:
-
令 p 为一个新的 promise。
-
按如下并行执行:
-
令 gatheredStats 为特定于 this
WebTransportReceiveStream的统计信息 列表, 用于准确填充WebTransportReceiveStreamStats的 字典成员。 -
排入网络任务队列,以 transport 运行以下步骤:
-
-
返回 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 为带 reason 取消 stream 的操作。
-
用字节读取支持初始化 stream, pullAlgorithm 设为 pullAlgorithm, cancelAlgorithm 设为 cancelAlgorithm。
-
将 stream 添加到 transport.
[[ReceiveStreams]]。 -
返回 stream。
要从 WebTransportReceiveStream
stream
拉取字节,执行以下步骤。
-
令 transport 为 stream.
[[Transport]]。 -
令 internalStream 为 stream.
[[InternalStream]]。 -
令 promise 为一个新的 promise。
-
令 buffer、offset 和 maxBytes 均为 null。
-
如果 stream 的 当前 BYOB 请求 view 不为 null:
-
令 offset 为 stream 的 当前 BYOB 请求 view.[[ByteOffset]]。
-
令 maxBytes 为 stream 的 当前 BYOB 请求 view 的 字节长度。
-
令 buffer 为 stream 的 当前 BYOB 请求 view 的 底层缓冲区。
-
-
否则:
-
令 offset 为 0。
-
令 maxBytes 为实现自定义的字节数。
-
令 buffer 为一个新的
ArrayBuffer,大小为 maxBytes。 如果分配ArrayBuffer失败,返回 一个被拒绝的 promise,异常为RangeError。
-
-
按如下并行执行步骤:
-
写入从 internalStream 读取 到的字节至 buffer,偏移为 offset,最多 maxBytes 字节。 等待直至读到至少 1 个字节或收到 FIN。令 read 为读取的字节数, hasReceivedFIN 表示是否收到 FIN。
用户代理可拥有缓冲区以提升传输性能。此类缓冲区应有固定上限,以将背压信息传递给服务端。
注: 该操作可能在未填满 buffer 时就返回。
-
如果上一步失败,终止剩余步骤。
注: 此处不 reject promise,因为网络错误会在其他地方处理,届时会 使 stream 出错,从而 reject 所有等待该 pull 的读取请求。
-
排入网络任务队列,transport 执行以下步骤:
注: 如果上述缓冲区可在此过程所在事件循环中访问,则下述步骤可立即执行。
-
如果 read > 0:
-
令 view 为带有 buffer、offset、read 的新
Uint8Array。 -
入队 view 到 stream 中。
-
-
如果 hasReceivedFIN 为 true:
-
移除 stream 出 transport.
[[ReceiveStreams]]。 -
关闭 stream。
-
-
解决 promise,值为 undefined。
-
-
-
返回 promise。
要取消
一个 WebTransportReceiveStream
stream 携带 reason,执行以下
步骤。
-
令 transport 为 stream.
[[Transport]]。 -
令 internalStream 为 stream.
[[InternalStream]]。 -
令 promise 为一个新的 promise。
-
令 code 为 0。
-
如果 reason 是
WebTransportError且 reason.[[StreamErrorCode]]不为 null,则将 code 设为 reason.[[StreamErrorCode]]。 -
如果 code < 0,则将 code 设为 0。
-
如果 code > 4294967295,则将 code 设为 4294967295。
注: code 的有效值为 0 到 4294967295(含)。如果底层连接 使用 HTTP/3,则码值会被编码到 [0x52e4a40fa8db, 0x52e5ac983162] 区间,详见 [WEB-TRANSPORT-HTTP3]。
-
移除 stream 从 transport.
[[SendStreams]]。 -
按如下并行执行步骤:
-
返回 promise。
9.4. 接收来自服务器的发送中止信号
WebTransportReceiveStream
stream 关联的 WebTransport 流
从服务器收到
发送已中止 信号时,执行以下步骤:
-
令 transport 为 stream.
[[Transport]]。 -
令 code 为附加在 发送已中止 信号上的应用协议错误码。
注: code 的有效值为 0 到 4294967295(含)。 若 底层连接 使用的是 HTTP/3,则错误码会被编码为 [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 ;unsigned long long bytesRead ; };
字典应具有以下属性:
bytesReceived, 类型为 unsigned long long-
用于指示服务端应用发送到此
WebTransportReceiveStream的字节中,已成功接收的进度。 只统计第一个缺失字节之前的所有连续字节。该数值只会递增。注意: 仅为单条流中接收到的应用层数据进度,不包括任何网络开销。
bytesRead, 类型为 unsigned long long-
应用已从此
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,只读-
getter 步骤为返回 this 的
[[Readable]]。 writable, 类型为 WebTransportSendStream,只读-
getter 步骤为返回 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
始终通过
create
过程创建。
[Exposed=*,SecureContext ]interface :WebTransportWriter WritableStreamDefaultWriter {Promise <undefined >atomicWrite (optional any );chunk undefined commit (); };
11.1. 方法
atomicWrite(chunk)-
如果传入 chunk 时,无法在当前发送时的流控窗口内 整体发出,
atomicWrite方法会拒绝该操作。 此行为用于满足对流控 死锁敏感的特殊交易型应用需求 ([RFC9308] 第 4.4 节)。注:
atomicWrite发送部分数据后仍可能被拒绝。它只在流控层面保证原子性,其他错误仍可能发生。atomicWrite并不能避免数据被分包或与其它数据交错发送。只有发送方能够知道atomicWrite是否因流控额度不足而失败。注: 若原子写前面有非原子写操作排队,原子写可能阻塞。 若原子写被拒绝,其后的所有队列写入操作都会被拒绝。 以此方式被拒绝的非原子写会 使流 error。 因此建议应用总是 await 原子写操作。
当调用
atomicWrite(chunk)时,用户代理必须按以下步骤执行:-
如果 stream 为 undefined,返回 一个被拒绝的 promise,异常为
TypeError。 -
将 stream.
[[InsideSynchronousAtomicWrite]]设为 true。 -
将 stream.
[[InsideSynchronousAtomicWrite]]设为 false。 -
将 p 添加到 stream.
[[AtomicWriteRequests]]。 -
返回 在 p settled 时执行如下步骤的结果:
-
如果 stream.
[[AtomicWriteRequests]]包含 p, 移除 p。 -
如果 p 被拒绝,异常为 r,则返回 一个被拒绝的 promise,异常为 r。
-
返回 undefined。
-
commit()-
commit方法会将流的[[CommittedOffset]]更新为该流已经写入的字节数 ([[BytesWritten]])。 这样可确保这些字节即使在写入被中止、 导致流中止发送后, 也会被可靠传递到对端。 相关机制见 [RELIABLE-RESET]。注: 这无法保证连接失败时数据一定能传递, 只保证流已中止发送时传递。
当对 stream 调用
commit时,用户代理必须执行以下步骤:-
将 stream.
[[CommittedOffset]]设为 stream.[[BytesWritten]]的值。
-
11.2. 过程
要创建
一个 WebTransportWriter,
给定 WebTransportSendStream
stream,执行以下步骤:
-
令 writer 为 新的
WebTransportWriter。 -
执行 SetUpWritableStreamDefaultWriter(writer, 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,只读-
getter 步骤为返回 this 的
[[Source]]。 streamErrorCode, 类型为 unsigned long,只读,可为 null-
getter 步骤为返回 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效果 |
|---|---|
| 会话开始排水 | await wt.draining
|
如果 底层连接 使用 HTTP/3,则适用于从 [WEB-TRANSPORT-HTTP3] 的以下协议行为。
在 WebTransportError
错误中的应用 streamErrorCode
转换为 httpErrorCode,反之亦然,如 [WEB-TRANSPORT-HTTP3]
第4.3节 中所述。
| API 方法 | QUIC 协议操作 |
|---|---|
writable.abort(error)
| 中止发送(STREAM),带 httpErrorCode 以及
对应于该 [[CommittedOffset]]
的偏移(外加任意流头);详见 [RELIABLE-RESET]
|
writable.close()
| 发送 STREAM,并设置 FIN 标志位 |
writable.getWriter().write(chunk)()
| 发送 STREAM |
writable.getWriter().close()
| 发送 STREAM,并设置 FIN 标志位 |
writable.getWriter().abort(error)
| 中止发送(STREAM),带 httpErrorCode 以及
对应于该 [[CommittedOffset]]
的偏移(外加任意流头);详见 [RELIABLE-RESET]
|
readable.cancel(error)
| 中止接收(STREAM),带 httpErrorCode |
readable.getReader().cancel(error)
| 中止接收(STREAM),带 httpErrorCode |
wt.close(closeInfo)
| 终止会话,带 closeInfo |
| QUIC 协议操作 | API 效果 |
|---|---|
| 收到 STOP_SENDING(带 httpErrorCode) | 使 writable
报错,带 streamErrorCode
|
| 收到 STREAM | (await
readable.getReader().read()).value
|
| 收到 STREAM(带 FIN bit) | (await
readable.getReader().read()).done
|
| 收到 RESET_STREAM(带 httpErrorCode) | 使 readable
报错,带 streamErrorCode
|
| 会话被正常终止,带 closeInfo | (await wt.closed).closeInfo,
并且
使 所有打开的流报错
|
| 网络错误 | (await wt.closed)
reject,并且
使 所有打开的流报错
|
注意: 如 [QUIC] RFC9000 §3.2 所述, 收到 RESET_STREAM 帧或 RESET_STREAM_AT 帧([RELIABLE-RESET]) 并不总是会通知应用层。 重置信号可以立即触发,导致流数据传递被中断,未消费的数据会被丢弃。 但协议不要求必须立即通知,信号可能会延迟,以便传递 RESET_STREAM_AT 帧中的 Reliable Size 字段所指示的数据。 如果流数据已完整接收但尚未被应用读取,则发送中止信号可被抑制。 WebTransport 始终使用 RESET_STREAM_AT 帧,以确保流头部能够可靠传递; 参见 §4.1 和 §4.2 [WEB-TRANSPORT-HTTP3]。
| HTTP/3 协议动作 | API效果 |
|---|---|
| 会话开始排水 | await wt.draining
|
如果底层连接使用 HTTP/2,则适用 [WEB-TRANSPORT-HTTP2] 中的协议行为。注意,与 HTTP/3 不同,流错误码不需要转换为 HTTP 错误码,反之亦然。
| API 方法 | HTTP/2 协议操作 |
|---|---|
writable.abort(error)
| 中止发送 WT_STREAM,附带 error |
writable.close()
| 发送 带 FIN 标志位的 WT_STREAM |
writable.getWriter().write()
| 发送 WT_STREAM |
writable.getWriter().close()
| 发送 带 FIN 标志位的 WT_STREAM |
writable.getWriter().abort(error)
| 中止发送 WT_STREAM,附带 error |
readable.cancel(error)
| 中止接收 WT_STREAM,附带 error |
readable.getReader().cancel(error)
| 中止接收 WT_STREAM,附带 error |
wt.close(closeInfo)
| 终止会话,带 closeInfo |
| HTTP/2 协议操作 | API 效果 |
|---|---|
| 收到 WT_STOP_SENDING,附带 error | 使 writable
报错,带 streamErrorCode
|
| 收到 WT_STREAM | (await
readable.getReader().read()).value
|
| 收到 带 FIN 标志位的 WT_STREAM | (await
readable.getReader().read()).done
|
| 收到 WT_RESET_STREAM,附带 error | 使 readable
报错,带 streamErrorCode
|
| 会话被正常终止,带 closeInfo | (await wt.closed).closeInfo,
并且
使 所有打开的流报错
|
| 网络错误 | (await wt.closed)
reject,并且
使 所有打开的流报错
|
| 会话开始draining | await 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-OVERVIEW] 第 6 节, [WEB-TRANSPORT-HTTP3] 第 8 节,以及 [WEB-TRANSPORT-HTTP2] 第 9 节。
网络 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
读取器,
这样可以更精确地控制缓冲区分配,从而避免复制。下例将 datagram 读入一个 64 Kibibytes 的内存缓冲区中。
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
reader,
这样可以更精确地控制缓冲区分配,以避免数据拷贝。下面的示例将 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
接口,并已为在本规范中使用而进行了调整。