H3 工具集H3 Utils实战导读在可组合 HTTP 框架里接住数据、守住边界【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3假设你要写一个上传接口解析 multipart 请求体、限制体积、防路径穿越、用完要清理资源——全靠手写既繁琐又容易出错。本文以可组合 HTTP 框架 H3 为例带你把 H3 Utils工具集按场景走一遍哪个功能该找哪个函数、哪条线不能碰、每个工具怎么用才安全。读完你应能把自己的需求映射到具体工具并理解它背后的安全取舍。可组合到底组合了什么可组合 HTTP 框架的最小内核内核只做三件事。H3实例本身只负责请求生命周期把Request包成H3Event、按路由分发、串行执行中间件。其余一切——请求体解析、响应构造、Cookie、安全校验、反向代理、JSON-RPC——都是独立工具函数统一从 src/index.ts 的 Utils 段导出src/utils/ 目录下一个文件对应一个功能域每个函数都带完整 JSDoc 与示例。文档侧的导航见 docs/2.utils/0.index.md其中也点明了同一件事要么用内置工具要么自己写一个。对比很直接——不用 H3 要手写用 H3 是一行请求体解析手写Content-Type分支 接一个 multipart 解析库 →readBody(event, { type: formData })CORS手拼预检与普通响应共五类头、还要处理预检状态码 →handleCors(event, options)Cookie 写入手工序列化、处理同名去重 →setCookie(event, name, value, options)两个直接收益。一是按需加载用不到的能力不 import 就不进代码路径应用保持轻量二是签名统一——内置工具与你自写的工具遵循同一套(event) ...约定自己写一个解析工具也能无缝混进路由或中间件不需要额外适配层。接收入站数据的几条硬规则H3 请求体解析与参数单层解码场景还是那个上传接口。先立三条纪律再谈怎么用。规则一解析是 opt-in 的。readBody默认按 JSON 解析仅当Content-Type为application/x-www-form-urlencoded时回退到 URL 编码解析multipart/form-data等其他类型必须显式传options.type: formData绝不从请求头自动探测——否则任何客户端都能把服务器拖进昂贵的 multipart 解析src/utils/body.ts 注释里的 #875 问题。规则二体积限制要声明。assertBodySize(event, limit)不是预缓冲而是把请求包装成边读边计数的代理累计字节超限立即以413中断谎报偏小的Content-Length也能在流中途被抓住诚实地超大Content-Length会在处理前直接413拒绝同时携带Content-Length与Transfer-Encoding的请求按请求走私处理返回400RFC 7230。中间件形态bodyLimit(limit)语义相同。规则三路由参数只解一层切勿二次解码。getRouterParams(event, { decode: true })只做一次解码且路径分隔符%2f、%5c及其任意%25嵌套永远保持编码——解码后的参数绝不能长出路由从未匹配过的/或\。解码一次的结果仍可能含百分号转义如%252e%252e解一次变成%2e%2e请把它当最终形态校验不要再次decodeURIComponent否则../和 NUL 字节会被还原出来。一个坑最常见的错误是decode: true之后再补一次decodeURIComponent保险——恰恰是第二次解码制造了穿越。一个短用法app.post(/upload, async (event) { assertBodySize(event, 10 * 1024 * 1024); // 10MB const data await readBody(event, { type: formData }); return received ${Object.keys(data).length} fields; });查询侧同理getQuery(event)取解析结果getValidatedQuery(event, schema)解析后立即校验函数、zod、valibot 都可用onError可定制错误响应方法校验用isMethod/assertMethod后者不匹配时抛带Allow头的405RFC 9110。行为基准可对照 test/body.test.ts 与 test/unit/path.test.ts。把响应安全地送出状态码净化、流式输出与 HTML 转义场景渲染一个动态页面、推一个进度流或者把用户发来的内容写进 HTML。状态码净化。sanitizeStatusCode保证状态码落在 100–599 的合法整数区间sanitizeStatusMessage把描述文本过滤到只剩水平制表符、空格与可见 ASCIIRFC 7230 §3.1.2防止控制字符破坏响应解析。流式输出。iterable(iterable)按顺序逐块发送允许在 yield 之间穿插异步任务每个 chunk 必须是字符串或 Buffer生成器的返回值与 yield 值同等对待。注意首个 chunk 会在响应创建前被 await因此第一块之前设置的event.res.status与 headers 仍生效之后设置的一切都会被忽略头已上线。HTML 转义。html标签模板自动转义插值里的 raw(markup)把一段字符串标记为已信任、已预转义并直接输出——切勿把用户输入传给raw否则重新引入 XSS。另注意转义只保护元素内容与带引号的属性值未加引号的属性、javascript:之类的 URL 属性、script/style内容需要另行校验。重定向。redirect(location, status)默认302响应体附带 meta refresh 页面兜底忽略响应头的客户端。若location来自用户输入必须对照白名单校验否则就是开放重定向回到上一页用redirectBack(event, { fallback })更稳妥它只取同源referer的 pathnameallowQuery: true才保留查询串缺失或跨源时回退fallback——而fallback必须是硬编码的可信路径。生命周期清理。onDispose(event, cb)在事件彻底结束响应体流完、客户端断开或响应体出错时按注册顺序执行回调晚于全局onResponse钩子。它表示h3 处理完了这个事件不是客户端已收到响应要在仍在产出响应时响应客户端断开比如中止上游 fetch用event.req.signal。空负载用noContent(status)默认 204writeEarlyHints可写出103 Early Hints在不原生支持的运行时回退为 CDN 可用的Link头。信任边界哪些请求头能信以及 H3 路径穿越防护⚠️ 把文档里散落的安全警示收拢成一句话凡客户端能自行写入的头默认不可信要用就 opt-in。场景是你需要回答客户端是谁和这个路径是否合法。Host 与 x-forwarded-*。getRequestHost/getRequestURL/getRequestProtocol默认都不信任x-forwarded-*头各自有独立开关仅在确认运行在会覆写这些头的可信反向代理/CDN 之后才开启。由Host头推导出的 origin 不能用于 CSRF/origin 校验、缓存键或生成发给其他用户的绝对链接——除非上游对 Host 做了白名单固定。toRequest基于host头把相对路径合成完整 URL 时协议恒为http忽略x-forwarded-protohost 仅作 authority 且不能横向扩进路径——想控制 origin 就传绝对 URL含/ \ ? # 或空白的 host 判为非法并回退localhost。请求 IP。getRequestIP默认取event.req.ip连接对端或服务器被配置信任上游代理后解析出的客户端地址。xForwardedFor: true改为取x-forwarded-for链中第一个条目——代理惯例是往链尾追加所以那个位置正是客户端自己写的值开启它等于允许任何调用者自报 IPIP 白名单、限流与审计全部失效。更优做法是让服务器如 srvx 的trustProxy从右往左解析可信链此选项保持关闭。H3 路径穿越防护。resolveDotSegments(path, opts)解析./..段永远不逃逸到根/之上结果恒为单一前导/的绝对路径杜绝//host协议相对形态任意%25嵌套深度解码百分号编码的点段%2e、%252e……并把\规范化为/因此编码式、反斜杠式穿越与字面../一样被捕获%2f/%5c编码的分隔符默认保持编码。当下游如 nginx会自行把%2f解码回/时安全/范围检查应开启decodeSlashes下游合并//时开启mergeSlashes尾部./..按目录处理并保留结尾斜杠RFC 3986 §5.2.4内部空段保留。isCanonicalPath(path, opts)判断路径是否已处于规范形态即resolveDotSegments会原样返回。导出它是给热路径每次请求的 scope 检查、规则匹配跳过规范化调用用的——文档说得直白scope 检查里漏掉一次规范化是绕过漏洞不是性能问题。路由参数若将用作文件系统或上游路径用这里的能力解析而不是继续解码对应上一节只解一层的红线。会话、认证与跨域加密会话与 origin 校验场景需要记住登录态还要允许受控的跨域调用。Basic Auth。basicAuth(opts)是中间件形态requireBasicAuth(event, opts)对当前请求就地生效失败抛401并带WWW-Authenticate挑战成功后把{ username, password, realm }写入event.context.basicAuth。凭据比较用恒定时间函数且所有 401 路径带计时抖动——响应耗时无法区分用户不存在和密码错误。会话。useSession(event, config)创建会话管理器getSession只读匿名读取不会下发Set-Cookie第一次写入才持久化updateSession接受对象或差量更新函数clearSession清空sealSession/unsealSession负责加密签名与解密验签src/utils/session.ts底层是 src/utils/internal/iron-crypto.ts 的 iron 加密原语。几个要点password就是安全边界本身至少 32 字符且必须来自加密随机源——可猜测的口令即便够长也不安全maxAge是绝对寿命从创建起算idleTimeout是滑动窗口重密封lastSeenAt前移且重密封被节流到每半窗口最多一次会话数据加密签名后存入块状 Cookie——超大值自动分块无需关心浏览器单 Cookie 大小上限Cookie 属性默认secure; httpOnly; sameSitelax。CORS。handleCors(event, options)一站式处理预检请求附加预检头并返回204返回值非false即表示请求已被处理、无需后续动作。appendCorsHeaders/appendCorsPreflightHeaders/isCorsOriginAllowed/isPreflightRequest是更细粒度的底层工具。一个坑origin 列表中的正则条目是无锚定匹配的/example\.com/会同时命中https://example.com.evil.test——务必^…$锚定并转义字面点。开启credentials后浏览器把*当作字面方法/头名应显式列举。请求指纹。getRequestFingerprint(event, opts)把 ip默认、method、url、user-agent 的固定组合哈希成唯一指纹默认 SHA-256适合做限流、防滥用的键。每个分量占固定槽位且值经过转义单个分量的值无法伪造另一个槽位的结构。把请求转交出去内部子请求与外部代理的凭据边界场景你的服务处在中间要把请求转走。src/utils/proxy.ts 里三个工具粒度递进fetchWithEvent(event, url, init?)携带事件上下文的 fetch。内部 URL/开头经event.app.fetch()派发为子请求永不出进程继承入站请求的过滤后头与运行时元数据ip、waitUntil等外部 URL用原生fetch(url, init)原样发送事件的头与上下文不被继承。proxyRequest(event, target, opts)把入站请求代理到目标。请求体流式透传、不缓冲因此此前若读过 bodyreadBody()、读体中间件会锁死流导致代理失败——需要先检查再代理时从event.req.clone()读取保持原流完好。proxy(event, target, opts)代理并把响应回传。与proxyRequest的关键差异默认不转发入站头只发送调用方通过opts.headers显式传递的头proxyRequest用的opts.filterHeaders对它无效。凭据Cookie / Authorization转发策略对比工具Cookie / Authorization 处理fetchWithEvent内部 target继承入站过滤后头内置 denylist 已剔除会导致问题的头属正确行为fetchWithEvent外部 target从不继承事件头——向任意主机转发凭据不安全proxyRequest原样转发入站头同信任的反向代理正确对不完全信任的上游用filterHeaders: [cookie, authorization]剥离proxy只发显式传入的opts.headers客户端凭据需你手动传两条共同红线永远不要把未净化的用户输入当作url/target——主机白名单、拦截内部路径、强制协议都是调用方的职责以/开头的内部 target 经event.app.fetch()派发绕过任何外部安全层反向代理认证、IP 白名单、mTLS。上游 3xx 默认透传给客户端而非跟随需跟随时设fetchOptions: { redirect: follow }但流式请求体无法重放跟随重定向可能失败。getProxyRequestHeaders(event)可以取出已剔除代理时已知会出问题的头hop-by-hop 帧头等的对象供自行控制转发时使用。从 JSON-RPC 到 WebSocketMCP 服务与双向通道场景想让 H3 应用直接承担 MCPModel Context Protocol等 JSON-RPC 服务端职责实现见 src/utils/json-rpc.ts。defineJsonRpcHandler(methods)创建实现 JSON-RPC 2.0 的 H3 事件处理器只接受 POST否则405内置三个值得记住的安全默认值要求 JSONContent-Type否则415——否则 HTML 表单就构成 CORS simple request可带着受害者 Cookie 跨源发出这是一道 CSRF 防线默认拒绝跨源请求403——默认仅同源放行防 CSRF 与 DNS rebinding不带Origin头的请求CLI、服务间调用直接放行。若传显式 allowlist正则同样无锚定记得锚定批处理上限 50 条——批处理项并发派发无上限的批会把一次 HTTP 请求放大为任意多次方法调用扇出放大。app.post(/rpc, defineJsonRpcHandler({ methods: { echo: ({ params }, event) Received \${params}\, sum: ({ params }) params.a params.b, }, }));异常也有讲究只有你自己抛出的HTTPError会暴露message/data其他内部异常统一遮罩为 Internal error 再映射到 JSON-RPC 错误码HTTP 状态与错误码之间有语义映射如429 → -32029。defineJsonRpcWebSocketHandler(opts)实现 JSON-RPC 2.0 over WebSocket每条入站文本消息按请求处理并回发响应。注意差异与 HTTP 版本不同它不校验请求Origin——WebSocket 升级不受 CORS 约束任意源页面都能带着访客 Cookie 建立连接跨站 WebSocket 劫持。请在upgrade钩子里校验Origin不匹配时抛出Response中止连接。适配层与社区生态对接 Node.js 生态挂载与事件工具。withBase(base, input)返回新处理器调用原处理器前先剥离 base 前缀适合把子应用挂到某路径app.use(/api/**, withBase(/api, api.handler))。isEvent/isHTTPEvent/getEventContext/mockEvent是类型判断、取事件上下文、构造模拟事件测试用的四个基础件。中间件钩子。onRequest(hook)/onResponse(hook)/onError(hook)分别挂在请求前、响应后与错误时onResponse可返回新 Response 替换原响应onError可返回新 Response 优雅兜底。bodyLimit(limit)即assertBodySize的中间件形态同为随读随限未被读取的 body 不计数。WebSocket 工具。defineWebSocket(hooks)定义open/message/close等钩子defineWebSocketHandler(hooks, http?)定义 WebSocket 事件处理器——非升级普通 HTTP请求默认返回426 Upgrade Required传入http处理器可让同一路由同时服务 WebSocket 升级与普通 HTTP要自定义升级握手本身用 crossws 的upgrade钩子。Node 适配器。defineNodeHandler(handler)、defineNodeMiddleware(handler)、fromNodeHandler(handler)、fromWebHandler(handler)全部在 src/adapters.ts 实现把 Nodereq/res风格处理器带入 H3 事件系统注意fromNodeHandler的产物只能在 Node 运行时执行。社区生态。docs/2.utils/99.community.md 收录了多个与 H3 v2 兼容的库ApitallyAPI 监控、分析与请求日志、H3ravel Framework把 Laravel 体验移植到 JS 生态、Intlify srvmid服务端国际化、Clear RouterLaravel 风格路由、unjwt基于 Web Crypto、零运行时依赖的 JWS/JWE/JWK 工具含 H3 v2 适配、Arkstack运行时无关的 TypeScript 后端框架。也欢迎社区以 PR 提交新的兼容库。收尾最少的代码最强的安全基线回看这套工具集每个决策都有出处body 解析 opt-in、代理凭据有明确转发策略、路径穿越有编码防御、Host 头有信任边界——安全的那种用法在这里都是默认行为。所有工具直接从包入口导入例如import { readBody, getQuery, setCookie, handleCors, proxyRequest, defineJsonRpcHandler } from h3。src/index.ts 的 Utils 段是完整清单src/utils/ 每个文件对应一个功能域可运行示例见 examples/examples/cookies.mjs、examples/cors.mjs、examples/auth.mjs、examples/websocket.mjs、examples/server-sent-events.mjs行为基准见 test/test/cookies.test.ts、test/proxy.test.ts、test/security.test.ts、test/session.test.ts、test/json-rpc.test.ts完整分类导航在 docs/2.utils/。下次要查某个具体行为先翻这三处基本不会走偏。【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考