responseheaders.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package responseheaders
  2. import (
  3. "net/http"
  4. "strings"
  5. "github.com/Wei-Shaw/sub2api/internal/config"
  6. )
  7. // defaultAllowed 定义允许透传的响应头白名单
  8. // 注意:以下头部由 Go HTTP 包自动处理,不应手动设置:
  9. // - content-length: 由 ResponseWriter 根据实际写入数据自动设置
  10. // - transfer-encoding: 由 HTTP 库根据需要自动添加/移除
  11. // - connection: 由 HTTP 库管理连接复用
  12. var defaultAllowed = map[string]struct{}{
  13. "content-type": {},
  14. "content-encoding": {},
  15. "content-language": {},
  16. "cache-control": {},
  17. "etag": {},
  18. "last-modified": {},
  19. "expires": {},
  20. "vary": {},
  21. "date": {},
  22. "x-request-id": {},
  23. "x-ratelimit-limit-requests": {},
  24. "x-ratelimit-limit-tokens": {},
  25. "x-ratelimit-remaining-requests": {},
  26. "x-ratelimit-remaining-tokens": {},
  27. "x-ratelimit-reset-requests": {},
  28. "x-ratelimit-reset-tokens": {},
  29. "retry-after": {},
  30. "location": {},
  31. "www-authenticate": {},
  32. // Codex uses this response header to avoid estimating reasoning tokens a
  33. // second time when upstream usage already includes them.
  34. "x-reasoning-included": {},
  35. }
  36. // hopByHopHeaders 是跳过的 hop-by-hop 头部,这些头部由 HTTP 库自动处理
  37. var hopByHopHeaders = map[string]struct{}{
  38. "content-length": {},
  39. "transfer-encoding": {},
  40. "connection": {},
  41. }
  42. type CompiledHeaderFilter struct {
  43. allowed map[string]struct{}
  44. forceRemove map[string]struct{}
  45. }
  46. var defaultCompiledHeaderFilter = CompileHeaderFilter(config.ResponseHeaderConfig{})
  47. func CompileHeaderFilter(cfg config.ResponseHeaderConfig) *CompiledHeaderFilter {
  48. allowed := make(map[string]struct{}, len(defaultAllowed)+len(cfg.AdditionalAllowed))
  49. for key := range defaultAllowed {
  50. allowed[key] = struct{}{}
  51. }
  52. // 关闭时只使用默认白名单,additional/force_remove 不生效
  53. if cfg.Enabled {
  54. for _, key := range cfg.AdditionalAllowed {
  55. normalized := strings.ToLower(strings.TrimSpace(key))
  56. if normalized == "" {
  57. continue
  58. }
  59. allowed[normalized] = struct{}{}
  60. }
  61. }
  62. forceRemove := map[string]struct{}{}
  63. if cfg.Enabled {
  64. forceRemove = make(map[string]struct{}, len(cfg.ForceRemove))
  65. for _, key := range cfg.ForceRemove {
  66. normalized := strings.ToLower(strings.TrimSpace(key))
  67. if normalized == "" {
  68. continue
  69. }
  70. forceRemove[normalized] = struct{}{}
  71. }
  72. }
  73. return &CompiledHeaderFilter{
  74. allowed: allowed,
  75. forceRemove: forceRemove,
  76. }
  77. }
  78. func FilterHeaders(src http.Header, filter *CompiledHeaderFilter) http.Header {
  79. if filter == nil {
  80. filter = defaultCompiledHeaderFilter
  81. }
  82. filtered := make(http.Header, len(src))
  83. for key, values := range src {
  84. lower := strings.ToLower(key)
  85. if _, blocked := filter.forceRemove[lower]; blocked {
  86. continue
  87. }
  88. if _, ok := filter.allowed[lower]; !ok {
  89. continue
  90. }
  91. // 跳过 hop-by-hop 头部,这些由 HTTP 库自动处理
  92. if _, isHopByHop := hopByHopHeaders[lower]; isHopByHop {
  93. continue
  94. }
  95. for _, value := range values {
  96. filtered.Add(key, value)
  97. }
  98. }
  99. return filtered
  100. }
  101. func WriteFilteredHeaders(dst http.Header, src http.Header, filter *CompiledHeaderFilter) {
  102. filtered := FilterHeaders(src, filter)
  103. for key, values := range filtered {
  104. for _, value := range values {
  105. dst.Add(key, value)
  106. }
  107. }
  108. }