ホームページ バックエンド開発 PHPチュートリアル Nginx での HTTP モジュールの初期化

Nginx での HTTP モジュールの初期化

Aug 08, 2016 am 09:30 AM
conf http module nbsp

概要

前回の記事では『 「Nginx構成解析」では一般モジュールの構成項目解析について簡単に説明し、HTTPモジュールの構成項目解析処理については大まかに説明しています。本記事ではより詳細な解析を掲載しています。 HTTP モジュールの初期化処理。 HTTP モジュールの初期化プロセスには主に、コンテキスト構造の初期化、構成アイテムの解析、構成アイテムのマージ、サーバー関連のポート設定が含まれます。

HTTP モジュールインターフェース

ngx_http_module_t 構造体

Nginx では、構造体 ngx_module_tNginx モジュールの最も基本的なインターフェースです。異なるタイプのモジュールごとに、このタイプのモジュールの共通インターフェイスを記述する特定の構造があります。 Nginx で定義 HTTP ファイル src/http/ngx_http_config.h で定義されているモジュール ngx_http_module_t 構造の共通インターフェース: http{}, server{}, location{ } ブロックの設定項目は、それぞれmainsrvlocレベルの設定項目と呼ばれます。

/* 所有HTTP模块的通用接口结构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、srv级别的配置项值合并到loc级别的配置项中;
     */
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
ログイン後にコピー
xngx_http_conf_ctx_t 構造体

Http モジュールでは、Http モジュール構成の構造体は、create_main_conf によって作成された構造体ポインターで構成される配列によって実装されます。 create_srv_confcreate_loc_conf HTTPモジュールのメソッド。 ngx_http_conf_ctx_t 構造はファイルで定義されています src/http/ngx_http_config.h in:

/* HTTP框架的上下文结构体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_ctx_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_http_module コアモジュール

ngx_http_module

コアモジュール定義

ngx_http_module

HTTP モジュールのモジュール、このモジュールの機能は次のとおりです。新しい HTTP モジュール タイプを定義し、各 HTTP モジュールの共通インターフェースを定義する ngx_http_module_t 構造体は、HTTP モジュールによって生成された構成項目構造を管理し、HTTP クラス構成項目を解析します。このモジュールはファイルで定義されています src/http/ngx_http.c in:

/* 定义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
};
ログイン後にコピー
ngx_http_module コアモジュールとして、コアモジュールの一般的なインターフェースコンテキスト構造を定義する必要があり、その一般的なインターフェースコンテキスト構造はファイル

src/httpで定義されます/ngx_http.c: http モジュールの名前のみが ngx_http_module コア モジュールで定義されます。

/* 定义核心模块的通用接口上下文结构 */
static ngx_core_module_t  ngx_http_module_ctx = {
    ngx_string("http"),
    NULL,
    NULL
};
ログイン後にコピー
ngx_http_moduleモジュールで定義されています

http{} は、対象の構成項目配列をブロックします。構成項目配列はファイル内で定義されます。 src/http/ngx_http.c in:

/* 定义http模块感兴趣的配置项,即配置项指令数组 */
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
};
ログイン後にコピー
ngx_http_module モジュールの定義から、このモジュールには初期化処理メソッド

ngx_http_block が 1 つだけあることがわかります。 HTTP モジュールの初期化関数。 ngx_http_module

コアモジュールの初期化

上記のngx_http_module

モジュールの定義から、

HTTPモジュールの初期化プロセスは関数ngx_http_blockによって実装されることが既に知られています。関数が与えられ、その関数が特定の分析を実行します。この関数は、ファイル src/http/ngx_http.c で定義されています:

