1 定义ngx_http_charset_header_filter 函数 定义在 src/http/modules/ngx_http_charset_filter_module.cstaticngx_int_tngx_http_charset_header_filter(ngx_http_request_t*r){ngx_int_tcharset,source_charset;ngx_str_tdst,src;ngx_http_charset_t*charsets;ngx_http_charset_main_conf_t*mcf;if(rr-main){charsetngx_http_destination_charset(r,dst);}else{charsetngx_http_main_request_charset(r,dst);}if(charsetNGX_ERROR){returnNGX_ERROR;}if(charsetNGX_DECLINED){returnngx_http_next_header_filter(r);}/* charset: charset index or NGX_HTTP_NO_CHARSET */source_charsetngx_http_source_charset(r,src);if(source_charsetNGX_ERROR){returnNGX_ERROR;}/* * source_charset: charset index, NGX_HTTP_NO_CHARSET, * or NGX_HTTP_CHARSET_OFF */ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r-connection-log,0,charset: \%V\ \%V\,src,dst);if(source_charsetNGX_HTTP_CHARSET_OFF){ngx_http_set_charset(r,dst);returnngx_http_next_header_filter(r);}if(charsetNGX_HTTP_NO_CHARSET||source_charsetNGX_HTTP_NO_CHARSET){if(source_charset!charset||ngx_strncasecmp(dst.data,src.data,dst.len)!0){gotono_charset_map;}ngx_http_set_charset(r,dst);returnngx_http_next_header_filter(r);}if(source_charsetcharset){r-headers_out.content_type.lenr-headers_out.content_type_len;ngx_http_set_charset(r,dst);returnngx_http_next_header_filter(r);}/* source_charset ! charset */if(r-headers_out.content_encodingr-headers_out.content_encoding-value.len){returnngx_http_next_header_filter(r);}mcfngx_http_get_module_main_conf(r,ngx_http_charset_filter_module);charsetsmcf-charsets.elts;if(charsets[source_charset].tablesNULL||charsets[source_charset].tables[charset]NULL){gotono_charset_map;}r-headers_out.content_type.lenr-headers_out.content_type_len;ngx_http_set_charset(r,dst);returnngx_http_charset_ctx(r,charsets,charset,source_charset);no_charset_map:ngx_log_error(NGX_LOG_ERR,r-connection-log,0,no \charset_map\ between the charsets \%V\ and \%V\,src,dst);returnngx_http_next_header_filter(r);}2 目的ngx_http_charset_header_filter是 Nginx 字符集处理模块的头部过滤器。它的设计目的和必要性紧密围绕 HTTP 协议中一个基础却关键的字段Content-Type头部的charset参数。这个函数的核心任务是根据 Nginx 配置中定义的字符集映射规则在发送响应头之前自动且正确地设置或转换响应Content-Type头中的charset参数。HTTP 响应中的Content-Type头例如text/html; charsetutf-8告诉浏览器应当用哪种字符集来解码响应体。如果这个字符集和实际内容的编码不一致用户看到的就是乱码。如果没有一个集中的函数来处理这些场景每个内容来源 都要自己实现字符集判断和设置逻辑会导致代码重复并容易出现不一致3 详解3-1 函数签名staticngx_int_tngx_http_charset_header_filter(ngx_http_request_t*r)1. 函数名ngx_http_charset_header_filtercharset指明该过滤器的操作与字符集相关。具体来说它负责设置或转换 HTTP 响应头Content-Type中的charset参数例如text/html; charsetutf-8。header表明它是一个头部过滤器在响应头发送之前被调用负责读取和修改响应头。filter表明它是一个过滤器是 Nginx 过滤器链中的一环。因此ngx_http_charset_header_filter的完整含义是HTTP 模块中专门用于处理字符集charset相关的头部过滤器函数。该函数对应的模块是ngx_http_charset_filter_module它实现了charset、source_charset、charset_map等配置指令。这个过滤器是这些指令生效的执行点。2 逻辑流程{ngx_int_tcharset,source_charset;ngx_str_tdst,src;ngx_http_charset_t*charsets;ngx_http_charset_main_conf_t*mcf;局部变量声明if(rr-main){charsetngx_http_destination_charset(r,dst);}else{charsetngx_http_main_request_charset(r,dst);}确定目标字符集r r-main判断当前请求是否为主请求。如果是主请求需要根据当前 location 配置独立计算目标字符集如果是子请求则应当继承主请求的目标字符集以保证整个请求处理链中字符集设置一致。ngx_http_destination_charset(r, dst)从当前请求的配置中解析出目标字符集。返回值是字符集在数组中的索引同时将名称写入dst。可能的返回值还包括NGX_ERROR发生错误。NGX_DECLINED配置中未设置目标字符集不需要处理。NGX_HTTP_NO_CHARSET配置中显式设置了 charset off即不添加 charset 参数。ngx_http_main_request_charset(r, dst)直接从主请求中获取已经确定的目标字符集子请求不再独立解析保证一致性。ngx_http_destination_charsetif(charsetNGX_ERROR){returnNGX_ERROR;}目标字符集获取失败时返回错误if(charsetNGX_DECLINED){returnngx_http_next_header_filter(r);}目标字符集为“拒绝处理”时跳过NGX_DECLINED表示当前配置中没有指定目标字符集或者根据charset_types等条件判断不应处理该响应。此时过滤器不做任何修改直接调用下一个头部过滤器并返回。/* charset: charset index or NGX_HTTP_NO_CHARSET */注释说明此时 charset 的值要么是一个有效的字符集索引要么是NGX_HTTP_NO_CHARSET表示不输出 charset。source_charsetngx_http_source_charset(r,src);确定源字符集ngx_http_source_charset函数分析当前响应的Content-Type头中已有的charset参数或者请求中source_charset配置强制指定的源字符集。返回值也是字符集索引并将名称写入 src。特殊返回值包括NGX_ERROR出错。NGX_HTTP_NO_CHARSET无法确定源字符集响应中没有 charset 参数且未配置 source_charset。NGX_HTTP_CHARSET_OFF配置中显式关闭了源字符集识别表示不需要进行转换只需设置目标字符集声明。if(source_charsetNGX_ERROR){returnNGX_ERROR;}源字符集获取失败时返回错误/* * source_charset: charset index, NGX_HTTP_NO_CHARSET, * or NGX_HTTP_CHARSET_OFF */ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r-connection-log,0,charset: \%V\ \%V\,src,dst);if(source_charsetNGX_HTTP_CHARSET_OFF){ngx_http_set_charset(r,dst);returnngx_http_next_header_filter(r);}源字符集为 OFF 时的处理NGX_HTTP_CHARSET_OFF表示配置明确告诉模块不要考虑源字符集直接强制将响应的Content-Type 中 charset设置为目标字符集。这通常对应于source_charset off指令。ngx_http_set_charset(r, dst)修改响应头中的Content-Type将其 charset 参数设置为目标字符集 dst。如果有必要还会调整 content_type_len 等字段。设置完成后直接调用下一个头部过滤器并返回不会进行任何字符集转换。if(charsetNGX_HTTP_NO_CHARSET||source_charsetNGX_HTTP_NO_CHARSET){if(source_charset!charset||ngx_strncasecmp(dst.data,src.data,dst.len)!0){gotono_charset_map;}ngx_http_set_charset(r,dst);returnngx_http_next_header_filter(r);}if(source_charsetcharset){r-headers_out.content_type.lenr-headers_out.content_type_len;ngx_http_set_charset(r,dst);returnngx_http_next_header_filter(r);}/* source_charset ! charset */if(r-headers_out.content_encodingr-headers_out.content_encoding-value.len){returnngx_http_next_header_filter(r);}mcfngx_http_get_module_main_conf(r,ngx_http_charset_filter_module);charsetsmcf-charsets.elts;if(charsets[source_charset].tablesNULL||charsets[source_charset].tables[charset]NULL){gotono_charset_map;}r-headers_out.content_type.lenr-headers_out.content_type_len;ngx_http_set_charset(r,dst);returnngx_http_charset_ctx(r,charsets,charset,source_charset);no_charset_map:ngx_log_error(NGX_LOG_ERR,r-connection-log,0,no \charset_map\ between the charsets \%V\ and \%V\,src,dst);returnngx_http_next_header_filter(r);}