static ngx_int_t
ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
    size_t     size;
    ssize_t    n;
    ngx_int_t  rc;
    ngx_buf_t  b;
    u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http read discarded body");

    ngx_memzero(&b, sizeof(ngx_buf_t));

    b.temporary = 1;

    for ( ;; ) {
        if (r->headers_in.content_length_n == 0) {
            r->read_event_handler = ngx_http_block_reading;
            return NGX_OK;
        }

        if (!r->connection->read->ready) {
            return NGX_AGAIN;
        }

        size = (size_t) ngx_min(r->headers_in.content_length_n,
                                NGX_HTTP_DISCARD_BUFFER_SIZE);

        n = r->connection->recv(r->connection, buffer, size);

        if (n == NGX_ERROR) {
            r->connection->error = 1;
            return NGX_OK;
        }

        if (n == NGX_AGAIN) {
            return NGX_AGAIN;
        }

        if (n == 0) {
            return NGX_OK;
        }

        b.pos = buffer;
        b.last = buffer + n;

        rc = ngx_http_discard_request_body_filter(r, &b);

        if (rc != NGX_OK) {
            return rc;
        }
    }
}
ngx_int_t
ngx_http_discard_request_body(ngx_http_request_t *r)
{
    ssize_t       size;
    ngx_int_t     rc;
    ngx_event_t  *rev;



    if (r != r->main || r->discard_body || r->request_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->headers_in.chunked) {
        return NGX_OK;
    }

    size = r->header_in->last - r->header_in->pos;

    if (size || r->headers_in.chunked) {
        rc = ngx_http_discard_request_body_filter(r, r->header_in);

        if (rc != NGX_OK) {
            return rc;
        }

        if (r->headers_in.content_length_n == 0) {
            return NGX_OK;
        }
    }

    rc = ngx_http_read_discarded_request_body(r);

    if (rc == NGX_OK) {
        r->lingering_close = 0;
        return NGX_OK;
    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    /* rc == NGX_AGAIN */

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

    r->count++;
    r->discard_body = 1;

    return NGX_OK;
}
// 读取请求体数据并丢弃
// 使用固定的4k缓冲区接受丢弃的数据
// 一直读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN
// 因为使用的是et模式,所以必须把数据读完
// 需要使用回调ngx_http_discarded_request_body_handler读取数据
static ngx_int_t
ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
    size_t     size;
    ssize_t    n;
    ngx_int_t  rc;
    ngx_buf_t  b;

    // 使用固定的4k缓冲区接受丢弃的数据
    u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http read discarded body");

    // 读取用的缓冲区对象
    ngx_memzero(&b, sizeof(ngx_buf_t));

    // 标记为可写
    b.temporary = 1;

    // 一直读数据并解析,检查content_length_n
    // 如果无数据可读就返回NGX_AGAIN
    // 需要使用回调ngx_http_discarded_request_body_handler读取数据
    for ( ;; ) {

        // 判断content_length_n,为0就是已经读取完请求体
        // 就不需要再读了,读事件设置为block,返回成功
        if (r->headers_in.content_length_n == 0) {
            r->read_event_handler = ngx_http_block_reading;
            return NGX_OK;
        }

        // content_length_n大于0,表示还有数据需要读取
        // 看读事件是否ready,即是否有数据可读
        // 如果没数据那么就返回again
        // 需要使用回调ngx_http_discarded_request_body_handler读取数据
        if (!r->connection->read->ready) {
            return NGX_AGAIN;
        }

        // 决定要读取的数据长度,不能超过4k
        // #define NGX_HTTP_DISCARD_BUFFER_SIZE       4096
        size = (size_t) ngx_min(r->headers_in.content_length_n,
                                NGX_HTTP_DISCARD_BUFFER_SIZE);

        // 调用底层recv读取数据
        // 每次都从buffer的0位置放置数据,也就是丢弃之前读取的全部数据
        n = r->connection->recv(r->connection, buffer, size);

        // 出错也允许,因为丢弃数据不需要关心
        // 但需要置error标记
        if (n == NGX_ERROR) {
            r->connection->error = 1;
            return NGX_OK;
        }

        // again表示无数据可读
        // 需要使用回调ngx_http_discarded_request_body_handler读取数据
        if (n == NGX_AGAIN) {
            return NGX_AGAIN;
        }

        // 读到了0字节,即连接被客户端关闭,client abort
        // 也是ok
        if (n == 0) {
            return NGX_OK;
        }

        // 读取了n字节的数据,但不使用
        // 交给ngx_http_discard_request_body_filter来检查
        b.pos = buffer;
        b.last = buffer + n;

        // 检查请求结构体里的缓冲区数据,丢弃
        // 有content_length_n指定确切长度,那么只接收,不处理,移动缓冲区指针
        // chunked数据需要解析数据
        // content_length_n==0表示数据已经全部读完
        // 就已经完成了丢弃任务,否则就要加入epoll读事件继续读
        rc = ngx_http_discard_request_body_filter(r, &b);

        if (rc != NGX_OK) {
            return rc;
        }

        // 如果是ok,那么在for开始的地方检查content_length_n
    }
}
// 要求nginx丢弃请求体数据
// 子请求不与客户端直接通信,不会有请求体的读取
// 已经设置了discard_body标志,表示已经调用了此函数
// request_body指针不空,表示已经调用了此函数
// 这三种情况就无需再启动读取handler,故直接返回成功
// 因为要丢弃数据,所以不需要检查超时,也就是说即使超时也不算是错误
// 如果头里的长度是0且不是chunked
// 说明没有请求体数据,那么就无需再读,直接返回成功
// *一直*读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN
// 因为使用的是et模式,所以必须把数据读完
// 调用ngx_http_discard_request_body_filter检查收到的数据
// 使用回调ngx_http_discarded_request_body_handler读取数据
ngx_int_t
ngx_http_discard_request_body(ngx_http_request_t *r)
{
    ssize_t       size;
    ngx_int_t     rc;
    ngx_event_t  *rev;

    // 子请求不与客户端直接通信,不会有请求体的读取
    // 已经设置了discard_body标志,表示已经调用了此函数
    // request_body指针不空,表示已经调用了此函数
    // 这三种情况就无需再启动读取handler,故直接返回成功
    // discard_body在本函数最末尾设置,防止重入
    if (r != r->main || r->discard_body || r->request_body) {
        return NGX_OK;
    }

#if (NGX_HTTP_V2)
    if (r->stream) {
        r->stream->skip_data = 1;
        return NGX_OK;
    }
#endif

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

    // 如果头里的长度未设置、或者是0且不是chunked
    // 说明没有请求体数据,那么就无需再读,直接返回成功
    if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
        return NGX_OK;
    }

    // 检查缓冲区里在解析完头后是否还有数据
    // 也就是说之前可能读取了部分请求体数据
    size = r->header_in->last - r->header_in->pos;

    // 有数据,或者是chunked数据
    // 有可能已经读取了一些请求体数据,所以先检查一下
    if (size || r->headers_in.chunked) {
        // 检查请求结构体里的缓冲区数据,丢弃
        // 有content_length_n指定确切长度,那么只接收,不处理,移动缓冲区指针
        // chunked数据需要解析数据
        rc = ngx_http_discard_request_body_filter(r, r->header_in);

        // 不是ok表示出错,不能再读取数据
        if (rc != NGX_OK) {
            return rc;
        }

        // content_length_n==0表示数据已经全部读完
        // 就已经完成了丢弃任务,否则就要加入epoll读事件继续读
        if (r->headers_in.content_length_n == 0) {
            return NGX_OK;
        }
    }

    // 走到这里,表明content_length_n>=0,还有数据要读取
    // 接下来就读取请求体数据并丢弃
    // 使用固定的4k缓冲区接受丢弃的数据
    // 一直读数据并解析,检查content_length_n,如果无数据可读就返回NGX_AGAIN
    // 因为使用的是et模式,所以必须把数据读完
    // 需要使用回调ngx_http_discarded_request_body_handler读取数据
    rc = ngx_http_read_discarded_request_body(r);

    if (rc == NGX_OK) {
        r->lingering_close = 0;
        return NGX_OK;
    }

    // 出错
    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    /* rc == NGX_AGAIN */

    // 读事件not ready,无数据可读,那么就要在epoll里加入读事件和handler
    // 注意,不再需要加入定时器
    // 之后再有数据来均由ngx_http_discarded_request_body_handler处理
    // 里面还是调用ngx_http_read_discarded_request_body读数据

    r->read_event_handler = ngx_http_discarded_request_body_handler;

    // 注意,读事件的handler实际上是ngx_http_request_handler
    // 但最终会调用r->read_event_handler
    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    // 引用计数器增加,表示此请求还有关联的操作,不能直接销毁
    r->count++;

    // 设置丢弃标志,防止再次进入本函数
    r->discard_body = 1;

    return NGX_OK;
}
//ngx_http_read_discarded_request_body方法与ngx_http_do_read_client_request_body方法很类似
static ngx_int_t
ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
    size_t     size;
    ssize_t    n;
    ngx_int_t  rc;
    ngx_buf_t  b;
    u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http read discarded body");

    ngx_memzero(&b, sizeof(ngx_buf_t));

    b.temporary = 1;

    for ( ;; ) {
/*
    丢弃包体时请求的request_body成员实际上是NULL室指针,那么用什么变量来表示已经丢弃的包体有多大呢?实际上这时使用
了请求ngx_http_request_t结构体headers- in成员里的content length_n,最初它等于content-length头部,而每丢弃一部分包体,就会在
content_length_n变量中减去相应的大小。因此,content_length_n表示还需要丢弃的包体长度,这里首先检查请求的content_length_n成员,
如果它已经等于0,则表示已经接收到完整的包体,这时要把read event_handler重置为ngx_http_block_reading方法,表示如果再有可读
事件被触发时,不做任何处理。同时返回NGX_OK,告诉上层的方法已经丢弃了所有包体。
  */
        if (r->headers_in.content_length_n == 0) {
            r->read_event_handler = ngx_http_block_reading;
            return NGX_OK;
        }

        /* 如果连接套接字的缓冲区上没有可读内容,则直接返回NGX_AGAIN,告诉上层方法需要等待读事件的触发,等待Nginx框架的再次调度。 */
        if (!r->connection->read->ready) {
            return NGX_AGAIN;
        }

        size = (size_t) ngx_min(r->headers_in.content_length_n,
                                NGX_HTTP_DISCARD_BUFFER_SIZE);

        n = r->connection->recv(r->connection, buffer, size);

        if (n == NGX_ERROR) {
            r->connection->error = 1;
            return NGX_OK;
        }

        if (n == NGX_AGAIN) { //如果套接字缓冲区中没有读取到内容
            return NGX_AGAIN;
        }

        if (n == 0) { //如果客户端主动关闭了连接
            return NGX_OK;
        }

        b.pos = buffer;
        b.last = buffer + n;

        //接收到包体后,要更新请求的content_length_n成员,从而判断是否读取完毕,如果为0表示读取完毕,同时继续循环
        rc = ngx_http_discard_request_body_filter(r, &b);

        if (rc != NGX_OK) {
            return rc;
        }
    }
}
ngx_int_t
ngx_http_discard_request_body(ngx_http_request_t *r)
{
    ssize_t       size;
    ngx_int_t     rc;
    ngx_event_t  *rev;

#if (NGX_HTTP_SPDY)
    if (r->spdy_stream && r == r->main) {
        r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD;
        return NGX_OK;
    }
#endif

    /*
     首先检查当前请求是一个子请求还是原始请求。为什么要检查这个呢?因为对于子请求而言,它木是来自客户端的请求,所以不存在处理HTTP
     请求包体的概念。如果当前请求是原始请求,则继续执行;如果它是子请求,则直接返回NGX_OK表示丢弃包体成功。检查ngx_http_request_t结构
     体的request_body成员,如果它已经被赋值过且不再为NULL空指针,则说明已经接收过包体了,这时也需要返回NGX_OK表示成功。
     */
    if (r != r->main || r->discard_body || r->request_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");

    /*
    检查请求连接上的读事件是否在定时器中,这是因为丢弃包体不用考虑超时问题(linger_timer例外,本章不考虑此情况)。如果读事件
    的timer set标志位为1,则从定时器中移除此事件。还要检查content-length头部,如果它的值小于或等于0,同样意味着可以直接返回
    NGX一OK,表示成功丢弃了全部包体。
     */
    if (rev->timer_set) {
        ngx_del_timer(rev, NGX_FUNC_LINE);
    }
    if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
        return NGX_OK;
    }


    size = r->header_in->last - r->header_in->pos;

    if (size || r->headers_in.chunked) {
        rc = ngx_http_discard_request_body_filter(r, r->header_in);

        if (rc != NGX_OK) {
            return rc;
        }

        if (r->headers_in.content_length_n == 0) {
            return NGX_OK;
        }
    }

    /*
      在接收HTTP头部时,还是要检查是否凑巧已经接收到完整的包体(如果包体很小,那么这是非常可能发生的事),如果已经接收到完整的包
    体,则直接返回NGX OK,表示丢弃包体成功,否则,说明需要多次的调度才能完成丢弃包体这一动作,此时把请求的read_event_handler
    成员设置为ngx_http_discarded_request_body_handler方法。
      */
    rc = ngx_http_read_discarded_request_body(r);

    if (rc == NGX_OK) {
        /* 返回NGX一OK表示已经接收到完整的包体了,这时将请求的lingering_close延时关闭标志位设为0,表示不需要为了包体的接收而
        延时关闭了,同时返回NGX―OK表示丢弃包体成功。 */
        r->lingering_close = 0;
        return NGX_OK;
    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    //返回非NGX_OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作

    /* rc == NGX_AGAIN */

    r->read_event_handler = ngx_http_discarded_request_body_handler; //下次读事件到来时通过ngx_http_request_handler来调用

    if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != NGX_OK) { //调用ngx_handle_read_event方法把读事件添加到epoll中  handle为ngx_http_request_handler
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    /*
    返回非NGX_ OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作,于是先把引用计数加1,防止这边还在丢弃包体,
    而其他事件却已让请求意外销毁,引发严重错误。同时把ngx_http_request_t结构体的discard_body标志位置为1,表示正在丢弃包体,并
    返回NGX―OK,当然,达时的NGX OK绝不表示已经成功地接收完包体,只是说明ngx_http_discard_request_body执行完毕而已。
     */
    r->count++;
    r->discard_body = 1;

    return NGX_OK;
}