Nginx开发一个简单的HTTP过滤模块_ngx_http_chunked_filter_module-程序员宅基地

技术标签: nginx  

转载:http://blog.csdn.net/zhangxiao93/article/details/53691642

记录为了学习

开发一个HTTP过滤模块的步骤和相关知识跟开发一个普通的HTTP模块是类似的,只不过HTTP过滤模块的地位、作用与正常的HTTP过滤模块不同,它的工作是对发送给用户的HTTP响应包做一些加工。

本文将学习开发一个简单的HTTP过滤模块,它能够对Content-Type为text/plain的包体前加上前缀字符串prefix。

(一)过滤模块的调用顺序

过滤模块可以叠加,也就是说一个请求会被所有的HTTP过滤模块依次处理。

过滤模块的调用是有顺序的,它的顺序在编译的时候就决定了。控制编译的脚本位于auto/modules中,当你编译完Nginx以后,可以在objs目录下面看到一个ngx_modules.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_headers_filter_module,
        &ngx_http_copy_filter_module,
        &ngx_http_range_body_filter_module,
        &ngx_http_not_modified_filter_module,
        NULL
};
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

write_filternot_modified_filter,模块的执行顺序是反向的。也就是说最早执行的是not_modified_filter,然后各个模块依次执行。所有第三方的模块只能加入到copy_filterheaders_filter模块之间执行。

在编译Nginx源码时,已经定义了一个由所有HTTP过滤模块组成的单链表,这个单链表是这样的:

链表的每一个元素都是一个C源代码文件,这个C源代码文件中有两个指针,分别指向下一个过滤模块(文件)的过滤头部和包体的方法(可理解为链表中的next指针)

过滤模块单链表示意图: 

这里写图片描述

这两个指针的声明如下:

/*过滤模块处理HTTP头部的函数指针类型定义,它携带一个参数:请求*/
typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
/*过滤模块处理HTTP包体的函数指针类型定义,它携带两个参数:请求、要发送的包体*/
typedef ngx_int_t (*ngx_http_output_body_filter_pt)
    (ngx_http_request_t *r, ngx_chain_t *chain);
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在我们定义的第三方模块中则有如下声明:

/*用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;
 
 
  
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

那么怎么将这个源文件(节点),插入到HTTP过滤模块组成的单链表中去呢? 
Nginx采用头插法的办法,所有的新节点都插入在链表的开头:

//插入到头部处理方法链表的首部
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;
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中两个top指针声明如下:

extern ngx_http_output_header_filter_pt ngx_http_next_header_filter;
extern ngx_http_output_body_filter_pt ngx_http_next_body_filter;
 
 
  
  • 1
  • 2
  • 1
  • 2

由于是头插法,这样就解释了,越早插入链表的过滤模块,就会越晚执行。


(二)开发一个简单的过滤模块

要开发一个简单的过滤模块,它的功能是对Content-Typetext/plain的响应添加一个前缀,类似于开发一个HTTP模块,它应该遵循如下步骤:

1.确定源代码文件名称,源代码所在目录创建config脚本文件,config文件的编写方式跟HTTP模块开发基本一致,不同的是需要将HTTP_MODULES改成HTTP_FILTER_MODULES

2.定义过滤模块。实例化ngx_module_t类型模块结构,因为HTTP过滤模块也是HTTP模块,所以其中的type成员也是NGX_HTTP_MODULE

3.处理感兴趣的配置项,通过设置ngx_module_t中的ngx_command_t数组来处理感兴趣的配置项。

4.实现初始化方法。初始化方法就是把本模块中处理HTTP头部的ngx_http_output_header_filter_pt方法和处理HTTP包体的ngx_http_output_body_filter_pt方法插入到过滤模块链表的首部。

5.实现4.中提到两个处理头部和包体的方法。

接下来按照上述步骤依次来实现:

2.1 确定源代码文件目录,编写config文件

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"
 
 
  
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

2.2 定义过滤模块,实例化ngx_module_t

/*定义过滤模块,ngx_module_t结构体实例化*/
ngx_module_t ngx_http_myfilter_module =
{
    NGX_MODULE_V1,                 /*Macro*/
    &ngx_http_myfilter_module_ctx,         /*module context*/
    ngx_http_myfilter_commands,            /*module directives*/
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING                  /*Macro*/
};
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.3 处理感兴趣的配置项

