3.6.3 获取HTTP头部
在ngx_http_request_t*r中就可以取到请求中的HTTP头部,比如使用下面的成员:
struct ngx_http_request_s{
……
ngx_buf_t
*header_in;
ngx_http_headers_in_t
headers_in;
……
};
其中,header_in指向Nginx收到的未经解析的HTTP头部,这里暂不关注它(在第11章中可以看到,header_in就是接收HTTP头部的缓冲区)。ngx_http_headers_in_t类型的headers_in则存储已经解析过的HTTP头部。下面介绍ngx_http_headers_in_t结构体中的成员。
typedef struct{
/所有解析过的HTTP头部都在headers链表中,可以使用3.2.3节中介绍的遍历链表的方法来获取所有的HTTP头部。注意,这里headers链表的每一个元素都是3.2.4节介绍过的ngx_table_elt_t成员/
ngx_list_t
headers;
/以下每个ngx_table_elt_t成员都是RFC1616规范中定义的HTTP头部,它们实际都指向headers链表中的相应成员。注意,当它们为NULL空指针时,表示没有解析到相应的HTTP头部/
ngx_table_elt_t
*host;
ngx_table_elt_t
*connection;
ngx_table_elt_t
*if_modified_since;
ngx_table_elt_t
*if_unmodified_since;
ngx_table_elt_t
*user_agent;
ngx_table_elt_t
*referer;
ngx_table_elt_t
*content_length;
ngx_table_elt_t
*content_type;
ngx_table_elt_t
*range;
ngx_table_elt_t
*if_range;
ngx_table_elt_t
*transfer_encoding;
ngx_table_elt_t
*expect;
if(NGX_HTTP_GZIP)
ngx_table_elt_t
*accept_encoding;
ngx_table_elt_t
*via;
endif
ngx_table_elt_t
*authorization;
ngx_table_elt_t
*keep_alive;
if(NGX_HTTP_PROXY||NGX_HTTP_REALIP||NGX_HTTP_GEO)
ngx_table_elt_t
*x_forwarded_for;
endif
if(NGX_HTTP_REALIP)
ngx_table_elt_t
*x_real_ip;
endif
if(NGX_HTTP_HEADERS)
ngx_table_elt_t
*accept;
ngx_table_elt_t
*accept_language;
endif
if(NGX_HTTP_DAV)
ngx_table_elt_t
*depth;
ngx_table_elt_t
*destination;
ngx_table_elt_t
*overwrite;
ngx_table_elt_t
*date;
endif
/user和passwd是只有ngx_http_auth_basic_module才会用到的成员,这里可以忽略/
ngx_str_t
user;
ngx_str_t
passwd;
/cookies是以ngx_array_t数组存储的,本章先不介绍这个数据结构,感兴趣的话可以直接跳到7.3节了解ngx_array_t的相关用法/
ngx_array_t
cookies;
//server名称
ngx_str_t
server;
//根据ngx_table_elt_t*content_length计算出的HTTP包体大小
off_t
content_length_n;
time_t
keep_alive_n;
/HTTP连接类型,它的取值范围是0、NGX_http_CONNECTION_CLOSE或者NGX_HTTP_CONNECTION_KEEP_ALIVE/
unsigned
connection_type:2;
/以下7个标志位是HTTP框架根据浏览器传来的“useragent”头部,它们可用来判断浏览器的类型,值为1时表示是相应的浏览器发来的请求,值为0时则相反/
unsigned
msie:1;
unsigned
msie6:1;
unsigned
opera:1;
unsigned
gecko:1;
unsigned
chrome:1;
unsigned
safari:1;
unsigned
konqueror:1;
}ngx_http_headers_in_t;
获取HTTP头部时,直接使用r->headers_in的相应成员就可以了。这里举例说明一下如何通过遍历headers链表获取非RFC2616标准的HTTP头部,读者可以先回顾一下ngx_list_t链表和ngx_table_elt_t结构体的用法。前面3.2.3节中已经介绍过,headers是一个ngx_list_t链表,它存储着解析过的所有HTTP头部,链表中的元素都是ngx_table_elt_t类型。下面尝试在一个用户请求中找到"Rpc-Description"头部,首先判断其值是否为"uploadFile",再决定后续的服务器行为,代码如下。
ngx_list_part_t*part=&r->headers_in.headers.part;
ngx_table_elt_t*header=part->elts;
//开始遍历链表
for(i=0;/void/;i++){
//判断是否到达链表中当前数组的结尾处
if(i>=part->nelts){
//是否还有下一个链表数组元素
if(part->next==NULL){
break;
}
/part设置为next来访问下一个链表数组;header也指向下一个链表数组的首地址;i设置为0时,表示从头开始遍历新的链表数组/
part=part->next;
header=part->elts;
i=0;
}
//hash为0时表示不是合法的头部
if(header[i].hash==0){
continue;
}
/判断当前的头部是否是“Rpc-Description”。如果想要忽略大小写,则应该先用header[i].lowcase_key代替header[i].key.data,然后比较字符串/
if(0==ngx_strncasecmp(header[i].key.data,
(u_char*)"Rpc-Description",
header[i].key.len))
{
//判断这个HTTP头部的值是否是“uploadFile”
if(0==ngx_strncmp(header[i].value.data,
"uploadFile",
header[i].value.len))
{
//找到了正确的头部,继续向下执行
}
}
}
对于常见的HTTP头部,直接获取r->headers_in中已经由HTTP框架解析过的成员即可,而对于不常见的HTTP头部,需要遍历r->headers_in.headers链表才能获得。