# Nginx Http 模块原理

看如下默认的 nginx.conf 文件,我们在配置完核心模块和事件 epoll 模块后,将会配置 http 模块用于处理 http请求,于是我们需要 ngx_http_module 模块来完成该模块的配置。那么,本文就来分析Http 模块的配置原理,以及我们在这里配置的端口与 epoll 事件处理模块如何关联。

worker_processes  1;
events {
    worker_connections  1024;
}
http { // 默认未注释的设置
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# ngx_http_module 模块

可以看到该模块没有实现回调函数,也没有实现 ngx_core_module_t 结构的 create_conf 和 init_conf 函数指针,仅仅保留对 http 块的配置函数 : ngx_http_block,我们知道:当Nginx 解析 Nginx.conf (opens new window) 中的 http { } 块时,将会回调该函数,在该函数中我们创建了所有 用于 保存 http 配置信息的上下文以及附属结构。

ngx_module_t  ngx_http_module = {
    NGX_MODULE_V1,
    &ngx_http_module_ctx,                  /* module context */
    ngx_http_commands,                     /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};// 未实现 create_conf 与 init_conf 函数
static ngx_core_module_t  ngx_http_module_ctx = {
    ngx_string("http"),
    NULL,
    NULL
};static ngx_command_t  ngx_http_commands[] = {
    { ngx_string("http"),
      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_http_block,
      0,
      0,
      NULL },
​
      ngx_null_command
};
​
​
static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
    ...
    /* 创建http上下文对象*/
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));*(ngx_http_conf_ctx_t **) conf = ctx; // 从配置对象中获取到配置结构的指针/* 计算HTTP模块的数量并建立索引 */
    ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);/* HTTP 模块的 main_conf 上下文数组,在所有HTTP上下文中都是同一个 */
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    
    // 分配用于保存 server {} 块的 srv_conf 配置的结构数组( sizeof(void *) 计算地址大小,后面为http模块数量 )
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
​
​
    // 分配用于保存 server {} 块的 loc_conf 配置的结构数组
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }// 遍历所有 http 模块,创建他们的 main_conf 和 srv_conf 和 loc_conf 结构(注意:上面我们只是创建了保存他们的数组结构,并没有创建具体结构)
    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) { // 创建该 http 模块的 主配置 信息
            ctx->main_conf[mi] = module->create_main_conf(cf);
            if (ctx->main_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
        if (module->create_srv_conf) { // 创建该 http 模块的 server配置 信息
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
            if (ctx->srv_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
        if (module->create_loc_conf) { // 创建该 http 模块的 location地位配置 信息
            ctx->loc_conf[mi] = module->create_loc_conf(cf);
            if (ctx->loc_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }
    ...
    for (m = 0; cf->cycle->modules[m]; m++) { // 遍历所有 http 模块,若存在 preconfiguration 函数,那么回调
        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;
            }
        }
    }/* 转换配置文件的 http{} 块信息 */
    cf->module_type = NGX_HTTP_MODULE;
    cf->cmd_type = NGX_HTTP_MAIN_CONF;
    rv = ngx_conf_parse(cf, NULL);/*
     * 初始化 http{} 主配置信息, 组合 server{} 块的 srv_conf 配置信息 和  location{} 块的 loc_conf 配置信息
     */
    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;
        // 回调 HTTP 模块 init_main_conf 函数
        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); // 对当前 http 模块的配置信息进行父子配置处理,也即回调子模块的 merge_srv_conf 和 merge_loc_conf 函数进行配置,详情看后面 http_core 模块的描述
        if (rv != NGX_CONF_OK) {
            goto failed;
        }
    }// 根据上述的配置信息,创建 location 定位树(此时依靠该树,可以用于接收到 http 的 /xx/xx 路径后匹配用于处理的 location {} 块)
    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) { // 初始化 http 处理阶段的回调函数数组
        return NGX_CONF_ERROR;
    }
    if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { // 初始化用于处理http头部 hash 表
        return NGX_CONF_ERROR;
    }for (m = 0; cf->cycle->modules[m]; m++) { // 回调 Http 模块的 postconfiguration 回调函数
        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_init_phase_handlers(cf, cmcf) != NGX_OK) { // 初始化回调函数数组中的具体处理函数地址
        return NGX_CONF_ERROR;
    }/* 优化端口、地址和服务器名称列表,并将其放入到事件循环的 listen端列表 */
    if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
        return NGX_CONF_ERROR;
    }
    return NGX_CONF_OK;
    ...
}// http 模块配置结构的回调函数定义
typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);void       *(*create_main_conf)(ngx_conf_t *cf);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
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

