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

ngx_http_index_handler

1 定义

ngx_http_index_handler 函数 定义在 src/http/modules/ngx_http_index_module.c

2 作用

ngx_http_index_handler 函数 它处理以 '/' 结尾的目录请求, 根据 index 指令指定的文件列表依次尝试找到存在的索引文件,如果找到则内部重定向到该文件, 若所有尝试失败则返回 NGX_DECLINED 让其他模块继续处理。

3 详解

1 函数签名

staticngx_int_tngx_http_index_handler(ngx_http_request_t*r)

1. 返回值类型ngx_int_t

用于统一表示操作状态码。


2. 函数名ngx_http_index_handler

前缀ngx_http_
表明该函数属于 HTTP 子系统,处理的是 HTTP 协议相关的逻辑。

模块名index
直接指明它是ngx_http_index_module的一部分,该模块实现了index指令的功能。

后缀handler
Nginx 的命名惯例,表示该函数是一个阶段处理器
会被注册到某个 HTTP 处理阶段。
对于index模块,它注册在NGX_HTTP_CONTENT_PHASE阶段,
负责为目录请求寻找默认索引文件。

整体语义
该函数是 Nginx 中处理目录请求的核心入口,
它根据index指令指定的文件列表,依次在文件系统中查找第一个存在的索引文件,
并通过内部重定向让该文件的内容处理器生成最终响应。
函数名准确地反映了它的功能和在请求处理流程中的角色。


3. 参数ngx_http_request_t *r

指向 当前 HTTP 请求上下文结构体


2 逻辑流程

1 前置检查:只处理合法的目录请求 请求 URI 必须以 '/' 结尾 这是判断“目录请求”的唯一标准, 如果不满足,直接返回 `NGX_DECLINED`, 表明本模块不处理,避免了不必要的文件探测。 仅允许 GET、HEAD、POST 方法。 限制只有这三种方法才允许使用索引文件 对于 PUT、DELETE 等方法,索引功能无意义, 提早退出也能防止资源浪费和潜在的安全问题。 这两步过滤保证了只有真正需要索引服务的请求才会进入后续流程,使模块职责清晰,执行效率高。 2 获取配置与初始化工作变量 3 遍历索引文件列表 3-1 计算当前文件名所需的长度(len)和预留空间(reserve) 3-2 确保文件系统路径缓冲区有足够空间 3-3 拼接完整的文件系统路径 3-4 测试文件是否存在 3-5 构造内部重定向 URI 并执行重定向 当文件存在且类型匹配后,函数需要将请求以新 URI 重新投入处理流水线。 4 全部未命中 循环结束,返回 NGX_DECLINED 如果 for 循环遍历完所有索引文件,没有一个文件存在且满足条件,则退出循环, 执行最后的 return NGX_DECLINED;。这表示索引模块无法处理该请求, 内容检查器会继续调用下一个内容模块,或最终由核心模块返回 403/404。 这种“尝试-重定向”的设计,将“决定用哪个文件”和“如何发送该文件”彻底解耦。 索引模块只负责路由选择,实际内容生成完全委托给后续阶段,符合 Nginx 的模块化架构。

