当前位置: > Linux服务器 > nginx >

nginx的请求接收流程(二)

时间:2015-12-01 18:52来源:linux.it.net.cn 作者:IT

在ngx_http_process_request_line函数中,解析完请求行之后,如果请求行的uri里面包含了域名部分,则将其保持在请求结构的headers_in成员的server字段,headers_in用来保存所有请求头,它的类型为ngx_http_headers_in_t:

 
  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>  
typedef struct {
    ngx_list_t                        headers;

    ngx_table_elt_t                  *host;
    ngx_table_elt_t                  *connection;
    ngx_table_elt_t                  *if_modified_since;
    ngx_table_elt_t                  *if_unmodified_since;
    ngx_table_elt_t                  *user_agent;
    ngx_table_elt_t                  *referer;
    ngx_table_elt_t                  *content_length;
    ngx_table_elt_t                  *content_type;

    ngx_table_elt_t                  *range;
    ngx_table_elt_t                  *if_range;

    ngx_table_elt_t                  *transfer_encoding;
    ngx_table_elt_t                  *expect;

#if (NGX_HTTP_GZIP)
    ngx_table_elt_t                  *accept_encoding;
    ngx_table_elt_t                  *via;
#endif

    ngx_table_elt_t                  *authorization;

    ngx_table_elt_t                  *keep_alive;

#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
    ngx_table_elt_t                  *x_forwarded_for;
#endif

#if (NGX_HTTP_REALIP)
    ngx_table_elt_t                  *x_real_ip;
#endif

#if (NGX_HTTP_HEADERS)
    ngx_table_elt_t                  *accept;
    ngx_table_elt_t                  *accept_language;
#endif

#if (NGX_HTTP_DAV)
    ngx_table_elt_t                  *depth;
    ngx_table_elt_t                  *destination;
    ngx_table_elt_t                  *overwrite;
    ngx_table_elt_t                  *date;
#endif

    ngx_str_t                         user;
    ngx_str_t                         passwd;
    ngx_array_t                       cookies;

    ngx_str_t                         server;
    off_t                             content_length_n;
    time_t                            keep_alive_n;

    unsigned                          connection_type:2;
    unsigned                          msie:1;
    unsigned                          msie6:1;
    unsigned                          opera:1;
    unsigned                          gecko:1;
    unsigned                          chrome:1;
    unsigned                          safari:1;
    unsigned                          konqueror:1;
} ngx_http_headers_in_t;

接着,该函数会检查进来的请求是否使用的是http0.9,如果是的话则使用从请求行里得到的域名,调用ngx_http_find_virtual_server()函数来查找用来处理该请求的虚拟服务器配置,之前通过端口和地址找到的默认配置不再使用,找到相应的配置之后,则直接调用ngx_http_process_request()函数处理该请求,因为http0.9是最原始的http协议,它里面没有定义任何请求头,显然就不需要读取请求头的操作。

 
  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>  
            if (r->host_start && r->host_end) {

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

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

                if (n < 0) {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                r->headers_in.server.len = n;
                r->headers_in.server.data = host;
            }

            if (r->http_version < NGX_HTTP_VERSION_10) {

                if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
                                                 r->headers_in.server.len)
                    == NGX_ERROR)
                {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                ngx_http_process_request(r);
                return;
            }

当然,如果是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,并随后马上调用了该函数。

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

            if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
                               sizeof(ngx_table_elt_t *))
                != NGX_OK)
            {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

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

            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);
ngx_http_process_request_headers函数循环的读取所有的请求头,并保存和初始化和请求头相关的结构,下面详细分析一下该函数:
因为nginx对读取请求头有超时限制,ngx_http_process_request_headers函数作为读事件处理函数,一并处理了超时事件,如果读超时了,nginx直接给该请求返回408错误:
[cpp] view plaincopyprint?
  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>  
    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;
    }

读取和解析请求头的逻辑和处理请求行差不多,总的流程也是循环的调用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全局数组中:
 
  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>  
typedef struct {
    ngx_str_t                         name;
    ngx_uint_t                        offset;
    ngx_http_header_handler_pt        handler;
} ngx_http_header_t;