/* HTTP框架初始化 */
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;

    /* the main http context */

    /* 分配HTTP框架的上下文结构ngx_http_conf_ctx_t 空间 */
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    /*
     * conf 是结构体ngx_cycle_t 成员conf_ctx数组中的元素,
     * 该元素conf指向ngx_http_module模块所对应的配置项结构信息;
     */
    *(ngx_http_conf_ctx_t **) conf = ctx;


    /* count the number of the http modules and set up their indices */

    /* 初始化所有HTTP模块的ctx_index序号 */
    ngx_http_max_module = 0;
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        ngx_modules[m]->ctx_index = ngx_http_max_module++;
    }


    /*
     * 分配存储HTTP模块main级别下的main_conf配置项的空间;
     */
    /* the http main_conf context, it is the same in the all http contexts */

    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * 分配存储HTTP模块main级别下的srv_conf配置项的空间;
     */
    /*
     * the http null srv_conf context, it is used to merge
     * the server{}s' srv_conf's
     */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * 分配存储HTTP模块main级别下的loc_conf配置项的空间;
     */
    /*
     * the http null loc_conf context, it is used to merge
     * the server{}s' loc_conf's
     */

    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模块,为每个HTTP模块创建main级别的配置项结构main_conf、srv_conf、loc_conf;
     */
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        mi = ngx_modules[m]->ctx_index;

        /*
         * 调用create_main_conf创建main级别的配置项结构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创建main级别的配置项结构srv_conf;
         */
        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创建main级别的配置项结构loc_conf;
         */
        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的副本为pcf,待解析完毕后恢复cf;
     * 这里备份是由于配置指令解析函数ngx_conf_parse递归调用,因此为了不影响外层的调用环境;
     */
    pcf = *cf;
    /*
     * 把HTTP模块解析指令的上下文参数保存到配置项结构ngx_http_conf_ctx_t ctx中;
     */
    cf->ctx = ctx;/* 值-结果 模式 */

    /* 遍历所有HTTP模块,并调用每个模块的preconfiguration回调函数 */
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

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

    /*
     * 调用模块通用配置项解析函数ngx_conf_parse解析http{}块内的指令;
     */
    /* parse inside the http{} block */

    cf->module_type = NGX_HTTP_MODULE;/* 模块类型为HTTP模块 */
    cf->cmd_type = NGX_HTTP_MAIN_CONF;/* 指令类型为HTTP模块的main级别指令 */
    /*
     * 开始解析http{}块内的指令;
     *      这里必须注意的是:http{}块内可能会包含server{}块,
     * 而server{}可能会包含location{}块,location{}块会嵌套location{}块;
     *      还需注意的是http{}块内可能有多个server{}块,
     * location{}块也可能有多个location{}块;
     * 因此,配置项解析函数ngx_conf_parse是被递归调用的;*/
    rv = ngx_conf_parse(cf, NULL);

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


    /*
     * 解析完成http{}块内的所有指令后(包括server{}、location{}块的解析),
     * 进行下面的程序
     */
    /*
     * init http{} main_conf's, merge the server{}s' srv_conf's
     * and its location{}s' loc_conf's
     */

    /* 获取ngx_http_core_module模块的main_conf配置项结构 */
    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
    /* 获取所有srv_conf配置项结构 */
    cscfp = cmcf->servers.elts;

    /*
     * 遍历所有HTTP模块,并初始化每个HTTP模块的main_conf结构,
     * 同时合并srv_conf 结构(当然srv_conf结构里面包含loc_conf结构,所有也合并loc_conf结构);
     */
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        /*
         * ngx_modules[m]是一个ngx_module_t模块结构体,
         * 它的ctx成员相对于HTTP模块来说是ngx_http_module_t接口;
         */
        module = ngx_modules[m]->ctx;
        /* 获取当前HTTP模块在HTTP模块类的序号 */
        mi = ngx_modules[m]->ctx_index;


        /* 初始化HTTP模块的main_conf结构 */
        /* init http{} main_conf's */

        if (module->init_main_conf) {
            rv = module->init_main_conf(cf, ctx->main_conf[mi]);
            if (rv != NGX_CONF_OK) {
                goto failed;
            }
        }

        /* 合并当前HTTP模块不同级别的配置项结构 */
        rv = ngx_http_merge_servers(cf, cmcf, module, mi);
        if (rv != NGX_CONF_OK) {
            goto failed;
        }
    }


    /* 以下是监听端口管理的内容 */

    /* 静态二叉查找树来保存location配置 */
    /* create location trees */

    /* 遍历http{}块下的所有server{}块 */
    for (s = 0; s < cmcf->servers.nelts; s++) {

        /* 获取server{}块下location{}块所对应的ngx_http_core_loc_conf_t loc_conf结构体 */
        clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];

        /*
         * 将ngx_http_core_loc_conf_t 组成的双向链表按照location匹配字符串进行排序;
         * 注意:location{}块可能嵌套location{}块,所以该函数是递归调用;
         */
        if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
            return NGX_CONF_ERROR;
        }

        /*
         * 按照已排序的location{}的双向链表构建静态的二叉查找树,
         * 该方法也是递归调用;
         */
        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }


    /* 初始化可添加自定义处理方法的7个HTTP阶段的动态数组 */
    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    /* 将HTTP请求的头部header初始化成hash结构 */
    if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }


    /* 调用所有HTTP模块的postconfiguration方法 */
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

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

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

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

    *cf = pcf;


    /* 初始化phase_engine_handlers数组 */
    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }


    /* optimize the lists of ports, addresses and server names */

    /* 设置server与监听端口的关系,并设置新连接事件的处理方法 */
    if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;

failed:

    *cf = pcf;

    return rv;
}
ログイン後にコピー
上記の分析から、HTTP モジュールの初期化プロセスは次のように結論付けることができます:

  • Nginx 进程进入主循环,在主循环中调用配置解析器解析配置文件nginx.conf;
  • 在配置文件中遇到 http{} 块配置,则 HTTP 框架开始初始化并启动,其由函数 ngx_http_block() 实现;
    • HTTP 框架初始化所有 HTTP 模块的序列号,并创建 3 个类型为 ngx_http_conf_ctx_t 结构的数组用于存储所有HTTP 模块的create_main_confcreate_srv_confcreate_loc_conf方法返回的指针地址;
    • 调用每个 HTTP 模块的 preconfiguration 方法;
    • HTTP 框架调用函数 ngx_conf_parse() 开始循环解析配置文件 nginx.conf 中的http{}块里面的所有配置项,http{} 块内可嵌套多个server{} 块,而 server{} 块可嵌套多个 location{}location{} 依旧可以嵌套location{},因此配置项解析函数是递归调用;
    • HTTP 框架处理完毕 http{} 配置项,根据解析配置项的结果,必要时调用ngx_http_merge_servers 方法进行配置项合并处理,即合并mainsrvloc 级别下serverlocation 相关的配置项;
    • 初始化可添加处理方法的 HTTP 阶段的动态数组;
    • 调用所有 HTTP 模块的 postconfiguration 方法使 HTTP 模块可以处理HTTP 阶段,即将 HTTP 模块的 ngx_http_handler_pt 处理方法添加到HTTP 阶段中;
    • 根据 HTTP 模块处理 HTTP 阶段的方法构造 phase_engine_handlers 数组;
    • 构造 server 相关的监听端口,并设置新连接事件的回调方法为ngx_http_init_connection
  • 继续处理其他 http{} 块之外的配置项,直到配置文件解析器处理完所有配置项后通知Nginx 主循环配置项解析完毕。此时,Nginx 才会启动Web 服务器;

ngx_http_core_module 模块

ngx_http_core_main_conf_t 结构体

        在初始化 HTTP 模块的过程中,会调用配置项解析函数 ngx_conf_parse 解析 http{} 块内的配置项,当遇到server{} 块、 location{} 块配置项时,此时会调用配置项解析函数解析 server{}location{} 块,在解析这两个配置块的过程中依旧会创建属于该块的配置项结构srv_confloc_conf,因此就会导致与http{} 块所创建的配置项结构体重复,这时候就需要对这些配置项进行管理与合并。首先先看下结构体ngx_http_core_main_conf_tngx_http_core_main_conf_tngx_http_core_modulemain_conf,存储了 http{} 层的配置参数。该结构体定义在文件src/http/ngx_http_core_module.h中:

