5.3.4 在process_header方法中解析包头
process_header负责解析上游服务器发来的基于TCP的包头,在本例中,就是解析HTTP响应行和HTTP头部,因此,这里使用mytest_process_status_line方法解析HTTP响应行,使用mytest_upstream_process_header方法解析http响应头部。之所以使用两个方法解析包头,这也是HTTP的复杂性造成的,因为无论是响应行还是响应头部都是不定长的,都需要使用状态机来解析。实际上,这两个方法也是通用的,它们适用于解析所有的HTTP响应包,而且这两个方法的代码与ngx_http_proxy_module模块的实现几乎是完全一致的。
static ngx_int_t
mytest_process_status_line(ngx_http_request_t*r)
{
size_t
len;
ngx_int_t
rc;
ngx_http_upstream_t*u;
//上下文中才会保存多次解析HTTP响应行的状态,下面首先取出请求的上下文
ngx_http_mytest_ctx_t*ctx=ngx_http_get_module_ctx(r,ngx_http_mytest_module);
if(ctx==NULL){
return NGX_ERROR;
}
u=r->upstream;
/HTTP框架提供的ngx_http_parse_status_line方法可以解析HTTP响应行,它的输入就是收到的字符流和上下文中的ngx_http_status_t结构/
rc=ngx_http_parse_status_line(r,&u->buffer,&ctx->status);
//返回NGX_AGAIN时,表示还没有解析出完整的HTTP响应行,需要接收更多的字符流再进行解析*/
if(rc==NGX_AGAIN){
return rc;
}
//返回NGX_ERROR时,表示没有接收到合法的HTTP响应行
if(rc==NGX_ERROR){
ngx_log_error(NGX_LOG_ERR,r->connection->log,0,
"upstream sent no valid HTTP/1.0 header");
r->http_version=NGX_HTTP_VERSION_9;
u->state->status=NGX_HTTP_OK;
return NGX_OK;
}
/以下表示在解析到完整的HTTP响应行时,会做一些简单的赋值操作,将解析出的信息设置到r->upstream->headers_in结构体中。当upstream解析完所有的包头时,会把headers_in中的成员设置到将要向下游发送的r->headers_out结构体中,也就是说,现在用户向headers_in中设置的信息,最终都会发往下游客户端。为什么不直接设置r->headers_out而要多此一举呢?因为upstream希望能够按照ngx_http_upstream_conf_t配置结构体中的hide_headers等成员对发往下游的响应头部做统一处理/
if(u->state){
u->state->status=ctx->status.code;
}
u->headers_in.status_n=ctx->status.code;
len=ctx->status.end-ctx->status.start;
u->headers_in.status_line.len=len;
u->headers_in.status_line.data=ngx_pnalloc(r->pool,len);
if(u->headers_in.status_line.data==NULL){
return NGX_ERROR;
}
ngx_memcpy(u->headers_in.status_line.data,ctx->status.start,len);
/下一步将开始解析HTTP头部。设置process_header回调方法为mytest_upstream_process_header,之后再收到的新字符流将由mytest_upstream_process_header解析/
u->process_header=mytest_upstream_process_header;
/如果本次收到的字符流除了HTTP响应行外,还有多余的字符,那么将由mytest_upstream_process_header方法解析/
return mytest_upstream_process_header(r);
}
mytest_upstream_process_header方法可以解析HTTP响应头部,而这里只是简单地把上游服务器发送的HTTP头部添加到了请求r->upstream->headers_in.headers链表中。如果有需要特殊处理的HTTP头部,那么应该在mytest_upstream_process_header方法中进行。
static ngx_int_t
mytest_upstream_process_header(ngx_http_request_t*r)
{
ngx_int_t
rc;
ngx_table_elt_t
*h;
ngx_http_upstream_header_t
*hh;
ngx_http_upstream_main_conf_t*umcf;
/这里将upstream模块配置项ngx_http_upstream_main_conf_t取出来,目的只有一个,就是对将要转发给下游客户端的HTTP响应头部进行统一处理。该结构体中存储了需要进行统一处理的HTTP头部名称和回调方法/
umcf=ngx_http_get_module_main_conf(r,ngx_http_upstream_module);
//循环地解析所有的HTTP头部
for(;){
/HTTP框架提供了基础性的ngx_http_parse_header_line方法,它用于解析HTTP头部/
rc=ngx_http_parse_header_line(r,&r->upstream->buffer,1);
//返回NGX_OK时,表示解析出一行HTTP头部
if(rc==NGX_OK){
//向headers_in.headers这个ngx_list_t链表中添加HTTP头部
h=ngx_list_push(&r->upstream->headers_in.headers);
if(h==NULL){
return NGX_ERROR;
}
//下面开始构造刚刚添加到headers链表中的HTTP头部
h->hash=r->header_hash;
h->key.len=r->header_name_end-r->header_name_start;
h->value.len=r->header_end-r->header_start;
//必须在内存池中分配存放HTTP头部的内存空间
h->key.data=ngx_pnalloc(r->pool,
h->key.len+1+h->value.len+1+h->key.len);
if(h->key.data==NULL){
return NGX_ERROR;
}
h->value.data=h->key.data+h->key.len+1;
h->lowcase_key=h->key.data+h->key.len+1+h->value.len+1;
ngx_memcpy(h->key.data,r->header_name_start,h->key.len);
h->key.data[h->key.len]='\0';
ngx_memcpy(h->value.data,r->header_start,h->value.len);
h->value.data[h->value.len]='\0';
if(h->key.len==r->lowcase_index){
ngx_memcpy(h->lowcase_key,r->lowcase_header,h->key.len);
}else{
ngx_strlow(h->lowcase_key,h->key.data,h->key.len);
}
//upstream模块会对一些HTTP头部做特殊处理
hh=ngx_hash_find(&umcf->headers_in_hash,h->hash,
h->lowcase_key,h->key.len);
if(hh&&hh->handler(r,h,hh->offset)!=NGX_OK){
return NGX_ERROR;
}
continue;
}
/返回NGX_HTTP_PARSE_HEADER_DONE时,表示响应中所有的HTTP头部都解析完毕,接下来再接收到的都将是HTTP包体/
if(rc==NGX_HTTP_PARSE_HEADER_DONE){
/如果之前解析HTTP头部时没有发现server和date头部,那么下面会根据HTTP协议规范添加这两个头部/
if(r->upstream->headers_in.server==NULL){
h=ngx_list_push(&r->upstream->headers_in.headers);
if(h==NULL){
return NGX_ERROR;
}
h->hash=ngx_hash(ngx_hash(ngx_hash(ngx_hash(
ngx_hash('s','e'),'r'),'v'),'e'),'r');
ngx_str_set(&h->key,"Server");
ngx_str_null(&h->value);
h->lowcase_key=(u_char*)"server";
}
if(r->upstream->headers_in.date==NULL){
h=ngx_list_push(&r->upstream->headers_in.headers);
if(h==NULL){
return NGX_ERROR;
}
h->hash=ngx_hash(ngx_hash(ngx_hash('d','a'),'t'),'e');
ngx_str_set(&h->key,"Date");
ngx_str_null(&h->value);
h->lowcase_key=(u_char*)"date";
}
return NGX_OK;
}
/如果返回NGX_AGAIN,则表示状态机还没有解析到完整的HTTP头部,此时要求upstream模块继续接收新的字符流,然后交由process_header回调方法解析/
if(rc==NGX_AGAIN){
return NGX_AGAIN;
}
//其他返回值都是非法的
ngx_log_error(NGX_LOG_ERR,r->connection->log,0,
"upstream sent invalid header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
}
当mytest_upstream_process_header返回NGX_OK后,upstream模块开始把上游的包体(如果有的话)直接转发到下游客户端。