NGX_HTTP_MODULE 配置解析

一 源起

ngx_http_module 是 HTTP 类型模块在核心模块的代理,ngx_http_module 的指令 http 的解析(ngx_http_block 函数)为 HTTP 类型模块解析的入口。

二 ngx_http_block 分析

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
ngx_uint_t mi, m, s;
ngx_conf_t pcf;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_core_main_conf_t *cmcf;

if (*(ngx_http_conf_ctx_t **) conf) {
return "is duplicate";
}

/* the main http context */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

// 参数 conf 为 ngx_http_module 的 ctx,可以参见配置指令解析
*(ngx_http_conf_ctx_t **) conf = ctx;


/* count the number of the http modules and set up their indices */
ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);


/* 所有 HTTP 模块的 main_conf */
ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

/* 所有模块的 srv_conf, 出现在 http{} 中的 srv_conf 配置项存储在此,后续合并使用 */
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

/* 所有模块的 loc_conf, 出现在 http{} 中的 loc_conf 配置项存储在此,后续合并使用 */
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = cf->cycle->modules[m]->ctx;
mi = cf->cycle->modules[m]->ctx_index;

if (module->create_main_conf) {
// ngx_http_core_module 会创建 servers 数组
ctx->main_conf[mi] = module->create_main_conf(cf);
}
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
}
if (module->create_loc_conf) {
ctx->loc_conf[mi] = module->create_loc_conf(cf);
}
}

pcf = *cf;
cf->ctx = ctx;

for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = cf->cycle->modules[m]->ctx;

if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}

/* parse inside the http{} block */
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
rv = ngx_conf_parse(cf, NULL);

if (rv != NGX_CONF_OK) {
goto failed;
}

/*
* init http{} main_conf's, merge the server{}s' srv_conf's
* and its location{}s' loc_conf's
*/

cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
cscfp = cmcf->servers.elts;

for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = cf->cycle->modules[m]->ctx;
mi = cf->cycle->modules[m]->ctx_index;

/* init http{} main_conf's */
if (module->init_main_conf) {
rv = module->init_main_conf(cf, ctx->main_conf[mi]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}

rv = ngx_http_merge_servers(cf, cmcf, module, mi);
if (rv != NGX_CONF_OK) {
goto failed;
}
}


/* create location trees */
for (s = 0; s < cmcf->servers.nelts; s++) {

clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];

if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}


if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}


for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = cf->cycle->modules[m]->ctx;

if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}

if (ngx_http_variables_init_vars(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}

/*
* http{}'s cf->ctx was needed while the configuration merging
* and in postconfiguration process
*/

*cf = pcf;


if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}


/* optimize the lists of ports, addresses and server names */
if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
return NGX_CONF_ERROR;
}

return NGX_CONF_OK;

failed:

*cf = pcf;

return rv;
}

三 server 指令解析函数 ngx_http_core_server

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

static char *
ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
char *rv;
void *mconf;
ngx_uint_t i;
ngx_conf_t pcf;
ngx_http_module_t *module;
struct sockaddr_in *sin;
ngx_http_conf_ctx_t *ctx, *http_ctx;
ngx_http_listen_opt_t lsopt;
ngx_http_core_srv_conf_t *cscf, **cscfp;
ngx_http_core_main_conf_t *cmcf;

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

http_ctx = cf->ctx;
ctx->main_conf = http_ctx->main_conf;

/* the server{}'s srv_conf */
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

/* the server{}'s loc_conf */
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}

// 对当前 server 块依次调用所有 HTTP 模块的 create_srv_conf,create_loc_conf
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}

module = cf->cycle->modules[i]->ctx;

if (module->create_srv_conf) {
mconf = module->create_srv_conf(cf);
ctx->srv_conf[cf->cycle->modules[i]->ctx_index] = mconf;
}

if (module->create_loc_conf) {
mconf = module->create_loc_conf(cf);
ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;
}
}


/* the server configuration context */
cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
cscf->ctx = ctx;

cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

// 将当前 server 块配置添加到动态数组中
cscfp = ngx_array_push(&cmcf->servers);
if (cscfp == NULL) {
return NGX_CONF_ERROR;
}
*cscfp = cscf;


/* parse inside server{} */

pcf = *cf;
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_SRV_CONF;

rv = ngx_conf_parse(cf, NULL);

*cf = pcf;

// 对为设置监听端口的 server 设置默认监听端口 80、8080
if (rv == NGX_CONF_OK && !cscf->listen) {
ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

sin = &lsopt.sockaddr.sockaddr_in;

sin->sin_family = AF_INET;
#if (NGX_WIN32)
sin->sin_port = htons(80);
#else
sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
#endif
sin->sin_addr.s_addr = INADDR_ANY;


(void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen, lsopt.addr, NGX_SOCKADDR_STRLEN, 1);

if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {
return NGX_CONF_ERROR;
}
}