ngx_http_header_t  ngx_http_headers_in[] = {
    { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
                 ngx_http_process_host },

    { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
                 ngx_http_process_connection },

    { ngx_string("If-Modified-Since"),
                 offsetof(ngx_http_headers_in_t, if_modified_since),
                 ngx_http_process_unique_header_line },

    { ngx_string("If-Unmodified-Since"),
                 offsetof(ngx_http_headers_in_t, if_unmodified_since),
                 ngx_http_process_unique_header_line },

    { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
                 ngx_http_process_user_agent },


    { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),
                 ngx_http_process_header_line },

    { ngx_string("Content-Length"),
                 offsetof(ngx_http_headers_in_t, content_length),
                 ngx_http_process_unique_header_line },

    { ngx_string("Content-Type"),
                 offsetof(ngx_http_headers_in_t, content_type),
                 ngx_http_process_header_line },

    { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),
                 ngx_http_process_header_line },

    { ngx_string("If-Range"),
                 offsetof(ngx_http_headers_in_t, if_range),
                 ngx_http_process_unique_header_line },

    { ngx_string("Transfer-Encoding"),
                 offsetof(ngx_http_headers_in_t, transfer_encoding),
                 ngx_http_process_header_line },

    { ngx_string("Expect"),
                 offsetof(ngx_http_headers_in_t, expect),
                 ngx_http_process_unique_header_line },

#if (NGX_HTTP_GZIP)
    { ngx_string("Accept-Encoding"),
                 offsetof(ngx_http_headers_in_t, accept_encoding),
                 ngx_http_process_header_line },

    { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),
                 ngx_http_process_header_line },
#endif

    { ngx_string("Authorization"),
                 offsetof(ngx_http_headers_in_t, authorization),
                 ngx_http_process_unique_header_line },

    { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),
                 ngx_http_process_header_line },


#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
    { ngx_string("X-Forwarded-For"),
                 offsetof(ngx_http_headers_in_t, x_forwarded_for),
                 ngx_http_process_header_line },
#endif

#if (NGX_HTTP_REALIP)
    { ngx_string("X-Real-IP"),
                 offsetof(ngx_http_headers_in_t, x_real_ip),
                 ngx_http_process_header_line },
#endif

#if (NGX_HTTP_HEADERS)
    { ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),
                 ngx_http_process_header_line },

    { ngx_string("Accept-Language"),
                 offsetof(ngx_http_headers_in_t, accept_language),
                 ngx_http_process_header_line },
#endif

#if (NGX_HTTP_DAV)
    { ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),
                 ngx_http_process_header_line },

    { ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),
                 ngx_http_process_header_line },

    { ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),
                 ngx_http_process_header_line },

    { ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),
                 ngx_http_process_header_line },
#endif

    { ngx_string("Cookie"), 0, ngx_http_process_cookie },

    { ngx_null_string, 0, NULL }
};

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 plaincopyprint?
  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>  
static ngx_int_t
ngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
    ngx_uint_t offset)
{
    ngx_table_elt_t  **ph;

    ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);

    if (*ph == NULL) {
        *ph = h;
    }

    return NGX_OK;
}

这个函数只是简单将该请求头在ngx_http_headers_in_t结构中保存一份引用。ngx_http_process_unique_header_line功能类似,不同点在于该函数会检查这个请求头是否是重复的,如果是的话,则给该请求返回400错误。

ngx_http_headers_in数组中剩下的请求头都有自己特殊的处理函数,这些特殊的函数根据对应的请求头有一些特殊的处理,下面我们拿Host头的处理函数ngx_http_process_host做一下介绍:
[cpp] view plaincopyprint?
  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>  
static ngx_int_t
ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
    ngx_uint_t offset)
{
    u_char   *host;
    ssize_t   len;

    if (r->headers_in.host == NULL) {
        r->headers_in.host = h;
    }

    host = h->value.data;
    len = ngx_http_validate_host(r, &host, h->value.len, 0);

    if (len == 0) {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                      "client sent invalid host header");
        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return NGX_ERROR;
    }

    if (len < 0) {
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return NGX_ERROR;
    }

    if (r->headers_in.server.len) {
        return NGX_OK;
    }

    r->headers_in.server.len = len;
    r->headers_in.server.data = host;

    return NGX_OK;
}
此函数的目的也是保存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 plaincopyprint?
  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>  
static ngx_int_t
ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
    ngx_uint_t request_line)
{
    u_char                    *old, *new;
    ngx_buf_t                 *b;
    ngx_http_connection_t     *hc;
    ngx_http_core_srv_conf_t  *cscf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http alloc large header buffer");

    /*
     * 在解析请求行阶段,如果客户端在发送请求行之前发送了大量回车换行符将
     * 缓冲区塞满了,针对这种情况,nginx只是简单的重置缓冲区,丢弃这些垃圾
     * 数据,不需要分配更大的内存。
     */
    if (request_line && r->state == 0) {

        /* the client fills up the buffer with "\r\n" */

        r->request_length += r->header_in->end - r->header_in->start;

        r->header_in->pos = r->header_in->start;
        r->header_in->last = r->header_in->start;

        return NGX_OK;
    }

    /* 保存请求行或者请求头在旧缓冲区中的起始地址 */
    old = request_line ? r->request_start : r->header_name_start;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

    /* 如果一个大缓冲区还装不下请求行或者一个请求头,则返回错误 */
    if (r->state != 0
        && (size_t) (r->header_in->pos - old)
                                     >= cscf->large_client_header_buffers.size)
    {
        return NGX_DECLINED;
    }

    hc = r->http_connection;

    /* 首先在ngx_http_connection_t结构中查找是否有空闲缓冲区,有的话,直接取之 */
    if (hc->nfree) {
        b = hc->free[--hc->nfree];

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http large header free: %p %uz",
                       b->pos, b->end - b->last);

    /* 检查给该请求分配的请求头缓冲区个数是否已经超过限制,默认最大个数为4个 */
    } else if (hc->nbusy < cscf->large_client_header_buffers.num) {

        if (hc->busy == NULL) {
            hc->busy = ngx_palloc(r->connection->pool,
                  cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
            if (hc->busy == NULL) {
                return NGX_ERROR;
            }
        }

        /* 如果还没有达到最大分配数量,则分配一个新的大缓冲区 */
        b = ngx_create_temp_buf(r->connection->pool,
                                cscf->large_client_header_buffers.size);
        if (b == NULL) {
            return NGX_ERROR;
        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http large header alloc: %p %uz",
                       b->pos, b->end - b->last);

    } else {
        /* 如果已经达到最大的分配限制,则返回错误 */
        return NGX_DECLINED;
    }

    /* 将从空闲队列取得的或者新分配的缓冲区加入已使用队列 */
    hc->busy[hc->nbusy++] = b;

    /*
     * 因为nginx中,所有的请求头的保存形式都是指针(起始和结束地址),
     * 所以一行完整的请求头必须放在连续的内存块中。如果旧的缓冲区不能
     * 再放下整行请求头,则分配新缓冲区,并从旧缓冲区拷贝已经读取的部分请求头,
     * 拷贝完之后,需要修改所有相关指针指向到新缓冲区。
     * status为0表示解析完一行请求头之后,缓冲区正好被用完,这种情况不需要拷贝
     */
    if (r->state == 0) {
        /*
         * r->state == 0 means that a header line was parsed successfully
         * and we do not need to copy incomplete header line and
         * to relocate the parser header pointers
         */

        r->request_length += r->header_in->end - r->header_in->start;

        r->header_in = b;

        return NGX_OK;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http large header copy: %d", r->header_in->pos - old);

    r->request_length += old - r->header_in->start;

    new = b->start;

    /* 拷贝旧缓冲区中不完整的请求头 */
    ngx_memcpy(new, old, r->header_in->pos - old);

    b->pos = new + (r->header_in->pos - old);
    b->last = new + (r->header_in->pos - old);

    /* 修改相应的指针指向新缓冲区 */
    if (request_line) {
        r->request_start = new;

        if (r->request_end) {
            r->request_end = new + (r->request_end - old);
        }

        r->method_end = new + (r->method_end - old);

        r->uri_start = new + (r->uri_start - old);
        r->uri_end = new + (r->uri_end - old);

        if (r->schema_start) {
            r->schema_start = new + (r->schema_start - old);
            r->schema_end = new + (r->schema_end - old);
        }

        if (r->host_start) {
            r->host_start = new + (r->host_start - old);
            if (r->host_end) {
                r->host_end = new + (r->host_end - old);
            }
        }

        if (r->port_start) {
            r->port_start = new + (r->port_start - old);
            r->port_end = new + (r->port_end - old);
        }

        if (r->uri_ext) {
            r->uri_ext = new + (r->uri_ext - old);
        }

        if (r->args_start) {
            r->args_start = new + (r->args_start - old);
        }

        if (r->http_protocol.data) {
            r->http_protocol.data = new + (r->http_protocol.data - old);
        }

    } else {
        r->header_name_start = new;
        r->header_name_end = new + (r->header_name_end - old);
        r->header_start = new + (r->header_start - old);
        r->header_end = new + (r->header_end - old);
    }

    r->header_in = b;

    return NGX_OK;
}

当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接收请求接收流程就介绍完毕。
(责任编辑:IT)
------分隔线----------------------------
栏目列表
推荐内容