# ngx_http_core_module 模块

从 http 模块中我们看到回调流程如下:

1、ngx_http_core_create_main_conf -> ngx_http_core_create_srv_conf -> ngx_http_core_create_loc_conf

2、ngx_http_core_preconfiguration

3、转换 http 模块 (ngx_conf_parse,保存http{}块内的所有结构都已经创建完毕,那么我们很容易的理解,当该方法完成后 nginx.conf (opens new window) 中的结构化的字符数据将全部放置到对应的配置结构中)

4、ngx_http_core_init_main_conf -> ngx_http_core_merge_srv_conf -> ngx_http_core_merge_loc_conf

5、ngx_http_core_postconfiguration

ngx_module_t  ngx_http_core_module = {
    NGX_MODULE_V1,
    &ngx_http_core_module_ctx,             /* module context */
    ngx_http_core_commands,                /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};// 核心模块的配置函数定义
static ngx_http_module_t  ngx_http_core_module_ctx = {
    ngx_http_core_preconfiguration,        /* preconfiguration */
    ngx_http_core_postconfiguration,       /* postconfiguration */
​
    ngx_http_core_create_main_conf,        /* create main configuration */
    ngx_http_core_init_main_conf,          /* init main configuration */
​
    ngx_http_core_create_srv_conf,         /* create server configuration */
    ngx_http_core_merge_srv_conf,          /* merge server configuration */
​
    ngx_http_core_create_loc_conf,         /* create location configuration */
    ngx_http_core_merge_loc_conf           /* merge location configuration */
};// 核心模块的解析函数定义(由于配置项太多,不可能一一例举,我们这里看 server{} 块的解析即可)
static ngx_command_t  ngx_http_core_commands[] = {
    { ngx_string("server"),
      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_http_core_server,
      0,
      0,
      NULL },
    ... // 省略其他,建议读者理解了为 server{}块的解析后,去看看 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

# ngx_http_core_create_main_conf 函数

该函数用于创建 http 核心模块的 ngx_http_core_main_conf_t 配置结构,并初始化其成员变量。

static void * ngx_http_core_create_main_conf(ngx_conf_t *cf)
{
    ngx_http_core_main_conf_t  *cmcf;
    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t)); // 创建当前模块的配置结构
    if (cmcf == NULL) {
        return NULL;
    }
    if (ngx_array_init(&cmcf->servers, cf->pool, 4,
                       sizeof(ngx_http_core_srv_conf_t *)) // 初始化配置结构中存放 server 配置信息的结构数组
        != NGX_OK)
    {
        return NULL;
    }// 其他变量初始化为 -1 
    cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;
    cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;
​
    cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;
    cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;return cmcf;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# ngx_http_core_create_srv_conf 函数

该函数用于创建保存 server 结构的配置信息,实现很简单,源码如下。

static void * ngx_http_core_create_srv_conf(ngx_conf_t *cf) {
    ngx_http_core_srv_conf_t  *cscf;
​
    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t)); // server{} 配置结构
    if (cscf == NULL) {
        return NULL;
    }if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,
                       sizeof(ngx_http_server_name_t))
        != NGX_OK)
    {  // 初始化放置server名字的数组
        return NULL;
    }// 初始化其他变量为初始值 -1
    cscf->connection_pool_size = NGX_CONF_UNSET_SIZE;
    cscf->request_pool_size = NGX_CONF_UNSET_SIZE;
    cscf->client_header_timeout = NGX_CONF_UNSET_MSEC;
    cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE;
    cscf->ignore_invalid_headers = NGX_CONF_UNSET;
    cscf->merge_slashes = NGX_CONF_UNSET;
    cscf->underscores_in_headers = NGX_CONF_UNSET;
​
    cscf->file_name = cf->conf_file->file.name.data;
    cscf->line = cf->conf_file->line;return cscf;
}
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

# ngx_http_core_create_loc_conf 函数

该函数同上述创建配置信息的函数一模一样:创建location配置项,然后初始化默认值,由于内部成员太多,这里将其省略。

static void * ngx_http_core_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_core_loc_conf_t  *clcf;
    clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t)); // 创建保存 location{} 块的配置结构
    if (clcf == NULL) {
        return NULL;
    }// 初始化成员为初始值 -1
    clcf->client_max_body_size = NGX_CONF_UNSET;
    ...return clcf;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# ngx_http_core_preconfiguration 函数

