ngx_http_range_header_filter

📅 2026/7/12 18:19:37
ngx_http_range_header_filter
1 定义ngx_http_range_header_filter 函数 定义在 src/http/modules/ngx_http_range_filter_module.cstaticngx_int_tngx_http_range_header_filter(ngx_http_request_t*r){time_tif_range_time;ngx_str_t*if_range,*etag;ngx_uint_tranges;ngx_http_core_loc_conf_t*clcf;ngx_http_range_filter_ctx_t*ctx;if(r-http_versionNGX_HTTP_VERSION_10||r-headers_out.status!NGX_HTTP_OK||(r!r-main!r-subrequest_ranges)||r-headers_out.content_length_n-1||!r-allow_ranges){returnngx_http_next_header_filter(r);}clcfngx_http_get_module_loc_conf(r,ngx_http_core_module);if(clcf-max_ranges0){returnngx_http_next_header_filter(r);}if(r-headers_in.rangeNULL||r-headers_in.range-value.len7||ngx_strncasecmp(r-headers_in.range-value.data,(u_char*)bytes,6)!0){gotonext_filter;}if(r-headers_in.if_range){if_ranger-headers_in.if_range-value;if(if_range-len2if_range-data[if_range-len-1]){if(r-headers_out.etagNULL){gotonext_filter;}etagr-headers_out.etag-value;ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r-connection-log,0,http ir:%V etag:%V,if_range,etag);if(if_range-len!etag-len||ngx_strncmp(if_range-data,etag-data,etag-len)!0){gotonext_filter;}gotoparse;}if(r-headers_out.last_modified_time(time_t)-1){gotonext_filter;}if_range_timengx_parse_http_time(if_range-data,if_range-len);ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r-connection-log,0,http ir:%T lm:%T,if_range_time,r-headers_out.last_modified_time);if(if_range_time!r-headers_out.last_modified_time){gotonext_filter;}}parse:ctxngx_pcalloc(r-pool,sizeof(ngx_http_range_filter_ctx_t));if(ctxNULL){returnNGX_ERROR;}ctx-offsetr-headers_out.content_offset;rangesr-single_range?1:clcf-max_ranges;switch(ngx_http_range_parse(r,ctx,ranges)){caseNGX_OK:ngx_http_set_ctx(r,ctx,ngx_http_range_body_filter_module);r-headers_out.statusNGX_HTTP_PARTIAL_CONTENT;r-headers_out.status_line.len0;if(ctx-ranges.nelts1){returnngx_http_range_singlepart_header(r,ctx);}returnngx_http_range_multipart_header(r,ctx);caseNGX_HTTP_RANGE_NOT_SATISFIABLE:returnngx_http_range_not_satisfiable(r);caseNGX_ERROR:returnNGX_ERROR;default:/* NGX_DECLINED */break;}next_filter:r-headers_out.accept_rangesngx_list_push(r-headers_out.headers);if(r-headers_out.accept_rangesNULL){returnNGX_ERROR;}r-headers_out.accept_ranges-hash1;r-headers_out.accept_ranges-nextNULL;ngx_str_set(r-headers_out.accept_ranges-key,Accept-Ranges);ngx_str_set(r-headers_out.accept_ranges-value,bytes);returnngx_http_next_header_filter(r);}2 目的ngx_http_range_header_filter是 Nginx 实现 HTTP 范围请求的核心头部过滤器。它的设计目的和必要性直接源于 HTTP/1.1 协议中定义的部分内容传输机制RFC 7233ngx_http_range_header_filter的设计目的是在响应头确定之后、发送之前统一处理 HTTP 范围请求将符合条件的请求转为 206 Partial Content 响应或为正常响应添加 Accept-Ranges 声明从而以一致、高效的方式实现部分内容传输。这个函数在响应头发送之前执行承担两个关键职责处理客户端发来的 Range 请求头如果当前响应满足条件它会解析请求中的 Range 头部并将正常响应200 OK改造为范围响应206 Partial Content然后交给后续过滤器发送部分内容。宣告服务器对范围请求的支持如果当前请求没有 Range 头部或条件不满足函数会向响应头中添加 Accept-Ranges: bytes告诉客户端该资源支持范围请求以便客户端在未来发起点播或断点续传。概括起来这个函数是一个集中的范围请求决策和执行点它保证了无论响应由哪个模块生成静态文件、反向代理、FastCGI 等范围请求的处理逻辑都是一致且正确的。3 详解1 函数签名staticngx_int_tngx_http_range_header_filter(ngx_http_request_t*r)1 函数名ngx_http_range_header_filter函数名由下划线分隔的多个部分组成遵循 Nginx 的命名惯例range指明该过滤器的功能与 HTTP 范围请求 相关。它负责处理请求头中的 Range 字段并根据配置决定是否将响应转变为部分内容206 Partial Content或添加 Accept-Ranges 头。header表明它是一个头部过滤器在响应头发送之前被调用负责读取请求头、修改响应头。filter表明它是一个过滤器是 Nginx 过滤器链中的一环。因此ngx_http_range_header_filter的完整含义是HTTP 模块中负责处理范围请求的头部过滤器函数。该函数对应的模块是ngx_http_range_filter_module它实现了对 Range 和 If-Range 请求头的解析以及多范围响应、条件范围响应的构造。2 逻辑流程1 条件检查 过滤不能进行 range 处理的请求 2 配置检查 3 range 请求头检查 4 检查是否存在 If-Range 请求头 5 解析 Range 头部 6 添加 Accept-Ranges 头部{time_tif_range_time;ngx_str_t*if_range,*etag;ngx_uint_tranges;ngx_http_core_loc_conf_t*clcf;ngx_http_range_filter_ctx_t*ctx;局部变量声明1 条件检查if(r-http_versionNGX_HTTP_VERSION_10||r-headers_out.status!NGX_HTTP_OK||(r!r-main!r-subrequest_ranges)||r-headers_out.content_length_n-1||!r-allow_ranges){returnngx_http_next_header_filter(r);}条件检查如果任意条件成立函数直接调用下一个头部过滤器并返回不进行任何范围处理。r-http_version NGX_HTTP_VERSION_10HTTP 版本低于 1.0。范围请求是 HTTP/1.1 特性但一些 1.0 客户端也支持。若版本过低则跳过。r-headers_out.status ! NGX_HTTP_OK响应状态码不是 200 OK。范围请求只能针对完整的成功响应其他状态如 404、301 等无意义。(r ! r-main !r-subrequest_ranges)当前请求不是主请求且没有设置subrequest_ranges标志。范围请求通常只在主请求上处理除非子请求明确允许范围支持。r-headers_out.content_length_n -1内容长度未知-1 表示未设置如分块编码或动态生成。范围请求需要知道完整长度才能正确切片未知长度则无法处理。!r-allow_rangesallow_ranges 标志为假。该标志可由其他模块如 Gzip 压缩后设置表示禁止范围请求。若以上任一条件成立直接调用ngx_http_next_header_filter(r)放行。2 配置检查clcfngx_http_get_module_loc_conf(r,ngx_http_core_module);获取核心模块的location配置if(clcf-max_ranges0){returnngx_http_next_header_filter(r);}检查max_ranges是否为 0clcf-max_ranges是配置指令max_ranges的值。如果为 0表示完全禁用范围请求。此时直接跳过传递给下一个过滤器。3 range 请求头检查if(r-headers_in.rangeNULL||r-headers_in.range-value.len7||ngx_strncasecmp(r-headers_in.range-value.data,(u_char*)bytes,6)!0){gotonext_filter;}检查Range请求头是否存在且格式正确r-headers_in.range解析后的 Range 请求头条目。如果为 NULL表示客户端没有发送 Range 头部。r-headers_in.range-value.len 7Range 头部值的长度小于 7。最小的合法值是 bytes0长度为 7所以小于 7 则无效。ngx_strncasecmp(..., bytes, 6) ! 0不区分大小写比较前 6 个字节是否为bytes。如果不是说明不是字节范围请求可能是其他单位Nginx 不支持。如果以上任一条件成立说明客户端没有携带有效范围请求头则使用goto next_filter跳转到后面的标签统一添加Accept-Ranges头并放行。4 检查是否存在 If-Range 请求头if(r-headers_in.if_range){if_ranger-headers_in.if_range-value;if(if_range-len2if_range-data[if_range-len-1]){if(r-headers_out.etagNULL){gotonext_filter;}etagr-headers_out.etag-value;ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r-connection-log,0,http ir:%V etag:%V,if_range,etag);if(if_range-len!etag-len||ngx_strncmp(if_range-data,etag-data,etag-len)!0){gotonext_filter;}gotoparse;}if(r-headers_out.last_modified_time(time_t)-1){gotonext_filter;}if_range_timengx_parse_http_time(if_range-data,if_range-len);ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r-connection-log,0,http ir:%T lm:%T,if_range_time,r-headers_out.last_modified_time);if(if_range_time!r-headers_out.last_modified_time){gotonext_filter;}}5 解析 Range 头部parse:ctxngx_pcalloc(r-pool,sizeof(ngx_http_range_filter_ctx_t));if(ctxNULL){returnNGX_ERROR;}ctx-offsetr-headers_out.content_offset;rangesr-single_range?1:clcf-max_ranges;switch(ngx_http_range_parse(r,ctx,ranges)){caseNGX_OK:ngx_http_set_ctx(r,ctx,ngx_http_range_body_filter_module);r-headers_out.statusNGX_HTTP_PARTIAL_CONTENT;r-headers_out.status_line.len0;if(ctx-ranges.nelts1){returnngx_http_range_singlepart_header(r,ctx);}returnngx_http_range_multipart_header(r,ctx);caseNGX_HTTP_RANGE_NOT_SATISFIABLE:returnngx_http_range_not_satisfiable(r);caseNGX_ERROR:returnNGX_ERROR;default:/* NGX_DECLINED */break;}6 添加 Accept-Ranges 头部next_filter:r-headers_out.accept_rangesngx_list_push(r-headers_out.headers);if(r-headers_out.accept_rangesNULL){returnNGX_ERROR;}r-headers_out.accept_ranges-hash1;r-headers_out.accept_ranges-nextNULL;ngx_str_set(r-headers_out.accept_ranges-key,Accept-Ranges);ngx_str_set(r-headers_out.accept_ranges-value,bytes);returnngx_http_next_header_filter(r);}当不需要进行范围处理时跳转到这里统一添加 Accept-Ranges 头部向响应头链表添加 Accept-Ranges 条目设置 Accept-Ranges 头部内容置键为 “Accept-Ranges”值为 “bytes”。这告诉客户端服务器支持字节范围请求继续下一个过滤器