决定自己的HTTP模块如何起作用
- 一个HTTP请求会被许多个配置项控制,这是因为一个HTTP请求可以被许多个HTTP模块同时处理,所以肯定会有一个先后顺序的问题。
- 我们面临的问题:
- 我们希望自己的模块在哪个时刻开始处理请求?
- 我们是希望自己的模块对到达Nginx的所有请求都起作用,还是只对某一类请求(如URI匹配了location后表达式的请求)起作用?
- 定义第一个HTTP模块介入Nginx的方式
- 模块并不对所有的HTTP请求起作用。
- 在nginx.conf文件中的http{}、server{}或者location{}块内定义mytest配置项,如果一个用户请求通过主机域名、URI等匹配上了相应的配置块,而这个配置块下又具有mytest配置项,那么希望mytest模块开始处理请求。
- 在这种介入方式下,模块处理请求的顺序是固定的,即必须在HTTP框架定义的NGX_HTTP_CONTENT_PHASE阶段开始处理请求。
开始定义mytest模块
- 定义mytest配置项的处理
- 只需要定义一个ngx_command_t数组,并将mytest配置项出现后的解析方法设置为ngx_http_mytest:
static ngx_command_t ngx_http_mytest_commands[] = {
{
ngx_string("mytest"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
ngx_http_mytest,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};
- ngx_http_mytest是ngx_command_t结构体中的set成员,当在某个配置块中出现mytest配置项时,Nginx将会调用ngx_http_mytest方法。
- 实现ngx_http_mytest方法
static char * ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
ngx_http_core_loc_conf_t *clcf;
/* 首先找到mytest配置项所属的配置块,clcf看上去像是location块内的数据结构,其实不然,
* 它可以是main、src或者loc级别的配置项,也就是说,在每个http{}和server{}内也都有一个
* ngx_http_core_loc_conf结构体
*/
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
/* HTTP框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段是,如果请求的主机域名、URL
* 与mytest配置项所在的配置块相匹配,就将调用我们实现的ngx_http_mytest_handler方法处
* 理这个请求
* */
clcf->handler = ngx_http_mytest_handler;
return NGX_CONF_OK;
}
- 当Nginx接收完HTTP请求的头部信息时,就会调用HTTP框架处理请求,NGX_HTTP_CONTENT_PHASE阶段将有可能调用mytest模块处理请求。
- 在ngx_http_mytest方法中,定义了请求的处理方法为ngx_http_mytest_handler。
- HTTP框架共定义了11个阶段,第三方模块只能介入其中的7个阶段处理请求。11个阶段的定义如下:
typedef enum {
// 在接收到完整的HTTP头部后处理的HTTP阶段
NGX_HTTP_POST_READ_PHASE = 0,
/* 在还没有查询到URI匹配的location前,这时rewrite重写URL也作为一个独立的HTTP阶段 */
NGX_HTTP_SERVER_REWRITE_PHASE,
/*
根据URI寻找匹配的location,这个阶段通常由ngx_http_core_module模块实现。
不建议其他HTTP模块重新定义这一阶段的行为。
*/
NGX_HTTP_FIND_CONFIG_PHASE,
/*
NGX_HTTP_FIND_CONFIG_PHASE阶段后重写URL的意义与NGX_HTTP_SERVER_REWRITE_PHASE阶段不同
因为这两者会导致查找到不同的location块(location是与URI进行匹配的)
*/
NGX_HTTP_REWRITE_PHASE,
/*
该阶段用于在rewrite重写URL后跳到NGX_HTTP_FIND_CONFIG_PHASE,找到与新的URI匹配的location。
这一阶段无法由第三方HTTP模块处理,仅由ngx_http_core_module模块使用
*/
NGX_HTTP_POST_REWRITE_PHASE,
// 处理NGX_HTTP_ACCESS_PHASE阶段前,HTTP模块可以介入的处理阶段
NGX_HTTP_PREACCESS_PHASE,
// 这个阶段用于HTTP模块判断是否允许这个请求访问Nginx服务器
NGX_HTTP_ACCESS_PHASE,
/*
当NGX_HTTP_ACCESS_PHASE阶段中HTTP模块的handler处理方法返回不允许访问的错误码时
(实际是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED),这个阶段负责构造拒绝服务的用户响应。
这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾。
*/
NGX_HTTP_POST_ACCESS_PHASE,
/*
这个阶段完全是为try_files配置项而设立的。
当HTTP请求访问静态文件资源时,try_files配置项可以使这个请求顺序地访问多个静态文件资源。
如果某一次访问失败,则继续访问try_files中指定的下一个静态资源。
这个功能完全是在NGX_HTTP_TRY_FILES_PHASE阶段中实现的。
*/
NGX_HTTP_PRECONTENT_PHASE,
// 用于处理HTTP请求内容的阶段,这是大部分HTTP模块最喜欢介入的阶段
NGX_HTTP_CONTENT_PHASE,
/*
处理完请求后记录日志的阶段。
ngx_http_log_module模块就在这个阶段中加入了一个handler处理方法,
使得每个HTTP请求处理完毕后会记录access_log日志
*/
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
- 定义ngx_http_module_t接口
- 如果没有什么工作是必须在HTTP框架初始化时完成的,那就不必实现ngx_http_module_t的8个回调方法,定义方法如下:
static ngx_http_module_t ngx_http_mytest_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL, /* merge location configuration */
};
- 定义mytest模块
ngx_module_t ngx_http_mytest_module = {
NGX_MODULE_V1,
&ngx_http_mytest_module_ctx, /* module context */
ngx_http_mytest_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
至此,mytest模块在编译时将会被加入到ngx_modules全局数组中。Nginx在启动时,会调用所有模块的初始化回调方法,当然,这个例子中我们没有实现它们(也没有实现HTTP框架初始化时会调用的ngx_http_module_t中的8个方法)。