前言

上一篇文章(https://blog.csdn.net/zzhongcy/article/details/86086369)简单说明了nginx的11个阶段,

今天网上看到一篇详细介绍的文章,这里转载记录一下。

处理HTTP请求的11个阶段

前面分析了nginx如何读取一个HTTP请求、如何查询到HTTP对应的配置,本节分析如何处理HTTP请求。

nginx将处理HTTP请求划分为了11个阶段,原因在于nginx是一个重度模块化的系统,划分为不同阶段以后,不同的模块可以根据自己的需求在相应的模块中添加自己的处理函数。

简单看看这11个模块的定义:

typedef enum {

// 在接收到完整的HTTP头部后处理的HTTP阶段

NGX_HTTP_POST_READ_PHASE = 0,

// 在将请求的URI与location表达式匹配前,修改请求的

// URI(所谓的重定向)是一个独立的HTTP阶段

NGX_HTTP_SERVER_REWRITE_PHASE,

// 根据请求的URL寻找匹配的location表达式,这个阶段

// 只能由ngx_http_core_module模块实现,不建议其他HTTP

// 模块模块重新定义这一阶段的行为

NGX_HTTP_FIND_CONFIG_PHASE,

// 在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location

// 之后再修改请求的URI

NGX_HTTP_REWRITE_PHASE,

// 这一阶段用于在rewrite重写URL后,防止错误的nginx配置导致

// 死循环(递归地修改URI),因此,这一阶段仅由ngx_http_core_module

// 模块处理。目前,控制死循环的方法就是看rewrite次数,超过一定阈值

// 就认为出现了死循环,返回500

NGX_HTTP_POST_REWRITE_PHASE,

// 表示在处理NGX_HTTP_ACCESS_PHASE阶段决定请求的访问权限前,HTTP模块可以介入的处理阶段

NGX_HTTP_PREACCESS_PHASE,

// 这个阶段用于让HTTP模块判断是否允许这个请求访问Nginx服务器

NGX_HTTP_ACCESS_PHASE,

// 在NGX_HTTP_ACCESS_PHASE阶段中,当HTTP模块的handler处理函数返回不允许访问的错误码时(

// 实际就是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED),这里将负责向用户发送拒绝服务的

// 错误响应,因此这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾

NGX_HTTP_POST_ACCESS_PHASE,

// 这个阶段完全为try_files配置项而设立的,当HTTP访问静态文件资源时,try_files配置项可以

// 使这个请求顺序地访问多个静态文件资源,如果某一次访问失败,则继续访问try_files中指定的

// 下一个静态资源。

NGX_HTTP_PRECONTENT_PHASE,

// 用于处理HTTP请求内容的阶段,这是大部分HTTP模块最愿意介入的阶段

NGX_HTTP_CONTENT_PHASE,

// 处理完请求记录日志的阶段。

NGX_HTTP_LOG_PHASE

} ngx_http_phases;

  1. NGX_HTTP_SERVER_REWRITE_PHASE——“请求URI重写“阶段(比如内置的rewrite模块,支持if,break,return,rewrite,set等配置指令)
  2. NGX_HTTP_FIND_CONFIG_PHASE——根据请求位置查找配置信息(这个阶段不能自定义挂载处理函数)
  3. NGX_HTTP_REWRITE_PHASE——在location作用域内的URI重写
  4. NGX_HTTP_POST_REWRITE_PHASE——URI重写后的处理阶段(不能自定义挂载处理函数)
  5. NGX_HTTP_PREACCESS_PHASE——HTTP访问权限控制前一阶段(包含如limit_rate,limit_conn等模块)
  6. NGX_HTTP_ACCESS_PHASE——访问权限检查阶段(包含如auth_basic,或者第三方模块auth_request都在这个阶段)
  7. NGX_HTTP_POST_ACCESS_PHASE——访问权限检查后一阶段
  8. NGX_HTTP_TRY_FILES_PHASE——try_files指令的处理阶段
  9. NGX_HTTP_CONTENT_PHASE——内容生成阶段
  10. NGX_HTTP_LOG_PHASE——日志记录阶段

这11个阶段里,有一些是可以由模块开发者插入自己的处理函数,有一些只能使用nginx的http框架的实现。另外,每个阶段并不是一定只能有一个处理函数,有的可以提供多个处理函数,在同一个阶段中顺序被调用。

每个阶段你都可以注册任意数量的自定义处理器,除了下面几个

  • NGX_HTTP_FIND_CONFIG_PHASE——这个阶段没有处理器,只有一个查找配置信息的操作会被执行,还有添加Location头信息
  • NGX_HTTP_POST_ACCESS_PHASE——这个阶段没有处理器,只会解释处理ACCESS阶段的结果,不过配合倘若配置了satisfy指令(可以参照源代码)
  • NGX_HTTP_POST_REWRITE_PHASE——这个阶段没有处理器,只有内置的URI重写后的函数被调用
  • NGX_HTTP_TRY_FILES_PHASE——这个阶段只有nginx的try_files指令被调用

每个阶段都有自己的一系列处理函数,一旦把处理函数注册到对应的阶段,则函数可以返回下面的值。

  • NGX_OK——http请求已经被正确处理,需要传递到下一阶段
  • NGX_DECLINED——请求需要被发给本阶段的下一个处理器(handler)
  • NGX_AGAIN,NGX_DONE——请求已经被正确处理,而且需要被挂起知道网络事件(比如子请求结束,socket可写等)发生再次调用此处理器
  • NGX_ERROR,NGX_HTTP_..处理请求时出现错误

你需要获取ngx_http_core_module模块的配置结构体,然后将处理函数添加到需要的阶段(phase)

static ngx_int_t

ngx_http_sample_module_init(ngx_conf_t *cf)

{

ngx_http_handler_pt *h;

ngx_http_core_main_conf_t *cmcf;

cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);

if (h == NULL) {

return NGX_ERROR;

}

*h = ngx_http_sample_module_handler;

return NGX_OK;

}

