Nginx的header_filter模块

1.相关指令

header_filter模块主要有以下几个指令

1
2
3
Syntax:	add_header name value [always];
Default: —

1
2
3
4
5
Context:	http, server, location, if in location
Syntax: add_trailer name value [always];
Default: —
Context: http, server, location, if in location
This directive appeared in version 1.13.2.
1
2
3
4
5
6
Syntax:	expires [modified] time;
expires epoch | max | off;
Default:
expires off;
Context: http, server, location, if in location

配置示例

1
2
3
4
5
6
7
8
9
expires    24h;
expires modified +24h;
expires @24h;
expires 0;
expires -1;
expires epoch;
expires $expires;
add_header Cache-Control private;

2. 源码解析

2.1 配置解析函数

  1. 首先来看下add_header指令解析函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    static char *
    ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
    ngx_http_headers_conf_t *hcf = conf;

    ngx_str_t *value;
    ngx_uint_t i;
    ngx_array_t **headers;
    ngx_http_header_val_t *hv;
    ngx_http_set_header_t *set;
    ngx_http_compile_complex_value_t ccv;

    value = cf->args->elts;

    // 取到数组指针
    headers = (ngx_array_t **) ((char *) hcf + cmd->offset);

    if (*headers == NULL) {
    *headers = ngx_array_create(cf->pool, 1,
    sizeof(ngx_http_header_val_t));
    if (*headers == NULL) {
    return NGX_CONF_ERROR;
    }
    }

    //从数组中push一个元素
    hv = ngx_array_push(*headers);
    if (hv == NULL) {
    return NGX_CONF_ERROR;
    }

    // 取配置header的key
    hv->key = value[1];
    hv->handler = NULL;
    hv->offset = 0;
    hv->always = 0;

    if (headers == &hcf->headers) {
    hv->handler = ngx_http_add_header;

    set = ngx_http_set_headers;
    for (i = 0; set[i].name.len; i++) {
    if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
    continue;
    }

    hv->offset = set[i].offset;
    hv->handler = set[i].handler;

    break;
    }
    }

    if (value[2].len == 0) {
    ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));

    } else {
    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;
    ccv.value = &value[2];
    ccv.complex_value = &hv->value;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
    return NGX_CONF_ERROR;
    }
    }

    if (cf->args->nelts == 3) {
    return NGX_CONF_OK;
    }

    if (ngx_strcmp(value[3].data, "always") != 0) {
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    "invalid parameter \"%V\"", &value[3]);
    return NGX_CONF_ERROR;
    }

    hv->always = 1;

    return NGX_CONF_OK;
    }

  1. 接下来看下expires指令解析函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    static char *
    ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
    ngx_http_headers_conf_t *hcf = conf;

    char *err;
    ngx_str_t *value;
    ngx_int_t rc;
    ngx_uint_t n;
    ngx_http_complex_value_t cv;
    ngx_http_compile_complex_value_t ccv;

    // 判断是否已经设置过该指令
    if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
    return "is duplicate";
    }

    value = cf->args->elts;

    //判断指令后的参数
    if (cf->args->nelts == 2) {

    hcf->expires = NGX_HTTP_EXPIRES_ACCESS;

    n = 1;

    } else { /* cf->args->nelts == 3 */

    if (ngx_strcmp(value[1].data, "modified") != 0) {
    return "invalid value";
    }

    hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;

    n = 2;
    }

    // 准备变量
    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;
    ccv.value = &value[n];
    ccv.complex_value = &cv;

    //编译变量
    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
    return NGX_CONF_ERROR;
    }

    if (cv.lengths != NULL) {

    hcf->expires_value = ngx_palloc(cf->pool,
    sizeof(ngx_http_complex_value_t));
    if (hcf->expires_value == NULL) {
    return NGX_CONF_ERROR;
    }

    *hcf->expires_value = cv;

    return NGX_CONF_OK;
    }

    rc = ngx_http_parse_expires(&value[n], &hcf->expires, &hcf->expires_time,
    &err);
    if (rc != NGX_OK) {
    return err;
    }

    return NGX_CONF_OK;
    }

