Compare commits

..

15 Commits

Author SHA1 Message Date
WJQSERVER
060453f070 Merge pull request #88 from WJQSERVER-STUDIO/dev
3.0.2
2025-04-15 13:26:46 +08:00
wjqserver
f110c96c1f update readme 2025-04-15 13:22:51 +08:00
wjqserver
73aac79c1b 3.0.2 2025-04-15 13:14:53 +08:00
wjqserver
bed6c486dc 25w28b 2025-04-15 10:23:29 +08:00
wjqserver
ab77c5c7da 25w28a 2025-04-14 12:27:12 +08:00
wjqserver
bf21bd197a 25w28t-2 2025-04-11 07:29:03 +08:00
wjqserver
8af107c584 update for touka-httpc 0.4.0 2025-04-11 07:24:50 +08:00
wjqserver
d6d54b222f dix auth checker 2025-04-10 23:07:48 +08:00
wjqserver
005a4543d4 update deps 2025-04-10 23:07:13 +08:00
wjqserver
a85eb38de5 update deps 2025-04-08 20:50:55 +08:00
里見 灯花
152fb8aa71 Merge pull request #84 from WJQSERVER-STUDIO/dev
3.0.1
2025-04-08 20:49:27 +08:00
wjqserver
3e9e43cd44 3.0.1 2025-04-08 20:48:56 +08:00
wjqserver
8a50b311fc 25w27a 2025-04-07 18:51:22 +08:00
wjqserver
dcc50401c4 update deps 2025-04-07 18:34:00 +08:00
wjqserver
d62a1f9769 [docs] update config docs 2025-04-07 18:33:47 +08:00
17 changed files with 215 additions and 104 deletions

View File

@@ -1,5 +1,49 @@
# 更新日志
3.0.2 - 2025-04-15
---
- CHANGE: 避免重复的re编译操作
- CHANGE: 去除不必要的请求
- CHANGE: 改进`httpc`相关配置
- CHANGE: 更新`httpc` 0.4.0
- CHANGE: 为不遵守`RFC 2616`, `RFC 9112`的客户端带来兼容性改进
25w28b - 2025-04-15
---
- PRE-RELEASE: 此版本是v3.0.2预发布版本,请勿在生产环境中使用;
- CHANGE: 改进resp关闭
- CHANGE: 避免重复的re编译操作
25w28a - 2025-04-14
---
- PRE-RELEASE: 此版本是预发布版本,请勿在生产环境中使用;
- CHANGE: 去除不必要的请求
- CHANGE: 改进`httpc`相关配置
- CHANGE: 合入test版本修改
25w28t-2 - 2025-04-11
---
- TEST: 测试验证版本
- CHANGE: 为不遵守`RFC 2616`, `RFC 9112`的客户端带来兼容性改进
25w28t-1 - 2025-04-11
---
- TEST: 测试验证版本
- CHANGE: 更新httpc 0.4.0
3.0.1 - 2025-04-08
---
- CHANGE: 加入`memLimit`指示gc
- CHANGE: 加入`hlog`输出路径配置
- CHANGE: 修正H2C配置问题
25w27a - 2025-04-07
---
- PRE-RELEASE: 此版本是v3.0.1的预发布版本,请勿在生产环境中使用;
- CHANGE: 加入`memLimit`指示gc
- CHANGE: 加入`hlog`输出路径配置
- CHANGE: 修正H2C配置问题
3.0.0 - 2025-04-04
---
- RELEASE: Next Gen; 下一个起点;

View File

@@ -1 +1 @@
25w26a
25w28b

View File