/* ngx_http_core_main_conf_t 结构体 */
typedef struct {
    /* 指针数组,每个指针指向表示server{}块的ngx_http_core_srv_conf_t结构体地址 */
    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */

    /* 各HTTP阶段处理方法构成的phases数组构建的阶段索引 */
    ngx_http_phase_engine_t    phase_engine;

    ngx_hash_t                 headers_in_hash;

    ngx_hash_t                 variables_hash;

    ngx_array_t                variables;       /* ngx_http_variable_t */
    ngx_uint_t                 ncaptures;

    /* 配置项中散列桶bucket最大数量 */
    ngx_uint_t                 server_names_hash_max_size;
    /* 配置项中每个散列桶bucket占用内存的最大值 */
    ngx_uint_t                 server_names_hash_bucket_size;

    ngx_uint_t                 variables_hash_max_size;
    ngx_uint_t                 variables_hash_bucket_size;

    ngx_hash_keys_arrays_t    *variables_keys;

    /* 存放http{}配置块下监听的所有ngx_http_conf_port_t 端口*/
    ngx_array_t               *ports;

    ngx_uint_t                 try_files;       /* unsigned  try_files:1 */

    /*
     * 在HTTP模块初始化时,使各HTTP模块在HTTP阶段添加处理方法,
     * 数组中每一个成员ngx_http_phase_t结构对应一个HTTP阶段;
     */
    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;
ログイン後にコピー

ngx_http_core_srv_conf_t 结构体

        结构体 ngx_http_core_srv_conf_tngx_http_core_srv_conf_tngx_http_core_modulesrv_conf,存储了 server{} 层的配置参数。该结构体定义在文件src/http/ngx_http_core_module.h中:

/* ngx_http_core_srv_conf_t结构体 */
typedef struct {
    /* array of the ngx_http_server_name_t, "server_name" directive */
    ngx_array_t                 server_names;

    /* server ctx */
    /* 指向当前server{}块所属的ngx_http_conf_ctx_t结构体 */
    ngx_http_conf_ctx_t        *ctx;

    /* 当前server{}块的虚拟主机名 */
    ngx_str_t                   server_name;

    size_t                      connection_pool_size;
    size_t                      request_pool_size;
    size_t                      client_header_buffer_size;

    ngx_bufs_t                  large_client_header_buffers;

    ngx_msec_t                  client_header_timeout;

    ngx_flag_t                  ignore_invalid_headers;
    ngx_flag_t                  merge_slashes;
    ngx_flag_t                  underscores_in_headers;

    unsigned                    listen:1;
#if (NGX_PCRE)
    unsigned                    captures:1;
#endif

    ngx_http_core_loc_conf_t  **named_locations;
} ngx_http_core_srv_conf_t;
ログイン後にコピー

ngx_http_core_loc_conf_t 结构体

        结构体 ngx_http_core_loc_conf_tngx_http_core_loc_conf_tngx_http_core_moduleloc_conf,存储了 location{} 层的配置参数。该结构体定义在文件src/http/ngx_http_core_module.h中:

struct ngx_http_core_loc_conf_s {
    /* location名称,即nginx.conf配置文件中location后面的表达式 */
    ngx_str_t     name;          /* location name */

#if (NGX_PCRE)
    ngx_http_regex_t  *regex;
#endif

    unsigned      noname:1;   /* "if () {}" block or limit_except */
    unsigned      lmt_excpt:1;
    unsigned      named:1;

    unsigned      exact_match:1;
    unsigned      noregex:1;

    unsigned      auto_redirect:1;
#if (NGX_HTTP_GZIP)
    unsigned      gzip_disable_msie6:2;
#if (NGX_HTTP_DEGRADATION)
    unsigned      gzip_disable_degradation:2;
#endif
#endif

    ngx_http_location_tree_node_t   *static_locations;
#if (NGX_PCRE)
    ngx_http_core_loc_conf_t       **regex_locations;
#endif

    /* 指向所属location{}块内ngx_http_conf_ctx_t 结构体中的loc_conf指针数组 */
    /* pointer to the modules' loc_conf */
    void        **loc_conf;

    uint32_t      limit_except;
    void        **limit_except_loc_conf;

    ngx_http_handler_pt  handler;

    /* location name length for inclusive location with inherited alias */
    size_t        alias;
    ngx_str_t     root;                    /* root, alias */
    ngx_str_t     post_action;

    ngx_array_t  *root_lengths;
    ngx_array_t  *root_values;

    ngx_array_t  *types;
    ngx_hash_t    types_hash;
    ngx_str_t     default_type;

    off_t         client_max_body_size;    /* client_max_body_size */
    off_t         directio;                /* directio */
    off_t         directio_alignment;      /* directio_alignment */

    size_t        client_body_buffer_size; /* client_body_buffer_size */
    size_t        send_lowat;              /* send_lowat */
    size_t        postpone_output;         /* postpone_output */
    size_t        limit_rate;              /* limit_rate */
    size_t        limit_rate_after;        /* limit_rate_after */
    size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
    size_t        read_ahead;              /* read_ahead */

    ngx_msec_t    client_body_timeout;     /* client_body_timeout */
    ngx_msec_t    send_timeout;            /* send_timeout */
    ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */
    ngx_msec_t    lingering_time;          /* lingering_time */
    ngx_msec_t    lingering_timeout;       /* lingering_timeout */
    ngx_msec_t    resolver_timeout;        /* resolver_timeout */

    ngx_resolver_t  *resolver;             /* resolver */

    time_t        keepalive_header;        /* keepalive_timeout */

    ngx_uint_t    keepalive_requests;      /* keepalive_requests */
    ngx_uint_t    keepalive_disable;       /* keepalive_disable */
    ngx_uint_t    satisfy;                 /* satisfy */
    ngx_uint_t    lingering_close;         /* lingering_close */
    ngx_uint_t    if_modified_since;       /* if_modified_since */
    ngx_uint_t    max_ranges;              /* max_ranges */
    ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */

    ngx_flag_t    client_body_in_single_buffer;
                                           /* client_body_in_singe_buffer */
    ngx_flag_t    internal;                /* internal */
    ngx_flag_t    sendfile;                /* sendfile */
#if (NGX_HAVE_FILE_AIO)
    ngx_flag_t    aio;                     /* aio */
#endif
    ngx_flag_t    tcp_nopush;              /* tcp_nopush */
    ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
    ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
    ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
    ngx_flag_t    port_in_redirect;        /* port_in_redirect */
    ngx_flag_t    msie_padding;            /* msie_padding */
    ngx_flag_t    msie_refresh;            /* msie_refresh */
    ngx_flag_t    log_not_found;           /* log_not_found */
    ngx_flag_t    log_subrequest;          /* log_subrequest */
    ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
    ngx_flag_t    server_tokens;           /* server_tokens */
    ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */
    ngx_flag_t    etag;                    /* etag */

#if (NGX_HTTP_GZIP)
    ngx_flag_t    gzip_vary;               /* gzip_vary */

    ngx_uint_t    gzip_http_version;       /* gzip_http_version */
    ngx_uint_t    gzip_proxied;            /* gzip_proxied */

#if (NGX_PCRE)
    ngx_array_t  *gzip_disable;            /* gzip_disable */
#endif
#endif

#if (NGX_HAVE_OPENAT)
    ngx_uint_t    disable_symlinks;        /* disable_symlinks */
    ngx_http_complex_value_t  *disable_symlinks_from;
#endif

    ngx_array_t  *error_pages;             /* error_page */
    ngx_http_try_file_t    *try_files;     /* try_files */

    ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */

    ngx_open_file_cache_t  *open_file_cache;
    time_t        open_file_cache_valid;
    ngx_uint_t    open_file_cache_min_uses;
    ngx_flag_t    open_file_cache_errors;
    ngx_flag_t    open_file_cache_events;

    ngx_log_t    *error_log;

    ngx_uint_t    types_hash_max_size;
    ngx_uint_t    types_hash_bucket_size;

    /* 将同一个server{}块内多个表达location{}块的ngx_http_core_loc_conf_t 结构体以双向链表方式组织,
     * 该指针指向ngx_http_location_queue_t结构体
     */
    ngx_queue_t  *locations;

#if 0
    ngx_http_core_loc_conf_t  *prev_location;
#endif
};

typedef struct{

      /* 作为ngx_queue_t 双向链表容器,将ngx_http_location_queue_t结构体连接起来 */
      ngx_queue_t queue;

      /* 若location中字符串可以精确匹配(包括正则表达式),
       * exact将指向对应的ngx_http_core_loc_conf_t结构体,否则为NULL
       */
       ngx_http_core_loc_conf_t *exact;

       /* 若location中字符串无精确匹配(包括自定义通配符),
       * inclusive将指向对应的ngx_http_core_loc_conf_t结构体,否则为NULL
       */
       ngx_http_core_loc_conf_t *inclusive;

       /* 指向location的名称 */
       ngx_str_t *name;
       ...

}ngx_http_location_queue_t; 
ログイン後にコピー

ngx_http_core_module 模块定义

        ngx_http_core_module 模块是 HTTP 模块中的第一个模块,该模块管理 srvloc 级别的配置项结构。该模块在文件src/http/ngx_http_core_module.c中定义:

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
};
ログイン後にコピー

        在模块的定义中,其中定义了 HTTP 模块的上下文结构 ngx_http_core_module_ctx,该上下文结构体定义如下:

