Nginx-HTTP之靜態網頁訪問流程分析一


假設訪問靜態網頁的配置如下:

worker_processes 1;
error_log stderr debug;
daemon off;
master_process on;

events {
    worker_connections 1024;
}

http {
    include           mime.types;
    default_type      application/octet-stream;
    sendfile          on;
    keepalive_timeout 65;
    server {
        listen 8080;
        location / {
            root html;
            index index.html index.htm;
        }
        error_page 500 502 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }
}

假設在瀏覽器輸入如下指令:http://serverip:8080/index.html,Nginx 服務器即會返回所請求的頁面。大致流程如下。

1. ngx_connection_t

typedef struct ngx_connection_s      ngx_connection_t;

/*
 * 這個連接表示是客戶端主動發起的,Nginx服務器被動接受
 * 的 TCP 連接,簡稱之為被動連接
 */
struct ngx_connection_s {
    void               *data;
    /*
     * 連接對應的讀事件
     */
    ngx_event_t        *read;
    /*
     * 連接對應的寫事件
     */
    ngx_event_t        *write;

    /*
     * 該連接對應已連接套接字
     */
    ngx_socket_t        fd;

    /*
     * 直接接收網絡字節流的方法
     */
    ngx_recv_pt         recv;
    /*
     * 直接發送網絡字節流的方法
     */
    ngx_send_pt         send;
    /*
     * 以 ngx_chain_t 鏈表為參數來接收網絡字節流的方法
     */
    ngx_recv_chain_pt   recv_chain;
    /*
     * 以 ngx_chain_t 鏈表為參數來發送網絡字節流的方法
     */
    ngx_send_chain_pt   send_chain;

    /*
     * 這個連接對應的 ngx_listening_t 監聽對象,此連接由 listening 
     * 監聽端口的事件建立.
     */
    ngx_listening_t    *listening;

    /*
     * 這個連接上已經發送出去的字節數
     */
    off_t               sent;

    /*
     * 記錄日志的 ngx_log_t 對象
     */
    ngx_log_t          *log;

    /*
     * 內存池。一般在 accept 一個新連接時,會創建一個內存池,而在這個連接
     * 結束時會銷毀內存池。注意,這里所說的連接是指成功建立的 TCP 連接,
     * 所有的 ngx_connection_t 結構體都是預分配的。這個內存池的大小將由
     * listening 監聽對象中的 pool_size 成功決定.
     */
    ngx_pool_t         *pool;

    int                 type;

    /*
     * 連接客戶端的 sockaddr 結構體
     */
    struct sockaddr    *sockaddr;
    socklen_t           socklen;
    /*
     * 連接客戶端字符串形式的 IP 地址
     */
    ngx_str_t           addr_text;

    ngx_str_t           proxy_protocol_addr;
    in_port_t           proxy_protocol_port;

#if (NGX_SSL || NGX_COMPAT)
    ngx_ssl_connection_t  *ssl;
#endif

    /*
     * 本機的監聽端口對應的 sockaddr 結構體,也就是 listening 監聽
     * 對象中的 sockaddr 成員.
     */
    struct sockaddr    *local_sockaddr;
    socklen_t           local_socklen;

    /*
     * 用於接收、緩存客戶端發來的字符流,每個事件消費模塊可自由決定從
     * 連接池中分配多大的空間給 buffer 這個接收緩存字段。例如,在 HTTP
     * 模塊中,它的大小決定於 client_header_buffer_size 配置項.
     */
    ngx_buf_t          *buffer;

    /*
     * 該字段用來將當前連接以雙向鏈表元素的形式添加到 ngx_cycle_t 核心結構體
     * 的 reuseable_connections_queue 雙向鏈表中,表示可重用的連接.
     */
    ngx_queue_t         queue;

    /*
     * 連接使用次數。ngx_connection_t 結構體每次建立一條來自客戶端的連接,
     * 或者用於主動向后端服務器發起連接時(ngx_peer_connection_t 也是用該字
     * 段),number 都會加 1
     * 
     */
    ngx_atomic_uint_t   number;

    /*
     * 處理的請求次數
     */
    ngx_uint_t          requests;

    /* 
     * 緩存中的業務類型。任何事件消費模塊都可以自定義需要的標志位。這個
     * buffered 字段有 8 位,最多可以同時表示 8 個不同的業務。第三方模塊
     * 在自定義buffered標志位時注意不要與可能使用的模塊定義的標志位沖突。
     * 目前 openssl 模塊定義了一個標志位:
     *     - #define NGX_SSL_BUFFERED     0x01
     * HTTP官方模塊定義了以下標志位:
     *     - #define NGX_HTTP_LOWLEVEL_BUFFERED      0xf0
     *     - #define NGX_HTTP_WRITE_BUFFERED         0x10
     *     - #define NGX_HTTP_GZIP_BUFFERED          0x20
     *     - #define NGX_HTTP_SSI_BUFFERED           0x01
     *     - #define NGX_HTTP_SUB_BUFFERED           0x02
     *     - #define NGX_HTTP_COPY_BUFFERED          0x04
     *     - #define NGX_HTTP_IMAGE_BUFFERED         0x08
     * 同時,對於 HTTP 模塊而言,buffered 的低4位要慎用,在實際發送響應的
     * ngx_http_write_filter_module 過濾模塊中,低 4 位標志位為 1 則意味着
     * Nginx 會一直認為有 HTTP 模塊還需要處理這個請求,必須等待 HTTP 模塊
     * 將低 4 位全置為 0 才會正常結束請求。檢查低 4 位的宏如下:
     *     - #define NGX_LOWLEVEL_BUFFERED           0xf0
     */
    unsigned            buffered:8;

    /* 
     * 本連接記錄日志的級別 
     */
    unsigned            log_error:3;     /* ngx_connection_log_error_e */

    /* 
     * 標志位,為 1 時表示連接已超時 
     */
    unsigned            timedout:1;
    /* 
     * 標志位,為 1 時表示連接處理過程中出現錯誤 
     */
    unsigned            error:1;
    /* 
     * 標志位,為 1 時表示連接已經銷毀。這里的連接指的是 TCP 連接,而不是
     * ngx_connection_t 結構體。當 destroyed 為 1 時,ngx_connection_t 結
     * 構體仍然存在,但其對應的套接字、內存池等已經不可用
     */
    unsigned            destroyed:1;

    /* 
     * 標志位,為 1 時表示連接處於空閑狀態,如 keepalive 請求中兩次請求之間的狀態 
     */
    unsigned            idle:1;
    /* 
     * 標志位,為 1 時表示連接可重用,它與上面的 queue 字段是對應使用的
     */
    unsigned            reusable:1;
    /*
     * 標志位,為1時表示連接關閉
     */
    unsigned            close:1;
    unsigned            shared:1;

    /* 
     * 標志位,為 1 時表示正在將文件中的數據發往連接的另一端
     */
    unsigned            sendfile:1;
    /* 
     * 標志位,如果為 1,則表示只有在連接套接字對應的發送緩沖區必須滿足
     * 最低設置的大小閾值時,事件驅動模塊才會分發該事件。這與 
     * ngx_handle_write_event 方法中的 lowat 參數是對應的 
     */
    unsigned            sndlowat:1;
    /* 
     * 標志位,表示如何使用 TCP 的 nodelay 特性。它的取值范圍是下面這個枚舉
     * 類型 ngx_connection_tcp_nodelay_e:
     * typedef enum {
     *     NGX_TCP_NODELAY_UNSET = 0,
     *     NGX_TCP_NODELAY_SET,
     *     NGX_TCP_NODELAY_DISABLED
     * }ngx_connection_tcp_nodelay_e;
     */
    unsigned            tcp_nodelay:2;   /* ngx_connection_tcp_nodelay_e */
    /* 
     * 標志位,表示如何使用 TCP 的 nopush 特性。它的取值范圍是下面這個枚舉類型
     * ngx_connection_tcp_nopush_e:
     * typedef enum {
     *     NGX_TCP_NOPUSH_UNSET = 0,
     *     NGX_TCP_NOPUSH_SET,
     *     NGX_TCP_NOPUSH_DISABLED
     * }ngx_connection_tcp_nopush_e;
     */
    unsigned            tcp_nopush:2;    /* ngx_connection_tcp_nopush_e */

    unsigned            need_last_buf:1;

#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
    unsigned            busy_count:2;
#endif

#if (NGX_THREADS || NGX_COMPAT)
    ngx_thread_task_t  *sendfile_task;
#endif
};

2. ngx_http_init_connection

