1. 程式人生 > >nginx原始碼分析(7)——請求處理

nginx原始碼分析(7)——請求處理

        在建立連線過程中,對於nginx監聽到的每個客戶端連線,都會將它的讀事件的handler設定為ngx_http_init_request函式,這個函式就是請求處理的入口。在處理請求時,主要就是要解析http請求,比如:uri,請求行等,然後再根據請求生成響應。下面看一下nginx處理的具體過程。


1. ngx_http_init_request

        在ngx_http_init_connection函式中,將連線的讀事件的handler設定為這個函式,在客戶端傳送請求時會被呼叫。

    /* ngx_event_t的data域存放事件對應的連線控制代碼 */
    c = rev->data;

    /* 在ngx_init_connection中對讀事件添加了timer,超時直接返回*/
    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

        ngx_http_close_connection(c);
        return;
    }

    /* 連線處理的request的計數 */
    c->requests++;

    /* ngx_http_connection_t */
    hc = c->data;

    if (hc == NULL) {
        hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
        if (hc == NULL) {
            ngx_http_close_connection(c);
            return;
        }
    }

    r = hc->request;

    if (r) {
        ngx_memzero(r, sizeof(ngx_http_request_t));

        r->pipeline = hc->pipeline;

        if (hc->nbusy) {
            r->header_in = hc->busy[0];
        }

    } else {
    	/* 為request分配空間 */
        r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));
        if (r == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        hc->request = r;
    }

    c->data = r;
    r->http_connection = hc;

    c->sent = 0;
    r->signature = NGX_HTTP_MODULE;

        上面一段程式碼完成獲取連線、請求並進行一部分初始化工作,比如會為新的請求分配記憶體。

    /**
     * ngx_listening_t的servers存放監聽同一埠的server,但它們的監聽的地址可以不同。
     * 
     * 		      port
     * 		   /   |   \
     * 	        addr1 addr2 addr3 	   
     *           |     |     |
     *          conf1 conf2  conf3
     */
    port = c->listening->servers;

    r->connection = c;

    if (port->naddrs > 1) {

        /*
         * there are several addresses on this port and one of them
         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
         * is required to determine a server address
         */

    	/* 獲取連線c的socket繫結的本地地址 */
        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        /* 根據連線c的socket地址匹配port->addrs,找到對應的address:port */
        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
                    break;
                }
            }

            addr_conf = &addr6[i].conf;

            break;
#endif

        default: /* AF_INET */
            sin = (struct sockaddr_in *) c->local_sockaddr;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (addr[i].addr == sin->sin_addr.s_addr) {
                    break;
                }
            }

            addr_conf = &addr[i].conf;

            break;
        }

    } else {

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            addr6 = port->addrs;
            addr_conf = &addr6[0].conf;
            break;
#endif

        default: /* AF_INET */
            addr = port->addrs;
            addr_conf = &addr[0].conf;
            break;
        }
    }

    /* virtual hosts based on the address:port */
    r->virtual_names = addr_conf->virtual_names;

    /* the default server configuration for the address:port */
    cscf = addr_conf->default_server;

    /* 初始化為default server的配置,後續虛擬主機匹配成功會採用對應的配置 */
    r->main_conf = cscf->ctx->main_conf;
    r->srv_conf = cscf->ctx->srv_conf;
    r->loc_conf = cscf->ctx->loc_conf;

        註釋中解釋的很清楚,這段程式碼是為地址addr:port匹配server config,從而確定該請求的配置。由於nginx支援虛擬主機,所以這裡確定了r->virtual_names是該addr:port對應的虛擬主機陣列,後面會根據請求的HOST匹配對應的虛擬主機從而確定最終的配置。nginx的每個請求都有執行環境,這個環境就是ngx_request_t請求的main_conf、srv_conf和loc_conf。請求的執行環境就是nginx各個模組的配置資訊,根據這些資訊的不同請求的處理效果是不一樣的。待解釋完虛擬主機匹配後,再詳細說明執行環境查詢。

    rev->handler = ngx_http_process_request_line;
    r->read_event_handler = ngx_http_block_reading;
        將連線讀事件的handler設定為ngx_http_process_request_line,在本方法的最後會直接呼叫rev->handler(rev)。為什麼這麼做?

        我的猜測是,在客戶端第一次請求時,該連線的讀事件的handler是ngx_init_request,用於處理話請求,後續請求直接複用,所以不需要執行ngx_init_request,所以需要將rev->handler設定成ngx_http_process_request_line,最後直接呼叫handler是為了在ngx_init_request中處理第一個請求。不知道這樣對不對?

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    c->log->file = clcf->error_log->file;
    if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
        c->log->log_level = clcf->error_log->log_level;
    }

    /* initialise the temporary buffer for the request's connection */
    if (c->buffer == NULL) {
        c->buffer = ngx_create_temp_buf(c->pool,
                                        cscf->client_header_buffer_size);
        if (c->buffer == NULL) {
            ngx_http_close_connection(c);
            return;
        }
    }

    /* raw request header string will be stored in the connection's buffer */
    if (r->header_in == NULL) {
        r->header_in = c->buffer;
    }

    /* initialise the memory pool of the request */
    r->pool = ngx_create_pool(cscf->request_pool_size, c->log);
    if (r->pool == NULL) {
        ngx_http_close_connection(c);
        return;
    }
        初始化連線的日誌資訊,為連線分配臨時buffer,將request的header_in指向該臨時buffer,後面會看到header_in用於存放請求頭資訊。然後為該請求分配記憶體池。
    if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
                      sizeof(ngx_table_elt_t))
        != NGX_OK)
    {
        ngx_destroy_pool(r->pool);
        ngx_http_close_connection(c);
        return;
    }

    // ?
    r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
    if (r->ctx == NULL) {
        ngx_destroy_pool(r->pool);
        ngx_http_close_connection(c);
        return;
    }
        為http響應header分配空間,request的ctx屬性目前不知道幹什麼用的。。。
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    /**
     * 為所有變數在此請求中分配空間
     * Allocate memory space for the variable values of the request.
     * The type of variables array is ngx_http_variable_value_t.
     */
    r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
                                        * sizeof(ngx_http_variable_value_t));
    if (r->variables == NULL) {
        ngx_destroy_pool(r->pool);
        ngx_http_close_connection(c);
        return;
    }
        nginx中的變數的生命週期是基於請求的,也就是說請求之間的變數時獨立的。request的variables相當於變數的值,cmcf->variables相當於變數的定義。為了處理變數,nginx實現了一個小型的指令碼引擎,也就是一個直譯器。在解析配置檔案時生成了程式碼,在處理請求時去執行這個程式碼,具體就是在ngx_http_rewrite_module註冊的phase handler中處理的,後面介紹nginx變數機制時再詳細說明。

        這裡就是為請求分配所有變數的佔用空間。

    c->single_connection = 1;
    c->destroyed = 0;

    /* 主請求 */
    r->main = r;
    r->count = 1;

    /* Store start time of the request */
    tp = ngx_timeofday();
    r->start_sec = tp->sec;
    r->start_msec = tp->msec;

    /* HTTP method, e.g. POST, GET, PUT */
    r->method = NGX_HTTP_UNKNOWN;

    r->headers_in.content_length_n = -1;
    r->headers_in.keep_alive_n = -1;
    r->headers_out.content_length_n = -1;
    r->headers_out.last_modified_time = -1;

    r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;

    /* 子請求個數的限制 */
    r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;

    r->http_state = NGX_HTTP_READING_REQUEST_STATE;

    ctx = c->log->data;
    ctx->request = r;
    ctx->current_request = r;
    r->log_handler = ngx_http_log_error_handler;

