Skip to content

[finding] @objectstack/http-conformanceIHttpServer.setFallbackHandler 零断言 —— ADR-0076 OQ#10 的落点仍空着 #6143

Description

@qq9340100

观察类发现,记录于 #5122(退役 runtime HttpServer 包装器)实现期间。今天没有用户会撞上:该成员目前只有一个实现,没有第二方可以与之分叉。

事实

packages/spec/src/contracts/http-server.tssetFallbackHandler?(handler) 的契约写得很实(#5040 §1-C),四条可测的保证:

  1. 只在所有显式注册的路由都未命中之后运行 —— 零注册顺序依赖,实现必须映射到框架自己的 not-found 钩子(Hono 的 app.notFound),而不是一条 ${prefix}/* 通配路由;
  2. req.body 在这里是可读的 —— 与 use() 的 Middleware seam 明确相反(那里解析 body 会提前吃掉流),这条正是声明式端点必须走这个 seam 而不能走 use() 的原因;
  3. 重复调用是替换而非串链 —— 只有一个 fallback;
  4. handler 不写响应时,适配器自己的 unmatched-request 答复(404/405 + Allow)原样留在原位

@objectstack/http-conformance(packages/qa/http-conformance)是这批语义的跨适配器守卫 —— IHttpServer 的 TSDoc 直接点名它,404/405 + Allow 都在它的必测面里。但它对上面四条一条也不断言

后果的形状:第二个实现该成员的适配器可以在这四条上任意分叉而 CI 全绿。而 #5111 之后 setFallbackHandler 已经是声明式 apis: 端点的唯一进入通路,所以分叉的代价不是「行为略有差异」,而是「这个适配器上的声明式端点行为不可预期」。

为什么标 finding

今天仓内只有 HonoHttpServer 实现该成员;conformance 包自己的参考适配器 NodeHttpServer(packages/qa/http-conformance/src/adapter.ts)刻意不实现它 —— 它的文件头明确只认领契约 + 两个软扩展(streaming、getPort())。所以没有第二方可以分叉,缺口是潜伏的,不是现网故障。它会在第二个适配器实现该成员的那天变成现实。

为什么没有随 #5122 的退役 PR 顺手做掉

#5122 的维护者裁决写的是「随退役 PR 顺手或另拆」。实测不顺手:要让断言有意义,得二选一,而两条都不是移除 PR 该带的货 ——

  • 先给 NodeHttpServer 补上 setFallbackHandler 实现(那是功能 PR:node:http 上没有现成的 not-found 钩子,得在路由未命中分支里接一条,并保证 405 + Allow 仍然优先);
  • 或写一条只在 Hono 上跑的条件用例(对「跨适配器一致」什么也没证明,正是本单要防的那种绿)。

修法草案(留给分诊,不预判)

给 conformance 套件加一组共享用例,逐条转写上面四条契约,并让 NodeHttpServer 一起过 —— 即先补实现再加断言,顺序不能反(先加断言会把参考适配器判红,而它今天「不实现」是合规的)。

关联:#5122(退役,PR #6141)、#5080(契约成员落地)、#5090(Hono 侧实现)、#5111(E7 翻转 —— 该 seam 成为唯一通路)、#5409(runtime 侧 warn 兜底)、ADR-0076 D11 / OQ#10。


🤖 Generated with Claude Code

https://claude.ai/code/session_01Wbxm29qPKnLf44AbSxizqW

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions