ngx_http_core_location

定义在 src\http\ngx_http_core_module.c

复制代码
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
    char                      *rv;
    u_char                    *mod;
    size_t                     len;
    ngx_str_t                 *value, *name;
    ngx_uint_t                 i;
    ngx_conf_t                 save;
    ngx_http_module_t         *module;
    ngx_http_conf_ctx_t       *ctx, *pctx;
    ngx_http_core_loc_conf_t  *clcf, *pclcf;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    pctx = cf->ctx;
    ctx->main_conf = pctx->main_conf;
    ctx->srv_conf = pctx->srv_conf;

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }

    for (i = 0; cf->cycle->modules[i]; i++) {
        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[i]->ctx;

        if (module->create_loc_conf) {
            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =
                                                   module->create_loc_conf(cf);
            if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
    clcf->loc_conf = ctx->loc_conf;

    value = cf->args->elts;

    if (cf->args->nelts == 3) {

        len = value[1].len;
        mod = value[1].data;
        name = &value[2];

        if (len == 1 && mod[0] == '=') {

            clcf->name = *name;
            clcf->exact_match = 1;

        } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {

            clcf->name = *name;
            clcf->noregex = 1;

        } else if (len == 1 && mod[0] == '~') {

            if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
                return NGX_CONF_ERROR;
            }

        } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {

            if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
                return NGX_CONF_ERROR;
            }

        } else {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid location modifier \"%V\"", &value[1]);
            return NGX_CONF_ERROR;
        }

    } else {

        name = &value[1];

        if (name->data[0] == '=') {

            clcf->name.len = name->len - 1;
            clcf->name.data = name->data + 1;
            clcf->exact_match = 1;

        } else if (name->data[0] == '^' && name->data[1] == '~') {

            clcf->name.len = name->len - 2;
            clcf->name.data = name->data + 2;
            clcf->noregex = 1;

        } else if (name->data[0] == '~') {

            name->len--;
            name->data++;

            if (name->data[0] == '*') {

                name->len--;
                name->data++;

                if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
                    return NGX_CONF_ERROR;
                }

            } else {
                if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
                    return NGX_CONF_ERROR;
                }
            }

        } else {

            clcf->name = *name;

            if (name->data[0] == '@') {
                clcf->named = 1;
            }
        }
    }

    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

    if (cf->cmd_type == NGX_HTTP_LOC_CONF) {

        /* nested location */

#if 0
        clcf->prev_location = pclcf;
#endif

        if (pclcf->exact_match) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "location \"%V\" cannot be inside "
                               "the exact location \"%V\"",
                               &clcf->name, &pclcf->name);
            return NGX_CONF_ERROR;
        }

        if (pclcf->named) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "location \"%V\" cannot be inside "
                               "the named location \"%V\"",
                               &clcf->name, &pclcf->name);
            return NGX_CONF_ERROR;
        }

        if (clcf->named) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "named location \"%V\" can be "
                               "on the server level only",
                               &clcf->name);
            return NGX_CONF_ERROR;
        }

        len = pclcf->name.len;

#if (NGX_PCRE)
        if (clcf->regex == NULL
            && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
#else
        if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
#endif
        {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "location \"%V\" is outside location \"%V\"",
                               &clcf->name, &pclcf->name);
            return NGX_CONF_ERROR;
        }
    }

    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    save = *cf;
    cf->ctx = ctx;
    cf->cmd_type = NGX_HTTP_LOC_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

    return rv;
}

该函数是 Nginx 解析 location 配置块的核心实现,负责:

  1. 解析 location 语法 :支持精确匹配(=)、前缀匹配(^~)、正则表达式(~~*)及命名 location(@)。
  2. 创建配置上下文 :为当前 location 块初始化模块配置结构体。
  3. 管理嵌套规则 :确保 location 块的嵌套符合 Nginx 语法限制(如禁止在精确匹配或命名 location 内嵌套)。
  4. 注册 location 到父级配置 :将解析后的 location 添加到所属父级(server 或上层 location)的配置中。

