nginx过滤器模块
来源:互联网 发布:苹果mac怎么关闭程序 编辑:程序博客网 时间:2024/06/02 06:40
nginx服务器给客户端发送响应时,包括http响应头部、http响应包体内容。可以调用http框架提供的两个函数ngx_http_send_header,ngx_http_output_filter,分别用于给客户端发送http响应头部、http响应包体。这两个函数会调用各个过滤器模块,对将要发送给客户端的响应头部、响应包体进行过滤处理。例如: 是否需要添加http响应头部字段、是否需要断点续传、是否只发送一个区间块的数据、响应包体是否需要压缩等。
一、过滤器模块链表创建
nginx服务器有一个全局的变量ngx_http_top_header_filter,这是一个函数指针变量。 也是http响应头部过滤链表的头部。对于每一个过滤模块,都会有一个静态的变量ngx_http_next_header_filter,同样这也是一个函数指针变量,指向过滤链表的下一个节点。例如:在由6个过滤器模块组成的http响应头部过滤链表内存布局如下:
从这张图可以看出,链表采用的是头插法。也就是说第一个过滤器模块变成了链表的尾节点,而最后一个过滤器模块变成了链表的首节点。同样的http响应包体组成的过滤链表内存布局和上图是类似的,都采用的是头插法。只不过http响应过滤包体链表头指针为一个全局的函数指针ngx_http_top_body_filter,每一个http过滤模块的静态变量ngx_http_next_body_filter则组成响应包体链表的next指针。
下面从代码角度分析下这个过滤链表是如何创建的。以http响应头部过滤链表的创建为例。
ngx_http_header_filter_module是第一个http响应头部过滤模块,在这个模块的ngx_http_header_filter_init函数中,将ngx_http_top_header_filter函数指针指向ngx_http_header_filter, 此时这个为链表的第一个节点。
static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf){ ngx_http_top_header_filter = ngx_http_header_filter; return NGX_OK;}ngx_http_chunked_filter_module是第二个http响应头部过滤模块,在这个模块的ngx_http_chunked_filter_init函数中将ngx_http_next_header_filter指向ngx_http_top_header_filter,也就是第一个http响应过滤头部模块,而ngx_http_top_header_filter指向本模块的回调ngx_http_chunked_header_filte,使用头部插入法构成一个链表。
static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf){ ngx_http_next_header_filter = ngx_http_top_header_filter; //next指针指向之前的模块 ngx_http_top_header_filter = ngx_http_chunked_header_filter;//top指针指向当前这个模块的回调 return NGX_OK;}其它过滤器模块插入到链表的过程是类似的,这里就不再对每一个过滤模块如何插入到链表进行详细分析了, 没有这个必要。
二、常见http过滤模块的功能
过滤模块是比较简单的模块,对于每一个过滤模块,代码量也不过几百行而已。仔细分析很容易看懂,这里就不对这些过滤模块进行详细分析了。但需要总结下几个常见过滤模块是做什么的。
三、发送响应头部
ngx_http_send_header函数用于发送http响应头部,这个是http框架提供的接口,可以被http模块调用。看下这个函数的实现。
ngx_int_t ngx_http_send_header(ngx_http_request_t *r){ if (r->err_status) { r->headers_out.status = r->err_status; r->headers_out.status_line.len = 0; } return ngx_http_top_header_filter(r);}这个函数会从http响应头部过滤器链表头部开始遍历,使用各个过滤器模块对响应头部进行处理,最终把处理后的响应头部发送给客户端。而最后一个http响应头部过滤器链表节点的回调为:ngx_http_header_filter,看下这个函数的实现。
//将http响应头部的各个头部信息拼装到buf缓冲区中,然后将这个缓冲区的数据发送给客户端static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r){/*********以下是为了统计响应头部的总大小 **********//*********目的是为了开辟一个应答包头大小的缓冲区 **********/ len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1 + sizeof(CRLF) - 1;//统计状态码字符串的长度//status_line存放的是状态码的内容,例如:"302 Moved Temporarily" if (r->headers_out.status_line.len){ len += r->headers_out.status_line.len; status_line = &r->headers_out.status_line; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);//服务器的版本信息长度 if (r->headers_out.server == NULL) { len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1: sizeof(ngx_http_server_string) - 1; }//开辟一个buf结构,buf大小为应答包头的大小 b = ngx_create_temp_buf(r->pool, len); if (b == NULL) { return NGX_ERROR; }/********以下操作将http应答包头的各个字段拷贝到开辟的缓冲区***********///拷贝HTTP1.1 200 OK状态行 /* "HTTP/1.x " */ b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); if (status_line) { b->last = ngx_copy(b->last, status_line->data, status_line->len); } *b->last++ = CR; *b->last++ = LF;//拷贝应答包体长度字段 if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF,r->headers_out.content_length_n); }//构造发送缓冲区 out.buf = b; out.next = NULL;//发送http响应头部信息给客户端 return ngx_http_write_filter(r, &out);}四、发送响应包体
ngx_http_output_filter函数用于向客户端发送http响应包体数据。这个是http框架提供的接口,可以被http模块调用。看下这个函数的实现:
ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in){ ngx_int_t rc; ngx_connection_t *c;//发送响应包体 rc = ngx_http_top_body_filter(r, in); return rc;}这个函数会从http响应包体过滤器链表头部开始遍历,使用各个过滤器模块对响应包体进行处理,最终把处理后的响应包体发送给客户端。而最后一个http响应包体过滤器链表节点的回调为:ngx_http_write_filter,看下这个函数的实现。
ngx_http_write_filter这个函数会将响应包体发送给客户端,如果一次没法发送完成,则会保存未发送完成的数据到链表中,同时删除已经发送完成的链表数据。
ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in){//计算out缓冲区占用字数数 for (cl = r->out; cl; cl = cl->next) { ll = &cl->next; size += ngx_buf_size(cl->buf); } /* add the new chain to the existent one *///将本次待发送的缓冲区加到out尾部,并计算出out总长度 for (ln = in; ln; ln = ln->next) { cl = ngx_alloc_chain_link(r->pool); cl->buf = ln->buf; *ll = cl; ll = &cl->next;//计算这个链表节点缓冲区数据大小 size += ngx_buf_size(cl->buf); } *ll = NULL;//待发送的响应包体没有达到阈值,则暂时不发送 if (!last && !flush && in && size < (off_t) clcf->postpone_output) { return NGX_OK; }//需要延迟发送响应,用于限速功能,本次不发送的会,对客户端来讲,自然速度降低了 if (c->write->delayed) { c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; }//判断是否需要限速 if (r->limit_rate) { limit = r->limit_rate * (ngx_time() - r->start_sec + 1) - (c->sent - clcf->limit_rate_after);//limit小于等于0,表示超发的字节数,需要限速 if (limit <= 0) { c->write->delayed = 1;//需要延迟发送响应 ngx_add_timer(c->write, (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1)); c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; }//计算本次最大可以发送多少数据给客户端 if (clcf->sendfile_max_chunk && (off_t) clcf->sendfile_max_chunk < limit) { limit = clcf->sendfile_max_chunk; } }else { limit = clcf->sendfile_max_chunk; }//调用ngx_linux_sendfile_chain发送数据,返回值是未发送完的数据 chain = c->send_chain(c, r->out, limit);//ngx_writev_chain//清空已经发送的缓冲 for (cl = r->out; cl && cl != chain; /* void */){ ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); }//保存未发送完成的数据,待下次事件触发后在发送给客户端 r->out = chain; return NGX_OK;}到此过滤器模块已经分析完成了。过滤器模块还是比较简单的,代码量也不太,读者可以好好分析上图中的这6个过滤器模块的实现方式。下一篇文章将分析nginx是如何接收http请求包体的,如果不需要接收请求包体,那nginx又是如何丢弃http请求包体的。
- nginx过滤器模块
- NGINX模块
- nginx模块
- nginx模块
- Nginx模块-lua-nginx-module
- Nginx基础知识. Nginx模块开发
- Nginx基础. Nginx模块上下文
- nginx+fast-nginx-module模块
- nginx安装etag模块
- Nginx filter 模块解析
- Nginx mp4支持模块
- Nginx模块开发入门
- Nginx模块开发入门
- Nginx主模块
- nginx模块开发
- nginx开启监控模块
- nginx模块开发说明
- nginx安装etag模块
- SPOJ - Linearian Colony【分解为子问题】
- 【NOIP2014八校联考第2场第2试9.28】分组(group)
- UOJ 79 带花树入门
- 使用策略模式进行简单的form验证
- uoj 279: [UTR #2]题目交流通道 动态规划
- nginx过滤器模块
- LCD12864 为什么就是显示不了内容!
- 步步扎进Java-面向对象
- bzoj 2820: YY的GCD 莫比乌斯反演
- 图解Linux命令之--comm命令
- liferay ga6获取当前语言环境
- 使用selenium+phantomjs
- 简书30日排行爬虫代码
- 2017重新起航