static ngx_http_module_t  ngx_http_core_module_ctx = {
    ngx_http_core_preconfiguration,        /* preconfiguration */
    NULL,                                  /* 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 */
};
ログイン後にコピー

        由于该模块中感兴趣的配置项太多,这里只列出 serverlocation 配置项。定义如下:

    ...

    { ngx_string("server"),
      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_http_core_server,
      0,
      0,
      NULL },

    ...

    { ngx_string("location"),
      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
      ngx_http_core_location,
      NGX_HTTP_SRV_CONF_OFFSET,
      0,
      NULL },

      ...
      ngx_null_command
};
ログイン後にコピー

管理 HTTP 模块不同级别的配置项结构体

        在 HTTP 模块的 http{} 配置项解析过程中,可能遇到多个嵌套 server{} 块以及location{},不同块之间的解析都会创建相应的结构体保存配置项参数,但是由于属于嵌套关系,所有必须管理好不同块之间的配置项结构体,方便解析完毕后合并相应的配置项,以下针对不同级别的配置项结构体进行分析。

main 级别的配置项结构体

        在 ngx_http_module 模块 http{} 块配置项解析的初始化过程中由函数 ngx_http_block 实现,在实现过程中创建并初始化了HTTP 模块 main 级别的配置项 main_confsrv_confloc_conf 结构体。main 级别的配置项结构体之间的关系如下图所示:

