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模块开始把上游的包体(如果有的话)直接转发到下游客户端。