WebTransport

W3C 工作草案

关于本文档的更多信息
此版本:
https://www.w3.org/TR/2026/WD-webtransport-20260325/
最新发布版本:
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] 第4.2节
接收数据报 [WEB-TRANSPORT-OVERVIEW] 第4.2节
创建出站单向 [WEB-TRANSPORT-OVERVIEW] 第4.3节
创建双向 [WEB-TRANSPORT-OVERVIEW] 第4.3节
接收入站单向 [WEB-TRANSPORT-OVERVIEW] 第4.3节
接收双向 [WEB-TRANSPORT-OVERVIEW] 第4.3节

WebTransport 会话 session 处于排空状态时,表示CONNECT 流 已由服务器请求优雅关闭,如 [WEB-TRANSPORT-OVERVIEW] 第4.1节所述。

要使用可选的整数code和可选的字节序列reason终止 WebTransport 会话 session,请遵循 [WEB-TRANSPORT-OVERVIEW] 第4.1节

CONNECT 流被服务器关闭时,WebTransport 会话 session终止(可选择 一个整数code字节序列reason) 如 [WEB-TRANSPORT-OVERVIEW] 第4.1节所述。

3.2. WebTransport 流

WebTransport 流是用于 WebTransport 会话 上可靠有序字节流的一个概念,详见 [WEB-TRANSPORT-OVERVIEW] 第4.3节

WebTransport 流分为入站单向出站单向双向

上传输的数据 会作为未分类的字节序列进行传递。 发送端的单次写入操作的字节数,可能与 接收端单次读取操作的字节数不一致。 多次独立的写入可被合并为一个字节序列交付; 一次写入也可能被分开发送。

这包括原子写入, 原子写入的目的在于避免流控死锁, 而不是保证数据同时被传递。

依赖基于消息协议,且需要在发送端与接收端之间 保持消息边界的应用,需要添加帧封装层来 便于重组成消息。

一个WebTransport 流具有以下能力:

能力 定义 入站单向 出站单向 双向
发送 字节(可能带 FIN) [WEB-TRANSPORT-OVERVIEW] 第 4.3 节
接收 字节(可能带 FIN) [WEB-TRANSPORT-OVERVIEW] 第 4.3 节
中止接收WebTransport 流[WEB-TRANSPORT-OVERVIEW] 第 4.3 节
中止发送WebTransport 流[WEB-TRANSPORT-OVERVIEW] 第 4.3 节

一个WebTransport 流具有以下信号:

事件 定义 入站单向 出站单向 双向
接收已中止 [WEB-TRANSPORT-OVERVIEW] 第 4.3 节
发送已中止 [WEB-TRANSPORT-OVERVIEW] 第 4.3 节
流控 [WEB-TRANSPORT-OVERVIEW] 第 4.3 节

4. WebTransportDatagramsWritable 接口

WebTransportDatagramsWritable 是一个 WritableStream,为发送数据报提供出站流式传输功能。

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

4.1. 内部插槽

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