server 级别的配置项结构体

        在 ngx_http_module 模块在调用函数 ngx_conf_parse 解析 http{} 块main 级别配置项时,若遇到 server{} 块配置项,则会递归调用函数 ngx_conf_parse 解析ngx_http_core_module 模块中 server{} 块配置项,并调用方法ngx_http_core_server 初始化 server{} 块 ,该方法创建并初始化了 HTTP 模块srv 级别的配置项 srv_confloc_conf 结构体。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;

    /* 分配HTTP框架的上下文结构ngx_http_conf_ctx_t */
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    /* 其中main_conf将指向所属于http{}块下ngx_http_conf_ctx_t 结构体的main_conf指针数组 */
    http_ctx = cf->ctx;
    ctx->main_conf = http_ctx->main_conf;

    /* the server{}'s srv_conf */

    /* 分配存储HTTP模块srv级别下的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 */

    /* 分配存储HTTP模块srv级别下的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模块,为每个模块创建srv级别的配置项结构srv_conf、loc_conf */
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[i]->ctx;

        /* 调用create_srv_conf创建srv级别的配置项结构srv_conf */
        if (module->create_srv_conf) {
            mconf = module->create_srv_conf(cf);
            if (mconf == NULL) {
                return NGX_CONF_ERROR;
            }

            ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;
        }

        /* 调用create_loc_conf创建srv级别的配置项结构loc_conf */
        if (module->create_loc_conf) {
            mconf = module->create_loc_conf(cf);
            if (mconf == NULL) {
                return NGX_CONF_ERROR;
            }

            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
        }
    }


    /*
     * 将属于当前server{}块的ngx_http_core_srv_conf_t 添加到
     * 结构体ngx_http_core_main_conf_t成员servers的动态数组中;
     */
    /* the server configuration context */

    cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
    cscf->ctx = ctx;


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

    cscfp = ngx_array_push(&cmcf->servers);
    if (cscfp == NULL) {
        return NGX_CONF_ERROR;
    }

    *cscfp = cscf;


    /* 解析当前server{}块下的全部srv级别的配置项 */
    /* parse inside server{} */

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

    rv = ngx_conf_parse(cf, NULL);

    /* 设置listen监听端口 */
    *cf = pcf;

    if (rv == NGX_CONF_OK && !cscf->listen) {
        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

        sin = &lsopt.u.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.u.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;
}
ログイン後にコピー

srv 级别的配置项结构体之间的关系如下图所示:

location 级别的配置项结构体

        在 ngx_http_core_module 模块在调用函数ngx_conf_parse 解析 server{} 块srv 级别配置项时,若遇到 location{} 块配置项,则会递归调用函数 ngx_conf_parse 解析ngx_http_core_module 模块中 location{} 块配置项,并调用方法ngx_http_core_location 初始化 location{} 块 ,该方法创建并初始化了 HTTP 模块loc 级别的配置项 loc_conf 结构体。location{} 块配置项的初始化函数创建配置项结构体的源码如下所示:

static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
    char                      *rv;
    u_char                    *mod;
    size_t                     len;
    ngx_str_t                 *value, *name;
    ngx_uint_t                 i;
    ngx_conf_t                 save;
    ngx_http_module_t         *module;
    ngx_http_conf_ctx_t       *ctx, *pctx;
    ngx_http_core_loc_conf_t  *clcf, *pclcf;

    /* 分配HTTP框架的上下文结构ngx_http_conf_ctx_t */
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    /*
     * 其中main_conf、srv_conf将指向所属于server{}块下ngx_http_conf_ctx_t 结构体
     * 的main_conf、srv_conf指针数组;
     */
    pctx = cf->ctx;
    ctx->main_conf = pctx->main_conf;
    ctx->srv_conf = pctx->srv_conf;

    /* 分配存储HTTP模块loc级别下的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模块,为每个模块创建loc级别的配置项结构体loc_conf */
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[i]->ctx;

        /* 调用模块的create_loc_conf创建loc级别的配置项结构体loc_conf */
        if (module->create_loc_conf) {
            ctx->loc_conf[ngx_modules[i]->ctx_index] =
                                                   module->create_loc_conf(cf);
            /* 将loc_conf配置项结构体按照ctx_index顺序保存到loc_conf指针数组中 */
            if (ctx->loc_conf[ngx_modules[i]->ctx_index] == NULL) {
                 return NGX_CONF_ERROR;
            }
        }
    }

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

    value = cf->args->elts;

    /* 以下是对正则表达式的处理 */
    if (cf->args->nelts == 3) {

        len = value[1].len;
        mod = value[1].data;
        name = &value[2];

        if (len == 1 && mod[0] == '=') {

            clcf->name = *name;
            clcf->exact_match = 1;

        } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {

            clcf->name = *name;
            clcf->noregex = 1;

        } else if (len == 1 && mod[0] == '~') {

            if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
                return NGX_CONF_ERROR;
            }

        } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {

            if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
                return NGX_CONF_ERROR;
            }

        } else {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid location modifier \"%V\"", &value[1]);
            return NGX_CONF_ERROR;
        }

    } else {

        name = &value[1];

        if (name->data[0] == '=') {

            clcf->name.len = name->len - 1;
            clcf->name.data = name->data + 1;
            clcf->exact_match = 1;

        } else if (name->data[0] == '^' && name->data[1] == '~') {

            clcf->name.len = name->len - 2;
            clcf->name.data = name->data + 2;
            clcf->noregex = 1;

        } else if (name->data[0] == '~') {

            name->len--;
            name->data++;

            if (name->data[0] == '*') {

                name->len--;
                name->data++;

                if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
                    return NGX_CONF_ERROR;
                }

            } else {
                if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
                    return NGX_CONF_ERROR;
                }
            }

        } else {

            clcf->name = *name;

            if (name->data[0] == '@') {
                clcf->named = 1;
            }
        }
    }

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

    if (pclcf->name.len) {

        /* nested location */

#if 0
        clcf->prev_location = pclcf;
#endif

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

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

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

        len = pclcf->name.len;

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

    /* 将ngx_http_location_queue_t添加到双向链表中 */
    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

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

    /* 解析当前location{}块下的所有loc级别配置项 */
    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    return rv;
}
ログイン後にコピー

        loc 级别的配置项结构体之间的关系如下图所示:若 location 是精确匹配、正则表达式、@命名则exact 字段有效,否则就是 inclusive 字段有效,画图过程中只画出 exact 字段有效。


