L7 HTTP 过滤器
HTTP 过滤器是你写自定义逻辑(认证、限流、改 header)最常打交道的层。今天看它的接口(比 L4 丰富得多)、decode/encode 双向迭代、状态码怎么控制流程。
L7 vs L4
Authorization 头、哪段是 /admin 路径。要做"检查登录态""按 URL 路径限流""给响应加个头"这类业务逻辑,你需要看到的是解析好的、有语义的 HTTP,而不是一堆生字节。sendLocalReply(401)),不用把表格递到后面部门(上游)。Higress 的绝大多数插件都是这类"审批员"。丰富的状态码
envoy/http/filter.h 的 FilterHeadersStatus(:38):
| 状态 | 含义 |
|---|---|
| Continue | 继续下一个过滤器 |
| StopIteration | 停止(等异步) |
| ContinueAndDontEndStream | 继续但延迟结束流(以便加 body) |
| StopAllIterationAndBuffer | 停止并缓冲后续数据 |
| StopAllIterationAndWatermark | 停止并背压 |
StopIterationAndBuffer),有时要背压(StopIterationAndWatermark,配合 Day 19 的水位缓冲)。FilterDataStatus/FilterTrailersStatus 同理。注释极详细,值得逐条读。👶 小白:L4 就俩状态够用,为啥 L7 要搞出 StopIterationAndBuffer 这么多花样?
👨🏫 老师:因为 HTTP 请求"分批到达"且"想整体处理"的需求多。举个例子:一个插件要给上传的图片打水印,它必须拿到完整的请求体才能改。但请求体是一片一片 decodeData 传来的。这时它返回 StopAllIterationAndBuffer——告诉框架"你先帮我把后续 body 全攒住别往下发,攒齐了我一次处理"。L4 只是"字节转发",没有"攒齐一个语义整体"的需求,所以两个状态就够。状态多,是因为 HTTP 层要照顾的情况多。
Decoder / Encoder 过滤器
// StreamDecoderFilter(filter.h:940)—— 请求(解码)方向
virtual FilterHeadersStatus decodeHeaders(RequestHeaderMap& headers, bool end_stream) PURE; // :948
virtual FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream) PURE; // :957
virtual FilterTrailersStatus decodeTrailers(RequestTrailerMap& trailers) PURE; // :963
// StreamEncoderFilter(filter.h:1156)—— 响应(编码)方向
virtual FilterHeadersStatus encodeHeaders(ResponseHeaderMap& headers, bool end_stream) PURE; // :1183
virtual FilterDataStatus encodeData(Buffer::Instance& data, bool end_stream) PURE; // :1192
// StreamFilter(filter.h:1226)—— 同时继承二者(双向过滤器)
decode*=处理请求(进来方向)、encode*=处理响应(出去方向)。一个过滤器可以只管一个方向(DecoderFilter)或都管(StreamFilter)。比如认证过滤器只需 decode(检查请求);加响应头的过滤器只需 encode。基类 StreamFilterBase::onDestroy()(:896)必须清理定时器/异步。回调操纵流
过滤器通过回调(StreamDecoderFilterCallbacks,filter.h:553)操纵流:continueDecoding()(恢复)、decodingBuffer()(拿缓冲)、addDecodedData()(加数据)、sendLocalReply()(:669,直接返回本地响应不走上游)、route()(拿路由)、streamInfo()(流信息)。
sendLocalReply() 很关键——它让过滤器能直接生成一个响应返回给客户端,不再转发到上游。比如认证过滤器发现"没登录",直接 sendLocalReply(401) 把请求挡回去,不浪费上游资源。这是网关"拦截"能力的基础:限流拒绝、认证失败、参数非法,都靠它当场返回。HIGRESS 定制了 modifyDecodingBuffer 等重载(filter.h:578-583)供 Wasm 插件改请求体。GET /admin,但没有 Authorization 头。认证过滤器在
decodeHeaders 里发现缺 token → 调 callbacks_->sendLocalReply(401, "unauthorized") → 直接生成 HTTP/1.1 401 Unauthorized 返回客户端,请求根本不会传给后面的限流过滤器,更不会转发到上游 → 返回 StopIteration 中断 decode 链。省下了后端的一次无谓处理——这就是"审批员当场驳回"。
decode 迭代
// source/common/http/filter_manager.cc:603 —— L7 请求处理的心脏
for (; entry != decoder_filters_.end(); entry++) {
FilterHeadersStatus status = (*entry)->decodeHeaders(headers, end_stream); // :621 调过滤器
const auto continue_iteration = (*entry)->commonHandleAfterHeadersCallback(status, end_stream); // :643
if (!continue_iteration && std::next(entry) != decoder_filters_.end()) {
return; // 停止迭代,等过滤器调 continueDecoding() 恢复
}
}
commonHandleAfterHeadersCallback(L06)把状态码翻译成"继续/停止"。起点由 commonDecodePrefix 决定(从头或从下一个)。状态→行为映射
// filter_manager.cc:165 commonHandleAfterHeadersCallback
case StopIteration: iteration_state_ = StopSingleIteration; break;
case StopAllIterationAndBuffer: iteration_state_ = StopAllBuffer; break;
case ContinueAndDontEndStream: end_stream = false; headers_continued_ = true; break;
case Continue: headers_continued_ = true; break;
// 返回 false(停止)当 stoppedAll() 或 StopIteration
Continue→继续;各种 Stop→记下停止模式并中断。这是"状态码"和"迭代控制"之间的转换层,L7 逻辑的关键枢纽。| 过滤器返回的状态 | 翻译成的迭代行为 | 什么场景用 |
|---|---|---|
Continue | 标记 headers 已放行,继续下一个过滤器 | 处理完没意见,放行(最常见) |
StopIteration | 停单个(本过滤器),等它 continueDecoding() 恢复 | 要异步查外部鉴权,先挂起 |
StopAllIterationAndBuffer | 全停,并把后续 body 攒进缓冲 | 要看完整个请求体才能决定(如改写 body) |
ContinueAndDontEndStream | 继续但先别结束流(end_stream=false) | 准备往请求里补一段 body |
encode 对称路径
FilterManager::encodeHeaders()(filter_manager.cc:1289)——响应方向,结构与 decode 对称,遍历 encoder_filters_,走完后 filter_manager_callbacks_.encodeHeaders()(:1369)回调到 HCM 的 ActiveStream::encodeHeaders(Day 07),最终 codec 编码写回。
今日小结 + 动手
🧠 今天你应该能回答
- L7 过滤器和 L4 的本质区别?
- 为什么 L7 状态码更丰富?举两个特殊状态。
- decode 和 encode 各处理什么方向?
- sendLocalReply 让过滤器能做什么?
- 状态码怎么翻译成迭代行为?decode/encode 的镜像关系?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/envoy
sed -n '38,110p' envoy/http/filter.h # FilterHeadersStatus
sed -n '940,990p' envoy/http/filter.h # StreamDecoderFilter
sed -n '603,680p' source/common/http/filter_manager.cc # decode 迭代