跳转至

web.compress

web 压缩面:gzip 响应压缩中间件(Plan 2c Task 1;http/enc 薄收编,调用不复制) 设计: docs/superpowers/specs/2026-09-27-web-framework-design.md §4.5/§8.3;纪律 §14-1

use 面:web.core(Resp/Req/取头)+ web.router(测试装配链)+ std.str(前缀/join) + http.enc(gzip_compress/gzip_decompress/content_coding_negotiate/enc_level_default —— web 包内唯一 http.enc 消费叶,无菱形;auth 单叶口径同款)。

判定面四门(绑定口径,次序即实现序): ① 响应已带 Content-Encoding → 透传;② !wants_gzip(req Accept-Encoding) → 透传; ③ body.len < threshold → 透传;④ Content-Type 非文本族前缀 → 透传。 压缩面:替 body + 追 Content-Encoding: gzip + Content-Length 原位换值(保序) + 追 Vary: Accept-Encoding;压缩后 ≥ 原大 → 回退原文(Vary 仍追,RFC 9110 §12.5.3 协商过即缓存面有变——enc.ct 头注口径)。

wants_gzip 收编调整(登记):绑定简判「contains gzip 且非 gzip;q=0」升级为 content_coding_negotiate(accept_enc, "gzip") == "gzip" —— 大小写/x-gzip 弃用 同义/q 值/通配 * 一次收编(http/enc 纯面,零新增依赖),contains 简判的 漏判面("*;q=0.5 应收"、"GZIP" 大小写)自然闭合。

—— 两条结构性边界(实证登记,task-1-report 在册;非临时态)——

① interp 臂不可触压缩原语(enc_fixtures x_ 同款,tests/http/run.sh enc 块 登记):ctron_deflate 垫片 extern 为 &I64[] lane 帧,解释口径 extern 桥仅 标量帧("i:/s:")——实证:interp 调 gzip_compress 即 「extern 解释口径:不支持参数类型 A」rc=1。故本文件 interp 臂测试只覆 判定面(协商矩阵/四门/头 surgery 假体)+ 透传装配;压缩面往返 e2e 属 emit 臂(夹具形态 = tests/http/enc_fixtures/x_web_compress.ct,待收口 任务随 pathspec 放行落盘;本波已手动 emit 臂验证,证据在 task-1-report)。 为此压缩面拆出纯判定三件:gzip_eligible(四门)/ apply_gzip(头 surgery, 假体可测)/ compress_mw(装配面)——interp 臂测试面最大化。

② L4 载体边界:Str 语义全 strlen(C 串)——实证 utf8_enc(0).len == 0、 "AB"+NUL+"CD" == "ABCD"(NUL 即截/即吞;03m_binary_nul_probe 同根)。 gzip 成员恒含 NUL(FLG=0/MTIME=0/ISIZE 高位零,enc.ct 组框定值)→ 压缩字节流【不可能】以原形过 Str 载体。故 gzip_bytes 以 U+0100..U+01FF 映射为载体(每字节 → utf8_enc(b+256) 两字节 UTF-8;NUL-free,Str 全 语义保真,gunzip_bytes 逆映射精确往返)。wire 面(真 gzip 原形出线) 待 render 咽喉加载体解码一处(render = 全仓唯一报文组装点,serve.ct §4.4 支点)或 Resp 增 lane 槽(§13-③ Bytes 面)——挂 L4 立项,登记。 本波 compress_mw 落载体形 body:挂链方(v1 无人挂)须俟上述咽喉解码, 不得裸上线(头注即闸)。 同根边界:带内 NUL 文本体入 gzip_bytes 会在 fill_lane 处按 strlen 截短 (静默丢尾,非 panic)——文本族门是唯一防线,二进制混入面 v1 不设防。

定长域(v1;emit 臂栈道,x_ 夹具同款):输入 ≤65536(超域 gzip_bytes 回 "" → 中间件透传);输出道 66000(存储块最坏 = n + 块头 5 + 容器 18); gunzip 出道上限 65536(ISIZE 超域回 "")。

pub struct

struct Pair

Field Type
k Str
v Str

struct Resp

Field Type
status I32
headers List[Pair]
body Str

struct JObj

Field Type
parts List[Str]

struct Req[S]

Field Type
method Str
path Str
query Str
body Str
headers List[Pair]
cookies List[Pair]
params List[Pair]
session List[Pair]
state S

