WebTransport

W3C 工作草案

关于本文档的更多信息
此版本:
https://www.w3.org/TR/2025/WD-webtransport-20250702/
最新发布版本:
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-HTTP3][WEB-TRANSPORT-HTTP2]。 本规范与 IETF WEBTRANS 工作组制定的协议规范同步开发。

本文档状态

本节描述了本文档在发布时的状态。当前 W3C 发布文档和本技术报告的最新修订版可在 W3C 技术报告索引中查阅。

本文档由 WebTransport 工作组推荐标准流程的工作草案形式发布。 本文档计划成为 W3C 推荐标准。

欢迎就本文档提出反馈和建议。请在 GitHub issue 区 提交问题,相关内容见 本仓库

作为工作草案发布并不意味着 W3C 及其成员的认可。 此文档为草案,随时可能更新、替换或废弃。除非作为进展文档引用,否则不应被引用为标准。

本文档由依照W3C 专利政策运作的小组制定。 W3C 维护着与本组交付成果相关的专利公开列表; 该页面还包含专利披露的相关说明。 个人如掌握认为包含必要声明的专利,应根据 W3C 专利政策第6节披露相关信息。

本文档受2023年11月3日 W3C 流程文档约束。

1. 简介

本节为非规范性内容。

本规范使用 [WEB-TRANSPORT-HTTP3][WEB-TRANSPORT-HTTP2] 来向服务器发送数据和从服务器接收数据。 它可以像 WebSockets 一样使用,但支持多路流、单向流、乱序交付以及可靠和不可靠的传输。

注意: 本规范中提供的 API 代表了基于 IETF WEBTRANS WG 正在进行的工作的初步提案。 由于 [WEB-TRANSPORT-HTTP3][WEB-TRANSPORT-HTTP2] 规范仍在制定中,因此协议和 API 都可能会在未来发生重大变化。

2. 一致性

除了标记为非规范性的章节外,本规范中的所有编写指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。

关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、“MAY”和“OPTIONAL”应按照 [RFC2119][RFC8174] 中的描述进行解释,当且仅当它们全部以大写形式出现时,如此处所示。

本规范定义了一致性标准,该标准适用于单个产品:实现其中包含的接口的用户代理。

以算法或特定步骤表达的一致性要求可以通过任何方式实现,只要最终结果是等效的。(特别是,本规范中定义的算法旨在易于理解,而不是为了高性能。)

使用 ECMAScript 实现本规范中定义的 API 的实现必须以符合 Web IDL 规范 [WEBIDL] 中定义的 ECMAScript 绑定的方式实现它们,因为本规范使用了该规范及其术语。

3. 协议概念

WebTransport 有两个主要的协议概念:会话和流。每个 WebTransport 会话 可以包含多个 WebTransport 流

这些不应与 协议名称 混淆,后者是应用层 API 构造。

3.1. WebTransport 会话

一个 WebTransport 会话 是指通过 HTTP/3 或 HTTP/2 底层连接建立的 WebTransport 会话。启用连接池时,一个连接上可能存在多个 WebTransport 会话

根据 [WEB-TRANSPORT-OVERVIEW] 的定义,一个 WebTransport 会话具有以下能力:

