nginx模块开发入门(六)-3.1 Anatomy of a Handler (Non-proxying)

[size=x-large]3. Handlers[/size]

接下来我们把模块的细节放到显微镜下面来看,它们到底怎么运行的。

[size=large]3.1. 剖析Handler(非代理)
Anatomy of a Handler (Non-proxying)[/size]

Handler一般做4件事:获取location配置;生成合适的响应;发送响应头;发送响应体。Handler有一个参数,即请求结构体。请求结构体包含很多关于客户请求的有用信息,比如说请求方法,URI,请求头等等。我们一个个地来看。
[b]
3.1.1. 获取location配置[/b]

这部分很简单。只需要调用 [color=blue]ngx_http_get_module_loc_conf[/color],传入当前请求的结构体和模块定义即可。下面是我的circle gif handler的相关部分:

static ngx_int_t
ngx_http_circle_gif_handler(ngx_http_request_t *r)
{
ngx_http_circle_gif_loc_conf_t *cglcf;
cglcf = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module);
...


现在我们就可以访问之前在合并函数中设置的所有变量了。

[b]3.1.2. 生成响应[/b]
ngx_http_circle_gif_handler(ngx_http_request_t *r)

先来探讨一下参数[color=blue]ngx_http_request_t[/color]

这才是模块真正干活的地方,很有趣哦。

这里要用到请求结构体,主要是这些结构体成员:
typedef struct {
...
/* the memory pool, used in the ngx_palloc functions */
ngx_pool_t *pool;
ngx_str_t uri;
ngx_str_t args;
ngx_http_headers_in_t headers_in;
ngx_http_headers_out_t headers_out;

...
} ngx_http_request_t;



[color=blue]uri[/color] 是请求的路径, e.g. "/query.cgi".

[color=blue]args[/color] 请求串参数中问号后面的参数 (e.g. "name=john").

[color=blue]headers_in[/color] 包含有很多有用的东西,比如说cookie啊,浏览器信息啊什么的,但是许多模块可能用不到这些东东。如果你感兴趣的话,可以参看[url=http://lxr.evanmiller.org/http/source/http/ngx_http_request.h#L158]http/ngx_http_request.h [/url]。

对于生成输出,这些信息应该是够了。完整的[color=blue]ngx_http_request_t[/color]结构体定义在[url=http://lxr.evanmiller.org/http/source/http/ngx_http_request.h#L316]http/ngx_http_request.h[/url]。

[b]3.1.3. 发送响应头[/b]

响应头存放在结构体[color=blue]headers_out[/color]中,它的引用存放在请求结构体中。 Handler设置相应的响应头的值,然后调用[color=blue]ngx_http_send_header(r)[/color]。[color=blue]headers_out[/color]中比较有用的是:

typedef stuct {
...
ngx_uint_t status;
size_t content_type_len;
ngx_str_t content_type;
ngx_table_elt_t *content_encoding;
off_t content_length_n;
time_t date_time;
time_t last_modified_time;
..
} ngx_http_headers_out_t;


(剩下的可以在 [url=http://lxr.evanmiller.org/http/source/http/ngx_http_request.h#L220]http/ngx_http_request.h[/url]找到。)

举例来说,如果一个模块要设置Content-Type 为 "image/gif", Content-Length 为 100, 并返回 HTTP 200 OK 的响应, 代码应当是这样的:

r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = 100;
r->headers_out.content_type.len = sizeof("image/gif") - 1;
r->headers_out.content_type.data = (u_char *) "image/gif";
ngx_http_send_header(r);

上面的HTTP headers设定方式针对大多数参数都是有效的。但一些头部(headers)的变量设定要比上面的例子要麻烦;比如,[color=blue]content_encoding[/color] 它还含有类型[color=blue](ngx_table_elt_t*)[/color], 所以必须先为此分配空间。可以用一个叫做[color=blue]ngx_list_push[/color]的函数来做,它传入一个[color=blue]ngx_list_t[/color](与数组类似),返回一个list中的新成员(类型是[color=blue]ngx_table_elt_t[/color])。下面的代码设置了Content-Encoding为"deflate"并发送了响应头:

r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.content_encoding == NULL) {
return NGX_ERROR;
}
r->headers_out.content_encoding->hash = 1;
r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;
r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";
r->headers_out.content_encoding->value.len = sizeof("deflate") - 1;
r->headers_out.content_encoding->value.data = (u_char *) "deflate";
ngx_http_send_header(r);

当头部有多个值时,这个机制常常被用到。它(理论上讲)使得过滤模块添加、删除某个值而保留其他值的时候更加容易,在操纵字符串的时候,不需要把字符串重新排序。

[b]3.1.4. 发送响应体(Sending the body)[/b]

现在模块已经生成了一个响应,并存放在了内存中。接下来它需要将这个响应分配给一个特定的缓冲区,然后把这个缓冲区加入到链表,然后调用链表中“发送响应体(send body)”的函数。

链表在这里起什么作用呢?Nginx 中,handler模块(其实filter模块也是)生成响应到buffer中是同时完成的;链表中的每个元素都有指向下一个元素的指针,如果是NULL 则说明链表到头了。简单起见,我们假设只有一个buffer。

首先,模块需要先声明buffer和链表:

ngx_buf_t *b;
ngx_chain_t out;


接着,需要给buffer分配空间,并将我们的响应数据指向它:

b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"Failed to allocate response buffer.");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

b->pos = some_bytes; /* first position in memory of the data */
b->last = some_bytes + some_bytes_length; /* last position */

b->memory = 1; /* content is in read-only memory */
/* (i.e., filters should copy it rather than rewrite in place) */

b->last_buf = 1; /* there will be no more buffers in the request */

现在就可以把数据挂在链表上了:

out.buf = b;
out.next = NULL;

最后,我们发送这个响应体,返回值是链表在一次调用后的状态:

return ngx_http_output_filter(r, &out);

Buffer链是Nginx IO模型中的关键部分,你得比较熟悉它的工作方式。
[quote]
问: 为什么buffer还需要有个`last_buf`变量啊,我们不是可以通过判断next是否是NULL来知道哪个是链表的最末端了吗?

答: 链表可能是不完整的,比如说,当有多个buffer的时候,并不是所有的buffer都属于当前的请求和响应。所以有些buffer可能是buffer链表的表尾,但是不是请求的结束。这给我们引入了接下来的内容……
[/quote]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值