当一个模块想要主动的丢弃客户端发过的请求体body,可以调用nginx核心提供的ngx_http_discard_request_body()接口,主动丢弃的原因可能有很多种,如模块的业务逻辑压根不需要请求体 ,客户端发送的请求体是非法的等。下面开始分析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;
}
//ngx_http_test_expect函数是处理http1.1 expect的情况,
//根据http1.1的expect机制,如果客户端发送了expect头,
//而服务端不希望接收请求体时,必须返回417(Expectation Failed)错误。
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;
}
}
//数据还没有读取完毕,则挂载ngx_http_discarded_request_body_handler处理函数
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; //数据还没有读取完毕,置变量,read_event_handler会下次处理
}
return NGX_OK;
}
void
ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_msec_t timer;
ngx_event_t *rev;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
rev = c->read;
//检测是否超时,超时则直接断开连接
if (rev->timedout) {
c->timedout = 1;
c->error = 1;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
//在ngx_http_finalize_connection()函数中,如果检查到还有未丢弃的请求体时,
//nginx会添加一个读事件定时器,它的时长为lingering_timeout指令所指定,
//默认为5秒,不过这个时间仅仅两次读事件之间的超时时间,
//等待请求体的总时长为lingering_time指令所指定,默认为30秒。
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);
}
}