豫言 / 豫言操作系统网页答复 @ 0.5.0

回应当前入站网页请求:JSON、文字、字节、空正文与改址,含逐项标头的带头答复。

源码浏览已生成;不能解析的文件以纯文本展示,不表示通过类型检查。

规范.汉语.md

# 网页答复接口规范 0.5.0

`回应入站癸象文` 接受一段有效的 UTF-8 JSON 文字,返回给当前入站请求,状态码必须是 200 至 599,且不能是禁止正文的 204、205 或 304。宿主解析并验证 JSON,再序列化为响应正文,设置 `Content-Type: application/json; charset=utf-8`、`Cache-Control: no-store` 和 `X-Content-Type-Options: nosniff`;无当前请求、状态码无效、正文超过 2 MiB 或本次请求已有最终响应时调用失败。调用不允许引用另一次请求的句柄。接口包本身只依赖标准库。

本包是在编译器仍以 `_start` 为入口期间使用的过渡宿主能力。长期入口契约由 `豫言操作系统网页服务` 的 `网页处理。应用接口。豫` 定义;后续接通带参入口后,应以该包的类型化响应替代此过渡调用。

`回应入站空答` 接受 200 至 599 的状态码,设置 `Cache-Control: no-store`,正文为空,不设置内容类型。无当前请求、状态码无效或本次请求已有最终响应时失败;204、205、304 在这里可用。

`回应入站文字` 接受 200 至 599 中允许正文的状态码、内容类型和纯文字,按 UTF-8 发出,正文不得超过 2 MiB;宿主设置 `Cache-Control: no-store` 与 `X-Content-Type-Options: nosniff`。内容类型按 HTTP 标头规则验证,非法值失败。`回应入站同源改址` 仅接受 301、302、303、307、308 与当前请求同源的绝对 `http:` 或 `https:` URL;非法网址、跨源目标或重复最终响应失败。此处四项答复函数合计每次请求只能成功调用一次。

`回应入站外址改址` 接受 301、302、303、307、308 与应用构造的绝对 HTTPS URL。宿主验证方案为 `https:`,目标由应用按自己的路由和配置选择;应用若把未验证的用户输入用作目标,会形成开放重定向。此函数与本包其余答复函数共用每次请求唯一的最终响应。

## 带标头的答复(0.5.0 新增)

`回应入站带头文字`、`回应入站带头癸象文`、`回应入站带头空答`、`回应入站带头字节` 与 `回应入站带头改址` 以调用方给出的**有序标头列表**回应当前入站请求。标头列表的类型是 `(列于(字符串合字符串))`,每项是(名称,值),保持顺序,允许同名多项。它们与旧函数的区别有三:

1. **不隐式添加任何标头。** 旧的 `回应入站癸象文`、`回应入站文字` 与 `回应入站空答` 会自动加 `Cache-Control: no-store`、`X-Content-Type-Options: nosniff`;带头系列一概不加,应用需要就显式传入(例如 `Cache-Control`)。内容类型只经参数(JSON 版固定为 `application/json; charset=utf-8`)设置。平台发送时按正文长度自行处理的标头不在此列。
2. 同名标头按列表原序逐个追加,不合并、不覆盖;`Set-Cookie` 各项保持独立。
3. 严格校验,违者抛出可捕获的豫言异常(违规)。此时不产生任何响应,应用仍可另行回应错误。

各函数:

