From 6e787ced6e44b01aba79bb6661357368a5a5981b Mon Sep 17 00:00:00 2001 From: WJQSERVER <114663932+WJQSERVER@users.noreply.github.com> Date: Sun, 26 Jan 2025 16:24:37 +0800 Subject: [PATCH] 2.0.4 (#39) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RELEASE: v2.0.4正式版发布; - CHANGE: 优化GitReq的`HTTP Client`参数, 使其更符合本项目使用场景 - CHANGE: 优化Matches - REMOVE: 移除Caddyfile残留 - REMOVE: 由于v2改进后稳定性增强, 故移除健康检测 --- CHANGELOG.md | 20 ++++++ DEV-VERSION | 2 +- README.md | 1 + VERSION | 2 +- caddyfile/dev/Caddyfile | 102 ----------------------------- caddyfile/nocache/Caddyfile | 99 ---------------------------- caddyfile/release/Caddyfile | 103 ------------------------------ docker/dockerfile/dev/init.sh | 10 +-- docker/dockerfile/release/init.sh | 10 +-- init.sh | 9 +-- main.go | 6 +- proxy/chunkreq.go | 17 ++--- proxy/gitreq.go | 23 ++++++- proxy/matchrepo.go | 16 +++-- 14 files changed, 67 insertions(+), 353 deletions(-) delete mode 100644 caddyfile/dev/Caddyfile delete mode 100644 caddyfile/nocache/Caddyfile delete mode 100644 caddyfile/release/Caddyfile diff --git a/CHANGELOG.md b/CHANGELOG.md index b0e8403..7d1abbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # 更新日志 +2.0.4 +--- +- RELEASE: v2.0.4正式版发布; +- CHANGE: 优化GitReq的`HTTP Client`参数, 使其更符合本项目使用场景 +- CHANGE: 优化Matches +- REMOVE: 移除Caddyfile残留 +- REMOVE: 由于v2改进后稳定性增强, 故移除健康检测 + +25w08b +--- +- PRE-RELEASE: 此版本是v2.0.4的预发布版本,请勿在生产环境中使用; +- REMOVE: 由于v2改进后稳定性增强, 故移除健康检测 + +25w08a +--- +- PRE-RELEASE: 此版本是v2.0.4的预发布版本,请勿在生产环境中使用; +- CHANGE: 优化GitReq的`HTTP Client`参数, 使其更符合本项目使用场景 +- CHANGE: 优化Matches +- REMOVE: 移除Caddyfile残留 + 2.0.3 --- - RELEASE: v2.0.3正式版发布; diff --git a/DEV-VERSION b/DEV-VERSION index c721171..bf16ee9 100644 --- a/DEV-VERSION +++ b/DEV-VERSION @@ -1 +1 @@ -25w07b \ No newline at end of file +25w08b \ No newline at end of file diff --git a/README.md b/README.md index 77796fd..29aff6b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # GHProxy ![pull](https://img.shields.io/docker/pulls/wjqserver/ghproxy.svg) +![Docker Image Size (tag)](https://img.shields.io/docker/image-size/wjqserver/ghproxy/latest) [![Go Report Card](https://goreportcard.com/badge/github.com/WJQSERVER-STUDIO/ghproxy)](https://goreportcard.com/report/github.com/WJQSERVER-STUDIO/ghproxy) 使用Go实现的GHProxy,用于加速部分地区Github仓库的拉取,支持速率限制,用户鉴权,支持Docker部署 diff --git a/VERSION b/VERSION index 6acdb44..26e3379 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.3 \ No newline at end of file +2.0.4 \ No newline at end of file diff --git a/caddyfile/dev/Caddyfile b/caddyfile/dev/Caddyfile deleted file mode 100644 index cd58447..0000000 --- a/caddyfile/dev/Caddyfile +++ /dev/null @@ -1,102 +0,0 @@ -{ - debug - http_port 80 - https_port 443 - order cache before rewrite - cache { - cache_name GHProxyCache - } - log { - level INFO - output file /data/caddy/log/caddy.log { - roll_size 5MB - roll_keep 10 - } - } - servers :80 { - protocols h1 h2c - } -} - -(log) { - log { - format transform `{request>headers>X-Forwarded-For>[0]:request>remote_ip} - {user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}"` { - time_format "02/Jan/2006:15:04:05 -0700" - } - output file /data/caddy/log/{args[0]}/access.log { - roll_size 5MB - roll_keep 10 - roll_keep_for 24h - } - } -} - -(error_page) { - handle_errors { - rewrite * /{err.status_code}.html - root * /data/caddy/pages/errors - file_server - } -} - -(encode) { - encode { - zstd best - br 5 v2 - gzip 5 - minimum_length 512 - } -} - -(cache) { - cache { - allowed_http_verbs GET - stale {args[0]} - ttl {args[1]} - } -} - -(header_realip) { - header_up X-Real-IP {remote_host} - header_up X-Real-IP {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-For {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-Proto {http.request.header.CF-Visitor} -} - -(rate_limit) { - route /* { - rate_limit {remote.ip} {args[0]}r/m 10000 429 - } -} - -:80 { - reverse_proxy { - to 127.0.0.1:8080 - import header_realip - transport http { - versions 1.1 h2c - } - } - import log ghproxy - import cache 0s 300s - import error_page - import encode - import rate_limit 60 - route / { - root /data/www - file_server - import cache 0s 24h - } - route /favicon.ico { - root /data/www - file_server - import cache 0s 24h - } - - route /api* { - rate_limit {remote.ip} 15r/m 10000 429 - import cache 0s 6h - } -} - -import /data/caddy/config.d/* diff --git a/caddyfile/nocache/Caddyfile b/caddyfile/nocache/Caddyfile deleted file mode 100644 index b63e936..0000000 --- a/caddyfile/nocache/Caddyfile +++ /dev/null @@ -1,99 +0,0 @@ -{ - debug - http_port 80 - https_port 443 - order cache before rewrite - cache { - cache_name GHProxyCache - } - log { - level INFO - output file /data/caddy/log/caddy.log { - roll_size 5MB - roll_keep 10 - } - } - servers :80 { - protocols h1 h2c - } -} - - -(log) { - log { - format transform `{request>headers>X-Forwarded-For>[0]:request>remote_ip} - {user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}"` { - time_format "02/Jan/2006:15:04:05 -0700" - } - output file /data/caddy/log/{args[0]}/access.log { - roll_size 5MB - roll_keep 10 - roll_keep_for 24h - } - } -} - -(error_page) { - handle_errors { - rewrite * /{err.status_code}.html - root * /data/caddy/pages/errors - file_server - } -} - -(encode) { - encode { - zstd best - br 5 v2 - gzip 5 - minimum_length 256 - } -} - -(cache) { - cache { - allowed_http_verbs GET - stale {args[0]} - ttl {args[1]} - } -} - -(header_realip) { - header_up X-Real-IP {remote_host} - header_up X-Real-IP {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-For {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-Proto {http.request.header.CF-Visitor} -} - -(rate_limit) { - route /* { - rate_limit {remote.ip} {args[0]}r/m 10000 429 - } -} - -:80 { - reverse_proxy { - to h2c://127.0.0.1:8080 - import header_realip - } - import log ghproxy - import error_page - import encode - import rate_limit 60 - route / { - root /data/www - file_server - import cache 300s - } - route /favicon.ico { - root /data/www - file_server - import cache 300s - } - - route /api* { - rate_limit {remote.ip} 15r/m 10000 429 - import cache 300s - } -} - -import /data/caddy/config.d/* diff --git a/caddyfile/release/Caddyfile b/caddyfile/release/Caddyfile deleted file mode 100644 index 962fa39..0000000 --- a/caddyfile/release/Caddyfile +++ /dev/null @@ -1,103 +0,0 @@ -{ - debug - http_port 80 - https_port 443 - order cache before rewrite - cache { - cache_name GHProxyCache - } - log { - level INFO - output file /data/caddy/log/caddy.log { - roll_size 5MB - roll_keep 10 - } - } - servers :80 { - protocols h1 h2c - } -} - -(log) { - log { - format transform `{request>headers>X-Forwarded-For>[0]:request>remote_ip} - {user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}"` { - time_format "02/Jan/2006:15:04:05 -0700" - } - output file /data/caddy/log/{args[0]}/access.log { - roll_size 5MB - roll_keep 10 - roll_keep_for 24h - } - } -} - -(error_page) { - handle_errors { - rewrite * /{err.status_code}.html - root * /data/caddy/pages/errors - file_server - } -} - -(encode) { - encode { - zstd best - br 5 v2 - gzip 5 - minimum_length 512 - } -} - -(cache) { - cache { - allowed_http_verbs GET - stale {args[0]} - ttl {args[1]} - } -} - -(header_realip) { - header_up X-Real-IP {remote_host} - header_up X-Real-IP {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-For {http.request.header.CF-Connecting-IP} - header_up X-Forwarded-Proto {http.request.header.CF-Visitor} -} - -(rate_limit) { - route /* { - rate_limit {remote.ip} {args[0]}r/m 10000 429 - } -} - -:80 { - reverse_proxy { - to 127.0.0.1:8080 - import header_realip - transport http { - versions 1.1 h2c - } - } - import log ghproxy - import cache 0s 300s - import error_page - import encode - import rate_limit 60 - route / { - root /data/www - file_server - import cache 0s 24h - } - route /favicon.ico { - root /data/www - file_server - import cache 0s 24h - - } - - route /api* { - rate_limit {remote.ip} 15r/m 10000 429 - import cache 0s 6h - } -} - -import /data/caddy/config.d/* diff --git a/docker/dockerfile/dev/init.sh b/docker/dockerfile/dev/init.sh index 21c150a..f9a10e7 100644 --- a/docker/dockerfile/dev/init.sh +++ b/docker/dockerfile/dev/init.sh @@ -14,12 +14,4 @@ if [ ! -f /data/${APPLICATION}/config/config.toml ]; then cp /data/${APPLICATION}/config.toml /data/${APPLICATION}/config/config.toml fi -/data/${APPLICATION}/${APPLICATION} -cfg /data/${APPLICATION}/config/config.toml > /data/${APPLICATION}/log/run.log 2>&1 & - -sleep 30 - -while [[ true ]]; do - # Failure Circuit Breaker - curl -f --max-time 5 -retry 3 http://127.0.0.1:8080/api/healthcheck || exit 1 - sleep 120 -done \ No newline at end of file +/data/${APPLICATION}/${APPLICATION} -cfg /data/${APPLICATION}/config/config.toml > /data/${APPLICATION}/log/run.log 2>&1 diff --git a/docker/dockerfile/release/init.sh b/docker/dockerfile/release/init.sh index 0555c6f..f9a10e7 100644 --- a/docker/dockerfile/release/init.sh +++ b/docker/dockerfile/release/init.sh @@ -14,12 +14,4 @@ if [ ! -f /data/${APPLICATION}/config/config.toml ]; then cp /data/${APPLICATION}/config.toml /data/${APPLICATION}/config/config.toml fi -/data/${APPLICATION}/${APPLICATION} -cfg /data/${APPLICATION}/config/config.toml > /data/${APPLICATION}/log/run.log 2>&1 & - -sleep 30 - -while [[ true ]]; do - # Failure Circuit Breaker - curl -f --max-time 5 -retry 3 http://127.0.0.1:8080/api/healthcheck || exit 1 - sleep 120 -done \ No newline at end of file +/data/${APPLICATION}/${APPLICATION} -cfg /data/${APPLICATION}/config/config.toml > /data/${APPLICATION}/log/run.log 2>&1 diff --git a/init.sh b/init.sh index 70da645..601185f 100644 --- a/init.sh +++ b/init.sh @@ -14,12 +14,5 @@ if [ ! -f /data/${APPLICATON}/config/config.yaml ]; then cp /data/${APPLICATON}/config.yaml /data/${APPLICATON}/config/config.yaml fi -/data/${APPLICATON}/${APPLICATON} > /data/${APPLICATON}/log/run.log 2>&1 & - -sleep 30 - -while [[ true ]]; do - curl -f http://localhost:8080/api/healthcheck || exit 1 - sleep 120 -done +/data/${APPLICATON}/${APPLICATON} > /data/${APPLICATON}/log/run.log 2>&1 diff --git a/main.go b/main.go index 4cd1968..d7e5c6d 100644 --- a/main.go +++ b/main.go @@ -91,8 +91,8 @@ func setupRateLimit(cfg *config.Config) { } } -func InitChunkedReq() { - proxy.InitChunkedReq(cfg.Server.BufferSize) +func InitReq() { + proxy.InitReq(cfg.Server.BufferSize) } func init() { @@ -100,7 +100,7 @@ func init() { flag.Parse() loadConfig() setupLogger(cfg) - InitChunkedReq() + InitReq() loadlist(cfg) setupRateLimit(cfg) diff --git a/proxy/chunkreq.go b/proxy/chunkreq.go index e94f5ec..02893b9 100644 --- a/proxy/chunkreq.go +++ b/proxy/chunkreq.go @@ -14,13 +14,14 @@ import ( var chunkedBufferSize int var ( - client *http.Client - tr *http.Transport + cclient *http.Client + ctr *http.Transport ) -func InitChunkedReq(cfgBufferSize int) { +func InitReq(cfgBufferSize int) { initChunkedBufferSize(cfgBufferSize) initChunkedHTTPClient() + initGitHTTPClient() } func initChunkedBufferSize(cfgBufferSize int) { @@ -32,13 +33,13 @@ func initChunkedBufferSize(cfgBufferSize int) { } func initChunkedHTTPClient() { - tr = &http.Transport{ + ctr = &http.Transport{ MaxIdleConns: 100, MaxConnsPerHost: 60, IdleConnTimeout: 20 * time.Second, } - client = &http.Client{ - Transport: tr, + cclient = &http.Client{ + Transport: ctr, } } @@ -59,7 +60,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri removeWSHeader(headReq) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头) AuthPassThrough(c, cfg, headReq) - headResp, err := client.Do(headReq) + headResp, err := cclient.Do(headReq) if err != nil { HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) return @@ -91,7 +92,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri removeWSHeader(req) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头) AuthPassThrough(c, cfg, req) - resp, err := client.Do(req) + resp, err := cclient.Do(req) if err != nil { HandleError(c, fmt.Sprintf("发送请求失败: %v", err)) return diff --git a/proxy/gitreq.go b/proxy/gitreq.go index a429c6c..424951e 100644 --- a/proxy/gitreq.go +++ b/proxy/gitreq.go @@ -6,16 +6,33 @@ import ( "ghproxy/config" "io" "net/http" + "time" "github.com/gin-gonic/gin" ) +var ( + gclient *http.Client + gtr *http.Transport +) + +func initGitHTTPClient() { + gtr = &http.Transport{ + MaxIdleConns: 30, + MaxConnsPerHost: 30, + IdleConnTimeout: 30 * time.Second, + } + gclient = &http.Client{ + Transport: gtr, + } +} + func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) { method := c.Request.Method logInfo("%s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto) // 创建HTTP客户端 - client := &http.Client{} + //client := &http.Client{} // 发送HEAD请求, 预获取Content-Length headReq, err := http.NewRequest("HEAD", u, nil) @@ -26,7 +43,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s setRequestHeaders(c, headReq) AuthPassThrough(c, cfg, headReq) - headResp, err := client.Do(headReq) + headResp, err := gclient.Do(headReq) if err != nil { HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) return @@ -55,7 +72,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s setRequestHeaders(c, req) AuthPassThrough(c, cfg, req) - resp, err := client.Do(req) + resp, err := gclient.Do(req) if err != nil { HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) return diff --git a/proxy/matchrepo.go b/proxy/matchrepo.go index 58090c7..5a5d000 100644 --- a/proxy/matchrepo.go +++ b/proxy/matchrepo.go @@ -9,17 +9,19 @@ import ( "github.com/gin-gonic/gin" ) +// 预定义regex +var ( + pathRegex = regexp.MustCompile(`^([^/]+)/([^/]+)/([^/]+)/.*`) // 匹配路径 + gistRegex = regexp.MustCompile(`^(?:https?://)?gist\.github(?:usercontent|)\.com/([^/]+)/([^/]+)/.*`) // 匹配gist路径 +) + // 提取用户名和仓库名 func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches []string) (string, string) { - var gistregex = regexp.MustCompile(`^(?:https?://)?gist\.github(?:usercontent|)\.com/([^/]+)/([^/]+)/.*`) - var gistmatches []string - if gistregex.MatchString(rawPath) { - gistmatches = gistregex.FindStringSubmatch(rawPath) - logInfo("%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], "" + if gistMatches := gistRegex.FindStringSubmatch(rawPath); len(gistMatches) == 3 { + logInfo("%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], "" } // 定义路径 - pathRegex := regexp.MustCompile(`^([^/]+)/([^/]+)/([^/]+)/.*`) if pathMatches := pathRegex.FindStringSubmatch(matches[2]); len(pathMatches) >= 4 { return pathMatches[2], pathMatches[3] }