当前位置: 首页 > article >正文

ngx_http_core_error_page

定义在 src\http\ngx_http_core_module.c

static char *
ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf = conf;

    u_char                            *p;
    ngx_int_t                          overwrite;
    ngx_str_t                         *value, uri, args;
    ngx_uint_t                         i, n;
    ngx_http_err_page_t               *err;
    ngx_http_complex_value_t           cv;
    ngx_http_compile_complex_value_t   ccv;

    if (clcf->error_pages == NULL) {
        clcf->error_pages = ngx_array_create(cf->pool, 4,
                                             sizeof(ngx_http_err_page_t));
        if (clcf->error_pages == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    value = cf->args->elts;

    i = cf->args->nelts - 2;

    if (value[i].data[0] == '=') {
        if (i == 1) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid value \"%V\"", &value[i]);
            return NGX_CONF_ERROR;
        }

        if (value[i].len > 1) {
            overwrite = ngx_atoi(&value[i].data[1], value[i].len - 1);

            if (overwrite == NGX_ERROR) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "invalid value \"%V\"", &value[i]);
                return NGX_CONF_ERROR;
            }

        } else {
            overwrite = 0;
        }

        n = 2;

    } else {
        overwrite = -1;
        n = 1;
    }

    uri = value[cf->args->nelts - 1];

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;
    ccv.value = &uri;
    ccv.complex_value = &cv;

    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    ngx_str_null(&args);

    if (cv.lengths == NULL && uri.len && uri.data[0] == '/') {
        p = (u_char *) ngx_strchr(uri.data, '?');

        if (p) {
            cv.value.len = p - uri.data;
            cv.value.data = uri.data;
            p++;
            args.len = (uri.data + uri.len) - p;
            args.data = p;
        }
    }

    for (i = 1; i < cf->args->nelts - n; i++) {
        err = ngx_array_push(clcf->error_pages);
        if (err == NULL) {
            return NGX_CONF_ERROR;
        }

        err->status = ngx_atoi(value[i].data, value[i].len);

        if (err->status == NGX_ERROR || err->status == 499) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid value \"%V\"", &value[i]);
            return NGX_CONF_ERROR;
        }

        if (err->status < 300 || err->status > 599) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "value \"%V\" must be between 300 and 599",
                               &value[i]);
            return NGX_CONF_ERROR;
        }

        err->overwrite = overwrite;

        if (overwrite == -1) {
            switch (err->status) {
                case NGX_HTTP_TO_HTTPS:
                case NGX_HTTPS_CERT_ERROR:
                case NGX_HTTPS_NO_CERT:
                case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
                    err->overwrite = NGX_HTTP_BAD_REQUEST;
            }
        }

        err->value = cv;
        err->args = args;
    }

    return NGX_CONF_OK;
}

ngx_http_core_error_page 函数是 Nginx 中用于解析和配置自定义错误页面的核心函数


    if (clcf->error_pages == NULL) {
        clcf->error_pages = ngx_array_create(cf->pool, 4,
                                             sizeof(ngx_http_err_page_t));
        if (clcf->error_pages == NULL) {
            return NGX_CONF_ERROR;
        }
    }

检查当前配置块(clcf)的 error_pages 数组是否已初始化

如果 error_pages 未初始化(NULL),说明这是第一次为此配置块设置错误页面规则,需要创建数组

ngx_http_core_loc_conf_t-CSDN博客

clcf->error_pages 是 Nginx 中 ngx_http_core_loc_conf_t 结构体的一个字段,用于存储当前配置块中定义的所有 error_page 规则

ngx_http_err_page_t-CSDN博客

每个元素是一个 ngx_http_err_page_t 结构体,记录了以下信息:

  • 错误状态码 (如 404500
  • 重写后的响应状态码 
  • 目标 URI 
  • URI 的查询参数 
    value = cf->args->elts;

    i = cf->args->nelts - 2;

    if (value[i].data[0] == '=') {
        if (i == 1) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid value \"%V\"", &value[i]);
            return NGX_CONF_ERROR;
        }

        if (value[i].len > 1) {
            overwrite = ngx_atoi(&value[i].data[1], value[i].len - 1);

            if (overwrite == NGX_ERROR) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "invalid value \"%V\"", &value[i]);
                return NGX_CONF_ERROR;
            }

        } else {
            overwrite = 0;
        }

        n = 2;

    } else {
        overwrite = -1;
        n = 1;
    }
