zoukankan      html  css  js  c++  java
  • Nginx模块开发(5)————开发简单的HTTP过滤模块

    该模块可实现如下的功能,在浏览器输入http://你的IP/lcw.text,能够读出你在根目录下创建的lcw.txt里面的内容,并在前面加上一句字符串where there is a will,there is a way!
    代码如下:

    //start from the very beginning,and to create greatness
    //@author: Chuangwei Lin
    //@E-mail:979951191@qq.com
    //@brief: 一个简单的nginx的HTTP过滤模块
    
    #include <ngx_config.h>//包含必要的头文件
    #include <ngx_core.h>
    #include <ngx_http.h>
    //建立ngx_http_lcwfilter_conf_t结构体用于存储配置项
    typedef struct
    {
        ngx_flag_t enable;
    } ngx_http_lcwfilter_conf_t;
    //建立ngx_http_lcwfilter_ctx_t结构体作为HTTP上下文结构
    typedef struct
    {
        ngx_int_t add_prefix;
    } ngx_http_lcwfilter_ctx_t;
    
    //定义静态指针用于指向下一个过滤模块的HTTP头部处理方法
    static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
    //定义静态指针用于指向下一个过滤模块的HTTP包体处理方法
    static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
    
    //将在包体中添加这个前缀
    static ngx_str_t filter_prefix = ngx_string("where there is a will,there is a way!");
    
    //先声明函数
    static ngx_int_t ngx_http_lcwfilter_init(ngx_conf_t *cf);
    static ngx_int_t ngx_http_lcwfilter_header_filter(ngx_http_request_t *r);
    static ngx_int_t ngx_http_lcwfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
    static void* ngx_http_lcwfilter_create_conf(ngx_conf_t *cf);
    static char *ngx_http_lcwfilter_merge_conf(ngx_conf_t *cf, void *parent, void *child);
    //ngx_command_t定义模块的配置文件参数
    static ngx_command_t ngx_http_lcwfilter_commands[] =
    {
        {
            //配置项名称
            ngx_string("lcw_add_prefix"),
            //配置项类型,将指定配置项可以出现的位置
            //例如出现在server{}或location{}中,以及他可以携带的参数个数(现在有一个参数)
             NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_FLAG,
             //ngx_command_t结构体中的set成员,
             //当在某个配置块中出现lcw_add_prefix配置项时,Nginx将会调用ngx_conf_set_flag_slot方法
             ngx_conf_set_flag_slot,//这个是预设函数,用于解析配置项,不用我们自己实现
             //在配置文件中的偏移量conf
             NGX_HTTP_LOC_CONF_OFFSET,
             //offset通常用于使用预设的解析方法解析配置项,需要与conf配合使用
             //解析配置后将配置项参数解析到ngx_http_lcwfilter_conf_t上下文结构体的enable成员中
             offsetof(ngx_http_lcwfilter_conf_t, enable),
             //配置项读取后的处理方法,必须是ngx_conf_post_t结构的指针
             NULL
        },
        //ngx_null_command是一个空的ngx_command_t结构,用来表示数组的结尾
        ngx_null_command
    };
    //ngx_http_module_t的8个回调方法,因为目前没有什么工作是必须在HTTP框架初始化
    //时完成的,所以暂时不必实现ngx_http_module_t的8个回调方法
    static ngx_http_module_t  ngx_http_lcwfilter_module_ctx =
    {
        NULL,       // preconfiguration解析配置文件前调用
        ngx_http_lcwfilter_init,//postconfiguration 完成配置文件解析后调用
    
        NULL,       // create main configuration当需要创建数据结构用于存储main级别的
                    //(直属于http{}块的配置项)的全局配置项时
        NULL,       // init main configuration常用于初始化main级别的配置项
    
        NULL,       // create server configuration当需要创建数据结构用于存储srv级别的
                    //(直属于server{}块的配置项)的配置项时 
        NULL,       // merge server configuration用于合并main级别和srv级别下的同名配置项
    
        ngx_http_lcwfilter_create_conf,// create location configuration 当需要创建数据结构用于存储loc级别的
                    //(直属于location{}块的配置项)的配置项时
        ngx_http_lcwfilter_merge_conf// merge location configuration 用于合并srv和loc级别下的同名配置项
    };
    //定义lcwfilter模块
    //lcwfilter模块在编译时会被加入到ngx_modules全局数组中
    //Nginx在启动时,会调用所有模块的初始化回调方法
    //HTTP框架初始化时会调用ngx_http_module_t中的8个方法
    //HTTP模块数据结构
    ngx_module_t  ngx_http_lcwfilter_module =
    {
        NGX_MODULE_V1,//该宏为下面的ctx_index,index,spare0,spare1,spare2,spare3,version变量
                      //提供了初始化的值:0,0,0,0,0,0,1
        //ctx_index表示当前模块在这类模块中的序号
        //index表示当前模块在所有模块中的序号,Nginx启动时会根据ngx_modules数组设置各模块的index值
        //spare0   spare系列的保留变量,暂未使用
        //spare1
        //spare2
        //spare3
        //version模块的版本,便于将来的扩展,目前只有一种,默认为1
        &ngx_http_lcwfilter_module_ctx, //ctx用于指向一类模块的上下文结构
        ngx_http_lcwfilter_commands,   //commands将处理nginx.conf中的配置项
        NGX_HTTP_MODULE,        //模块的类型,与ctx指针紧密相关,取值范围是以下5种:
                                //NGX_HTTP_MODULE,NGX_CORE_MODULE,NGX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MAIL_MODULE
        //以下7个函数指针表示有7个执行点会分别调用这7种方法,对于任一个方法而言,如果不需要nginx在某个是可执行它
        //那么简单地将他设为空指针即可
        NULL,                           //master进程启动时回调init_master
        NULL,                           //init_module回调方法在初始化所有模块时被调用,在master/worker模式下,
                                        //这个阶段将在启动worker子进程前完成
        NULL,                           //init_process回调方法在正常服务前被调用,在master/worker模式下,
                                        //多个worker子进程已经产生,在每个worker子进程的初始化过程会调用所有模块的init_process函数
        NULL,                           //由于nginx暂不支持多线程模式,所以init thread在框架代码中没有被调用过
        NULL,                           // exit thread,也不支持
        NULL,                           //exit process回调方法将在服务停止前调用,在master/worker模式下,worker进程会在退出前调用它
        NULL,                           //exit master回调方法将在master进程退出前被调用
        NGX_MODULE_V1_PADDING           //这里是8个spare_hook变量,是保留字段,目前没有使用,Nginx提供了NGX_MODULE_V1_PADDING宏来填充
    };
    /******************************************************
    函数名:ngx_http_lcwfilter_init(ngx_conf_t *cf)
    参数:
    功能:初始化HTTP过滤模块
    *******************************************************/
    static ngx_int_t ngx_http_lcwfilter_init(ngx_conf_t *cf)
    {
        //插入到头部处理方法链表的首部
        ngx_http_next_header_filter = ngx_http_top_header_filter;
        ngx_http_top_header_filter = ngx_http_lcwfilter_header_filter;//这个方法在下面实现
        //插入到包体处理方法链表的首部
        ngx_http_next_body_filter = ngx_http_top_body_filter;
        ngx_http_top_body_filter = ngx_http_lcwfilter_body_filter;//这个方法也在下面实现
        return NGX_OK;
    }
    /******************************************************
    函数名:ngx_http_lcwfilter_header_filter(ngx_http_request_t *r)
    参数:
    功能:HTTP头部处理方法
    *******************************************************/
    static ngx_int_t ngx_http_lcwfilter_header_filter(ngx_http_request_t *r)
    {
        //这个函数主要是获取配置项参数,并根据参数设置HTTP上下文
        ngx_http_lcwfilter_ctx_t   *ctx;//用于获取HTTP上下文结构
        ngx_http_lcwfilter_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_lcwfilter_module);
        if (ctx)
        {
            //该请求的上下文已经存在,这说明
            // ngx_http_lcwfilter_header_filter已经被调用过1次,
            //直接交由下一个过滤模块处理
            return ngx_http_next_header_filter(r);
        }
        //获取存储配置项的ngx_http_lcwfilter_conf_t结构体
        conf = ngx_http_get_module_loc_conf(r, ngx_http_lcwfilter_module);
        //如果enable成员为0,也就是配置文件中没有配置lcw_add_prefix配置项,
        //或者lcw_add_prefix配置项的参数值是off,这时直接交由下一个过滤模块处理
        if (conf->enable == 0)
        {
            return ngx_http_next_header_filter(r);
        }   
        //这里已经确认配置
        //构造http上下文结构体ngx_http_lcwfilter_ctx_t
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_lcwfilter_ctx_t));
        if (ctx == NULL)
        {
            return NGX_ERROR;
        }
        //add_prefix为0表示不加前缀
        ctx->add_prefix = 0;
        //将构造的上下文设置到当前请求中
        ngx_http_set_ctx(r, ctx, ngx_http_lcwfilter_module);    
        //lcwfilter过滤模块只处理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);
    }
    /******************************************************
    函数名:ngx_int_t ngx_http_lcwfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
    参数:
    功能:HTTP包体处理方法
    *******************************************************/
    static ngx_int_t ngx_http_lcwfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        ngx_http_lcwfilter_ctx_t   *ctx;//上下文结构
        ctx = ngx_http_get_module_ctx(r, ngx_http_lcwfilter_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_lcwfilter_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);
    }
    /******************************************************
    函数名:ngx_http_lcwfilter_create_conf(ngx_conf_t *cf)
    参数:
    功能:用于创建数据结构用于存储配置项的结构体
    *******************************************************/
    static void* ngx_http_lcwfilter_create_conf(ngx_conf_t *cf)
    {
        ngx_http_lcwfilter_conf_t  *mycf;
        //创建存储配置项的结构体
        mycf = (ngx_http_lcwfilter_conf_t  *)ngx_pcalloc(cf->pool, sizeof(ngx_http_lcwfilter_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;
    }
    /******************************************************
    函数名:ngx_http_lcwfilter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
    参数:
    功能:用于合并同名的配置项
    *******************************************************/
    static char *ngx_http_lcwfilter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
    {
        ngx_http_lcwfilter_conf_t *prev = (ngx_http_lcwfilter_conf_t *)parent;
        ngx_http_lcwfilter_conf_t *conf = (ngx_http_lcwfilter_conf_t *)child;
        //合并ngx_flat_t类型的配置项enable
        ngx_conf_merge_value(conf->enable, prev->enable, 0);
        return NGX_CONF_OK;
    }

    主要的配置块如下:

     location / {
            root /;
            lcw_add_prefix on;
            }
    

    然后在根目录创建文件lcw.txt,或者是其他我可以,我只在里面写了lcw三个字符
    运行结果:
    这里写图片描述

  • 相关阅读:
    指针、字符串、数组操作
    字符串转换为数字(str2int)
    新的,开始。
    Hello, World.
    Go语言趣学指南lesson1
    hdoj2058
    poj2378
    hdoj1233
    poj2398
    hdoj1392
  • 原文地址:https://www.cnblogs.com/sigma0-/p/12630502.html
Copyright © 2011-2022 走看看