- `回应入站带头文字 状态 内容类型 标头列 正文`:正文是文字,按 UTF-8 发出,不超过 8 MiB(按字节计,超限为违规,消息“文字响应超过 8 MiB”)。
- `回应入站带头癸象文 状态 标头列 JSON文`:先用 `癸象解析` 与 `癸象表示` 规范化(无效 JSON 为违规,消息以“JSON 响应正文无效:”起首);内容类型固定;规范化后的文字不超过 2 MiB(“JSON 响应超过 2 MiB”)。标头列表中不得再含 Content-Type。
- `回应入站带头空答 状态 标头列`:无正文,不设内容类型;204、205、304 可用。
- `回应入站带头字节 状态 内容类型 标头列 字节串`:正文是任意字节(含零字节、无 UTF-8 要求),不超过 16 MiB(“字节响应超过 16 MiB”)。
- `回应入站带头改址 状态 目标 标头列`:状态只能是 301、302、303、307 或 308。目标(`Location`)只能是下列两种之一:以单个 `/` 起首的路径(第二个字节不得是 `/` 或 `\`);或以 `http://`、`https://` 起首(方案不分大小写)、可被 URL 标准解析、协议为 http 或 https、无用户名口令且主机非空的绝对网址(`https:///x` 这类无主机形式拒绝)。全文只含 0x21 至 0x7E 的 ASCII 且不含反斜线,长度 1 至 8192 字节,因此不得含空白、CR、LF、控制字符或非 ASCII(应用须自行百分号编码)。标头列表中不得再出现 `Location`。无正文。不合规目标的消息是“重定向目标无效”,状态不合规的消息是“重定向状态不受支持”。

共同规则(违者一律是可捕获的违规,消息为中文):

| 项 | 规则 |
| --- | --- |
| 状态 | 200 至 599(“响应状态不在 200 至 599”)。204、205、304 不得带非空正文(“该 HTTP 状态不得带非空正文”) |
| 标头名 | RFC 9110 令牌字符(数字、字母与 ``!#$%&'*+-.^_`|~``),1 至 64 字节 |
| 标头值 | 只允许可见 ASCII(0x20 至 0x7E)、空格与水平制表;不含 CR、LF、NUL、其他控制字符与非 ASCII;至多 8192 字节;`Set-Cookie` 每项至多 4096 字节 |
| 数量 | 至多 64 项,多个 `Set-Cookie` 各计一项 |
| 禁止应用设置(不分大小写) | `Content-Length`、`Transfer-Encoding`、`Connection`、`Keep-Alive`、`Upgrade`、`TE`、`Trailer`、`Content-Type`、`Content-Encoding`;前七个由宿主管理,后两个由参数决定,列表中出现即违规(“响应标头由宿主或参数管理,不得设置”) |
| 内容类型(参数) | 形如 `type/subtype`,其后只能有空白和以分号起首的参数;全文限可见 ASCII 或水平制表,1 至 256 字节(“内容类型无效”) |
| 同名标头 | 保序逐个追加;`Set-Cookie` 允许多项 |

**HEAD 请求**:宿主对 HEAD 响应不发送正文,适配不做特殊处理:响应仍按上述规则构造,状态与标头照旧,应用不必为 HEAD 另写分支。

所有答复函数(含旧函数)合计每次请求只能成功回应一次,之后再调用失败。

## 失败类别

违规(可捕获的豫言异常):状态、标头名称与值、标头数量、禁用标头、内容类型、正文过大、状态与正文不兼容、JSON 无效、重定向目标或状态不合规。平台故障:没有当前 HTTP 请求、本次请求已有最终响应。这些都发生在响应产生之前,应用可以据此改答错误。

## 一致性样例

| 情境 | 必须观察到 |
| --- | --- |
| 带头文字 201,头 `X-A: 1`、`x-a: 2`、两个 `Set-Cookie` | 状态 201;`x-a` 合并为 `1, 2`;两个 Set-Cookie 保持独立;只有 Content-Type 与所给标头 |
| 带头空答 200,列表为空 | 没有任何标头,正文为空 |
| 标头值 `a\r\nInjected: 1` | 违规,不产生响应 |
| 标头名 `Content-Length`、`content-type`、`TE` | 违规 |
| 带头文字 204 且正文非空 | 违规;正文为空则成功 |
| 带头字节 256 个全部字节值 | 响应正文逐字节相同 |
| 带头改址 302 到 `/next?a=1` | 只有 `Location` 与所给标头,正文为空 |
| 带头改址目标 `//evil.example`、`http://u:p@h/`、`javascript:x` | 违规 |
| 65 个标头,或 4097 字节的 Set-Cookie | 违规 |

## 不做什么

带头系列不设置缓存或安全类默认标头,不做内容协商,不压缩,不支持 1xx 状态与 Trailer,不支持流式正文(正文一次给出)。需要这些时由应用显式设置标头或另立接口。

## 补充:JSON 答复的宿主校验与旧函数的越界防护(独立审查后修订)

- `回应入站癸象文`、`回应入站带头癸象文` 先把正文文字交宿主 `JSON.parse` 校验:无效 JSON(含未闭合字符串、坏转义)抛可捕获的豫言异常,消息以「JSON 响应正文无效:」起首;随后写出宿主 `JSON.stringify` 的规范文字(去冗白,数字按 ECMAScript 书写:`1e21` 得 `1e+21`,超过 2^53 的整数取双精度近似),不再经癸象解析往返,因此畸形字符串不会陷阱、大整数不会回绕成负数、合法的 `1e21` 不会被拒、上万项的大数组不会栈溢出。
- `回应入站同源改址`、`回应入站外址改址` 的目标须是宿主 `URL.canParse` 可解析的绝对网址,否则抛可捕获的「重定向目标不是有效的绝对网址」;`回应入站文字` 的内容类型含 CR 或 LF 时抛可捕获的「内容类型含换行」。宿主的 TypeError 不再穿出 Wasm。

规范.文言.md

# 网页答复公约 0.5.0

`回应入站癸象文` 受有效 UTF-8 JSON 之文,答当前入站之请;状态须在二百至五百九十九,且非二零四、二零五、三零四等禁正文之号。宿主先析验 JSON,复序为答,置 JSON 内容种类、`Cache-Control: no-store` 与 `X-Content-Type-Options: nosniff`。无当前请、状态非法、正文逾二 MiB 或此请已有终答,则调用败;不得执异请之柄。公约包惟依标准库。

此包为编器犹以 `_start` 启客时之过渡能力。久约在 `豫言操作系统网页服务` 之 `网页处理。应用接口。豫`;带参入口既通,当以其类型化之答代此术。

`回应入站空答` 受二百至五百九十九之号,以空体终当前请,置 `Cache-Control: no-store`,不置内容种类。无当前请、号非法或已有终答则败;二零四、二零五、三零四于此可用。

`回应入站文字` 受可载正文之二百至五百九十九状态、内容种类及纯文,以 UTF-8 发之,不逾二 MiB;宿主置无缓存与 `nosniff`。内容种类循 HTTP 头规,不合则败。`回应入站同源改址` 惟受三零一、三零二、三零三、三零七、三零八五号与当前请同源之绝对 `http:` 或 `https:` 址;坏址、异源或再设终答皆败。四答术合计每请惟得成功一次。

`回应入站外址改址` 受三零一、三零二、三零三、三零七、三零八及应用所定绝对 HTTPS 址。宿主验其方案,应用自守目标之来历;若径由客任意供给,或成开放改址。此术与诸答术共占每请惟一终答。

## 带头之答(0.5.0 新增)

`回应入站带头文字`、`回应入站带头癸象文`、`回应入站带头空答`、`回应入站带头字节` 与 `回应入站带头改址` 以客所授之**有序头列**答当前入站之请。头列之型为 `(列于(字符串合字符串))`,每项为(名,值),守其序,许同名多项。较旧术有三异:

一、**不暗增一头。** 旧之 `回应入站癸象文`、`回应入站文字`、`回应入站空答` 自增 `Cache-Control: no-store` 与 `X-Content-Type-Options: nosniff`;带头诸术皆不增,客欲之则明授(如 `Cache-Control`)。内容种类惟由参数(JSON 之术固为 `application/json; charset=utf-8`)而置。平台发送时依正文之长自理之头,不在此列。

二、同名之头依列序逐一追加,不并不覆;`Set-Cookie` 各项自独。

三、严验,违者报可捕之事故(违约)。是时不生一答,客仍可别答以误。

诸术:

- `回应入站带头文字 状 类 头列 正文`:正文为文,以 UTF-8 发之,不逾八 MiB(按字节计,逾则违约,辞曰「文字响应超过 8 MiB」)。
- `回应入站带头癸象文 状 头列 JSON文`:先以 `癸象解析` 与 `癸象表示` 正其文(JSON 无效为违约,辞以「JSON 响应正文无效:」起);内容种类固定;正其文后不逾二 MiB(「JSON 响应超过 2 MiB」)。头列不得复含 Content-Type。
- `回应入站带头空答 状 头列`:无正文,不置内容种类;二零四、二零五、三零四可用。
- `回应入站带头字节 状 类 头列 字节串`:正文为任意字节(含零字节,不须为 UTF-8),不逾十六 MiB(「字节响应超过 16 MiB」)。
- `回应入站带头改址 状 目标 头列`:状惟三零一、三零二、三零三、三零七、三零八。目标(`Location`)惟二形之一:以单一 `/` 起首之径(次字节不得为 `/` 或 `\`);或以 `http://`、`https://` 起首(方案不辨大小写)、可为 URL 之规所析、协议为 http 或 https、无用户名口令而主机非空之绝对址(`https:///x` 之类无主机者拒之)。全文惟含 0x21 至 0x7E 之 ASCII 而无反斜线,长一至八一九二字节,故不得含空白、CR、LF、控字符及非 ASCII(客须自行百分号编码)。头列不得复出 `Location`。无正文。目标不合,辞曰「重定向目标无效」;状不合,辞曰「重定向状态不受支持」。

诸术共守之规(违者皆为可捕之违约,辞为中文):

| 项 | 规 |
| --- | --- |
| 状 | 二百至五百九十九(「响应状态不在 200 至 599」)。二零四、二零五、三零四不得带非空正文(「该 HTTP 状态不得带非空正文」) |
| 头名 | RFC 9110 令牌字(数、字母及 ``!#$%&'*+-.^_`|~``),一至六十四字节 |
| 头值 | 惟许可见 ASCII(0x20 至 0x7E)、空格与水平制表;不含 CR、LF、NUL、他控字符及非 ASCII;至多八一九二字节;`Set-Cookie` 每项至多四零九六字节 |
| 数 | 至多六十四项,多个 `Set-Cookie` 各计一项 |
| 客不得设者(不辨大小写) | `Content-Length`、`Transfer-Encoding`、`Connection`、`Keep-Alive`、`Upgrade`、`TE`、`Trailer`、`Content-Type`、`Content-Encoding`;前七者宿主所管,后二者由参数所定,列中一出即违约(「响应标头由宿主或参数管理,不得设置」) |
| 内容种类(参数) | 式如 `type/subtype`,其后惟许空白及以分号起首之参;全文限可见 ASCII 或水平制表,一至二五六字节(「内容类型无效」) |
| 同名之头 | 依序逐一追加;`Set-Cookie` 许多项 |

**HEAD 之请**:宿主于 HEAD 之答不发正文,适配不特为之处:答仍依上规而造,状与头如常,客不必为 HEAD 别设一支。

诸答复之术(含旧术)合计,每请惟得成功一答,此后再调则败。

## 败之类

违约(可捕之事故):状、头名与值、头数、禁用之头、内容种类、正文过大、状与正文不容、JSON 无效、改址之目标或状不合。平台之故:无当前 HTTP 请、此请已有终答。此皆在造答之前,客可据之改答以误。

## 一致之例

| 情 | 必见 |
| --- | --- |
| 带头文字二零一,头 `X-A: 1`、`x-a: 2` 与二 `Set-Cookie` | 状二零一;`x-a` 并为 `1, 2`;二 Set-Cookie 各自独立;惟有 Content-Type 与所授之头 |
| 带头空答二零零,头列为空 | 无一头,正文为空 |
| 头值 `a\r\nInjected: 1` | 违约,不生答 |
| 头名 `Content-Length`、`content-type`、`TE` | 违约 |
| 带头文字二零四而正文非空 | 违约;正文空则成 |
| 带头字节之二百五十六个全部字节值 | 答之正文逐字节同 |
| 带头改址三零二至 `/next?a=1` | 惟有 `Location` 与所授之头,正文空 |
| 带头改址目标 `//evil.example`、`http://u:p@h/`、`javascript:x` | 违约 |
| 六十五头,或四零九七字节之 Set-Cookie | 违约 |

## 所不为

带头诸术不置缓存或安全类之默认头,不作内容协商,不压缩,不支持 1xx 之状与 Trailer,不支持流式正文(正文一次而授)。欲此诸能者,客明置其头或另立公约。

## 补:JSON 答之宿主验与旧术之防越(独立审后修订)

- `回应入站癸象文`、`回应入站带头癸象文` 先以正文之文授宿主 `JSON.parse` 验之:无效之 JSON(含未闭之串、坏转义)抛可捕之豫言异,辞以「JSON 响应正文无效:」为首;继而书宿主 `JSON.stringify` 之规范文(去冗白,数依 ECMAScript:`1e21` 得 `1e+21`,逾 2^53 之整数取双精度之近似),不复经癸象解析往返,故畸串不陷、大整不回绕为负、合法之 `1e21` 不见拒、万项之大数组不溢栈。
- `回应入站同源改址`、`回应入站外址改址` 之目标须为宿主 `URL.canParse` 可析之绝对网址,否则抛可捕之「重定向目标不是有效的绝对网址」;`回应入站文字` 之内容类型含 CR 或 LF 者抛可捕之「内容类型含换行」。宿主之 TypeError 不复穿出 Wasm。