nginx 学习五 filter模块简介和实现一个简单的filter模块

来源:互联网 发布:组织架构优化 编辑:程序博客网 时间:2024/05/06 12:57

1 nginx过滤模块简介

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

2 过滤模块执行顺序

2.1 ngx_http_output_(head, body)_filter_pt

先看一下nginx常用的过滤模块,在ngx_moudles.c中有一下代码:

ngx_module_t *ngx_modules[] = {   ......    &ngx_http_write_filter_module,    &ngx_http_header_filter_module,    &ngx_http_chunked_filter_module,    &ngx_http_range_header_filter_module,    &ngx_http_gzip_filter_module,    &ngx_http_postpone_filter_module,    &ngx_http_ssi_filter_module,    &ngx_http_charset_filter_module,    &ngx_http_userid_filter_module,    &ngx_http_myfilter_module,    &ngx_http_headers_filter_module,    &ngx_http_copy_filter_module,    &ngx_http_range_body_filter_module,    &ngx_http_not_modified_filter_module,    NULL};

nginx过滤模块的执行顺序是从ngx_http_not_modified_filter_module 到 ngx_http_write_filter_module。下面我们来看一下这个执行顺序是怎么用链表构成的。

既然是用链表构成的,肯定是要有链表指针,而且有两个,一个链接处理头部的函数,一个链接处理包体的函数,下面来看看这两个链表指针的定义:

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 *in);

ngx_http_output_header_filter_pt 指向处理头部的函数指针,每一个过滤模块中如果要处理头部,就必要定义一个局部的这种类型的指针和函数。

ngx_http_output_body_filter_pt 指向处理包体的函数指针,每一个过滤模块中如果要处理包体,就必要定义一个局部的这种类型的指针和函数。

2.2 过滤模块链表的入口

过滤模块链表的入口是两个静态的函数指针:

extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; extern ngx_http_output_body_filter_pt   ngx_http_body_body_filter;

当执行ngx_http_send_header发送http头部时,就开始从ngx_http_tou_header_filter指针遍历所有的http头部过滤模块。

ngx_http_send_header:

ngx_int_t  ngx_http_send_header(ngx_http_request_t *r)  {      if (r->err_status) {          r->headers_out.status = r->err_status;          r->headers_out.status_line.len = 0;      }        return ngx_http_top_header_filter(r);//链表头,指向第一个过滤头部的模块函数}  

当执行ngx_http_output_filter发送包体时,就从ngx_http_top_body_filter指针遍历所有的http包体过滤模块。

ngx_http_output_filter:

ngx_int_t  ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)  {      ngx_int_t          rc;ngx_connection_t  *c;c = r->connection;rc = ngx_http_top_body_filter(r, in);if (rc == NGX_ERROR) {c->error = 1;}return rc;//链表头,指向第一个过滤包体的模块函数}

那么nginx是怎样用ngx_http_top_header_filter 和 ngx_http_top_body_filter把所有过滤头部的模块和所有过滤包体的模块链接起来的呢?

2.3 过滤模块的链接

原来每个过滤模块都定义了两个(这里假设每个过滤模块都处理头部和包体)static的函数指针:

static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt   ngx_http_next_body_filter;

ngx_http_next_header_filter来保存ngx_http_top_header_filter指向的处理模块函数,而ngx_http_top_header_filter则指向本模块处理头部的过滤函数。

ngx_http_next_body_filter来保存ngx_http_top_body_filter指向的处理模块函数,而ngx_http_top_body_filter则指向本模块处理包体的过滤函数。

代码表示如下:

ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter  = ngx_http_current_myfilter_header_filter;//处理头部的过滤函数ngx_http_next_body_filter   = ngx_http_top_body_filter;ngx_http_top_body_filter    = ngx_http_current_myfilter_body_filter;//处理包体的过滤函数

这样所有过滤模块就被链接起来了。

比如现在有两个过滤模块filter_a, filter_b,filter_a模块先定义,filter_b模块后定义。

在filter_a 里面有如下代码:

ngx_http_next_header_filter_a = ngx_http_top_header_filter;ngx_http_top_header_filter  = ngx_http_filtera_header_filter;ngx_http_next_body_filter_a   = ngx_http_top_body_filter;ngx_http_top_body_filter    = ngx_http_filtera_body_filter;

注意这时ngx_http_header_filter指向filter_a的头部处理函数,ngx_http_top_body_filter指向filter_a的包体处理函数。

在filter_b里面有如下代码:

ngx_http_next_header_filter_b = ngx_http_top_header_filter;ngx_http_top_header_filter  = ngx_http_filterb_header_filter;ngx_http_next_body_filter_b   = ngx_http_top_body_filter;ngx_http_top_body_filter    = ngx_http_filterb_body_filter;

这样以后,ngx_http_next_header_filter_b保存的则是ngx_http_filtera_header_filter,即指向filtera中处理头部的过滤函数,filtera和filterb的头部过滤链表就建立起来了。

同样,ngx_http_next_body_filter_b保存的是ngx_http_filterb_body_filter,即指向filtera中处理包体的过滤函数,filtera和filterb的包体过滤链表就建立起来了。

而此时ngx_http_top_header_filter保存的是filterb的头部过滤函数,ngx_http_top_body_filter保存的是filterb的包体过滤函数。

我们实现filterb的头部过滤函数时的代码往往如下:

ngx_int_t ngx_http_next_header_filter_b(ngx_http_request_r *r){//处理头部的代码    ......    return ngx_http_next_header_filter_b(r, in);//调用filtera处理头部的函数}

实现filterb的包体过滤函数时的代码往往如下:

ngx_int_t ngx_http_next_body_filter_b(ngx_http_request_t *r, ngx_chain_t *in){//处理包体的代码......    return ngx_http_header_filter_b(r, in); //调用filtera处理包体的函数}

呵呵,这样模块filtera和模块filterb就串联起来了,并且filterb的执行在filtera执行之前,这就解释了2.1中模块的执行为什么是倒序。

假如有模块filterc、filterd......都是按照filtera和filterb链接的方式链接在两个链表中的。

3实现一个简单的过滤模块

实现一个简单的过滤模块在头部加入字符串"[my filter prefix] "。

3.1config的编写:

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

注意与handler模块的区别:HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_myfilter_module"

刚开始我就在这里跳进坑了,找了好久,原来不是handler模块中写的HTTP_MODULES。

3.2代码编写

其实实现挺简单的分为几个步骤:

1)三个模块结构体的实现:ngx_command_t 、ngx_http_moudle_t、ngx_moudle_t可以参考这篇文章

2)把当前处理头部和包体的过滤函数链接如链表

3)分别实现过滤头部的函数和过滤包体的函数

