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链表才能获得。