nginx对于一个请求的处理分了很多个phase,我们可以根据自己的需求添加模块到期望的phase
phase相关的所有函数都是保存在cmcf的phase_engine.handlers里面的,所有r都会使用
nginx的phase有:
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0,
NGX_HTTP_SERVER_REWRITE_PHASE,
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE,NGX_HTTP_POST_REWRITE_PHASE,
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_TRY_FILES_PHASE,
NGX_HTTP_CONTENT_PHASE,
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
在http的处理函数 ngx_http_block里面,
会有如下代码:
if (ngx_http_init_phases(cf, cmcf) != NGX_OK)
{
return NGX_CONF_ERROR;
}
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_http_init_phases函数的执行流程为:
该函数主要是对cmcf->phases[各个phase].handlers申请空间
if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
中间会调用每个模块的postconfiguration,该函数会把当前模块的处理函数加入到对应的
cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers里面
每个handler是一个ngx_array_t类型,里面的elts是ngx_http_handler_pt类型
比如:
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
*h = ngx_http_auth_request_with_args_handler;
ngx_http_init_phase_handlers函数执行流程为:
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;
接下来遍历所有的phase,然后把里面包含的函数个数加起来,
但是n初始化方式为:n = use_rewrite + use_access + cmcf->try_files + 1,为什么会多几个?暂时还没有想明白
分配n个sizeof(ngx_http_phase_handler_t)空间+1个
然后 cmcf->phase_engine.handlers = ph;
n=0;
除了下面列举的phase,其它的任何phase
checker设置为: checker = ngx_http_core_generic_phase;
n保存为当前phase的函数个数
接下来遍历NGX_HTTP_POST_READ_PHASE的elts,对于每个ph(cmcf->phase_engine.handlers)赋值;
ph->checker设置为ngx_http_core_generic_phase
ph->handler = h[j]; 当前ph的handler设置为当前phase的第j个函数
ph->next = n;每个ph都保存着下一个phase的开始地址,这样可以在每个phase的任何一个函数都可以调到下一个phase
ph++,调到下一个ph
如果是NGX_HTTP_SERVER_REWRITE_PHASE,
会把cmcf->phase_engine.server_rewrite_index 设置为 n,n即为前面phase所有的函数个数之和
checker = ngx_http_core_rewrite_phase;
接下来更新n,之前的phase里面的函数加上当前phase的函数个数
如果是NGX_HTTP_FIND_CONFIG_PHASE
find_config_index = n;
ph->checker = ngx_http_core_find_config_phase;
n++;
ph++;
只会对当前checker进行赋值,然后把n++,ph++
如果是NGX_HTTP_REWRITE_PHASE
会把cmcf->phase_engine.location_rewrite_index = n,对cmcf->phase_engine.location_rewrite_index进行赋值
把checker设置为ngx_http_core_rewrite_phase
接下来把当前phase的所有所有函数都加到ph里面
ph->checker赋值为当前设置的checker,ph->checker = checker;
ph->handler = h[j];
ph->next = n;
ph++;
如果是NGX_HTTP_POST_REWRITE_PHASE
if (use_rewrite) { //判断在NGX_HTTP_REWRITE_PHASEphase里面有无函数
ph->checker = ngx_http_core_post_rewrite_phase;
ph->next = find_config_index;//下一个phase会到NGX_HTTP_FIND_CONFIG_PHASE
n++;
ph++;
}
如果是NGX_HTTP_ACCESS_PHASE
checker = ngx_http_core_access_phase;
n++; //n++ 为什么?暂时还没有想明白
接下来相同的操作把当前phase里面的函数加到ph链表里面
如果是NGX_HTTP_POST_ACCESS_PHASE
if (use_access) {//查看access有无处理函数
ph->checker = ngx_http_core_post_access_phase;
ph->next = n;
ph++;
}
这个总体是跳过access阶段
如果是 NGX_HTTP_TRY_FILES_PHASE:
if (cmcf->try_files) {//try_files是个命令
ph->checker = ngx_http_core_try_files_phase;
n++;
ph++;
}
如果是NGX_HTTP_CONTENT_PHASE
checker = ngx_http_core_content_phase;
把当前phase的函数都加入到ph中
上面是对cmcf->phase_engine.handlers结构的赋值,是一块连续的内存。
那如何调用呢?
在ngx_http_handler函数中
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
ngx_http_core_run_phases函数的执行流程为:
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers; //获取所有phase的执行函数首地址
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); //依次调用每个checker,r->phase_handler记录当前走到哪里了
if (rc == NGX_OK) {
return;
}
}
接下来逐个分析checker函数
NGX_HTTP_POST_READ_PHASE NGX_HTTP_PREACCESS_PHASE NGX_HTTP_LOG_PHASE
ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
rc = ph->handler(r); //依次调用每一个handler函数
if (rc == NGX_OK) { NGX_OK会调到下一个phase
r->phase_handler = ph->next;
return NGX_AGAIN;
}
if (rc == NGX_DECLINED) { NGX_DECLINED顺序执行
r->phase_handler++;
return NGX_AGAIN;
}
if (rc == NGX_AGAIN || rc == NGX_DONE) { NGX_AGAIN NGX_DONE返回NGX_OK,下次接着执行当前处理函数
return NGX_OK;
}
/* rc == NGX_ERROR || rc == NGX_HTTP_... */
ngx_http_finalize_request(r, rc);
}
NGX_HTTP_SERVER_REWRITE_PHASE
ngx_http_core_rewrite_phase
rc = ph->handler(r);
if (rc == NGX_DECLINED) { NGX_DECLINED依次执行下一个处理函数
r->phase_handler++;
return NGX_AGAIN;
}
if (rc == NGX_DONE) { 下一次接着执行
return NGX_OK;
}
/* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_... */
ngx_http_finalize_request(r, rc);
NGX_HTTP_FIND_CONFIG_PHASE
该阶段只有一个处理函数,不能自己添加模块在该阶段
NGX_HTTP_REWRITE_PHASE
ngx_int_t rc;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"rewrite phase: %ui", r->phase_handler);
rc = ph->handler(r);
if (rc == NGX_DECLINED) { NGX_DECLINED依次执行下一个函数
r->phase_handler++;
return NGX_AGAIN;
}
if (rc == NGX_DONE) { NGX_DONE下次接着执行
return NGX_OK;
}
/* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_... */ 其它的返回状态码则会断开当前连接
ngx_http_finalize_request(r, rc);
NGX_HTTP_POST_REWRITE_PHASE
ngx_http_core_srv_conf_t *cscf;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"post rewrite phase: %ui", r->phase_handler);
if (!r->uri_changed) { uri_changed为0,则会顺序执行下一个函数
r->phase_handler++;
return NGX_AGAIN;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"uri changes: %d", r->uri_changes);
/*
* gcc before 3.3 compiles the broken code for
* if (r->uri_changes-- == 0)
* if the r->uri_changes is defined as
* unsigned uri_changes:4
*/
r->uri_changes--;
if (r->uri_changes == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"rewrite or internal redirection cycle "
"while processing \"%V\"", &r->uri);
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_OK;
}
r->phase_handler = ph->next; 跳到NGX_HTTP_FIND_CONFIG_PHASE阶段
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
r->loc_conf = cscf->ctx->loc_conf; 修改r->loc_conf为当前server的loc_conf
return NGX_AGAIN; //接下来会接着从NGX_HTTP_FIND_CONFIG_PHASE开始往下处理
NGX_HTTP_ACCESS_PHASE 该阶段首先把n++
ngx_int_t rc;
ngx_http_core_loc_conf_t *clcf;
if (r != r->main) {
r->phase_handler = ph->next; //子请求不会再次进入access阶段,直接跳过该阶段
return NGX_AGAIN;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"access phase: %ui", r->phase_handler);
rc = ph->handler(r);
if (rc == NGX_DECLINED) { NGX_DECLINED依次执行下一个函数
r->phase_handler++;
return NGX_AGAIN; 接着再次执行
}
if (rc == NGX_AGAIN || rc == NGX_DONE) {
return NGX_OK; 退出当前r的执行流程,等待下一次再次进入
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
if (rc == NGX_OK) { 如果是satisfyall,NGX_OK会把所有的当前phase的处理函数都遍历一遍
r->phase_handler++;
return NGX_AGAIN;
}
} else {
if (rc == NGX_OK) {
r->access_code = 0;
if (r->headers_out.www_authenticate) {
r->headers_out.www_authenticate->hash = 0;
}
r->phase_handler = ph->next; 在access阶段,只需要满足其中一个处理函数,则直接跳到下一个phase
return NGX_AGAIN;
}
if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {
if (r->access_code != NGX_HTTP_UNAUTHORIZED) {
r->access_code = rc;
}
r->phase_handler++; 如果是NGX_HTTP_FORBIDDEN,则会接着执行下一个函数,返回403内容是什么时候返回?
return NGX_AGAIN;
}
}
/* rc == NGX_ERROR || rc == NGX_HTTP_... */
ngx_http_finalize_request(r, rc);
return NGX_OK;
NGX_HTTP_POST_ACCESS_PHASE (暂时没有接触到)
NGX_HTTP_TRY_FILES_PHASE(暂时没有接触到)
ngx_http_core_content_phase
size_t root;
ngx_int_t rc;
ngx_str_t path;
if (r->content_handler) { 如果有设置对应的函数,直接调用该函数,不会再走该阶段其它处理函数
r->write_event_handler = ngx_http_request_empty_handler;
ngx_http_finalize_request(r, r->content_handler(r));
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"content phase: %ui", r->phase_handler);
rc = ph->handler(r);
if (rc != NGX_DECLINED) { 在该阶段,只有NGX_DECLINED才不会finalize当前请求
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
/* rc == NGX_DECLINED */
ph++;
if (ph->checker) {
r->phase_handler++;
return NGX_AGAIN;
}
/* no content handler was found */
if (r->uri.data[r->uri.len - 1] == '/') { 什么时候会走到这里,暂时没有想到
if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"directory index of \"%s\" is forbidden", path.data);
}
ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
return NGX_OK;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_OK;