1. 程式人生 > >Nginx原始碼閱讀(ngx_http_process_request_line)

Nginx原始碼閱讀(ngx_http_process_request_line)

ngx_http_process_request_line()

static void
ngx_http_process_request_line(ngx_event_t *rev)
{
    ssize_t              n;
    ngx_int_t            rc, rv;
    ngx_str_t            host;
    ngx_connection_t    *c;
    ngx_http_request_t  *r;

    c = rev->data; // 當前事件對應的連線
    r = c->data; // 當前連線對應的請求

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                   "http process request line");

    // 若當前讀事件已經超時
    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        c->timedout = 1; // 設定當前連線的超時標誌位為1
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }

    // NGX_AGAIN表示接收緩衝區header_in中沒有未解析的資料
    rc = NGX_AGAIN;

    for ( ;; ) {

        // 若header_in中沒有未解析的資料
        if (rc == NGX_AGAIN) {
            // 把核心套接字緩衝區的資料複製到header_in中
            n = ngx_http_read_request_header(r);

            // NGX_AGAIN表示若需要繼續接收資料,NGX_ERROR表示客戶端關閉連線或連線錯誤
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }

        // 若header_in中有未解析的資料,用狀態機解析資料
        rc = ngx_http_parse_request_line(r, r->header_in);

        // 若請求行被正確解析
        if (rc == NGX_OK) {

            /* the request line has been parsed successfully */

            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;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http request line: \"%V\"", &r->request_line);

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

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

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

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


            // 初始化連結串列,為接收http請求頭做好準備
            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";

            // 修改當前連線的讀事件的回撥函式為ngx_http_process_request_headers()並呼叫該函式
            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);

            return;
        }

        // 若ngx_http_parse_request_line()返回錯誤
        if (rc != NGX_AGAIN) {

            /* there was error while a request line parsing */

            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_AGAIN: a request line parsing is still incomplete */

        // 若ngx_http_parse_request_line()返回NGX_AGAIN,需要判斷是緩衝區大小不夠,還是已讀資料不夠

        // 若緩衝區已經用盡
        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;
            }
        }
    }
}



ngx_http_read_request_header()

static ssize_t
ngx_http_read_request_header(ngx_http_request_t *r)
{
    ssize_t                    n;
    ngx_event_t               *rev;
    ngx_connection_t          *c;
    ngx_http_core_srv_conf_t  *cscf;
 
    c = r->connection; // 當前請求對應的連線
    rev = c->read; // 當前連線對應的讀事件
 
    n = r->header_in->last - r->header_in->pos;
 
    // 若當前請求的接收緩衝區header_in中有未解析的資料,直接返回
    if (n > 0) {
        return n;
    }
 
    // 若當前讀事件已就緒
    if (rev->ready) {
        // 迴圈呼叫recv()將核心套接字緩衝區中的資料複製到header_in中
        n = c->recv(c, r->header_in->last,
                    r->header_in->end - r->header_in->last);
    } else {
        n = NGX_AGAIN;
    }
 
    if (n == NGX_AGAIN) {
        if (!rev->timer_set) {
            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
            // 將讀事件加入到定時器中
            ngx_add_timer(rev, cscf->client_header_timeout);
        }
 
        // 將讀事件加入到epoll中
        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return NGX_ERROR;
        }
 
        return NGX_AGAIN;
    }
 
    // 若客戶端關閉連線
    if (n == 0) {
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client prematurely closed connection");
    }
 
    // 若客戶端關閉連線或連線錯誤
    if (n == 0 || n == NGX_ERROR) {
        c->error = 1;
        c->log->action = "reading client request headers";
 
        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return NGX_ERROR;
    }
 
    r->header_in->last += n;
 
    return n;
}



ngx_http_process_request_headers()