能力 定义
发送数据报 [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 流

耗尽一个 WebTransport 会话 session,请按照 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中的说明进行操作。

当服务器请求优雅关闭 CONNECT 流时,WebTransport 会话 session 处于耗尽状态,如 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中所述。

要使用可选整数 code 和可选字节序列 reason 终止一个 WebTransport 会话 session,请按照 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中的说明进行操作。

当服务器关闭 CONNECT 流时,WebTransport 会话 session 处于已终止状态,并可选择包含整数 code字节序列 reason,如 [WEB-TRANSPORT-OVERVIEW] Section 4.1 中所述。

3.2. WebTransport 流

WebTransport 流WebTransport 会话上可靠且按顺序排列的字节流的概念,如 [WEB-TRANSPORT-OVERVIEW] Section 4.3 中所述。

一个 WebTransport 流入向单向出向单向双向中的一种。

一个 WebTransport 流具有以下能力:

能力 定义 入向单向 出向单向 双向
发送 字节(可能带有 FIN) [WEB-TRANSPORT-OVERVIEW] Section 4.3
接收 字节(可能带有 FIN) [WEB-TRANSPORT-OVERVIEW] Section 4.3
发送 STOP_SENDING [WEB-TRANSPORT-OVERVIEW] Section 4.3
重置 WebTransport 流 [WEB-TRANSPORT-OVERVIEW] Section 4.3

一个 WebTransport 流具有以下信号:

事件 定义 入向单向 出向单向 双向
STOP_SENDING [WEB-TRANSPORT-OVERVIEW] Section 4.3
RESET_STREAM [WEB-TRANSPORT-OVERVIEW] Section 4.3
流量控制 [WEB-TRANSPORT-OVERVIEW] Section 4.3

4. WebTransportDatagramsWritable 接口

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

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

4.1. 内部插槽

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

内部插槽 描述 (非规范性)
[[OutgoingDatagramsQueue]] 一个由出站数据报、时间戳和 promise 组成的元组队列,该 promise 在数据报发送或丢弃时被解析。
[[Transport]] 拥有此 WebTransportDatagramsWritable 的一个 WebTransport
[[SendGroup]] 一个可选的 WebTransportSendGroup,或者为 null。
[[SendOrder]] 一个可选的发送顺序号,默认为 0。

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

  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
[[Writables]] 一个 WebTransportDatagramsWritable 流的有序集合,最初为空。
[[IncomingDatagramsQueue]] 一个由传入数据报和时间戳对组成的队列。
[[IncomingDatagramsPullPromise]] 一个由 pullDatagrams 设置的 promise,用于等待传入数据报。
[[IncomingDatagramsHighWaterMark]] 一个表示传入数据报的高水位线unrestricted double
[[IncomingDatagramsExpirationDuration]] 一个表示传入数据报的过期持续时间(以毫秒为单位)的 unrestricted double,或者为 null。
[[OutgoingDatagramsHighWaterMark]] 一个表示传出数据报的高水位线unrestricted double
[[OutgoingDatagramsExpirationDuration]] 一个 unrestricted double 值,表示传出数据报的过期持续时间(以毫秒为单位),或者为 null。
[[OutgoingMaxDatagramSize]] 一个表示传出数据报的最大大小的整数。
最大数据报大小取决于所使用的协议。在 HTTP/3 [WEB-TRANSPORT-HTTP3] 中,该值与路径 MTU 的估计值相关,该估计值会减少一些实现定义量以考虑任何开销。在 HTTP/2 [WEB-TRANSPORT-HTTP2] 中,没有等效的限制。

由于数据报的处理通常涉及将整个数据报保存在内存中,因此实现将对大小有限制。未来的协议扩展可以启用这些大小限制的信令,以用于所有协议变体。

对于任何 WebTransport 对象,用户代理可以更新 [[OutgoingMaxDatagramSize]],其 [[State]]"connecting""connected"

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

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

    [[Readable]]

    readable

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

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

  8. 返回 一个 promise 被解析,并返回 undefined。

receiveDatagrams,给定一个 WebTransport 对象 transport,运行以下步骤:

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

  2. queuedatagrams.[[IncomingDatagramsQueue]]

  3. durationdatagrams.[[IncomingDatagramsExpirationDuration]]

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

  5. sessiontransport.[[Session]]

  6. session 上有 可用的传入数据报时:

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

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

    3. chunkdatagramtimestamp 对。

    4. 入队 chunkqueue

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

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

  9. queue 不为空时:

    1. bytestimestampqueue 的第一个元素。

    2. 如果自 timestamp 以来经过的时间超过 duration,则 出队 queue

    3. 否则,跳出此循环。

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

    1. bytestimestamp出队 queue 的结果。

    2. promisedatagrams.[[IncomingDatagramsPullPromise]]

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

    4. 将一个网络任务入队,使用 transport 运行以下步骤:

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

      2. 入队 chunkdatagrams.[[Readable]]

      3. 解析 promise 并返回 undefined。

对于任何 WebTransport 对象,用户代理都应该尽快运行 receiveDatagrams,其 [[State]]"connected",只要算法能够取得进展。

6. WebTransport 接口

WebTransport 提供了一个 API,用于访问 [WEB-TRANSPORT-OVERVIEW] 中定义的底层传输功能。

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

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

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

  readonly attribute WebTransportDatagramDuplexStream datagrams;

  Promise<WebTransportBidirectionalStream> createBidirectionalStream(
      optional WebTransportSendStreamOptions 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. parsedURLURL 记录,此记录是通过使用 baseURL 解析 url 得到的。

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

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

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

  6. allowPoolingoptionsallowPooling

  7. dedicatedallowPooling 的否定。

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

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

  10. requireUnreliableoptionsrequireUnreliable

  11. congestionControloptionscongestionControl

  12. 如果 congestionControl 不是 "default",并且用户代理不支持任何针对 congestionControl 优化的拥塞控制算法(如 [RFC9002] Section 7 允许的那样),则将 congestionControl 设置为 "default"

  13. protocolsoptionsprotocols

  14. 如果 protocols 中的任何值出现多次,或者不符合构成 WebTransport 协议定义的协商应用程序协议值的元素的条件,或者其同构编码长度为 0 或超过 512,则抛出一个 SyntaxError 异常。 [WEB-TRANSPORT-OVERVIEW] Section 3.1

  15. anticipatedConcurrentIncomingUnidirectionalStreamsoptionsanticipatedConcurrentIncomingUnidirectionalStreams

  16. anticipatedConcurrentIncomingBidirectionalStreamsoptionsanticipatedConcurrentIncomingBidirectionalStreams

  17. incomingDatagrams 为一个新的 ReadableStream

  18. datagrams创建一个 WebTransportDatagramDuplexStream 的结果,其readable 设置为 incomingDatagrams

  19. 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

  20. pullDatagramsAlgorithm 为一个运行 pullDatagrams,参数为 transport 的操作。

注意: 建议将 64kB 缓冲区用于数据报,因为有效的最大 WebTransport 数据报帧大小具有 QUIC 最大数据报帧大小的上限,建议为 64kB (参见 [QUIC-DATAGRAM] Section 3)。这将确保流不会因数据报大于缓冲区而出错。

  1. 设置字节读取支持 incomingDatagrams,其中 pullAlgorithm 设置为 pullDatagramsAlgorithm,并且 highWaterMark 设置为 0。

  2. pullBidirectionalStreamAlgorithm 为一个运行 pullBidirectionalStream,参数为 transport 的操作。

  3. 设置 transport.[[IncomingBidirectionalStreams]],其中 pullAlgorithm 设置为 pullBidirectionalStreamAlgorithm,并且 highWaterMark 设置为 0。

  4. pullUnidirectionalStreamAlgorithm 为一个运行 pullUnidirectionalStream,参数为 transport 的操作。

  5. 设置 transport.[[IncomingUnidirectionalStreams]],其中 pullAlgorithm 设置为 pullUnidirectionalStreamAlgorithm,并且 highWaterMark 设置为 0。

  6. 使用 HTTP 初始化 WebTransport,参数为 transportparsedURLdedicatedrequireUnreliablecongestionControlprotocolsserverCertificateHashes

  7. 返回 transport

通过 HTTP 初始化 WebTransport,给定一个 WebTransport 对象 transport,一个 URL 记录 url,一个布尔值 dedicated,一个布尔值 requireUnreliable,一个 WebTransportCongestionControl congestionControl, 一个 protocols 数组,以及一个 sequence<WebTransportHash> serverCertificateHashes,运行以下步骤。
  1. clienttransport相关设置对象

  2. originclient

  3. request 为一个新的 请求,其 URLurl客户端client策略容器client策略容器目标为一个空字符串, origin重定向模式为 "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. 如果 dedicated 为 false,则设 newConnection 为 "no"; 否则为 "yes-and-dedicated"。

    2. connection获取连接的结果,使用 networkPartitionKeyurl、false、newConnectionrequireUnreliable。如果用户代理 支持多个拥塞控制算法,请选择一个适合 congestionControl 的算法,用于在此 connection 上发送数据。当获取连接时,如果 指定了 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 不包含值为 1 的 SETTINGS_ENABLE_WEBTRANPORT,或者它不 包含值为 1 的 H3_DATAGRAM,则中止剩余步骤并 将网络任务排队transport 以运行以下步骤:

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

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

      3. 清理 transport,使用 error

    6. 建立一个 WebTransport 会话,使用 originprotocolsconnection 上。

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

    7. 如果上一步失败,则中止剩余步骤并 将网络任务排队transport 以运行以下步骤:

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

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

      3. 清理 transport,使用 error

    8. session 为已建立的 WebTransport 会话

    9. 断言:maxDatagramSize 是一个整数。

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

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

        1. 并行终止 session

        2. 中止这些步骤。

      2. 设置 transport.[[State]]"connected"

      3. 设置 transport.[[Session]]session

      4. 设置 transport.[[Protocol]] 为协商的应用程序的字符串值 协议(如果存在),遵循 [WEB-TRANSPORT-OVERVIEW] Section 3.1, 或者如果不存在,则为 ""

      5. 如果连接是 HTTP/3 连接,则设置 transport.[[Reliability]]"supports-unreliable"

      6. 如果连接是 HTTP/2 连接 [WEB-TRANSPORT-HTTP2],则设置 transport[[Reliability]]"reliable-only"

      7. 解析 transport.[[Ready]] 为未定义。

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. 等待直到存在一个 可用的传入双向流

    2. internalStream接收双向流的结果。

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

      1. stream创建一个 WebTransportBidirectionalStream (使用 internalStreamtransport)的结果。

      2. Enqueuestream 排入 transport[[IncomingBidirectionalStreams]]

      3. 解析 p 为未定义。

  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. 等待直到存在一个 可用的传入单向流

    2. internalStream接收传入单向流的结果。

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

      1. stream创建一个 WebTransportReceiveStream (使用 internalStreamtransport)的结果。

      2. Enqueuestream 排入 transport[[IncomingUnidirectionalStreams]]

      3. 解析 p 为未定义。

  6. 返回 p

6.3. 属性

ready, 类型为 Promise<undefined>, readonly

在获取时,它必须返回 this[[Ready]]

closed, 类型为 Promise<WebTransportCloseInfo>, readonly

在获取时,它必须返回 this[[Closed]]

draining, 类型为 Promise<undefined>, readonly

在获取时,它必须返回 this[[Draining]]

datagrams, 类型为 WebTransportDatagramDuplexStream, readonly

用于通过此会话发送和接收数据报的单个双工流。 datagrams 属性的 getter 步骤应为:

  1. 返回 this[[Datagrams]]

incomingBidirectionalStreams, 类型为 ReadableStream, readonly

返回已从服务器接收的 ReadableStream WebTransportBidirectionalStream

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

incomingBidirectionalStreams 属性的 getter 步骤应为:

  1. 返回 this[[IncomingBidirectionalStreams]]

incomingUnidirectionalStreams, 类型为 ReadableStream, readonly

单向流的 ReadableStream,每个流都由一个 WebTransportReceiveStream 表示,这些流已从服务器接收。

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

incomingUnidirectionalStreams 的 getter 步骤为:

  1. 返回 this.[[IncomingUnidirectionalStreams]]

reliability, 类型为 WebTransportReliabilityMode, readonly

连接是否支持不可靠(通过 UDP)传输或仅支持可靠(通过 TCP 回退)传输。 在建立连接之前,返回 "pending"。 getter 步骤是返回 this[[Reliability]]

congestionControl, 类型为 WebTransportCongestionControl, readonly

应用程序的偏好(如果在构造函数中请求,并且用户代理满足),用于针对此连接上的发送进行吞吐量或低延迟优化的拥塞控制算法。如果请求了偏好但未满足,则该值为 "default" getter 步骤是返回 this[[CongestionControl]]

supportsReliableOnly, 类型为 boolean, readonly

如果用户代理支持通过专门可靠的 WebTransport 会话 连接,则返回 true;否则返回 false。

anticipatedConcurrentIncomingUnidirectionalStreams, 类型为 unsigned short, nullable

可选地让应用程序指定它预计服务器创建的并发打开的 传入单向流的数量。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑 [[AnticipatedConcurrentIncomingUnidirectionalStreams]] 来减少未来的往返行程。

getter 步骤是返回 this[[AnticipatedConcurrentIncomingUnidirectionalStreams]]

给定 value,setter 步骤是将 this[[AnticipatedConcurrentIncomingUnidirectionalStreams]] 设置为 value

anticipatedConcurrentIncomingBidirectionalStreams, 类型为 unsigned short, nullable

可选地让应用程序指定它预计服务器创建的并发打开的 双向流的数量。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑 [[AnticipatedConcurrentIncomingBidirectionalStreams]] 来减少未来的往返行程。

getter 步骤是返回 this[[AnticipatedConcurrentIncomingBidirectionalStreams]]

给定 value,setter 步骤是将 this[[AnticipatedConcurrentIncomingBidirectionalStreams]] 设置为 value

注意: 设置 anticipatedConcurrentIncomingUnidirectionalStreamsanticipatedConcurrentIncomingBidirectionalStreams 不能保证 应用程序将接收到它预计的流数。

protocol, 类型为 DOMString, readonly

一旦建立了 WebTransport 会话,并且使用了 protocols 构造函数选项来提供非空数组,则返回服务器选择的应用程序级协议(如果有)。否则,返回一个空字符串。 getter 步骤是返回 this[[Protocol]]

6.4. 方法

关闭(closeInfo)

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

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

  1. transportthis

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

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

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

    2. errortransport 进行 清理

    3. 中止这些步骤。

  4. sessiontransport.[[Session]]

  5. codecloseInfo.closeCode

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

  7. reasonreasonString,并进行 UTF-8 编码

  8. 并行,用 codereason 终止 session

    注意:这也会对 transport.[[SendStreams]][[ReceiveStreams]] 包含的 WebTransport 流进行 复位发送 STOP_SENDING

  9. AbortErrorcloseInfotransport 进行 清理

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 密钥材料导出器 导出密钥材料, 用于其 底层连接

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

  1. transportthis

  2. labelLengthlabel.字节长度

  3. 如果 labelLength 超过 255,返回一个 被拒绝的 promise,错误类型为 RangeError

  4. contextLength 为 0。

  5. 如果提供了 context,则设置 contextLengthcontext.字节长度

  6. 如果 contextLength 超过 255,返回一个 被拒绝的 promise,错误类型为 RangeError

  7. p 为一个新的 promise。

  8. 并行运行以下步骤,但当 transport[[State]] 变为 "closed""failed" 时中止,并 队列化一个网络任务,用 transport 拒绝 p,并给出 InvalidStateError

    1. keyingMaterial 为导出 TLS 密钥材料的结果,定义见 [WEB-TRANSPORT-HTTP3] 第 4.7 节, 参数为 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 为一个新的、对 transport.[[Session]] 有效且唯一的流 ID,定义见 [QUIC] 第 19.11 节。如果由于资源耗尽无法马上获得, 且 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 为一个新的、对 transport.[[Session]] 有效且唯一的流 ID,定义见 [QUIC] 第 19.11 节。如果由于资源耗尽无法马上获得, 且 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,则使用 error 拒绝 stream.[[PendingOperation]]

    2. Error 使用 errorstream 进行错误处理。

  15. 对于 receiveStreams 中的每个 stream,使用 error 错误化 stream

    注意: 脚本作者可以注入代码,这些代码在 Promise 解析中同步运行。因此,从这里开始,不要接触 transport,因为它可能会被脚本以不可预测的方式修改。 这也适用于调用此过程的逻辑。

  16. 如果给定了 closeInfo,则:

    1. Resolve 使用 closeInfo 解析 closed

    2. 断言:ready解决

    3. 关闭 incomingBidirectionalStreams

    4. 关闭 incomingUnidirectionalStreams

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

    6. 关闭 incomingDatagrams

  17. 否则:

    1. Reject 使用 error 拒绝 closed

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

    3. Reject 使用 error 拒绝 ready

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

    5. Error 使用 error 错误处理 incomingBidirectionalStreams

    6. Error 使用 error 错误处理 incomingUnidirectionalStreams

    7. outgoingDatagramWritables 中的每个 writable,使用 error 错误处理该 writable

    8. Error 使用 error 错误处理 incomingDatagrams

要与一个 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 对象进行垃圾回收,则用户代理必须 终止 WebTransport 会话,使用应用程序错误代码 0 和应用程序错误消息 ""

6.9. 配置

dictionary WebTransportHash {
  DOMString algorithm;
  BufferSource value;
};

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

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

WebTransportOptions 是一个参数字典,用于确定如何建立和使用 WebTransport 会话

allowPooling, 类型为 boolean, 默认为 false

如果设置为 true,则可以池化 WebTransport 会话,也就是说,可以与其他 WebTransport 会话共享其 底层连接

requireUnreliable, 类型为 boolean, 默认为 false

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

serverCertificateHashes, 类型为 sequence<WebTransportHash>

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

如果支持且为非空,则用户代理应仅在可以成功地针对 serverCertificateHashes 验证证书哈希并满足 自定义证书要求时,才认为服务器证书是受信任的。用户代理应忽略任何使用未知 algorithm 的哈希。 如果为空,则用户代理应使用它通常用于正常 fetch 操作的证书验证程序。

这不能与 allowPooling 一起使用。

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

可选地指定应用程序首选的拥塞控制算法,该算法经过调整以用于在此连接上发送数据时的吞吐量或低延迟。这是对用户代理的提示。

由于在编写时,浏览器中缺乏针对低延迟优化的拥塞控制算法的实现,因此此配置选项被认为是存在风险的功能。

anticipatedConcurrentIncomingUnidirectionalStreams, 类型为 unsigned short, nullable, 默认为 null

可选地让应用程序指定它预计服务器创建的并发打开的 传入单向流的数量。 用户代理最初必须允许来自服务器的至少 100 个传入单向流。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑 [[AnticipatedConcurrentIncomingUnidirectionalStreams]] 来减少往返行程。

anticipatedConcurrentIncomingBidirectionalStreams, 类型为 unsigned short, nullable, 默认为 null

可选地让应用程序指定它预计服务器创建的并发打开的 双向流的数量。 用户代理最初必须允许服务器创建至少 100 个双向流。 如果不是 null,则用户代理应尝试通过在其与服务器的协商中考虑 [[AnticipatedConcurrentIncomingBidirectionalStreams]] 来减少往返行程。

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

一个可选提供的应用程序级协议名称数组。选择首选的应用程序协议并将其传达给客户端对于服务器是可选的。 如果未提供合适的协议,服务器可能会拒绝该请求。

计算证书哈希,给定一个 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 的错误代码相关的信息。 此信息用于设置 CONNECTION_CLOSE 帧的错误代码和原因。

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

在整个连接上观察到的最小往返时间。

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, nullable

getter 步骤是:

  1. 返回this[[SendGroup]]

给定value,setter 步骤是:

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

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

sendOrder, 类型为 long long

getter 步骤是:

  1. 返回this[[SendOrder]]

给定value,setter 步骤是:

  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]] 一个WebTransport,它拥有此WebTransportSendStream