合并配置项

        HTTP 框架解析完毕 http{} 块配置项时,会根据解析的结果进行合并配置项操作,即合并 http{}server{}location{} 不同级别下各 HTTP 模块生成的存放配置项的结构体。其合并过程在文件 src/http/ngx_http.c中定义,如下所示:

  • HTTP 模块实现了 merge_srv_conf 方法,则将 http{} 块下由 create_srv_conf 生成的 main 级别结构体与遍历每一个 server{}块下由 create_srv_conf 生成的 srv 级别的配置项结构体进行 merge_srv_conf 操作;
  • HTTP 模块实现了 merge_loc_conf 方法,则将 http{} 块下由 create_loc_conf 生成的 main 级别的配置项结构体与嵌套在每一个server{} 块下由 create_loc_conf 生成的 srv级别的配置项结构体进行merge_loc_conf 操作;
  • HTTP 模块实现了 merge_loc_conf 方法,由于在上一步骤已经将mainsrv级别由create_loc_conf 生成的结构体进行合并,只要把上一步骤合并的结果在 server{} 块下与嵌套每一个location{}块下由create_loc_conf 生成的配置项结构体再次进行merge_loc_conf 操作;
  • HTTP 模块实现了 merge_loc_conf 方法,则将上一步骤的合并结果与与嵌套每一个location{}块下由 create_loc_conf 生成的的配置项结构体再次进行merge_loc_conf 操作;

具体合并过程如下图所示:

/* 合并配置项操作 */
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;

    /*
     * ngx_http_core_main_conf_t 中的成员servers是指针数组,
     * servers数组中的指针指向ngx_http_core_srv_conf_t结构体;
     */
    cscfp = cmcf->servers.elts;
    ctx = (ngx_http_conf_ctx_t *) cf->ctx;
    saved = *ctx;
    rv = NGX_CONF_OK;

    /* 遍历每一个server{}块内对应的ngx_http_core_srv_conf_t结构体 */
    for (s = 0; s < cmcf->servers.nelts; s++) {

        /* merge the server{}s' srv_conf's */

        /* srv_conf指向所有HTTP模块产生的server相关的srv级别配置项结构体 */
        ctx->srv_conf = cscfp[s]->ctx->srv_conf;

        /*
         * 这里合并http{}块下main、server{}块下srv级别与server相关的配置项结构;
         *
         * 若定义了merge_srv_conf 方法;
         * 则将当前HTTP模块在http{}块下由create_srv_conf 生成的结构体
         * 与遍历每个server{}块由create_srv_conf生成的配置项结构体进行merge_srv_conf合并操作;
         * saved.srv_conf[ctx_index]表示当前HTTP模块在http{}块下由create_srv_conf方法创建的结构体;
         * cscfp[s]->ctx->srv_conf[ctx_index]表示当前HTTP模块在server{}块下由create_srv_conf方法创建的结构体;
         */
        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;
            }
        }

        /*
         * 这里合并http{}块下main、server{}块下srv级别与location相关的配置项结构;
         *
         * 若定义了merge_loc_conf 方法;
         * 则将当前HTTP模块在http{}块下由create_loc_conf 生成的结构体
         * 与嵌套在server{}块内由create_loc_conf生成的配置项结构体进行merge_loc_conf合并操作;
         *
         * 其中saved.loc_conf[ctx_index]表示当前HTTP模块在http{}块下由create_loc_conf方法生成的配置项结构体;
         * cscfp[s]->ctx->loc_conf[ctx_index]表示当前HTTP模块在server{}块下由create_loc_conf方法创建的配置项结构体;
         */
        if (module->merge_loc_conf) {

            /* merge the server{}'s loc_conf */

            ctx->loc_conf = cscfp[s]->ctx->loc_conf;

            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 */

            /*
             * 若定义了merge_loc_conf 方法;
             * 则进行server{}块下create_loc_conf 生成的结构体与嵌套location{}块配置项生成的结构体进行merge_loc_conf操作;
             */

            /* clcf表示ngx_http_core_module模块在server{}块下由create_loc_conf方法创建的ngx_http_core_loc_conf_t 结构体 */
            clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];

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


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;

    /*
     * 若定义了merge_loc_conf 方法;
     * 则进行location{}块下create_loc_conf 生成的结构体与嵌套location{}块配置项生成的结构体进行merge_loc_conf操作;
     */

    /* 遍历locations双向链表 */
    for (q = ngx_queue_head(locations);
         q != ngx_queue_sentinel(locations);
         q = ngx_queue_next(q))
    {
        lq = (ngx_http_location_queue_t *) q;

        /* exact 与 inclusive 的区别在文章中已经说过 */
        clcf = lq->exact ? lq->exact : lq->inclusive;
        /* 获取由create_loc_conf方法创建的结构体指针 */
        ctx->loc_conf = clcf->loc_conf;

        /* 合并srv、loc级别的location相关的配置项结构 */
        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;
}
ログイン後にコピー

HTTP 请求处理阶段

        按照下列顺序将各个模块设置的phase handler依次加入cmcf->phase_engine.handlers列表,各个phasephase handlerchecker不同。checker主要用于限定某个phase的框架逻辑,包括处理返回值。 在 Nginx 定义了 11 个处理阶段,有一部分是不能添加 phase handler 方法的。在文件 src/http/ngx_http_core_module.h中定义,如下所示:

