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: 重构路由匹配
|
||||||
- CHANGE: 更新相关依赖以修复错误
|
- CHANGE: 更新相关依赖以修复错误
|
||||||
|
|
||||||
25w17a - 2025-03-12
|
25w17a - 2025-03-13
|
||||||
---
|
---
|
||||||
- PRE-RELEASE: 此版本是v2.4.1的预发布版本,请勿在生产环境中使用;
|
- PRE-RELEASE: 此版本是v2.4.1的预发布版本,请勿在生产环境中使用;
|
||||||
- CHANGE: 重构路由匹配
|
- CHANGE: 重构路由匹配
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
25w17a
|
25w18a
|
||||||
14
README.md
14
README.md
@@ -105,6 +105,7 @@ maxConnsPerHost = 0 # only for advanced mode 仅用于高级模式
|
|||||||
[gitclone]
|
[gitclone]
|
||||||
mode = "bypass" # bypass / cache 运行模式, cache模式依赖smart-git
|
mode = "bypass" # bypass / cache 运行模式, cache模式依赖smart-git
|
||||||
smartGitAddr = "http://127.0.0.1:8080" # smart-git组件地址
|
smartGitAddr = "http://127.0.0.1:8080" # smart-git组件地址
|
||||||
|
ForceH2C = false # 强制使用H2C连接
|
||||||
|
|
||||||
[pages]
|
[pages]
|
||||||
mode = "internal" # "internal" or "external" 内部/外部 前端 默认内部
|
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服务器开支及开发者时间成本支出,感谢您的支持!
|
如果您觉得本项目对您有帮助,欢迎赞助支持,您的赞助将用于Demo服务器开支及开发者时间成本支出,感谢您的支持!
|
||||||
|
|||||||
@@ -54,10 +54,12 @@ type HttpcConfig struct {
|
|||||||
[gitclone]
|
[gitclone]
|
||||||
mode = "bypass" # bypass / cache
|
mode = "bypass" # bypass / cache
|
||||||
smartGitAddr = ":8080"
|
smartGitAddr = ":8080"
|
||||||
|
ForceH2C = true
|
||||||
*/
|
*/
|
||||||
type GitCloneConfig struct {
|
type GitCloneConfig struct {
|
||||||
Mode string `toml:"mode"`
|
Mode string `toml:"mode"`
|
||||||
SmartGitAddr string `toml:"smartGitAddr"`
|
SmartGitAddr string `toml:"smartGitAddr"`
|
||||||
|
ForceH2C bool `toml:"ForceH2C"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ maxConnsPerHost = 0 # only for advanced mode
|
|||||||
[gitclone]
|
[gitclone]
|
||||||
mode = "bypass" # bypass / cache
|
mode = "bypass" # bypass / cache
|
||||||
smartGitAddr = "http://127.0.0.1:8080"
|
smartGitAddr = "http://127.0.0.1:8080"
|
||||||
|
ForceH2C = false
|
||||||
|
|
||||||
[pages]
|
[pages]
|
||||||
mode = "internal" # "internal" or "external"
|
mode = "internal" # "internal" or "external"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ maxConnsPerHost = 0 # only for advanced mode
|
|||||||
[gitclone]
|
[gitclone]
|
||||||
mode = "bypass" # bypass / cache
|
mode = "bypass" # bypass / cache
|
||||||
smartGitAddr = "http://127.0.0.1:8080"
|
smartGitAddr = "http://127.0.0.1:8080"
|
||||||
|
ForceH2C = false
|
||||||
|
|
||||||
[pages]
|
[pages]
|
||||||
mode = "internal" # "internal" or "external"
|
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
|
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)
|
||||||
|
|
||||||
logInfo("U:%s", u)
|
logDump("Url Before FMT:%s", u)
|
||||||
if cfg.GitClone.Mode == "cache" {
|
if cfg.GitClone.Mode == "cache" {
|
||||||
userPath, repoPath, remainingPath, err := extractParts(u)
|
userPath, repoPath, remainingPath, queryParams, err := extractParts(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleError(c, fmt.Sprintf("Failed to extract parts from URL: %v", err))
|
HandleError(c, fmt.Sprintf("Failed to extract parts from URL: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 构建新url
|
// 构建新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)
|
body, err := readRequestBody(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleError(c, err.Error())
|
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)
|
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 cfg.GitClone.Mode == "cache" {
|
||||||
if err != nil {
|
req, err := gitclient.NewRequest(method, u, bodyReader)
|
||||||
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
if err != nil {
|
||||||
return
|
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 resp.Body.Close()
|
||||||
defer func(Body io.ReadCloser) {
|
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 中提取所需的部分
|
// extractParts 从给定的 URL 中提取所需的部分
|
||||||
func extractParts(rawURL string) (string, string, string, error) {
|
func extractParts(rawURL string) (string, string, string, url.Values, error) {
|
||||||
// 解析 URL
|
// 解析 URL
|
||||||
parsedURL, err := url.Parse(rawURL)
|
parsedURL, err := url.Parse(rawURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return "", "", "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取路径部分并分割
|
// 获取路径部分并分割
|
||||||
@@ -136,7 +159,7 @@ func extractParts(rawURL string) (string, string, string, error) {
|
|||||||
|
|
||||||
// 提取所需的部分
|
// 提取所需的部分
|
||||||
if len(pathParts) < 3 {
|
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
|
// 提取 /WJQSERVER-STUDIO 和 /go-utils.git
|
||||||
@@ -149,5 +172,8 @@ func extractParts(rawURL string) (string, string, string, error) {
|
|||||||
remainingPath = "/" + remainingPath
|
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)
|
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
|
// 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)
|
repouser := fmt.Sprintf("%s/%s", username, repo)
|
||||||
|
|
||||||
// 白名单检查
|
// 白名单检查
|
||||||
|
|||||||
@@ -14,12 +14,17 @@ var BufferSize int = 32 * 1024 // 32KB
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
tr *http.Transport
|
tr *http.Transport
|
||||||
|
gittr *http.Transport
|
||||||
BufferPool *sync.Pool
|
BufferPool *sync.Pool
|
||||||
client *httpc.Client
|
client *httpc.Client
|
||||||
|
gitclient *httpc.Client
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitReq(cfg *config.Config) {
|
func InitReq(cfg *config.Config) {
|
||||||
initHTTPClient(cfg)
|
initHTTPClient(cfg)
|
||||||
|
if cfg.GitClone.Mode == "cache" {
|
||||||
|
initGitHTTPClient(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化固定大小的缓存池
|
// 初始化固定大小的缓存池
|
||||||
BufferPool = &sync.Pool{
|
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) {
|
func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches []string) (string, string) {
|
||||||
if gistMatches := gistRegex.FindStringSubmatch(rawPath); len(gistMatches) == 3 {
|
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], ""
|
return gistMatches[1], ""
|
||||||
}
|
}
|
||||||
// 定义路径
|
// 定义路径
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
// 日志模块
|
// 日志模块
|
||||||
var (
|
var (
|
||||||
logw = logger.Logw
|
logw = logger.Logw
|
||||||
LogDump = logger.LogDump
|
logDump = logger.LogDump
|
||||||
logDebug = logger.LogDebug
|
logDebug = logger.LogDebug
|
||||||
logInfo = logger.LogInfo
|
logInfo = logger.LogInfo
|
||||||
logWarning = logger.LogWarning
|
logWarning = logger.LogWarning
|
||||||
|
|||||||
Reference in New Issue
Block a user