| package responseheaders |
|
|
| import ( |
| "net/http" |
| "strings" |
|
|
| "github.com/Wei-Shaw/sub2api/internal/config" |
| ) |
|
|
| |
| |
| |
| |
| |
| 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": {}, |
| } |
|
|
| |
| var hopByHopHeaders = map[string]struct{}{ |
| "content-length": {}, |
| "transfer-encoding": {}, |
| "connection": {}, |
| } |
|
|
| type CompiledHeaderFilter struct { |
| allowed map[string]struct{} |
| forceRemove map[string]struct{} |
| } |
|
|
| var defaultCompiledHeaderFilter = CompileHeaderFilter(config.ResponseHeaderConfig{}) |
|
|
| func CompileHeaderFilter(cfg config.ResponseHeaderConfig) *CompiledHeaderFilter { |
| allowed := make(map[string]struct{}, len(defaultAllowed)+len(cfg.AdditionalAllowed)) |
| for key := range defaultAllowed { |
| allowed[key] = struct{}{} |
| } |
| |
| if cfg.Enabled { |
| for _, key := range cfg.AdditionalAllowed { |
| normalized := strings.ToLower(strings.TrimSpace(key)) |
| if normalized == "" { |
| continue |
| } |
| allowed[normalized] = struct{}{} |
| } |
| } |
|
|
| forceRemove := map[string]struct{}{} |
| if cfg.Enabled { |
| 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{}{} |
| } |
| } |
|
|
| return &CompiledHeaderFilter{ |
| allowed: allowed, |
| forceRemove: forceRemove, |
| } |
| } |
|
|
| func FilterHeaders(src http.Header, filter *CompiledHeaderFilter) http.Header { |
| if filter == nil { |
| filter = defaultCompiledHeaderFilter |
| } |
|
|
| filtered := make(http.Header, len(src)) |
| for key, values := range src { |
| lower := strings.ToLower(key) |
| if _, blocked := filter.forceRemove[lower]; blocked { |
| continue |
| } |
| if _, ok := filter.allowed[lower]; !ok { |
| continue |
| } |
| |
| if _, isHopByHop := hopByHopHeaders[lower]; isHopByHop { |
| continue |
| } |
| for _, value := range values { |
| filtered.Add(key, value) |
| } |
| } |
| return filtered |
| } |
|
|
| func WriteFilteredHeaders(dst http.Header, src http.Header, filter *CompiledHeaderFilter) { |
| filtered := FilterHeaders(src, filter) |
| for key, values := range filtered { |
| for _, value := range values { |
| dst.Add(key, value) |
| } |
| } |
| } |
|
|