数组phases对每个阶段都有个“入口”,每个“入口”包含一个处理器的指针,指向注册到本阶段的一组处理器。

处理器的调用顺序是反向的,最后注册到配置中的处理器会被最先调用。

从上面可以看出,请求处理的顺序和配置文件中的配置指令的先后顺序无关,无论配置文件中指令的顺序如何,各个阶段的处理函数都会按照预先的顺序执行。因此一个处理函数需要知道自己什么时候会被调用,何时需要返回NGX_DECLINED,而且保证减少性能损耗。

NGX_HTTP_ACCESS_PHASE阶段会调用一些处理器限制请求访问资源。这个阶段各个处理器的执行顺序由指令satisfy决定,处理器的返回值也有特定的意义。

  • NGX_OK——处理器允许请求访问URI资源
  • NGX_HTTP_FORBIDDEN,NGX_HTTP_UNAUTHORIZED——不允许访问资源

如果是配置了satisfy all,所有处理函数都需要返回NGX_OK保证能通过权限审核

如果配置了satisfy any,则至少有一个处理函数需要返回NGX_OK保证通过审核

Nginx在NGX_HTTP_CONTENT_PHASE阶段产生响应内容,所有请求都会到这个处理函数来,我们可以自定义自己的内容处理器,如果没有配置,则会调用默认的处理函数。

如何自定义自己的内容生成函数呢,下面是个例子

static char *

ngx_http_sample_module_command(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

ngx_http_core_loc_conf_t *clcf;

clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

clcf->handler = ngx_http_sample_handler;

return NGX_CONF_OK;

}

content handler 有下面几个特点:

  • 它是“不可恢复”的,即就算处理器返回NGX_AGAIN或者NGX_DONE也不会再次被调用,相反,处理器必须改变请求的read/write函数
  • 每个location都有自己专用的content handler
  • 如果自定义处理器返回NGX_DECLINED,则nginx会用默认的处理器去产生内容

那我们怎么知道自己的处理函数需要挂载在哪个阶段呢

虽然这篇博客不是对Nginx有所指责,但是确实是个问题。根据我的经验和nginx作者Igor Sysoev的建议,phases这个东西是从apache遗留下来的,对于模块开发者他们并不灵活,当然这可以改进,但是迄今为止也只能给点建议。

  • 如果你要放在phase阶段,则你的模块需要配合satisfy指令
  • 如果你想做权限控制,则放在pre-access阶段
  • 如果是URI重写或者操作变量,则放在rewrite阶段
  • 如果是产生响应内容,则放在content阶段,注意处理器的注册顺序
  • 如果是做日志处理,则放在logging阶段

