<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    nginx的請求接收流程(二)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    在ngx_http_process_request_line函數中,解析完請求行之后,如果請求行的uri里面包含了域名部分,則將其保持在請求結構的headers_in成員的server字段,headers_in用來保存所有請求頭,它的類型為ngx_http_headers_in_t:

    [cpp] view plaincopy  
    1. <span style="font-size: 18px; ">typedef struct {  
    2.     ngx_list_t                        headers;  
    3.   
    4.     ngx_table_elt_t                  *host;  
    5.     ngx_table_elt_t                  *connection;  
    6.     ngx_table_elt_t                  *if_modified_since;  
    7.     ngx_table_elt_t                  *if_unmodified_since;  
    8.     ngx_table_elt_t                  *user_agent;  
    9.     ngx_table_elt_t                  *referer;  
    10.     ngx_table_elt_t                  *content_length;  
    11.     ngx_table_elt_t                  *content_type;  
    12.   
    13.     ngx_table_elt_t                  *range;  
    14.     ngx_table_elt_t                  *if_range;  
    15.   
    16.     ngx_table_elt_t                  *transfer_encoding;  
    17.     ngx_table_elt_t                  *expect;  
    18.   
    19. #if (NGX_HTTP_GZIP)  
    20.     ngx_table_elt_t                  *accept_encoding;  
    21.     ngx_table_elt_t                  *via;  
    22. #endif  
    23.   
    24.     ngx_table_elt_t                  *authorization;  
    25.   
    26.     ngx_table_elt_t                  *keep_alive;  
    27.   
    28. #if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)  
    29.     ngx_table_elt_t                  *x_forwarded_for;  
    30. #endif  
    31.   
    32. #if (NGX_HTTP_REALIP)  
    33.     ngx_table_elt_t                  *x_real_ip;  
    34. #endif  
    35.   
    36. #if (NGX_HTTP_HEADERS)  
    37.     ngx_table_elt_t                  *accept;  
    38.     ngx_table_elt_t                  *accept_language;  
    39. #endif  
    40.   
    41. #if (NGX_HTTP_DAV)  
    42.     ngx_table_elt_t                  *depth;  
    43.     ngx_table_elt_t                  *destination;  
    44.     ngx_table_elt_t                  *overwrite;  
    45.     ngx_table_elt_t                  *date;  
    46. #endif  
    47.   
    48.     ngx_str_t                         user;  
    49.     ngx_str_t                         passwd;  
    50.     ngx_array_t                       cookies;  
    51.   
    52.     ngx_str_t                         server;  
    53.     off_t                             content_length_n;  
    54.     time_t                            keep_alive_n;  
    55.   
    56.     unsigned                          connection_type:2;  
    57.     unsigned                          msie:1;  
    58.     unsigned                          msie6:1;  
    59.     unsigned                          opera:1;  
    60.     unsigned                          gecko:1;  
    61.     unsigned                          chrome:1;  
    62.     unsigned                          safari:1;  
    63.     unsigned                          konqueror:1;  
    64. } ngx_http_headers_in_t;</span>  

    接著,該函數會檢查進來的請求是否使用的是http0.9,如果是的話則使用從請求行里得到的域名,調用ngx_http_find_virtual_server()函數來查找用來處理該請求的虛擬服務器配置,之前通過端口和地址找到的默認配置不再使用,找到相應的配置之后,則直接調用ngx_http_process_request()函數處理該請求,因為http0.9是最原始的http協議,它里面沒有定義任何請求頭,顯然就不需要讀取請求頭的操作。

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">            if (r->host_start && r->host_end) {  
    2.   
    3.                 host = r->host_start;  
    4.                 n = ngx_http_validate_host(r, &host,  
    5.                                            r->host_end - r->host_start, 0);  
    6.   
    7.                 if (n == 0) {  
    8.                     ngx_log_error(NGX_LOG_INFO, c->log, 0,  
    9.                                   "client sent invalid host in request line");  
    10.                     ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);  
    11.                     return;  
    12.                 }  
    13.   
    14.                 if (n < 0) {  
    15.                     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);  
    16.                     return;  
    17.                 }  
    18.   
    19.                 r->headers_in.server.len = n;  
    20.                 r->headers_in.server.data = host;  
    21.             }  
    22.   
    23.             if (r->http_version < NGX_HTTP_VERSION_10) {  
    24.   
    25.                 if (ngx_http_find_virtual_server(r, r->headers_in.server.data,  
    26.                                                  r->headers_in.server.len)  
    27.                     == NGX_ERROR)  
    28.                 {  
    29.                     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);  
    30.                     return;  
    31.                 }  
    32.   
    33.                 ngx_http_process_request(r);  
    34.                 return;  
    35.             }</span>  

    當然,如果是1.0或者更新的http協議,接下來要做的就是讀取請求頭了,首先nginx會為請求頭分配空間,ngx_http_headers_in_t結構的headers字段為一個鏈表結構,它被用來保存所有請求頭,初始為它分配了20個節點,每個節點的類型為ngx_table_elt_t,保存請求頭的name/value值對,還可以看到ngx_http_headers_in_t結構有很多類型為ngx_table_elt_t*的指針成員,而且從它們的命名可以看出是一些常見的請求頭名字,nginx對這些常用的請求頭在ngx_http_headers_in_t結構里面保存了一份引用,后續需要使用的話,可以直接通過這些成員得到,另外也事先為cookie頭分配了2個元素的數組空間,做完這些內存準備工作之后,該請求對應的讀事件結構的處理函數被設置為ngx_http_process_request_headers,并隨后馬上調用了該函數。

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">            if (ngx_list_init(&r->headers_in.headers, r->pool, 20,  
    2.                               sizeof(ngx_table_elt_t))  
    3.                 != NGX_OK)  
    4.             {  
    5.                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);  
    6.                 return;  
    7.             }  
    8.   
    9.             if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,  
    10.                                sizeof(ngx_table_elt_t *))  
    11.                 != NGX_OK)  
    12.             {  
    13.                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);  
    14.                 return;  
    15.             }  
    16.   
    17.             c->log->action = "reading client request headers";  
    18.   
    19.             rev->handler = ngx_http_process_request_headers;  
    20.             ngx_http_process_request_headers(rev);</span>  

    ngx_http_process_request_headers函數循環的讀取所有的請求頭,并保存和初始化和請求頭相關的結構,下面詳細分析一下該函數:
    因為nginx對讀取請求頭有超時限制,ngx_http_process_request_headers函數作為讀事件處理函數,一并處理了超時事件,如果讀超時了,nginx直接給該請求返回408錯誤:

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">    if (rev->timedout) {  
    2.         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");  
    3.         c->timedout = 1;  
    4.         ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);  
    5.         return;  
    6.     }</span>  

    讀取和解析請求頭的邏輯和處理請求行差不多,總的流程也是循環的調用ngx_http_read_request_header()函數讀取數據,然后再調用一個解析函數來從讀取的數據中解析請求頭,直到解析完所有請求頭,或者發生解析錯誤為主。當然由于涉及到網絡io,這個流程可能發生在多個io事件的上下文中。

    接著來細看該函數,先調用了ngx_http_read_request_header()函數讀取數據,如果當前連接并沒有數據過來,再直接返回,等待下一次讀事件到來,如果讀到了一些數據則調用ngx_http_parse_header_line()函數來解析,同樣的該解析函數實現為一個有限狀態機,邏輯很簡單,只是根據http協議的解析一個請求頭的name/vale對,每次調用該函數最多解析出一個請求頭,該函數返回4種不同返回值,表示不同解析結果:

    1,返回NGX_OK,表示解析出了一行請求頭,這時還要判斷解析出的請求頭名字里面是否有非法字符,名字里面合法的字符包括字母,數字和連字符(-),另外如果設置了underscores_in_headers指令為on,則下劃線也是合法字符,但是nginx默認下劃線不合法,當請求頭里面包含了非法的字符,nginx默認只是忽略這一行請求頭;如果一切都正常,nginx會將該請求頭及請求頭名字的hash值保存在請求結構體的headers_in成員的headers鏈表,而且對于一些常見的請求頭,如Host,Connection,nginx采用了類似于配置指令的方式,事先給這些請求頭分配了一個處理函數,當解析出一個請求頭時,會檢查該請求頭是否有設置處理函數,有的話則調用之,nginx所有有處理函數的請求頭都記錄在ngx_http_headers_in全局數組中:

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">typedef struct {  
    2.     ngx_str_t                         name;  
    3.     ngx_uint_t                        offset;  
    4.     ngx_http_header_handler_pt        handler;  
    5. } ngx_http_header_t;  
    6.   
    7. ngx_http_header_t  ngx_http_headers_in[] = {  
    8.     { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),  
    9.                  ngx_http_process_host },  
    10.   
    11.     { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),  
    12.                  ngx_http_process_connection },  
    13.   
    14.     { ngx_string("If-Modified-Since"),  
    15.                  offsetof(ngx_http_headers_in_t, if_modified_since),  
    16.                  ngx_http_process_unique_header_line },  
    17.   
    18.     { ngx_string("If-Unmodified-Since"),  
    19.                  offsetof(ngx_http_headers_in_t, if_unmodified_since),  
    20.                  ngx_http_process_unique_header_line },  
    21.   
    22.     { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),  
    23.                  ngx_http_process_user_agent },  
    24.   
    25.   
    26.     { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),  
    27.                  ngx_http_process_header_line },  
    28.   
    29.     { ngx_string("Content-Length"),  
    30.                  offsetof(ngx_http_headers_in_t, content_length),  
    31.                  ngx_http_process_unique_header_line },  
    32.   
    33.     { ngx_string("Content-Type"),  
    34.                  offsetof(ngx_http_headers_in_t, content_type),  
    35.                  ngx_http_process_header_line },  
    36.   
    37.     { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),  
    38.                  ngx_http_process_header_line },  
    39.   
    40.     { ngx_string("If-Range"),  
    41.                  offsetof(ngx_http_headers_in_t, if_range),  
    42.                  ngx_http_process_unique_header_line },  
    43.   
    44.     { ngx_string("Transfer-Encoding"),  
    45.                  offsetof(ngx_http_headers_in_t, transfer_encoding),  
    46.                  ngx_http_process_header_line },  
    47.   
    48.     { ngx_string("Expect"),  
    49.                  offsetof(ngx_http_headers_in_t, expect),  
    50.                  ngx_http_process_unique_header_line },  
    51.   
    52. #if (NGX_HTTP_GZIP)  
    53.     { ngx_string("Accept-Encoding"),  
    54.                  offsetof(ngx_http_headers_in_t, accept_encoding),  
    55.                  ngx_http_process_header_line },  
    56.   
    57.     { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),  
    58.                  ngx_http_process_header_line },  
    59. #endif  
    60.   
    61.     { ngx_string("Authorization"),  
    62.                  offsetof(ngx_http_headers_in_t, authorization),  
    63.                  ngx_http_process_unique_header_line },  
    64.   
    65.     { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),  
    66.                  ngx_http_process_header_line },  
    67.   
    68.   
    69. #if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)  
    70.     { ngx_string("X-Forwarded-For"),  
    71.                  offsetof(ngx_http_headers_in_t, x_forwarded_for),  
    72.                  ngx_http_process_header_line },  
    73. #endif  
    74.   
    75. #if (NGX_HTTP_REALIP)  
    76.     { ngx_string("X-Real-IP"),  
    77.                  offsetof(ngx_http_headers_in_t, x_real_ip),  
    78.                  ngx_http_process_header_line },  
    79. #endif  
    80.   
    81. #if (NGX_HTTP_HEADERS)  
    82.     { ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),  
    83.                  ngx_http_process_header_line },  
    84.   
    85.     { ngx_string("Accept-Language"),  
    86.                  offsetof(ngx_http_headers_in_t, accept_language),  
    87.                  ngx_http_process_header_line },  
    88. #endif  
    89.   
    90. #if (NGX_HTTP_DAV)  
    91.     { ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),  
    92.                  ngx_http_process_header_line },  
    93.   
    94.     { ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),  
    95.                  ngx_http_process_header_line },  
    96.   
    97.     { ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),  
    98.                  ngx_http_process_header_line },  
    99.   
    100.     { ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),  
    101.                  ngx_http_process_header_line },  
    102. #endif  
    103.   
    104.     { ngx_string("Cookie"), 0, ngx_http_process_cookie },  
    105.   
    106.     { ngx_null_string, 0, NULL }  
    107. };</span>  

    ngx_http_headers_in數組當前包含了25個常用的請求頭,每個請求頭都設置了一個處理函數,當前其中一部分請求頭設置的是公共的處理函數,這里有2個公共的處理函數,ngx_http_process_header_line和ngx_http_process_unique_header_line。

    先來看一下處理函數的函數指針定義:
    typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
        ngx_table_elt_t *h, ngx_uint_t offset);
    它有3個參數,r為對應的請求結構,h為該請求頭在headers_in.headers鏈表節點的指針,offset為該請求頭的引用在ngx_http_headers_in_t結構中的偏移。
    再來看ngx_http_process_header_line函數:

     

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">static ngx_int_t  
    2. ngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,  
    3.     ngx_uint_t offset)  
    4. {  
    5.     ngx_table_elt_t  **ph;  
    6.   
    7.     ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);  
    8.   
    9.     if (*ph == NULL) {  
    10.         *ph = h;  
    11.     }  
    12.   
    13.     return NGX_OK;  
    14. }</span>  

    這個函數只是簡單將該請求頭在ngx_http_headers_in_t結構中保存一份引用。ngx_http_process_unique_header_line功能類似,不同點在于該函數會檢查這個請求頭是否是重復的,如果是的話,則給該請求返回400錯誤。

    ngx_http_headers_in數組中剩下的請求頭都有自己特殊的處理函數,這些特殊的函數根據對應的請求頭有一些特殊的處理,下面我們拿Host頭的處理函數ngx_http_process_host做一下介紹:

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">static ngx_int_t  
    2. ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,  
    3.     ngx_uint_t offset)  
    4. {  
    5.     u_char   *host;  
    6.     ssize_t   len;  
    7.   
    8.     if (r->headers_in.host == NULL) {  
    9.         r->headers_in.host = h;  
    10.     }  
    11.   
    12.     host = h->value.data;  
    13.     len = ngx_http_validate_host(r, &host, h->value.len, 0);  
    14.   
    15.     if (len == 0) {  
    16.         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,  
    17.                       "client sent invalid host header");  
    18.         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);  
    19.         return NGX_ERROR;  
    20.     }  
    21.   
    22.     if (len < 0) {  
    23.         ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);  
    24.         return NGX_ERROR;  
    25.     }  
    26.   
    27.     if (r->headers_in.server.len) {  
    28.         return NGX_OK;  
    29.     }  
    30.   
    31.     r->headers_in.server.len = len;  
    32.     r->headers_in.server.data = host;  
    33.   
    34.     return NGX_OK;  
    35. }</span>  

    此函數的目的也是保存Host頭的快速引用,它會對Host頭的值做一些合法性檢查,并從中解析出域名,保存在headers_in.server字段,實際上前面在解析請求行時,headers_in.server可能已經被賦值為從請求行中解析出來的域名,根據http協議的規范,如果請求行中的uri帶有域名的話,則域名以它為準,所以這里需檢查一下headers_in.server是否為空,如果不為空則不需要再賦值。

    其他請求頭的特殊處理函數,不再做介紹,大致都是根據該請求頭在http協議中規定的意義及其值設置請求的一些屬性,必備后續使用。

    對一個合法的請求頭的處理大致為如上所述;

    2,返回NGX_AGAIN,表示當前接收到的數據不夠,一行請求頭還未結束,需要繼續下一輪循環。在下一輪循環中,nginx首先檢查請求頭緩沖區header_in是否已滿,如夠滿了,則調用ngx_http_alloc_large_header_buffer()函數分配更多緩沖區,下面分析一下ngx_http_alloc_large_header_buffer函數:

    [cpp] view plaincopy  
    1. <span style="font-size:18px;">static ngx_int_t  
    2. ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,  
    3.     ngx_uint_t request_line)  
    4. {  
    5.     u_char                    *old, *new;  
    6.     ngx_buf_t                 *b;  
    7.     ngx_http_connection_t     *hc;  
    8.     ngx_http_core_srv_conf_t  *cscf;  
    9.   
    10.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,  
    11.                    "http alloc large header buffer");  
    12.   
    13.     /* 
    14.      * 在解析請求行階段,如果客戶端在發送請求行之前發送了大量回車換行符將 
    15.      * 緩沖區塞滿了,針對這種情況,nginx只是簡單的重置緩沖區,丟棄這些垃圾 
    16.      * 數據,不需要分配更大的內存。 
    17.      */  
    18.     if (request_line && r->state == 0) {  
    19.   
    20.         /* the client fills up the buffer with "\r\n" */  
    21.   
    22.         r->request_length += r->header_in->end - r->header_in->start;  
    23.   
    24.         r->header_in->pos = r->header_in->start;  
    25.         r->header_in->last = r->header_in->start;  
    26.   
    27.         return NGX_OK;  
    28.     }  
    29.   
    30.     /* 保存請求行或者請求頭在舊緩沖區中的起始地址 */  
    31.     old = request_line ? r->request_start : r->header_name_start;  
    32.   
    33.     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);  
    34.   
    35.     /* 如果一個大緩沖區還裝不下請求行或者一個請求頭,則返回錯誤 */  
    36.     if (r->state != 0  
    37.         && (size_t) (r->header_in->pos - old)  
    38.                                      >= cscf->large_client_header_buffers.size)  
    39.     {  
    40.         return NGX_DECLINED;  
    41.     }  
    42.   
    43.     hc = r->http_connection;  
    44.   
    45.     /* 首先在ngx_http_connection_t結構中查找是否有空閑緩沖區,有的話,直接取之 */  
    46.     if (hc->nfree) {  
    47.         b = hc->free[--hc->nfree];  
    48.   
    49.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,  
    50.                        "http large header free: %p %uz",  
    51.                        b->pos, b->end - b->last);  
    52.   
    53.     /* 檢查給該請求分配的請求頭緩沖區個數是否已經超過限制,默認最大個數為4個 */  
    54.     } else if (hc->nbusy < cscf->large_client_header_buffers.num) {  
    55.   
    56.         if (hc->busy == NULL) {  
    57.             hc->busy = ngx_palloc(r->connection->pool,  
    58.                   cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));  
    59.             if (hc->busy == NULL) {  
    60.                 return NGX_ERROR;  
    61.             }  
    62.         }  
    63.   
    64.         /* 如果還沒有達到最大分配數量,則分配一個新的大緩沖區 */  
    65.         b = ngx_create_temp_buf(r->connection->pool,  
    66.                                 cscf->large_client_header_buffers.size);  
    67.         if (b == NULL) {  
    68.             return NGX_ERROR;  
    69.         }  
    70.   
    71.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,  
    72.                        "http large header alloc: %p %uz",  
    73.                        b->pos, b->end - b->last);  
    74.   
    75.     } else {  
    76.         /* 如果已經達到最大的分配限制,則返回錯誤 */  
    77.         return NGX_DECLINED;  
    78.     }  
    79.   
    80.     /* 將從空閑隊列取得的或者新分配的緩沖區加入已使用隊列 */  
    81.     hc->busy[hc->nbusy++] = b;  
    82.   
    83.     /* 
    84.      * 因為nginx中,所有的請求頭的保存形式都是指針(起始和結束地址), 
    85.      * 所以一行完整的請求頭必須放在連續的內存塊中。如果舊的緩沖區不能 
    86.      * 再放下整行請求頭,則分配新緩沖區,并從舊緩沖區拷貝已經讀取的部分請求頭, 
    87.      * 拷貝完之后,需要修改所有相關指針指向到新緩沖區。 
    88.      * status為0表示解析完一行請求頭之后,緩沖區正好被用完,這種情況不需要拷貝 
    89.      */  
    90.     if (r->state == 0) {  
    91.         /* 
    92.          * r->state == 0 means that a header line was parsed successfully 
    93.          * and we do not need to copy incomplete header line and 
    94.          * to relocate the parser header pointers 
    95.          */  
    96.   
    97.         r->request_length += r->header_in->end - r->header_in->start;  
    98.   
    99.         r->header_in = b;  
    100.   
    101.         return NGX_OK;  
    102.     }  
    103.   
    104.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,  
    105.                    "http large header copy: %d", r->header_in->pos - old);  
    106.   
    107.     r->request_length += old - r->header_in->start;  
    108.   
    109.     new = b->start;  
    110.   
    111.     /* 拷貝舊緩沖區中不完整的請求頭 */  
    112.     ngx_memcpy(new, old, r->header_in->pos - old);  
    113.   
    114.     b->pos = new + (r->header_in->pos - old);  
    115.     b->last = new + (r->header_in->pos - old);  
    116.   
    117.     /* 修改相應的指針指向新緩沖區 */  
    118.     if (request_line) {  
    119.         r->request_start = new;  
    120.   
    121.         if (r->request_end) {  
    122.             r->request_end = new + (r->request_end - old);  
    123.         }  
    124.   
    125.         r->method_end = new + (r->method_end - old);  
    126.   
    127.         r->uri_start = new + (r->uri_start - old);  
    128.         r->uri_end = new + (r->uri_end - old);  
    129.   
    130.         if (r->schema_start) {  
    131.             r->schema_start = new + (r->schema_start - old);  
    132.             r->schema_end = new + (r->schema_end - old);  
    133.         }  
    134.   
    135.         if (r->host_start) {  
    136.             r->host_start = new + (r->host_start - old);  
    137.             if (r->host_end) {  
    138.                 r->host_end = new + (r->host_end - old);  
    139.             }  
    140.         }  
    141.   
    142.         if (r->port_start) {  
    143.             r->port_start = new + (r->port_start - old);  
    144.             r->port_end = new + (r->port_end - old);  
    145.         }  
    146.   
    147.         if (r->uri_ext) {  
    148.             r->uri_ext = new + (r->uri_ext - old);  
    149.         }  
    150.   
    151.         if (r->args_start) {  
    152.             r->args_start = new + (r->args_start - old);  
    153.         }  
    154.   
    155.         if (r->http_protocol.data) {  
    156.             r->http_protocol.data = new + (r->http_protocol.data - old);  
    157.         }  
    158.   
    159.     } else {  
    160.         r->header_name_start = new;  
    161.         r->header_name_end = new + (r->header_name_end - old);  
    162.         r->header_start = new + (r->header_start - old);  
    163.         r->header_end = new + (r->header_end - old);  
    164.     }  
    165.   
    166.     r->header_in = b;  
    167.   
    168.     return NGX_OK;  
    169. }</span>  

    當ngx_http_alloc_large_header_buffer函數返回NGX_DECLINED)時,表示客戶端發送了過大的一行請求頭,或者是整個請求頭部超過了限制,nginx會返回494錯誤,注意到nginx再返回494錯誤之前將請求的lingering_close標識置為了1,這樣做的目的是在返回響應之丟棄掉客戶端發過來的其他數據;

    3,返回NGX_HTTP_PARSE_INVALID_HEADER,表示請求頭解析過程中遇到錯誤,一般為客戶端發送了不符合協議規范的頭部,此時nginx返回400錯誤。

    4,返回NGX_HTTP_PARSE_HEADER_DONE,表示所有請求頭已經成功的解析,這時請求的狀態被設置為NGX_HTTP_PROCESS_REQUEST_STATE,意味著結束了請求讀取階段,正式進入了請求處理階段,但是實際上請求可能含有請求體,nginx在請求讀取階段并不會去讀取請求體,這個工作交給了后續的請求處理階段的模塊,這樣做的目的是nginx本身并不知道這些請求體是否有用,如果后續模塊并不需要的話,一方面請求體一般較大,如果全部讀取進內存,則白白耗費大量的內存空間,另一方面即使nginx將請求體寫進磁盤,但是涉及到磁盤io,會耗費比較多時間。所以交由后續模塊來決定讀取還是丟棄請求體是最明智的辦法。

    讀取完請求頭之后,nginx調用了ngx_http_process_request_header()函數,這個函數主要做了兩個方面的事情,一是調用ngx_http_find_virtual_server()函數查找虛擬服務器配置;二是對一些請求頭做一些協議的檢查。比如對那些使用http1.1協議但是卻沒有發送Host頭的請求,nginx給這些請求返回400錯誤。還有nginx現在的版本并不支持chunked格式的輸入,如果某些請求申明自己使用了chunked格式的輸入(請求帶有值為chunked的transfer_encoding頭部),nginx給這些請求返回411錯誤。等等。
    最后調用ngx_http_process_request()函數處理請求;
    至此,nginx接收請求接收流程就介紹完畢。

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全