zoukankan      html  css  js  c++  java
  • 《深入理解Nginx》阅读与实践(四):简单的HTTP过滤模块

    一、Nginx的HTTP过滤模块特征

      一个请求可以被任意个HTTP模块处理

      在普通HTTP模块处理请求完毕并调用ngx_http_send_header()发送HTTP头部或调用ngx_http_output_filter()发送HTTP包体时,才会由这两个方法一次调用所有的HTTP过滤模块来处理这个请求。HTTP过滤模块仅处理服务器发送到客户端的响应,而不处理客户端发往服务器的HTTP请求。

      多个过滤模块的顺序的形成以及Nginx自带的过滤模块请参考原书。

    二、编写一个HTTP过滤模块

       以向返回给用户的文本格式响应包体前加一段字符串"[my filter prefix]"为例,展示如何编写一个HTTP过滤模块。源代码来自于《深入理解Nginx》。

    1.config文件的编写

      与前几篇博文的HTTP模块不同,HTTP过滤模块需要HTTP_FILTER_MODULES一项以把所有过滤模块一同编译,因此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"

      进行configure时,--add-module=PATH是一样的。

    2.编写模块基本内容:模块定义、配置项处理

      由于需要在nginx.conf中加入一项flag类型的add_fix来控制这个过滤模块的使用与否,与这个配置项处理相关的ngx_http_myfilter_create_conf()、ngx_http_myfilter_merge_conf()、ngx_http_mytest_commands[]需要对应地进行处理。

    typedef struct {
        ngx_flag_t enable;
    } ngx_http_myfilter_conf_t;
    
    typedef struct {
        ngx_int_t add_prefix;
    } ngx_http_myfilter_ctx_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;
        }
        mycf->enable = NGX_CONF_UNSET;
        return mycf;
    }
    ngx_http_myfilter_create_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_conf_merge_value(conf->enable,prev->enable,0);
        return NGX_CONF_OK;
    }
    ngx_http_myfilter_merge_conf
    static ngx_command_t ngx_http_mytest_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
    };
    ngx_http_mytest_commands[]

      这样之后才是模块的上下文和模块定义:

    static ngx_http_module_t ngx_http_myfilter_module_ctx = {
        NULL,
        ngx_http_myfilter_init,
        NULL,
        NULL,
    
        NULL,
        NULL,
        ngx_http_myfilter_create_conf,
        ngx_http_myfilter_merge_conf
    };
    ngx_http_myfilter_module_ctx
    ngx_module_t ngx_http_myfilter_module = {
        NGX_MODULE_V1,
        &ngx_http_myfilter_module_ctx,
        ngx_http_mytest_commands,
        NGX_HTTP_MODULE,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NGX_MODULE_V1_PADDING
    };
    ngx_http_myfilter_module

      从模块上下文可以看出,过滤功能在模块完成配置项处理后开始,其初始化方法为ngx_myfilter_init()。

    3.过滤功能实现

      初始化方法ngx_myfilter_init()的功能仅仅是把当前过滤模块插入Nginx所有过滤模块的链表中。

    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;
    }
    ngx_int_t ngx_http_myfilter_init()

      头部处理方法是为了确定返回的类型是否为text/plain。如果是,则包体处理方法需要添加前缀。这里把前缀硬编码至模块源码中。

    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;
    
        if(r->headers_out.status != NGX_HTTP_OK)
        {
            return ngx_http_next_header_filter(r);
        }
    
        ctx = ngx_http_get_module_ctx(r,ngx_http_myfilter_module);
        if(ctx) {
            return ngx_http_next_header_filter(r);
        }
        conf = ngx_http_get_module_loc_conf(r,ngx_http_myfilter_module);
        if(conf->enable == 0)
        {
            return ngx_http_next_header_filter(r);
        }
        ctx = ngx_pcalloc(r->pool,sizeof(ngx_http_myfilter_ctx_t));
        if(ctx == NULL)
        {
            return NGX_ERROR;
        }
        ctx->add_prefix = 0;
    
        ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);
        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_myfilter_header_filter(r);
    }
    ngx_http_myfilter_header_filter()

      包体处理方法根据头部处理方法的结果来为包体添加前缀。

    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);
        if(ctx==NULL||ctx->add_prefix != 1) {
            return ngx_http_next_body_filter(r,in);
        }
    
        ctx->add_prefix = 2;
    
        ngx_buf_t* b= ngx_create_temp_buf(r->pool,filter_prefix.len);
        b->start = b->pos = filter_prefix.data;
        b->last = b->pos + filter_prefix.len;
    
        ngx_chain_t *c1 = ngx_alloc_chain_link(r->pool);
        c1->buf = b;
        c1->next = in;
        return ngx_http_next_body_filter(r,c1);
    }
    ngx_http_myfilter_body_filter()

    三、过滤模块测试

      根据原作者编写的nginx.conf

        server {
            listen 8080;
    
            location / {
                root /;
                add_prefix on;
            }
        }

    可以看出,需要在/目录下(系统根目录)添加一个或多个任意内容的文本文件来进行测试。我写了一个内容为test的文本文件test.txt。

      输入curl http://localhost:8080/test.txt,可以看到返回的内容是[my filter prefix]test。

      当然,如果你放在/的不是纯文本文件,而是html文件或者其他类型文件,是不会增加这个前缀的。

      另外,把on改成off,你会发现前缀不再出现,说明过滤模块功能已经关闭。

      p.s.此书的后续章节是源码分析,实践环节比较少,“《深入理解Nginx》阅读与实践”系列可能到此为止。

  • 相关阅读:
    又玩起了“数独”
    WebService应用:音乐站图片上传
    大家都来DIY自己的Blog啦
    CSS导圆角,不过这个代码没有怎么看懂,与一般的HTML是不同
    网站PR值
    CommunityServer2.0何去何从?
    网络最经典命令行
    炎热八月,小心"落雪"
    Topology activation failed. Each partition must have at least one index component from the previous topology in the new topology, in the same host.
    SharePoint 2013服务器场设计的一些链接
  • 原文地址:https://www.cnblogs.com/wuyuegb2312/p/3271919.html
Copyright © 2011-2022 走看看