void
ngx_http_init_connection(ngx_connection_t *c)
{
    ngx_uint_t              i;
    ngx_event_t            *rev;
    struct sockaddr_in     *sin;
    ngx_http_port_t        *port;
    ngx_http_in_addr_t     *addr;
    ngx_http_log_ctx_t     *ctx;
    ngx_http_connection_t  *hc;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6    *sin6;
    ngx_http_in6_addr_t    *addr6;
#endif
    
    /* 為當前的 HTTP 連接創建一個 ngx_http_connection_t 結構體,該結構體
     * 代表當前的 HTTP 連接 */
    hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
    if (hc == NULL) {
        ngx_http_close_connection(c);
        return;
    }
    
    /* 將 data 指針指向表示當前 HTTP 連接的 ngx_http_connection_t */
    c->data = hc;

    /* find the server configuration for the address:port */

    /* listening:這個連接對應的 ngx_listening_t 監聽對象,此連接由 listening
     * 監聽端口的事件建立.
     * servers: 對於 HTTP 模塊,該指針指向 ngx_http_port_t 結構體,該結構體
     * 實際保存着當前監聽端口的地址信息.
     */
    port = c->listening->servers;

    /* 若該端口對應主機上的多個地址 */
    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
         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

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

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

            hc->addr_conf = &addr[i].conf;

            break;
        }

    } else {

        /* 本機的監聽端口對應的 sockaddr 結構體 */
        switch (c->local_sockaddr->sa_family) {

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

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

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

    ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
    if (ctx == NULL) {
        ngx_http_close_connection(c);
        return;
    }

    ctx->connection = c;
    ctx->request = NULL;
    ctx->current_request = NULL;

    c->log->connection = c->number;
    c->log->handler = ngx_http_log_error;
    c->log->data = ctx;
    c->log->action = "waiting for request";

    c->log_error = NGX_ERROR_INFO;

    /* 連接對應的讀事件 */
    rev = c->read;
    /* 為該連接的讀事件設置回調處理函數 */
    rev->handler = ngx_http_wait_request_handler;
    /* 為該連接的寫事件設置回調處理函數,該函數為一個空函數,什么也不做 */
    c->write->handler = ngx_http_empty_handler;

#if (NGX_HTTP_V2)
    if (hc->addr_conf->http2) {
        rev->handler = ngx_http_v2_init;
    }
#endif

#if (NGX_HTTP_SSL)
    {
    ngx_http_ssl_srv_conf_t  *sscf;

    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);

    if (sscf->enable || hc->addr_conf->ssl) {

        c->log->action = "SSL handshaking";

        if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
            ngx_log_error(NGX_LOG_ERR, c->log, 0,
                          "no \"ssl_certificate\" is defined "
                          "in server listening on SSL port");
            ngx_http_close_connection(c);
            return;
        }

        hc->ssl = 1;

        rev->handler = ngx_http_ssl_handshake;
    }
    }
#endif

    if (hc->addr_conf->proxy_protocol) {
        hc->proxy_protocol = 1;
        c->log->action = "reading PROXY protocol";
    }

    /* 
     * 標志位,為1時表示當前事件已經准備就緒,也就是說,允許這個事件的消費者模塊
     * 處理這個事件。在HTTP框架中,經常會檢查事件的ready標志位以確定是否可以接收
     * 請求或者發送響應 */
    if (rev->ready) {
        /* the deferred accept(), iocp */

        /* 為 1,表示開啟了負載均衡機制,此時不會立刻執行該讀事件,而是將當前的
         * 讀事件添加到 ngx_posted_events 延遲執行隊列中 */
        if (ngx_use_accept_mutex) {
            ngx_post_event(rev, &ngx_posted_events);
            return;
        }

        /* 若沒有開啟負載均衡機制,則直接處理該讀事件 */
        rev->handler(rev);
        return;
    }

    /* 將讀事件添加到定時器中,超時時間為 post_accept_timeout 毫秒 
     * post_accept_timeout 在配置文件中沒有配置的話,默認為 60000
     * 毫秒 */
    ngx_add_timer(rev, c->listening->post_accept_timeout);
    /* 將該連接添加到可重用雙向鏈表的頭部 */
    ngx_reusable_connection(c, 1);

    /* 將該讀事件添加到事件驅動模塊中,這樣當該事件對應的 TCP 連接上
     * 一旦出現可讀事件(如接收到 TCP 連接的另一端發送來的字節流)就會
     * 回調該事件的 handler 方法 */
    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
        ngx_http_close_connection(c);
        return;
    }
}

3. ngx_add_timer

添加一個定時器事件,超時時間為 timer 毫秒.

/*
 * @ev: 需要操作的事件
 * @timer: 單位是毫秒,它告訴定時器事件 ev 希望 timer 毫秒后超時,
 *         同時需要回調 ev 的handler 方法.
 */
static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
    ngx_msec_t      key;
    ngx_msec_int_t  diff;

    key = ngx_current_msec + timer;

    /* 若該事件已經添加到定時器中(即紅黑樹) */
    if (ev->timer_set) {

        /*
         * Use a previous timer value if difference between it and a new
         * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows
         * to minimize the rbtree operations for fast connections.
         */

        /* 若該事件之前已經添加到定時器中,則計算此時兩者的超時時間之差 */
        diff = (ngx_msec_int_t) (key - ev->timer.key);

        /* 若兩者之差小於 300 毫秒,則直接返回 */
        if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) {
            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                           "event timer: %d, old: %M, new: %M",
                            ngx_event_ident(ev->data), ev->timer.key, key);
            return;
        }

        /* 否則,則先刪除該定時器事件,下面再以新的超時時間添加到定時器中 */
        ngx_del_timer(ev);
    }

    /* 超時時間 */
    ev->timer.key = key;

    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                   "event timer add: %d: %M:%M",
                    ngx_event_ident(ev->data), timer, ev->timer.key);

    /*
     * 將事件添加到紅黑樹中
     * 這種添加是間接性的,每個事件對象封裝結構體中都有一個timer字段,
     * 其為ngx_rbtree_node_t 類型變量,加入到紅黑樹中就是該字段,
     * 而非事件對象結構體本身。后面要獲取該事件結構體時可以通過利用
     * offsetof宏來根據該timer字段方便找到其所在的對應事件對象結構體.
     */
    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);

    /* 置位該變量,表示添加到紅黑樹中 */
    ev->timer_set = 1;
}

4. ngx_reusable_connection

void
ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
{
    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                   "reusable connection: %ui", reusable);

    /* 標志位,為 1 表示該連接可重用 */
    if (c->reusable) {
        ngx_queue_remove(&c->queue);
        ngx_cycle->reusable_connections_n--;

#if (NGX_STAT_STUB)
        (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);
#endif
    }

    /* 用參數值重新設置該標志位 */
    c->reusable = reusable;

    /* 若調用者指定該連接可重用 */
    if (reusable) {
        /* need cast as ngx_cycle is volatile */

        /* 則將該連接添加到 reusable_connections_queue 雙向鏈表頭中 */
        ngx_queue_insert_head(
            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
        /* 可重用連接的個數加 1 */
        ngx_cycle->reusable_connections_n++;

#if (NGX_STAT_STUB)
        /* 將原子變量 ngx_stat_waiting 的值加 1 */
        (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);
#endif
    }
}

5. ngx_handle_read_event

/*
 * 將讀事件添加到事件驅動模塊中,這樣該事件對應的 TCP 連接上一旦出現
 * 可讀事件(如接收到 TCP 連接另一端發送來的字符流)就會回調該事件的
 * handler 方法
 */
ngx_int_t
ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
{
    /* 在使用 epoll 的情況下,NGX_USE_CLEAR_EVENT 宏表示為該 epoll 使用
     * ET(即邊緣)模式。實際上,在 Nginx 中,epoll 是默認使用邊緣模式的,
     * 該模式僅支持非阻塞方式。所謂邊緣模式,即為當一個新的事件到來時,
     * ET 模式從 epoll_wait 調用獲取這個事件,可是如果這次沒有把這個事件
     * 對應的套接字緩存區處理完,在這個套接字沒有新的事件再次到來時,在 ET
     * 模式是無法再次從 epoll_wait 調用中獲取這個事件的;而 LT(即水平)模式
     * 相反,只要有一個事件對應的套接字緩沖區還有數據,就總能從 epoll_wait 中
     * 獲取這個事件。因此,LT 模式相對簡單,而在 ET 模式下事件發生時,若沒有
     * 徹底將緩沖區數據處理完,則會導致緩沖區中的用戶請求得不到響應。 */
    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        /* kqueue, epoll */
        
        /* 若當前讀事件不是活躍的且該讀事件還未准備就緒,則將該讀事件以
         * 邊緣模式添加到 epoll 中 */
        if (!rev->active && !rev->ready) {
            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)
                == NGX_ERROR)
            {
                return NGX_ERROR;
            }
        }

        return NGX_OK;

    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {

        /* select, poll, /dev/poll */

        if (!rev->active && !rev->ready) {
            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
                == NGX_ERROR)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }

        if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {
            if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)
                == NGX_ERROR)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }

    } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {

        /* event ports */

        if (!rev->active && !rev->ready) {
            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return NGX_ERROR;
            }

            return NGX_OK;
        }

        if (rev->oneshot && !rev->ready) {
            if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return NGX_ERROR;
            }

            return NGX_OK;
        }
    }

    /* iocp */

    return NGX_OK;
}

