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

2019-10-17 20:00:17刘景俊
由于函数不长,这里把它完整的列出来了,函数的开始同样先判断了不需要再做处理的情况:子请求不需要处理,已经调用过此函数的也不需要再处理。接着调用ngx_http_test_expect() 处理http1.1 expect的情况,根据http1.1的expect机制,如果客户端发送了expect头,而服务端不希望接收请求体时,必须返回417(Expectation Failed)错误。nginx并没有这样做,它只是简单的让客户端把请求体发送过来,然后丢弃掉。接下来,函数删掉了读事件上的定时器,因为这时本身就不需要请求体,所以也无所谓客户端发送的快还是慢了,当然后面还会将到,当nginx已经处理完该请求但客户端还没有发送完无用的请求体时,nginx会在读事件上再挂上定时器。
函数同样还会检查请求头中的content-length头,客户端如果打算发送请求体,就必须发送content-length头,同时还会查看其他地方是不是已经读取了请求体。如果确实有待处理的请求体,函数接着检查请求头buffer中预读的数据,预读的数据会直接被丢掉,当然如果请求体已经被全部预读,函数就直接返回了。

接下来,如果还有剩余的请求体未处理,该函数调用ngx_handle_read_event()在事件处理机制中挂载好读事件,并把读事件的处理函数设置为ngx_http_discarded_request_body_handler。做好这些准备之后,该函数最后调用ngx_http_read_discarded_request_body()接口读取客户端过来的请求体并丢弃。如果客户端并没有一次将请求体发过来,函数会返回,剩余的数据等到下一次读事件过来时,交给ngx_http_discarded_request_body_handler()来处理,这时,请求的discard_body将被设置为1用来标识这种情况。另外请求的引用数(count)也被加1,这样做的目的是客户端可能在nginx处理完请求之后仍未完整发送待发送的请求体,增加引用是防止nginx核心在处理完请求后直接释放了请求的相关资源。

ngx_http_read_discarded_request_body()函数非常简单,它循环的从链接中读取数据并丢弃,直到读完接收缓冲区的所有数据,如果请求体已经被读完了,该函数会设置读事件的处理函数为ngx_http_block_reading,这个函数仅仅删除水平触发的读事件,防止同一事件不断被触发。
再来看一下读事件的处理函数ngx_http_discarded_request_body_handler,这个函数每次读事件来时会被调用,先看一下它的源码:

void 
ngx_http_discarded_request_body_handler(ngx_http_request_t *r) 
{ 
 ... 
 
 c = r->connection; 
 rev = c->read; 
 
 if (rev->timedout) { 
 c->timedout = 1; 
 c->error = 1; 
 ngx_http_finalize_request(r, NGX_ERROR); 
 return; 
 } 
 
 if (r->lingering_time) { 
 timer = (ngx_msec_t) (r->lingering_time - ngx_time()); 
 
 if (timer <= 0) { 
  r->discard_body = 0; 
  r->lingering_close = 0; 
  ngx_http_finalize_request(r, NGX_ERROR); 
  return; 
 } 
 
 } else { 
 timer = 0; 
 } 
 
 rc = ngx_http_read_discarded_request_body(r); 
 
 if (rc == NGX_OK) { 
 r->discard_body = 0; 
 r->lingering_close = 0; 
 ngx_http_finalize_request(r, NGX_DONE); 
 return; 
 } 
 
 /* rc == NGX_AGAIN */ 
 
 if (ngx_handle_read_event(rev, 0) != NGX_OK) { 
 c->error = 1; 
 ngx_http_finalize_request(r, NGX_ERROR); 
 return; 
 } 
 
 if (timer) { 
 
 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 
 
 timer *= 1000; 
 
 if (timer > clcf->lingering_timeout) { 
  timer = clcf->lingering_timeout; 
 } 
 
 ngx_add_timer(rev, timer); 
 } 
}