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;
        }
    }
}
相關文章
相關標籤/搜索