/* HTTP请求的11个处理阶段 */
typedef enum {
    /* 接收到完整的HTTP头部后处理的HTTP阶段,可自定义handler处理方法 */
    NGX_HTTP_POST_READ_PHASE = 0,

    /* 将请求的URI与location表达式匹配前,修改请求的URI的HTTP阶段,可自定义handler处理方法 */
    NGX_HTTP_SERVER_REWRITE_PHASE,

    /* 根据请求的URI寻找匹配的location表达式,只能由ngx_http_core_module模块实现,
     * 且不可自定义handler处理方法 */
    NGX_HTTP_FIND_CONFIG_PHASE,
    /* 在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location之后再修改请求的URI,
     * 可自定义handler处理方法 */
    NGX_HTTP_REWRITE_PHASE,
    /* 在rewrite重写URI后,防止错误的nginx.conf配置导致死循环,
     * 只能用ngx_http_core_module模块处理,不可自定义handler处理方法 */
    NGX_HTTP_POST_REWRITE_PHASE,

    /* 在处理NGX_HTTP_ACCESS_PHASE阶段决定请求的访问权限前,处理该阶段,可自定义handler处理方法 */
    NGX_HTTP_PREACCESS_PHASE,

    /* 由HTTP模块判断是否允许请求访问Nginx服务器,可自定义handler处理方法 */
    NGX_HTTP_ACCESS_PHASE,
    /* 向用户发送拒绝服务的错误响应,不可自定义handler处理方法 */
    NGX_HTTP_POST_ACCESS_PHASE,

    /* 使请求顺序的访问多个静态文件资源,不可自定义handler处理方法 */
    NGX_HTTP_TRY_FILES_PHASE,
    /* 处理HTTP请求内容,可自定义handler处理方法 */
    NGX_HTTP_CONTENT_PHASE,

    /* 处理完请求后记录日志阶段,可自定义handler处理方法 */
    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

ログイン後にコピー

每个HTTPchecker方法与handler处理如下所示:

typedef struct ngx_http_phase_handler_s  ngx_http_phase_handler_t;

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框架中所有的checker方法都有ngx_http_core_module模块实现,其他普通模块不能对其重定义;
     * 在处理某一个HTTP阶段时,HTTP框架会首先调用checker方法,然后在checker方法里面再调用handler方法;
     */
    ngx_http_phase_handler_pt  checker;
    /* 由HTTP模块实现的handler方法处理HTTP阶段,一般用于普通HTTP模块 */
    ngx_http_handler_pt        handler;
    /* 下一个将要执行的HTTP阶段 */
    ngx_uint_t                 next;
};
ログイン後にコピー

        完成 http{} 块的解析后,根据 nginx.conf 文件中配置产生由 ngx_http_phase_handler_t  组成的数组,在处理 HTTP 请求时,一般情况下按照阶段的方向顺序 phase handler 加入到回调表中。ngx_http_phase_engine_t 结构体由所有 ngx_http_phase_handler_t 组成的数组,如下所示:

typedef struct {
    /* 由ngx_http_phase_handler_t 构成的数组首地址,
     * 表示一个请求可能经历的所有ngx_http_handler_pt处理方法 */
    ngx_http_phase_handler_t  *handlers;
    /* 表示NGX_HTTP_SERVER_REWRITE_PHASE阶段第一个ngx_http_phase_handler_pt处理方法在handlers数组中的序号;*/
    ngx_uint_t                 server_rewrite_index;
    /* 表示NGX_HTTP_REWRITE_PHASE阶段第一个ngx_http_phase_handler_pt处理方法在handlers数组中的序号;*/
    ngx_uint_t                 location_rewrite_index;
} ngx_http_phase_engine_t;
ログイン後にコピー

        ngx_http_phase_engine_t 中保存在当前 nginx.conf 配置下,一个用户请求可能经历的所有 ngx_http_handler_pt 处理方法。

typedef struct {
    /* 保存在每一个HTTP模块初始化时添加到当前阶段的处理方法 */
    ngx_array_t                handlers;
} ngx_http_phase_t;

ログイン後にコピー

        在 HTTP模块初始化过程中,HTTP模块通过postconfiguration方法将自定义的方法添加到handler数组中,即该方法会被添加到phase_engine数组中。下面以NGX_HTTP_POST_READ_PHASE阶段为例,讲解了该阶段的 checker方法的实现:

ngx_int_t
ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
    ngx_int_t  rc;

    /*
     * generic phase checker,
     * used by the post read and pre-access phases
     */

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "generic phase: %ui", r->phase_handler);

    /* 调用当前阶段各HTTP模块中的handler处理方法 */
    rc = ph->handler(r);

    /* 进入下一阶段处理,忽略当前阶段其他的处理方法 */
    if (rc == NGX_OK) {
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }

    /* 进入下一个处理方法,该处理方法可能属于当前阶段,也可能属于下一个阶段 */
    if (rc == NGX_DECLINED) {
        r->phase_handler++;
        return NGX_AGAIN;
    }

    /* 当前请求依旧处于当前处理阶段 */
    if (rc == NGX_AGAIN || rc == NGX_DONE) {
        return NGX_OK;
    }

    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */

    /* 若出错,结束请求 */
    ngx_http_finalize_request(r, rc);

    return NGX_OK;
}

ログイン後にコピー

以上就介绍了Nginx 中 HTTP模块初始化,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

解決策: 組織では PIN を変更する必要があります。 解決策: 組織では PIN を変更する必要があります。 Oct 04, 2023 pm 05:45 PM

ログイン画面に「組織から PIN の変更を求められています」というメッセージが表示されます。これは、個人のデバイスを制御できる組織ベースのアカウント設定を使用しているコンピューターで PIN の有効期限の制限に達した場合に発生します。ただし、個人アカウントを使用して Windows をセットアップした場合、エラー メッセージは表示されないのが理想的です。常にそうとは限りませんが。エラーが発生したほとんどのユーザーは、個人アカウントを使用して報告します。私の組織が Windows 11 で PIN を変更するように要求するのはなぜですか?アカウントが組織に関連付けられている可能性があるため、主なアプローチはこれを確認することです。ドメイン管理者に問い合わせると解決できます。さらに、ローカル ポリシー設定が間違っていたり、レジストリ キーが間違っていたりすると、エラーが発生する可能性があります。今すぐ

Windows 11 でウィンドウの境界線の設定を調整する方法: 色とサイズを変更する Windows 11 でウィンドウの境界線の設定を調整する方法: 色とサイズを変更する Sep 22, 2023 am 11:37 AM

Windows 11 では、新鮮でエレガントなデザインが前面に押し出されており、最新のインターフェイスにより、ウィンドウの境界線などの細部をカスタマイズして変更することができます。このガイドでは、Windows オペレーティング システムで自分のスタイルを反映した環境を作成するのに役立つ手順について説明します。ウィンドウの境界線の設定を変更するにはどうすればよいですか? + を押して設定アプリを開きます。 Windows [個人用設定] に移動し、[色の設定] をクリックします。ウィンドウの境界線の色の変更設定ウィンドウ 11" width="643" height="500" > [タイトル バーとウィンドウの境界線にアクセント カラーを表示する] オプションを見つけて、その横にあるスイッチを切り替えます。 [スタート] メニューとタスク バーにアクセント カラーを表示するにはスタート メニューとタスク バーにテーマの色を表示するには、[スタート メニューとタスク バーにテーマを表示] をオンにします。

