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

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

做好准备工作之后,函数开始检查请求是否带有content_length头,如果没有该头或者客户端发送了一个值为0的content_length头,表明没有请求体,这时直接调用回调函数并返回NGX_OK即可。当然如果client_body_in_file_only指令被设置为on,且content_length为0时,该函数在调用回调函数之前,会创建一个空的临时文件。

进入到函数下半部分,表明客户端请求确实表明了要发送请求体,该函数会先检查是否在读取请求头时预读了请求体,这里的检查是通过判断保存请求头的缓存(r->header_in)中是否还有未处理的数据。如果有预读数据,则分配一个ngx_buf_t结构,并将r->header_in中的预读数据保存在其中,并且如果r->header_in中还有剩余空间,并且能够容下剩余未读取的请求体,这些空间将被继续使用,而不用分配新的缓存,当然甚至如果请求体已经被整个预读了,则不需要继续处理了,此时调用回调函数后返回。

如果没有预读数据或者预读不完整,该函数会分配一块新的内存(除非r->header_in还有足够的剩余空间),另外如果request_body_in_single_buf指令被设置为no,则预读的数据会被拷贝进新开辟的内存块中,真正读取请求体的操作是在ngx_http_do_read_client_request_body()函数,该函数循环的读取请求体并保存在缓存中,如果缓存被写满了,其中的数据会被清空并写回到临时文件中。当然这里有可能不能一次将数据读到,该函数会挂载读事件并设置读事件handler为ngx_http_read_client_request_body_handler,另外nginx核心对两次请求体的读事件之间也做了超时设置,client_body_timeout指令可以设置这个超时时间,默认为60s,如果下次读事件超时了,nginx会返回408给客户端。

最终读完请求体后,ngx_http_do_read_client_request_body()会根据配置,将请求体调整到预期的位置(内存或者文件),所有情况下请求体都可以从r->request_body的bufs链表得到,该链表最多可能有2个节点,每个节点为一个buffer,但是这个buffer的内容可能是保存在内存中,也可能是保存在磁盘文件中。另外$request_body变量只在当请求体已经被读取并且是全部保存在内存中,才能取得相应的数据。

2,丢弃请求体

一个模块想要主动的丢弃客户端发过的请求体,可以调用nginx核心提供的ngx_http_discard_request_body()接口,主动丢弃的原因可能有很多种,如模块的业务逻辑压根不需要请求体 ,客户端发送了过大的请求体,另外为了兼容http1.1协议的pipeline请求,模块有义务主动丢弃不需要的请求体。总之为了保持良好的客户端兼容性,nginx必须主动丢弃无用的请求体。下面开始分析ngx_http_discard_request_body()函数:

ngx_int_t 
ngx_http_discard_request_body(ngx_http_request_t *r) 
{ 
 ssize_t size; 
 ngx_event_t *rev; 
 
 if (r != r->main || r->discard_body) { 
 return NGX_OK; 
 } 
 
 if (ngx_http_test_expect(r) != NGX_OK) { 
 return NGX_HTTP_INTERNAL_SERVER_ERROR; 
 } 
 
 rev = r->connection->read; 
 
 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); 
 
 if (rev->timer_set) { 
 ngx_del_timer(rev); 
 } 
 
 if (r->headers_in.content_length_n <= 0 || r->request_body) { 
 return NGX_OK; 
 } 
 
 size = r->header_in->last - r->header_in->pos; 
 
 if (size) { 
 if (r->headers_in.content_length_n > size) { 
  r->header_in->pos += size; 
  r->headers_in.content_length_n -= size; 
 
 } else { 
  r->header_in->pos += (size_t) r->headers_in.content_length_n; 
  r->headers_in.content_length_n = 0; 
  return NGX_OK; 
 } 
 } 
 
 r->read_event_handler = ngx_http_discarded_request_body_handler; 
 
 if (ngx_handle_read_event(rev, 0) != NGX_OK) { 
 return NGX_HTTP_INTERNAL_SERVER_ERROR; 
 } 
 
 if (ngx_http_read_discarded_request_body(r) == NGX_OK) { 
 r->lingering_close = 0; 
 
 } else { 
 r->count++; 
 r->discard_body = 1; 
 } 
 
 return NGX_OK; 
}