3.6.4 获取HTTP包体

HTTP包体的长度有可能非常大,如果试图一次性调用并读取完所有的包体,那么多半会阻塞Nginx进程。HTTP框架提供了一种方法来异步地接收包体:


ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t*r,ngx_http_client_body_handler_pt post_handler);


ngx_http_read_client_request_body是一个异步方法,调用它只是说明要求Nginx开始接收请求的包体,并不表示是否已经接收完,当接收完所有的包体内容后,post_handler指向的回调方法会被调用。因此,即使在调用了ngx_http_read_client_request_body方法后它已经返回,也无法确定这时是否已经调用过post_handler指向的方法。换句话说,ngx_http_read_client_request_body返回时既有可能已经接收完请求中所有的包体(假如包体的长度很小),也有可能还没开始接收包体。如果ngx_http_read_client_request_body是在ngx_http_mytest_handler处理方法中调用的,那么后者一般要返回NGX_DONE,因为下一步就是将它的返回值作为参数传给ngx_http_finalize_request。NGX_DONE的意义在3.6.1节中已经介绍过,这里不再赘述。

下面看一下包体接收完毕后的回调方法原型ngx_http_client_body_handler_pt是如何定义的:


typedef void(ngx_http_client_body_handler_pt)(ngx_http_request_tr);


其中,有参数ngx_http_request_tr,这个请求的信息都可以从r中获得。这样可以定义一个方法void func(ngx_http_request_tr),在Nginx接收完包体时调用它,另外,后续的流程也都会写在这个方法中,例如:


void ngx_http_mytest_body_handler(ngx_http_request_t*r)

{

……

}


注意 ngx_http_mytest_body_handler的返回类型是void,Nginx不会根据返回值做一些收尾工作,因此,我们在该方法里处理完请求时必须要主动调用ngx_http_finalize_request方法来结束请求。

接收包体时可以这样写:


ngxint_t rc=ngx_http_read_client_request_body(r,ngx_http_mytest_body

handler);

if(rc>=NGX_http_SPECIAL_RESPONSE){

return rc;

}

return NGX_DONE;


Nginx异步接收HTTP请求的包体的内容将在11.8节中详述。

如果不想处理请求中的包体,那么可以调用ngx_http_discard_request_body方法将接收自客户端的HTTP包体丢弃掉。例如:


ngx_int_t rc=ngx_http_discard_request_body(r);

if(rc!=NGX_OK){

return rc;

}


ngx_http_discard_request_body只是丢弃包体,不处理包体不就行了吗?何必还要调用ngx_http_discard_request_body方法呢?其实这一步非常有意义,因为有些客户端可能会一直试图发送包体,而如果HTTP模块不接收发来的TCP流,有可能造成客户端发送超时。

接收完请求的包体后,可以在r->request_body->temp_file->file中获取临时文件(假定将r->request_body_in_file_only标志位设为1,那就一定可以在这个变量获取到包体。更复杂的接收包体的方式本节暂不讨论)。file是一个ngx_file_t类型,在3.8节会详细介绍它的用法。这里,我们可以从r->request_body->temp_file->file.name中获取Nginx接收到的请求包体所在文件的名称(包括路径)。