Windows 11 でタスクバーのサムネイル プレビューを有効または無効にする方法 Windows 11 でタスクバーのサムネイル プレビューを有効または無効にする方法 Sep 15, 2023 pm 03:57 PM

タスクバーのサムネイルは楽しい場合もありますが、気が散ったり煩わしい場合もあります。この領域にマウスを移動する頻度を考えると、重要なウィンドウを誤って閉じてしまったことが何度かある可能性があります。もう 1 つの欠点は、より多くのシステム リソースを使用することです。そのため、リソース効率を高める方法を探している場合は、それを無効にする方法を説明します。ただし、ハードウェアの仕様が対応可能で、プレビューが気に入った場合は、有効にすることができます。 Windows 11でタスクバーのサムネイルプレビューを有効にする方法は? 1. 設定アプリを使用してキーをタップし、[設定] をクリックします。 Windows では、「システム」をクリックし、「バージョン情報」を選択します。 「システムの詳細設定」をクリックします。 [詳細設定] タブに移動し、[パフォーマンス] の下の [設定] を選択します。 「視覚効果」を選択します

Windows 11 でのディスプレイ スケーリング ガイド Windows 11 でのディスプレイ スケーリング ガイド Sep 19, 2023 pm 06:45 PM

Windows 11 のディスプレイ スケーリングに関しては、好みが人それぞれ異なります。大きなアイコンを好む人もいれば、小さなアイコンを好む人もいます。ただし、適切なスケーリングが重要であることには誰もが同意します。フォントのスケーリングが不十分であったり、画像が過度にスケーリングされたりすると、作業中の生産性が大幅に低下する可能性があるため、システムの機能を最大限に活用するためにカスタマイズする方法を知る必要があります。カスタム ズームの利点: これは、画面上のテキストを読むのが難しい人にとって便利な機能です。一度に画面上でより多くの情報を確認できるようになります。特定のモニターおよびアプリケーションにのみ適用するカスタム拡張プロファイルを作成できます。ローエンド ハードウェアのパフォーマンスの向上に役立ちます。画面上の内容をより詳細に制御できるようになります。 Windows 11の使用方法

Windows 11で明るさを調整する10の方法 Windows 11で明るさを調整する10の方法 Dec 18, 2023 pm 02:21 PM

画面の明るさは、最新のコンピューティング デバイスを使用する上で不可欠な部分であり、特に長時間画面を見る場合には重要です。目の疲れを軽減し、可読性を向上させ、コンテンツを簡単かつ効率的に表示するのに役立ちます。ただし、設定によっては、特に新しい UI が変更された Windows 11 では、明るさの管理が難しい場合があります。明るさの調整に問題がある場合は、Windows 11 で明るさを管理するすべての方法を次に示します。 Windows 11で明るさを変更する方法【10の方法を解説】 シングルモニターユーザーは、次の方法でWindows 11の明るさを調整できます。これには、ラップトップだけでなく、単一のモニターを使用するデスクトップ システムも含まれます。はじめましょう。方法 1: アクション センターを使用する アクション センターにアクセスできる

http ステータス コード 520 は何を意味しますか? http ステータス コード 520 は何を意味しますか? Oct 13, 2023 pm 03:11 PM

HTTP ステータス コード 520 は、サーバーがリクエストの処理中に不明なエラーに遭遇し、より具体的な情報を提供できないことを意味します。サーバーがリクエストを処理しているときに不明なエラーが発生したことを示すために使用されます。サーバー構成の問題、ネットワークの問題、またはその他の不明な理由が原因である可能性があります。これは通常、サーバー構成の問題、ネットワークの問題、サーバーの過負荷、またはコーディング エラーが原因で発生します。ステータス コード 520 エラーが発生した場合は、Web サイト管理者またはテクニカル サポート チームに連絡して詳細情報と支援を得ることが最善です。

iPhoneのSafariでプライベートブラウジング認証をオフにする方法は? iPhoneのSafariでプライベートブラウジング認証をオフにする方法は? Nov 29, 2023 pm 11:21 PM

iOS 17 では、Apple はモバイル オペレーティング システムにいくつかの新しいプライバシーおよびセキュリティ機能を導入しました。その 1 つは、Safari のプライベート ブラウジング タブに対して 2 段階認証を要求する機能です。その仕組みとオフにする方法は次のとおりです。 iOS 17 または iPadOS 17 を実行している iPhone または iPad では、Safari でプライベート ブラウズ タブを開いていて、再度アクセスするためにセッションまたはアプリを終了する場合、Apple のブラウザでは Face ID/Touch ID 認証またはパスコードが必要になります。言い換えれば、ロックが解除されている iPhone または iPad を誰かが手に入れても、パスコードを知らなければプライバシーを閲覧することはできません。

Win10/11 デジタル アクティベーション スクリプト MAS バージョン 2.2 がデジタル アクティベーションを再サポート Win10/11 デジタル アクティベーション スクリプト MAS バージョン 2.2 がデジタル アクティベーションを再サポート Oct 16, 2023 am 08:13 AM

有名なアクティベーション スクリプト MAS2.2 バージョンでは、デジタル アクティベーションが再びサポートされています。このメソッドは @asdcorp とそのチームが考案したもので、MAS 作成者はそれを HWID2 と呼んでいます。 https://github.com/massgravel/Microsoft-Activation-Scripts から Gatherosstate.exe (オリジナルではなく、変更されたもの) をダウンロードし、パラメータを指定して実行し、AuthenticTicket.xml を生成します。まず元のメソッド: Gatherosstate.exePfn=xxxxxxx;DownlevelOriginalState=1 を確認し、次に最新のメソッド: Gatheros と比較します。

See all articles