什么时候需要用到content handler呢?

content phase handler和content handler有什么区别呢?

  • content phase handler是比较混乱的,所有请求到了content阶段都会调用这个特殊的处理器,而content handler则是在访问特定的location时才会被调用。
  • 每个location都可以访问多个content phase handler,而只能有一个content handler

一个结合这两种类型处理器的例子是nginx mogilefs module模块中处理PUT类型的请求(如下):

Main location中存在一个content phase handler,这个处理器有三个阶段(create_open命令,保存资源,create_close命令)每个阶段处理器都发起一个子请求,子请求结束时唤醒main content phase handler,在create_open和create_close命令中子请求会去访问有自己的content handler的location目录,这个处理函数通过upstrean模块和MogileFS的服务进行通信。

核心数据结构

ngx_http_phase_handler_t

定义每阶段相关的处理方法,在数据结构ngx_http_phase_handler_t中,包括以下成员:

  • ngx_http_phase_handler_pt checker:在处理任何一个HTTP阶段的时候,HTTP框架将会在checker方法已经实现的情况下首先调用该方法来处理请求。这种情况下不是直接调用handler方法,而是在checker中间接调用handler方法。但是所有的checker方法都是由框架中的ngx_http_core_module模块实现的,普通的HTTP模块无法重定义checker方法。
  • ngx_http_handler_pt handler:定义处理某阶段HTTP请求的回调函数。
  • ngx_uint_t next:将要执行的下一个HTTP处理阶段的索引。有了这个索引值,阶段的执行不见得一定得按照顺序来,可以向前或者向后跳转阶段来处理。

ngx_http_phase_engine_t

ngx_http_phase_engine_t结构体用于保存所有ngx_http_phase_handler_t组成的数组,其成员如下:

  • ngx_http_phase_handler_t *handlers:存放handler的数组。
  • ngx_uint_t server_rewrite_index:表示NGX_HTTP_SERVER_REWRITE_PHASE阶段第一个handler处理方法在handlers数组中的索引,用于在执行HTTP请求的任何阶段快速跳转到NGX_HTTP_SERVER_REWRITE_PHASE阶段处理请求。
  • ngx_uint_t location_rewrite_index:表示NGX_HTTP_REWRITE_PHASE阶段第一个处理方法在handlers数组中的索引。

ngx_http_core_main_conf_t中与HTTP阶段相关的成员

在ngx_http_core_main_conf_t结构体中,与HTTP处理阶段相关的成员有如下两个:

  • ngx_http_phase_engine_t phase_engine:控制运行过程中一个HTTP请求所要经过的HTTP处理阶段,将配合ngx_http_request结构体中的phase_handler成员使用。
  • ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]:用于在HTTP框架初始化时帮助各个HTTP模块在任意阶段中添加HTTP处理方法,由11个成员组成(因为有11个HTTP阶段)的数组,其中每个成员对应一个HTTP阶段。在HTTP框架初始化完毕之后,运行过程中的phases数组是无用的。

而ngx_http_phase_t结构体实际上就是一个存放handler的数组:

typedef struct {

ngx_array_t handlers;

} ngx_http_phase_t;

ngx_http_core_main_conf_t.phases数组,仅在启动时用于保存初始化注册进来的handler只用,是一个”静态“的数据,后续不会再做变动了。

核心流程

介绍完处理HTTP请求相关的核心数据结构,现在回到处理HTTP请求的流程分析中。

在前面分析读HTTP请求部分,提到过当读取完毕HTTP请求,nginx将进入到ngx_http_process_request函数正式开始处理HTTP请求。

由于到这里已经读取完毕HTTP的请求行以及header,所以不再有状态机机制,来看看这部分的核心流程:

  • 由于已经读取完毕HTTP请求行以及HTTP头部数据,此时不再存在接收HTTP请求头超时的问题,因此需要将读事件的定时器删除。
  • 由于也不再需要继续读取HTTP请求行以及头部,因此重新设置当前连接的读、写事件回调函数为ngx_http_request_handler。
  • 将ngx_http_request_t结构体的read_event_handler回调函数设置为ngx_http_block_reading,这个函数实际上并不做任何事情。可以理解为:在当前HTTP请求没有处理结束之前,即使再次有读事件被触发,也不做任何处理,实际上相当于读事件被阻塞了。
  • 接下来调用ngx_http_handler函数。

