WebTransport

W3C 工作草案

关于本文档的更多信息
此版本:
https://www.w3.org/TR/2025/WD-webtransport-20251217/
最新发布版本:
https://www.w3.org/TR/webtransport/
编辑草案:
https://w3c.github.io/webtransport/
历史:
https://www.w3.org/standards/history/webtransport/
反馈:
public-webtransport@w3.org 主题行请注明 “[webtransport] … 消息主题 …” (邮件存档)
GitHub
规范内标注
编辑:
Nidhi Jaju (Google)
Victor Vasiliev (Google)
Jan-Ivar Bruaroey (Mozilla)
前任编辑:
Bernard Aboba (Microsoft Corporation)
Peter Thatcher (Google)
Robin Raymond (Optical Tone Ltd.)
Yutaka Hirano (Google)

摘要

本文档在 WebIDL 中定义了一组 ECMAScript API,用于在浏览器与服务器之间发送和接收数据,利用 [WEB-TRANSPORT-OVERVIEW]。 本规范与 IETF WEBTRANS 工作组制定的协议规范协同开发。

本文档状态

本节描述了本文档在发布时的状态。当前 W3C 出版物列表及本技术报告的最新修订版可在 W3C 技术报告索引中找到。

本文档由 WebTransport 工作组 按照 推荐轨道 作为工作草案发布。本文档旨在成为 W3C 推荐标准。

欢迎对本文档提出反馈和意见。请在本文档的 GitHub 问题区 GitHub 仓库中提交 issue。

作为工作草案发布并不代表 W3C 及其成员的认可。本文档为草稿,可能随时被更新、替换或废弃。除作为进行中的工作外,不适宜引用本文件。

本文件由在 W3C 专利政策下运作的团队制作。 W3C 维护一份任何专利披露的公开列表 ,该页面也包含专利披露的说明。知悉某专利且认为包含必要权利要求的个人,必须依照W3C 专利政策第6节披露信息。

本文件受 2025年8月18日 W3C 流程文件约束。

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 会话具有以下能力:

能力 定义
发送数据报 [WEB-TRANSPORT-OVERVIEW] Section 4.2
接收数据报 [WEB-TRANSPORT-OVERVIEW] Section 4.2
创建出向单向 [WEB-TRANSPORT-OVERVIEW] Section 4.3
创建双向 [WEB-TRANSPORT-OVERVIEW] Section 4.3
接收入向单向 [WEB-TRANSPORT-OVERVIEW] Section 4.3
接收双向 [WEB-TRANSPORT-OVERVIEW] Section 4.3

当服务器请求CONNECT 流优雅关闭时,WebTransport 会话 session处于排空状态,详见[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] Section 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 transportsendGroupsendOrder,执行以下步骤。
  1. stream 为一个 WebTransportDatagramsWritable, 其各项为:

    [[OutgoingDatagramsQueue]]

    一个空队列

    [[Transport]]

    transport

    [[SendGroup]]

    sendGroup

    [[SendOrder]]

    sendOrder

  2. writeDatagramsAlgorithm 为执行 writeDatagrams, 参数为 transportstream 的操作。

  3. 设置 streamwriteAlgorithm 设置为 writeDatagramsAlgorithm

  4. 返回 stream

4.2. 属性

sendGroup, 类型为 WebTransportSendGroup, 可空

getter 步骤:

  1. 返回 this[[SendGroup]]

setter 步骤,给定 value

  1. 如果 value 非空,且 value.[[Transport]] 不是 this.[[Transport]]抛出一个 InvalidStateError

  2. 设置 this.[[SendGroup]]value

sendOrder, 类型为 long long

getter 步骤:

  1. 返回 this[[SendOrder]]

setter 步骤,给定 value

  1. 设置 this.[[SendOrder]]value

4.3. 过程

writeDatagrams 算法给定一个 transportwritable 作为参数,以及 data 作为输入。它通过运行以下步骤来定义:

  1. timestamp 为一个表示当前时间的 timestamp。

  2. 如果 data 不是一个 BufferSource 对象,则返回 一个 promise 被拒绝,并返回一个 TypeError

  3. datagramstransport.[[Datagrams]]

  4. 如果 datagrams.[[OutgoingMaxDatagramSize]] 小于 data 的 [[ByteLength]],则返回 一个 promise 被解析,并返回 undefined。

  5. promise 为一个新的 promise。

  6. bytesdata 表示的字节的副本。

  7. chunk 为一个元组,包含 bytestimestamppromise

  8. chunk 入队到 writable.[[OutgoingDatagramsQueue]]

  9. 如果 writable.[[OutgoingDatagramsQueue]] 的长度小于 datagrams.[[OutgoingDatagramsHighWaterMark]],则解析 promise 并返回 undefined。

  10. 返回 promise

注意: 关联的 WritableStream 仅当该流的 writeDatagrams 返回的所有 promise 都已解析时,才会调用 writeDatagrams。因此,只有当 Web 开发人员关注 WritableStreamDefaultWriter.ready 时,时间戳和过期持续时间才能很好地工作。

sendDatagrams,给定一个 WebTransport 对象 transport 和一个 WebTransportDatagramsWritable 对象 writable将一个网络任务入队,使用 transport 运行以下步骤:

  1. queuewritable.[[OutgoingDatagramsQueue]] 的副本。

    注意: 上述副本以及将网络任务排队以运行这些步骤都可以进行优化。

  2. maxSizetransport.[[Datagrams]].[[OutgoingMaxDatagramSize]]

  3. durationtransport.[[Datagrams]].[[OutgoingDatagramsExpirationDuration]]

  4. 如果 duration 为 null,则将 duration 设置为一个 实现定义的值。

  5. 并行运行以下步骤:

    1. queue 不为空时:

      1. bytestimestamppromisequeue 的第一个元素。

      2. 如果自 timestamp 以来经过的时间超过 duration 毫秒,则:

        1. queue 中移除第一个元素。

        2. 将一个网络任务入队,使用 transport 解析 promise 并返回 undefined。

      3. 否则,跳出此循环。

    2. 如果 transport.[[State]] 不是 "connected",则返回。

    3. queue 不为空时:

      1. bytestimestamppromisequeue 的第一个元素。

      2. 如果 bytes 的长度 ≤ maxSize

        1. 如果无法立即将 bytes 发送到网络,则跳出此循环。

        2. 发送一个数据报,使用 transport.[[Session]]bytes

      3. queue 中移除第一个元素。

      4. 将一个网络任务入队,使用 transport 解析 promise 并返回 undefined。

对于任何 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. 内部插槽

