详解Nginx的配置函数对于请求体的读取

2019-10-17 20:00:17刘景俊

static ngx_int_t 
ngx_http_proxy_handler(ngx_http_request_t *r) 
{ 
 ... 
 rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); 
 
 
 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { 
 return rc; 
 } 
 
 return NGX_DONE; 
}

上面的代码是在porxy模块的content handler,ngx_http_proxy_handler()中调用了ngx_http_read_client_request_body()函数,其中ngx_http_upstream_init()被作为回调函数传入进接口中,另外nginx中模块的content handler调用的上下文如下:

ngx_int_t 
ngx_http_core_content_phase(ngx_http_request_t *r, 
 ngx_http_phase_handler_t *ph) 
{ 
 ... 
 if (r->content_handler) { 
 r->write_event_handler = ngx_http_request_empty_handler; 
 ngx_http_finalize_request(r, r->content_handler(r)); 
 return NGX_OK; 
 } 
 ... 
} 

上面的代码中,content handler调用之后,它的返回值作为参数调用了ngx_http_finalize_request()函数,在请求体没有被接收完全时,ngx_http_read_client_request_body()函数返回值为NGX_AGAIN,此时content handler,比如ngx_http_proxy_handler()会返回NGX_DONE,而NGX_DONE作为参数传给ngx_http_finalize_request()函数会导致主请求的引用计数减1,所以正好抵消了ngx_http_read_client_request_body()函数开头对主请求计数的加1。

接下来回到ngx_http_read_client_request_body()函数,它会检查该请求的请求体是否已经被读取或者被丢弃了,如果是的话,则直接调用回调函数并返回NGX_OK,这里实际上是为子请求检查,子请求是nginx中的一个概念,nginx中可以在当前请求中发起另外一个或多个全新的子请求来访问其他的location,关于子请求的具体介绍会在后面的章节作详细分析,一般而言子请求不需要自己去读取请求体。

函数接着调用ngx_http_test_expect()检查客户端是否发送了Expect: 100-continue头,是的话则给客户端回复"HTTP/1.1 100 Continue",根据http 1.1协议,客户端可以发送一个Expect头来向服务器表明期望发送请求体,服务器如果允许客户端发送请求体,则会回复"HTTP/1.1 100 Continue",客户端收到时,才会开始发送请求体。

接着继续为接收请求体做准备工作,分配一个ngx_http_request_body_t结构,并保存在r->request_body,这个结构用来保存请求体读取过程用到的缓存引用,临时文件引用,剩余请求体大小等信息,它的定义如下。

<span style="font-family:SimSun;font-size:18px;">typedef struct { 
 ngx_temp_file_t   *temp_file; 
 ngx_chain_t   *bufs; 
 ngx_buf_t   *buf; 
 off_t    rest; 
 ngx_chain_t   *to_write; 
 ngx_http_client_body_handler_pt post_handler; 
} ngx_http_request_body_t;</span> 
  temp_file: 指向储存请求体的临时文件的指针; bufs: 指向保存请求体的链表头; buf: 指向当前用于保存请求体的内存缓存; rest: 当前剩余的请求体大小; post_handler:保存传给ngx_http_read_client_request_body()函数的回调函数。