目标
目标是统一整个网络平台上的获取操作,并提供一致的处理方式,包括以下内容:
- URL 方案
- 重定向
- 跨域语义
- CSP [CSP]
- Fetch Metadata [FETCH-METADATA]
- 服务工作线程 [SW]
- 混合内容 [MIX]
- 升级不安全请求 [UPGRADE-INSECURE-REQUESTS]
- `
Referer
` [REFERRER]
为此,它还取代了最初定义于The Web Origin Concept中的 HTTP `Origin
` 标头语义。 [ORIGIN]
1. 前言
在高层次上,获取资源是一个相当简单的操作。请求进入,响应出来。然而,这一操作的细节相当复杂,过去往往没有被仔细记录下来,且在不同的 API 中存在差异。
众多 API 提供了获取资源的能力,例如 HTML 的 img
和 script
元素,CSS 的 cursor
和
list-style-image
,
以及 navigator.sendBeacon()
和 self.importScripts()
JavaScript API。Fetch
标准为这些功能提供了统一的架构,使它们在处理重定向和 CORS 协议等各种获取方面保持一致。
Fetch 标准还定义了 fetch()
JavaScript API,它以相对较低的抽象层次暴露了大部分网络功能。
2. 基础设施
本规范依赖于 Infra 标准。[INFRA]
本规范使用了以下标准中的术语:ABNF、Encoding、HTML、 HTTP、MIME Sniffing、Streams、URL、Web IDL、 和 WebSockets。[ABNF] [ENCODING] [HTML] [HTTP] [MIMESNIFF] [STREAMS] [URL] [WEBIDL] [WEBSOCKETS]
ABNF 指由 HTTP 增强的 ABNF(特别是添加了
#
)以及 RFC 7405。[RFC7405]
凭据是指 HTTP Cookie、TLS 客户端证书,以及 认证条目(用于 HTTP 认证)。[COOKIES] [TLS] [HTTP]
Fetch 参数 是 结构体,用于 Fetch 算法中的簿记细节。它包含以下 项:
- 请求
- 一个 请求。
- 处理请求体块长度(默认值为 null)
- 处理请求体结束(默认值为 null)
- 处理早期提示响应(默认值为 null)
- 处理响应(默认值为 null)
- 处理响应体结束(默认值为 null)
- 处理响应体消耗(默认值为 null)
- null 或算法。
- 任务目的地(默认值为 null)
- null、全局对象,或 并行队列。
- 跨域隔离能力(默认值为 false)
- 一个布尔值。
- 控制器(默认值为一个新的 Fetch 控制器)
- 一个 Fetch 控制器。
- 时序信息
- 一个 Fetch 时序信息。
- 预加载响应候选项(默认值为 null)
- null、"
pending
",或 响应。
Fetch 控制器 是一个 结构体,用于让 Fetch 的调用者在开始后对其执行某些操作。它包含以下 项:
- 状态(默认值为
"
ongoing
") - "
ongoing
"、"terminated
" 或 "aborted
"。 - 完整时序信息(默认值为 null)
- null 或 Fetch 时序信息。
- 报告时序步骤(默认值为 null)
- null 或接受一个 全局对象 的算法。
- 序列化的中止原因(默认值为 null)
- null 或 记录(结构化序列化 的结果)。
- 下一个手动重定向步骤(默认值为 null)
- null 或不接受任何参数的算法。
要为一个 Fetch 控制器 controller 执行 处理下一个手动重定向:
-
断言:controller 的 下一个手动重定向步骤 不为 null。
-
调用 controller 的 下一个手动重定向步骤。
要中止一个fetch 控制器 controller,可选error:
-
将 controller 的 状态 设置为 "
aborted
"。 -
将 fallbackError 设置为 "
AbortError
"DOMException
。 -
如果未给定 error,则将其设置为 fallbackError。
-
将 serializedError 设置为 结构化序列化(error)。 如果抛出异常,则捕获并将 serializedError 设置为 结构化序列化(fallbackError)。
-
将 controller 的 序列化的中止原因 设置为 serializedError。
要反序列化一个序列化的中止原因,给定 null 或 记录 abortReason 及 realm realm:
-
将 fallbackError 设置为 "
AbortError
"DOMException
。 -
将 deserializedError 设置为 fallbackError。
-
如果 abortReason 不为 null,则将 deserializedError 设置为 结构化反序列化(abortReason, realm)。如果抛出异常或返回 undefined,则将 deserializedError 设置为 fallbackError。
-
返回 deserializedError。
一个 Fetch
参数 fetchParams 如果其 控制器 的
状态 是
"aborted
",则为中止。
一个 Fetch
参数 fetchParams 如果其 控制器 的
状态 是
"aborted
" 或 "terminated
",则为取消。
Fetch 时序信息 是一个 结构体,用于维护 资源 时序 和 导航时序 所需的时序信息。它包含以下 项:[RESOURCE-TIMING] [NAVIGATION-TIMING]
- 开始时间(默认值为 0)
- 重定向开始时间(默认值为 0)
- 重定向结束时间(默认值为 0)
- 重定向后开始时间(默认值为 0)
- 最终服务工作线程开始时间(默认值为 0)
- 最终网络请求开始时间(默认值为 0)
- 第一个临时网络响应开始时间(默认值为 0)
- 最终网络响应开始时间(默认值为 0)
- 结束时间(默认值为 0)
- 一个
DOMHighResTimeStamp
。 - 最终连接时序信息(默认值为 null)
- null 或 连接时序信息。
- 服务器时序标头(默认值为 « »)
- 一个字符串 列表。
- 渲染阻塞(默认值为 false)
- 一个布尔值。
响应体信息 是一个 结构体,用于维护 资源时序 和 导航时序 所需的信息。它包含以下 项:[RESOURCE-TIMING] [NAVIGATION-TIMING]
- 编码大小(默认值为 0)
- 解码大小(默认值为 0)
- 一个数字。
- 内容类型(默认值为空字符串)
- 一个 ASCII 字符串。
- 内容编码(默认为空字符串)
- 一个 ASCII 字符串。
要 创建不透明时序信息,给定一个 Fetch 时序信息 timingInfo,返回一个新的 Fetch 时序信息,其 开始时间 和 重定向后开始时间 与 timingInfo 的 开始时间 相同。
要 序列化一个整数,将其表示为最短的小数字符串。
这将被 Infra 中更详细的算法替代。参见 infra/201。
2.1. URL
local scheme 是 "about
"、"blob
" 或
"data
"。
如果 URL 的 scheme 是 local scheme,则该 URL 是本地的。
此定义还被 Referrer Policy 使用。[REFERRER]
HTTP(S) scheme 是 "http
" 或
"https
"。
fetch scheme 是 "about
"、"blob
"、
"data
"、"file
" 或 HTTP(S) scheme。
HTTP(S) scheme 和 fetch scheme 也被 HTML 使用。[HTML]
2.2. HTTP
虽然 fetch 不仅仅涵盖 HTTP,但它从 HTTP
中借用了许多概念,并将这些概念应用于通过其他方式获取的资源(例如 data
URL)。
HTTP 制表符或空格 是 U+0009 制表符或 U+0020 空格。
HTTP 空白字符 是 U+000A 换行符、U+000D 回车符,或 HTTP 制表符或空格。
HTTP 空白字符 仅适用于在 HTTP 标头上下文之外重用的特定构造(例如 MIME 类型)。对于 HTTP 标头值,优先使用 HTTP 制表符或空格,而在该上下文之外则优先使用 ASCII 空白字符。与 ASCII 空白字符 不同的是,这里不包括 U+000C 换页符。
HTTP 换行字节 是 0x0A (LF) 或 0x0D (CR)。
HTTP 制表符或空格字节 是 0x09 (HT) 或 0x20 (SP)。
HTTP 空白字节 是 HTTP 换行字节 或 HTTP 制表符或空格字节。
要 收集 HTTP 引号字符串,从 字符串 input 中,给定一个 位置变量 position 及一个可选的布尔值 extract-value(默认为 false):
-
将 positionStart 设为 position。
-
将 value 设为空字符串。
-
将 position 前移 1。
-
执行如下操作,直到结束:
-
如果 extract-value 为 true,则返回 value。
-
返回 input 中从 positionStart 到 position 的 码点,包括在内。
2.2.1. 方法
CORS 安全列入白名单的方法 是
`GET
`、
`HEAD
` 或 `POST
` 的 方法。
禁止的方法 是 `CONNECT
`、`TRACE
` 或
`TRACK
` 的 方法,与
字节不区分大小写
相匹配。[HTTPVERBSEC1]、[HTTPVERBSEC2]、
[HTTPVERBSEC3]
要对 方法 进行 规范化,如果它是
`DELETE
`、`GET
`、
`HEAD
`、`OPTIONS
`、`POST
` 或 `PUT
` 的 字节不区分大小写
匹配项,则将其 字节大写化。
规范化 是为了向后兼容和跨 API 一致性进行的,因为 方法 实际上是 “区分大小写” 的。
使用
`patch
` 很可能会导致
`405 Method Not Allowed
`。而 `PATCH
` 更有可能成功。
对 方法
没有任何限制。
`CHICKEN
` 是完全可以接受的(而不是 `CHECKIN
` 的拼写错误)。除了那些 规范化
的方法外,对大小写也没有限制。
`Egg
` 或 `eGg
` 也是可以的,尽管为了统一建议使用大写。
2.2.2. 头
HTTP通常将头部称为“字段”或“头字段”。Web平台使用更口语化的术语“头”。[HTTP]
头列表本质上是一个专门的多映射:一个有序的键值对列表,可能有重复的键。由于除了`Set-Cookie
`之外的头在暴露给客户端JavaScript时总是被合并,实施可以选择更高效的表示,只要它们也支持与`Set-Cookie
`头相关的数据结构即可。
要从标头列表 list 中,根据给定的标头名称 name 和字符串 type 获取结构化字段值,请执行以下步骤。它们返回 null 或一个结构化字段值。
要在标头列表 list 中,根据给定的元组(标头名称 name, 结构化字段值 structuredValue)设置结构化字段值:
结构化字段值被定义为 HTTP 可以(最终)以有趣且高效的方式序列化的对象。目前,Fetch 仅支持将标头值作为字节序列,这意味着这些对象只能通过序列化设置在标头列表中,并且只能通过解析从标头列表中获取它们。将来,它们作为对象的特性可能会被端到端保留。[RFC9651]
这是获取、解码和拆分的实际工作方式,name参数为`A
`:
头(如在网络上) | 输出 |
---|---|
| « "nosniff ", "" »
|
| |
| « "" » |
| null |
| « "text/html;", x/x " »
|
| |
| « "x/x;test="hi" ", "y/y " »
|
| |
| « "x / x ", "", "", "1 " »
|
| |
| « ""1,2" ", "3 " »
|
|
要获取、解码和拆分一个头值value,运行这些步骤。它们返回字符串列表。
-
让input成为等同解码value的结果。
-
让position成为input的位置变量,初始指向input的开头。
-
让values成为一个字符串列表,初始为« »。
-
让temporaryValue为空字符串。
-
当为真时:
-
将结果收集的代码点序列(不是U+0022(")或U+002C(,))从input中,给定position,添加到temporaryValue。
结果可能是空字符串。
-
如果 position 没有超出 input 的末尾,且 input 中 position 位置的 代码点 是 U+0022 ("):
-
将从 input 中,基于 position,收集到的 HTTP 引号字符串 结果追加到 temporaryValue。
- 如果 position 没有超出 input 的末尾,则 继续。
-
-
移除 temporaryValue 开头和结尾的所有HTTP 制表符或空格。
-
将 temporaryValue 追加到 values。
-
将 temporaryValue 设为空字符串。
-
如果 position 超出 input 的末尾,则返回 values。
-
将 position 前进 1。
-
除了特别指定的调用点,以上算法不得直接调用。应使用 获取、解码并拆分 代替。
一个头是一个元组,由一个名字(一个头名)和值(一个头值)组成。
一个头值是一个字节序列,匹配以下条件:
-
没有前导或尾随HTTP标签或空格字节。
-
不包含0x00(NUL)或HTTP换行字节。
头值的定义并不基于字段值标记生成,因为它与已部署的内容不兼容。
要确定是否一个头(name,value)是一个CORS安全列出的请求头,运行这些步骤:
-
如果value的长度大于128,则返回false。
-
字节小写name并根据结果切换:
- `
accept
` -
如果value包含一个CORS不安全的请求头字节,则返回false。
- `
accept-language
` - `
content-language
` -
如果value包含一个不在范围0x30(0)到0x39(9)之间的字节,不在范围0x41(A)到0x5A(Z)之间的字节,不在范围0x61(a)到0x7A(z)之间的字节,并且不是0x20(SP),0x2A(*),0x2C(,),0x2D(-),0x2E(.),0x3B(;),或0x3D(=)的字节,则返回false。
- `
content-type
` -
-
如果value包含一个CORS不安全的请求头字节,则返回false。
-
让mimeType成为结果解析 的结果,解析value的结果。
-
如果mimeType解析失败,则返回false。
-
如果mimeType的本质不是"
application/x-www-form-urlencoded
","multipart/form-data
",或"text/plain
",则返回false。
这故意不使用提取MIME类型,因为该算法相对宽容,而服务器并不期望实现它。
如果使用了提取MIME类型,以下请求将不会导致CORS预检,服务器上的简单解析器可能会将请求体视为JSON:
fetch
( "https://victim.example/naïve-endpoint" , { method: "POST" , headers: [ [ "Content-Type" , "application/json" ], [ "Content-Type" , "text/plain" ] ], credentials: "include" , body: JSON. stringify( exerciseForTheReader) }); -
- `
range
` -
-
让rangeValue成为结果解析单个范围头值给定value和false。
-
如果rangeValue解析失败,则返回false。
-
如果rangeValue[0]为null,则返回false。
由于网络浏览器历史上没有发出类似`
bytes=-500
`的范围,本算法不将它们列入白名单。
-
- 其他情况
-
返回false。
- `
-
返回true。
对于`Content-Type
`头的安全列表,有有限的例外,如在CORS协议例外中记录的那样。
一个CORS不安全的请求头字节是一个字节byte,满足以下任一条件:
-
byte小于0x20且不是0x09 HT
-
byte是0x22("),0x28(左括号),0x29(右括号),0x3A(:),0x3C(<),0x3E(>),0x3F(?),0x40(@),0x5B([),0x5C(\),0x5D(]),0x7B({),0x7D(}),或0x7F DEL。
CORS不安全的请求头名称,给定头列表headers,确定如下:
CORS非通配符请求头名称是一个头名,是与`Authorization
`字节不区分大小写匹配的。
特权的no-CORS请求头名称是一个头名,是与以下之一字节不区分大小写匹配的
- `
Range
`。
这些是可以由特权API设置的头,如果它们的关联请求对象被复制,则会被保留,但如果请求被非特权API修改,则会被移除。
提供了一个帮助程序来将范围头添加到特定请求。
CORS安全列出的响应头名称,给定列表的头名list,是一个头名,与以下之一字节不区分大小写匹配的:
- `
Cache-Control
` - `
Content-Language
` - `
Content-Length
` - `
Content-Type
` - `
Expires
` - `
Last-Modified
` - `
Pragma
` - 列表中的任何项,不是一个禁止的响应头名。
no-CORS安全列出的请求头名称是一个头名,是与以下之一字节不区分大小写匹配的:
- `
Accept
` - `
Accept-Language
` - `
Content-Language
` - `
Content-Type
`
要确定是否一个头(name,value)是一个no-CORS安全列出的请求头,运行这些步骤:
-
如果name不是一个no-CORS安全列出的请求头名,则返回false。
-
返回是否(name,value)是一个CORS安全列出的请求头。
一个头(name,value)是禁止的请求头,如果这些步骤返回true:
-
如果name是一个字节不区分大小写匹配的:
- `
Accept-Charset
` - `
Accept-Encoding
` - `
Access-Control-Request-Headers
` - `
Access-Control-Request-Method
` - `
Connection
` - `
Content-Length
` - `
Cookie
` - `
Cookie2
` - `
Date
` - `
DNT
` - `
Expect
` - `
Host
` - `
Keep-Alive
` - `
Origin
` - `
Referer
` - `
Set-Cookie
` - `
TE
` - `
Trailer
` - `
Transfer-Encoding
` - `
Upgrade
` - `
Via
`
则返回true。
- `
-
如果name是一个字节不区分大小写匹配的:
- `
X-HTTP-Method
` - `
X-HTTP-Method-Override
` - `
X-Method-Override
`
则:
- `
-
返回false。
禁止的响应头名是一个头名,是与以下之一字节不区分大小写匹配的:
- `
Set-Cookie
` - `
Set-Cookie2
`
请求体头名称是一个头名,是与以下之一字节不区分大小写匹配的:
- `
Content-Encoding
` - `
Content-Language
` - `
Content-Location
` - `
Content-Type
`
要提取头值,给定头header,运行这些步骤:
要构建内容范围,给定一个整数rangeStart,一个整数rangeEnd,和一个整数fullLength,运行这些步骤:
要解析单个范围头值从字节序列value和一个布尔值allowWhitespace,运行这些步骤:
-
让data成为同构解码的value。
-
如果data不以"
bytes
"开头,则返回失败。 -
如果allowWhitespace为true,收集一个代码点序列,这些是HTTP制表符或空格,来自data给定position。
-
如果position内data的代码点不是U+003D(=),则返回失败。
-
将position前进1。
-
如果allowWhitespace为true,收集一个代码点序列,这些是HTTP制表符或空格,来自data给定position。
-
让rangeStartValue成为rangeStart,解释为十进制数,如果rangeStart不是空字符串;否则为null。
-
如果allowWhitespace为true,收集一个代码点序列,这些是HTTP制表符或空格,来自data给定position。
-
如果position内data的代码点不是U+002D(-),则返回失败。
-
将position前进1。
-
如果allowWhitespace为true,收集一个代码点序列,这些是HTTP制表符或空格,来自data给定position。
-
让rangeEndValue成为rangeEnd,解释为十进制数,如果rangeEnd不是空字符串;否则为null。
-
如果position不是data的末尾,则返回失败。
-
如果rangeEndValue和rangeStartValue是null,则返回失败。
-
如果rangeStartValue和rangeEndValue是数字,并且rangeStartValue大于rangeEndValue,则返回失败。
-
返回(rangeStartValue,rangeEndValue)。
范围结束或起始可以省略,例如`
bytes=0-
`或`bytes=-500
`是有效的范围。
解析单个范围头值对于允许的范围头值子集是成功的,但它是用户代理在请求媒体或恢复下载时使用的最常见形式。这种格式的范围头值可以使用添加范围头设置。
默认的`User-Agent
`值是一个实现定义的头值为`User-Agent
`头。
由于 Web 兼容性的限制,强烈建议网页浏览器将该值以 `Mozilla/5.0 (
` 开头,并整体仿照其他网页浏览器的格式。
要获取环境设置对象 environment 的
环境默认
`User-Agent
` 值:
-
令 userAgent 为 WebDriver BiDi 模拟的 User-Agent,对应 environment。
-
如果 userAgent 非 null,则返回 userAgent, 同构编码。
文档`Accept
`头值是`text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
`。
2.2.3. 状态码
状态码是一个在0到999范围内的整数。
将HTTP/1的status-code
映射到这个概念中的各种边缘情况正在问题 #1156中处理。
空体状态码是一个状态码,其值为101、103、204、205或304。
成功状态码是一个在200到299范围内的状态码。
重定向状态码是一个状态码,其值为301、302、303、307或308。
2.2.4. 主体
主体包括以下内容:
-
流(一个
ReadableStream
对象)。 -
长度(null或整数),初始为null。
要逐步读取一个主体body,给定一个算法processBodyChunk、一个算法processEndOfBody、一个算法processBodyError,以及一个可选的null、并行队列或全局对象taskDestination(默认为null),请执行以下步骤。processBodyChunk必须是接受字节序列的算法。processEndOfBody必须是接受无参数的算法。processBodyError必须是接受异常的算法。
要执行逐步读取循环,给定一个ReadableStreamDefaultReader
对象reader、并行队列或全局对象taskDestination、算法processBodyChunk、算法processEndOfBody和算法processBodyError:
-
让readRequest成为以下读取请求:
-
从reader读取一个块,给定readRequest。
要完全读取一个主体body,给定一个算法processBody、一个算法processBodyError,以及一个可选的null、并行队列或全局对象taskDestination(默认为null),请执行以下步骤。processBody必须是接受字节序列的算法。processBodyError必须是一个可选的接受异常的算法。
带类型的主体是一个元组,包括一个主体(一个主体)和一个类型(一个头部值或null)。
要处理内容编码,给定codings和bytes,请执行以下步骤:
-
如果codings不受支持,则返回bytes。
-
返回使用codings解码bytes的结果,如HTTP中所述,如果解码未导致错误,则返回结果,否则失败。[HTTP]
2.2.5. 请求
本节详细记录了请求的工作原理。要开始,请参阅 设置请求。
输入到fetch的是一个请求。
一个请求有一个相关联的方法(一个方法)。除非另有说明,否则它是`GET
`。
在重定向过程中,可以将其更新为`GET
`,如HTTP 获取中所述。
建议实现将其设为指向URL列表中第一个URL的指针。它仅作为其他标准集成Fetch的便利字段提供。
一个请求有一个相关联的仅限本地URL标志。除非另有说明,否则该标志未设置。
一个请求有一个相关联的头列表(一个头列表)。除非另有说明,否则它为« »。
一个请求有一个相关联的不安全请求标志。除非另有说明,否则该标志未设置。
通过APIs如fetch()
和XMLHttpRequest
,设置不安全请求标志,以确保根据所提供的方法和头列表执行CORS预检Fetch。这并不意味着API可以使用禁止的方法和禁止的请求头。
一个请求有一个相关联的body(null,一个字节序列,或一个主体)。除非另有说明,否则它为null。
将会安全地提取到一个主体中,并且在HTTP 获取过程中,可能会因为某些重定向而将此字段设置为null。
一个请求有一个相关联的客户端(null或一个环境设置对象)。
一个请求有一个相关联的保留的客户端(null,一个环境,或一个环境设置对象)。除非另有说明,否则它为null。
这仅用于导航请求和worker请求,但不包括服务worker请求。它引用了一个环境用于导航请求和一个环境设置对象用于worker请求。
一个请求有一个相关联的替代客户端ID(一个字符串)。除非另有说明,否则它是空字符串。
这仅用于导航请求。它是目标浏览上下文的id的活动文档的环境设置对象的ID。
请求具有一个关联的用户提示的可遍历对象,其值为 "no-traversable
"、"client
"
或一个可遍历导航对象。
除非另有说明,否则其值为 "client
"。
这用于确定是否以及在何处显示请求所需的 UI,例如身份验证提示或客户端证书对话框。
当在请求的用户提示的可遍历对象中显示与该请求关联的用户界面时,用户代理应更新地址栏以显示从请求的当前 URL 派生的内容(而不是,例如,将其保留为先前的值,即从请求发起者的 URL 派生的值)。此外,用户代理应避免在用户提示的可遍历对象中显示来自请求发起者的内容,尤其是在跨源请求的情况下。 在此类提示后面显示空白页是满足这些要求的好方法。未能遵循这些准则可能会使用户混淆哪个源对提示负责。
请求具有一个关联的布尔值keepalive。 除非另有说明,否则其值为 false。
这可用于允许请求的生命周期超过环境设置对象,例如,
navigator.sendBeacon()
和 HTML img
元素使用此特性。将此设置为 true 的请求需要额外的处理要求。
请求具有一个关联的发起者类型,其值为 null、
"audio
"、
"beacon
"、
"body
"、
"css
"、
"early-hints
"、
"embed
"、
"fetch
"、
"font
"、
"frame
"、
"iframe
"、
"image
"、
"img
"、
"input
"、
"link
"、
"object
"、
"ping
"、
"script
"、
"track
"、
"video
"、
"xmlhttprequest
" 或
"other
"。除非另有说明,否则其值为 null。[RESOURCE-TIMING]
请求具有一个关联的service workers 模式,其值为 "all
" 或
"none
"。除非另有说明,否则其值为 "all
"。
这决定了哪些 service worker 将为此获取接收一个 fetch
事件。
- "
all
" - 相关的 service worker 将为此获取接收一个
fetch
事件。 - "
none
" - 没有 service worker 会为此获取接收事件。
请求具有一个关联的发起者,其值为空字符串、
"download
"、
"imageset
"、
"manifest
"、
"prefetch
"、
"prerender
" 或
"xslt
"。除非另有说明,否则其值为空字符串。
请求的发起者目前粒度不够细,因为其他规范没有要求它更细。它主要是一个规范工具,用于协助定义 CSP 和混合内容。它不向 JavaScript 公开。[CSP] [MIX]
一个目标类型是以下之一:
空字符串、
"audio
"、
"audioworklet
"、
"document
"、
"embed
"、
"font
"、
"frame
"、
"iframe
"、
"image
"、
"json
"、
"manifest
"、
"object
"、
"paintworklet
"、
"report
"、
"script
"、
"serviceworker
"、
"sharedworker
"、
"style
"、
"track
"、
"video
"、
"webidentity
"、
"worker
" 或
"xslt
"。
一个请求有一个关联的 目标,它是一个 目标类型。除非另有说明,否则它是一个空字符串。
这些都反映在 RequestDestination
上,但 "serviceworker
"
和 "webidentity
" 除外,因为具有这些目的地的获取会跳过 service worker。
请求的目的地如果为
"audioworklet
"、
"paintworklet
"、"script
"、"serviceworker
"、
"sharedworker
" 或 "worker
",则类似脚本。
使用类似脚本的算法也应考虑
"xslt
",因为它也可能导致脚本执行。它未包含在列表中,因为它并非总是相关,并且可能需要不同的行为。
下表说明了请求的发起者、目的地、CSP 指令以及特性之间的关系。 关于特性,它并非详尽无遗。特性需要在其各自的标准中定义相关值。
发起者 | 目的地 | CSP 指令 | 功能 |
---|---|---|---|
"" | "report "
|
— | CSP,NEL报告。 |
"document "
|
HTML的导航算法(仅限顶级)。 | ||
"frame "
|
child-src
|
HTML的<frame>
|
|
"iframe "
|
child-src
|
HTML的<iframe>
|
|
"" | connect-src
|
navigator.sendBeacon() 、EventSource 、
HTML 的 <a ping=""> 和 <area ping=""> 、
fetch() 、fetchLater() 、XMLHttpRequest 、
WebSocket 、
Cache API
|
|
"object "
|
object-src
|
HTML的<object>
|
|
"embed "
|
object-src
|
HTML的<embed>
|
|
"audio "
|
media-src
|
HTML的<audio>
|
|
"font "
|
font-src
|
CSS的@font-face
|
|
"image "
|
img-src
|
HTML的<img src> ,/favicon.ico 资源,SVG的<image> ,CSS的background-image ,CSS的cursor ,CSS的list-style-image ,…
|
|
"audioworklet "
|
script-src
|
audioWorklet.addModule()
|
|
"paintworklet "
|
script-src
|
CSS.paintWorklet.addModule()
|
|
"script "
|
script-src
|
HTML的<script> ,importScripts()
|
|
"serviceworker "
|
child-src ,script-src ,worker-src
|
navigator.serviceWorker.register()
|
|
"sharedworker "
|
child-src ,script-src ,worker-src
|
SharedWorker
|
|
"webidentity "
|
connect-src
|
Federated Credential Management请求
|
|
"worker "
|
child-src ,script-src ,worker-src
|
Worker
|
|
"json "
|
connect-src
|
import "..." with { type: "json" }
|
|
"style "
|
style-src
|
HTML的<link rel=stylesheet> ,CSS的@import ,import "..." with { type: "css" }
|
|
"track "
|
media-src
|
HTML的<track>
|
|
"video "
|
media-src
|
HTML的<video> 元素
|
|
"download "
|
"" | — | HTML的download="" ,“另存为...” UI
|
"imageset "
|
"image "
|
img-src
|
HTML的<img srcset> 和<picture>
|
"manifest "
|
"manifest "
|
manifest-src
|
HTML的<link rel=manifest>
|
"prefetch "
|
"" | default-src (无特定指令)
|
HTML的<link rel=prefetch>
|
"prerender "
|
HTML的<link rel=prerender>
|
||
"xslt "
|
"xslt "
|
script-src
|
<?xml-stylesheet>
|
CSP的form-action
需要直接在HTML的导航或表单提交算法中挂钩。
CSP 还需要检查请求的客户端的全局对象的关联的 Document
的祖先可导航对象的各种 CSP 指令。
请求具有一个关联的优先级,其值为 "high
"、"low
" 或
"auto
"。除非另有说明,否则其值为 "auto
"。
请求具有一个关联的内部优先级(null 或一个实现定义的对象)。除非另有说明,否则其值为 null。
请求具有一个关联的源,其值为 "client
" 或一个源。除非另有说明,否则其值为 "client
"。
"client
" 在获取期间会更改为一个源。它为标准提供了一种便捷的方式,使其不必设置请求的源。
一个 请求 具有关联的 顶级导航发起者源,它是一个 源(origin) 或 null。除非另有说明,否则为 null。
一个 请求 具有关联的
策略容器,其值为
"client
" 或一个 策略容器。除非另有说明,否则为
"client
"。
"client
" 在获取期间会更改为一个策略容器。它为标准提供了一种便捷的方式,使其不必设置请求的策略容器。
请求具有一个关联的引用者,其值为 "no-referrer
"、"client
" 或一个URL。除非另有说明,否则其值为 "client
"。
"client
" 在获取期间会更改为 "no-referrer
" 或一个URL。它为标准提供了一种便捷的方式,使其不必设置请求的引用者。
请求具有一个关联的引用者策略,它是一个引用者策略。除非另有说明,否则其值为空字符串。[REFERRER]
这可用于覆盖用于此请求的引用者策略。
请求具有一个关联的模式,其值为
"same-origin
"、"cors
"、"no-cors
"、"navigate
" 或
"websocket
"。除非另有说明,否则其值为 "no-cors
"。
- "
same-origin
" - 用于确保请求发往同源 URL。获取操作将在请求未发往同源 URL 时返回一个网络错误。
- "
cors
" - 对于其响应污染设置为 "
cors
" 的请求, 将其设为一个CORS 请求 — 在这种情况下,如果请求的资源不理解CORS 协议,或者请求的资源有意不参与CORS 协议,则获取操作将返回一个网络错误。 - "
no-cors
" - 将请求限制为使用CORS 安全列表中的方法和CORS 安全列表中的请求标头。成功后,获取操作将返回一个不透明的过滤响应。
- "
navigate
" - 这是一种仅在文档之间导航时使用的特殊模式。
- "
websocket
" - 这是一种仅在建立 WebSocket 连接时使用的特殊模式。
请求具有一个关联的use-CORS-preflight 标志。除非另有说明,否则它未设置。
设置 use-CORS-preflight 标志是导致 CORS 预检请求的几个条件之一。如果在 XMLHttpRequestUpload
对象上注册了一个或多个事件侦听器,或者在请求中使用了 ReadableStream
对象,则会设置 use-CORS-preflight 标志。
请求具有一个关联的凭据模式,
其值为 "omit
"、"same-origin
" 或 "include
"。除非另有说明,否则其值为
"same-origin
"。
- "
omit
" - 从此请求中排除凭据,并导致响应中发送回的任何凭据被忽略。
- "
same-origin
" - 在向同源 URL 发出的请求中包含凭据,并使用从同源 URL 的响应中发送回的任何凭据。
- "
include
" - 始终在此请求中包含凭据,并始终使用响应中发送回的任何凭据。
请求的凭据模式控制获取期间凭据的流动。当请求的模式为 "navigate
"
时,其凭据模式
假定为 "include
",并且获取目前不考虑其他值。如果HTML在此处发生更改,则此标准将需要相应的更改。
请求具有一个关联的use-URL-credentials 标志。 除非另有说明,否则它未设置。
设置此标志后,当请求的URL具有用户名和密码,并且请求存在可用的身份验证条目时,则URL的凭据优先于身份验证条目的凭据。现代规范避免设置此标志,因为不鼓励将凭据放在URL中,但某些较旧的功能出于兼容性原因设置了此标志。
请求具有一个关联的缓存模式,其值为
"default
"、"no-store
"、"reload
"、"no-cache
"、"force-cache
"
或 "only-if-cached
"。除非另有说明,否则其值为 "default
"。
- "
default
" - Fetch将在访问网络的途中检查HTTP缓存。如果HTTP缓存中包含匹配的新鲜响应,它将被返回。如果HTTP缓存中包含匹配的stale-while-revalidate响应,它将被返回,并将进行一个条件网络获取以更新HTTP缓存中的条目。如果HTTP缓存中包含匹配的过期响应,将返回一个条件网络获取以更新HTTP缓存中的条目。否则,将返回一个非条件网络获取以更新HTTP缓存中的条目。[HTTP][HTTP-CACHING][STALE-WHILE-REVALIDATE]
- "
no-store
" - Fetch的行为就好像根本没有HTTP缓存一样。
- "
reload
" - Fetch的行为就像在访问网络的途中没有HTTP缓存。因此,它会创建一个正常请求,并用响应更新HTTP缓存。
- "
no-cache
" - 如果HTTP缓存中有响应,Fetch将创建一个条件请求,否则创建一个正常请求。然后它用响应更新HTTP缓存。
- "
force-cache
" - Fetch使用HTTP缓存中与请求匹配的任何响应,而不关注陈旧性。如果没有响应,它会创建一个正常请求,并用响应更新HTTP缓存。
- "
only-if-cached
" - Fetch 使用 HTTP 缓存中与请求匹配的任何响应,不考虑其是否陈旧。如果没有响应,则返回网络错误。(仅当请求的模式为
"
same-origin
" 时才能使用。假设请求的重定向模式为 "follow
" 并且重定向不违反请求的模式,则将遵循任何缓存的重定向。)
如果头列表包含`If-Modified-Since
`、`If-None-Match
`、`If-Unmodified-Since
`、`If-Match
`或`If-Range
`,fetch将在其缓存模式为"default
"时,将其设置为"no-store
"。
请求具有一个关联的重定向模式,其值为 "follow
"、"error
" 或
"manual
"。
除非另有说明,否则其值为 "follow
"。
- "
follow
" - 在获取资源时遵循所有重定向。
- "
error
" - 当请求遇到重定向时,返回一个网络错误。
- "
manual
" - 当请求遇到重定向时,检索一个不透明重定向过滤响应, 以允许 service worker 离线重播重定向。否则,该响应与网络错误无法区分,以避免违反原子 HTTP 重定向处理。
请求具有关联的完整性元数据(一个字符串)。除非另有说明,否则其值为空字符串。
请求具有关联的加密随机数元数据(一个字符串)。除非另有说明,否则其值为空字符串。
请求具有关联的解析器元数据,其值为空字符串、"parser-inserted
" 或
"not-parser-inserted
"。除非另有说明,否则其值为空字符串。
请求的加密随机数元数据和解析器元数据通常从负责创建请求的 HTML 元素的属性和标志中填充。它们被内容安全策略中的各种算法用来确定在给定上下文中是否应阻止请求或响应。[CSP]
请求具有一个关联的重载导航标志。 除非另有说明,否则它未设置。
此标志专供 HTML 的导航算法使用。[HTML]
请求具有一个关联的历史导航标志。 除非另有说明,否则它未设置。
此标志专供 HTML 的导航算法使用。[HTML]
请求具有一个关联的布尔值用户激活。 除非另有说明,否则其值为 false。
这专供 HTML 的导航算法使用。[HTML]
请求具有一个关联的布尔值渲染阻塞。 除非另有说明,否则其值为 false。
此标志专供 HTML 的渲染阻塞机制使用。[HTML]
请求具有一个关联的URL 列表(一个包含一个或多个URL 的列表)。除非另有说明,否则它是一个包含请求的URL 副本的列表。
请求具有一个关联的当前 URL。它是一个指向请求的URL 列表中最后一个URL 的指针。
请求具有一个关联的重定向计数。 除非另有说明,否则其值为零。
请求具有一个关联的响应污染,
其值为 "basic
"、"cors
" 或 "opaque
"。
除非另有说明,否则其值为 "basic
"。
请求具有一个关联的阻止 no-cache cache-control 标头修改标志。 除非另有说明,否则它未设置。
请求具有一个关联的完成标志。 除非另有说明,否则它未设置。
请求具有一个关联的计时允许失败标志。除非另有说明,否则它未设置。
请求的URL 列表、当前 URL、重定向计数、响应污染、完成标志以及计时允许失败标志被获取算法用作簿记细节。
子资源请求是一个请求,其目的地为 "audio
"、
"audioworklet
"、
"font
"、"image
"、"json
"、"manifest
"、
"paintworklet
"、"script
"、"style
"、"track
"、
"video
"、"xslt
" 或空字符串。
非子资源请求是一个请求,其目的地为
"document
"、"embed
"、
"frame
"、"iframe
"、"object
"、"report
"、
"serviceworker
"、"sharedworker
" 或 "worker
"。
导航请求是一个请求,其目的地为
"document
"、"embed
"、"frame
"、"iframe
" 或
"object
"。
要计算
请求
request 的 重定向污染,执行以下步骤。返回值为
"same-origin
"、"same-site
" 或 "cross-site
"。
-
令 lastURL 为 null。
-
令 taint 为 "
same-origin
"。 -
返回 taint。
给定 请求 request, 序列化请求源 的步骤如下:
要克隆一个请求 request,请执行以下步骤:
要向请求 request 添加范围标头,并带有整数 first 和可选整数 last,请执行以下步骤:
-
断言:未给出 last,或者 first 小于或等于 last。
-
令 rangeValue 为 `
bytes=
`。 -
将 0x2D (-) 追加到 rangeValue。
范围标头表示一个包含性的字节范围。因此,first 为 0 且 last 为 500 的范围标头表示一个 501 字节的范围。
将多个响应组合成一个逻辑资源的特性在历史上是安全漏洞的来源。请为处理部分响应的特性寻求安全审查。
要序列化用于报告的响应 URL,给定一个响应 response,请执行以下步骤:
要检查跨源嵌入器策略是否允许凭据,给定一个请求 request,请执行以下步骤:
2.2.6. 响应
Fetch的结果是一个响应。一个响应会随着时间的推移而演变。也就是说,并不是所有的字段都能立即获得。
一个响应有一个相关联的类型,它可以是
"basic
",
"cors
",
"default
",
"error
",
"opaque
",或
"opaqueredirect
"。
除非另有说明,否则它是"default
"。
一个响应可以有一个相关联的中止标志,该标志最初未设置。
这表明请求被开发人员或终端用户故意中止。
一个响应有一个相关联的URL。它是指向URL的指针,位于响应的URL 列表的最后,如果响应的URL 列表为空,则为null。
一个响应有一个相关联的URL 列表(一个列表,包含零个或多个URL)。除非另有说明,否则它是« »。
除去第一个和最后一个URL(如果有的话),响应的URL 列表不会直接暴露给脚本,因为那样会违反原子 HTTP 重定向处理。
一个响应有一个相关联的状态,它是一个状态。 除非另有说明,否则它是200。
一个响应有一个相关联的状态消息。除非另有说明,否则它是空字节序列。
通过 HTTP/2 连接的响应始终将状态消息设为空字节序列,因为 HTTP/2 不支持它们。
一个响应有一个相关联的标头列表(一个标头列表)。除非另有说明,否则它是« »。
一个响应有一个相关联的正文(为null或正文)。除非另有说明,否则它是null。
一个响应有一个相关联的缓存状态(空字符串,
"local
",或"validated
")。除非另有说明,否则它是空字符串。
这是为了供Service Workers和Resource Timing使用。[SW] [RESOURCE-TIMING]
一个响应有一个相关联的CORS暴露的标头名称列表(零个或多个标头的名称的列表)。除非另有说明,否则列表为空。
一个响应通常会通过从`Access-Control-Expose-Headers
`标头中提取标头值来设置其CORS暴露的标头名称列表。此列表用于CORS 过滤响应以确定要暴露哪些标头。
一个响应有一个相关联的请求范围标志,最初未设置。
这是用来防止将早期范围请求的部分响应提供给没有发出范围请求的API。有关攻击的详细描述,请参见标志的使用情况。
一个响应有一个相关联的请求包含凭证(布尔值),最初为true。
一个响应有一个相关联的时间允许通过标志,最初未设置。
这是为了使Fetch的调用方可以通过查看返回响应的标志来确定是否允许对提取的资源使用敏感的时间数据。因为在重定向链中的前一个响应已设置标志时,重定向响应的标志也必须设置,这也通过请求的时间允许失败标志进行内部跟踪。
一个响应有一个相关联的正文信息(一个响应正文信息)。除非另有说明,否则它是新的响应正文信息。
一个响应有一个相关联的Service Worker计时信息(为null或Service Worker计时信息),最初为null。
响应 具有关联的 重定向污染
("same-origin
"、"same-site
" 或 "cross-site
"),初始值为
"same-origin
"。
一个网络错误是一个响应,其类型为"error
",状态
为0,状态消息为空字节序列,标头列表为« »,正文为
null,且正文信息为新的响应正文信息。
一个过滤响应是一个响应,它对相关联的响应提供有限的视图。此相关联的响应可以通过内部响应访问(一个既不是网络错误也不是过滤响应的响应)。
除非另有说明,过滤响应的相关概念(如其正文)指的是其内部响应的相关概念。(对这些的例外情况列在定义过滤响应的具体类型时。)
Fetch算法通过processResponse和等效参数来暴露过滤响应,以确保它们不会意外泄露信息。如果出于遗留原因需要揭示信息,例如向解码器提供图像数据,规范算法可以使用相关联的内部响应。
新规范不应在不透明过滤响应或不透明重定向过滤响应上进一步构建。它们是遗留构造,并且在当代计算机架构下无法始终得到充分保护。
一个基本过滤响应是一个过滤响应,其类型为"basic
",且标头列表排除内部响应的标头列表中名称为禁止的响应标头名称的任何标头。
一个CORS
过滤响应是一个过滤响应,其类型为"cors
",且标头列表排除内部响应的标头列表中名称CORS-安全列入的响应标头名称的标头。
一个不透明过滤响应是一个过滤响应,其类型为"opaque
",URL
列表为« »,状态为0,状态消息为空字节序列,标头列表为« »,正文为null,且正文信息为新的响应正文信息。
一个不透明重定向过滤响应是一个过滤响应,其类型为"opaqueredirect
",状态为0,状态消息为空字节序列,标头列表为« »,正文为null,且正文信息为新的响应正文信息。
暴露不透明重定向过滤响应的URL 列表是无害的,因为未执行任何重定向。
换句话说,一个不透明过滤响应和一个不透明重定向过滤响应与网络错误几乎无法区分。在引入新的 API 时,请不要在规范算法中使用内部响应,因为那样会泄露信息。
这也意味着 JavaScript API,例如response.ok
,将返回相当无用的结果。
console. log( new Response(). type); // "default"
console. log(( await fetch( "/" )). type); // "basic"
console. log(( await fetch( "https://api.example/status" )). type); // "cors"
console. log(( await fetch( "https://crossorigin.example/image" , { mode: "no-cors" })). type); // "opaque"
console. log(( await fetch( "/surprise-me" , { redirect: "manual" })). type); // "opaqueredirect"
(这假设存在各种资源,https://api.example/status
具有适当的 CORS 标头,并且/surprise-me
使用重定向状态。)
要克隆一个响应 response,运行以下步骤:
一个陈旧但重新验证响应是一个响应,它不是一个新鲜响应,其当前年龄在陈旧但重新验证寿命内。[HTTP-CACHING] [STALE-WHILE-REVALIDATE]
一个陈旧响应是一个响应,它不是一个新鲜响应或一个陈旧但重新验证响应。
给定null或一个ASCII 字符串 requestFragment,一个响应 response的位置 URL是以下步骤返回的值。它们返回null、失败或一个URL。
-
将location设置为给定`
Location
`和response的标头列表提取标头列表值的结果。 -
如果location是一个标头值,则将location设置为使用response的URL解析location的结果。
如果response是通过
Response
构造函数构造的,则response的URL将为null,这意味着只有它是一个带片段的绝对 URL 字符串时,location才会成功解析。 -
如果location是一个URL,其片段为null,则将location的片段设置为requestFragment。
这确保了合成响应(实际上是所有响应)遵循 HTTP 定义的重定向处理模型。[HTTP]
-
返回location。
2.2.7. 杂项
潜在目的地是
“fetch
”或目的地,且不是空字符串。
要转换potentialDestination的潜在目的地,运行以下步骤:
2.3. 认证条目
认证条目和代理认证条目是用户名、密码和领域的元组,用于HTTP认证和HTTP代理认证,并与一个或多个请求关联。
用户代理应允许这些条目与HTTP cookie和类似的跟踪功能一起清除。
更多详细信息由HTTP定义。[HTTP] [HTTP-CACHING]
2.4. Fetch 组
每个 环境设置对象 都有一个关联的 获取组,它保存一个 获取组。
获取组保存有关获取操作的信息。
获取组关联以下内容:
- 获取记录
- 一个 列表,包含 获取记录。
- 延迟获取记录
- 一个 列表,包含 延迟获取记录。
- 请求
- 一个 请求。
- 控制器
- 一个 获取控制器 或 null。
延迟获取记录是一个 结构体,用于维护延迟执行获取所需的状态,例如在文档卸载或变为非 完全活动状态时。它包含以下 项:
- 请求
- 一个 请求。
- 通知已调用
- 一个不接受参数的算法。
- 调用状态(默认值为 "
pending
") - "
pending
"、"sent
" 或 "aborted
"。
当一个 fetch group fetchGroup 被 终止时:
-
对于每个 获取记录 record 属于 fetchGroup 的 获取记录,若 record 的 控制器 非 null 且 record 的 请求 的 完成标志 未设置且 keepalive 为 false,则终止 record 的 控制器。
-
处理延迟获取,针对 fetchGroup。
2.5. 解析域名
要解析一个源,给定一个网络分区键
key 和一个源 origin:
-
如果origin的host的公共后缀是"
localhost
"或"localhost.
",则返回«::1
,127.0.0.1
»。 -
执行一个实现定义的操作,将origin转换为一个或多个IP地址的集合。
是否执行其他操作以获取除IP 地址之外的连接信息,也由实现定义。例如,如果 origin 的方案是HTTP(S) 方案,则实现可能会为 HTTPS RR 执行 DNS 查询。[SVCB]
-
返回失败。
解析来源的结果可以被缓存。如果它们被缓存,则应使用key作为缓存密钥的一部分。
2.6. 连接
用户代理有一个关联的连接池。一个连接池是一个有序集合,包含零个或多个连接。每个连接通过一个关联的密钥(一个网络分区密钥)、origin(一个来源)、以及凭据(一个布尔值)来标识。
连接时间信息是一个用于维护获取连接过程中的时间信息的结构体。它包含以下项目:
- 域名查找开始时间(默认为0)
- 域名查找结束时间(默认为0)
- 连接开始时间(默认为0)
- 连接结束时间(默认为0)
- 安全连接开始时间(默认为0)
- 一个
DOMHighResTimeStamp
。 - ALPN 协商协议(默认为空的字节序列)
- 一个字节序列。
要限制和粗化连接时间信息,给定一个连接时间信息timingInfo、一个DOMHighResTimeStamp
defaultStartTime和一个布尔值crossOriginIsolatedCapability,运行以下步骤:
-
如果timingInfo的连接开始时间小于defaultStartTime,则返回一个新的连接时间信息,其中域名查找开始时间为defaultStartTime,域名查找结束时间为defaultStartTime,连接开始时间为defaultStartTime,连接结束时间为defaultStartTime,安全连接开始时间为defaultStartTime,以及ALPN 协商协议为timingInfo的ALPN 协商协议。
-
返回一个新的连接时序信息,其中域名查询开始时间是给定timingInfo的域名查询开始时间和 crossOriginIsolatedCapability的粗化时间的结果,域名查询结束时间是给定timingInfo的域名查询结束时间和 crossOriginIsolatedCapability的粗化时间的结果,连接开始时间是给定timingInfo的连接开始时间和 crossOriginIsolatedCapability的粗化时间的结果,连接结束时间是给定timingInfo的连接结束时间和 crossOriginIsolatedCapability的粗化时间的结果,安全连接开始时间 是给定timingInfo的连接结束时间和 crossOriginIsolatedCapability的粗化时间的结果,ALPN 协议协商是 timingInfo的ALPN 协议协商。
新连接设置为"no
"、"yes
"或"yes-and-dedicated
"。
要获取一个连接,给定一个网络分区密钥key、URLurl、布尔值credentials、一个可选的新连接设置new(默认值为"no
"),以及一个可选的布尔值requireUnreliable(默认值为false),运行以下步骤:
-
如果new为"
no
",则: -
令proxies为以实现定义的方式找到的用于url的代理的结果。如果没有代理,则令proxies为«"
DIRECT
"»。这是非标准技术(如Web Proxy Auto-Discovery Protocol (WPAD)和proxy auto-config (PAC))发挥作用的地方。"
DIRECT
"值意味着对于这个特定的url不使用代理。 -
令timingInfo为新的连接时间信息。
-
对于每个proxies中的proxy:
-
如果proxy为"
DIRECT
",则将hosts设置为运行解析一个origin给定key和url的来源的结果。 -
如果hosts为失败,则继续。
-
令connection为运行以下步骤的结果:运行创建一个连接给定key、url的来源、credentials、proxy、从hosts中以实现定义的方式获取的host、timingInfo、和requireUnreliable,以实现定义的次数彼此并行运行,并等待至少1个返回一个值。在实现定义的方式中,从返回的值中选择一个值并返回它。任何其他返回的为连接的值可能会被关闭。
本质上,这允许实现从解析一个origin的返回值中选择一个或多个IP地址(假设proxy为"
DIRECT
")并让它们彼此竞争,优先选择IPv6地址,在超时的情况下重试等。 -
如果connection失败,则继续。
-
返回connection。
-
返回失败。
这故意有点模糊,因为连接管理有很多细微差别,最好由实现者自行决定。描述这一点有助于解释<link rel=preconnect>
功能,并明确规定连接
以凭证为关键。这后一部分澄清了,例如,TLS会话标识符不会在凭证为假的连接与凭证为真的连接之间复用。
要创建一个连接,给定一个网络分区密钥key、来源origin、布尔值credentials、字符串proxy、hosthost、连接时间信息timingInfo和布尔值requireUnreliable,运行以下步骤:
-
令connection为新的连接,其中key为密钥,origin为origin,credentials为凭据,timingInfo为时间信息。记录连接时间信息,给定connection,并使用connection建立一个到host的HTTP连接,考虑proxy和origin,具有以下警告:[HTTP][HTTP1][TLS]
-
如果requireUnreliable为true,则建立一个支持不可靠传输的连接,例如一个HTTP/3连接。[HTTP3]
-
在建立一个支持不可靠传输的连接时,启用WebTransport所需的选项。对于HTTP/3,这意味着在初始
SETTINGS
帧中包括SETTINGS_ENABLE_WEBTRANSPORT
,值为1
,以及H3_DATAGRAM
,值为1
。[WEBTRANSPORT-HTTP3][HTTP3-DATAGRAM] -
如果credentials为false,则不要发送TLS客户端证书。
-
如果建立连接不成功(例如,UDP、TCP或TLS错误),则返回失败。
-
-
将timingInfo的ALPN 协商协议设置为connection的ALPN协议ID,具有以下警告:[RFC7301]
-
当配置了代理时,如果建立了隧道连接,则这必须是隧道协议的ALPN协议ID,否则必须是到代理的第一跳的ALPN协议ID。
-
如果用户代理使用了实验性、未注册的协议,则用户代理必须使用使用的ALPN协议ID(如果有)。如果没有使用ALPN进行协议协商,则用户代理可以使用另一个描述性字符串。
timingInfo的ALPN 协商协议旨在标识使用的网络协议,无论它实际上是如何协商的;即使ALPN未用于协商网络协议,这也是表示使用的协议的ALPN协议ID。
IANA维护ALPN协议ID列表。
-
-
返回connection。
要记录连接时间信息,给定一个连接connection,令timingInfo为connection的时间信息,并遵守以下要求:
-
timingInfo的连接结束时间应为在建立与服务器或代理的连接后立即的不安全共享当前时间,如下所示:
-
返回的时间必须包括建立传输连接的时间间隔,以及其他时间间隔,例如SOCKS身份验证时间。它还必须包括完成足够的TLS握手以请求资源所需的时间间隔。
-
如果用户代理在此连接中使用了TLS False Start,则此时间间隔不应包括接收服务器Finished消息所需的时间。[RFC7918]
-
如果用户代理在未等待完整握手完成的情况下发送了带有早期数据的请求,则此时间间隔不应包括接收服务器ServerHello消息所需的时间。[RFC8470]
-
如果用户代理等待完整握手完成后发送请求,则此时间间隔包括完整的TLS握手,即使其他请求是使用早期数据在connection上发送的。
假设用户代理通过TLS 1.3建立HTTP/2连接以发送
GET
请求和POST
请求。它在时间t1发送ClientHello,然后使用早期数据发送GET
请求。POST
请求不安全([HTTP],第9.2.1节),所以用户代理在时间t2等待完成握手后才发送它。虽然两个请求都使用了相同的连接,但GET
请求报告的连接结束时间是t1,而POST
请求报告的是t2。 -
-
如果使用了安全传输,则timingInfo的安全连接开始时间应为在开始握手过程以安全connection之前调用不安全共享当前时间的结果。[TLS]
-
如果connection是HTTP/3连接,则timingInfo的连接开始时间和timingInfo的安全连接开始时间必须相等。(在HTTP/3中,安全传输握手过程是初始连接设置的一部分。)[HTTP3]
限制和粗化连接时间信息算法确保了重用连接的细节不会暴露,并且时间值被粗化。
2.7. 网络分区密钥
要确定网络分区密钥,给定一个环境environment:
要确定网络分区密钥,给定一个请求request:
2.8. HTTP缓存分区
要确定HTTP缓存分区,给定一个请求 request:
-
令key为给定request确定网络分区密钥的结果。
-
如果key为null,则返回null。
-
返回与key关联的唯一HTTP缓存。[HTTP-CACHING]
2.9. 端口封锁
新协议可以通过使用ALPN在TLS中协商协议,避免封锁端口的需求。在这种情况下,该协议无法通过HTTP请求被伪造。[RFC7301]
要确定是否由于使用了不良端口而阻止获取一个请求request:
-
令url为request的当前URL。
-
如果url的scheme是HTTP(S) scheme,并且url的端口是不良端口,则返回阻止。
-
返回允许。
端口是不良端口,如果它在以下表格的第一列中列出。
端口 | 典型服务 |
---|---|
0 | — |
1 | tcpmux |
7 | echo |
9 | discard |
11 | systat |
13 | daytime |
15 | netstat |
17 | qotd |
19 | chargen |
20 | ftp-data |
21 | ftp |
22 | ssh |
23 | telnet |
25 | smtp |
37 | time |
42 | name |
43 | nicname |
53 | domain |
69 | tftp |
77 | — |
79 | finger |
87 | — |
95 | supdup |
101 | hostname |
102 | iso-tsap |
103 | gppitnp |
104 | acr-nema |
109 | pop2 |
110 | pop3 |
111 | sunrpc |
113 | auth |
115 | sftp |
117 | uucp-path |
119 | nntp |
123 | ntp |
135 | epmap |
137 | netbios-ns |
139 | netbios-ssn |
143 | imap |
161 | snmp |
179 | bgp |
389 | ldap |
427 | svrloc |
465 | submissions |
512 | exec |
513 | login |
514 | shell |
515 | printer |
526 | tempo |
530 | courier |
531 | chat |
532 | netnews |
540 | uucp |
548 | afp |
554 | rtsp |
556 | remotefs |
563 | nntps |
587 | submission |
601 | syslog-conn |
636 | ldaps |
989 | ftps-data |
990 | ftps |
993 | imaps |
995 | pop3s |
1719 | h323gatestat |
1720 | h323hostcall |
1723 | pptp |
2049 | nfs |
3659 | apple-sasl |
4045 | npp |
4190 | sieve |
5060 | sip |
5061 | sips |
6000 | x11 |
6566 | sane-port |
6665 | ircu |
6666 | ircu |
6667 | ircu |
6668 | ircu |
6669 | ircu |
6679 | osaut |
6697 | ircs-u |
10080 | amanda |
3. HTTP扩展
3.1. Cookies(Cookie)
`Cookie
` 请求头和 `Set-Cookie
` 响应头主要在各自的规范中定义。我们在此处定义了额外的基础设施,以便更方便地使用它们。[COOKIES]。
3.1.1. `Cookie
` 请求头
要追加请求
`Cookie
` 头,给定一个 请求
request:
-
如果用户代理被配置为为 request 禁用 Cookie,则应返回。
-
令 sameSite 为 确定 same-site 模式 的结果,参数为 request。
-
令 isSecure 为 true,如果 request 的 当前 URL 的 scheme 为 "
https
";否则为 false。 -
令 httpOnlyAllowed 为 true。
由于此步骤是由 fetch 触发的,而不是例如
document.cookie
的 getter 步骤,因此为 true。 -
令 cookies 为运行 检索 Cookie 的结果,参数为 isSecure、request 的 当前 URL 的 host、request 的 当前 URL 的 path、httpOnlyAllowed 和 sameSite。
Cookie 存储会返回一个有序的 Cookie 列表
-
如果 cookies 为空,则返回。
-
令 value 为运行 序列化 Cookie 的结果,参数为 cookies。
3.1.2. `Set-Cookie
` 响应头
要解析并存储响应 `Set-Cookie
` 头,给定一个
请求
request 和一个 响应 response:
-
如果用户代理被配置为为 request 禁用 Cookie,则应返回。
-
令 allowNonHostOnlyCookieForPublicSuffix 为 false。
-
令 isSecure 为 true,如果 request 的 当前 URL 的 scheme 为 "
https
";否则为 false。 -
令 httpOnlyAllowed 为 true。
由于此步骤是由 fetch 触发的,而不是例如
document.cookie
的 getter 步骤,因此为 true。 -
令 sameSiteStrictOrLaxAllowed 为 true,如果 确定 same-site 模式 的结果为 "
strict-or-less
",否则为 false。 -
对于每个 header 属于 response 的 头列表:
-
解析并存储一个 Cookie,参数为 header 的 value、isSecure、request 的 当前 URL 的 host、request 的 当前 URL 的 path、httpOnlyAllowed、allowNonHostOnlyCookieForPublicSuffix 和 sameSiteStrictOrLaxAllowed。
-
垃圾回收 Cookie,参数为 request 的 当前 URL 的 host。
如其他地方所述,`
Set-Cookie
` 头不能合并,因此每个出现都要独立处理。其他头都不允许这样做。
3.1.3. Cookie 基础设施
要确定 same-site 模式,给定一个 请求 request:
为了获取一个序列化的 Cookie 默认路径,给定一个URL url:
-
令cloneURL为url的一个副本。
-
将cloneURL的路径设为 cloneURL的Cookie 默认路径。
-
返回cloneURL的URL 路径序列化。
3.2. `Origin
` 标头
请求的`Origin
`
标头指示fetch
的来源。
`Origin
` 标头是一个不显示路径的
`Referer
` [sic] 标头版本。它用于所有HTTP
fetches,其中request的
response tainting为`cors
`,以及
request的method既不是
`GET
`也不是`HEAD
`。由于兼容性约束,它不包含在所有fetches`中。
其可能的值是给定request时字节序列化请求来源的所有返回值。
这取代了Web Origin 概念中的定义。[ORIGIN]
为了附加请求的`Origin
`标头,
给定request,
request,请运行以下步骤:
-
将serializedOrigin设为使用request 字节序列化请求来源的结果。
-
如果request的response tainting为`
cors
`或 request的模式为`websocket
`,则将(``Origin
``, serializedOrigin)附加到request的标头列表中。 -
否则,如果request的method既不是`
GET
`也不是`HEAD
`,则:-
如果request的模式不是`
cors
`, 则根据request的引用者策略进行切换:- "
no-referrer
" -
将serializedOrigin设置为`
null
`。 - "
no-referrer-when-downgrade
" - "
strict-origin
" - "
strict-origin-when-cross-origin
" -
如果request的来源是元组来源,其scheme为 `"
https
"`,且request的当前URL的scheme不是 `"https
"`,则将serializedOrigin设置为`null
`。 - "
same-origin
" -
如果request的来源与request的当前URL的来源不同源,则将serializedOrigin设置为 `"
null
"`。 - 否则
- 什么也不做。
- "
-
所有fetches
中,request的
引用者策略都会被考虑在内,而fetcher没有明确选择与服务器共享其来源,
例如通过使用CORS协议。
3.3. CORS协议
为了允许跨来源共享响应,并允许比HTML的form
元素可能实现的更灵活的fetches,
CORS协议应运而生。它
构建于HTTP之上,并允许响应声明它们可以与其他来源共享。
这需要是一种选择加入的机制,以防止泄露防火墙后(内网)的响应数据。此外,对于包含凭据的请求, 也需要选择加入,以防止泄露潜在敏感数据。
本节解释了与服务器开发者相关的CORS协议。用户代理的要求是fetch算法的一部分, 除了新的HTTP标头语法。
3.3.1. 概述
CORS协议由一组标头组成, 这些标头指示响应是否可以跨来源共享。
对于比 HTML 的
form
元素所能实现的更复杂的请求,会执行一个CORS
预检请求,以确保请求的当前
URL 支持 CORS 协议。
3.3.2. HTTP 请求
CORS 请求是包含
`Origin
`
头的HTTP请求。它不能被可靠地识别为参与
CORS协议,因为 `Origin
`
头也会包含在
所有请求中,这些请求的方法
既不是 `GET
` 也不是 `HEAD
`。
CORS预检请求 是一种
CORS
请求,
用于检查是否理解CORS协议。它使用
`OPTIONS
` 作为
方法,并包含以下头:
3.3.3. HTTP 响应
- `
Access-Control-Allow-Origin
` - `
Access-Control-Allow-Credentials
` -
指示当请求的请求的 凭据模式为 "
include
" 时,响应是否可以共享。对于 CORS 预检请求,请求的 请求的 凭据模式总是 "
same-origin
",即排除凭据,但对于任何后续的 CORS 请求, 它可能不是。因此,也需要在对CORS预检请求的HTTP响应中指明支持。
- `
Access-Control-Allow-Methods
` -
指示响应的响应的 URL 为 CORS 协议 支持哪些方法。
`
Allow
` 头 与CORS协议无关。 - `
Access-Control-Allow-Headers
` - `
Access-Control-Max-Age
` -
指示由 `
Access-Control-Allow-Methods
` 和 `Access-Control-Allow-Headers
` 头提供的信息 可以缓存的秒数(默认5秒)。
对CORS 请求但不是 CORS 预检请求的HTTP响应还可以包含以下 头:
- `
Access-Control-Expose-Headers
` -
指示可以作为响应的一部分公开的头,通过列出它们的名称。
对CORS 请求的成功HTTP响应, 即服务器开发者打算共享的响应,可以使用任何 状态, 只要它包含上述头,且头的 值 与请求匹配。
对CORS 预检请求的成功HTTP响应类似,除非它限制为 ok 状态,例如200或204。
任何其他类型的HTTP响应都不是成功的,最终将无法共享或失败 CORS 预检请求。请注意,服务器执行的任何工作仍可能通过侧信道泄露, 例如时间。如果服务器开发者希望明确表示这一点,可以使用403 状态, 并省略相关头。
如果需要,也可以共享“失败”,但这将使其成为成功的HTTP响应。 这就是为什么对于不是 CORS 预检请求的CORS请求的成功HTTP响应, 状态可以是任何值,包括403。
最终,服务器开发者在如何处理HTTP响应方面有很大的自由,这些策略可以在对 CORS 预检请求和随后对 CORS 请求的响应之间有所不同:
-
他们可以提供静态响应。这在处理缓存中间件时很有帮助。静态响应既可以成功,也可以不成功,具体取决于 CORS 请求。 这是可以的。
-
他们可以提供动态响应,针对CORS 请求进行调整。这在响应体需要针对特定来源定制或响应需要包含凭据且在一组来源中成功时很有帮助。
3.3.4. HTTP 新头语法
Access-Control-Request-Method = method
Access-Control-Request-Headers = 1 #field-name
wildcard = "*"
Access-Control-Allow-Origin = origin-or-null / wildcard
Access-Control-Allow-Credentials = %s"true" ; case-sensitive
Access-Control-Expose-Headers = #field-name
Access-Control-Max-Age = delta-seconds
Access-Control-Allow-Methods = #method
Access-Control-Allow-Headers = #field-name
对于 `Access-Control-Expose-Headers
`,
`Access-Control-Allow-Methods
`,和 `Access-Control-Allow-Headers
`
响应头,对于不包含
凭据的请求,`*
`作为值
算作通配符。对于这样的请求,
没有办法单独匹配一个头名称
或 `*
` 的方法。
3.3.5. CORS 协议与凭据
当请求
的凭据模式为 "include
" 时,
它对CORS
协议
的影响不仅限于在fetch中包含
凭据。
在过去,XMLHttpRequest
可以用于将请求
的凭据模式设置为 "include
":
var client = new XMLHttpRequest()
client. open( "GET" , "./" )
client. withCredentials = true
/* … */
如今,fetch("./", { credentials:"include" }).then(/* … */)
就足够了。
请求的凭据模式不一定能在服务器上观察到;只有当请求存在凭据时,才能通过包含的凭据观察到它。请注意,即便如此,CORS 预检请求也从不包含凭据。
因此,服务器开发人员需要决定是否可以共享被凭据“污染”的响应。并且还需要决定需要CORS 预检请求的请求是否可以包含凭据。一般来说,共享响应和允许带有凭据的请求都相当不安全,必须格外小心以避免混乱代理问题。
要共享带有凭据的响应,Access-Control-Allow-Origin
和
Access-Control-Allow-Credentials
头是重要的。
以下表格旨在说明对于请求 https://rabbit.invalid/
的各种合法和非法组合:
请求的凭据模式 | Access-Control-Allow-Origin
|
Access-Control-Allow-Credentials
|
共享? | 备注 |
---|---|---|---|---|
"omit " |
* |
省略 | ✅ | — |
"omit " |
* |
true |
✅ | 如果凭据模式不是 "include ",则
Access-Control-Allow-Credentials
被忽略。
|
"omit " |
https://rabbit.invalid/ |
省略 | ❌ | 序列化来源没有尾随斜杠。 |
"omit " |
https://rabbit.invalid |
省略 | ✅ | — |
"include " |
* |
true |
❌ | 如果凭据模式是 "include ",
Access-Control-Allow-Origin
不能是
* 。
|
"include " |
https://rabbit.invalid |
true |
✅ | — |
"include " |
https://rabbit.invalid |
True |
❌ | true 是字节大小写敏感的。 |
同样地,`Access-Control-Expose-Headers
`、
`Access-Control-Allow-Methods
` 和
`Access-Control-Allow-Headers
` 响应标头仅当请求的凭据模式不是
"include
" 时才能使用 `*
` 作为值。
3.3.6. 示例
一个位于https://foo.invalid/
的脚本想要从https://bar.invalid/
获取一些数据。(凭据或响应头访问并不重要。)
var url = "https://bar.invalid/api?key=730d67a37d7f3d802e96396d00280768773813fbe726d116944d814422fc1a45&data=about:unicorn" ;
fetch( url). then( success, failure)
这将使用CORS
协议,尽管这对来自foo.invalid
的开发者来说是完全透明的。作为CORS
协议的一部分,
用户代理将在请求中包含 `Origin
` 头:
Origin: https://foo.invalid
在从bar.invalid
接收到响应后,用户代理将验证
`Access-Control-Allow-Origin
`响应头。
如果它的值是 `https://foo.invalid
` 或 `*
`,用户代理将调用success
回调。
如果它有任何其他值,或者缺失,用户代理将调用failure
回调。
foo.invalid
的开发者回来了,现在希望在从bar.invalid
获取一些数据的同时访问响应头。
fetch( url). then( response => {
var hsts = response. headers. get( "strict-transport-security" ),
csp = response. headers. get( "content-security-policy" )
log( hsts, csp)
})
bar.invalid
提供了一个正确的
`Access-Control-Allow-Origin
`响应头,
如前面的示例所示。hsts
和csp
的值将取决于
`Access-Control-Expose-Headers
`响应头。
例如,如果响应包括以下头:
Content-Security-Policy: default-src 'self'
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Access-Control-Expose-Headers: Content-Security-Policy
那么hsts
将为 null,csp
将为"default-src 'self'
",即使响应中确实包含了两个头。
这是因为bar.invalid
需要通过在 `Access-Control-Expose-Headers
`响应头中列出它们的名称来明确共享每个头。
或者,如果bar.invalid
想要共享所有响应头,对于不包含凭据的请求,可以使用
`*
` 作为
`Access-Control-Expose-Headers
`响应头的值。
如果请求包含凭据,则响应头名称必须明确列出,且不能使用
`*
`。
foo.invalid
的开发者再次回来了,这次是在包含凭据的情况下从bar.invalid
获取一些数据。
这次CORS 协议对开发者不再透明,
因为凭据需要显式选择加入:
fetch( url, { credentials: "include" }). then( success, failure)
这也使得bar.invalid
中包含的任何 `Set-Cookie
`响应头完全有效(否则将被忽略)。
用户代理将确保在请求中包含任何相关的凭据。它还将对响应施加更严格的要求。bar.invalid
不仅需要在
`Access-Control-Allow-Origin
`头中列出
`https://foo.invalid
` 作为值(当涉及到凭据时,不允许使用
`*
`),还必须包含
`Access-Control-Allow-Credentials
`头:
Access-Control-Allow-Origin: https://foo.invalid
Access-Control-Allow-Credentials: true
如果响应未包含这两个头,或它们的值不正确,将调用failure
回调。然而,任何`Set-Cookie
`响应头仍将被尊重。
3.3.7. CORS 协议例外情况
规范允许针对非安全列表的 `Content-Type
` 头值的 CORS 安全列表进行有限的例外。这些例外适用于可以由 Web 内容触发但其头和主体只能由 Web
内容进行最小控制的请求。因此,服务器应预期跨域 Web 内容被允许触发具有以下非安全列表 `Content-Type
` 头值的非预检请求:
- `
application/csp-report
` [CSP] - `
application/expect-ct-report+json
` [RFC9163] - `
application/xss-auditor-report
` - `
application/ocsp-request
` [RFC6960]
规范应避免引入新的例外,且应仅在充分考虑安全后果的情况下才这样做。可以通过提交问题来提出新的例外。
3.4. `Content-Length
` 头
`Content-Length
` 头主要在 HTTP 中定义。由于 HTTP 中定义的处理模型与 Web 内容不兼容,其处理模型在此定义。[HTTP]
要从 头列表 headers 中提取长度,请按以下步骤操作:
3.5. `Content-Type
` 头
`Content-Type
` 头主要在 HTTP 中定义。由于 HTTP 中定义的处理模型与 Web 内容不兼容,其处理模型在此定义。[HTTP]
要从 头列表 headers 中提取 MIME 类型,请运行以下步骤。它们将返回失败或MIME 类型。
-
让 charset 为 null。
-
让 essence 为 null。
-
让 mimeType 为 null。
-
让 values 是 获取、解码并分割自 headers 的 `
Content-Type
` 的结果。 -
如果 values 为 null,则返回失败。
-
对于每个 values 中的 value:
-
如果 mimeType 为 null,则返回失败。
-
返回 mimeType。
当提取 MIME 类型返回失败或MIME 类型的本质对于给定格式不正确时,将其视为致命错误。现有的 Web 平台功能并未始终遵循这一模式,这多年来已成为这些功能中安全漏洞的主要来源。相比之下,MIME 类型的参数通常可以安全忽略。
这是 提取 MIME 类型 的实际运作方式:
头(如网络上) | 输出(序列化) |
---|---|
|
text/html
|
|
text/html;x=y;charset=gbk
|
|
|
|
text/html;x=y
|
|
text/html
|
|
|
|
在失败或给定MIME 类型 mimeType 和 编码 fallbackEncoding 时,传统提取编码,请运行以下步骤:
-
如果 mimeType 为失败,则返回 fallbackEncoding。
-
如果 mimeType["
charset
"]不存在,则返回 fallbackEncoding。 -
让 tentativeEncoding 是 获取编码自 mimeType["
charset
"] 的结果。 -
如果 tentativeEncoding 为失败,则返回 fallbackEncoding。
-
返回 tentativeEncoding。
此算法允许 mimeType 失败,因此它可以更容易地与提取 MIME 类型结合使用。
它被标记为传统,因为现代格式将专门使用UTF-8。
3.6.
`X-Content-Type-Options
` 标头
可以使用
`X-Content-Type-Options
`
响应标头,以要求检查响应的
`Content-Type
` 标头是否与
请求的目标一致。
为了确定 nosniff,给定一个标头列表list,请执行以下步骤:
-
令 values 为 获取、解码和分割`
X-Content-Type-Options
` 自 list的结果。 -
如果 values 为 null,则返回 false。
-
如果 values[0] 是 ASCII 不区分大小写的匹配 "
nosniff
",则返回 true。 -
返回 false。
Web 开发者和一致性检查者必须使用以下 值的ABNF用于
`X-Content-Type-Options
`:
X-Content-Type-Options = "nosniff" ; case-insensitive
3.6.1. 是否应因 nosniff 而阻止 response 到 request?
执行以下步骤:
-
如果确定 nosniff并且response的标头列表为 false,则返回allowed。
-
将mimeType设置为从response的提取 MIME 类型的结果。
-
将destination设置为request的目标。
-
如果destination是类似脚本,并且mimeType为失败或不是JavaScript MIME 类型,则返回blocked。
-
如果destination是"
style
"并且mimeType失败或其本质不是"text/css
",则返回blocked。 -
返回allowed。
只有请求目标是类似脚本或"style
"的才被考虑,因为任何漏洞都与它们有关。此外,考虑到"image
"与已部署的内容不兼容。
3.7.
`Cross-Origin-Resource-Policy
` 标头
当请求的模式为
"no-cors
" 时,可以使用
`Cross-Origin-Resource-Policy
`
响应标头来要求检查该请求的当前
URL的源与该请求的源。
Cross-Origin-Resource-Policy = %s"same-origin" / %s"same-site" / %s"cross-origin" ; case-sensitive
要执行跨源资源策略检查,给定源 origin,一个环境设置对象settingsObject,一个字符串destination,一个响应 response,以及一个可选的布尔值forNavigation,请执行以下步骤:
-
如果未给定forNavigation,将其设置为 false。
-
如果origin,"
unsafe-none
",response和 forNavigation一起执行跨源资源策略内部检查返回blocked,则返回blocked。此步骤是必要的,因为我们不希望报告与下面的 Cross-Origin Embedder Policy 无关的违规行为。
-
如果origin,embedderPolicy的仅报告值,response和forNavigation一起执行的跨源资源策略内部检查返回blocked,则排队一个跨源嵌入者策略 CORP 违规报告与response,settingsObject,destination和 true 一起执行。
-
如果origin,embedderPolicy的值,response和forNavigation一起执行的跨源资源策略内部检查返回allowed,则返回allowed。
-
排队一个跨源嵌入者策略 CORP 违规报告与response,settingsObject,destination和 false 一起执行。
-
返回blocked。
只有 HTML 的导航算法在forNavigation设置为 true 时才会使用此检查,并且总是用于嵌套导航。否则,response要么是内部响应的不透明过滤响应或响应,将成为内部响应的不透明过滤响应。[HTML]
要执行跨源资源策略内部检查,给定源 origin,一个嵌入者策略值embedderPolicyValue,一个响应response,以及一个布尔值forNavigation,请执行以下步骤:
-
如果forNavigation为 true,并且embedderPolicyValue为"
unsafe-none
",则返回allowed。 -
将policy设置为从response的获取`
Cross-Origin-Resource-Policy
`。这意味着`
Cross-Origin-Resource-Policy: same-site, same-origin
`在下方作为allowed出现,因为只要embedderPolicyValue是"unsafe-none
",它将永远不会匹配任何内容。两个或多个`Cross-Origin-Resource-Policy
`标头将具有相同效果。 -
如果policy既不是`
same-origin
`,`same-site
`,也不是`cross-origin
`,则将policy设置为 null。 -
如果policy为 null,则根据embedderPolicyValue切换:
- "
unsafe-none
" -
什么都不做。
- "
credentialless
" -
在以下情况下,将policy设置为`
same-origin
`:- response的请求包含凭据为 true,或者
- forNavigation为 true。
- "
require-corp
" -
将policy设置为`
same-origin
`。
- "
-
根据policy切换:
要排队一个跨源嵌入者策略 CORP 违规报告,给定一个响应response,一个环境设置对象settingsObject,一个字符串destination,以及一个布尔值reportOnly,请执行以下步骤:
-
将endpoint设置为settingsObject的策略容器的嵌入者策略的仅报告的报告终端,如果reportOnly为 true,并且settingsObject的策略容器的嵌入者策略的报告终端。
-
将serializedURL设置为通过序列化响应 URL 以进行报告与response执行的结果。
-
将disposition设置为"
reporting
",如果reportOnly为 true;否则为"enforce
"。 -
将body设置为一个包含以下属性的新对象:
键 值 " type
"" corp
"" blockedURL
"serializedURL " destination
"destination " disposition
"disposition -
为settingsObject的全局对象生成并排队一个报告,给定"
coep
" 报告类型,endpoint和body。[REPORTING]
3.8. `Sec-Purpose
` 标头
`Sec-Purpose
` HTTP
请求标头指定该请求除了为用户立即使用资源之外,还具有一个或多个其他目的。
`Sec-Purpose
` 标头字段是一个结构化标头,其值必须是一个令牌。
定义的唯一令牌是
prefetch
。它表示请求的目的是获取预计很快需要的资源。
服务器可以使用它来调整预取的缓存到期时间,禁止预取,或者在计算页面访问次数时对其进行不同处理。
4. 获取
下面的算法定义了获取。概括地说,它接受一个请求和一个或多个在操作期间不同点运行的算法。一个响应会传递给下面列出的最后两个算法。前两个算法可用于捕获上传。
要获取,给定一个请求 request,一个可选算法 processRequestBodyChunkLength,一个可选算法 processRequestEndOfBody,一个可选算法 processEarlyHintsResponse,一个可选算法 processResponse,一个可选算法 processResponseEndOfBody,一个可选算法 processResponseConsumeBody,以及一个可选布尔值 useParallelQueue(默认为 false),运行以下步骤。如果给定,processRequestBodyChunkLength 必须是一个接受表示已传输字节数的整数的算法。如果给定,processRequestEndOfBody 必须是一个不接受参数的算法。如果给定,processEarlyHintsResponse 必须是一个接受响应的算法。如果给定,processResponse 必须是一个接受响应的算法。如果给定,processResponseEndOfBody 必须是一个接受响应的算法。如果给定,processResponseConsumeBody 必须是一个接受响应以及 null、failure 或字节序列的算法。
用户代理可能会被要求暂停正在进行的获取。用户代理可以接受或忽略暂停请求。暂停的获取可以被恢复。如果正在进行的获取正在更新 HTTP 缓存中请求的响应,用户代理应忽略暂停请求。
如果请求的缓存模式为 "no-store" 或响应中出现
`Cache-Control: no-store
`
标头,则用户代理不会更新 HTTP 缓存中的条目。[HTTP-CACHING]
-
断言:request 的模式是 "
navigate
" 或 processEarlyHintsResponse 为 null。 -
令 taskDestination 为 null。
-
令 crossOriginIsolatedCapability 为 false。
-
给定 request,从客户端填充请求。
-
如果 request 的客户端非 null,则:
-
如果 useParallelQueue 为 true,则将 taskDestination 设置为启动新并行队列的结果。
-
令 timingInfo 为一个新的获取计时信息,其开始时间和重定向后开始时间是给定 crossOriginIsolatedCapability 的粗略共享当前时间,并且渲染阻塞设置为 request 的渲染阻塞。
-
令 fetchParams 为一个新的获取参数,其请求为 request,计时信息为 timingInfo,处理请求主体块长度为 processRequestBodyChunkLength,处理请求主体结束为 processRequestEndOfBody,处理早期提示响应为 processEarlyHintsResponse,处理响应为 processResponse,处理响应消费主体为 processResponseConsumeBody,处理响应主体结束为 processResponseEndOfBody,任务目标为 taskDestination,以及跨源隔离能力为 crossOriginIsolatedCapability。
-
如果以下所有条件都为真:
-
request 的URL 的方案是一个HTTP(S) 方案
-
request 的模式是 "
same-origin
"、"cors
" 或 "no-cors
" -
request 的方法是 `
GET
`
则:
-
令 onPreloadedResponseAvailable 为一个算法,该算法在给定响应 response 的情况下运行以下步骤:将 fetchParams 的预加载响应候选设置为 response。
-
令 foundPreloadedResource 为调用消费预加载资源的结果,针对 request 的客户端,给定 request 的URL、request 的目的地、request 的模式、request 的凭据模式、request 的完整性元数据和 onPreloadedResponseAvailable。
-
如果 foundPreloadedResource 为 true 且 fetchParams 的预加载响应候选为 null,则将 fetchParams 的预加载响应候选设置为 "
pending
"。
-
-
如果 request 的标头列表不包含 `
Accept
`,则:-
令 value 为 `
*/*
`。 -
如果 request 的发起者是 "
prefetch
",则将 value 设置为文档 `Accept
` 标头值。 -
否则,用户代理应根据 request 的目的地切换,将 value 设置为第一个匹配的语句(如果存在):
- "
document
" - "
frame
" - "
iframe
" - 文档 `
Accept
` 标头值 - "
image
" - `
image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5
` - "
json
" - `
application/json,*/*;q=0.5
` - "
style
" - `
text/css,*/*;q=0.1
`
- "
-
-
如果 request 的标头列表不包含 `
Accept-Language
`,则用户代理应将 (`Accept-Language
`, 一个适当的标头值) 追加到 request 的标头列表。 -
如果 request 的内部优先级为 null,则以实现定义的方式使用 request 的优先级、发起者、目的地和渲染阻塞,将 request 的内部优先级设置为一个实现定义的对象。
实现定义的对象可以包含 HTTP/2 的流权重和依赖关系,HTTP 可扩展优先级方案中用于适用传输(包括 HTTP/3)的优先级,以及用于优先处理 HTTP/1 获取的调度和处理的等效信息。[RFC9218]
-
如果 request 是一个子资源请求,则:
-
给定 fetchParams,运行主获取。
-
返回 fetchParams 的控制器。
要从客户端填充请求,给定一个请求 request:
4.1. 主获取
要执行主获取,给定获取参数fetchParams和一个可选的布尔值recursive(默认为false),执行以下步骤:
-
令request为fetchParams的请求。
-
令response为null。
-
如果是否应因端口错误而阻止 request、 是否应将获取 request 作为混合内容阻止、 是否应由内容安全策略阻止 request或 是否应由完整性策略阻止 request 返回已阻止,则将 response 设置为网络错误。
-
如果request的引用不是“
no-referrer
”,则将request的引用设置为调用确定request的引用的结果。[REFERRER]如在引用策略中所述,用户代理可以为最终用户提供覆盖request的引用为“
no-referrer
”或暴露较少敏感信息的选项。 -
如果所有以下条件为真,则将request的当前URL的方案设置为“
https
”:- request 的当前 URL 的方案是
"
http
" - request 的当前 URL 的主机是一个域
- request 的当前 URL 的主机的公共后缀不是 "
localhost
" 或 "localhost.
" - 根据已知 HSTS 主机域名匹配匹配
request
的当前 URL 的主机,结果为带有断言的
includeSubDomains
指令的超域匹配或一致匹配(带有或不带有断言的includeSubDomains
指令)[HSTS];或 请求的 DNS 解析根据第 9.5 节的[SVCB] 找到匹配的 HTTPS RR。[HSTS] [SVCB]
由于所有DNS操作通常都是实现定义的,如何确定DNS解析是否包含HTTPS RR也是实现定义的。由于传统上DNS操作不会在尝试获取连接之前执行,用户代理可能需要提前执行DNS操作,查询本地DNS缓存,或在获取算法的后期等待并可能在发现需要更改request的当前URL的方案时撤销逻辑。
- request 的当前 URL 的方案是
"
-
如果recursive为false,则request的以下步骤并行执行。
-
如果response为null,则将response设置为运行以下第一个匹配语句对应步骤的结果:
- fetchParams的预加载响应候选项不为null
- request 的当前 URL 的源与
request 的源同源,并且 request 的响应污染为 "
basic
" - request 的当前 URL 的方案为
"
data
" - request的模式为“
navigate
”或“websocket
” -
HTML分配从URL创建的任何文档和工作者,其方案为“
data
”一个唯一的不透明来源。仅可以从URL创建Service Worker,其方案为HTTP(S)方案。 - request的模式为“
same-origin
” -
返回网络错误。
- request的模式为“
no-cors
” - request的当前URL的方案不是HTTP(S)方案
-
返回网络错误。
- request的use-CORS-preflight标志已设置
- request的unsafe-request标志已设置,且request的方法不是CORS-safelisted方法或CORS-unsafe请求标头名称中request的标头列表不为空
- 否则
-
如果recursive为true,则返回response。
-
如果response不是网络错误且response不是过滤响应,则:
-
如果request的响应污染为“
cors
”,则:-
将headerNames设置为从提取的结果标头列表值给定 `
Access-Control-Expose-Headers
`和response的标头列表。 -
如果request的凭据模式不是“
include
”且headerNames包含`*
`,则将response的CORS公开标头名称列表设置为response的标头名称中所有唯一的值response的标头列表。 -
否则,如果headerNames不为null或失败,则将response的CORS公开标头名称列表设置为headerNames。
-
-
-
如果response不是网络错误,且以下任何返回已阻止:
则将response和internalResponse设置为网络错误。
-
如果response的类型为“
opaque
”,internalResponse的状态为206,internalResponse的range-requested标志已设置,且request的标头列表不包含`Range
`,则将response和internalResponse设置为网络错误。传统上,即使未请求范围,API也接受范围响应。这可以防止先前范围请求的部分响应被提供给未发出范围请求的API。
进一步详情
上述步骤防止以下攻击:
使用媒体元素请求跨源HTML资源的范围。虽然这是无效媒体,但可以在Service Worker中保留响应的克隆。这可以稍后用作脚本元素获取的响应。如果部分响应是有效的JavaScript(即使整个资源不是),执行它将泄露私人数据。
-
如果response不是网络错误,且request的方法为`
HEAD
`或`CONNECT
`,或internalResponse的状态为空体状态,则将internalResponse的主体设置为null并忽略向其进行的任何入队(如果有)。这标准化了对违反HTTP的服务器的错误处理。
-
如果request的完整性元数据不为空字符串,则:
-
否则,运行获取响应交接给定fetchParams和response。
给定获取响应交接,给定获取参数fetchParams和响应response,执行以下步骤:
-
令timingInfo为fetchParams的计时信息。
-
如果response不是网络错误,且fetchParams的请求的客户端是一个安全上下文,则将timingInfo的服务器计时标头设置为获取、解码和拆分`
Server-Timing
`的结果 来自response的内部响应的标头列表。使用_response_的内部响应是安全的,因为通过`
Timing-Allow-Origin
`标头保护暴露`Server-Timing
`标头数据。用户代理也可以决定将`
Server-Timing
`标头暴露给非安全上下文的请求。 -
如果 fetchParams 的 request 的 destination 为 "
document
",则将 fetchParams 的 controller 的 full timing info 设为 fetchParams 的 timing info。 -
令processResponseEndOfBody为以下步骤:
-
令unsafeEndTime为不安全共享当前时间。
-
将fetchParams的控制器的报告计时步骤设置为以下步骤,给定全局对象global:
-
令cacheState为response的缓存状态。
-
令bodyInfo为response的主体信息。
-
如果response的计时允许通过标志未设置,则将timingInfo设置为创建一个不透明计时信息的结果,并将cacheState设置为空字符串。
这包括response为网络错误的情况。
-
将responseStatus设置为0。
-
如果 fetchParams 的 请求 的 模式 不为 "
navigate
",或 response 的 重定向污染 为 "same-origin
":-
将responseStatus设置为response的状态。
-
令mimeType为从response的标头列表中提取的MIME类型的结果。
-
如果mimeType不是失败,则将bodyInfo的内容类型设置为最小化的受支持MIME类型的结果,给定mimeType。
-
-
如果fetchParams的请求的启动者类型不为null,则标记资源计时给定timingInfo,fetchParams的请求的URL,fetchParams的请求的启动者类型,global,cacheState,bodyInfo和responseStatus。
-
令processResponseEndOfBodyTask为以下步骤:
-
-
如果fetchParams的处理响应不为null,则排队一个获取任务以运行fetchParams的处理响应给定response,以fetchParams的任务目标。
-
如果internalResponse的主体为null,则运行processResponseEndOfBody。
-
否则:
-
令transformStream为一个新的
TransformStream
。 -
令identityTransformAlgorithm为一个算法,该算法给定chunk,入队chunk到transformStream。
-
设置transformStream,将transformAlgorithm设置为identityTransformAlgorithm,并将flushAlgorithm设置为processResponseEndOfBody。
-
将internalResponse的主体的流设置为internalResponse的主体的流的管道传输transformStream的结果。
此
TransformStream
是为了在流到达终点时接收通知的目的,并且是一个身份转换流。 -
-
如果fetchParams的处理响应消耗主体不为null,则:
4.2. 覆盖抓取
要覆盖抓取,给定"scheme-fetch
"或"http-fetch
"
type、抓取参数
fetchParams,以及可选布尔值 makeCORSPreflight(默认值为 false):
可能覆盖请求响应算法接收一个请求 request,返回一个响应或 null。其行为是 实现自定义,允许用户代理直接返回响应以干预 请求,或返回 null 以允许请求继续。
默认情况下,算法有如下简单实现:
-
返回 null。
用户代理通常会用更复杂的行为覆盖此默认实现。例如,用户代理可能认为为了用户安全,应当一般性地阻止对 `https://unsafe.example/` 的请求,同时为广泛使用的资源 `https://unsafe.example/widget.js` 合成一个 shim,以避免破坏。该实现可能如下:
4.3. Scheme抓取
要Scheme抓取,给定一个 抓取参数 fetchParams:
-
令 request 为 fetchParams 的请求。
-
根据 request 的当前URL的scheme进行分支并执行相应步骤:
- "
about
" -
如果 request 的当前URL的路径为字符串"
blank
",则返回一个新的响应,其状态信息为 `OK
`,头列表为 « (`Content-Type
`, `text/html;charset=utf-8
`) »,主体为空字节序列作为主体。 - "
blob
" -
-
如果 request 的方法不是 `
GET
` 或 blobURLEntry 为 null,则返回网络错误。[FILEAPI]`
GET
`方法限制仅为实现一致性,不具备其他实际用途。 -
令 requestEnvironment 为 确定环境,参数为 request 的结果。
-
令 isTopLevelNavigation 为 true 如果 request 的 目标为"
document
",否则为 false。 -
如果 isTopLevelNavigation 为 false 且 requestEnvironment 为 null, 则返回网络错误。
-
令 navigationOrEnvironment 为字符串"
navigation
"如果 isTopLevelNavigation为 true,否则为 requestEnvironment。 -
令 blob 为获取 blob 对象,参数为 blobURLEntry 和 navigationOrEnvironment 的结果。
-
令 response 为新的响应。
-
令 fullLength 为 blob 的
size
。 -
令 type 为 blob 的
type
。 -
否则:
-
设置 response 的范围请求标志。
-
令 rangeValue 为解析单个范围头值 参数为 rangeHeader 和 true 的结果。
-
如果 rangeValue 失败,则返回网络错误。
-
令 (rangeStart, rangeEnd) 为 rangeValue。
-
如果 rangeStart 为 null:
-
设置 rangeStart 为 fullLength − rangeEnd。
-
设置 rangeEnd 为 rangeStart + rangeEnd − 1。
-
-
否则:
-
如果 rangeStart 大于等于 fullLength,则 返回网络错误。
-
如果 rangeEnd 为 null 或 rangeEnd 大于等于 fullLength,则设置 rangeEnd 为 fullLength − 1。
-
-
令 slicedBlob 为调用切片blob,参数为 blob、rangeStart、rangeEnd + 1、type 的结果。
范围头表示包含端点的字节范围,而切片blob 算法输入范围不包含终点。需要对 rangeEnd 增加 1。
-
令 slicedBodyWithType 为 安全提取 slicedBlob 的结果。
-
令 contentRange 为调用构建内容范围 参数为 rangeStart、rangeEnd、fullLength 的结果。
-
设置 response 的状态为 206。
-
设置 response 的状态信息为 `
Partial Content
`。 -
设置 response 的头列表为 « (`
Content-Length
`, serializedSlicedLength), (`Content-Type
`, type), (`Content-Range
`, contentRange) »。
-
-
返回 response。
- "
data
" - "
file
" -
目前,遗憾的是,
file:
URL 暂留作读者练习。如有疑问,返回网络错误。
- HTTP(S) scheme
-
返回运行HTTP抓取,参数为 fetchParams 的结果。
- "
-
返回网络错误。
要确定环境,给定一个请求 request:
4.4. HTTP抓取
要HTTP抓取, 给定一个抓取参数 fetchParams和一个可选布尔值makeCORSPreflight(默认为false),执行以下步骤:
-
令request为fetchParams的请求。
-
令response和internalResponse都为null。
-
如果request的service-workers模式为"
all
",则:-
令requestForServiceWorker为request的克隆。
-
如果requestForServiceWorker的主体非空,则:
-
令transformStream为新的
TransformStream
。 -
令transformAlgorithm,参数为chunk,步骤如下:
-
设置transformStream, 其 transformAlgorithm 设为transformAlgorithm。
-
设置requestForServiceWorker的主体的流为 requestForServiceWorker的主体的流 通过管道 transformStream后的结果。
-
-
设置response为调用处理抓取, 参数为requestForServiceWorker、fetchParams的 控制器和fetchParams的 跨域隔离能力。
-
如果response非空,则:
-
设置fetchParams的时序信息的 最终service worker开始时间 为serviceWorkerStartTime。
- 如果request的主体非空,则 取消 request的主体,参数为undefined。
-
如果response不是过滤响应,则设置internalResponse为response;否则设置为 response的内部响应。
-
如果以下条件之一为真
-
response的类型为"
error
" - request的重定向模式不为
"
manual
"且 response的类型为 "opaqueredirect
" - request的重定向模式不为
"
follow
"且 response的URL列表有多于一项。
则返回网络错误。
-
-
-
-
如果response为null,则:
-
如果makeCORSPreflight为true且以下任一条件为真:
-
没有与request的方法对应的方法缓存项,且request的 方法不是CORS安全方法或 request的使用CORS预检标志被设置。
- 在request的头列表中的CORS不安全请求头名 有至少一项,没有与其对应的头名缓存项。
则:
此步骤会检查CORS预检缓存,如果没有合适的缓存项则执行CORS预检抓取,并且如果成功则会填充缓存。CORS预检抓取的目的是确保抓取的资源 熟悉CORS协议。缓存的目的是最小化 CORS预检抓取的数量。
-
-
如果request的重定向模式为 "
follow
",则设置 request的service-workers模式为 "none
"。来自网络的重定向(而不是来自service worker)不能暴露给service worker。
-
设置response和internalResponse为运行 HTTP网络或缓存抓取 参数为fetchParams的结果。
-
如果request的响应污染为 "
cors
"且对request和response执行CORS检查返回失败,则 返回网络错误。
-
-
如果request的响应污染或 response的 类型为"
opaque
",且对request的来源、 request的客户端、request的 目的地和internalResponse执行跨域资源策略检查返回阻止,则 返回网络错误。跨域资源策略检查会对来自网络和service worker的响应进行,这与CORS检查不同,因为request的客户端和service worker可以有不同的嵌入策略。
-
-
如果internalResponse的状态不是303,且request的 主体非空,且连接使用HTTP/2,则用户代理可以,甚至鼓励,发送
RST_STREAM
帧。303被排除,因为某些社区赋予其特殊含义。
-
根据request的 重定向模式分支:
- "
error
" -
-
将response设置为网络错误。
-
- "
manual
" -
-
如果request的模式为 "
navigate
",则设置 fetchParams的控制器的 下一个手动重定向步骤为运行HTTP重定向抓取, 参数为fetchParams和response。 -
否则,将response设置为一个不透明重定向过滤响应 其内部响应为internalResponse。
-
- "
follow
" -
-
将response设置为运行HTTP重定向抓取 参数为 fetchParams和response的结果。
-
- "
-
4.5. HTTP-重定向抓取
要HTTP-重定向抓取,给定一个 fetch params fetchParams和一个response response,执行以下步骤:
-
令 request 为 fetchParams 的 request。
-
令 internalResponse 为 response(如果 response 不是filtered response);否则令其为 response 的 internal response。
-
令 locationURL 为 internalResponse 的 location URL,其基于 request 的 current URL 的 fragment。
-
如果 locationURL 为 null,则返回 response。
-
如果 locationURL 为 failure,则返回一个network error。
-
如果 locationURL 的 scheme 不是 HTTP(S) scheme,则返回一个network error。
-
如果 request 的 redirect count 为 20,则返回一个network error。
-
将 request 的 redirect count 增加 1。
-
如果 request 的 mode 为 "
cors
",且 locationURL includes credentials,并且 request 的 origin 与 locationURL 的 origin 不是 same origin,则返回一个network error。 -
如果 request 的 response tainting 为 "
cors
" 并且 locationURL includes credentials,则返回一个network error。此项用于捕获跨源资源重定向到同源 URL 的情况。
-
如果 internalResponse 的 status 不是 303,且 request 的 body 非空,并且 request 的 body 的 source 为 null,则返回一个network error。
-
如果下列任一为真:
-
internalResponse 的 status 为 301 或 302 且 request 的 method 为 `
POST
`。 -
internalResponse 的 status 为 303 且 request 的 method 既不是 `
GET
` 也不是 `HEAD
`。
则:
-
对每个 headerName(遍历 request-body-header name),从 request 的 header list 中 删除 该 headerName。
-
-
如果 request 的 current URL 的 origin 与 locationURL 的 origin 不是 same origin,则对每个 headerName(遍历 CORS non-wildcard request-header name),从 request 的 header list 中 删除 该 headerName。
即:一旦在初始请求之后看到另一个 origin,就会移除 `
Authorization
` 头。 -
如果 request 的 body 非空,则将 request 的 body 设为对 request 的 safely extracting request 的 body 的 source 的结果的 body。
-
令 timingInfo 为 fetchParams 的 timing info。
-
将 timingInfo 的 redirect end time 和 post-redirect start time 设为 基于 fetchParams 的 coarsened shared current time。
-
如果 timingInfo 的 redirect start time 为 0,则将 timingInfo 的 redirect start time 设为 timingInfo 的 start time。
-
对 request 和 internalResponse 调用 set request 的 referrer policy on redirect。[REFERRER]
-
令 recursive 为 true。
-
如果 request 的 redirect mode 为 "
manual
",则: -
返回运行 main fetch,参数为 fetchParams 和 recursive 的结果。
这必须调用 main fetch,以确保 request 的 response tainting 正确。
4.6. HTTP-网络或缓存抓取
要HTTP-network-or-cache fetch,给定一个 fetch params fetchParams、可选布尔值 isAuthenticationFetch(默认 false),以及可选布尔值 isNewConnectionFetch(默认 false),执行以下步骤:
某些实现可能支持部分内容的缓存,参见 HTTP Caching。然而,这在浏览器缓存中并不广泛支持。[HTTP-CACHING]
-
令 request 为 fetchParams 的 request。
-
令 httpFetchParams 为 null。
-
令 httpRequest 为 null。
-
令 response 为 null。
-
令 storedResponse 为 null。
-
令 httpCache 为 null。
-
令 revalidatingFlag 为 未设置。
-
运行下列步骤,但当 fetchParams 被 abort when fetchParams 被canceled 时中止:
-
如果 request 的 traversable for user prompts 为 "
no-traversable
" 并且 request 的 redirect mode 为 "error
",则将 httpFetchParams 设为 fetchParams,并将 httpRequest 设为 request。 -
否则:
-
将 httpRequest 设为 request 的一个 clone。
实现建议在 request 的 body 的 stream 的 source 为 null 时,避免对其进行 tee 操作,因为那种情况下只需要单一的 body。
-
将 httpFetchParams 设为 fetchParams 的副本。
-
将 httpFetchParams 的 request 设为 httpRequest。
如果可能出现用户提示或重定向,用户代理可能需要在用户回答提示或确定重定向位置后使用一组新的头重新发送请求。届时原始请求体可能已部分发送,因此需要提前克隆请求(包括 body),以便有备用副本可用。
-
-
令 includeCredentials 为 true 当且仅当下列之一为真:
- request 的 credentials mode 为
"
include
" - request 的 credentials mode 为
"
same-origin
" 且 request 的 response tainting 为 "basic
"
否则为 false。
- request 的 credentials mode 为
"
-
如果 Cross-Origin-Embedder-Policy allows credentials 对 request 返回 false,则将 includeCredentials 设为 false。
-
令 contentLength 为 httpRequest 的 body 的 length(如果 httpRequest 的 body 非空);否则为 null。
-
令 contentLengthHeaderValue 为 null。
-
如果 httpRequest 的 body 为 null 且 httpRequest 的 method 为 `
POST
` 或 `PUT
`,则将 contentLengthHeaderValue 设为 `0
`。 -
如果 contentLength 非空,则将 contentLengthHeaderValue 设为 contentLength,并对其进行序列化及同构编码。
-
如果 contentLengthHeaderValue 非空,则向 httpRequest 的 header list append (`
Content-Length
`, contentLengthHeaderValue)。 -
如果 contentLength 非空且 httpRequest 的 keepalive 为 true,则:
-
令 inflightKeepaliveBytes 为 0。
-
令 group 为 httpRequest 的 client 的 fetch group。
-
令 inflightRecords 为 group 中那些 fetch records 的集合,这些记录的 request 的 keepalive 为 true 且 done flag 未设置。
-
对每个 fetchRecord(遍历 inflightRecords):
-
如果 contentLength 与 inflightKeepaliveBytes 的和 大于 64 kibibytes,则返回一个network error。
上述限制确保了被允许超出 环境设置对象 且包含请求体的请求,其大小是受限的,并且不会无限期存活。
-
-
如果 httpRequest 的 referrer 是一个 URL,则:
-
向 httpRequest 的 header list append (`
Referer
`, referrerValue)。
-
如果 httpRequest 的 initiator 为 "
prefetch
",则在 httpRequest 的 header list 中设置结构化字段值 (`Sec-Purpose
`, tokenprefetch
)。 -
如果 httpRequest 的 首部列表 不包含 `
User-Agent
`,则用户代理应:-
令 userAgent 为 httpRequest 的 client 的 环境默认 `
User-Agent
` 值。
-
-
如果 httpRequest 的 cache mode 为 "
default
" 并且其 header list 包含 `If-Modified-Since
`、`If-None-Match
`、`If-Unmodified-Since
`、`If-Match
` 或 `If-Range
`,则将 httpRequest 的 cache mode 设为 "no-store
"。 -
如果 httpRequest 的 cache mode 为 "
no-cache
",且 httpRequest 的 prevent no-cache cache-control header modification flag 未设置,且 httpRequest 的 header list 不包含 `Cache-Control
`,则向该 header list append (`Cache-Control
`, `max-age=0
`)。 -
如果 httpRequest 的 cache mode 为 "
no-store
" 或 "reload
",则:-
如果 httpRequest 的 header list 不包含 `
Pragma
`,则 append (`Pragma
`, `no-cache
`) 到该 header list。 -
如果 httpRequest 的 header list 不包含 `
Cache-Control
`,则 append (`Cache-Control
`, `no-cache
`) 到该 header list。
-
-
如果 httpRequest 的 header list 包含 `
Range
`,则向该 header list append (`Accept-Encoding
`, `identity
`)。这可以避免在处理已编码响应的一部分时出现失败。
此外,许多服务器 在接受非 identity 编码时错误地忽略 `
Range
` 头。 -
按 HTTP 规范修改 httpRequest 的 header list。如果 httpRequest 的 header list 已包含某个 header 的 name,则不要再次 append 该 header。
我们希望能使此处更具规范性。目前某些头(如 `
Accept-Encoding
`、`Connection
`、`DNT
`、`Host
`)在必要时应被追加。`
Accept
`、`Accept-Charset
` 和 `Accept-Language
` 此时不得被包含。`
Accept
` 和 `Accept-Language
` 已经被包含(除非使用fetch()
,默认不包含后者),而 `Accept-Charset
` 则浪费字节。详见 HTTP header layer division。 -
如果 includeCredentials 为 true,则:
-
如果 httpRequest 的 header list 不包含 `
Authorization
`,则:-
令 authorizationValue 为 null。
-
如果存在针对 httpRequest 的 authentication entry,并且要么 httpRequest 的 use-URL-credentials flag 未设置,要么 httpRequest 的 current URL 不 include credentials,则将 authorizationValue 设为该 authentication entry。
-
否则,如果 httpRequest 的 current URL include credentials 且 isAuthenticationFetch 为 true,则将 authorizationValue 设为 httpRequest 的 current URL,并将其 转换为 `
Authorization
` 值。 -
如果 authorizationValue 非空,则向 httpRequest 的 header list append (`
Authorization
`, authorizationValue)。
-
-
如果存在 proxy-authentication entry,则按需使用它。
这刻意不依赖于 httpRequest 的 credentials mode。
-
将 httpCache 设为运行 determining the HTTP cache partition,参数为 httpRequest 的结果。
-
如果 httpCache 为 null,则将 httpRequest 的 cache mode 设为 "
no-store
"。 -
如果 httpRequest 的 cache mode 既不是 "
no-store
" 也不是 "reload
",则:-
将 storedResponse 设为从 httpCache 中选择的响应(可能需要验证),按 HTTP Caching 的 "Constructing Responses from Caches" 章节 的规定(如果有的话)。[HTTP-CACHING]
按照 HTTP 要求,这仍会考虑 `
Vary
` 头。 -
如果 storedResponse 非空,则:
-
如果 httpRequest 的 cache mode 为 "
default
",且 storedResponse 为 stale-while-revalidate response,并且 httpRequest 的 client 非空,则:-
将 response 设为 storedResponse。
-
将 response 的 cache state 设为 "
local
"。 -
令 revalidateRequest 为 request 的一个 clone。
-
将 revalidateRequest 的 cache mode 设为 "
no-cache
"。 -
设置 revalidateRequest 的 prevent no-cache cache-control header modification flag。
-
将 revalidateRequest 的 service-workers mode 设为 "
none
"。 -
并行运行 main fetch,参数为新的 fetch params,其 request 为 revalidateRequest。
该抓取仅用于更新 httpCache 的状态,响应在当前请求中不会被使用。该抓取在客户端上下文中发出,因此如果该客户端消失,请求将被终止。
-
-
否则:
-
如果 storedResponse 是一个 stale response,则设置 revalidatingFlag。
-
如果 revalidatingFlag 已设置且 httpRequest 的 cache mode 既不是 "
force-cache
" 也不是 "only-if-cached
",则:-
如果 storedResponse 的 header list 包含 `
ETag
`,则向 httpRequest 的 header list append (`If-None-Match
`, `ETag
` 的 value)。 -
如果 storedResponse 的 header list 包含 `
Last-Modified
`,则向 httpRequest 的 header list append (`If-Modified-Since
`, `Last-Modified
` 的 value)。
另见 HTTP Caching 中 "Sending a Validation Request" 一章。[HTTP-CACHING]
-
-
否则,将 response 设为 storedResponse,并将其 cache state 设为 "
local
"。
-
-
-
-
-
如果被中止,则返回针对 fetchParams 的 appropriate network error。
-
如果 response 为 null,则:
-
如果 httpRequest 的 cache mode 为 "
only-if-cached
",则返回一个network error。 -
令 forwardResponse 为运行 HTTP-network fetch,参数为 httpFetchParams、includeCredentials 和 isNewConnectionFetch 的结果。
-
如果 httpRequest 的 method 为 unsafe 并且 forwardResponse 的 status 在 200 到 399(含)之间,则按 HTTP Caching 的 "Invalidating Stored Responses" 章节 使 httpCache 中的适当已存响应失效,并将 storedResponse 设为 null。[HTTP-CACHING]
-
如果 revalidatingFlag 已设置且 forwardResponse 的 status 为 304,则:
-
使用 forwardResponse 的 header list 来更新 storedResponse 的 header list,按 HTTP Caching 中 "Freshening Stored Responses upon Validation" 一章 的规定。[HTTP-CACHING]
这也会更新缓存中的存储响应。
-
将 response 设为 storedResponse。
-
将 response 的 cache state 设为 "
validated
"。
-
-
如果 response 仍为 null,则:
-
将 response 设为 forwardResponse。
-
将 httpRequest 与 forwardResponse 存入 httpCache,按 HTTP Caching 的 "Storing Responses in Caches" 章节 的规定。[HTTP-CACHING]
如果 forwardResponse 是一个network error,这实际上会缓存该网络错误(有时称为“负缓存”)。
关联的 body info 会与响应一起存储在缓存中。
-
-
-
将 response 的 URL list 设为 clone 自 httpRequest 的 URL list 的结果。
-
如果 httpRequest 的 header list 包含 `
Range
`,则将 response 的 range-requested flag 设为真。 -
将 response 的 request-includes-credentials 设为 includeCredentials。
-
如果 response 的 status 为 401,且 httpRequest 的 response tainting 不是 "
cors
",includeCredentials 为 true,且 request 的 traversable for user prompts 是一个 traversable navigable:-
需要测试:多个 `
WWW-Authenticate
` 头,缺失,解析问题。 -
如果 request 的 body 非空,则:
-
如果 request 的 body 的 source 为 null,则返回一个network error。
-
将 request 的 body 设为对 request 的 safely extracting 其 body 的 source 后的 body。
-
-
如果 request 的 use-URL-credentials flag 未设置或 isAuthenticationFetch 为 true,则:
-
如果 fetchParams 被 canceled,则返回针对 fetchParams 的 appropriate network error。
-
令 username 和 password 分别为在 request 的 traversable for user prompts 中提示终端用户获取的用户名和密码。
-
调用 Set the username,参数为 request 的 current URL 与 username。
-
调用 Set the password,参数为 request 的 current URL 与 password。
-
-
将 response 设为运行 HTTP-network-or-cache fetch,参数为 fetchParams 和 true 的结果。
-
-
如果 response 的 status 为 407,则:
-
如果 request 的 traversable for user prompts 为 "
no-traversable
",则返回一个network error。 -
需要测试:多个 `
Proxy-Authenticate
` 头,缺失,解析问题。 -
如果 fetchParams 被 canceled,则返回针对 fetchParams 的 appropriate network error。
-
在 request 的 traversable for user prompts 中按需提示终端用户并将结果存为一个 proxy-authentication entry。[HTTP]
代理认证的剩余细节由 HTTP 定义。
-
将 response 设为运行 HTTP-network-or-cache fetch,参数为 fetchParams 的结果。
-
-
如果下列全部为真:
-
response 的 status 为 421
-
isNewConnectionFetch 为 false
则:
-
如果 fetchParams 被 canceled,则返回针对 fetchParams 的 appropriate network error。
-
将 response 设为运行 HTTP-network-or-cache fetch,参数为 fetchParams、isAuthenticationFetch 和 true 的结果。
-
-
如果 isAuthenticationFetch 为 true,则为 request 和给定的 realm 创建一个 authentication entry。
4.7. HTTP-网络抓取
要进行HTTP-network fetch,给定一个fetch params fetchParams、可选布尔值 includeCredentials(默认 false)以及可选布尔值 forceNewConnection(默认 false),执行以下步骤:
-
令 request 为 fetchParams 的 request。
-
令 response 为 null。
-
令 timingInfo 为 fetchParams 的 timing info。
-
令 networkPartitionKey 为对 request 运行 determining the network partition key 的结果。
-
如果 forceNewConnection 为 true,则令 newConnection 为 "
yes
"; 否则为 "no
"。 -
根据 request 的 mode 分支:
- "
websocket
" -
令 connection 为对 request 的 current URL 运行 obtaining a WebSocket connection 的结果。
- 否则
-
令 connection 为运行 obtaining a connection,参数为 networkPartitionKey、request 的 current URL、 includeCredentials,以及 newConnection 的结果。
- "
-
运行下列步骤,但当 fetchParams 被 abort when canceled 时中止:
-
如果 connection 为 failure,则返回一个network error。
-
将 timingInfo 的 final connection timing info 设为对 connection 的 timing info 调用 clamp and coarsen connection timing info 的结果, 参数还包括 timingInfo 的 post-redirect start time 和 fetchParams 的 cross-origin isolated capability。
-
如果 connection 是 HTTP/1.x 连接,且 request 的 body 非空, 并且该 request 的 body 的 source 为 null,则返回一个network error。
- 将 timingInfo 的 final network-request start time 设为基于 fetchParams 的 coarsened shared current time,使用 fetchParams 的 cross-origin isolated capability。
-
使用 connection 通过 HTTP 发起对 request 的请求,并将结果设为 response,但遵循下列注意事项:
-
遵从 HTTP 的相关要求。[HTTP] [HTTP-CACHING]
-
如果 request 的 body 非空,且该 body 的 source 为 null, 则用户代理可使用最多 64 KiB 的缓冲区并将 request 的部分 body 存入该缓冲区。 如果用户代理从 request 的 body 读取超出该缓冲区的内容且需要重发 request, 则应改为返回一个network error。
-
循环执行以下步骤:
-
将 timingInfo 的 final network-response start time 设为在用户代理的 HTTP 解析器接收到响应的第一个字节后(例如 HTTP/2 的帧头字节或 HTTP/1.x 的响应状态行)基于 fetchParams 的 coarsened shared current time。
-
等待直到所有 HTTP 响应头被传输完毕。
-
令 status 为 HTTP 响应的状态码。
-
如果 status 在 100 到 199 的范围内(含):
-
如果 timingInfo 的 first interim network-response start time 为 0,则将其设为 timingInfo 的 final network-response start time。
-
如果 status 为 103 且 fetchParams 的 process early hints response 非空, 则 queue a fetch task 来运行 fetchParams 的 process early hints response,并传入 response。
这些类型的 HTTP 响应最终会被一个“最终”HTTP 响应跟随。
-
-
Fetch 与 HTTP 之间的确切分层仍需厘清,因此此处的 response 同时代表一个 response 与 HTTP 响应。
如果 HTTP 请求导致显示 TLS 客户端证书对话框,则:
-
如果 request 的 traversable for user prompts 是一个 traversable navigable,则应在该 request 的 traversable for user prompts 中提供该对话框。
-
否则,返回一个network error。
要传输 request 的 body body,运行以下步骤:
-
如果 body 为 null 且 fetchParams 的 process request end-of-body 非空, 则 queue a fetch task,使用 fetchParams 的 process request end-of-body 以及 fetchParams 的 task destination。
-
否则,若 body 非空:
-
令 processBodyChunk(参数为 bytes) 为下列步骤:
-
如果 fetchParams 被 canceled,则中止这些步骤。
-
并行运行此步骤:传输 bytes。
-
如果 fetchParams 的 process request body chunk length 非空, 则运行 fetchParams 的 process request body chunk length,参数为 bytes 的 length。
-
-
令 processEndOfBody 为下列步骤:
-
如果 fetchParams 被 canceled,则中止这些步骤。
-
如果 fetchParams 的 process request end-of-body 非空, 则运行该 process request end-of-body。
-
-
令 processBodyError(参数为 e) 为下列步骤:
-
如果 fetchParams 被 canceled,则中止这些步骤。
-
如果 e 是一个 "
AbortError
"DOMException
, 则 abort fetchParams 的 controller。 -
否则,terminate fetchParams 的 controller。
-
-
调用 Incrementally read,对 request 的 body 使用 processBodyChunk、processEndOfBody、processBodyError,以及 fetchParams 的 task destination。
-
-
-
-
如果被中止(If aborted),则:
-
如果 connection 使用 HTTP/2,则传输一个
RST_STREAM
帧。 -
返回针对 fetchParams 的 appropriate network error。
-
-
令 buffer 为一个空的 byte sequence。
这表示用户代理网络层内部的一个内部缓冲区。
-
令 stream 为一个新的
ReadableStream
。 -
令 pullAlgorithm 为下列步骤:
-
令 promise 为一个 new promise。
-
并行运行下列步骤:
-
如果 buffer 的大小小于用户代理选择的一个下限,且正在进行的抓取处于 suspended 状态, 则 resume 该抓取。
-
等待直到 buffer 非空。
-
Queue a fetch task 来运行下列步骤,使用 fetchParams 的 task destination。
-
从 buffer 中 Pull from bytes 到 stream。
-
如果 stream 已 errored,则 terminate fetchParams 的 controller。
-
Resolve promise,值为 undefined。
-
-
-
返回 promise。
-
-
令 cancelAlgorithm 为一个算法,给定 reason 时用 reason 调用并 aborts fetchParams 的 controller。
-
使用带字节读取支持的方式 Set up stream, 并将其 pullAlgorithm 设为 pullAlgorithm,将其 cancelAlgorithm 设为 cancelAlgorithm。
-
如果 includeCredentials 为 true, 则用户代理应当对给定的 request 和 response 执行 parse and store response `
Set-Cookie
` headers。 -
并行运行下列步骤:
-
运行下列步骤,但当 fetchParams 被 abort when canceled 时中止:
-
循环执行:
-
如果已从 response 的消息主体传输了一个或多个字节,则:
-
令 bytes 为已传输的字节。
-
令 codings 为对 `
Content-Encoding
` 和 response 的 header list 运行 extracting header list values 的结果。 -
令 filteredCoding 为 "
@unknown
"。 -
如果 codings 为 null 或 failure,则将 filteredCoding 设为空字符串。
-
否则,如果 codings 的 size 大于 1,则将 filteredCoding 设为 "
multiple
"。 -
否则,如果 codings[0] 是空字符串,或者它被用户代理支持,且与 HTTP Content Coding Registry 中列出的某项条目进行 byte-case-insensitive 匹配,则将 filteredCoding 设为对 codings[0] 进行 byte-lowercasing 的结果。[IANA-HTTP-PARAMS]
-
将 response 的 body info 的 content encoding 设为 filteredCoding。
-
将 response 的 body info 的 encoded size 增加 bytes 的 length。
-
令 bytes 为对 codings 和 bytes 运行 handling content codings 的结果。
这会使 `
Content-Length
` 头在其原有可靠性范围内变得不可靠。 -
将 response 的 body info 的 decoded size 增加 bytes 的 length。
-
如果 bytes 为 failure,则 terminate fetchParams 的 controller。
-
将 bytes 追加到 buffer。
-
如果 buffer 的大小大于用户代理选择的上限,则请求用户代理对正在进行的抓取执行 suspend 操作。
-
-
否则,如果对 response 的消息主体的字节传输正常完成,且 stream 可读(readable),则 close stream,并中止这些并行步骤。
-
-
-
如果中止(If aborted),则:
-
如果 fetchParams 已被 aborted,则:
-
设置 response 的 aborted flag。
-
如果 stream 可读,则使用对 fetchParams 的 controller 的 serialized abort reason 进行 deserialize a serialized abort reason,并用该结果以及一个 implementation-defined realm 对 stream 执行 error。
-
-
如果 connection 使用 HTTP/2,则传输一个
RST_STREAM
帧。 -
否则,除非出于性能考虑不宜关闭连接,用户代理应关闭 connection。
例如,如果用户代理知道在可重用连接上只剩少量字节要传输,则关闭连接并重新建立连接以处理下一次抓取可能会更差。
-
这些步骤是并行运行的,因为此时尚不清楚 response 的 body 是否相关(response 可能是一个重定向)。
-
4.8. CORS预检抓取
这实际上是用户代理用于检查是否理解 CORS协议的实现。 即所谓的CORS预检请求。如果成功,则会填充CORS预检缓存,以减少此类 抓取的次数。
要CORS预检抓取,给定一个request request,执行以下步骤:
-
令preflight为一个新的request,其 method为`
OPTIONS
`, URL列表为request的 URL列表的克隆, initiator为request的initiator, destination为request的destination, origin为request的origin, referrer为request的referrer, referrer policy为request的referrer policy, mode为"cors
", response tainting为"cors
"。preflight的service-workers模式无关紧要, 因为此算法使用HTTP-网络或缓存抓取而不是HTTP抓取。
-
追加( `
Access-Control-Request-Method
`, request的method )到preflight的头列表。 -
令headers为用request的头列表获取的CORS不安全请求头名。
-
如果headers非空,则:
-
令value为headers中的条目用`
,
`分隔。 -
追加( `
Access-Control-Request-Headers
`, value )到preflight的头列表。
此处特意不使用combine, 因为 0x2C 后跟 0x20 并不是实际实现方式,无论好坏。
-
-
令response为运行HTTP-网络或缓存抓取,参数为一个新的fetch params,其request为preflight的结果。
-
如果对request和response的CORS检查返回成功,并且 response的状态是ok状态,则:
该CORS检查针对request而不是preflight, 以确保使用正确的credentials mode。
-
令methods为对`
Access-Control-Allow-Methods
`和response的头列表运行extracting header list values的结果。 -
令headerNames为对`
Access-Control-Allow-Headers
`和response的头列表运行extracting header list values的结果。 -
如果methods或headerNames为failure,则返回网络错误。
-
如果methods为null且request的use-CORS-preflight flag已设置, 则设置methods为一个包含request的method的新列表。
这确保由于request的use-CORS-preflight flag被设置而发生的CORS预检抓取会被缓存。
-
如果request的method不在methods中,且request的method不是CORS安全方法,并且 request的credentials mode为"
include
"或 methods不包含`*
`,则返回网络错误。 -
如果request的头列表的某个名称是CORS非通配请求头名,且不是headerNames中的任何条目的不区分大小写字节匹配,则返回网络错误。
-
对用request的头列表获取的CORS不安全请求头名中的每个unsafeName, 如果unsafeName不是headerNames中的任何条目的不区分大小写字节匹配,且request的credentials mode为"
include
"或headerNames不包含`*
`,则返回网络错误。 -
令max-age为对`
Access-Control-Max-Age
`和response的头列表运行extracting header list values的结果。 -
如果max-age为failure或null,则将max-age设为5。
-
如果max-age大于对最大年龄施加的限制,则将max-age设为该限制值。
-
如果用户代理不提供缓存,则返回response。
-
对每个methods中的method,如果存在用request进行的方法缓存项匹配,则将匹配项的最大年龄设为max-age。
-
对每个methods中的method,如果不存在用request进行的方法缓存项匹配,则创建新的缓存项,参数为request、max-age、method和null。
-
对每个headerNames中的headerName,如果存在用request进行的头名缓存项匹配,则将匹配项的最大年龄设为max-age。
-
对每个headerNames中的headerName,如果不存在用request进行的头名缓存项匹配,则创建新的缓存项,参数为request、max-age、null和headerName。
-
返回response。
-
-
否则,返回网络错误。
4.9. CORS预检缓存
用户代理有一个关联的CORS预检缓存。一个 CORS预检缓存是一个列表,包含缓存项。
一个缓存项包括:
- 键(一个网络分区键)
- 字节序列化来源(一个 字节序列)
- URL(一个URL)
- 最大年龄(以秒为单位的数字)
- 凭证(一个布尔值)
- 方法(null、`
*
`,或一个 method) - 头名(null、`
*
`, 或一个头名)
缓存项必须在其 最大年龄字段指定的秒数后被移除(自存储该项起计)。缓存项也可以在此时间点之前被移除。
要创建新缓存项,给定request、 max-age、method和headerName,执行如下步骤:
要清除缓存项,给定request, 从用户代理的CORS预检缓存中 移除所有 缓存项,这些项满足: 键为对 request运行确定网络分区键的结果, 字节序列化来源为 对request运行字节序列化请求来源的结果, URL 为request的当前URL。
对于一个缓存项entry与request, 如果entry的键为对request运行确定网络分区键的结果, entry的字节序列化来源为对request运行字节序列化请求来源的结果, entry的URL为request的当前URL,并且下列之一为真:
- entry的凭证为true
- entry的凭证为false且request的
credentials mode不为"
include
"
则为缓存项匹配。
有方法缓存项匹配,
当用户代理的CORS预检缓存中存在某缓存项,且该项与request有缓存项匹配,并且其方法为method或`*
`时。
有头名缓存项匹配, 当用户代理的CORS预检缓存中存在某缓存项,且该项与request有缓存项匹配,并且下列之一为真:
- 其头名与headerName为不区分大小写字节匹配
- 其头名为`
*
`且 headerName不是CORS非通配请求头名
则为真。
4.10. CORS检查
要对request和response进行CORS检查,执行如下步骤:
-
令origin为从response的头列表 获取 `
Access-Control-Allow-Origin
`的结果。 -
如果origin为null,则返回失败。
null不是`
null
`。 -
如果request的credentials mode不为"
include
", 且origin为`*
`,则返回成功。 -
如果对request运行字节序列化请求来源的结果不等于origin,则返回失败。
-
如果request的credentials mode不为"
include
", 则返回成功。 -
令credentials为从response的头列表 获取 `
Access-Control-Allow-Credentials
`的结果。 -
如果credentials为`
true
`,则返回成功。 -
返回失败。
4.11. TAO检查
要对request和response进行TAO检查,执行如下步骤:
4.12. 延迟抓取
延迟抓取允许调用方请求在尽可能晚的时刻进行抓取,即在抓取组被终止时,或在超时之后。
延迟抓取任务源是一个任务源,用于更新延迟抓取的结果。用户代理必须优先处理该任务源中的任务,高于其他可能导致运行脚本的任务源,例如
DOM操作任务源,以便在运行任何可能依赖于
fetchLater()
调用的脚本之前,反映最新的状态。
要排队延迟抓取,给定一个request
request,一个null或
DOMHighResTimeStamp
activateAfter,以及onActivatedWithoutTermination,这是一个无参算法:
-
对request运行从客户端填充请求。
-
将request的service-workers模式设为"
none
"。 -
将request的keepalive设为true。
-
令deferredRecord为一个新的延迟抓取记录,其 request为request,notify invoked为onActivatedWithoutTermination。
-
如果activateAfter非null,则并行运行以下步骤:
-
返回deferredRecord。
要计算request request的总请求长度:
要对fetch group fetchGroup进行处理延迟抓取:
要处理延迟抓取 deferredRecord:
4.12.1. 延迟抓取配额
本节为非规范性内容。
延迟抓取配额分配给一个顶层可遍历对象(即“标签页”),共640KiB。顶层文档及其同源直接嵌套文档可使用此配额进行延迟抓取队列,也可通过权限策略将部分配额委托给跨域嵌套文档。
默认情况下,这640KiB中的128KiB用于委托给跨域嵌套文档,每个嵌套文档可预留8KiB。
顶层文档及其后续嵌套文档可以通过权限策略控制分配给跨域子文档的配额。默认情况下,
"deferred-fetch-minimal
"
策略对任何来源都启用,
而
"deferred-fetch
"
仅对顶层文档来源启用。通过放宽"deferred-fetch
"
策略,可为特定来源及嵌套文档分配64KiB。同理,通过限制"deferred-fetch-minimal
"
策略,可防止文档默认预留的8KiB。禁用顶层文档自身的"deferred-fetch-minimal
"
策略后,全部128KiB委托配额将重新归入640KiB主池。
对于某个文档分配的配额,针对同一报告来源(即request的URL的来源),只能同时使用最多64KiB。这防止了某些第三方库在未有数据要发送时就机会性地预留配额的情况。
以下对 fetchLater()
的调用均会因请求本身超出分配给报告源的 64 KiB
配额而抛出异常。注意,请求的大小包括 URL、body、头列表以及 referrer。
fetchLater( a_72_kb_url);
fetchLater( "https://origin.example.com" , { headers: headers_exceeding_64kb});
fetchLater( a_32_kb_url, { headers: headers_exceeding_32kb});
fetchLater( "https://origin.example.com" , { method: "POST" , body: body_exceeding_64_kb});
fetchLater( a_62_kb_url /* with a 3kb referrer */ );
在以下序列中,前两个请求会成功,第三个请求会抛出异常。因为前两次调用未超出整体 640 KiB 配额,但第三次请求超出了 https://a.example.com
的报告源配额。
fetchLater( "https://a.example.com" , { method: "POST" , body: a_64kb_body});
fetchLater( "https://b.example.com" , { method: "POST" , body: a_64kb_body});
fetchLater( "https://a.example.com" );
同源嵌套文档与父文档共享配额。但跨源或跨 agent 的 iframe 默认每个只获得 8 KiB 配额。所以在以下例子中,前三次调用会成功,最后一次会抛出异常。
// In main page
fetchLater( "https://a.example.com" , { method: "POST" , body: a_64kb_body});
// In same-origin nested document
fetchLater( "https://b.example.com" , { method: "POST" , body: a_64kb_body});
// In cross-origin nested document at https://fratop.example.com
fetchLater( "https://a.example.com" , { body: a_5kb_body});
fetchLater( "https://a.example.com" , { body: a_12kb_body});
要让上例不抛出异常,顶级文档可以通过例如以下响应头将部分配额委托给 https://fratop.example.com
:
Permissions-Policy: deferred-fetch=(self "https://fratop.example.com")
每个嵌套文档会保留自己的配额,所以以下代码可正常工作,因为每个 frame 都会保留 8 KiB:
// In cross-origin nested document at https://fratop.example.com/frame-1
fetchLater( "https://a.example.com" , { body: a_6kb_body});
// In cross-origin nested document at https://fratop.example.com/frame-2
fetchLater( "https://a.example.com" , { body: a_6kb_body});
下面的树形结构演示了配额如何分配给不同嵌套文档:
-
https://top.example.com
,权限策略为Permissions-policy: deferred-fetch=(self "https://ok.example.com")
-
https://top.example.com/frame
:与顶级可遍历项共享配额,因为它们同源。-
https://x.example.com
:获得 8 KiB。
-
-
https://x.example.com
:获得 8 KiB。-
https://top.example.com
:0。即使与顶级可遍历项同源,也不会自动共享配额,因为它们之间有跨源中介。
-
-
https://ok.example.com/good
:通过 "deferred-fetch
" 策略获得 64 KiB。-
https://x.example.com
:无配额。只有与顶级可遍历项同源的文档才可依据 "deferred-fetch-minimal
" 策略分配 8 KiB。
-
-
https://ok.example.com/redirect
,导航到https://x.example.com
:无配额。为https://ok.example.com
保留的 64 KiB 不可用于https://x.example.com
。 -
https://ok.example.com/back
,导航到https://top.example.com
:与顶级可遍历项共享配额,因为它们同源。
-
在上述例子中,顶级可遍历项及其同源后代共享 384 KiB 配额。该值计算如下:
-
最初为 顶级可遍历项分配 640 KiB。
-
128 KiB 保留给 "
deferred-fetch-minimal
" 策略。 -
64 KiB 保留给导航到
https://ok.example/good
的容器。 -
64 KiB 保留给导航到
https://ok.example/redirect
的容器,导航离开后则丢失该配额。 https://ok.example.com/back
未保留 64 KiB,因为它导航回了 顶级可遍历项的源。-
640 − 128 − 64 − 64 = 384 KiB。
本规范定义了一个由字符串 "deferred-fetch
" 标识的策略受控特性。其默认允许列表为 "self
"。
本规范定义了一个由字符串 "deferred-fetch-minimal
"
标识的策略受控特性。其默认允许列表为 "*
"。
为
deferred-fetch-minimal
保留的配额为 128 KiB。
每个 可导航容器都有一个关联的数字 保留的延迟获取配额。其可能值为 最小配额(8 KiB),以及 普通配额(0 或 64 KiB)。除非另有说明,默认为 0。
要获取 文档 document 和 origin-或-null origin 的 可用延迟获取配额:
-
令 controlDocument 为 document 的 延迟获取控制文档。
-
令 navigable 为 controlDocument 的 节点可导航项。
-
令 isTopLevel 为 true,当 controlDocument 的 节点可导航项为 顶级可遍历项时;否则为 false。
-
令 deferredFetchAllowed 为 true,如果 controlDocument被允许使用策略受控特性 "
deferred-fetch
",否则为 false。 -
令 deferredFetchMinimalAllowed 为 true,如果 controlDocument被允许使用策略受控特性 "
deferred-fetch-minimal
",否则为 false。 -
令 quota 为首个匹配语句的结果:
- isTopLevel 为 true 且 deferredFetchAllowed 为 false
- 0
- isTopLevel 为 true 且 deferredFetchMinimalAllowed 为 false
-
640 KiB
640kb 应该足够所有人使用。
- isTopLevel 为 true
-
512 KiB
默认 640 KiB,减去 为
deferred-fetch-minimal
保留的配额) - deferredFetchAllowed 为 true 且 navigable 的 可导航容器的 保留的延迟获取配额为 普通配额
- 普通配额
- deferredFetchMinimalAllowed 为 true 且 navigable 的 可导航容器的 保留的延迟获取配额为 最小配额
- 最小配额
- 否则
- 0
-
令 quotaForRequestOrigin 为 64 KiB。
-
对于每个 navigable 属于 controlDocument 的 节点可导航项的 包含性后代可导航项,其 活动文档的 延迟获取控制文档为 controlDocument:
-
如果 quota 小于等于 0,则返回 0。
-
如果 quota 小于 quotaForRequestOrigin,则返回 quota。
-
返回 quotaForRequestOrigin。
要为 可导航容器 container 和 origin originToNavigateTo 保留延迟获取配额:
本算法在导航时调用,当导航的源文档为可导航项的父文档。若权限策略允许,可能为容器及其可导航项保留 64kb 或 8kb 配额。容器文档无法观察保留的配额是否实际被使用。算法假定容器文档可能会将配额委托给被导航容器,保留的配额只在此情况下应用,若最终为共享则忽略。如果配额已保留且文档最终与父文档同源,则该配额会释放。
-
将 container 的 保留的延迟获取配额设置为 0。
-
如果 容器继承的策略 对于 "
deferred-fetch
", container 和 originToNavigateTo 为"Enabled"
,且 可用延迟获取配额对 controlDocument 大于等于 普通配额,则将 container 的 保留的延迟获取配额设置为 普通配额并返回。 -
如果所有以下条件均为真:
要释放延迟获取配额(如有必要),针对 文档 document,如果 document 的 节点可导航项的 容器文档不为 null,且其 origin与 document 同源,则 将 document 的 节点可导航项的 可导航容器的 保留的延迟获取配额设置为 0。
本算法在创建 文档时调用。确保同源嵌套文档不保留配额,因为它们本就与父文档共享。仅能在文档创建时调用,因为 文档的 origin只有在处理重定向后才能确定。
要获取 文档 document 的 延迟获取控制文档:
5. Fetch API
这个fetch()
方法是用于fetching资源的相对底层的API。它涵盖的范围比XMLHttpRequest
略广,但在请求进展方面(非响应进展)目前有所欠缺。
fetch()
方法使得fetch资源并将其内容提取为Blob
相当简单:
fetch( "/music/pk/altes-kamuffel.flac" )
. then( res => res. blob()). then( playBlob)
如果你只关心记录一个特定的响应头:
fetch( "/" , { method: "HEAD" })
. then( res => log( res. headers. get( "strict-transport-security" )))
如果你想检查一个特定的响应头,然后处理跨域资源的响应:
fetch( "https://pk.example/berlin-calling.json" , { mode: "cors" })
. then( res => {
if ( res. headers. get( "content-type" ) &&
res. headers. get( "content-type" ). toLowerCase(). indexOf( "application/json" ) >= 0 ) {
return res. json()
} else {
throw new TypeError ()
}
}). then( processJSON)
如果你想处理URL查询参数:
var url = new URL( "https://geo.example.org/api" ),
params = { lat: 35.696233 , long : 139.570431 }
Object. keys( params). forEach( key => url. searchParams. append( key, params[ key]))
fetch( url). then( /* … */ )
如果你想逐步接收主体数据:
function consume( reader) {
var total = 0
return pump()
function pump() {
return reader. read(). then(({ done, value}) => {
if ( done) {
return
}
total += value. byteLength
log( `received ${ value. byteLength} bytes ( ${ total} bytes in total)` )
return pump()
})
}
}
fetch( "/music/pk/altes-kamuffel.flac" )
. then( res => consume( res. body. getReader()))
. then(() => log( "consumed the entire body without keeping the whole thing in memory!" ))
. catch ( e => log( "something went wrong: " + e))
5.1. Headers 类
typedef (sequence <sequence <ByteString >>or record <ByteString ,ByteString >); [
HeadersInit Exposed =(Window ,Worker )]interface {
Headers constructor (optional HeadersInit );
init undefined append (ByteString ,
name ByteString );
value undefined delete (ByteString );
name ByteString ?get (ByteString );
name sequence <ByteString >getSetCookie ();boolean has (ByteString );
name undefined set (ByteString ,
name ByteString );
value iterable <ByteString ,ByteString >; };
Headers
对象有一个关联的
头列表
(一个 头列表),初始为空。
这可以是指向其他对象的 头列表 的指针,例如
请求,如 Request
对象所示。
一个Headers
对象也有一个关联的guard(保护),它是一个headers guard(头保护)。一个头保护是“immutable
”、“request
”、“request-no-cors
”、“response
”或“none
”。
-
headers = new Headers([init])
-
创建一个新的
Headers
对象。init可以用来填充它的内部头列表,如下面的示例所示。 -
headers . append(name, value)
-
将一个头添加到headers中。
-
headers . delete(name)
-
从headers中删除一个头。
-
headers . get(name)
-
以字符串形式返回所有名称为name的头的值,用逗号和空格分隔。
-
headers . getSetCookie()
-
返回所有名称为`
Set-Cookie
`的头的值的列表。 -
headers . has(name)
-
返回是否存在名称为name的头。
-
headers . set(name, value)
-
用value替换第一个名称为name的头的值,并删除任何剩余的名称为name的头。
for(const [name, value] of headers)
-
headers可以被迭代。
未共享“request-no-cors
”的步骤,因为在CORS-安全列出的请求头中,无法获得始终成功的虚假值(对于delete()
)。
要追加一个头(name,
value)到一个Headers
对象headers,请执行以下步骤:
-
规范化 value。
-
如果为headers验证(name, value)返回false,则返回。
-
如果headers的保护是“
request-no-cors
”:-
如果temporaryValue为null,则将temporaryValue设置为value。
-
否则,将temporaryValue设置为temporaryValue,后跟0x2C 0x20,再后跟value。
-
如果(name, temporaryValue)不是一个no-CORS安全列出的请求头,则返回。
-
如果headers的保护是“
request-no-cors
”,则从headers中移除受保护的no-CORS请求头。
要填充一个Headers
对象headers,使用给定对象object,请执行以下步骤:
方法delete(name)
的步骤是:
方法getSetCookie()
的步骤是:
方法set(name, value)
的步骤是:
要迭代的值对是通过对排序和组合与this的头列表运行来返回的值。
5.2. BodyInit 联合类型
typedef (Blob 或 BufferSource 或 FormData 或 URLSearchParams 或 USVString );
XMLHttpRequestBodyInit typedef (ReadableStream 或 XMLHttpRequestBodyInit );
BodyInit
为了从安全提取一个带类型的body,从一个字节序列或BodyInit
对象object中,执行以下步骤:
-
如果object是一个
ReadableStream
对象,则: -
返回提取object的结果。
为了从提取一个带类型的body,从一个字节序列或BodyInit
对象object中,使用一个可选的布尔值keepalive(默认值为false),执行以下步骤:
-
让stream为null。
-
如果object是一个
ReadableStream
对象,则将stream设置为object。 -
否则,将stream设置为新的
ReadableStream
对象,并设置stream为字节读取支持。 -
断言:stream 是一个
ReadableStream
对象。 -
让action为null。
-
让source为null。
-
让length为null。
-
让type为null。
-
根据object进行切换:
Blob
-
将source设置为object。
将length设置为object的
size
。如果object的
type
属性不是空字节序列,则将type设置为它的值。 - 字节序列
-
将source设置为object。
BufferSource
-
将source设置为对象持有字节的副本。
FormData
-
将action设置为此步骤:运行
multipart/form-data
编码算法,使用object的条目列表和UTF-8。将source设置为object。
将length设置为不明确,请参见html/6424以改进此内容。
将type设置为`
multipart/form-data; boundary=
`,后跟multipart/form-data
边界字符串,由multipart/form-data
编码算法生成。 URLSearchParams
-
将source设置为运行
application/x-www-form-urlencoded
序列化器的结果,与object的列表。将type设置为`
application/x-www-form-urlencoded;charset=UTF-8
`。 - 标量值字符串
-
将source设置为object的UTF-8编码。
将type设置为`
text/plain;charset=UTF-8
`。 ReadableStream
-
如果action不为null,则并行执行以下步骤:
-
运行action。
只要有一个或多个字节可用且stream没有错误,就将从可用字节创建的结果作为
Uint8Array
入队到stream中。当运行action完成时,关闭stream。
-
-
返回(body, type)。
5.3. Body 混入
interface mixin {
Body readonly attribute ReadableStream ?body ;readonly attribute boolean bodyUsed ; [NewObject ]Promise <ArrayBuffer >arrayBuffer (); [NewObject ]Promise <Blob >blob (); [NewObject ]Promise <Uint8Array >bytes (); [NewObject ]Promise <FormData >formData (); [NewObject ]Promise <any >json (); [NewObject ]Promise <USVString >text (); };
像HTML这样不希望网络层依赖的格式可能不会在此公开。相反,HTML解析器API可能会在适当的时候接受一个流。
包括Body
接口混入的对象有一个关联的body(null或body)。
包括Body
接口混入的对象在其body为非null且其body的stream为disturbed或locked时,称为unusable。
-
requestOrResponse . body
-
以
ReadableStream
形式返回requestOrResponse的body。 -
requestOrResponse . bodyUsed
-
返回requestOrResponse的body是否已被读取。
-
requestOrResponse . arrayBuffer()
-
返回一个promise,该promise在requestOrResponse的body作为
ArrayBuffer
时被实现。 -
requestOrResponse . blob()
-
返回一个promise,该promise在requestOrResponse的body作为
Blob
时被实现。 -
requestOrResponse . bytes()
-
返回一个promise,该promise在requestOrResponse的body作为
Uint8Array
时被实现。 -
requestOrResponse . formData()
-
返回一个promise,该promise在requestOrResponse的body作为
FormData
时被实现。 -
requestOrResponse . json()
-
返回一个promise,该promise在requestOrResponse的body被解析为JSON时被实现。
-
requestOrResponse . text()
-
返回一个promise,该promise在requestOrResponse的body作为字符串时被实现。
要获取MIME类型,给定一个Request
或Response
对象requestOrResponse:
-
将headers设为null。
-
如果requestOrResponse是一个
Request
对象,则将headers设为requestOrResponse的request的header list。 -
否则,将headers设为requestOrResponse的response的header list。
-
将mimeType设为从headers中提取MIME类型的结果。
-
如果mimeType失败,则返回null。
-
返回mimeType。
consume body算法,在给定一个包括Body
的object和一个接受字节序列并返回一个JavaScript值或抛出异常的算法convertBytesToJSValue时,执行以下步骤:
arrayBuffer()
方法的步骤是:将this与以下步骤一起运行consume body,给定一个字节序列bytes,返回从bytes创建的ArrayBuffer在this的相关领域中。
上面的方法可能会以RangeError
作为拒绝。
blob()
方法的步骤是:将this与以下步骤一起运行consume body,给定一个字节序列bytes,返回一个bytes作为内容的Blob
,其type
属性是使用this的获取MIME类型的结果。
bytes()
方法的步骤是返回运行consume body的结果,使用this并在给定字节序列bytes的情况下:返回创建的Uint8Array
的结果,这个this的relevant realm。
上述方法可能会因RangeError
而被拒绝。
formData()
方法的步骤是:将this与以下步骤一起运行consume body,给定一个字节序列bytes:
-
将mimeType设为使用this运行获取MIME类型的结果。
-
如果mimeType为非null,则切换mimeType的精髓并运行相应的步骤:
- "
multipart/form-data
" -
-
根据Returning Values from Forms: multipart/form-data中规定的规则,使用mimeType中的`
boundary
`参数的值来解析bytes。[RFC7578]每个`
Content-Disposition
`头包含`filename
`参数的部分,必须解析为一个条目,其值是一个File
对象,该对象的内容是该部分的内容。name
属性的值必须是该部分的`filename
`参数的值。type
属性的值必须是该部分的`Content-Type
`头的值(如果该部分有此头),否则为`text/plain
`([RFC7578]第4.4节定义的默认值)。每个`
Content-Disposition
`头不包含`filename
`参数的部分,必须解析为一个条目,其值为该部分内容的UTF-8无BOM解码的内容。这与是否存在或具有`Content-Type
`头以及`charset
`参数的值无关。一个`
Content-Disposition
`头包含`name
`参数,其值为`_charset_
`的部分,解析方式与其他部分相同。它不会更改编码。 -
返回一个新的
FormData
对象,将解析操作产生的每个entry附加到其entry list。
上面是
multipart/form-data
的粗略近似,正在编写更详细的解析规范。欢迎志愿者。 -
- "
application/x-www-form-urlencoded
"
- "
json()
方法的步骤是:将this与以下步骤一起运行consume body并从字节序列中解析JSON。
上面的方法可能会以SyntaxError
作为拒绝。
text()
方法的步骤是:将this与以下步骤一起运行consume body并UTF-8解码。
5.4. Request类
typedef (Request or USVString ); [
RequestInfo Exposed =(Window ,Worker )]interface {
Request constructor (RequestInfo ,
input optional RequestInit = {});
init readonly attribute ByteString method ;readonly attribute USVString url ; [SameObject ]readonly attribute Headers headers ;readonly attribute RequestDestination destination ;readonly attribute USVString referrer ;readonly attribute ReferrerPolicy referrerPolicy ;readonly attribute RequestMode mode ;readonly attribute RequestCredentials credentials ;readonly attribute RequestCache cache ;readonly attribute RequestRedirect redirect ;readonly attribute DOMString integrity ;readonly attribute boolean keepalive ;readonly attribute boolean isReloadNavigation ;readonly attribute boolean isHistoryNavigation ;readonly attribute AbortSignal signal ;readonly attribute RequestDuplex duplex ; [NewObject ]Request clone (); };Request includes Body ;dictionary {
RequestInit ByteString ;
method HeadersInit ;
headers BodyInit ?;
body USVString ;
referrer ReferrerPolicy ;
referrerPolicy RequestMode ;
mode RequestCredentials ;
credentials RequestCache ;
cache RequestRedirect ;
redirect DOMString ;
integrity boolean ;
keepalive AbortSignal ?;
signal RequestDuplex ;
duplex RequestPriority ;
priority any ; // can only be set to null };
window enum {
RequestDestination ,
"" ,
"audio" ,
"audioworklet" ,
"document" ,
"embed" ,
"font" ,
"frame" ,
"iframe" ,
"image" ,
"json" ,
"manifest" ,
"object" ,
"paintworklet" ,
"report" ,
"script" ,
"sharedworker" ,
"style" ,
"track" ,
"video" ,
"worker" };
"xslt" enum {
RequestMode ,
"navigate" ,
"same-origin" ,
"no-cors" };
"cors" enum {
RequestCredentials ,
"omit" ,
"same-origin" };
"include" enum {
RequestCache ,
"default" ,
"no-store" ,
"reload" ,
"no-cache" ,
"force-cache" };
"only-if-cached" enum {
RequestRedirect ,
"follow" ,
"error" };
"manual" enum {
RequestDuplex };
"half" enum {
RequestPriority ,
"high" ,
"low" };
"auto"
"serviceworker
" 从 RequestDestination
中省略了,因为它无法从
JavaScript 中观察到。实现仍然需要将其作为一个 destination 支持。"websocket
" 从 RequestMode
中省略了,因为它既不能使用也不能从 JavaScript 中观察到。
一个 Request
对象有一个相关的 request
(一个 request)。
一个 Request
对象也有一个相关的 headers(null 或
一个 Headers
对象),初始值为 null。
一个 Request
对象有一个相关的 signal(null 或
一个 AbortSignal
对象),初始值为 null。
一个 Request
对象的 body 是它的 request 的
body。
-
request = new Request(input [, init])
-
返回一个新的 request,其
url
属性是 input 如果 input 是字符串,并且 input 的url
如果 input 是一个Request
对象。init 参数是一个对象,其属性可以按如下方式设置:
method
- 一个字符串,用于设置 request 的
method
。 headers
- 一个
Headers
对象、一个对象字面量或一个由两个项目数组组成的数组,用于设置 request 的headers
。 body
- 一个
BodyInit
对象或 null 用于设置 request 的 body。 referrer
- 一个字符串,其值为同源 URL、"
about:client
" 或空字符串,用于设置 request 的 referrer。 referrerPolicy
- 一个 referrer policy 用于设置 request 的
referrerPolicy
。 mode
- 一个字符串,用于指示请求是使用 CORS,还是仅限于同源 URL。设置 request 的
mode
。 如果 input 是一个字符串,它默认为 "cors
"。 credentials
- 一个字符串,指示请求时是否始终、从不发送凭据,还是仅在发送到同源 URL 时发送——以及响应中返回的任何凭据是否始终、从不使用,还是仅在从同源 URL 接收到时使用。设置
request 的
credentials
。 如果 input 是一个字符串,它默认为 "same-origin
"。 cache
- 一个字符串,指示请求将如何与浏览器的缓存交互,以设置
request 的
cache
。 redirect
- 一个字符串,指示 request 是遵循重定向,在遇到重定向时产生错误,还是返回重定向(以不透明方式)。设置 request 的
redirect
。 integrity
- 通过 request 获取的资源的加密哈希值。设置 request 的
integrity
。 keepalive
- 一个布尔值,用于设置 request 的
keepalive
。 signal
- 一个
AbortSignal
用于设置 request 的signal
。 window
- 只能为 null。用于将 request 与任何
Window
解除关联。 duplex
- "
half
" 是唯一有效的值,它用于启动半双工 Fetch(即用户代理在处理响应之前发送整个请求)。 "full
" 保留供将来使用,用于启动全双工 Fetch(即用户代理可以在发送整个请求之前处理响应)。当body
是ReadableStream
对象时需要设置该成员。参见 issue #1254 以定义 "full
"。 priority
- 一个字符串,用于设置 request 的 priority。
-
request . method
- 返回 request 的 HTTP method,默认值为 "
GET
"。 -
request . url
- 以字符串形式返回 request 的 URL。
-
request . headers
- 返回一个
Headers
对象,该对象由与 request 关联的 headers 组成。 请注意,由用户代理在网络层添加的 headers 不会在此对象中计算,例如 "Host
" header。 -
request . destination
- 返回 request 请求的资源种类,例如 "
document
" 或 "script
"。 -
request . referrer
- 返回 request 的 referrer。如果在 init 中显式设置,其值可以是同源 URL、表示没有 referrer 的空字符串,以及默认为 global
默认值时的 "
about:client
"。这在 fetch 期间用于确定所发出请求的Referer
header 的值。 -
request . referrerPolicy
- 返回与 request 关联的 referrer policy。这在 fetch 期间用于计算 request 的 referrer 的值。
-
request . mode
- 返回与 request 关联的 mode,它是一个字符串,用于指示 请求是使用 CORS,还是仅限于同源 URL。
-
request . credentials
- 返回与 request 关联的 credentials mode,它是一个字符串, 指示请求时是否始终、从不发送凭据,还是仅在发送到同源 URL 时发送。
-
request . cache
- 返回与 request 关联的 cache mode,它是一个字符串,用于指示 请求在 fetch 时将如何与浏览器的缓存交互。
-
request . redirect
- 返回与request关联的重定向模式,这是一个字符串, 指示在抓取过程中如何处理该请求的重定向。一个请求默认会跟随重定向。
-
request . integrity
- 返回 request 的子资源完整性元数据,它是 被 fetch 的资源的加密哈希。它的值由多个以空格分隔的哈希组成。[SRI]
-
request . keepalive
- 返回一个布尔值,指示 request 是否可以在其创建的 global 生命周期结束后继续存在。
-
request . isReloadNavigation
- 返回一个布尔值,指示 request 是否用于重新加载导航。
-
request . isHistoryNavigation
- 返回一个布尔值,指示 request 是否用于历史导航(即前进/后退导航)。
-
request . signal
- 返回与 request 关联的 signal,它是一个
AbortSignal
对象,指示 request 是否已被中止,以及其 abort 事件处理程序。 -
request . duplex
- 返回 "
half
",这意味着 fetch 将是半双工的(即用户代理在处理响应之前发送整个请求)。将来,它也可以返回 "full
",这意味着 fetch 将是全双工的(即用户代理可以在发送整个请求之前处理响应),以指示 fetch 将是全双工的。参见 issue #1254 以 定义 "full
"。 -
request . clone()
-
返回 request 的一个克隆。
要 创建
一个 Request
对象,给定一个 request
request,headers
guard
guard,AbortSignal
对象 signal,以及 realm realm:
-
将 requestObject 的 request 设置为 request。
-
将 requestObject 的 headers 设置为一个 新的
Headers
对象,具有 realm,其 headers list 是 request 的 headers list 且 guard 是 guard。 -
将 requestObject 的 signal 设置为 signal。
-
返回 requestObject。
new Request(input, init)
构造函数步骤如下:
-
令 request 为 null。
-
令 fallbackMode 为 null。
-
令 baseURL 为 this 的 相关设置对象 的 API base URL。
-
令 signal 为 null。
-
如果 input 是字符串,则:
-
否则:
-
令 traversableForUserPrompts 为 "
client
"。 -
如果 request 的用户提示可遍历对象是一个环境设置对象并且其源与 origin 同源,则将 traversableForUserPrompts 设置为 request 的用户提示可遍历对象。
-
如果 init["
window
"] 存在,则将 traversableForUserPrompts 设置为 "no-traversable
"。 -
将 request 设置为一个新的 request, 其属性如下:
- URL
- request 的URL。
- 方法
- request 的方法。
- 标头列表
- request 的标头列表的副本。
- 不安全请求标志
- 已设置。
- 客户端
- This 的相关设置对象。
- 用户提示可遍历对象
- traversableForUserPrompts。
- 内部优先级
- request 的内部优先级。
- 源
- request 的源。 源的传播仅对由服务工作线程处理的导航请求有意义。在这种情况下,请求的源可能与当前客户端不同。
- 引用者
- request 的引用者。
- 引用者策略
- request 的引用者策略。
- 模式
- request 的模式。
- 凭据模式
- request 的凭据模式。
- 缓存模式
- request 的缓存模式。
- 重定向模式
- request 的重定向模式。
- 完整性元数据
- request 的完整性元数据。
- keepalive
- request 的keepalive。
- 重新加载导航标志
- request 的重新加载导航标志。
- 历史导航标志
- request 的历史导航标志。
- URL 列表
- request 的URL 列表的克隆。
- 发起者类型
- "
fetch
"。
-
如果 init 不为空,则:
-
如果 request 的模式是 "
navigate
",则将其设置为 "same-origin
"。 -
取消设置 request 的重新加载导航标志。
-
取消设置 request 的历史导航标志。
-
将 request 的源设置为 "
client
"。 -
将 request 的引用者设置为 "
client
"。 -
将 request 的引用者策略设置为空字符串。
这样做是为了确保当服务工作线程 "重定向" 请求时,例如从跨域样式表中的 图片,并进行修改时,它不再看起来来自原始源(即跨域样式表),而是来自执行请求 "重定向" 的服务工作线程。这一点很重要,因为原始源可能甚至无法生成与服务工作线程相同类型的请求。因此,如果不这样做,则可能会利用信任原始源的服务,尽管这有些牵强。
-
-
如果 init["
referrerPolicy
"] 存在,则将 request 的 referrer policy 设置为该值。 -
如果 mode 非 null,则将 request 的 mode 设置为 mode。
-
如果 init["
credentials
"] 存在,则将 request 的 credentials mode 设置为该值。 -
如果 init["
cache
"] 存在,则将 request 的 cache mode 设置为该值。 -
如果 request 的 cache mode 是 "
only-if-cached
" 并且 request 的 mode 不是 "same-origin
",则 抛出 一个TypeError
。 -
如果 init["
redirect
"] 存在,则将 request 的 redirect mode 设置为该值。 -
如果 init["
integrity
"] 存在,则将 request 的 integrity metadata 设置为该值。 -
-
如果 request 的 internal priority 非 null,则以 实现定义 的方式更新 request 的 internal priority。
-
-
令 signals 为 « signal » 如果 signal 非 null;否则为 « »。
-
将 this 的 signal 设置为 创建一个依赖的中止信号 的结果,使用
AbortSignal
和 this 的 相关 realm。 -
将 this 的 headers 设置为一个 新的
Headers
对象,使用 this 的 相关 realm,其 header list 是 request 的 header list 并且 guard 为 "request
"。 -
如果 init 不为空,则:
这些 headers 经过了消毒,因为它们可能包含此模式不允许的 headers。否则,它们之前已经经过消毒,或者自设置以来没有被修改,因为它们是由一个特权 API 设置的。
-
令 inputBody 为 input 的 request 的 body,如果 input 是一个
Request
对象;否则为 null。 -
如果 init["
body
"] 存在 并且非 null 或 inputBody 非 null,并且 request 的 method 是 `GET
` 或 `HEAD
`,则 抛出 一个TypeError
。 -
令 initBody 为 null。
-
令 inputOrInitBody 为 initBody 如果它非 null;否则为 inputBody。
-
如果 inputOrInitBody 非 null 并且 inputOrInitBody 的 source 为 null,则:
-
令 finalBody 为 inputOrInitBody。
-
如果 initBody 为 null 并且 inputBody 非 null,则:
The method
getter 的步骤是返回 this 的 request 的 method。
The url
getter 的步骤是返回 this
的 request
的
URL,并且序列化。
The headers
getter 的步骤是返回 this 的 headers。
The destination
getter 的步骤是返回 this 的 request 的 destination。
The referrer
getter 的步骤是:
The referrerPolicy
getter 的步骤是返回 this 的 request 的 referrer policy。
The mode
getter 的步骤是返回 this 的 request 的 mode。
The credentials
getter 的步骤是返回 this 的 request 的 credentials
mode。
The cache
getter 的步骤是返回 this 的 request 的 cache
mode。
The redirect
getter 的步骤是返回 this 的 request 的 redirect mode。
The integrity
getter 的步骤是返回 this 的 request 的 integrity
metadata。
The keepalive
getter 的步骤是返回 this 的 request 的 keepalive。
The isReloadNavigation
getter 的步骤是返回
true 如果 this 的
request 的
reload-navigation flag 被设置;
否则返回 false。
The isHistoryNavigation
getter 的步骤是返回
true 如果 this 的
request 的
history-navigation flag 被设置;
否则返回 false。
The signal
getter 的步骤是返回 this 的 signal。
This的signal总是在 构造函数中以及 克隆时初始化。
The duplex
getter 的步骤是返回
"half
"。
clone()
方法的步骤如下:
5.5. Response 类
[Exposed =(Window ,Worker )]interface {
Response constructor (optional BodyInit ?=
body null ,optional ResponseInit = {}); [
init NewObject ]static Response error (); [NewObject ]static Response redirect (USVString ,
url optional unsigned short = 302); [
status NewObject ]static Response json (any ,
data optional ResponseInit = {});
init readonly attribute ResponseType type ;readonly attribute USVString url ;readonly attribute boolean redirected ;readonly attribute unsigned short status ;readonly attribute boolean ok ;readonly attribute ByteString statusText ; [SameObject ]readonly attribute Headers headers ; [NewObject ]Response clone (); };Response includes Body ;dictionary {
ResponseInit unsigned short = 200;
status ByteString = "";
statusText HeadersInit ; };
headers enum {
ResponseType ,
"basic" ,
"cors" ,
"default" ,
"error" ,
"opaque" };
"opaqueredirect"
一个 Response
对象有一个关联的response(一个 response)。
一个 Response
对象也有一个关联的headers(null 或一个 Headers
对象),最初为 null。
一个 Response
对象的body是其response的body。
-
response = new Response(body = null [, init])
-
创建一个
Response
,其body为 body,状态、状态消息和 headers 由 init 提供。 -
response = Response . error()
-
创建网络错误
Response
。 -
response = Response . redirect(url, status = 302)
-
创建一个重定向
Response
,重定向到 url,状态为 status。 -
response = Response . json(data [, init])
-
创建一个
Response
,其body是JSON编码的 data,状态、状态 消息和 headers 由 init 提供。 -
response . type
-
返回 response 的类型,例如 "
cors
"。 -
response . url
-
返回 response 的 URL(如果有);否则返回空字符串。
-
response . redirected
-
返回 response 是否通过重定向获得。
-
response . status
-
返回 response 的状态。
-
response . ok
-
返回 response 的状态是否为 ok 状态。
-
response . statusText
-
返回 response 的状态消息。
-
response . headers
-
返回 response 的 headers 作为
Headers
。 -
response . clone()
-
返回 response 的克隆。
要创建一个 Response
对象,给定
response
response、headers
guard guard,以及
realm
realm,执行以下步骤:
要初始化响应,给定
Response
对象 response、
ResponseInit
init,以及 null 或 body
with type body:
-
如果 init["
status
"] 不在 200 到 599 的范围内, 则抛出RangeError
。 -
如果 init["
statusText
"] 不是空字符串,且不符合reason-phrase 产生式,则抛出TypeError
。 -
设置 response 的response的状态信息 为 init["
statusText
"]。 -
如果 init["
headers
"] 存在,则 用 init["headers
"] 填充 response 的headers。 -
如果 body 非 null,则:
new Response(body, init)
构造函数步骤如下:
静态 error()
方法的步骤是:返回用新的 网络错误、"immutable
" 和
当前
realm 作为参数 创建 Response
对象的结果。
静态
redirect(url, status)
方法步骤如下:
静态
json(data, init)
方法步骤如下:
type
获取步骤为:返回 this 的
response 的 type。
url
获取步骤为:如果 this 的 response 的 URL 为
null,则返回空字符串;
否则,返回 this 的
response 的 URL,并用 序列化,其中 exclude fragment
设置为 true。
redirected
获取步骤为:如果
this 的 response 的 URL list 的 size 大于
1,则返回 true;否则返回 false。
如需过滤因重定向产生的 response,可直接通过 API 实现,例如
fetch(url, { redirect:"error" })
。
这样,潜在不安全的 response
就不会意外泄漏。
status
获取步骤为:返回
this 的 response 的 status。
ok
获取步骤为:如果
this 的 response 的 status 是 ok status,则返回 true;否则返回 false。
statusText
获取步骤为:返回
this 的 response 的 status
message。
headers
获取步骤为:返回
this 的 headers。
clone()
方法的步骤如下:
5.6. Fetch 方法
partial interface mixin WindowOrWorkerGlobalScope { [NewObject ]Promise <Response >fetch (RequestInfo ,
input optional RequestInit = {}); };
init dictionary :
DeferredRequestInit RequestInit {DOMHighResTimeStamp ; }; [
activateAfter Exposed =Window ]interface {
FetchLaterResult readonly attribute boolean activated ; };partial interface Window { [NewObject ,SecureContext ]FetchLaterResult fetchLater (RequestInfo ,
input optional DeferredRequestInit = {}); };
init
fetch(input, init)
方法的步骤如下:
-
令 p 为 一个新的 promise。
-
令 requestObject 为用 input 和 init 作为参数调用
Request
构造函数的结果。如果抛出异常,则用该异常 拒绝 p 并返回 p。 -
令 request 为 requestObject 的 request。
-
如果 requestObject 的 signal 已被 中止,则:
-
终止
fetch()
调用,参数为 p、request、null 以及 requestObject 的 signal 的 中止原因。 -
返回 p。
-
-
令 globalObject 为 request 的 client 的 global object。
-
如果 globalObject 是
ServiceWorkerGlobalScope
对象,则将 request 的 service-workers mode 设为 "none
"。 -
令 responseObject 为 null。
-
令 locallyAborted 为 false。
这样可以在请求中止来自与 fetch 调用相同线程时,用可预测的时机拒绝 promise。
-
令 controller 为 null。
-
将 controller 设为调用 fetch,参数为 request 和 processResponse,其中 response 的处理步骤如下:
-
如果 locallyAborted 为 true,则中止这些步骤。
-
如果 response 的 aborted flag 已设置,则:
-
令 deserializedError 为 反序列化序列化中止原因, 参数为 controller 的 序列化中止原因 和 relevantRealm。
-
终止
fetch()
调用,参数为 p、request、 responseObject 和 deserializedError。 -
中止这些步骤。
-
-
将 responseObject 设为 创建一个
Response
对象,参数为 response、"immutable
" 和 relevantRealm。 -
解析 p,值为 responseObject。
-
-
返回 p。
要中止 fetch()
调用
,参数为 promise、request、responseObject 和 error:
FetchLaterResult
具有一个关联的 activated getter 步骤,
它是一个返回布尔值的算法。
activated
获取步骤为:返回
运行 this 的 activated 获取步骤 的结果。
fetchLater(input, init)
方法的步骤如下:
-
令 requestObject 为用 input 和 init 作为参数调用
Request
构造函数的结果。 -
令 request 为 requestObject 的 request。
-
令 activateAfter 为 null。
-
如果给定了 init 且 init["
activateAfter
"] 存在,则将 activateAfter 设为 init["activateAfter
"]。 -
如果 activateAfter 小于 0,则抛出
RangeError
。 -
如果 request 的 URL 的 scheme 不是 HTTP(S) scheme, 则抛出
TypeError
。 -
如果 request 的 body 不为 null,且 request 的 body length 为 null, 则抛出
TypeError
。body 为
ReadableStream
对象的请求不能被延迟。 -
如果 可用延迟 fetch 配额, 参数为 request 的 client 和 request 的 URL 的 origin,小于 request 的 总请求长度, 则抛出 "
QuotaExceededError
"DOMException
。 -
令 activated 为 false。
-
令 deferredRecord 为调用 排队延迟 fetch, 参数为 request、activateAfter 以及以下步骤:将 activated 设为 true 的结果。
-
将下列中止步骤添加到 requestObject 的 signal:将 deferredRecord 的 invoke state 设为 "
aborted
"。 -
返回一个新的
FetchLaterResult
, 其 activated 获取步骤为返回 activated。
以下调用会将一个请求排队,在文档终止时进行获取:
fetchLater( "https://report.example.com" , {
method: "POST" ,
body: JSON. stringify( myReport),
headers: { "Content-Type" : "application/json" }
})
以下调用同样会将该请求延迟 5 秒排队,且返回值允许调用者观察请求是否已被激活。注意即使用户代理限制定时器,请求也保证会被执行。
const result = fetchLater( "https://report.example.com" , {
method: "POST" ,
body: JSON. stringify( myReport),
headers: { "Content-Type" : "application/json" },
activateAfter: 5000
});
function check_if_fetched() {
return result. activated;
}
FetchLaterResult
对象可与 AbortSignal
结合使用。例如:
let accumulated_events = [];
let previous_result = null ;
const abort_signal = new AbortSignal();
function accumulate_event( event) {
if ( previous_result) {
if ( previous_result. activated) {
// 请求已激活,可重新开始。
accumulated_events = [];
} else {
// 中止本次请求,并用所有事件重新发起新请求。
signal. abort();
}
}
accumulated_events. push( event);
result = fetchLater( "https://report.example.com" , {
method: "POST" ,
body: JSON. stringify( accumulated_events),
headers: { "Content-Type" : "application/json" },
activateAfter: 5000 ,
abort_signal
});
}
以下对 fetchLater()
的调用都会抛出异常:
// 只支持 潜在可信 URL 。
fetchLater( "http://untrusted.example.com" );
// 延迟请求的长度必须可知。
fetchLater( "https://origin.example.com" , { body: someDynamicStream});
// 延迟获取仅适用于活动窗口。
const detachedWindow = iframe. contentWindow;
iframe. remove();
detachedWindow. fetchLater( "https://origin.example.com" );
参见 延迟获取配额示例,了解延迟获取配额的工作方式。
5.7. 垃圾回收
如果终止操作无法通过脚本观察到,用户代理可以终止正在进行的 fetch。
“通过脚本观察到”指的是通过 fetch()
的参数和返回值观察到。其他方式,例如通过侧信道与服务器通信,则不包括在内。
服务器能够观察到垃圾回收是有先例的,例如,WebSocket
和 XMLHttpRequest
对象。
用户代理可以终止 fetch,因为终止操作无法被观察到。
fetch( "https://www.example.com/" )
用户代理无法终止 fetch,因为终止操作可以通过 promise 被观察到。
window. promise = fetch( "https://www.example.com/" )
用户代理可以终止 fetch,因为关联的 body 无法被观察到。
window. promise = fetch( "https://www.example.com/" ). then( res => res. headers)
用户代理可以终止 fetch,因为终止操作无法被观察到。
fetch( "https://www.example.com/" ). then( res => res. body. getReader(). closed)
用户代理无法终止 fetch,因为可以通过为 promise 对象注册处理程序来观察到终止操作。
window. promise = fetch( "https://www.example.com/" )
. then( res => res. body. getReader(). closed)
用户代理无法终止 fetch,因为终止操作会通过已注册的处理程序被观察到。
fetch( "https://www.example.com/" )
. then( res => {
res. body. getReader(). closed. then(() => console. log( "stream closed!" ))
})
(以上关于不可观察性的示例假设内置属性和函数,例如 body.getReader()
,未被重写。)
6. data:
URLs
有关 data:
URL 的说明,请参阅 RFC 2397。本节替换了该 RFC 的规范处理要求,以兼容已部署的内容。[RFC2397]
data:
URL 结构体是一个结构体,包含一个
MIME 类型(一个MIME
类型)和一个主体(一个字节序列)。
The data:
URL processor 接受一个 URL dataURL,然后执行以下步骤:
-
令 input 为在 dataURL 上运行 URL serializer 并将 exclude fragment 设为 true 的结果。
-
从 input 中移除前导 "
data:
"。 -
令 position 指向 input 的起始位置。
-
令 mimeType 为在给定 position 时 收集一段代码点序列(这些代码点不等于 U+002C (,))的结果。
-
从 mimeType 中 删除首尾的 ASCII 空白。
这只会移除可能存在的 U+0020 SPACE 代码点。
-
如果 position 已超出 input 的末尾,则返回 failure。
-
将 position 前进 1。
-
令 encodedBody 为 input 的剩余部分。
-
令 body 为对 encodedBody 的 percent-decoding 的结果。
-
如果 mimeType 以 U+003B (;) 结尾,后面跟零个或多个 U+0020 SPACE,再后面是对 "
base64
" 的 ASCII 不区分大小写 匹配,则:-
令 stringBody 为 body 的 isomorphic decode 的结果。
-
将 body 设为对 stringBody 的 forgiving-base64 decode 的结果。
-
如果 body 为 failure,则返回 failure。
-
从 mimeType 删除最后 6 个 代码点。
-
从 mimeType 删除尾部的 U+0020 SPACE 代码点(如有)。
-
从 mimeType 删除最后一个 U+003B (;)。
-
-
如果 mimeType 以 "
;
" 开头,则在 mimeType 前添加 "text/plain
"。 -
令 mimeTypeRecord 为对 mimeType 的 解析 结果。
-
如果 mimeTypeRecord 为 failure,则将 mimeTypeRecord 设为
text/plain;charset=US-ASCII
。 -
返回一个新的
data:
URL struct,其 MIME type 为 mimeTypeRecord 且 body 为 body。
背景阅读
本节及其子节仅供参考。
HTTP 头层划分
在 Fetch 的上下文中,存在 API 层(HTML 的 img
,CSS 的 background-image
)、早期 Fetch 层、Service Worker
层以及网络与缓存层。`Accept
` 和 `Accept-Language
` 在早期 Fetch 层中设置(通常由用户代理设置)。大多数由用户代理控制的其他头,如
`Accept-Encoding
`、`Host
` 和 `Referer
`,在网络与缓存层中设置。开发者可以在 API 层或 Service
Worker 层(通常通过一个 Request
对象)设置头。开发者几乎无法控制禁止的请求头,但可以控制 `Accept
`,并且能够限制和省略
`Referer
` 之类的头。
原子 HTTP 重定向处理
重定向(即响应的状态或 内部响应(如有)的状态为 重定向状态)不会暴露给API。暴露重定向可能泄露通过跨站脚本攻击无法获得的信息。
例如,发起到
https://example.org/auth
的
Fetch 请求,其中包含标记为 HttpOnly
的 Cookie
,可能会导致重定向到
https://other-origin.invalid/4af955781ea1c84a3b11
。这个新的 URL 包含了一个秘密。如果我们暴露重定向,该秘密可能通过跨站脚本攻击被获取。
基本安全的 CORS 协议设置
对于通过 IP 认证或防火墙保护的数据资源(遗憾的是仍然相对常见),使用 CORS 协议是不安全的。 (这就是CORS 协议必须被发明的原因。)
然而,使用以下 头是安全的:
Access-Control-Allow-Origin: *
即使资源基于 Cookie 或 HTTP 认证暴露了额外的信息,使用上述 头也不会泄露它。它会与 XMLHttpRequest
等 API 共享资源,就像它已经与 curl
和 wget
共享资源一样。
因此,换句话说,如果资源不能通过连接到网络的随机设备使用 curl
和 wget
访问,则不应包含上述 头。然而,如果可以访问,则完全可以包含。
CORS 协议与 HTTP 缓存
如果CORS
协议要求比将 `Access-Control-Allow-Origin
` 设置为
*
或静态 源 更加复杂,则应使用 `Vary
`。[HTML] [HTTP] [HTTP-CACHING]
Vary: Origin
尤其是,考虑如果在上面描述的相同场景中没有使用 `Vary
` 会发生什么情况,并且服务器配置为仅在响应 CORS 请求时才发送 `Access-Control-Allow-Origin
`。当用户代理接收到非
CORS
请求的响应(例如,作为 导航请求的结果)时,该响应将缺少 `Access-Control-Allow-Origin
`,用户代理将缓存该响应。然后,如果用户代理随后遇到该资源的
CORS
请求,它将使用先前非 CORS
请求的缓存响应,而不会包含 `Access-Control-Allow-Origin
`。
但如果在上述场景中使用 `Vary: Origin
`,它将使用户代理获取包含 `Access-Control-Allow-Origin
`
的响应,而不是使用先前非
CORS
请求的缓存响应,该响应不包含 `Access-Control-Allow-Origin
`。
然而,如果将 `Access-Control-Allow-Origin
` 设置为
*
或特定资源的静态 源,则配置服务器以始终在该资源的响应中发送 `Access-Control-Allow-Origin
`,无论是非 CORS
请求还是 CORS 请求,并且不要使用
`Vary
`。
WebSocket
在建立连接的过程中,WebSocket
对象会发起一种特殊的
fetch(使用 request,其 mode 为
"websocket
"),这使其能够共享许多 fetch 策略决策,例如
HTTP 严格传输安全(HSTS)。最终,fetch 会调用
WebSockets 来获得专用连接。 [WEBSOCKETS]
[HSTS]
Fetch 过去直接定义了 获取 WebSocket 连接和 建立 WebSocket 连接,但现在这两个都在 WebSockets 标准中定义。 [WEBSOCKETS]
在其他标准中使用 fetch
本质上,fetch 是一个 request 与 response 的交换。但实际上,它是一个标准采用和正确使用起来相当复杂的机制。本节旨在提供一些建议。
务必请领域专家进行审查。
此内容尚在完善中。
设置请求
进行 fetch 的第一步是创建一个 request,并填充其 items。
首先设置 request 的 URL 和 method,遵循
HTTP
的定义。如果你的 `POST
` 或 `PUT
` request 需要一个请求体,可以将 request 的 body 设为一个 字节序列,或者一个新的 body,其 stream 是你创建的 ReadableStream
。[HTTP]
根据 destination table 的指引选择你的 request 的 destination。Destination 会影响
内容安全策略(CSP),并有如
`Sec-Fetch-Dest
`
头这样的其它影响,因此它远不止是元数据。如果新功能需要 destination,但不在 destination
table 中,请
提交
issue 讨论。[CSP]
将你的 request 的 client
设置为你正在操作的 环境设置对象。Web 公开 API 通常用 Web IDL 定义,所有实现了 接口的对象都有一个可用的 相关设置对象。例如,和 request 关联的 element 会把 request 的 client
设为元素的
节点文档 的 相关设置对象。所有直接被 JavaScript、HTML、CSS 或其它 Document
子资源公开的功能都应有一个 client。
如果你的 fetch 不是直接 web
公开,比如后台发送,不依赖于当前 Window
或 Worker
,把
request 的 client 设为
null,并设置 request 的 origin、policy container、service-workers
mode、referrer
等为合适的值,比如提前从 环境设置对象复制。在这些高级场景下,确保你的 fetch 细节包括 CSP 和 referrer policy 的处理,也要注意并发,因为回调(见 调用
fetch 和处理响应)会被投递到 parallel queue。[REFERRER] [CSP]
考虑你要如何处理跨域资源。有些功能只在同源下有效,则设置 request 的 mode 为 "same-origin
"。否则,新 web 公开功能几乎总是应设置 mode 为
"cors
"。如果你的功能不是 web 公开,或还有其它原因要跨域抓取但不用 CORS,请提交
issue讨论。
对于跨域请求,还需确定是否要包含凭证,如果需要,则设置 request 的 credentials mode 为 "include
"。
考虑你的 fetch 是否需要上报到 资源时序(Resource Timing),以及用哪个 initiator type。通过给 request 传递 initiator type,fetch 完成且 response 下载完成后会自动进行 Resource Timing 上报。[RESOURCE-TIMING]
如果你的请求需要额外的 HTTP 头,设置其 header list 为包含这些头的 头列表,例如 «
(`My-Header-Name
`,
`My-Header-Value
`) »。发送自定义头可能有影响,比如需要 CORS预检,请谨慎处理。
如果你要覆盖默认缓存机制,比如禁用本次 request
的缓存,将请求的 cache mode 设为非 "default
" 的值。
确定是否允许你的请求重定向。如果不允许,设置其 redirect mode 为 "error
"。
浏览 request 的其它参数,看是否还有与你相关的内容。其它参数使用频率较低,通常用于特殊目的,详情见本标准 § 2.2.5 Requests。
调用 fetch 并处理响应
除了 request 外,fetch 操作还接受多个可选参数。对于接受算法的参数:算法会从任务(或当 useParallelQueue 为 true 时在 parallel queue)中调用。
一旦 request 设置好,确定要传递给 fetch 哪些算法,要考虑你希望如何处理 response,特别是你希望在哪个阶段收到回调:
- 完成时
-
大多数调用者都是这样处理 response 的,比如 脚本和 样式资源。 response 的 body 会被完整读取为 字节序列,然后由调用者处理。
如需在完成时处理 response,将算法作为 processResponseConsumeBody 参数传递给 fetch。该算法会收到一个 response 和一个表示完整读取 body(即 response 的 internal response)的参数。第二个参数的含义如下:
- null
- response 的 body 为 null,原因是响应是 网络错误或具有 null body status。
- failure
- 尝试 完整读取 response 的 body 时失败,例如 I/O 错误。
- 一个 字节序列
-
完整读取 response 的 internal response 的 body 成功。
一个包含全部内容的 字节序列也会传递给 request,其 mode 为 "
no-cors
"。调用者在处理此类内容时要非常小心,这些内容不应该暴露给请求的 origin。例如,调用者可以用 "no-cors
" response 的内容直接给用户展示图片,但这些图片内容不能被嵌入文档的脚本直接访问。
- 先处理头,再分块处理
-
有些场景下,例如播放视频或渐进加载图片,调用者可能希望流式处理响应,一次处理一个块。当头被处理后,response 会交给 fetch 的调用者,之后调用者继续处理。
如需分块处理 response,将算法作为 processResponse 参数传递给 fetch。该算法会在响应头处理完毕后收到一个 response,并负责读取 response 的 body 的 stream 以下载剩余响应内容。你还可以为 processResponseEndOfBody 参数传递一个算法,在你完全读取响应及其 body 后调用。注意,与 processResponseConsumeBody 不同,传递 processResponse 或 processResponseEndOfBody 参数并不会保证响应会被完整读取,调用者需自行读取。
processResponse 参数也很适合只处理 response 的 头列表 和 状态,而不处理 body。例如用于处理非 ok status 的响应。
- 忽略响应
-
某些情况下,完全不需要 response,例如
navigator.sendBeacon()
。处理响应和传递回调给 fetch 是可选的,省略回调则 fetch 时不会期望响应。在这种情况下,response 的 body 的 stream 会被丢弃,调用者无需担心不必要地下载内容。Fetch 一个 request,其 URL 为
https://fire-and-forget.example.com/
,method 为 `POST
`,client 为 this 的 相关设置对象。
除了处理响应的回调外,fetch 还接受高级场景的回调。processEarlyHintsResponse 专为 response 的 状态为 103 的情况设计,目前仅导航时处理。processRequestBodyChunkLength 和 processRequestEndOfBody 用于通知调用者请求体上传进度。
需要注意的是,fetch 操作会在调用它的同一线程启动,然后会并行运行其内部操作(in parallel)。上述回调会投递到特定的 事件循环,默认是 client 的 全局对象。如需并行处理响应并自己处理与主线程的交互,则可以将 useParallelQueue 设为 true 后 fetch。
操作进行中的 fetch
如需操作已启动的 fetch 操作,使用调用 fetch 返回的 fetch controller。例如,你可以根据用户或页面逻辑 abort fetch controller,或因浏览器内部原因 terminate 它。
除了终止和中止外,调用者还可以 上报时序(如果没有通过 initiator type 自动完成),或者 提取完整时序信息
并在调用方处理(仅导航用)。fetch
controller 还用于 处理下一个手动重定向,适用于 request 的 redirect mode 设为
"manual
" 的情况。
致谢
感谢 Adam Barth, Adam Lavin, Alan Jeffrey, Alexey Proskuryakov, Andreas Kling, Andrés Gutiérrez, Andrew Sutherland, Andrew Williams, Ángel González, Anssi Kostiainen, Arkadiusz Michalski, Arne Johannessen, Artem Skoretskiy, Arthur Barstow, Arthur Sonzogni, Asanka Herath, Axel Rauschmayer, Ben Kelly, Benjamin Gruenbaum, Benjamin Hawkes-Lewis, Benjamin VanderSloot, Bert Bos, Björn Höhrmann, Boris Zbarsky, Brad Hill, Brad Porter, Bryan Smith, Caitlin Potter, Cameron McCormack, Carlo Cannas, 白丞祐 (Cheng-You Bai), Chirag S Kumar, Chris Needham, Chris Rebert, Clement Pellerin, Collin Jackson, Daniel Robertson, Daniel Veditz, Dave Tapuska, David Benjamin, David Håsäther, David Orchard, Dean Jackson, Devdatta Akhawe, Domenic Denicola, Dominic Farolino, Dominique Hazaël-Massieux, Doug Turner, Douglas Creager, Eero Häkkinen, Ehsan Akhgari, Emily Stark, Eric Lawrence, Eric Orth, Feng Yu, François Marier, Frank Ellerman, Frederick Hirsch, Frederik Braun, Gary Blackwood, Gavin Carothers, Glenn Maynard, Graham Klyne, Gregory Terzian, Guohui Deng(邓国辉), Hal Lockhart, Hallvord R. M. Steen, Harris Hancock, Henri Sivonen, Henry Story, Hiroshige Hayashizaki, Honza Bambas, Ian Hickson, Ilya Grigorik, isonmad, Jake Archibald, James Graham, Jamie Mansfield, Janusz Majnert, Jeena Lee, Jeff Carpenter, Jeff Hodges, Jeffrey Yasskin, Jensen Chappell, Jeremy Roman, Jesse M. Heines, Jianjun Chen, Jinho Bang, Jochen Eisinger, John Wilander, Jonas Sicking, Jonathan Kingston, Jonathan Watt, 최종찬 (Jongchan Choi), Jordan Stephens, Jörn Zaefferer, Joseph Pecoraro, Josh Matthews, jub0bs, Julian Krispel-Samsel, Julian Reschke, 송정기 (Jungkee Song), Jussi Kalliokoski, Jxck, Kagami Sascha Rosylight, Keith Yeung, Kenji Baheux, Lachlan Hunt, Larry Masinter, Liam Brummitt, Linus Groh, Louis Ryan, Luca Casonato, Lucas Gonze, Łukasz Anforowicz, 呂康豪 (Kang-Hao Lu), Maciej Stachowiak, Malisa, Manfred Stock, Manish Goregaokar, Marc Silbey, Marcos Caceres, Marijn Kruisselbrink, Mark Nottingham, Mark S. Miller, Martin Dürst, Martin O’Neal, Martin Thomson, Matt Andrews, Matt Falkenhagen, Matt Menke, Matt Oshry, Matt Seddon, Matt Womer, Mhano Harkness, Michael Ficarra, Michael Kohler, Michael™ Smith, Mike Pennisi, Mike West, Mohamed Zergaoui, Mohammed Zubair Ahmed, Moritz Kneilmann, Ms2ger, Nico Schlömer, Nicolás Peña Moreno, Nidhi Jaju, Nikhil Marathe, Nikki Bee, Nikunj Mehta, Noam Rosenthal, Odin Hørthe Omdal, Olli Pettay, Ondřej Žára, O. Opsec, Patrick Meenan, Perry Jiang, Philip Jägenstedt, R. Auburn, Raphael Kubo da Costa, Robert Linder, Rondinelly, Rory Hewitt, Ross A. Baker, Ryan Sleevi, Sam Atkins, Samy Kamkar, Sébastien Cevey, Sendil Kumar N, Shao-xuan Kang, Sharath Udupa, Shivakumar Jagalur Matt, Shivani Sharma, Sigbjørn Finne, Simon Pieters, Simon Sapin, Simon Wülker, Srirama Chandra Sekhar Mogali, Stephan Paul, Steven Salat, Sunava Dutta, Surya Ismail, Tab Atkins-Bittner, Takashi Toyoshima, 吉野剛史 (Takeshi Yoshino), Thomas Roessler, Thomas Steiner, Thomas Wisniewski, Tiancheng "Timothy" Gu, Tobie Langel, Tom Schuster, Tomás Aparicio, triple-underscore, 保呂毅 (Tsuyoshi Horo), Tyler Close, Ujjwal Sharma, Vignesh Shanmugam, Vladimir Dzhuvinov, Wayne Carr, Xabier Rodríguez, Yehuda Katz, Yoav Weiss, Youenn Fablet, Yoichi Osato, 平野裕 (Yutaka Hirano), and Zhenbin Xu 感谢你们的卓越贡献。
本标准由 Anne van Kesteren(Apple,annevk@annevk.nl)撰写。
知识产权
版权所有 © WHATWG(Apple、Google、Mozilla、Microsoft)。本作品依据 知识共享署名4.0国际许可协议 授权。若其中部分内容被纳入源代码,这些部分在源代码中将依据 BSD 3-Clause License 授权。
这是现行标准。对专利审查版本感兴趣的读者请查阅 现行标准审查草案。