net 非正式提议
本页不属于任何正式 Host API 版本,名字、边界和签名均未冻结。现有浏览器与 shell 无法证明 DNS 检查结果和实际 HTTPS 连接地址一致;在地址绑定的平台代理完成端到端验证前,本提议不得进入 manifest、SDK 或运行时能力表。
net 让 guest 通过平台代理发起受 manifest 限制的请求—响应。guest 不获得浏览器 fetch、socket、DNS、真实连接、代理凭据或任意重定向权。
基本示例
const response = await host.net.fetch({
target: 'catalog-api',
path: '/v1/catalog',
method: 'GET',
headers: { accept: 'application/json' },
});
if (response.status !== 200) throw new Error(`HTTP ${response.status}`);
const catalog = JSON.parse(new TextDecoder().decode(response.body));
和浏览器 Fetch 一样,HTTP 404/500 是正常响应状态,不自动变成 HostError;代理失败、策略拒绝和预算失败才抛 HostError。
适用场景
- 调用应用在 manifest 中公开声明的 HTTPS API。
- 获取有明确大小上限的 JSON 或二进制响应。
- 由平台代理执行目的地校验、地址过滤和审计。
WebSocket、WebTransport、WebRTC、P2P、局域网发现、任意 TCP/UDP 和无限流不在本提议内。
L1 边界
宿主独占的是外部网络、目的地中介、SSRF 防护与审计。guest 无法只靠计算访问远端;重试、缓存、同步协议、REST 客户端和业务数据模型都能基于有界请求在 guest 中实现,不进入 L1。
manifest 与能力声明
{
"capabilities": ["host:net.fetch"],
"network": {
"targets": {
"catalog-api": {
"origin": "https://api.example.com",
"pathPrefix": "/v1/",
"methods": ["GET", "POST"]
}
}
}
}
target 是 manifest 键,不是 host 字符串。target 键使用 [a-z][a-z0-9-]{0,62}。origin 必须是没有 userinfo、path、query 或 fragment 的 HTTPS origin,host 使用小写 ASCII DNS 名而非 IP 字面量,候选契约只允许默认 443 端口。pathPrefix 必须以 / 开头和结尾。发布器解析并规范化配置;运行时按 target 查表,不做字符串 host 匹配。
Reference
net.fetch(request, options?) -> NetResponse
NetRequest {
target: manifest-network-target
path: string
method: "GET" | "HEAD" | "POST" | "PUT" | "PATCH" | "DELETE"
headers?: HeaderMap
body?: Bytes
timeoutMs?: u32
}
NetResponse {
status: u16
headers: HeaderMap
body: Bytes
}
path 必须以 / 开头,可含 query,不得含 scheme、authority、fragment、反斜杠、控制字符、非法 percent encoding,或 percent-decode 后为 ./.. 的 path segment。宿主以 UTF-8 percent-decode 每个 segment 一次,拒绝结果中的 /、\、U+0000 和第二层 percent encoding,再以规范 percent-encoding 重建 URL。规范化后的 path component 必须以声明的 pathPrefix 为 segment 前缀;query 不参与前缀判断。
HeaderMap 是小写 ASCII 名到单个可见 ASCII 值(U+0020–U+007E)的映射;值去除首尾空格后不得为空,不允许重复名、折行或控制字符。候选契约请求头白名单仅为 accept、content-type、authorization、if-match、if-none-match;响应头白名单仅为 content-type、content-length、etag、last-modified、cache-control。其他头不进入 guest。
GET 和 HEAD 禁止 body 与 content-type;其他方法的 body 可省略。timeoutMs 必须位于 [1, net.maxTimeoutMs],省略时等于 net.maxTimeoutMs。只向 guest 返回最终状态码 [200, 599];1xx 不构成响应,101 协议升级不受支持。HEAD 以及 204、205、304 响应的 body 固定为空。若上游使用内容编码,代理必须在进入 guest 前完成解码、移除 content-encoding,并让 content-length 与返回 Bytes 一致。
POST 与取消示例
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5_000);
try {
const response = await host.net.fetch({
target: 'catalog-api',
path: '/v1/items',
method: 'POST',
headers: { 'content-type': 'application/json' },
body: new TextEncoder().encode(JSON.stringify({ name: 'demo' })),
timeoutMs: 5_000,
}, { signal: controller.signal });
handleResponse(response);
} finally {
clearTimeout(timeout);
}
取消表示 guest 不再需要结果;宿主应尽快停止上游请求并以 cancelled 结束本地调用,但不能承诺远端服务器没有处理已发送的数据。
原生 WASM 示例
NetRequest request = {
.target = "catalog-api",
.path = "/v1/catalog",
.method = NET_GET,
.headers = accept_json_headers(),
};
NetResponse response = host_net_fetch(request, byte_sink(body, sizeof(body)), cancel_token);
if (response.status == 200) decode_catalog(body, response.written);
响应正文整体不得超过 net.maxResponseBytes。超过时整次调用以 response-too-large 失败;原生绑定先从应用声明的最低预算分配接收区,绝不能截断正文却保留 200 状态。
重定向与身份
- 候选契约从不跟随重定向。3xx 作为普通 HTTP 状态返回,
Location不在响应头白名单内。 - 候选契约不附加 ambient Cookie、平台登录凭据或其他 target secret。guest 可以显式提供自己持有的
authorization值;宿主只把它送给本次已校验 target,且日志、错误与遥测不得记录该值。需要由宿主代持服务凭据的能力必须在后续版本单独评审。 Set-Cookie被丢弃,不建立 guest 可观察或不可观察的 cookie jar。- URL 用户名/密码、IP 字面量、非 443 端口和解析到非公共地址的 target 在发布时或调用前拒绝;宿主在建连地址变化时重新执行公共地址检查。
安全与预算
- 信任档位:lime;peer 为 manifest 声明的 third-party,mediation 为 brokered。
- 候选契约预算键为
net.maxUrlBytes、net.maxHeaderBytes、net.maxRequestBytes、net.maxResponseBytes、net.maxConcurrentRequests、net.requestsPerMinute、net.maxTimeoutMs。 - 响应头只返回白名单;移除网络拓扑、代理实现和身份凭据。
- 日志只记录受限目的地和统计信息,不记录正文或秘密请求头。
- 宿主必须防止 SSRF,包括解析后的地址类别检查和重定向再校验。
错误与测试
特有错误包括 destination-denied、invalid-request、timeout、cancelled、response-too-large 和 upstream-unavailable。一致性测试至少覆盖未知 target、单/双 percent encoding、编码斜杠、dot segment 与 path-prefix 绕过、Unicode 域名、重定向不跟随、DNS 重绑定、头过滤、无 ambient 凭据、404 正常返回、取消竞态和响应上限。