使用Envoy Lua Filter无法限制流式请求大小的问题排查
Envoy Lua过滤器实现流式请求体大小限制(针对Docker镜像仓库上传)
问题分析
你的方案核心问题在于错误选择了envoy_on_request阶段处理请求体:
envoy_on_request是请求头处理完成后、请求体开始传输前的阶段,当你在这个阶段遍历bodyChunks()时,Envoy已经开始将请求头转发到上游服务,此时无法再调用respond()中断请求(因为响应头已无法发送给客户端),这就是日志中respond() cannot be called if headers have been continued错误的原因。- 同时该阶段遍历
bodyChunks()不会阻止请求体被转发到上游,所以docker push仍能推送超大层。
正确解决方案:使用envoy_on_data阶段流式处理
要在不缓冲整个请求体的前提下限制大小,必须在**envoy_on_data**阶段处理——这个阶段会在每个请求体数据块到达时触发,且可以在此时中断请求并返回错误响应。
修改后的Lua脚本
local max_bytes = 10 * 1024 * 1024 -- 测试用10MB,实际可设为>10GB function envoy_on_data(request_handle, data, end_stream) -- 从stream元数据中读取累计字节数,初始为0 local total = request_handle:streamInfo():dynamicMetadata():get("lua", "total_bytes") or 0 total = total + #data -- 更新累计值到stream元数据,保持请求上下文状态 request_handle:streamInfo():dynamicMetadata():set("lua", "total_bytes", total) -- 检查是否超过限制,触发则返回错误 if total > max_bytes then request_handle:respond( { [":status"] = "413", ["content-type"] = "application/json" }, '{"errors":[{"code":"TOOBIG","message":"Layer exceeds maximum allowed size"}]}' ) return end -- 放行当前数据块,继续处理后续请求体 request_handle:continue(data, end_stream) end
配置修正要点
- 过滤器插入位置:确保Lua过滤器插入到
router过滤器之前(只有在请求转发到上游前拦截,才能生效),修正后的Patch配置示例:
- applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: envoy.filters.network.http_connection_manager subFilter: name: envoy.filters.http.router # 插入到router过滤器之前 patch: operation: INSERT_BEFORE value: name: envoy.filters.http.lua typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua default_source_code: inline_string: | local max_bytes = 10 * 1024 * 1024 -- 测试用10MB,实际可设为>10GB function envoy_on_data(request_handle, data, end_stream) local total = request_handle:streamInfo():dynamicMetadata():get("lua", "total_bytes") or 0 total = total + #data request_handle:streamInfo():dynamicMetadata():set("lua", "total_bytes", total) if total > max_bytes then request_handle:respond( { [":status"] = "413", ["content-type"] = "application/json" }, '{"errors":[{"code":"TOOBIG","message":"Layer exceeds maximum allowed size"}]}' ) return end request_handle:continue(data, end_stream) end
- Docker Registry适配:Docker镜像上传的请求多为分块传输,
envoy_on_data会逐块累计大小,一旦超过限制立即中断请求,完全符合流式处理需求,不会缓冲整个大文件。
关键原理说明
envoy_on_data阶段特性:每次请求体数据块到达时触发,data为当前块内容,end_stream标记是否为最后一块。- Stream元数据存储状态:用
request_handle:streamInfo():dynamicMetadata()存储累计字节数,保证每个请求的上下文独立,不会互相干扰。 - 即时中断机制:当累计大小超限,调用
respond()返回413错误,Envoy会立即停止接收后续请求体,将错误响应返回客户端并终止上游连接。
内容的提问来源于stack exchange,提问作者notbob
相关产品推荐
相关产品推荐

