现在的位置: 首页 > 综合 > 正文

Nginx的phase的调用

2014年07月03日 ⁄ 综合 ⁄ 共 10418字 ⁄ 字号 评论关闭

前面的文章已经说了,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跑一遍phase
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);  //获取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_t
ngx_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_t
ngx_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_t
ngx_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_t
ngx_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阶段的checker
ngx_int_t
ngx_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_t
ngx_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_t
ngx_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了。

抱歉!评论已关闭.