ngx_http_init_connection 函數主要是為當前的 HTTP 連接創建並初始化一個 ngx_http_connection_t 結構體,並使 ngx_connection_t 結構體的 data 指針指向該結構體,然后將當前連接的讀事件添加到定時器和 epoll 事件監控機制中,等待客戶端發送數據來觸發該讀事件.

當監聽到客戶端發送數據過來時,會調用該讀事件的 ngx_http_wait_request_handler 回調函數。

7. ngx_http_wait_request_handler

static void
ngx_http_wait_request_handler(ngx_event_t *rev)
{
    u_char                    *p;
    size_t                     size;
    ssize_t                    n;
    ngx_buf_t                 *b;
    ngx_connection_t          *c;
    ngx_http_connection_t     *hc;
    ngx_http_core_srv_conf_t  *cscf;

    /* 事件相關的對象。通常 data 都是指向 ngx_connection_t 連接對象。
     * 開啟文件異步 I/O 時,它可能會指向 ngx_event_aio_t 結構體 */
    c = rev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");

    /* 檢查該讀事件是否已經超時,若超時,則關閉該連接 */
    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        ngx_http_close_connection(c);
        return;
    }

    /* 標志位,為 1 時表示連接關閉 */
    if (c->close) {
        ngx_http_close_connection(c);
        return;
    }

    /* 由 ngx_http_init_connection 函數知,此時該 data 指針指向
     * ngx_http_connection_t 結構體 */
    hc = c->data;
    /* 獲取該 server{} 對應的配置項結構體 */
    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);

    size = cscf->client_header_buffer_size;

    /* 用於接收、緩存客戶端發來的字節流,每個事件消費模塊可自由決定從連接池中
     * 分配多大的空間給 buffer 這個接收緩存字段。例如,在 HTTP 模塊中,它的大小
     * 決定於 client_header_buffer_size 配置項 */
    b = c->buffer;

    /* 若沒有為當前連接的接收/發送緩存分配內存 */
    if (b == NULL) {
        /* 分配一個 size 大小的臨時緩存(表示該緩存中的數據在內存中且
         * 該緩存中的數據可以被修改) */
        b = ngx_create_temp_buf(c->pool, size);
        if (b == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        c->buffer = b;

    } else if (b->start == NULL) {

        b->start = ngx_palloc(c->pool, size);
        if (b->start == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        b->pos = b->start;
        b->last = b->start;
        b->end = b->last + size;
    }

    /* 調用接收字節流的回調函數 ngx_unix_recv 接收客戶端發送的數據 */
    n = c->recv(c, b->last, size);

    if (n == NGX_AGAIN) {

        if (!rev->timer_set) {
            ngx_add_timer(rev, c->listening->post_accept_timeout);
            ngx_reusable_connection(c, 1);
        }

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        /*
         * We are trying to not hold c->buffer's memory for an idle connection.
         */

        if (ngx_pfree(c->pool, b->start) == NGX_OK) {
            b->start = NULL;
        }

        return;
    }

    if (n == NGX_ERROR) {
        ngx_http_close_connection(c);
        return;
    }

    if (n == 0) {
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client closed connection");
        ngx_http_close_connection(c);
        return;
    }

    /* last 指向緩存中有效數據的末尾 */
    b->last += n;

    if (hc->proxy_protocol) {
        hc->proxy_protocol = 0;

        p = ngx_proxy_protocol_read(c, b->pos, b->last);

        if (p == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        b->pos = p;

        if (b->pos == b->last) {
            c->log->action = "waiting for request";
            b->pos = b->start;
            b->last = b->start;
            ngx_post_event(rev, &ngx_posted_events);
            return;
        }
    }

    c->log->action = "reading client request line";

    /* 將該連接從 reusable_connections_queue 可重用雙向鏈表中刪除 */
    ngx_reusable_connection(c, 0);

    /* 為當前客戶端連接創建並初始化一個 ngx_http_request_t 結構體
     * 並將 c->data 指向該結構體 */
    c->data = ngx_http_create_request(c);
    if (c->data == NULL) {
        ngx_http_close_connection(c);
        return;
    }

    /* 設置該讀事件的回調處理函數 */
    rev->handler = ngx_http_process_request_line;
    ngx_http_process_request_line(rev);
}

8. ngx_http_request_t

typedef struct ngx_http_request_s     ngx_http_request_t;

struct ngx_http_request_s {
    uint32_t                                signature;  /* "HTTP" */

    /* 
     * 這個請求對應的客戶端連接 
     */
    ngx_connection_t                       *connection;

    /* 
     * 存放指向所有的 HTTP 模塊的上下文結構體的指針數組 
     */
    void                                  **ctx;

    /* 
     * 存放請求對應的存放 main 級別配置結構體的指針數組 
     */
    void                                  **main_conf;

    /* 
     * 存放請求對應的存放srv級別配置結構體的指針數組 
     */
    void                                  **srv_conf;
    
    /* 
     * 存放請求對應的存放loc級別配置結構體的指針數組 
     */
    void                                  **loc_conf;

    /* 
     * 在接收完 HTTP 頭部,第一次在業務上處理 HTTP 請求時,HTTP 框架提供
     * 的處理方法是 ngx_http_process_request。但如果該方法無法一次處理完
     * 該請求的全部業務,在歸還控制權到 epoll 事件模塊后,該請求回調時,
     * 將通過 ngx_http_request_handler 方法來處理,而這個方法中對於可讀事
     * 件的處理就是調用 read_event_handler 處理請求,也就是說,HTTP 模塊希望
     * 在底層處理請求的讀事件時,重新實現 read_event_handler 方法 
     */
    ngx_http_event_handler_pt               read_event_handler;
    ngx_http_event_handler_pt               write_event_handler;

#if (NGX_HTTP_CACHE)
    ngx_http_cache_t                       *cache;
#endif
    
    ngx_http_upstream_t                    *upstream;
    ngx_array_t                            *upstream_states;

    /* 
     * 表示這個請求的內存池,在 ngx_http_free_request 方法中銷毀。
     * 它與 ngx_connection_t 中的內存池意義不同,當請求釋放時,TCP 
     * 連接可能並沒有關閉,這時請求的內存池會銷毀,但 ngx_connection_t 
     * 的內存池並不會銷毀 
     */
    ngx_pool_t                             *pool;
    /* 
     * 存儲讀取到的HTTP頭部數據 
     */
    ngx_buf_t                              *header_in;

    /* 
     * ngx_http_process_request_headers方法在接收、解析完HTTP請求的頭部后,會把解析
     * 完的每一個HTTP頭部加入到headers_in的headers鏈表中,同時會構造headers_in中的
     * 其他成員 
     */
    ngx_http_headers_in_t                   headers_in;

    /* 
     * HTTP模塊會把想要發送的HTTP響應信息放到headers_out中,期望HTTP框架將
     * headers_out中的成員序列化為HTTP響應包發送給用戶 
     */
    ngx_http_headers_out_t                  headers_out;

    /* 接收HTTP請求中包體的數據結構 */
    ngx_http_request_body_t                *request_body;

    /* 延遲關閉連接的時間 */
    time_t                                  lingering_time;

    /* 
     * 當前請求初始化時的時間。start_sec是格林威治時間1970年1月1日0:0:0到
     * 當前時間的秒數。如果這個請求是子請求,則該時間是子請求的生成時間;
     * 如果這個請求是用戶發來的請求,則是在建立起TCP連接后,第一次接收到
     * 可讀事件時的時間 
     */
    time_t                                  start_sec;
    /* 
     * 與start_sec配合使用,表示相對於start_sec秒的毫秒偏移量 
     */
    ngx_msec_t                              start_msec;

    /* 
     * 以下 9 個成員都是 ngx_http_process_request_lint 方法在接收、解析 
     * HTTP 請求行時解析出的信息 
     */
    ngx_uint_t                              method;
    ngx_uint_t                              http_version;

    ngx_str_t                               request_line;
    ngx_str_t                               uri;
    ngx_str_t                               args;
    ngx_str_t                               exten;
    ngx_str_t                               unparsed_uri;

    ngx_str_t                               method_name;
    ngx_str_t                               http_protocol;

    /* 
     * 表示需要發送給客戶端的HTTP響應。out中保存着由headers_out中序列化后的表示HTTP頭部
     * 的TCP流。在調用ngx_http_output_filter方法后,out中還會保存着待發送的HTTP包體,它是
     * 實現異步發送HTTP響應的關鍵 
     */
    ngx_chain_t                            *out;

    /* 
     * 當前請求即可能是用戶發來的請求,也可能是派生出的子請求,而main則標識一系列相關的
     * 派生子請求的原始請求,我們一般可通過main和當前請求的地址是否相等來判斷當前請求是
     * 否為用戶發來的原始請求 
     */
    ngx_http_request_t                     *main;

    /* 
     * 當前請求的父請求。注意,父請求未必是原始請求 
     */
    ngx_http_request_t                     *parent;
    ngx_http_postponed_request_t           *postponed;    
    ngx_http_post_subrequest_t             *post_subrequest;

    /* 
     * 所有的子請求都是通過posted_requests這個單鏈表來鏈接起來的,執行post子請求時調用
     * 的ngx_http_run_posted_requests方法就是通過遍歷該鏈表來執行子請求的 
     */
    ngx_http_posted_request_t              *posted_requests;

    /* 
     * 全局的ngx_http_phase_engine_t結構體中定義了一個ngx_http_phase_handler_t回調方法
     * 組成的數組,而phase_handler成員則與該數組配合使用,表示請求下次應當執行以
     * phase_handler 作為序號指定的數組中的回調方法。HTTP框架正是以這種方式把各個HTTP
     * 模塊集成起來處理請求的 
     */
    ngx_int_t                               phase_handler;

    /* 
     * 表示NGX_HTTP_CONTENT_PHASE階段提供給HTTP模塊處理請求的一種方式,content_handler指向
     * HTTP模塊實現的請求處理方法 
     */
    ngx_http_handler_pt                     content_handler;

    /* 
     * 在NGX_HTTP_ACCESS_PHASE階段需要判斷請求是否具有訪問權限時,通過access_code來傳遞HTTP
     * 模塊的handler回調方法的返回值,如果access_code為0,則表示請求具備訪問權限,反之則
     * 說明請求不具備訪問權限 
     */
    ngx_uint_t                              access_code;

    ngx_http_variable_value_t              *variables;

#if (NGX_PCRE)
    ngx_uint_t                              ncaptures;
    int                                    *captures;
    u_char                                 *captures_data;
#endif

    size_t                                  limit_rate;
    size_t                                  limit_rate_after;
    
    /* used to learn the Apache compatible response length without a header */
    size_t                                  header_size;

    /* 
     * HTTP請求的全部長度,包括HTTP包體 
     */
    off_t                                   request_length;

    ngx_uint_t                              err_status;

    ngx_http_connection_t                  *http_connection;
    ngx_http_v2_stream_t                   *stream;

    ngx_http_log_handler_pt                 log_handler;

    /* 
     * 在這個請求中如果打開了某些資源,並需要在請求結束時釋放,那么都需要在把定義
     * 的釋放資源方法添加到cleanup成員中 
     */
    ngx_http_cleanup_t                     *cleanup;

    /* 
     * 表示當前請求的引用次數。例如,在使用subrequest功能時,依附在這個請求上的子請求數目會
     * 返回到count上,每增加一個子請求,count數就要加1.其中任何一個子請求派生出新的子請求時,
     * 對應的原始請求(main指針指向的請求)的count值都要加1.又如,當我們接收HTTP包體時,由於
     * 這也是一個異步調用,所有count上也需要加1,這樣在結束請求時,就不會在count引用計數未
     * 清零時銷毀請求。
     * 在HTTP模塊中每進行一類新的操作,包括為一個請求添加新的事件,或者把一些已經由定時器、
     * epoll中移除的事件重新加入其中,都需要把這個請求的引用計數加1,這是因為需要讓HTTP
     * 框架知道,HTTP模塊對於該請求有獨立的異步處理機制,將由該HTTP模塊決定這個操作什么時候
     * 結束,防止在這個操作還未結束時HTTP框架卻把這個請求銷毀了 
     */
    unsigned                                count:16;
    unsigned                                subrequests:8;
    unsigned                                blocked:8;

    /* 標志位,為1時表示當前請求正在使用異步文件I/O */
    unsigned                                aio:1;

    unsigned                                http_state:4;

    /* URI with "/." and on Win32 with "//" */
    unsigned                                complex_uri:1;

    /* URI with "%" */
    unsigned                                quoted_uri:1;

    /* URI with "+" */
    unsigned                                plus_in_uri:1;

    /* URI with " " */
    unsigned                                space_in_uri:1;

    unsigned                                invalid_header:1;

    unsigned                                add_uri_to_alias:1;
    unsigned                                valid_location:1;
    unsigned                                valid_unparsed_uri:1;

    /* 標志位,為1時表示URL發生過rewrite重寫 */
    unsigned                                uri_changed:1;
    /* 表示使用rewrite重寫URL的次數。因為目前最多可以更改10次,所以uri_changes初始化
     * 為11,而每重寫URL一次就把uri_changes減1,一旦uri_changes等於0,則向用戶返回失敗*/
    unsigned                                uri_changes:4;

    unsigned                                request_body_in_single_buf:1;
    unsigned                                request_body_in_file_only:1;
    unsigned                                request_body_in_persistent_file:1;
    unsigned                                request_body_in_clean_file:1;
    unsigned                                request_body_file_group_access:1;
    unsigned                                request_body_file_log_level:3;
    unsigned                                request_body_no_buffering:1;

    unsigned                                subrequest_in_memory:1;
    unsigned                                waited:1;

#if (NGX_HTTP_CACHE)
    unsigned                                cached:1;
#endif

#if (NGX_HTTP_GZIP)
    unsigned                                gzip_tested:1;
    unsigned                                gzip_ok:1;
    unsigned                                gzip_vary:1;
#endif

    unsigned                                proxy:1;
    unsigned                                bypass_cache:1;
    unsigned                                no_cache:1;

    /*
     * instead of using the request context data in 
     * ngx_http_limit_conn_module and ngx_http_limit_req_module
     * we use the single bits in the request structure
     */
    unsigned                                limit_conn_set:1;
    unsigned                                limit_req_set:1;

    unsigned                                pipeline:1;
    unsigned                                chunked:1;
    unsigned                                header_only:1;
    unsigned                                expect_trailers:1;
    /* 標志位,為1時表示當前請求是keepalive請求 */
    unsigned                                keepalive:1;
    /* 延遲關閉標志位,為1時表示需要延遲關閉。例如,在接收完HTTP頭部時如果發現包體
     * 存在,該標志位會設為1,而放棄接收包體時會設為0 */
    unsigned                                lingering_close:1;

    /* 標志位,為1時表示正在丟棄HTTP請求中的包體 */
    unsigned                                discard_body:1;
    unsigned                                reading_body:1;
    /* 標志位,為1時表示請求的當前狀態是在做內部跳轉 */
    unsigned                                internal:1;
    unsigned                                error_page:1;
    unsigned                                filter_finalize:1;
    unsigned                                post_action:1;
    unsigned                                request_complete:1;
    unsigned                                request_output:1;
    /* 標志位,為1時表示發送給客戶端的HTTP響應頭部已經發送。在調用ngx_http_send_header方法
     * 后,若已經成功地啟動響應頭部發送流程,該標志位就會置為1,用來防止反復地發送頭部 */
    unsigned                                header_sent:1;
    unsigned                                expect_tested:1;
    unsigned                                root_tested:1;
    unsigned                                done:1;
    unsigned                                logged:1;

    /* 表示緩沖中是否有待發送內容的標志位 */
    unsigned                                buffered:4;
    
    unsigned                                main_filter_need_in_memory:1;
    unsigned                                filter_need_in_memory:1;
    unsigned                                filter_need_temporary:1;
    unsigned                                allow_ranges:1;
    unsigned                                subrequest_ranges:1;
    unsigned                                single_range:1;
    unsigned                                disable_not_modified:1;
    unsigned                                stat_reading:1;
    unsigned                                stat_writing:1;
    unsigned                                stat_processing:1;

    unsigned                                background:1;
    unsigned                                health_check:1;

    /* used to parse HTTP headers */

    /* 狀態機解析HTTP時使用state來表示當前的解析狀態 */
    ngx_uint_t                              state;

    ngx_uint_t                              header_hash;
    ngx_uint_t                              lowcase_index;
    u_char                                  lowcase_header[NGX_HTTP_LC_HEADER_LEN];

    u_char                                 *header_name_start;
    u_char                                 *header_name_end;
    u_char                                 *header_start;
    u_char                                 *header_end;

    /*
     * a memory that can be reused after parsing a request line 
     * via ngx_http_ephmeral_t
     */

    u_char                                 *uri_start;
    u_char                                 *uri_end;
    u_char                                 *uri_ext;
    u_char                                 *args_start;
    u_char                                 *request_start;
    u_char                                 *request_end;
    u_char                                 *method_end;
    u_char                                 *schema_start;
    u_char                                 *schema_end;
    u_char                                 *host_start;
    u_char                                 *host_end;
    u_char                                 *port_start;
    u_char                                 *port_end;

    unsigned                                http_minor:16;
    unsigned                                http_major:16;
};

9. ngx_http_create_request

ngx_http_request_t *
ngx_http_create_request(ngx_connection_t *c)
{
    ngx_pool_t                 *pool;
    ngx_time_t                 *tp;
    ngx_http_request_t         *r;
    ngx_http_log_ctx_t         *ctx;
    ngx_http_connection_t      *hc;
    ngx_http_core_srv_conf_t   *cscf;
    ngx_http_core_loc_conf_t   *clcf;
    ngx_http_core_main_conf_t  *cmcf;

    /* 處理請求的次數加 1 */
    c->requests++;

    /* 在該函數返回前,data 還是指向 ngx_http_connection_t 結構體 */
    hc = c->data;

    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);

    /* 為該客戶端請求分配一個內存池 */
    pool = ngx_create_pool(cscf->request_pool_size, c->log);
    if (pool == NULL) {
        return NULL;
    }

    r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));
    if (r == NULL) {
        ngx_destroy_pool(pool);
        return NULL;
    }
    
    /* 該請求的內存池,在 ngx_http_free_request 方法中銷毀。
     * 它與 ngx_connection_t 中的內存池意義不同,當請求釋放時,TCP 連接
     * 可能並沒有關閉,這時請求的內存池會銷毀,但 ngx_connection_t 的
     * 內存池並不會銷毀. */
    r->pool = pool;

    /* 代表當前 HTTP 連接 */
    r->http_connection = hc;
    r->signature = NGX_HTTP_MODULE;
    /* 指向這個請求對應的客戶端連接 */
    r->connection = c;

    /* 存放請求對應的存放 main 級別配置結構體的指針數組 */
    r->main_conf = hc->conf_ctx->main_conf;
    /* 存放請求對應的存放 srv 級別配置結構體的指針數組 */
    r->srv_conf = hc->conf_ctx->srv_conf;
    /* 存放請求對應的存放 loc 級別配置結構體的指針數組 */
    r->loc_conf = hc->conf_ctx->loc_conf;

    /* 在接收完 HTTP 頭部,第一次在業務上處理 HTTP 請求時,HTTP 框架提供的
     * 處理方法是 ngx_http_process_request。但如果該方法無法一次處理完該
     * 請求的全部業務,在歸還控制權到 epoll 事件模塊后,該請求回調時,
     * 將通過 ngx_http_request_handler 方法來處理,而這個方法中對於可讀
     * 事件的處理就是調用 read_event_handler 處理請求,也就是說,HTTP 模塊
     * 希望在底層處理請求的讀事件時,重新實現 read_event_handler 方法 */
    r->read_event_handler = ngx_http_block_reading;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    ngx_set_connection_log(r->connection, clcf->error_log);

    /* header_in: 存儲讀取到的 HTTP 頭部數據 */
    r->header_in = hc->busy ? hc->busy->buf : c->buffer;

    /* headers_out: HTTP 模塊會把想要發送的 HTTP 響應信息放到 headers_out 中,
     * 期望 HTTP 框架將 headers_out 中的成員序列化為 HTTP 響應包發送給用戶 */
    if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
                      sizeof(ngx_table_elt_t))
        != NGX_OK)
    {
        ngx_destroy_pool(r->pool);
        return NULL;
    }

    if (ngx_list_init(&r->headers_out.trailers, r->pool, 4,
                      sizeof(ngx_table_elt_t))
        != NGX_OK)
    {
        ngx_destroy_pool(r->pool);
        return NULL;
    }

    /* 存放指向所有的 HTTP 模塊的上下文結構體的指針數組 */
    r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
    if (r->ctx == NULL) {
        ngx_destroy_pool(r->pool);
        return NULL;
    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
                                        * sizeof(ngx_http_variable_value_t));
    if (r->variables == NULL) {
        ngx_destroy_pool(r->pool);
        return NULL;
    }

