ngx_http_merge_types

ngx_http_merge_types
1 定义ngx_http_merge_types 函数 定义在 ./nginx-1.24.0/src/http/ngx_http.cchar*ngx_http_merge_types(ngx_conf_t*cf,ngx_array_t**keys,ngx_hash_t*types_hash,ngx_array_t**prev_keys,ngx_hash_t*prev_types_hash,ngx_str_t*default_types){ngx_hash_init_thash;if(*keys){if(*keys(void*)-1){returnNGX_CONF_OK;}hash.hashtypes_hash;hash.keyNULL;hash.max_size2048;hash.bucket_size64;hash.nametest_types_hash;hash.poolcf-pool;hash.temp_poolNULL;if(ngx_hash_init(hash,(*keys)-elts,(*keys)-nelts)!NGX_OK){returnNGX_CONF_ERROR;}returnNGX_CONF_OK;}if(prev_types_hash-bucketsNULL){if(*prev_keysNULL){if(ngx_http_set_default_types(cf,prev_keys,default_types)!NGX_OK){returnNGX_CONF_ERROR;}}elseif(*prev_keys(void*)-1){*keys*prev_keys;returnNGX_CONF_OK;}hash.hashprev_types_hash;hash.keyNULL;hash.max_size2048;hash.bucket_size64;hash.nametest_types_hash;hash.poolcf-pool;hash.temp_poolNULL;if(ngx_hash_init(hash,(*prev_keys)-elts,(*prev_keys)-nelts)!NGX_OK){returnNGX_CONF_ERROR;}}*types_hash*prev_types_hash;returnNGX_CONF_OK;}2 目的ngx_http_merge_types是 Nginx 提供的一个通用配置合并函数。它的设计目的是将 HTTP 模块中那些用于匹配 MIME 类型的列表从父级配置合并到子级配置并自动为这些类型列表构建一个用于运行时快速查找的哈希表。为什么需要设计这样一个函数1 避免重复实现相同的继承逻辑Nginx 中有好几个模块都支持根据 MIME 类型来筛选响应charset_types决定哪些 MIME 类型允许设置字符集。gzip_types决定哪些 MIME 类型允许被 gzip 压缩。sub_filter_types决定哪些 MIME 类型允许进行内容替换。这些模块都需要实现几乎一模一样的逻辑如果没有ngx_http_merge_types这段逻辑就会在charset、gzip、sub等多个模块中重复出现造成代码冗余和维护困难。将该逻辑抽取成一个公共函数是软件工程中“消除重复代码”原则的直接体现。3 详解1 函数签名char*ngx_http_merge_types(ngx_conf_t*cf,ngx_array_t**keys,ngx_hash_t*types_hash,ngx_array_t**prev_keys,ngx_hash_t*prev_types_hash,ngx_str_t*default_types)1. 返回值类型char *char *表示函数返回一个指向字符的指针。在 Nginx 配置解析的约定中char *返回值实际被用作状态指示器而不是普通字符串。调用者通过检查返回值是否为NGX_CONF_OK来判断操作是否成功。这与所有 Nginx 配置处理函数保持一致。2. 函数名ngx_http_merge_types函数名由下划线分隔的多个部分组成严格遵循 Nginx 的命名惯例ngx全局前缀表明这是 Nginx 源码的一部分用于防止符号冲突。http表明该函数属于 HTTP 子系统处理的是与 HTTP 模块配置相关的逻辑。merge动词意为“合并”。这里指将父级和当前级别的 MIME 类型配置按照继承规则合成最终值。types特指MIME 类型列表即配置文件中类似charset_types、gzip_types等指令所定义的一组媒体类型字符串例如text/html。因此ngx_http_merge_types的完整含义是在 HTTP 模块配置合并过程中用于合并 MIME 类型列表并为其构建查找哈希表的通用函数。3. 参数列表该函数接收六个参数提供了合并所需的全部原始数据、输出位置以及默认值。1ngx_conf_t *cf指向ngx_conf_t的指针。该结构体代表 Nginx 解析配置文件时的配置上下文。提供配置期间的内存池cf-pool用于哈希表的构建内存分配。此外配置日志等信息也可通过cf获取。2ngx_array_t **keys类型指向ngx_array_t *的指针即“指向数组指针的指针”。内容*keys是一个数组ngx_array_t数组中存放的是 MIME 类型字符串的键ngx_str_t例如text/html、text/plain。这个数组通常由模块的charset_types或gzip_types指令解析后填充。为何使用**因为函数可能需要修改调用者传递进来的数组指针本身。如果当前级别没有显式设置类型列表函数可能会将它指向父级的数组或者使用默认类型数组填充。通过传递指针的指针函数可以改变keys这个指针的值使之指向最终的数组。keys是函数的主要输入/输出之一最终它必须指向当前级别生效的类型数组无论来自自己、父级还是默认值。3ngx_hash_t *types_hash类型指向ngx_hash_t结构体的指针。内容ngx_hash_t是 Nginx 的哈希表结构用于运行时快速判断一个给定的 MIME 类型是否在列表中。作用函数会根据最终的keys数组为*types_hash构建一个哈希表通过ngx_hash_init或者直接从父级哈希表复制过来。调用者传入的是当前级别配置结构体中的哈希表字段地址。配置合并不仅需要记录“类型列表”还要提供运行时查找用的哈希表。这两个数据是紧密关联的该函数负责保证它们的同步。4ngx_array_t **prev_keys类型同样是指向数组指针的指针。内容*prev_keys指向父级配置server或http级别中的类型键数组。为何使用**原因与keys类似。父级数组的指针可能需要被修改——如果父级数组原本是NULL函数可能会为其填充默认类型并需要让*prev_keys指向这个新数组以便父级配置也能直接使用。实现“懒加载”——只有需要继承父级配置时才可能触发对父级默认值的填充。这样避免了在父级不相关时做多余工作。5ngx_hash_t *prev_types_hash类型指向ngx_hash_t的指针。内容父级配置中对应的哈希表。作用如果当前级别需要继承父级的类型列表函数会检查父级哈希表是否已经构建prev_types_hash-buckets NULL。如果没有构建则会使用*prev_keys为父级构建哈希表。然后将*types_hash直接赋值为*prev_types_hash从而共享同一个哈希表避免重复构建。6ngx_str_t *default_types类型指向ngx_str_t数组的指针。内容一个以空字符串data为NULL、len为 0结尾的字符串数组包含该模块内置的默认 MIME 类型列表。例如字符集模块传入的默认值包含text/html等。作用当父级和子级都没有配置类型列表时函数会调用ngx_http_set_default_types使用这个默认数组来填充*keys。允许每个调用模块指定自己的默认类型而无需将默认值硬编码在ngx_http_merge_types中保持了函数的通用性。7 签名整体反映出的设计信息通用工具函数没有static修饰该函数是可跨模块调用的公开 API体现了 Nginx 对重复逻辑的抽取。输出参数设计通过多重指针**keys、*types_hash来实现对调用者数据的直接修改是 C 语言中典型的“输出参数”用法。配置合并与运行时优化的结合函数同时处理了配置值的继承和哈希表的构建/共享将配置加载与运行效率统一考虑。父级懒初始化通过prev_keys和prev_types_hash的配合父级配置可以按需填充默认值并构建哈希表避免了不必要的计算和内存分配。2 逻辑流程1 当前级别有显式类型列表*keys ! NULL 1-1 如果列表是禁用标记 (void *) -1直接跳过。 1-2 否则使用当前级别的数组构建哈希表并写入 *types_hash。 完成后返回。 2 当前级别没有类型列表*keys NULL 2-1 父级哈希表未构建 2-1-1 父级类型列表为空使用 默认 2-1-2 父级类型列表为禁用 2-1-3 构建父级哈希表 2-2 父级哈希表已构建 将子级哈希表指针指向父级哈希表实现共享。{ngx_hash_init_thash;声明局部变量hash是一个ngx_hash_init_t类型的结构体变量用于临时保存哈希表初始化所需的参数1 当前级别有显式类型列表if(*keys){检查当前级别是否有自己的类型列表*keys解引用指针得到指向当前级别类型数组的指针。如果非 NULL说明当前配置块如 location中显式地通过指令如 charset_types定义了自己的 MIME 类型列表。此时应该使用当前级别自己的列表而忽略父级。1-1 列表是禁用if(*keys(void*)-1){returnNGX_CONF_OK;}(void *) -1是一个特殊的指针值在 Nginx 中常被用作“禁用”或“空列表”的标记。例如可能表示用户配置了charset_types off;或类似指令将数组指针设置为这个值。如果当前级别的列表是这个标记表示不处理任何 MIME 类型。此时不需要构建哈希表直接返回成功。1-2 使用当前级别的数组构建哈希表hash.hashtypes_hash;hash.keyNULL;hash.max_size2048;hash.bucket_size64;hash.nametest_types_hash;hash.poolcf-pool;hash.temp_poolNULL;设置哈希表初始化参数hash.hash types_hash指定要初始化的哈希表是当前级别的*types_hashhash.key NULL哈希函数指针。设为 NULL 表示使用 Nginx 内部的默认哈希函数对字符串键计算哈希值并在查找时进行不区分大小写的比较适合 MIME 类型。hash.max_size 2048哈希表桶的最大数量。2048 是一个常用的默认值足够容纳大多数类型列表。hash.bucket_size 64每个桶的字节大小用于分配内存。hash.name test_types_hash哈希表名称用于调试日志输出。hash.pool cf-pool内存池从配置内存池中分配哈希表所需的内存。hash.temp_pool NULL临时内存池这里不需要。if(ngx_hash_init(hash,(*keys)-elts,(*keys)-nelts)!NGX_OK){returnNGX_CONF_ERROR;}调用ngx_hash_init构建哈希表如果初始化失败例如内存不足返回NGX_CONF_ERRORreturnNGX_CONF_OK;}哈希表构建成功当前级别有自己的列表且已处理完毕直接返回成功2 当前级别没有类型列表2-1 父级哈希表未构建if(prev_types_hash-bucketsNULL){若*keys为 NULL开始继承父级逻辑程序执行到这里说明*keys 为 NULL即当前级别没有显式配置类型列表需要继承父级。prev_types_hash-buckets是父级哈希表的桶指针。如果为 NULL表示父级的哈希表尚未构建。此时需要先确保父级哈希表构建完成因为子级将共享它。2-1-1 父级类型列表为空使用 默认if(*prev_keysNULL){检查父级数组是否为空如果父级的类型数组指针也为 NULL说明父级也没有显式配置类型列表或者从未被初始化过。此时需要使用模块的默认类型列表来填充父级。if(ngx_http_set_default_types(cf,prev_keys,default_types)!NGX_OK){returnNGX_CONF_ERROR;}为父级填充默认类型列表ngx_http_set_default_types函数根据default_types数组以空字符串结尾创建一个数组并将*prev_keys指向这个新数组。这样父级就拥有了一个默认的类型列表。如果创建失败返回错误。ngx_http_set_default_types2-1-2 父级类型列表为禁用}elseif(*prev_keys(void*)-1){*keys*prev_keys;returnNGX_CONF_OK;}如果父级类型数组指针是特殊标记(void *) -1表示空列表子级也直接继承这个标记*keys *prev_keys然后返回成功当前级别也不处理任何类型。2-1-3 构建父级哈希表hash.hashprev_types_hash;hash.keyNULL;hash.max_size2048;hash.bucket_size64;hash.nametest_types_hash;hash.poolcf-pool;hash.temp_poolNULL;为父级构建哈希表如果父级有有效的类型数组*prev_keys 非 NULL 且非标记就需要为父级构建哈希表。这里设置hash.hash prev_types_hash表示这次是为父级哈希表初始化。if(ngx_hash_init(hash,(*prev_keys)-elts,(*prev_keys)-nelts)!NGX_OK){returnNGX_CONF_ERROR;}}调用ngx_hash_init为父级构建哈希表2-2 父级哈希表已构建*types_hash*prev_types_hash;子级继承父级哈希表将父级哈希表结构体整体复制给子级。因为ngx_hash_t结构体包含指向桶数组的指针等复制后子级和父级共享同一个哈希表。这避免了重复构建。returnNGX_CONF_OK;}返回成功