new-api/service/http_client.go

116 lines
2.6 KiB
Go
Raw Normal View History

2024-02-29 01:08:18 +08:00
package service
import (
2025-02-02 22:15:06 +08:00
"context"
"fmt"
"net"
2024-02-29 01:08:18 +08:00
"net/http"
2025-02-02 22:15:06 +08:00
"net/url"
2024-02-29 01:08:18 +08:00
"one-api/common"
"sync"
2024-02-29 01:08:18 +08:00
"time"
2025-05-11 17:00:33 +08:00
"golang.org/x/net/proxy"
2024-02-29 01:08:18 +08:00
)
var (
httpClient *http.Client
proxyClientLock sync.Mutex
proxyClients = make(map[string]*http.Client)
)
2024-02-29 01:08:18 +08:00
func InitHttpClient() {
2024-02-29 01:08:18 +08:00
if common.RelayTimeout == 0 {
httpClient = &http.Client{}
} else {
httpClient = &http.Client{
Timeout: time.Duration(common.RelayTimeout) * time.Second,
}
}
}
func GetHttpClient() *http.Client {
return httpClient
}
// ResetProxyClientCache 清空代理客户端缓存,确保下次使用时重新初始化
func ResetProxyClientCache() {
proxyClientLock.Lock()
defer proxyClientLock.Unlock()
for _, client := range proxyClients {
if transport, ok := client.Transport.(*http.Transport); ok && transport != nil {
transport.CloseIdleConnections()
}
}
proxyClients = make(map[string]*http.Client)
}
2025-02-02 22:15:06 +08:00
// NewProxyHttpClient 创建支持代理的 HTTP 客户端
func NewProxyHttpClient(proxyURL string) (*http.Client, error) {
if proxyURL == "" {
return http.DefaultClient, nil
}
proxyClientLock.Lock()
if client, ok := proxyClients[proxyURL]; ok {
proxyClientLock.Unlock()
return client, nil
}
proxyClientLock.Unlock()
2025-02-02 22:15:06 +08:00
parsedURL, err := url.Parse(proxyURL)
if err != nil {
return nil, err
}
switch parsedURL.Scheme {
case "http", "https":
client := &http.Client{
2025-02-02 22:15:06 +08:00
Transport: &http.Transport{
Proxy: http.ProxyURL(parsedURL),
},
}
2025-09-27 22:29:27 +08:00
client.Timeout = time.Duration(common.RelayTimeout) * time.Second
proxyClientLock.Lock()
proxyClients[proxyURL] = client
proxyClientLock.Unlock()
return client, nil
2025-02-02 22:15:06 +08:00
2025-05-11 17:00:33 +08:00
case "socks5", "socks5h":
// 获取认证信息
var auth *proxy.Auth
if parsedURL.User != nil {
auth = &proxy.Auth{
User: parsedURL.User.Username(),
Password: "",
}
if password, ok := parsedURL.User.Password(); ok {
auth.Password = password
}
}
2025-02-02 22:15:06 +08:00
// 创建 SOCKS5 代理拨号器
2025-05-11 17:00:33 +08:00
// proxy.SOCKS5 使用 tcp 参数,所有 TCP 连接包括 DNS 查询都将通过代理进行。行为与 socks5h 相同
dialer, err := proxy.SOCKS5("tcp", parsedURL.Host, auth, proxy.Direct)
2025-02-02 22:15:06 +08:00
if err != nil {
return nil, err
}
client := &http.Client{
2025-02-02 22:15:06 +08:00
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer.Dial(network, addr)
},
},
}
2025-09-27 22:29:27 +08:00
client.Timeout = time.Duration(common.RelayTimeout) * time.Second
proxyClientLock.Lock()
proxyClients[proxyURL] = client
proxyClientLock.Unlock()
return client, nil
2025-02-02 22:15:06 +08:00
default:
return nil, fmt.Errorf("unsupported proxy scheme: %s, must be http, https, socks5 or socks5h", parsedURL.Scheme)
2025-02-02 22:15:06 +08:00
}
}