Envoy gRPC HTTP/1.1 Reverse Bridge 过滤器大响应缓冲 SEGFAULT 修复深度解析
Envoy gRPC HTTP/1.1 Reverse Bridge 过滤器大响应缓冲 SEGFAULT 修复深度解析【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy导读本文围绕 Envoy 当前仓库中grpc_http1_reverse_bridge过滤器的一则崩溃修复changelog 条目 grpc_http1_reverse_bridge__fixed-segfault-when-buffering-large-response.rst展开该过滤器在withhold_grpc_frames开启且未配置response_size_header时如果上游响应体超过下游 HTTP/2 流控窗口会因一次性缓冲并释放全部数据而触发 SEGFAULT。修复后的过滤器改为优先利用上游Content-Length头增量流式传输响应。读完本文你将掌握该过滤器的完整工作原理、崩溃产生的根因、基于Content-Length的新流式路径在 filter.cc 中的实现细节以及如何通过配置规避类似问题。过滤器背景把 gRPC 请求降级为 HTTP/1.1grpc_http1_reverse_bridge是 Envoy 的一个 HTTP 流过滤器stream filter其核心能力正如 filter.h 中的注释所述将一个入站的 gRPC HTTP 请求降级为 h/1.1 请求从而让一个不理解 HTTP/2、HTTP/3 或 gRPC 语义的上游服务也能处理 gRPC 流量。官方文档 grpc_http1_reverse_bridge_filter.rst 将其工作流程总结为五步检查入站请求的 content-type若为 gRPC 请求则启用过滤器将 content-type 改写为可配置的值配置为application/grpc时可视为 noop可选地从请求体中剥离 gRPC frame header并相应调整 Content-Length收到响应后校验响应 content-type并把 HTTP 状态码映射为 grpc-status 写入响应 trailers可选地在响应体前补上 gRPC frame header同样需要调整 Content-Length。由于最终映射到 HTTP/1.1该过滤器只支持 unary一元gRPC 调用。过滤器的配置定义在 v3/config.proto包含三个核心字段字段类型说明content_typestring必填min_len: 1转发给上游的 content-type同时用于校验上游响应是否带相同 content-typewithhold_grpc_framesbool为 true 时过滤器假定上游不理解 gRPC 帧负责剥掉请求中的 gRPC 帧头、在响应中加回帧头向上游隐藏 gRPC 语义response_size_headerstring须为合法 HTTP 头名配合withhold_grpc_frames使用指定上游某个响应头来告知响应大小从而无需缓冲即可流式转发若该头缺失或与实际响应体大小不符过滤器将把上游响应视为错误此外还有 per-route 配置FilterConfigPerRoute仅含一个disabled字段用于在特定 virtualhost/route/weighted-cluster 上关闭该过滤器。崩溃场景缓冲路径撞上下游 H2 流控窗口在修复之前当withhold_grpc_frames true且没有配置response_size_header时过滤器无法预先得知响应体大小只能走全量缓冲路径。旧逻辑在 filter.cc 中表现为每个非终止的encodeData调用都把数据buffer_.move(buffer)移入自有缓冲返回StopIterationAndBuffer直到end_stream才一次性把缓冲的数据 prepend 上 gRPC 帧头、整体释放给下游。这带来的问题正是 changelog 条目所描述的Fixed a crash (SEGFAULT) in thegrpc_http1_reverse_bridgefilter whenwithhold_grpc_framesis enabled withoutresponse_size_headerand the upstream response body exceeds the downstream HTTP/2 stream flow control window. The filter now uses the upstreamContent-Lengthheader to stream the response incrementally instead of buffering and releasing it all at once.即当上游响应体大小超过下游 HTTP/2 流控窗口时把整个响应在单次encodeData中一次性释放出去会让 H2 codec 一次需要排入过多帧overwhelm the H2 codec with too many frames at once见 filter.cc 的注释从而触发进程崩溃SEGFAULT。从 reverse_bridge_test.cc 的测试注释可以印证这一点该测试专门验证当withhold_grpc_frames为 true 且未配置response_size_header时过滤器使用上游响应头中的 Content-Length 来流式传输响应而不是缓冲整个响应体以防止大响应一次性产生过多帧压垮 H2 codec。修复方案借上游 Content-Length 实现增量流式传输修复的核心思路很直接既然无法从response_size_header得知响应大小那就读上游响应的Content-Length头。gRPC 反向桥接通常面对的是上游返回单个序列化 protobuf 消息unary 场景绝大多数上游会设置 Content-Length这为流式化提供了可靠的依据。修复在encodeHeaders阶段落地filter.cc新增了content_length_from_header_与response_message_length_两个状态字段定义见 filter.h读取上游响应的Content-Length若存在且为正数先做合法性校验单个 protobuf 消息不能超过 2GBuint32_t上限超过则直接以本地回复报错grpc_bridge_content_length_wrong记录response_message_length_置位content_length_from_header_ true把响应头中的 Content-Length 调整为响应体长度 Grpc::GRPC_FRAME_HEADER_SIZEgRPC 帧头固定 5 字节保证下游看到的长度与实际下发的数据一致若没有可用的 Content-Length则退回到原有的缓冲路径adjustContentLength加 5 字节帧头后交给encodeData缓冲。encodeData侧随之进入已知大小、直接流式的分支filter.cc第一段数据到达时用buildGrpcFrameHeader在数据前补上 5 字节 gRPC 帧头帧内长度字段填response_message_length_随后frame_header_added_ true中间的数据段原样透传不再进入自有缓冲直接返回Continue数据流结束end_stream时校验upstream_response_bytes_累计收到的上游响应字节数是否等于response_message_length_不一致则以本地回复报错grpc_bridge_content_length_wrong同时写入携带 grpc-status 的 trailers。由此整个响应体被拆成多个小的encodeData调用逐步下发给 H2 codec单次释放的数据量受控不再触发流控窗口相关的崩溃。三类响应路径的完整对照结合 filter.cc 的实现withhold_grpc_frames true时响应处理实际上分三种路径场景encodeHeaders 行为encodeData 行为备注配置了response_size_header从指定头取值计算大小头缺失即报错已知大小直接流式帧头加在第一段数据前原有流式路径未配置response_size_header但上游有合法Content-Length置content_length_from_header_按长度5调整 Content-Length已知大小直接流式本次修复新增校验最终字节数与声明值一致未配置response_size_header且上游无Content-Length仅调整 Content-Length5全量缓冲到buffer_结束时 prepend 帧头整体下发旧缓冲路径仅作为兜底保留三种路径在encodeTrailers中还会做最后的兜底当既没有response_size_header也没有content_length_from_header_即走了缓冲路径时在 trailers 阶段把缓冲内容补上帧头后一次性addEncodedDatafilter.cc。配置示例与 per-route 关闭仓库自带的完整示例见 grpc-reverse-bridge-filter.yaml核心配置如下http_filters: - name: envoy.filters.http.grpc_http1_reverse_bridge typed_config: type: type.googleapis.com/envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3.FilterConfig content_type: application/grpcproto withhold_grpc_frames: true - name: envoy.filters.http.router typed_config: type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router如果某些路由不希望启用该过滤器可通过路由级typed_per_filter_config指定FilterConfigPerRoute.disabled: true将其关闭routes: - match: prefix: /route-with-filter-disabled route: host_rewrite_literal: localhost cluster: grpc timeout: 5.00s # per_filter_config disables the filter for this route typed_per_filter_config: envoy.filters.http.grpc_http1_reverse_bridge: type: type.googleapis.com/envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3.FilterConfigPerRoute disabled: true该逻辑在 filter.cc 中通过resolveMostSpecificPerFilterConfig解析最具体的 per-filter-config 生效过滤器在 config.cc 中以envoy.filters.http.grpc_http1_reverse_bridge名称静态注册为NamedHttpFilterConfigFactory。需要提醒的是过滤器在请求路径上会剥离 gRPC 帧头并调整请求 Content-Lengthfilter.cc若请求体不足 5 字节不可能包含完整 gRPC 帧会直接返回本地错误grpc_bridge_data_too_small而上游返回的 content-type 若与配置的content_type不一致也会以grpc_bridge_content_type_wrong直接终止响应filter.cc。这些细节在排查修复后仍然报错的场景时非常有用。测试验证三条针对性的单测本次修复在 reverse_bridge_test.cc 中配套了三条针对性测试WithholdGrpcFramesStreamsWithContentLength约 L1009上游返回Content-Length: 12的响应验证头调整后为 1712 5 字节帧头第一段数据被补上 5 字节帧头变为 9 字节中间段原样 4 字节透传最终段校验并写入grpc-status: 0trailers——完整覆盖新流式路径WithholdGrpcFramesContentLengthMismatch约 L1068上游声明Content-Length: 100但实际只发 8 字节验证end_stream时以本地回复报错 envoy reverse bridge: upstream set incorrect content lengthgrpc-status Internal确认流式路径的完整性校验生效WithholdGrpcFramesContentLengthTooLarge约 L1117上游声明超过uint32_t上限的 Content-Length验证在encodeHeaders阶段即被拒绝防止构造出非法 gRPC 帧。这三条测试恰好从正常流式、声明不符、超限拒绝三个角度锁定了新逻辑的行为边界。从源码结构看过滤器通过upstream_response_bytes_在每次encodeData累加filter.cc这一计数是实现流式路径字节数校验的基础。局限与最佳实践依赖上游 Content-Length修复后的流式路径依赖上游如实设置Content-Length。若上游用 chunked 传输或根本不设 Content-Length过滤器只能退回缓冲路径崩溃风险场景并未完全消除。对于已知会产生大响应的服务建议上游总是设置准确的 Content-Length或直接配置response_size_header走确定性更强的流式路径。unary 限制由于映射到 HTTP/1.1仅支持一元 gRPC 调用流式调用不在本过滤器支持范围内。单消息 2GB 上限单个 protobuf 消息不能超过 2GB这是 gRPC 帧长度字段uint32的硬限制超限会直接报错而非崩溃。升级验证该修复属于 bug_fixes 类别见 changelogs/current/bug_fixes部署到生产前建议先运行上述单测与集成测试 reverse_bridge_integration_test.cc 确认行为符合预期。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →