diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d2b774..7fd3f06 100644 --- a/CHANGELOG.md +++ b/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: 重构路由匹配 diff --git a/DEV-VERSION b/DEV-VERSION index bfc48ec..08b9602 100644 --- a/DEV-VERSION +++ b/DEV-VERSION @@ -1 +1 @@ -25w17a \ No newline at end of file +25w18a \ No newline at end of file diff --git a/README.md b/README.md index 0e4c6bb..b28b954 100644 --- a/README.md +++ b/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主题 ![ghproxy-demo.png](https://webp.wjqserver.com/ghproxy/1.8.1-light.png) ![ghproxy-demo-dark.png](https://webp.wjqserver.com/ghproxy/1.8.1-dark.png) +#### Nebula主题 +![nebula-dark-v2.3.0.png](https://webp.wjqserver.com/ghproxy/nebula-dark.png) +![nebula-light-v2.3.0.png](https://webp.wjqserver.com/ghproxy/nebula-light.png) + ## 赞助 如果您觉得本项目对您有帮助,欢迎赞助支持,您的赞助将用于Demo服务器开支及开发者时间成本支出,感谢您的支持! diff --git a/VERSION b/VERSION index 58073ef..acdc3f1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.4.1 \ No newline at end of file +2.4.2 \ No newline at end of file diff --git a/config/config.go b/config/config.go index 2d55564..9724212 100644 --- a/config/config.go +++ b/config/config.go @@ -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"` } /* diff --git a/config/config.toml b/config/config.toml index 7b07a17..b539ce0 100644 --- a/config/config.toml +++ b/config/config.toml @@ -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" diff --git a/deploy/config.toml b/deploy/config.toml index e61fdd0..ff41325 100644 --- a/deploy/config.toml +++ b/deploy/config.toml @@ -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" diff --git a/proxy/gitreq.go b/proxy/gitreq.go index 5bf432b..551ab87 100644 --- a/proxy/gitreq.go +++ b/proxy/gitreq.go @@ -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 } diff --git a/proxy/handler.go b/proxy/handler.go index 9d05903..2730226 100644 --- a/proxy/handler.go +++ b/proxy/handler.go @@ -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) // 白名单检查 diff --git a/proxy/httpc.go b/proxy/httpc.go index e1ac2ea..4b78fc8 100644 --- a/proxy/httpc.go +++ b/proxy/httpc.go @@ -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), + ) + } +} diff --git a/proxy/matchrepo.go b/proxy/matchrepo.go index a074193..b69627e 100644 --- a/proxy/matchrepo.go +++ b/proxy/matchrepo.go @@ -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], "" } // 定义路径 diff --git a/proxy/proxy.go b/proxy/proxy.go index cd1233e..cee0b87 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -12,7 +12,7 @@ import ( // 日志模块 var ( logw = logger.Logw - LogDump = logger.LogDump + logDump = logger.LogDump logDebug = logger.LogDebug logInfo = logger.LogInfo logWarning = logger.LogWarning