上篇笔记完成了一个简单的handler模块hello_module,本篇笔记将在上篇笔记的基础上增加一个filter模块,目的是将hanlder模块输出内容加粗变颜色。文章参考了http://tengine.taobao.org/book/chapter_04.html
一、filter模块介绍
过滤(filter)模块是过滤响应头和内容的模块,可以对回复的头和内容进行处理。它的处理时间在获取回复内容之后,向用户发送响应之前。它的处理过程分为两个阶段,过滤HTTP回复的头部和主体,在这两个阶段可以分别对头部和主体进行修改。
过滤模块中通常都有两个函数
ngx_http_top_header_filter(r);
ngx_http_top_body_filter(r, in);
是分别对头部和主体进行过滤的函数。所有模块的响应内容要返回给客户端,都必须调用这两个接口。
过滤模块的调用是有顺序的,它的顺序在编译的时候就决定了。可以在nginx源码所在路径的objs/ngx_modules.c文件中看到
模块的执行顺序是反向的。也就是说最早执行的是not_modified_filter,然后各个模块依次执行。所有第三方的过滤模块只能加入到copy_filter和headers_filter模块之间执行。可以看到编写的content_bold_filter模块就在它们之间。
所有filter模块头部过滤函数和主体过滤函数共同组成了一个链表结构。nginx为了按照次序来执行各个过滤模块,通常通过局部的全局变量和挂载函数完成
ngx_http_top_header_filter是一个全局变量。当编译进一个filter模块的时候,就被赋值为当前filter模块的处理函数。而ngx_http_next_header_filter是一个局部全局变量,它保存了编译前上一个filter模块的处理函数。所以整体看来,就像用全局变量组成的一条单向链表。
每个模块想执行下一个过滤函数,只要调用一下ngx_http_next_header_filter这个局部变量。而整个过滤模块链的入口,需要调用ngx_http_top_header_filter这个全局变量。ngx_http_top_body_filter的行为与header fitler类似。
用图形象表示
二、filter模块实战
filter模块很多地方都跟handler模块很像,首先,都需要定义模块指令配置、模块上下文、模块的定义。
与handler模块不一样的比如上面挂载函数的方式,一般都使用上面的写法,然后就是两个主要的过滤函数header_filter处理函数和body_filter处理函数
其实想将handler模块的字体加粗变色逻辑很简单,可以使用一些基本的html语言,设置一下style就行了,所以可以声明两个字符串表示html标签的头和尾
所以body_filter处理函数主要就是需要把handler输出链表的首尾加上这两个字符串以达成加粗变色的目的,而header_filter处理函数就是需要改变这个新的输出体的长度。
Body_filter处理函数
Header_filter处理函数
其他逻辑与handler模块大致相同,不再说明,详见代码
三、编译运行
和handler模块一样,首先要编写config文件,注意filter模块的config文件和handler不同,通常是这样
本例的写法
该config文件和ngx_http_content_filter_module.c共同放在/usr/local/nginx-1.10.2/test/ngx_http_content_filter_module中
进入nginx源码的目录/usr/local/nginx-1.10.2/ 执行./configure --prefix=/usr/local/nginx/ --add-module=/home/lyz/ngx_http_hello_world_module --add-module=/usr/local/nginx-1.10.2/test/ngx_http_content_bold_filter_module
然后make && make install
最后修改/usr/local/nginx/conf/nginx.conf加入content_bold指令
打开nginx,访问6.5.128.160:12345/hello可以看到
加粗变红,完成。
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/** 模块的配置结构 */
typedef struct
{
ngx_int_t bold_flag;
} ngx_http_bold_filter_loc_conf_t;
static ngx_int_t ngx_http_content_bold_init(ngx_conf_t *cf);
static void *ngx_http_content_bold_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_content_bold_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
/** 模块指令,这里我们创建了一个配置指令content_bold */
static ngx_command_t ngx_http_content_bold_commands[] = {
{
ngx_string("content_bold"),
/* 该指令只能出现在location块中,并且有一个参数 */
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
/* 读取解析参数的回调函数 */
ngx_http_content_bold_string,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_bold_filter_loc_conf_t, bold_flag),
NULL
},
ngx_null_command
};
/* * 该模块上下文的定义。 * ngx_http_content_bold_init:这个回调函数负责在读取完配置信息之后注册filter函数
* ngx_http_content_bold_create_loc_conf:此回调函数负责在读取location配置之后,创建模块配置结构的存储空间 */
static ngx_http_module_t ngx_http_content_bold_module_ctx = {
NULL,
ngx_http_content_bold_init,
NULL,
NULL,
NULL,
NULL,
ngx_http_content_bold_create_loc_conf,
NULL
};
/* 该模块的定义,指定了该模块对应的指令、上下文及模块类型 */
ngx_module_t ngx_http_content_bold_filter_module = {
NGX_MODULE_V1,
&ngx_http_content_bold_module_ctx,
ngx_http_content_bold_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
static u_char header_str[30] = "<html><h1 style=\"color:red\">";
static u_char tail_str[13] = "</h1></html>";
/** * 请求体处理函数,过滤请求体功能主要是在这里完成的 * 这里主要是在输入链表的首位分别加上header_str和tail_str已达到加粗的目的 */
static ngx_int_t ngx_http_content_bold_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_chain_t *out;
ngx_chain_t *last_chain_ele;
ngx_buf_t *b;
ngx_buf_t *e;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "content_bold filter");
ngx_http_bold_filter_loc_conf_t * my_conf;
/* 这里是获取该指令的配置信息 */
my_conf = ngx_http_get_module_loc_conf(r, ngx_http_content_bold_filter_module);
/* 如果没有配置content_bold或者值为off。则什么都不处理直接调用下一个body_filter */
if (my_conf->bold_flag == NGX_CONF_UNSET || my_conf->bold_flag == 0) {
return ngx_http_next_body_filter(r, in);
}
/* 如果响应体内容为空,也什么都不做直接调用下一个body_filter */
if (in == NULL) {
return ngx_http_next_body_filter(r, in);
}
/** 为链表首尾结点申请内存 */
out = ngx_pcalloc(r->pool, sizeof(ngx_chain_t));
last_chain_ele = ngx_pcalloc(r->pool, sizeof(ngx_chain_t));
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
e = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (out==NULL || b == NULL||e == NULL||last_chain_ele==NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* 为第一个结点赋值:header_str */
b->pos = header_str;
b->last = header_str + ngx_strlen(header_str);
b->memory = 1;
b->last_buf = 0;
out->buf = b;
out->next = in;
ngx_chain_t *last_but_one = ngx_pcalloc(r->pool, sizeof(ngx_chain_t));
while(in) {
if(in!=NULL){
in->buf->last_buf = 0;
last_but_one = in;
}
in = in -> next;
}
/* 为最后一个结点赋值:tail_str */
e->pos = tail_str;
e->last = tail_str + ngx_strlen(tail_str);
e->memory = 1;
e->last_buf = 1;
last_chain_ele->buf = e;
last_but_one->next = last_chain_ele;
return ngx_http_next_body_filter(r, out);
}
/** * 头部过滤函数,这里主要是更改content_length */
static ngx_int_t ngx_http_content_bold_header_filter(ngx_http_request_t *r)
{
r->headers_out.content_length_n = r->headers_out.content_length_n+ ngx_strlen(header_str) + ngx_strlen(tail_str);
return ngx_http_next_header_filter(r);
}
/** * 为该模块的配置结构申请存储空间 */
static void * ngx_http_content_bold_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_bold_filter_loc_conf_t* local_conf = NULL;
local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_bold_filter_loc_conf_t));
if (local_conf == NULL)
{
return NULL;
}
local_conf->bold_flag = NGX_CONF_UNSET;
return local_conf;
}
/** * 解析指令参数 */
static char *ngx_http_content_bold_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_bold_filter_loc_conf_t* local_conf;
local_conf = conf;
char* rv = NULL;
rv = ngx_conf_set_flag_slot(cf, cmd, conf);
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "bold_flag:%d", local_conf->bold_flag);
return rv;
}
/** * 挂载header_filter处理函数和body_filter处理函数 */
static ngx_int_t ngx_http_content_bold_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_content_bold_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_content_bold_body_filter;
return NGX_OK;
}