typedef enum {
/*最开始的阶段,貌似Nginx原生没有该阶段的handler*/
NGX_HTTP_POST_READ_PHASE = 0,
/*配置在server块的rewrite指令,则会处理hander,否则,handler进去直接return*/
NGX_HTTP_SERVER_REWRITE_PHASE,
/*根据URI进行location块查找*/
NGX_HTTP_FIND_CONFIG_PHASE,
/*对URI进行修改*/
NGX_HTTP_REWRITE_PHASE,
/*决定是跳回NGX_HTTP_FIND_CONFIG_PHASE还是继续下一个handler,如果URI被rewrite了,则跳回去*/
NGX_HTTP_POST_REWRITE_PHASE,
/*下面三个都是访问控制,不了解*/
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_TRY_FILES_PHASE,
/*content阶段,常用,例如proxy_pass指令等都在这里触发*/
NGX_HTTP_CONTENT_PHASE,
/关闭r的时候,会调用,用于access打日志*/
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
一开始以为Nginx的所谓的阶段和Netfilter
各个钩子一样,嵌入在整个转发流程的各个地方(local_in,ip_forward等),后来发现理解错了,Nginx的阶段是在读取HTTP全部请求首部后,在一个地方统一跑了一遍(log阶段除外)。
ngx_http_core_run_phases
函数负责处理所有可能处理的阶段(log阶段除外)。
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}
上面代码可见,Nginx把所有的handler
放在一个数组里面:cmcf->phase_engine.handlers
,数组每个元素是ngx_http_phase_handler_t
结构体,每个结构体包含1个handler
和1个checker
,所有ngx_http_phase_handler_t
包含了所有phase的所有handler
(或者checker
)。
checker
主要作用是判断handler
的返回值,然后依据返回值决定下一步怎么处理,例如接着处理下一个hander(checker返回NGX_AGAIN),或者处理完毕,不需要走下去了(checker返回NGX_OK)。所以我们编写模块handler
的时候,需要特别注意自己的返回值。
下面是配置解析阶段最后,将各模块的 handler
变成数组。
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;
use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;
/*计算所有注册phase的handler个数*/
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
n += cmcf->phases[i].handlers.nelts;
}
/*为这些handler分配空间*/
ph = ngx_pcalloc(cf->pool,
n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
if (ph == NULL) {
return NGX_ERROR;
}
cmcf->phase_engine.handlers = ph;
n = 0;
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
h = cmcf->phases[i].handlers.elts;
switch (i) {
case NGX_HTTP_SERVER_REWRITE_PHASE:
if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine.server_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
/*break后,所有该阶段的checker都为 ngx_http_core_rewrite_phase*/
break;
case NGX_HTTP_FIND_CONFIG_PHASE:
find_config_index = n;
ph->checker = ngx_http_core_find_config_phase;
n++;
ph++;
/*不通过break处理,表示该阶段只有checker,没有handler,且只有一个*/
continue;
case NGX_HTTP_REWRITE_PHASE:
if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine.location_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_POST_REWRITE_PHASE:
if (use_rewrite) {
ph->checker = ngx_http_core_post_rewrite_phase;
ph->next = find_config_index;
n++;
ph++;
}
continue;
case NGX_HTTP_ACCESS_PHASE:
checker = ngx_http_core_access_phase;
n++;
break;
case NGX_HTTP_POST_ACCESS_PHASE:
if (use_access) {
ph->checker = ngx_http_core_post_access_phase;
ph->next = n;
ph++;
}
continue;
case NGX_HTTP_TRY_FILES_PHASE:
if (cmcf->try_files) {
ph->checker = ngx_http_core_try_files_phase;
n++;
ph++;
}
continue;
case NGX_HTTP_CONTENT_PHASE:
checker = ngx_http_core_content_phase;
break;
default:
checker = ngx_http_core_generic_phase;
}
n += cmcf->phases[i].handlers.nelts;
/*把phases[i]阶段的handler按逆序排列,next指向的是下一个phase的第一个handler*/
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;
}
同一个phase内,checker一样,handler不一样。如下图这种形式,phase0有3个handler,但是checker是一样的。
phase = 0 phase = 1 phase = 2
--------------------------------------------------------
| h1 | h2 | h3 | h4 | h5 | h6 | h7 | h8 | h9 |
--------------------------------------------------------
next 用来跳到另一个phase(根据自己phase逻辑处理,注意这里的措词,是另一个phase,而不是下一个phase)。
例如 在 rewrite阶段改变了url,且没有配置break,则会重新跳回find config阶段(在checker为ngx_http_core_post_rewrite_phase
中设置了r->phase_handler = ph->next
)。反正想跳到哪里,设置r->phase_handler
就对了。r->phase_handler
可以简单的++表示处理下一个hander。也可以指定成next,next具体指,完全看自己业务逻辑。