菜鸟学习nginx之HTTP请求处理(2)

在上一篇介绍了Nginx定义的11个阶段,本篇将深入介绍Nginx是如何处理HTTP请求的。

一、ngx_http_process_request

/**
 * 处理HTTP请求
 * @param r HTTP请求
 * 当成功接收到请求行和Header时 就可以处理HTTP请求流程
 * 至于是否存在body 则在后续流程中处理
 */
void ngx_http_process_request(ngx_http_request_t *r)
{
    ngx_connection_t *c;

    c = r->connection;

#if (NGX_HTTP_SSL)

    ...

#endif
    //既然已经开始处理请求 表明没有发生超时事件 因此要及时删除定时器
    if (c->read->timer_set)
    {
        ngx_del_timer(c->read);
    }

#if (NGX_STAT_STUB)
    ...
#endif
    /**
     * 重新设置读写事件
     * 为什么设置成ngx_http_request_handler:
     * 进入此函数表示已经成功接收到HTTP请求行以及Header 后续再有读事件可能就是接收
     * body。如果接收body只需要ngx_http_request_handler处理即可。 不需要再走过往
     * 流程
     */
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;

    r->read_event_handler = ngx_http_block_reading; //对于epoll模型下 没有实际意义  

    /* 调度Nginx中所有HTTP模块协同处理该请求 */
    ngx_http_handler(r);
    
    /**
     * 执行post请求 这里的post请求相当于子请求 与HTTP协议中post请求不一样
     */
    ngx_http_run_posted_requests(c);
}

讲部分没有实际意义的代码删除掉,可以很清楚知道它的逻辑:

1、设置以后读写事件处理函数为ngx_http_request_handler,只针对当前请求来说。

2、调用所有HTTP模块协同处理当前请求,ngx_http_handler。

3、如果存在post请求,则处理post请求。这里可以理解成子请求。 

二、ngx_http_handler

/**
 * 调用所有HTTP模块协同处理请求
 * @param r HTTP请求
 */
void ngx_http_handler(ngx_http_request_t *r)
{
    ngx_http_core_main_conf_t *cmcf;

    r->connection->log->action = NULL;

    if (!r->internal)
    {//表示 当前请求来自客户端 而非内部请求
        switch (r->headers_in.connection_type)
        {
        case 0:
            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
            break;

        case NGX_HTTP_CONNECTION_CLOSE:
            r->keepalive = 0;
            break;

        case NGX_HTTP_CONNECTION_KEEP_ALIVE:
            r->keepalive = 1;
            break;
        }

        r->lingering_close = (r->headers_in.content_length_n > 0 || r->headers_in.chunked);
        r->phase_handler = 0;
    }
    else
    {// 表示内部请求 例如生成子请求
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
        r->phase_handler = cmcf->phase_engine.server_rewrite_index;
    }

    r->valid_location = 1;
#if (NGX_HTTP_GZIP)
    r->gzip_tested = 0;
    r->gzip_ok = 0;
    r->gzip_vary = 0;
#endif

    r->write_event_handler = ngx_http_core_run_phases; //设置写事件处理函数
    ngx_http_core_run_phases(r);// 处理请求
}

三、执行流水线ngx_http_core_run_phases

/**
 * 执行流水线 处理HTTP请求
 * @param r HTTP请求
 */
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)
    {
        //调用checker函数 然后再执行handler函数
        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); //ngx_http_core_rewrite_phase ngx_http_core_find_config_phase ngx_http_core_generic_phase ngx_http_core_access_phase

        if (rc == NGX_OK)
        {
            return;
        }
    }
}

四、checker函数定义

由于checker函数比较多,这里介绍两个checker函数,其中一个是比较经典的ngx_http_core_generic_phase,另外一个是我们经常用到的阶段NGX_HTTP_CONTENT_PHASE所对应的ngx_http_core_content_phase。

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

    if (rc == NGX_OK)
    {
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }

    if (rc == NGX_DECLINED)
    {
        r->phase_handler++;
        return NGX_AGAIN;
    }

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

回到函数handler返回值说明:

返回值说明
NGX_OK结束本阶段处理调到下一阶段。例如:在NGX_HTTP_ACCESS_PHASE阶段有两个HTTP模块A和B,都注册了该回调函数。当A模块的回调函数返回NGX_OK,则不会执行B模块定义的回调函数。而是跳到下一个阶段,如NGX_HTTP_POST_ACCESS_PHASE。
NGX_DECLINED继续执行本阶段中下一个处理函数。例如,在NGX_HTTP_ACCESS_PHASE阶段有两个HTTP模块A和B,都注册了该回调函数。当A模块的回调函数返回NGX_DECLINED,则执行B模块定义的回调函数,且阶段不变仍为NGX_HTTP_ACCESS_PHASE。

NGX_AGAIN

NGX_DONE

表示handler函数在一次调度中是无法完成相应的任务,需要多次调度。此时会把控制权交给事件驱动。
其他直接结束当前HTTP请求。

 4.2、ngx_http_core_content_phase

/**
 * http content阶段处理函数
 * @param r http请求
 * @param ph handler对象
 */
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赋值在函数ngx_http_update_location_config中.
     * 取值实际为ngx_http_core_loc_conf_t中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);

这个地方需要特别说明一下:我们在上一篇介绍了,NGX_HTTP_CONTENT_PHASE阶段特有的介入方法。Nginx为了访问方便,在ngx_http_request_t中增加了一个回调函数content_handler,其值实际为ngx_http_core_loc_conf_t中handler方法,可参考ngx_http_update_location_config函数。所以,当我们自定义了回调函数,这里就会执行自定义回调函数,否则执行默认方法。

    rc = ph->handler(r);

    if (rc != NGX_DECLINED)
    {//结束HTTP请求
        ngx_http_finalize_request(r, rc);
        return NGX_OK;
    }

    /* rc == NGX_DECLINED */

    ph++;

    if (ph->checker)
    {//表示当前handler不是最后一个 继续执行下一个handler
        r->phase_handler++;
        return NGX_AGAIN;
    }

    /* no content handler was found */
    /** 
     * 已经是最后一个handler
     * 根据请求uri不一样 返回不同的响应
     * 1) 根目录请求 返回403
     * 2) 其他返回404
     */
    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;
}

五、总结

本篇以及上一篇介绍了Nginx是如何处理HTTP请求的。这里需要明确两点内容:

1、Nginx中11个阶段是如何管理以及流水线处理方式

2、Nginx中NGX_HTTP_CONTENT_PHASE阶段中,自定义回调函数是如何介入和调用的。

这里并没有把所有checker函数都介绍,主要原因是几乎每一个checker函数都很类似,只需要简单看一下就可以。如果仍然有不清楚的,可阅读《深入理解Nginx模块开发与架构解析》第二版,第11.6节内容。下一篇介绍处理HTTP body。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值