static void
ngx_http_process_request_headers(ngx_event_t *rev)
{
    u_char                     *p;
    size_t                      len;
    ssize_t                     n;
    ngx_int_t                   rc, rv;
    ngx_table_elt_t            *h;
    ngx_connection_t           *c;
    ngx_http_header_t          *hh;
    ngx_http_request_t         *r;
    ngx_http_core_srv_conf_t   *cscf;
    ngx_http_core_main_conf_t  *cmcf;

    c = rev->data;
    r = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                   "http process request header line");

    // 若當前讀事件已經超時
    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        c->timedout = 1; // 設定當前連線的超時標誌位為1
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    rc = NGX_AGAIN;

    for ( ;; ) {

        if (rc == NGX_AGAIN) {

            // 若緩衝區已經用盡
            if (r->header_in->pos == r->header_in->end) {

                // 分配另一塊大緩衝區並拷貝之前的資料
                rv = ngx_http_alloc_large_header_buffer(r, 0);

                if (rv == NGX_ERROR) {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                // 若大緩衝區仍然裝不下整個請求頭
                if (rv == NGX_DECLINED) {
                    p = r->header_name_start;

                    r->lingering_close = 1;

                    if (p == NULL) {
                        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                      "client sent too large request");
                        ngx_http_finalize_request(r,
                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
                        return;
                    }

                    len = r->header_in->end - p;

                    if (len > NGX_MAX_ERROR_STR - 300) {
                        len = NGX_MAX_ERROR_STR - 300;
                    }

                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                "client sent too long header line: \"%*s...\"",
                                len, r->header_name_start);

                    ngx_http_finalize_request(r,
                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
                    return;
                }
            }

            // 把核心套接字緩衝區的資料複製到header_in中
            n = ngx_http_read_request_header(r);

            // NGX_AGAIN表示若需要繼續接收資料,NGX_ERROR表示客戶端關閉連線或連線錯誤
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }

        /* the host header could change the server configuration context */
        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        // 若header_in中有未解析的資料,用狀態機解析資料
        rc = ngx_http_parse_header_line(r, r->header_in,
                                        cscf->underscores_in_headers);

        // 返回NGX_OK,表示解析出了一行請求頭
        if (rc == NGX_OK) {

            r->request_length += r->header_in->pos - r->header_name_start;

            if (r->invalid_header && cscf->ignore_invalid_headers) {

                /* there was error while a header line parsing */

                ngx_log_error(NGX_LOG_INFO, c->log, 0,
                              "client sent invalid header line: \"%*s\"",
                              r->header_end - r->header_name_start,
                              r->header_name_start);
                continue;
            }

            /* a header line has been parsed successfully */

            h = ngx_list_push(&r->headers_in.headers);
            if (h == NULL) {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;
            h->key.data = r->header_name_start;
            h->key.data[h->key.len] = '\0';

            h->value.len = r->header_end - r->header_start;
            h->value.data = r->header_start;
            h->value.data[h->value.len] = '\0';

            h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
            if (h->lowcase_key == NULL) {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

            if (h->key.len == r->lowcase_index) {
                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

            } else {
                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
            }

            hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
                               h->lowcase_key, h->key.len);

            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
                return;
            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http header: \"%V: %V\"",
                           &h->key, &h->value);

            continue;
        }

        // 返回NGX_HTTP_PARSE_HEADER_DONE,表示解析出了整個請求頭
        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http header done");

            r->request_length += r->header_in->pos - r->header_name_start;

            // 設定請求狀態為NGX_HTTP_PROCESS_REQUEST_STATE
            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;

            /* ngx_http_process_request_header() {
                   ...
                   ngx_http_set_virtual_server() {
                       ...
                       ngx_http_find_virtual_server()
                       ...
                   }
               }   
               ngx_http_process_request_header()主要做了兩個方面的事情:
               一是呼叫ngx_http_find_virtual_server()查詢虛擬伺服器配置;
               二是對一些請求頭做一些協議的檢查 */
            rc = ngx_http_process_request_header(r);

            if (rc != NGX_OK) {
                return;
            }

            // 處理http請求
            ngx_http_process_request(r);

            return;
        }

        // 返回NGX_AGAIN,表示需要繼續接收資料
        if (rc == NGX_AGAIN) {

            /* a header line parsing is still not complete */

            continue;
        }

        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */

        // 返回NGX_HTTP_PARSE_INVALID_HEADER,表示請求頭解析過程中遇到錯誤
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client sent invalid header line");

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return;
    }
}


--------------------- 
作者:hz5034 
來源:CSDN 
原文:https://blog.csdn.net/hz5034/article/details/81055155 
版權宣告:本文為博主原創文章,轉載請附上博文連結!