内部槽位 描述(非规范性
[[OutgoingDatagramsQueue]] 一个包含外发数据报、时间戳和 promise 的队列,该 promise 会在数据报被发送或丢弃时被解决。
[[Transport]] WebTransport 拥有的这个 WebTransportDatagramsWritable
[[SendGroup]] 可选的 WebTransportSendGroup, 或 null。
[[SendOrder]] 可选的发送顺序号,默认为 0。
创建一个 WebTransportDatagramsWritable, 给定一个 WebTransport 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.[[OutgoingMaxBufferedDatagrams]],则解析 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, 并且只要算法能取得进展,应尽快这么做。

注: 当传输的 [[State]]"connecting" 时允许写入数据报。 这些数据报会被存储在 [[OutgoingDatagramsQueue]], 并且可以和 "connected" 状态下一样被丢弃。 一旦传输的 [[State]] 变为 "connected",就会开始发送队列中的数据报。

发送顺序规则 指的是,整体发送操作可以与之前排队的流和数据报的发送交错进行, 也可以和还未排队但即将通过本传输发送的流和数据报交错进行, 但前提是,只有当所有具有同一 [[SendGroup]] 且更高 [[SendOrder]] ,并且既未出错,也未被流控阻塞的字节全部发送后, 当前发送操作才能继续。

注: 默认的 null sendGroup 也是有效的 sendGroup。

5. WebTransportDatagramDuplexStream 接口

一个 WebTransportDatagramDuplexStream 是一个通用的双工流。

[Exposed=(Window,Worker), SecureContext]
interface WebTransportDatagramDuplexStream {
  WebTransportDatagramsWritable createWritable(
      optional WebTransportSendOptions options = {});
  readonly attribute ReadableStream readable;

  readonly attribute unsigned long maxDatagramSize;
  attribute unrestricted double? incomingMaxAge;
  attribute unrestricted double? outgoingMaxAge;
  attribute unsigned long incomingMaxBufferedDatagrams;
  attribute unsigned long outgoingMaxBufferedDatagrams;
};

5.1. 内部插槽

内部槽 描述(非规范性
[[Transport]] 拥有此 WebTransportWebTransportDatagramDuplexStream
[[Readable]] 用于接收数据报的 ReadableStream
[[ReadableType]] 用于接收数据报的 ReadableStreamType
[[Writables]] 一个有序集合WebTransportDatagramsWritable 流,初始为空。
[[IncomingDatagramsQueue]] 一个队列,包含每个接收数据报和其时间戳的对。
[[IncomingDatagramsPullPromise]] 通过 pullDatagrams 设置的一个 promise,用于等待接收数据报。
[[IncomingMaxBufferedDatagrams]] 一个 unsigned long, 用于表示接收队列的长度(以数据报数量计),超出后队首的数据报会被丢弃。
[[IncomingDatagramsExpirationDuration]] 一个 unrestricted double, 表示接收数据报的过期时长(毫秒),或为 null。
[[OutgoingMaxBufferedDatagrams]] 一个 unsigned long, 表示底层 sink 的发送队列长度(以数据报数量计),超出后将产生背压。
[[OutgoingDatagramsExpirationDuration]] 一个 unrestricted double ,表示发送数据报的过期时长(毫秒),或为 null。
[[OutgoingMaxDatagramSize]] 一个整数,表示单个发送数据报的最大尺寸。
最大数据报大小取决于所用协议。 在 HTTP/3 [WEB-TRANSPORT-HTTP3] 中,该值与路径 MTU 的估算有关, 并会因实现定义的开销而减小。 在 HTTP/2 [WEB-TRANSPORT-HTTP2] 中没有类似限制。

由于处理数据报时通常需要在内存中暂存整个数据报, 实现会有限制其大小的机制。 将来协议扩展可能支持全部协议变体下大小限制的信号。

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

创建 一个 WebTransportDatagramDuplexStream ,给定 WebTransport transportreadablereadableType,执行以下步骤:
  1. stream 是一个 新建 WebTransportDatagramDuplexStream,其属性:

    [[Transport]]

    transport

    [[Readable]]

    readable

    [[ReadableType]]

    readableType

    [[Writables]]

    一个空的 有序集合

    [[IncomingDatagramsQueue]]

    一个空队列

    [[IncomingDatagramsPullPromise]]

    null

    [[IncomingMaxBufferedDatagrams]]

    一个由实现定义的值

    [[IncomingDatagramsExpirationDuration]]

    null

    [[OutgoingMaxBufferedDatagrams]]

    一个由实现定义的值

    此实现定义的值应调优以保证传输吞吐量合理,同时不影响传输数据的时效性。

    [[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. 如果 value0,将 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. 如果 value0,将 value 设为 null。

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

incomingMaxBufferedDatagrams 类型为 unsigned long

getter 步骤如下:

  1. 返回 this.[[IncomingMaxBufferedDatagrams]]

setter 步骤,给定 value

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

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

outgoingMaxBufferedDatagrams 类型为 unsigned long

getter 步骤如下:

  1. 返回 this.[[OutgoingMaxBufferedDatagrams]]

setter 步骤,给定 value

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

  2. this.[[OutgoingMaxBufferedDatagrams]] 设为 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 请求 view 不为 null,则:

      1. viewdatagrams.[[Readable]]当前 BYOB 请求 view

      2. 如果 view字节长度 小于 datagram 的大小,则返回 一个被拒绝的 promise,原因是 RangeError

      3. elementSizetyped array 构造表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。

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

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

  2. queue 等于 datagrams.[[IncomingDatagramsQueue]]

  3. duration 等于 datagrams.[[IncomingDatagramsExpirationDuration]]

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

  5. session 等于 transport.[[Session]]

  6. 只要 session 上有 可用传入数据报

    1. datagram 等于 接收数据报的结果, 参数为 session

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

    3. chunkdatagramtimestamp 的对。

    4. 入队 chunkqueue

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

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

  9. 只要 queue 非空:

    1. bytestimestampqueue 的第一个元素。

    2. 如果 timestamp 距现在已过去超过 duration 毫秒,则 出队 queue

    3. 否则,跳出此循环。

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

    1. bytestimestamp 等于 出队 queue 的结果。

    2. promise 等于 datagrams.[[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<Uint8Array> exportKeyingMaterial(BufferSource label, optional BufferSource context);
  readonly attribute Promise<undefined> ready;
  readonly attribute WebTransportReliabilityMode reliability;
  readonly attribute WebTransportCongestionControl congestionControl;
  attribute [EnforceRange] unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams;
  attribute [EnforceRange] unsigned short? anticipatedConcurrentIncomingBidirectionalStreams;
  [SameObject] readonly attribute Headers? responseHeaders;
  readonly attribute DOMString protocol;

  readonly attribute Promise<WebTransportCloseInfo> closed;
  readonly attribute Promise<undefined> draining;
  undefined close(optional WebTransportCloseInfo closeInfo = {});

  readonly attribute WebTransportDatagramDuplexStream datagrams;

  Promise<WebTransportBidirectionalStream> createBidirectionalStream(
      optional WebTransportSendStreamOptions 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 会话 建立 后 fulfill, 如果 建立过程失败则 reject。
[[Reliability]] 一个 WebTransportReliabilityMode ,指示首跳是否支持不可靠(UDP)传输或仅支持可靠(TCP)传输。连接建立前返回 "pending"
[[CongestionControl]] 一个 WebTransportCongestionControl ,用于指示应用是否请求并被用户代理满足了优化吞吐量或低延迟的拥塞控制算法,或 "default"
[[AnticipatedConcurrentIncomingUnidirectionalStreams]] 应用预期服务器创建的并发打开的 入向单向流数量,或为 null。
[[AnticipatedConcurrentIncomingBidirectionalStreams]] 应用预期服务器创建的并发打开的 双向流数量,或为 null。
[[ResponseHeaders]] 一个 Headers 对象或 null。初始值为 null。
[[Protocol]] 一个字符串,指示服务器选择的应用层协议(如有)。初始值为空字符串。
[[Closed]] 在相关 WebTransport 对象 正常关闭时 fulfill,异常或初始化失败时 reject。
[[Draining]] 在相关 WebTransport 会话 开始 draining 时 fulfill。
[[Datagrams]] 一个 WebTransportDatagramDuplexStream
[[Session]] 用于此 WebTransport 对象的 WebTransport 会话,或 null。
[[NewConnection]] "no" 或 "yes-and-dedicated"。
[[RequireUnreliable]] 一个布尔值,表示是否需要 UDP。

6.2. 构造函数

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

  2. url 为以 baseURL 作为基础,解析 url 后得到的URL 记录

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

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

  5. 如果 urlfragment 不为 null,抛出一个 SyntaxError 异常。

  6. 如果 optionsallowPooling 为 true,则 newConnection 为 "no";否则为 "yes-and-dedicated"。

  7. serverCertificateHashesoptionsserverCertificateHashes

  8. 如果 newConnection 为 "no" 且 serverCertificateHashes 非空,则 抛出 NotSupportedError 异常。

  9. requireUnreliableoptionsrequireUnreliable

  10. congestionControloptionscongestionControl

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

  12. protocolsoptionsprotocols

  13. 如果 protocols 中的任一值出现多次,或不符合 WebTransport 协议定义的协商应用协议项要求,或者其等同编码长度为 0 或超出 512,则抛出一个 SyntaxError 异常。 [WEB-TRANSPORT-OVERVIEW] 第 3.1 节

  14. anticipatedConcurrentIncomingUnidirectionalStreamsoptionsanticipatedConcurrentIncomingUnidirectionalStreams

  15. anticipatedConcurrentIncomingBidirectionalStreamsoptionsanticipatedConcurrentIncomingBidirectionalStreams

  16. datagramsReadableTypeoptionsdatagramsReadableType

  17. incomingDatagrams 为一个新的 ReadableStream

  18. transport 为新构造的 WebTransport 对象,具备如下属性:

    [[SendStreams]]

    空的有序集合

    [[ReceiveStreams]]

    空的有序集合

    [[IncomingBidirectionalStreams]]

    新的 ReadableStream

    [[IncomingUnidirectionalStreams]]

    新的 ReadableStream

    [[State]]

    "connecting"

    [[Ready]]

    新的 promise

    [[Reliability]]

    "pending"

    [[CongestionControl]]

    congestionControl

    [[AnticipatedConcurrentIncomingUnidirectionalStreams]]

    anticipatedConcurrentIncomingUnidirectionalStreams

    [[AnticipatedConcurrentIncomingBidirectionalStreams]]

    anticipatedConcurrentIncomingBidirectionalStreams

    [[ResponseHeaders]]

    null

    [[Protocol]]

    空字符串

    [[Closed]]

    新的 promise

    [[Draining]]

    新的 promise

    [[Datagrams]]

    undefined

    [[Session]]

    null

    [[NewConnection]]

    newConnection

    [[RequireUnreliable]]

    requireUnreliable

  19. transport.[[Datagrams]] 设为 创建 WebTransportDatagramDuplexStream 的结果, 参数为 transportincomingDatagramsdatagramsReadableType

  20. pullDatagramsAlgorithm 为运行 pullDatagrams 并传入 transport 的动作。

    注: 推荐和 datagrams 一起使用 64 KiB 缓冲区,因为 WebTransport 有效的最大 datagram 帧大小受 QUIC 最大 datagram 帧大小的上限限制, 推荐值为 64 KiB(见 [QUIC-DATAGRAM] 第 3 节)。 这将确保由于 datagram 超过缓冲区而导致的流错误不会发生。

  21. 如果 datagramsReadableType"bytes"使用字节读取支持初始化 incomingDatagramspullAlgorithm 设为 pullDatagramsAlgorithmhighWaterMark 设为 0。否则,初始化 incomingDatagramspullAlgorithm 设为 pullDatagramsAlgorithmhighWaterMark 设为 0。

  22. pullBidirectionalStreamAlgorithm 为运行 pullBidirectionalStream 并传入 transport 的动作。

  23. 初始化 transport.[[IncomingBidirectionalStreams]]pullAlgorithm 设为 pullBidirectionalStreamAlgorithmhighWaterMark 设为 0。

  24. pullUnidirectionalStreamAlgorithm 为运行 pullUnidirectionalStream 并传入 transport 的动作。

  25. 初始化 transport.[[IncomingUnidirectionalStreams]]pullAlgorithm 设为 pullUnidirectionalStreamAlgorithmhighWaterMark 设为 0。

  26. clienttransport相关设置对象

  27. originclientorigin

  28. request 为新建的 请求(request),其 URLurlclientclientservice-workers 模式为"none", referrer为"no-referrer",mode为"webtransport", credentials mode为"omit", cache mode为"no-store", policy containerclient策略容器destination 为空字符串, originoriginWebTransport-hash 列表serverCertificateHashesredirect mode为 "error"。

    注: 不会跟随重定向。重定向导致的网络错误与其他网络错误无法区分。有意如此:在跨域上下文中,这可以避免暴露通常会被 CORS 阻止的信息;在同源上下文中,避免应用滥用握手作为信息通道。

  29. requestmethod 设置为 "CONNECT",并将该方法相关联的 :protocol 伪首部(pseudo-header) 设为 "webtransport"

  30. headers 为以 Headers 对象,填充options["headers"]。

  31. requestHeaders 为新建的 Headers 对象,其 header listrequestheader listguard 为 "request"。

  32. 对于 headers 的每个 header list 中的 header

    1. 如果 ascii 小写 headername 是 "wt-available-protocols", 抛出 TypeError

    2. 追加 headerrequestHeaders

    注: 头部会受 Fetch forbidden request-header 限制。

  33. 如果 protocols 非空,设置结构化字段值, 即在 requestheader list 中, 设置 (WT-Available-Protocols, 一个 结构化头部列表,其成员为 结构化头部字符串 项,按照 protocols 顺序)。

  34. Fetch requestuseParallelQueue 设为 true, processResponse 设为以下步骤,参数为 response

    1. 处理 WebTransport fetch 响应,参数为 responsetransport

  35. 返回 transport

获取一个 WebTransport 连接,给定一个 网络分区键 networkPartitionKey,以及一个 request request
  1. transport 为与 request 关联的 WebTransport 对象。

  2. urlrequest当前 URL

  3. newConnectiontransport[[NewConnection]]

  4. requireUnreliabletransport[[RequireUnreliable]]

  5. webTransportHashesrequestWebTransport-hash 列表 中的值。

  6. connection 为使用 networkPartitionKeyurl、false、newConnectionrequireUnreliablewebTransportHashes 调用 obtaining a connection 的结果。

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

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

  9. 如果 settings 不包含 SETTINGS_ENABLE_CONNECT_PROTOCOL(0x08,HTTP/2 参见 Section 3[RFC8441];HTTP/3 参见 Section 3[RFC9220])且其值不为 1,则返回失败。

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

    注: SETTINGS_WT_MAX_SESSIONS 在 IETF 中仍在变动,可能会改回 SETTINGS_ENABLE_WEBTRANSPORT

  11. 返回 connection

处理一个 WebTransport fetch 响应,给定一个 response response 和一个 WebTransport transport,运行以下步骤:
  1. 如果 response网络错误,则中止剩余步骤并对 transport 排列一个网络任务以运行下列步骤:

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

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

    3. transport 执行 Cleanup,参数为 error

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

  3. 遵循 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中的任何限制,在 connection建立 一个 WebTransport 会话,使用服务器的 response, 并令 session 为所得的 WebTransport 会话。 由此产生的底层传输流称为该会话的 CONNECT 流

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

  4. 如果前一步失败,则中止剩余步骤并对 transport 排列一个网络任务以运行下列步骤:

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

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

    3. transport 执行 Cleanup,参数为 error

  5. 如果用户代理支持多个拥塞控制算法,则为此 connection 上的数据发送选择一个适合 transport[[CongestionControl]]

  6. transport 排列一个网络任务以运行下列步骤:

    1. 断言:this[[Datagrams]][[OutgoingMaxDatagramSize]] 为整数。

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

      1. 并行地,对 session 执行 terminate

      2. 中止这些步骤。

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

    4. transport[[Session]] 设为 session

    5. responseHeadersresponseheader list 的副本。

    6. responseHeaders删除 "wt-protocol"。

    7. transport[[ResponseHeaders]] 设为一个新的 Headers 对象, 其 header listresponseHeaders,且 guard 为 "immutable"。

    8. transport[[Protocol]] 设为协商出的应用层协议的字符串值(如果存在),遵循 [WEB-TRANSPORT-OVERVIEW] Section 3.1,若不存在则设为 ""

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

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

    11. Resolve transport[[Ready]] 为 undefined。

pullBidirectionalStream,给定一个 WebTransport 对象 transport,运行下列步骤。
  1. 如果 transport[[State]]"connecting",则在 transport[[Ready]] 被满足后,返回执行下列步骤的结果:

    1. transport 为参数再次调用并返回 pullBidirectionalStream 的结果。

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

  3. sessiontransport[[Session]]

  4. p 为一个新的 promise。

  5. 并行运行下列步骤:

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

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

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

      1. stream 为使用 internalStreamtransport 创建的 WebTransportBidirectionalStream 对象的结果。

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

      3. Resolve p,值为 undefined。

  6. 返回 p

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

    1. transport 为参数再次调用并返回 pullUnidirectionalStream 的结果。

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

  3. sessiontransport[[Session]]

  4. p 为一个新的 promise。

  5. 并行运行下列步骤:

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

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

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

      1. stream 为使用 internalStreamtransport 创建的 WebTransportReceiveStream 的结果。

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

      3. Resolve 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 不能保证应用会收到预期数量的流。

responseHeaders, 类型为 Headers,只读,可为 null

一旦建立了 WebTransport 会话, 保存由服务器设置的应用层响应头(如果有)。初始值为 null。 获取器步骤是返回 this[[ResponseHeaders]]

protocol, 类型为 DOMString,只读

一旦建立了 WebTransport 会话 且构造函数选项中的 protocols 提供了非空数组,则返回服务器选择的应用层协议(如果有)。否则返回空字符串。 获取器步骤是返回 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 执行 Cleanup,参数为 error

    3. 中止这些步骤。

  4. sessiontransport.[[Session]]

  5. codecloseInfo.closeCode

  6. reasonStringcloseInfo.reason 的最大代码单元前缀,使得该前缀经 UTF-8 编码 后的 长度 不超过 1024。

  7. reason 为对 reasonString 进行 UTF-8 编码 的结果。

  8. 并行地,使用 codereasonsession 执行 terminate

    注: 这也会中止 WebTransport 流(包含在 transport.[[SendStreams]][[ReceiveStreams]] 中) 的发送或接收。

  9. transport 执行 Cleanup,参数为 AbortErrorcloseInfo

getStats()

收集该 WebTransport底层连接 的统计信息,并异步报告结果。

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

  1. transportthis

  2. p 为一个新的 promise。

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

  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. stats 解决 p(Resolve p)。

  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 节)获得的 Uint8Array, 参数为 labelLength, label, contextLength,以及如有则包括 context

    2. 排入网络任务队列,使 transport 解决 p,值为 keyingMaterial

  9. 返回 p

createBidirectionalStream()

为一个外发的双向流创建一个 WebTransportBidirectionalStream 对象。 注意,流的创建本身在未用于发送数据前并不会立即对对端可见。

注: 服务器在未收到数据前并不会感知该流的存在,这是预期行为。

When createBidirectionalStream is called, the user agent MUST run the following steps:
  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. streamId 为一个对 transport.[[Session]] 有效且唯一的新流 ID,如 [QUIC] 第 19.11 节 所定义。若因耗尽而无法立即获得,则:若 waitUntilAvailable 为 true,则等待可用;若为 false, 则在对 transport 排列一个网络任务以 拒绝 p(原因为 QuotaExceededError, 其 requestedquota 均为 null)之后中止这些步骤。

    2. internalStream 为使用 transport.[[Session]]streamId 创建双向流的结果(参见 session 创建双向流)。

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

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

      2. stream 为使用 internalStreamtransportsendGroupsendOrder 创建的 WebTransportBidirectionalStream

      3. stream 解决 p

  9. 返回 p

createUnidirectionalStream()

为一个外发的单向流创建一个 WebTransportSendStream。 注意,流的创建本身在未用于发送数据前并不会立即对服务器可见。

注: 在未发送数据之前,服务器不会感知该流的存在,这是预期行为。

When createUnidirectionalStream() method is called, the user agent MUST run the following steps:
  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. streamId 为一个对 transport.[[Session]] 有效且唯一的新流 ID,如 [QUIC] 第 19.11 节 所定义。若因耗尽而无法立即获得,则:若 waitUntilAvailable 为 true,则等待可用;若为 false, 则在对 transport 排列一个网络任务以 拒绝 p(原因为 QuotaExceededError)之后中止这些步骤。

    2. internalStream 为使用 transport.[[Session]]streamId 创建的外发单向流的结果(参见 session 创建外发单向流)。

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

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

      2. stream 为使用 internalStreamtransportsendGroupsendOrder 创建的 WebTransportSendStream

      3. stream 解决 p

  9. return p.

createSendGroup()

创建一个 WebTransportSendGroup

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

  2. 返回对 创建 一个 WebTransportSendGroup 的结果,参数为 this

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;
  HeadersInit headers = {};
  sequence<WebTransportHash> serverCertificateHashes = [];
  WebTransportCongestionControl congestionControl = "default";
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams = null;
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingBidirectionalStreams = null;
  sequence<DOMString> protocols = [];
  ReadableStreamType datagramsReadableType;
};

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

WebTransportOptions 是一个字典,包含用于控制 WebTransport 会话的建立与使用的参数。

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

若设为 true,则 WebTransport 会话允许池化。即其 底层连接 可与其他 WebTransport 会话共享。

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

若设为 true,则若无法建立 HTTP/3 连接WebTransport 会话不能通过 HTTP/2 连接建立。

headers, 类型为 HeadersInit,默认值为 {}

可选项。可以是 Headers 对象、对象字面量、或二维数组, 用于设置 请求header list,用于 建立 WebTransport 会话时。

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

仅支持专用连接的传输协议。 不支持该功能的传输层,若该字段非空,则必须抛出 NotSupportedError 异常。

若支持且非空,用户代理只在可成功 验证证书哈希serverCertificateHashes 匹配且符合 自定义证书要求时认定证书可信。未知 algorithm 的哈希将被忽略。 若 为空,用户代理采用正常 fetch 流程的证书验证方式。

不可与 allowPooling 一起使用。

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

可选项,指明应用对拥塞控制算法更偏向吞吐量还是低延迟,发送数据时应采用何种算法。 这是对用户代理的优化建议。

此选项属于风险特性,因为至今浏览器尚无针对低延迟优化的拥塞控制算法实现。

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

可选项,允许应用指定预期服务器将并发创建多少 入向单向流。 用户代理最初必须允许至少 100 个 入向单向流来自服务器。 若不为 null,用户代理应尝试通过与服务器协商 [[AnticipatedConcurrentIncomingUnidirectionalStreams]] 以减少往返。

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

可选项,允许应用指定预期服务器将并发创建多少 双向流。 用户代理最初必须允许服务器创建至少 100 个 双向流。 若不为 null,用户代理应尝试通过与服务器协商 [[AnticipatedConcurrentIncomingBidirectionalStreams]] 以减少往返。

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

可选,提供一个应用层 协议名称数组。服务器可以选择性地选定优先应用协议并通知客户端。 若未提供合适协议,服务器可能会拒绝请求。

datagramsReadableType, 类型为 ReadableStreamType

可选,指明应用偏好用于接收数据报的 可读字节流。否则使用默认 可读流

注: 虽然 可读流 与数据报语义兼容, 可读字节流则并不兼容。数据报是区分的消息(每个消息为零或多个字节),可以无序到达, 与无分隔的字节序列不同。空的数据报会丢失,且 min 会丢失消息边界。

计算证书哈希,给定 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 特定统计信息。

注: 当使用池化时,多个 WebTransport 会话 被池化到同一个 连接上,会收到相同的信息,即这些信息会在持有同一个 网络分区键的池化 会话之间披露。

注: 任意不可用的统计项都会在 字典中缺省, 即 WebTransportConnectionStats 字典不会包含该项。

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

该字典应具有以下属性:

bytesSent类型为 unsigned long long

底层连接上发送的负载字节数,不包括任何帧开销或重传。

bytesSentOverhead 类型为 unsigned long 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 会话的流和数据报, 由拥塞控制算法(可能由 congestionControl 选择)计算。 此估算不包括任何帧开销,表示应用负载的数据发送速率。若用户代理当前没有估算,此成员必须为 null。 该成员即使之前非 null,后续结果中也可能变为 null

atSendCapacity类型为 boolean,默认值为 false

若值为 false,表示 estimatedSendRate 可能受应用限制, 即应用发送的数据量远小于拥塞控制允许的量。在应用受限情形下,拥塞控制器可能无法准确估算可用网络容量。

若值为 true,表示应用以网络容量发送数据,estimatedSendRate 反映应用可用的网络容量。

atSendCapacitytrueestimatedSendRate 反映了网络上限。 只要应用发送速率持续,estimatedSendRate 将随网络状况调整。不过,estimatedSendRate 即使在 atSendCapacity 为 true 时也允许为 null

6.14. WebTransportDatagramStats 字典

WebTransportDatagramStats 字典包括 有关 底层连接 上数据报传输的统计信息。

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

该字典应具有以下属性:

droppedIncoming 类型为 unsigned long 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

获取器步骤如下:

  1. 返回 this[[SendGroup]]

设置器步骤,给定 value,如下:

  1. value 非 null 且 value.[[Transport]] 不是 this.[[Transport]]抛出 InvalidStateError

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

sendOrder类型为 long long

获取器步骤如下:

  1. 返回 this[[SendOrder]]

设置器步骤,给定 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设为 gatheredStats 中的对应 条目

      3. 解决 p,值为 stats

  3. 返回 p

getWriter()

为该 创建一个 WebTransportWriter

当调用 getWriter 时,返回 创建WebTransportWriter, 参数为 this

7.3. 内部插槽

WebTransportSendStream 拥有以下内部槽。

内部槽 说明(非规范性
[[InternalStream]] 一个出站单向双向 WebTransport 流
[[PendingOperation]] 表示挂起写入或关闭操作的 promise,或为 null。
[[Transport]] 拥有该 WebTransportWebTransportSendStream
[[SendGroup]] 一个可选的 WebTransportSendGroup, 或 null。
[[SendOrder]] 可选的发送顺序号,默认值为 0。
[[AtomicWriteRequests]] promise 的有序集合, 用于追踪排队提交至底层 sink 并要求原子操作的写入请求子集。
[[InsideSynchronousAtomicWrite]] 布尔值,仅在执行 atomicWrite 方法的同步部分时为 true。
[[BytesWritten]] 已写入到流中的字节数。
[[CommittedOffset]] 流内偏移量,记录即使在流已中止发送时 也将交付给对端的字节数; 见 [RELIABLE-RESET]

7.4. 过程

创建一个 WebTransportSendStream ,其参数为一个 外发单向双向 WebTransport 流 internalStream,一个 WebTransport transportsendGroup,以及 sendOrder,请执行如下步骤:

  1. stream 为一个 新建的 WebTransportSendStream, 其属性如下:

    [[InternalStream]]

    internalStream

    [[PendingOperation]]

    null

    [[Transport]]

    transport

    [[SendGroup]]

    sendGroup

    [[SendOrder]]

    sendOrder

    [[AtomicWriteRequests]]

    一个空的 有序 promise 集合

    [[InsideSynchronousAtomicWrite]]

    false

    [[BytesWritten]]

    0

    [[CommittedOffset]]

    0

  2. writeAlgorithm 为一个动作,该动作 写入 chunkstream(参数为 chunk 时)。

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

  4. abortAlgorithm 为一个动作,该动作 终止 stream,参数为 reason

  5. 设置 streamwriteAlgorithm 设为 writeAlgorithmcloseAlgorithm 设为 closeAlgorithmabortAlgorithm 设为 abortAlgorithm

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

  7. 添加如下步骤到 abortSignal

    1. pendingOperationstream.[[PendingOperation]]

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

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

    4. reasonabortSignal中止原因

    5. promise终止 stream ,参数为 reason 的结果。

    6. 在 promise fulfilled 时拒绝 pendingOperation,原因为 reason

  8. 追加 streamtransport.[[SendStreams]]

  9. 返回 stream

写入 chunkWebTransportSendStream stream,执行如下步骤:
  1. transportstream.[[Transport]]

  2. 如果 chunk 不是 BufferSource, 返回一个被拒绝的 promise,异常为 TypeError

  3. promise 为一个新的 promise。

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

  5. 如果 bytes长度为 0, 解决 promise,值为 undefined 并返回 promise

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

  7. inFlightWriteRequeststream.inFlightWriteRequest

  8. atomic 为 true,若 stream.[[AtomicWriteRequests]] 包含 inFlightWriteRequeststream.[[InsideSynchronousAtomicWrite]] 为 true,否则为 false。

  9. 按如下并行执行步骤:

    1. atomic 为 true,且当前流控窗口过小,无法将 bytes 一次性发出,则中止后续步骤,排入网络任务队列transport 执行以下子步骤:

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

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

    2. 否则,发送 bytesstream.[[InternalStream]] 并等待操作完成。 该发送 必须遵循发送顺序规则

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

      此处可并行访问 stream.[[SendOrder]]。 用户代理应支持发送过程中该值的实时更新, 具体细节为实现自定义

      注: 重传的排序实现自定义, 但强烈建议用户代理优先对 [[SendOrder]] 值更大的数据进行重传。

      流控以及 错误情况外,此发送不能饿死其他流。

      用户代理应当为所有未被饿死的流公平分配带宽。

      注: 此处公平的定义实现自定义

    3. 如果上一步因网络错误失败,终止剩余步骤。

      注: 此处不会 reject promise,因为网络错误会在其他地方处理,届时会 reject stream.[[PendingOperation]]

    4. 否则,排入网络任务队列transport 执行以下步骤:

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

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

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

      4. 解决 promise,值为 undefined。

  10. 返回 promise

注: 此算法(或 write(chunk)) 返回的 promise 被 fulfill 不代表 该 chunk 已被服务器 [QUIC] 确认(acked)。 它可能仅意味着该 chunk 被附加到了缓冲区。要确保 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终止所有 atomic write 请求,执行如下步骤:
  1. writeRequestsstream.writeRequests

  2. requestsToAbortstream.[[AtomicWriteRequests]]

  3. 如果 writeRequests 包含 任意不属于 requestsToAbort 的 promise,则 使 stream 出错,错误类型为 AbortError, 并终止这些步骤。

  4. 清空 stream.[[AtomicWriteRequests]]

  5. 依次requestsToAbort 中每个 promisereject promise,异常为 AbortError

  6. 并行地, requestsToAbort 中的每个 promise, 中止与该 promise 关联的字节发送

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

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

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

    注: code 的有效值为 0 到 4294967295(含)。 若 底层连接 使用的是 HTTP/3,则错误码会被编码为 [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,则 reject stream.[[PendingOperation]] ,原因为 error

    5. 使 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 上的数据传输。

WebTransportSendStreamWebTransportDatagramsWritable, 在创建或通过其 sendGroup 属性赋值后, 都可以在任意时刻被归组 到至多一个 WebTransportSendGroup空 sendGroup。 默认情况下, WebTransportSendStreamWebTransportDatagramsWritable 被分配到 空 sendGroup

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

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

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

8.1. 方法

getStats()

聚合所有 WebTransportSendStream 分组到本 sendGroup 下的统计信息,并以异步方式返回结果。

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

  1. p 为一个新的 promise。

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

  3. 按如下并行执行:

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

    2. 排入网络任务队列,用 transport 执行以下步骤:

      1. stats新的 WebTransportSendStreamStats 对象。

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

      3. 解决 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. gatheredStats 为特定于 this WebTransportReceiveStream 的统计信息 列表, 用于准确填充 WebTransportReceiveStreamStats字典成员

    2. 排入网络任务队列,以 transport 运行以下步骤:

      1. stats新的 WebTransportReceiveStreamStats 对象。

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

      3. 解决 p,值为 stats

  3. 返回 p

9.2. 内部槽

WebTransportReceiveStream 拥有以下内部槽。

内部槽 说明(非规范性
[[InternalStream]] 一个入站单向双向 WebTransport 流
[[Transport]] 拥有该 WebTransportWebTransportReceiveStream 对象。

9.3. 过程

创建 一个 WebTransportReceiveStream, 给定一个入站单向双向 WebTransport 流 internalStream 以及一个 WebTransport transport,执行以下步骤:

  1. stream新的 WebTransportReceiveStream,属性如下:

    [[InternalStream]]

    internalStream

    [[Transport]]

    transport

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

  3. cancelAlgorithm 为带 reason 取消 stream 的操作。

  4. 字节读取支持初始化 streampullAlgorithm 设为 pullAlgorithmcancelAlgorithm 设为 cancelAlgorithm

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

  6. 返回 stream

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

  1. transportstream.[[Transport]]

  2. internalStreamstream.[[InternalStream]]

  3. promise 为一个新的 promise。

  4. bufferoffsetmaxBytes 均为 null。

  5. 如果 stream当前 BYOB 请求 view 不为 null:

    1. offsetstream当前 BYOB 请求 view.[[ByteOffset]]。

    2. maxBytesstream当前 BYOB 请求 view字节长度

    3. bufferstream当前 BYOB 请求 view底层缓冲区

  6. 否则:

    1. offset 为 0。

    2. maxBytes实现自定义的字节数。

    3. buffer 为一个新的 ArrayBuffer,大小为 maxBytes。 如果分配 ArrayBuffer 失败,返回 一个被拒绝的 promise,异常为 RangeError

  7. 按如下并行执行步骤:

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

      用户代理可拥有缓冲区以提升传输性能。此类缓冲区应有固定上限,以将背压信息传递给服务端。

      注: 该操作可能在未填满 buffer 时就返回。

    2. 如果上一步失败,终止剩余步骤。

      注: 此处不 reject promise,因为网络错误会在其他地方处理,届时会 使 stream 出错,从而 reject 所有等待该 pull 的读取请求。

    3. 排入网络任务队列transport 执行以下步骤:

      注: 如果上述缓冲区可在此过程所在事件循环中访问,则下述步骤可立即执行。

      1. 如果 read > 0:

        1. view 为带有 bufferoffsetread 的新 Uint8Array

        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,则码值会被编码到 [0x52e4a40fa8db, 0x52e5ac983162] 区间,详见 [WEB-TRANSPORT-HTTP3]

  8. 移除 streamtransport.[[SendStreams]]

  9. 按如下并行执行步骤:

    1. internalStream 上用 code 中止接收

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

      注: 如果上述缓冲区可在此过程所在事件循环中访问,则下述步骤可立即执行。

      1. 移除 streamtransport.[[ReceiveStreams]]

      2. 解决 promise,值为 undefined。

  10. 返回 promise

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

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

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

    注: code 的有效值为 0 到 4294967295(含)。 若 底层连接 使用的是 HTTP/3,则错误码会被编码为 [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;
  unsigned long long bytesRead;
};

字典应具有以下属性:

bytesReceived 类型为 unsigned long long

用于指示服务端应用发送到此 WebTransportReceiveStream 的字节中,已成功接收的进度。 只统计第一个缺失字节之前的所有连续字节。该数值只会递增。

注意: 仅为单条流中接收到的应用层数据进度,不包括任何网络开销。

bytesRead 类型为 unsigned long long

应用已从此 WebTransportReceiveStream 成功读取的字节总数。该数值只会递增,且始终小于等于 bytesReceived

10. 接口 WebTransportBidirectionalStream

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

10.1. 内部插槽

一个 WebTransportBidirectionalStream 具有以下内部槽。

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

10.2. 属性

readable 类型为 WebTransportReceiveStream,只读

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

writable 类型为 WebTransportSendStream,只读

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

10.3. 过程

创建一个 WebTransportBidirectionalStream ,给定一个 双向 WebTransport 流 internalStream、一个 WebTransport 对象 transport 和一个 sendOrder,执行以下步骤:
  1. readable创建 WebTransportReceiveStream 的结果,参数为 internalStreamtransport

  2. writable创建 WebTransportSendStream 的结果,参数为 internalStreamtransportsendOrder

  3. stream新建WebTransportBidirectionalStream,其属性如下:

    [[Readable]]

    readable

    [[Writable]]

    writable

    [[Transport]]

    transport

  4. 返回 stream

11. WebTransportWriter 接口

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

WebTransportWriter 始终通过 create 过程创建。

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

11.1. 方法

atomicWrite(chunk)

如果传入 chunk 时,无法在当前发送时的流控窗口内 整体发出, atomicWrite 方法会拒绝该操作。 此行为用于满足对流控 死锁敏感的特殊交易型应用需求 ([RFC9308] 第 4.4 节)。

注: atomicWrite 发送部分数据后仍可能被拒绝。它只在流控层面保证原子性,其他错误仍可能发生。 atomicWrite 并不能避免数据被分包或与其它数据交错发送。只有发送方能够知道 atomicWrite 是否因流控额度不足而失败。

注: 若原子写前面有非原子写操作排队,原子写可能阻塞。 若原子写被拒绝,其后的所有队列写入操作都会被拒绝。 以此方式被拒绝的非原子写会 使流 error。 因此建议应用总是 await 原子写操作。

当调用 atomicWrite(chunk) 时,用户代理必须按以下步骤执行:

  1. streamthis.stream

  2. 如果 stream 为 undefined,返回 一个被拒绝的 promise,异常为 TypeError

  3. stream.[[InsideSynchronousAtomicWrite]] 设为 true。

  4. p写入 chunkthis 的结果。

  5. stream.[[InsideSynchronousAtomicWrite]] 设为 false。

  6. p 添加到 stream.[[AtomicWriteRequests]]

  7. 返回 p settled 时执行如下步骤的结果:

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

    2. 如果 p 被拒绝,异常为 r,则返回 一个被拒绝的 promise,异常为 r

    3. 返回 undefined。

commit()

commit 方法会将流的 [[CommittedOffset]] 更新为该流已经写入的字节数 ([[BytesWritten]])。 这样可确保这些字节即使在写入被中止、 导致流中止发送后, 也会被可靠传递到对端。 相关机制见 [RELIABLE-RESET]

注: 这无法保证连接失败时数据一定能传递, 只保证流已中止发送时传递。

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

  1. stream.[[CommittedOffset]] 设为 stream.[[BytesWritten]] 的值。

11.2. 过程

创建 一个 WebTransportWriter, 给定 WebTransportSendStream stream,执行以下步骤:

  1. writer新的 WebTransportWriter

  2. 执行 SetUpWritableStreamDefaultWriter(writer, 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 没有 legacy 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效果
会话开始排水 await wt.draining

如果 底层连接 使用 HTTP/3,则适用于从 [WEB-TRANSPORT-HTTP3] 的以下协议行为。

WebTransportError 错误中的应用 streamErrorCode 转换为 httpErrorCode,反之亦然,如 [WEB-TRANSPORT-HTTP3] 第4.3节 中所述。

API 方法 QUIC 协议操作
writable.abort(error) 中止发送(STREAM),带 httpErrorCode 以及 对应于该 [[CommittedOffset]] 的偏移(外加任意流头);详见 [RELIABLE-RESET]
writable.close() 发送 STREAM,并设置 FIN 标志位
writable.getWriter().write(chunk)() 发送 STREAM
writable.getWriter().close() 发送 STREAM,并设置 FIN 标志位
writable.getWriter().abort(error) 中止发送(STREAM),带 httpErrorCode 以及 对应于该 [[CommittedOffset]] 的偏移(外加任意流头);详见 [RELIABLE-RESET]
readable.cancel(error) 中止接收(STREAM),带 httpErrorCode
readable.getReader().cancel(error) 中止接收(STREAM),带 httpErrorCode
wt.close(closeInfo) 终止会话,带 closeInfo
QUIC 协议操作 API 效果
收到 STOP_SENDING(带 httpErrorCode) 使 writable 报错,带 streamErrorCode
收到 STREAM (await readable.getReader().read()).value
收到 STREAM(带 FIN bit) (await readable.getReader().read()).done
收到 RESET_STREAM(带 httpErrorCode) 使 readable 报错,带 streamErrorCode
会话被正常终止,带 closeInfo
(await wt.closed).closeInfo, 并且 使 所有打开的流报错
网络错误
(await wt.closed) reject,并且 使 所有打开的流报错

注意:[QUIC] RFC9000 §3.2 所述, 收到 RESET_STREAM 帧或 RESET_STREAM_AT 帧([RELIABLE-RESET]) 并不总是会通知应用层。 重置信号可以立即触发,导致流数据传递被中断,未消费的数据会被丢弃。 但协议不要求必须立即通知,信号可能会延迟,以便传递 RESET_STREAM_AT 帧中的 Reliable Size 字段所指示的数据。 如果流数据已完整接收但尚未被应用读取,则发送中止信号可被抑制。 WebTransport 始终使用 RESET_STREAM_AT 帧,以确保流头部能够可靠传递; 参见 §4.1§4.2 [WEB-TRANSPORT-HTTP3]

HTTP/3 协议动作 API效果
会话开始排水 await wt.draining

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

API 方法 HTTP/2 协议操作
writable.abort(error) 中止发送 WT_STREAM,附带 error
writable.close() 发送 带 FIN 标志位的 WT_STREAM
writable.getWriter().write() 发送 WT_STREAM
writable.getWriter().close() 发送 带 FIN 标志位的 WT_STREAM
writable.getWriter().abort(error) 中止发送 WT_STREAM,附带 error
readable.cancel(error) 中止接收 WT_STREAM,附带 error
readable.getReader().cancel(error) 中止接收 WT_STREAM,附带 error
wt.close(closeInfo) 终止会话,带 closeInfo
HTTP/2 协议操作 API 效果
收到 WT_STOP_SENDING,附带 error 使 writable 报错,带 streamErrorCode
收到 WT_STREAM (await readable.getReader().read()).value
收到 带 FIN 标志位的 WT_STREAM (await readable.getReader().read()).done
收到 WT_RESET_STREAM,附带 error 使 readable 报错,带 streamErrorCode
会话被正常终止,带 closeInfo
(await wt.closed).closeInfo, 并且 使 所有打开的流报错
网络错误
(await wt.closed) reject,并且 使 所有打开的流报错
会话开始draining await wt.draining

14. 隐私与安全注意事项

此部分为非规范性内容;它未指定任何新的行为,而是总结了规范其他部分中已经存在的信息。

14.1. 通信机密性

通信正在进行的事实无法对能够观察网络的对手隐瞒,因此这必须被视为公开信息。

本文件中描述的所有传输协议都使用 TLS [RFC8446] 或语义上等效的协议,从而提供了 TLS 的所有安全属性,包括流量的机密性和完整性。基于 HTTP 的 WebTransport 使用与出站 HTTP 请求相同的证书验证机制,因此依赖于相同的公钥基础设施来验证远程服务器的身份。在 WebTransport 中,证书验证错误是致命的;没有允许绕过证书验证的中间页面。

14.2. 状态持久性

WebTransport 本身不会创建任何新的唯一标识符或持久存储状态的新方法,也不会自动向服务器公开任何现有的持久状态。例如,[WEB-TRANSPORT-HTTP3][WEB-TRANSPORT-HTTP2] 都不发送 Cookies,也不支持 HTTP 身份验证或缓存失效机制。由于它们确实使用 TLS,它们继承了 TLS 的持久状态,例如 TLS 会话票据,这虽然对被动网络观察者不可见,但可能会被服务器用于关联来自同一客户端的不同连接。

14.3. 协议安全性

WebTransport 强加了一组要求,如 [WEB-TRANSPORT-OVERVIEW] 中所述,包括:

  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级(Content Security Policy Level 3)。2026年3月11日。WD。网址:https://www.w3.org/TR/CSP3/
[DOM]
Anne van Kesteren。DOM 标准(DOM Standard)。现行标准。 网址:https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范。网址:https://tc39.es/ecma262/multipage/
[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 标准。现行标准。网址:https://fetch.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss。高精度时间(High Resolution Time)。2026年3月24日。 WD。网址:https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren 等。HTML 标准。 现行标准。网址:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren;Domenic Denicola。Infra 标准。现行标准。网址: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。互联网 X.509 公钥基础设施证书和证书吊销列表(CRL)配置文件的算法与标识符。2002年4月。建议标准。网址:https://www.rfc-editor.org/rfc/rfc3279
[RFC5280]
D. Cooper 等。互联网 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 及更早版本的椭圆曲线密码套件(ECC)。2018年8月。建议标准。网址:https://www.rfc-editor.org/rfc/rfc8422
[RFC8441]
P. McManus。用 HTTP/2 启动 WebSockets。2018年9月。建议标准。网址:https://httpwg.org/specs/rfc8441.html
[RFC9002]
J. Iyengar, 编辑;I. Swett, 编辑。QUIC 丢包检测与拥塞控制。2021年5月。建议标准。网址:https://www.rfc-editor.org/rfc/rfc9002
[RFC9220]
R. Hamilton。用 HTTP/3 启动 WebSockets。2022年6月。建议标准。网址:https://httpwg.org/specs/rfc9220.html
[RFC9525]
P. Saint-Andre;R. Salz。TLS 中的服务身份。2023年11月。建议标准。网址:https://www.rfc-editor.org/rfc/rfc9525
[STREAMS]
Adam Rice 等。Streams 标准。现行标准。网址:https://streams.spec.whatwg.org/
[URL]
Anne van Kesteren。URL 标准。现行标准。 网址: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 协议框架。互联网草案。网址:https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-overview
[WEBIDL]
Edgar Chen;Timothy Gu。Web IDL 标准。现行标准。网址: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 unsigned long incomingMaxBufferedDatagrams;
  attribute unsigned long outgoingMaxBufferedDatagrams;
};

[Exposed=(Window,Worker), SecureContext]
interface WebTransport {
  constructor(USVString url, optional WebTransportOptions options = {});

  Promise<WebTransportConnectionStats> getStats();
  [NewObject] Promise<Uint8Array> exportKeyingMaterial(BufferSource label, optional BufferSource context);
  readonly attribute Promise<undefined> ready;
  readonly attribute WebTransportReliabilityMode reliability;
  readonly attribute WebTransportCongestionControl congestionControl;
  attribute [EnforceRange] unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams;
  attribute [EnforceRange] unsigned short? anticipatedConcurrentIncomingBidirectionalStreams;
  [SameObject] readonly attribute Headers? responseHeaders;
  readonly attribute DOMString protocol;

  readonly attribute Promise<WebTransportCloseInfo> closed;
  readonly attribute Promise<undefined> draining;
  undefined close(optional WebTransportCloseInfo closeInfo = {});

  readonly attribute WebTransportDatagramDuplexStream datagrams;

  Promise<WebTransportBidirectionalStream> createBidirectionalStream(
      optional WebTransportSendStreamOptions 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;
  HeadersInit headers = {};
  sequence<WebTransportHash> serverCertificateHashes = [];
  WebTransportCongestionControl congestionControl = "default";
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingUnidirectionalStreams = null;
  [EnforceRange] unsigned short? anticipatedConcurrentIncomingBidirectionalStreams = null;
  sequence<DOMString> protocols = [];
  ReadableStreamType datagramsReadableType;
};

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

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 上已被工作组标识为有实现担忧的风险功能。