`
simohayha
  • 浏览: 1386271 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

nginx中handler的处理(二)

阅读更多
这次我们来看各个phase的checker的处理。

首先我们要弄明白一个事情,那就是在nginx中,一般来说,都是在NGX_HTTP_CONTENT_PHASE中调用outputfilter的,也就是说filter是在handler中调用的,这样看来只能有一个handler能够执行outputfiler.所以说在写nginx的handler模块的话,要注意不同的phase的返回值代表的不同意思。

而当ngx_http_core_run_phases返回,也就是某个phase的checker返回了NGX_OK的话,那么也就代表当前的请求已经处理结束。

按照顺序来。

首先是ngx_http_core_generic_phase,他主要是处理下面几个phase:

引用
post read, server rewrite, rewrite, and pre-access phases


这些phase的含义就不介绍了,前一篇blog已经有详细说明了。

在这几个phase的checker中,它将所要执行的handler的返回值分为4种类型。

1 NGX_OK 此时返回NGX_AGAIN,这里我们知道如果checker返回ok的话,整个handler的处理就会直接返回,也就是这次处理结束。并且这里phase_handler被赋值为ph->next,也就是下一个phase的索引。也就是说下次将会调用它的下一个phase的checker。

2 NGX_DECLINED 此时也返回NGX_AGAIN,而这个和上面有所不同,那就是phase_handler的赋值,这里这个值只是简单的++,也就是说会紧接着处理当前phase的下一个phase,只有当前的phase的handelr处理完毕了,才可能会处理下一个phase的handler

3 NGX_AGAIN 或者NGX_DONE,这个的话直接返回OK,也就是会结束handler的处理。

4 剩余的情况,主要是处理NGX_ERROR,以及NGX_HTTP_(也就是返回一些http的状态码)的处理。

ngx_int_t
ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
    ngx_int_t  rc;

//调用handler
    rc = ph->handler(r);

//处理NGX_OK
    if (rc == NGX_OK) {
//下一个phase的索引
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }

//处理NGX_DECLINED
    if (rc == NGX_DECLINED) {
//处理本phase的下一个handler
        r->phase_handler++;
        return NGX_AGAIN;
    }
//直接返回OK
    if (rc == NGX_AGAIN || rc == NGX_DONE) {
        return NGX_OK;
    }

    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */
//剩余的情况。
    ngx_http_finalize_request(r, rc);

    return NGX_OK;
}


这里我们会发现有一个ngx_http_finalize_request函数,顾名思义,这个函数就是用来释放request的,比如request相关的内存池,比如需要返回的一些状态码,比如需要断开连接等等操作,这个函数我们会在后面的专门分析nginx关闭request(包括handler以及filter)时会详细分析。

接着来看NGX_HTTP_FIND_CONFIG_PHASE这个phase的checker。
这个phase对应的checker是ngx_http_core_find_config_phase.

由于nginx中location的处理还是一个很复杂的过程,而这里我们主要来看phase的处理,因此这里就介绍下location这个phase的处理,后面会有更详细的分析location。

这个checker主要作用是讲url和对应得location关联起来,其实也就是讲对应的location的命令关联起来,nginx这里location的实现是用一个tree来做得,这里就不详细分析location的实现了。主要来看这个phase的处理。

这个checker有可能会被调用多次的。因为每次url的改变都会改变对应的location,因此在前一篇里面,我们能看到专门有个find_config_index的索引,来供其他的phase调用。

注意这个phase只会有一个handler也就是ngx_http_core_find_location,checker中调用这个handler来挂载对应的location。

然后通过find_location的返回值来进行不同的操作,比如当返回error的时候,就需要调用ngx_http_finalize_request来回收请求。

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;
//find location
    rc = ngx_http_core_find_location(r);

    if (rc == NGX_ERROR) {
//回收request,然后返回OK,也就是停止handler的处理。
        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);

//如果当前的请求不是internal,可是location却是内部请求,此时和error处理一样。
    if (!r->internal && clcf->internal) {
        ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
        return NGX_OK;
    }
.....................................................
}


接下来我们来看NGX_HTTP_POST_REWRITE_PHASE这个phase的checker,ngx_http_core_post_rewrite_phase。

这个rewrite phase主要是用来进行一些校验,比如rewrite的最大次数,如果大于这个次数,则会直接finalize request。

这里有几个变量需要注意:

uri_changed:表示当前的uri是否有改变,也就是是否有被重定向。
uri_changes:这个的初始值是11,它的意思就是最多的rewrite次数是10次。

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);
//如果没有rewrite的话,直接返回again,继续接下来的handler处理。
    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);
//changes减减。
    r->uri_changes--;

//如果为0,则说明rewrite太多次数,此时就直接finalize request
    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;
    }
//否则进入下一个phase的处理,这里我们知道如果有use_rewrite,它的下一个phase是NGX_HTTP_FIND_CONFIG_PHASE。

    r->phase_handler = ph->next;

    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
//重新给loc conf赋值
    r->loc_conf = cscf->ctx->loc_conf;
//然后返回again,继续下面的handler处理。
    return NGX_AGAIN;
}


然后是NGX_HTTP_ACCESS_PHASE这个phase的checker,ngx_http_core_access_phase。

这个chekcer的主要逻辑也是比较简单,就是通过handler的返回值来进行不同的操作。不过这里她还有一些自己特殊处理的部分,先来看相关的两个域:

1 satisfy  这个域对应于命令satisfy_any(这个已经被deprecated)和satisfy(建议使用这个)。其中它的默认值就是NGX_HTTP_SATISFY_ALL.这个命令的意思是如果值为all,则必须满足所有的验证,为any,则只满足一个就可以通过。

2 access_code 这个主要是传递给下一个phase,post_access phase进行处理的。

然后来看它的返回值。

1 NGX_DECLINED 表示由于一些原因,access没有进行,此时则直接返回again,然后继续下面的access。

2 NGX_AGAIN 和NGX_DONE  找了下貌似access模块没有返回这两个的情况,不过我们只要知道如果要handler直接终止的话,返回这两个就ok了。

3 NGX_OK 表示通过access验证。

4 其他,比如error 以及一些http的错误吗等, 就交给ngx_http_finalize_request处理。

access phase主要是用来验证当前的请求是否允许通过。来看代码

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;

//r->main不等于r则表示是子请求。
    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);
//调用handler
    rc = ph->handler(r);
//如果返回declined,则会接下来的handler处理。
    if (rc == NGX_DECLINED) {
        r->phase_handler++;
        return NGX_AGAIN;
    }
//返回ok,终止handler的处理。
    if (rc == NGX_AGAIN || rc == NGX_DONE) {
        return NGX_OK;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
//如果为all,则说明必须全部的access满足。
    if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
//如果通过一个则继续本phase的下一个handler验证。
        if (rc == NGX_OK) {
            r->phase_handler++;
            return NGX_AGAIN;
        }

    } else {
        if (rc == NGX_OK) {
//清零access_code码。
            r->access_code = 0;

            if (r->headers_out.www_authenticate) {
                r->headers_out.www_authenticate->hash = 0;
            }
//否则满足一个access就可以通过,所以下一个handler为下一个phase的handler。
            r->phase_handler = ph->next;
            return NGX_AGAIN;
        }
//如果没有通过
        if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {
//则设置access_code,这里可以看到如果多个handler都没通过,则access_code为最后一个
            r->access_code = rc;
//本phase的下一个handler
            r->phase_handler++;
            return NGX_AGAIN;
        }
    }

    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */
//最后清理request
    ngx_http_finalize_request(r, rc);
    return NGX_OK;
}


然后是NGX_HTTP_ACCESS_PHASE这个phase的checker,ngx_http_core_post_access_phase。这里要注意,只有use_access才会调用这个checker的,不然的话,checker链中就没有这个phase的。

这个phase比较简单,主要是处理access_code,也就是access phase中设置的access_code.

ngx_int_t
ngx_http_core_post_access_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph)
{
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "post access phase: %ui", r->phase_handler);
//如果有access_code
    if (r->access_code) {
//打印error
        if (r->access_code == NGX_HTTP_FORBIDDEN) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "access forbidden by rule");
        }
//回收request
        ngx_http_finalize_request(r, r->access_code);
        return NGX_OK;
    }
//否则进入下一个handler
    r->phase_handler++;
    return NGX_AGAIN;
}


然后就是NGX_HTTP_TRY_FILES_PHASE这个phase的checker,ngx_http_core_try_files_phase。

这个phase的代码有点小多,不过逻辑还是比较简单的,那就是如果请求文件不在的话,则会打开try_file定义的location然后调用对应的文件,最后正常返回,也就是返回AGAIN.

最后就是NGX_HTTP_CONTENT_PHASE的checker,ngx_http_core_content_phase。

这个phase一般来说,我们编写的大部分handler都是属于这个phase的,他就是用来生成内容。

不过这里要注意一个域content_handler,如果这个handler存在的话,整个content phase就会只执行这一个handler,然后返回NGX_OK.

然后来分析handler返回值。

1 NGX_DONE 此时说明handler执行成功。

2 NGX_DECLINED 此时表示需要执行本phase的下一个handler

3 其他,此时需要finalize request。

来看代码
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;

//如果有content_handler,则直接调用conteng_handler
    if (r->content_handler) {
        r->write_event_handler = ngx_http_request_empty_handler;
        ngx_http_finalize_request(r, r->content_handler(r));
//返回NGX_OK
        return NGX_OK;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "content phase: %ui", r->phase_handler);
//否则调用push进去的handler
    rc = ph->handler(r);

    if (rc == NGX_DONE) {
//直接返回ok,停止处理。
        return NGX_OK;
    }

    if (rc != NGX_DECLINED) {
//嗲用finalize requst
        ngx_http_finalize_request(r, rc);
        return NGX_OK;
    }

    /* rc == NGX_DECLINED */

    ph++;

//如果下一个handler的checker存在则返回again,以待下次调用
    if (ph->checker) {
        r->phase_handler++;
        return NGX_AGAIN;
    }

    /* no content handler was found */

//没有handler的情况。
    if (r->uri.data[r->uri.len - 1] == '/' && !r->zero_in_uri) {

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





2
1
分享到:
评论

相关推荐

    深入剖析Nginx

    然后,分别深入分析了Nginx的进程模型、数据结构、配置指令、主要功能模块、I/O事件处理、变量机制、客户端请求过程、Filter模块实例、负载均衡策略以及Handler模块等。附录部分提供了Nginx的编译模块、运行配置等...

    Nginx源码剖析

    本书详细介绍了Nginx的进程模型,内存管理,request请求的解析,handler的处理等

    nginx开发从入门到精通

    nginx开发从入门到精通,nginx负载均衡技术学习资料,nginx handler、过滤模块、upstream模块讲解,原理解析,架构详解。比较详尽。

    详解Nginx 工作原理

    Nginx工作原理 Nginx由内核和模块组成。...handler模块负责处理请求,完成响应内容的生成,而filter模块对响应内容进行处理。 用户根据自己的需要开发的模块都属于第三方模块。正是有了这么多模块的支撑,Ng

    Nginx丢弃http包体处理实例详解

    Nginx丢弃http包体处理实例详解 http框架丢弃http请求包体和上一篇文章http框架接收包体, 都是由http框架提供的两个方法,供http各个模块调用,从而决定对包体做什么处理。是选择丢弃还是接收,都是由模块决定的。...

    nginx-lua-contact:Nginx + Lua 联系表单处理程序

    Nginx + Lua 联系表单处理程序示例简单的联系表单处理程序,接收表单数据并将其发送到指定的电子邮件地址的 SMTP 服务器。 适用于 ngx_lua。 试一试(使用 vagrant 和 virtualbox): git clone ...

    nginx处理http请求实例详解

    本文在这基础上分析nginx服务器收到http请求行、请求头部后,http框架是如何调度各个http模块共同...因为一个http请求由11个处理阶段组成,而每一个处理阶段都允许多个http模块介入,因此在这个函数中,将调度各个阶段

    详解Nginx的配置函数对于请求体的读取

    nginx核心本身不会主动读取请求体,这个工作是交给请求处理阶段...这两个接口是nginx核心提供的处理请求体的标准接口,如果希望配置文件中一些请求体相关的指令(比如client_body_in_file_only,client_body_buffer_si

    Nginx学习笔记之事件驱动框架处理流程

    其中包括将“请求连接”这样一个读事件对应的处理方法(handler)设置为ngx_event_accept函数,并将此事件添加到epoll模块中。当有新连接事件发生时,ngx_event_accept就会被调用。大致流程是这样: worker进程在ngx...

    nginx-redmine-access:基于Nginx的基于Redmine的身份验证

    通过传入句柄和auth领域来创建AccessHandler的实例,然后handle方法将处理当前的nginx请求。 例如, init_by_lua ' local dbi = require("DBI") local access = require("access") local db, err = dbi....

    tcp-nginx-module:使用nginx作为通用TCP服务器框架

    tcp-nginx-module 使用nginx作为通用TCP服务器框架 描述 写这些的动机是为了使用nginx作为一个通用的TCP服务器框架,所以叫ngx tcp。ngx tcp.jpg说明了这个框架。大部分代码是从nginx邮件模块修改而来的。我开发了...

    nginx共享内存机制详解

    nginx的共享内存,是其能够实现高性能的主要原因之一,而其主要是用于对文件的缓存。本文首先会讲解共享内存的使用方式,然后会讲解nginx是如何实现共享内存的管理的。 1. 使用示例 nginx声明共享内存的指令为: ...

    lemonldap-lua-handler:LemonLDAP 具有缓存功能的基本处理程序

    柠檬ldap-lua-处理程序 LemonLDAP::NG websso 具有缓存功能的基本处理程序 如何安装 安装在 debian 上测试的 nginx:nginx-extras 1.4.1 apt-get install nginx-extras ... 配置 nginx 以使用处理程序: 在 /etc/ngi

    MiniServer v2.0 php运行环境(Apache+Nginx+MySQL+PHP)

    4.菜单中有修改端口的功能,修改前请确保其他软件没有占用所修改的端口,如果遇到MiniServer中某组件无法正常启动,请使用菜单中的调试模式检查错误信息,或者检查端口占用情况 5.请勿在在含有中文路径或目录下使用...

    详解Nginx反向代理WebSocket响应403的解决办法

    在Nginx反向代理一个带有WebSocket功能的Spring Web程序(源代码地址 )时,发现访问WebSocket接口时总是出现403响应,Nginx的配置参考的是 官方文档 : http { // ssl 相关配置 ... map $http_upgrade $...

    nginx线程池源码分析

    周末看了nginx线程池部分的代码,顺手照抄了一遍,写成了自己的版本。实现上某些地方还是有差异的,不过基本结构全部摘抄。  在这里分享一下。如果你看懂了我的版本,也就... CB_FUN handler; //任务函数(返回值必

    基于SSM框架的前后端分离新闻网站+源代码+文档说明

    第二步:修改src\main\resources\jdbc.properties中的数据库连接方式 第三步:按照nginx-default.conf的配置修改nginx.conf,并将root路径指定为StaticWeb的文件夹位置 第四步:修改src/main/java/...

    itwc

    base_request_handler* echo_request_handler::Init(std::string location_prefix, NginxConfig &config) base_request_handler* static_request_handler::Init(std::string location_prefix, NginxConfig &config...

    Nginx源码研究之nginx限流模块详解

    高并发系统常见的限流有:限制总并发数(数据库连接池)、限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(nginx的limit_req模块,用来限制每秒的平均速率);...

Global site tag (gtag.js) - Google Analytics