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)就可以了。