nginx
《深入理解nginx》阅读与实践(四):简单的http过滤模块 -凯发ag旗舰厅登录网址下载
一个请求可以被任意个http模块处理;
在普通http模块处理请求完毕并调用ngx_http_send_header()发送http头部或调用ngx_http_output_filter()发送http包体时,才会由这两个方法一次调用所有的http过滤模块来处理这个请求。http过滤模块仅处理服务器发送到客户端的响应,而不处理客户端发往服务器的http请求。
多个过滤模块的顺序的形成以及nginx自带的过滤模块请参考原书。
以向返回给用户的文本格式响应包体前加一段字符串"[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》阅读与实践”系列可能到此为止。
总结
以上是凯发ag旗舰厅登录网址下载为你收集整理的《深入理解nginx》阅读与实践(四):简单的http过滤模块的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得凯发ag旗舰厅登录网址下载网站内容还不错,欢迎将凯发ag旗舰厅登录网址下载推荐给好友。
- 上一篇: spanning-tree potoco
- 下一篇: