第6章 开发一个简单的HTTP过滤模块

本章开始介绍如何开发HTTP过滤模块。顾名思义,HTTP过滤模块也是一种HTTP模块,所以第3章中讨论过的如何定义一个HTTP模块以及第4章中讨论的使用配置文件、上下文、日志的方法对它来说都是适用的。事实上,开发HTTP过滤模块用到的大部分知识在第3章和第4章中都已经介绍过了,只不过,HTTP过滤模块的地位、作用与正常的HTTP处理模块是不同的,它所做的工作是对发送给用户的HTTP响应包做一些加工。在6.1节和6.2节中将会介绍默认编译进Nginx的官方HTTP过滤模块,从这些模块的功能上就可以对比出HTTP过滤模块与HTTP处理模块的不同之处。HTTP过滤模块不会去访问第三方服务,所以第5章中介绍的upstream和subrequest机制在本章中都不会使用到。

实际上,在阅读完第3章和第4章内容后再来学习本章内容,相信读者会发现开发HTTP过滤模块是一件非常简单的事情。在6.4节中,我们通过一个简单的例子来演示如何开发HTTP过滤模块。

6.1 过滤模块的意义

HTTP过滤模块与普通HTTP模块的功能是完全不同的,下面先来回顾一下普通的HTTP模块有何种功能。

HTTP框架为HTTP请求的处理过程定义了11个阶段,相关代码如下所示:


typedef enum{

NGX_HTTP_POST_READ_PHASE=0,

NGX_HTTP_SERVER_REWRITE_PHASE,

NGX_HTTP_FIND_CONFIG_PHASE,

NGX_HTTP_REWRITE_PHASE,

NGX_HTTP_POST_REWRITE_PHASE,

NGX_HTTP_PREACCESS_PHASE,

NGX_HTTP_ACCESS_PHASE,

NGX_HTTP_POST_ACCESS_PHASE,

NGX_HTTP_TRY_FILES_PHASE,

NGX_HTTP_CONTENT_PHASE,

NGX_HTTP_LOG_PHASE

}ngx_http_phases;


HTTP框架允许普通的HTTP处理模块介入其中的7个阶段处理请求,但是通常大部分HTTP模块(官方模块或者第三方模块)都只在NGX_HTTP_CONTENT_PHASE阶段处理请求。在这一阶段处理请求有一个特点,即HTTP模块有两种介入方法,第一种方法是,任一个HTTP模块会对所有的用户请求产生作用,第二种方法是,只对请求的URI匹配了nginx.conf中某些location表达式下的HTTP模块起作用。就像第3章中定义的mytest模块一样,大部分模块都使用上述的第二种方法处理请求,这种方法的特点是一种请求仅由一个HTTP模块(在NGX_HTTP_CONTENT_PHASE阶段)处理。如果希望多个HTTP模块共同处理一个请求,则多半是由subrequest功能来完成,即将原始请求分为多个子请求,每个子请求再由一个HTTP模块在NGX_HTTP_CONTENT_PHASE阶段处理。

然而,HTTP过滤模块则不同于此,一个请求可以被任意个HTTP过滤模块处理。因此,普通的HTTP模块更倾向于完成请求的核心功能,如static模块负责静态文件的处理。HTTP过滤模块则处理一些附加的功能,如gzip过滤模块可以把发送给用户的静态文件进行gzip压缩处理后再发出去,image_filter这个第三方过滤模块可以将图片类的静态文件制作成缩略图。而且,这些过滤模块的效果是可以根据需要叠加的,比如先由not_modify过滤模块处理请求中的浏览器缓存信息,再交给range过滤模块处理HTTP range协议(支持断点续传),然后交由gzip过滤模块进行压缩,可以看到,一个请求经由各HTTP过滤模块流水线般地依次进行处理了。

HTTP过滤模块的另一个特性是,在普通HTTP模块处理请求完毕,并调用ngx_http_send_header发送HTTP头部,或者调用ngx_http_output_filter发送HTTP包体时,才会由这两个方法依次调用所有的HTTP过滤模块来处理这个请求。因此,HTTP过滤模块仅处理服务器发往客户端的HTTP响应,而不处理客户端发往服务器的HTTP请求。

Nginx明确地将HTTP响应分为两个部分:HTTP头部和HTTP包体。因此,对应的HTTP过滤模块可以选择性地只处理HTTP头部或者HTTP包体,当然也可以二者皆处理。例如,not_modify过滤模块只处理HTTP头部,完全不关心http包体;而gzip过滤模块首先会处理HTTP头部,如检查浏览器请求中是否支持gzip解压,然后检查响应中HTTP头部里的Content-Type是否属于nginx.conf中指定的gzip压缩类型,接着才处理HTTP包体,针对每一块buffer缓冲区都进行gzip压缩,这样再交给下一个HTTP过滤模块处理。