来看ngx_http_handler函数的流程:

  • 检查ngx_http_request_t结构体的internal标志位,区分以下情况:

    • 0:表示不需要重定向,设置ngx_http_request_t.phase_handler为0,表示从ngx_http_phase_engine_t指定数组的第一个回调方法开始执行。
    • 1:表示需要重定向,设置ngx_http_request_t.phase_handler为phase_engine.server_rewrite_index,server_rewrite_index索引保存的是handler数组中NGX_HTTP_REWRITE_PHASE处理阶段的第一个handler回调函数。phase_handler保存着将要执行的handlers数组中的索引,因此通过修改这个索引值,可以实现HTTP处理阶段的跳转。这里将这个索引值切换为NGX_HTTP_REWRITE_PHASE,意味着无论当前在哪个阶段,都将重新回到NGX_HTTP_REWRITE_PHASE阶段开始再次执行。这是nginx代码中实现请求可以反复rewrite重定向的基础。
  • 将ngx_http_request_t.write_event_handler设置为ngx_http_core_run_phases。
  • 调用ngx_http_core_run_phases函数。

终于来到与HTTP不同阶段处理流程相关的函数ngx_http_core_run_phases了,代码如下:

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

ph = cmcf->phase_engine.handlers;

while (ph[r->phase_handler].checker) {

rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

if (rc == NGX_OK) {

return;

}

}

}

可以看到该函数的核心流程就是:

  • 根据当前phase_handler索引,取出checker(如果存在的话)进行调用。
  • 返回值为非NGX_OK,意味着将继续向下执行phase_engine的各处理函数;
  • 反之,checker方法返回NGX_OK的时候,意味着控制权交还给nginx的事件模块,等待被IO事件唤醒时再次被触发调用。

处理HTTP请求的11个阶段

下面具体看处理HTTP请求的11个阶段。

HTTP阶段 checker
NGX_HTTP_POST_READ_PHASE ngx_http_core_generic_phase
NGX_HTTP_SERVER_REWRITE_PHASE ngx_http_core_rewrite_phase
NGX_HTTP_FIND_CONFIG_PHASE ngx_http_core_find_config_phase
NGX_HTTP_REWRITE_PHASE ngx_http_core_rewrite_phase
NGX_HTTP_POST_REWRITE_PHASE ngx_http_core_post_rewrite_phase
NGX_HTTP_PREACCESS_PHASE ngx_http_core_generic_phase
NGX_HTTP_ACCESS_PHASE ngx_http_core_access_phase
NGX_HTTP_POST_ACCESS_PHASE ngx_http_core_post_access_phase
NGX_HTTP_TRY_FILES_PHASE ngx_http_core_try_files_phase
NGX_HTTP_CONTENT_PHASE ngx_http_core_content_phase
NGX_HTTP_LOG_PHASE ngx_http_core_generic_phase

NGX_HTTP_POST_READ_PHASE(可添加自定义HTTP模块)

接收完毕HTTP请求头之后的第一个阶段,任何需要在接收完毕请求HTTP头之后添加处理的模块都可以放到这个阶段来。 当前realip模块就是在这个阶段,用于获取客户端的真是IP地址。

本阶段的checker函数是ngx_http_core_generic_phase,该函数也是是NGX_HTTP_POST_READ_PHASE、NGX_HTTP_PREACCESS_PHASE、NGX_HTTP_LOG_PHASE三个阶段的checker函数,根据调用handler的返回值,有以下的处理:

  • NGX_OK:执行下一个HTTP阶段的handler函数,跳过本阶段的下一个handler。
  • NGX_DECLINED:按照注册顺序执行本阶段下一个handler函数。
  • NGX_AGAIN:当前阶段的handler尚未处理结束,将控制权返回事件模块,等待下一次被事件触发再次被调用。
  • NGX_DONE:同NGX_AGAIN。
  • NGX_ERROR:调用ngx_http_finalize_request结束请求。

NGX_HTTP_SERVER_REWRITE_PHASE(可添加自定义HTTP模块)

