File size: 3,616 Bytes
1de7911
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package service

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"time"

	"zencoder-2api/internal/service/provider"
)

// ProxyRequestOptions 代理请求选项
type ProxyRequestOptions struct {
	UseProxy         bool          // 是否使用代理
	MaxRetries       int           // 最大重试次数
	RetryDelay       time.Duration // 重试延迟
	OnError          func(error) bool // 错误判断函数,返回true表示需要重试
}

// DefaultProxyRequestOptions 默认代理请求选项
func DefaultProxyRequestOptions() ProxyRequestOptions {
	return ProxyRequestOptions{
		UseProxy:   true,
		MaxRetries: 3,
		RetryDelay: time.Second,
		OnError:    isNetworkError,
	}
}

// isNetworkError 判断是否为网络错误(可重试的错误)
func isNetworkError(err error) bool {
	if err == nil {
		return false
	}
	
	errStr := err.Error()
	
	// 网络连接错误
	if strings.Contains(errStr, "connection refused") ||
		strings.Contains(errStr, "connection reset") ||
		strings.Contains(errStr, "connection timed out") ||
		strings.Contains(errStr, "timeout") ||
		strings.Contains(errStr, "network is unreachable") ||
		strings.Contains(errStr, "no such host") ||
		strings.Contains(errStr, "dial tcp") ||
		strings.Contains(errStr, "i/o timeout") ||
		strings.Contains(errStr, "EOF") {
		return true
	}
	
	// SOCKS代理相关错误
	if strings.Contains(errStr, "socks connect") ||
		strings.Contains(errStr, "proxy") {
		return true
	}
	
	return false
}

// DoRequestWithProxyRetry 执行带代理重试的HTTP请求
func DoRequestWithProxyRetry(ctx context.Context, req *http.Request, originalProxy string, options ProxyRequestOptions) (*http.Response, error) {
	// 首先尝试使用原始代理(如果有的话)
	client := provider.NewHTTPClient(originalProxy, 0)
	
	resp, err := client.Do(req)
	if err == nil {
		// 请求成功,返回结果
		return resp, nil
	}
	
	// 检查错误是否可重试
	if !options.OnError(err) {
		return nil, err
	}
	
	// 如果不使用代理池,直接返回错误
	if !options.UseProxy {
		return nil, err
	}
	
	proxyPool := provider.GetProxyPool()
	if !proxyPool.HasProxies() {
		return nil, fmt.Errorf("原始请求失败且无可用代理: %v", err)
	}
	
	var lastErr error = err
	
	// 使用代理池进行重试
	for i := 0; i < options.MaxRetries; i++ {
		// 获取下一个代理
		proxyURL := proxyPool.GetNextProxy()
		if proxyURL == "" {
			break
		}
		
		// 创建使用代理的HTTP客户端
		proxyClient, clientErr := provider.NewHTTPClientWithProxy(proxyURL, 0)
		if clientErr != nil {
			lastErr = clientErr
			time.Sleep(options.RetryDelay)
			continue
		}
		
		// 克隆请求(因为request body可能已经被消费)
		newReq := req.Clone(ctx)
		
		resp, err := proxyClient.Do(newReq)
		if err == nil {
			// 请求成功
			return resp, nil
		}
		
		// 检查错误是否继续重试
		if !options.OnError(err) {
			return nil, err
		}
		
		lastErr = err
		time.Sleep(options.RetryDelay)
	}
	
	return nil, fmt.Errorf("所有代理重试均失败,最后错误: %v", lastErr)
}

// CreateHTTPClientWithFallback 创建支持代理fallback的HTTP客户端
func CreateHTTPClientWithFallback(originalProxy string, useProxyPool bool) *http.Client {
	// 如果不使用代理池,使用原始逻辑
	if !useProxyPool {
		return provider.NewHTTPClient(originalProxy, 0)
	}
	
	// 如果有原始代理,先尝试原始代理
	if originalProxy != "" {
		client, err := provider.NewHTTPClientWithProxy(originalProxy, 0)
		if err == nil {
			return client
		}
	}
	
	// 使用代理池
	return provider.NewHTTPClientWithPoolProxy(true, 0)
}