Nginx过滤器模块实现
1 Nginx模块添加过程
nginx模块一般分为3类:一类是handler模块,就是当接受到接收到http请求后,nginx直接处理,并且返回给客户端;一类是filter模块是当我们接受到http请求后可以在服务器返回之前对http服务器返回的内容进行修改;第三类是upstream模块,主要是接受到http请求后,在转发给服务器前对http请求进行修改。
1 配置文件编写
模块编写前需要先进行配置文件编写一般放在模块的根目录下:包含三部分信息,一个是模块的名称这里名称需要与代码中的定义的模块名称ngx_module_t一致;第二部分是指定模块的类型和名称,这里定义的是一个filter模块;最后是指定模块源文件路径。
ngx_addon_name=ngx_http_prefix_filter_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_prefix_filter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_prefix_filter_module.c"
编译过程,需要对nginx源码进行编译不清楚可以参考nginx安装与配置;这里需要增加新增模块的路径一般用绝对路径
--add-module=PATH
2 实现过程
实现过程分为两个部分一是进行配置的解析,二是实现业务逻辑。
2.1 配置解析过程
第一步是实现模块的定义并且设置模块的类型,http模块的上下文和模块的命令解析相关。
ngx_module_t ngx_http_prefix_filter_module = {NGX_MODULE_V1,&ngx_http_prefix_filter_module_ctx,//http模块定义ngx_http_prefix_filter_commands,//filter模块定义NGX_HTTP_MODULE,//模块类型HTTPNULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING
};
第二部分是http上下文回调函数设置,主要是为后续数据处理做准备,这里需要根据你配置项需要作用的范围来选择不同的创建函数和初始化函数,包括三种范围一种是根配置,第二种是server范围内;第三种作用域最小只作用在对应的location上。
typedef struct {ngx_int_t (*preconfiguration)(ngx_conf_t *cf);//配置解析前处理函数ngx_int_t (*postconfiguration)(ngx_conf_t *cf);//配置解析后处理函数void *(*create_main_conf)(ngx_conf_t *cf);//主配置创建函数char *(*init_main_conf)(ngx_conf_t *cf, void *conf);//主配置初始化函数void *(*create_srv_conf)(ngx_conf_t *cf);//server配置创建函数char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);//主配置初始化函数void *(*create_loc_conf)(ngx_conf_t *cf);//location配置创建函数char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);//location配置初始化函数
} ngx_http_module_t;
static ngx_http_module_t ngx_http_prefix_filter_module_ctx = {NULL,ngx_http_prefix_filter_init,NULL,NULL,NULL,NULL,ngx_http_prefix_filter_create_conf,ngx_http_prefix_filter_merge_conf
};
第三部分是初始化配置项的解析部分,这部分包括配置名称;配置项作用域和配置项的类型;设置槽函数;
struct ngx_command_s {ngx_str_t name;ngx_uint_t type;char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);ngx_uint_t conf;ngx_uint_t offset;void *post;
};
static ngx_command_t ngx_http_prefix_filter_commands[] = {{ngx_string("add_prefix"),//名称add_prefixNGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,//作用域location和类型flagngx_conf_set_flag_slot,//选择flag类型的槽函数NGX_HTTP_LOC_CONF_OFFSET,//location偏移量offsetof(ngx_http_prefix_filter_conf_t, enable),//传入结构体和保存变量的成员NULL},ngx_null_command
};
3 nginx过滤模块数据流:
nginx模块调用整体数据流程如下:首先nginx进入模块解析部分,首先是解析模块的定义部分,其中两个比较核心的参数一个是command对象一个是ctx上下文;这里分为两条线路,一条线路上下文主要是加载http模块,将新的模块插入到nginx原filter模块流程中间,同时对配置内容进行初始化,注册http头解析和body解析的回调函数;另一条线路是配置命令解析,主要是设置配置项名称,作用域,槽函数和配置项解析后保存的位置。
首先是配置的结构体空间分配:利用nginx内存池分配空间,保存配置值;并初始化;
static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf) {ngx_http_prefix_filter_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_prefix_filter_conf_t));if (conf == NULL) {return NULL;}conf->enable = NGX_CONF_UNSET;return conf;
}
配置修改的过程,主要是利用新旧配置的更替,当有修改是优先使用修改,否则采用旧值。
static char *ngx_http_prefix_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) {ngx_http_prefix_filter_conf_t *prev = (ngx_http_prefix_filter_conf_t*)parent;ngx_http_prefix_filter_conf_t *conf = (ngx_http_prefix_filter_conf_t*)child;ngx_conf_merge_value(conf->enable, prev->enable, 0);return NGX_CONF_OK;
}
将模块加入httpfilter列表:这里添加的位置是固定的,当模块插入后固定为加入到http_top_header_filter的前面。
static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf) {ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter = ngx_http_prefix_filter_header_filter;//头处理函数ngx_http_next_body_filter = ngx_http_top_body_filter;ngx_http_top_body_filter = ngx_http_prefix_filter_body_filter;//body处理函数return NGX_OK;
}
头部过滤函数:主要是修改头部内容的长度,方便后面可以新增或者修改内容。
static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r) {ngx_http_prefix_filter_ctx_t *ctx;ngx_http_prefix_filter_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_prefix_filter_module);if (ctx) {//交给后面的模块处理return ngx_http_next_header_filter(r);}conf = ngx_http_get_module_loc_conf(r, ngx_http_prefix_filter_module);if (conf == NULL) {return ngx_http_next_header_filter(r);}if (conf->enable == 0) {return ngx_http_next_header_filter(r);}//创建标志,用于防止多次进行处理ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_prefix_filter_ctx_t));if (ctx == NULL) {return NGX_ERROR;}ctx->add_prefix = 0;//将标志加入到上下文ngx_http_set_ctx(r, ctx, ngx_http_prefix_filter_module);if (r->headers_out.content_type.len >= sizeof("text/html") - 1&& ngx_strncasecmp(r->headers_out.content_type.data, (u_char*)"text/html", sizeof("text/html")-1) == 0) {//这里将http头修改,成新的body的长度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);
}
body内容处理:内容处理主要是修改内容。这里内容处理修改采用
static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {//利用标志位保证头修改正确后,才能修改内容。ngx_http_prefix_filter_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);if (ctx == NULL || ctx->add_prefix != 1) {return ngx_http_next_body_filter(r, in);}ctx->add_prefix = 2;//内容修改采用的是ngx_buf_t进行纯粹,获取到块内存后,将数据加入start,同时设置数据的结束位置。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节点将数据填充进入到链头部节点ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);cl->buf = b;cl->next = in;//最后将body交由下一个模块处理return ngx_http_next_body_filter(r, cl);
}
标签:
相关文章
-
无相关信息