Nginx的phase的调用

来源:互联网 发布:电信网络资源管理 编辑:程序博客网 时间:2024/05/20 18:45

前面的文章已经说了,Nginx把整个http的请求分为11个phase,每个phase都会有其对应的handler,有的phase只有一个handler,有的phase则会有多个handler,而且nginx将他们的handler组织成了类似于链表的结构,方便在调用的时候可以按照phase的顺序进行调用。这篇文章就来详细心的讲如何来调用这些handler的。

在前面的文章我们也已经说过,当nginx获取连接,然后构造request,对请求进行一些预处理以后,就会调用ngx_http_core_run_phases函数,对这个请求跑一遍phase,我们首先还来看看ngx_http_core_run_phases的定义:

//该函数用于对request跑一遍phasevoidngx_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);  //获取ngx_http_core_main_conf_t    ph = cmcf->phase_engine.handlers;   //获取所有的phase handler    /* 遍历phase上注册的所有handler,这里是以r->phase_handler为索引组成的链表 */      while (ph[r->phase_handler].checker) {        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);        if (rc == NGX_OK) {    //表示已经处理完成了,那么可以直接退出了。            return;        }    }
request的phase_handler域用来标记当前request所处在的phase对应的handler的下标,ph指向了ngx_http_core_main_conf_t结构中保存的所有handler结构,这里我们可以看到其实实际调用的是checker函数而不是handler函数,这是因为在checker函数中会调用当前对应的handler函数。

其实分析对phase的处理函数的调用其实就是分析每个phase的checker函数。嗯,接下来就进行分析。

首先来看的phase是NGX_HTTP_POST_READ_PHASE,它主要用于读取post的请求,它的checker函数是ngx_http_core_generic_phase:

ngx_int_tngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){    ngx_int_t  rc;    /*     * generic phase checker,     * used by the post read and pre-access phases     */    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "generic phase: %ui", r->phase_handler);    rc = ph->handler(r);   //调用当前的handler函数处理request    if (rc == NGX_OK) {   //表示这个phase的处理已经完成了,那么可以进入下一个phase了        r->phase_handler = ph->next;   //将r的phase_hander的数值设置为下一个phase的第一个handler        return NGX_AGAIN;  //返回NGX_AGIN,说明只是当前的phase处理完了,但是不代表整个request处理完了,因而还要继续接下来的phase    }    if (rc == NGX_DECLINED) {   //表示这个request不适合用这个handler来处理,那么交个下面的handler来处理        r->phase_handler++;    //表示将handler的下标加1,可能会执行当前phase的下一个handler,也可能会执行下一个phase        return NGX_AGAIN;    }    if (rc == NGX_AGAIN || rc == NGX_DONE) {  //这里表示整个request都完成了,那么可以返回 NGX_OK,直接退出整个phase的执行        return NGX_OK;    }    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */    ngx_http_finalize_request(r, rc);   // 遇到了错误,关闭request    return NGX_OK;}
嗯,上面代码其实很简单,注释已经说的很清楚了,好我们接下来看下一个phase,NGX_HTTP_SERVER_REWRITE_PHASE,这个phase主要用于处理server 级别的rewrite,它的checker函数是ngx_http_core_rewrite_phase:
ngx_int_tngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){    ngx_int_t  rc;    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "rewrite phase: %ui", r->phase_handler);    rc = ph->handler(r);  //用对应的handler来处理这个request    if (rc == NGX_DECLINED) {   //表示这个requet不适合用这个handler来处理,交给下一个handler        r->phase_handler++;        return NGX_AGAIN;    }    if (rc == NGX_DONE) {  //表示整个request已经处理完了,那么可以直接返回NGX_OK,退出phase的调用        return NGX_OK;      }    /* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_...  *///出错了,那么直接释放request    ngx_http_finalize_request(r, rc);    return NGX_OK;}
其实这个函数也很简单,与前面的那一个大同小异,注释就已经说的差不多了,好吧进入下一个phase,NGX_HTTP_FIND_CONFIG_PHASE,这个phase 主要是根据uri来查找对应的location,将它们对应起来,他的checker函数是ngx_http_core_find_config_phase:
ngx_int_tngx_http_core_find_config_phase(ngx_http_request_t *r,    ngx_http_phase_handler_t *ph){    u_char                    *p;    size_t                     len;    ngx_int_t                  rc;    ngx_http_core_loc_conf_t  *clcf;    r->content_handler = NULL;    r->uri_changed = 0;    rc = ngx_http_core_find_location(r);    //根据request的uri信息来查找location,先静态查找,再正则匹配    if (rc == NGX_ERROR) {        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);        return NGX_OK;    }    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);/* 非内部请求访问内部location是非法的,所有与error处理类似 */      if (!r->internal && clcf->internal) {        ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);        return NGX_OK;    }    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "using configuration \"%s%V\"",                   (clcf->noname ? "*" : (clcf->exact_match ? "=" : "")),                   &clcf->name);/* 根据匹配的location设置request的属性 */      ngx_http_update_location_config(r);    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "http cl:%O max:%O",                   r->headers_in.content_length_n, clcf->client_max_body_size);/* 判断请求内容大小是否超过限制 */    if (r->headers_in.content_length_n != -1        && !r->discard_body        && clcf->client_max_body_size        && clcf->client_max_body_size < r->headers_in.content_length_n)    {        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,                      "client intended to send too large body: %O bytes",                      r->headers_in.content_length_n);        (void) ngx_http_discard_request_body(r);        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);        return NGX_OK;    }//处理重定向    if (rc == NGX_DONE) {        ngx_http_clear_location(r);        r->headers_out.location = ngx_list_push(&r->headers_out.headers);        if (r->headers_out.location == NULL) {            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);            return NGX_OK;        }        /*         * we do not need to set the r->headers_out.location->hash and         * r->headers_out.location->key fields         */        if (r->args.len == 0) {            r->headers_out.location->value = clcf->name;        } else {            len = clcf->name.len + 1 + r->args.len;            p = ngx_pnalloc(r->pool, len);            if (p == NULL) {                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                return NGX_OK;            }            r->headers_out.location->value.len = len;            r->headers_out.location->value.data = p;            p = ngx_cpymem(p, clcf->name.data, clcf->name.len);            *p++ = '?';            ngx_memcpy(p, r->args.data, r->args.len);        }        ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);        return NGX_OK;    }    r->phase_handler++;   //这里其实phase就进入了NGX_HTTP_REWRITE_PHASE,因为NGX_HTTP_FIND_CONFIG_PHASE只有一个handler    return NGX_AGAIN;}
这个phase的checker函数稍微复杂一些,而且看前面phase的初始化的时候我们可以发现NGX_HTTP_FIND_CONFIG_PHASE实际的handler是空的,只是占了一个位而已,所以最后phase_handler++就已经将phase移向了下一个phase,好吧,我们来看下一个phase,NGX_HTTP_REWRITE_PHASE,这个phase主要处理location级别的rewrite,它的checker函数是ngx_http_core_rewrite_phase,与NGX_HTTP_SERVER_REWRITE_PHASE相同,这里就不讲了。

好了,接下来进入NGX_HTTP_POST_REWRITE_PHASE,这个phase主要用于进行一些收尾和效验的工作,它的checker函数是ngx_http_core_post_rewrite_phase,用于判断uri是否已经重写,而且这个phase的next指向的是NGX_HTTP_FIND_CONFIG_PHASE,而且这个phase的实际handler也是空的,只不过占了一个位置而已。

ngx_int_tngx_http_core_post_rewrite_phase(ngx_http_request_t *r,    ngx_http_phase_handler_t *ph){    ngx_http_core_srv_conf_t  *cscf;    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "post rewrite phase: %ui", r->phase_handler);/** * uri_changed标志位表示uri是否有被重写。 * 如果没有的话,这里累加r->phase_handler,由于post rewrite只有一个handler, * 所以就会跳到下一个phase继续执行。 */    if (!r->uri_changed) {        r->phase_handler++;        return NGX_AGAIN;    }    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "uri changes: %d", r->uri_changes);    /*     * gcc before 3.3 compiles the broken code for     *     if (r->uri_changes-- == 0)     * if the r->uri_changes is defined as     *     unsigned  uri_changes:4     */    r->uri_changes--;    if (r->uri_changes == 0) {        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,                      "rewrite or internal redirection cycle "                      "while processing \"%V\"", &r->uri);        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);        return NGX_OK;    }/* * 在ngx_http_init_phase_handlers中,如果use_rewrite不为0,那么  * post rewrite phase handler的next指向find config的phase handler。  * 接下来会进入find config phase  */      r->phase_handler = ph->next;/* server rewrite有可能改变了server config,所以要对r->loc_conf重新赋值 */    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);    r->loc_conf = cscf->ctx->loc_conf;    return NGX_AGAIN;}
这个checker函数其实还是很简单的,说白了就是判断当前uri是否已经重写,如果没有的话,那么可以进入下一个phase,如果重写了那么就回到NGX_HTTP_FIND_CONFIG_PHASE。

好了接下来进入NGX_HTTP_PREACCESS_PHASE,这个phase主要用于一些比较粗的access,它的checker函数也是ngx_http_core_generic_phase,嗯,就不讲了,

接下来进入NGX_HTTP_ACCESS_PHASE,这个phase会进行一些存取控制,权限验证等,但是处理一般情况下是交给下一个phase来做的,这里只是做一些细粒度的access。它的checker函数是ngx_http_core_access_phase:

//NGX_HTTP_ACCESS_PHASE阶段的checkerngx_int_tngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){    ngx_int_t                  rc;    ngx_http_core_loc_conf_t  *clcf;//只能针对主请求    if (r != r->main) {        r->phase_handler = ph->next;        return NGX_AGAIN;    }    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "access phase: %ui", r->phase_handler);    rc = ph->handler(r);  //用相应的handler来处理/* 跳到本phase的下一个handler */  // 或者进入下一个phase    if (rc == NGX_DECLINED) {  //表示这个phase当前的这个handler不适合这个request        r->phase_handler++;        return NGX_AGAIN;    }/* 请求处理完毕 *///直接退出phase    if (rc == NGX_AGAIN || rc == NGX_DONE) {        return NGX_OK;    }    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);/* 相当于satisfy all,就是必须满足所有条件,所以继续执行access handler */     if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {        if (rc == NGX_OK) {            r->phase_handler++;            return NGX_AGAIN;        }    } else {    /* 否则只要满足任意一个条件即可,所以执行下一个phase的第一个handler */          if (rc == NGX_OK) {            r->access_code = 0;            if (r->headers_out.www_authenticate) {                r->headers_out.www_authenticate->hash = 0;            }            r->phase_handler = ph->next;            return NGX_AGAIN;        }        if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {            r->access_code = rc;            r->phase_handler++;            return NGX_AGAIN;        }    }    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */    ngx_http_finalize_request(r, rc);    return NGX_OK;}
嗯,代码还是很简单,一看就明白了。

嗯,接下来进入NGX_HTTP_POST_ACCESS_PHASE,这个phase主要是用于根据前面的access获取的access_code进行操作,checker函数是ngx_http_core_post_access_phase:

//NGX_HTTP_POST_ACCESS_PHASE阶段的checker函数ngx_int_tngx_http_core_post_access_phase(ngx_http_request_t *r,    ngx_http_phase_handler_t *ph){    ngx_int_t  access_code;    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "post access phase: %ui", r->phase_handler);    access_code = r->access_code;/* 设置了access_code,说明没有权限,则终结请求 */     if (access_code) {        if (access_code == NGX_HTTP_FORBIDDEN) {            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,                          "access forbidden by rule");        }        r->access_code = 0;        ngx_http_finalize_request(r, access_code);        return NGX_OK;    }    r->phase_handler++;  //跳到下一个phase    return NGX_AGAIN;}
嗯,代码很简单,进入下一个phase,NGX_HTTP_TRY_FILES_PHASE,嗯这个phase对应的是配置文件里面的try_file命令。

接下来进入最重要的pahse,NGX_HTTP_CONTENT_PHASE,它用于最终生成request响应的信息,并将它们发送出去,我们大多数人编写的大部分模块都是在这个phase的。而且需要注意的是如果当前的location设置了其的content_handler,那么就只会执行这个handler,而不会执行其余的handler,它的checker函数是ngx_http_core_content_phase:

ngx_int_tngx_http_core_content_phase(ngx_http_request_t *r,    ngx_http_phase_handler_t *ph){    size_t     root;    ngx_int_t  rc;    ngx_str_t  path;/* * 在find config phase中如果匹配到的location具有handler,则会赋值给r->content_handler。 * 而这里可以看到,如果r->content_handler存在则只会执行这一个handler,然后返回。 * 也就是说如果location设置了handler,则只会执行这一个content handler,不会执行其他的。 */    if (r->content_handler) {        r->write_event_handler = ngx_http_request_empty_handler;        ngx_http_finalize_request(r, r->content_handler(r));        return NGX_OK;    }    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,                   "content phase: %ui", r->phase_handler);    rc = ph->handler(r);    if (rc != NGX_DECLINED) {   //表示当前的handler可以处理这个request,那么就代表这个request的response已经完成了,可以关闭了,并且终止phase        ngx_http_finalize_request(r, rc);        return NGX_OK;    }/* handler返回NGX_DECLINED会由接下来的content handler继续处理 */      /* rc == NGX_DECLINED */    ph++;/* 如果下一个handler的checker存在,则返回NGX_AGAIN,继续调用下一个handler */      if (ph->checker) {        r->phase_handler++;        return NGX_AGAIN;    }    /* no content handler was found */    if (r->uri.data[r->uri.len - 1] == '/') {        if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,                          "directory index of \"%s\" is forbidden", path.data);        }        ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);        return NGX_OK;    }    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");    ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);    return NGX_OK;}
嗯,这个checker函数也很简单,要注意的情况是这个phase的handler只会出现两种情况,要么可以处理这个request,那么就执行完成了,直接退出phase,或者不能处理,那么交给下一个handler。

好了,基本上的phase的调用就差不多了,接下来可以具体分析每一个phase的handler了。