ngx_http_gzip_header_filter

1 定义

ngx_http_gzip_header_filter 函数 定义在 src/http/modules/ngx_http_gzip_filter_module.c
staticngx_int_tngx_http_gzip_header_filter(ngx_http_request_t*r){ngx_table_elt_t*h;ngx_http_gzip_ctx_t*ctx;ngx_http_gzip_conf_t*conf;conf=ngx_http_get_module_loc_conf(r,ngx_http_gzip_filter_module);if(!conf->enable||(r->headers_out.status!=NGX_HTTP_OK&&r->headers_out.status!=NGX_HTTP_FORBIDDEN&&r->headers_out.status!=NGX_HTTP_NOT_FOUND)||(r->headers_out.content_encoding&&r->headers_out.content_encoding->value.len)||(r->headers_out.content_length_n!=-1&&r->headers_out.content_length_n<conf->min_length)||ngx_http_test_content_type(r,&conf->types)==NULL||r->header_only){returnngx_http_next_header_filter(r);}r->gzip_vary=1;#if(NGX_HTTP_DEGRADATION){ngx_http_core_loc_conf_t*clcf;clcf=ngx_http_get_module_loc_conf(r,ngx_http_core_module);if(clcf->gzip_disable_degradation&&ngx_http_degraded(r)){returnngx_http_next_header_filter(r);}}#endifif(!r->gzip_tested){if(ngx_http_gzip_ok(r)!=NGX_OK){returnngx_http_next_header_filter(r);}}elseif(!r->gzip_ok){returnngx_http_next_header_filter(r);}ctx=ngx_pcalloc(r->pool,sizeof(ngx_http_gzip_ctx_t));if(ctx==NULL){returnNGX_ERROR;}ngx_http_set_ctx(r,ctx,ngx_http_gzip_filter_module);ctx->request=r;ctx->buffering=(conf->postpone_gzipping!=0);ngx_http_gzip_filter_memory(r,ctx);h=ngx_list_push(&r->headers_out.headers);if(h==NULL){returnNGX_ERROR;}h->hash=1;h->next=NULL;ngx_str_set(&h->key,"Content-Encoding");ngx_str_set(&h->value,"gzip");r->headers_out.content_encoding=h;r->main_filter_need_in_memory=1;ngx_http_clear_content_length(r);ngx_http_clear_accept_ranges(r);ngx_http_weak_etag(r);returnngx_http_next_header_filter(r);}

2 目的

ngx_http_gzip_header_filter是 Nginx Gzip 压缩模块的头部过滤器。
它的设计目的和必要性紧密围绕 HTTP 内容压缩这一核心功能。


这个函数的核心任务是:
在响应头发送之前,决定是否对当前响应启用 Gzip 压缩。
如果启用,则做好所有的初始化和响应头修改工作,
为后续的响应体压缩(由ngx_http_gzip_body_filter完成)
准备好运行环境。


3 详解

1 函数签名

staticngx_int_tngx_http_gzip_header_filter(ngx_http_request_t*r)

1 返回值类型ngx_int_t

返回一个整数状态码,表示执行结果(NGX_OK 成功,NGX_ERROR 出错)。


2ngx_http_gzip_header_filter

函数名,表示 HTTP Gzip 模块的头部过滤器。


3ngx_http_request_t *r

参数r是指向当前 HTTP 请求结构体的指针,包含请求的所有信息。


2 逻辑流程

1 条件过滤 筛选掉不需要 gzip 压缩的请求 2 处理需要 gzip 压缩的请求

{ngx_table_elt_t*h;ngx_http_gzip_ctx_t*ctx;ngx_http_gzip_conf_t*conf;

局部变量声明


conf=ngx_http_get_module_loc_conf(r,ngx_http_gzip_filter_module);

获取 location 配置


1 条件过滤

if(!conf->enable||(r->headers_out.status!=NGX_HTTP_OK&&r->headers_out.status!=NGX_HTTP_FORBIDDEN&&r->headers_out.status!=NGX_HTTP_NOT_FOUND)||(r->headers_out.content_encoding&&r->headers_out.content_encoding->value.len)||(r->headers_out.content_length_n!=-1&&r->headers_out.content_length_n<conf->min_length)||ngx_http_test_content_type(r,&conf->types)==NULL||r->header_only){returnngx_http_next_header_filter(r);}

快速退出条件检查

只要任何一个条件为真,就不进行Gzip压缩,
直接调用下一个头部过滤器并返回。各条件详解:

!conf->enable:配置中gzip未开启(值为 off 或 0)。

(r->headers_out.status != NGX_HTTP_OK && ...)
响应状态码不是 200、403、404 之一。
Gzip只对少数成功或部分错误状态的响应体进行压缩,
避免浪费资源于无意义的响应(如 301 重定向没有响应体)。

(r->headers_out.content_encoding && r->headers_out.content_encoding->value.len)
响应头中已经存在Content-Encoding(例如上游已经压缩过),
不能再压缩,否则会损坏数据。

(r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length)
如果 Content-Length 已知(不是 -1)且其值小于 min_length 配置(默认 20 字节),
压缩没有意义,还可能增大体积,跳过。

ngx_http_test_content_type(r, &conf->types) == NULL:响应的 Content-Type 不在 gzip_types 指定的类型列表中(默认只有 text/html)。

r->header_only
这是一个 HEAD 请求,只要求返回响应头,没有响应体,无需压缩。

这些检查在头部过滤器执行,此时响应状态码、内容长度、内容类型等均已确定,
因此能够准确判断。任何条件不满足,都会导致函数直接放行,开销极低。


2 处理需要 gzip 压缩的请求

r->gzip_vary=1;#if(NGX_HTTP_DEGRADATION){ngx_http_core_loc_conf_t*clcf;clcf=ngx_http_get_module_loc_conf(r,ngx_http_core_module);if(clcf->gzip_disable_degradation&&ngx_http_degraded(r)){returnngx_http_next_header_filter(r);}}#endifif(!r->gzip_tested){if(ngx_http_gzip_ok(r)!=NGX_OK){returnngx_http_next_header_filter(r);}}elseif(!r->gzip_ok){returnngx_http_next_header_filter(r);}ctx=ngx_pcalloc(r->pool,sizeof(ngx_http_gzip_ctx_t));if(ctx==NULL){returnNGX_ERROR;}ngx_http_set_ctx(r,ctx,ngx_http_gzip_filter_module);ctx->request=r;ctx->buffering=(conf->postpone_gzipping!=0);ngx_http_gzip_filter_memory(r,ctx);h=ngx_list_push(&r->headers_out.headers);if(h==NULL){returnNGX_ERROR;}h->hash=1;h->next=NULL;ngx_str_set(&h->key,"Content-Encoding");ngx_str_set(&h->value,"gzip");r->headers_out.content_encoding=h;r->main_filter_need_in_memory=1;ngx_http_clear_content_length(r);ngx_http_clear_accept_ranges(r);ngx_http_weak_etag(r);returnngx_http_next_header_filter(r);}