ngx_http_set_etag 1 定义ngx_http_set_etag 函数 定义在 ./nginx-1.24.0/src/http/ngx_http_core_module.cngx_int_tngx_http_set_etag(ngx_http_request_t*r){ngx_table_elt_t*etag;ngx_http_core_loc_conf_t*clcf;clcfngx_http_get_module_loc_conf(r,ngx_http_core_module);if(!clcf-etag){returnNGX_OK;}etagngx_list_push(r-headers_out.headers);if(etagNULL){returnNGX_ERROR;}etag-hash1;etag-nextNULL;ngx_str_set(etag-key,ETag);etag-value.datangx_pnalloc(r-pool,NGX_OFF_T_LENNGX_TIME_T_LEN3);if(etag-value.dataNULL){etag-hash0;returnNGX_ERROR;}etag-value.lenngx_sprintf(etag-value.data,\%xT-%xO\,r-headers_out.last_modified_time,r-headers_out.content_length_n)-etag-value.data;r-headers_out.etagetag;returnNGX_OK;}2 作用1. HTTP 缓存与 ETag 的作用 在 HTTP 协议中一个资源比如一个 HTML 文件、一张图片经常会被客户端浏览器缓存下来。 当用户再次请求同一个资源时客户端可以直接使用缓存避免重新下载。 但这产生了一个问题客户端怎么知道它缓存的版本是不是最新的 为了解决这个问题HTTP 设计了 条件请求 和 验证器 。 其中一种验证器就叫 ETag实体标签。 ETag 是服务器为某个资源的特定版本生成的一个唯一标识符通常是一个字符串。 服务器在响应头中返回这个标识ETag: xxxx。 当客户端再次请求该资源时可以在请求头中带上 If-None-Match: xxxx 意思是“如果资源的 ETag 还是 xxxx说明它没变我就不需要重新下载。” 服务器收到请求后比较客户端发来的 ETag 和当前资源的 ETag。 如果相同服务器就只返回一个 304 Not Modified 状态码不带响应体 告诉客户端“你缓存的就是最新的”。这样就节省了传输整个响应体的带宽和时间。 因此设计一个生成 ETag 的函数 就是为了让服务器能够自动为响应生成一个有效的验证标识 从而支持 HTTP 的条件请求和缓存验证机制。ngx_http_set_etag 函数的存在是为了给 Nginx 提供一个开箱即用的、 高性能的 HTTP ETag 自动生成能力。 它通过读取配置开关利用请求中已有的最后修改时间和内容长度 快速计算出一个足够唯一的 ETag并添加到响应头中。 这使得 Nginx 能够支持基于 If-None-Match 的条件请求 从而在静态文件服务等场景下大幅节省带宽并提升响应速度 这一切都无需用户编写任何额外代码只需在配置中打开 etag on 即可。3 详解1 函数签名ngx_int_tngx_http_set_etag(ngx_http_request_t*r)1. 返回值类型 这个函数返回的不是计算出的 ETag 字符串本身 而是一个 状态码用来向调用者报告函数的执行结果。 NGX_OK操作成功。 可能意味着 ETag 头部已成功生成并加入响应头链表 也可能意味着配置指示不需要生成 ETag所以成功无事发生。 NGX_ERROR 操作失败。例如内存分配失败无法生成 ETag 头部。 调用者通过检查返回值是否为 NGX_OK 来判断函数是否正常完成。2. 函数名ngx_http_set_etag 函数名由下划线分隔的多个部分组成遵循 Nginx 的命名惯例 ngx 全局前缀所有 Nginx 源码中的类型、函数、宏都以此开头用以避免与其他库的名字冲突。 http表明该函数属于 Nginx 的 HTTP 核心模块处理 HTTP 协议相关的逻辑。 set动词意思是“设置、赋值”。这里指设置或添加一个响应头。 etag专有名词直接对应 HTTP 协议中的 ETag 响应头部实体标签。 因此ngx_http_set_etag 的完整含义是 在 HTTP 模块中为响应生成并设置 ETag 头部值的函数。3. 参数列表(ngx_http_request_t *r) ngx_http_request_t 是一个结构体类型 它包含了 Nginx 处理一个 HTTP 请求所需的全部信息2 逻辑流程1 检查配置是否开启了 ETag 功能 2 在响应头链表中创建一个新的头部项 3 设置头部的基本属性 4 分配内存用于存储计算出的 ETag 值 5 生成 ETag 值核心计算 6 将生成的头部关联到请求中 这个函数的意义在于为响应自动生成一个与资源版本相关的 ETag 以支持 HTTP 条件请求If-None-Match从而提升缓存效率、节省带宽。 它的实现非常高效完全基于已有的元数据不需要读取文件内容。{ngx_table_elt_t*etag;ngx_http_core_loc_conf_t*clcf;声明局部变量1 检查配置clcfngx_http_get_module_loc_conf(r,ngx_http_core_module);获取当前请求的 location 配置if(!clcf-etag){returnNGX_OK;}检查 ETag 功能是否开启如果未开启 ETag函数无需做任何工作直接返回 NGX_OK 表示“成功无事发生”。这是正常情况调用者不会认为出错。2 在响应头链表中创建一个新的头部项etagngx_list_push(r-headers_out.headers);if(etagNULL){returnNGX_ERROR;}在响应头链表中新增一个头部条目3 设置头部的基本属性etag-hash1;etag-nextNULL;ngx_str_set(etag-key,ETag);etag-hash用于 Nginx 内部快速查找头部。Nginx 在查找响应头时会计算头部名的哈希值并与这个字段匹配。设置hash 1是一个简化操作实际上正确的哈希值应在设置 key 后计算。这里提前赋值为 1是一种标记表明该条目已在使用中它的核心作用是让这个头部能被识别为有效条目。设置 next 指针为 NULLngx_table_elt_t结构体中有一个next指针用于将多个头部项链接起来例如处理同名头部时。这里将其设为 NULL表示当前没有后续链接项。设置头部字段名为 “ETag”执行后etag-key.data指向常量字符串 “ETag”etag-key.len等于 4不包括空字符。此时这个头部条目就有了名字 “ETag”。现在它是一个结构完整的头部只是 value 还没有设置。4 分配内存用于存储计算出的 ETag 值etag-value.datangx_pnalloc(r-pool,NGX_OFF_T_LENNGX_TIME_T_LEN3);if(etag-value.dataNULL){etag-hash0;returnNGX_ERROR;}为 ETag 的值分配内存分配大小是NGX_OFF_T_LEN NGX_TIME_T_LEN 3NGX_OFF_T_LEN一个宏表示off_t类型文件偏移量转换成字符串后的最大长度。NGX_TIME_T_LEN一个宏表示time_t类型时间戳转换成字符串后的最大长度。3额外的 3 个字节用于容纳格式化字符串中的两个双引号 和 以及一个连字符-内存分配失败处理etag-hash 0;将之前设置的hash重置为 0使这个头部条目无效化防止后续处理误用。返回NGX_ERROR表示函数执行失败。5 生成 ETag 值etag-value.lenngx_sprintf(etag-value.data,\%xT-%xO\,r-headers_out.last_modified_time,r-headers_out.content_length_n)-etag-value.data;生成格式化的 ETag 字符串并计算长度ngx_sprintf这是 Nginx 对标准 Csprintf的封装。它接受一个目标缓冲区指针、一个格式字符串以及随后的可变参数将格式化后的字符串写入目标缓冲区并返回写入完成后指向缓冲区末尾的指针即最后一个写入字符的下一个位置。参数解释etag-value.data目标缓冲区即我们刚刚分配的内存。\%xT-%xO\格式字符串。各部分含义\输出一个双引号字符 。%xT将参数r-headers_out.last_modified_time以十六进制小写格式输出%x是输出十六进制整数的格式占位符-输出一个字面的连字符。%xO将参数r-headers_out.content_length_n以十六进制小写输出\再输出一个双引号因此生成的字符串类似 “5f7b3c2a-1a40”前后有双引号。这正是 HTTP ETag 所要求的格式强验证器通常用双引号括起。r-headers_out.last_modified_time最后修改时间的整数值time_t。表示响应的资源最后被修改的时间。r-headers_out.content_length_n响应体的内容长度off_t。对于静态文件它通常由文件大小得到。etag-value.datangx_sprintf返回的指针减去缓冲区的起始指针etag-value.data差值就是写入的字符数总之etag-value.len被赋值为生成的 ETag 字符串的长度。6 将生成的头部关联到请求中r-headers_out.etagetag;将生成的 ETag 关联到请求的headers_out中r-headers_out是一个结构体存储所有待发送的响应头信息。其中有一个成员etag类型为ngx_table_elt_t *专门指向表示 ETag 头部的条目。returnNGX_OK;}返回成功函数成功完成所有步骤向调用者返回NGX_OK