return rv;
}


server 指令解析过程中会设置当前 server {} 块的 ngx_http_conf_ctx_tmain_confhttp {}main_conf,对于 srv_confloc_conf 会依次调用所有 HTTP 模块的 create_srv_confcreate_loc_conf 回调创建相应的配置结构,然后进入 server {} 块的解析。在 server {} 解析完毕后,会将当前 server {} 的配置 ngx_http_conf_ctx_t 加入 ngx_http_core_moduleservers 数组中。

四 location 指令解析函数 ngx_http_core_location

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
char *rv;
u_char *mod;
size_t len;
ngx_str_t *value, *name;
ngx_uint_t i;
ngx_conf_t save;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx, *pctx;
ngx_http_core_loc_conf_t *clcf, *pclcf;

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}

// cf 是在 ngx_http_core_server 中设置,cf 的值为当前 server {} 的 ngx_http_conf_ctx_t
// pctx meaning parent ctx
pctx = cf->ctx;
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_conf;

ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

// 依次对所有 http 模块调用 create_loc_conf
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}

module = cf->cycle->modules[i]->ctx;

if (module->create_loc_conf) {
ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = module->create_loc_conf(cf);
}
}

clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
clcf->loc_conf = ctx->loc_conf;

value = cf->args->elts;

...... // 忽略 location name 的处理,关注 main、srv、loc 配置的组织

pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

/* 嵌套 location 的校验规则 */
if (cf->cmd_type == NGX_HTTP_LOC_CONF) {
#if 0
clcf->prev_location = pclcf;
#endif

if (pclcf->exact_match) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" cannot be inside the exact location \"%V\"", &clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
}

if (pclcf->named) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" cannot be inside the named location \"%V\"",&clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
}

if (clcf->named) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "named location \"%V\" can be on the server level only", &clcf->name);
return NGX_CONF_ERROR;
}

len = pclcf->name.len;

#if (NGX_PCRE)
if (clcf->regex == NULL && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
#else
if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
#endif
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "location \"%V\" is outside location \"%V\"", &clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
}
}

// 将当前 location 添加到 ngx_http_core_module 模块父 location 的 loc_conf 配置中 locations 队列
if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

save = *cf;
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_LOC_CONF;

rv = ngx_conf_parse(cf, NULL);

*cf = save;

return rv;
}

在解析 server {} 内的配置项时会使用如下代码:

1
2
3
4
5
6
7
8
pcf = *cf;
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_SRV_CONF;

rv = ngx_conf_parse(cf, NULL);

*cf = pcf;

此时设置 cf 为当前 server {} 的配置。

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
ngx_int_t
ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations, ngx_http_core_loc_conf_t *clcf)
{
ngx_http_location_queue_t *lq;

if (*locations == NULL) {
*locations = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));

ngx_queue_init(*locations);
}

lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));
if (lq == NULL) {
return NGX_ERROR;
}

if (clcf->exact_match
#if (NGX_PCRE)
|| clcf->regex
#endif
|| clcf->named || clcf->noname)
{
lq->exact = clcf;
lq->inclusive = NULL;

} else {
lq->exact = NULL;
lq->inclusive = clcf;
}

lq->name = &clcf->name;
lq->file_name = cf->conf_file->file.name.data;
lq->line = cf->conf_file->line;

ngx_queue_init(&lq->list);

ngx_queue_insert_tail(*locations, &lq->queue);

return NGX_OK;
}

五 总结

http {}server {}location {} 的配置项是通过 ngx_http_conf_ctx_t 结构组织的,其定义如下:

1
2
3
4
5
6
typedef struct {
void **main_conf;
void **srv_conf;
void **loc_conf;
} ngx_http_conf_ctx_t;

http {} 级别的配置项由 ngx_http_module 模块管理,ngx_http_module 会持有 ngx_http_core_module,进而管理 server {}location {} 级别配置。

server {}location {} 两种级别的配置项都是通过 ngx_http_core_module 模块管理。

  • main_conf 是所有模块的 http{} 级配置项,由于只能有一个 http{} 存在他的组织是比较简单的,每个模块只需要有一个 main_confsrv_confloc_conf 只需要指向对应模块的 main_conf 即可。
  • 对于 srv_conf 的组织 NGINX 使用 ngx_http_core_module 模块作为管理者,所有的 server{} 块配置组织到动态数组中,数组的每个元素为一个 server{} 配置项,其中又包含各个模块的 ngx_http_conf_ctx_t 配置。
  • loc_conf 的配置 NGINX 使用 ngx_http_core_module 模块来管理,将 location {} 放入父配置项(ngx_http_conf_ctx_t)的 loc_conflocation 队列中。