ngx_http_discard_request_body 函式分析

langeldep發表於2013-04-10

        當一個模組想要主動的丟棄客戶端發過的請求體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);
    }
}



相關文章