value = cf->args->elts;

获取当前配置指令的所有参数列表

i = cf->args->nelts - 2;

cf->args->nelts 是参数总数

i 的值为 参数总数 - 2,即最后一个参数前的参数索引

此时 i=4

if (value[i].data[0] == '=') {

检查当前参数是否以 = 开头(即是否为覆盖状态码的语法)

  • 若参数以 = 开头(如 =200),则进入覆盖状态码的解析流程。
  • 否则,跳转到 else 分支,表示不覆盖状态码

此时条件不成立

else { overwrite = -1; n = 1; }

处理不覆盖状态码的情况

overwrite = -1:表示不覆盖原始错误码

n = 1:仅需跳过 uri 参数,后续处理所有错误码参数

    uri = value[cf->args->nelts - 1];

    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

    ccv.cf = cf;
    ccv.value = &uri;
    ccv.complex_value = &cv;
uri = value[cf->args->nelts - 1];

提取 error_page 指令的最后一个参数(即目标 URI)


ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

初始化 ngx_http_compile_complex_value_t 结构体

ngx_http_compile_complex_value_t-CSDN博客


ccv.cf = cf;

将当前配置解析上下文(cf)关联到编译器结构体

  • ccv 是编译复杂值的上下文结构体。
  • cf 包含配置解析的内存池、日志等关键信息

ccv.value = &uri;
  • uri 是用户配置的字符串(如 "/404.html"
  • ccv.value 是编译器的输入参数。

ccv.complex_value = &cv;

  • cvngx_http_complex_value_t 类型的变量,用于存储编译后的动态值。
  • ccv.complex_value 是编译器的输出目标。
    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

调用 ngx_http_compile_complex_value 函数,并检查返回值

error_page 的 URI 可能包含变量(如 /error_$status.html),需要通过 ngx_http_compile_complex_value 函数将其编译为运行时可解析的结构体。若编译失败,需终止配置解析

ngx_http_compile_complex_value-CSDN博客

ngx_str_null(&args);

 将 args 字符串(ngx_str_t 类型)初始化为空字符串

    if (cv.lengths == NULL && uri.len && uri.data[0] == '/') {
        p = (u_char *) ngx_strchr(uri.data, '?');

        if (p) {
            cv.value.len = p - uri.data;
            cv.value.data = uri.data;
            p++;
            args.len = (uri.data + uri.len) - p;
            args.data = p;
        }
    }
if (cv.lengths == NULL && uri.len && uri.data[0] == '/') {

检查 URI 是否为静态路径(无变量)且以 / 开头

  • cv.lengths == NULL:表示 URI 不含变量(如 $variable),是纯静态字符串。
  • uri.len:确保 URI 非空。
  • uri.data[0] == '/':URI 必须以 / 开头(本地路径)。

此时 条件成立

p = (u_char *) ngx_strchr(uri.data, '?');

在 URI 中查找 ? 的位置,用于分割路径和查询参数 

此时 p=NULL

    for (i = 1; i < cf->args->nelts - n; i++) {
        err = ngx_array_push(clcf->error_pages);
        if (err == NULL) {
            return NGX_CONF_ERROR;
        }

 

for (i = 1; i < cf->args->nelts - n; i++) {

遍历用户配置的错误码参数

i = 1:从第二个参数开始(第一个参数是 error_page 指令本身)

cf->args->nelts:配置指令的总参数个数

n:表示后续参数占用的槽位数

循环条件 i < 总参数数 - n,即处理所有状态码参数,跳过覆盖码和 URI 参数。

  • 示例
    • 配置 error_page 404 500 =200 /error.html;
      • cf->args->nelts = 5(参数列表:error_page, 404, 500, =200, /error.html)。
      • n = 2(覆盖码 =200 和 URI /error.html 占用 2 个槽位)。
      • 循环次数:i < 5 - 2i < 3i=1(处理 404)、i=2(处理 500)。

err = ngx_array_push(clcf->error_pages);

为当前错误码分配一个 ngx_http_err_page_t 结构,并添加到 clcf->error_pages 数组

ngx_http_err_page_t-CSDN博客


 

        err->status = ngx_atoi(value[i].data, value[i].len);

        if (err->status == NGX_ERROR || err->status == 499) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "invalid value \"%V\"", &value[i]);
            return NGX_CONF_ERROR;
        }

        if (err->status < 300 || err->status > 599) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "value \"%V\" must be between 300 and 599",
                               &value[i]);
            return NGX_CONF_ERROR;
        }

 

err->status = ngx_atoi(value[i].data, value[i].len);

将用户配置的字符串形式的状态码(如 "404")转换为整数

if (err->status == NGX_ERROR || err->status == 499) {

验证状态码的合法性

NGX_ERROR:表示字符串转换失败

499:Nginx 内部使用的特殊状态码(客户端主动关闭连接),禁止用户配置

if (err->status < 300 || err->status > 599) {

确保状态码在 HTTP 标准的错误码范围内。

HTTP 状态码规则:

3xx:重定向。

4xx:客户端错误。

5xx:服务器端错误。

Nginx 要求 error_page 仅处理 300-599 的状态码。

禁止配置非错误码(如 200 OK)或无效范围值。

        err->overwrite = overwrite;

        if (overwrite == -1) {
            switch (err->status) {
                case NGX_HTTP_TO_HTTPS:
                case NGX_HTTPS_CERT_ERROR:
                case NGX_HTTPS_NO_CERT:
                case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
                    err->overwrite = NGX_HTTP_BAD_REQUEST;
            }
        }

        err->value = cv;
        err->args = args;

 

err->overwrite = overwrite;

将用户配置的覆盖码(或默认值 -1)赋值给错误页结构体

if (overwrite == -1) {

当用户未显式指定覆盖码时,检查是否需要为某些特殊状态码设置默认覆盖值

switch (err->status) {
    case NGX_HTTP_TO_HTTPS:
    case NGX_HTTPS_CERT_ERROR:
    case NGX_HTTPS_NO_CERT:
    case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
        err->overwrite = NGX_HTTP_BAD_REQUEST;
}

为特定状态码设置默认覆盖码 400 Bad Request

匹配的状态码

NGX_HTTP_TO_HTTPS(497):客户端通过 HTTP 请求 HTTPS 服务。

NGX_HTTPS_CERT_ERROR(495):客户端证书验证失败。

NGX_HTTPS_NO_CERT(496):需要客户端证书但未提供。

NGX_HTTP_REQUEST_HEADER_TOO_LARGE(494):请求头过大。

覆盖逻辑 :将响应码改为 400 Bad Request,隐藏内部实现细节。

意义

安全性 :避免暴露敏感状态码(如 495、496)给客户端。

标准化 :统一返回标准 HTTP 状态码,符合 RFC 规范。

err->value = cv;

将处理后的 URI 存储到错误页结构体

cvngx_http_complex_value_t 类型,包含静态或动态 URI(如 /404.html 或包含变量的 /error_$status.html)。

在错误发生时,Nginx 会根据 cv 的值执行内部跳转或返回静态文件。

err->args = args;

将查询参数保存到错误页结构体。

args 是从 URI 中解析出的查询参数(如 code=404)。

在重定向或内部跳转时,携带参数传递给目标资源。

return NGX_CONF_OK;

 返回 NGX_CONF_OK;



http://www.kler.cn/a/610255.html

相关文章:

  • 回退N帧协议(GBN)有差错情况下的详细流程
  • Unity2D 五子棋 + Photon联网双人对战
  • Android系统的安全问题 - Linux的能力模型(Capability)和 SELinux 的区别
  • Checksum方法实现
  • DDR4、DDR5、固态硬盘(SSD)和机械硬盘(HDD)在连续读/写、随机读/写性能的对比分析
  • Softmax 回归 + 损失函数 + 图片分类数据集
  • 重生细胞全符文获取攻略
  • LangChain4j(1):初识LangChain4j
  • 3. 轴指令(omron 机器自动化控制器)——>MC_GearInPos
  • Open CASCADE学习|根据给定的点集拟合一条B样条曲线
  • MATLAB导入Excel数据
  • 【论文精读-图像恢复】 All-In-One Image Restoration for Unknown Corruption
  • Kotlin 协程官方文档知识汇总(二)
  • MySQL 和 Redis 数据一致性解决方案
  • 字节码生成技术
  • springboot启动事件CommandLineRunner使用
  • HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
  • BFS专项练习 —— 蓝桥杯刷题
  • SICAR 标准 KUKA 机器人标准功能块说明手册
  • Linux操作系统7- 线程同步与互斥7(RingQueue环形队列生产者消费者模型改进)