第一步 代码:

typedef struct{ngx_flag_t arg_flag;}ngx_http_myfilter_loc_conf_t;typedef struct{    ngx_int_t flag_count;}ngx_http_myfilter_ctx_t;static ngx_str_t filter_prefix = ngx_string("[my filter prefix]");//static限定这两个变量只在当前文件中有效static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt   ngx_http_next_body_filter;static char* ngx_http_myfilter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);static void* ngx_http_myfilter_create_loc_conf(ngx_conf_t *cf);static char* ngx_http_myfilter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);static ngx_int_t ngx_http_myfilter_post_config(ngx_conf_t *cf);static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r);static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);           static ngx_command_t ngx_http_myfilter_commands[] = {    {        ngx_string("add_prefix"),        NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,        ngx_http_myfilter_set,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_myfilter_loc_conf_t, arg_flag),        NULL    },    ngx_null_command};static ngx_http_module_t ngx_http_myfilter_module_ctx = {NULL,ngx_http_myfilter_post_config,NULL,NULL,NULL,NULL,ngx_http_myfilter_create_loc_conf,ngx_http_myfilter_merge_loc_conf};ngx_module_t ngx_http_myfilter_module = {NGX_MODULE_V1,&ngx_http_myfilter_module_ctx,ngx_http_myfilter_commands,NGX_HTTP_MODULE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING};

第二步代码:

static ngx_int_t ngx_http_myfilter_post_config(ngx_conf_t *cf){ngx_http_next_header_filter = ngx_http_top_header_filter;    ngx_http_top_header_filter  = ngx_http_myfilter_header_filter;    ngx_http_next_body_filter   = ngx_http_top_body_filter;    ngx_http_top_body_filter    = ngx_http_myfilter_body_filter;    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "post config is called!");        return NGX_OK;}

第三步代码:

static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r){if (r->headers_out.status != NGX_HTTP_OK)    {ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "headers_out.status = %d", r->headers_out.status);return ngx_http_next_header_filter(r);}ngx_http_myfilter_loc_conf_t *mlcf;mlcf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);if (mlcf->arg_flag == 0) //flag = 0,表示不加前缀{ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "arg_flag = 0");return ngx_http_next_header_filter(r);}ngx_http_myfilter_ctx_t *mctx;mctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);if (mctx != NULL) //mctx不为空,表示这个头部已经处理过,不许要再处理了{return ngx_http_next_header_filter(r);}mctx = ngx_pcalloc(r->pool, sizeof(ngx_http_myfilter_ctx_t));if (mctx == NULL){return NGX_ERROR;}ngx_http_set_ctx(r, mctx, ngx_http_myfilter_module);mctx->flag_count = 0; //0:初始化为不加前缀ngx_str_t type = ngx_string("text/plain");if (r->headers_out.content_type.len >= type.len&& ngx_strncasecmp(r->headers_out.content_type.data, type.data, type.len) == 0){mctx->flag_count = 1; //1:头部类型为“text/plain”的要加前缀if (r->headers_out.content_length_n > 0){r->headers_out.content_length_n += filter_prefix.len;}}return ngx_http_next_header_filter(r);}static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in){ngx_http_myfilter_ctx_t *mctx;mctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);if (mctx == NULL || mctx->flag_count != 1){return ngx_http_next_body_filter(r, in);}mctx->flag_count = 2;ngx_buf_t* buf = ngx_create_temp_buf(r->pool, filter_prefix.len);buf->start = filter_prefix.data;buf->pos   = filter_prefix.data;    buf->last  = filter_prefix.data + filter_prefix.len;ngx_chain_t* out = ngx_alloc_chain_link(r->pool);out->buf         = buf;    out->next        = in;    return ngx_http_next_body_filter(r, out);}


