http过滤模块仅处理服务器发往客户端的http响应,不处理客户端发往服务器的http请求。

过滤模块的调用顺序

过滤链表

在编译nginx源码时,已经定义了一个由所有http过滤模块组成的单链表(每个元素都是一个独立的c源码文件。这个文件会通过两个static指针指向下一个文件中的过滤方法)

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_t *r,ngx_chain_t *chain); //chain表示要发送的包体

http框架中定义的链表入口

extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
extern ngx_http_body_header_filter_pt ngx_http_top_body_filter;

过滤链表的顺序

每个http过滤模块的初始化方法都会把自己加入到单链表的首部

大多数官方http过滤模块都会把初始化方法放到postconfiguration指针中

charles 过滤域名不抓包_初始化方法

http过滤模块的开发步骤

  1. 确定源代码文件名称
  2. 在源代码所在目录创建config脚本文件,在执行configure时将该目录添加进去。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头部的方法
  7. 实现处理http包体的方法
  8. 编译安装,修改nginx.conf文件并启动自定义过滤模块

http过滤模块的简单例子

在返回包体前加一段字符串 “[my fiter prefix]”

charles 过滤域名不抓包_链表_02


charles 过滤域名不抓包_配置项_03

编写config文件

使用一个源文件实现此功能,源文件名为:ngx_http_myfilter_module.c
在该文件所在目录添加config文件:

ngx_addon_name=ngx_http_myfilter_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_myfilter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_myfilter_module.c"

配置项和上下文

首先,希望在nginx.conf中有一个控制当前http过滤模块是否生效的配置项 参数为on | off。建立ngx_http_myfilter_conf_t结构体来存储配置项,使用ngx_flag_t类型的enable变量来存储这个参数值

typedef struct{
    ngx_flag_t enable;
}ngx_http_myfilter_conf_t;

下面的ngx_http_myfilter_create_conf用于分配存储配置项的结构体ngx_http_myfilter_conf_t;

static void* ngx_http_myfilter_create_conf(ngx_conf_t *cf){
    ngx_http_myfilter_conf_t *mycf;

    //创建存储配置项的结构体
    mycf=(ngx_http_myfilter_conf_t *)ngx_pcalloc(cf->pool,sizeof(ngx_http_myfilter_conf_t));
    if(mycf==NULL){
        return NULL;
    }
    //ngx_flat_t 类型的变量。如果使用预设函数ngx_conf_set_flag_slot解析配置项参数,那么必须初始化为NGX_CONF_UNSET
    mycf->enable=NGX_CONF_UNSET;

    return mycf;
}

希望配置不止出现在location{…},还可以出现在server{…}或者http{…}
还需要实现一个配置项值得合并方法-ngx_http_myfilter_merge_conf

static char * ngx_http_myfilter_merge_conf(ngx_conf_t *cf,void *parent,void *child){
    ngx_http_myfilter_conf_t *prev=(ngx_http_myfilter_conf_t*)parent;
    ngx_http_myfilter_conf_t *conf=(ngx_http_myfilter_conf_t*)child;

    //合并ngx_flat_t类型的配置项enable
    ngx_conf_merge_value(conf->enable,prev->enable,0);

    return NGX_CONF_OK;
}

再建立一个http上下文结构体ngx_http_myfilter_ctx_t,其中包括add_prefix整型成员,表示是否添加前缀

typedef struct{
    ngx_int_t add_prefix;
}ngx_http_myfilter_ctx_t;

为0,表示不加;为1,表示加;为2,表示头已加过

HTTP头部处理方法在1个请求中只会被调用1次。但包体处理方法在1个请求中有可能被多次调用。

定义http过滤模块

定义模块前,需要先定义两个关键成员:ngx_command_t类型的commands数组和ngx_http_module_t类型的ctx 成员

定义ngx_http_myfilter_commands数组,处理配置项,将配置项解析到ngx_http_myfilter_conf_t上下文结构体的成员中

charles 过滤域名不抓包_charles 过滤域名不抓包_04


在定义ngx_http_module_t类型的ngx_http_myfilter_module_ctx时,需要将ngx_http_myfilter_create_conf回调方法放到create_loc_conf成员;中,ngx_http_myfilter_merge_conf回调方法则要放到merge_loc_conf成员中;

ngx_http_myfilter_init模块初始化方法放到postconfiguration。

charles 过滤域名不抓包_charles 过滤域名不抓包_05


charles 过滤域名不抓包_配置项_06

有了ngx_command_t类型的commands数组和ngx_http_module_t类型的ctx之后,就可以定义ngx_http_myfilter_module过滤模块了

charles 过滤域名不抓包_链表_07

初始化http过滤模块

在定义ngx_http_myfilter_init方法时,首先需要定义静态指针ngx_http_next_header_filter,用于指向下一个过滤模块的http头部处理方法,定义静态指针ngx_http_next_body_filter,用于指向下一个过滤模块的http包体处理方法

charles 过滤域名不抓包_初始化方法_08

处理请求中的http头部

charles 过滤域名不抓包_初始化方法_09


charles 过滤域名不抓包_配置项_10

处理请求的http包体

charles 过滤域名不抓包_链表_11


charles 过滤域名不抓包_初始化方法_12