zoukankan      html  css  js  c++  java
  • Nginx:HTTP过滤模块

    参考资料<深入理解Nginx>

    HTTP过滤模块也是一种HTTP模块,与普通HTTP处理模块不同在于:

    1.一个请求仅由一个HTTP处理模块处理,而可以被任意个HTTP过滤模块处理

    2.普通的HTTP模块倾向于完成请求的核心功能,而HTTP过滤模块所做的工作是对发送给用户的HTTP响应包做一些加工

     

    HTTP过滤模块的简单例子

    该过滤模块实现的功能是:用户的请求由static静态文件模块进行处理,根据URI返回磁盘中的文件给用户,然后该过滤模块就会在返回给用户的相应包体前添加一段字符串:"[my filter prefix]"

    static静态文件模块处理完成后会调用ngx_http_send_header跟ngx_http_output_filter来调用过滤模块

    1.编写config文件

      跟普通HTTP模块不同的是HTTP_MODULES变量要改为HTTP_FILTER_MODULES

    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"

    2.配置项与上下文

      该过滤模块希望在nginx.conf中由一个控制当前HTTP过滤模块是否生效的配置项,它的参数值是on或者off。首先建立如下结构来存储配置项

    typedef struct {
        ngx_flag_t enable;
    } ngx_http_myfilter_conf_t;

    下面实现的ngx_http_myfilter_create_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;
        }
        mycf->enable=NGX_CONF_UNSET;
        return mycf;
    }

    因为Nginx的异步处理,一个HTTP包体处理方法在1个请求中可能被多次调用,而实际上我们只希望在包头加1次前缀。因此再建立一个HTTP上下文结构体来处理HTTP包体时是否添加前缀

    typedef struct {
        ngx_int_t add_prefix;
    } ngx_http_myfilter_ctx_t;

    3.定义HTTP过滤模块,与普通的HTTP模块类似,不多解释。

    定义ngx_http_myfilter_commands数组,出现配置项是使用Nginx预设的方法ngx_conf_set_flag_slot来处理

    static ngx_command_t ngx_http_myfilter_commands[]={
        {
            //配置项名称
            ngx_string("add_prefix"),
            //配置项类型(可出现的位置,参数的个数)
            NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,
            //出现了name中指定的配置项后,将会调用该方法处理配置项的参数
            ngx_conf_set_flag_slot,
            NGX_HTTP_LOC_CONF_OFFSET,
            offsetof(ngx_http_myfilter_conf_t,enable),
            NULL
        },
        ngx_null_command
    };

    定义ngx_http_module_t,ngx_http_myfilter_init方法用于初始化HTTP过滤模块,下面有它的实现代码

    static ngx_http_module_t ngx_http_myfilter_module_ctx={
        NULL,
        ngx_http_myfilter_init,
        NULL,
        NULL,
        NULL,
        NULL,
        ngx_http_myfilter_create_conf,
        NULL
    };

    定义ngx_module_t

    ngx_module_t ngx_http_myfilter_module={
        NGX_MODULE_V1,
        //指向ngx_http_module_t结构体
        &ngx_http_myfilter_module_ctx,
        //用来处理nginx.conf中的配置项
        ngx_http_myfilter_commands,
        //表示该模块的类型
        NGX_HTTP_MODULE,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NGX_MODULE_V1_PADDING
    };

    4.初始化HTTP过滤模块

    一个请求可以由多个过滤模块处理,所以过滤模块是有调用顺序的,该方法用于构建过滤链表

    //用于初始化HTTP过滤模块
    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)
    {
        //插入到头部处理方法链表的首部
        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_str_t filter_prefix = ngx_string("[filter_prefix]");

    5.处理请求的HTTP头部

     1 static ngx_int_t
     2 ngx_http_myfilter_header_filter(ngx_http_request_t *r)
     3 {
     4     ngx_http_myfilter_ctx_t *ctx;
     5     ngx_http_myfilter_conf_t *conf;
     6     //如果返回失败,则直接交由下一个过滤模块处理
     7     if(r->headers_out.status!=NGX_HTTP_OK){
     8         return ngx_http_next_header_filter(r);
     9     }
    10     //获取HTTP上下文
    11     ctx=ngx_http_get_module_ctx(r,ngx_http_myfilter_module);
    12     if(ctx){
    13         //如果请求的上下文已经存在,说明ngx_http_myfilter_header_filter已经被调用过1次
    14         return ngx_http_next_header_filter(r);
    15     }
    16     //获取存储配置项的ngx_http_myfilter_conf结构体
    17     conf=ngx_http_get_module_loc_conf(r,ngx_http_myfilter_module);
    18     //如果enable成员为0,则直接交给下一个
    19     if(conf->enable==0){
    20         return ngx_http_next_header_filter(r);
    21     }
    22     //构造上下文结构体ngx_http_myfilter_ctx_t
    23     ctx=ngx_pcalloc(r->pool,sizeof(ngx_http_myfilter_ctx_t));
    24     if(ctx==NULL){
    25         return NGX_ERROR;
    26     }
    27     //add_prefix为0表示不加前缀
    28     ctx->add_prefix=0;
    29     //将构造的上下文设置到当前请求
    30     ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);
    31     //myfilter过滤模块只处理Content-Type是"text-plain"类型的HTTP响应
    32     if(r->headers_out.content_type.len>=sizeof("text/plain")-1
    33        && ngx_strncasecmp(r->headers_out.content_type.data,(u_char *)"text/plain",sizeof("text/plain")-1)==0)
    34     {
    35         //设置为1表示需要再HTTP包体前加入前缀
    36         ctx->add_prefix=1;
    37         //把前缀字符串的长度加入Content-length中
    38         if(r->headers_out.content_length_n>0){
    39             r->headers_out.content_length_n+=filter_prefix.len;
    40         }
    41     }
    42     //交由下一个过滤模块继续处理
    43     return ngx_http_next_header_filter(r);
    44 }
    View Code

    6.处理请求的HTTP包体

     1 //处理请求中的HTTP包体
     2 static ngx_int_t
     3 ngx_http_myfilter_body_filter(ngx_http_request_t *r,ngx_chain_t *in)
     4 {
     5     ngx_http_myfilter_ctx_t *ctx;
     6     ctx=ngx_http_get_module_ctx(r,ngx_http_myfilter_module);
     7     //如果获取不到上下文,或者上下文结构体中add_prefix为0或2时,都不会添加前缀
     8     if(ctx==NULL||ctx->add_prefix!=1){
     9         return ngx_http_next_body_filter(r,in);
    10     }
    11     //将add_prefix设置为2,防止重复添加前缀
    12     ctx->add_prefix=2;
    13     //分配内存用于存储字符串前缀
    14     ngx_buf_t *b=ngx_create_temp_buf(r->pool,filter_prefix.len);
    15     //将ngx_buf_t中的指针指向filter_prefix字符串
    16     b->start=b->pos=filter_prefix.data;
    17     b->last=b->pos+filter_prefix.len;
    18     //生成要发送的ngx_chain_t链表,加上刚分配的ngx_buf_t成员
    19     ngx_chain_t *cl=ngx_alloc_chain_link(r->pool);
    20     cl->buf=b;
    21     cl->next=in;
    22     //调用下一个过滤模块
    23     return ngx_http_next_body_filter(r,cl);
    24 }
    View Code

     7.配置nginx.conf文件

     添加如下配置,在nginx安装目录下添加txt/test.txt文件,访问localhost/test.txt。

    location / {
        root txt;
        add_prefix on;
    }
  • 相关阅读:
    九种常用排序的性能分析总结
    C语言输出格式总结
    线程安全的单例模式
    反射原理
    二进制的计算(计算机为什么采用补码存储数据)
    java程序员必须会的技能
    09网易校园招聘笔试题
    Spring获取ApplicationContext方式,和读取配置文件获取bean的几种方式
    【转】策略与机制分离
    VM PowerCli的简单安装和使用学习
  • 原文地址:https://www.cnblogs.com/runnyu/p/4897022.html
Copyright © 2011-2022 走看看