内部槽位 描述(非规范性
[[Transport]] WebTransport 拥有的这个 WebTransportDatagramDuplexStream
[[Readable]] 用于接收数据报的 ReadableStream
[[ReadableType]] 用于接收数据报的 ReadableStreamType
[[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] 中,没有对应的限制。

由于数据报的处理通常需要将整个报文驻留在内存中, 实现会对此大小有上限。 未来的协议扩展可能允许所有协议变体对这些大小限制进行信令协商。

用户代理可以随时更新任何 [[OutgoingMaxDatagramSize]] ,只要对应的 WebTransport 对象其 [[State]]"connecting""connected"

创建一个 WebTransportDatagramDuplexStream ,给定一个 WebTransport transportreadablereadableType,执行以下步骤。
  1. stream 为一个 WebTransportDatagramDuplexStream, 其各项为:

    [[Transport]]

    transport

    [[Readable]]

    readable

    [[ReadableType]]

    readableType

    [[Writables]]

    一个空的 有序集合

    [[IncomingDatagramsQueue]]

    一个空队列

    [[IncomingDatagramsPullPromise]]

    null

    [[IncomingDatagramsHighWaterMark]]

    一个 实现定义的值

    [[IncomingDatagramsExpirationDuration]]

    null

    [[OutgoingDatagramsHighWaterMark]]

    一个 实现定义的值

    此实现定义的值应根据传输速率进行调优,保证传输性能不影响数据时效性。

    [[OutgoingDatagramsExpirationDuration]]

    null

    [[OutgoingMaxDatagramSize]]

    一个 实现定义的整数。

  2. 返回 stream

5.2. 方法

createWritable()

创建一个 WebTransportDatagramsWritable

当调用 createWritable() 方法时,用户代理必须执行以下步骤:
  1. transport 为与 this 关联的 WebTransport 对象。

  2. 如果 transport.[[State]]"closed""failed"抛出一个 InvalidStateError

  3. sendGroupoptionssendGroup

  4. 如果 sendGroup 不为 null 且 sendGroup.[[Transport]] 不等于 this.[[Transport]]抛出 一个 InvalidStateError

  5. sendOrderoptionssendOrder

  6. 返回创建一个 WebTransportDatagramsWritable 的结果,参数为 transportsendGroupsendOrder

5.3. 属性

readable, 类型为 ReadableStream, 只读

getter 步骤如下:

  1. 返回 this.[[Readable]]

incomingMaxAge, 类型为 unrestricted double, 可为 null

getter 步骤如下:

  1. 返回 this.[[IncomingDatagramsExpirationDuration]]

setter 步骤(给定 value)如下:

  1. 如果 value 为负数或 NaN,抛出 RangeError

  2. 如果 value 等于 0,将 value 设为 null。

  3. this.[[IncomingDatagramsExpirationDuration]] 设置为 value

maxDatagramSize, 类型为 unsigned long, 只读

可传递给 WebTransportDatagramsWritable 的最大数据尺寸。getter 步骤为返回 this.[[OutgoingMaxDatagramSize]]

outgoingMaxAge, 类型为 unrestricted double, 可为 null

getter 步骤如下:

  1. 返回 this[[OutgoingDatagramsExpirationDuration]]

setter 步骤(给定 value)如下:

  1. 如果 value 为负数或 NaN,抛出 RangeError

  2. 如果 value 等于 0,将 value 设为 null。

  3. this.[[OutgoingDatagramsExpirationDuration]] 设置为 value

incomingHighWaterMark, 类型为 unrestricted double

getter 步骤如下:

  1. 返回 this.[[IncomingDatagramsHighWaterMark]]

setter 步骤(给定 value)如下:

  1. 如果 value 为负数或 NaN,抛出 RangeError

  2. 如果 value 小于 1,将 value 设为 1

  3. this.[[IncomingDatagramsHighWaterMark]] 设置为 value

outgoingHighWaterMark, 类型为 unrestricted double

getter 步骤如下:

  1. 返回 this.[[OutgoingDatagramsHighWaterMark]]

setter 步骤(给定 value)如下:

  1. 如果 value 为负数或 NaN,抛出 RangeError

  2. 如果 value 小于 1,将 value 设为 1

  3. this.[[OutgoingDatagramsHighWaterMark]] 设置为 value

5.4. 过程

拉取数据报,给定 WebTransport 对象 transport,执行以下步骤:

  1. datagramstransport.[[Datagrams]]

  2. 断言:datagrams.[[IncomingDatagramsPullPromise]] 为 null。

  3. queuedatagrams.[[IncomingDatagramsQueue]]

  4. 如果 queue 为空,则:

    1. datagrams.[[IncomingDatagramsPullPromise]] 设为新 promise。

    2. 返回 datagrams.[[IncomingDatagramsPullPromise]]

  5. datagramtimestamp 作为 出队 queue 的结果。

  6. 如果 datagrams.[[ReadableType]]"bytes",则:

    1. 如果 datagrams.[[Readable]]当前 BYOB 请求视图不为 null,则:

      1. viewdatagrams.[[Readable]]当前 BYOB 请求视图

      2. 如果 view字节长度 小于 datagram 的大小,返回 一个带有 RangeError 的 reject promise。

      3. elementSize类型化数组构造函数表view.[[TypedArrayName]] 指定的元素大小。如果 view 没有 [[TypedArrayName]] 内部槽(即为 DataView),则 elementSize 为 0。

      4. 如果 elementSize 不为 1,返回 一个带有 TypeError 的 reject promise。

    2. 从字节拉取 datagramdatagrams.[[Readable]]

  7. 否则:

    1. chunk 为一个代表 datagram 的新 Uint8Array 对象。

    2. 入队 chunktransport.[[Datagrams]].[[Readable]]

  8. 返回 一个已解决为 undefined 的 promise。

接收数据报,给定 WebTransport 对象 transport,执行以下步骤:

  1. timestamp 为当前时刻的时间戳。

  2. queuedatagrams.[[IncomingDatagramsQueue]]

  3. durationdatagrams.[[IncomingDatagramsExpirationDuration]]

  4. 如果 duration 为 null,则将 duration 设为实现定义的值。

  5. sessiontransport.[[Session]]

  6. 只要 session 上有可用的接收数据报,则:

    1. datagram 为用 session 接收一个数据报 的结果。

    2. timestamp 为当前时刻的时间戳。

    3. chunkdatagramtimestamp 的对。

    4. 入队 chunkqueue

  7. toBeRemovedqueue 的长度减去 datagrams.[[IncomingDatagramsHighWaterMark]]

  8. 如果 toBeRemoved 为正数,则重复 出队 queue toBeRemoved下取整)次。

  9. 只要 queue 不为空:

    1. bytestimestampqueue 的首个元素。

    2. 如果自 timestamp 开始已有超过 duration 毫秒,则 出队 queue

    3. 否则 跳出此循环。

  10. 如果 queue 不为空且 datagrams.[[IncomingDatagramsPullPromise]] 非 null,则:

    1. bytestimestamp出队 queue 的结果。

    2. promisedatagrams.[[IncomingDatagramsPullPromise]]

    3. datagrams.[[IncomingDatagramsPullPromise]] 设为 null。

    4. 排队一个网络任务,以 transport 运行以下步骤:

      1. chunk 为表示 bytes 的新 Uint8Array 对象。

      2. 入队 chunkdatagrams.[[Readable]]

      3. 解决 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<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 options = {});
  /* a ReadableStream of WebTransportBidirectionalStream objects */
  readonly attribute ReadableStream incomingBidirectionalStreams;

  Promise<WebTransportSendStream> createUnidirectionalStream(
      optional WebTransportSendStreamOptions options = {});
  /* a ReadableStream of WebTransportReceiveStream objects */
  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 会话 建立 时完成的 promise, 或建立过程失败则被拒绝。
[[Reliability]] 一个 WebTransportReliabilityMode ,表示首跳是否支持不可靠(UDP)传输或仅支持可靠(TCP 回退)传输。在连接建立前返回 "pending"
[[CongestionControl]] 一个 WebTransportCongestionControl ,表示应用是否请求并由用户代理满足了针对吞吐量或低延迟优化的拥塞控制算法,或 "default"
[[AnticipatedConcurrentIncomingUnidirectionalStreams]] 应用预期服务器将创建的并发打开的 入向单向流的数量,或为 null。
[[AnticipatedConcurrentIncomingBidirectionalStreams]] 应用预期服务器将创建的并发打开的 双向流的数量,或为 null。
[[Protocol]] 一个字符串,指示服务器选择的应用层协议(如有)。初始为空字符串。
[[Closed]] 当关联的 WebTransport 对象 被正常关闭时完成,或被异常关闭或初始化失败时被拒绝的 promise。
[[Draining]] 当相关 WebTransport 会话排空 时完成的 promise。
[[Datagrams]] 一个 WebTransportDatagramDuplexStream
[[Session]] WebTransport 对象对应的 WebTransport 会话,或为 null。
[[NewConnection]] 为 "no" 或 "yes-and-dedicated"。
[[RequireUnreliable]] 一个布尔值,指示是否需要 UDP。
[[ServerCertificateHashes]] 一个 列表,包含零个或多个 WebTransportHash 对象。

6.2. 构造函数

WebTransport() 构造函数被调用时,用户代理必须运行以下步骤:
  1. baseURLthis相关设置对象API 基础 URL

  2. url 为使用 baseURLurl 进行 解析 后得到的 URL 记录

  3. 如果 url 失败,抛出 一个 SyntaxError 异常。

  4. 如果 urlscheme 不是 https抛出 一个 SyntaxError 异常。

  5. 如果 urlfragment 非空,抛出 一个 SyntaxError 异常。

  6. allowPoolingoptionsallowPooling

  7. dedicatedallowPooling 的否定。

  8. serverCertificateHashesoptionsserverCertificateHashes

  9. 如果 dedicated 为 false 且 serverCertificateHashes 非空,则 抛出 一个 NotSupportedError 异常。

  10. newConnection 为当 dedicated 为 false 时的 "no";否则为 "yes-and-dedicated"。

  11. requireUnreliableoptionsrequireUnreliable

  12. congestionControloptionscongestionControl

  13. 如果 congestionControl 不是 "default",且用户代理不支持任何针对 congestionControl 优化的拥塞控制算法(按 [RFC9002] 第 7 节 所允许),则将 congestionControl 设为 "default"

  14. protocolsoptionsprotocols

  15. 如果 protocols 中的任一值出现多次、未能满足 WebTransport 协议定义的协商应用协议值组成元素的要求,或其 isomorphic encoded 长度为 0 或超过 512,则 抛出 一个 SyntaxError 异常。 [WEB-TRANSPORT-OVERVIEW] 第 3.1 节

  16. anticipatedConcurrentIncomingUnidirectionalStreamsoptionsanticipatedConcurrentIncomingUnidirectionalStreams

  17. anticipatedConcurrentIncomingBidirectionalStreamsoptionsanticipatedConcurrentIncomingBidirectionalStreams

  18. datagramsReadableTypeoptionsdatagramsReadableType

  19. incomingDatagrams 为一个新的 new ReadableStream

  20. 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]]

    未定义

    [[Session]]

    null

    [[NewConnection]]

    newConnection

    [[RequireUnreliable]]

    requireUnreliable

    [[ServerCertificateHashes]]

    serverCertificateHashes

  21. transport.[[Datagrams]] 设置为通过 创建 一个 WebTransportDatagramDuplexStream 得到的结果,传入 transportincomingDatagramsdatagramsReadableType

  22. pullDatagramsAlgorithm 为一个运行 pullDatagrams(参数为 transport) 的操作。

    注:建议在数据报上使用 64 KiB 的缓冲区,因为有效的最大 WebTransport 数据报帧大小上限与 QUIC 最大数据报帧大小的推荐值相同,即 64 KiB(参见 [QUIC-DATAGRAM] 第 3 节)。这将确保流不会因数据报大于缓冲区而出错。

  23. 如果 datagramsReadableType"bytes",则应为 incomingDatagrams 设置 字节读取支持,并将其 pullAlgorithm 设为 pullDatagramsAlgorithm,将 highWaterMark 设为 0。否则,按照常规为 incomingDatagrams 设置,将 pullAlgorithm 设为 pullDatagramsAlgorithm,并将 highWaterMark 设为 0。

  24. pullBidirectionalStreamAlgorithm 为一个运行 pullBidirectionalStream(参数为 transport) 的操作。

  25. transport.[[IncomingBidirectionalStreams]] 设置 pullAlgorithmpullBidirectionalStreamAlgorithm,并将 highWaterMark 设为 0。

  26. pullUnidirectionalStreamAlgorithm 为一个运行 pullUnidirectionalStream(参数为 transport) 的操作。

  27. transport.[[IncomingUnidirectionalStreams]] 设置 pullAlgorithmpullUnidirectionalStreamAlgorithm,并将 highWaterMark 设为 0。

  28. clienttransport相关设置对象

  29. originclientorigin

  30. request 为一个新的 request,其 URLurlclientclientservice-workers 模式 为 "none", referrer 为 "no-referrer",mode 为 "webtransport", credentials mode 为 "omit", cache mode 为 "no-store", policy containerclientpolicy containerdestination 为 "", originorigin,并且 redirect mode 为 "error"。

    注:不会跟随重定向。由重定向引起的网络错误故意与其他网络错误不可区分。在跨源上下文中,这会泄露通常由 CORS 阻止的信息。在同源上下文中,可能会鼓励应用滥用握手作为传递信息的途径。

  31. requestmethod 设置为 "CONNECT",并将该 method 关联的 :protocol pseudo-header 设置为 "webtransport"

  32. 如果 protocols 非空, requestheader list 中设置一个结构化字段值,键为 WT-Available-Protocols,值为一个结构化 header 列表,其成员为 protocols 中按顺序的结构化 header 字符串项。

  33. Fetch request,将 useParallelQueue 设为 true,并将 processResponse 设为在给定 response 时运行以下步骤:

    1. responseoriginprotocolscongestionControl 运行 处理 WebTransport fetch 响应

  34. 返回 transport

