This commit is contained in:
WJQSERVER 2025-02-16 19:48:53 +08:00
parent bd666e08d1
commit 785a74dfeb
14 changed files with 241 additions and 126 deletions

View file

@ -1,5 +1,14 @@
# 更新日志 # 更新日志
25w14t-2
---
- PRE-RELEASE: 此版本是测试验证版本,请勿在生产环境中使用;
- CHANGE: 使用`touka-httpc`封装`HTTP Client`,更新到`v0.1.0`版本, 参看`touka-httpc`
- CHANGE: 重构`whitelist`实现
- CHANGE: 对`proxy`进行结构性调整
- CHANGE: `chunckedreq``gitreq`共用`BufferPool``HTTP Client`
- CHANGE: 新增`HTTP Client`配置块
25w14t-1 25w14t-1
--- ---
- PRE-RELEASE: 此版本是测试验证版本,请勿在生产环境中使用; - PRE-RELEASE: 此版本是测试验证版本,请勿在生产环境中使用;

View file

@ -1 +1 @@
25w14t-1 25w14t-2

View file

@ -91,6 +91,12 @@ port = 8080 # 监听端口
sizeLimit = 125 # 125MB sizeLimit = 125 # 125MB
enableH2C = "on" # 是否开启H2C传输(latest和dev版本请开启) on/off enableH2C = "on" # 是否开启H2C传输(latest和dev版本请开启) on/off
[httpc]
mode = "auto" # "auto" or "advanced" HTTP客户端模式 自动/高级模式
maxIdleConns = 100 # only for advanced mode 仅用于高级模式
maxIdleConnsPerHost = 60 # only for advanced mode 仅用于高级模式
maxConnsPerHost = 0 # only for advanced mode 仅用于高级模式
[pages] [pages]
enabled = false # 是否开启内置静态页面(Docker版本请关闭此项) enabled = false # 是否开启内置静态页面(Docker版本请关闭此项)
staticPath = "/data/www" # 静态页面文件路径 staticPath = "/data/www" # 静态页面文件路径

View file