#if (NGX_STAT_STUB)
    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
    r->stat_reading = 1;
    (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
#endif

    /* Start processing request */
    rev->handler(rev);
        對連線和請求的屬性進行初始化,後面講解subrequest時再具體分析r->main和r->subrequests。最後呼叫rev->handler(rev)實際就是呼叫ngx_http_process_request_line。

2. ngx_http_process_request_line

        這個函式是用來處理請求行的,會不斷呼叫ngx_http_read_request_header從socket讀取頭部並儲存在request的header_in欄位中,實際上header_in是一個緩衝區,就是指向連線的臨時緩衝區。

    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;
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }
        從rev中獲取連線以及請求,並判斷是否超時。

        http請求行格式是:

GET /index.php HTTP/1.1
        接下來的for迴圈就是試圖從socket中讀取足夠的資訊,然後解析出HTTP Method、URI以及HTTP版本等資訊。
        if (rc == NGX_AGAIN) {

           /**
            * 讀取請求行,儲存到r->header_in緩衝區
            */
            n = ngx_http_read_request_header(r);

            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }
        讀取request line並儲存在header_in緩衝區。
        /**
         * 解析請求行,解析後的資訊的是以r->uri_start,r->uri_end,r->arg_start等
         * 一些指標儲存的,這些指標指向r->header_in
         */
        rc = ngx_http_parse_request_line(r, r->header_in);
        解析請求行,ngx_http_request_t中比如uri_start、uri_end、arg_start、request_start、request_end等指標用於請求行的解析,識別各個屬性在header_in緩衝區中的指標的位置。ngx_http_parse_request_line在ngx_http_parse.c中,用於解析請求行,實際上就是一個狀態機的實現,所有的狀態包括:
    enum {
        sw_start = 0,
        sw_method,
        sw_spaces_before_uri,
        sw_schema,
        sw_schema_slash,
        sw_schema_slash_slash,
        sw_host,
        sw_port,
        sw_host_http_09,
        sw_after_slash_in_uri,
        sw_check_uri,
        sw_check_uri_http_09,
        sw_uri,
        sw_http_09,
        sw_http_H,
        sw_http_HT,
        sw_http_HTT,
        sw_http_HTTP,
        sw_first_major_digit,
        sw_major_digit,
        sw_first_minor_digit,
        sw_minor_digit,
        sw_spaces_after_digit,
        sw_almost_done
    } state;
        每個狀態表示解析到哪一步,根據輸入字元決定下一狀態。這裡不糾結於狀態機的實現,只要記住在本方法呼叫完後請求行解析完畢,所有資訊儲存在request的欄位中;要不就是解析沒有完成,進行下一次迭代;要不就是解析出錯。

        解析成功後,會根據ngx_http_request_t中用於解析的指標去初始化request中的屬性,比如uri、args等。如果使用的是complex uri會進入其解析過程。

            /* the request line has been parsed successfully */

            r->request_line.len = r->request_end - r->request_start;
            r->request_line.data = r->request_start;


            if (r->args_start) {
                r->uri.len = r->args_start - 1 - r->uri_start;
            } else {
                r->uri.len = r->uri_end - r->uri_start;
            }
        初始化request_line屬性,是一個ngx_str_t,然後初始化uri的長度。
           if (r->complex_uri || r->quoted_uri) {

                r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);
                if (r->uri.data == NULL) {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

                rc = ngx_http_parse_complex_uri(r, cscf->merge_slashes);

                if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) {
                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                  "client sent invalid request");
                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
                    return;
                }

            } else {
                r->uri.data = r->uri_start;
            }
        初始化uri的data欄位,如果使用complex uri就進入其解析過程,否則直接賦值為uri_start。
            /* 未解析的uri */
            r->unparsed_uri.len = r->uri_end - r->uri_start;
            r->unparsed_uri.data = r->uri_start;

            r->valid_unparsed_uri = r->space_in_uri ? 0 : 1;

            /* http method */
            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 (r->uri_ext) {
                if (r->args_start) {
                    r->exten.len = r->args_start - 1 - r->uri_ext;
                } else {
                    r->exten.len = r->uri_end - r->uri_ext;
                }

                r->exten.data = r->uri_ext;
            }

            /* 請求引數 */
            if (r->args_start && r->uri_end > r->args_start) {
                r->args.len = r->uri_end - r->args_start;
                r->args.data = r->args_start;
            }
        接下來對其他的屬性初始化。
            /* 解析並驗證host */
            if (r->host_start && r->host_end) {

                host = r->host_start;
                n = ngx_http_validate_host(r, &host,
                                           r->host_end - r->host_start, 0);

                if (n == 0) {
                    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 (n < 0) {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                /* 初始化request header的server欄位 */
                r->headers_in.server.len = n;
                r->headers_in.server.data = host;
            }

        解析並驗證host,然後初始化request header的server欄位。

            if (r->http_version < NGX_HTTP_VERSION_10) {

            	/**
            	 * 根據請求的host,即虛擬主機名,去查詢對應的server
            	 */
                if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
                                                 r->headers_in.server.len)
                    == NGX_ERROR)
                {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                ngx_http_process_request(r);
                return;
            }
        如果使用的http協議版本小於1.0,那麼不支援header以及請求體,所以接下來直接呼叫ngx_http_process_request處理請求。前面已經介紹過根據請求的addr:port確定了該請求的虛擬主機集合,接下來就要根據請求的host確定具體是哪個虛擬主機來處理該請求,通過ngx_http_find_virtual_server處理,r->headers_in.server存放的就是host。這個函式處理的很簡單,因為已經將該addr:port對應的虛擬主機集合組織成map,首先是根據host從這個map中get,如果有對應的虛擬主機直接返回。如果沒有,但是nginx提供正則表示式支援,則逐一進行匹配直到找一個為止。找到host對應的虛擬主機後,最重要的是將request的執行環境設定為該虛擬主機對應的配置。ngx_http_process_request後面會詳說。對於http0.9然後會直接返回,而1.0或1.1還有沒有處理完。
            /* Initialise list for request headers */
            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;
            }


            /* Initialise array for request cookies  */
            if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
                               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";
        因為http1.0和http1.1支援header和cookie,這裡先為它們分配空間,後面再解析這些資料。
            /* process request headers */
            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);

            return;
        設定讀事件rev的handler為ngx_http_process_request_headers,然後呼叫ngx_http_process_request_headers進行header解析。

        下面先看看for迴圈的最後部分,主要是錯誤處理和緩衝區擴容。

        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;
        }
        請求解析出錯,這裡會終結這個請求,並log。
        /* NGX_AGAIN: a request line parsing is still incomplete */

        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;
            }
        }
        rc等於NGX_AGAIN,說起request line沒有處理完畢,如果header_in大小不夠,則呼叫ngx_http_alloc_large_header_buffer進行擴容。