获取 WebTransport 连接,在给定一个 网络分区键 networkPartitionKey 和一个 请求记录 request 时:
  1. transport 为与 request 关联的 WebTransport 对象。

  2. urlrequest当前 URL

  3. newConnectiontransport.[[NewConnection]]

  4. requireUnreliabletransport.[[RequireUnreliable]]

  5. serverCertificateHashestransport.[[ServerCertificateHashes]] 中的值。

  6. connection 为使用 networkPartitionKeyurl、false、newConnectionrequireUnreliable 获取连接的结果。当获取连接时,如果 serverCertificateHashes 非空,则不要使用默认的证书验证算法;当满足 自定义证书要求 且对证书哈希的 验证 相对于 serverCertificateHashes 返回 true 时,才将证书视为有效。如果任一条件不满足,结果为失败。

  7. 如果 connection 为失败,返回失败。

  8. 等待 connection 接收第一个 SETTINGS 帧,并令 settings 为表示该 SETTINGS 帧的字典。

  9. 如果 settings 不包含 SETTINGS_ENABLE_CONNECT_PROTOCOL(0x08,见 RFC8441 第 3 节 用于 HTTP/2;0x08,见 RFC9220 第 3 节 用于 HTTP/3)且其值不是 1,则返回失败。

  10. 如果 settings 未指示服务器支持 WebTransport,则返回失败。 [WEB-TRANSPORT-OVERVIEW] 第 4.1 节

    注: SETTINGS_WT_MAX_SESSIONS 在 IETF 中仍在调整,可能会改回 SETTINGS_ENABLE_WEBTRANSPORT

  11. 返回 connection

处理 WebTransport fetch 响应,在给定 responsecongestionControl 时,运行下列步骤:
  1. 如果 response网络错误,则中止剩余步骤并为 transport 排队一个网络任务 来运行这些步骤:

    1. 如果 transport.[[State]]"closed""failed",则中止这些步骤。

    2. error 为新创建的一个 异常,类型为 WebTransportError,其 source"session"

    3. 清理 transport,使用 error

  2. connection 为与 response 关联的底层连接。

  3. 遵循 [WEB-TRANSPORT-OVERVIEW] 第 4.1 节 中的任何限制,以在 connection 上使用服务器的 response 建立 一个 WebTransport 会话

  4. session 为刚刚在 connection建立WebTransport 会话。由此产生的底层传输流称为该会话的 CONNECT 流

    注:该步骤也完成了在 [QUIC-DATAGRAM] 中指定的传输参数交换。

  5. 如果上一步失败, 中止剩余步骤并为 transport 排队一个网络任务 来运行这些步骤:

    1. 如果 transport.[[State]]"closed""failed",则中止这些步骤。

    2. error 为新创建的一个 异常,类型为 WebTransportError,其 source"session"

    3. 清理 transport,使用 error

  6. 如果用户代理支持多于一种拥塞控制算法,则为此 connection 选择一个适合 congestionControl 的算法用于发送数据。

  7. transport 排队一个网络任务 来运行这些步骤:

    1. 断言:this[[Datagrams]][[OutgoingMaxDatagramSize]] 是一个整数。

    2. 如果 transport.[[State]] 不是 "connecting"

      1. 并行地终止 session

      2. 中止这些步骤。

    3. transport.[[State]] 设为 "connected"

    4. transport.[[Session]] 设为 session

    5. transport.[[Protocol]] 设为协商得到的应用协议的字符串值(如果存在),按 [WEB-TRANSPORT-OVERVIEW] 第 3.1 节 的说明,否则设为 ""

    6. 如果连接为 HTTP/3 连接,则将 transport.[[Reliability]] 设为 "supports-unreliable"

    7. 如果连接为 HTTP/2 连接 [WEB-TRANSPORT-HTTP2],则将 transport[[Reliability]] 设为 "reliable-only"

    8. 解析 transport.[[Ready]] 为 undefined。

pullBidirectionalStream,在给定一个 WebTransport 对象 transport 时,运行下列步骤。
  1. 如果 transport.[[State]]"connecting",则在 transport.[[Ready]] 实现后 执行 以下步骤,并返回其结果:

    1. 返回对 pullBidirectionalStream(参数为 transport) 的调用结果。

  2. 如果 transport.[[State]] 不是 "connected",则返回一个新的被拒绝的 promise,拒绝原因为一个 InvalidStateError

  3. sessiontransport.[[Session]]

  4. p 为一个新的 promise。

  5. 并行运行以下步骤:

    1. 等待直到 session 中有一个 可用的传入双向流

    2. internalStream 为从 session 接收 到的双向流 的结果。

    3. transport 排队一个网络任务 来运行这些步骤:

      1. stream 为通过 创建 一个 WebTransportBidirectionalStream (参数为 internalStreamtransport) 得到的结果。

      2. stream 入队transport.[[IncomingBidirectionalStreams]]

      3. 解析 p 为 undefined。

  6. 返回 p

pullUnidirectionalStream,在给定一个 WebTransport 对象 transport 时,运行下列步骤。
  1. 如果 transport.[[State]]"connecting",则在 transport.[[Ready]] 实现后 执行 以下步骤,并返回其结果:

    1. 返回对 pullUnidirectionalStream(参数为 transport) 的调用结果。

  2. 如果 transport.[[State]] 不是 "connected",则返回一个新的被拒绝的 promise,拒绝原因为一个 InvalidStateError

  3. sessiontransport.[[Session]]

  4. p 为一个新的 promise。

  5. 并行运行以下步骤:

    1. 等待直到 session 中有一个 可用的传入单向流

    2. internalStream 为从 session 接收 到的传入单向流 的结果。

    3. transport 排队一个网络任务 来运行这些步骤:

      1. stream 为通过 创建 一个 WebTransportReceiveStream (参数为 internalStreamtransport) 得到的结果。

      2. stream 入队transport.[[IncomingUnidirectionalStreams]]

      3. 解析 p 为 undefined。

  6. 返回 p

6.3. 属性

ready类型为 Promise<undefined>,只读

读取时,必须返回 this[[Ready]]

closed类型为 Promise<WebTransportCloseInfo>,只读

读取时,必须返回 this[[Closed]]

draining类型为 Promise<undefined>,只读

读取时,必须返回 this[[Draining]]

datagrams类型为 WebTransportDatagramDuplexStream, 只读

用于在本会话中收发数据报的单个双工流。 datagrams 属性的 getter 步骤如下:

  1. 返回 this[[Datagrams]]

incomingBidirectionalStreams类型为 ReadableStream,只读

返回由服务器接收的 ReadableStream ,其元素为 WebTransportBidirectionalStream

注:传入流是否已有数据,取决于服务器行为。

incomingBidirectionalStreams 属性的 getter 步骤如下:

  1. 返回 this[[IncomingBidirectionalStreams]]

incomingUnidirectionalStreams类型为 ReadableStream,只读

一个单向流的 ReadableStream, 每个流由一个 WebTransportReceiveStream 表示, 它们都来自服务器。

注:传入流是否已有数据,取决于服务器行为。

incomingUnidirectionalStreams 的 getter 步骤如下:

  1. 返回 this.[[IncomingUnidirectionalStreams]]

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

注:设置 anticipatedConcurrentIncomingUnidirectionalStreamsanticipatedConcurrentIncomingBidirectionalStreams 不能保证应用会收到预期数量的流。

protocol类型为 DOMString,只读

建立 WebTransport 会话 且在构造函数中 protocols 选项为非空数组时,返回服务器选择的应用层协议(如有)。否则为空字符串。 getter 步骤为返回 this[[Protocol]]

6.4. 方法

close(closeInfo)

终止与该 WebTransport 对象关联的 WebTransport 会话

当调用 close 时,用户代理必须运行以下步骤:

  1. transportthis

  2. 如果 transport.[[State]]"closed""failed",则中止这些步骤。

  3. 如果 transport.[[State]]"connecting"

    1. error 为新创建的一个 WebTransportError,其 source"session"

    2. 清理 transport,使用 error

    3. 中止这些步骤。

  4. sessiontransport.[[Session]]

  5. codecloseInfo.closeCode

  6. reasonStringcloseInfo.reason 的最大 代码单元前缀,保证其 UTF-8 编码后长度不超过 1024。

  7. reasonreasonStringUTF-8 编码值。

  8. 并行地,使用 codereason 终止 session

    注:这也会对 transport.[[SendStreams]][[ReceiveStreams]] 中包含的 WebTransport 流 执行 中止发送中止接收

  9. 清理 transport,使用 AbortErrorcloseInfo

getStats()

收集本 WebTransport底层连接 的统计数据并异步返回结果。

当调用 getStats 时,用户代理必须运行以下步骤:

  1. transportthis

  2. p 为一个新的 promise。

  3. 如果 transport.[[State]]"failed",则 拒绝 p,原因为 InvalidStateError,并中止这些步骤。

  4. 并行运行以下步骤:

    1. 如果 transport.[[State]]"connecting",则等待其状态改变。

    2. 如果 transport.[[State]]"failed",则在 transport 排队一个网络任务后,拒绝 p,原因为 InvalidStateError,然后中止这些步骤。

    3. 如果 transport.[[State]]"closed",则在 transport 排队一个网络任务后,解析 p,其值为当前连接的最新可用统计信息。实际收集统计的时间点为实现定义

    4. gatheredStats 为用于准确填充 WebTransportConnectionStatsWebTransportDatagramStats底层连接的统计数据列表。

    5. transport 排队一个网络任务,运行下列步骤:

      1. stats新建WebTransportConnectionStats 对象。

      2. datagramStats新建WebTransportDatagramStats 对象。

      3. stats["datagrams"] 设为 datagramStats

      4. 对于 statsdatagramStats 中代理欲公开的每个 字典成员 member,将 member 设为 gatheredStats 中对应的 条目

      5. 解析 p,值为 stats

  5. 返回 p