该函数较为简单:遍历ngx_http_core_variables数组,该数组为预定义数组,遍历其保存在当前模块的 ngx_http_core_main_conf_t 配置结构中。

static ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf){
    return ngx_http_variables_add_core_vars(cf);
}ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf){
    ngx_http_variable_t        *cv, *v;
    ngx_http_core_main_conf_t  *cmcf;
    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); // 获取在 ngx_http_module 模块中创建的该模块的 配置结构 
    cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
                                       sizeof(ngx_hash_keys_arrays_t)); // 分配存放变量的数组包装结构 ngx_hash_keys_arrays_t
    if (cmcf->variables_keys == NULL) {
        return NGX_ERROR;
    }
    cmcf->variables_keys->pool = cf->pool;
    cmcf->variables_keys->temp_pool = cf->pool;
    if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL) // 初始化ngx_hash_keys_arrays_t包装结构中的数组
        != NGX_OK)
    {
        return NGX_ERROR;
    }if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8, sizeof(ngx_http_variable_t)) 
        != NGX_OK) // 初始化放置 http 前缀的变量描述结构  ngx_http_variable_t
    {
        return NGX_ERROR;
    }for (cv = ngx_http_core_variables; cv->name.len; cv++) {  // 遍历所有 http 使用的变量信息(头部变量和前缀变量)将其放入上面创建的 前缀变量数组 和 ngx_hash_keys_arrays_t 包装数组结构中的数组
        v = ngx_http_add_variable(cf, &cv->name, cv->flags);
        if (v == NULL) {
            return NGX_ERROR;
        }
        *v = *cv; // 添加成功后,将原有变量信息复制到添加后的数组项中
    }return NGX_OK;
}// http 头部和前缀定义,太多了,读者自行查看
static ngx_http_variable_t  ngx_http_core_variables[] = {
    { ngx_string("http_host"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.host), 0, 0 },
    { ngx_string("content_length"), NULL, ngx_http_variable_content_length,
      0, 0, 0 },
    { ngx_string("content_type"), NULL, ngx_http_variable_header,
      offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 }
    ....
}// http 变量结构,可见包含 获取和设置的封装函数
struct ngx_http_variable_s {
    ngx_str_t                     name;  
    ngx_http_set_variable_pt      set_handler;
    ngx_http_get_variable_pt      get_handler;
    uintptr_t                     data;
    ngx_uint_t                    flags;
    ngx_uint_t                    index;
};
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

# ngx_http_core_init_main_conf 函数

我们知道在 create 创建时,将会初始化为 -1 初始值,然后当我们解析配置文件将会把配置文件中设置的信息放置到结构中,但有些变量用户并没有设置,那么这时,我们需要在该配置项中填入默认值,读者并不需要知道该默认值是什么含义,我们在后面使用到时自然明白。源码如下。

static char * ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf){
    // 初始化主模块变量的默认值
    ngx_http_core_main_conf_t *cmcf = conf;
    ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);
    ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,
                             ngx_cacheline_size);
    cmcf->server_names_hash_bucket_size =
            ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);
    ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);
    ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);
    cmcf->variables_hash_bucket_size =
               ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size);
    if (cmcf->ncaptures) {
        cmcf->ncaptures = (cmcf->ncaptures + 1) * 3;
    }
    return NGX_CONF_OK;
}// 设置时,将会看看配置文件中是否已经设置值,否则将其设置为默认值
#define ngx_conf_init_uint_value(conf, default)                              \
    if (conf == NGX_CONF_UNSET_UINT) {                                       \
        conf = default;                                                      \
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# ngx_http_core_merge_srv_conf 函数 与 ngx_http_core_merge_loc_conf 函数

这两个函数均用于将解析到的 server 信息进行父子配置,子配置项依赖父配置项的信息来初始化自身的属性,若父配置项不存在该信息,那么使用设置的默认值。源码如下。

// 配置server信息
static char * ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_core_srv_conf_t *prev = parent; 
    ngx_http_core_srv_conf_t *conf = child; // 当前需要组合的信息ngx_str_t                name;
    ngx_http_server_name_t  *sn;// 组合成员变量
    ngx_conf_merge_size_value(conf->connection_pool_size,
                              prev->connection_pool_size, 64 * sizeof(void *));
    ngx_conf_merge_size_value(conf->request_pool_size,
                              prev->request_pool_size, 4096);
    ngx_conf_merge_msec_value(conf->client_header_timeout,
                              prev->client_header_timeout, 60000);
    ngx_conf_merge_size_value(conf->client_header_buffer_size,
                              prev->client_header_buffer_size, 1024);
    ngx_conf_merge_bufs_value(conf->large_client_header_buffers,
                              prev->large_client_header_buffers,
                              4, 8192);
    ...
    return NGX_CONF_OK;
}// 配置location信息
static char * ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_core_loc_conf_t *prev = parent;
    ngx_http_core_loc_conf_t *conf = child;
    ...
    ngx_conf_merge_uint_value(conf->types_hash_max_size,
                              prev->types_hash_max_size, 1024);ngx_conf_merge_uint_value(conf->types_hash_bucket_size,
                              prev->types_hash_bucket_size, 64);
    ...
}
​
​
// 宏定义设置 conf 值,可以看到当conf为初始化值时,才根据 parent的值来进行设置,若parent没有设置该值,那么取默认值
#define ngx_conf_merge_size_value(conf, prev, default)                       \
    if (conf == NGX_CONF_UNSET_SIZE) {                                       \
        conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev;               \
    }
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

