Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97ae0044e7 | ||
|
|
b95582ae1a | ||
|
|
9aaa3e64d3 | ||
|
|
9e0f222125 | ||
|
|
21d30dee53 | ||
|
|
a061b8d369 | ||
|
|
68346717a5 | ||
|
|
2b7fbd2a0d | ||
|
|
4c5d288f03 |
2
.github/workflows/build-dev.yml
vendored
2
.github/workflows/build-dev.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
goarch: [amd64, arm64]
|
||||
env:
|
||||
OUTPUT_BINARY: ghproxy
|
||||
GO_VERSION: 1.23.6
|
||||
GO_VERSION: 1.24
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
goarch: [amd64, arm64]
|
||||
env:
|
||||
OUTPUT_BINARY: ghproxy
|
||||
GO_VERSION: 1.23.6
|
||||
GO_VERSION: 1.24
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,5 +1,23 @@
|
||||
# 更新日志
|
||||
|
||||
2.2.0
|
||||
---
|
||||
- RELEASE: v2.2.0正式版发布;
|
||||
- CHANGE: 更新Go版本至1.24.0
|
||||
- ADD: 加入`Socks5`和`HTTP(S)`出站支持
|
||||
- CHANGE: 配置新增`Outbound`配置块
|
||||
|
||||
25w13b
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v2.2.0的预发布版本,请勿在生产环境中使用;
|
||||
- CHANGE: 更新Go版本至1.24.0
|
||||
|
||||
25w13a
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v2.2.0的预发布版本,请勿在生产环境中使用;
|
||||
- ADD: 加入`Socks5`和`HTTP(S)`出站支持
|
||||
- CHANGE: 配置新增`Outbound`配置块
|
||||
|
||||
2.1.0
|
||||
---
|
||||
- RELEASE: v2.1.0正式版发布;
|
||||
|
||||
@@ -1 +1 @@
|
||||
25w12d
|
||||
25w13b
|
||||
22
README.md
22
README.md
@@ -71,6 +71,12 @@ docker run -p 7210:8080 -v ./ghproxy/log/run:/data/ghproxy/log -v ./ghproxy/log/
|
||||
wget -O install.sh https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/install.sh && chmod +x install.sh &&./install.sh
|
||||
```
|
||||
|
||||
Dev一键部署脚本:
|
||||
|
||||
```bash
|
||||
wget -O install-dev.sh https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/deploy/install-dev.sh && chmod +x install-dev.sh && ./install-dev.sh
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 外部配置文件
|
||||
@@ -115,6 +121,10 @@ enabled = false # 是否开启速率限制
|
||||
rateMethod = "total" # "ip" or "total" 速率限制方式
|
||||
ratePerMinute = 180 # 每分钟限制请求数量
|
||||
burst = 5 # 突发请求数量
|
||||
|
||||
[outbound]
|
||||
enabled = false # 是否使用自定义代理出站
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890" 支持Socks5/HTTP(S)出站传输
|
||||
```
|
||||
|
||||
### 黑名单配置
|
||||
@@ -160,3 +170,15 @@ example.com {
|
||||
|
||||

|
||||

|
||||
|
||||
## 赞助
|
||||
|
||||
如果您觉得本项目对您有帮助,欢迎赞助支持,您的赞助将用于Demo服务器开支及开发者时间成本支出,感谢您的支持!
|
||||
|
||||
为爱发电,开源不易
|
||||
|
||||
爱发电: https://afdian.com/a/wjqserver
|
||||
|
||||
### 捐赠列表
|
||||
|
||||
虚位以待...
|
||||
|
||||
@@ -13,6 +13,7 @@ type Config struct {
|
||||
Blacklist BlacklistConfig
|
||||
Whitelist WhitelistConfig
|
||||
RateLimit RateLimitConfig
|
||||
Outbound OutboundConfig
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
@@ -62,6 +63,16 @@ type RateLimitConfig struct {
|
||||
Burst int `toml:"burst"`
|
||||
}
|
||||
|
||||
/*
|
||||
[outbound]
|
||||
enabled = false
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
*/
|
||||
type OutboundConfig struct {
|
||||
Enabled bool `toml:"enabled"`
|
||||
Url string `toml:"url"`
|
||||
}
|
||||
|
||||
// LoadConfig 从 TOML 配置文件加载配置
|
||||
func LoadConfig(filePath string) (*Config, error) {
|
||||
var config Config
|
||||
|
||||
@@ -36,3 +36,7 @@ enabled = false
|
||||
rateMethod = "total" # "ip" or "total"
|
||||
ratePerMinute = 180
|
||||
burst = 5
|
||||
|
||||
[outbound]
|
||||
enabled = false
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
@@ -2,17 +2,17 @@
|
||||
host = "127.0.0.1"
|
||||
port = 8080
|
||||
sizeLimit = 125 # MB
|
||||
enableH2C = false
|
||||
enableH2C = "on"
|
||||
debug = false
|
||||
|
||||
[pages]
|
||||
enabled = false
|
||||
staticDir = "/usr/local/ghproxy/pages"
|
||||
level = "info"
|
||||
|
||||
[log]
|
||||
logFilePath = "/usr/local/ghproxy/log/ghproxy.log"
|
||||
maxLogSize = 5 # MB
|
||||
level = "info" # dump, debug, info, warn, error, none
|
||||
|
||||
[cors]
|
||||
enabled = true
|
||||
@@ -36,3 +36,7 @@ enabled = false
|
||||
rateMethod = "total" # "ip" or "total"
|
||||
ratePerMinute = 180
|
||||
burst = 5
|
||||
|
||||
[outbound]
|
||||
enabled = false
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
|
||||
@@ -73,8 +73,51 @@ if [ -z "$ghproxy_dir" ]; then
|
||||
ghproxy_dir="/usr/local/ghproxy"
|
||||
fi
|
||||
|
||||
make_systemd_service() {
|
||||
# 创建目录
|
||||
mkdir -p ${ghproxy_dir}
|
||||
mkdir -p ${ghproxy_dir}/config
|
||||
mkdir -p ${ghproxy_dir}/log
|
||||
mkdir -p ${ghproxy_dir}/pages
|
||||
|
||||
# 获取最新版本号
|
||||
VERSION=$(curl -s https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/DEV-VERSION)
|
||||
wget -q -O ${ghproxy_dir}/VERSION https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/DEV-VERSION
|
||||
|
||||
# 下载ghproxy
|
||||
wget -q -O ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/$VERSION/ghproxy-linux-$ARCH.tar.gz
|
||||
install tar
|
||||
tar -zxvf ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz -C ${ghproxy_dir}
|
||||
chmod +x ${ghproxy_dir}/ghproxy
|
||||
|
||||
# 下载pages
|
||||
wget -q -O ${ghproxy_dir}/pages/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/pages/index.html
|
||||
wget -q -O ${ghproxy_dir}/pages/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/pages/favicon.ico
|
||||
|
||||
|
||||
# 下载配置文件
|
||||
if [ -f ${ghproxy_dir}/config/config.toml ]; then
|
||||
echo "配置文件已存在, 跳过下载"
|
||||
echo "[WARNING] 请检查配置文件是否正确,DEV版本升级时请注意配置文件兼容性"
|
||||
sleep 2
|
||||
else
|
||||
wget -q -O ${ghproxy_dir}/config/config.toml https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/deploy/config.toml
|
||||
fi
|
||||
|
||||
# 替换 port = 8080
|
||||
sed -i "s/port = 8080/port = $PORT/g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i 's/host = "127.0.0.1"/host = "'"$IP"'"/g' ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|staticDir = \"/usr/local/ghproxy/pages\"|staticDir = \"${ghproxy_dir}/pages\"|g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|logFilePath = \"/usr/local/ghproxy/log/ghproxy.log\"|logFilePath = \"${ghproxy_dir}/log/ghproxy.log\"|g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|blacklistFile = \"/usr/local/ghproxy/config/blacklist.json\"|blacklistFile = \"${ghproxy_dir}/config/blacklist.json\"|g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|whitelistFile = \"/usr/local/ghproxy/config/whitelist.json\"|whitelistFile = \"${ghproxy_dir}/config/whitelist.json\"|g" ${ghproxy_dir}/config/config.toml
|
||||
|
||||
# 下载systemd服务文件
|
||||
if [ "$ghproxy_dir" = "/usr/local/ghproxy" ]; then
|
||||
wget -q -O /etc/systemd/system/ghproxy.service https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/dev/deploy/ghproxy.service
|
||||
else
|
||||
|
||||
cat <<EOF > /etc/systemd/system/ghproxy.service
|
||||
|
||||
[Unit]
|
||||
Description=Github Proxy Service
|
||||
After=network.target
|
||||
@@ -91,51 +134,6 @@ WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
# 创建目录
|
||||
mkdir -p ${ghproxy_dir}
|
||||
mkdir -p ${ghproxy_dir}/config
|
||||
mkdir -p ${ghproxy_dir}/log
|
||||
mkdir -p ${ghproxy_dir}/pages
|
||||
|
||||
# 获取最新版本号
|
||||
VERSION=$(curl -s https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/DEV-VERSION)
|
||||
wget -q -O ${ghproxy_dir}/VERSION https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/DEV-VERSION
|
||||
|
||||
# 下载ghproxy
|
||||
wget -q -O ${ghproxy_dir}/ghproxy https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/$VERSION/ghproxy-linux-$ARCH.tar.gz
|
||||
install tar
|
||||
tar -zxvf ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz -C ${ghproxy_dir}
|
||||
chmod +x ${ghproxy_dir}/ghproxy
|
||||
|
||||
# 下载pages
|
||||
wget -q -O ${ghproxy_dir}/pages/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/index.html
|
||||
wget -q -O ${ghproxy_dir}/pages/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/favicon.ico
|
||||
|
||||
|
||||
# 下载配置文件
|
||||
if [ -f ${ghproxy_dir}/config/config.toml ]; then
|
||||
echo "配置文件已存在, 跳过下载"
|
||||
echo "[WARNING] 请检查配置文件是否正确,DEV版本升级时请注意配置文件兼容性"
|
||||
sleep 2
|
||||
else
|
||||
wget -q -O ${ghproxy_dir}/config/config.toml https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/config.toml
|
||||
fi
|
||||
|
||||
# 替换 port = 8080
|
||||
sed -i "s/port = 8080/port = $PORT/g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i 's/host = "127.0.0.1"/host = "'"$IP"'"/g' ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|staticDir = \"/usr/local/ghproxy/pages\"|staticDir = \"${ghproxy_dir}/pages\"|g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|logFilePath = \"/usr/local/ghproxy/log/ghproxy.log\"|logFilePath = \"${ghproxy_dir}/log/ghproxy.log\"|g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|blacklistFile = \"/usr/local/ghproxy/config/blacklist.json\"|blacklistFile = \"${ghproxy_dir}/config/blacklist.json\"|g" ${ghproxy_dir}/config/config.toml
|
||||
sed -i "s|whitelistFile = \"/usr/local/ghproxy/config/whitelist.json\"|whitelistFile = \"${ghproxy_dir}/config/whitelist.json\"|g" ${ghproxy_dir}/config/config.toml
|
||||
|
||||
# 下载systemd服务文件
|
||||
if [ "$ghproxy_dir" = "/usr/local/ghproxy" ]; then
|
||||
wget -q -O /etc/systemd/system/ghproxy.service https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/ghproxy.service
|
||||
else
|
||||
make_systemd_service()
|
||||
fi
|
||||
|
||||
# 启动ghproxy
|
||||
|
||||
@@ -73,26 +73,6 @@ if [ -z "$ghproxy_dir" ]; then
|
||||
ghproxy_dir="/usr/local/ghproxy"
|
||||
fi
|
||||
|
||||
make_systemd_service() {
|
||||
cat <<EOF > /etc/systemd/system/ghproxy.service
|
||||
[Unit]
|
||||
Description=Github Proxy Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/bash -c '$ghproxy_dir/ghproxy -cfg $ghproxy_dir/config/config.toml > $ghproxy_dir/log/run.log 2>&1'
|
||||
WorkingDirectory=$ghproxy_dir
|
||||
Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
# 创建目录
|
||||
mkdir -p ${ghproxy_dir}
|
||||
mkdir -p ${ghproxy_dir}/config
|
||||
@@ -104,7 +84,7 @@ VERSION=$(curl -s https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/mai
|
||||
wget -q -O ${ghproxy_dir}/VERSION https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/VERSION
|
||||
|
||||
# 下载ghproxy
|
||||
wget -q -O ${ghproxy_dir}/ghproxy https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/${VERSION}/ghproxy-linux-${ARCH}.tar.gz
|
||||
wget -q -O ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/${VERSION}/ghproxy-linux-${ARCH}.tar.gz
|
||||
install tar
|
||||
tar -zxvf ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz -C ${ghproxy_dir}
|
||||
chmod +x ${ghproxy_dir}/ghproxy
|
||||
@@ -135,7 +115,25 @@ sed -i "s|whitelistFile = \"/usr/local/ghproxy/config/whitelist.json\"|whitelist
|
||||
if [ "$ghproxy_dir" = "/usr/local/ghproxy" ]; then
|
||||
wget -q -O /etc/systemd/system/ghproxy.service https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/ghproxy.service
|
||||
else
|
||||
make_systemd_service()
|
||||
|
||||
cat <<EOF > /etc/systemd/system/ghproxy.service
|
||||
|
||||
[Unit]
|
||||
Description=Github Proxy Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/bash -c '$ghproxy_dir/ghproxy -cfg $ghproxy_dir/config/config.toml > $ghproxy_dir/log/run.log 2>&1'
|
||||
WorkingDirectory=$ghproxy_dir
|
||||
Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
fi
|
||||
|
||||
# 启动ghproxy
|
||||
|
||||
4
go.mod
4
go.mod
@@ -1,11 +1,12 @@
|
||||
module ghproxy
|
||||
|
||||
go 1.23.6
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/time v0.10.0
|
||||
)
|
||||
|
||||
@@ -31,7 +32,6 @@ require (
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -73,6 +73,8 @@ golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
|
||||
6
main.go
6
main.go
@@ -100,8 +100,8 @@ func setupRateLimit(cfg *config.Config) {
|
||||
}
|
||||
}
|
||||
|
||||
func InitReq() {
|
||||
proxy.InitReq()
|
||||
func InitReq(cfg *config.Config) {
|
||||
proxy.InitReq(cfg)
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -109,7 +109,7 @@ func init() {
|
||||
flag.Parse()
|
||||
loadConfig()
|
||||
setupLogger(cfg)
|
||||
InitReq()
|
||||
InitReq(cfg)
|
||||
loadlist(cfg)
|
||||
setupRateLimit(cfg)
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ var (
|
||||
BufferPool *sync.Pool
|
||||
)
|
||||
|
||||
func InitReq() {
|
||||
initChunkedHTTPClient()
|
||||
initGitHTTPClient()
|
||||
func InitReq(cfg *config.Config) {
|
||||
initChunkedHTTPClient(cfg)
|
||||
initGitHTTPClient(cfg)
|
||||
|
||||
// 初始化固定大小的缓存池
|
||||
BufferPool = &sync.Pool{
|
||||
@@ -34,7 +34,7 @@ func InitReq() {
|
||||
}
|
||||
}
|
||||
|
||||
func initChunkedHTTPClient() {
|
||||
func initChunkedHTTPClient(cfg *config.Config) {
|
||||
ctr = &http.Transport{
|
||||
MaxIdleConns: 100,
|
||||
MaxConnsPerHost: 60,
|
||||
@@ -47,6 +47,9 @@ func initChunkedHTTPClient() {
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
}
|
||||
if cfg.Outbound.Enabled {
|
||||
initTransport(cfg, ctr)
|
||||
}
|
||||
cclient = &http.Client{
|
||||
Transport: ctr,
|
||||
}
|
||||
|
||||
135
proxy/dial.go
Normal file
135
proxy/dial.go
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
made&PR by @lfhy
|
||||
https://github.com/WJQSERVER-STUDIO/ghproxy/pull/46
|
||||
*/
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"ghproxy/config"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
// initTransport 初始化 HTTP 传输层的代理设置
|
||||
func initTransport(cfg *config.Config, transport *http.Transport) {
|
||||
// 如果代理功能未启用,直接返回
|
||||
if !cfg.Outbound.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果代理 URL 未设置,使用环境变量中的代理配置
|
||||
if cfg.Outbound.Url == "" {
|
||||
transport.Proxy = http.ProxyFromEnvironment
|
||||
logWarning("Outbound proxy is not set, using environment variables")
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试解析代理 URL
|
||||
proxyInfo, err := url.Parse(cfg.Outbound.Url)
|
||||
if err != nil {
|
||||
// 如果解析失败,记录错误日志并使用环境变量中的代理配置
|
||||
logError("Failed to parse outbound proxy URL %v", err)
|
||||
transport.Proxy = http.ProxyFromEnvironment
|
||||
return
|
||||
}
|
||||
|
||||
// 根据代理 URL 的 scheme(协议类型)选择代理类型
|
||||
switch strings.ToLower(proxyInfo.Scheme) {
|
||||
case "http", "https": // 如果是 HTTP/HTTPS 代理
|
||||
transport.Proxy = http.ProxyURL(proxyInfo) // 设置 HTTP(S) 代理
|
||||
logInfo("Using HTTP(S) proxy: %s", proxyInfo.Redacted())
|
||||
case "socks5": // 如果是 SOCKS5 代理
|
||||
// 调用 newProxyDial 创建 SOCKS5 代理拨号器
|
||||
proxyDialer := newProxyDial(cfg.Outbound.Url)
|
||||
transport.Proxy = nil // 禁用 HTTP Proxy 设置,因为 SOCKS5 不需要 HTTP Proxy
|
||||
|
||||
// 尝试将 Dialer 转换为支持上下文的 ContextDialer
|
||||
if contextDialer, ok := proxyDialer.(proxy.ContextDialer); ok {
|
||||
transport.DialContext = contextDialer.DialContext
|
||||
} else {
|
||||
// 如果不支持 ContextDialer,则回退到传统的 Dial 方法
|
||||
transport.Dial = proxyDialer.Dial
|
||||
logWarning("SOCKS5 dialer does not support ContextDialer, using legacy Dial")
|
||||
}
|
||||
logInfo("Using SOCKS5 proxy chain: %s", cfg.Outbound.Url)
|
||||
default: // 如果代理协议不支持
|
||||
logError("Unsupported proxy scheme: %s", proxyInfo.Scheme)
|
||||
transport.Proxy = http.ProxyFromEnvironment // 回退到环境变量代理
|
||||
}
|
||||
}
|
||||
|
||||
// newProxyDial 创建一个 SOCKS5 代理拨号器
|
||||
func newProxyDial(proxyUrls string) proxy.Dialer {
|
||||
var proxyDialer proxy.Dialer = proxy.Direct // 初始为直接连接,不使用代理
|
||||
|
||||
// 支持多个代理 URL(以逗号分隔)
|
||||
for _, proxyUrl := range strings.Split(proxyUrls, ",") {
|
||||
proxyUrl = strings.TrimSpace(proxyUrl) // 去除首尾空格
|
||||
if proxyUrl == "" { // 跳过空的代理 URL
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析代理 URL
|
||||
urlInfo, err := url.Parse(proxyUrl)
|
||||
if err != nil {
|
||||
// 如果 URL 解析失败,记录错误日志并跳过
|
||||
logError("Failed to parse proxy URL %q: %v", proxyUrl, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查代理协议是否为 SOCKS5
|
||||
if urlInfo.Scheme != "socks5" {
|
||||
logWarning("Skipping non-SOCKS5 proxy: %s", urlInfo.Scheme)
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析代理认证信息(用户名和密码)
|
||||
auth := parseAuth(urlInfo)
|
||||
|
||||
// 创建 SOCKS5 代理拨号器
|
||||
dialer, err := createSocksDialer(urlInfo.Host, auth, proxyDialer)
|
||||
if err != nil {
|
||||
// 如果创建失败,记录错误日志并跳过
|
||||
logError("Failed to create SOCKS5 dialer for %q: %v", proxyUrl, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 更新代理拨号器,支持代理链
|
||||
proxyDialer = dialer
|
||||
}
|
||||
|
||||
return proxyDialer
|
||||
}
|
||||
|
||||
// parseAuth 解析代理 URL 中的认证信息(用户名和密码)
|
||||
func parseAuth(urlInfo *url.URL) *proxy.Auth {
|
||||
// 如果 URL 中没有用户信息,返回 nil
|
||||
if urlInfo.User == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取用户名
|
||||
username := urlInfo.User.Username()
|
||||
|
||||
// 获取密码(注意:Password() 返回两个值,需要显式处理第二个值)
|
||||
password, passwordSet := urlInfo.User.Password()
|
||||
if !passwordSet {
|
||||
password = "" // 如果密码未设置,使用空字符串
|
||||
}
|
||||
|
||||
// 返回包含用户名和密码的认证信息
|
||||
return &proxy.Auth{
|
||||
User: username,
|
||||
Password: password, // 允许空密码
|
||||
}
|
||||
}
|
||||
|
||||
// createSocksDialer 创建 SOCKS5 拨号器
|
||||
func createSocksDialer(host string, auth *proxy.Auth, previous proxy.Dialer) (proxy.Dialer, error) {
|
||||
// 调用 golang.org/x/net/proxy 提供的 SOCKS5 方法创建拨号器
|
||||
return proxy.SOCKS5("tcp", host, auth, previous)
|
||||
}
|
||||
@@ -17,12 +17,15 @@ var (
|
||||
gtr *http.Transport
|
||||
)
|
||||
|
||||
func initGitHTTPClient() {
|
||||
func initGitHTTPClient(cfg *config.Config) {
|
||||
gtr = &http.Transport{
|
||||
MaxIdleConns: 30,
|
||||
MaxConnsPerHost: 30,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
}
|
||||
if cfg.Outbound.Enabled {
|
||||
initTransport(cfg, gtr)
|
||||
}
|
||||
gclient = &http.Client{
|
||||
Transport: gtr,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user