设为首页 收藏本站
查看: 572|回复: 0

[经验分享] nginx模块开发入门(九) -4 Filters

[复制链接]

尚未签到

发表于 2016-12-27 06:07:27 | 显示全部楼层 |阅读模式
4. Filters
    注:本节事例http/modules/ngx_http_chunked_filter_module.c
    Filter操作handler生成的响应。头部filter操作HTTP头,body filter操作响应的内容。
4.1. 剖析Header Filter
Anatomy of a Header Filter


    Header Filter由三个步骤组成:
   1. 决定何时操作响应
   2. 操作响应
   3. 调用下一个filter
    举个例子,比如有一个简化版本的"not modified" header filter:如果客户请求头中的If- Modified-Since和响应头中的Last-Modified相符,它把响应状态设置成304。注意这个头部filter只读入一个参数:ngx_http_request_t结构体,而我们可以通过它操作到客户请求header和一会将被发送的响应response header。

static
ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
time_t  if_modified_since;
if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
r->headers_in.if_modified_since->value.len);
/* step 1: decide whether to operate */
if (if_modified_since != NGX_ERROR &&
if_modified_since == r->headers_out.last_modified_time) {
/* step 2: operate on the header */
r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
r->headers_out.content_type.len = 0;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
}
/* step 3: call the next filter */
return ngx_http_next_header_filter(r);
}

结构headers_out和我们在hander那一节中看到的是一样的(参考http/ngx_http_request.h),也可以随意处置。
4.2. 剖析Body Filter
Anatomy of a Body Filter
    因为body filter一次只能操作一个buffer chain(链表),这使得编写body filter需要一定的技巧。模块需要知道什么时候可以覆盖输入buffer,用新申请的buffer替换已有的,或者在现有的某个buffer前或后插入一个新buffer。有时候模块会收到许多buffer使得它不得不操作一个不完整的链表,这使得事情变得更加复杂了。而更加不幸的是,Nginx没有为我们提供上层的API来操作buffer链表,所以body filter是比较难懂(当然也比较难写)。但是,有些操作你还是可以看出来的。

    一个body filter原型大概是这个样子(例子代码从Nginx源代码的“chunked” filter中取得):

static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in);


    第一个参数是我们的老朋友"请求结构体"(ngx_http_request_t) ,第二个参数则是指向当前部分链表(chain)表头的指针(可能包含0,1,或更多的buffer)。
    再来举个例子好了。假设我们想要做的是在每个请求之后插入文本"<l!-- Served by Nginx -->"。首先,我们需要判断给我们的buffer链表中是否已经包含响应的最终buffer。就像之前我说的,这里没有简便好用的API,所以我们只能自己来写个循环:

ngx_chain_t *chain_link;
int chain_contains_last_buffer = 0;
chain_link = in;
for ( ; ; ) {
if (chain_link->buf->last_buf)
chain_contains_last_buffer = 1;
if (chain_link->next == NULL)
break;
chain_link = chain_link->next;
}


     如果我们没有最后的缓冲区,就返回:

if (!chain_contains_last_buffer)
return ngx_http_next_body_filter(r, in);


     很好,现在最后一个缓冲区已经存在链表中了。接下来我们分配一个新缓冲区:

ngx_buf_t    *b;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}


     把数据放进去:

b->pos = (u_char *) "<!-- Served by Nginx -->";
b->last = b->pos + sizeof("<!-- Served by Nginx -->") - 1;


     把这个缓冲区挂在新的链表上:

ngx_chain_t   *added_link;
added_link = ngx_alloc_chain_link(r->pool);
if (added_link == NULL)
return NGX_ERROR;
added_link->buf = b;
added_link->next = NULL;


     最后,把这个新链表挂在先前链表的末尾:

chain_link->next = added_link;


     并根据变化重置变量"last_buf"的值:

chain_link->buf->last_buf = 0;
added_link->buf->last_buf = 1;


     再将修改过的链表传递给下一个输出过滤函数:

return ngx_http_next_body_filter(r, in);


     现有的函数做了比我们更多的工作,比如mod_perl($response->body =~ s/$/<!-- Served by mod_perl -->/),但是缓冲区链确实是一个强大的构想,它可以让程序员渐进地处理数据,这使得客户端可以尽可能早地得到响应。但是依我来看,缓冲区链表实在需要一个更为干净的接口,这样程序员也可以避免操作不一致状态的链表。但是目前为止,所有的操作风险都得自己控制。
4.3. Filter的装载
Filter Installation
      Filter在回调函数post-configuration中被装载。header filter和body filter都是在这里被装载的。
      我们以chunked filter模块为例来具体看看:

static ngx_http_module_t  ngx_http_chunked_filter_module_ctx = {
NULL,                                  /* preconfiguration */
ngx_http_chunked_filter_init,          /* postconfiguration */
...
};


      ngx_http_chunked_filter_init中的具体实现如下:

static ngx_int_t
ngx_http_chunked_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_chunked_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_chunked_body_filter;
return NGX_OK;
}

    发生了什么呢?好吧,如果你还记得,过滤模块组成了一条”接力链表“(CHAIN OF RESPONSIBILITY)。当handler生成一个响应后,调用2个函数:ngx_http_output_filter它调用全局函数ngx_http_top_body_filter
以及ngx_http_send_header 它调用全局函数ngx_top_header_filter
    ngx_http_top_body_filterngx_http_top_header_filter是body和header各自的头部filter链的”链表头“。链表上的每一个”连接“都保存着链表中下一个连接的函数引用(分别是 ngx_http_next_body_filter ngx_http_next_header_filter)。当一个filter完成工作之后,它只需要调用下一个filter,直到一个特殊的被定义成”write“的filter被调用,这个”write“filter的作用是包装最终的HTTP响应。你在这个filter_init函数中看到的就是,模块把自己添加到filter链表中;它先把旧的”头部“filter当做是自己的”下一个“,然后再声明”它自己“是”头部“filter。(因此,最后一个被添加的filter会第一个被执行。)
引用
边注: 这到底是怎么工作的?
     每个filter要么返回一个错误码,要么用下面的作为返回语句
return ngx_http_next_body_filter();

     因此,如果filter顺利链执行到了链尾(那个特别定义的的”write“filter),将返回一个"OK"响应,但如果执行过程中遇到了错误,链将被砍断,同时Nginx将给出一个错误的信息。这是一个单向的,错误快速返回的,只使用函数引用实现的链表。帅啊!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-319769-1-1.html 上篇帖子: nginx 请求server与location配置定位 下篇帖子: nginx 中文技术指南 V3.0 附pdf下载
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表