WebTransport

W3C 工作草案

关于本文档的更多信息
此版本:
https://www.w3.org/TR/2025/WD-webtransport-20251022/
最新发布版本:
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

建立 origin 以及 protocols 数组的 WebTransport 会话,请按照 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中的说明,使用 origin序列化同构编码后的值作为请求的 `Origin` 头部,并将同构编码后的 protocols 作为客户端希望服务器在此会话中使用的协议列表(按优先级排序),遵循 [WEB-TRANSPORT-OVERVIEW] Section 3.1 中的描述。建立会话时,客户端不得提供任何凭据。生成的底层传输流称为会话的CONNECT 流

当服务器请求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]] 拥有此 WebTransportDatagramsWritable 的一个 WebTransport
[[SendGroup]] 一个可选的 WebTransportSendGroup,或者为 null。
[[SendOrder]] 一个可选的发送顺序号,默认为 0。

创建一个 WebTransportDatagramsWritable,给定一个 WebTransport transport,一个 sendGroup,以及一个 sendOrder,请执行以下步骤。

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

    [[OutgoingDatagramsQueue]]

    一个空队列

    [[Transport]]

    transport

    [[SendGroup]]

    sendGroup

    [[SendOrder]]

    sendOrder

  2. writeDatagramsAlgorithm 为一个运行 writeDatagrams,参数为 transportstream 的操作。

  3. 设置 stream,其中 writeAlgorithm 设置为 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. 内部插槽

一个 WebTransportDatagramDuplexStream 对象具有以下内部插槽。

内部槽 描述(非规范性
[[Readable]] 用于接收数据报的 ReadableStream
[[ReadableMode]] 接收数据报时使用的 DatagramsReadableMode
[[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] 中则没有类似限制。

由于数据报处理通常会将整个数据报保存在内存中,实际实现会对大小有限制。 未来的协议扩展可以实现对所有协议变体的数据报大小限制进行信号通知。

用户代理 MAY 可为任何 WebTransport 对象更新 [[OutgoingMaxDatagramSize]] ,只要其 [[State]]"connecting""connected"

创建一个 WebTransportDatagramDuplexStream ,给定 readablereadableMode, 执行以下步骤。

  1. stream新建WebTransportDatagramDuplexStream,属性如下:

    [[Readable]]

    readable

    [[ReadableMode]]

    readableMode

    [[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. sendOrderoptionssendOrder

  5. 返回使用 transportsendGroupsendOrder 创建一个 WebTransportDatagramsWritable 的结果。

5.3. 属性

readable, 类型为 ReadableStream, readonly

getter 步骤:

  1. 返回 this.[[Readable]]

incomingMaxAge, 类型为 unrestricted double, nullable

getter 步骤:

  1. 返回 this.[[IncomingDatagramsExpirationDuration]]

setter 步骤,给定 value

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

  2. 如果 value0,设置 value 为 null。

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

maxDatagramSize, 类型为 unsigned long, readonly

可以传递给 WebTransportDatagramsWritable 的最大大小数据。 getter 步骤是返回 this.[[OutgoingMaxDatagramSize]]

outgoingMaxAge, 类型为 unrestricted double, nullable

getter 步骤:

  1. 返回 this[[OutgoingDatagramsExpirationDuration]]

setter 步骤,给定 value

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

  2. 如果 value0,设置 value 为 null。

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

incomingHighWaterMark, 类型为 unrestricted double

getter 步骤:

  1. 返回 this.[[IncomingDatagramsHighWaterMark]]

setter 步骤,给定 value

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

  2. 如果 value < 1,设置 value1

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

outgoingHighWaterMark, 类型为 unrestricted double

getter 步骤:

  1. 返回 this.[[OutgoingDatagramsHighWaterMark]]

setter 步骤,给定 value

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

  2. 如果 value < 1,设置 value1

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

5.4. 过程

pullDatagrams,给定一个 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.[[ReadableMode]]"bytes",则:

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

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

      2. 如果 view字节长度小于 datagram 的大小,返回 一个被拒绝的 promise,错误为 RangeError

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

      4. 如果 elementSize 不为 1,返回 一个被拒绝的 promise,错误为 TypeError

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

  7. 否则:

    1. chunk 为一个新的 Uint8Array 对象,表示 datagram

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

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

receiveDatagrams,给定一个 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 为一个新的 Uint8Array 对象,表示 bytes

      2. 入队 chunkdatagrams.[[Readable]]

      3. 解决 promise,值为 undefined。

用户代理 SHOULD 在任何 WebTransport 对象的 [[State]]"connected" 时,只要该算法可以推进,应尽快运行 receiveDatagrams

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]] WebTransport 拥有的一个 WebTransportSendStream有序集合
[[ReceiveStreams]] WebTransport 拥有的一个 WebTransportReceiveStream有序集合
[[IncomingBidirectionalStreams]] 一个由 WebTransportBidirectionalStream 对象组成的 ReadableStream
[[IncomingUnidirectionalStreams]] 一个由 WebTransportReceiveStream 组成的 ReadableStream
[[State]] 一个枚举,指示传输的状态。为 "connecting""connected""draining""closed""failed" 之一。
[[Ready]] 当关联的 WebTransport 会话 建立时,此 promise 会被实现;如果建立过程失败,则此 promise 会被拒绝。
[[Reliability]] 一个 WebTransportReliabilityMode,指示第一跳是否支持不可靠的 (UDP) 传输,或者是否仅支持可靠的 (TCP 回退) 传输。在建立连接之前,返回 "pending"
[[CongestionControl]] 一个 WebTransportCongestionControl,指示应用程序是否请求了针对吞吐量或低延迟优化的拥塞控制算法的偏好,并且该偏好是否已由用户代理满足;或者 "default"
[[AnticipatedConcurrentIncomingUnidirectionalStreams]] 应用程序预计服务器将创建的并发打开的入向单向流的数量,或者为 null。
[[AnticipatedConcurrentIncomingBidirectionalStreams]] 应用程序预计服务器将创建的并发打开的双向流的数量,或者为 null。
[[Protocol]] 一个字符串,指示服务器选择的应用程序级协议(如果有)。最初为空字符串。
[[Closed]] 当关联的 WebTransport 对象正常关闭时,此 promise 会被实现;当其突然关闭或在初始化时失败时,此 promise 会被拒绝。
[[Draining]] 当关联的 WebTransport 会话 耗尽时,此 promise 会被实现。
[[Datagrams]] 一个 WebTransportDatagramDuplexStream
[[Session]] WebTransport 对象的WebTransport 会话,或者为 null。

6.2. 构造函数

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

  2. parsedURL 为用 baseURL 解析 url 得到的 URL 记录

  3. 如果 parsedURL 为失败,则 抛出 SyntaxError 异常。

  4. 如果 parsedURLscheme 不是 https,则 抛出 SyntaxError 异常。

  5. 如果 parsedURLfragment 不为 null,则 抛出 SyntaxError 异常。

  6. allowPoolingoptionsallowPooling

  7. dedicatedallowPooling 的取反值。

  8. serverCertificateHashesoptionsserverCertificateHashes (如果存在),否则为 null。

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

  10. requireUnreliableoptionsrequireUnreliable

  11. congestionControloptionscongestionControl

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

  13. protocolsoptionsprotocols

  14. 如果 protocols 中的任意值出现超过一次、未能匹配 WebTransport 协议定义的协商应用协议的元素要求、或其 同构编码长度为0或超过512,则 抛出 SyntaxError 异常。 [WEB-TRANSPORT-OVERVIEW] 第3.1节

  15. anticipatedConcurrentIncomingUnidirectionalStreamsoptionsanticipatedConcurrentIncomingUnidirectionalStreams

  16. anticipatedConcurrentIncomingBidirectionalStreamsoptionsanticipatedConcurrentIncomingBidirectionalStreams

  17. datagramsReadableModeoptionsdatagramsReadableMode

  18. incomingDatagrams 为一个 新建ReadableStream

  19. datagrams创建 WebTransportDatagramDuplexStream 的结果,readable 设为 incomingDatagramsreadableMode 设为 datagramsReadableMode

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

    datagrams

    [[Session]]

    null

  21. pullDatagramsAlgorithm 为一个运行 pullDatagrams,参数为 transport 的动作。

    注意: 建议数据报使用 64kB 的缓冲区,因为 WebTransport 数据报帧有效最大值受 QUIC 最大数据报帧的上限影响,建议为 64kB(参见 [QUIC-DATAGRAM] 第3节)。 这样可以确保流不会因数据报大于缓冲区而出错。

  22. 如果 datagramsReadableMode"bytes",则 以字节读取支持方式 设置 incomingDatagramspullAlgorithm 设为 pullDatagramsAlgorithmhighWaterMark 设为 0。否则,设置 incomingDatagramspullAlgorithm 设为 pullDatagramsAlgorithmhighWaterMark 设为 0。

  23. pullBidirectionalStreamAlgorithm 为一个运行 pullBidirectionalStream,参数为 transport 的动作。

  24. 设置 transport.[[IncomingBidirectionalStreams]]pullAlgorithm 设为 pullBidirectionalStreamAlgorithmhighWaterMark 设为 0。

  25. pullUnidirectionalStreamAlgorithm 为一个运行 pullUnidirectionalStream,参数为 transport 的动作。

  26. 设置 transport.[[IncomingUnidirectionalStreams]]pullAlgorithm 设为 pullUnidirectionalStreamAlgorithmhighWaterMark 设为 0。

  27. 初始化 WebTransport over HTTP,参数为 transport, parsedURL, dedicated, requireUnreliable, congestionControl, protocols, serverCertificateHashes

  28. 返回 transport

初始化 WebTransport over HTTP,给定 WebTransport 对象 transportURL 记录 url、布尔值 dedicated、布尔值 requireUnreliableWebTransportCongestionControl congestionControl、数组 protocols 和 序列 WebTransportHash serverCertificateHashes,运行以下步骤。
  1. clienttransport相关设置对象

  2. originclientorigin

  3. request 为一个新的 请求,其 URLurlclientclientpolicy containerclientpolicy containerdestination 为空字符串, originoriginredirect mode 为 "error"。

  4. 运行 request 进行内容安全策略违规报告

  5. 如果 内容安全策略应阻止请求?,参数为 request 返回 "Blocked",或 request 因端口不合法应被阻止 返回 blocked,则终止剩余步骤并 队列一个网络任务,参数为 transport,执行以下步骤:

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

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

    3. 清理 transport,参数为 error

  6. networkPartitionKey 为用 transport相关设置对象 确定网络分区键的结果。

  7. 运行以下步骤 并行,但当 transport.[[State]] 变为 "closed""failed"终止

    1. newConnection 为 "no",如果 dedicated 为 false;否则为 "yes-and-dedicated"。

    2. connection 为用 networkPartitionKeyurl、false、newConnectionrequireUnreliable 获取连接的结果。如果用户代理支持多种拥塞控制算法,选择适合 congestionControl 的算法用于该连接的数据发送。当获取连接时, 如果指定了 serverCertificateHashes,则不采用默认证书验证算法,只要满足 自定义证书要求证书哈希校验serverCertificateHashes 返回 true,则证书有效,否则 connection 为失败。

    3. 如果 connection 为失败,则终止剩余步骤并 队列一个网络任务,参数为 transport,执行以下步骤:

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

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

      3. 清理 transport,参数为 error

        注意: 不跟随重定向。由于重定向导致的网络错误与其他网络错误不可区分。在跨域环境下,这会暴露通常被 CORS 阻止的信息。在同源环境下,则可能鼓励应用滥用握手作为信息传递渠道。

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

    5. 如果 settings 未包含 SETTINGS_ENABLE_WEBTRANPORT 且值为 1,或未包含 H3_DATAGRAM 且值为 1,则终止剩余步骤并 队列一个网络任务,参数为 transport,执行以下步骤:

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

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

      3. 清理 transport,参数为 error

    6. 建立一个 WebTransport 会话,参数为 originprotocols,在 connection 上。

      注意: 此步骤还包含 [QUIC-DATAGRAM] 规定的传输参数交换。

    7. 如果上一步失败,则终止剩余步骤并 队列一个网络任务,参数为 transport,执行以下步骤:

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

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

      3. 清理 transport,参数为 error

    8. session 为已建立的 WebTransport 会话

    9. 队列一个网络任务,参数为 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. 入队 streamtransport.[[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. 入队 streamtransport.[[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. 并行终止 session,参数为 codereason

    注意:这也会 中止发送中止接收 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. 收集 底层连接的统计信息,包括数据报的统计。

    5. 队列一个网络任务,参数为 transport,执行以下步骤:

      1. stats 为一个新的 new WebTransportConnectionStats 对象,表示收集到的统计信息。

      2. 解决 p,值为 stats

  5. 返回 p

exportKeyingMaterial(BufferSource label, optional BufferSource context)

从与该 WebTransport 底层连接 唯一关联的 TLS 会话中导出密钥材料,使用 TLS Keying Material Exporter

调用 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 为导出 TLS 密钥材料的结果,如 [WEB-TRANSPORT-OVERVIEW] 第 4.1 节 所定义, 使用 labelLengthlabelcontextLength 以及(如有)context

    2. 队列一个网络任务,参数为 transport解决 p,值为 keyingMaterial

  9. 返回 p

createBidirectionalStream()

创建一个用于出向双向流的 WebTransportBidirectionalStream 对象。注意,仅创建流并不会立即让对端可见,只有发送数据后才会被对端感知。

注意:服务器在数据发送前不会感知流的创建。

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

  1. transportthis

  2. 如果 transport.[[State]]"closed""failed",返回一个新的 被拒绝的 promise,错误为 InvalidStateError

  3. sendGroupoptionssendGroup

  4. sendOrderoptionssendOrder

  5. waitUntilAvailableoptionswaitUntilAvailable

  6. p 为一个新的 promise。

  7. 并行运行以下步骤,但当 transport[[State]] 变为 "closed""failed" 时终止,并改为 队列一个网络任务,参数为 transport拒绝 p,错误为 InvalidStateError

    1. streamId 为一个新的流 ID,对 transport.[[Session]] 有效且唯一,定义见 [QUIC] 第 19.11 节。如因耗尽未能立即获得流 ID,若 waitUntilAvailable 为 true,则等待可用;否则为 false 时,队列一个网络任务,参数为 transport拒绝 p,错误为 QuotaExceededError,并终止这些步骤。

    2. internalStream创建双向流的结果,参数为 transport.[[Session]]streamId

    3. 队列一个网络任务,参数为 transport,执行以下步骤:

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

      2. stream创建 WebTransportBidirectionalStream 的结果,参数为 internalStreamtransportsendGroupsendOrder

      3. 解决 p,值为 stream

  8. 返回 p

createUnidirectionalStream()

创建一个用于出向单向流的 WebTransportSendStream 对象。注意,仅创建流并不会立即让服务器可见,只有发送数据后才会被服务器感知。

注意:服务器在数据发送前不会感知流的创建。

调用 createUnidirectionalStream() 方法时,用户代理必须运行以下步骤:

  1. transportthis

  2. 如果 transport.[[State]]"closed""failed",返回一个新的 被拒绝的 promise,错误为 InvalidStateError

  3. sendGroupoptionssendGroup

  4. sendOrderoptionssendOrder

  5. waitUntilAvailableoptionswaitUntilAvailable

  6. p 为一个新的 promise。

  7. 并行运行以下步骤,但当 transport[[State]] 变为 "closed""failed" 时终止,并改为 队列一个网络任务,参数为 transport拒绝 p,错误为 InvalidStateError

    1. streamId 为一个新的流 ID,对 transport.[[Session]] 有效且唯一,定义见 [QUIC] 第 19.11 节。如因耗尽未能立即获得流 ID,若 waitUntilAvailable 为 true,则等待可用;否则为 false 时,队列一个网络任务,参数为 transport拒绝 p,错误为 QuotaExceededError,并终止这些步骤。

    2. internalStream创建出向单向流的结果,参数为 transport.[[Session]]streamId

    3. 队列一个网络任务,参数为 transport,执行以下步骤:

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

      2. stream创建 WebTransportSendStream 的结果,参数为 internalStreamtransportsendGroupsendOrder

      3. 解决 p,值为 stream

  8. 返回 p

createSendGroup()

创建一个 WebTransportSendGroup

调用 createSendGroup() 方法时,用户代理必须运行以下步骤:

  1. transportthis

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

  3. 返回 创建 WebTransportSendGroup 的结果,参数为 transport

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. 断言:readysettled

    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报错 writable,错误为 error

    8. 报错 incomingDatagrams,错误为 error

队列一个网络任务,参数为 WebTransport transport 和一系列步骤 steps,请执行以下步骤:

  1. 将一个全局任务入队网络任务源,使用 transport相关全局对象,以运行 steps

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

每当与 WebTransport transport 关联的 WebTransport 会话 使用可选的 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 解码

      注意: 没有语言或方向 元数据可用于 reasonBytesFirst-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]]锁定,或 readydrainingclosed promise 被观察时,不能被垃圾回收。

WebTransport 对象的 [[State]]"connected" 时,如果 [[IncomingBidirectionalStreams]][[IncomingUnidirectionalStreams]]、 任何 WebTransportReceiveStream, 或 [[Datagrams]].[[Readable]]锁定,或 drainingclosed promise 被观察时,不能被垃圾回收。

WebTransport 对象的 [[State]]"draining" 时,如果 [[IncomingBidirectionalStreams]][[IncomingUnidirectionalStreams]]、 任何 WebTransportReceiveStream, 或 [[Datagrams]].[[Readable]]锁定,或 closed promise 被观察时,不能被垃圾回收。

当一个 WebTransport 对象的 WebTransport 会话已建立,且有排队待发送到网络的数据(包括在 [[Datagrams]].[[OutgoingDatagramsQueue]] 的数据报),不能被垃圾回收。

如果 WebTransport 对象在 底层连接 仍然打开时被垃圾回收,用户代理必须 以应用错误码 0 和应用错误消息 "" 终止 WebTransport 会话

6.9. 配置

dictionary WebTransportHash {
  DOMString algorithm;
  BufferSource value;
};

dictionary WebTransportOptions {
  boolean allowPooling = false;
  boolean requireUnreliable = false;
  sequence<WebTransportHash> serverCertificateHashes;
  WebTransportCongestionControl congestionControl = "default";
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams = null;
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingBidirectionalStreams = null;
  sequence<DOMString> protocols = [];
  DatagramsReadableMode datagramsReadableMode;
};

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

enum DatagramsReadableMode { "bytes" };

WebTransportOptions 是用于决定如何建立和使用 WebTransport 会话的参数字典。

allowPooling, 类型为 boolean,默认值为 false

当设置为 true 时,WebTransport 会话可以被池化,即其 底层连接 可以与其他 WebTransport 会话共享。

requireUnreliable, 类型为 boolean,默认值为 false

当设置为 true 时,如果无法建立 HTTP/3 连接,则不能通过 HTTP/2 连接建立 WebTransport 会话

serverCertificateHashes, 类型为 sequence<WebTransportHash>

该选项仅支持专用连接的传输协议。对于不支持此功能的传输协议,如果该字段非空,则会抛出 NotSupportedError 异常。

如果支持且非空,用户代理只有在能够成功验证证书哈希,并满足 自定义证书要求时,才认为服务器证书可信。用户代理必须忽略任何使用未知 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>,默认值为 []

可选应用层协议名称数组。服务器可以选择性地选择首选应用协议并通知客户端。如果没有提供合适的协议,服务器可能会拒绝请求。

datagramsReadableMode, 类型为 DatagramsReadableMode

可选地指定应用偏好是否为入向数据报使用可读字节流。否则,将使用默认的可读流

注意: 默认可读流用于检测零长度数据报。 但可读字节流可以更高效地处理字节,尤其是在与BYOB reader配合使用时能减少拷贝。

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

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

验证证书哈希,给定一个 certificate 和一个哈希数组 hashes, 执行以下步骤:
  1. referenceHash 为使用 certificate 计算证书哈希的结果。

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

    1. 如果 hash.value 不为 null 且 hash.algorithm 与 "sha-256" ASCII 大小写不敏感匹配

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

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

  3. 返回 false。

自定义证书要求custom certificate requirements如下:该证书必须是 [RFC5280]中定义的 X.509v3 证书, Subject Public Key 字段中使用的密钥必须是允许的公钥算法之一, 当前时间必须在证书的有效期内,如[RFC5280]的第 4.1.2.5 节中所定义, 且有效期的总长度不得超过两周。用户代理可以对证书施加额外的实现定义的要求。

Subject Public Key Info 字段(以及因此,在 TLS CertificateVerify 消息中)中使用的允许的公钥算法的确切列表是实现定义的;但是,它必须包括使用 secp256r1 (NIST P-256) 命名组的 ECDSA([RFC3279], 第 2.3.5 节;[RFC8422]) 以提供可互操作的默认值。它不得包含 RSA 密钥([RFC3279], 第 2.3.1 节)。

6.10. WebTransportCloseInfo 字典

WebTransportCloseInfo 字典包含用于在终止WebTransport 会话时设置错误码和原因的信息。

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 特定统计信息的 信息。

注意: 当使用池化时,池化在同一连接上的多个WebTransport 会话都接收相同的信息,即,该信息跨持有相同网络分区密钥的池化的会话公开。

注意: 任何不可用的统计信息都将从WebTransportConnectionStats字典中缺失

dictionary WebTransportConnectionStats {
  unsigned long long bytesSent = 0;
  unsigned long long packetsSent = 0;
  unsigned long long bytesLost = 0;
  unsigned long long packetsLost = 0;
  unsigned long long bytesReceived = 0;
  unsigned long long packetsReceived = 0;
  required DOMHighResTimeStamp smoothedRtt;
  required DOMHighResTimeStamp rttVariation;
  required DOMHighResTimeStamp minRtt;
  required WebTransportDatagramStats datagrams;
  unsigned long long? estimatedSendRate = null;
  boolean atSendCapacity = false;
};

该字典应具有以下属性:

bytesSent, 类型为 unsigned long long, 默认为 0

底层连接上发送的字节数,包括重传。 不包括 UDP 或任何其他外部帧。

packetsSent, 类型为 unsigned long long, 默认为 0

底层连接上发送的数据包数,包括那些被确定为丢失的数据包。

bytesLost, 类型为 unsigned long long, 默认为 0

底层连接上丢失的字节数(不会单调递增,因为随后可能会收到声明为丢失的数据包)。 不包括 UDP 或任何其他外部帧。

packetsLost, 类型为 unsigned long long, 默认为 0

底层连接上丢失的数据包数(不会单调递增,因为随后可能会收到声明为丢失的数据包)。

bytesReceived, 类型为 unsigned long long, 默认为 0

底层连接上收到的总字节数,包括流的重复数据。不包括 UDP 或任何其他外部帧。

packetsReceived, 类型为 unsigned long long, 默认为 0

底层连接上收到的总数据包数,包括无法处理的数据包。

smoothedRtt, 类型为 DOMHighResTimeStamp

当前在连接上观察到的平滑往返时间 (RTT),如[RFC9002] 第 5.3 节中所定义。

rttVariation, 类型为 DOMHighResTimeStamp

当前在连接上观察到的往返时间样本的平均变化,如[RFC9002] 第 5.3 节中所定义。

minRtt, 类型为 DOMHighResTimeStamp

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

estimatedSendRate, 类型为 unsigned long long, nullable, 默认为 null

用户代理将发送排队数据的估计速率,以每秒比特数为单位。 此速率适用于共享WebTransport 会话的所有流和数据报, 并且由拥塞控制算法计算(可能由congestionControl选择)。 此估计不包括任何帧开销,并且表示可以发送应用程序有效负载的速率。如果用户代理当前没有估计值,则成员必须为 null 值。即使在先前的结果中不是 null,该成员也可以是 null

atSendCapacity, 类型为 boolean, 默认为 false

值 false 表示 estimatedSendRate 可能受应用程序限制, 这意味着应用程序发送的数据明显少于拥塞控制器允许的数据。当应用程序受到限制时,拥塞控制器可能会对可用的网络容量产生较差的估计。

值 true 表示应用程序正在以网络容量发送数据,并且estimatedSendRate反映了应用程序可用的网络容量。

atSendCapacitytrue 时,estimatedSendRate反映了一个上限。 只要应用程序发送速率持续,estimatedSendRate将适应网络状况。但是,允许 estimatedSendRatenull,而atSendCapacity为 true。

6.14. WebTransportDatagramStats 字典

WebTransportDatagramStats字典包含有关通过底层连接进行数据报传输的统计信息。

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

该字典应具有以下属性:

droppedIncoming, 类型为 unsigned long long, 默认为 0

由于应用程序未从 datagramsreadable 读取,导致新数据报溢出接收队列而丢弃的传入数据报的数量。

expiredIncoming, 类型为 unsigned long long, 默认为 0

由于传入数据报早于 incomingMaxAge,因此在从 datagramsreadable 读取之前被丢弃的传入数据报的数量。

expiredOutgoing, 类型为 unsigned long long, 默认为 0

由于排队发送的数据报早于 outgoingMaxAge,因此在能够发送之前被丢弃的排队发送的数据报的数量。

lostOutgoing, 类型为 unsigned long long, 默认为 0

声明为丢失的已发送数据报的数量,如[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. 收集特定于此WebTransportSendStream的统计信息。

    2. 等待统计信息准备就绪。

    3. 使用transport将网络任务排队以运行以下步骤:

      1. stats为一个新的 WebTransportSendStreamStats对象,表示收集的统计信息。

      2. 使用stats解析p

  3. 返回p

getWriter()

此方法必须以与从WritableStream继承的getWriter相同的方式实现, 只是代替创建WritableStreamDefaultWriter,它必须创建一个WebTransportWriter,其中包含this

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

要将写入 chunk 到一个WebTransportSendStream stream,运行以下步骤:
  1. transportstream[[Transport]]

  2. 如果chunk不是一个BufferSource, 则返回一个被拒绝的 promise,并带有一个TypeError

  3. promise为一个新的 promise。

  4. bytes字节序列chunk表示)的副本。

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

  6. inFlightWriteRequeststreaminFlightWriteRequest

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

  8. 并行运行以下步骤:

    1. 如果atomic为 true,并且当前的流量控制窗口太小,无法完全发送bytes,则中止剩余步骤,并使用transport将网络任务排队,以运行以下子步骤:

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

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

    2. 否则,在stream[[InternalStream]]发送bytes,并等待操作完成。 此发送可以与先前排队的流和数据报的发送交错,以及尚未排队以通过此传输发送的流和数据报。

      用户代理可以有一个缓冲区来提高传输性能。这样的缓冲区应该有一个固定的上限,以将反压信息传递给WebTransportSendStream的用户。

      此发送必须饿死 直到所有在具有相同[[SendGroup]]和更高[[SendOrder]]的流上排队等待发送的字节,既不是出错,也没有被流量控制阻止,都已发送。

      我们并行访问stream[[SendOrder]]。 用户代理应该响应发送期间这些值的实时更新,但细节是实现定义的

      注意:重新传输的排序是实现定义的, 但强烈建议用户代理优先重新传输具有更高[[SendOrder]]值的 数据。

      此发送不得以其他方式饿死, 除非出于流量控制原因或错误

      用户代理应该在所有未被饿死的流之间公平地分配带宽。

      注意:这里的公平性定义是实现定义的

    3. 如果由于网络错误导致上一步失败,则中止剩余步骤。

      注意:我们不在此处拒绝promise,因为我们在其他地方处理网络错误,并且这些步骤拒绝stream[[PendingOperation]]

    4. 否则,使用transport将网络任务排队,以运行以下步骤:

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

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

      3. 如果stream[[AtomicWriteRequests]]包含inFlightWriteRequest,则删除inFlightWriteRequest

      4. 使用 undefined解析promise

  9. 返回promise

注意:从此算法(或write(chunk))返回的 promise 的实现一定意味着该块已通过服务器[QUIC]确认。它可能只是意味着该块已附加到缓冲区。为了确保该块到达服务器,服务器需要发送一个应用程序级别的确认消息。

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

  2. promise 为一个新的 promise。

  3. 移除 streamtransport.[[SendStreams]]

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

  5. 并行运行以下步骤:in parallel:

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

    2. 等待 stream.[[InternalStream]] 进入 “所有数据已提交” 状态。[QUIC]

    3. 排队一个网络任务 使用 transport 来运行以下步骤:

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

      2. 解析 promise 并返回 undefined。

  6. 返回 promise

中止一个 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,则将 code 设为 0。

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

  8. committedOffsetstream.[[CommittedOffset]]

    注意: code 的有效值为 0 至 4294967295(含)。如果 底层连接 使用 HTTP/3,则该 code 会被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 区间的数值,详见 [WEB-TRANSPORT-HTTP3]

  9. 并行运行以下步骤:

    1. 中止发送 stream.[[InternalStream]] ,参数为 codecommittedOffset

    2. 队列一个网络任务,参数为 transport解决 promise,值为 undefined。

  10. 返回 promise

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

  2. requestsToAbortstream.[[AtomicWriteRequests]]

  3. 如果 writeRequests 包含某个不在 requestsToAbort 中的 promise,则 出错 stream,错误类型为 AbortError, 并中止这些步骤。

  4. 清空 stream.[[AtomicWriteRequests]]

  5. 对于每个 promise 属于 requestsToAbort拒绝promise,错误类型为 AbortError

  6. 并行对于每个 promise 属于 requestsToAbort,中止与该 promise 相关联的字节发送操作。

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

每当与 WebTransport stream 关联的 WebTransportSendStream stream 收到来自服务器的 接收中止信号时,执行以下步骤:
  1. transportstream.[[Transport]]

  2. code 为附加在 接收中止信号上的应用协议错误码。

    注意: code 的有效值为 0 到 4294967295(含)。如果 底层连接使用 HTTP/3,则该 code 会被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 区间的数字,详见 [WEB-TRANSPORT-HTTP3]

  3. 队列一个网络任务,参数为 transport,执行以下步骤:

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

    2. 移除 streamtransport.[[SendStreams]]

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

    4. 如果 stream.[[PendingOperation]] 不为 null,则拒绝 stream.[[PendingOperation]],错误为 error

    5. 报错 stream,错误为 error

7.6. WebTransportSendStreamStats 字典

WebTransportSendStreamStats 字典 包括一个 WebTransportSendStream 的特定统计信息。

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

字典应具有以下属性:

bytesWritten, 类型为 unsigned long long, 默认值为 0

应用程序已成功写入此 WebTransportSendStream 的总字节数。此数字只能增加。

bytesSent, 类型为 unsigned long long, 默认值为 0

指示应用程序写入此 WebTransportSendStream 的字节已至少发送一次的进度。此数字只能增加,并且始终小于或等于 bytesWritten

注意: 此值仅表示单个流上的应用数据发送进度,不包括任何网络开销。

bytesAcknowledged, 类型为 unsigned long long, 默认值为 0

指示应用程序写入此 WebTransportSendStream 的字节已发送并通过服务器的 QUIC ACK 机制确认接收的进度。只有连续的字节(直到但不包括第一个未确认的字节)被计入。此数字只能增加,并且始终小于或等于 bytesSent

注意: 如果连接是通过 HTTP/2,则此值将与 bytesSent 匹配。

8. 接口 WebTransportSendGroup

WebTransportSendGroup 是一个可选的组织对象,用于跟踪分布在许多个单独 (通常是 严格排序的) WebTransportSendStream 上的数据传输。

WebTransportSendStream 可以在创建时或通过赋值其 sendGroup 属性,被归为最多一个 分组到一个 WebTransportSendGroup。默认情况下,它们是 未分组的。

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

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

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

8.1. 方法

getStats()

聚合所有 WebTransportSendStream 分组this sendGroup 下的流的统计信息,并异步返回结果。

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

  1. p 为一个新的 promise。

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

  3. 并行运行以下步骤:

    1. streams 中所有流收集统计信息。

    2. 队列一个网络任务,参数为 transport,执行以下步骤:

      1. stats 为一个新的 new WebTransportSendStreamStats 对象,表示已收集统计信息的聚合数值。

      2. 解决 p,值为 stats

  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. 收集此 WebTransportReceiveStream 的特定统计数据。

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

      1. stats 为一个新的 WebTransportReceiveStreamStats 对象,表示收集到的统计数据。

      2. 解析 p 并返回 stats

  3. 返回 p

9.2. 内部槽

WebTransportReceiveStream 拥有以下内部槽。

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

9.3. 过程

创建 一个WebTransportReceiveStream, 使用一个入向单向双向 WebTransport 流 internalStream 和一个 WebTransport transport,请执行以下步骤:

  1. stream 为一个新的 WebTransportReceiveStream ,绑定:

    [[InternalStream]]

    internalStream

    [[Transport]]

    transport

  2. pullAlgorithm 为从 stream 拉取字节 的动作。

  3. cancelAlgorithm 为取消 stream 的动作,并提供 reason

  4. 使用字节读取支持设置 stream,将 pullAlgorithm 设置为 pullAlgorithm, 将 cancelAlgorithm 设置为 cancelAlgorithm

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

  6. 返回 stream.

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

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

  2. internalStream 设为 stream.[[InternalStream]].

  3. 创建一个新的 promise.

  4. buffer, offset, 和 maxBytes 初始化为 null.

  5. 如果 stream当前 BYOB 请求视图 不为空:

    1. 设置 offsetstream当前 BYOB 请求视图.[[ByteOffset]].

    2. 设置 maxBytesstream当前 BYOB 请求视图字节长度.

    3. 设置 bufferstream当前 BYOB 请求视图底层缓冲区.

  6. 否则:

    1. offset 设置为 0.

    2. 设置 maxBytes 为一个 实现定义 的大小.

    3. buffer 设置为一个新的 ArrayBuffer ,大小为 maxBytes. 如果分配失败,返回 一个被拒绝的 promise,错误为 RangeError.

  7. 并行运行以下步骤:

    1. 写入internalStream 读取的字节到 buffer,偏移量为 offset,最多 maxBytes 字节. 等待至少读取一个字节或接收 FIN. 设定 read 为读取字节数,设定 hasReceivedFIN 为是否接收到 FIN.

    2. 如果上述步骤失败,中止剩余步骤.

    3. 排队一个网络任务 使用 transport 执行以下步骤:

      1. 如果 read > 0:

        1. 设置 view 为一个新的 Uint8Array ,使用 buffer, offset, 和 read.

        2. 放入队列 viewstream.

      2. 如果 hasReceivedFIN 为 true:

        1. 移除 streamtransport.[[ReceiveStreams]].

        2. 关闭 stream.

      3. 解析 promise 并返回 undefined.

  8. 返回 promise.

取消一个 WebTransportReceiveStream stream,参数为 reason,请执行以下步骤。

  1. transportstream.[[Transport]]

  2. internalStreamstream.[[InternalStream]]

  3. promise 为一个新的 promise。

  4. code 为 0。

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

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

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

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

  8. 移除 streamtransport.[[SendStreams]]

  9. 并行运行以下步骤:

    1. 中止接收 internalStream,参数为 code

    2. 队列一个网络任务,参数为 transport,执行以下步骤:

      注意: 如果上述缓冲区在当前过程运行的 事件循环可用,则以下步骤可立即运行。

      1. 移除 streamtransport.[[ReceiveStreams]]

      2. 解决 promise,值为 undefined。

  10. 返回 promise

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

每当与 WebTransport stream 关联的 WebTransportReceiveStream stream 收到来自服务器的 发送中止信号时,执行以下步骤:
  1. transportstream.[[Transport]]

  2. code 为附加在 发送中止信号上的应用协议错误码。

    注意: code 的有效值为 0 到 4294967295(含)。如果 底层连接使用 HTTP/3,则该 code 会被编码为 [0x52e4a40fa8db, 0x52e5ac983162] 区间的数字,详见 [WEB-TRANSPORT-HTTP3]

  3. 队列一个网络任务,参数为 transport,执行以下步骤:

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

    2. 移除 streamtransport.[[ReceiveStreams]]

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

    4. 报错 stream,错误为 error

9.5. WebTransportReceiveStreamStats 字典

WebTransportReceiveStreamStats 字典 包括一个 WebTransportReceiveStream 的特定统计信息。

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

字典应具有以下属性:

bytesReceived, 类型为 unsigned long long, 默认值为 0

指示服务器应用程序的字节进度,这些字节是针对此 WebTransportReceiveStream 到目前为止已接收的数量。只有连续的字节(直到但不包括第一个丢失的字节)被计入。此数字只能增加。

注意: 此值仅表示单个流上的应用数据接收进度,不包括任何网络开销。

bytesRead, 类型为 unsigned long long, 默认值为 0

应用程序已成功读取此 WebTransportReceiveStream 的总字节数。此数字只能增加,并且始终小于或等于 bytesReceived

10. 接口 WebTransportBidirectionalStream

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

10.1. 内部插槽

一个 WebTransportBidirectionalStream 具有以下内部槽。

内部槽 描述 (非规范性)
[[Readable]] 一个 WebTransportReceiveStream
[[Writable]] 一个 WebTransportSendStream
[[Transport]] 拥有此 WebTransport 对象的 WebTransportBidirectionalStream

10.2. 属性

readable, 类型为 WebTransportReceiveStream, 只读

获取器步骤是返回 this[[Readable]]

writable, 类型为 WebTransportSendStream, 只读

获取器步骤是返回 this[[Writable]]

10.3. 过程

创建一个 WebTransportBidirectionalStream ,使用 双向 WebTransport 流 internalStream、一个 WebTransport 对象 transportsendOrder,请执行以下步骤。
  1. readable 设为通过 创建一个 WebTransportReceiveStream ,并绑定 internalStreamtransport 的结果。

  2. writable 设为通过 创建一个 WebTransportSendStream ,并绑定 internalStreamtransportsendOrder 的结果。

  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

    注意: 此 name 没有对应的旧版 code 映射,因此 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 reader, 这样可以更精确地控制缓冲区分配,以避免数据拷贝。下面的示例将数据报读入一个 64kB 的内存缓冲区。

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

dictionary WebTransportOptions {
  boolean allowPooling = false;
  boolean requireUnreliable = false;
  sequence<WebTransportHash> serverCertificateHashes;
  WebTransportCongestionControl congestionControl = "default";
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams = null;
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingBidirectionalStreams = null;
  sequence<DOMString> protocols = [];
  DatagramsReadableMode datagramsReadableMode;
};

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

enum DatagramsReadableMode { "bytes" };

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 = 0;
  unsigned long long packetsSent = 0;
  unsigned long long bytesLost = 0;
  unsigned long long packetsLost = 0;
  unsigned long long bytesReceived = 0;
  unsigned long long packetsReceived = 0;
  required DOMHighResTimeStamp smoothedRtt;
  required DOMHighResTimeStamp rttVariation;
  required DOMHighResTimeStamp minRtt;
  required WebTransportDatagramStats datagrams;
  unsigned long long? estimatedSendRate = null;
  boolean atSendCapacity = false;
};

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

[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 = 0;
  unsigned long long bytesSent = 0;
  unsigned long long bytesAcknowledged = 0;
};

[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 = 0;
  unsigned long long bytesRead = 0;
};

[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

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