Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92c4c62b46 | ||
|
|
545144c7b5 | ||
|
|
866638ba8e | ||
|
|
e3fd604945 | ||
|
|
90709539f4 | ||
|
|
1011a25d16 | ||
|
|
bd63ed3070 | ||
|
|
3c11e9826e | ||
|
|
ef3b1bf1f0 | ||
|
|
ad4d8eb670 | ||
|
|
030f0d12a9 | ||
|
|
e57432a01c | ||
|
|
ace795fe9d | ||
|
|
3f51e5319a | ||
|
|
55769d9a40 | ||
|
|
7eb312243c | ||
|
|
6ca31bc252 | ||
|
|
dfc49ae28b | ||
|
|
a0cca13deb | ||
|
|
1498aaed14 | ||
|
|
086aa999e1 | ||
|
|
bf92cc8429 | ||
|
|
d94f6c0f5d | ||
|
|
f540b2edcd | ||
|
|
8aef197fde | ||
|
|
52d6f8e759 | ||
|
|
a7be65a111 |
54
CHANGELOG.md
54
CHANGELOG.md
@@ -1,5 +1,59 @@
|
||||
# 更新日志
|
||||
|
||||
3.2.3 - 2025-05-07
|
||||
---
|
||||
- CHANGE: 迁移logger库到新的仓库, 开启异步日志记录
|
||||
- CHANGE: 更新Go版本到go1.24.3
|
||||
- CHANGE: 迁移httpc到新的仓库
|
||||
|
||||
25w34b - 2025-05-07
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v3.2.3预发布版本,请勿在生产环境中使用;
|
||||
- CHANGE: 更新Go版本到go1.24.3
|
||||
|
||||
25w34a - 2025-05-05
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v3.2.3预发布版本,请勿在生产环境中使用;
|
||||
- CHANGE: 迁移logger库到新的仓库, 开启异步日志记录
|
||||
- CHANGE: 迁移httpc到新的仓库
|
||||
|
||||
3.2.2 - 2025-04-29
|
||||
---
|
||||
- ADD: 实验性的raw Header处置, 用于应对Github对zh-CN的限制
|
||||
- FIX: 修正Header部分的一些处理问题
|
||||
- REVERT: 为`git clone`部分回滚 3.1.0中的 "使用`bodystream`进行req方向的body复制, 而不是使用额外的`buffer reader`" 修改
|
||||
|
||||
25w33b - 2025-04-29
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v3.2.2预发布版本,请勿在生产环境中使用;
|
||||
- REVERT: 为`git clone`部分回滚 3.1.0中的 "使用`bodystream`进行req方向的body复制, 而不是使用额外的`buffer reader`" 修改
|
||||
|
||||
25w33a - 2025-04-29
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v3.2.2预发布版本,请勿在生产环境中使用;
|
||||
- ADD: 实验性的raw Header处置, 用于应对Github对zh-CN的限制
|
||||
- FIX: 修正Header部分的一些处理问题
|
||||
|
||||
3.2.1 - 2025-04-29
|
||||
---
|
||||
- FIX: 修复在`HertZ`路由匹配器下`matcher`键值不一致的问题
|
||||
|
||||
25w32a - 2025-04-29
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v3.2.1预发布版本,请勿在生产环境中使用;
|
||||
- FIX: 修复在`HertZ`路由匹配器下`matcher`键值不一致的问题
|
||||
|
||||
3.2.0 - 2025-04-27
|
||||
---
|
||||
- CHANGE: 加入`ghcr`和`dockerhub`反代功能
|
||||
- FIX: 修复在`HertZ`路由匹配器下与用户名相关功能异常的问题
|
||||
|
||||
25w31a - 2025-04-27
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v3.2.0预发布版本,请勿在生产环境中使用;
|
||||
- CHANGE: 加入`ghcr`和`dockerhub`反代功能
|
||||
- FIX: 修复在`HertZ`路由匹配器下与用户名相关功能异常的问题
|
||||
|
||||
3.1.0 - 2025-04-24
|
||||
---
|
||||
- CHANGE: 对标准url使用`HertZ`路由匹配器, 而不是自制匹配器, 以提升效率
|
||||
|
||||
@@ -1 +1 @@
|
||||
25w30e
|
||||
25w34b
|
||||
@@ -1,6 +1,11 @@
|
||||
# GHProxy
|
||||
|
||||
[](https://goreportcard.com/report/github.com/WJQSERVER-STUDIO/ghproxy)
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://goreportcard.com/report/github.com/WJQSERVER-STUDIO/ghproxy)
|
||||
|
||||
|
||||
支持 Git clone、raw、releases的 Github 加速项目, 支持自托管的同时带来卓越的性能与极低的资源占用(Golang和HertZ带来的优势), 同时支持多种额外功能
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"ghproxy/config"
|
||||
"ghproxy/middleware/nocache"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/WJQSERVER-STUDIO/logger"
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
"github.com/cloudwego/hertz/pkg/app/server"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"ghproxy/config"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/WJQSERVER-STUDIO/logger"
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ type Config struct {
|
||||
Whitelist WhitelistConfig
|
||||
RateLimit RateLimitConfig
|
||||
Outbound OutboundConfig
|
||||
Docker DockerConfig
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -49,12 +50,14 @@ mode = "auto" # "auto" or "advanced"
|
||||
maxIdleConns = 100 # only for advanced mode
|
||||
maxIdleConnsPerHost = 60 # only for advanced mode
|
||||
maxConnsPerHost = 0 # only for advanced mode
|
||||
useCustomRawHeaders = false
|
||||
*/
|
||||
type HttpcConfig struct {
|
||||
Mode string `toml:"mode"`
|
||||
MaxIdleConns int `toml:"maxIdleConns"`
|
||||
MaxIdleConnsPerHost int `toml:"maxIdleConnsPerHost"`
|
||||
MaxConnsPerHost int `toml:"maxConnsPerHost"`
|
||||
UseCustomRawHeaders bool `toml:"useCustomRawHeaders"`
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -143,6 +146,16 @@ type OutboundConfig struct {
|
||||
Url string `toml:"url"`
|
||||
}
|
||||
|
||||
/*
|
||||
[docker]
|
||||
enabled = false
|
||||
target = "ghcr" # ghcr/dockerhub
|
||||
*/
|
||||
type DockerConfig struct {
|
||||
Enabled bool `toml:"enabled"`
|
||||
Target string `toml:"target"`
|
||||
}
|
||||
|
||||
// LoadConfig 从 TOML 配置文件加载配置
|
||||
func LoadConfig(filePath string) (*Config, error) {
|
||||
if !FileExists(filePath) {
|
||||
@@ -244,5 +257,9 @@ func DefaultConfig() *Config {
|
||||
Enabled: false,
|
||||
Url: "socks5://127.0.0.1:1080",
|
||||
},
|
||||
Docker: DockerConfig{
|
||||
Enabled: false,
|
||||
Target: "ghcr",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ mode = "auto" # "auto" or "advanced"
|
||||
maxIdleConns = 100 # only for advanced mode
|
||||
maxIdleConnsPerHost = 60 # only for advanced mode
|
||||
maxConnsPerHost = 0 # only for advanced mode
|
||||
useCustomRawHeaders = false
|
||||
|
||||
[gitclone]
|
||||
mode = "bypass" # bypass / cache
|
||||
@@ -58,4 +59,8 @@ burst = 5
|
||||
|
||||
[outbound]
|
||||
enabled = false
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
|
||||
[docker]
|
||||
enabled = false
|
||||
target = "ghcr" # ghcr/dockerhub
|
||||
@@ -58,3 +58,7 @@ burst = 5
|
||||
[outbound]
|
||||
enabled = false
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
|
||||
[docker]
|
||||
enabled = false
|
||||
target = "ghcr" # ghcr/dockerhub
|
||||
@@ -24,6 +24,7 @@ mode = "auto" # "auto" or "advanced"
|
||||
maxIdleConns = 100 # only for advanced mode
|
||||
maxIdleConnsPerHost = 60 # only for advanced mode
|
||||
maxConnsPerHost = 0 # only for advanced mode
|
||||
useCustomRawHeaders = false
|
||||
|
||||
[gitclone]
|
||||
mode = "bypass" # bypass / cache
|
||||
@@ -70,6 +71,10 @@ burst = 5
|
||||
[outbound]
|
||||
enabled = false
|
||||
url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
|
||||
[docker]
|
||||
enabled = false
|
||||
target = "ghcr" # ghcr/dockerhub
|
||||
```
|
||||
|
||||
### 配置项详细说明
|
||||
@@ -134,6 +139,10 @@ url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
* 类型: 整数 (`int`)
|
||||
* 默认值: `0` (不限制)
|
||||
* 说明: 设置 HTTP 客户端连接池中,每个主机允许建立的最大连接数。设置为 `0` 表示不限制。
|
||||
* `useCustomRawHeaders`: 使用预定义header避免github waf对应zh-CN的封锁
|
||||
* 类型: 布尔值(`bool`)
|
||||
* 默认值: `false`(停用)
|
||||
* 说明: 启用后, 拉取raw文件会使用程序预定义的固定headers, 而不是原先的复制行为
|
||||
|
||||
* **`[gitclone]` - Git 克隆配置**
|
||||
|
||||
@@ -295,6 +304,21 @@ url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
|
||||
* 支持协议: `socks5://` 和 `http://`
|
||||
* 说明: 设置出站代理服务器的 URL。支持 SOCKS5 和 HTTP 代理协议。
|
||||
|
||||
* **`[docker]` - Docker 镜像代理配置**
|
||||
|
||||
* `enabled`: 是否启用 Docker 镜像代理功能。
|
||||
* 类型: 布尔值 (`bool`)
|
||||
* 默认值: `false` (禁用)
|
||||
* 说明: 当设置为 `true` 时,`ghproxy` 将尝试代理 Docker 镜像的下载请求,以加速从 GitHub Container Registry (GHCR) 或 Docker Hub 下载镜像。
|
||||
|
||||
* `target`: 代理的目标 Docker 注册表。
|
||||
* 类型: 字符串 (`string`)
|
||||
* 默认值: `"ghcr"` (代理 GHCR)
|
||||
* 可选值: `"ghcr"` 或 `"dockerhub"`
|
||||
* 说明: 指定要代理的 Docker 注册表。
|
||||
* `"ghcr"`: 代理 GitHub Container Registry (ghcr.io)。
|
||||
* `"dockerhub"`: 代理 Docker Hub (docker.io)。
|
||||
|
||||
## `blacklist.json` - 黑名单配置
|
||||
|
||||
`blacklist.json` 文件用于配置黑名单规则,阻止对特定用户或仓库的访问。
|
||||
|
||||
18
go.mod
18
go.mod
@@ -1,20 +1,20 @@
|
||||
module ghproxy
|
||||
|
||||
go 1.24.2
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.5.0
|
||||
github.com/WJQSERVER-STUDIO/httpc v0.5.0
|
||||
github.com/WJQSERVER-STUDIO/logger v1.6.0
|
||||
github.com/cloudwego/hertz v0.9.7
|
||||
github.com/hertz-contrib/http2 v0.1.8
|
||||
github.com/satomitouka/touka-httpc v0.4.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/time v0.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4 // indirect
|
||||
github.com/WJQSERVER-STUDIO/go-utils/log v0.0.1 // indirect
|
||||
github.com/WJQSERVER-STUDIO/go-utils/log v0.0.2 // indirect
|
||||
github.com/bytedance/gopkg v0.1.2 // indirect
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // 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.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
|
||||
golang.org/x/arch v0.17.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
)
|
||||
|
||||
32
go.sum
32
go.sum
@@ -2,10 +2,12 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4 h1:JLtFd00AdFg/TP+dtvIzLkdHwKUGPOAijN1sMtEYoFg=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4/go.mod h1:FZ6XE+4TKy4MOfX1xWKe6Rwsg0ucYFCdNh1KLvyKTfc=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/log v0.0.1 h1:gJEQspQPB527Vp2FPcdOrynQEj3YYtrg1ixVSB/JvZM=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/log v0.0.1/go.mod h1:j9Q+xnwpOfve7/uJnZ2izRQw6NNoXjvJHz7vUQAaLZE=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.5.0 h1:Uk4N7Sh4OPth3am3xVv17JlAm7tsna97ZLQRpQj7r5c=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.5.0/go.mod h1:mtxlnDdwsHcqDDpAQLa94nxbPFwNHSAHbBbIXQAA3po=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/log v0.0.2 h1:9CSf+V0ZQPl2ijC/g6v/ObemmhpKcikKVIodsaLExTA=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/log v0.0.2/go.mod h1:j9Q+xnwpOfve7/uJnZ2izRQw6NNoXjvJHz7vUQAaLZE=
|
||||
github.com/WJQSERVER-STUDIO/httpc v0.5.0 h1:0yJA+dOgbnO3R/mAWPjlbUq5lIqaxRV38XfiX3jt6pg=
|
||||
github.com/WJQSERVER-STUDIO/httpc v0.5.0/go.mod h1:M7KNUZjjhCkzzcg9lBPs9YfkImI+7vqjAyjdA19+joE=
|
||||
github.com/WJQSERVER-STUDIO/logger v1.6.0 h1:xK2xV7hlkMXaWzvj4+cNoNWA+JfnJaHX6VU+RrPnr7Q=
|
||||
github.com/WJQSERVER-STUDIO/logger v1.6.0/go.mod h1:TICMsR7geROHBg6rxwkqUNGydo34XVsX93yeoxyfuyY=
|
||||
github.com/bytedance/gopkg v0.1.1/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/gopkg v0.1.2 h1:8o2feYuxknDpN+O7kPwvSXfMEKfYvJYiA2K7aonoMEQ=
|
||||
github.com/bytedance/gopkg v0.1.2/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
@@ -50,8 +52,6 @@ github.com/nyaruka/phonenumbers v1.6.1 h1:XAJcTdYow16VrVKfglznMpJZz8KMJoMjx/91sX
|
||||
github.com/nyaruka/phonenumbers v1.6.1/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.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.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
|
||||
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
|
||||
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
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-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
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.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.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.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
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=
|
||||
|
||||
32
main.go
32
main.go
@@ -18,7 +18,7 @@ import (
|
||||
"ghproxy/proxy"
|
||||
"ghproxy/rate"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/WJQSERVER-STUDIO/logger"
|
||||
"github.com/hertz-contrib/http2/factory"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
@@ -415,50 +415,54 @@ func main() {
|
||||
setupApi(cfg, r, version)
|
||||
setupPages(cfg, r)
|
||||
|
||||
r.GET("/github.com/:username/:repo/releases/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "release")
|
||||
r.GET("/github.com/:user/:repo/releases/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "releases")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.GET("/github.com/:username/:repo/archive/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "release")
|
||||
r.GET("/github.com/:user/:repo/archive/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "releases")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.GET("/github.com/:username/:repo/blob/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
r.GET("/github.com/:user/:repo/blob/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "blob")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.GET("/github.com/:username/:repo/raw/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
r.GET("/github.com/:user/:repo/raw/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "raw")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.GET("/github.com/:username/:repo/info/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "gitclone")
|
||||
r.GET("/github.com/:user/:repo/info/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "clone")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
r.GET("/github.com/:username/:repo/git-upload-pack", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "gitclone")
|
||||
r.GET("/github.com/:user/:repo/git-upload-pack", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "clone")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.GET("/raw.githubusercontent.com/:username/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
r.GET("/raw.githubusercontent.com/:user/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "raw")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.GET("/gist.githubusercontent.com/:username/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
r.GET("/gist.githubusercontent.com/:user/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "gist")
|
||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.GET("/api.github.com/repos/:username/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
r.GET("/api.github.com/repos/:user/:repo/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
c.Set("matcher", "api")
|
||||
proxy.RoutingHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
r.Any("/v2/*filepath", func(ctx context.Context, c *app.RequestContext) {
|
||||
proxy.GhcrRouting(cfg)(ctx, c)
|
||||
})
|
||||
|
||||
r.NoRoute(func(ctx context.Context, c *app.RequestContext) {
|
||||
proxy.NoRouteHandler(cfg, limiter, iplimiter)(ctx, c)
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/WJQSERVER-STUDIO/logger"
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,29 +11,6 @@ import (
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
)
|
||||
|
||||
var (
|
||||
respHeadersToRemove = map[string]struct{}{
|
||||
"Content-Security-Policy": {},
|
||||
"Referrer-Policy": {},
|
||||
"Strict-Transport-Security": {},
|
||||
"X-Github-Request-Id": {},
|
||||
"X-Timer": {},
|
||||
"X-Served-By": {},
|
||||
"X-Fastly-Request-Id": {},
|
||||
}
|
||||
|
||||
reqHeadersToRemove = map[string]struct{}{
|
||||
"CF-IPCountry": {},
|
||||
"CF-RAY": {},
|
||||
"CF-Visitor": {},
|
||||
"CF-Connecting-IP": {},
|
||||
"CF-EW-Via": {},
|
||||
"CDN-Loop": {},
|
||||
"Upgrade": {},
|
||||
"Connection": {},
|
||||
}
|
||||
)
|
||||
|
||||
func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, cfg *config.Config, matcher string) {
|
||||
|
||||
var (
|
||||
@@ -51,8 +28,7 @@ func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, c
|
||||
return
|
||||
}
|
||||
|
||||
setRequestHeaders(c, req)
|
||||
//removeWSHeader(req) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头)
|
||||
setRequestHeaders(c, req, cfg, matcher)
|
||||
AuthPassThrough(c, cfg, req)
|
||||
|
||||
resp, err = client.Do(req)
|
||||
|
||||
115
proxy/docker.go
Normal file
115
proxy/docker.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"ghproxy/config"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
)
|
||||
|
||||
func GhcrRouting(cfg *config.Config) app.HandlerFunc {
|
||||
return func(ctx context.Context, c *app.RequestContext) {
|
||||
if cfg.Docker.Enabled {
|
||||
if cfg.Docker.Target == "ghcr" {
|
||||
GhcrRequest(ctx, c, "https://ghcr.io"+string(c.Request.RequestURI()), cfg, "ghcr")
|
||||
} else if cfg.Docker.Target == "dockerhub" {
|
||||
GhcrRequest(ctx, c, "https://registry-1.docker.io"+string(c.Request.RequestURI()), cfg, "dockerhub")
|
||||
} else {
|
||||
ErrorPage(c, NewErrorWithStatusLookup(403, "Docker Target is not Allowed"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ErrorPage(c, NewErrorWithStatusLookup(403, "Docker is not Allowed"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GhcrRequest(ctx context.Context, c *app.RequestContext, u string, cfg *config.Config, matcher string) {
|
||||
|
||||
var (
|
||||
method []byte
|
||||
req *http.Request
|
||||
resp *http.Response
|
||||
err error
|
||||
)
|
||||
|
||||
method = c.Request.Method()
|
||||
|
||||
rb := client.NewRequestBuilder(string(method), u)
|
||||
rb.NoDefaultHeaders()
|
||||
rb.SetBody(c.Request.BodyStream())
|
||||
|
||||
//req, err = client.NewRequest(string(method), u, c.Request.BodyStream())
|
||||
req, err = rb.Build()
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
c.Request.Header.VisitAll(func(key, value []byte) {
|
||||
headerKey := string(key)
|
||||
headerValue := string(value)
|
||||
req.Header.Add(headerKey, headerValue)
|
||||
})
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 错误处理(404)
|
||||
if resp.StatusCode == 404 {
|
||||
ErrorPage(c, NewErrorWithStatusLookup(404, "Page Not Found (From Github)"))
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
bodySize int
|
||||
contentLength string
|
||||
sizelimit int
|
||||
)
|
||||
|
||||
sizelimit = cfg.Server.SizeLimit * 1024 * 1024
|
||||
contentLength = resp.Header.Get("Content-Length")
|
||||
if contentLength != "" {
|
||||
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 {
|
||||
var finalURL string
|
||||
finalURL = resp.Request.URL.String()
|
||||
err = resp.Body.Close()
|
||||
if err != nil {
|
||||
logError("Failed to close response body: %v", err)
|
||||
}
|
||||
c.Redirect(301, []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, bodySize)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 复制响应头,排除需要移除的 header
|
||||
for key, values := range resp.Header {
|
||||
for _, value := range values {
|
||||
//c.Header(key, value)
|
||||
c.Response.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
c.Status(resp.StatusCode)
|
||||
|
||||
if contentLength != "" {
|
||||
c.SetBodyStream(resp.Body, bodySize)
|
||||
return
|
||||
}
|
||||
c.SetBodyStream(resp.Body, -1)
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"html/template"
|
||||
"io/fs"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/WJQSERVER-STUDIO/logger"
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"ghproxy/config"
|
||||
@@ -13,7 +14,10 @@ import (
|
||||
func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Config, mode string) {
|
||||
method := string(c.Request.Method())
|
||||
|
||||
logDump("Url Before FMT:%s", u)
|
||||
bodyReader := bytes.NewBuffer(c.Request.Body())
|
||||
|
||||
//bodyReader := c.Request.BodyStream()
|
||||
|
||||
if cfg.GitClone.Mode == "cache" {
|
||||
userPath, repoPath, remainingPath, queryParams, err := extractParts(u)
|
||||
if err != nil {
|
||||
@@ -22,7 +26,6 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
|
||||
}
|
||||
// 构建新url
|
||||
u = cfg.GitClone.SmartGitAddr + userPath + repoPath + remainingPath + "?" + queryParams.Encode()
|
||||
logDump("New Url After FMT:%s", u)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -30,13 +33,17 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
|
||||
)
|
||||
|
||||
if cfg.GitClone.Mode == "cache" {
|
||||
req, err := gitclient.NewRequest(method, u, c.Request.BodyStream())
|
||||
rb := gitclient.NewRequestBuilder(method, u)
|
||||
rb.NoDefaultHeaders()
|
||||
rb.SetBody(bodyReader)
|
||||
|
||||
req, err := rb.Build()
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||
return
|
||||
}
|
||||
setRequestHeaders(c, req)
|
||||
//removeWSHeader(req)
|
||||
|
||||
setRequestHeaders(c, req, cfg, "clone")
|
||||
AuthPassThrough(c, cfg, req)
|
||||
|
||||
resp, err = gitclient.Do(req)
|
||||
@@ -45,13 +52,17 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
|
||||
return
|
||||
}
|
||||
} else {
|
||||
req, err := client.NewRequest(method, u, c.Request.BodyStream())
|
||||
rb := client.NewRequestBuilder(string(c.Request.Method()), u)
|
||||
rb.NoDefaultHeaders()
|
||||
rb.SetBody(bodyReader)
|
||||
|
||||
req, err := rb.Build()
|
||||
if err != nil {
|
||||
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||
return
|
||||
}
|
||||
setRequestHeaders(c, req)
|
||||
//removeWSHeader(req)
|
||||
|
||||
setRequestHeaders(c, req, cfg, "clone")
|
||||
AuthPassThrough(c, cfg, req)
|
||||
|
||||
resp, err = client.Do(req)
|
||||
@@ -78,7 +89,8 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
|
||||
|
||||
for key, values := range resp.Header {
|
||||
for _, value := range values {
|
||||
c.Header(key, value)
|
||||
//c.Header(key, value)
|
||||
c.Response.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
httpc "github.com/satomitouka/touka-httpc"
|
||||
"github.com/WJQSERVER-STUDIO/httpc"
|
||||
)
|
||||
|
||||
var BufferSize int = 32 * 1024 // 32KB
|
||||
|
||||
@@ -1,17 +1,75 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"ghproxy/config"
|
||||
"net/http"
|
||||
|
||||
"github.com/cloudwego/hertz/pkg/app"
|
||||
)
|
||||
|
||||
func setRequestHeaders(c *app.RequestContext, req *http.Request) {
|
||||
c.Request.Header.VisitAll(func(key, value []byte) {
|
||||
headerKey := string(key)
|
||||
headerValue := string(value)
|
||||
if _, shouldRemove := reqHeadersToRemove[headerKey]; !shouldRemove {
|
||||
req.Header.Set(headerKey, headerValue)
|
||||
var (
|
||||
respHeadersToRemove = map[string]struct{}{
|
||||
"Content-Security-Policy": {},
|
||||
"Referrer-Policy": {},
|
||||
"Strict-Transport-Security": {},
|
||||
"X-Github-Request-Id": {},
|
||||
"X-Timer": {},
|
||||
"X-Served-By": {},
|
||||
"X-Fastly-Request-Id": {},
|
||||
}
|
||||
|
||||
reqHeadersToRemove = map[string]struct{}{
|
||||
"CF-IPCountry": {},
|
||||
"CF-RAY": {},
|
||||
"CF-Visitor": {},
|
||||
"CF-Connecting-IP": {},
|
||||
"CF-EW-Via": {},
|
||||
"CDN-Loop": {},
|
||||
"Upgrade": {},
|
||||
"Connection": {},
|
||||
}
|
||||
|
||||
cloneHeadersToRemove = map[string]struct{}{
|
||||
"CF-IPCountry": {},
|
||||
"CF-RAY": {},
|
||||
"CF-Visitor": {},
|
||||
"CF-Connecting-IP": {},
|
||||
"CF-EW-Via": {},
|
||||
"CDN-Loop": {},
|
||||
}
|
||||
)
|
||||
|
||||
// 预定义headers
|
||||
var (
|
||||
defaultHeaders = map[string]string{
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip",
|
||||
"Transfer-Encoding": "chunked",
|
||||
"User-Agent": "GHProxy/1.0",
|
||||
}
|
||||
)
|
||||
|
||||
func setRequestHeaders(c *app.RequestContext, req *http.Request, cfg *config.Config, matcher string) {
|
||||
if matcher == "raw" && cfg.Httpc.UseCustomRawHeaders {
|
||||
// 使用预定义Header
|
||||
for key, value := range defaultHeaders {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
})
|
||||
} else if matcher == "clone" {
|
||||
c.Request.Header.VisitAll(func(key, value []byte) {
|
||||
headerKey := string(key)
|
||||
headerValue := string(value)
|
||||
if _, shouldRemove := cloneHeadersToRemove[headerKey]; !shouldRemove {
|
||||
req.Header.Set(headerKey, headerValue)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
c.Request.Header.VisitAll(func(key, value []byte) {
|
||||
headerKey := string(key)
|
||||
headerValue := string(value)
|
||||
if _, shouldRemove := reqHeadersToRemove[headerKey]; !shouldRemove {
|
||||
req.Header.Set(headerKey, headerValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/WJQSERVER-STUDIO/logger"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user