ngx_http_process_request_line函數解析

處理請求行,一般的http請求過程中,ngx_http_process_request_line函數被ngx_http_wait_request_handler調用,並且在ngx_http_wait_request_handler中被設置爲讀事件的回調函數。在連接被accept之後,有數據到達之後會執行ngx_http_create_request函數,之後再有讀事件被觸發時被調用的回調函數就是ngx_http_process_request_line。主要代碼和解析如下。

判斷是否超時,如果超時,報錯並且結束請求。

if (rev->timedout) {
    ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
    c->timedout = 1;
    ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
    return;
}

剩下的過程全部在一個for(;;){...}塊中執行。

ngx_http_read_request_header函數如果返回NGX_AGAIN,說明尚有數據未接受完,讀事件還會被觸發,而且讀事件回調函數依然是ngx_http_process_request_line。如果返回NGX_ERROR,說明出問題了,需要結束請求。正常的話,ngx_http_read_request_header返回了接受到的字符個數,並且說明已經接受完了。

if (rc == NGX_AGAIN) {
    n = ngx_http_read_request_header(r);
    if (n == NGX_AGAIN || n == NGX_ERROR) {
        return;
    }
}

ngx_http_read_request_header函數將接受到的內容存到了r->header_in,接下來ngx_http_parse_request_line函數解析請求行。ngx_http_parse_request_line函數有可能返回的結果有三類,一類是NGX_OK,一類是INVALID相關的,另一類是NGX_AGAIN,下面分類進行分析。

rc = ngx_http_parse_request_line(r, r->header_in);

按照代碼的順序,首先是返回了NGX_OK,返回NGX_OK說明請求行已經完整的接受並解析,主要代碼如下。

if (rc == NGX_OK) {

    根據請求行解析結果初始化r結構體

    r->request_line.len = r->request_end - r->request_start;
    r->request_line.data = r->request_start;
    r->request_length = r->header_in->pos - r->request_start;
    r->method_name.len = r->method_end - r->request_start + 1;
    r->method_name.data = r->request_line.data;
    if (r->http_protocol.data) {
        r->http_protocol.len = r->request_end - r->http_protocol.data;
    }

    解析uri,函數中主要初始化了r結構體中的uri和args相關的變量,並且判斷了請求行中是否存在非法字符。

    if (ngx_http_process_request_uri(r) != NGX_OK) {
        return;
    }

    關於host_start和host_end在ngx_http_parse_request_line函數中有可能被賦值。如果在本機起一個nginx,監聽80端口,用curl工具發送這樣一條請求:curl -x 127.0.0.1:80 http://192.168.119.51:80/test.html。如果GDB調試的話可以看到請求行是:GET http://192.168.119.51:80/test.html HTTP/1.1xxxxxx。而非常見的GET /test.html HTTP/1.1xxxxxx。個人認爲這是爲了兼容http 0.9。此時上述兩個變量就不是0x0了。在淘寶團隊的nginx博客中是這樣描述的:請求行的uri裏面包含了域名部分。本人也理解的不是很透徹。

    if (r->host_start && r->host_end) {
        host.len = r->host_end - r->host_start;
        host.data = r->host_start;
        rc = ngx_http_validate_host(&host, r->pool, 0);
        if (rc == NGX_DECLINED) {
            ngx_log_error(NGX_LOG_INFO, c->log, 0,
                          "client sent invalid host in request line");
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            return;
        }
        if (rc == NGX_ERROR) {
            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }
        if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
            return;
        }
        r->headers_in.server = host;
    }

    http0.9不需要解析請求頭,直接處理請求即可。r->headers_in.server變量是在上一個if塊中被賦值的。http0.9的請求不知道怎麼模擬。。。

    if (r->http_version < NGX_HTTP_VERSION_10) {
        if (r->headers_in.server.len == 0
            && ngx_http_set_virtual_server(r, &r->headers_in.server)
               == NGX_ERROR)
        {
            return;
        }
        ngx_http_process_request(r);
        return;
    }
    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
                      sizeof(ngx_table_elt_t))
        != NGX_OK)
    {
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }
    c->log->action = "reading client request headers";

    設置讀事件回調函數,再有數據到達時調用請求頭處理函數。當前可能已經收到了請求頭數據,所以馬上執行一次。

    rev->handler = ngx_http_process_request_headers;
    ngx_http_process_request_headers(rev);
    return;
}

ngx_http_parse_request_line函數返回結果如果不是NGX_OK,也不是NGX_AGAIN,那就一定是INVALID相關的,需要返回錯誤並且結束請求。

if (rc != NGX_AGAIN) {
    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                  ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);
    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
    return;
}

最後只剩下ngx_http_parse_request_line函數返回NGX_AGAIN的情況了。需要判斷是否請求頭部過大,過大的話就要開闢更大的buffer,並執行ngx_http_finalize_request函數,這個函數較爲複雜,我們只討論常規的http請求。在這裏執行的ngx_http_finalize_request請求,並不會真的結束請求,而且還會再進入到這個函數中。如果沒有出現頭部過大的情況,就要再次執行for循環語句,繼續執行ngx_http_read_request_header函數,讀取緩衝區中的數據,繼續ngx_http_parse_request_line函數。

if (r->header_in->pos == r->header_in->end) {
    rv = ngx_http_alloc_large_header_buffer(r, 1);
    if (rv == NGX_ERROR) {
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }
    if (rv == NGX_DECLINED) {
        r->request_line.len = r->header_in->end - r->request_start;
        r->request_line.data = r->request_start;
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client sent too long URI");
        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
        return;
    }
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章