web.serve
HTTP 序列化咽喉:reason 表 + render(全仓唯一状态行/头区拼接点) + serve 循环(§6;2b Task 3 填充) 设计: docs/superpowers/specs/2026-09-27-web-framework-design.md §4.4/§6;纪律 §14-1
§4.4 演进契约支点:报文组装只有 render 一处——状态行、头区、Content-Length、 Connection: close 全在此拼;其余代码只造 Resp(值语义),永不手拼报文串。 render 无需转义(headers/body 均为上游已构建的成品段;json_esc/ht_esc 各守其界)。 Connection: close 为 2a 同款串行口径;keep-alive 挂 L6/L7 后的并发波(spec §13-④)。
serve 循环(§6;探针定界见 task-3-report):
Step 0 探针(2026-09-28)——引导 interp 双臂(bootstrap ctc.sh 裸跑 + ctron-cc
run)对 use net 合并态一律 W8052 ×3(Box[Box64] extern 形参非 C-ABI)→ rc=1:
interp-net 死,net 内建不可 interp 执行,真 socket e2e 挂 L6/L7 原生翻账(在册)。
因此 socket 边界做成值驱动 SrvIo(fn 值五件 listen/accept/read/write/close):
循环本体 serve_with 全量 interp 可测(fake 驱动脚本化 feed,文件尾测试即证);
真 net 接线 = 调用点十余行(net.* 五件映射进 SrvIo,例见 task-3-report)——
不入本件:use net 会把 net/bind 的 W8052 拉进 web 合并态,check/run 门全红
(interp 净土纪律;驱动注入 = 依赖倒置的 Ctron 形,fn 值字段 Route.chain 同族)。
头解析为 Str 面自建(parse_head):todo_api 骨架的 http_parse_head 走 &I64[]
lane,interp 执行面未证且 web 载体即 Str——「增量读、逐块试解析」的骨架形态
原样保留(parse 每 read 一块),载体偏离如实登记。
panic 面(执行时核实,task-3-report 在册):Ctron 无进程内 catch(? 是
Option/Result-try,非异常);探针实证 panic = 印消息后整跑中止(两 interp 臂
rc=1;native ctron_panic 终止进程)——§6「panic 兜 500」不可进程内实现,登记为
进程级边界:handler panic 即服务进程亡。缓解 = 装配期 has_conflict 自检(§7-5
失败在启动,不在半夜)+ handler 测试门(test_call);真兜底挂语言项翻账。
职责落点(§6):listen(127.0.0.1,port 参)→ accept → 增量头读(头区上限
32KB,超 400)→ CL 读体(body_limit 默认 1MB;形坏 400/超限 413 不读体)→
构造 Req(hdrs/cookies 经 dispatch_h→req_full,params 随匹配)→ 预拼链
→handler→render→write;路径安全默认(.. 段一票 404;多余斜杠由 router
path_segs 空段丢弃归一);HEAD 自动应 GET 路由(去 body 保头);内置排空端点
POST /__shutdown(loop 直拦,不过路由表;200 "draining" 后断循环关监听)。
访问日志不做 loop 侧行(log_requests 中间件即声明序挂点,§4.5 形态①)。
装配面(Task 1/2 交接,调用点口径):with_sessions(key, now) 时钟参须
| | now_ms() / 1000(now/ttl 恒 unix 秒,now_ms 是毫秒——勿直喂);会话/flash
v1 边界重申(M-T1b-3):drop_session 忽略 key,flash 自清仅随 with_sessions 链。
v1 串行(§8.2):一连接一请求,多余缓冲字节(pipelining)随 close 丢弃。
pub struct
struct SrvIo
SrvIo —— socket 边界值驱动(fn 值五件;fd 全 I64 句柄,与 net/bind 同口径)。
rc 约定:listen <0 失败;accept <0 关停/失败;read 返 "" = eof/err(超时政策
由真驱动的 read 闭包内嵌,如 net_read_t 5000ms);write 返 n(<0 err);close 无值。
| Field | Type |
|---|---|
listen |
fn(Str, I64) -> I64 |
accept |
fn(I64) -> I64 |
read |
fn(I64, I64) -> Str |
write |
fn(I64, Str) -> I64 |
close |
fn(I64) |
struct HeadReq
| Field | Type |
|---|---|
method |
Str |
target |
Str |
hdrs |
List[Pair] |
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 |
|---|---|---|
reason(n: I32) |
Str |
— |
render(r: Resp) |
Str |
— |
body_limit_default() |
I64 |
— |
serve_with[S](io: SrvIo, host: Str, port: I64, r: Router[S]) |
I32 |
serve_with —— §6 循环(阻塞;排空即返 0,监听失败返 1)。启动自检 has_conflict (路由装配 panic 的静默双兜,router.ct 前置口径)。真 net 装配 = SrvIo 五件 映射 net.*(见 task-3-report 例);端口 :0 时实际端口由驱动方经 net_tcp_sockname 回读(本件不藏 sockname——驱动句柄面已是调用点能力)。 |
serve_with_limit[S](io: SrvIo, host: Str, port: I64, r: Router[S], body_limit: I64) |
I32 |
— |
loop_one[S](r: Router[S], method: Str, target: Str, hdrs: List[Pair], body: Str) |
Str |
loop_one —— 单请求纯核(fake 直驱可测):.. 段拒(§6 路径安全默认)→HEAD 自动应 GET 路由 → dispatch_h(全 target;查询串在 dispatch_h 内分裂)→ render;HEAD 去 body 保头(状态行+头区原样,Content-Length 保持 GET 形态)。 |
head_end(buf: Str) |
I32 |
头区终点:首个 "\r\n\r\n" 起点(其 +4 起为体);未完 -1 |
parse_head(head: Str) |
HeadReq |
请求行 + 头区:首行 "METHOD SP target SP HTTP/x.y"(HTTP 版后缀丢弃); 头行 "K: V" OWS 削平;同名头逗并,键保留首见大小写(值合并语义以 eq_ignore_ascii_case 同名可达为准——core hdr_get/serve cl_of 均大小写 不敏感;§8.1 请求侧政策;Cookie 双头并后 ';'-解析见 task-3-report 登记——v1 单 Cookie 头为常态面)。行切 '\n' 后削尾 '\r'。 |
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 |
— |