前面的文章已经说了,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了。