复制代码
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

location 块创建独立的配置上下文 ,用于存储该作用域内的模块配置数据

ngx_http_conf_ctx_t -CSDN博客

复制代码
    pctx = cf->ctx;
    ctx->main_conf = pctx->main_conf;
    ctx->srv_conf = pctx->srv_conf;

继承父级配置上下文 ,确保 location 块可以复用父级(httpserver 块)的全局配置,同时允许独立覆盖或扩展自己的 loc_conf

  • main_conf :所有 location 共享同一份 http 配置。
  • srv_conf :同一 server 下的所有 location 共享 server 配置。
  • loc_conf :每个 location 独立,用于覆盖或扩展配置。
复制代码
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }

location 块的模块配置分配存储空间 ,确保每个 HTTP 模块可以在当前作用域(location)中存储自己的配置数据

复制代码
    for (i = 0; cf->cycle->modules[i]; i++) {
        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[i]->ctx;

        if (module->create_loc_conf) {
            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =
                                                   module->create_loc_conf(cf);
            if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

为每个 HTTP 模块初始化 location 级别的配置结构体

复制代码
    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
    clcf->loc_conf = ctx->loc_conf;

将当前 location 的配置上下文(loc_conf 数组)关联到核心模块的配置结构体中 ,使得核心模块能够访问所有模块的配置数据,协调请求处理流程

ngx_http_core_loc_conf_t-CSDN博客

复制代码
    value = cf->args->elts;

获取指令的参数列表

复制代码
    if (cf->args->nelts == 3) {
参数数量检查
  • cf->args->nelts
    表示当前配置指令(如 location)的参数数量。
    • nelts == 3 :用户使用了显式修饰符(如 location = /exact)。
    • nelts == 2 :用户未显式指定修饰符

此时 cf->args->nelts=2

条件不成立

进入 else

复制代码
else {

        name = &value[1];

获取 location 指令的第一个参数

此时 name->data=/

复制代码
        if (name->data[0] == '=') {

此时条件不成立

复制代码
} else if (name->data[0] == '^' && name->data[1] == '~') {

此时 条件不成立

复制代码
} else if (name->data[0] == '~') {

此时 条件不成立

复制代码
        } else {

            clcf->name = *name;

            if (name->data[0] == '@') {
                clcf->named = 1;
            }
        }

namelocation 指令的路径参数(如 value[1],即用户配置中的第二个参数)。

将路径字符串赋值给核心模块配置结构体的 name 字段

此时 这里的 if 条件不成立

复制代码
pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

获取父级 location 的核心模块配置 ,用于校验嵌套规则或继承配置

通过索引从父级的 loc_conf 数组中获取核心模块的配置结构体

指向父级 location 的核心模块配置结构体

复制代码
if (cf->cmd_type == NGX_HTTP_LOC_CONF) {

判断当前是否处于 location 块的配置解析阶段

此时 cf->cmd_type=4000000 NGX_HTTP_LOC_CONF=8000000

条件不成立,当前还未进入 location 块内部配置解析的阶段

复制代码
    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

将新解析的 location 配置添加到父级(server 或上层 location)的链表中 ,确保请求处理时能够遍历所有 location 进行匹配。

  • ngx_http_add_location 函数 :
    • 功能 :将当前 locationclcf)添加到父级的 locations 链表(pclcf->locations)。
    • 参数 :
      • cf:配置解析上下文。
      • &pclcf->locations:父级的 locations 链表指针。
      • clcf:当前 location 的核心模块配置结构体。

locations 链表 :
存储所有 location 配置

ngx_http_add_location-CSDN博客

复制代码
    save = *cf;
    cf->ctx = ctx;
    cf->cmd_type = NGX_HTTP_LOC_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = save;

save = *cf;

  • 保存当前配置解析上下文。
  • 逻辑 :
    • cf 是指向当前配置解析上下文(ngx_conf_t 结构)的指针。
    • save 是一个局部变量,类型为 ngx_conf_t
    • 通过 *cf 解引用操作,将当前配置解析状态(包括上下文指针、命令类型等)完整复制到 save 中。
  • 意义 :
    • 在后续解析 location 块时,会临时修改 cf 的上下文。此操作保存原始状态,以便解析完成后恢复。

cf->ctx = ctx;

  • 作用 :切换配置上下文到当前 location 的上下文。
  • 逻辑 :
    • ctx 是新创建的 ngx_http_conf_ctx_t 结构,专用于当前 location 块。
    • cf->ctx 是配置解析的核心上下文指针,指向当前生效的配置存储结构。
    • cf->ctx 指向新的 ctx,使得后续解析的指令会将配置存储到该 location 的上下文中。

cf->cmd_type = NGX_HTTP_LOC_CONF;

  • 作用 :设置当前解析的指令类型为 "HTTP Location 配置"。
  • 逻辑 :
    • cmd_typengx_conf_t 中的字段,用于标识当前解析的指令类型。
    • NGX_HTTP_LOC_CONF 表示接下来解析的指令属于 location 块。
  • 意义 :
    • 指令过滤 :Nginx 根据 cmd_type 调用对应模块的指令处理函数。例如,只有声明为 NGX_HTTP_LOC_CONF 类型的指令会被处理,其他指令(如 server 级指令)会触发错误。

rv = ngx_conf_parse(cf, NULL);

  • 作用 :解析 location 块内的配置指令。
  • 逻辑 :
    • ngx_conf_parse 是 Nginx 的核心配置解析函数。
    • 传入 cf(已切换到 location 上下文)和 NULL(表示继续解析当前配置流,而非新文件)。
    • 返回值 rv 表示解析结果(成功或错误码)。
  • 意义 :
    • 递归解析 :处理 location 块内的所有指令

ngx_conf_parse - location块-CSDN博客


*cf = save;

  • 作用 :恢复原始配置解析上下文。
  • 逻辑 :
    • 将之前保存的 save 值复制回 cf,覆盖当前修改后的 cf 状态。
    • 恢复字段包括 ctx(父级上下文)和 cmd_type(如 server 级指令类型)。
  • 意义 :
    • 上下文回退 :确保解析完当前 location 后,父级配置解析(如 server 块)能继续正确执行。
    • 避免污染 :防止 location 的临时配置影响后续指令的解析。

整体逻辑总结

  1. 保存现场 :备份父级配置上下文。
  2. 切换上下文 :为 location 块创建独立的配置存储空间。
  3. 标记指令类型 :确保仅处理 location 级指令。
  4. 解析子配置 :处理 location 内的所有指令。
  5. 恢复现场 :回到父级配置解析流程。
复制代码
return rv;

返回 NGX_CONF_OK

相关推荐
Kendra9196 小时前
Keepalive+LVS+Nginx+NFS高可用架构
nginx·架构·lvs
若云止水6 小时前
ngx_http_core_main_conf_t
nginx
开发小能手-roy9 小时前
Ubuntu 系统中安装 Nginx
数据库·nginx·ubuntu
小破程序员12 小时前
docker安装ngnix
服务器·nginx·docker
棕生14 小时前
架构师面试(二十三):负载均衡
nginx·负载均衡·lvs·架构师面试·rpc连接池·vip+keepalive
音视频牛哥1 天前
Nginx RTMP DASH 模块分析 (ngx_rtmp_dash_module.c)
运维·nginx·大牛直播sdk·dash·nginx rtmp服务器·nginx dash·dash播放
若云止水1 天前
ngx_http_core_error_page
nginx
清风序来1 天前
为什么使用Flask + uWSGI + Nginx 部署服务?
python·nginx·flask
啃瓜子的松鼠2 天前
Nginx 学习笔记
linux·后端·nginx
家乡的落日3 天前
Linux\CentOS解决OpenSSH和Nginx安全漏洞
linux·nginx·centos