#if (NGX_HTTP_SSL)
    if (c->ssl) {
        r->main_filter_need_in_memory = 1;
    }
#endif

    /* 當前請求既可能是用戶發來的請求,也可能是派生出的子請求,而 main
     * 則標識一系列相關的派生子請求的原始請求,一般可通過 main 和當前
     * 請求的地址是否相等來判斷當前請求是否為用戶發來的原始請求 */
    r->main = r;
    /* 表示當前請求的引用次數。例如,在使用 subrequest 功能時,依附在
     * 這個請求上的子請求數目會返回到 count 上,每增加一個子請求,count
     * 數就要加 1. 其中任何一個子請求派生出新的子請求時,對應的原始請求
     *(main 指針指向的請求)的 count 值都要加 1。又如,當我們接收 HTTP 
     * 包體時,由於這也是一個異步調用,所有 count 上也需要加 1,這樣在結束
     * 請求時,就不會在 count 引用計數未清零時銷毀請求。
     *
     * 在 HTTP 模塊中每進行一類新的操作,包括為一個請求添加新的事件,或者把
     * 一些已經由定時器、epoll 中移除的事件重新加入其中,都需要把這個請求的
     * 引用計數加 1,這是因為需要讓 HTTP 框架知道,HTTP 模塊對於該請求有
     * 獨立的異步處理機制,將由該 HTTP 模塊決定這個操作什么時候結束,防止
     * 在這個操作還未結束時 HTTP 框架卻把這個請求銷毀了 */
    r->count = 1;

    tp = ngx_timeofday();
    /* 當前請求初始化時的時間。start_sec是格林威治時間1970年1月1日0:0:0到當前時間的秒數。
     * 如果這個請求是子請求,則該時間是子請求的生成時間;如果這個請求是用戶發來的請求,
     * 則是在建立起TCP連接后,第一次接收到可讀事件時的時間 */
    r->start_sec = tp->sec;
    /* 與start_sec配合使用,表示相對於start_sec秒的毫秒偏移量 */
    r->start_msec = tp->msec;

    r->method = NGX_HTTP_UNKNOWN;
    r->http_version = NGX_HTTP_VERSION_10;

    /* ngx_http_process_request_headers 方法在接收、解析完 HTTP 請求的
     * 頭部后,會把解析完的每一個HTTP頭部加入到 headers_in 的 headers 鏈表中,
     * 同時會構造 headers_in 中的其他成員 */
    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;

    /* 表示使用 rewrite 重寫 URL 的次數。因為目前最多可以更改 10 次,
     * 所以 uri_changes 初始化為 11,而每重寫 URL 一次就把 uri_changes 
     * 減 1,一旦 uri_changes 等於 0,則向用戶返回失敗 */
    r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
    /* 表示允許派生子請求的個數,當前最多可為 50,因此該值初始化為 51 */
    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

    return r;
}