/*处理感兴趣的配置项*/
static ngx_command_t ngx_http_myfilter_commands[]=
{
    {
    ngx_string("add_prefix"), //配置项名称
    NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_FLAG,//配置项只能携带一个参数并且是on或者off
    ngx_conf_set_flag_slot,//使用nginx自带方法,参数on/off
    NGX_HTTP_LOC_CONF_OFFSET,//使用create_loc_conf方法产生的结构体来存储
    //解析出来的配置项参数
    offsetof(ngx_http_myfilter_conf_t, enable),//on/off
    NULL
    },
    ngx_null_command //
};
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其中定义结构体ngx_http_myfilter_conf_t来保存配置项参数:

typedef struct
{
    ngx_flag_t enable;
}ngx_http_myfilter_conf_t;
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2.4 实现初始化方法

头插入法将本过滤模块插入到单链表的首部:

/*初始化方法*/
static ngx_int_t
ngx_http_myfilter_init(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;
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.5 实现头部和包体过滤方法

2.5.1 函数声明
/*头部处理方法*/
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);
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
2.5.2 函数实现

(1)头部处理方法:最终处理效果头部信息Content-Length的值加上prefix的长度。

/*头部处理方法*/
static ngx_int_t
ngx_http_myfilter_header_filter(ngx_http_request_t *r)
{
    ngx_http_myfilter_ctx_t *ctx;
    ngx_http_myfilter_conf_t *conf;
    //如果不是返回成功,这时是不需要理会是否加前缀的,
    //直接交由下一个过滤模块
    //处理响应码非200的情形
    if (r->headers_out.status != NGX_HTTP_OK)
    {
        return ngx_http_next_header_filter(r);
    }


    /*获取http上下文*/
    ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);

    if(ctx)
    {
        //该请求的上下文已经存在,这说明
        // ngx_http_myfilter_header_filter已经被调用过1次,
        //直接交由下一个过滤模块处理
        return ngx_http_next_header_filter(r);
    }


    //获取存储配置项参数的结构体
    conf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);

    //如果enable成员为0,也就是配置文件中没有配置add_prefix配置项,
    //或者add_prefix配置项的参数值是off,这时直接交由下一个过滤模块处理
    if (conf->enable == 0)
    {
        return ngx_http_next_header_filter(r);
    }

    //conf->enable==1
    //构造http上下文结构体ngx_http_myfilter_ctx_t
    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_myfilter_ctx_t));
    if(NULL==ctx)
    {
        return NGX_ERROR;
    }
    ctx->add_prefix=0;
    ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);

    //只处理Content-Type是"text/plain"类型的http响应
    if (r->headers_out.content_type.len >= sizeof("text/plain") - 1
        && ngx_strncasecmp(r->headers_out.content_type.data, (u_char *) "text/plain", sizeof("text/plain") - 1) == 0)
    {
        ctx->add_prefix=1;
        if(r->headers_out.content_length_n > 0)
        {
            r->headers_out.content_length_n+=filter_prefix.len;
        }
    }
    //交由下一个过滤模块继续处理
    return ngx_http_next_header_filter(r);
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

(2)响应包体处理方法:最终处理效果在包体前面添加前缀。

/*包体处理方法*/
static ngx_int_t
ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_myfilter_ctx_t *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);

    //如果获取不到上下文,或者上下文结构体中的add_prefix为0或者2时,
    //都不会添加前缀,这时直接交给下一个http过滤模块处理
    if (ctx == NULL || ctx->add_prefix != 1)
    {
        return ngx_http_next_body_filter(r, in);
    }

    //将add_prefix设置为2,这样即使ngx_http_myfilter_body_filter
    //再次回调时,也不会重复添加前缀
    ctx->add_prefix = 2;


    //从请求的内存池中分配内存,用于存储字符串前缀
    ngx_buf_t* b = ngx_create_temp_buf(r->pool, filter_prefix.len);

    //将ngx_buf_t中的指针正确地指向filter_prefix字符串
    b->start = b->pos = filter_prefix.data;
    b->last = b->pos + filter_prefix.len;

    //从请求的内存池中生成ngx_chain_t链表,将刚分配的ngx_buf_t设置到
    //其buf成员中,并将它添加到原先待发送的http包体前面
    ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
    /*note: in表示原来待发送的包体*/
    cl->buf = b;
    cl->next = in;

    //调用下一个模块的http包体处理方法,注意这时传入的是新生成的cl链表
    return ngx_http_next_body_filter(r, cl);
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

