http_client.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // utils/http_client.go
  2. package utils
  3. import (
  4. "bytes"
  5. "context"
  6. "crypto/tls"
  7. "encoding/json"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "net/url"
  12. "strings"
  13. "time"
  14. )
  15. // 扩展 HTTPClient 接口
  16. type HTTPClient interface {
  17. RequestJSON(ctx context.Context, method string, reqURL string, body interface{}) ([]byte, error)
  18. RequestWithProxy(ctx context.Context, method string, reqURL string, body interface{}, proxyURL string) ([]byte, error)
  19. }
  20. // 修改 httpClient 结构体
  21. type httpClient struct {
  22. client *http.Client
  23. defaultProxy string // 新增默认代理配置
  24. skipSSLVerify bool
  25. }
  26. func NewHttpClient(timeout time.Duration, isSkipSSLVerify bool, defaultProxy string) HTTPClient {
  27. baseTransport := &http.Transport{
  28. TLSClientConfig: &tls.Config{
  29. InsecureSkipVerify: isSkipSSLVerify,
  30. },
  31. MaxIdleConns: 100,
  32. IdleConnTimeout: 90 * time.Second,
  33. DisableCompression: true,
  34. }
  35. return &httpClient{
  36. client: &http.Client{
  37. Timeout: timeout,
  38. Transport: baseTransport,
  39. },
  40. defaultProxy: defaultProxy,
  41. skipSSLVerify: isSkipSSLVerify,
  42. }
  43. }
  44. // 改进后的带代理请求方法
  45. func (c *httpClient) RequestWithProxy(ctx context.Context, method string, reqURL string, body interface{}, proxyURL string) ([]byte, error) {
  46. // 自动补充协议前缀(支持多种代理协议)
  47. if !strings.Contains(proxyURL, "://") {
  48. proxyURL = "http://" + proxyURL
  49. fmt.Printf("自动补充HTTP协议前缀: %s\n", proxyURL)
  50. }
  51. // 校验并解析代理URL
  52. parsedProxy, err := url.Parse(proxyURL)
  53. if err != nil {
  54. return nil, fmt.Errorf("代理地址格式错误(需要包含协议,示例:socks5://127.0.0.1:1080 或 http://proxy.com): %w", err)
  55. }
  56. // 配置代理传输
  57. proxyTransport := c.client.Transport.(*http.Transport).Clone()
  58. proxyTransport.Proxy = http.ProxyURL(parsedProxy)
  59. // 添加Transport配置验证
  60. if proxyTransport.Proxy == nil {
  61. return nil, fmt.Errorf("代理配置未正确应用到Transport")
  62. }
  63. // 创建带缓存的客户端
  64. proxyClient := &http.Client{
  65. Transport: proxyTransport,
  66. Timeout: c.client.Timeout,
  67. }
  68. // 执行请求并处理错误
  69. respData, err := c.doRequest(ctx, proxyClient, method, reqURL, body)
  70. if err != nil {
  71. return nil, fmt.Errorf("代理请求失败(%s): %w", proxyURL, err)
  72. }
  73. return respData, nil
  74. }
  75. // 修改方法签名
  76. func (c *httpClient) RequestJSON(ctx context.Context, method string, reqURL string, body interface{}) ([]byte, error) {
  77. return c.doRequest(ctx, c.client, method, reqURL, body)
  78. }
  79. // 重命名并修改doPostRequest
  80. func (c *httpClient) doRequest(ctx context.Context, client *http.Client, method string, reqURL string, body interface{}) ([]byte, error) {
  81. var reqBody []byte
  82. if body != nil && method != "GET" {
  83. var err error
  84. if byteBody, ok := body.([]byte); ok {
  85. reqBody = byteBody
  86. } else {
  87. if reqBody, err = json.Marshal(body); err != nil {
  88. return nil, fmt.Errorf("JSON序列化失败: %w", err)
  89. }
  90. }
  91. }
  92. if method == "GET" && body != nil {
  93. if params, ok := body.(map[string]string); ok {
  94. query := url.Values{}
  95. for k, v := range params {
  96. query.Add(k, v)
  97. }
  98. reqURL = fmt.Sprintf("%s?%s", reqURL, query.Encode())
  99. }
  100. }
  101. req, err := http.NewRequestWithContext(ctx, method, reqURL, bytes.NewReader(reqBody))
  102. if err != nil {
  103. return nil, fmt.Errorf("创建请求失败: %w", err)
  104. }
  105. req.Header.Set("Content-Type", "application/json; charset=utf-8")
  106. req.Header.Set("Accept", "application/json")
  107. // 原有脱敏日志
  108. // Logger.Printf("[HTTP请求] %s %s\nHeaders: %+v\nBody: %.100s",
  109. // method,
  110. // strings.ReplaceAll(reqURL, "access_token=[^\"]*", "access_token=***"),
  111. // req.Header,
  112. // string(reqBody))
  113. resp, err := client.Do(req)
  114. if resp != nil {
  115. // 记录响应日志(脱敏处理)
  116. bodyBytes, _ := io.ReadAll(resp.Body)
  117. resp.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
  118. // Logger.Printf("[HTTP响应] Status: %d\nHeaders: %+v\nBody: %.200s",
  119. // resp.StatusCode,
  120. // resp.Header,
  121. // string(bodyBytes))
  122. }
  123. if err != nil {
  124. return nil, fmt.Errorf("网络请求失败: %w", err)
  125. }
  126. defer resp.Body.Close()
  127. if resp.StatusCode < 200 || resp.StatusCode >= 300 {
  128. Logger.WithField("status", resp.StatusCode).Warn("非成功状态码")
  129. return nil, fmt.Errorf("非成功状态码: %d", resp.StatusCode)
  130. }
  131. return io.ReadAll(resp.Body)
  132. }