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