{u_char*p,*name;size_tlen,root,reserve,allocated;ngx_int_trc;ngx_str_tpath,uri;ngx_uint_ti,dir_tested;ngx_http_index_t*index;ngx_open_file_info_tof;ngx_http_script_code_pt code;ngx_http_script_engine_te;ngx_http_core_loc_conf_t*clcf;ngx_http_index_loc_conf_t*ilcf;ngx_http_script_len_code_pt lcode;

局部变量 声明


1 前置检查:只处理合法的目录请求

if(r->uri.data[r->uri.len-1]!='/'){returnNGX_DECLINED;}if(!(r->method&(NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))){returnNGX_DECLINED;}

2 获取配置与初始化工作变量

ilcf=ngx_http_get_module_loc_conf(r,ngx_http_index_module);clcf=ngx_http_get_module_loc_conf(r,ngx_http_core_module);allocated=0;root=0;dir_tested=0;name=NULL;/* suppress MSVC warning */path.data=NULL;

3 遍历索引文件列表

index=ilcf->indices->elts;for(i=0;i<ilcf->indices->nelts;i++){

3-1 计算当前文件名所需的长度
if(index[i].lengths==NULL){if(index[i].name.data[0]=='/'){returnngx_http_internal_redirect(r,&index[i].name,&r->args);}reserve=ilcf->max_index_len;len=index[i].name.len;

静态文件名(index[i].lengths == NULL):

如果文件名是普通字符串(不含变量),
直接取index[i].name.len作为长度,该长度通常包含结尾的 ‘\0’。

如果该字符串的第一个字符是 ‘/’,说明它是一个绝对路径,
此时不需要在文件系统中查找,直接调用ngx_http_internal_redirect进行内部重定向并返回。

reserve被设置为ilcf->max_index_len——所有静态索引名的最大长度。
这样做的好处是,只要第一次路径映射时预留了最大空间,
后续所有静态名都可以复用同一个缓冲区,不必每次都重新映射路径。


}else{ngx_memzero(&e,sizeof(ngx_http_script_engine_t));e.ip=index[i].lengths->elts;e.request=r;e.flushed=1;/* 1 is for terminating '\0' as in static names */len=1;while(*(uintptr_t*)e.ip){lcode=*(ngx_http_script_len_code_pt*)e.ip;len+=lcode(&e);}/* 16 bytes are preallocation */reserve=len+16;}

动态文件名(含有变量,index[i].lengths != NULL):
需要利用脚本引擎计算实际长度。
首先初始化引擎 e,将编译好的长度计算指令序列(index[i].lengths->elts)交给引擎,循环执行每一条指令,累加得到总长度 len。初始 len = 1 是为结尾的 ‘\0’ 预留。
然后预留空间 reserve = len + 16,其中 16 字节是额外安全边界。


3-2 确保文件系统路径缓冲区有足够空间
if(reserve>allocated){name=ngx_http_map_uri_to_path(r,&path,&root,reserve);if(name==NULL){returnNGX_HTTP_INTERNAL_SERVER_ERROR;}allocated=path.data+path.len-name;}

确保文件系统路径缓冲区有足够空间

ngx_http_map_uri_to_path函数分配一个缓冲区用来 拼接文件完整路径
缓冲区 首地址是path.data, 缓冲区长度是path.len
path.dataname之间是 根据root/alias指令设置的根路径而
映射得到的 系统绝对路径,allocated是缓冲区剩余的空闲空间
是在之后可以用来拼接index[i].name文件名的剩余空间

reserve是需要为文件名预留的空间大小,
实际剩余空间小于这个大小就需要重新分配空间
失败(返回 NULL),直接返回 500
成功更新allocated实际剩余大小

这个机制使得在整个索引文件列表的遍历过程中,路径映射只会在必要时执行,
极大地减少了系统调用开销。


3-3 拼接完整的文件系统路径
if(index[i].values==NULL){/* index[i].name.len includes the terminating '\0' */ngx_memcpy(name,index[i].name.data,index[i].name.len);path.len=(name+index[i].name.len-1)-path.data;}else{e.ip=index[i].values->elts;e.pos=name;while(*(uintptr_t*)e.ip){code=*(ngx_http_script_code_pt*)e.ip;code((ngx_http_script_engine_t*)&e);}if(*name=='/'){uri.len=len-1;uri.data=name;returnngx_http_internal_redirect(r,&uri,&r->args);}path.len=e.pos-path.data;*e.pos='\0';}

拼接完整的文件系统路径


静态文件名(index[i].values == NULL):
直接将静态文件名(包含结尾 ‘\0’)拷贝到 name 位置,
然后调整 path.len,使其表示完整的文件路径长度(不包括结尾 ‘\0’)。


动态文件名(需要执行脚本生成):
将脚本引擎的值生成指令序列交给引擎,执行后将生成的字符串写入 name。
如果生成的字符串以 ‘/’ 开头,同样视为绝对路径,直接内部重定向返回。
生成结束后,更新 path.len 并在 *e.pos = ‘\0’ 添加字符串结束符,形成完整的 C 字符串路径。


3-4 测试文件是否存在
ngx_log_debug1(NGX_LOG_DEBUG_HTTP,r->connection->log,0,"open index \"%V\"",&path);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.test_only=1;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){returnNGX_HTTP_INTERNAL_SERVER_ERROR;}

初始化ngx_open_file_info_t结构体of并清零

设置of.test_only = 1
表示仅测试文件是否存在及属性,不真正打开文件,从而节省文件描述符。

设置 of.read_ahead、of.directio、of.valid 等字段,以便测试时遵循与正式文件传输相同的 I/O 策略和文件缓存行为。

调用ngx_http_set_disable_symlinks根据配置调整符号链接处理方式。


if(ngx_open_cached_file(clcf->open_file_cache,&path,&of,r->pool)!=NGX_OK){if(of.err==0){returnNGX_HTTP_INTERNAL_SERVER_ERROR;}ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r->connection->log,of.err,"%s \"%s\" failed",of.failed,path.data);#if(NGX_HAVE_OPENAT)if(of.err==NGX_EMLINK||of.err==NGX_ELOOP){returnNGX_HTTP_FORBIDDEN;}#endifif(of.err==NGX_ENOTDIR||of.err==NGX_ENAMETOOLONG||of.err==NGX_EACCES){returnngx_http_index_error(r,clcf,path.data,of.err);}if(!dir_tested){rc=ngx_http_index_test_dir(r,clcf,path.data,name-1);if(rc!=NGX_OK){returnrc;}dir_tested=1;}if(of.err==NGX_ENOENT){continue;}ngx_log_error(NGX_LOG_CRIT,r->connection->log,of.err,"%s \"%s\" failed",of.failed,path.data);returnNGX_HTTP_INTERNAL_SERVER_ERROR;}

调用ngx_open_cached_file实际进行测试。
该函数会先检查文件缓存,若无缓存或已过期则进行stat等系统调用。

ngx_open_cached_file


测试失败处理:

如果 of.err == 0,说明未知错误,返回 500。

如果错误是 NGX_ENOENT(文件不存在),这是最常见的情况,continue 尝试下一个索引文件。

对于 NGX_EMLINK(链接数过多)或 NGX_ELOOP(符号链接循环),返回 403。

对于 NGX_ENOTDIR(路径成分不是目录)、NGX_ENAMETOOLONG(名称太长)或 NGX_EACCES(权限不足),调用 ngx_http_index_error 返回适当的错误响应。

在首次遇到错误且尚未测试过父目录时,调用 ngx_http_index_test_dir 检查父目录是否存在,若不存在则返回相应错误(如 404),避免后续对同一目录的无意义尝试。

其他错误记录 CRIT 级别日志,返回 500。

文件存在但类型不匹配:如果 of.is_dir != test_dir(例如期望文件但实际是目录,或反之),则 continue 尝试下一个索引文件。


3-5 构造内部重定向 URI 并执行重定向
uri.len=r->uri.len+len-1;if(!clcf->alias){uri.data=path.data+root;}else{uri.data=ngx_pnalloc(r->pool,uri.len);if(uri.data==NULL){returnNGX_HTTP_INTERNAL_SERVER_ERROR;}p=ngx_copy(uri.data,r->uri.data,r->uri.len);ngx_memcpy(p,name,len-1);}returnngx_http_internal_redirect(r,&uri,&r->args);}

