upstream_request_id.go 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package service
  2. import (
  3. "net/http"
  4. "strings"
  5. "unicode/utf8"
  6. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  7. "golang.org/x/net/http/httpguts"
  8. )
  9. // AccountExtraUpstreamRequestIDHeader 是账户 extra 中的键,值为直接上游声明请求标识的响应头名。
  10. // 未指定时不记录上游请求标识。
  11. const AccountExtraUpstreamRequestIDHeader = "upstream_request_id_header"
  12. const (
  13. maxUpstreamRequestIDHeaderNameLen = 64
  14. // maxUsageUpstreamRequestIDLen 与 usage_logs.upstream_request_id VARCHAR(128) 对齐。
  15. maxUsageUpstreamRequestIDLen = 128
  16. )
  17. // UpstreamRequestIDHeaderName 返回账户指定的上游请求标识头名,未指定时为空串。
  18. func UpstreamRequestIDHeaderName(account *Account) string {
  19. if account == nil {
  20. return ""
  21. }
  22. return strings.TrimSpace(account.GetExtraString(AccountExtraUpstreamRequestIDHeader))
  23. }
  24. // UpstreamRequestIDFromHeaders 从直接上游的响应头解析请求标识。
  25. // 只读账户指定的头;账户未指定头名时恒为空串。
  26. func UpstreamRequestIDFromHeaders(account *Account, h http.Header) string {
  27. if len(h) == 0 {
  28. return ""
  29. }
  30. name := UpstreamRequestIDHeaderName(account)
  31. if name == "" {
  32. return ""
  33. }
  34. return strings.TrimSpace(h.Get(name))
  35. }
  36. // usageUpstreamRequestIDPtr 生成落库到 usage_logs.upstream_request_id 的值。
  37. // WS 轮次没有 HTTP 响应头,保持 nil;超长时截断到列宽而不是让整条用量行失败。
  38. func usageUpstreamRequestIDPtr(account *Account, h http.Header, wsMode bool) *string {
  39. if wsMode {
  40. return nil
  41. }
  42. id := UpstreamRequestIDFromHeaders(account, h)
  43. if id == "" {
  44. return nil
  45. }
  46. if len(id) > maxUsageUpstreamRequestIDLen {
  47. id = id[:maxUsageUpstreamRequestIDLen]
  48. for len(id) > 0 && !utf8.ValidString(id) {
  49. id = id[:len(id)-1]
  50. }
  51. }
  52. if id == "" {
  53. return nil
  54. }
  55. return &id
  56. }
  57. // ValidateUpstreamRequestIDHeaderExtra 校验并规范化 extra 中的上游请求标识头名:
  58. // 必须是合法的 HTTP 头字段名且不超过 64 字节;空白值视为未指定并从 extra 中移除。
  59. func ValidateUpstreamRequestIDHeaderExtra(extra map[string]any) error {
  60. if extra == nil {
  61. return nil
  62. }
  63. raw, ok := extra[AccountExtraUpstreamRequestIDHeader]
  64. if !ok || raw == nil {
  65. return nil
  66. }
  67. name, ok := raw.(string)
  68. if !ok {
  69. return infraerrors.BadRequest("INVALID_UPSTREAM_REQUEST_ID_HEADER",
  70. "upstream_request_id_header must be a string")
  71. }
  72. name = strings.TrimSpace(name)
  73. if name == "" {
  74. delete(extra, AccountExtraUpstreamRequestIDHeader)
  75. return nil
  76. }
  77. if len(name) > maxUpstreamRequestIDHeaderNameLen || !httpguts.ValidHeaderFieldName(name) {
  78. return infraerrors.BadRequest("INVALID_UPSTREAM_REQUEST_ID_HEADER",
  79. "upstream_request_id_header must be a valid HTTP header name of at most 64 bytes")
  80. }
  81. extra[AccountExtraUpstreamRequestIDHeader] = name
  82. return nil
  83. }