处理模块一般做四样东西:获得位置配置结构体,产生合适的回复,发送HTTP头部和发送HTTP主体。它只有一个变量--请求结构体。这个结构体有很多关于客户端请求的有用信息,比如请求方法(re quest method),URI和请求头部。我们会一步一步分析整个过程。 3.1.1.获得位置配置结构体 这部分很简单。所有你需要做的是根据当前的请求结构体和模块定义,调用 ngx_http_get_module_loc_conf,获得当前的配置结构体。这是我写的 circle gif hanlder函数的相关部分。
static ngx_int_t 现在我们就能访问在合并函数中初始化的各个变量。 3.1.2.产生回复 这部分很有趣,也是模块真正起作用的地方。 请求结构体在这里也很有用,特别是这些成员变量:
typedef struct {
... uri是请求的路径,比如:"/query.cgi"。 args是在问号之后请求的参数(比如 "name=john")。 headers_in有很多有用的东西,如cookie和浏览器信息。如果你感兴趣,请看这里http/ngx_http_request.h 。 这里应该有足够的信息来产生有用的输出。ngx_http_request_t结构体的完整定义在http/ngx_http_request.h 。 3.1.3.发送 HTTP头部 回复头部存在于被称为 headers_out的结构体中。它包含在请求结构体中。这个处理函数生成头部变量,然后调用ngx_http_send_header(r)函数,下面列出些有用的部分:
typedef stuct { (另外一些你可以在这里找到: http/ngx_http_request.h .) 举个例子,如果一个模块把 Co ntent-Type需要设定为 “image/gif”,Content-Length为100,然后返回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); 上面的设定方式针对大多数参数都是有效的。但一些头部的变量设定要比上面的例子要麻烦;比如,content_encoding含有类型(ngx_table_elt_t*),这时模块必须为它分配内存。可以用一个叫ngx_list_push的函数来做。它需要传入一个ngx_list_t变量(与数组类似),然后返回一个list中的新成员(类型是ngx_table_elt_t)。下面的代码把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); 当头部有多个值的时候,这个机制会经常用到;它(理论上讲)使得过滤模块添加、删除某个值而保留其他值的时候更加容易,在操纵字符串的时候,不需要把字符串重新排序。 3.1.4.发送 HTTP主体 现在模块已经产生了一个回复,把它放到内存中。需要为回复分配一块特别的 buffer,并把这个buffer连接到一个链表,然后调用“s end body”函数发送。 这些链表有什么用?在Nginx中,处理模块和过滤模块在处理完成后产生的回复都包含在缓冲中,每次产生一个buffer;每个链表成员保存指向下一个成员的指针,如果是最后的buffer,就置为 NULL。这里我们简单地假定只有一个 buffer成员。 首先,模块声明一块buffer和一条链表: ngx_buf_t *b; ngx_chain_t out; 第二步是分配缓冲,然后指向我们的回复数据: 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 */ 现在模块buffer添加到了链表上: out.buf = b; out.next = NULL; 最后,我们把主体发送出去,返回值是 output_filter函数对整个链表的返回状态。 return ngx_http_output_filter(r, &out); 缓冲链表是一个典型的Nginx IO模型,你必须清楚它们是如何工作的。 问题:为什么会有变量last_buf,什么时候我们才能说这条链表结束了,并把 next设为 NULL?
回答:链表可能不完全的,比如,有多个buffer的时候,但是不是所有的 buffer都在这个请求或者回复中。所以一些 buffer是链表的结尾,而不是请求的结尾。这意味着模块判断是否是请求的结尾,并设置相应的值。 |