[[SendGroup]] 一个可选的WebTransportSendGroup,或 null。
[[SendOrder]] 一个可选的发送顺序号,默认为 0。
[[AtomicWriteRequests]] 一组有序集合的 promises,用于跟踪底层接收器排队处理的原子写入请求的子集。
[[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]]

    一个空的 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. 并行运行以下步骤:

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

    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. 如果 reasonWebTransportError 并且 reason.[[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. 服务器发送的STOP_SENDING信号

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

  2. code 为附加到STOP_SENDING帧的应用协议错误代码。 [QUIC]

    注意: 有效的 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. 错误 streamerror

7.6. WebTransportSendStreamStats 字典

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

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

字典应具有以下属性:

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

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

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

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

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

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

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

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

8. 接口 WebTransportSendGroup

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

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

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

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

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

8.1. 方法

getStats()

汇总 WebTransportSendStream 的统计数据,这些流在 分组到当前 sendGroup 下,并异步报告结果。

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

  1. 创建一个新的 p promise。

  2. streams 是所有 WebTransportSendStream ,其 [[SendGroup]] 是当前 sendGroup。

  3. 并行运行以下步骤:

    1. 收集 streams 中所有流的统计数据。

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

      1. stats 为一个新的 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流

它是一个 ReadableStreamUint8Array ,可以读取以消费从服务器接收的数据。WebTransportReceiveStream 是一个 可读字节流, 因此允许其消费者使用 BYOB读取器 以及 默认读取器

[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. transport 设置为 stream.[[Transport]].

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

  3. 创建一个新的 promise.

  4. code 设置为 0.

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

  6. 如果 code 小于 0,则将其设置为 0.

  7. 如果 code 大于 4294967295,则将其设置为 4294967295.

  8. 移除 streamtransport.[[SendStreams]].

  9. 并行运行以下步骤:

    1. 发送 STOP_SENDING 并提供 internalStreamcode.

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

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

      2. 解析 promise 并返回 undefined.

  10. 返回 promise.

9.4. 来自服务器的重置信号

每当与一个 WebTransport流 相关联的 WebTransportReceiveStream stream 接收到来自服务器的 RESET_STREAM 信号时,请执行以下步骤:
  1. transport 设为 stream.[[Transport]].

  2. code 设为附加到 RESET_STREAM_AT 或 WT_RESET_STREAM 帧的应用协议错误代码。 [RELIABLE-RESET] [WEB-TRANSPORT-HTTP2]

    注意: 有效的 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. 错误 streamerror

9.5. WebTransportReceiveStreamStats 字典

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

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

字典应具有以下属性:

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

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

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

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

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

10. 接口 WebTransportBidirectionalStream

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

10.1. 内部插槽

一个 WebTransportBidirectionalStream 具有以下内部槽。

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

10.2. 属性

readable, 类型为 WebTransportReceiveStream, 只读

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

writable, 类型为 WebTransportSendStream, 只读

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

10.3. 过程

创建一个 WebTransportBidirectionalStream ,并绑定一个 双向 WebTransport流 internalStream、一个 WebTransport 对象 transport 和一个 sendOrder,请执行以下步骤:
  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

    注意: 此名称没有映射到遗留代码,因此 thiscode 为 0。

12.3. 属性

source, 类型为 WebTransportErrorSource, 只读

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

streamErrorCode, 类型为 unsigned long, 只读,nullable

获取器步骤是返回 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) 发送 RESET_STREAM_AT 带有 httpErrorCode 和与 [[CommittedOffset]] 对应的偏移量,加上任何流头;参见 [RELIABLE-RESET]
writable.close() 发送 STREAM 并设置 FIN 位
writable.getWriter().write(chunk)() 发送 STREAM
readable.cancel(error) 发送 STOP_SENDING 带有 httpErrorCode

注意:第3.2节 中所述 [QUIC], 接收 RESET_STREAM 帧或 RESET_STREAM_AT 帧 ([RELIABLE-RESET]) 不一定会向应用程序发出指示。接收重置可能会立即发出信号,打断流数据的传递,未使用的数据将被丢弃。然而,立即发出信号并非必需。

HTTP/3 协议行为 API 效果
会话 耗尽 等待 wt.draining

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

API 方法 HTTP/2 协议行为
writable.abort(error) 发送 WT_RESET_STREAM 带有错误
writable.close() 发送 WT_STREAM 并设置 FIN 位
writable.getWriter().write() 发送 WT_STREAM
writable.getWriter().close() 发送 WT_STREAM 并设置 FIN 位
writable.getWriter().abort(error) 发送 WT_RESET_STREAM 带有错误
readable.cancel(error) 发送 WT_STOP_SENDING 带有错误
readable.getReader().cancel(error) 发送 WT_STOP_SENDING 带有错误
wt.close(closeInfo) 终止会话并带有 closeInfo
HTTP/2 协议行为 API 效果
接收 WT_STOP_SENDING 带有错误 错误 writable 并带有 streamErrorCode
接收 WT_STREAM (等待 readable.getReader().read()).value
接收 WT_STREAM 并设置 FIN 位 (等待 readable.getReader().read()).done
接收 WT_RESET_STREAM 带有错误 错误 readable 并带有 streamErrorCode
会话干净地 终止 并带有 closeInfo
(等待 wt.closed).closeInfo, 并 错误 打开流
网络错误
(等待 wt.closed) 拒绝,并且 错误 打开流
会话 耗尽 等待 wt.draining

14. 隐私与安全注意事项

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

14.1. 通信机密性

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

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

14.2. 状态持久性

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

14.3. 协议安全性

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

  1. 确保远程服务器知道正在使用 WebTransport 协议,并确认远程服务器愿意使用 WebTransport 协议。[WEB-TRANSPORT-HTTP3] 使用 ALPN [RFC7301]、 HTTP/3 设置和 :protocol 伪标头的组合来标识 WebTransport 协议。[WEB-TRANSPORT-HTTP2] 使用 ALPN、 HTTP/2 设置和 :protocol 伪标头的组合来标识 WebTransport 协议。

  2. 允许服务器根据资源的来源过滤连接,该资源发起了传输会话。会话建立请求上的 Origin 标头字段携带此信息。

有关协议安全的注意事项在 [WEB-TRANSPORT-HTTP3][WEB-TRANSPORT-HTTP2] 的“安全注意事项”部分中进行了描述。

网络 API 通常可用于扫描本地网络以查找可用主机,因此可用于指纹识别和其他形式的攻击。WebTransport 遵循 WebSocket 方法 解决此问题: 直到端点被验证为 WebTransport 端点之前,具体的连接错误不会返回;因此,Web 应用程序无法区分不存在的端点和不愿意接受 Web 连接的端点。

14.4. 使用证书哈希进行身份验证

通常,用户代理通过验证所提供的 TLS 服务器证书的有效性与 URL 中的服务器名称 [RFC9525] 相比来对其自身与远程端点之间的 TLS 连接进行身份验证。这是通过将服务器证书链接到用户代理维护的信任锚之一来实现的;相关的信任锚负责对证书中的服务器名称进行身份验证。我们将此系统称为 Web PKI。

此 API 为 Web 应用程序提供了一种连接到远程网络端点的能力,该端点通过指定的服务器证书而不是其服务器名称进行身份验证。此机制使连接到难以获取长期证书的端点成为可能,包括那些本质上是短暂的主机(例如短期虚拟机),或不可公开路由的主机。由于此机制替代了基于 Web PKI 的单个连接身份验证,我们需要比较两者的安全属性。

只有在远程服务器拥有与指定证书的公钥相对应的私钥时,它才能成功执行 TLS 握手。API 使用其哈希值来标识证书。只要所使用的加密哈希函数具有第二原像抗性,这种方法才是安全的。本文档中唯一定义的函数是 SHA-256;API 提供了一种通过允许指定多个算法-哈希对来引入新哈希函数的方法。

需要注意的是,Web PKI 除了简单地为服务器名称建立信任链之外,还提供了额外的安全机制。其中之一是处理证书吊销。在使用的证书是短暂的情况下,这种机制没有必要。在其他情况下,Web 应用程序必须考虑证书哈希值的供应机制;例如,如果哈希值作为缓存的 HTTP 资源提供,则在相应证书因泄露而被轮换时需要使缓存失效。Web PKI 提供的另一个安全功能是针对密钥生成的某些问题的保障,例如拒绝具有已知弱密钥的证书;虽然本文档没有提供任何具体的指导,但浏览器可以作为实现定义的行为的一部分拒绝这些证书。

Web PKI 对证书实施了过期周期要求。此要求限制了潜在密钥泄露的范围;它还迫使服务器运营商设计支持并积极执行密钥轮换的系统。出于这个原因,WebTransport 对证书施加了类似的过期要求;由于证书预计是短暂或短期的,因此过期周期限制为两周。两周的限制是在尽可能低地设置过期限制以最大限度地减少密钥泄露的后果之间,以及保持其足够高以适应设备之间的时钟偏差,并降低客户端和服务器端同步证书的成本之间的平衡。

WebTransport API 允许应用程序一次指定多个证书哈希值,从而允许客户端在新证书推出期间接受多个证书。

WebRTC 中类似的机制不同,WebTransport 中的服务器证书哈希 API 不提供任何身份验证客户端的方法;客户端知道服务器证书是什么或如何联系它这一事实并不足够。应用程序如果需要,必须在带内建立客户端的身份。

14.5. 指纹识别与跟踪

此 API 使站点能够生成网络活动并密切观察此活动的效果。通过这种方式获取的信息可能是识别性的信息。

需要认识到其他 Web 平台 API(例如 fetch[webrtc])提供了非常类似的网络功能。因此,添加 WebTransport 对隐私的不利影响微乎其微。本节中的注意事项同样适用于其他网络功能。

测量网络特性需要使用网络并测量该使用的效果,而这两者都由此 API 启用。WebTransport 为站点提供了向其选择的服务器生成网络活动并观察效果的能力。可以观察网络路径的稳定属性和网络使用的动态效果。

有关网络的信息可以通过服务器自己的网络堆栈直接获得,通过客户端消耗或传输数据的速率间接获得,或者作为由 API 提供的统计信息的一部分获得(参见 § 6.13 WebTransportConnectionStats Dictionary)。因此,对用户代理信息的限制并不是管理这些隐私风险的唯一机制。

14.5.1. 静态观察

站点可以观察用户代理与选定服务器之间的可用网络容量或往返时间(RTT)。当与其他跟踪矢量结合时,这些信息可能具有识别性。RTT 还可以揭示用户代理的物理位置,尤其是在可以从多个观察点进行多次测量的情况下。

尽管网络是共享的,但网络使用通常是零散的,这意味着站点通常可以观察未受争用或负载轻的网络路径的容量和往返时间。这些属性对于许多人来说是稳定的,因为他们的网络位置没有改变,网络瓶颈的位置--决定可用容量--可能靠近用户代理。

14.5.2. 共享网络

受争用的链接为站点提供了启用跨站点识别的机会,这可能会被用来执行未经授权的跟踪 [UNSANCTIONED-TRACKING]。 网络容量是有限的共享资源,因此用户代理同时访问不同站点可能会揭示每个站点呈现的身份之间的连接。

在一个站点上使用网络功能会减少其他站点可用的容量,可以使用网络 API 观察到这一点。网络使用和指标可能会动态变化,因此任何变化都可以实时观察到。这可能允许站点增加信心,即不同站点上的活动来自同一用户。

用户代理可以限制或降低对反馈机制(例如统计信息(§ 6.13 WebTransportConnectionStats Dictionary))的访问,针对不活跃或未获得焦点的站点(HTML § 6.6 Focus)。如所述,这并不能阻止服务器观察网络中的变化。

14.5.3. 池化会话

与共享网络场景类似,当会话在单个连接上池化时,一个会话的信息会受到另一个会话活动的影响。一个会话可以推断关于另一个会话活动的信息,例如另一个应用程序发送数据的速率。

使用共享连接已经允许服务器关联会话。使用 网络分区键 禁用池化,其中共享会话的使用可能启用不必要的跨站点识别。

15. 示例

15.1. 发送数据报缓冲区

此部分为非规范性内容。

可以通过使用 datagrams' createWritable 方法以及生成的流的 writer 来发送数据报缓冲区。在以下示例中,仅当传输准备好发送时才发送数据报。

async function sendDatagrams(url, datagrams) {
  const wt = new WebTransport(url);
  const writable = wt.datagrams.createWritable();
  const writer = writable.getWriter();
  for (const 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 读取器,这允许更精确地控制缓冲区分配,以避免复制。此示例将数据报读取到一个 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 读取器,这允许更精确地控制缓冲区分配,以避免复制。此示例从 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. Content Security Policy Level 3. 2025年6月30日. 工作草案. 网址: https://www.w3.org/TR/CSP3/
[DOM]
Anne van Kesteren. DOM Standard. 现行标准. 网址: https://dom.spec.whatwg.org/
[ECMASCRIPT-6.0]
Allen Wirfs-Brock. ECMA-262 6th Edition, The ECMAScript 2015 Language Specification. 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 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 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: A UDP-Based Multiplexed and Secure Transport. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc9000
[QUIC-DATAGRAM]
Tommy Pauly; Eric Kinnear; David Schinazi. An Unreliable Datagram Extension to QUIC. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc9221
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. 最佳实践. 网址: https://datatracker.ietf.org/doc/html/rfc2119
[RFC3279]
L. Bassham; W. Polk; R. Housley. Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile. 2002年4月. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc3279
[RFC5280]
D. Cooper; 等. Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile. 2008年5月. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc5280
[RFC8174]
B. Leiba. Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. 2017年5月. 最佳实践. 网址: https://www.rfc-editor.org/rfc/rfc8174
[RFC8422]
Y. Nir; S. Josefsson; M. Pegourie-Gonnard. Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier. 2018年8月. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc8422
[RFC9002]
J. Iyengar, Ed.; I. Swett, Ed.. QUIC Loss Detection and Congestion Control. 2021年5月. 建议标准. 网址: https://www.rfc-editor.org/rfc/rfc9002
[RFC9525]
P. Saint-Andre; R. Salz. Service Identity in 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 Standard. 现行标准. 网址: https://url.spec.whatwg.org/
[WEB-TRANSPORT-HTTP2]
Alan Frindell; 等. WebTransport over HTTP/2. 互联网草案. 网址: https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http2
[WEB-TRANSPORT-HTTP3]
Alan Frindell; Eric Kinnear; Victor Vasiliev. WebTransport over HTTP/3. 互联网草案. 网址: 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 Standard. 现行标准. 网址: https://webidl.spec.whatwg.org/
[WEBTRANSPORT]
Nidhi Jaju; Victor Vasiliev. WebTransport. 2025年2月12日. 工作草案. 网址: https://www.w3.org/TR/webtransport/

信息性参考文献

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

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

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