• Nginx-HTTP之静态网页访问流程分析二


    11. HTTP 阶段执行

    下面会依次执行以下阶段:

    1. NGX_HTTP_SERVER_REWRITE_PHASE: 在将请求的 URI 与 location 表达式匹配前,修改请求的 URI (所谓重定向)是一个独立的 HTTP 阶段。
    2. NGX_HTTP_FIND_CONFIG_PHASE:根据请求的 URI 寻找匹配的 location 表达式,这个阶段只能由 ngx_http_core_module 模块实现,不建议其他模块介入该阶段。
    3. NGX_HTTP_REWRITE_PHASE:在 NGX_HTTP_FIND_CONFIG_PHASE 阶段寻找到匹配的 location 之后再修改请求的 URI。
    4. NGX_HTTP_POST_REWRITE_PHASE:这一阶段是用于在 rewrite 重写 URL 后,防止错误的 nginx.conf 配置导致死循环(递归地修改 URI)。因此,这一阶段仅由 ngx_http_core_module 模块处理。目前,控制死循环的方式很简单,首先检查 rewrite 的次数,如果一个请求超过 10 次重定向,就认为进入了 rewrite 死循环,这时在 NGX_HTTP_POST_REWRITE_PHASE 阶段就会向用户返回 500,表示服务器内部错误.
    5. NGX_HTTP_PREACCESS_PHASE:该阶段表示在处理 NGX_HTTP_ACCESS_PHASE 阶段决定请求的访问权限前,HTTP 模块可以介入的处理阶段。
    6. NGX_HTTP_ACCESS_PHASE:这个阶段用于让 HTTP 模块判断是否允许这个请求访问 Nginx 服务器.
    7. NGX_HTTP_POST_ACCESS_PHASE:在 NGX_HTTP_ACCESS_PAHSE 阶段中,当 HTTP 模块的 handler 处理函数返回不允许访问的错误码时(实际就是 NGX_HTTP_FORBIDDEN 或 NGX_HTTP_AUTHORIZED),这里将负责向用户发送拒绝服务的错误响应。因此,这个阶段实际上用于给 NGX_HTTP_ACCESS_PAHSE 阶段收尾.
    8. NGX_HTTP_CONTENT_PHASE:该阶段用于处理 HTTP 请求内容的阶段,这是大部分 HTTP 模块最愿意介入的阶段。

    11.1 NGX_HTTP_SERVER_REWRITE_PHASE

    基于当前环境配置下,首先执行的第一个阶段为 NGX_HTTP_SERVER_REWRITE_PHASE,由前面 ngx_http_init_phase_handlers 函数知,该阶段实现了 checker 回调函数 ngx_http_core_rewrite_phase。因此会首先调用该 checker 函数,在 checker 函数中才会去调用 handler 方法。

    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 执行完毕,按照顺序执行下一个 ngx_http_handler_pt 处理方法 */
        if (rc == NGX_DECLINED) {
            r->phase_handler++;
            return NGX_AGAIN;
        }
    
        /* 当前 ngx_http_handler_pt 处理方法尚未结束,这意味着该处理方法在当前阶段
         * 中有机会再次被调用,这里返回 NGX_OK,会使得 HTTP 框架立刻把控制权交还给
         * epoll 等事件模块,不再处理当前请求,唯有这个请求上的事件再次被触发时才会
         * 继续执行 */
        if (rc == NGX_DONE) {
            return NGX_OK;
        }
    
        /* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_...  */
    
        ngx_http_finalize_request(r, rc);
    
        return NGX_OK;
    }
    

    对于 NGX_HTTP_SERVER_REWRITE_PHASE 阶段,当前 HTTP 框架仅有 ngx_http_rewrite_module 模块介入了该阶段,handler 函数为 ngx_http_rewrite_handler。

    11.1.1 ngx_http_rewrite_handler

    static ngx_int_t
    ngx_http_rewrite_handler(ngx_http_request_t *r)
    {
        ngx_int_t                     index;
        ngx_http_script_code_pt       code;
        ngx_http_script_engine_t     *e;
        ngx_http_core_srv_conf_t     *cscf;
        ngx_http_core_main_conf_t    *cmcf;
        ngx_http_rewrite_loc_conf_t  *rlcf;
    
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
        /*
         * 表示 NGX_HTTP_REWRITE_PHASE 阶段第 1 个 ngx_http_phase_handler_t 处理方法
         * 在 handlers 数组中的序号,用于在执行 HTTP 请求的任何阶段中快速跳转到
         * NGX_HTTP_REWRITE_PHASE 阶段处理请求
         */
        index = cmcf->phase_engine.location_rewrite_index;
    
        if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) {
            /* skipping location rewrite phase for server null location */
            return NGX_DECLINED;
        }
    
        rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
    
        /* 若没有在配置文件中对该模块的相应配置项进行设置,则这里直接返回
         * NGX_DECLINED,表示该 handler 执行完毕,依次指向下一个 handler */
        if (rlcf->codes == NULL) {
            return NGX_DECLINED;
        }
    
        e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));
        if (e == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        e->sp = ngx_pcalloc(r->pool,
                            rlcf->stack_size * sizeof(ngx_http_variable_value_t));
        if (e->sp == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        e->ip = rlcf->codes->elts;
        e->request = r;
        e->quote = 1;
        e->log = rlcf->log;
        e->status = NGX_DECLINED;
    
        while (*(uintptr_t *) e->ip) {
            code = *(ngx_http_script_code_pt *) e->ip;
            code(e);
        }
    
        if (e->status < NGX_HTTP_BAD_REQUEST) {
            return e->status;
        }
    
        if (r->err_status == 0) {
            return e->status;
        }
    
        return r->err_status;
    }
    

    11.2 NGX_HTTP_FIND_CONFIG_PHASE

    该阶段是:根据请求的 URI 寻找匹配的 location 表达式,这个阶段只能由 ngx_http_core_module 模块实现,不建议其他模块介入该阶段

    该阶段实现的 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;
    
        /* content_handler: 
         * 表示 NGX_HTTP_CONTENT_PHASE 阶段提供给 HTTP 模块处理请求的一种方式,
         * content_handler 指向 HTTP 模块实现的请求处理方法 */
        r->content_handler = NULL;
        /* 标志位,为 1 时表示 URL 发生过 rewrite 重写 */
        r->uri_changed = 0;
    
        rc = ngx_http_core_find_location(r);
    
        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);
    
        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);
    
        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);
    
            r->expect_tested = 1;
            (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;
            }
    
            r->headers_out.location->hash = 1;
            ngx_str_set(&r->headers_out.location->key, "Location");
    
            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_clear_location(r);
                    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++;
        return NGX_AGAIN;
    }
    

    11.2.1 ngx_http_core_find_location

    /*
     * NGX_OK       - exact or regex match
     * NGX_DONE     - auto redirect
     * NGX_AGAIN    - inclusive match
     * NGX_ERROR    - regex error
     * NGX_DECLINED - no match
     */
    
    static ngx_int_t
    ngx_http_core_find_location(ngx_http_request_t *r)
    {
        ngx_int_t                  rc;
        ngx_http_core_loc_conf_t  *pclcf;
    #if (NGX_PCRE)
        ngx_int_t                  n;
        ngx_uint_t                 noregex;
        ngx_http_core_loc_conf_t  *clcf, **clcfp;
    
        noregex = 0;
    #endif
    
        pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        rc = ngx_http_core_find_static_location(r, pclcf->static_locations);
    
        if (rc == NGX_AGAIN) {
    
    #if (NGX_PCRE)
            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
            noregex = clcf->noregex;
    #endif
    
            /* look up nested locations */
    
            rc = ngx_http_core_find_location(r);
        }
    
        if (rc == NGX_OK || rc == NGX_DONE) {
            return rc;
        }
    
        /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */
    
    #if (NGX_PCRE)
    
        if (noregex == 0 && pclcf->regex_locations) {
    
            for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {
    
                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                               "test location: ~ "%V"", &(*clcfp)->name);
    
                n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri);
    
                if (n == NGX_OK) {
                    r->loc_conf = (*clcfp)->loc_conf;
    
                    /* look up nested locations */
    
                    rc = ngx_http_core_find_location(r);
    
                    return (rc == NGX_ERROR) ? rc : NGX_OK;
                }
    
                if (n == NGX_DECLINED) {
                    continue;
                }
    
                return NGX_ERROR;
            }
        }
    #endif
    
        return rc;
    }
    

    11.2.2 ngx_http_core_find_static_location

    /*
     * NGX_OK       - exact match
     * NGX_DONE     - auto redirect
     * NGX_AGAIN    - inclusive match
     * NGX_DECLINED - no match
     */
    
    static ngx_int_t
    ngx_http_core_find_static_location(ngx_http_request_t *r,
        ngx_http_location_tree_node_t *node)
    {
        u_char     *uri;
        size_t      len, n;
        ngx_int_t   rc, rv;
    
        len = r->uri.len;
        uri = r->uri.data;
    
        rv = NGX_DECLINED;
    
        for ( ;; ) {
    
            if (node == NULL) {
                return rv;
            }
    
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "test location: "%*s"",
                           (size_t) node->len, node->name);
    
            /* 以最小的长度为准 */
            n = (len <= (size_t) node->len) ? len : node->len;
    
            /* 比较这两个字符串的前 n 个字符是否相同,相同返回 0 */
            rc = ngx_filename_cmp(uri, node->name, n);
    
            if (rc != 0) {
                /* 当前节点不匹配 */
                node = (rc < 0) ? node->left : node->right;
    
                continue;
            }
    
            if (len > (size_t) node->len) {
    
                if (node->inclusive) {
    
                    r->loc_conf = node->inclusive->loc_conf;
                    rv = NGX_AGAIN;
    
                    node = node->tree;
                    uri += n;
                    len -= n;
    
                    continue;
                }
    
                /* exact only */
    
                node = node->right;
    
                continue;
            }
    
            if (len == (size_t) node->len) {
    
                if (node->exact) {
                    r->loc_conf = node->exact->loc_conf;
                    return NGX_OK;
    
                } else {
                    r->loc_conf = node->inclusive->loc_conf;
                    return NGX_AGAIN;
                }
            }
    
            /* len < node->len */
    
            if (len + 1 == (size_t) node->len && node->auto_redirect) {
    
                r->loc_conf = (node->exact) ? node->exact->loc_conf:
                                              node->inclusive->loc_conf;
                rv = NGX_DONE;
            }
    
            node = node->left;
        }
    }
    

    11.3 NGX_HTTP_REWRITE_PHASE

    该阶段是:在 NGX_HTTP_FIND_CONFIG_PHASE 阶段寻找到匹配的 location 之后再修改请求的 URI。

    该阶段实现的 checker 方法为 ngx_http_core_rewrite_phase,介入该阶段的模块也仅为 ngx_http_rewrite_module,handler 为 ngx_http_rewrite_handler。因此与 NGX_HTTP_SERVER_REWRITE_PHASE 阶段一样。具体可看该阶段的实现。

    11.4 NGX_HTTP_POST_REWRITE_PHASE

    这一阶段是用于在 rewrite 重写 URL 后,防止错误的 nginx.conf 配置导致死循环(递归地修改 URI)。因此,这一阶段仅由 ngx_http_core_module 模块处理。目前,控制死循环的方式很简单,首先检查 rewrite 的次数,如果一个请求超过 10 次重定向,就认为进入了 rewrite 死循环,这时在 NGX_HTTP_POST_REWRITE_PHASE 阶段就会向用户返回 500,表示服务器内部错误.

    该阶段实现的 checker 方法为 ngx_http_core_post_rewrite_phase(不允许其他模块介入):

    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);
    
        /* 标志位,为 1 表示 URL 发生过 rewrite 重写 */
        if (!r->uri_changed) {
            /* 若没有发生过 rewrite 重写,phase_handler 加 1,按序执行下一个 handler */
            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;
        }
    
        r->phase_handler = ph->next;
    
        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
        r->loc_conf = cscf->ctx->loc_conf;
    
        return NGX_AGAIN;
    }
    

    11.5 NGX_HTTP_PREACCESS_PHASE

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

    该阶段默认的 checker 方法为 ngx_http_core_generic_phase,该阶段分别有以下两个模块介入:

    • ngx_http_limit_req_module: handler 为 ngx_http_limit_req_handler
    • ngx_http_limit_conn_module: handler 为 ngx_http_limit_conn_handler

    以下两个模块需要通过指定添加到 nginx 中才会在 ngx_modules 中有这里两个模块,否则默认无:

    • ngx_http_degradation_module: handler 为 ngx_http_degradation_handler
    • ngx_http_realip_module:handler 为 ngx_http_realip_handler

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

    11.5.1 ngx_http_limit_req_handler

    static ngx_int_t
    ngx_http_limit_req_handler(ngx_http_request_t *r)
    {
        uint32_t                     hash;
        ngx_str_t                    key;
        ngx_int_t                    rc;
        ngx_uint_t                   n, excess;
        ngx_msec_t                   delay;
        ngx_http_limit_req_ctx_t    *ctx;
        ngx_http_limit_req_conf_t   *lrcf;
        ngx_http_limit_req_limit_t  *limit, *limits;
    
        if (r->main->limit_req_set) {
            return NGX_DECLINED;
        }
    
        lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
        limits = lrcf->limits.elts;
    
        excess = 0;
    
        rc = NGX_DECLINED;
    
    #if (NGX_SUPPRESS_WARN)
        limit = NULL;
    #endif
    
        for (n = 0; n < lrcf->limits.nelts; n++) {
    
            ...
        }
    
        if (rc == NGX_DECLINED) {
            return NGX_DECLINED;
        }
    
        ...
    }
    

    对于当前配置文件,没有对该 ngx_http_limit_req_module 模块进行设置,因此该函数中会直接返回,不做其他处理。

    11.5.2 ngx_http_limit_conn_handler

    static ngx_int_t
    ngx_stream_limit_conn_handler(ngx_stream_session_t *s)
    {
        size_t                            n;
        uint32_t                          hash;
        ngx_str_t                         key;
        ngx_uint_t                        i;
        ngx_slab_pool_t                  *shpool;
        ngx_rbtree_node_t                *node;
        ngx_pool_cleanup_t               *cln;
        ngx_stream_limit_conn_ctx_t      *ctx;
        ngx_stream_limit_conn_node_t     *lc;
        ngx_stream_limit_conn_conf_t     *lccf;
        ngx_stream_limit_conn_limit_t    *limits;
        ngx_stream_limit_conn_cleanup_t  *lccln;
    
        lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module);
        limits = lccf->limits.elts;
    
        for (i = 0; i < lccf->limits.nelts; i++) {
            ...
        }
    
        return NGX_DECLINED;
    }
    

    该模块 ngx_http_limit_conn_module 没有进行相应配置,暂时忽略。

    11.6 NGX_HTTP_ACCESS_PHASE

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

    该阶段实现的 checker 方法为 ngx_http_core_access_phase,介入的模块有:

    • ngx_http_access_module:handler 为 ngx_http_access_handler
    • ngx_http_auth_basic_module:handler 为 ngx_http_auth_basic_handler

    该模块没有指定添加到 nginx 中进行:

    • ngx_http_auth_request_module:handler 为 ngx_http_auth_request_handler

    ngx_http_core_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 为子请求的话,直接返回 */
        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);
    
        if (rc == NGX_DECLINED) {
            r->phase_handler++;
            return NGX_AGAIN;
        }
    
        if (rc == NGX_AGAIN || rc == NGX_DONE) {
            return NGX_OK;
        }
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
    
            if (rc == NGX_OK) {
                r->phase_handler++;
                return NGX_AGAIN;
            }
    
        } else {
            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) {
                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;
    }
    

    11.6.1 ngx_http_access_handler

    该 ngx_http_access_module 同样没有进行相应的配置,因此暂时不分析。

    11.6.2 ngx_http_auth_basic_module

    没有配置,暂不分析.

    11.7 NGX_HTTP_POST_ACCESS_PHASE

    在 NGX_HTTP_ACCESS_PAHSE 阶段中,当 HTTP 模块的 handler 处理函数返回不允许访问的错误码时(实际就是 NGX_HTTP_FORBIDDEN 或 NGX_HTTP_AUTHORIZED),这里将负责向用户发送拒绝服务的错误响应。因此,这个阶段实际上用于给 NGX_HTTP_ACCESS_PAHSE 阶段收尾.

    该阶段实现的 checker 方法为 ngx_http_core_post_access_phase,且没有 handler 方法,即不允许其他 HTTP 模块介入。

    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;
    
        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++;
        return NGX_AGAIN;
    }
    

    注:由于没有配置 try_files 配置项,因此直接跳过 NGX_HTTP_TRY_FILES_PHASE 阶段。

    11.8 NGX_HTTP_CONTENT_PHASE

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

    该阶段实现的 checker 方法为 ngx_http_core_content_phase,介入的模块有:

    • ngx_http_index_module:handler 为 ngx_http_index_handler
    • ngx_http_autoindex_module: handler 为 ngx_http_autoindex_handler
    • ngx_http_dav_module:handler 为 ngx_http_dav_handler
    • ngx_http_gzip_static_module:handler 为 ngx_http_gzip_static_handler
    • ngx_http_random_index_module:handler 为 ngx_http_random_index_handler
    • ngx_http_static_module:handler ngx_http_static_handler

    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;
    
        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) {
            ngx_http_finalize_request(r, rc);
            return NGX_OK;
        }
    
        /* rc == NGX_DECLINED */
    
        ph++;
    
        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;
    }
    

    11.8.1 ngx_http_index_handler

    /*
     * Try to open/test the first index file before the test of directory
     * existence because valid requests should prevail over invalid ones.
     * If open()/stat() of a file will fail then stat() of a directory 
     * should be faster because kernel may have already cached some data.
     * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
     * Unix has ENOTDIR error; however, it's less helpful than Win32's one:
     * is only indicates that path points to a regular file, not a directory.
     */
    static ngx_int_t
    ngx_http_index_handler(ngx_http_request_t *r)
    {
        ...
    
        /* 因为当前请求 uri 的最后一个字符不为 '/',直接返回 */
        if (r->uri.data[r->uri.len - 1] != '/') {
            return NGX_DECLINED;
        }
    
        ...
    }
    

    11.8.2 ngx_http_autoindex_handler

    static ngx_int_t
    ngx_http_autoindex_handler(ngx_http_request_t *r)
    {
        ...
        
        /* 由于当前请求 uri 的最后一个字节不为 '/',因此直接返回 */
        if (r->uri.data[r->uri.len - 1] != '/') {
            return NGX_DECLINED;
        }
        
        ...
    }
    

    11.8.3 ngx_http_static_handler

    static ngx_int_t
    ngx_http_static_handler(ngx_http_request_t *r)
    {
        u_char                    *last, *location;
        size_t                     root, len;
        ngx_str_t                  path;
        ngx_int_t                  rc;
        ngx_uint_t                 level;
        ngx_log_t                 *log;
        ngx_buf_t                 *b;
        ngx_chain_t                out;
        ngx_open_file_info_t       of;
        ngx_http_core_loc_conf_t  *clcf;
    
        if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
            return NGX_HTTP_NOT_ALLOWED;
        }
    
        if (r->uri.data[r->uri.len - 1] == '/') {
            return NGX_DECLINED;
        }
    
        log = r->connection->log;
    
        /*
         * ngx_http_map_uri_to_path() allocates memory for terminating ''
         * so we do not need to reserve memory for '/' for possible redirect
         */
    
        /* 根据所访问文件的根目录和锁请求访问的静态文件,得出该文件的
         * 绝对路径,保存在 path 中 */
        last = ngx_http_map_uri_to_path(r, &path, &root, 0);
        if (last == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        path.len = last - path.data;
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                       "http filename: "%s"", path.data);
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        ngx_memzero(&of, sizeof(ngx_open_file_info_t));
    
        of.read_ahead = clcf->read_ahead;
        of.directio = clcf->directio;
        of.valid = clcf->open_file_cache_valid;
        of.min_uses = clcf->open_file_cache_min_uses;
        of.errors = clcf->open_file_cache_errors;
        of.events = clcf->open_file_cache_events;
    
        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        /* 打开该文件并获取该文件的信息,同时设置当释放该内存池时调用的回调函数
         * 以便释放该文件的描述符 */
        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
            != NGX_OK)
        {
            switch (of.err) {
    
            case 0:
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
    
            case NGX_ENOENT:
            case NGX_ENOTDIR:
            case NGX_ENAMETOOLONG:
    
                level = NGX_LOG_ERR;
                rc = NGX_HTTP_NOT_FOUND;
                break;
    
            case NGX_EACCES:
    #if (NGX_HAVE_OPENAT)
            case NGX_EMLINK:
            case NGX_ELOOP:
    #endif
    
                level = NGX_LOG_ERR;
                rc = NGX_HTTP_FORBIDDEN;
                break;
    
            default:
    
                level = NGX_LOG_CRIT;
                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
                break;
            }
    
            if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
                ngx_log_error(level, log, of.err,
                              "%s "%s" failed", of.failed, path.data);
            }
    
            return rc;
        }
    
        r->root_tested = !r->error_page;
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
    
        if (of.is_dir) {
    
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
    
            ngx_http_clear_location(r);
    
            r->headers_out.location = ngx_list_push(&r->headers_out.headers);
            if (r->headers_out.location == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
    
            len = r->uri.len + 1;
    
            if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {
                location = path.data + clcf->root.len;
    
                *last = '/';
    
            } else {
                if (r->args.len) {
                    len += r->args.len + 1;
                }
    
                location = ngx_pnalloc(r->pool, len);
                if (location == NULL) {
                    ngx_http_clear_location(r);
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
    
                last = ngx_copy(location, r->uri.data, r->uri.len);
    
                *last = '/';
    
                if (r->args.len) {
                    *++last = '?';
                    ngx_memcpy(++last, r->args.data, r->args.len);
                }
            }
    
            r->headers_out.location->hash = 1;
            ngx_str_set(&r->headers_out.location->key, "Location");
            r->headers_out.location->value.len = len;
            r->headers_out.location->value.data = location;
    
            return NGX_HTTP_MOVED_PERMANENTLY;
        }
    
    #if !(NGX_WIN32) /* the not regular files are probably Unix specific */
    
        if (!of.is_file) {
            ngx_log_error(NGX_LOG_CRIT, log, 0,
                          ""%s" is not a regular file", path.data);
    
            return NGX_HTTP_NOT_FOUND;
        }
    
    #endif
    
        if (r->method == NGX_HTTP_POST) {
            return NGX_HTTP_NOT_ALLOWED;
        }
    
        /* 丢弃 HTTP 的请求包体,若本来就没有,则直接返回 */
        rc = ngx_http_discard_request_body(r);
    
        if (rc != NGX_OK) {
            return rc;
        }
    
        /* 下面将要执行的动作是发送响应给客户端 */
        log->action = "sending response to client";
    
        /* 设置响应的状态码 */
        r->headers_out.status = NGX_HTTP_OK;
        /* 设置响应的包体的大小 */
        r->headers_out.content_length_n = of.size;
        /* 该文件上一次修改时间(Unix时间戳) */
        r->headers_out.last_modified_time = of.mtime;
    
        /* 添加一个 ETag 响应头,ETag 对应实体内容的一个实体标签,与实体
         * 内容紧密相关,实体内容发生任何变化都会使这个头的值发生变化 */
        if (ngx_http_set_etag(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        /* 实体头 Content-Type:服务器在返回内容时需要告诉浏览器本响应的内容是什么
         * 类型。HTTP 中把这种不同媒体类型的格式称为多媒体文件格式(MIME),而本实体
         * 头指出所传输实体内容的 MIME。由于 Web 服务器不知道所返回的的内容文件是
         * 哪种 MIME,所以需要通过对 Web 服务器进行设置,使文件扩展名与 MIME 之间进行
         * 映射。如 : Content-Type: text/html; */
        if (ngx_http_set_content_type(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        /* 若当前请求为子请求 */
        if (r != r->main && of.size == 0) {
            return ngx_http_send_header(r);
        }
    
        r->allow_ranges = 1;
    
        /* we need to allocate all before the header would be sent */
    
        /* 为响应数据分配一个缓存 */
        b = ngx_calloc_buf(r->pool);
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
        if (b->file == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        /* 该函数主要是调用 ngx_http_top_header_filter 链表中的函数,
         * 构造响应消息的消息报头,写入到 r->out 链表中 */
        rc = ngx_http_send_header(r);
    
        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
            return rc;
        }
    
        b->file_pos = 0;
        b->file_last = of.size;
    
        b->in_file = b->file_last ? 1: 0;
        /* 若当前请求为父请求,则表明这个最后一个 buf 了 */
        b->last_buf = (r == r->main) ? 1: 0;
        b->last_in_chain = 1;
    
        b->file->fd = of.fd;
        b->file->name = path;
        b->file->log = log;
        b->file->directio = of.is_directio;
    
        out.buf = b;
        out.next = NULL;
        
        /* 接下来调用 ngx_http_top_body_filter 链表中的函数构造响应消息的响应正文 */
        return ngx_http_output_filter(r, &out);
    }
    

    11.8.3.1 ngx_http_map_uri_to_path

    u_char *
    ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,
        size_t *root_length, size_t reserved)
    {
        u_char                    *last;
        size_t                     alias;
        ngx_http_core_loc_conf_t  *clcf;
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        alias = clcf->alias;
    
        if (alias && !r->valid_location) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                          ""alias" cannot be used in location "%V" "
                          "where URI was rewritten", &clcf->name);
            return NULL;
        }
    
        if (clcf->root_lengths == NULL) {
    
            /* 根目录路径的长度 */
            *root_length = clcf->root.len;
    
            /* 计算根目录的路径加上客户当访问的文件的长度 */
            path->len = clcf->root.len + reserved + r->uri.len - alias + 1;
    
            path->data = ngx_pnalloc(r->pool, path->len);
            if (path->data == NULL) {
                return NULL;
            }
    
            /* 将  clcf->root.data 中保存的静态文件的根目录拷贝到 path->data 中 */
            last = ngx_copy(path->data, clcf->root.data, clcf->root.len);
    
        } else {
    
            if (alias == NGX_MAX_SIZE_T_VALUE) {
                reserved += r->add_uri_to_alias ? r->uri.len + 1 : 1;
    
            } else {
                reserved += r->uri.len - alias + 1;
            }
    
            if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved,
                                    clcf->root_values->elts)
                == NULL)
            {
                return NULL;
            }
    
            if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, path)
                != NGX_OK)
            {
                return NULL;
            }
    
            *root_length = path->len - reserved;
            last = path->data + *root_length;
    
            if (alias == NGX_MAX_SIZE_T_VALUE) {
                if (!r->add_uri_to_alias) {
                    *last = '';
                    return last;
                }
    
                alias = 0;
            }
        }
    
        /* 若没有别名的话,这里拷贝的 last 为空字符串 */
        last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1);
    
        return last;
    }
    

    11.8.3.2 ngx_open_cached_file

    ngx_int_t
    ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
        ngx_open_file_info_t *of, ngx_pool_t *pool)
    {
        time_t                          now;
        uint32_t                        hash;
        ngx_int_t                       rc;
        ngx_file_info_t                 fi;
        ngx_pool_cleanup_t             *cln;
        ngx_cached_open_file_t         *file;
        ngx_pool_cleanup_file_t        *clnf;
        ngx_open_file_cache_cleanup_t  *ofcln;
    
        of->fd = NGX_INVALID_FILE;
        of->err = 0;
    
        if (cache == NULL) {
    
            if (of->test_only) {
    
                if (ngx_file_info_wrapper(name, of, &fi, pool->log)
                    == NGX_FILE_ERROR)
                {
                    return NGX_ERROR;
                }
    
                of->uniq = ngx_file_uniq(&fi);
                of->mtime = ngx_file_mtime(&fi);
                of->size = ngx_file_size(&fi);
                of->fs_size = ngx_file_fs_size(&fi);
                of->is_dir = ngx_is_dir(&fi);
                of->is_file = ngx_is_file(&fi);
                of->is_link = ngx_is_link(&fi);
                of->is_exec = ngx_is_exec(&fi);
    
                return NGX_OK;
            }
    
            /* 该接口用于对内存与其他资源文件的管理,从内存池里申请一块内存时,
             * 可能外部会附加一些其他资源(如打开的文件),这些资源的使用和申请的
             * 内存是绑定在一起的,那么在进行资源释放的时候,希望这些资源的释放
             * 能和内存池释放一起进行(通过 handler 回调函数),既能避免无意的
             * 资源泄露,又省得单独执行资源释放的麻烦. */
            cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
            if (cln == NULL) {
                return NGX_ERROR;
            }
    
            /* 打开该文件并获取该文件的信息 */
            rc = ngx_open_and_stat_file(name, of, pool->log);
    
            /* 若该文件不是一个目录 */
            if (rc == NGX_OK && !of->is_dir) {
                /* 设置当释放该资源时,调用的回调函数 */
                cln->handler = ngx_pool_cleanup_file;
                clnf = cln->data;
    
                /* 该文件的打开描述符 */
                clnf->fd = of->fd;
                /* 客户端请求的文件的绝对路径名 */
                clnf->name = name->data;
                clnf->log = pool->log;
            }
    
            return rc;
        }
    
        cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
        if (cln == NULL) {
            return NGX_ERROR;
        }
    
        now = ngx_time();
    
        hash = ngx_crc32_long(name->data, name->len);
    
        file = ngx_open_file_lookup(cache, name, hash);
    
        if (file) {
    
            file->uses++;
    
            ngx_queue_remove(&file->queue);
    
            if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
    
                /* file was not used often enough to keep open */
    
                rc = ngx_open_and_stat_file(name, of, pool->log);
    
                if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
                    goto failed;
                }
    
                goto add_event;
            }
    
            if (file->use_event
                || (file->event == NULL
                    && (of->uniq == 0 || of->uniq == file->uniq)
                    && now - file->created < of->valid
    #if (NGX_HAVE_OPENAT)
                    && of->disable_symlinks == file->disable_symlinks
                    && of->disable_symlinks_from == file->disable_symlinks_from
    #endif
                ))
            {
                if (file->err == 0) {
    
                    of->fd = file->fd;
                    of->uniq = file->uniq;
                    of->mtime = file->mtime;
                    of->size = file->size;
    
                    of->is_dir = file->is_dir;
                    of->is_file = file->is_file;
                    of->is_link = file->is_link;
                    of->is_exec = file->is_exec;
                    of->is_directio = file->is_directio;
    
                    if (!file->is_dir) {
                        file->count++;
                        ngx_open_file_add_event(cache, file, of, pool->log);
                    }
    
                } else {
                    of->err = file->err;
    #if (NGX_HAVE_OPENAT)
                    of->failed = file->disable_symlinks ? ngx_openat_file_n
                                                        : ngx_open_file_n;
    #else
                    of->failed = ngx_open_file_n;
    #endif
                }
    
                goto found;
            }
    
            ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
                           "retest open file: %s, fd:%d, c:%d, e:%d",
                           file->name, file->fd, file->count, file->err);
    
            if (file->is_dir) {
    
                /*
                 * chances that directory became file are very small
                 * so test_dir flag allows to use a single syscall
                 * in ngx_file_info() instead of three syscalls
                 */
    
                of->test_dir = 1;
            }
    
            of->fd = file->fd;
            of->uniq = file->uniq;
    
            rc = ngx_open_and_stat_file(name, of, pool->log);
    
            if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
                goto failed;
            }
    
            if (of->is_dir) {
    
                if (file->is_dir || file->err) {
                    goto update;
                }
    
                /* file became directory */
    
            } else if (of->err == 0) {  /* file */
    
                if (file->is_dir || file->err) {
                    goto add_event;
                }
    
                if (of->uniq == file->uniq) {
    
                    if (file->event) {
                        file->use_event = 1;
                    }
    
                    of->is_directio = file->is_directio;
    
                    goto update;
                }
    
                /* file was changed */
    
            } else { /* error to cache */
    
                if (file->err || file->is_dir) {
                    goto update;
                }
    
                /* file was removed, etc. */
            }
    
            if (file->count == 0) {
    
                ngx_open_file_del_event(file);
    
                if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                                  ngx_close_file_n " "%V" failed", name);
                }
    
                goto add_event;
            }
    
            ngx_rbtree_delete(&cache->rbtree, &file->node);
    
            cache->current--;
    
            file->close = 1;
    
            goto create;
        }
    
        /* not found */
    
        rc = ngx_open_and_stat_file(name, of, pool->log);
    
        if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
            goto failed;
        }
    
    create:
    
        if (cache->current >= cache->max) {
            ngx_expire_old_cached_files(cache, 0, pool->log);
        }
    
        file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
    
        if (file == NULL) {
            goto failed;
        }
    
        file->name = ngx_alloc(name->len + 1, pool->log);
    
        if (file->name == NULL) {
            ngx_free(file);
            file = NULL;
            goto failed;
        }
    
        ngx_cpystrn(file->name, name->data, name->len + 1);
    
        file->node.key = hash;
    
        ngx_rbtree_insert(&cache->rbtree, &file->node);
    
        cache->current++;
    
        file->uses = 1;
        file->count = 0;
        file->use_event = 0;
        file->event = NULL;
    
    add_event:
    
        ngx_open_file_add_event(cache, file, of, pool->log);
    
    update:
    
        file->fd = of->fd;
        file->err = of->err;
    #if (NGX_HAVE_OPENAT)
        file->disable_symlinks = of->disable_symlinks;
        file->disable_symlinks_from = of->disable_symlinks_from;
    #endif
    
        if (of->err == 0) {
            file->uniq = of->uniq;
            file->mtime = of->mtime;
            file->size = of->size;
    
            file->close = 0;
    
            file->is_dir = of->is_dir;
            file->is_file = of->is_file;
            file->is_link = of->is_link;
            file->is_exec = of->is_exec;
            file->is_directio = of->is_directio;
    
            if (!of->is_dir) {
                file->count++;
            }
        }
    
        file->created = now;
    
    found:
    
        file->accessed = now;
    
        ngx_queue_insert_head(&cache->expire_queue, &file->queue);
    
        ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
                       "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
                       file->name, file->fd, file->count, file->err, file->uses);
    
        if (of->err == 0) {
    
            if (!of->is_dir) {
                cln->handler = ngx_open_file_cleanup;
                ofcln = cln->data;
    
                ofcln->cache = cache;
                ofcln->file = file;
                ofcln->min_uses = of->min_uses;
                ofcln->log = pool->log;
            }
    
            return NGX_OK;
        }
    
        return NGX_ERROR;
    
    failed:
    
        if (file) {
            ngx_rbtree_delete(&cache->rbtree, &file->node);
    
            cache->current--;
    
            if (file->count == 0) {
    
                if (file->fd != NGX_INVALID_FILE) {
                    if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
                        ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                                      ngx_close_file_n " "%s" failed",
                                      file->name);
                    }
                }
    
                ngx_free(file->name);
                ngx_free(file);
    
            } else {
                file->close = 1;
            }
        }
    
        if (of->fd != NGX_INVALID_FILE) {
            if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                              ngx_close_file_n " "%V" failed", name);
            }
        }
    
        return NGX_ERROR;
    }
    

    11.8.3.3 ngx_pool_cleanup_add

    ngx_pool_cleanup_t *
    ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
    {
        ngx_pool_cleanup_t  *c;
    
        c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
        if (c == NULL) {
            return NULL;
        }
    
        if (size) {
            c->data = ngx_palloc(p, size);
            if (c->data == NULL) {
                return NULL;
            }
    
        } else {
            c->data = NULL;
        }
    
        c->handler = NULL;
        c->next = p->cleanup;
    
        p->cleanup = c;
    
        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);
    
        return c;
    }
    

    11.8.3.4 ngx_open_and_stat_file

    static ngx_int_t
    ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,
        ngx_log_t *log)
    {
        ngx_fd_t         fd;
        ngx_file_info_t  fi;
    
        /* 若该文件已经打开了 */
        if (of->fd != NGX_INVALID_FILE) {
    
            if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
                of->fd = NGX_INVALID_FILE;
                return NGX_ERROR;
            }
    
            if (of->uniq == ngx_file_uniq(&fi)) {
                goto done;
            }
    
        } else if (of->test_dir) {
    
            if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
                of->fd = NGX_INVALID_FILE;
                return NGX_ERROR;
            }
    
            if (ngx_is_dir(&fi)) {
                goto done;
            }
        }
    
        if (!of->log) {
    
            /*
             * Use non-blocking open() not to hang on FIFO files, etc.
             * This flag has no effect on a regular files.
             */
    
            /* 使用只读且为非阻塞方法打开该文件 */
            fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
                                       NGX_FILE_OPEN, 0, log);
    
        } else {
            fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,
                                       NGX_FILE_CREATE_OR_OPEN,
                                       NGX_FILE_DEFAULT_ACCESS, log);
        }
    
        if (fd == NGX_INVALID_FILE) {
            of->fd = NGX_INVALID_FILE;
            return NGX_ERROR;
        }
    
        /* 调用 fstat 获取该文件的信息 */
        if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
                          ngx_fd_info_n " "%V" failed", name);
    
            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                              ngx_close_file_n " "%V" failed", name);
            }
    
            of->fd = NGX_INVALID_FILE;
    
            return NGX_ERROR;
        }
    
        /* 检测是否是一个目录 */
        if (ngx_is_dir(&fi)) {
            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                              ngx_close_file_n " "%V" failed", name);
            }
    
            of->fd = NGX_INVALID_FILE;
    
        } else {
            /* 否则为一个普通的文件 */
            of->fd = fd;
    
            if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
                if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
                    ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                                  ngx_read_ahead_n " "%V" failed", name);
                }
            }
    
            if (of->directio <= ngx_file_size(&fi)) {
                if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                                  ngx_directio_on_n " "%V" failed", name);
    
                } else {
                    of->is_directio = 1;
                }
            }
        }
    
    done:
    
        /* 根据上面 stat 返回的文件信息初始化该文件 */
        of->uniq = ngx_file_uniq(&fi);
        of->mtime = ngx_file_mtime(&fi);
        of->size = ngx_file_size(&fi);
        of->fs_size = ngx_file_fs_size(&fi);
        of->is_dir = ngx_is_dir(&fi);
        of->is_file = ngx_is_file(&fi);
        of->is_link = ngx_is_link(&fi);
        of->is_exec = ngx_is_exec(&fi);
    
        return NGX_OK;
    }
    

    11.8.3.5 ngx_open_file_wrapper

    static ngx_fd_t
    ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
        ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)
    {
        ngx_fd_t  fd;
    
    #if !(NGX_HAVE_OPENAT)
    
        fd = ngx_open_file(name->data, mode, create, access);
    
        if (fd == NGX_INVALID_FILE) {
            of->err = ngx_errno;
            of->failed = ngx_open_file_n;
            return NGX_INVALID_FILE;
        }
    
        return fd;
    
    #else
    
        u_char           *p, *cp, *end;
        ngx_fd_t          at_fd;
        ngx_str_t         at_name;
    
        if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
            /* 调用 open 函数打开该文件 */
            fd = ngx_open_file(name->data, mode, create, access);
    
            if (fd == NGX_INVALID_FILE) {
                of->err = ngx_errno;
                of->failed = ngx_open_file_n;
                return NGX_INVALID_FILE;
            }
    
            /* 打开成功,则直接返回该文件描述符 */
            return fd;
        }
    
        p = name->data;
        end = p + name->len;
    
        at_name = *name;
    
        if (of->disable_symlinks_from) {
    
            cp = p + of->disable_symlinks_from;
    
            *cp = '';
    
            at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
                                  NGX_FILE_OPEN, 0);
    
            *cp = '/';
    
            if (at_fd == NGX_INVALID_FILE) {
                of->err = ngx_errno;
                of->failed = ngx_open_file_n;
                return NGX_INVALID_FILE;
            }
    
            at_name.len = of->disable_symlinks_from;
            p = cp + 1;
    
        } else if (*p == '/') {
    
            at_fd = ngx_open_file("/",
                                  NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
                                  NGX_FILE_OPEN, 0);
    
            if (at_fd == NGX_INVALID_FILE) {
                of->err = ngx_errno;
                of->failed = ngx_openat_file_n;
                return NGX_INVALID_FILE;
            }
    
            at_name.len = 1;
            p++;
    
        } else {
            at_fd = NGX_AT_FDCWD;
        }
    
        for ( ;; ) {
            cp = ngx_strlchr(p, end, '/');
            if (cp == NULL) {
                break;
            }
    
            if (cp == p) {
                p++;
                continue;
            }
    
            *cp = '';
    
            if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
                fd = ngx_openat_file_owner(at_fd, p,
                                           NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,
                                           NGX_FILE_OPEN, 0, log);
    
            } else {
                fd = ngx_openat_file(at_fd, p,
                               NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,
                               NGX_FILE_OPEN, 0);
            }
    
            *cp = '/';
    
            if (fd == NGX_INVALID_FILE) {
                of->err = ngx_errno;
                of->failed = ngx_openat_file_n;
                goto failed;
            }
    
            if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                              ngx_close_file_n " "%V" failed", &at_name);
            }
    
            p = cp + 1;
            at_fd = fd;
            at_name.len = cp - at_name.data;
        }
    
        if (p == end) {
    
            /*
             * If pathname ends with a trailing slash, assume the last path
             * component is a directory and reopen it with requested flags;
             * if not, fail with ENOTDIR as per POSIX.
             *
             * We cannot rely on O_DIRECTORY in the loop above to check
             * that the last path component is a directory because
             * O_DIRECTORY doesn't work on FreeBSD 8.  Fortunately, by
             * reopening a directory, we don't depend on it at all.
             */
    
            fd = ngx_openat_file(at_fd, ".", mode, create, access);
            goto done;
        }
    
        if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER
            && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))
        {
            fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);
    
        } else {
            fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);
        }
    
    done:
    
        if (fd == NGX_INVALID_FILE) {
            of->err = ngx_errno;
            of->failed = ngx_openat_file_n;
        }
    
    failed:
    
        if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                          ngx_close_file_n " "%V" failed", &at_name);
        }
    
        return fd;
    #endif
    }
    

    11.8.3.6 ngx_http_set_etag

    ngx_int_t
    ngx_http_set_etag(ngx_http_request_t *r)
    {
        ngx_table_elt_t           *etag;
        ngx_http_core_loc_conf_t  *clcf;
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        if (!clcf->etag) {
            return NGX_OK;
        }
    
        /* 将 ETag 头部放入到 headers_out.header 链表中 */
        etag = ngx_list_push(&r->headers_out.headers);
        if (etag == NULL) {
            return NGX_ERROR;
        }
    
        etag->hash = 1;
        ngx_str_set(&etag->key, "ETag");
    
        etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);
        if (etag->value.data == NULL) {
            etag->hash = 0;
            return NGX_ERROR;
        }
    
        /* 根据该文件的上一次修改时间和该文件的大小构成一个 ETag 头部的值 */
        etag->value.len = ngx_sprintf(etag->value.data, ""%xT-%xO"",
                                      r->headers_out.last_modified_time,
                                      r->headers_out.content_length_n)
                          - etag->value.data;
    
        r->headers_out.etag = etag;
    
        return NGX_OK;
    }
    

    11.8.3.7 ngx_http_set_content_type

    ngx_int_t
    ngx_http_set_content_type(ngx_http_request_t *r)
    {
        u_char                     c, *exten;
        ngx_str_t                 *type;
        ngx_uint_t                 i, hash;
        ngx_http_core_loc_conf_t  *clcf;
    
        /* 若该 headers_out 中已经有 Content-Type 头了,则直接返回 */
        if (r->headers_out.content_type.len) {
            return NGX_OK;
        }
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        /* 该请求文件的扩展名,如 "html" */
        if (r->exten.len) {
    
            hash = 0;
    
            for (i = 0; i < r->exten.len; i++) {
                c = r->exten.data[i];
    
                if (c >= 'A' && c <= 'Z') {
    
                    exten = ngx_pnalloc(r->pool, r->exten.len);
                    if (exten == NULL) {
                        return NGX_ERROR;
                    }
    
                    hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);
    
                    r->exten.data = exten;
    
                    break;
                }
    
                /* 生成一个哈希查找的 key */
                hash = ngx_hash(hash, c);
            }
    
            /* 在哈希表 types_hash 中根据 key(即 hash)查找与 r->exten.data 相同的项 */
            type = ngx_hash_find(&clcf->types_hash, hash,
                                 r->exten.data, r->exten.len);
    
            if (type) {
                r->headers_out.content_type_len = type->len;
                /* 对于 html 类型的,这里对应的 Content-Type 为 "text/html" */
                r->headers_out.content_type = *type;
    
                return NGX_OK;
            }
        }
    
        r->headers_out.content_type_len = clcf->default_type.len;
        r->headers_out.content_type = clcf->default_type;
    
        return NGX_OK;
    }
    

    11.8.3.8 ngx_hash_find

    /*
     * 返回散列表中关键字与 name、len 指定关键字完全相同的槽中,
     * ngx_hash_elt_t 结构体中 value 成员所指向的用户数据.
     */
    void *
    ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
    {
        ngx_uint_t       i;
        ngx_hash_elt_t  *elt;
    
    #if 0
        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:"%*s"", len, name);
    #endif
    
        /* 对 key 取模得到对应的 hash 节点 */
        elt = hash->buckets[key % hash->size];
    
        if (elt == NULL) {
            return NULL;
        }
    
        /* 然后在该 hash 节点所对应的 bucket 里逐个(该 bucket 的实现类似数组,结束有
         * 哨兵保证)对于元素名称来找到唯一的那个实际元素,最后返回其 value 值
         * (比如,如果在 addr->hash 结构里找到对应的实际元素,返回的 value 就是
         * 其 ngx_http_core_srv_cnf_t 配置)*/
        while (elt->value) {
            if (len != (size_t) elt->len) {
                goto next;
            }
    
            for (i = 0; i < len; i++) {
                if (name[i] != elt->name[i]) {
                    goto next;
                }
            }
    
            /* 将返回该名称和完成都相同的项 */
            return elt->value;
    
        next:
    
            elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
                                                   sizeof(void *));
            continue;
        }
    
        return NULL;
    }
    

    11.8.3.9 ngx_http_send_header

    ngx_int_t
    ngx_http_send_header(ngx_http_request_t *r)
    {
        if (r->post_action) {
            return NGX_OK;
        }
    
        /* 标志位,为 1 表示该 header 已经发送了 */
        if (r->header_sent) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                          "header already sent");
            return NGX_ERROR;
        }
    
        /* 错误状态码 */
        if (r->err_status) {
            r->headers_out.status = r->err_status;
            r->headers_out.status_line.len = 0;
        }
    
        /* ngx_http_top_header_filter 是个全局函数指针,由各个HTTP模块
         * 共同构造的一个单链表,存放的是各个 HTTP 模块在此阶段介入的
         * 处理 */
        return ngx_http_top_header_filter(r);
    }
    

    11.3.8.10 ngx_http_output_filter

    ngx_int_t
    ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        ngx_int_t          rc;
        ngx_connection_t  *c;
    
        c = r->connection;
    
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http output filter "%V?%V"", &r->uri, &r->args);
    
        /* ngx_http_top_body_filter 是一个由各个 HTTP 模块构造的函数链表,
         * 保存的是各个 HTTP 模块想要在响应消息的响应正文中做的处理 */
        rc = ngx_http_top_body_filter(r, in);
    
        if (rc == NGX_ERROR) {
            /* NGX_ERROR may be returned by any filter */
            c->error = 1;
        }
    
        return rc;
    }
    

    11.3.8.11 ngx_http_finalize_request

    void
    ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
    {
        ngx_connection_t          *c;
        ngx_http_request_t        *pr;
        ngx_http_core_loc_conf_t  *clcf;
    
        c = r->connection;
    
        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http finalize request: %i, "%V?%V" a:%d, c:%d",
                       rc, &r->uri, &r->args, r == c->data, r->main->count);
    
        if (rc == NGX_DONE) {
            ngx_http_finalize_connection(r);
            return;
        }
    
        if (rc == NGX_OK && r->filter_finalize) {
            c->error = 1;
        }
    
        if (rc == NGX_DECLINED) {
            r->content_handler = NULL;
            r->write_event_handler = ngx_http_core_run_phases;
            ngx_http_core_run_phases(r);
            return;
        }
    
        if (r != r->main && r->post_subrequest) {
            rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
        }
    
        if (rc == NGX_ERROR
            || rc == NGX_HTTP_REQUEST_TIME_OUT
            || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
            || c->error)
        {
            if (ngx_http_post_action(r) == NGX_OK) {
                return;
            }
    
            ngx_http_terminate_request(r, rc);
            return;
        }
    
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE
            || rc == NGX_HTTP_CREATED
            || rc == NGX_HTTP_NO_CONTENT)
        {
            if (rc == NGX_HTTP_CLOSE) {
                ngx_http_terminate_request(r, rc);
                return;
            }
    
            if (r == r->main) {
                if (c->read->timer_set) {
                    ngx_del_timer(c->read);
                }
    
                if (c->write->timer_set) {
                    ngx_del_timer(c->write);
                }
            }
    
            c->read->handler = ngx_http_request_handler;
            c->write->handler = ngx_http_request_handler;
    
            ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));
            return;
        }
    
        if (r != r->main) {
            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
            if (r->background) {
                if (!r->logged) {
                    if (clcf->log_subrequest) {
                        ngx_http_log_request(r);
                    }
    
                    r->logged = 1;
    
                } else {
                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                                  "subrequest: "%V?%V" logged again",
                                  &r->uri, &r->args);
                }
    
                r->done = 1;
                ngx_http_finalize_connection(r);
                return;
            }
    
            if (r->buffered || r->postponed) {
    
                if (ngx_http_set_write_handler(r) != NGX_OK) {
                    ngx_http_terminate_request(r, 0);
                }
    
                return;
            }
    
            pr = r->parent;
    
            if (r == c->data) {
    
                r->main->count--;
    
                if (!r->logged) {
                    if (clcf->log_subrequest) {
                        ngx_http_log_request(r);
                    }
    
                    r->logged = 1;
    
                } else {
                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                                  "subrequest: "%V?%V" logged again",
                                  &r->uri, &r->args);
                }
    
                r->done = 1;
    
                if (pr->postponed && pr->postponed->request == r) {
                    pr->postponed = pr->postponed->next;
                }
    
                c->data = pr;
    
            } else {
    
                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                               "http finalize non-active request: "%V?%V"",
                               &r->uri, &r->args);
    
                r->write_event_handler = ngx_http_request_finalizer;
    
                if (r->waited) {
                    r->done = 1;
                }
            }
    
            if (ngx_http_post_request(pr, NULL) != NGX_OK) {
                r->main->count++;
                ngx_http_terminate_request(r, 0);
                return;
            }
    
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http wake parent request: "%V?%V"",
                           &pr->uri, &pr->args);
    
            return;
        }
    
        if (r->buffered || c->buffered || r->postponed) {
    
            if (ngx_http_set_write_handler(r) != NGX_OK) {
                ngx_http_terminate_request(r, 0);
            }
    
            return;
        }
    
        if (r != c->data) {
            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                          "http finalize non-active request: "%V?%V"",
                          &r->uri, &r->args);
            return;
        }
    
        /* 标志位,为 1 表示当前请求结束 */
        r->done = 1;
    
        r->read_event_handler = ngx_http_block_reading;
        r->write_event_handler = ngx_http_request_empty_handler;
    
        if (!r->post_action) {
            /* 为 1 表示当前请求已经完全处理完 */
            r->request_complete = 1;
        }
    
        if (ngx_http_post_action(r) == NGX_OK) {
            return;
        }
    
        if (c->read->timer_set) {
            ngx_del_timer(c->read);
        }
    
        if (c->write->timer_set) {
            c->write->delayed = 0;
            ngx_del_timer(c->write);
        }
    
        if (c->read->eof) {
            ngx_http_close_request(r, 0);
            return;
        }
    
        ngx_http_finalize_connection(r);
    }
    
  • 相关阅读:
    在模拟器安装测试APP,给指定设备安装APP
    设置安卓模拟器,打开模拟器,设置语言为中文
    使用appium1.4在android8.0真机上测试程序时报错command failed shell "ps 'uiautomator'"的解决方式
    appium1.4+华为8.0执行自动化脚本,报启动session失败,原因是unicode_ime_apk\Uni codeIMEdebug.apk在手机上已存在,再次安装失败,导致启动session失败,解决办法:换高版本的appium
    搭建appium+maven手机自动化测试环境搭建
    appium1.7的使用
    SDK打开模拟器遇到SDK包里缺少API组件,附上我的解决历程,心累
    简单记录下Jmeter通过CSV保存测试数据,测试用例,及将测试结果导出到Excel里
    基于webpack的React项目搭建(一)
    基于webpack的React项目搭建(二)
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/9215719.html
Copyright © 2020-2023  润新知