http 框架的初始化与 nginx-rtmp 框架的初始化类似: Nginx-rtmp之配置项的管理
1. ngx_http_module_t
ngx_http_module 核心模块定义了新的模块类型 NGX_HTTP_MODULE。这样的 HTTP 模块对于 ctx 上下文使用了不同于核心模块、事件模块的新接口 ngx_http_module_t。对于每一个 HTTMP 模块,都必须实现 ngx_http_module_t 接口。
typedef struct {
/*
* 在解析 http{} 内的配置项前调用
*/
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
/*
* 解析完 http{} 内的所有配置后回调
*/
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
/*
* 创建用于存储 HTTP 全局配置项的结构体,该结构体中的成员将保存直属于
* http{} 块的配置项参数,它会在解析 main 配置项前调用
*/
void *(*create_main_conf)(ngx_conf_t *cf);
/*
* 解析完 main 配置项后回调
*/
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
/*
* 创建用于存储可同时出现在 main、srv 级别配置项的结构体,该结构体中的成员
* 与 server 配置是相关联的
*/
void *(*create_srv_conf)(ngx_conf_t *cf);
/*
* create_srv_conf 产生的结构体所要解析的配置项,可能同时出现在 main、srv 级别中,
* merge_srv_conf 方法可以把出现在 main 级别中的配置项合并到 srv 级别配置项中
*/
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
/*
* 创建可用于存储可同时出现在 main、srv、loc 级别配置项的结构体,该结构体中的成员与
* location 配置是相关联的
*/
void *(*create_loc_conf)(ngx_conf_t *cf);
/*
* create_loc_conf 产生的结构体所要解析的配置项,可能同时出现在 main、srv、loc 级别中,
* merge_loc_conf 方法可以分别把出现在 main、serv 级别中的配置项值合并到 loc 级别的
* 配置项中
*/
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
2. ngx_http_conf_ctx_t
typedef struct {
/*
* 指向一个指针数组,数组中的每个成员都是由所有 HTTP 模块的 create_main_conf
* 方法创建的存放全局配置项的结构体,它们存放着解析直属于 http{} 块内的 main
* 级别的配置项参数.
*/
void **main_conf;
/*
* 指向一个指针数组,数组中的每个成员都是由所有 HTTP 模块的 create_srv_conf
* 方法创建的与 server 相关的结构体,它们或存放 main 级别的配置项,或存放
* srv 级别的配置项,这与当前 ngx_http_conf_t 是在解析 http{} 或者 server{}
* 块时创建的有关
*/
void **srv_conf;
/*
* 指向一个指针数组,数组中的每个成员都是由所有 HTTP 模块的 create_loc_conf
* 方法创建的与 location 相关的结构体,它们可能存放着 main、srv、loc 级别的
* 配置项,这与当前的 ngx_http_conf_ctx_t 是在解析 http{}、server{} 或者
* location{} 块时创建有关的
*/
void **loc_conf;
} ngx_http_conf_ctx_t;
在核心结构体 ngx_cycle_t 的 conf_ctx 成员指向的指针数组中,第 8 个指针由 ngx_http_module 模块使用(假设当前没有添加任何第三方模块,且使用默认配置时,则 ngx_http_module 模块的 index 序号为 7,由于从 0 开始,所以它在 ngx_modules 数组中排行第 8。在存放全局配置结构体的 conf_ctx 数组中,第 8 个成员指向 ngx_http_module 模块),这个指针设置为指向解析 http{} 块时生成的 ngx_http_conf_ctx_t 结构体,而 ngx_http_conf_ctx_t 的 3 个成员则分别指向新分配的 3 个指针数组新的指针数组中成员的意义由每个 HTTP 模块的 ctx_index 序号指定(ctx_index 在 HTTP 模块中表明它处于 HTTP 模块间的序号)。
在 ngx_cycle_t 核心结构体找到 main 级别的配置结构体的方法如下:
#define ngx_http_cycle_get_module_main_conf(cycle, module)
(cycle->conf_ctx[ngx_http_module.index] ?
((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index])
->main_conf[module.ctx_index]:
NULL)
3. ngx_http_block
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;
/* ngx_http_conf_ctx_t 结构体必须在解析 http{} 时分配 */
if (*(ngx_http_conf_ctx_t **) conf) {
return "is duplicate";
}
/* the main http context */
/* 为 ngx_http_conf_ctx_t 结构体分配内存,该结构体管理着解析 http{}
* 时所有 HTTP 模块的配置项 */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
/* 传入的 conf 指针为核心结构体 ngx_cycle_t 中成员 conf_ctx 指针数组
* 中的第 8 号元素,即序号为 7 的元素,该 conf_ctx[7] 元素是给
* 核心模块 ngx_http_module 使用的 */
*(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);
/* the http main_conf context, it is the same in the all http contexts */
/*
* main_conf:
* 指向一个指针数组,数组中的每个成员都是由所有 HTTP 模块的 create_main_conf
* 方法创建的存放全局配置项的结构体,它们存放着解析直属于 http{} 块内的 main
* 级别的配置项参数.
*/
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
if (ctx->main_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* the http null srv_conf context, it is used to merge
* the server{}s' srv_conf's
*/
/*
* srv_conf:
* 指向一个指针数组,数组中的每个成员都是由所有 HTTP 模块的 create_srv_conf
* 方法创建的与 server 相关的结构体,它们或存放 main 级别的配置项,或存放
* srv 级别的配置项,这与当前 ngx_http_conf_t 是在解析 http{} 或者 server{}
* 块时创建的有关
*/
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* the http null loc_conf context, it is used to merge
* the server{}s' loc_conf's
*/
/*
* loc_conf:
* 指向一个指针数组,数组中的每个成员都是由所有 HTTP 模块的 create_loc_conf
* 方法创建的与 location 相关的结构体,它们可能存放着 main、srv、loc 级别的
* 配置项,这与当前的 ngx_http_conf_ctx_t 是在解析 http{}、server{} 或者
* location{} 块时创建有关的
*/
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* create the main_conf's, the null srv_conf's, and the null loc_conf's
* of the all http modules
*/
/* 在开始解析 http{} 的 main 级别的配置项前,调用所有 HTTP 模块的
* create_main_conf、create_srv_conf 以及 create_loc_conf 方法,
* 分别创建用于存放于 main、srv、location 相关的配置项结构体,
* 这三个方法返回的配置项结构体分别存放在 ngx_http_conf_ctx_t
* 的成员 main_conf、srv_conf 以及 loc_conf 这个三个指针数组
* 中该模块的 ctx_index 对应的下标处 */
/* 遍历所有的 HTTP 模块 */
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
/* 获取该模块的上下文结构体 */
module = cf->cycle->modules[m]->ctx;
/* 该模块在 HTTP 模块中的序号 */
mi = cf->cycle->modules[m]->ctx_index;
/* 调用该 HTTP 模块的 create_main_conf 方法,创建存放全局
* 配置项的结构体 */
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
if (ctx->main_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
/* 调用该模块的 create_srv_conf,创建存放与 server{} 相关的
* 配置项结构体,该结构体可能存放着 main 或 server 级别的
* 配置项 */
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
if (ctx->srv_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
/* 调用该模块的 create_loc_conf,创建存放于 location 相关的
* 配置项结构体,该结构体可能存放 main、server 或 location
* 级别的配置项 */
if (module->create_loc_conf) {
ctx->loc_conf[mi] = module->create_loc_conf(cf);
if (ctx->loc_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
}
/* 临时缓存 cf 和 ctx 变量 */
pcf = *cf;
cf->ctx = ctx;
/* 在解析配置项前调用所有模块的 precofiguration 函数,
* 该函数主要是将一些 variable 添加到 ngx_http_core_main_conf_t
* 结构体中的 variables_keys 哈希数组中,主要有以下几个 http 模块
* 实现了该函数:
* ngx_http_core_module
* ngx_http_upstream
* ngx_http_proxy_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;
if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
/* parse inside the http{} block */
/* 设置当前解析的模块类型为 HTTP 模块 */
cf->module_type = NGX_HTTP_MODULE;
/* 设置当前解析配置项为 main 级别的 */
cf->cmd_type = NGX_HTTP_MAIN_CONF;
/* 开始解析 http{} 中的配置项 */
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是ngx_http_core_module在http下的全局配置结构体,它的servers成员
* 是一个动态数组,保存着所有ngx_http_core_srv_conf_t的指针,从而关联了
* 所有的server块 */
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 */
/* 解析配置项完成后,调用所有模块的 init_main_conf 方法,
* 初始化所有模块与 main 相关的配置项结构体中各成员的值 */
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是server块下ngx_http_core_loc_conf_t结构体,
* 它的locations成员以双向链表关联着隶属于这个server块的
* 所有location块对应的ngx_http_core_loc_conf_t结构体 */
clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
/* 将ngx_http_core_loc_conf_t组成的双向链表按照location匹配字符串
* 进行排序。注意,这个操作是递归进行的,如果某个location块下还具
* 有其他location,那么它的locations链表也会被排序 */
if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* 根据已经按照locations字符串排序过的双向链表,快速地构建
* 静态的二叉查找树。与ngx_http_init_locations方法类似,
* 这个操作也是递归进行的 */
if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
/*
* 为 cmcf->phases 数组分配内存,该数组的每一个元素都为 ngx_http_phase_t 类型的结构体,
* phases 数组是用于在 HTTP 框架初始化时帮助各个 HTTP 模块在任意阶段中添加 HTTP 处理方
* 法,它是一个有 11 个成员的 ngx_http_phase_t 数组,其中每一个 ngx_http_phase_t 结构体
* 对应一个 HTTP 阶段。在 HTTP 框架初始化完毕后,运行过程中的 phases 数组是无用的.
*/
if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* 将一些常用的 HTTP 头部字段及其处理方法存放到一个散列表中 */
if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* postconfiguration 主要做的事情有:
* 1. 向 cmcf->phases 数组中插入该 HTTP 模块对某个阶段的处理方法
* 2. 向 ngx_http_top_header_filter、ngx_http_top_body_filter、
* ngx_http_top_request_body_filter 这三个单链表中添加自己的处理方法
* 3. 有些特殊模块仅是检测当前的配置是否合理,如 charset 模块等 */
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;
}
}
}
/* 初始化 http variables,并将其保存到 hash 表中 */
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;
/* 初始化各个阶段所有 HTTP 模块介入的处理方法 */
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;
}
4. ngx_http_core_server
当解析到 http{} 中的 server{} 时,回调该函数进行解析。
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;
/* 当开始解析 server{} 时,创建一个属于该 server{} 下的
* ngx_http_conf_ctx_t 结构体,该结构体中有三个指针数组,
* 分别为 main_conf、srv_conf、loc_conf */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
/* 传入的 cf->ctx 上下文结构体指向当前 server{} 所属的 http{}
* 解析 main 级别时创建的 ngx_http_conf_ctx_t 结构体 */
http_ctx = cf->ctx;
/* 将当前 server{} 所属的 ngx_http_conf_ctx_t 的 main_conf 指针
* 指向 http{} 下解析 main{} 级别时创建的 ngx_http_conf_ctx_t
* 结构体的 main_conf 成员 */
ctx->main_conf = http_ctx->main_conf;
/* 下面调用所有 http 模块的 create_srv_conf 和 create_loc_conf
* 方法,为所有的 http 模块创建该模块与 server{} 和 location{}
* 相关的配置项结构体 */
/* the server{}'s srv_conf */
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}
/* 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;
}
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;
}
}
/* the server configuration context */
/* 获取该 server{} 下与 srv 级别相关的第一个模块的配置结构体 */
cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
/* 指向当前 server{} 所属的 ngx_http_conf_ctx_t 结构体 */
cscf->ctx = ctx;
cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
/* 从 servers 数组中取出一个空闲的元素,servers 数组中的每一项
* 元素的类型都为 ngx_http_core_srv_conf_t 结构体类型,保存着
* http{} 下每一个 server{} 所属的 ngx_http_conf_ctx_t 结构体中
* srv_conf 指针数组的第一个元素,即 srv_conf[0],实际上该元素
* 指向该 server{} 下与 srv 级别相关的第一个模块即
* ngx_http_core_module 模块的配置项结构体 */
cscfp = ngx_array_push(&cmcf->servers);
if (cscfp == NULL) {
return NGX_CONF_ERROR;
}
/* 将该结构体存到 servers 数组中 */
*cscfp = cscf;
/* parse inside server{} */
/* 临时缓存变量 */
pcf = *cf;
/* 此时开始解析 server{},因此需要更新当前所属的上下文为
* server{} 所属的 ngx_http_conf_ctx_t 结构体 */
cf->ctx = ctx;
/* 设置当前指令的类型为 srv 级别 */
cf->cmd_type = NGX_HTTP_SRV_CONF;
/* 开始解析 server{} 下的配置项 */
rv = ngx_conf_parse(cf, NULL);
/* 重新恢复之前的缓存的值 */
*cf = pcf;
/* 若解析 server{} 成功且 server{} 下没有解析到 listen 指令,
* 则下面默认监听一个端口 */
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;
lsopt.socklen = sizeof(struct sockaddr_in);
lsopt.backlog = NGX_LISTEN_BACKLOG;
lsopt.rcvbuf = -1;
lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
lsopt.setfib = -1;
#endif
#if (NGX_HAVE_TCP_FASTOPEN)
lsopt.fastopen = -1;
#endif
lsopt.wildcard = 1;
(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;
}
5. ngx_http_merge_servers
static char *
ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
ngx_http_module_t *module, ngx_uint_t ctx_index)
{
char *rv;
ngx_uint_t s;
ngx_http_conf_ctx_t *ctx, saved;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
/* cmcf是ngx_http_core_module在http下的全局配置结构体,它的servers成员
* 是一个动态数组,保存着所有ngx_http_core_srv_conf_t的指针,从而关联了
* 所有的server块 */
cscfp = cmcf->servers.elts;
/* 将 ctx 指向 http{} 下解析 main 级别时创建的全局配置项上下文结构体 */
ctx = (ngx_http_conf_ctx_t *) cf->ctx;
saved = *ctx;
rv = NGX_CONF_OK;
/* 遍历 http{} 下所有的 server{} */
for (s = 0; s < cmcf->servers.nelts; s++) {
/* merge the server{}s' srv_conf's */
/* 将 ctx->srv_conf 指向 当前 server{} 所属的上下文结构体
* ngx_http_conf_ctx_t 中的 srv_conf 指针数组 */
ctx->srv_conf = cscfp[s]->ctx->srv_conf;
/* 调用该模块的 merge_srv_conf 函数,将同时出现在 main
* 级别下的配置项合并到 srv 级别下(前提是该配置项没有出现在
* srv级别下,若出现了,则不需要合并,直接以 srv 级别下的为主)*/
if (module->merge_srv_conf) {
rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],
cscfp[s]->ctx->srv_conf[ctx_index]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
if (module->merge_loc_conf) {
/* merge the server{}'s loc_conf */
/* 将 ctx->loc_conf 指向当前 server{} 下的
* loc_conf 指针数组 */
ctx->loc_conf = cscfp[s]->ctx->loc_conf;
/* 将 mian、srv 级别下的配置项合并到 loc 级别下 */
rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index],
cscfp[s]->ctx->loc_conf[ctx_index]);
if (rv != NGX_CONF_OK) {
goto failed;
}
/* merge the locations{}' loc_conf's */
/* clcf是server块下ngx_http_core_module模块使用create_loc_conf方法产生的
* ngx_http_core_loc_conf_t结构体,它的locations成员将以双向链表的形式关联
* 到所有当前server{}块下的location块 */
clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
/* 调用ngx_http_merge_locations方法,将server{}块与其所包含的location{}块下
* 的结构体进行合并 */
rv = ngx_http_merge_locations(cf, clcf->locations,
cscfp[s]->ctx->loc_conf,
module, ctx_index);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
}
failed:
*ctx = saved;
return rv;
}
6. ngx_http_merge_locations
static char *
ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations,
void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index)
{
char *rv;
ngx_queue_t *q;
ngx_http_conf_ctx_t *ctx, saved;
ngx_http_core_loc_conf_t *clcf;
ngx_http_location_queue_t *lq;
/* 如果locations链表为空,也就是说,当前server下没有location块,
* 则立即返回 */
if (locations == NULL) {
return NGX_CONF_OK;
}
ctx = (ngx_http_conf_ctx_t *) cf->ctx;
saved = *ctx;
/* 遍历locations双向链表 */
for (q = ngx_queue_head(locations);
q != ngx_queue_sentinel(locations);
q = ngx_queue_next(q))
{
lq = (ngx_http_location_queue_t *) q;
/* 如果locations后的匹配字符串不依靠Nginx自定义的通配符就可以完全匹配的话,
* 则exact指向当前locations对应的ngx_http_core_loc_conf_t结构体,否则使用
* inclusive指向该结构体,且exact的优先级高于inclusive */
clcf = lq->exact ? lq->exact : lq->inclusive;
/* clcf->loc_conf这个指针数组里保存着当前location下所有HTTP模块使用
* create_loc_conf方法生成的结构体的指针 */
ctx->loc_conf = clcf->loc_conf;
/* 调用merge_loc_conf方法合并srv、loc级别配置项 */
rv = module->merge_loc_conf(cf, loc_conf[ctx_index],
clcf->loc_conf[ctx_index]);
if (rv != NGX_CONF_OK) {
return rv;
}
/* 注意,因为location{}中可以嵌套location{}配置块,
* 所以是可以继续合并的。 */
rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf,
module, ctx_index);
if (rv != NGX_CONF_OK) {
return rv;
}
}
*ctx = saved;
return NGX_CONF_OK;
}
7. ngx_http_init_headers_in_hash
将 HTTP 的一些常用的头部字段以及该字段对应的处理方法添加到 hash 表中,便于后续处理 HTTP 数据时可以快速找到该字段,并进行处理。
static ngx_int_t
ngx_http_init_headers_in_hash(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
ngx_array_t headers_in;
ngx_hash_key_t *hk;
ngx_hash_init_t hash;
ngx_http_header_t *header;
if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_ERROR;
}
for (header = ngx_http_headers_in; header->name.len; header++) {
hk = ngx_array_push(&headers_in);
if (hk == NULL) {
return NGX_ERROR;
}
/* 元素关键字为该头部字段名称 */
hk->key = header->name;
/* 以头部字段为关键字,通过散列方法 ngx_hash_key_lc 算出来的
* 关键码保存在 key_hash 中 */
hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
/* value 指向用户自定义的数据结构 */
hk->value = header;
}
/* 指向普通的完全匹配散列表 */
hash.hash = &cmcf->headers_in_hash;
/* 用于初始化添加元素的散列方法 */
hash.key = ngx_hash_key_lc;
/* 散列表中槽的最大数目 */
hash.max_size = 512;
/* 散列表中一个槽的空间大小,它限制了每个散列表元素关键字的最大长度 */
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
/* 散列表的名称 */
hash.name = "headers_in_hash";
/* 内存池,它分配散列表(最多3个,包括 1 个普通散列表、1个前置通配符
* 散列表、1个后置通配符散列表)中的所有槽 */
hash.pool = cf->pool;
/* 临时内存池,它仅存在于初始化散列表之前。它主要用于分配一些临时的动态数组,
* 带通配符的元素在初始化时需要用到这些数组 */
hash.temp_pool = NULL;
/* 初始化一个基本的散列表 */
if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
8. ngx_http_init_phase_handlers
typedef struct ngx_http_phase_handler_s ngx_http_phase_handler_t;
/*
* 一个 HTTP 处理阶段的 checker 检查方法,仅可以由 HTTP 框架实现,
* 以此控制 HTTP 请求的处理流程
*/
typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph);
/*
* 该结构体仅表示处理阶段中的一个处理方法
*/
struct ngx_http_phase_handler_s {
/*
* 在处理某一个 HTTP 阶段时,HTTP 框架将会在 checker 方法已实现的前提下
* 首先调用 checker 方法来处理请求,而不会直接调用任何阶段中的 handler
* 方法,只有在 checker 方法中才会去调用 handler 方法。因此,事实上所有
* 的 checker 方法都是由框架中的 ngx_http_core_module 模块实现的,且普通
* 的 HTTP 模块无法重定义 checker 方法
*/
ngx_http_phase_handler_pt checker;
/*
* 除 ngx_http_core_module 模块以外的 HTTP 模块,只能通过定义 handler
* 方法才能介入某一个 HTTP 处理阶段以处理请求.
*/
ngx_http_handler_pt handler;
/*
* 将要执行的下一个 HTTP 处理阶段的序号.
* next 的设计使得处理阶段不必按顺序依次执行,即可以向后跳跃数个阶段继续
* 执行,也可以跳跃到之前曾经执行过的某个阶段重新执行。通常,next 表示
* 下一个处理阶段中的第 1 个 ngx_http_phase_handler_t 处理方法
*/
ngx_uint_t next;
};
typedef enum {
/*
* 在接收到完整的HTTP头部后处理的HTTP阶段
*/
NGX_HTTP_POST_READ_PHASE = 0,
/*
* 在将请求的 URI 与 location 表达式匹配前,修改请求的 URI
* (所谓的重定向)是一个独立的HTTP阶段
*/
NGX_HTTP_SERVER_REWRITE_PHASE,
/*
* 根据请求的 URI 寻找匹配的 location 表达式,这个阶段只能由
* ngx_http_core_module 模块实现,不建议其他 HTTP 模块重新
* 定义这一阶段的行为.
*/
NGX_HTTP_FIND_CONFIG_PHASE,
/*
* 在 NGX_HTTP_FIND_CONFIG_PHASE 阶段寻找到匹配的 location
* 之后再修改请求的 URI.
*/
NGX_HTTP_REWRITE_PHASE,
/*
* 这一阶段是用于在 rewrite 重写 URL 后,防止错误的 nginx.conf
* 配置导致死循环(递归地修改 URI),因此,这一阶段仅由
* ngx_http_core_module 模块处理。目前,控制死循环的方式很简单,
* 首先检查 rewrite 的次数,如果一个请求超过 10 次重定向,就认为
* 进入了 rewrite 死循环,这时在 NGX_HTTP_POST_REWRITE_PHASE 阶段
* 就会向用户返回 500,表示服务器内部错误.
*/
NGX_HTTP_POST_REWRITE_PHASE,
/*
* 表示在处理 NGX_HTTP_ACCESS_PHASE 阶段决定请求的访问权限前,
* HTTP 模块可以介入的处理阶段.
*/
NGX_HTTP_PREACCESS_PHASE,
/*
* 这个阶段用于让 HTTP 模块判断是否允许这个请求访问服务器.
*/
NGX_HTTP_ACCESS_PHASE,
/*
* 在 NGX_HTTP_ACCESS_PHASE 阶段中,当 HTTP 模块的 handler 处理
* 函数返回不允许访问的错误码时(实际就是 NGX_HTTP_FORBIDDEN 或
* NGX_HTTP_UNAUTHORIZED),这里将负责向用户发送拒绝服务的错误
* 响应。因此,这个阶段实际上用于给 NGX_HTTP_ACCESS_PHASE 阶段
* 收尾.
*/
NGX_HTTP_POST_ACCESS_PHASE,
/*
* 这个阶段完全是 try_files 配置项而设计的,当 HTTP 请求访问静态
* 文件资源时,try_files 配置项可以使这个请求顺序地访问多个静态
* 文件资源,如果某一次访问失败,则继续访问 try_files 中指定的
* 下一个静态资源。这个功能完全是在 NGX_HTTP_TRY_FILES_PHASE 阶段
* 实现的.
*/
NGX_HTTP_TRY_FILES_PHASE,
/*
* 用于处理 HTTP 请求内容的阶段,这是大部分 HTTP 模块最愿意介入
* 阶段
*/
NGX_HTTP_CONTENT_PHASE,
/*
* 处理完请求后记录日志的阶段。如,ngx_http_log_module 模块就在
* 这个阶段中加入了一个 handler 处理方法,使得每个 HTTP 请求
* 处理完毕后会记录 access_log 访问日志.
*/
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
typedef struct {
/*
* handlers 是由 ngx_http_phase_handler_t 构成的数组首地址,它表示
* 一个请求可能经历的所有 ngx_http_handle_pt 处理方法
*/
ngx_http_phase_handler_t *handlers;
/*
* 表示 NGX_HTTP_SERVER_REWRITE_PAHSE 阶段第 1 个 ngx_http_phase_handler_t
* 处理方法在 handlers 数组中的序号,用于在执行 HTTP 请求的任何阶段中快速
* 跳转到 NGX_HTTP_SERVER_REWRITE_PHASE 阶段处理请求.
*/
ngx_uint_t server_rewrite_index;
/*
* 表示 NGX_HTTP_REWRITE_PHASE 阶段第 1 个 ngx_http_phase_handler_t 处理
* 方法在 handlers 数组中的序号,用于在执行 HTTP 请求的任何阶段中快速跳转
* 到 NGX_HTTP_REWRITE_PHASE 阶段处理请求.
*/
ngx_uint_t location_rewrite_index;
} ngx_http_phase_engine_t;
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
ngx_int_t j;
ngx_uint_t i, n;
ngx_uint_t find_config_index, use_rewrite, use_access;
ngx_http_handler_pt *h;
ngx_http_phase_handler_t *ph;
ngx_http_phase_handler_pt checker;
cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
find_config_index = 0;
/* 检测当前是否有 HTTP 模块介入到 NGX_HTTP_REWRITE_PHASE 阶段 */
use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
/* 检测当前是否有 HTTP 模块介入到 NGX_HTTP_ACCESS_PHASE 阶段 */
use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
n = 1 /* find config phase */
+ use_rewrite /* post rewrite phase */
+ use_access /* post access phase */
+ cmcf->try_files;
/* 统计phases数组中每个阶段的的 handlers 之和 */
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
n += cmcf->phases[i].handlers.nelts;
}
ph = ngx_pcalloc(cf->pool,
n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
if (ph == NULL) {
return NGX_ERROR;
}
/* handlers 是由 ngx_http_phase_handler_t 构成的数组首地址,它表示
* 一个请求可能经历的所有 ngx_http_handle_pt 处理方法
*/
cmcf->phase_engine.handlers = ph;
n = 0;
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
h = cmcf->phases[i].handlers.elts;
switch (i) {
/*
* 在将请求的 URI 与 location 表达式匹配前,修改请求的 URI
* (所谓的重定向)是一个独立的 HTTP 阶段
*/
case NGX_HTTP_SERVER_REWRITE_PHASE:
if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
/* 表示 NGX_HTTP_SERVER_REWRITE_PAHSE 阶段第 1 个 ngx_http_phase_handler_t
* 处理方法在 handlers 数组中的序号,用于在执行 HTTP 请求的任何阶段中快速
* 跳转到 NGX_HTTP_SERVER_REWRITE_PHASE 阶段处理请求.
*/
cmcf->phase_engine.server_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
break;
/*
* 根据请求的 URI 寻找匹配的 location 表达式,这个阶段只能由
* ngx_http_core_module 模块实现,不建议其他 HTTP 模块重新
* 定义这一阶段的行为.
*/
case NGX_HTTP_FIND_CONFIG_PHASE:
find_config_index = n;
/*
* 在处理某一个 HTTP 阶段时,HTTP 框架将会在 checker 方法已实现的前提下
* 首先调用 checker 方法来处理请求,而不会直接调用任何阶段中的 handler
* 方法,只有在 checker 方法中才会去调用 handler 方法。因此,事实上所有
* 的 checker 方法都是由框架中的 ngx_http_core_module 模块实现的,且普通
* 的 HTTP 模块无法重定义 checker 方法
*/
ph->checker = ngx_http_core_find_config_phase;
n++;
ph++;
continue;
/*
* 在 NGX_HTTP_FIND_CONFIG_PHASE 阶段寻找到匹配的 location
* 之后再修改请求的 URI.
*/
case NGX_HTTP_REWRITE_PHASE:
if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
/*
* 表示 NGX_HTTP_REWRITE_PHASE 阶段第 1 个 ngx_http_phase_handler_t 处理
* 方法在 handlers 数组中的序号,用于在执行 HTTP 请求的任何阶段中快速跳转
* 到 NGX_HTTP_REWRITE_PHASE 阶段处理请求.
*/
cmcf->phase_engine.location_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
break;
/* 这一阶段是用于在rewrite重写URL后,防止错误的nginx.conf配置导致死循环(递归地修改URI),
* 因此,这一阶段仅由ngx_http_core_module模块处理。目前,控制死循环的方式很简单,首先
* 检查 rewrite 的次数,如果一个请求超过10次重定向,就认为进入了rewrite死循环,这时在
* NGX_HTTP_POST_REWRITE_PHASE阶段就会向用户返回500,表示服务器内部错误 */
case NGX_HTTP_POST_REWRITE_PHASE:
if (use_rewrite) {
ph->checker = ngx_http_core_post_rewrite_phase;
/* 将要执行的下一个HTTP处理阶段的序号.
* next的设计使得处理阶段不必按顺序依次执行,既可以向后跳跃数个阶段继续执行,
* 也可以跳跃到之前曾经执行过的某个阶段重新执行。通常,next表示下一个处理阶
* 段中的第1个 ngx_http_phase_handler_t 处理方法 */
ph->next = find_config_index;
n++;
ph++;
}
continue;
/* 这个阶段用于让HTTP模块判断是否允许这个请求访问Nginx服务器 */
case NGX_HTTP_ACCESS_PHASE:
checker = ngx_http_core_access_phase;
n++;
break;
/* 在NGX_HTTP_ACCESS_PHASE阶段中,当HTTP模块的handler处理函数返回不允许
* 访问的错误码时(实际就是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED),
* 这里将负责向用户发送拒绝服务的错误响应。因此,这个阶段实际上用于给
* NGX_HTTP_ACCESS_PHASE阶段收尾 */
case NGX_HTTP_POST_ACCESS_PHASE:
if (use_access) {
ph->checker = ngx_http_core_post_access_phase;
ph->next = n;
ph++;
}
continue;
/* 这个阶段完全是为 try_files 配置项而设立的,当 HTTP 请求访问静态文件资源时,
* try_files 配置项可以使这个请求顺序地访问多个静态文件资源,如果某一次访
* 问失败,则继续访问 try_files 中指定的下一个静态资源。这个功能完全是在
* NGX_HTTP_TRY_FILES_PHASE 阶段实现的 */
case NGX_HTTP_TRY_FILES_PHASE:
if (cmcf->try_files) {
ph->checker = ngx_http_core_try_files_phase;
n++;
ph++;
}
continue;
/* 用于处理HTTP请求内容的阶段,这是大部分HTTP模块最愿意介入的阶段 */
case NGX_HTTP_CONTENT_PHASE:
checker = ngx_http_core_content_phase;
break;
/* 对于 NGX_HTTP_POST_READ_PHASE、NGX_HTTP_PREACCESS_PHASE 以及
* NGX_HTTP_LOG_PHASE 这三个阶段,统一使用同一个 checket 方法 */
default:
checker = ngx_http_core_generic_phase;
}
n += cmcf->phases[i].handlers.nelts;
/* 仅在当前阶段有 HTTP 模块介入自己的处理方法才会执行下面的语句 */
for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {
ph->checker = checker;
ph->handler = h[j];
ph->next = n;
ph++;
}
}
return NGX_OK;
}
9. ngx_http_optimize_servers
/*
* 每监听一个 TCP 端口,都将使用一个独立的 ngx_http_conf_port_t 结构体
* 表示。这个保存着监听端口的 ngx_http_conf_port_t 将由全局的
* ngx_http_core_main_conf_t 结构体保存.
*/
typedef struct {
/* socket 地址家族 */
ngx_int_t family;
/* 监听端口 */
in_port_t port;
/* 监听端口下对应着的所有 ngx_http_conf_addr_t 地址 */
ngx_array_t addrs; /* array of ngx_http_conf_addr_t */
} ngx_http_conf_port_t;
static ngx_int_t
ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
ngx_array_t *ports)
{
ngx_uint_t p, a;
ngx_http_conf_port_t *port;
ngx_http_conf_addr_t *addr;
if (ports == NULL) {
return NGX_OK;
}
port = ports->elts;
/* 遍历所有监听端口 */
for (p = 0; p < ports->nelts; p++) {
ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);
/*
* check whether all name-based servers have the same
* configuration as a default server for given address:port
*/
addr = port[p].addrs.elts;
for (a = 0; a < port[p].addrs.nelts; a++) {
if (addr[a].servers.nelts > 1
#if (NGX_PCRE)
|| addr[a].default_server->captures
#endif
)
{
if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
return NGX_ERROR;
}
}
}
/* 初始化监听端口 */
if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
9.1 ngx_http_init_listening
static ngx_int_t
ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
{
ngx_uint_t i, last, bind_wildcard;
ngx_listening_t *ls;
ngx_http_port_t *hport;
ngx_http_conf_addr_t *addr;
addr = port->addrs.elts;
last = port->addrs.nelts;
/*
* If there is a binding to an "*:port" then we need to bind() to
* the "*:port" only and ignore other implicit bindings. The bindings
* have been already sorted: explicit bindings are on the start, then
* implicit bindings go, and wildcard binding is in the end.
*/
if (addr[last - 1].opt.wildcard) {
addr[last - 1].opt.bind = 1;
bind_wildcard = 1;
} else {
bind_wildcard = 0;
}
i = 0;
while (i < last) {
if (bind_wildcard && !addr[i].opt.bind) {
i++;
continue;
}
ls = ngx_http_add_listening(cf, &addr[i]);
if (ls == NULL) {
return NGX_ERROR;
}
hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
if (hport == NULL) {
return NGX_ERROR;
}
ls->servers = hport;
hport->naddrs = i + 1;
switch (ls->sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {
return NGX_ERROR;
}
break;
#endif
default: /* AF_INET */
if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
return NGX_ERROR;
}
break;
}
if (ngx_clone_listening(cf, ls) != NGX_OK) {
return NGX_ERROR;
}
addr++;
last--;
}
return NGX_OK;
}
9.2 ngx_http_add_listening
static ngx_listening_t *
ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
{
ngx_listening_t *ls;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
/* 分配并初始化一个 ngx_listening_t 结构体,该结构体代表了
* 一个具体监听的端口 */
ls = ngx_create_listening(cf, &addr->opt.sockaddr.sockaddr,
addr->opt.socklen);
if (ls == NULL) {
return NULL;
}
/* 标志位,为 1 表示将网络地址转换为字符串形式的地址 */
ls->addr_ntop = 1;
/* 当新的 TCP 连接成功建立后的处理方法,即 accept 返回后调用的
* 处理方法 */
ls->handler = ngx_http_init_connection;
/* 该监听端口下对应的默认 server{} 虚拟主机 */
cscf = addr->default_server;
/*
* 如果为新的 TCP 连接创建内存池,则内存池的初始大小应该是 pool_size,
* 若配置文件中没有指定 tcp 连接时创建的内存池大小,则使用默认的大小
* 为 256 字节 */
ls->pool_size = cscf->connection_pool_size;
/*
* TCP_DEFER_ACCEPT 选项将在建立 TCP 连接成功且接收到用户的请求数据后,
* 才向对监听套接字感兴趣的进程发送事件通知,而连接建立成功后,如果
* post_accept_timeout 秒后仍然没有收到用户数据,则内核直接丢弃连接 */
ls->post_accept_timeout = cscf->client_header_timeout;
clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
ls->logp = clcf->error_log;
ls->log.data = &ls->addr_text;
ls->log.handler = ngx_accept_log_error;
#if (NGX_WIN32)
{
ngx_iocp_conf_t *iocpcf = NULL;
if (ngx_get_conf(cf->cycle->conf_ctx, ngx_events_module)) {
iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
}
if (iocpcf && iocpcf->acceptex_read) {
ls->post_accept_buffer_size = cscf->client_header_buffer_size;
}
}
#endif
ls->backlog = addr->opt.backlog;
ls->rcvbuf = addr->opt.rcvbuf;
ls->sndbuf = addr->opt.sndbuf;
ls->keepalive = addr->opt.so_keepalive;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
ls->keepidle = addr->opt.tcp_keepidle;
ls->keepintvl = addr->opt.tcp_keepintvl;
ls->keepcnt = addr->opt.tcp_keepcnt;
#endif
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
ls->accept_filter = addr->opt.accept_filter;
#endif
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
ls->deferred_accept = addr->opt.deferred_accept;
#endif
#if (NGX_HAVE_INET6)
ls->ipv6only = addr->opt.ipv6only;
#endif
#if (NGX_HAVE_SETFIB)
ls->setfib = addr->opt.setfib;
#endif
#if (NGX_HAVE_TCP_FASTOPEN)
ls->fastopen = addr->opt.fastopen;
#endif
#if (NGX_HAVE_REUSEPORT)
ls->reuseport = addr->opt.reuseport;
#endif
return ls;
}
9.3 ngx_create_listening
该函数是构造一个 ngx_listening_t 结构体,每一个该结构体代表一个监听端口.
ngx_listening_t *
ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,
socklen_t socklen)
{
size_t len;
ngx_listening_t *ls;
struct sockaddr *sa;
u_char text[NGX_SOCKADDR_STRLEN];
/* listening 是动态数组,每个数组元素存储着 ngx_listening_t 成员,
* 表示监听端口及相关的参数 */
ls = ngx_array_push(&cf->cycle->listening);
if (ls == NULL) {
return NULL;
}
ngx_memzero(ls, sizeof(ngx_listening_t));
sa = ngx_palloc(cf->pool, socklen);
if (sa == NULL) {
return NULL;
}
ngx_memcpy(sa, sockaddr, socklen);
/* 监听 sockaddr 地址 */
ls->sockaddr = sa;
ls->socklen = socklen;
/* 这里是将ip地址转换为点分十进制格式的字符串(若有端口,则加上端口:address:port)
* 假设当前 listen 的配置为 listen 80; 则此时返回的 text 为 "0.0.0.0:80" */
len = ngx_sock_ntop(sa, socklen, text, NGX_SOCKADDR_STRLEN, 1);
ls->addr_text.len = len;
/* 根据协议族,确定该 ip 对应的字符串的最大长度 */
switch (ls->sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN;
break;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
case AF_UNIX:
ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN;
len++;
break;
#endif
case AF_INET:
ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
break;
default:
ls->addr_text_max_len = NGX_SOCKADDR_STRLEN;
break;
}
ls->addr_text.data = ngx_pnalloc(cf->pool, len);
if (ls->addr_text.data == NULL) {
return NULL;
}
/* 将字符串形式的 ip跟端口拷贝到 addr_text 中 */
ngx_memcpy(ls->addr_text.data, text, len);
ls->fd = (ngx_socket_t) -1;
ls->type = SOCK_STREAM;
/* TCP实现监听时的backlog队列,它表示允许正在通过三次握手
* 建立TCP连接但还没有任何进程开始处理的连接最大个数 */
ls->backlog = NGX_LISTEN_BACKLOG;
ls->rcvbuf = -1;
ls->sndbuf = -1;
#if (NGX_HAVE_SETFIB)
ls->setfib = -1;
#endif
#if (NGX_HAVE_TCP_FASTOPEN)
ls->fastopen = -1;
#endif
return ls;
}