You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

配置修正要点

  1. 过滤器插入位置:确保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
  1. Docker Registry适配:Docker镜像上传的请求多为分块传输,envoy_on_data会逐块累计大小,一旦超过限制立即中断请求,完全符合流式处理需求,不会缓冲整个大文件。

关键原理说明

  • envoy_on_data阶段特性:每次请求体数据块到达时触发,data为当前块内容,end_stream标记是否为最后一块。
  • Stream元数据存储状态:用request_handle:streamInfo():dynamicMetadata()存储累计字节数,保证每个请求的上下文独立,不会互相干扰。
  • 即时中断机制:当累计大小超限,调用respond()返回413错误,Envoy会立即停止接收后续请求体,将错误响应返回客户端并终止上游连接。

内容的提问来源于stack exchange,提问作者notbob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 06:07:43