exportKeyingMaterial(BufferSource label, optional BufferSource context)

为唯一关联于此 WebTransport 的 TLS 会话, 通过 TLS 会话密钥导出器 导出密钥材料。

当调用 exportKeyingMaterial 时,用户代理必须运行以下步骤:

  1. transportthis

  2. labelLengthlabel字节长度

  3. 如果 labelLength 超过 255,则返回 一个被拒绝的 promise,拒绝原因为 RangeError

  4. contextLength 为 0。

  5. 如果传入 context,则将 contextLength 设为 context字节长度

  6. 如果 contextLength 超过 255,则返回 一个被拒绝的 promise,拒绝原因为 RangeError

  7. p 为一个新的 promise。

  8. 并行运行以下步骤,但当 transport[[State]] 变为 "closed""failed"中止,并且为 transport 排队一个网络任务拒绝 p,原因为 InvalidStateError

    1. keyingMaterial 为,根据 [WEB-TRANSPORT-OVERVIEW] 中第 4.1 节,使用 labelLengthlabelcontextLength 以及(如有)context 导出的 TLS 密钥材料。

    2. transport 排队一个网络任务, 解析 p,值为 keyingMaterial

  9. 返回 p

createBidirectionalStream()

创建一个用于发送的 WebTransportBidirectionalStream 双向流对象。注意,仅创建该流时对端不会立即可见,直到该流被用来发送数据。

注:在流被发送数据前,服务器不会感知该流。

当调用 createBidirectionalStream 时,用户代理必须运行以下步骤:
  1. 如果 this.[[State]]"closed""failed", 则返回一个被拒绝的 promise,拒绝原因为 InvalidStateError

  2. sendGroupoptionssendGroup

  3. 如果 sendGroup 不为 null,且 sendGroup.[[Transport]] 不等于 this抛出 InvalidStateError

  4. sendOrderoptionssendOrder

  5. waitUntilAvailableoptionswaitUntilAvailable

  6. p 为新 promise。

  7. transportthis

  8. 并行运行以下步骤,当 transport[[State]] 变为 "closed""failed"中止 并为 transport 排队一个网络任务拒绝 p,原因为 InvalidStateError

    1. streamIdtransport.[[Session]] 的一个有效且唯一的新 stream ID(见 [QUIC] 第 19.11 节)。如果因资源耗尽而无法立即创建,可根据 waitUntilAvailable 等待;否则在 waitUntilAvailable 为 false 时, 排队网络任务拒绝 p, 原因为 QuotaExceededError

    2. internalStream 为使用 transport.[[Session]]streamId 创建双向流结果。

    3. transport 排队一个网络任务,运行下列步骤:

      1. 如果 transport.[[State]]"closed""failed"拒绝 p,原因为 InvalidStateError 并中止这些步骤。

      2. stream 为对 internalStreamtransportsendGroupsendOrder 创建WebTransportBidirectionalStream

      3. 解析 p,值为 stream

  9. 返回 p

createUnidirectionalStream()

创建一个用于发送的 WebTransportSendStream 单向流对象。注意,仅创建该流时服务器不会立即可见,直到该流被用来发送数据。

注:在流被发送数据前,服务器不会感知该流。

当调用 createUnidirectionalStream() 方法时,用户代理必须运行以下步骤:
  1. 如果 this.[[State]]"closed""failed", 返回一个被拒绝的 promise,拒绝原因为 InvalidStateError

  2. sendGroupoptionssendGroup

  3. 如果 sendGroup 不为 null,且 sendGroup.[[Transport]] 不等于 this抛出 InvalidStateError

  4. sendOrderoptionssendOrder

  5. waitUntilAvailableoptionswaitUntilAvailable

  6. p 为新 promise。

  7. transportthis

  8. 并行运行以下步骤,当 transport[[State]] 变为 "closed""failed"中止 并为 transport 排队一个网络任务拒绝 p,原因为 InvalidStateError

    1. streamIdtransport.[[Session]] 的有效且唯一的新 stream ID(见 [QUIC] 第 19.11 节)。如果因资源耗尽无法立即创建,根据 waitUntilAvailable 选择等待或直接 排队网络任务拒绝 p,原因为 QuotaExceededError

    2. internalStream 为通过 transport.[[Session]]streamId 创建单向流的结果。

    3. transport 排队一个网络任务,执行如下:

      1. 如果 transport.[[State]]"closed""failed"拒绝 p,原因为 InvalidStateError 并中止步骤。

      2. stream 为对 internalStreamtransportsendGroupsendOrder 创建WebTransportSendStream

      3. 解析 p,值为 stream

  9. 返回 p

createSendGroup()

创建一个 WebTransportSendGroup

当调用 createSendGroup() 方法时,用户代理必须运行以下步骤:
  1. 如果 this.[[State]]"closed""failed"抛出 InvalidStateError

  2. 返回通过对 this 创建 WebTransportSendGroup 的结果。

6.5. 过程

清理一个 WebTransport transport 并传入 error 及可选的 closeInfo,请执行以下步骤:
  1. sendStreamstransport.[[SendStreams]] 的副本。

  2. receiveStreamstransport.[[ReceiveStreams]] 的副本。

  3. outgoingDatagramWritablestransport.[[Datagrams]].[[Writables]]

  4. incomingDatagramstransport.[[Datagrams]].[[Readable]]

  5. readytransport.[[Ready]]

  6. closedtransport.[[Closed]]

  7. incomingBidirectionalStreamstransport.[[IncomingBidirectionalStreams]]

  8. incomingUnidirectionalStreamstransport.[[IncomingUnidirectionalStreams]]

  9. transport.[[SendStreams]] 设为空的 集合

  10. transport.[[ReceiveStreams]] 设为空的 集合

  11. transport.[[Datagrams]].[[OutgoingDatagramsQueue]] 设为空的 队列

  12. transport.[[Datagrams]].[[IncomingDatagramsQueue]] 设为空的 队列

  13. 如果给定 closeInfo,则将 transport.[[State]] 设为 "closed"。 否则,将 transport.[[State]] 设为 "failed"

  14. 对于 sendStreams 中每一个 stream,执行以下步骤:

    1. 如果 stream.[[PendingOperation]] 不为 null,拒绝 stream.[[PendingOperation]] ,原因为 error

    2. 使流出错 stream, 原因为 error

  15. 对于 receiveStreams 中每一个 stream使 stream 出错, 原因为 error

    注:脚本作者可以注入代码在 Promise 解析时同步运行。因此,从此处开始,不要再触碰 transport,因为它可能会被脚本不可预测地更改。 调用此过程的逻辑同样适用。

  16. 如果给定 closeInfo,则:

    1. 解析 closed,值为 closeInfo

    2. 断言:ready 已经 已定

    3. 关闭 incomingBidirectionalStreams

    4. 关闭 incomingUnidirectionalStreams

    5. 对于 outgoingDatagramWritables 中每一个 writable关闭 writable

    6. 关闭 incomingDatagrams

  17. 否则:

    1. 拒绝 closed,原因为 error

    2. closed.[[PromiseIsHandled]] 设为 true。

    3. 拒绝 ready,原因为 error

    4. ready.[[PromiseIsHandled]] 设为 true。

    5. 使 incomingBidirectionalStreams 出错,原因为 error

    6. 使 incomingUnidirectionalStreams 出错,原因为 error

    7. 对于 outgoingDatagramWritables 中的每一个 writable使其出错,原因为 error

    8. 使 incomingDatagrams 出错,原因为 error

排队一个网络任务,给定 WebTransport transport 和一系列步骤 steps,请按以下步骤执行:

  1. 网络任务源 上,使用 transport关联全局对象,排队并运行 steps

6.6. 并非由客户端发起的会话终止

每当与 WebTransport transport 关联的 WebTransport sessionterminated 时,可选地带有 codereasonBytes,运行以下步骤:
  1. transport 排队一个网络任务并运行以下步骤:

    1. 如果 transport.[[State]]"closed""failed",中止这些步骤。

    2. error 为新创建的 WebTransportError,其 source"session"

    3. closeInfo 为一个新的 WebTransportCloseInfo 对象。

    4. 如果提供了 code,则将 closeInfocloseCode 设为 code

    5. 如果提供了 reasonBytes,则将 closeInforeason 设为 reasonBytesUTF-8 解码值。

      注: reasonBytes 不包含任何语言或方向元信息。 在展示该值时可以用 First-strong 启发式来判断方向。

    6. 清理 transport,并传入 errorcloseInfo

每当 WebTransport transport底层连接 发生连接错误时, 运行以下步骤:
  1. transport 排队一个网络任务并运行以下步骤:

    1. 如果 transport.[[State]]"closed""failed",中止这些步骤。

    2. error 为新创建的 WebTransportError,其 source"session"

    3. 清理 transport,传入 error

6.7. 上下文清理步骤

本规范将 上下文清理步骤 定义为以下步骤,传入 WebTransport transport

  1. 如果 transport.[[State]]"connected",则:

    1. transport.[[State]] 设为 "failed"

    2. 并行终止 transport.[[Session]]

    3. 排队一个网络任务,携带 transport,以运行以下步骤:

      1. error 为新创建WebTransportError, 其 source"session"

      2. 清理 transport,携带 error

  2. 如果 transport.[[State]]"connecting",将 transport.[[State]] 设为 "failed"

    这也需要在 worker 中完成。参见 #127whatwg/html#6731

6.8. 垃圾回收

WebTransport 对象的 [[State]]"connecting" 时,如果 [[IncomingBidirectionalStreams]][[IncomingUnidirectionalStreams]]、 任意 WebTransportReceiveStream, 或 [[Datagrams]].[[Readable]] 处于锁定状态,或 readydraining、 或 closed promise 被监视时,禁止被垃圾回收。

WebTransport 对象的 [[State]]"connected" 时,如果 [[IncomingBidirectionalStreams]][[IncomingUnidirectionalStreams]]、 任意 WebTransportReceiveStream, 或 [[Datagrams]].[[Readable]] 处于锁定状态,或 drainingclosed 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;
  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 session 可以被池化,即其 底层连接 可以与其他 WebTransport session 共享。

requireUnreliable类型为 boolean,默认值为false

