-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.go
More file actions
166 lines (139 loc) · 4.88 KB
/
Copy pathclient.go
File metadata and controls
166 lines (139 loc) · 4.88 KB
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package proxy
import (
"crypto/tls"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
// createClient creates an HTTP client for the given proxy type
func (c *Checker) createClient(proxyURL *url.URL, scheme string, result *ProxyResult) (*http.Client, error) {
if c.debug {
result.DebugInfo += fmt.Sprintf("[DEBUG] Creating client for scheme: %s\n", scheme)
result.DebugInfo += fmt.Sprintf("[DEBUG] Proxy URL: %s\n", c.cleanProxyURL(proxyURL))
}
// Extract authentication information
auth := c.getProxyAuth(proxyURL, result)
// Try to use connection pool if available
if c.config.ConnectionPool != nil {
if pool, ok := c.config.ConnectionPool.(interface {
GetClient(string, time.Duration) (*http.Client, error)
}); ok {
fullProxyURL := fmt.Sprintf("%s://%s", scheme, proxyURL.Host)
client, err := pool.GetClient(fullProxyURL, c.config.Timeout)
if err == nil {
if c.debug {
result.DebugInfo += fmt.Sprintf("[DEBUG] Using connection pool client for: %s\n", fullProxyURL)
}
return client, nil
}
if c.debug {
result.DebugInfo += fmt.Sprintf("[DEBUG] Connection pool failed, falling back to manual client creation: %v\n", err)
}
}
}
// Fallback to manual client creation with authentication support
var transport *http.Transport
switch {
case scheme == "http" || scheme == "https":
transport = c.createAuthenticatedHTTPTransport(proxyURL, scheme, auth, result)
case scheme == "socks4" || scheme == "socks5":
transport = &http.Transport{
TLSHandshakeTimeout: c.config.Timeout / 2,
ResponseHeaderTimeout: c.config.Timeout / 2,
ExpectContinueTimeout: 1 * time.Second,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
DisableKeepAlives: true,
ForceAttemptHTTP2: false,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
transport.DialContext = c.createAuthenticatedSOCKSDialer(proxyURL, scheme, auth, result)
}
// Set TLS config if not already set
if transport.TLSClientConfig == nil {
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
// Add warning about disabled TLS verification
if transport.TLSClientConfig != nil && transport.TLSClientConfig.InsecureSkipVerify {
result.SecurityWarnings = append(result.SecurityWarnings,
"TLS certificate verification disabled - proxy could perform MITM attacks")
}
client := &http.Client{
Transport: transport,
Timeout: c.config.Timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
if c.debug {
result.DebugInfo += fmt.Sprintf("[DEBUG] Created client with timeout: %v\n", client.Timeout)
}
return client, nil
}
// testClientWithDetails tests if the client works with a simple request and returns detailed information
func (c *Checker) testClientWithDetails(client *http.Client, proxyType ProxyType, result *ProxyResult) (bool, string, *CheckResult) {
// Use different validation URLs based on proxy type
testURL := c.config.ValidationURL
if proxyType == ProxyTypeSOCKS4 || proxyType == ProxyTypeSOCKS5 {
// For SOCKS proxies, try a plain HTTP URL first
testURL = "http://api.ipify.org?format=json"
}
checkResult := &CheckResult{
URL: testURL,
}
start := time.Now()
// Apply rate limiting
c.applyRateLimit(testURL, result)
req, err := http.NewRequest("GET", testURL, nil)
if err != nil {
checkResult.Error = err.Error()
return false, err.Error(), checkResult
}
// Add default headers
for key, value := range c.config.DefaultHeaders {
req.Header.Set(key, value)
}
if c.config.UserAgent != "" {
req.Header.Set("User-Agent", c.config.UserAgent)
}
resp, err := client.Do(req)
if err != nil {
checkResult.Error = err.Error()
checkResult.Speed = time.Since(start)
return false, err.Error(), checkResult
}
defer resp.Body.Close()
checkResult.StatusCode = resp.StatusCode
checkResult.Speed = time.Since(start)
// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
checkResult.Error = err.Error()
return false, err.Error(), checkResult
}
checkResult.BodySize = int64(len(body))
// Check if response is valid
if !c.validateResponse(resp, body) {
checkResult.Error = "response validation failed"
return false, "response validation failed", checkResult
}
checkResult.Success = true
return true, "", checkResult
}
// testClientWithError tests if the client works with a simple request and returns an error message
func (c *Checker) testClientWithError(client *http.Client, proxyType ProxyType, result *ProxyResult) (bool, string) {
success, errorMsg, _ := c.testClientWithDetails(client, proxyType, result)
return success, errorMsg
}
// testClient tests if the client works with a simple request
func (c *Checker) testClient(client *http.Client, proxyType ProxyType, result *ProxyResult) bool {
success, _, _ := c.testClientWithDetails(client, proxyType, result)
return success
}