Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
284b38bab4 | ||
|
|
d73dfe7db5 | ||
|
|
dc286e002c |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,11 +1,24 @@
|
||||
# 更新日志
|
||||
|
||||
2.4.1 - 2025-03-12
|
||||
2.4.2 - 2025-03-14
|
||||
---
|
||||
- CHANGE: 在GitClone Cache模式下, 相关请求会使用独立httpc client
|
||||
- CHANGE: 为GitClone Cache的独立httpc client增加ForceH2C选项
|
||||
- FIX: 修正GitClone Cache模式下的Url生成问题
|
||||
|
||||
25w18a - 2025-03-14
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v2.4.2的预发布版本,请勿在生产环境中使用;
|
||||
- CHANGE: 在GitClone Cache模式下, 相关请求会使用独立httpc client
|
||||
- CHANGE: 为GitClone Cache的独立httpc client增加ForceH2C选项
|
||||
- FIX: 修正GitClone Cache模式下的Url生成问题
|
||||
|
||||
2.4.1 - 2025-03-13
|
||||
---
|
||||
- CHANGE: 重构路由匹配
|
||||
- CHANGE: 更新相关依赖以修复错误
|
||||
|
||||
25w17a - 2025-03-12
|
||||
25w17a - 2025-03-13
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v2.4.1的预发布版本,请勿在生产环境中使用;
|
||||
- CHANGE: 重构路由匹配
|
||||
|
||||
@@ -1 +1 @@
|
||||
25w17a
|
||||
25w18a
|
||||
14
README.md
14
README.md
@@ -105,6 +105,7 @@ maxConnsPerHost = 0 # only for advanced mode 仅用于高级模式
|
||||
[gitclone]
|
||||
mode = "bypass" # bypass / cache 运行模式, cache模式依赖smart-git
|
||||
smartGitAddr = "http://127.0.0.1:8080" # smart-git组件地址
|
||||
ForceH2C = false # 强制使用H2C连接
|
||||
|
||||
[pages]
|
||||
mode = "internal" # "internal" or "external" 内部/外部 前端 默认内部
|
||||
@@ -170,19 +171,16 @@ url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890" 支持Socks5/HTTP(S)
|
||||
}
|
||||
```
|
||||
|
||||
### Caddy反代配置
|
||||
|
||||
```Caddyfile
|
||||
example.com {
|
||||
reverse_proxy * 127.0.0.1:7210
|
||||
}
|
||||
```
|
||||
|
||||
### 前端页面
|
||||
|
||||
#### Bootstrap主题
|
||||

|
||||

|
||||
|
||||
#### Nebula主题
|
||||

|
||||

|
||||
|
||||
## 赞助
|
||||
|
||||
如果您觉得本项目对您有帮助,欢迎赞助支持,您的赞助将用于Demo服务器开支及开发者时间成本支出,感谢您的支持!
|
||||
|
||||
@@ -54,10 +54,12 @@ type HttpcConfig struct {
|
||||
[gitclone]
|
||||
mode = "bypass" # bypass / cache
|
||||
smartGitAddr = ":8080"
|
||||
ForceH2C = true
|
||||
*/
|
||||
type GitCloneConfig struct {
|
||||
Mode string `toml:"mode"`
|
||||
SmartGitAddr string `toml:"smartGitAddr"`
|
||||
ForceH2C bool `toml:"ForceH2C"`
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,6 +15,7 @@ maxConnsPerHost = 0 # only for advanced mode
|
||||
[gitclone]
|
||||
mode = "bypass" # bypass / cache
|
||||
smartGitAddr = "http://127.0.0.1:8080"
|
||||
ForceH2C = false
|
||||
|
||||
[pages]
|
||||
mode = "internal" # "internal" or "external"
|
||||
|
||||
@@ -15,6 +15,7 @@ maxConnsPerHost = 0 # only for advanced mode
|
||||
[gitclone]
|
||||
mode = "bypass" # bypass / cache
|
||||
smartGitAddr = "http://127.0.0.1:8080"
|
||||
ForceH2C = false
|
||||
|
||||
[pages]
|
||||
mode = "internal" # "internal" or "external"
|
||||
|
||||
@@ -18,17 +18,23 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
|
||||
method := c.Request.Method
|
||||
logInfo("%s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||
|
||||
logInfo("U:%s", u)
|
||||
logDump("Url Before FMT:%s", u)
|
||||
if cfg.GitClone.Mode == "cache" {
|
||||
userPath, repoPath, remainingPath, err := extractParts(u)
|
||||
userPath, repoPath, remainingPath, queryParams, err := extractParts(u)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to extract parts from URL: %v", err))
|
||||
return
|
||||
}
|
||||
// 构建新url
|
||||
u = cfg.GitClone.SmartGitAddr + userPath + repoPath + remainingPath
|
||||
u = cfg.GitClone.SmartGitAddr + userPath + repoPath + remainingPath + "?" + queryParams.Encode()
|
||||
logDump("New Url After FMT:%s", u)
|
||||
}
|
||||
|
||||
var (
|
||||
resp *http.Response
|
||||
err error
|
||||
)
|
||||
|
||||
body, err := readRequestBody(c)
|
||||
if err != nil {
|
||||
HandleError(c, err.Error())
|
||||
@@ -37,18 +43,35 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
|
||||
|
||||
bodyReader := bytes.NewBuffer(body)
|
||||
// 创建请求
|
||||
req, err := client.NewRequest(method, u, bodyReader)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||
return
|
||||
}
|
||||
setRequestHeaders(c, req)
|
||||
AuthPassThrough(c, cfg, req)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
||||
return
|
||||
if cfg.GitClone.Mode == "cache" {
|
||||
req, err := gitclient.NewRequest(method, u, bodyReader)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||
return
|
||||
}
|
||||
setRequestHeaders(c, req)
|
||||
AuthPassThrough(c, cfg, req)
|
||||
|
||||
resp, err = gitclient.Do(req)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
req, err := client.NewRequest(method, u, bodyReader)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||
return
|
||||
}
|
||||
setRequestHeaders(c, req)
|
||||
AuthPassThrough(c, cfg, req)
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
//defer resp.Body.Close()
|
||||
defer func(Body io.ReadCloser) {
|
||||
@@ -124,11 +147,11 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
|
||||
}
|
||||
|
||||
// extractParts 从给定的 URL 中提取所需的部分
|
||||
func extractParts(rawURL string) (string, string, string, error) {
|
||||
func extractParts(rawURL string) (string, string, string, url.Values, error) {
|
||||
// 解析 URL
|
||||
parsedURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
return "", "", "", nil, err
|
||||
}
|
||||
|
||||
// 获取路径部分并分割
|
||||
@@ -136,7 +159,7 @@ func extractParts(rawURL string) (string, string, string, error) {
|
||||
|
||||
// 提取所需的部分
|
||||
if len(pathParts) < 3 {
|
||||
return "", "", "", fmt.Errorf("URL path is too short")
|
||||
return "", "", "", nil, fmt.Errorf("URL path is too short")
|
||||
}
|
||||
|
||||
// 提取 /WJQSERVER-STUDIO 和 /go-utils.git
|
||||
@@ -149,5 +172,8 @@ func extractParts(rawURL string) (string, string, string, error) {
|
||||
remainingPath = "/" + remainingPath
|
||||
}
|
||||
|
||||
return repoOwner, repoName, remainingPath, nil
|
||||
// 查询参数
|
||||
queryParams := parsedURL.Query()
|
||||
|
||||
return repoOwner, repoName, remainingPath, queryParams, nil
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
|
||||
|
||||
logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, username, repo)
|
||||
// dump log 记录详细信息 c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, full Header
|
||||
LogDump("%s %s %s %s %s %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, c.Request.Header)
|
||||
logDump("%s %s %s %s %s %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, c.Request.Header)
|
||||
repouser := fmt.Sprintf("%s/%s", username, repo)
|
||||
|
||||
// 白名单检查
|
||||
|
||||
@@ -14,12 +14,17 @@ var BufferSize int = 32 * 1024 // 32KB
|
||||
|
||||
var (
|
||||
tr *http.Transport
|
||||
gittr *http.Transport
|
||||
BufferPool *sync.Pool
|
||||
client *httpc.Client
|
||||
gitclient *httpc.Client
|
||||
)
|
||||
|
||||
func InitReq(cfg *config.Config) {
|
||||
initHTTPClient(cfg)
|
||||
if cfg.GitClone.Mode == "cache" {
|
||||
initGitHTTPClient(cfg)
|
||||
}
|
||||
|
||||
// 初始化固定大小的缓存池
|
||||
BufferPool = &sync.Pool{
|
||||
@@ -93,3 +98,60 @@ func initHTTPClient(cfg *config.Config) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func initGitHTTPClient(cfg *config.Config) {
|
||||
|
||||
var proTolcols = new(http.Protocols)
|
||||
proTolcols.SetHTTP1(true)
|
||||
proTolcols.SetHTTP2(true)
|
||||
proTolcols.SetUnencryptedHTTP2(true)
|
||||
if cfg.GitClone.ForceH2C {
|
||||
proTolcols.SetHTTP1(false)
|
||||
proTolcols.SetHTTP2(false)
|
||||
proTolcols.SetUnencryptedHTTP2(true)
|
||||
}
|
||||
if cfg.Httpc.Mode == "auto" {
|
||||
|
||||
gittr = &http.Transport{
|
||||
//MaxIdleConns: 160,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
WriteBufferSize: 32 * 1024, // 32KB
|
||||
ReadBufferSize: 32 * 1024, // 32KB
|
||||
Protocols: proTolcols,
|
||||
}
|
||||
} else if cfg.Httpc.Mode == "advanced" {
|
||||
gittr = &http.Transport{
|
||||
MaxIdleConns: cfg.Httpc.MaxIdleConns,
|
||||
MaxConnsPerHost: cfg.Httpc.MaxConnsPerHost,
|
||||
MaxIdleConnsPerHost: cfg.Httpc.MaxIdleConnsPerHost,
|
||||
WriteBufferSize: 32 * 1024, // 32KB
|
||||
ReadBufferSize: 32 * 1024, // 32KB
|
||||
Protocols: proTolcols,
|
||||
}
|
||||
} 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")
|
||||
gittr = &http.Transport{
|
||||
//MaxIdleConns: 160,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
WriteBufferSize: 32 * 1024, // 32KB
|
||||
ReadBufferSize: 32 * 1024, // 32KB
|
||||
}
|
||||
}
|
||||
if cfg.Outbound.Enabled {
|
||||
initTransport(cfg, gittr)
|
||||
}
|
||||
if cfg.Server.Debug {
|
||||
gitclient = httpc.New(
|
||||
httpc.WithTransport(gittr),
|
||||
httpc.WithDumpLog(),
|
||||
)
|
||||
} else {
|
||||
gitclient = httpc.New(
|
||||
httpc.WithTransport(gittr),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ var (
|
||||
// 提取用户名和仓库名
|
||||
func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches []string) (string, string) {
|
||||
if gistMatches := gistRegex.FindStringSubmatch(rawPath); len(gistMatches) == 3 {
|
||||
LogDump("%s %s %s %s %s Matched-Username: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, gistMatches[1])
|
||||
logDump("%s %s %s %s %s Matched-Username: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, gistMatches[1])
|
||||
return gistMatches[1], ""
|
||||
}
|
||||
// 定义路径
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// 日志模块
|
||||
var (
|
||||
logw = logger.Logw
|
||||
LogDump = logger.LogDump
|
||||
logDump = logger.LogDump
|
||||
logDebug = logger.LogDebug
|
||||
logInfo = logger.LogInfo
|
||||
logWarning = logger.LogWarning
|
||||
|
||||
Reference in New Issue
Block a user