@ -18,10 +18,18 @@ var (
func Init(cfg *config.Config) { func Init(cfg *config.Config) {
if cfg.Blacklist.Enabled { if cfg.Blacklist.Enabled {
InitBlacklist(cfg) err := InitBlacklist(cfg)
if err != nil {
logError(err.Error())
return
}
} }
if cfg.Whitelist.Enabled { if cfg.Whitelist.Enabled {
LoadWhitelist(cfg) err := InitWhitelist(cfg)
if err != nil {
logError(err.Error())
return
}
} }
logDebug("Auth Init") logDebug("Auth Init")
} }

View file

@ -2,58 +2,90 @@ package auth
import ( import (
"encoding/json" "encoding/json"
"fmt"
"ghproxy/config" "ghproxy/config"
"os" "os"
"strings" "strings"
"sync"
) )
type WhitelistConfig struct { // Whitelist 用于存储白名单信息
Whitelist []string `json:"whitelist"` type Whitelist struct {
userSet map[string]struct{} // 用户级白名单
repoSet map[string]map[string]struct{} // 仓库级白名单
initOnce sync.Once // 确保初始化只执行一次
initialized bool // 初始化状态标识
} }
var ( var (
whitelistfile = "/data/ghproxy/config/whitelist.json" whitelistInstance *Whitelist
whitelist *WhitelistConfig whitelistInitErr error
) )
func LoadWhitelist(cfg *config.Config) { // InitWhitelist 初始化白名单(线程安全,仅执行一次)
whitelistfile = cfg.Whitelist.WhitelistFile func InitWhitelist(cfg *config.Config) error {
whitelist = &WhitelistConfig{} whitelistInstance = &Whitelist{
userSet: make(map[string]struct{}),
repoSet: make(map[string]map[string]struct{}),
}
data, err := os.ReadFile(whitelistfile) data, err := os.ReadFile(cfg.Whitelist.WhitelistFile)
if err != nil { if err != nil {
logError("Failed to read whitelist file: %v", err) return fmt.Errorf("failed to read whitelist: %w", err)
} }
err = json.Unmarshal(data, whitelist) var list struct {
if err != nil { Entries []string `json:"whitelist"`
logError("Failed to unmarshal whitelist JSON: %v", err) }
if err := json.Unmarshal(data, &list); err != nil {
return fmt.Errorf("invalid whitelist format: %w", err)
}
for _, entry := range list.Entries {
user, repo := splitUserRepoWhitelist(entry)
switch {
case repo == "" || repo == "*":
whitelistInstance.userSet[user] = struct{}{}
default:
if _, exists := whitelistInstance.repoSet[user]; !exists {
whitelistInstance.repoSet[user] = make(map[string]struct{})
}
whitelistInstance.repoSet[user][repo] = struct{}{}
} }
} }
func CheckWhitelist(fullrepo string, user string, repo string) bool { whitelistInstance.initialized = true
return forRangeCheckWhitelist(whitelist.Whitelist, fullrepo, user) return nil
} }
func sliceRepoName_Whitelist(fullrepo string) (string, string) { // CheckWhitelist 检查用户和仓库是否在白名单中(无锁设计)
s := strings.Split(fullrepo, "/") func CheckWhitelist(username, repo string) bool {
if len(s) != 2 { if whitelistInstance == nil || !whitelistInstance.initialized {
return "", ""
}
return s[0], s[1]
}
func forRangeCheckWhitelist(wlist []string, fullrepo string, user string) bool {
for _, passd := range wlist {
users, _ := sliceRepoName_Whitelist(passd)
if users == user {
if strings.HasSuffix(passd, "/*") {
return true
}
if fullrepo == passd {
return true
}
}
}
return false return false
} }
// 先检查用户级白名单
if _, exists := whitelistInstance.userSet[username]; exists {
return true
}
// 再检查仓库级白名单
if repos, userExists := whitelistInstance.repoSet[username]; userExists {
// 允许仓库名为空时的全用户仓库匹配
if repo == "" {
return true
}
_, repoExists := repos[repo]
return repoExists
}
return false
}
// splitUserRepoWhitelist 分割用户和仓库信息(仅初始化时使用)
func splitUserRepoWhitelist(fullRepo string) (user, repo string) {
if idx := strings.Index(fullRepo, "/"); idx > 0 {
return fullRepo[:idx], fullRepo[idx+1:]
}
return fullRepo, ""
}

View file

@ -6,6 +6,7 @@ import (
type Config struct { type Config struct {
Server ServerConfig Server ServerConfig
Httpc HttpcConfig
Pages PagesConfig Pages PagesConfig
Log LogConfig Log LogConfig
CORS CORSConfig CORS CORSConfig
@ -24,6 +25,20 @@ type ServerConfig struct {
Debug bool `toml:"debug"` Debug bool `toml:"debug"`
} }
/*
[httpc]
mode = "auto" # "auto" or "advanced"
maxIdleConns = 100 # only for advanced mode
maxIdleConnsPerHost = 60 # only for advanced mode
maxConnsPerHost = 0 # only for advanced mode
*/
type HttpcConfig struct {
Mode string `toml:"mode"`
MaxIdleConns int `toml:"maxIdleConns"`
MaxIdleConnsPerHost int `toml:"maxIdleConnsPerHost"`
MaxConnsPerHost int `toml:"maxConnsPerHost"`
}
type PagesConfig struct { type PagesConfig struct {
Enabled bool `toml:"enabled"` Enabled bool `toml:"enabled"`
StaticDir string `toml:"staticDir"` StaticDir string `toml:"staticDir"`

View file

@ -5,6 +5,12 @@ sizeLimit = 125 # MB
enableH2C = "on" # "on" or "off" enableH2C = "on" # "on" or "off"
debug = false debug = false
[httpc]
mode = "auto" # "auto" or "advanced"
maxIdleConns = 100 # only for advanced mode
maxIdleConnsPerHost = 60 # only for advanced mode
maxConnsPerHost = 0 # only for advanced mode
[pages] [pages]
enabled = false enabled = false
staticDir = "/data/www" staticDir = "/data/www"

View file

@ -5,6 +5,12 @@ sizeLimit = 125 # MB
enableH2C = "on" enableH2C = "on"
debug = false debug = false
[httpc]
mode = "auto" # "auto" or "advanced"
maxIdleConns = 100 # only for advanced mode
maxIdleConnsPerHost = 60 # only for advanced mode
maxConnsPerHost = 0 # only for advanced mode
[pages] [pages]
enabled = false enabled = false
staticDir = "/usr/local/ghproxy/pages" staticDir = "/usr/local/ghproxy/pages"

4
go.mod
View file

@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v1.4.0 github.com/BurntSushi/toml v1.4.0
github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0 github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/satomitouka/touka-httpc v0.0.3 github.com/satomitouka/touka-httpc v0.1.0
golang.org/x/net v0.35.0 golang.org/x/net v0.35.0
golang.org/x/time v0.10.0 golang.org/x/time v0.10.0
) )
@ -19,7 +19,7 @@ require (
github.com/gin-contrib/sse v1.0.0 // indirect github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect github.com/go-playground/validator/v10 v10.25.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect

14
go.sum
View file

@ -27,6 +27,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@ -53,6 +55,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satomitouka/touka-httpc v0.0.3 h1:SLb14DWBIDeIaNQ0wMwRwJMjUDakHVR1Jbdct3Qi8fA= github.com/satomitouka/touka-httpc v0.0.3 h1:SLb14DWBIDeIaNQ0wMwRwJMjUDakHVR1Jbdct3Qi8fA=
github.com/satomitouka/touka-httpc v0.0.3/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM= github.com/satomitouka/touka-httpc v0.0.3/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.4 h1:sZs/2kqTSyLQ/pDHs/71l7MSG46j4rZNKfqn3CFAboU=
github.com/satomitouka/touka-httpc v0.0.4/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.5 h1:ov1v29vrjvwRNbGqFJHmrCp+3/qXLoyWubO4kTDvb28=
github.com/satomitouka/touka-httpc v0.0.5/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.6 h1:1iSaTB9KpviXy2NHvMXuRzy5mkcvle+fktWPhpS907c=
github.com/satomitouka/touka-httpc v0.0.6/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.7 h1:igoLqXs6R1yNIdKMcfpwRB1l6KLLus6DvWT3xL1T5FY=
github.com/satomitouka/touka-httpc v0.0.7/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.0.8 h1:KW521Z2z9BarnTgCajug/W/tIbnoIH+CzA7CON19iAg=
github.com/satomitouka/touka-httpc v0.0.8/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/satomitouka/touka-httpc v0.1.0 h1:CXCsr6NhdskK/W/ezvhwK2CP8QGCxewkBhsEjrM7K8s=
github.com/satomitouka/touka-httpc v0.1.0/go.mod h1:ULB/0Ze0Apm46YKl35Jmj1hW5YLVVeOGqCqn+ijqGPM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

View file

@ -7,66 +7,15 @@ import (
"io" "io"
"net/http" "net/http"
"strconv" "strconv"
"sync"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
httpc "github.com/satomitouka/touka-httpc"
) )
var BufferSize int = 32 * 1024 // 32KB
var (
ctr *http.Transport
BufferPool *sync.Pool
cclient *httpc.Client
)
func InitReq(cfg *config.Config) {
initChunkedHTTPClient(cfg)
initGitHTTPClient(cfg)
// 初始化固定大小的缓存池
BufferPool = &sync.Pool{
New: func() interface{} {
return make([]byte, BufferSize)
},
}
}
func initChunkedHTTPClient(cfg *config.Config) {
/*
ctr = &http.Transport{
MaxIdleConns: 100,
MaxConnsPerHost: 60,
IdleConnTimeout: 20 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
}
*/
ctr = &http.Transport{
MaxIdleConns: 100,
MaxConnsPerHost: 60,
IdleConnTimeout: 20 * time.Second,
}
if cfg.Outbound.Enabled {
initTransport(cfg, ctr)
}
cclient = httpc.New(
httpc.WithTransport(ctr),
)
}
func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) { func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) {
method := c.Request.Method method := c.Request.Method
// 发送HEAD请求, 预获取Content-Length // 发送HEAD请求, 预获取Content-Length
headReq, err := cclient.NewRequest("HEAD", u, nil) headReq, err := client.NewRequest("HEAD", u, nil)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to create request: %v", err)) HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return return
@ -75,7 +24,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri
removeWSHeader(headReq) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头) removeWSHeader(headReq) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头)
AuthPassThrough(c, cfg, headReq) AuthPassThrough(c, cfg, headReq)
headResp, err := cclient.Do(headReq) headResp, err := client.Do(headReq)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
return return
@ -107,7 +56,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri
bodyReader := bytes.NewBuffer(body) bodyReader := bytes.NewBuffer(body)
req, err := cclient.NewRequest(method, u, bodyReader) req, err := client.NewRequest(method, u, bodyReader)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to create request: %v", err)) HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return return
@ -116,7 +65,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri
removeWSHeader(req) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头) removeWSHeader(req) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头)
AuthPassThrough(c, cfg, req) AuthPassThrough(c, cfg, req)
resp, err := cclient.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
return return

