现在的位置: 首页 > 综合 > 正文

ngx_http_discard_request_body 函数分析

2013年03月11日 ⁄ 综合 ⁄ 共 2548字 ⁄ 字号 评论关闭

        当一个模块想要主动的丢弃客户端发过的请求体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);
    }
}


抱歉!评论已关闭.