9.1 ngx_list_init

初始化一個鏈表.

typedef struct ngx_list_part_s  ngx_list_part_t;

/* 該結構體抽象為一個 數組,擁有連續的內存 */
struct ngx_list_part_s {
    /* 
     * 指向數組的首地址 
     */
    void             *elts;
    /* 
     * 當前數組中元素個數
     */
    ngx_uint_t        nelts;
    /* 
     * 下一個鏈表元素 ngx_list_part_t 的地址 
     */
    ngx_list_part_t  *next;
};


/* 該結構體 ngx_list_t 描述整個鏈表 */
typedef struct {
    /*
     * 指向鏈表中最后一個數組元素
     */
    ngx_list_part_t  *last;
    
    /* 
     * 鏈表的首個數組元素
     */
    ngx_list_part_t   part;
    /*
     * ngx_list_part_t 數組中每個元素占用的空間大小,也就是用戶要
     * 存儲的一個數據所占用的字節數必須小於或等於 size 
     */
    size_t            size;
    /*
     * 鏈表的數組元素一旦分配后是不可更改的。nalloc 表示每個 ngx_list_part_t
     * 數組的容量,即最多可以存儲多少個數據
     */
    ngx_uint_t        nalloc;
    /*
     * 鏈表中管理內存分配的內存池對象.
     */
    ngx_pool_t       *pool;
} ngx_list_t;