2.2 filter函数

2.2.1 headers_filter函数

首先看下ngx_http_headers_filter函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
static ngx_int_t
ngx_http_headers_filter(ngx_http_request_t *r)
{
ngx_str_t value;
ngx_uint_t i, safe_status;
ngx_http_header_val_t *h;
ngx_http_headers_conf_t *conf;

// 子请求不做处理,直接返回
if (r != r->main) {
return ngx_http_next_header_filter(r);
}

// 获取location级别配置
conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);

if (conf->expires == NGX_HTTP_EXPIRES_OFF
&& conf->headers == NULL
&& conf->trailers == NULL)
{
return ngx_http_next_header_filter(r);
}

switch (r->headers_out.status) {

case NGX_HTTP_OK:
case NGX_HTTP_CREATED:
case NGX_HTTP_NO_CONTENT:
case NGX_HTTP_PARTIAL_CONTENT:
case NGX_HTTP_MOVED_PERMANENTLY:
case NGX_HTTP_MOVED_TEMPORARILY:
case NGX_HTTP_SEE_OTHER:
case NGX_HTTP_NOT_MODIFIED:
case NGX_HTTP_TEMPORARY_REDIRECT:
case NGX_HTTP_PERMANENT_REDIRECT:
safe_status = 1;
break;

default:
safe_status = 0;
break;
}

if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {
if (ngx_http_set_expires(r, conf) != NGX_OK) {
return NGX_ERROR;
}
}

if (conf->headers) {
h = conf->headers->elts;
for (i = 0; i < conf->headers->nelts; i++) {

if (!safe_status && !h[i].always) {
continue;
}

if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
return NGX_ERROR;
}

if (h[i].handler(r, &h[i], &value) != NGX_OK) {
return NGX_ERROR;
}
}
}

if (conf->trailers) {
h = conf->trailers->elts;
for (i = 0; i < conf->trailers->nelts; i++) {

if (!safe_status && !h[i].always) {
continue;
}

r->expect_trailers = 1;
break;
}
}

return ngx_http_next_header_filter(r);
}

2.2.2 ngx_http_trailers_filter函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
static ngx_int_t
ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_str_t value;
ngx_uint_t i, safe_status;
ngx_chain_t *cl;
ngx_table_elt_t *t;
ngx_http_header_val_t *h;
ngx_http_headers_conf_t *conf;

//获取location级别配置
conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);

// 如果满足如下条件,则跳过本filter
if (in == NULL
|| conf->trailers == NULL
|| !r->expect_trailers
|| r->header_only)
{
return ngx_http_next_body_filter(r, in);
}

// 找到最后一个buf
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_buf) {
break;
}
}

// 如果cl为null,则跳过本filter
if (cl == NULL) {
return ngx_http_next_body_filter(r, in);
}

// 根据响应码做选择
switch (r->headers_out.status) {

case NGX_HTTP_OK:
case NGX_HTTP_CREATED:
case NGX_HTTP_NO_CONTENT:
case NGX_HTTP_PARTIAL_CONTENT:
case NGX_HTTP_MOVED_PERMANENTLY:
case NGX_HTTP_MOVED_TEMPORARILY:
case NGX_HTTP_SEE_OTHER:
case NGX_HTTP_NOT_MODIFIED:
case NGX_HTTP_TEMPORARY_REDIRECT:
case NGX_HTTP_PERMANENT_REDIRECT:
safe_status = 1;
break;

default:
safe_status = 0;
break;
}

// 遍历配置的所有trailer
h = conf->trailers->elts;
for (i = 0; i < conf->trailers->nelts; i++) {

if (!safe_status && !h[i].always) {
continue;
}

if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
return NGX_ERROR;
}

if (value.len) {
t = ngx_list_push(&r->headers_out.trailers);
if (t == NULL) {
return NGX_ERROR;
}

t->key = h[i].key;
t->value = value;
t->hash = 1;
}
}

return ngx_http_next_body_filter(r, in);
}