1 定义ngx_http_chunked_header_filter 函数 定义在 src/http/modules/ngx_http_chunked_filter_module.cstaticngx_int_tngx_http_chunked_header_filter(ngx_http_request_t*r){ngx_http_core_loc_conf_t*clcf;ngx_http_chunked_filter_ctx_t*ctx;if(r-headers_out.statusNGX_HTTP_NOT_MODIFIED||r-headers_out.statusNGX_HTTP_NO_CONTENT||r-headers_out.statusNGX_HTTP_OK||r!r-main||r-methodNGX_HTTP_HEAD){returnngx_http_next_header_filter(r);}if(r-headers_out.content_length_n-1||r-expect_trailers){clcfngx_http_get_module_loc_conf(r,ngx_http_core_module);if(r-http_versionNGX_HTTP_VERSION_11clcf-chunked_transfer_encoding){if(r-expect_trailers){ngx_http_clear_content_length(r);}r-chunked1;ctxngx_pcalloc(r-pool,sizeof(ngx_http_chunked_filter_ctx_t));if(ctxNULL){returnNGX_ERROR;}ngx_http_set_ctx(r,ctx,ngx_http_chunked_filter_module);}elseif(r-headers_out.content_length_n-1){r-keepalive0;}}returnngx_http_next_header_filter(r);}2 目的ngx_http_chunked_header_filter是Nginx 中负责启用和管理 HTTP 分块传输编码 的头部过滤器。它的设计目的和必要性直接源于 HTTP/1.1 协议中定义的传输编码机制。这个函数在响应头发送之前执行其核心任务是当响应体的长度无法预先确定时如果协议版本和配置允许就启用分块传输编码chunked并准备好后续体过滤器和连接处理的必要状态如果无法启用分块且长度未知则强制关闭连接以保证协议正确3 详解1 函数签名staticngx_int_tngx_http_chunked_header_filter(ngx_http_request_t*r)1 函数名ngx_http_chunked_header_filter函数名由下划线分隔的多个部分组成遵循 Nginx 的命名惯例chunked指明该过滤器的功能与 HTTP 分块传输编码 相关。它负责决定是否启用Transfer-Encoding: chunked并设置相关状态。header表明它是一个头部过滤器在响应头发送之前被调用负责读取响应状态和头部并修改连接和请求标志。filter表明它是一个过滤器是 Nginx 过滤器链中的一环。因此ngx_http_chunked_header_filter的完整含义是HTTP 模块中负责处理分块传输编码的头部过滤器函数。该函数对应的模块是ngx_http_chunked_filter_module它实现了响应体的分块编码发送。2 逻辑流程1 排除不需要响应体的响应 如果是 304、204、1xx 状态码、子请求或 HEAD 方法直接放行。 2 判断是否有分块需求 当内容长度未知-1或需要发送尾部头时进入处理。 2-1 检查是否允许分块 如果 HTTP 版本 1.1 且配置未禁用分块则启用分块 如果有尾部头清除 Content-Length避免共存。 设置 r-chunked 1。 分配分块上下文并绑定到请求供体过滤器使用。 2-2 不允许分块时 如果内容长度未知且不能分块设置 r-keepalive 0 强制关闭连接防止客户端挂起。 3 最终始终调用下一个头部过滤器继续响应头处理。 这个设计确保了动态内容、尾部头与 HTTP 协议的正确兼容同时最大限度保持连接复用能力。{ngx_http_core_loc_conf_t*clcf;ngx_http_chunked_filter_ctx_t*ctx;局部变量声明1 排除不需要响应体的响应if(r-headers_out.statusNGX_HTTP_NOT_MODIFIED||r-headers_out.statusNGX_HTTP_NO_CONTENT||r-headers_out.statusNGX_HTTP_OK||r!r-main||r-methodNGX_HTTP_HEAD){returnngx_http_next_header_filter(r);}快速排除不需要分块的情况如果任意一个条件为真则当前响应不需要使用分块传输编码因为要么没有响应体要么不能使用分块。此时直接调用ngx_http_next_header_filter(r)将请求传递给下一个头部过滤器并返回其返回值。r-headers_out.status NGX_HTTP_NOT_MODIFIED状态码 304 Not Modified。该响应没有响应体不需要分块。r-headers_out.status NGX_HTTP_NO_CONTENT状态码 204 No Content。同样没有响应体不需要分块。r-headers_out.status NGX_HTTP_OK状态码小于 200即 1xx 信息性响应。这些响应通常不含响应体或不允许使用分块也不处理。r ! r-main当前请求不是主请求而是子请求。子请求的响应不会直接发送给客户端因此分块传输只应在主请求上启用子请求跳过。r-method NGX_HTTP_HEADHTTP 方法是 HEAD。HEAD 请求只要求返回响应头没有响应体无需分块。这些检查保证了后续逻辑只针对真正可能需要分块的主请求、正常响应状态 200 且不是 204/304以及非 HEAD 方法的请求。2 判断是否有分块需求if(r-headers_out.content_length_n-1||r-expect_trailers){判断是否需要分块或处理未知长度只有当 两个条件至少满足其一时才进入分块相关的处理分支r-headers_out.content_length_n -1响应体的内容长度未知。这意味着 Nginx 无法预先知道整个响应体的大小这种情况下如果不使用分块编码HTTP/1.1 连接就无法安全复用无法通过关闭连接来表示内容结束只能关闭连接。分块编码是解决该问题的标准方法。r-expect_trailers这个标志表示响应计划在响应体结束后附加一些尾部头Trailers通常由add_trailer指令引发。HTTP 协议规定Trailer 头只能在分块传输编码中使用。因此必须启用分块。如果不满足这两个条件即内容长度已知且没有尾部头则不进入该分支直接跳到最后的return ngx_http_next_header_filter(r)放行。2-1 检查是否允许分块clcfngx_http_get_module_loc_conf(r,ngx_http_core_module);获取核心模块的 location 配置if(r-http_versionNGX_HTTP_VERSION_11clcf-chunked_transfer_encoding){检查是否允许启用分块传输编码条件为真则表示可以启用分块传输。两个条件必须同时满足r-http_version NGX_HTTP_VERSION_11请求的 HTTP 版本至少为 1.1。HTTP/1.0 不支持分块传输编码如果错误地使用了老客户端无法解析。因此必须版本要求。clcf-chunked_transfer_encoding核心配置中的chunked_transfer_encoding指令为开启状态。该指令默认为on但管理员可以通过chunked_transfer_encoding off;显式禁用分块编码。如果被禁用即使条件满足也不能使用分块。if(r-expect_trailers){ngx_http_clear_content_length(r);}如果有尾部头清除 Content-Lengthr-expect_trailers如果为真表示需要在响应体后发送尾部头。由于此时即将使用分块编码而分块编码和Content-Length头不能同时存在因此必须显式调用ngx_http_clear_content_length(r)清除可能已经设置的Content-Length响应头确保协议正确。r-chunked1;设置 chunked 标志ctxngx_pcalloc(r-pool,sizeof(ngx_http_chunked_filter_ctx_t));if(ctxNULL){returnNGX_ERROR;}ngx_http_set_ctx(r,ctx,ngx_http_chunked_filter_module);分配分块传输上下文将上下文绑定到当前请求2-2 不允许分块时}elseif(r-headers_out.content_length_n-1){r-keepalive0;}}如果不能启用分块传输要么 HTTP 版本低于 1.1要么配置禁用了分块则检查另一种情况r-headers_out.content_length_n -1。无法分块且长度未知时禁用keepaliver-keepalive 0将请求的keepalive标志设为 0。这告诉 Nginx 在处理完本次请求后不要尝试复用该 TCP 连接即关闭连接。因为 HTTP/1.0 或不支持分块时对于长度未知的响应客户端只能通过连接关闭来判断响应体结束。如果不关闭连接客户端会一直等待造成挂起。因此强制关闭连接是必须的。注意如果内容长度已知content_length_n ! -1且只是因为有expect_trailers才进入外层 if但在内层又不允许分块这种情况不会走到 else if因为内层 if 只检查了内容长度未知的情况。实际上expect_trailers 在不能分块时没有特殊处理尾部头将被忽略响应可能不带 Trailer 发送但配置本身会要求分块管理员应保证配合正确。3 下一个头部过滤器returnngx_http_next_header_filter(r);}传递请求给下一个过滤器