server级别的URI重写阶段。在使用URI匹配location配置之前,修改URI用于重定向。 本阶段的checker函数为ngx_http_core_rewrite_phase,是NGX_HTTP_SERVER_REWRITE_PHASE和NGX_HTTP_REWRITE_PHASE的checker函数,根据handler返回值有以下处理:

  • NGX_DECLINED:递增phase_handler跳到下一个回调方法,并且返回NGX_AGAIN,因为返回的不是NGX_OK,因此HTTP框架不会把控制权返回给事件框架,而是马上执行下一个handler。
  • NGX_DONE:意味着handler无法在一次调度中处理完这个阶段,此时返回NGX_OK,等待被事件框架再一次唤醒执行。
  • 其他情况:调用ngx_http_finalize_request结束请求。

可以看到,ngx_http_core_generic_phase与ngx_http_core_rewrite_phase的区别在于:后者永远不会跨越同一个HTTP阶段的其他处理方法,就直接跳到下一个阶段来处理请求。原因在于:许多HTTP模块在NGX_HTTP_SERVER_REWRITE_PHASE和NGX_HTTP_REWRITE_PHASE同时处理URL重写这样的业务,HTTP框架认为这两个阶段的HTTP模块是完全平等的,序号靠前的HTTP模块并不优先级更高,不能决定后续模块是否被调用。

NGX_HTTP_FIND_CONFIG_PHASE(不可添加自定义HTTP模块)

用于根据URI查询匹配的location表达式。 参看前面的根据URI查询location一节内容。

需要注意的是:一次请求可能会多次来到该阶段,因为被rewrite之后的请求需要重新查找location。

NGX_HTTP_REWRITE_PHASE(可添加自定义HTTP模块)

location级别URI重写阶段,该阶段执行location级别的重写指令,有可能导致重新回到上面的NGX_HTTP_FIND_CONFIG_PHASE阶段。 checker函数同上面的NGX_HTTP_SERVER_REWRITE_PHASE阶段。

NGX_HTTP_POST_REWRITE_PHASE(不可添加自定义HTTP模块)

本阶段用于检查重写URI次数,不能超过10次以避免出现rewrite死循环的情况。本阶段不能自定义HTTP模块,checker函数为ngx_http_core_post_rewrite_phase。

NGX_HTTP_PREACCESS_PHASE(可添加自定义HTTP模块)

来到这一阶段,说明已经根据URI查询到了对应的location,即对应的loc_conf配置已经确定下来,该阶段主要主要用于访问资源控制,如限制频率、链接数等,本阶段的checker仍然是前面的ngx_http_core_generic_phase。

NGX_HTTP_ACCESS_PHASE(可添加自定义HTTP模块)

本阶段与上一阶段的区别在于,使用的checker为ngx_http_core_access_phase,主要用于检测用户发起的请求是否合法,比如IP地址是否允许访问等。

