详解Nginx的核心配置模块中对于请求体的接受流程

2019-10-17 19:57:32王旭

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; 
} 

由于函数不长,这里把它完整的列出来了,函数的开始同样先判断了不需要再做处理的情况:子请求不需要处理,已经调用过此函数的也不需要再处理。接着调用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核心在处理完请求后直接释放了请求的相关资源。