package responseheaders import ( "net/http" "strings" "github.com/Wei-Shaw/sub2api/internal/config" ) // defaultAllowed 定义允许透传的响应头白名单 // 注意:以下头部由 Go HTTP 包自动处理,不应手动设置: // - content-length: 由 ResponseWriter 根据实际写入数据自动设置 // - transfer-encoding: 由 HTTP 库根据需要自动添加/移除 // - connection: 由 HTTP 库管理连接复用 var defaultAllowed = map[string]struct{}{ "content-type": {}, "content-encoding": {}, "content-language": {}, "cache-control": {}, "etag": {}, "last-modified": {}, "expires": {}, "vary": {}, "date": {}, "x-request-id": {}, "x-ratelimit-limit-requests": {}, "x-ratelimit-limit-tokens": {}, "x-ratelimit-remaining-requests": {}, "x-ratelimit-remaining-tokens": {}, "x-ratelimit-reset-requests": {}, "x-ratelimit-reset-tokens": {}, "retry-after": {}, "location": {}, "www-authenticate": {}, } // hopByHopHeaders 是跳过的 hop-by-hop 头部,这些头部由 HTTP 库自动处理 var hopByHopHeaders = map[string]struct{}{ "content-length": {}, "transfer-encoding": {}, "connection": {}, } func FilterHeaders(src http.Header, cfg config.ResponseHeaderConfig) http.Header { allowed := make(map[string]struct{}, len(defaultAllowed)+len(cfg.AdditionalAllowed)) for key := range defaultAllowed { allowed[key] = struct{}{} } for _, key := range cfg.AdditionalAllowed { normalized := strings.ToLower(strings.TrimSpace(key)) if normalized == "" { continue } allowed[normalized] = struct{}{} } forceRemove := make(map[string]struct{}, len(cfg.ForceRemove)) for _, key := range cfg.ForceRemove { normalized := strings.ToLower(strings.TrimSpace(key)) if normalized == "" { continue } forceRemove[normalized] = struct{}{} } filtered := make(http.Header, len(src)) for key, values := range src { lower := strings.ToLower(key) if _, blocked := forceRemove[lower]; blocked { continue } if _, ok := allowed[lower]; !ok { continue } // 跳过 hop-by-hop 头部,这些由 HTTP 库自动处理 if _, isHopByHop := hopByHopHeaders[lower]; isHopByHop { continue } for _, value := range values { filtered.Add(key, value) } } return filtered } func WriteFilteredHeaders(dst http.Header, src http.Header, cfg config.ResponseHeaderConfig) { filtered := FilterHeaders(src, cfg) for key, values := range filtered { for _, value := range values { dst.Add(key, value) } } }