协议
这一部分讲 pingora-core 里「字节进来、HTTP 出去」的那一层:src/protocols/(L4 流、HTTP/1、HTTP/2、TLS 和它们共用的摘要类型)和 src/modules/(HTTP 模块)。监听器怎么 accept 是服务器部分的事,连接上游时同一套类型怎么用是连接上游部分的事,钩子按什么顺序调用是代理部分的事。
pingora 不用 hyper。HTTP/1 是它自己在 httparse 之上写的,请求头和消息体各有一个状态机;HTTP/2 交给 h2 crate,pingora 只管握手参数、拆流和校验;TLS 有四个后端,用 cfg 拼成一套同名的 API。所有连接最后都装进同一个 Box<dyn IO>,HTTP 层不关心底下是 TCP、Unix 域套接字还是 TLS。
- L4 流:
l4::Stream是RawStream外面套BufStream、回退栈和摘要;accept 时缓冲容量给 0、握手时换成 64 KiB,上游连接一直是 0;tokio 1.54 拒绝零容量BufReader的来龙去脉,以及 main 上5b30d71的修法。 - HTTP/1 服务端:
read_request用httparse在栈上解析、零拷贝建请求头并保留大小写;各种 400 的实测原因;0.9.0 的请求目标校验、默认删逐跳头和可选的 pipelining;第一个请求之后空闲连接不再超时。 - HTTP/1 消息体:
BodyReader和BodyWriter的状态机;22 种边界和畸形请求体的实测回应;扩展长度上限只在块头被拆开时生效;空数据写成0\r\n\r\n的问题。 - HTTP/2:ALPN 和 h2c 两条入口,h2c 前言检测会一直等满 24 字节;0.9.0 起默认 100 个并发流和 64 KiB 头列表,传
Some(H2Options::new())会把它们丢掉;每个流一个会话,1xx 被丢弃。 - TLS:四个后端怎么用 cfg 拼出同一套名字;
TlsSettings::intermediate在 openssl 和 rustls 上做的事完全不同;自签名证书在两个后端上各握手一遍,未知 ALPN 时一个继续、一个失败。 - HTTP 模块与压缩:
HttpModule是按order排好、每个请求新建一份的过滤器;ResponseCompression按Accept-Encoding的第一项选算法、不看 q 值;在request_filter里才打开压缩为时已晚。
flowchart TB tcp["TcpStream"] --> l4["l4::Stream"] l4 --> tls["TLS 流"] l4 --> io["Box dyn IO"] tls --> io io --> v1["v1 HttpSession"] io --> v2["h2 握手"] v2 --> s2["每流一个会话"] v1 --> body["消息体"] v1 --> mods["HTTP 模块"] s2 --> mods
前三页按请求主线的顺序读:字节先进 L4 流,再被 HTTP/1 解析成请求头,然后是消息体。HTTP/2 和 TLS 是这条线上的两个岔路,模块那一页回到代理的钩子之间。和 hyper 的逐项对照放在设计取舍。
💬 关联讨论
发起讨论加载中...