static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
    /* 為該數組 elts 分配 n 個元素大小為 size 的內存 */
    list->part.elts = ngx_palloc(pool, n * size);
    if (list->part.elts == NULL) {
        return NGX_ERROR;
    }
    
    /* 剛創建時,該數組中元素個數為 0 */
    list->part.nelts = 0;
    /* 設置下一個鏈表為 NULL */
    list->part.next = NULL;
    list->last = &list->part;
    /* 該鏈表中的每個數組元素的大小 */
    list->size = size;
    /* 鏈表的數組元素一旦分配后是不可更改的。nalloc 表示每個 
     * ngx_list_part_t 數組的容量,即最多可以存儲多少個數據 */
    list->nalloc = n;
    /* 鏈表中管理內存分配的內存池對象。 */
    list->pool = pool;

    return NGX_OK;
}

10. 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;

    /* rev->data 指向當前客戶端連接對象 ngx_connection_t */
    c = rev->data;
    /* 由前面知,當接收到客戶端的請求數據並為該請求創建一個 
     * ngx_http_request_t 結構體后,c->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;
    }

    rc = NGX_AGAIN;

    for ( ;; ) {

        if (rc == NGX_AGAIN) {
            /* 讀取客戶端的請求數據到 header_in 指向的緩存中,若該緩存中
             * 已有數據,則直接返回該緩存中數據的大小 */
            n = ngx_http_read_request_header(r);

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

        /* 解析請求行 */
        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);

            /* 該 HTTP 的方法名 */
            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 */
            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;
            }


            /* 初始化該 header_in.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;
            }

            c->log->action = "reading client request headers";

            /* 上面解析完請求行后,開始處理請求的頭部數據 */
            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);

            return;
        }

        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]);

            if (rc == NGX_HTTP_PARSE_INVALID_VERSION) {
                ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED);

            } else {
                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            }

            return;
        }

        /* NGX_AGAIN: a request line parsing is still incomplete */

        /* ngx_http_parse_reqeust_line 方法返回NGX_AGAIN,則表示需要接收更多的字符流,
         * 這時需要對header_in緩沖區做判斷,檢查是否還有空閑的內存,如果還有未使用的
         * 內存可以繼續接收字符流,否則調用ngx_http_alloc_large_header_buffer方法
         * 分配更多的接收緩沖區。到底是分配多大?這有nginx.conf文件中的
         * large_client_header_buffers 配置項指定 */
        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;
            }
        }
    }
}

10.1 ngx_http_read_request_header

讀取請求頭,將數據保存到 header_in 指向的緩存中。

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接收緩沖區中是否有未解析的字符流,若有則直接返回,
     * 否則調用封裝的recv方法把Linux內核套接字緩沖區中的TCP流復制到header_in
     * 緩沖區中. */
    if (n > 0) {
        /* 若 header_in 緩存中已經有數據了,則直接返回 */
        return n;
    }

    /* 若 header_in 緩存中還沒有從該連接的socket套接字接收到
     * 數據,則下面開始接收數據到 header_in 中 */
    
    /* 若該讀事件已經准備好允許消費者模塊處理這個事件 */
    if (rev->ready) {
        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);
        }

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

10.2 ngx_http_parse_request_line