View file

@ -7,42 +7,16 @@ import (
"io" "io"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
httpc "github.com/satomitouka/touka-httpc"
) )
var (
gclient *httpc.Client
gtr *http.Transport
)
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,
}
*/
gclient = httpc.New(
httpc.WithTransport(gtr),
)
}
func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) { func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) {
method := c.Request.Method method := c.Request.Method
logInfo("%s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto) logInfo("%s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto)
// 发送HEAD请求, 预获取Content-Length // 发送HEAD请求, 预获取Content-Length
headReq, err := gclient.NewRequest("HEAD", u, nil) headReq, err := client.NewRequest("HEAD", u, nil)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to create request: %v", err)) HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return return
@ -50,7 +24,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
setRequestHeaders(c, headReq) setRequestHeaders(c, headReq)
AuthPassThrough(c, cfg, headReq) AuthPassThrough(c, cfg, headReq)
headResp, err := gclient.Do(headReq) headResp, err := client.Do(headReq)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
return return
@ -84,7 +58,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
bodyReader := bytes.NewBuffer(body) bodyReader := bytes.NewBuffer(body)
// 创建请求 // 创建请求
req, err := gclient.NewRequest(method, u, bodyReader) req, err := client.NewRequest(method, u, bodyReader)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to create request: %v", err)) HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return return
@ -92,7 +66,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
setRequestHeaders(c, req) setRequestHeaders(c, req)
AuthPassThrough(c, cfg, req) AuthPassThrough(c, cfg, req)
resp, err := gclient.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
return return
@ -139,7 +113,15 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
c.Status(resp.StatusCode) c.Status(resp.StatusCode)
if _, err := io.Copy(c.Writer, resp.Body); err != nil { // 使用固定32KB缓冲池
logError("%s %s %s %s %s Response-Copy-Error: %v", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto, err) buffer := BufferPool.Get().([]byte)
defer BufferPool.Put(buffer)
_, err = io.CopyBuffer(c.Writer, resp.Body, buffer)
if err != nil {
logError("%s %s %s %s %s Failed to copy response body: %v", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto, err)
return
} else {
c.Writer.Flush() // 确保刷入
} }
} }

