### 概述
? ? ? 在前面的文章《 [Nginx 配置解析](http://blog.csdn.net/chenhanzhun/article/details/42615433)》簡單講解了通用模塊的配置項解析,并且大概講解了HTTP 模塊的配置項解析過程,本文更具體的分析HTTP 模塊的初始化過程。HTTP 模塊初始化過程主要有:上下文結構初始化、配置項解析、配置項合并、server 相關端口設置。
### HTTP 模塊接口
### ngx_http_module_t 結構體
? ? ? 在 Nginx 中,結構體 ngx_module_t 是 Nginx 模塊最基本的接口。對于每一種不同類型的模塊,都有一個具體的結構體來描述這一類模塊的通用接口。在Nginx 中定義了HTTP 模塊的通用接口ngx_http_module_t 結構體,該結構體定義在文件[src/http/ngx_http_config.h](http://lxr.nginx.org/source/src/http/ngx_http_config.h):我們把直屬于http{}、server{}、location{} 塊的配置項分別稱為main、srv、loc 級別配置項。
~~~
/* 所有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;
~~~
### ngx_http_conf_ctx_t 結構體
? ? ? 在 HTTP 模塊中,管理 HTTP 模塊配置項的結構由 ngx_http_conf_ctx_t 實現,該結構有三個成員,分別指向三個指針數組,指針數組是由相應地?HTTP 模塊create_main_conf、create_srv_conf、create_loc_conf 方法創建的結構體指針組成的數組。ngx_http_conf_ctx_t 結構體定義在文件[src/http/ngx_http_config.h](http://lxr.nginx.org/source/src/http/ngx_http_config.h) 中:
~~~
/* 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](http://lxr.nginx.org/source/src/http/ngx_http.c) 中:
~~~
/* 定義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://lxr.nginx.org/source/src/http/ngx_http.c) 中:在 ngx_http_module 核心模塊中只定義了 http 模塊的名稱。
~~~
/* 定義核心模塊的通用接口上下文結構 */
static ngx_core_module_t ngx_http_module_ctx = {
ngx_string("http"),
NULL,
NULL
};
~~~
? ? ? 在 ngx_http_module 模塊中定義了http{} 塊感興趣的配置項數組,配置項數組定義在文件[src/http/ngx_http.c](http://lxr.nginx.org/source/src/http/ngx_http.c) 中:
~~~
/* 定義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,該處理方法是HTTP 模塊的初始化作用。
### ngx_http_module 核心模塊初始化
? ? ? 在上面 ngx_http_module 模塊的定義中已經知道,HTTP 模塊的初始化過程由函數ngx_http_block 實現,首先先給出該函數的整體分析,接著對該函數進行具體的分析。該函數定義在文件[src/http/ngx_http.c](http://lxr.nginx.org/source/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_conf*、*create_srv_conf*、*create_loc_conf*方法返回的指針地址;
- 調用每個 HTTP 模塊的 preconfiguration 方法;
- HTTP 框架調用函數 ngx_conf_parse() 開始循環解析配置文件?*nginx.conf?*中的http{}塊里面的所有配置項,http{} 塊內可嵌套多個server{} 塊,而 server{} 塊可嵌套多個 location{},location{} 依舊可以嵌套location{},因此配置項解析函數是遞歸調用;
- HTTP 框架處理完畢 http{} 配置項,根據解析配置項的結果,必要時調用ngx_http_merge_servers 方法進行配置項合并處理,即合并main、srv、loc 級別下server、location 相關的配置項;
- 初始化可添加處理方法的 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_conf、loc_conf,因此就會導致與http{} 塊所創建的配置項結構體重復,這時候就需要對這些配置項進行管理與合并。首先先看下結構體ngx_http_core_main_conf_t,ngx_http_core_main_conf_t 是ngx_http_core_module 的 main_conf,存儲了 http{} 層的配置參數。該結構體定義在文件[src/http/ngx_http_core_module.h](http://lxr.nginx.org/source/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_t,ngx_http_core_srv_conf_t 是ngx_http_core_module 的srv_conf,存儲了server{} 層的配置參數。該結構體定義在文件[src/http/ngx_http_core_module.h](http://lxr.nginx.org/source/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_t,ngx_http_core_loc_conf_t 是ngx_http_core_module 的loc_conf,存儲了location{} 層的配置參數。該結構體定義在文件[src/http/ngx_http_core_module.h](http://lxr.nginx.org/source/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 模塊中的第一個模塊,該模塊管理srv、loc 級別的配置項結構。該模塊在文件[src/http/ngx_http_core_module.c](http://lxr.nginx.org/source/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 */
};
~~~
? ? ? 由于該模塊中感興趣的配置項太多,這里只列出 server、location 配置項。定義如下:
~~~
...
{ 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{},不同塊之間的解析都會創建相應的結構體保存配置項參數,但是由于屬于嵌套關系,所有必須管理好不同塊之間的配置項結構體,方便解析完畢后合并相應的配置項,以下針對不同級別的配置項結構體進行分析。
### 獲取不同級別配置項結構
? ? ? 根據不同結構體變量的參數獲取不同級別的配置項結構體由宏定義實現,在文件 [src/http/ngx_http_config.h](http://lxr.nginx.org/source/src/http/ngx_http_config.h)中定義如下:
~~~
/* 利用結構體變量ngx_http_request_t r獲取HTTP模塊main、srv、loc級別的配置項結構體 */
#define ngx_http_get_module_main_conf(r, module) \
(r)->main_conf[module.ctx_index]
#define ngx_http_get_module_srv_conf(r, module) (r)->srv_conf[module.ctx_index]
#define ngx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index]
/* 利用結構體變量ngx_conf_t cf獲取HTTP模塊的main、srv、loc級別的配置項結構體 */
#define ngx_http_conf_get_module_main_conf(cf, module) \
((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
#define ngx_http_conf_get_module_srv_conf(cf, module) \
((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
#define ngx_http_conf_get_module_loc_conf(cf, module) \
((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]
/* 利用全局變量ngx_cycle_t cycle獲取HTTP模塊的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)
~~~
### main 級別的配置項結構體
? ? ? 在 ngx_http_module 模塊 http{} 塊配置項解析的初始化過程中由函數 ngx_http_block 實現,在實現過程中創建并初始化了HTTP 模塊main 級別的配置項main_conf、srv_conf、loc_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_conf、loc_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://lxr.nginx.org/source/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* 方法,由于在上一步驟已經將main、srv級別由*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列表,各個phase的phase handler的checker不同。checker主要用于限定某個phase的框架邏輯,包括處理返回值。 在Nginx 定義了 11 個處理階段,有一部分是不能添加 phase handler 方法的。在文件[src/http/ngx_http_core_module.h](http://lxr.nginx.org/source/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;
~~~
每個HTTP的checker方法與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 配置文件
- Nginx 內存池管理
- Nginx 基本數據結構
- Nginx 數組結構 ngx_array_t
- Nginx 鏈表結構 ngx_list_t
- Nginx 隊列雙向鏈表結構 ngx_queue_t
- Nginx 哈希表結構 ngx_hash_t
- Nginx 紅黑樹結構 ngx_rbtree_t
- Nginx 模塊開發
- Nginx 啟動初始化過程
- Nginx 配置解析
- Nginx 中的 upstream 與 subrequest 機制
- Nginx 源碼結構分析
- Nginx 事件模塊
- Nginx 的 epoll 事件驅動模塊
- Nginx 定時器事件
- Nginx 事件驅動模塊連接處理
- Nginx 中 HTTP 模塊初始化
- Nginx 中處理 HTTP 請求
- Nginx 中 upstream 機制的實現
- Nginx 中 upstream 機制的負載均衡