(三)完整代码与测试

至此,已经完成大部分的工作,我们还需要为这个过滤模块编写模块上下文,编写创建和合并配置项参数结构体的函数等。

3.1 完整代码

/*ngx_http_myfilter_module.c*/

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>


/*用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 ngx_int_t
ngx_http_myfilter_init(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);


typedef struct
{
    ngx_flag_t enable;
}ngx_http_myfilter_conf_t;

/*请求上下文*/
typedef struct
{
    ngx_int_t add_prefix;
}ngx_http_myfilter_ctx_t;

/*在包体中添加的前缀*/
static ngx_str_t filter_prefix=ngx_string("[my filter prefix]");



/*处理感兴趣的配置项*/
static ngx_command_t ngx_http_myfilter_commands[]=
{
    {
        ngx_string("add_prefix"), //配置项名称
        NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_FLAG,//配置项只能携带一个参数并且是on或者off
        ngx_conf_set_flag_slot,//使用nginx自带方法,参数on/off
        NGX_HTTP_LOC_CONF_OFFSET,//使用create_loc_conf方法产生的结构体来存储
        //解析出来的配置项参数
        offsetof(ngx_http_myfilter_conf_t, enable),//on/off
        NULL
    },
    ngx_null_command //
};

static void* ngx_http_myfilter_create_conf(ngx_conf_t *cf);
static char*
ngx_http_myfilter_merge_conf(ngx_conf_t *cf,void*parent,void*child);

/*模块上下文*/
static ngx_http_module_t ngx_http_myfilter_module_ctx=
{
    NULL,                                  /* preconfiguration方法  */
    ngx_http_myfilter_init,            /* postconfiguration方法 */

    NULL,                                  /*create_main_conf 方法 */
    NULL,                                  /* init_main_conf方法 */

    NULL,                                  /* create_srv_conf方法 */
    NULL,                                  /* merge_srv_conf方法 */

    ngx_http_myfilter_create_conf,    /* create_loc_conf方法 */
    ngx_http_myfilter_merge_conf      /*merge_loc_conf方法*/
};


/*定义过滤模块,ngx_module_t结构体实例化*/
ngx_module_t ngx_http_myfilter_module =
{
    NGX_MODULE_V1,                 /*Macro*/
    &ngx_http_myfilter_module_ctx,         /*module context*/
    ngx_http_myfilter_commands,            /*module directives*/
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING                  /*Macro*/
};


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

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;

}



/*初始化方法*/
static ngx_int_t
ngx_http_myfilter_init(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;
    return NGX_OK;
}

/*头部处理方法*/
static ngx_int_t
ngx_http_myfilter_header_filter(ngx_http_request_t *r)
{
    ngx_http_myfilter_ctx_t *ctx;
    ngx_http_myfilter_conf_t *conf;
    //如果不是返回成功,这时是不需要理会是否加前缀的,
    //直接交由下一个过滤模块
    //处理响应码非200的情形
    if (r->headers_out.status != NGX_HTTP_OK)
    {
        return ngx_http_next_header_filter(r);
    }


    /*获取http上下文*/
    ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);

    if(ctx)
    {
        //该请求的上下文已经存在,这说明
        // ngx_http_myfilter_header_filter已经被调用过1次,
        //直接交由下一个过滤模块处理
        return ngx_http_next_header_filter(r);
    }


    //获取存储配置项参数的结构体
    conf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);

    //如果enable成员为0,也就是配置文件中没有配置add_prefix配置项,
    //或者add_prefix配置项的参数值是off,这时直接交由下一个过滤模块处理
    if (conf->enable == 0)
    {
        return ngx_http_next_header_filter(r);
    }

    //conf->enable==1
    //构造http上下文结构体ngx_http_myfilter_ctx_t
    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_myfilter_ctx_t));
    if(NULL==ctx)
    {
        return NGX_ERROR;
    }
    ctx->add_prefix=0;
    ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);

    //只处理Content-Type是"text/plain"类型的http响应
    if (r->headers_out.content_type.len >= sizeof("text/plain") - 1
        && ngx_strncasecmp(r->headers_out.content_type.data, (u_char *) "text/plain", sizeof("text/plain") - 1) == 0)
    {
        ctx->add_prefix=1;
        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 *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);

    //如果获取不到上下文,或者上下文结构体中的add_prefix为0或者2时,
    //都不会添加前缀,这时直接交给下一个http过滤模块处理
    if (ctx == NULL || ctx->add_prefix != 1)
    {
        return ngx_http_next_body_filter(r, in);
    }

    //将add_prefix设置为2,这样即使ngx_http_myfilter_body_filter
    //再次回调时,也不会重复添加前缀
    ctx->add_prefix = 2;
    //从请求的内存池中分配内存,用于存储字符串前缀
    ngx_buf_t* b = ngx_create_temp_buf(r->pool, filter_prefix.len);

    //将ngx_buf_t中的指针正确地指向filter_prefix字符串
    b->start = b->pos = filter_prefix.data;
    b->last = b->pos + filter_prefix.len;

    //从请求的内存池中生成ngx_chain_t链表,将刚分配的ngx_buf_t设置到
    //其buf成员中,并将它添加到原先待发送的http包体前面
    ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
    /*note: in表示原来待发送的包体*/
    cl->buf = b;
    cl->next = in;

    //调用下一个模块的http包体处理方法,注意这时传入的是新生成的cl链表
    return ngx_http_next_body_filter(r, cl);
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240