涉及到nginx配置文件中的satisfy配置项,根据本阶段handler的不同返回值,有以下区别:

  • NGX_OK:如果在配置文件中配置了satisfy all,那么将按照顺序执行下一个ngx_http_handler_pt方法;如果配置了satisfy any,那么将会执行下一个ngx_http_phase阶段的第一个ngx_http_handler_pt方法。
  • NGX_DECLINED:按照顺序执行下一个ngx_http_handler_pt方法。
  • NGX_AGAIN, NGX_DONE:当前的ngx_http_handler_pt方法尚未结束,当前方法有机会被再次调用。
  • NGX_HTTP_FORBIDDEN: 如果在配置文件中配置了satisfy any,那么将ngx_http_request_t中的access_code成员设置为返回值。按照顺序执行下一个ngx_http_handler_pt处理方法;如果是satisfy all,那么调用ngx_http_finalize_request结束请求。

     
     

    // NGX_HTTP_ACCESS_PHASE阶段的处理函数,用于控制用户发起的请求是否合法,

    // 如检测客户端的IP地址是否允许被访问

    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 != r->main表示当前请求只是派生出来的子请求,子请求不需要执行

    // NGX_HTTP_ACCESS_PHASE阶段的处理

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

    // 返回NGX_DECLINED意味着希望立刻执行下一个handler,

    // 无论是否属于NGX_HTTP_ACCESS_PHASE

    if (rc == NGX_DECLINED) {

    r->phase_handler++;

    return NGX_AGAIN;

    }

    // 返回AGAIN或者DONE意味着当前的NGX_HTTP_ACCESS_PHASE没有一次性执行完毕

    if (rc == NGX_AGAIN || rc == NGX_DONE) {

    return NGX_OK;

    }

    // 以下是返回值不是 NGX_DECLINED、NGX_AGAIN、NGX_DONE的情况

    // 由于NGX_HTTP_ACCESS_PHASE阶段在NGX_HTTP_FIND_CONFIG_PHASE阶段后面,

    // 因此此时请求已经找到了匹配的的location模块

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { // NGX_HTTP_SATISFY_ALL表示nginx配置文件中配置了satisfy all参数

    // satisfy all要求所有NGX_HTTP_ACCESS_PHASE阶段的handler方法共同作用于该请求

    if (rc == NGX_OK) { // 该模块返回NGX_OK,意味着该模块认为满足访问要求,需要继续调用下一个模块查询是否满足要求

    r->phase_handler++;

    return NGX_AGAIN;

    }

    // 如果返回值不是NGX_OK,则调用ngx_http_finalize_request结束请求

    } else { // 这里是配置中配置了satisfy any的情况

    // satisfy any不要求所有NGX_HTTP_ACCESS_PHASE阶段的模块都通过请求,只要有一个同意就可以放行

    // 返回NGX_OK,表示该模块认为有权限访问,因此phase_handler指向下一个handler

    // 并且access_code置为0

    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;

    }

    // NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED表示该模块不同意访问

    if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {

    if (r->access_code != 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(不可添加自定义HTTP模块)

本阶段处理上一阶段的处理结果,具体是根据ngx_http_request_t的access_code成员来进行处理。本阶段不允许添加自定义HTTP模块。

NGX_HTTP_TRY_FILES_PHASE(不可添加自定义HTTP模块)

本阶段的checker函数为ngx_http_core_try_files_phase,与nginx配置文件中的try_files配置指令相关。

NGX_HTTP_CONTENT_PHASE(可添加自定义HTTP模块)

处理HTTP请求内容的阶段,绝大部分用户自定义的HTTP模块工作在这一阶段。

与其他阶段不同的是,该阶段除了可以调用注册进来的HTTP模块的handler之外,还可以调用location配置中的content_handler,在存在location配置的content_handler的情况下,将优先调用这个函数处理请求。

 
 

// NGX_HTTP_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;

if (r->content_handler) {

// content_handler不为空意味着在NGX_HTTP_FIND_CONFIG_PHASE阶段中,

// 匹配了URI请求的location内,是否有HTTP模块把处理方法设置到了

// ngx_http_core_loc_conf_t结构体的handler成员中。

// ngx_http_request_empty_handler是什么都不做的方法,设置为这个函数

// 意味着再有写事件不做任何事情

r->write_event_handler = ngx_http_request_empty_handler;

// 将content_handler的返回值放入ngx_http_finalize_request中

ngx_http_finalize_request(r, r->content_handler(r));

return NGX_OK;

}

// 到了这里说明没有content_handler

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) {

// 没有返回NGX_DECLINED意味着不再执行该阶段的其他handler

ngx_http_finalize_request(r, rc);

return NGX_OK;

}

/* rc == NGX_DECLINED */

// 以下是返回NGX_DECLINED的情况

ph++;

if (ph->checker) { // 只有在该阶段下一个handler存在的情况下才继续执行

r->phase_handler++;

return NGX_AGAIN;

}

// 以下是找不到该阶段handler的情况

/* no content handler was found */

if (r->uri.data[r->uri.len - 1] == '/') {

// URI以/结尾,返回403

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");

// 返回404

ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);

return NGX_OK;

}

NGX_HTTP_LOG_PHASE

记录访问日志。

转自:

https://www.codedump.info/post/20190213-nginx-process-http-request/

https://blog.csdn.net/ciaos/article/details/7516715

