upstream_response_limit.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package service
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "github.com/Wei-Shaw/sub2api/internal/config"
  8. "github.com/gin-gonic/gin"
  9. )
  10. var ErrUpstreamResponseBodyTooLarge = errors.New("upstream response body too large")
  11. // defaultUpstreamResponseReadMaxBytes 源自 config.DefaultUpstreamResponseReadMaxBytes,
  12. // 仅在 cfg 为 nil 时作为兜底(测试或极端场景)。
  13. const defaultUpstreamResponseReadMaxBytes = config.DefaultUpstreamResponseReadMaxBytes
  14. func resolveUpstreamResponseReadLimit(cfg *config.Config) int64 {
  15. if cfg != nil && cfg.Gateway.UpstreamResponseReadMaxBytes > 0 {
  16. return cfg.Gateway.UpstreamResponseReadMaxBytes
  17. }
  18. return defaultUpstreamResponseReadMaxBytes
  19. }
  20. func readUpstreamResponseBodyLimited(reader io.Reader, maxBytes int64) ([]byte, error) {
  21. if reader == nil {
  22. return nil, errors.New("response body is nil")
  23. }
  24. if maxBytes <= 0 {
  25. maxBytes = defaultUpstreamResponseReadMaxBytes
  26. }
  27. body, err := io.ReadAll(io.LimitReader(reader, maxBytes+1))
  28. if err != nil {
  29. return nil, err
  30. }
  31. if int64(len(body)) > maxBytes {
  32. return nil, fmt.Errorf("%w: limit=%d", ErrUpstreamResponseBodyTooLarge, maxBytes)
  33. }
  34. return body, nil
  35. }
  36. // TooLargeWriter 在响应超限时向客户端写格式化的错误响应。
  37. type TooLargeWriter func(c *gin.Context)
  38. // ReadUpstreamResponseBody 读取上游非流式响应体。
  39. // 超限时自动记录 ops error 并调用 onTooLarge 向客户端写错误。
  40. func ReadUpstreamResponseBody(reader io.Reader, cfg *config.Config, c *gin.Context, onTooLarge TooLargeWriter) ([]byte, error) {
  41. maxBytes := resolveUpstreamResponseReadLimit(cfg)
  42. body, err := readUpstreamResponseBodyLimited(reader, maxBytes)
  43. if err != nil {
  44. if errors.Is(err, ErrUpstreamResponseBodyTooLarge) {
  45. setOpsUpstreamError(c, http.StatusBadGateway, "upstream response too large", "")
  46. if onTooLarge != nil {
  47. onTooLarge(c)
  48. }
  49. }
  50. return nil, err
  51. }
  52. return body, nil
  53. }
  54. // anthropicTooLargeError 以 Anthropic Messages API 格式写入超限错误。
  55. func anthropicTooLargeError(c *gin.Context) {
  56. c.JSON(http.StatusBadGateway, gin.H{
  57. "type": "error",
  58. "error": gin.H{
  59. "type": "upstream_error",
  60. "message": "Upstream response too large",
  61. },
  62. })
  63. }
  64. // openAITooLargeError 以 OpenAI / Gemini 格式写入超限错误。
  65. func openAITooLargeError(c *gin.Context) {
  66. c.JSON(http.StatusBadGateway, gin.H{
  67. "error": gin.H{
  68. "type": "upstream_error",
  69. "message": "Upstream response too large",
  70. },
  71. })
  72. }