http://blog.csdn.net/xiaoliangsky/article/details/39522357


完整代码以后参考:

#include <ngx_config.h>#include <ngx_core.h>#include <ngx_http.h>typedef struct{ngx_flag_t arg_flag;}ngx_http_myfilter_loc_conf_t;typedef struct{    ngx_int_t flag_count;}ngx_http_myfilter_ctx_t;static ngx_str_t filter_prefix = ngx_string("[my filter prefix]");//static限定这两个变量只在当前文件中有效static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt   ngx_http_next_body_filter;static char* ngx_http_myfilter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);static void* ngx_http_myfilter_create_loc_conf(ngx_conf_t *cf);static char* ngx_http_myfilter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);static ngx_int_t ngx_http_myfilter_post_config(ngx_conf_t *cf);static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r);static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);           static ngx_command_t ngx_http_myfilter_commands[] = {    {        ngx_string("add_prefix"),        NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,        ngx_http_myfilter_set,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_myfilter_loc_conf_t, arg_flag),        NULL    },    ngx_null_command};static ngx_http_module_t ngx_http_myfilter_module_ctx = {NULL,ngx_http_myfilter_post_config,NULL,NULL,NULL,NULL,ngx_http_myfilter_create_loc_conf,ngx_http_myfilter_merge_loc_conf};ngx_module_t ngx_http_myfilter_module = {NGX_MODULE_V1,&ngx_http_myfilter_module_ctx,ngx_http_myfilter_commands,NGX_HTTP_MODULE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING};static ngx_int_t ngx_http_myfilter_post_config(ngx_conf_t *cf){ngx_http_next_header_filter = ngx_http_top_header_filter;    ngx_http_top_header_filter  = ngx_http_myfilter_header_filter;    ngx_http_next_body_filter   = ngx_http_top_body_filter;    ngx_http_top_body_filter    = ngx_http_myfilter_body_filter;    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "post config is called!");        return NGX_OK;}static char* ngx_http_myfilter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){ngx_http_myfilter_loc_conf_t *mlcf = conf;    char* rc = ngx_conf_set_flag_slot(cf, cmd, conf);    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "arg_flag = %d", mlcf->arg_flag);return rc;}static void* ngx_http_myfilter_create_loc_conf(ngx_conf_t *cf){ngx_http_myfilter_loc_conf_t *mlcf;    mlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_myfilter_loc_conf_t));if (mlcf == NULL)    {return NULL;    }        mlcf->arg_flag = NGX_CONF_UNSET;    return mlcf;}static char* ngx_http_myfilter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child){ngx_http_myfilter_loc_conf_t* prev = parent;ngx_http_myfilter_loc_conf_t* conf = child;ngx_conf_merge_value(conf->arg_flag, prev->arg_flag, 0);return NGX_CONF_OK;}static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r){if (r->headers_out.status != NGX_HTTP_OK)    {ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "headers_out.status = %d", r->headers_out.status);return ngx_http_next_header_filter(r);}ngx_http_myfilter_loc_conf_t *mlcf;mlcf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);if (mlcf->arg_flag == 0) //flag = 0,表示不加前缀{ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "arg_flag = 0");return ngx_http_next_header_filter(r);}ngx_http_myfilter_ctx_t *mctx;mctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);if (mctx != NULL) //mctx不为空,表示这个头部已经处理过,不许要再处理了{return ngx_http_next_header_filter(r);}mctx = ngx_pcalloc(r->pool, sizeof(ngx_http_myfilter_ctx_t));if (mctx == NULL){return NGX_ERROR;}ngx_http_set_ctx(r, mctx, ngx_http_myfilter_module);mctx->flag_count = 0; //0:初始化为不加前缀ngx_str_t type = ngx_string("text/plain");if (r->headers_out.content_type.len >= type.len&& ngx_strncasecmp(r->headers_out.content_type.data, type.data, type.len) == 0){mctx->flag_count = 1; //1:头部类型为“text/plain”的要加前缀if (r->headers_out.content_length_n > 0){r->headers_out.content_length_n += filter_prefix.len;}}return ngx_http_next_header_filter(r);}static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in){ngx_http_myfilter_ctx_t *mctx;mctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);if (mctx == NULL || mctx->flag_count != 1){return ngx_http_next_body_filter(r, in);}mctx->flag_count = 2;ngx_buf_t* buf = ngx_create_temp_buf(r->pool, filter_prefix.len);buf->start = filter_prefix.data;buf->pos   = filter_prefix.data;    buf->last  = filter_prefix.data + filter_prefix.len;ngx_chain_t* out = ngx_alloc_chain_link(r->pool);out->buf         = buf;    out->next        = in;    return ngx_http_next_body_filter(r, out);}

注意:因为处理头部的type类型为"text/plain",所以应该访问txt类型的文本才有效。

在nginx/html文件里面创建一个test.txt,内容为:hhhhh。

然后在浏览器中输入:localhost/test.txt

0 0
原创粉丝点击