【Linux网络编程】Nginx -- 模块开发(HTTP 过滤模块)

【Linux网络编程】Nginx -- 模块开发(HTTP 过滤模块)

【1】HTTP 过滤模块简介

HTTP过滤模块功能是对发送给用户的HTTP响应包做一些加工,一个请求可以被任意个HTTP过滤模块处理,HTTP过滤模块仅处理服务器发往客户端的HTTP响应,而不处理客户端发往服务器的HTTP请求;

【1.1】HTTP 过滤模块的调用顺序

默认编译进Nginx的官方HTTP过滤模块与第三方HTTP过滤模块间的顺序

【1.2】Nginx 中默认过滤模块功能

【2】HTTP 过滤模块开发代码示例

【2.1】Nginx 配置

...

http {

    ...

    server {

        ...

        location /test {
            mytest;
            add_prefix on;
        }

        ...
        
    }

    ...

}

...

【2.2】示例代码

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

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_http_output_header_filter_pt ngx_http_next_header_filter;
// 响应体过滤函数指针
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

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

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_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);


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,
        ngx_conf_set_flag_slot,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_myfilter_conf_t, enable),
        NULL
    },

    ngx_null_command
};

// 定义 HTTP 框架各阶段的回调方法
// 若HTTP框架初始化时无需完成特定的工作则可将回调置为NULL
// 此处定义了 postconfiguration、create_loc_conf、merge_loc_conf 回调
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 方法 */
};

// 定义 myfilter 模块
// 回调方法,init_module、init_process、exit_process、exit_master 由 Nginx 框架代码调用,与 HTTP 模块无关
ngx_module_t  ngx_http_myfilter_module =
{
    NGX_MODULE_V1,
    &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
};

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

// 处理 HTTP 响应头部
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);
    }

    //获取存储配置项的ngx_http_myfilter_conf_t结构体
    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);
    }

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

    //add_prefix为0表示不加前缀
    ctx->add_prefix = 0;

    //将构造的上下文设置到当前请求中
    ngx_http_set_ctx(r, ctx, ngx_http_myfilter_module);

    //myfilter过滤模块只处理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)
    {
        //1表示需要在http包体前加入前缀
        ctx->add_prefix = 1;

        //如果处理模块已经在Content-Length写入了http包体的长度,由于
        //加入了前缀字符串,所以需要把这个字符串的长度也加入到Content-Length中
        if (r->headers_out.content_length_n > 0)
            r->headers_out.content_length_n += filter_prefix.len;
    }

    //交由下一个过滤模块继续处理
    return ngx_http_next_header_filter(r);
}

// 处理 HTTP 响应体
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不为1时,
    //都不会添加前缀,这时直接交给下一个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);
    cl->buf = b;
    cl->next = in;

    //调用下一个模块的http包体处理方法,注意这时传入的是新生成的cl链表
    return ngx_http_next_body_filter(r, cl);
}

//create_loc_conf回调处理
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;
}

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

参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】深入理解 Nginx 模块开发与架构解析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值