当设置为 true 时,如果无法建立 HTTP/3 connection,则 WebTransport session 不得在 HTTP/2 connection 上建立。

serverCertificateHashes类型为 sequence<WebTransportHash>,默认值为 []

此选项仅支持使用独占连接(dedicated connections)的传输协议。 对于不支持此特性的传输协议,若该字段非空,将抛出 NotSupportedError 异常。

如支持且非空,用户代理只有在可以成功 验证证书哈希 并满足 自定义证书要求 时才认为服务器证书可信。用户代理会忽略任何使用未知 algorithm 的哈希。若 为空,用户代理应采用与普通 fetch 操作一致的证书校验流程。

此选项不能与 allowPooling 一同使用。

congestionControl类型为 WebTransportCongestionControl, 默认值为"default"

可选地指定应用偏好的拥塞控制算法(如偏好高吞吐量或低延迟),在通过此连接发送数据时供用户代理参考。

此配置项属于风险特性,因为在当前主流浏览器中,没有任何拥塞控制算法实现低延迟优化。

anticipatedConcurrentIncomingUnidirectionalStreams类型为 unsigned short,可为 null,默认值为 null

允许应用可选地声明其预期服务器将创建多少并发 传入单向流。用户代理初始必须允许来自服务器的至少 100 个 传入单向流。 若非 null,用户代理在与服务器协商时应尽量减少 RTT,可将 [[AnticipatedConcurrentIncomingUnidirectionalStreams]] 纳入考虑。

anticipatedConcurrentIncomingBidirectionalStreams类型为 unsigned short,可为 null,默认值为 null

允许应用可选地声明其预期服务器将创建多少并发 双向流。用户代理必须初始允许服务器创建至少 100 个双向流。 若非 null,用户代理在与服务器协商时应尽量减少 RTT,可将 [[AnticipatedConcurrentIncomingBidirectionalStreams]] 纳入考虑。

protocols类型为 sequence<DOMString>,默认值为[]

可选的应用层协议名称数组。应用层协议的选择以及是否告知客户端由服务器自行决定。如果未提供合适协议,服务器可能会拒绝请求。

datagramsReadableType 类型为 ReadableStreamType

可选,指定应用偏好使用 可读字节流 作为传入数据报的流类型。否则,默认使用 可读流

注:可读流 与数据报语义兼容, 可读字节流则不适用。数据报是零字节或多个字节的独立消息,可乱序到达,并非无分割的字节流;空数据报会丢失,且 min 会丢失消息边界。

计算证书哈希,给定 certificate,执行以下步骤:
  1. certcertificate,其表示为 [RFC5280] 中定义的证书消息的 DER 编码。

  2. 计算 cert 的 SHA-256 哈希并返回计算得到的值。

验证证书哈希,给定 certificate chain 和一个哈希数组 hashes,执行以下步骤:
  1. certificatecertificate chain 中的第一个证书(叶子证书)。

  2. referenceHash 为使用 certificate计算证书哈希的结果。

  3. 对于 hashes 中的每个哈希 hash

    1. hash.value 非 null 且 hash.algorithm 与 "sha-256" 为ASCII 不区分大小写匹配:

      1. hashValuehash.value 所表示的字节序列。

      2. hashValue 等于 referenceHash,则返回 true。

  4. 返回 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是一个基本参数字典,它会影响 createUnidirectionalStreamcreateBidirectionalStream 以及 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创建的 createUnidirectionalStreamcreateBidirectionalStream的行为。

dictionary WebTransportSendStreamOptions : WebTransportSendOptions {
  boolean waitUntilAvailable = false;
};

该字典应具有以下属性:

waitUntilAvailable, 类型为 boolean, 默认为 false

如果为 true,则 createUnidirectionalStreamcreateBidirectionalStream 调用返回的 promise 将不会解决, 直到底层连接具有足够的流量控制信用额度来创建流,或者连接达到无法再创建传出流的状态。如果为 false,则如果在调用时没有可用的流量控制窗口,则将拒绝该 promise。

6.13. WebTransportConnectionStats 字典

WebTransportConnectionStats 字典包含关于WebTransport 会话底层连接之 WebTransport 特定统计信息。

Note: 当使用池化时,多个池化在同一connection上的WebTransport 会话都会收到相同的信息,即这些信息会在持有相同network partition key的池化sessions间披露。

Note: 任何不可用的统计项将在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 long

底层连接上传送 bytesSent 数量有效载荷字节时的帧与重传开销。

bytesAcknowledged 类型为 unsigned long long

通过 QUIC 的 ACK 机制,在底层连接上被服务器确认收到的有效载荷字节数。不包括任何帧开销。

注:通常落后于 bytesSent,但由于丢包可能永久少于它。

bytesAcknowledgedWebTransportConnectionStats 中被工作组认为由于可实现性原因属于风险特性。
packetsSent类型为 unsigned long long

底层连接上发送的数据包数量,包括已确定丢失的包。

bytesLost类型为 unsigned long long

底层连接上丢失的字节数(不会单调递增,因为被判定为丢失的包后来仍可能收到)。 不包括 UDP 或其他外部帧开销。

packetsLost类型为 unsigned long long

底层连接上丢失的包数(不会单调递增,因为被判定为丢失的包后来仍可能收到)。

bytesReceived类型为 unsigned long long

底层连接上接收的总字节数(包括流的重复数据)。不包括 UDP 或其他外部帧开销。

packetsReceived类型为 unsigned long long

底层连接上接收的总数据包数量,包括不可处理的数据包。

smoothedRtt类型为 DOMHighResTimeStamp

该连接当前观测到的平滑往返时间(RTT), 参见[RFC9002] 第5.3节

rttVariation类型为 DOMHighResTimeStamp

该连接当前观测到的往返时间样本均方差,参见[RFC9002] 第5.3节

minRtt类型为 DOMHighResTimeStamp

整个连接期间观测到的最小往返时间,参见[RFC9002] 第5.2节

estimatedSendRate 类型为 unsigned long long,可为 null,默认值为 null

用户代理队列数据的预计发送速率,单位为比特/秒。该速率适用于所有共享同一 WebTransport session 的流和数据报,由拥塞控制算法(可能由 congestionControl 选择)计算。该估算排除了帧开销,代表应用有效负载的发送速率。如无可用估算,成员值必须为 null。即使前次有值,后续也可为 null

atSendCapacity类型为 boolean,默认值为false

为 false 时,estimatedSendRate 可能受应用限制,即应用实际发送远低于拥塞控制允许的数据量。在受应用限制期间,拥塞控制器可能无法准确估算可用网络容量。

为 true 时,表示应用已按网络容量发送数据,此时 estimatedSendRate 反映了应用可用的网络容量。

atSendCapacitytrue 时,estimatedSendRate 体现上限。只要应用发送速率持续,该估值会随网络变化自动调整,但即使 atSendCapacity 为 true,estimatedSendRate 也允许为 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 long

由于应用未从 datagramsreadable 读取数据,新的数据报溢出接收队列前被丢弃的数据报数量。

expiredIncoming 类型为 unsigned long long

由于在被从 datagramsreadable 读取前已超过 incomingMaxAge 而被丢弃的入站数据报数量。

expiredOutgoing 类型为 unsigned long long

排队等待发送的数据报,由于在能够发送前已超过 outgoingMaxAge 而被丢弃的数量。

lostOutgoing 类型为 unsigned long long

已发送的数据报中根据 [RFC9002] 第6.1节 被判定为丢失的数据报数量。

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

getter 步骤如下:

  1. 返回 this[[SendGroup]]

setter 步骤,给定 value

  1. 如果 value 非 null,且 value.[[Transport]] 不等于 this.[[Transport]]抛出 InvalidStateError

  2. this.[[SendGroup]] 设为 value

sendOrder 类型为 long long

getter 步骤如下:

  1. 返回 this[[SendOrder]]

setter 步骤,给定 value

  1. this.[[SendOrder]] 设为 value

7.2. 方法

getStats()

收集该 WebTransportSendStream 性能相关的统计信息, 并以异步方式报告结果。

调用 getStats 时,用户代理必须按如下步骤执行:

  1. p 为一个新的 promise。

  2. 并行 执行以下步骤:

    1. gatheredStats 为专用于 this WebTransportSendStream ,用于准确填充 WebTransportSendStreamStats 字典成员的统计信息 列表

    2. transport 排队一个网络任务,以执行以下步骤:

      1. stats 为一个 新的 WebTransportSendStreamStats 对象。

      2. 对于用户代理希望暴露的 stats 的每个 成员 member设置 membergatheredStats 中对应的 条目

      3. stats 完成(resolve) p

  3. 返回 p

getWriter()

此方法的实现必须与继承自 getWriter 的实现方式一致,区别在于不创建 WritableStreamDefaultWriter, 而是要 创建 一个以 this 为参数的 WebTransportWriter

7.3. 内部插槽

WebTransportSendStream 拥有以下内部槽。

内部槽 描述 (非规范性)
[[InternalStream]] 一个出站单向双向 WebTransport 流
[[PendingOperation]] 表示正在挂起写入或关闭操作的 promise,或为 null。
[[Transport]] 拥有此 WebTransportWebTransportSendStream
[[SendGroup]] 可选的 WebTransportSendGroup, 或 null。
[[SendOrder]] 可选的发送顺序号,默认值为 0。
[[AtomicWriteRequests]] promise 的有序集合, 用于追踪在底层 sink 队列中要原子提交的写入请求子集。
[[BytesWritten]] 已经写入流的字节数。
[[CommittedOffset]] 流中的偏移量,记录即使在流中止发送时也会被交付给对端的字节数; 参见 [RELIABLE-RESET]

7.4. 过程