ngx_int_t
ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
{
    u_char  c, ch, *p, *m;
    enum {
        sw_start = 0,
        sw_method,
        sw_spaces_before_uri,
        sw_schema,
        sw_schema_slash,
        sw_schema_slash_slash,
        sw_host_start,
        sw_host,
        sw_host_end,
        sw_host_ip_literal,
        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;

    /* 最開始時,state 為 0 */
    state = r->state;

    /* 一個個字符的讀取 */
    for (p = b->pos; p < b->last; p++) {
        ch = *p;

        switch (state) {

        /* HTTP methods: GET, HEAD, POST */
        case sw_start:
            r->request_start = p;

            if (ch == CR || ch == LF) {
                break;
            }

            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
                return NGX_HTTP_PARSE_INVALID_METHOD;
            }

            state = sw_method;
            break;

        case sw_method:
            /* 循環讀,直到遇到空格 */
            if (ch == ' ') {
                /* 這里讀取到 HTTP 請求的方法,GET、POST 等 */
                r->method_end = p - 1;
                m = r->request_start;

                /* 通過讀取到的 HTTP 方法的長度值,確定該客戶端請求的方法 */
                switch (p - m) {

                case 3:
                    if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
                        /* GET 方法 */
                        r->method = NGX_HTTP_GET;
                        break;
                    }

                    if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
                        /* PUT 方法 */
                        r->method = NGX_HTTP_PUT;
                        break;
                    }

                    break;

                case 4:
                    if (m[1] == 'O') {

                        if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
                            r->method = NGX_HTTP_POST;
                            break;
                        }

                        if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
                            r->method = NGX_HTTP_COPY;
                            break;
                        }

                        if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
                            r->method = NGX_HTTP_MOVE;
                            break;
                        }

                        if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
                            r->method = NGX_HTTP_LOCK;
                            break;
                        }

                    } else {

                        if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
                            r->method = NGX_HTTP_HEAD;
                            break;
                        }
                    }

                    break;

                case 5:
                    if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
                        r->method = NGX_HTTP_MKCOL;
                        break;
                    }

                    if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
                        r->method = NGX_HTTP_PATCH;
                        break;
                    }

                    if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
                        r->method = NGX_HTTP_TRACE;
                        break;
                    }

                    break;

                case 6:
                    if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
                        r->method = NGX_HTTP_DELETE;
                        break;
                    }

                    if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
                        r->method = NGX_HTTP_UNLOCK;
                        break;
                    }

                    break;

                case 7:
                    if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
                    {
                        r->method = NGX_HTTP_OPTIONS;
                    }

                    break;

                case 8:
                    if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
                    {
                        r->method = NGX_HTTP_PROPFIND;
                    }

                    break;

                case 9:
                    if (ngx_str9cmp(m,
                            'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
                    {
                        r->method = NGX_HTTP_PROPPATCH;
                    }

                    break;
                }
                
                /* 確定該 HTTP 請求的方法后,進入下一個階段 */
                state = sw_spaces_before_uri;
                break;
            }

            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
                return NGX_HTTP_PARSE_INVALID_METHOD;
            }

            break;

        /* space* before URI */
        case sw_spaces_before_uri:

            if (ch == '/') {
                /* 開始解析 uri */
                r->uri_start = p;
                state = sw_after_slash_in_uri;
                break;
            }

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'z') {
                r->schema_start = p;
                state = sw_schema;
                break;
            }

            switch (ch) {
            case ' ':
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_schema:

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'z') {
                break;
            }

            switch (ch) {
            case ':':
                r->schema_end = p;
                state = sw_schema_slash;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_schema_slash:
            switch (ch) {
            case '/':
                state = sw_schema_slash_slash;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_schema_slash_slash:
            switch (ch) {
            case '/':
                state = sw_host_start;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_host_start:

            r->host_start = p;

            if (ch == '[') {
                state = sw_host_ip_literal;
                break;
            }

            state = sw_host;

            /* fall through */

        case sw_host:

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'z') {
                break;
            }

            if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
                break;
            }

            /* fall through */

        case sw_host_end:

            r->host_end = p;

            switch (ch) {
            case ':':
                state = sw_port;
                break;
            case '/':
                r->uri_start = p;
                state = sw_after_slash_in_uri;
                break;
            case ' ':
                /*
                 * use single "/" from request line to preserve pointers,
                 * if request line will be copied to large client buffer
                 */
                r->uri_start = r->schema_end + 1;
                r->uri_end = r->schema_end + 2;
                state = sw_host_http_09;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_host_ip_literal:

            if (ch >= '0' && ch <= '9') {
                break;
            }

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'z') {
                break;
            }

            switch (ch) {
            case ':':
                break;
            case ']':
                state = sw_host_end;
                break;
            case '-':
            case '.':
            case '_':
            case '~':
                /* unreserved */
                break;
            case '!':
            case '$':
            case '&':
            case '\'':
            case '(':
            case ')':
            case '*':
            case '+':
            case ',':
            case ';':
            case '=':
                /* sub-delims */
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_port:
            if (ch >= '0' && ch <= '9') {
                break;
            }

            switch (ch) {
            case '/':
                r->port_end = p;
                r->uri_start = p;
                state = sw_after_slash_in_uri;
                break;
            case ' ':
                r->port_end = p;
                /*
                 * use single "/" from request line to preserve pointers,
                 * if request line will be copied to large client buffer
                 */
                r->uri_start = r->schema_end + 1;
                r->uri_end = r->schema_end + 2;
                state = sw_host_http_09;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        /* space+ after "http://host[:port] " */
        case sw_host_http_09:
            switch (ch) {
            case ' ':
                break;
            case CR:
                r->http_minor = 9;
                state = sw_almost_done;
                break;
            case LF:
                r->http_minor = 9;
                goto done;
            case 'H':
                r->http_protocol.data = p;
                state = sw_http_H;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;


        /* check "/.", "//", "%", and "\" (Win32) in URI */
        case sw_after_slash_in_uri:

            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
                state = sw_check_uri;
                break;
            }

            switch (ch) {
            case ' ':
                r->uri_end = p;
                state = sw_check_uri_http_09;
                break;
            case CR:
                r->uri_end = p;
                r->http_minor = 9;
                state = sw_almost_done;
                break;
            case LF:
                r->uri_end = p;
                r->http_minor = 9;
                goto done;
            case '.':
                r->complex_uri = 1;
                state = sw_uri;
                break;
            case '%':
                r->quoted_uri = 1;
                state = sw_uri;
                break;
            case '/':
                r->complex_uri = 1;
                state = sw_uri;
                break;
#if (NGX_WIN32)
            case '\\':
                r->complex_uri = 1;
                state = sw_uri;
                break;
#endif
            case '?':
                r->args_start = p + 1;
                state = sw_uri;
                break;
            case '#':
                r->complex_uri = 1;
                state = sw_uri;
                break;
            case '+':
                r->plus_in_uri = 1;
                break;
            case '\0':
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            default:
                state = sw_check_uri;
                break;
            }
            break;

        /* check "/", "%" and "\" (Win32) in URI */
        case sw_check_uri:

            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
                break;
            }

            switch (ch) {
            case '/':
#if (NGX_WIN32)
                if (r->uri_ext == p) {
                    r->complex_uri = 1;
                    state = sw_uri;
                    break;
                }
#endif
                r->uri_ext = NULL;
                state = sw_after_slash_in_uri;
                break;
            case '.':
                r->uri_ext = p + 1;
                break;
            case ' ':
                r->uri_end = p;
                state = sw_check_uri_http_09;
                break;
            case CR:
                r->uri_end = p;
                r->http_minor = 9;
                state = sw_almost_done;
                break;
            case LF:
                r->uri_end = p;
                r->http_minor = 9;
                goto done;
#if (NGX_WIN32)
            case '\\':
                r->complex_uri = 1;
                state = sw_after_slash_in_uri;
                break;
#endif
            case '%':
                r->quoted_uri = 1;
                state = sw_uri;
                break;
            case '?':
                r->args_start = p + 1;
                state = sw_uri;
                break;
            case '#':
                r->complex_uri = 1;
                state = sw_uri;
                break;
            case '+':
                r->plus_in_uri = 1;
                break;
            case '\0':
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        /* space+ after URI */
        case sw_check_uri_http_09:
            switch (ch) {
            case ' ':
                break;
            case CR:
                r->http_minor = 9;
                state = sw_almost_done;
                break;
            case LF:
                r->http_minor = 9;
                goto done;
            case 'H':
                r->http_protocol.data = p;
                state = sw_http_H;
                break;
            default:
                r->space_in_uri = 1;
                state = sw_check_uri;
                p--;
                break;
            }
            break;


        /* URI */
        case sw_uri:

            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
                break;
            }

            switch (ch) {
            case ' ':
                r->uri_end = p;
                state = sw_http_09;
                break;
            case CR:
                r->uri_end = p;
                r->http_minor = 9;
                state = sw_almost_done;
                break;
            case LF:
                r->uri_end = p;
                r->http_minor = 9;
                goto done;
            case '#':
                r->complex_uri = 1;
                break;
            case '\0':
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        /* space+ after URI */
        case sw_http_09:
            switch (ch) {
            case ' ':
                break;
            case CR:
                r->http_minor = 9;
                state = sw_almost_done;
                break;
            case LF:
                r->http_minor = 9;
                goto done;
            case 'H':
                r->http_protocol.data = p;
                state = sw_http_H;
                break;
            default:
                r->space_in_uri = 1;
                state = sw_uri;
                p--;
                break;
            }
            break;

        case sw_http_H:
            switch (ch) {
            case 'T':
                state = sw_http_HT;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_http_HT:
            switch (ch) {
            case 'T':
                state = sw_http_HTT;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_http_HTT:
            switch (ch) {
            case 'P':
                state = sw_http_HTTP;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        case sw_http_HTTP:
            switch (ch) {
            case '/':
                state = sw_first_major_digit;
                break;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        /* first digit of major HTTP version */
        case sw_first_major_digit:
            if (ch < '1' || ch > '9') {
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }

            r->http_major = ch - '0';

            if (r->http_major > 1) {
                return NGX_HTTP_PARSE_INVALID_VERSION;
            }

            state = sw_major_digit;
            break;

        /* major HTTP version or dot */
        case sw_major_digit:
            if (ch == '.') {
                state = sw_first_minor_digit;
                break;
            }

            if (ch < '0' || ch > '9') {
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }

            r->http_major = r->http_major * 10 + ch - '0';

            if (r->http_major > 1) {
                return NGX_HTTP_PARSE_INVALID_VERSION;
            }

            break;

        /* first digit of minor HTTP version */
        case sw_first_minor_digit:
            if (ch < '0' || ch > '9') {
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }

            r->http_minor = ch - '0';
            state = sw_minor_digit;
            break;

        /* minor HTTP version or end of request line */
        case sw_minor_digit:
            if (ch == CR) {
                state = sw_almost_done;
                break;
            }

            if (ch == LF) {
                goto done;
            }

            if (ch == ' ') {
                state = sw_spaces_after_digit;
                break;
            }

            if (ch < '0' || ch > '9') {
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }

            if (r->http_minor > 99) {
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }

            r->http_minor = r->http_minor * 10 + ch - '0';
            break;

        case sw_spaces_after_digit:
            switch (ch) {
            case ' ':
                break;
            case CR:
                state = sw_almost_done;
                break;
            case LF:
                goto done;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
            break;

        /* end of request line */
        case sw_almost_done:
            r->request_end = p - 1;
            switch (ch) {
            case LF:
                goto done;
            default:
                return NGX_HTTP_PARSE_INVALID_REQUEST;
            }
        }
    }

    b->pos = p;
    r->state = state;

    return NGX_AGAIN;

done:
    
    /* 解析請求行結束 */
    b->pos = p + 1;

    if (r->request_end == NULL) {
        r->request_end = p;
    }

    r->http_version = r->http_major * 1000 + r->http_minor;
    r->state = sw_start;

    if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
        return NGX_HTTP_PARSE_INVALID_09_METHOD;
    }

    return NGX_OK;
}

10.3 ngx_http_process_request_uri

解析請求的 uri。

ngx_int_t
ngx_http_process_request_uri(ngx_http_request_t *r)
{
    ngx_http_core_srv_conf_t  *cscf;

    if (r->args_start) {
        r->uri.len = r->args_start - 1 - r->uri_start;
    } else {
        /* 該請求 uri 中沒有參數時的長度 */
        r->uri.len = r->uri_end - r->uri_start;
    }

    /* 若該 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 NGX_ERROR;
        }

        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) {
            r->uri.len = 0;

            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                          "client sent invalid request");
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            return NGX_ERROR;
        }

    } else {
        r->uri.data = r->uri_start;
    }

    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;

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

#if (NGX_WIN32)
    {
    u_char  *p, *last;

    p = r->uri.data;
    last = r->uri.data + r->uri.len;

    while (p < last) {

        if (*p++ == ':') {

            /*
             * this check covers "::$data", "::$index_allocation" and
             * ":$i30:$index_allocation"
             */

            if (p < last && *p == '$') {
                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                              "client sent unsafe win32 URI");
                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
                return NGX_ERROR;
            }
        }
    }

    p = r->uri.data + r->uri.len - 1;

    while (p > r->uri.data) {

        if (*p == ' ') {
            p--;
            continue;
        }

        if (*p == '.') {
            p--;
            continue;
        }

        break;
    }

    if (p != r->uri.data + r->uri.len - 1) {
        r->uri.len = p + 1 - r->uri.data;
        ngx_http_set_exten(r);
    }

    }
#endif

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http uri: \"%V\"", &r->uri);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http args: \"%V\"", &r->args);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http exten: \"%V\"", &r->exten);

    return NGX_OK;
}

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

    /* 在該循環中,將 HTTP 請求頭一個個的解析出來,並添加到 
     * headers_in.header 鏈表中 */
    for ( ;; ) {

        if (rc == NGX_AGAIN) {

            /* 若當前 heder_in 指向的緩存已全部使用完,則需要分配更多的內存 */
            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 指向的緩存中仍然有未處理的數據,則
             * 直接返回,否則需要從 socket 中讀取數據 */
            n = ngx_http_read_request_header(r);

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

        /* 該函數是將頭部數據解析出一行 */
        rc = ngx_http_parse_header_line(r, r->header_in,
                                        cscf->underscores_in_headers);

        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 */
            
            /* 在headers_in.headers鏈表中取出一個空閑位置 */
            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) {
                /* r->lowcase_header 存放的上面解析出來的 h->key.data 
                 * 的小寫字符串  */
                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

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

            /* 在 headers_in_hash 指向的 hash 表中尋找是否與該 lowcase_key 
             * 相同的項*/
            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;
        }

        /* 若解析HTTP的頭部結束 */
        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;

            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;

            /* 對解析后的 HTTP 頭部字段的一些處理 */
            rc = ngx_http_process_request_header(r);

            if (rc != NGX_OK) {
                return;
            }

            /* 在解析並處理HTTP的頭部數據后,開始處理該 HTTP 請求 */
            ngx_http_process_request(r);

            return;
        }

        if (rc == NGX_AGAIN) {

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

            continue;
        }

        /* rc == 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;
    }
}

10.5 ngx_http_process_request

void
ngx_http_process_request(ngx_http_request_t *r)
{
    ngx_connection_t  *c;

    c = r->connection;

#if (NGX_HTTP_SSL)

    if (r->http_connection->ssl) {
        long                      rc;
        X509                     *cert;
        ngx_http_ssl_srv_conf_t  *sscf;

        if (c->ssl == NULL) {
            ngx_log_error(NGX_LOG_INFO, c->log, 0,
                          "client sent plain HTTP request to HTTPS port");
            ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
            return;
        }

        sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);

        if (sscf->verify) {
            rc = SSL_get_verify_result(c->ssl->connection);

            if (rc != X509_V_OK
                && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
            {
                ngx_log_error(NGX_LOG_INFO, c->log, 0,
                              "client SSL certificate verify error: (%l:%s)",
                              rc, X509_verify_cert_error_string(rc));

                ngx_ssl_remove_cached_session(sscf->ssl.ctx,
                                       (SSL_get0_session(c->ssl->connection)));

                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
                return;
            }

            if (sscf->verify == 1) {
                cert = SSL_get_peer_certificate(c->ssl->connection);

                if (cert == NULL) {
                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                  "client sent no required SSL certificate");

                    ngx_ssl_remove_cached_session(sscf->ssl.ctx,
                                       (SSL_get0_session(c->ssl->connection)));

                    ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
                    return;
                }

                X509_free(cert);
            }
        }
    }

#endif

    /* 由於現在已經開始准備調用各 HTTP 模塊處理請求了,不再存在
     * 接收 HTTP 請求頭部超時的問題,因此需要從定時器中將當前
     * 連接的讀事件移除 */
    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

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

    /* 設置該讀、寫事件的回調函數 */
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    /* 設置 ngx_http_request_t 結構體的 read_event_handler 方法為
     * ngx_http_block_reading。當再次有讀事件到來時,這個方法可以
     * 認為不做任何事,它的意義在於,目前已經開始處理 HTTP 請求,
     * 除非某個 HTTP 模塊重新設置了 read_event_handler 方法,否則
     * 任何讀事件都將得不到處理,也可以認為讀事件被阻塞了 */
    r->read_event_handler = ngx_http_block_reading;

    
    ngx_http_handler(r);

    ngx_http_run_posted_requests(c);
}