# ngx_http_core_postconfiguration 函数

该函数将在父子配置设置完成后调用,可以看到将 ngx_http_request_body_save_filter 函数作为全局 filter 过滤 http body的函数指针。源码如下。

static ngx_int_t ngx_http_core_postconfiguration(ngx_conf_t *cf)
{
    ngx_http_top_request_body_filter = ngx_http_request_body_save_filter; // 该函数用于相应http请求体,这里先暂时不用关系,我们把关注点放到对 http 核心模块的框架上
    return NGX_OK;
}
1
2
3
4
5

# ngx_http_core_server 函数

该函数将在nginx解析到 nginx.conf (opens new window) 中的 server{} 时调用,进一步完成该该块的结构化信息的解析,将其中设置的值放置在 http 模块对应的配置结构中。

typedef struct {
    void        **main_conf;
    void        **srv_conf;
    void        **loc_conf;
} ngx_http_conf_ctx_t; // 解析server{}块时用于包装 main、server、location 的配置结构的地址的数组
​
​
static char * ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
    ...
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); // 分配 http 上下文的配置结构,包装 main、server、location 的配置结构的地址
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }
    http_ctx = cf->ctx; // http 模块结构(管理整个 http 的配置)
    ctx->main_conf = http_ctx->main_conf; // http 主配置结构
    /* 创建放置 server{} 块的 srv_conf 结构的指针数组*/
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);  
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }/* 创建放置 server{} 中 location{} 的 loc_conf 结构的指针数组*/
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }// 遍历所有 http 模块,一次回调他们的 create_srv_conf 、create_loc_conf 来获取它们的对应块配置项,然后将其地址放置到上面创建的包装结构 ngx_http_conf_ctx_t 的数组中
    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);
            if (mconf == NULL) {
                return NGX_CONF_ERROR;
            }
            ctx->srv_conf[cf->cycle->modules[i]->ctx_index] = mconf;
        }
        if (module->create_loc_conf) {
            mconf = module->create_loc_conf(cf);
            if (mconf == NULL) {
                return NGX_CONF_ERROR;
            }
            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;
        }
    }
    ...
    /* 转换内部的server{}块 */
    ...
    rv = ngx_conf_parse(cf, NULL);
    ...
    if (rv == NGX_CONF_OK && !cscf->listen) { // 若没有设置 server 的监听端口,那么给端口配置一个默认值,可以看到若 uid 为root 用户,那么配置为 80,否则为 8080
        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
        p = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in));
        if (p == NULL) {
            return NGX_CONF_ERROR;
        }
        lsopt.sockaddr = (struct sockaddr *) p;
        sin = (struct sockaddr_in *) p;
        sin->sin_family = AF_INET;
        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
        sin->sin_addr.s_addr = INADDR_ANY;
        lsopt.socklen = sizeof(struct sockaddr_in);
        lsopt.backlog = NGX_LISTEN_BACKLOG;
        lsopt.rcvbuf = -1;
        lsopt.sndbuf = -1;
        lsopt.wildcard = 1;
        len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
        p = ngx_pnalloc(cf->pool, len);
        if (p == NULL) {
            return NGX_CONF_ERROR;
        }
        lsopt.addr_text.data = p;
        lsopt.addr_text.len = ngx_sock_ntop(lsopt.sockaddr, lsopt.socklen, p,
                                            len, 1);
        if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) { // 将端口信息、地址信息、server信息添加到配置信息中
            return NGX_CONF_ERROR;
        }
    }return rv;
}
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