🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### CONTENT阶段[](http://tengine.taobao.org/book/chapter_12.html#content "永久链接至标题") CONTENT阶段可以说是整个执行链中最重要的阶段,请求从这里开始执行业务逻辑并产生响应,下面来分析一下它的checker函数: [](http:// "点击提交Issue,反馈你的意见...") 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; 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_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; } CONTENT阶段有些特殊,它不像其他阶段只能执行固定的handler链,还有一个特殊的content_handler,每个location可以有自己独立的content handler,而且当有content handler时,CONTENT阶段只会执行content handler,不再执行本阶段的handler链。 默认情况下,Nginx会在CONTENT阶段的handler链挂上index模块,静态文件处理模块等的handler。另外模块还可以设置独立的content handler,比如ngx_http_proxy_module的proxy_pass指令会设置一个名为ngx_http_proxy_handler的content handler。 接下来看一下上面的checker函数的执行流程,首先检查是否设置了r->content_handler,如果设置了的话,则执行它,需要注意的是在执行它之前,Nginx将r->write_event_handler设置为了ngx_http_request_empty_handler,先看一下设置r->write_event_handler之前的值是什么,在ngx_http_handler函数中它被设置为ngx_http_core_run_phases,而ngx_http_core_run_phases会运行每个阶段的checker函数。正常流程中,如果某个阶段需要等待某个写事件发生时,该阶段的handler会返回NGX_OK来中断ngx_http_core_run_phases的运行,等到下次写事件过来时,会继续执行之前阶段的handler;当执行r->content_handler的流程时,Nginx默认模块会去处理r->write_event_handler的值,也就是假设r->content_handler只能执行1次,如果模块设置的content handler涉及到IO操作,就需要合理的设置处理读写事件的handler(r->read_event_handler和r->write_event_handler)。 还有一个需要注意的点是r->content_handler执行之后,Nginx直接用其返回值调用了ngx_http_finalize_request函数,Nginx将一大堆耦合的逻辑都集中在了这个函数当中,包括长连接,lingering_close,子请求等的处理都涉及到该函数,后面会有一节单独介绍这个函数。这里需要提醒的是r->content_handler如果并未完成整个请求的处理,而只是需要等待某个事件发生而退出处理流程的话,必须返回一个合适的值传给ngx_http_finalize_request,一般而言是返回NGX_DONE,而且需要将请求的引用计数(r->count)加1,确保ngx_http_finalize_request函数不会将该请求释放掉。 函数的其他部分处理走handler链的情况,特殊的地方是CONTENT阶段是ngx_http_core_run_phases函数跑的最后一个阶段,如果最后一个handler返回NGX_DECLINED,此时Nginx会给客户端返回NGX_HTTP_FORBIDDEN(403)或NGX_HTTP_NOT_FOUND(404)。