Nginx HTTP请求的11个阶段相关推荐

  1. Nginx处理请求的11个阶段

    Nginx 处理请求的全过程一共划分为 11 个阶段(如图),按阶段由上到下依次执行 (上一阶段的所有指令执行完毕,才进入下一阶段) 各阶段的含义如下: ² post-read: 接收到完整的http ...

  2. Nginx处理HTTP请求的11个阶段

    nginx将一个HTTP请求分为11个处理阶段,这样做让每个HTTP模块可以仅仅专注于完成一个独立,简单的功能.而一个请求的完整处理过程可以由多个HTTP模块共同合作完成.可以极大的提高多个模块合作的 ...

  3. nginx请求的11个阶段

    目录 1.Http请求处理的11个阶段(示意图): 2. 11个阶段的顺序处理 3.POST_READ阶段 4.REWRITE阶段 5.FIND_CONFIG阶段 6.PREACCESS阶段 6.1. ...

  4. 深入操作系统底层分析nginx网络请求及响应过程

    0. 网络传输阶段 比如说主机A是家里windows的一台笔记本电脑,主机B是linux服务器上的一个nginx,其监听80或443等web端口. 在笔记本的浏览器发送了一个http get请求,其数 ...

  5. Nginx源码实现的细枝末节 11个阶段的实现丨Nginx Filter|中间件开发|惊群效应|负载均衡丨组件丨c/c++linux服务器开发

    Nginx源码实现的细枝末节 11个阶段的实现 视频讲解如下,点击观看: Nginx源码实现的细枝末节 11个阶段的实现丨Nginx Filter|中间件开发|惊群效应|负载均衡丨组件丨c/c++li ...

  6. nginx子请求并发处理

    https://blog.csdn.net/ApeLife 子请求并非http协议标准的实现,可以说是nginx所特有的设计, 为什么需要子请求呢? 一般认为这主要是为了提高nginx内部对单个客户端 ...

  7. nginx的请求接收流程(一)

    今年我们组计划写一本nginx模块开发以及原理解析方面的书,整本书是以open book的形式在网上会定时的更新,网址为http://tengine.taobao.org/book/index.htm ...

  8. 12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件不记录日志和过期时间

    - 12.10 Nginx访问日志 - 12.11 Nginx日志切割 - 12.12 静态文件不记录日志和过期时间# 12.10 Nginx访问日志 - 日志的格式- vim /usr/local/ ...

  9. nginx的请求接收流程(二)

    在ngx_http_process_request_line函数中,解析完请求行之后,如果请求行的uri里面包含了域名部分,则将其保持在请求结构的headers_in成员的server字段,heade ...

最新文章

  1. 微服务化后,这几点一定要注意
  2. webstorm设置注释颜色_简单5步了解相关矩阵的注释热图
  3. python中类方法与实例方法的区别-python中类方法、类实例方法、静态方法的使用与区别...
  4. omwin密立根油滴实验数据处理程序_大物实验报告更新啦~
  5. CSS命名规则--参考表
  6. c++的thread类(c++线程简单用法)
  7. PostgreSQL 简单的查询
  8. day15 java的抽象类
  9. JAVA运行环境设置
  10. Atitit。 《吠陀》 《梨俱吠陀》overview 经读后感  是印度上古时期一些文献的总称
  11. Allow CORS: Access-Control-Allow-Origin插件安装与使用教程【Chrome插件小白式教程】
  12. Citrix 桌面云 XenApp_XenDesktop_7.15 部署系列(六)配置虚拟桌面控制器
  13. 打破信息茧房-我主动获取信息的方法 -#3
  14. 峰哥建议你要多「旷课」
  15. 中国魔芋胶行业研究与投资前景预测报告(2022版)
  16. int类型整数的表示范围
  17. [生存志] 第22节 历代大事件概览 五代十国
  18. addEventListener()使用方法
  19. Pointnet(part_seg)train.py,test.py代码随记
  20. 2021-08-15关于水卡数据算法,求助大神

热门文章

  1. 5.7.1 使用向导创建交叉表查询
  2. win7系统服务print spooler 无法启动解决方法(开启及关闭方法)
  3. [php] thinkphp实现 163 qq 邮箱收发邮件(切实可用)
  4. 十万条评论告诉你,给《流浪地球》评1星的都是什么心态? | Alfred数据室
  5. htmla标签下划线去除_html超链接去掉下划线 html去除取消超链接下划线
  6. 制作一个favcion.ico
  7. Linux系统简介分区基础命令(ADMIN01-1)
  8. 2021-2027全球与中国电动垂直起降(eVTOL)飞行器基础设施市场现状及未来发展趋势
  9. 【slam十四讲第二版】【课本例题代码向】【第九讲~后端Ⅰ】【安装Meshlab】【BAL数据集格式】【ceres求解BA】【g2o求解BA】
  10. oracle 幻影读,索引+事务