6.2 过滤模块的调用顺序
既然一个请求会被所有的HTTP过滤模块依次处理,那么下面来看一下这些HTTP过滤模块是如何组织到一起的,以及它们的调用顺序是如何确定的。
6.2.1 过滤链表是如何构成的
在编译Nginx源代码时,已经定义了一个由所有HTTP过滤模块组成的单链表,这个单链表与一般的链表是不一样的,它有另类的风格:链表的每一个元素都是一个独立的C源代码文件,而这个C源代码文件会通过两个static静态指针(分别用于处理HTTP头部和HTTP包体)再指向下一个文件中的过滤方法。在HTTP框架中定义了两个指针,指向整个链表的第一个元素,也就是第一个处理HTTP头部、HTTP包体的方法。
这两个处理HTTP头部和HTTP包体的方法是什么样的呢?HTTP框架进行了如下定义:
typedef ngx_int_t(*ngx_http_output_header_filter_pt)
(ngx_http_request_t*r);
typedef ngx_int_t(*ngx_http_output_body_filter_pt)
(ngx_http_request_tr,ngx_chain_tchain);
如上所示,ngx_http_output_header_filter_pt是每个过滤模块处理HTTP头部的方法原型,它仅接收1个参数r,也就是当前的请求,其返回值一般是与3.6.1节中介绍的返回码通用的,如NGX_ERROR表示失败,而NGX_OK表示成功。
ngx_http_output_body_filter_pt是每个过滤模块处理HTTP包体的方法原型,它接收两个参数——r和chain,其中r是当前的请求,chain是要发送的HTTP包体,其返回值与ngx_http_output_header_filter_pt相同。
所有的HTTP过滤模块需要实现这两个方法(或者仅实现其中的一个也是可以的)。因此,这个单向链表是围绕着每个文件(也就是HTTP过滤模块)中的这两个处理方法来建立的,也就是说,链表中的元素实际上就是处理方法。
先来看一下HTTP框架中定义的链表入口:
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
当执行ngx_http_send_header发送HTTP头部时,就从ngx_http_top_header_filter指针开始遍历所有的HTTP头部过滤模块,而在执行ngx_http_output_filter发送HTTP包体时,就从ngx_http_top_body_filter指针开始遍历所有的HTTP包体过滤模块。下面来看一下在Nginx源代码中是如何做的:
ngx_int_t
ngx_http_send_header(ngx_http_request_t*r)
{
if(r->err_status){
r->headers_out.status=r->err_status;
r->headers_out.status_line.len=0;
}
return ngx_http_top_header_filter(r);
}
在发送HTTP头部时,从ngx_http_top_header_filter指针指向的过滤模块开始执行。而发送HTTP包体时都是调用ngx_http_output_filter方法,如下所示:
ngx_int_t
ngx_http_output_filter(ngx_http_request_tr,ngx_chain_tin)
{
ngx_int_t
rc;
ngx_connection_t*c;
c=r->connection;
rc=ngx_http_top_body_filter(r,in);
if(rc==NGX_ERROR){
/NGX_ERROR可能由任何过滤模块返回/
c->error=1;
}
return rc;
}
遍历访问所有的HTTP过滤模块时,这个单链表中的元素是怎么用next指针连接起来的呢?很简单,每个HTTP过滤模块在初始化时,会先找到链表的首元素ngx_http_top_header_filter指针和ngx_http_top_body_filter指针,再使用static静态类型的ngx_http_next_header_filter和ngx_http_next_body_filter指针将自己插入到链表的首部,这样就行了。下面来看一下在每个过滤模块中ngx_http_next_header_filter和ngx_http_next_body_filter指针的定义:
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
注意,ngx_http_next_header_filter和ngx_http_next_body_filter都必须是static静态变量,为什么呢?因为static类型可以让上面两个变量仅在当前文件中生效,这就允许所有的HTTP过滤模块都有各自的ngx_http_next_header_filter和ngx_http_next_body_filter指针。这样,在每个HTTP过滤模块初始化时,就可以用上面这两个指针指向下一个HTTP过滤模块了。例如,可以像下列代码一样将当前HTTP过滤模块的处理方法添加到链表首部。
ngx_http_next_header_filter=ngx_http_top_header_filter;
ngx_http_top_header_filter=ngx_http_myfilter_header_filter;
ngx_http_next_body_filter=ngx_http_top_body_filter;
ngx_http_top_body_filter=ngx_http_myfilter_body_filter;
这样,在初始化到本模块时,自定义的ngx_http_myfilter_header_filter与ngx_http_myfilter_body_filter方法就暂时加入到了链表的首部,而且本模块所在文件中static类型的ngx_http_next_header_filter指针和ngx_http_next_body_filter指针也指向了链表中原来的首部。在实际使用中,如果需要调用下一个HTTP过滤模块,只需要调用ngx_http_next_header_filter(r)或者ngx_http_next_body_filter(r,chain)就可以了。