当文件存在且类型匹配后,函数需要将请求以新 URI 重新投入处理流水线。

计算新 URI 长度:
uri.len = r->uri.len + len - 1; 其中 len 包含结尾 ‘\0’,减 1 得到实际文件名字符数,加上原请求 URI 长度(即目录路径长度)即为新 URI 总长度。

构造 uri.data:

未使用 alias(即 root 模式):直接利用路径缓冲区中的指针偏移 uri.data = path.data + root; 获得新 URI 的起始地址,无需额外内存分配(零拷贝)。

使用了 alias:因为文件系统路径与请求 URI 不是简单的前缀对应关系,需要从内存池分配空间,先拷贝原请求 URI(目录部分),再拼接文件名(不含结尾 ‘\0’),构成新 URI。

调用 return ngx_http_internal_redirect(r, &uri, &r->args); 执行内部重定向。该函数会修改 r->uri,重置 phase_handler,让请求重新进入 NGX_HTTP_SERVER_REWRITE_PHASE,之后会再次经历 location 匹配和内容处理。函数直接返回该重定向的结果,结束索引处理。


4 全部未命中

returnNGX_DECLINED;}
http://www.cnnetsun.cn/news/3051769.html

相关文章:

  • cu-cockpit:轻量级Linux单节点运维管理平台入门指南
  • Python驱动Aspose.Words:精准提取Word文档结构化数据的实战指南
  • SAP二维码尺寸与布局的实战调优
  • 模型初始化常用参数设置
  • 大数据本科生不考研,靠项目能进优质企业吗?
  • 老旧安卓电视直播优化终极指南:如何用MyTV-Android让低端设备流畅播放
  • 非结构化数据服务模型训练的处理方式
  • 【Springboot毕设全套源码+文档】基于springboot智能垃圾分类系统的设计与实现(丰富项目+远程调试+讲解+定制)
  • Lua学习笔记:库函数
  • 2026闭眼入!5款AI论文工具亲测,摆脱无效加班,初稿质量效率翻倍
  • Adobe GenP 3.0完整教程:免费解锁Adobe CC全系列软件的终极指南
  • 免费音乐解锁工具:3分钟掌握跨平台音乐解密完整指南
  • 如何用SetDPI轻松解决Windows多显示器DPI缩放难题?
  • 基于pytest的接口自动化测试框架:从设计到实战
  • Go语言实现后量子密码算法Kyber与Dilithium:原理、挑战与工程实践
  • FastAdmin框架存储型XSS漏洞深度剖析与安全加固实战
  • 总结 6.28
  • rust 学习 多线程3
  • 接口自动化测试脚本生成Agent Skill
  • 渗透测试实战入门:从零到精通DC-1靶场攻防全流程解析
  • 终极指南:如何让Navicat Mac版实现永久免费试用
  • 实战深度解析:Unitree RL GYM如何实现机器人策略的多仿真环境无缝迁移
  • Ryujinx:C构建的任天堂Switch模拟器技术解析与应用指南
  • 、微信读书、知乎装进 Obsidian:我基于llm-wiki知识中枢搭建实录
  • 单层 ?? 的含义是:左边为 null 则取右边。
  • GHelper:为华硕笔记本量身打造的轻量级控制工具
  • 图片太大怎么缩小
  • FastCut 大更新:第一个能让 Codex / ZCode 直接操刀的浏览器剪辑台
  • Kindle漫画转换终极指南:让你的电子阅读器变身漫画图书馆
  • 【毕业设计】基于 SpringBoot 的餐厅订单统计与菜品管理系统 中小型餐厅订单业务管理平台设计与实现(源码+文档+远程调试,全bao定制等)