创建 一个WebTransportSendStream, 使用一个出向单向双向 WebTransport 流 internalStream、一个WebTransport transportsendGroupsendOrder,请执行以下步骤:

  1. stream为一个新的 WebTransportSendStream,具有:

    [[InternalStream]]

    internalStream

    [[PendingOperation]]

    null

    [[Transport]]

    transport

    [[SendGroup]]

    sendGroup

    [[SendOrder]]

    sendOrder

    [[AtomicWriteRequests]]

    一个空的 ordered set,其元素是 promise。

    [[BytesWritten]]

    0

    [[CommittedOffset]]

    0

  2. writeAlgorithm为一个动作,该动作将chunk写入stream,给定chunk

  3. closeAlgorithm为一个动作,该动作关闭stream

  4. abortAlgorithm为一个动作,该动作使用reason中止stream,给定reason

  5. 设置stream,其中writeAlgorithm设置为writeAlgorithmcloseAlgorithm设置为closeAlgorithmabortAlgorithm设置为abortAlgorithm

  6. abortSignalstream的[[controller]].[[abortController]].[[signal]]。

  7. 以下步骤添加到abortSignal

    1. pendingOperationstream[[PendingOperation]]

    2. 如果pendingOperation为 null,则中止这些步骤。

    3. stream[[PendingOperation]]设置为 null。

    4. reasonabortSignal中止原因

    5. promise中止流与reason的结果。

    6. promise实现后,使用reason拒绝pendingOperation

  8. stream附加到transport[[SendStreams]]

  9. 返回stream

write chunkWebTransportSendStream stream,执行以下步骤:
  1. transportstream.[[Transport]]

  2. 如果 chunk 不是 BufferSource, 返回 一个拒绝的 promise,错误为 TypeError

  3. promise 为新建的 promise。

  4. byteschunk 所表示的 字节序列的副本。

  5. stream.[[PendingOperation]] 设为 promise

  6. inFlightWriteRequeststream.inFlightWriteRequest

  7. 如果 stream.[[AtomicWriteRequests]] 包含 inFlightWriteRequest,则令 atomic 为 true,否则为 false。

  8. 并行执行以下步骤:

    1. 如果 atomic 为 true 且当前 流控窗口不足以完整发送 bytes, 则终止后续步骤,并队列网络任务,传入 transport,执行:

      1. stream.[[PendingOperation]] 设为 null。

      2. 中止该 stream 上的所有原子写请求

    2. 否则,发送 bytesstream.[[InternalStream]], 并等待操作完成。 此发送可能与前面排队好的流和数据报的发送交错进行,也可能与尚未排队将要发送的数据交错进行。

      用户代理可以有缓冲区提升传输性能。此缓冲区应有固定上限,以便向 WebTransportSendStream 的使用者传递背压信息。

      该发送操作在以下情况下必须饿死(starve):直到同组中所有 [[SendGroup]][[SendOrder]] 值更高 且未出错且未被 流控阻塞的stream所有已排队字节都被发送后,当前流才能发送。

      此处的 stream.[[SendOrder]] 可被并行访问,用户代理应该监听这些值的动态变化,具体细节为 实现自定

      注意: 重传的发送顺序为 实现自定,但强烈建议用户代理优先重传 [[SendOrder]] 值较高的数据。

      除此之外,除非受 流控错误影响,否则该发送不可被饿死。

      用户代理应尽量公平分配带宽给所有未饿死的流。

      注意: 这里所说的公平由 实现自定

    3. 如果上一步因网络错误失败,则中止后续步骤。

      注意: 此处不会拒绝 promise,因网络错误会在其他地方处理,相关步骤会拒绝 stream.[[PendingOperation]]

    4. 否则,队列网络任务,传入 transport,执行:

      1. stream.[[PendingOperation]] 设为 null。

      2. bytes 的长度加到 stream.[[BytesWritten]] 上。

      3. 如果 stream.[[AtomicWriteRequests]] 包含 inFlightWriteRequest移除 inFlightWriteRequest

      4. resolve promise,值为 undefined。

  9. 返回 promise

注意: 本算法返回的 promise(或 write(chunk)) 被 确认(fulfilled) 并不意味着该 chunk 已被服务器确认(acked), 很可能只是被加入了缓冲区。要确保 chunk 已送达服务器,服务器还需要应用层自己的确认消息。

close 一个 WebTransportSendStream stream,执行以下步骤:
  1. transportstream.[[Transport]]

  2. promise 为新建的 promise。

  3. 移除 streamtransport.[[SendStreams]] 中。

  4. stream.[[PendingOperation]] 设为 promise

  5. 并行执行以下步骤:

    1. 发送 FIN 到 stream.[[InternalStream]], 并等待操作完成。

    2. 等待 stream.[[InternalStream]] 进入 "all data committed" 状态。[QUIC]

    3. 队列网络任务,传入 transport,执行:

      1. stream.[[PendingOperation]] 设为 null。

      2. resolve promise,值为 undefined。

  6. 返回 promise

abort 一个 WebTransportSendStream stream,并传入 reason,执行以下步骤:
  1. transportstream.[[Transport]]

  2. promise 为新建的 promise。

  3. code 为 0。

  4. 移除 streamtransport.[[SendStreams]] 中。

  5. 如果 reasonWebTransportErrorreason.[[StreamErrorCode]] 不为 null,则将 code 设为 reason.[[StreamErrorCode]]

  6. 如果 code < 0,则置为 0。

  7. 如果 code > 4294967295,则置为 4294967295。

  8. committedOffsetstream.[[CommittedOffset]]

    注意: code 的有效取值为 0 到 4294967295(含)。如果 底层连接 使用 HTTP/3,code 会按 [WEB-TRANSPORT-HTTP3] 描述编码到 [0x52e4a40fa8db, 0x52e5ac983162] 区间。

  9. 并行执行以下步骤:

    1. 中止发送 stream.[[InternalStream]],传入 codecommittedOffset

    2. 队列网络任务,传入 transportresolve promise,值为 undefined。

  10. 返回 promise

中止所有原子写请求WebTransportSendStream stream,执行以下步骤:
  1. writeRequestsstream.writeRequests

  2. requestsToAbortstream.[[AtomicWriteRequests]]

  3. 如果 writeRequests 包含 存在于 requestsToAbort 之外的 promise, 则报错 stream,错误为 AbortError,并中止这些步骤。

  4. 清空 stream.[[AtomicWriteRequests]]

  5. 对于 requestsToAbort 中的每个 promise拒绝 promise,错误为 AbortError

  6. 并行对于 requestsToAbort 中的每个 promise,中止与 promise 关联的 数据发送

7.5. 接收来自服务器的中止信号

每当与 WebTransportSendStream stream 关联的 WebTransport stream 从服务器收到 receiving aborted 信号时,执行以下步骤:
  1. transportstream.[[Transport]]

  2. code 为附加在 receiving aborted 信号上的应用协议错误码。

    Note: code 的有效取值为 0 到 4294967295(含)之间的整数。若底层连接使用 HTTP/3,则该代码将被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 范围内的数字,如 [WEB-TRANSPORT-HTTP3] 所述。

  3. Queue a network task,携带 transport,以运行以下步骤:

    1. 如果 transport.[[State]]"closed""failed",则中止这些步骤。

    2. Remove streamtransport.[[SendStreams]]

    3. error 为新创建WebTransportError, 其 source"stream",且 streamErrorCodecode

    4. 如果 stream.[[PendingOperation]] 非 null,则以 error 拒绝 stream.[[PendingOperation]]

    5. 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 可以在创建时或通过赋值其 sendGroup 属性,被归为最多一个 分组到一个 WebTransportSendGroup。默认情况下,它们是 未分组的。

用户代理在为发送 WebTransportSendStream 分配带宽时, 会将 WebTransportSendGroup 视为同等。 每个 WebTransportSendGroup 还会为评估 sendOrder 编号建立一个独立的数字空间。

[Exposed=(Window,Worker), SecureContext]
interface WebTransportSendGroup {
  Promise<WebTransportSendStreamStats> getStats();
};

一个 WebTransportSendGroup 总是通过 创建过程生成。

8.1. 方法

getStats()

聚合属于 sendGroup 下所有 WebTransportSendStream 的统计信息,并以异步方式报告结果。

调用 getStats 时,用户代理必须运行如下步骤:

  1. p 为一个新的 promise。

  2. streams 为所有 WebTransportSendStream, 其 [[SendGroup]] 等于 this

  3. 如下 并行运行:

    1. gatheredStatsstreams 中所有流的聚合统计信息 列表, 用于准确填充 WebTransportSendStreamStats字典成员

    2. 为 transport 排队一个网络任务,以运行如下步骤:

      1. stats 为一个 新的 WebTransportSendStreamStats 对象。

      2. 对于用户代理希望暴露的 stats 的每个 成员 member设置 membergatheredStats 中对应的条目

      3. 用 stats resolve p

  4. 返回 p

8.2. 内部槽

一个 WebTransportSendGroup 拥有以下内部槽。

内部槽 描述 (非规范性)
[[Transport]] 拥有此 WebTransport 对象的 WebTransportSendGroup

8.3. 过程

创建 WebTransportSendGroup, 并指定 WebTransport transport,请执行以下步骤:

  1. sendGroup新建WebTransportSendGroup, 其中:

    [[Transport]]

    transport

  2. 返回 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 时,用户代理必须运行以下步骤:

  1. p 为一个新的 promise。

  2. 并行方式运行以下步骤:

    1. gatheredStats 为针对 this WebTransportReceiveStream 的统计信息列表,用于准确填充 WebTransportReceiveStreamStats字典成员

    2. 排队一个网络任务,携带 transport,以运行以下步骤:

      1. stats新建WebTransportReceiveStreamStats 对象。

      2. 对于用户代理希望暴露的 stats 中每个成员 member设置 membergatheredStats 中对应的条目

      3. 解析 p,携带 stats

  3. 返回 p

9.2. 内部槽

WebTransportReceiveStream 拥有以下内部槽。

