forked from zhuhaow/ProxyClient
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttpproxy.go
324 lines (279 loc) · 8.08 KB
/
httpproxy.go
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
package proxyclient
import (
"bufio"
"crypto/rand"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io"
"math/big"
srand "math/rand"
"net"
"net/http"
"strings"
"sync"
"time"
)
type httpTCPConn struct {
Conn //http 协议时是原始链接、https协议时是tls.Conn
rawConn TCPConn //原始链接
tlsConn *tls.Conn //tls链接
localAddr, remoteAddr net.TCPAddr
localHost, remoteHost string
LocalPort, remotePort uint16
proxyClient ProxyClient
r io.ReadCloser // http Body
}
type httpProxyClient struct {
proxyAddr string
proxyDomain string // 用于ssl证书验证
proxyType string // socks4 socks5
auth string
insecureSkipVerify bool
standardHeader bool
upProxy ProxyClient
query map[string][]string
}
// 创建代理客户端
// ProxyType http https
// ProxyAddr 127.0.0.1:5555
// proxyDomain ssl 验证域名,"" 则使用 proxyAddr 部分的域名
// insecureSkipVerify 使用https代理时是否忽略证书检查
// UpProxy
func newHTTPProxyClient(proxyType string, proxyAddr string, proxyDomain string, auth string, insecureSkipVerify bool, StandardHeader bool, upProxy ProxyClient, query map[string][]string) (ProxyClient, error) {
proxyType = strings.ToLower(strings.Trim(proxyType, " \r\n\t"))
if proxyType != "http" && proxyType != "https" {
return nil, errors.New("ProxyType 错误的格式,只支持http、https代理。")
}
if upProxy == nil {
nUpProxy, err := newDriectProxyClient("", false, 0, make(map[string][]string))
if err != nil {
return nil, fmt.Errorf("创建直连代理错误:%v", err)
}
upProxy = nUpProxy
}
if proxyDomain == "" {
host, _, err := net.SplitHostPort(proxyAddr)
if err != nil {
return nil, fmt.Errorf("proxyAddr 格式错误:%v", err)
}
proxyDomain = host
}
return &httpProxyClient{proxyAddr, proxyDomain, proxyType, auth, insecureSkipVerify, StandardHeader, upProxy, query}, nil
}
func (p *httpProxyClient) Dial(network, address string) (net.Conn, error) {
if strings.HasPrefix(strings.ToLower(network), "tcp") {
return p.DialTCPSAddr(network, address)
} else if strings.HasPrefix(strings.ToLower(network), "udp") {
return nil, errors.New("不支持UDP协议。")
} else {
return nil, errors.New("未知的 network 类型。")
}
}
func (p *httpProxyClient) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
switch network {
case "tcp", "tcp4", "tcp6":
return p.DialTCPSAddrTimeout(network, address, timeout)
default:
return nil, fmt.Errorf("不支持的协议")
}
}
func (p *httpProxyClient) DialTCP(network string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
if laddr != nil || laddr.Port != 0 {
return nil, errors.New("代理协议不支持指定本地地址。")
}
return p.DialTCPSAddr(network, raddr.String())
}
func (p *httpProxyClient) DialTCPSAddr(network string, raddr string) (ProxyTCPConn, error) {
return p.DialTCPSAddrTimeout(network, raddr, 0)
}
func (p *httpProxyClient) DialTCPSAddrTimeout(network string, raddr string, timeout time.Duration) (ProxyTCPConn, error) {
var rconn ProxyTCPConn
var rerr error
rMutex := sync.Mutex{}
// 截止时间
finalDeadline := time.Time{}
if timeout != 0 {
finalDeadline = time.Now().Add(timeout)
}
var tlsConn *tls.Conn
rawConn, err := p.upProxy.DialTCPSAddrTimeout(network, p.proxyAddr, timeout)
if err != nil {
return nil, fmt.Errorf("无法连接代理服务器 %v ,错误:%v", p.proxyAddr, err)
}
c := Conn(rawConn)
ch := make(chan int)
// 实际执行部分
run := func() {
closed := false
// 当连接不被使用时,ch<-1会引发异常,这时将关闭连接。
defer func() {
e := recover()
if e != nil && closed == false {
c.Close()
}
}()
if p.proxyType == "https" {
tlsConn = tls.Client(c, &tls.Config{ServerName: p.proxyDomain, InsecureSkipVerify: p.insecureSkipVerify})
if err := tlsConn.Handshake(); err != nil {
closed = true
c.Close()
rMutex.Lock()
rerr = fmt.Errorf("TLS 协议握手错误:%v", err)
rMutex.Unlock()
ch <- 0
return
}
if p.insecureSkipVerify == false && tlsConn.VerifyHostname(p.proxyDomain) != nil {
closed = true
tlsConn.Close()
rMutex.Lock()
rerr = fmt.Errorf("TLS 协议域名验证失败:%v", err)
rMutex.Unlock()
ch <- 0
return
}
c = tlsConn
}
req, err := http.NewRequest("CONNECT", "", nil)
if err != nil {
closed = true
c.Close()
rMutex.Lock()
rerr = fmt.Errorf("创建请求错误:%v", err)
rMutex.Unlock()
ch <- 0
return
}
//req.URL.Path = raddr
req.Host = raddr
//req.URL.RawPath = raddr
if p.standardHeader {
xpath := "/"
rInt, err := rand.Int(rand.Reader, big.NewInt(20))
var rInt64 int64
if err != nil {
rInt64 = srand.Int63n(20)
} else {
rInt64 = rInt.Int64()
}
for i := int64(-10); i < rInt64; i++ {
xpath += "X"
}
req.Header.Add("Ac0ept", "text/html, application/xhtml+xml, image/jxr, */*")
req.Header.Add("Acc000-Encoding", "gzip, deflate")
req.Header.Add("Ac000t-Language", "zh-CN")
req.Header.Add("XXnnection", "000p-0000")
req.Header.Add("Us0000gent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/000.00 (KHTML, like Gecko) Chrome/00.0.0000.0 Safari/000.00 Edge/00.00000")
req.Header.Add("Cookie+path", xpath)
}
if p.auth != "" {
auth := base64.StdEncoding.EncodeToString([]byte(p.auth))
auth = fmt.Sprintf("Basic %v", auth)
req.Header.Add("Proxy-Authorization", auth)
}
if err := req.Write(c); err != nil {
closed = true
c.Close()
rMutex.Lock()
rerr = fmt.Errorf("写请求错误:%v", err)
rMutex.Unlock()
ch <- 0
return
}
br := bufio.NewReader(c)
res, err := http.ReadResponse(br, req)
if err != nil {
closed = true
c.Close()
rMutex.Lock()
rerr = fmt.Errorf("响应格式错误:%v", err)
rMutex.Unlock()
ch <- 0
return
}
if res.StatusCode != 200 {
closed = true
c.Close()
rMutex.Lock()
rerr = fmt.Errorf("响应错误:%v", res)
rMutex.Unlock()
ch <- 0
return
}
rMutex.Lock()
rconn = &httpTCPConn{c, rawConn, tlsConn, net.TCPAddr{}, net.TCPAddr{}, "", "", 0, 0, p, res.Body}
rMutex.Unlock()
ch <- 1
return
}
if timeout == 0 {
go run()
select {
case <-ch:
rMutex.Lock()
defer rMutex.Unlock()
return rconn, rerr
}
} else {
c.SetDeadline(finalDeadline)
ntimeout := finalDeadline.Sub(time.Now())
if ntimeout <= 0 {
return nil, fmt.Errorf("timeout")
}
t := time.NewTimer(ntimeout)
defer t.Stop()
go run()
select {
case <-t.C:
return nil, fmt.Errorf("连接超时。")
case <-ch:
rMutex.Lock()
defer rMutex.Unlock()
if rerr == nil {
c.SetDeadline(time.Time{})
}
return rconn, rerr
}
}
}
// 重写了 Read 接口
// 由于 http 协议问题,解析响应需要读缓冲,所以必须重写 Read 来兼容读缓冲功能。
func (c *httpTCPConn) Read(b []byte) (n int, err error) {
return c.r.Read(b)
}
// 重写了 Read 接口
// 由于 http 协议问题,解析响应需要读缓冲,所以必须重写 Read 来兼容读缓冲功能。
func (c *httpTCPConn) Close() error {
c.r.Close()
return c.Conn.Close()
}
func (c *httpTCPConn) SetLinger(sec int) error {
return c.rawConn.SetLinger(sec)
}
func (c *httpTCPConn) SetNoDelay(noDelay bool) error {
return c.rawConn.SetNoDelay(noDelay)
}
func (c *httpTCPConn) SetReadBuffer(bytes int) error {
return c.rawConn.SetReadBuffer(bytes)
}
func (c *httpTCPConn) SetWriteBuffer(bytes int) error {
return c.rawConn.SetWriteBuffer(bytes)
}
func (p *httpProxyClient) DialUDP(network string, laddr, raddr *net.UDPAddr) (net.Conn, error) {
return nil, fmt.Errorf("%v 代理不支持 UDP 转发。", p.proxyType)
}
func (p *httpProxyClient) UpProxy() ProxyClient {
return p.upProxy
}
func (p *httpProxyClient) SetUpProxy(upProxy ProxyClient) error {
p.upProxy = upProxy
return nil
}
func (c *httpTCPConn) ProxyClient() ProxyClient {
return c.proxyClient
}
func (p *httpProxyClient) GetProxyAddrQuery() map[string][]string {
return p.query
}