3. ngx_http_process_request_headers

        這個函式處理request header。

    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;
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }
        獲取連線和請求,並判斷超時。接下來for迴圈會一次解析一個header。
           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;
                        p[len++] = '.'; p[len++] = '.'; p[len++] = '.';
                    }

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

            n = ngx_http_read_request_header(r);

            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        首先還是先判斷緩衝區大小是否夠用,如果不夠用就擴容,擴容失敗就會退出。然後呼叫ngx_http_read_request_header讀取請求並存到header_in緩衝區。
        rc = ngx_http_parse_header_line(r, r->header_in,
                                        cscf->underscores_in_headers);
        呼叫ngx_http_parse_header_line解析header,其內部實現和ngx_http_parse_request_line一樣,都是狀態機,這個函式會一次解析一個header,實際上就是一行。
        如果解析成功,即rc等NGX_OK,會執行:
            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;
            }
        解析到不合法的header,log然後解析下一個header。否則,說明成功解析了一個header,會進行header的初始化。
            /* r->header_hash是當前解析得到的header的hash值 */
            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);
            }
         把解析出的header新增到request的headers_in.header連結串列中,然後對其初始化,包括:header的name、value等。
            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;
            }

        在http模組的初始化函式ngx_http_block中,會呼叫ngx_http_init_headers_in_hash將所有可能的header初始化成hash map,然後儲存至ngx_http_core_main_conf_t的headers_in_hash欄位中。在ngx_http_request.c中,定義的ngx_http_headers_in陣列就是所有可能的header。這裡會檢視解析出的header是否在這個map,如果在的話會呼叫對應的handler進行初始化。

        如果rc等於NGX_HTTP_PARSE_HEADER_DONE,說明header解析完畢,接下來會執行:

        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_in->start;

            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;

            /* 根據host匹配虛擬主機,並對一些header初始化 */
            rc = ngx_http_process_request_header(r);

            if (rc != NGX_OK) {
                return;
            }

            ngx_http_process_request(r);

            return;
        }
        會呼叫ngx_http_process_request_header函式解析虛擬主機,並對一些header初始化。然後呼叫ngx_http_process_request進行後續的請求處理。ngx_http_process_request_headers的最後部分就是錯誤處理,這裡不詳述,下面看一下請求的處理部分。