内部槽 描述(非规范性
[[InternalStream]] 一个入向单向双向 WebTransport 流
[[Transport]] 拥有此 WebTransport 对象的 WebTransportReceiveStream

9.3. 过程

创建一个 WebTransportReceiveStream, 在具有一个 入站单向双向WebTransport stream internalStream 和一个 WebTransport transport 的情况下,执行以下步骤:

  1. stream 为一个新建WebTransportReceiveStream,其包含:

    [[InternalStream]]

    internalStream

    [[Transport]]

    transport

  2. pullAlgorithm 为一个操作,用于从 stream拉取字节

  3. cancelAlgorithm 为一个操作,给定 reason,用于以 reason取消 stream

  4. 以字节读取支持进行设置 stream,其中 pullAlgorithm 设为 pullAlgorithm,且 cancelAlgorithm 设为 cancelAlgorithm

  5. stream 追加到 transport.[[ReceiveStreams]]

  6. 返回 stream

要从一个 WebTransportReceiveStream stream拉取字节,执行以下步骤。

  1. transportstream.[[Transport]]

  2. internalStreamstream.[[InternalStream]]

  3. promise 为一个新的 promise。

  4. bufferoffsetmaxBytes 为 null。

  5. 如果 stream 的用于 stream当前 BYOB 请求视图不为 null:

    1. offset 设为 stream当前 BYOB 请求视图.[[ByteOffset]]。

    2. maxBytes 设为 stream当前 BYOB 请求视图字节长度

    3. buffer 设为 stream当前 BYOB 请求视图底层缓冲区

  6. 否则:

    1. offset 设为 0。

    2. maxBytes 设为一个实现自定义大小。

    3. buffer 设为一个新建ArrayBuffer, 大小为 maxBytes。如果分配该 ArrayBuffer 失败,返回一个被拒绝的 promise,携带 RangeError

  7. 并行方式运行以下步骤:

    1. 将字节写入internalStream读取buffer中,偏移为 offset,最多 maxBytes 字节。等待直到至少读取到一个字节或接收到 FIN。令 read 为已读取的字节数,令 hasReceivedFIN 表示是否伴随 FIN。

      用户代理可以具有一个缓冲区以提升传输性能。该缓冲区应具有固定的上限,以将背压信息传递到服务器。

      Note: 该操作可能在未填满整个 buffer 时返回。

    2. 如果前一步失败,则中止剩余步骤。

      Note: 我们在此不拒绝 promise,因为我们在其他地方处理网络错误,而那些步骤会使 stream 出错,从而拒绝任何等待此次拉取的读取请求。

    3. 排队一个网络任务,携带 transport,以运行以下步骤:

      Note: 如果上述缓冲区在运行此过程的事件循环中可用,则以下步骤可能会立即运行。

      1. 如果 read > 0:

        1. view 设为一个新的 Uint8Array, 以 bufferoffsetread 构造。

        2. 入队 viewstream

      2. 如果 hasReceivedFIN 为 true:

        1. transport.[[ReceiveStreams]] 中移除 stream

        2. 关闭 stream

      3. 解析 promise 为 undefined。

  8. 返回 promise

要以 reason取消一个 WebTransportReceiveStream stream,执行以下步骤。

  1. transportstream.[[Transport]]

  2. internalStreamstream.[[InternalStream]]

  3. promise 为一个新的 promise。

  4. code 为 0。

  5. 如果 reason 是一个 WebTransportErrorreason.[[StreamErrorCode]] 不为 null,则将 code 设为 reason.[[StreamErrorCode]]

  6. 如果 code < 0,则将 code 设为 0。

  7. 如果 code > 4294967295,则将 code 设为 4294967295。

    Note: code 的有效取值为 0 到 4294967295(含)。如果底层连接使用 HTTP/3,则该代码将被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 范围内的数字,如 [WEB-TRANSPORT-HTTP3] 所述。

  8. transport.[[SendStreams]] 中移除 stream

  9. 并行方式运行以下步骤:

    1. internalStream 上以 code中止接收

    2. 排队一个网络任务,携带 transport,以运行以下步骤:

      Note: 如果上述缓冲区在运行此过程的事件循环中可用,则以下步骤可能会立即运行。

      1. transport.[[ReceiveStreams]] 中移除 stream

      2. 解析 promise 为 undefined。

  10. 返回 promise

9.4. 接收来自服务器的发送中止信号

每当与 WebTransport stream 关联的 WebTransportReceiveStream stream 从服务器收到 sending aborted 信号时,执行以下步骤:
  1. transportstream[[Transport]]

  2. code 为附加在 sending aborted 信号上的应用协议错误码。

    Note: code 的有效取值为 0 到 4294967295(含)。如果 underlying connection 使用 HTTP/3,则该代码将被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 范围内的数字,如 [WEB-TRANSPORT-HTTP3] 所述。

  3. Queue a network task,携带 transport 以运行以下步骤:

    1. 如果 transport[[State]]"closed""failed",则中止这些步骤。

    2. Removestreamtransport[[ReceiveStreams]] 中移除。

    3. error 为新创建WebTransportError, 其 source"stream",且 streamErrorCodecode

    4. Errorerrorstream 出错。

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 stream internalStream、一个 WebTransport 对象 transport,以及一个 sendOrder,执行以下步骤。
  1. readable创建一个 WebTransportReceiveStream 的结果,给定 internalStreamtransport

  2. writable创建一个 WebTransportSendStream 的结果,给定 internalStreamtransport,以及 sendOrder

  3. stream 为一个新建WebTransportBidirectionalStream,其包含:

    [[Readable]]

    readable

    [[Writable]]

    writable

    [[Transport]]

    transport

  4. 返回 stream

11. WebTransportWriter 接口

WebTransportWriterWritableStreamDefaultWriter 的子类,添加了两个方法。

一个 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 时,用户代理必须执行以下步骤:

  1. p 为在 write(chunk) 上调用 WritableStreamDefaultWriter 的结果,并传递 chunk

  2. 附加 pstream.[[AtomicWriteRequests]]

  3. 返回对 p 的结果进行反应 (响应),并执行以下步骤:

    1. 如果 stream.[[AtomicWriteRequests]] 包含 p移除 p

    2. 如果 p 因原因 r 被拒绝,则返回 一个被拒绝的 promise,理由为 r

    3. 返回 undefined。

commit()

commit 方法会将流的 [[CommittedOffset]] 更新为已写入该流的字节数 ([[BytesWritten]])。 这样可以确保这些字节能够可靠地传递给对端,即使写入被中止,导致流中止发送。 该机制参见 [RELIABLE-RESET]

注意: 这无法保证连接失败时的数据可达,仅能保证当流中止发送时的可靠传递。

当为 stream 调用 commit 时,用户代理必须执行以下步骤:

  1. transport 设为 stream.[[Transport]]

  2. stream.[[CommittedOffset]] 设置为 stream.[[BytesWritten]] 的值。

11.2. 过程

创建一个 WebTransportWriter, 使用一个 WebTransportSendStream stream,执行以下步骤:

  1. writer 为一个新建WebTransportWriter

  2. 运行 new WritableStreamDefaultWriter(stream) 构造步骤,其中将 writer 作为 this,stream 作为构造参数。

  3. 返回 writer

12. WebTransportError 接口

WebTransportErrorDOMException 的子类,表示:

[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 source = "stream";
  [Clamp] unsigned long? streamErrorCode = null;
};

enum WebTransportErrorSource {
  "stream",
  "session",
};

12.1. 内部插槽

一个 WebTransportError 具有以下内部槽。

内部槽 描述 (非规范性)
[[Source]] 一个 WebTransportErrorSource ,指示此错误的来源。
[[StreamErrorCode]] 此错误的应用协议错误代码,或 null。

12.2. 构造函数

new WebTransportError(message, options) 的构造步骤如下:

  1. thisname 设为 "WebTransportError"

  2. thismessage 设为 message

  3. this 的内部槽设置如下:

    [[Source]]

    options.source

    [[StreamErrorCode]]

    options.streamErrorCode

    Note: 此名称没有映射到旧代码,因此 thiscode 为 0。

12.3. 属性

source类型为 WebTransportErrorSource,只读

getter 步骤为返回 this[[Source]]

streamErrorCode类型为 unsigned long,只读,可为 null

getter 步骤为返回 this[[StreamErrorCode]]

12.4. 序列化

WebTransportError 对象是 可序列化对象。 它们的 序列化步骤,给定 valueserialized,如下:

  1. 运行 DOMException序列化步骤,给定 valueserialized

  2. serialized.[[Source]] 设置为 value.[[Source]]

  3. serialized.[[StreamErrorCode]] 设置为 value.[[StreamErrorCode]]

它们的 反序列化步骤,给定 serializedvalue,如下:

  1. 运行 DOMException反序列化步骤,给定 serializedvalue

  2. value.[[Source]] 设置为 serialized.[[Source]]

  3. 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) 中止发送到 STREAM,带 httpErrorCode 和与该 [[CommittedOffset]] 对应的 offset,以及任何流头部;参见 [RELIABLE-RESET]
writable.close() 发送 STREAM 并设置 FIN 位
writable.getWriter().write(chunk)() 发送 STREAM
writable.getWriter().close() 发送 STREAM 并设置 FIN 位
writable.getWriter().abort(error) 中止发送到 STREAM,带 httpErrorCode 和与该 [[CommittedOffset]] 对应的 offset,以及任何流头部;参见 [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 位 (await readable.getReader().read()).done
接收 RESET_STREAM,带 httpErrorCode 报错 readable ,错误为 streamErrorCode
会话正常终止,带 closeInfo
(await wt.closed).closeInfo, 并且 报错所有未关闭的流
网络错误
(await wt.closed) 拒绝,并且 报错所有未关闭的流

注意:[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 效果
会话draining await wt.draining

如果底层连接使用 HTTP/2,则适用 [WEB-TRANSPORT-HTTP2] 中的协议行为。注意,与 HTTP/3 不同,流错误码不需要转换为 HTTP 错误码,反之亦然。

API 方法 HTTP/2 协议行为
writable.abort(error) 中止发送到 WT_STREAM,带 error
writable.close() 发送 WT_STREAM 并设置 FIN 位
writable.getWriter().write() 发送 WT_STREAM
writable.getWriter().close() 发送 WT_STREAM 并设置 FIN 位
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
接收 WT_STREAM 并设置 FIN 位 (await readable.getReader().read()).done
接收 WT_RESET_STREAM,带 error 报错 readable ,错误为 streamErrorCode
会话正常终止,带 closeInfo
(await wt.closed).closeInfo, 并且 报错所有未关闭的流
网络错误
(await wt.closed) 拒绝,并且 报错所有未关闭的流
会话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] 中所述,包括:

  1. 确保远程服务器知晓正在使用 WebTransport 协议,并确认远程服务器愿意使用 WebTransport 协议。[WEB-TRANSPORT-HTTP3] 通过 ALPN 与 [RFC7301] 的组合、HTTP/3 的设置以及 :protocol伪首部字段 来标识 WebTransport 协议。[WEB-TRANSPORT-HTTP2] 则通过 ALPN、HTTP/2 的设置以及 :protocol伪首部字段 来标识 WebTransport 协议。

  2. 允许服务器根据发起传输会话的资源的来源来过滤连接。会话建立请求中的 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 bytes of 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 datagram of wt.datagrams.readable) {
    // 处理数据报
  }
}