3.2 测试

我们的过滤模块只对Content-Typetext/plain的响应有效,查看Nginx的默认配置中的mime.types文件,发现

types{
#...
    text/plain                            txt;
#...
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

也即当请求资源为txt时才会调用我们过滤模块,如果想要强制将响应的Content-Type设置为text/plain呢? 
只需要修改nginx.conf文件如下即可:


#user  root;
worker_processes  1;

error_log  logs/error.log  debug;

events {
    worker_connections  1024;
}

http {
# 注释掉http块下的配置
#    include       mime.types;
#    default_type  application/octet-stream;

    keepalive_timeout  65;

    server {
        listen 1024;

        location / {
        #在location块下将默认类型设置为text/plain
            default_type text/plain;
            root   html;
            add_prefix on;
            index index.htm index.html;
        }

    }
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

将自定义的过滤模块编译进Nginx:

./configure --add-module=/home/zhangxiao/nginx/nginx-1.0.15/src/myHttpFilterModule/
make;sudo make install
 
 
  
  • 1
  • 2
  • 1
  • 2

重启nginx,用curl工具进行测试

curl -v localhost:1024
 
 
  
  • 1
  • 1

可以看到返回的包体添加了前缀: 

这里写图片描述


(四)参考

1.《深入理解Nginx》 
2. Nginx开发从入门到精通

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xiaofei0859/article/details/70819453

智能推荐

定位_csdn中如何设置定位-程序员宅基地

文章浏览阅读447次。为什么需要定位1.某个元素可以自由的在一个盒子内移动位置,并且压住其他盒子2.当我们滚动窗口的时候,盒子是固定在屏幕某个位置的以上效果,标准流或浮动都无法快速实现,此时需要定位来实现。所以: 1.浮动可以让多个块级盒子一行没有缝隙排列显示,经常用于横向排列盒子。 2.定位则是可以让盒子自由的在某个盒子内移动位置或者固定屏幕中某个位置,并且可以压住其他盒子。定位组成定位:将盒子定在某一个位置,所以定位也是在摆放盒子,按照定位的方式移动盒子。定位 = 定位模式 + 边偏移。定_csdn中如何设置定位

Python3输出格式化时间yyyy-mm-dd HH:MM:SS_python 打印yyyy-mm-dd hh:mm:ss-程序员宅基地

文章浏览阅读4.6k次,点赞2次,收藏2次。python3输出格式化时间yyyy-mm-dd HH:MM:SS_python 打印yyyy-mm-dd hh:mm:ss

自学编程、玩 vlog,90 后程序员们的冠军之路-程序员宅基地

文章浏览阅读734次。作者 | 唐小引出品 | CSDN(ID:CSDNnews)第一批 90 后迎来三十而立。最后一批 90 后已是二十弱冠。在 CSDN 组织的《2018-2019中国开发者调查报告》中,..._刘泓谷

腾讯副总裁曾宇:谈谈腾讯的技术价值观与技术人才修炼_价值观路落地 腾旭 陆文卓-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏4次。作者简介:曾宇先生,腾讯公司副总裁。 2002 年加入腾讯,曾负责腾讯研发线管理,后续担任互动娱乐事业群研发部总经理,负责游戏、互娱相关的技术研发及管理工作,2012 年升任公司 VP,16 年起主要负责移动互联网事业群技术管理工作,继续参与公司级技术管理工作。正文技术人员的核心素养腾讯的职业发展通道大概有 6 级,1 级是初入者,2 级是有经验者,3 级是骨干,4 级是专家,5 级和 6 级是权_价值观路落地 腾旭 陆文卓

html属于页面的底部标签是,HTML5中footer标签的用法你知道吗?,HTML5中的footer标签是什么意思?...-程序员宅基地

文章浏览阅读3.2k次。HTML5中footer标签的用法你知道吗?,HTML5中的footer标签是什么意思?本篇文章就为大家介绍HTML5中footer标签的定义和具体用法,还有footer中的标准属性,还有置于页面最底部的简单实现方法。HTML5中footer标签定义和用法: 标签定义 section 或 document 的页脚。在典型情况下,该元素会包含创作者的姓名、文档的创作日期以及/或者联系信息。在一个文档..._html5底部标签

CentOS7部署l2tp/IPsec服务-程序员宅基地

文章浏览阅读2.3k次。1、安装必要的工具yum install vim net-tools wget unzip -y2. 下载安装脚本wget -O StackScript.zip http://files.cnblogs.com/files/think8848/StackScript.zip3. 解压文件unzip StackScript.zip4. 执行..._stackscript centos

随便推点

C++程序设计案例实训教程第4章_第二个字符数组被声明为const以保证该数组中的内容不会在函数调用期间修改为-程序员宅基地

文章浏览阅读825次,点赞3次,收藏3次。第4章 C++字符串第3章介绍了C风格语言字符串的相关内容,字符串的处理在程序中应用广泛,C语言风格字符串是以‘“\0’”(空字符)来作为结尾的字符数组。字符串是用来存储一个以上字符的非数字值的变量。在使用时,程序员需要考虑字符数组大小的开辟和结尾空字符的处理,使用起来有诸多不便。鉴于此,C++提供了string类用于字符串的处理。string类定义在头文件string中,注意和上一章提到的_第二个字符数组被声明为const以保证该数组中的内容不会在函数调用期间修改为

TODO:Golang Linux进程退出说明_golang 启动进程 自身退出-程序员宅基地

文章浏览阅读752次。TODO:Golang Linux进程退出说明Golang使用os.Exit(code)进程退出导致当前程序退出并返回给定的状态代码。传统上,code代码为零表示成功退出,非零错误退出。syscall.Exit可以调用不同平台的操作系统的退出。code值为0-255,当code为非0时,os.Exit(code)会返回,对应值当状态码exit status 12_golang 启动进程 自身退出

Latex 经常见到的问题和解决方法_latex中出现newlabel-程序员宅基地

文章浏览阅读276次。Latex 经常见到的问题和解决方法_latex中出现newlabel

跟我学UDS(ISO14229) ———— 0x11(ECUReset)_31服务负响应-程序员宅基地

文章浏览阅读5.2k次,点赞2次,收藏7次。qwrq_31服务负响应

os练习题2_在unix操作系统中,把输入输出设备看作是-程序员宅基地

文章浏览阅读803次。①对存放在单一存储设备(如磁带)上的顺序文件连续存取速度快②顺序文件存放在多路存储设备(如磁盘)上时,在多道程序的情况下,由于别的用户可能驱使磁头移向其它柱面,会降低连续存取的速度。顺序文件多用于磁带从内到外, 硬件系统,操作系统,支撑软件,应用软件。计算机系统由硬件和软件(系统程序+应用程序)组成。在UNIX操作系统中,把输入/输出设备看作是特殊文件。在UNIX..._在unix操作系统中,把输入输出设备看作是

字节跳动高工面试:mysql查询重复数据sql_bytesql.eq-程序员宅基地

文章浏览阅读118次。正文作为后端开发,日常操作数据库最常用的是写操作和读操作。读操作我们下边会讲,这个分类里我们主要来看看写操作时为什么会导致 SQL 变慢。刷脏页脏页的定义是这样的:内存数据页和磁盘数据页不一致时,那么称这个内存数据页为脏页。那为什么会出现脏页,刷脏页又怎么会导致 SQL 变慢呢?那就需要我们来看看写操作时的流程是什么样的。对于一条写操作的 SQL 来说,执行的过程中涉及到写日志,内存及同步磁盘这几种情况。这里要提到一个日志文件,那就是 redo log,位于存储引擎层,用来存储物理日志。在写操_bytesql.eq