Nginx-HTTP之框架的初始化

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;
}

猜你喜欢

转载自www.cnblogs.com/jimodetiantang/p/9207036.html