15.4. 使用 BYOB 读取器接收数据报

此部分为非规范性内容。

由于 datagrams可读字节流,你可以为它们获取一个 BYOB 读取器, 这样可以更精确地控制缓冲区分配,从而避免复制。下例将 datagram 读入一个 64 Kibibytes 的内存缓冲区中。

const wt = new WebTransport(url);

for await (const datagram of 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 bytes of 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 readable of wt.incomingUnidirectionalStreams) {
    // 使用 IFFE 单独消费流,报告每个流的错误
    ((async () => {
      try {
        for await (const bytes of readable) {
          processTheData(bytes);
        }
      } catch (e) {
        console.error(e);
      }
    })());
  }
}

解码也可以通过管道传输到新的 WritableStreams,例如使用 TextDecoderStream。 此示例假定文本输出不应交错,因此一次只读取一个流。

async function receiveText(url, createWritableStreamForTextData) {
  const wt = new WebTransport(url);
  for await (const readable of 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 data of 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 readable of 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 data of 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 接口,并已为在本规范中使用而进行了调整。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性参考文献

[CSP3]
Mike West;Antonio Sartori。内容安全策略 第3级。2025年11月6日。工作草案(WD)。URL:https://www.w3.org/TR/CSP3/
[DOM]
Anne van Kesteren。DOM 标准。持续标准(Living Standard)。URL:https://dom.spec.whatwg.org/
[ECMASCRIPT-6.0]
Allen Wirfs-Brock。ECMA-262 第六版,ECMAScript 2015 语言规范。2015年6月。标准。URL:http://www.ecma-international.org/ecma-262/6.0/index.html
[ENCODING]
Anne van Kesteren。编码 标准。持续标准。URL:https://encoding.spec.whatwg.org/
[FETCH]
Anne van Kesteren。Fetch 标准。持续标准。URL:https://fetch.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss。高精度时间。2024年11月7日。工作草案(WD)。URL:https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren;等。HTML 标准。持续标准。URL:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren;Domenic Denicola。Infra 标准。持续标准。URL:https://infra.spec.whatwg.org/
[QUIC]
Jana Iyengar;Martin Thomson。QUIC:一种基于UDP的多路复用与安全传输。拟标准(Proposed Standard)。URL:https://www.rfc-editor.org/rfc/rfc9000
[QUIC-DATAGRAM]
Tommy Pauly;Eric Kinnear;David Schinazi。QUIC的不可靠数据报扩展。拟标准。URL:https://www.rfc-editor.org/rfc/rfc9221
[RFC2119]
S. Bradner。在RFC中用于指示需求级别的关键词。1997年3月。最佳现行实践(BCP)。URL:https://datatracker.ietf.org/doc/html/rfc2119
[RFC3279]
L. Bassham;W. Polk;R. Housley。互联网 X.509 公钥基础设施证书与证书吊销列表(CRL)算法与标识配置文件。2002年4月。拟标准。URL:https://www.rfc-editor.org/rfc/rfc3279
[RFC5280]
D. Cooper;等。互联网 X.509 公钥基础设施证书与证书吊销列表(CRL)配置文件。2008年5月。拟标准。URL:https://www.rfc-editor.org/rfc/rfc5280
[RFC8174]
B. Leiba。RFC 2119 关键词中大小写不一致的歧义。2017年5月。最佳现行实践。URL:https://www.rfc-editor.org/rfc/rfc8174
[RFC8422]
Y. Nir;S. Josefsson;M. Pegourie-Gonnard。椭圆曲线密码(ECC)套件用于TLS 1.2及更早版本。2018年8月。拟标准。URL:https://www.rfc-editor.org/rfc/rfc8422
[RFC8441]
P. McManus。使用HTTP/2引导WebSockets。2018年9月。拟标准。URL:https://httpwg.org/specs/rfc8441.html
[RFC9002]
J. Iyengar(编辑);I. Swett(编辑)。QUIC 丢失检测与拥塞控制。2021年5月。拟标准。URL:https://www.rfc-editor.org/rfc/rfc9002
[RFC9220]
R. Hamilton。使用HTTP/3引导WebSockets。2022年6月。拟标准。URL:https://httpwg.org/specs/rfc9220.html
[RFC9525]
P. Saint-Andre;R. Salz。TLS中的服务标识。2023年11月。拟标准。URL:https://www.rfc-editor.org/rfc/rfc9525
[STREAMS]
Adam Rice;等。Streams 标准。持续标准。URL:https://streams.spec.whatwg.org/
[URL]
Anne van Kesteren。URL 标准。持续标准。URL:https://url.spec.whatwg.org/
[WEB-TRANSPORT-HTTP2]
Alan Frindell;等。基于HTTP/2的WebTransport。互联网草案。URL:https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http2
[WEB-TRANSPORT-HTTP3]
Alan Frindell;Eric Kinnear;Victor Vasiliev。基于HTTP/3的WebTransport。互联网草案。URL:https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3
[WEB-TRANSPORT-OVERVIEW]
Victor Vasiliev。WebTransport 协议框架。互联网草案。URL:https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-overview
[WEBIDL]
Edgar Chen;Timothy Gu。Web IDL 标准。持续标准。URL:https://webidl.spec.whatwg.org/

信息性参考文献

[RELIABLE-RESET]
Marten Seemann; 奥一穂. QUIC Stream Resets with Partial Delivery. 互联网草案. 网址: https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset
[RFC7301]
S. Friedl; 等. Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension. 2014年7月. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc7301
[RFC8446]
E. Rescorla. The Transport Layer Security (TLS) Protocol Version 1.3. 2018年8月. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc8446
[RFC9308]
M. Kühlewind; B. Trammell. Applicability of the QUIC Transport Protocol. 2022年9月. 信息性. 网址: https://www.rfc-editor.org/rfc/rfc9308
[UNSANCTIONED-TRACKING]
Mark Nottingham. Unsanctioned Web Tracking. 2015年7月17日. TAG 发现. 网址: https://www.w3.org/2001/tag/doc/unsanctioned-tracking/
[WEBRTC]
Cullen Jennings; 等. WebRTC: Real-Time Communication in Browsers. 2025年3月13日. REC. 网址: https://www.w3.org/TR/webrtc/

IDL 索引

[Exposed=(Window,Worker), SecureContext, Transferable]
interface WebTransportDatagramsWritable : WritableStream {
  attribute WebTransportSendGroup? sendGroup;
  attribute long long sendOrder;
};

[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;
};

[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 options = {});
  /* a ReadableStream of WebTransportBidirectionalStream objects */
  readonly attribute ReadableStream incomingBidirectionalStreams;

  Promise<WebTransportSendStream> createUnidirectionalStream(
      optional WebTransportSendStreamOptions options = {});
  /* a ReadableStream of WebTransportReceiveStream objects */
  readonly attribute ReadableStream incomingUnidirectionalStreams;
  WebTransportSendGroup createSendGroup();

  static readonly attribute boolean supportsReliableOnly;
};

enum WebTransportReliabilityMode {
  "pending",
  "reliable-only",
  "supports-unreliable",
};

dictionary WebTransportHash {
  required DOMString algorithm;
  required 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 = [];
  ReadableStreamType datagramsReadableType;
};

enum WebTransportCongestionControl {
  "default",
  "throughput",
  "low-latency",
};

dictionary WebTransportCloseInfo {
  unsigned long closeCode = 0;
  USVString reason = "";
};

dictionary WebTransportSendOptions {
  WebTransportSendGroup? sendGroup = null;
  long long sendOrder = 0;
};

dictionary WebTransportSendStreamOptions : WebTransportSendOptions {
  boolean waitUntilAvailable = false;
};

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;
};

dictionary WebTransportDatagramStats {
  unsigned long long droppedIncoming;
  unsigned long long expiredIncoming;
  unsigned long long expiredOutgoing;
  unsigned long long lostOutgoing;
};

[Exposed=(Window,Worker), SecureContext, Transferable]
interface WebTransportSendStream : WritableStream {
  attribute WebTransportSendGroup? sendGroup;
  attribute long long sendOrder;
  Promise<WebTransportSendStreamStats> getStats();
  WebTransportWriter getWriter();
};

dictionary WebTransportSendStreamStats {
  unsigned long long bytesWritten;
  unsigned long long bytesSent;
  unsigned long long bytesAcknowledged;
};

[Exposed=(Window,Worker), SecureContext]
interface WebTransportSendGroup {
  Promise<WebTransportSendStreamStats> getStats();
};

[Exposed=(Window,Worker), SecureContext, Transferable]
interface WebTransportReceiveStream : ReadableStream {
  Promise<WebTransportReceiveStreamStats> getStats();
};

dictionary WebTransportReceiveStreamStats {
  unsigned long long bytesReceived;
  unsigned long long bytesRead;
};

[Exposed=(Window,Worker), SecureContext]
interface WebTransportBidirectionalStream {
  readonly attribute WebTransportReceiveStream readable;
  readonly attribute WebTransportSendStream writable;
};

[Exposed=*, SecureContext]
interface WebTransportWriter : WritableStreamDefaultWriter {
  Promise<undefined> atomicWrite(optional any chunk);
  undefined commit();
};

[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 source = "stream";
  [Clamp] unsigned long? streamErrorCode = null;
};

enum WebTransportErrorSource {
  "stream",
  "session",
};

问题索引

这也需要在 workers 中完成。请参阅 #127whatwg/html#6731

由于在撰写本文时,浏览器中尚未实现针对低延迟进行优化的拥塞控制算法,此配置选项被视为风险特性。

bytesAcknowledgedWebTransportConnectionStats 上已被工作组标识为有实现担忧的风险功能。