Nginx学习—http过滤模块

Posted 孙飞 Sunface

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx学习—http过滤模块相关的知识,希望对你有一定的参考价值。

HTTP过滤模块

为什么要使用过滤模块

HTTP过滤模块本质上也是一种HTTP模块,因此整个开发过程跟之前的《 Hello Nginx!》是相似的。不同的是,过滤模块所做的工作只是对发送给用户的HTTP响应包做一些加工。 HTTP框架为HTTP请求的处理过程定义了11个阶段,相关代码: [cpp]  view plain copy
  1. typedef enum   
  2.     NGX_HTTP_POST_READ_PHASE = 0,  
  3.   
  4.     NGX_HTTP_SERVER_REWRITE_PHASE,  
  5.   
  6.     NGX_HTTP_FIND_CONFIG_PHASE,  
  7.     NGX_HTTP_REWRITE_PHASE,  
  8.     NGX_HTTP_POST_REWRITE_PHASE,  
  9.   
  10.     NGX_HTTP_PREACCESS_PHASE,  
  11.   
  12.     NGX_HTTP_ACCESS_PHASE,  
  13.     NGX_HTTP_POST_ACCESS_PHASE,  
  14.   
  15.     NGX_HTTP_TRY_FILES_PHASE,  
  16.     NGX_HTTP_CONTENT_PHASE,          // 大部分HTTP模块只在此阶段处理  
  17.   
  18.     NGX_HTTP_LOG_PHASE  
  19.  ngx_http_phases;  
一般而言,普通HTTP模块倾向于完成请求的核心功能(如static模块负责静态文件处理),而HTTP过滤模块则负责处理一些附加功能(如gzip压缩)。 过滤模块特性:
  1. 过滤模块效果可以根据需要叠加;
  2. 普通HTTP模块处理请求完毕,开始发送HTTP头或包体时,才开始依次调用过滤模块处理请求,因此,HTTP过滤模块只处理服务器发往客户端的响应,不处理客户端发往服务器的请求;

过滤模块顺序

在编译nginx源码时,我们知道在ngx_modules.c文件中已经定义了一个模块数组,这个数组其实也就暗含了过滤模块的调用顺序。

过滤链表

所有HTTP过滤模块会构成一个单链表,链表的每一个元素都是一个独立的.c源文件。这个.c源文件会通过两个static静态指针(分别用于HTTP头和包体)指向下一个.c源文件。在ngx_http.c中定义两个指针,指向整个链表的首元素,也即第一个处理HTTP头和包体的方法。 [cpp]  view plain copy
  1. typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);  
  2. typedef ngx_int_t (*ngx_http_output_body_filter_pt)  
  3.     (ngx_http_request_t *r, ngx_chain_t *chain);  
  4.       
  5. extern ngx_http_output_header_filter_pt  ngx_http_top_header_filter;  
  6. extern ngx_http_output_body_filter_pt    ngx_http_top_body_filter;  
然后,在每一个过滤模块做初始化工作时,会先找到链表当前的首元素,然后再使用本身的static静态类型指针: [cpp]  view plain copy
  1. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;  
  2. static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;  
将自己插入到链表首部,源码实现如下: [cpp]  view plain copy
  1. static ngx_int_t  
  2. ngx_http_XXX_filter_init(ngx_conf_t *cf)  
  3.   
  4.     ngx_http_next_header_filter = ngx_http_top_header_filter;  
  5.     ngx_http_top_header_filter = ngx_http_XXX_header_filter;  
  6.       
  7.     ngx_http_next_body_filter = ngx_http_top_body_filter;  
  8.     ngx_http_top_body_filter = ngx_http_XXX_body_filter;  
  9.       
  10.     return NGX_OK;  
  11.   
当执行ngx_http_send_header发送HTTP头时,就开始遍历所有HTTP头部过滤模块了。 [cpp]  view plain copy
  1. ngx_int_t  
  2. ngx_http_send_header(ngx_http_request_t *r)  
  3.   
  4.     if (r->header_sent)   
  5.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,  
  6.                       "header already sent");  
  7.         return NGX_ERROR;  
  8.       
  9.   
  10.     if (r->err_status)   
  11.         r->headers_out.status = r->err_status;  
  12.         r->headers_out.status_line.len = 0;  
  13.       
  14.   
  15.     return ngx_http_top_header_filter(r);  
  16.   
HTTP包体链表类似。

过滤链表顺序

还是在编译Nginx源码时,编译进的ngx_modules.c模块数组,这个数组其实也就暗含了过滤模块的调用顺序。
首先,这个数组保存了所有的Nginx模块,包括HTTP普通模块与过滤模块,因此在初始化模块的顺序就按照这个该数组的成员顺序。又因为每个过滤模块都会将自己的初始化方法插入链表首部,因此过滤模块的调用顺序应该是数组中所有过滤模块的反向。具体的,结合书中内容,如下图:

过滤模块开发步骤

步骤基本与普通HTTP模块开发一致,概括总结如下:
  1. 增加.c源码文件;(HTTP过滤模块功能单一,一般一个.c文件即实现一个HTTP过滤模块)
  2. 在源码文件所在目录下创建config脚本文件,以便执行configure命令时将模块编译进Nginx;(与普通模块的config文件唯一不同就是HTTP_MODULES替换成HTTP_FILTER_MODULES)
  3. 定义过滤模块,即实例化ngx_module_t类型结构;
  4. 处理感兴趣的配置项,即设置ngx_command_t类型结构;
  5. 实现初始化方法,即插入单链表首部;
  6. 实现处理HTTP头部的方法;
  7. 实现处理HTTP包体的方法;
  8. 编译安装,修改nginx.conf文件并启动自定义过滤模块。

以上是关于Nginx学习—http过滤模块的主要内容,如果未能解决你的问题,请参考以下文章

Nginx http核心模块的内置变量

Nginx防盗链模块ngx_http_referer_module

Nginx之HTTP过滤模块

nginx编译安装WEB站点内容过滤功能模块(with-http_sub_module)

Nginx 过滤sub模块

nginx 配置相关解析