Nginx module开发 简单实例——Filter模块

目录

一 Filter模块介绍
1  Filter 模块调用顺序和作用
2  过滤模块中通常都有两个函数
二 Filter模块实战
1  定义模块指令配置、模块上下文、模块的定义
2  创建模块配置
3  指令
4  注册Filter函数(挂载自定义Filter)
5  Header Filter 处理
6  Body Filter
三、编译运行

正文:

一、Filter模块介绍

    过滤(filter)模块作用: Filter是过滤响应头和内容的模块,可以对回复的头和内容进行处理。
    使用位置: Filter的处理时间在获取回复内容之后,向用户发送响应之前。
    Filter处理过程分为两个阶段:过滤HTTP回复的头部和主体,在这两个阶段可以分别对头部和主体进行修改。 

整体结构图 

clip_image001

数据流程:从11 phrase结束之后->开始13个filter(中间可以加入自定义filter)-->client 

1 Filter 模块调用顺序和作用

Filter执行顺序在编译的时候就决定了。
所有第三方的过滤模块只能加入到copy_filter和headers_filter模块之间执行。
比如本例编写的ngx_http_content_bold_filter_moudle在它们之间.

Handler和Filter的使用区别:
自定义handler模块通过config文件的locate域,直接调用handler模块入口函数.在handler模块入口函数,将handler回调函数挂载到phrase.
自定义filter模块通过加入filter编译序列调用.自定义filter模块,将filter回调函数挂载到filter chain上.

2 过滤模块中通常都有两个函数

ngx_http_top_header_filter(r);
ngx_http_top_body_filter(r, in);

分别对头部和主体进行过滤的函数.所有模块的响应内容,要返回给客户端,都必须调用这两个接口.
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模块头部过滤函数和主体过滤函数共同组成了一个链表结构
nginx为了按照次序来执行各个过滤模块,通常通过局部的全局变量和挂载函数完成.

用图形象表示

在普通HTTP模块处理请求完毕并调用ngx_http_send_header()发送HTTP头部或调用ngx_http_output_filter()发送HTTP包体时,才会由这两个方法一次调用所有的HTTP过滤模块来处理这个请求 
notes:在nginx中这种使用方式很常见, 比如:ngx_rtmp_publish,next_publish。根据编译顺序决定先调用ngx_rtmp_relay_publish,后调用ngx_rtmp_live_publish

 二、Filter模块实战

这部分是对 Nginx module开发 简单实例——filter模块_Besko的博客-CSDN博客 实例的详细说明

Filter模块实现上和hello模块流程一样

设计:HTTP 过滤模块开发步骤

  1. 确定源代码文件名称
  2. 创建 config 文件,HTTP_MODULES 改为 HTTP_FILTER_MODULES 变量,多个源代码文件实现 1 个 HTTP 过滤模块时,需在 NGX_ADDON_SRCS 变量中添加其他源代码文件
  3. 定义过滤模块
  4. 处理感兴趣的配置项
  5. 实现初始化方法:初始化方法就是把模块中处理HTTP头部的ngx_http_output_header_filter_pt方法与处理HTTP包体的ngx_http_output_body_filter_pt方法插入到过滤模块链表的首部。
  6. 实现处理 HTTP 头部的方法:实现 ngx_http_ouput_header_filter_pt 原型的方法,用于处理 HTTP 头部
  7. 实现处理 HTTP 包体的方法:实现 ngx_http_ouput_body_filter_pt 原型的方法,用于处理 HTTP 包体
  8. 编译安装

实现: 

本例实现目的,是将hanlder模块输出内容加粗变颜色

0  Loc_conf struct 定义

#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;

1  模块指令配置、上下文、定义

/*1 该模块的定义,指定了该模块对应的指令、上下文及模块类型 */
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
};
/*2 该模块上下文的定义。 
* 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,       //挂载filter //postconfigure
        NULL,                        
        NULL,                         
        NULL,                        
        NULL,                         
        ngx_http_content_bold_create_loc_conf, //loc config
        NULL                          
};
/** 3 模块指令,这里我们创建了一个配置指令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 
};

2  create_loc_conf

当需要创建数据结构用于存储loca级别(也就是直属于location{...}的配置项)的全局配置项时, 可以通过此回调方法创建存储全局配置项的结构体

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));
    local_conf->bold_flag = NGX_CONF_UNSET;
    return local_conf;
}

3 模块指令解析回调函数

这是一个模块指令回调函数,不是filte模块。它的作用是设置filter模块的on,off

/** * 解析指令参数 */
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;
}

对于flag类型的配置指令,当值为off的时候,使用ngx_conf_set_flag_slot函数,会转化为0.
为on,则转化为非0。

4 注册Filter函数(挂载自定义filter)

ngx_http_content_bold_init:这个回调函数负责在读取完配置信息之后注册filter函数

/*挂载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;
}

挂载自定义ngx_http_content_bold_header_filter和ngx_http_content_bold_body_filter函数.

Filter模块与handler模块挂载函数的不同?
Handler挂载与phrase 绑定.
Filter挂载方法是,根据ngx_http_top_header_filter,ngx_http_top_body_filter 指针的地址,采用头插法将filter挂载函数插入链表.
notes: ngx_http_top_header_filter,ngx_http_top_body_filter是一个链表的两个指针.
Handler的挂载函数参见: 示例: hello handler 模块草稿_fdsafwagdagadg6576的专栏-CSDN博客
                                                “代码分析”的2.2部分.

5 Header filter 处理
Filter模块两个主要的过滤函数header_filter处理函数和body_filter处理函数.

/*头部过滤函数,这里主要是更改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);
}

6  Body filter处理
其实想将handler模块的字体加粗变色逻辑很简单,可以使用一些基本的html语言,设置一下style就行了,所以可以声明两个字符串表示html标签的头和尾

所以body_filter处理函数主要就是,需要把handler输出链表的首尾加上这两个字符串以达成加粗变色的目的,而header_filter处理函数就是需要改变这个新的输出体的长度。

/*请求体处理函数,过滤请求体功能主要是在这里完成的
*这里主要是在输入链表的首位分别加上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));
    /* 为第一个结点赋值: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);
}

作用是在respond body,前面加上static u_char header_str[30] = "<html><h1 style=\"color:red\">";
在最后加上static u_char tail_str[13] = "</h1></html>。
这样,中间的body string根据html语法,变成加粗,变红

三、编译运行

和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可以看到


加粗变红,完成。 

小结:

  • 作用:Filter是过滤响应头和内容的模块,可以对回复的头和内容进行处理
  • 执行位置:在获取回复内容之后,向用户发送响应之前.所有第三方的过滤模块只能加入到copy_filter和headers_filter模块之间执行
  • Filter执行顺序在编译的时候就决定了。
  • Filter处理过程分为两个阶段:过滤HTTP回复的头部和主体
  • 使用方法:
  1. 模块指令配置、上下文、定义
  2. create_loc_conf
  3. 模块指令解析回调函数
  4. 注册Filter函数(挂载自定义filter)
  5. Header filter 处理
  6. Body filter处理

http 13个filter 源码分析,请参考:http://tengine.taobao.org/book/chapter_04.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值