4. ngx_http_process_request

        這個函式驅動請求的處理,併為處理做些準備。

    /* remove timer of read event of the connection */
    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }
        刪除掉之前設定的讀事件的timer。
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;
        設定讀寫事件的handler為ngx_http_request_handler,這個函式貌似是處理subrequest的,還不敢確定。
    ngx_http_handler(r);

    // 處理subrequest
    ngx_http_run_posted_requests(c);
        呼叫ngx_http_handler繼續處理請求,ngx_http_run_posted_requests函式是處理子請求的。

5. ngx_http_handler

        為跑一遍所有phase hander做準備。

    r->connection->log->action = NULL;

    r->connection->unexpected_eof = 0;

    /**
     * 初始化r->phase_handler
     */
    if (!r->internal) {
        switch (r->headers_in.connection_type) {
        case 0:
            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
            break;

        case NGX_HTTP_CONNECTION_CLOSE:
            r->keepalive = 0;
            break;

        case NGX_HTTP_CONNECTION_KEEP_ALIVE:
            r->keepalive = 1;
            break;
        }

        r->lingering_close = (r->headers_in.content_length_n > 0);
        r->phase_handler = 0;

    } else {
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
        r->phase_handler = cmcf->phase_engine.server_rewrite_index;
    }
        初始化request的phase_handler,實際上就是第一個執行的phase handler在handlers陣列中的下標。
    r->write_event_handler = ngx_http_core_run_phases;
    ngx_http_core_run_phases(r);
        設定request的write_event_handler為ngx_http_core_run_phases,然後再呼叫這個函式把請求對應的phase handler執行一遍。下面看這個函式。

6. ngx_http_core_run_phases

        nginx把請求的處理劃分成11個階段,其中一些階段可以自定義新增handler,這個函式就是執行請求對應對應所有的phase handler。對應nginx對phase handler的處理會在之後的文章中分析。

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    ph = cmcf->phase_engine.handlers;
        ngx_http_core_main_conf_t的phase_engine的handlers就是所有的phase handler組成的陣列。
    /* 遍歷phase上註冊的所有handler,這裡是以r->phase_handler為索引組成的連結串列 */
    while (ph[r->phase_handler].checker) {

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {
            return;
        }
    }

        遍歷phase上註冊的所有handler,這裡是以r->phase_handler為索引組成的連結串列。在呼叫每個handler的checker時會更新request的phase_handler,從而實現一個handler的連結串列。

        在跑完所有的phase handler之後,這個請求就被處理完畢。實際的響應內容的輸出,是在content phase的handler中呼叫filter輸出實現的。下一篇文章會介紹nginx的phase handler的處理,後面還會介紹filter實現。