@@ -2,7 +2,7 @@
![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部署
支持 Git clone、raw、releases的 Github 加速项目, 支持自托管的同时带来卓越的性能与极低的资源占用(Golang和HertZ带来的优势), 同时支持多种额外功能
## 项目说明
@@ -16,9 +16,10 @@
- 🚫 **支持自定义黑名单/白名单**
- 🗄️ **支持 Git Clone 缓存(配合 [Smart-Git](https://github.com/WJQSERVER-STUDIO/smart-git)**
- 🐳 **支持 Docker 部署**
- 🐳 **支持自托管**
-**支持速率限制**
- 🔒 **支持用户鉴权**
- 🐚 **支持 shell 脚本嵌套加速**
- 🐚 **支持 shell 脚本多层嵌套加速**
### 项目相关

View File

@@ -1 +1 @@
3.0.0
3.0.2

View File

@@ -32,6 +32,7 @@ type ServerConfig struct {
Port int `toml:"port"`
Host string `toml:"host"`
SizeLimit int `toml:"sizeLimit"`
MemLimit int64 `toml:"memLimit"`
H2C bool `toml:"H2C"`
Cors string `toml:"cors"`
Debug bool `toml:"debug"`
@@ -86,9 +87,10 @@ type PagesConfig struct {
}
type LogConfig struct {
LogFilePath string `toml:"logFilePath"`
MaxLogSize int `toml:"maxLogSize"`
Level string `toml:"level"`
LogFilePath string `toml:"logFilePath"`
MaxLogSize int `toml:"maxLogSize"`
Level string `toml:"level"`
HertZLogPath string `toml:"hertzLogPath"`
}
/*
@@ -179,6 +181,7 @@ func DefaultConfig() *Config {
Port: 8080,
Host: "0.0.0.0",
SizeLimit: 125,
MemLimit: 0,
H2C: true,
Cors: "*",
Debug: false,
@@ -204,9 +207,10 @@ func DefaultConfig() *Config {
StaticDir: "/data/www",
},
Log: LogConfig{
LogFilePath: "/data/ghproxy/log/ghproxy.log",
MaxLogSize: 10,
Level: "info",
LogFilePath: "/data/ghproxy/log/ghproxy.log",
MaxLogSize: 10,
Level: "info",
HertZLogPath: "/data/ghproxy/log/hertz.log",
},
Auth: AuthConfig{
Enabled: false,

View File

@@ -2,6 +2,7 @@
host = "0.0.0.0"
port = 8080
sizeLimit = 125 # MB
memLimit = 0 # MB
H2C = true
cors = "*" # "*"/"" -> "*" ; "nil" -> "" ;
debug = false
@@ -30,6 +31,7 @@ staticDir = "/data/www"
logFilePath = "/data/ghproxy/log/ghproxy.log"
maxLogSize = 5 # MB
level = "info" # dump, debug, info, warn, error, none
hertzLogPath = "/data/ghproxy/log/hertz.log"
[auth]
method = "parameters" # "header" or "parameters"

View File

@@ -2,6 +2,7 @@
host = "127.0.0.1"
port = 8080
sizeLimit = 125 # MB
memLimit = 0 # MB
H2C = true
cors = "*" # "*"/"" -> "*" ; "nil" -> "" ;
debug = false
@@ -30,6 +31,7 @@ staticDir = "/usr/local/ghproxy/pages"
logFilePath = "/usr/local/ghproxy/log/ghproxy.log"
maxLogSize = 5 # MB
level = "info" # dump, debug, info, warn, error, none
hertzLogPath = "/usr/local/ghproxy/log/hertz.log"
[auth]
authMethod = "parameters" # "header" or "parameters"

View File

@@ -13,6 +13,7 @@
host = "0.0.0.0"
port = 8080
sizeLimit = 125 # MB
memLimit = 0 # MB
H2C = true
cors = "*" # "*"/"" -> "*" ; "nil" -> "" ;
debug = false
@@ -41,6 +42,7 @@ staticDir = "/data/www"
logFilePath = "/data/ghproxy/log/ghproxy.log"
maxLogSize = 5 # MB
level = "info" # dump, debug, info, warn, error, none
hertzLogPath = "/data/ghproxy/log/hertz.log"
[auth]
method = "parameters" # "header" or "parameters"
@@ -85,6 +87,10 @@ url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
* 类型: 整数 (`int`)
* 默认值: `125` (MB)
* 说明: 限制允许接收的请求体最大大小,单位为 MB。用于防止过大的请求导致服务压力过大。
* `memLimit`: `runtime`内存限制
* 类型: 整数 (`int64`)
* 默认值: `0` (不传入)
* 说明: 给`runtime`的指标, 让gc行为更高效
* `H2C`: 是否启用 H2C (HTTP/2 Cleartext) 传输。
* 类型: 布尔值 (`bool`)
* 默认值: `true` (启用)
@@ -193,6 +199,10 @@ url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
* `"warn"`: 输出警告和错误日志。
* `"error"`: 仅输出错误日志。
* `"none"`: 禁用所有日志输出。
* `hertzLogPath`: `HertZ`日志文件路径。
* 类型: 字符串 (`string`)
* 默认值: `"/data/ghproxy/log/hertz.log"`
* 说明: 设置 `HertZ` 日志文件的存储路径。
* **`[auth]` - 认证配置**

16
go.mod
View File

@@ -5,10 +5,10 @@ go 1.24.2
require (
github.com/BurntSushi/toml v1.5.0
github.com/WJQSERVER-STUDIO/go-utils/logger v1.5.0
github.com/cloudwego/hertz v0.9.6
github.com/cloudwego/hertz v0.9.7
github.com/hertz-contrib/http2 v0.1.8
github.com/satomitouka/touka-httpc v0.3.3
golang.org/x/net v0.38.0
github.com/satomitouka/touka-httpc v0.4.0
golang.org/x/net v0.39.0
golang.org/x/time v0.11.0
)
@@ -21,7 +21,7 @@ require (
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cloudwego/gopkg v0.1.4 // indirect
github.com/cloudwego/netpoll v0.7.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/nyaruka/phonenumbers v1.6.0 // indirect
@@ -30,9 +30,9 @@ require (
github.com/tidwall/pretty v1.2.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/arch v0.15.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/arch v0.16.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
)

32
go.sum
View File

@@ -20,8 +20,8 @@ github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCy
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/gopkg v0.1.4 h1:EoQiCG4sTonTPHxOGE0VlQs+sQR+Hsi2uN0qqwu8O50=
github.com/cloudwego/gopkg v0.1.4/go.mod h1:FQuXsRWRsSqJLsMVd5SYzp8/Z1y5gXKnVvRrWUOsCMI=
github.com/cloudwego/hertz v0.9.6 h1:Kj5SSPlKBC32NIN7+B/tt8O1pdDz8brMai00rqqjULQ=
github.com/cloudwego/hertz v0.9.6/go.mod h1:X5Ez52XhtszU4t+CTBGIJI4PqmcI1oSf8ULBz0SWfLo=
github.com/cloudwego/hertz v0.9.7 h1:tAVaiO+vTf+ZkQhvNhKbDJ0hmC4oJ7bzwDi1KhvhHy4=
github.com/cloudwego/hertz v0.9.7/go.mod h1:t6d7NcoQxPmETvzPMMIVPHMn5C5QzpqIiFsaavoLJYQ=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cloudwego/netpoll v0.7.0 h1:bDrxQaNfijRI1zyGgXHQoE/nYegL0nr+ijO1Norelc4=
github.com/cloudwego/netpoll v0.7.0/go.mod h1:PI+YrmyS7cIr0+SD4seJz3Eo3ckkXdu2ZVKBLhURLNU=
@@ -29,8 +29,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
@@ -50,8 +50,8 @@ github.com/nyaruka/phonenumbers v1.6.0 h1:r9ax45fFg+YLUs2X4bNXm5RAxWl00hYjFgNlv3
github.com/nyaruka/phonenumbers v1.6.0/go.mod h1:7gjs+Lchqm49adhAKB5cdcng5ZXgt6x7Jgvi0ZorUtU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satomitouka/touka-httpc v0.3.3 h1:Th0uJ5do3oqqZgdUDtqD1SH11x8TcJmrwHMJQlEIKCg=
github.com/satomitouka/touka-httpc v0.3.3/go.mod h1:sNXyW5XBufkwB9ZJ+PIlgN/6xiJ7aZV1fWGrXR0u3bA=
github.com/satomitouka/touka-httpc v0.4.0 h1:cnOONdyJHJImMY8L64bvYF+7Ow/5CPf2Yr3RQRRMZOU=
github.com/satomitouka/touka-httpc v0.4.0/go.mod h1:sNXyW5XBufkwB9ZJ+PIlgN/6xiJ7aZV1fWGrXR0u3bA=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@@ -81,14 +81,14 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -98,8 +98,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -113,8 +113,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -127,8 +127,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

59
main.go
View File

@@ -8,6 +8,7 @@ import (
"io/fs"
"net/http"
"os"
"runtime/debug"
"time"
"ghproxy/api"
@@ -23,7 +24,9 @@ import (
"github.com/cloudwego/hertz/pkg/app/middlewares/server/recovery"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/adaptor"
"github.com/cloudwego/hertz/pkg/common/hlog"
//"github.com/cloudwego/hertz/pkg/network/standard"
"github.com/hertz-contrib/http2/factory"
)
@@ -31,6 +34,7 @@ var (
cfg *config.Config
r *server.Hertz
configfile = "/data/ghproxy/config/config.toml"
hertZfile *os.File
cfgfile string
version string
runMode string
@@ -129,7 +133,30 @@ func setupLogger(cfg *config.Config) {
fmt.Printf("Log Level: %s\n", cfg.Log.Level)
logDebug("Config File Path: ", cfgfile)
logDebug("Loaded config: %v\n", cfg)
logInfo("Init Completed")
logInfo("Logger Initialized Successfully")
}
func setupHertZLogger(cfg *config.Config) {
var err error
if cfg.Log.HertZLogPath != "" {
hertZfile, err = os.OpenFile(cfg.Log.HertZLogPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
hlog.SetOutput(os.Stdout)
logWarning("Failed to open hertz log file: %v", err)
} else {
hlog.SetOutput(hertZfile)
}
hlog.SetLevel(hlog.LevelInfo)
}
}
func setMemLimit(cfg *config.Config) {
if cfg.Server.MemLimit > 0 {
debug.SetMemoryLimit((cfg.Server.MemLimit) * 1024 * 1024)
logInfo("Set Memory Limit to %d MB", cfg.Server.MemLimit)
}
}
func loadlist(cfg *config.Config) {
@@ -214,7 +241,6 @@ func setupPages(cfg *config.Config, r *server.Hertz) {
r.StaticFile("/style.css", stylesheetsPath)
r.StaticFile("/bootstrap.min.css", bootstrapPath)
r.StaticFile("/bootstrap.bundle.min.js", bootstrapBundlePath)
//router.StaticFile("/bootstrap.min.css", bootstrapPath)
default:
// 处理无效的Pages Mode
@@ -315,7 +341,9 @@ func init() {
loadConfig()
if cfg != nil { // 在setupLogger前添加空值检查
setupLogger(cfg)
setupHertZLogger(cfg)
InitReq(cfg)
setMemLimit(cfg)
loadlist(cfg)
setupRateLimit(cfg)
@@ -346,12 +374,19 @@ func main() {
addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
r := server.New(
server.WithHostPorts(addr),
server.WithH2C(true),
)
r.AddProtocol("h2", factory.NewServerFactory())
if cfg.Server.H2C {
r = server.New(
server.WithHostPorts(addr),
server.WithH2C(true),
// server.WithALPN(true),
// server.WithTransport(standard.NewTransporter),
)
r.AddProtocol("h2", factory.NewServerFactory())
} else {
r = server.New(
server.WithHostPorts(addr),
)
}
// 添加Recovery中间件
r.Use(recovery.Recovery())
@@ -414,5 +449,13 @@ func main() {
r.Spin()
defer logger.Close()
defer func() {
if hertZfile != nil {
err := hertZfile.Close()
if err != nil {
logError("Failed to close hertz log file: %v", err)
}
}
}()
fmt.Println("Program Exit")
}

View File

@@ -5,7 +5,6 @@ import (
"context"
"fmt"
"ghproxy/config"
"io"
"net/http"
"strconv"
@@ -15,39 +14,6 @@ import (
func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, cfg *config.Config, matcher string) {
method := c.Request.Method
// 发送HEAD请求, 预获取Content-Length
headReq, err := client.NewRequest("HEAD", u, nil)
if err != nil {
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return
}
setRequestHeaders(c, headReq)
removeWSHeader(headReq) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头)
AuthPassThrough(c, cfg, headReq)
headResp, err := client.Do(headReq)
if err != nil {
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
return
}
defer func(Body io.ReadCloser) {
if err := Body.Close(); err != nil {
logError("Failed to close response body: %v", err)
}
}(headResp.Body)
contentLength := headResp.Header.Get("Content-Length")
sizelimit := cfg.Server.SizeLimit * 1024 * 1024
if contentLength != "" {
size, err := strconv.Atoi(contentLength)
if err == nil && size > sizelimit {
finalURL := headResp.Request.URL.String()
c.Redirect(http.StatusMovedPermanently, []byte(finalURL))
logWarning("%s %s %s %s %s Final-URL: %s Size-Limit-Exceeded: %d", c.ClientIP(), c.Method(), c.Path(), c.Request.Header.Get("User-Agent"), c.Request.Header.GetProtocol(), finalURL, size)
return
}
}
body := c.Request.Body()
bodyReader := bytes.NewBuffer(body)
@@ -69,17 +35,33 @@ func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, c
// 错误处理(404)
if resp.StatusCode == 404 {
c.String(http.StatusNotFound, "File Not Found")
//c.String(http.StatusNotFound, "File Not Found")
c.Status(http.StatusNotFound)
return
}
var (
bodySize int
contentLength string
sizelimit int
)
sizelimit = cfg.Server.SizeLimit * 1024 * 1024
contentLength = resp.Header.Get("Content-Length")
if contentLength != "" {
size, err := strconv.Atoi(contentLength)
if err == nil && size > sizelimit {
var err error
bodySize, err = strconv.Atoi(contentLength)
if err != nil {
logWarning("%s %s %s %s %s Content-Length header is not a valid integer: %v", c.ClientIP(), c.Method(), c.Path(), c.UserAgent(), c.Request.Header.GetProtocol(), err)
bodySize = -1
}
if err == nil && bodySize > sizelimit {
finalURL := resp.Request.URL.String()
err := resp.Body.Close()
if err != nil {
logError("Failed to close response body: %v", err)
}
c.Redirect(http.StatusMovedPermanently, []byte(finalURL))
logWarning("%s %s %s %s %s Final-URL: %s Size-Limit-Exceeded: %d", c.ClientIP(), c.Method(), c.Path(), c.UserAgent(), c.Request.Header.GetProtocol(), finalURL, size)
logWarning("%s %s %s %s %s Final-URL: %s Size-Limit-Exceeded: %d", c.ClientIP(), c.Method(), c.Path(), c.UserAgent(), c.Request.Header.GetProtocol(), finalURL, bodySize)
return
}
}
@@ -131,6 +113,10 @@ func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, c
return
}
} else {
if contentLength != "" {
c.SetBodyStream(resp.Body, bodySize)
return
}
c.SetBodyStream(resp.Body, -1)
}

View File

@@ -72,6 +72,9 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
if contentLength != "" {
size, err := strconv.Atoi(contentLength)
sizelimit := cfg.Server.SizeLimit * 1024 * 1024
if err != nil {
logWarning("%s %s %s %s %s Content-Length header is not a valid integer: %v", c.ClientIP(), c.Method(), c.Path(), c.UserAgent(), c.Request.Header.GetProtocol(), err)
}
if err == nil && size > sizelimit {
finalURL := []byte(resp.Request.URL.String())
c.Redirect(http.StatusMovedPermanently, finalURL)

View File

@@ -106,13 +106,15 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
}
// 鉴权
var authcheck bool
authcheck, err = auth.AuthHandler(ctx, c, cfg)
if !authcheck {
//c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
c.AbortWithStatusJSON(401, map[string]string{"error": "Unauthorized"})
logWarning("%s %s %s %s %s Auth-Error: %v", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), err)
return
if cfg.Auth.Enabled {
var authcheck bool
authcheck, err = auth.AuthHandler(ctx, c, cfg)
if !authcheck {
//c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
c.AbortWithStatusJSON(401, map[string]string{"error": "Unauthorized"})
logWarning("%s %s %s %s %s Auth-Error: %v", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), err)
return
}
}
// IP METHOD URL USERAGENT PROTO MATCHES

View File

@@ -42,7 +42,6 @@ func initHTTPClient(cfg *config.Config) {
if cfg.Httpc.Mode == "auto" {
tr = &http.Transport{
//MaxIdleConns: 160,
IdleConnTimeout: 30 * time.Second,
WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB
@@ -64,7 +63,6 @@ func initHTTPClient(cfg *config.Config) {
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
@@ -87,23 +85,11 @@ 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{
@@ -112,7 +98,6 @@ func initGitHTTPClient(cfg *config.Config) {
MaxIdleConnsPerHost: cfg.Httpc.MaxIdleConnsPerHost,
WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB
Protocols: proTolcols,
}
} else {
// 错误的模式
@@ -130,14 +115,39 @@ func initGitHTTPClient(cfg *config.Config) {
if cfg.Outbound.Enabled {
initTransport(cfg, gittr)
}
if cfg.Server.Debug {
if cfg.Server.Debug && cfg.GitClone.ForceH2C {
gitclient = httpc.New(
httpc.WithTransport(gittr),
httpc.WithDumpLog(),
httpc.WithProtocols(httpc.ProtocolsConfig{
ForceH2C: true,
}),
)
} else if !cfg.Server.Debug && cfg.GitClone.ForceH2C {
gitclient = httpc.New(
httpc.WithTransport(gittr),
httpc.WithProtocols(httpc.ProtocolsConfig{
ForceH2C: true,
}),
)
} else if cfg.Server.Debug && !cfg.GitClone.ForceH2C {
gitclient = httpc.New(
httpc.WithTransport(gittr),
httpc.WithDumpLog(),
httpc.WithProtocols(httpc.ProtocolsConfig{
Http1: true,
Http2: true,
Http2_Cleartext: true,
}),
)
} else {
gitclient = httpc.New(
httpc.WithTransport(gittr),
httpc.WithProtocols(httpc.ProtocolsConfig{
Http1: true,
Http2: true,
Http2_Cleartext: true,
}),
)
}
}

View File

@@ -243,6 +243,8 @@ func extractParts(rawURL string) (string, string, string, url.Values, error) {
return repoOwner, repoName, remainingPath, queryParams, nil
}
var urlPattern = regexp.MustCompile(`https?://[^\s'"]+`)
// processLinks 处理链接,返回包含处理后数据的 io.Reader
func processLinks(input io.Reader, compress string, host string, cfg *config.Config) (readerOut io.Reader, written int64, err error) {
pipeReader, pipeWriter := io.Pipe() // 创建 io.Pipe
@@ -315,7 +317,6 @@ func processLinks(input io.Reader, compress string, host string, cfg *config.Con
}()
// 使用正则表达式匹配 http 和 https 链接
urlPattern := regexp.MustCompile(`https?://[^\s'"]+`)
for {
line, readErr := bufReader.ReadString('\n')
if readErr != nil {

View File

@@ -13,6 +13,9 @@ func setRequestHeaders(c *app.RequestContext, req *http.Request) {
})
}
// removeWSHeader removes the "Upgrade" and "Connection" headers from the given
// Request, which are added by the client when it wants to upgrade the
// connection to a WebSocket connection.
func removeWSHeader(req *http.Request) {
req.Header.Del("Upgrade")
req.Header.Del("Connection")