http 请求体数据处理2--ngx

  • HTTP 处理数据包, 有的业务不须要,此时只须要将数据包文读取后丢弃, 可是ngx 为何还要提供一个丢弃接口呢???解决了什么问题??
  • ------对于HTTP模块而言,放弃接收包体就是简单地不处理包体了,但是对于HTTP框架而言,并非不接收包体就能够的。由于对于客户端而言,一般
    会调用一些阻塞的发送方法来发送包体,若是HTTP框架一直不接收包体,会致使实现上不够健壮的客户端认为服务器超时无响应,于是简单地关
    闭链接,可这时Nginx模块可能还在处理这个链接。所以,HTTP模块中的放弃接收包体,对HTTP框架而言就是接收包体,可是接收后不作保存,直接丢弃。

    HTTP模块调用的ngx_http_discard_request_body方法用于第一次启动丢弃包体动做,而ngx_http_discarded_request_body_handler是做为请
    求的read_event_handler方法的,在有新的可读事件时会调用它处理包体。ngx_http_read discarded_request_body方法则是根据上述两个方法
    通用部分提取出的公共方法,用来读取包体且不作任何处理。

 

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_V2)
    if (r->stream && r == r->main) {
        r->stream->skip_data = NGX_HTTP_V2_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来调用
    /* 有可能执行了ngx_http_block_reading->ngx_http_block_reading,因此若是须要继续读取客户端请求,须要add event */
    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;
}

 

  HTTP模块调用的ngx_http_discard_request_body方法用于第一次启动丢弃包体动做,而ngx_http_discarded_request_body_handler是做为请
求的read_event_handler方法的,在有新的可读事件时会调用它处理包体。ngx_http_read_discarded_request_body方法则是根据上述两个方法
通用部分提取出的公共方法,用来读取包体且不作任何处理。服务器

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;

    //首先检查TCP链接上的读事件的timedout标志位,为1时表示已经超时,这时调用ngx_http_finalize_request方法结束请求,传递的参数是NGX_ERROR,流程结束
    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_msec_t) ngx_time();

        if ((ngx_msec_int_t) timer <= 0) {
            r->discard_body = 0;
            r->lingering_close = 0;
            ngx_http_finalize_request(r, NGX_ERROR);
            return;
        }

    } else {
        timer = 0;
    }

    //调用ngx_http_read_discarded_request_body方法接收包体,检测其返回值。
    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;
    }

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        c->error = 1;
        ngx_http_finalize_request(r, NGX_ERROR);
        return;
    }

    /* rc == NGX_AGAIN */

    if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != 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, NGX_FUNC_LINE);
    }
}

 

 

 

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