tower-http 源码解析
这一卷对着 tower-http 0.7.1 读源码:
| crate | 版本 | tag | commit | 日期 |
|---|---|---|---|---|
| tower-http | 0.7.1 | tower-http-0.7.1 | c941451 | 2026-08-31 |
tower-http 是 tower 的 HTTP 版中间件库:CORS、压缩、解压、超时、Trace、重定向、静态文件,几十个模块,src/ 一共 3.16 万行(含模块内测试)。它只认 http::Request / http::Response 和 http_body::Body,不依赖 hyper,也不依赖 axum,所以同一个 layer 能套在 axum 的 Router 上、纯 hyper 服务上,也能套在 hyper-util 的客户端外面。
axum 卷的 tower-http 部分讲了 Web 应用最常用的几样「怎么用、在 axum 里会怎样」,reqwest 卷讲了客户端用到的重定向和解压。这一卷讲它们没讲的:模块内部怎么组织、模块之间怎么复用、客户端方向和服务端方向的对照,以及那几个在别处几乎没出现过的模块(classify、on_early_drop、metrics)。
tower 卷的主线是 poll_ready 的背压,在这里几乎不存在:tower-http 的中间件把 poll_ready 原样转发给内层,不占资源。全卷换成两条主线:
- 作用点主线(三个时刻):一个中间件只能在三处改写请求或响应:
call时(改请求,或者不调内层直接短路)、响应 future 完成时(改状态码和头,决定换不换 body)、body 流动时(poll_frame逐帧改写或观察)。每章开头标出本模块占了哪几处。 - 结局主线(一个请求怎么算结束):成功还是失败、正常结束还是被放弃,被拆成三件独立的事:
classify判断「什么算失败」,trace的 body 包装判断「什么算结束」,Drop守卫判断「什么算放弃」。
flowchart TB intro[00 导读] --> overview[01 全景] overview --> skeleton[02 共同骨架] skeleton --> req[03 改请求] skeleton --> guard[04 拦请求] skeleton --> body[05 改 body] skeleton --> outcome[06 结局] body --> client[07 客户端] outcome --> client body --> services[08 服务] client --> design[09 设计取舍] services --> design req --> design guard --> design design --> pit[10 常见坑] pit --> site[11 在本站中] site --> hands[12 动手]
从「导读」开始。只想会用 tower-http 的读者,先读 01、02 和 06;想读懂实现的,按顺序读。
目录
| 部分 | 内容 |
|---|---|
| 00 导读 | 怎么读这一卷;源码地图:35 个 feature、模块之间的依赖 |
| 01 全景 | tower-http 解决什么问题;一个请求穿过三个时刻 |
| 02 共同骨架 | 每个模块都有的四件套与三个宏;ServiceBuilderExt 与 ServiceExt;body 模块 |
| 03 改请求 | set_header 一族、请求标识与头、小型改写层、校验与鉴权 |
| 04 拦请求 | CORS 的内部、CSRF、body 大小限制 |
| 05 改 body | 编码内核、压缩、响应解压、请求解压、超时一族 |
| 06 结局 | classify、Trace 的内部、CatchPanic、InFlightRequests、OnEarlyDrop |
| 07 客户端 | FollowRedirect 的策略;拼一个客户端栈 |
| 08 服务 | ServeDir 的 Backend trait;Redirect 服务 |
| 09 设计取舍 | 八个设计决定:换来什么、付出什么、由谁来补 |
| 10 常见坑 | CORS 配置 panic、多余字节、限额被解压绕过、307 原样返回、日志里的 token 等十一个 |
| 11 在本站中 | 本站用到的两样与手写的四样;升到 0.7.1 会怎样 |
| 12 动手 | 从零写一个跨三个时刻的中间件 |
💬 关联讨论
发起讨论加载中...