struct RouteSeg

Field Type
kind Str
text Str

struct Route[S]

Field Type
m Str
segs List[RouteSeg]
chain List[fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp]
h fn(&Req[S]) -> Resp

struct Router[S]

Field Type
state S
routes List[Route[S]]
mws List[fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp]
not_found_h List[fn(&Req[S]) -> Resp]
not_allowed_h List[fn(&Req[S]) -> Resp]

struct Match[S]

Field Type
kind I32
route Route[S]
params List[Pair]
splat Str
allow Str

pub fn

Signature Returns Description
wants_gzip(accept_enc: Str) Bool —
compress_mw[S](threshold: I32) fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp —
gzip_bytes(s: Str, level: I64) Str s(无 NUL 合法 UTF-8 文本,≤65536 字节)→ gzip 成员(载体形;失败/超域回 "")
gunzip_bytes(s: Str) Str 载体形 gzip 成员 → 原文(形坏/超域回 "";ISIZE 定出道上限,护栏语义同 enc)
html(body: Str) Resp —
json(body: Str) Resp —
text(body: Str) Resp —
redirect(loc: Str) Resp —
status(n: I32, body: Str) Resp —
xml(body: Str) Resp —
yaml(body: Str) Resp —
err_json(n: I32, msg: Str) Resp —
attachment(r: Resp, filename: Str) Resp —
with(r: Resp, k: Str, v: Str) Resp —
with_status(r: Resp, n: I32) Resp —
resp_header(r: Resp, k: Str) Str —
resp_body(r: Resp) Str —
resp_status(r: Resp) I32 —
json_obj() JObj —
str(b: JObj, k: Str, v: Str) JObj —
i64(b: JObj, k: Str, v: I64) JObj —
bool(b: JObj, k: Str, v: Bool) JObj —
strs(b: JObj, k: Str, xs: List[Str]) JObj —
end(b: JObj) Str —
end_status(b: JObj, n: I32) Resp —
csv_rows(headers: List[Str], rows: List[List[Str]]) Str —
req_of[S](method: Str, path: Str, state: S) Req[S] —
req_full[S](method: Str, path: Str, body: Str, hdrs: List[Pair], state: S) Req[S] —
req_set_params[S](r: &Req[S], params: List[Pair]) Req[S] —
req_set_session[S](r: &Req[S], sess: List[Pair]) Req[S] —
pdec(s: Str) Str —
param[S](r: &Req[S], name: Str) Str —
pair_get(ps: List[Pair], k: Str) Str —
param_i64[S](r: &Req[S], name: Str) Option[I64] —
param_f64[S](r: &Req[S], name: Str) Option[F64] —
query[S](r: &Req[S], name: Str) Str —
query_all[S](r: &Req[S], name: Str) List[Str] —
form[S](r: &Req[S], name: Str) Str —
form_all[S](r: &Req[S], name: Str) List[Str] —
header[S](r: &Req[S], name: Str) Str —
cookie[S](r: &Req[S], name: Str) Str —
session[S](r: &Req[S], name: Str) Str —
router[S](state: S) Router[S] —
get[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
post[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
put[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) Router[S] —
middleware[S](r: Router[S], mw: fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp) Router[S] —
mount_at[S](r: Router[S], prefix: Str, sub: Router[S]) Router[S] —
has_conflict[S](r: Router[S]) Bool —
install[S](r: Router[S], p: fn(Router[S]) -> Router[S]) Router[S] —
not_found[S](r: Router[S], h: fn(&Req[S]) -> Resp) Router[S] —
method_not_allowed[S](r: Router[S], h: fn(&Req[S]) -> Resp) Router[S] —
test_call[S](r: Router[S], method: Str, path: Str, body: Str) Resp —
route_match[S](r: Router[S], m: Str, path: Str) Match[S] —
dispatch_h[S](r: Router[S], method: Str, path: Str, body: Str, hdrs: List[Pair]) Resp —
dispatch[S](r: Router[S], method: Str, path: Str, body: Str) Resp —
enc_level_default() I64 —
gzip_compress(in: &[I64], in_n: I64, out: &[I64], cap: I64, level: I64) I64 —
gzip_decompress(in: &[I64], in_n: I64, out: &[I64], cap: I64) I64 —
content_coding_negotiate(accept_enc: Str, have: Str) Str —