|
|
package tools
|
|
|
|
|
|
import (
|
|
|
"crypto/tls"
|
|
|
"fmt"
|
|
|
C "github.com/metacubex/mihomo/constant"
|
|
|
"golang.org/x/net/context"
|
|
|
"io"
|
|
|
"log"
|
|
|
"net"
|
|
|
"net/http"
|
|
|
"net/url"
|
|
|
"sync"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
var dialerBaidu = &net.Dialer{
|
|
|
Resolver: &net.Resolver{
|
|
|
PreferGo: true,
|
|
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
|
d := net.Dialer{
|
|
|
Timeout: time.Duration(5000) * time.Millisecond,
|
|
|
}
|
|
|
return d.DialContext(ctx, "udp", "180.76.76.76:53")
|
|
|
},
|
|
|
},
|
|
|
}
|
|
|
|
|
|
var dialBaiduContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
return dialerBaidu.DialContext(ctx, network, addr)
|
|
|
}
|
|
|
|
|
|
// HttpGetByProxy 使用代理访问指定的URL并返回响应数据
|
|
|
func HttpGetByProxy(requestUrl, httpProxyUrl string) ([]byte, error) {
|
|
|
// 拼接代理地址
|
|
|
uri, _ := url.Parse(httpProxyUrl)
|
|
|
|
|
|
// 创建一个带代理的HTTP客户端
|
|
|
client := http.Client{
|
|
|
Transport: &http.Transport{
|
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
|
Proxy: http.ProxyURL(uri),
|
|
|
},
|
|
|
Timeout: 20 * time.Second,
|
|
|
}
|
|
|
|
|
|
// 创建一个GET请求
|
|
|
req, err := http.NewRequest(http.MethodGet, requestUrl, nil)
|
|
|
if err != nil {
|
|
|
log.Printf("HttpGetByProxy http.NewRequest %s %v\n", requestUrl, err)
|
|
|
return nil, err
|
|
|
}
|
|
|
req.Header.Set("Accept-Encoding", "utf-8")
|
|
|
req.Header.Set("Accept", "*/*")
|
|
|
req.Header.Set("User-Agent", C.UA)
|
|
|
|
|
|
// 发送请求并获取响应
|
|
|
resp, err := client.Do(req)
|
|
|
if err != nil {
|
|
|
log.Printf("HttpGetByProxy client.Do %s %v\n", requestUrl, err)
|
|
|
return nil, err
|
|
|
}
|
|
|
defer func(Body io.ReadCloser) {
|
|
|
err := Body.Close()
|
|
|
if err != nil {
|
|
|
// 处理异常情况
|
|
|
}
|
|
|
}(resp.Body)
|
|
|
// 读取响应数据
|
|
|
data, err := io.ReadAll(resp.Body)
|
|
|
if err != nil {
|
|
|
log.Printf("HttpGetByProxy io.ReadAll %s %v\n", requestUrl, err)
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
log.Printf("HttpGetByProxy StatusCode %s %d\n", requestUrl, resp.StatusCode)
|
|
|
return nil, fmt.Errorf("StatusCode %d", resp.StatusCode)
|
|
|
}
|
|
|
|
|
|
return data, nil
|
|
|
}
|
|
|
|
|
|
// HttpGet 使用HTTP GET方法请求指定的URL,并返回响应的数据和可能的错误。
|
|
|
func HttpGet(requestUrl string) ([]byte, error) {
|
|
|
timeOut := 15 * time.Second
|
|
|
return HttpGetWithTimeout(requestUrl, timeOut, true)
|
|
|
}
|
|
|
|
|
|
// HttpGetWithTimeout 使用HTTP GET方法请求指定的URL,并返回响应的数据和可能的错误。
|
|
|
func HttpGetWithTimeout(requestUrl string, outTime time.Duration, needDail bool) ([]byte, error) {
|
|
|
client := http.Client{
|
|
|
Timeout: outTime, // 请求超时时间
|
|
|
}
|
|
|
|
|
|
if needDail {
|
|
|
client.Transport = &http.Transport{
|
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 安全证书验证关闭
|
|
|
DialContext: dialBaiduContext,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
req, err := http.NewRequest(http.MethodGet, requestUrl, nil) // 创建一个新的GET请求
|
|
|
if err != nil {
|
|
|
log.Printf("HttpGetWithTimeout http.NewRequest %s %v\n", requestUrl, err)
|
|
|
return nil, err
|
|
|
}
|
|
|
req.Header.Set("Accept-Encoding", "utf-8") // 设置响应内容编码为utf-8
|
|
|
req.Header.Set("Accept", "*/*") // 设置响应内容类型为全部
|
|
|
req.Header.Set("User-Agent", C.UA) // 设置用户代理为C.UA
|
|
|
|
|
|
resp, err := client.Do(req) // 发送请求并获取响应
|
|
|
if err != nil {
|
|
|
log.Printf("HttpGetWithTimeout client.Do %s %v\n", requestUrl, err)
|
|
|
return nil, err
|
|
|
}
|
|
|
defer func(Body io.ReadCloser) {
|
|
|
err := Body.Close()
|
|
|
if err != nil {
|
|
|
// 处理关闭响应体的错误
|
|
|
}
|
|
|
}(resp.Body)
|
|
|
data, err := io.ReadAll(resp.Body) // 读取响应体的数据
|
|
|
if err != nil {
|
|
|
log.Printf("HttpGetWithTimeout io.ReadAll %s %v\n", requestUrl, err)
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
log.Printf("HttpGetWithTimeout StatusCode %s %d\n", requestUrl, resp.StatusCode)
|
|
|
return nil, fmt.Errorf("StatusCode %d", resp.StatusCode)
|
|
|
}
|
|
|
|
|
|
return data, nil // 返回响应数据和无错误
|
|
|
}
|
|
|
|
|
|
// ConcurrentHttpGet 并发获取指定URL的HTTP内容
|
|
|
func ConcurrentHttpGet(url string) (all []byte) {
|
|
|
// 开启多线程请求
|
|
|
cLock := sync.Mutex{}
|
|
|
done := make(chan bool, 1)
|
|
|
length := 128
|
|
|
wg := sync.WaitGroup{}
|
|
|
go func() {
|
|
|
wg.Add(1)
|
|
|
defer wg.Done()
|
|
|
|
|
|
content, err := HttpGetByProxy(url, "")
|
|
|
if err == nil {
|
|
|
cLock.Lock()
|
|
|
if all == nil && len(content) > length {
|
|
|
all = content
|
|
|
}
|
|
|
cLock.Unlock()
|
|
|
done <- true
|
|
|
}
|
|
|
}()
|
|
|
go func() {
|
|
|
wg.Add(1)
|
|
|
defer wg.Done()
|
|
|
|
|
|
content, err := HttpGet(url)
|
|
|
if err == nil {
|
|
|
cLock.Lock()
|
|
|
if all == nil && len(content) > length {
|
|
|
all = content
|
|
|
}
|
|
|
cLock.Unlock()
|
|
|
done <- true
|
|
|
}
|
|
|
}()
|
|
|
|
|
|
go func() {
|
|
|
time.Sleep(5 * time.Second)
|
|
|
wg.Wait()
|
|
|
done <- true
|
|
|
}()
|
|
|
|
|
|
select {
|
|
|
case <-done:
|
|
|
case <-time.After(20 * time.Second):
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|