前面的文章已经说了,Nginx把整个http的请求分为11个phase,每个phase都会有其对应的handler,有的phase只有一个handler,有的phase则会有多个handler,而且nginx将他们的handler组织成了类似于链表的结构,方便在调用的时候可以按照phase的顺序进行调用。这篇文章就来详细心的讲如何来调用这些handler的。
在前面的文章我们也已经说过,当nginx获取连接,然后构造request,对请求进行一些预处理以后,就会调用ngx_http_core_run_phases函数,对这个请求跑一遍phase,我们首先还来看看ngx_http_core_run_phases的定义:
//该函数用于对request跑一遍phase
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); //获取ngx_http_core_main_conf_t
ph = cmcf->phase_engine.handlers; //获取所有的phase handler
/* 遍历phase上注册的所有handler,这里是以r->phase_handler为索引组成的链表 */
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) { //表示已经处理完成了,那么可以直接退出了。
return;
}
}
request的phase_handler域用来标记当前request所处在的phase对应的handler的下标,ph指向了ngx_http_core_main_conf_t结构中保存的所有handler结构,这里我们可以看到其实实际调用的是checker函数而不是handler函数,这是因为在checker函数中会调用当前对应的handler函数。
其实分析对phase的处理函数的调用其实就是分析每个phase的checker函数。嗯,接下来就进行分析。
首先来看的phase是NGX_HTTP_POST_READ_PHASE,它主要用于读取post的请求,它的checker函数是ngx_http_core_generic_phase:
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);
rc = ph->handler(r); //调用当前的handler函数处理request
if (rc == NGX_OK) { //表示这个phase的处理已经完成了,那么可以进入下一个phase了
r->phase_handler = ph->next; //将r的phase_hander的数值设置为下一个phase的第一个handler
return NGX_AGAIN; //返回NGX_AGIN,说明只是当前的phase处理完了,但是不代表整个request处理完了,因而还要继续接下来的phase
}
if (rc == NGX_DECLINED) { //表示这个request不适合用这个handler来处理,那么交个下面的handler来处理
r->phase_handler++; //表示将handler的下标加1,可能会执行当前phase的下一个handler,也可能会执行下一个phase
return NGX_AGAIN;
}
if (rc == NGX_AGAIN || rc == NGX_DONE) { //这里表示整个request都完成了,那么可以返回 NGX_OK,直接退出整个phase的执行
return NGX_OK;
}
/* rc == NGX_ERROR || rc == NGX_HTTP_... */
ngx_http_finalize_request(r, rc); // 遇到了错误,关闭request
return NGX_OK;
}
嗯,上面代码其实很简单,注释已经说的很清楚了,好我们接下来看下一个phase,NGX_HTTP_SERVER_REWRITE_PHASE,这个phase主要用于处理server 级别的rewrite,它的checker函数是ngx_http_core_rewrite_phase:ngx_int_t
ngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
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); //用对应的handler来处理这个request
if (rc == NGX_DECLINED) { //表示这个requet不适合用这个handler来处理,交给下一个handler
r->phase_handler++;
return NGX_AGAIN;
}
if (rc == NGX_DONE) { //表示整个request已经处理完了,那么可以直接返回NGX_OK,退出phase的调用
return NGX_OK;
}
/* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_... */
//出错了,那么直接释放request
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
其实这个函数也很简单,与前面的那一个大同小异,注释就已经说的差不多了,好吧进入下一个phase,NGX_HTTP_FIND_CONFIG_PHASE,这个phase 主要是根据uri来查找对应的location,将它们对应起来,他的checker函数是ngx_http_core_find_config_phase:ngx_int_t
ngx_http_core_find_config_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
u_char *p;
size_t len;
ngx_int_t rc;
ngx_http_core_loc_conf_t *clcf;
r->content_handler = NULL;
r->uri_changed = 0;
rc = ngx_http_core_find_location(r); //根据request的uri信息来查找location,先静态查找,再正则匹配
if (rc == NGX_ERROR) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_OK;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
/* 非内部请求访问内部location是非法的,所有与error处理类似 */
if (!r->internal && clcf->internal) {
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_OK;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"using configuration \"%s%V\"",
(clcf->noname ? "*" : (clcf->exact_match ? "=" : "")),
&clcf->name);
/* 根据匹配的location设置request的属性 */
ngx_http_update_location_config(r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http cl:%O max:%O",
r->headers_in.content_length_n, clcf->client_max_body_size);
/* 判断请求内容大小是否超过限制 */
if (r->headers_in.content_length_n != -1
&& !r->discard_body
&& clcf->client_max_body_size
&& clcf->client_max_body_size < r->headers_in.content_length_n)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client intended to send too large body: %O bytes",
r->headers_in.content_length_n);
(void) ngx_http_discard_request_body(r);
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);
return NGX_OK;
}
//处理重定向
if (rc == NGX_DONE) {
ngx_http_clear_location(r);
r->headers_out.location = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.location == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_OK;
}
/*
* we do not need to set the r->headers_out.location->hash and
* r->headers_out.location->key fields
*/
if (r->args.len == 0) {
r->headers_out.location->value = clcf->name;
} else {
len = clcf->name.len + 1 + r->args.len;
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_OK;
}
r->headers_out.location->value.len = len;
r->headers_out.location->value.data = p;
p = ngx_cpymem(p, clcf->name.data, clcf->name.len);
*p++ = '?';
ngx_memcpy(p, r->args.data, r->args.len);
}
ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);
return NGX_OK;
}
r->phase_handler++; //这里其实phase就进入了NGX_HTTP_REWRITE_PHASE,因为NGX_HTTP_FIND_CONFIG_PHASE只有一个handler
return NGX_AGAIN;
}
这个phase的checker函数稍微复杂一些,而且看前面phase的初始化的时候我们可以发现NGX_HTTP_FIND_CONFIG_PHASE实际的handler是空的,只是占了一个位而已,所以最后phase_handler++就已经将phase移向了下一个phase,好吧,我们来看下一个phase,NGX_HTTP_REWRITE_PHASE,这个phase主要处理location级别的rewrite,它的checker函数是ngx_http_core_rewrite_phase,与NGX_HTTP_SERVER_REWRITE_PHASE相同,这里就不讲了。
好了,接下来进入NGX_HTTP_POST_REWRITE_PHASE,这个phase主要用于进行一些收尾和效验的工作,它的checker函数是ngx_http_core_post_rewrite_phase,用于判断uri是否已经重写,而且这个phase的next指向的是NGX_HTTP_FIND_CONFIG_PHASE,而且这个phase的实际handler也是空的,只不过占了一个位置而已。
ngx_int_t
ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
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);
/**
* uri_changed标志位表示uri是否有被重写。
* 如果没有的话,这里累加r->phase_handler,由于post rewrite只有一个handler,
* 所以就会跳到下一个phase继续执行。
*/
if (!r->uri_changed) {
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;
}
/*
* 在ngx_http_init_phase_handlers中,如果use_rewrite不为0,那么
* post rewrite phase handler的next指向find config的phase handler。
* 接下来会进入find config phase
*/
r->phase_handler = ph->next;
/* server rewrite有可能改变了server config,所以要对r->loc_conf重新赋值 */
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
r->loc_conf = cscf->ctx->loc_conf;
return NGX_AGAIN;
}
这个checker函数其实还是很简单的,说白了就是判断当前uri是否已经重写,如果没有的话,那么可以进入下一个phase,如果重写了那么就回到NGX_HTTP_FIND_CONFIG_PHASE。
好了接下来进入NGX_HTTP_PREACCESS_PHASE,这个phase主要用于一些比较粗的access,它的checker函数也是ngx_http_core_generic_phase,嗯,就不讲了,
接下来进入NGX_HTTP_ACCESS_PHASE,这个phase会进行一些存取控制,权限验证等,但是处理一般情况下是交给下一个phase来做的,这里只是做一些细粒度的access。它的checker函数是ngx_http_core_access_phase:
//NGX_HTTP_ACCESS_PHASE阶段的checker
ngx_int_t
ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
ngx_int_t rc;
ngx_http_core_loc_conf_t *clcf;
//只能针对主请求
if (r != r->main) {
r->phase_handler = ph->next;
return NGX_AGAIN;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"access phase: %ui", r->phase_handler);
rc = ph->handler(r); //用相应的handler来处理
/* 跳到本phase的下一个handler */ // 或者进入下一个phase
if (rc == NGX_DECLINED) { //表示这个phase当前的这个handler不适合这个request
r->phase_handler++;
return NGX_AGAIN;
}
/* 请求处理完毕 */ //直接退出phase
if (rc == NGX_AGAIN || rc == NGX_DONE) {
return NGX_OK;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
/* 相当于satisfy all,就是必须满足所有条件,所以继续执行access handler */
if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
if (rc == NGX_OK) {
r->phase_handler++;
return NGX_AGAIN;
}
} else {
/* 否则只要满足任意一个条件即可,所以执行下一个phase的第一个handler */
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;
return NGX_AGAIN;
}
if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {
r->access_code = rc;
r->phase_handler++;
return NGX_AGAIN;
}
}
/* rc == NGX_ERROR || rc == NGX_HTTP_... */
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
嗯,代码还是很简单,一看就明白了。
嗯,接下来进入NGX_HTTP_POST_ACCESS_PHASE,这个phase主要是用于根据前面的access获取的access_code进行操作,checker函数是ngx_http_core_post_access_phase:
//NGX_HTTP_POST_ACCESS_PHASE阶段的checker函数
ngx_int_t
ngx_http_core_post_access_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
ngx_int_t access_code;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"post access phase: %ui", r->phase_handler);
access_code = r->access_code;
/* 设置了access_code,说明没有权限,则终结请求 */
if (access_code) {
if (access_code == NGX_HTTP_FORBIDDEN) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"access forbidden by rule");
}
r->access_code = 0;
ngx_http_finalize_request(r, access_code);
return NGX_OK;
}
r->phase_handler++; //跳到下一个phase
return NGX_AGAIN;
}
嗯,代码很简单,进入下一个phase,NGX_HTTP_TRY_FILES_PHASE,嗯这个phase对应的是配置文件里面的try_file命令。
接下来进入最重要的pahse,NGX_HTTP_CONTENT_PHASE,它用于最终生成request响应的信息,并将它们发送出去,我们大多数人编写的大部分模块都是在这个phase的。而且需要注意的是如果当前的location设置了其的content_handler,那么就只会执行这个handler,而不会执行其余的handler,它的checker函数是ngx_http_core_content_phase:
ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
size_t root;
ngx_int_t rc;
ngx_str_t path;
/*
* 在find config phase中如果匹配到的location具有handler,则会赋值给r->content_handler。
* 而这里可以看到,如果r->content_handler存在则只会执行这一个handler,然后返回。
* 也就是说如果location设置了handler,则只会执行这一个content handler,不会执行其他的。
*/
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) { //表示当前的handler可以处理这个request,那么就代表这个request的response已经完成了,可以关闭了,并且终止phase
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
/* handler返回NGX_DECLINED会由接下来的content handler继续处理 */
/* rc == NGX_DECLINED */
ph++;
/* 如果下一个handler的checker存在,则返回NGX_AGAIN,继续调用下一个handler */
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;
}
嗯,这个checker函数也很简单,要注意的情况是这个phase的handler只会出现两种情况,要么可以处理这个request,那么就执行完成了,直接退出phase,或者不能处理,那么交给下一个handler。
好了,基本上的phase的调用就差不多了,接下来可以具体分析每一个phase的handler了。