10.6.11 NGX_HTTP_CONTENT_PHASE阶段
这是一个核心HTTP阶段,可以说大部分HTTP模块都会在此阶段重新定义Nginx服务器的行为,如第3章中提到的mytest模块。NGX_HTTP_CONTENT_PHASE阶段之所以被众多HTTP模块“钟爱”,主要基于以下两个原因:
其一,以上9个阶段主要专注于4件基础性工作:rewrite重写URL、找到location配置块、判断请求是否具备访问权限、try_files功能优先读取静态资源文件,这4个工作通常适用于绝大部分请求,因此,许多HTTP模块希望可以共享这9个阶段中已经完成的功能。
其二,NGX_HTTP_CONTENT_PHASE阶段与其他阶段都不相同的是,它向HTTP模块提供了两种介入该阶段的方式:第一种与其他10个阶段一样,通过向全局的ngx_http_core_main_conf_t结构体的phases数组中添加ngx_http_handler_pt处理方法来实现,而第二种是本阶段独有的,把希望处理请求的ngx_http_handler_pt方法设置到location相关的ngx_http_core_loc_conf_t结构体的handler指针中,这正是第3章中mytest例子的用法。
上面所说的第一种方式,也是HTTP模块介入其他10个阶段的唯一方式,是通过在必定会被调用的postconfiguration方法向全局的ngx_http_core_main_conf_t结构体的phases[NGX_HTTP_CONTENT_PHASE]动态数组添加ngx_http_handler_pt处理方法来达成的,这个处理方法将会应用于全部的HTTP请求。
而第二种方式是通过设置ngx_http_core_loc_conf_t结构体的handler指针来实现的,在10.2.3节中我们已经知道,每一个location都对应着一个独立的ngx_http_core_loc_conf_t结构体。这样,我们就不必在必定会被调用的postconfiguration方法中添加ngx_http_handler_pt处理方法了,而可以选择在ngx_command_t的某个配置项(如第3章中的mytest配置项)的回调方法中添加处理方法,将当前location块所属的ngx_http_core_loc_conf_t结构体中的handler设置为ngx_http_handler_pt处理方法。这样做的好处是,ngx_http_handler_pt处理方法不再应用于所有的HTTP请求,仅仅当用户请求的URI匹配了location时(也就是mytest配置项所在的location)才会被调用。这也就意味着它是一种完全不同于其他阶段的使用方式。
因此,当HTTP模块实现了某个ngx_http_handler_pt处理方法并希望介入NGX_HTTP_CONTENT_PHASE阶段来处理用户请求时,如果希望这个ngx_http_handler_pt方法应用于所有的用户请求,则应该在ngx_http_module_t接口的postconfiguration方法中,向ngx_http_core_main_conf_t结构体的phases[NGX_HTTP_CONTENT_PHASE]动态数组中添加ngx_http_handler_pt处理方法;反之,如果希望这个方式仅应用于URI匹配了某些location的用户请求,则应该在一个location下配置项的回调方法中,把ngx_http_handler_pt方法设置到ngx_http_core_loc_conf_t结构体的handler中。
注意 ngx_http_core_loc_conf_t结构体中仅有一个handler指针,它不是数组,这也就意味着如果采用上述的第二种方法添加ngx_http_handler_pt处理方法,那么每个请求在NGX_HTTP_CONTENT_PHASE阶段只能有一个ngx_http_handler_pt处理方法。而使用第一种方法时是没有这个限制的,NGX_HTTP_CONTENT_PHASE阶段可以经由任意个HTTP模块处理。
当同时使用这两种方式设置ngx_http_handler_pt处理方法时,只有第二种方式设置的ngx_http_handler_pt处理方法才会生效,也就是设置handler指针的方式优先级更高,而第一种方式设置的ngx_http_handler_pt处理方法将不会生效。如果一个location配置块内有多个HTTP模块的配置项在解析过程都试图按照第二种方式设置ngx_http_handler_pt处理方法,那么后面的配置项将有可能覆盖前面的配置项解析时对handler指针的设置。
NGX_HTTP_CONTENT_PHASE阶段的checker方法是ngx_http_core_content_phase。ngx_http_handler_pt处理方法的返回值在以上两种方式下具备了不同意义。
在第一种方式下,ngx_http_handler_pt处理方法无论返回任何值,都会直接调用ngx_http_finalize_request方法结束请求。当然,ngx_http_finalize_request方法根据返回值的不同未必会直接结束请求,这在第11章中会详细介绍。
在第二种方式下,如果ngx_http_handler_pt处理方法返回NGX_DECLINED,将按顺序向后执行下一个ngx_http_handler_pt处理方法;如果返回其他值,则调用ngx_http_finalize_request方法结束请求。