跳转至

web.static

web 静态文件适配:收编 http.frm.static(ETag/If-None-Match 304/ Range 206·416/穿越拒 全决策面零复制)+ Router .static(前缀, 目录) 装配挂点 (Plan 2b Task 2;设计 docs/superpowers/specs/2026-09-27-web-framework-design.md §4.1;纪律 §14-1)

收编口径(不复制):决策全在 frm——sd handler 把 req.path 剥前缀成 target 后 整体喂 st_serve(root, target, inm, range)(frm 唯一 IO 薄胶:fs_exists/read_file 内建,穿越拒/缺档 404/ETag/304/Range 全由它裁),回来只做「原始 HTTP 报文 → Resp」 翻译(sd_parse:frm 产报文形态确定,状态码恒 9..12 三字节、头区 K: V、\r\n\r\n 分体)。 web 侧补挂一条安全默认 X-Content-Type-Options: nosniff(frm 不发;web §4 纪律)。 边界:目录本尊请求(suffix 空 → target "/")落在 frm 穿越拒 → 403 fail-closed, 非 404——frm 从严即默认,如实继承不另裁。

前缀剥离:static_dir(dir) 产 handler 剥首段(单段前缀/直挂形态); .static(prefix, dir) 按 prefix 实际段数剥(多段前缀 "/a/b" 逐段剥),经 prefix + "/*sfx" splat 路由装配——handler 读不到 splat(面在 Match,router 在册), 故从 req.path 侧剥。根挂载 prefix=""(0 段)即全树映射 dir 下。

use 面:web.core(Req/Resp/取头)+ web.router(Router/get/装配)+ std.str + http.frm.static 单叶(决策面);fs 内建直呼(fx_fs 夹具口径)。 实测登记(探针 t_probe/t_triv,2026-09-28):byte_slice(start > end) 引导/native 两口径皆死循环挂死——本件一切切片算式先证 start < end 才切(sd_parse 各臂守卫)。

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
static_dir[S](dir: Str) fn(&Req[S]) -> Resp §4.1 冻结名面:static_dir(dir) → handler(剥首段;直挂/单段前缀形态)
static[S](r: Router[S], prefix: Str, dir: Str) Router[S] Router 装配挂点(§4.1 链式形态 r.static(前缀, 目录);挂 prefix + "/*sfx" splat
路由,按前缀段数剥——多段前缀 "/a/b" 亦正)。与 .get 同款值语义返回新 Router;
前缀与既有路由撞表 → r_add 装配期 panic(失败在启动,不在半夜)。
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 —
st_rc(r: StRange) I64 —
st_s(r: StRange) I64 —
st_e(r: StRange) I64 —
st_range_parse(range: Str, len: I64) StRange —
st_slice(s: Str, start: I64, end: I64) Str —
st_etag(content: Str) Str —
st_inm_match(inm: Str, etag: Str) Bool —
st_traversal_ok(target: Str) Bool —
st_ctype(name: Str) Str —
st_find(hay: Str, needle: Str) I32 —
st_resp_from_content(name: Str, content: Str, inm: Str, range: Str) Str —
st_serve(root: Str, target: Str, inm: Str, range: Str) Str —