10.6 ngx_http_handler

void
ngx_http_handler(ngx_http_request_t *r)
{
    ngx_http_core_main_conf_t  *cmcf;

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

    /* 如果 internal 標志位為 1,則表示當前需要做內部跳轉,將要把
     * 結構體中的 phase_handler 序號置為 server_rewrite_index. */
    if (!r->internal) {
        /* 當 internal 標志位為 0 時,表示不需要重定向(如剛開始處理請求時),
         * 將 phase_handler 序號置為 0,意味着從 ngx_http_phase_engine_t 指定
         * 數組的第一個回調方法開始執行 */
        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:
            /* 標志位,為 1 表示當前請求是 keepalive 請求,即長連接 */
            r->keepalive = 1;
            break;
        }

        /* 延遲關閉標志位,為 1 時表示需要延遲關閉。例如,在接收完
         * HTTP 頭部時如果發現包體存在,該標志位為設為 1,而放棄接收
         * 包體時會設為 0 */
        r->lingering_close = (r->headers_in.content_length_n > 0
                              || r->headers_in.chunked);
        
        /* 置為 0,表示從 ngx_http_phase_engine_t 指定數組的第一個回調
         * 方法開始執行 */
        r->phase_handler = 0;

    } else {
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
        /* 這里,把phase_handler序號設為server_rewrite_index,這意味着
         * 無論之前執行到哪一個階段,馬上都要重新從NGX_HTTP_SERVER_REWRITE_PHASE
         * 階段開始再次執行,這是Nginx的請求可以反復rewrite重定向的基礎 */
        r->phase_handler = cmcf->phase_engine.server_rewrite_index;
    }

    r->valid_location = 1;
#if (NGX_HTTP_GZIP)
    r->gzip_tested = 0;
    r->gzip_ok = 0;
    r->gzip_vary = 0;
#endif

    r->write_event_handler = ngx_http_core_run_phases;
    /* 開始執行 HTTP 請求的各個階段 */
    ngx_http_core_run_phases(r);
}

10.7 ngx_http_core_run_phases

/* ngx_http_phase_engine_t結構體就是所有的ngx_http_phase_handler_t組成的數組 */
typedef struct {
    /* handlers是由ngx_http_phase_handler_t構成的數組首地址,它表示一個請求可能
     * 經歷的所有ngx_http_handler_pt處理方法 */
    ngx_http_phase_handler_t        *handlers;

    /* 表示NGX_HTTP_SERVER_REWRITE_PHASE階段第1個ngx_http_phase_handler_t處理方法
     * 在handlers數組中的序號,用於在執行HTTP請求的任何階段中快速跳轉到
     * NGX_HTTP_SERVER_REWRITE_PHASE階段處理請求 */
    ngx_uint_t                       server_rewrite_index;

    /* 表示NGX_HTTP_REWRITE_PHASE階段第1個ngx_http_phase_handler_t處理方法
     * 在handlers數組中的序號,用於在執行HTTP請求的任何階段中快速跳轉到
     * NGX_HTTP_REWRITE_PHASE階段處理請求 */
    ngx_uint_t                       location_rewrite_index;
}ngx_http_phase_engine_t;

void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
    ngx_int_t                   rc;
    ngx_http_phase_handler_t   *ph;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    
    /* handlers 是由 ngx_http_phase_handler_t 構成的數組首地址,它表示
     * 一個請求可能經歷的所有 ngx_http_handler_pt 處理方法 */
    ph = cmcf->phase_engine.handlers;

    /*
     * 在處理到某一個 HTTP 階段時,HTTP 框架將會在 checker 方法已實現的前提下
     * 首先調用 checket 方法來處理請求,而不會直接調用任何階段中的handler方法,
     * 只有在checket方法中才會去調用handler方法。因此,事實上所有的checker方法
     * 都是由框架中的 ngx_http_core_module 模塊實現的,且普通的 HTTP 模塊無法
     * 重定義 checket 方法 */
    while (ph[r->phase_handler].checker) {

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

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

下面開始執行 HTTP 的各個階段,此時會調用到各個 HTTP 模塊介入到該階段的回調函數。

首先,基於當前配置以及訪問靜態網頁的請求,Nginx 會依次執行以下 HTTP 的階段(雖然划分了 11 個階段,但不會每個階段都執行到).

注意,有一個原則,就是若該階段實現有 checker 函數的話,會首先調用 checker 函數來處理請求,而不會直接調用任何階段中的 handler 方法,只有在 checker 函數中才會去調用 handler 方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM