nginx&http 第三章 ngx HTTP 请求的 11 个处理阶段

  nginx 将一个 HTTP 请求分为 11 个处理阶段,这样做让每一个 HTTP 模块可以仅仅专注于完成一个独立、简单的功能,而一个请求的完整处理过程可以由多个 HTTP 模块共同合作完成将一次 HTTP 请求划分为 11 个处理阶段,通过每个阶段内部多个 HTTP 模块流水式的处理请求

HTTP 处理阶段枚举类型 -- ngx_http_phases

  ngx_http_phases定义的11个阶段是有顺序的,必须按照其定义的顺序执行。同时也要意识到,并不是说一个用户请求最多只能经过11个
HTTP模块提供的ngx_http_handler_pt方法来处理,NGX_HTTP_POST_READ_PHASE、NGX_HTTP_SERVER_REWRITE_PHASE、NGX_HTTP_REWRITE_PHASE、
NGX_HTTP_PREACCESS_PHASE、NGX_HTTP_ACCESS_PHASE、NGX HTTP_CONTENT_PHASE、NGX_HTTP_LOG_PHASE这7个阶段可以包括任意多个处理方法
它们是可以同时作用于同一个用户请求的。而NGX_HTTP_FIND_CONFIG_PHASE、NGX_HTTP_POSTREWRITE_PHASE、NGX_HTTP_POST_ACCESS_PHASE、
NGX_HTTP_TRY_FILES_PHASE这4个阶段则不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求,它们仅由HTTP框架实现;

  NGX_HTTP_FIND_CONFIG_PHASE、NGX_HTTP_POSTREWRITE_PHASE、NGX_HTTP_POST_ACCESS_PHASE、NGX_HTTP_TRY_FILES_PHASE

这4个阶段则不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求--------------------

他们的会占用cmcf->phase_engine.handlers[]数组中的一个成员,见ngx_http_init_phase_handle

  typedef enum { //各个阶段的http框架check函数见ngx_http_init_phase_handlers //所有阶段的checker在ngx_http_core_run_phases中调用
  //在接收到完整的HTTP头部后处理的HTTP阶段   主要是获取客户端真实IP,因为客户端到nginx可能通过了vanish等缓存,
    //ngx_http_realip_module(ngx_http_realip_init->ngx_http_realip_handler)
    NGX_HTTP_POST_READ_PHASE = 0, //该阶段方法有:ngx_http_realip_handler  POST有"在....后"的意思,POST_READ应该就是在解析完请求行和头部行后


    /*在还没有查询到URI匹配的location前,这时rewrite重写URL也作为一个独立的HTTP阶段   Server内请求地址重写阶段 
 在 URI 与 location 匹配前修改请求的 URI(重定向)*/
    NGX_HTTP_SERVER_REWRITE_PHASE, //该阶段handler方法有:ngx_http_rewrite_module(ngx_http_rewrite_init->ngx_http_rewrite_handler) 

    /*根据URI寻找匹配的location,这个阶段通常由ngx_http_core_module模块实现,不建议其他HTTP模块重新定义这一阶段的行为
    // 根据请求 URI 匹配 location 表达式该阶段只能由 ngx_http_core_module 模块实现*/
    NGX_HTTP_FIND_CONFIG_PHASE,//该阶段handler方法有:无,不允许用户添加hander方法在该阶段  该阶段完成的是Nginx的特定任务,即进行Location定位

    //在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location之后再修改请求的URI  Location内请求地址重写阶段
    /*在NGX_HTTP_FIND_CONFIG_PHASE阶段之后重写URL的意义与NGX_HTTP_SERVER_REWRITE_PHASE阶段显然是不同的,
    因为这两者会导致查找到不同的location块(location是与URI进行匹配的) ----匹配 location 后修改请求 URI*/
    NGX_HTTP_REWRITE_PHASE,//该阶段handler方法有:ngx_http_rewrite_handler
    /*这一阶段是用于在rewrite重写URL后重新跳到NGX_HTTP_FIND_CONFIG_PHASE阶段,找到与新的URI匹配的location。所以,这一阶段是无法由第三方HTTP模块处理的,而仅由ngx_http_core_module模块使用*/
    /*
    这一阶段是用于在rewrite重写URL后,防止错误的nginx.conf配置导致死循环(递归地修改URI),因此,这一阶段仅由ngx_http_core_module模块处理。
目前,控制死循环的方式很简单,首先检查rewrite的次数,如果一个请求超过10次重定向,扰认为进入了rewrite死循环,这时在NGX_HTTP_POSTREWRITE_PHASE
阶段就会向用户返回500,表示服务器内部错误
     */
    NGX_HTTP_POST_REWRITE_PHASE,//该阶段handler方法有:无,不允许用户添加hander方法在该阶段

    /*
    NGXHTTPPREACCESSPHASE、NGX_HTTP_ACCESS_PHASE、NGX HTTPPOST_ACCESS_PHASE,很好理解,做访问权限检查的前期、中期、后期工作,
其中后期工作是固定的,判断前面访问权限检查的结果(状态码存故在字段r->access_code内),如果当前请求没有访问权限,那么直接返回状
态403错误,所以这个阶段也无法去挂载额外的回调函数。
     */
    //处理NGX_HTTP_ACCESS_PHASE阶段前,HTTP模块可以介入的处理阶段
    NGX_HTTP_PREACCESS_PHASE,//该阶段handler方法有:ngx_http_degradation_handler  ngx_http_limit_conn_handler  ngx_http_limit_req_handler ngx_http_realip_handler

     /*这个阶段用于让HTTP模块判断是否允许这个请求访问Nginx服务器*/
    NGX_HTTP_ACCESS_PHASE,//该阶段handler方法有:ngx_http_access_handler  ngx_http_auth_basic_handler  ngx_http_auth_request_handler
    /*当NGX_HTTP_ACCESS_PHASE阶段中HTTP模块的handler处理方法返回不允许访问的错误码时(实际是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED),
    这个阶段将负责构造拒绝服务的用户响应。所以,这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾*/
    NGX_HTTP_POST_ACCESS_PHASE,//该阶段handler方法有:无,不允许用户添加hander方法在该阶段

    /*这个阶段完全是为了try_files配置项而设立的。当HTTP请求访问静态文件资源时,try_files配置项可以使这个请求顺序地访问多个静态文件资源,
    如果某一次访问失败,则继续访问try_files中指定的下一个静态资源。另外,这个功能完全是在NGX_HTTP_TRY_FILES_PHASE阶段中实现的*/
    NGX_HTTP_TRY_FILES_PHASE,//该阶段handler方法有:无,不允许用户添加hander方法在该阶段

    /* 其余10个阶段中各HTTP模块的处理方法都是放在全局的ngx_http_core_main_conf_t结构体中的,也就是说,它们对任何一个HTTP请求都是有效的 
    NGX_HTTP_CONTENT_PHASE仅仅针对某种请求唯一生效

    ngx_http_handler_pt处理方法不再应用于所有的HTTP请求,仅仅当用户请求的URI匹配了location时(也就是mytest配置项所在的location)才会被调用。
这也就意味着它是一种完全不同于其他阶段的使用方式。 因此,当HTTP模块实现了某个ngx_http_handler_pt处理方法并希望介入NGX_HTTP_CONTENT_PHASE阶
段来处理用户请求时,如果希望这个ngx_http_handler_pt方法应用于所有的用户请求,则应该在ngx_http_module_t接口的postconfiguration方法中,
向ngx_http_core_main_conf_t结构体的phases[NGX_HTTP_CONTENT_PHASE]动态数组中添加ngx_http_handler_pt处理方法;反之,如果希望这个方式
仅应用于URI匹配丁某些location的用户请求,则应该在一个location下配置项的回调方法中,把ngx_http_handler_pt方法设置到ngx_http_core_loc_conf_t
结构体的handler中。
    注意ngx_http_core_loc_conf_t结构体中仅有一个handler指针,它不是数组,这也就意味着如果采用上述的第二种方法添加ngx_http_handler_pt处理方法,
那么每个请求在NGX_HTTP_CONTENT PHASE阶段只能有一个ngx_http_handler_pt处理方法。而使用第一种方法时是没有这个限制的,NGX_HTTP_CONTENT_PHASE阶
段可以经由任意个HTTP模块处理。
    */
    //用于处理HTTP请求内容的阶段,这是大部分HTTP模块最喜欢介入的阶段  //CONTENT_PHASE阶段的处理回调函数ngx_http_handler_pt比较特殊,见ngx_http_core_content_phase 
    NGX_HTTP_CONTENT_PHASE, //该阶段handler方法有:ngx_http_autoindex_handler  ngx_http_dav_handler ngx_http_gzip_static_handler  ngx_http_index_handler ngx_http_random_index_handler ngx_http_static_handler
    
    /*处理完请求后记录日志的阶段。例如,ngx_http_log_module模块就在这个阶段中加入了一个handler处理方法,使得每个HTTP请求处理完毕后会记录access_log日志*/
    NGX_HTTP_LOG_PHASE //该阶段handler方法有: ngx_http_log_handler
} ngx_http_phases; 

  在 ngx_http_core_main_conf_t 有一个 ngx_http_phase_engine_t 类型的 phase_engine 字段和一个 ngx_http_phase_t 类型的 phases[NGX_HTTP_LOG_PHASE + 1] 数组
他们存储了 HTTP 处理过程中的所有回调函数,其中 phase_engine 控制运行过程中一个 HTTP 请求所要经过的 HTTP 处理阶段,
而 phase 数组则仅会在 nginx 启动过程中按照 11 个阶段的概念初始化 phase_engine 中的 handlers 数组

  通常,在任意一个ngx_http_phases阶段,都可以拥有零个或多个ngx_http_phase_handler_s结构体,其含义更接近于某个HTTP模块的处理方法。
一个http{}块解析完毕后将会根据nginx.conf中的配置产生由ngx_http_phase_handler_t组成的数组,在处理HTTP请求时,一般情况下这些阶段是顺序
向后执行的,但ngx_http_phase_handler_t中的next成员使得它们也可以非顺序执行。ngx_http_phase_engine_t结构体就是所有ngx_http_phase_handler_t组成的数组

  ngx_http_phase_engine_t中保存了在当前nginx.conf配置下,一个用户请求可能经历的所有ngx_http_handler_pt处理方法,这是所有HTTP模块可以合作处理用户请求
的关键!这个ngx_http_phase_engine_t结构体是保存在全局的ngx_http_core_main_conf_t结构体中的

  在HTTP框架的初始化过程中,任何HTTP模块都可以在ngx_http_module_t接口的postconfiguration方法中将自定义的方法添加到handler动态数组中,这样,这个方法就会最
终添加到ngx_http_core_main_conf_t->phase_engine中

//空间创建及赋值参考ngx_http_init_phase_handlers
typedef struct { //ngx_http_phase_engine_t结构体是保存在全局的ngx_http_core_main_conf_t结构体中的
    /* handlers是由ngx_http_phase_handler_t构成的数组首地址,它表示一个请求可能经历的所有ngx_http_handler_pt处理方法,
    配合ngx_http_request_t结构体中的phase_handler成员使用(phase_handler指定了当前请求应当执行哪一个HTTP阶段)*/
    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_init_phase_handlers
    /*
    表示NGX_HTTP_REWRITE_PHASE阶段第1个ngx_http_phase_handler_t处理方法在handlers数组中的序号,用于在执行HTTP请求的任何阶段中
    快速跳转到NGX_HTTP_REWRITE_PHASE阶段处理请求
     */
    ngx_uint_t                 location_rewrite_index; //赋值参考ngx_http_init_phase_handlers
} ngx_http_phase_engine_t;

  一般来说,各个 HTTP 模块都是在 postconfiguration 方法中将自定义方法添加到 handler 动态数组中的,这样这个方法在 HTTP 框架初始化过程中最终会被添加到 phase_engine 中所有 postconfiguration 回调函数调用前,nginx 执行了 ngx_http_init_phases 初始化 phase 结构,然后在调用所有的 postconfiguration 回调函数以后执行了 ngx_http_init_phase_handlers 为 phase_engine 字段赋值;

/*
有7个HTTP阶段(NGX_HTTP_POST_READ_PHASE、NGX_HTTP_SERVER_REWRITE_PHASE、NGX_HTTP_REWRITE_PHASE、NGX_HTTP_PREACCESS_PHASE、
NGX_HTTP_ACCESS_PHASE、NGX_HTTP_CONTENT_PHASE、NGX_HTTP_LOG_PHASE)是允许任何一个HTTP模块实现自己的ngx_http_handler_pt处
理方法,并将其加入到这7个阶段中去的。在调用HTTP模块的postconfiguration方法向这7个阶段中添加处理方法前,需要先将phases数
组中这7个阶段里的handlers动态数组初始化(ngx_array_t类型需要执行ngx_array_init方法初始化),在这一步骤中,遁过调
用ngx_http_init_phases方法来初始化这7个动态数组。

    通过调用所有HTTP模块的postconfiguration方法。HTTP模块可以在这一步骤中将自己的ngx_http_handler_pt处理方法添加到以上7个HTTP阶段中。
这样在具体的每个阶段就可以执行到我们的handler回调
*/
static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
                       cf->pool, 2, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
                       cf->pool, 4, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    return NGX_OK;
}
/*
在各个HTTP模块能够介入的7个阶段中,实际上共享了4个checker方法:ngx_http_core_generic_phase、ngx_http_core_rewrite_phase、
ngx_http_core_access_phase、ngx_http_core_content_phase。这4个checker方法的主要任务在于,根据phase_handler执行某个HTTP模块实现的
回调方法,并根据方法的返回值决定下一步执行动作
*/
//cmcf->phases[]数组各个阶段的ngx_http_handler_pt节点信息全部赋值给cmcf->phase_engine.handlers数组队列
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    ph = ngx_pcalloc(cf->pool, 
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
    if (ph == NULL) {
        return NGX_ERROR;
    }

    cmcf->phase_engine.handlers = ph;
    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) { //所有阶段的checker在ngx_http_core_run_phases中调用
        /*
        NGX_HTTP_SERVERREWRITEPHASE阶段,和第3阶段NGXHTTPREWRITE_PHASE都属于地址重写,也都是针对rewrite模块而设定的阶段,前
        者用于server上下文里的地址重写,而后者用于location上下文里的地址重写。为什么要设置两个地址重写阶段,原因在于rewrite模块
        的相关指令(比如rewrite、if、set等)既可用于server上下文.又可用于location上下文。在客户端请求被Nginx接收后,首先做server
        查找与定位,在定位到server(如果没查找到就是默认server)后执行NGXHTTP_SERVER_REWRITEPHASE阶段上的回调函数,然后再进入到下
        一个阶段:NGX_HTTP_FIND_CONFIG_PHASE阶段。
          */
        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

/*
NGX_HTTP_FIND_CONFIG_PHASE、NGX_HTTP_POSTREWRITE_PHASE、NGX_HTTP_POST_ACCESS_PHASE、NGX_HTTP_TRY_FILES_PHASE这4个阶段则
不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求,但是他们的会占用cmcf->phase_engine.handlers[]数组中的一个成员

 NGX_HTTP_FIND_CONFIG_PHASE阶段上不能挂载任何回调函数,因为它们永远也不 会被执行,该阶段完成的是Nginx的特定任务,即进行
 Location定位。只有把当前请求的对应location找到了,才能以该location上下文中取出更多精确的用户配置值,做后续的进一步请求处理。
 */
        case NGX_HTTP_FIND_CONFIG_PHASE: //该阶段则不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求方法,
//直接把http框架方法加入到cmcf->phase_engine.handlers数组中
find_config_index = n; ph->checker = ngx_http_core_find_config_phase; // ph->phase = i; n++; //所有处理方法数增加一次 ph++; //指向cmcf->phase_engine.handlers数组中下一个未用的ngx_http_phase_handler_t节点位置 continue; case NGX_HTTP_REWRITE_PHASE: if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.location_rewrite_index = n; } checker = ngx_http_core_rewrite_phase; break; case NGX_HTTP_POST_REWRITE_PHASE://该阶段则不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求方法,
//直接把http框架方法加入到cmcf->phase_engine.handlers数组中
if (use_rewrite) { ph->checker = ngx_http_core_post_rewrite_phase; ph->next = find_config_index;//注意:NGX_HTTP_POST_REWRITE_PHASE的下一阶段是NGX_HTTP_FIND_CONFIG_PHASE ph->phase = i; n++; ph++; } continue; case NGX_HTTP_ACCESS_PHASE: checker = ngx_http_core_access_phase; n++; break; case NGX_HTTP_POST_ACCESS_PHASE://该阶段则不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求方法,
//直接把http框架方法加入到cmcf->phase_engine.handlers数组中
if (use_access) {//只有配置了try_files aaa bbb后才会在 cmcf->phase_engine.handlers添加节点pt,
//见ngx_http_init_phase_handlers,如果没有配置,则直接跳过try_files阶段
ph->checker = ngx_http_core_post_access_phase; ph->next = n; ph->phase = i; ph++; } continue; case NGX_HTTP_TRY_FILES_PHASE://该阶段则不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求方法,
//直接把http框架方法加入到cmcf->phase_engine.handlers数组中
if (cmcf->try_files) { ph->checker = ngx_http_core_try_files_phase; ph->phase = i; n++; ph++; } continue; case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; default: //NGX_HTTP_POST_READ_PHASE NGX_HTTP_PREACCESS_PHASE NGX_HTTP_LOG_PHASE默认都是该函数 checker = ngx_http_core_generic_phase; } //计算i阶段的ngx_http_handler_pt处理方法在所有阶段处理方法中的位置,也就是计算i阶段的处理方法应该存放在cmcf->phase_engine.handlers数组中的具体位置 n += cmcf->phases[i].handlers.nelts; //注意:每一个阶段中最后加入到handlers[]中的会首先添加到cmcf->phase_engine.handlers for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) { ph->checker = checker; //每个阶段对应的checker,例如:NGX_HTTP_SERVER_REWRITE_PHASE阶段为ngx_http_core_rewrite_phase //i阶段的所有ngx_http_handler_pt处理方法通过ph连接起来,也就是全部添加到cmcf->phase_engine.handlers数组中,各个成员通过ph->next连接在一起 ph->handler = h[j]; ph->phase = i; ph->next = n;//下一阶段的第一个ngx_http_handler_pt处理方法在数组中的位置 ph++; //指向cmcf->phase_engine.handlers数组中下一个未用的ngx_http_phase_handler_t节点位置 } } return NGX_OK; }

 

posted @ 2020-01-07 22:13  codestacklinuxer  阅读(560)  评论(0编辑  收藏  举报