View file

@ -61,7 +61,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
// 白名单检查 // 白名单检查
if cfg.Whitelist.Enabled { if cfg.Whitelist.Enabled {
whitelist := auth.CheckWhitelist(repouser, username, repo) whitelist := auth.CheckWhitelist(username, repo)
if !whitelist { if !whitelist {
logErrMsg := fmt.Sprintf("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, repouser) logErrMsg := fmt.Sprintf("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, repouser)
errMsg := fmt.Sprintf("Whitelist Blocked repo: %s", repouser) errMsg := fmt.Sprintf("Whitelist Blocked repo: %s", repouser)

88
proxy/httpc.go Normal file
View file

@ -0,0 +1,88 @@
package proxy
import (
"fmt"
"ghproxy/config"
"net/http"
"sync"
"time"
httpc "github.com/satomitouka/touka-httpc"
)
var BufferSize int = 32 * 1024 // 32KB
var (
tr *http.Transport
BufferPool *sync.Pool
client *httpc.Client
)
func InitReq(cfg *config.Config) {
initHTTPClient(cfg)
// 初始化固定大小的缓存池
BufferPool = &sync.Pool{
New: func() interface{} {
return make([]byte, BufferSize)
},
}
}
func initHTTPClient(cfg *config.Config) {
/*
ctr = &http.Transport{
MaxIdleConns: 100,
MaxConnsPerHost: 60,
IdleConnTimeout: 20 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
}
*/
if cfg.Httpc.Mode == "auto" {
tr = &http.Transport{
//MaxIdleConns: 160,
IdleConnTimeout: 30 * time.Second,
WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB
}
} else if cfg.Httpc.Mode == "advanced" {
tr = &http.Transport{
MaxIdleConns: cfg.Httpc.MaxIdleConns,
MaxConnsPerHost: cfg.Httpc.MaxConnsPerHost,
MaxIdleConnsPerHost: cfg.Httpc.MaxIdleConnsPerHost,
WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB
}
} else {
// 错误的模式
logError("unknown httpc mode: %s", cfg.Httpc.Mode)
fmt.Println("unknown httpc mode: ", cfg.Httpc.Mode)
logWarning("use Auto to Run HTTP Client")
fmt.Println("use Auto to Run HTTP Client")
tr = &http.Transport{
//MaxIdleConns: 160,
IdleConnTimeout: 30 * time.Second,
WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB
}
}
if cfg.Outbound.Enabled {
initTransport(cfg, tr)
}
if cfg.Server.Debug {
client = httpc.New(
httpc.WithTransport(tr),
httpc.WithDumpLog(),
)
} else {
client = httpc.New(
httpc.WithTransport(tr),
)
}
}