Compare commits

...

19 Commits

Author SHA1 Message Date
WJQSERVER
c656aa41ca Merge pull request #93 from WJQSERVER-STUDIO/dev
3.0.3
2025-04-19 21:26:39 +08:00
wjqserver
0b052f9c7f add debug output 2025-04-19 21:23:31 +08:00
wjqserver
6fb7e1150e 25w29b 2025-04-19 21:14:09 +08:00
wjqserver
5e0f95dae3 3.0.3 2025-04-19 20:44:43 +08:00
wjqserver
c1c39a5a1f remove unused bufferpool 2025-04-17 22:30:00 +08:00
wjqserver
dd2f5b5a12 25w29a 2025-04-17 22:20:06 +08:00
wjqserver
7e5b12dff8 Fix: Optimize header forwarding by excluding headers in a single pass 2025-04-16 15:50:04 +08:00
wjqserver
26a42b6510 add pprof for debug 2025-04-16 15:47:46 +08:00
wjqserver
254c9a8bad 25w29t-1 2025-04-15 15:05:36 +08:00
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
18 changed files with 312 additions and 148 deletions

View File

@@ -2,7 +2,7 @@
name: Features request name: Features request
about: 提出新功能建议 about: 提出新功能建议
title: "[Features]" title: "[Features]"
labels: enhancement labels: 改进
assignees: '' assignees: ''
--- ---

View File

@@ -1,6 +1,58 @@
# 更新日志 # 更新日志
3.0.1 -2025-04-08 3.0.3 - 2025-04-19
---
- CHANGE: 增加移除部分header的处置, 避免向服务端/客户端透露过多信息
- FIX: 修正非预期的header操作行为
- CHANGE: 合并header相关逻辑, 避免多次操作
- CHANGE: 对editor模式下的input进行处置, 增加隐式关闭处理
- CHANGE: 增加`netlib`配置项
25w29b - 2025-04-19
---
- PRE-RELEASE: 此版本是v3.0.3预发布版本,请勿在生产环境中使用;
- CHANGE: 增加`netlib`配置项
25w29a - 2025-04-17
---
- PRE-RELEASE: 此版本是v3.0.3预发布版本,请勿在生产环境中使用;
- CHANGE: 增加移除部分header的处置, 避免向服务端/客户端透露过多信息
- FIX: 修正非预期的header操作行为
- CHANGE: 合并header相关逻辑, 避免多次操作
- CHANGE: 对editor模式下的input进行处置, 增加隐式关闭处理
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: 加入`memLimit`指示gc
- CHANGE: 加入`hlog`输出路径配置 - CHANGE: 加入`hlog`输出路径配置

View File

@@ -1 +1 @@
25w27a 25w29b

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) ![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)** - 🗄️ **支持 Git Clone 缓存(配合 [Smart-Git](https://github.com/WJQSERVER-STUDIO/smart-git)**
- 🐳 **支持 Docker 部署** - 🐳 **支持 Docker 部署**
- 🐳 **支持自托管**
-**支持速率限制** -**支持速率限制**
- 🔒 **支持用户鉴权** - 🔒 **支持用户鉴权**
- 🐚 **支持 shell 脚本嵌套加速** - 🐚 **支持 shell 脚本多层嵌套加速**
### 项目相关 ### 项目相关

View File

@@ -1 +1 @@
3.0.1 3.0.3

View File

@@ -22,15 +22,20 @@ type Config struct {
/* /*
[server] [server]
host = "0.0.0.0" # 监听地址 host = "0.0.0.0"
port = 8080 # 监听端口 port = 8080
sizeLimit = 125 # 125MB netlib = "netpoll" # "netpoll" / "std" "standard" "net/http" "net"
H2C = true # 是否开启H2C传输 sizeLimit = 125 # MB
memLimit = 0 # MB
H2C = true
cors = "*" # "*"/"" -> "*" ; "nil" -> "" ;
debug = false
*/ */
type ServerConfig struct { type ServerConfig struct {
Port int `toml:"port"` Port int `toml:"port"`
Host string `toml:"host"` Host string `toml:"host"`
NetLib string `toml:"netlib"`
SizeLimit int `toml:"sizeLimit"` SizeLimit int `toml:"sizeLimit"`
MemLimit int64 `toml:"memLimit"` MemLimit int64 `toml:"memLimit"`
H2C bool `toml:"H2C"` H2C bool `toml:"H2C"`
@@ -180,6 +185,7 @@ func DefaultConfig() *Config {
Server: ServerConfig{ Server: ServerConfig{
Port: 8080, Port: 8080,
Host: "0.0.0.0", Host: "0.0.0.0",
NetLib: "netpoll",
SizeLimit: 125, SizeLimit: 125,
MemLimit: 0, MemLimit: 0,
H2C: true, H2C: true,

View File

@@ -1,6 +1,7 @@
[server] [server]
host = "0.0.0.0" host = "0.0.0.0"
port = 8080 port = 8080
netlib = "netpoll" # "netpoll" / "std" "standard" "net/http" "net"
sizeLimit = 125 # MB sizeLimit = 125 # MB
memLimit = 0 # MB memLimit = 0 # MB
H2C = true H2C = true

View File

@@ -1,6 +1,7 @@
[server] [server]
host = "127.0.0.1" host = "127.0.0.1"
port = 8080 port = 8080
netlib = "netpoll" # "netpoll" / "std" "standard" "net/http" "net"
sizeLimit = 125 # MB sizeLimit = 125 # MB
memLimit = 0 # MB memLimit = 0 # MB
H2C = true H2C = true

View File

@@ -12,6 +12,7 @@
[server] [server]
host = "0.0.0.0" host = "0.0.0.0"
port = 8080 port = 8080
netlib = "netpoll" # "netpoll" / "std" "standard" "net/http" "net"
sizeLimit = 125 # MB sizeLimit = 125 # MB
memLimit = 0 # MB memLimit = 0 # MB
H2C = true H2C = true
@@ -83,6 +84,10 @@ url = "socks5://127.0.0.1:1080" # "http://127.0.0.1:7890"
* 类型: 整数 (`int`) * 类型: 整数 (`int`)
* 默认值: `8080` * 默认值: `8080`
* 说明: 设置 `ghproxy` 监听的端口号。 * 说明: 设置 `ghproxy` 监听的端口号。
* `netlib`: 底层网络库。
* 类型: 字符串 (`string`)
* 默认值: `""` (HertZ默认处置)
* 说明: `"std"` `"standard"` `"net/http"` `"net"` 均会被设置为go标准库`net/http`, 设置为`"netpoll"`或`""`会由`HertZ`默认逻辑处理
* `sizeLimit`: 请求体大小限制。 * `sizeLimit`: 请求体大小限制。
* 类型: 整数 (`int`) * 类型: 整数 (`int`)
* 默认值: `125` (MB) * 默认值: `125` (MB)

8
go.mod
View File

@@ -5,10 +5,10 @@ go 1.24.2
require ( require (
github.com/BurntSushi/toml v1.5.0 github.com/BurntSushi/toml v1.5.0
github.com/WJQSERVER-STUDIO/go-utils/logger 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/hertz-contrib/http2 v0.1.8
github.com/satomitouka/touka-httpc v0.3.3 github.com/satomitouka/touka-httpc v0.4.0
golang.org/x/net v0.38.0 golang.org/x/net v0.39.0
golang.org/x/time v0.11.0 golang.org/x/time v0.11.0
) )
@@ -31,7 +31,7 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/arch v0.16.0 // indirect golang.org/x/arch v0.16.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sys v0.32.0 // indirect golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect golang.org/x/text v0.24.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect

16
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/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 h1:EoQiCG4sTonTPHxOGE0VlQs+sQR+Hsi2uN0qqwu8O50=
github.com/cloudwego/gopkg v0.1.4/go.mod h1:FQuXsRWRsSqJLsMVd5SYzp8/Z1y5gXKnVvRrWUOsCMI= 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.7 h1:tAVaiO+vTf+ZkQhvNhKbDJ0hmC4oJ7bzwDi1KhvhHy4=
github.com/cloudwego/hertz v0.9.6/go.mod h1:X5Ez52XhtszU4t+CTBGIJI4PqmcI1oSf8ULBz0SWfLo= 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/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 h1:bDrxQaNfijRI1zyGgXHQoE/nYegL0nr+ijO1Norelc4=
github.com/cloudwego/netpoll v0.7.0/go.mod h1:PI+YrmyS7cIr0+SD4seJz3Eo3ckkXdu2ZVKBLhURLNU= github.com/cloudwego/netpoll v0.7.0/go.mod h1:PI+YrmyS7cIr0+SD4seJz3Eo3ckkXdu2ZVKBLhURLNU=
@@ -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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.4.0 h1:cnOONdyJHJImMY8L64bvYF+7Ow/5CPf2Yr3RQRRMZOU=
github.com/satomitouka/touka-httpc v0.3.3/go.mod h1:sNXyW5XBufkwB9ZJ+PIlgN/6xiJ7aZV1fWGrXR0u3bA= 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 h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@@ -87,8 +87,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 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-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
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/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.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/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= 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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 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.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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-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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

60
main.go
View File

@@ -19,14 +19,16 @@ import (
"ghproxy/rate" "ghproxy/rate"
"github.com/WJQSERVER-STUDIO/go-utils/logger" "github.com/WJQSERVER-STUDIO/go-utils/logger"
"github.com/hertz-contrib/http2/factory"
"github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/middlewares/server/recovery" "github.com/cloudwego/hertz/pkg/app/middlewares/server/recovery"
"github.com/cloudwego/hertz/pkg/app/server" "github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/adaptor" "github.com/cloudwego/hertz/pkg/common/adaptor"
"github.com/cloudwego/hertz/pkg/common/hlog" "github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/cloudwego/hertz/pkg/network/standard"
"github.com/hertz-contrib/http2/factory" _ "net/http/pprof"
) )
var ( var (
@@ -146,6 +148,7 @@ func setupHertZLogger(cfg *config.Config) {
} else { } else {
hlog.SetOutput(hertZfile) hlog.SetOutput(hertZfile)
} }
hlog.SetLevel(hlog.LevelInfo)
} }
} }
@@ -239,7 +242,6 @@ func setupPages(cfg *config.Config, r *server.Hertz) {
r.StaticFile("/style.css", stylesheetsPath) r.StaticFile("/style.css", stylesheetsPath)
r.StaticFile("/bootstrap.min.css", bootstrapPath) r.StaticFile("/bootstrap.min.css", bootstrapPath)
r.StaticFile("/bootstrap.bundle.min.js", bootstrapBundlePath) r.StaticFile("/bootstrap.bundle.min.js", bootstrapBundlePath)
//router.StaticFile("/bootstrap.min.css", bootstrapPath)
default: default:
// 处理无效的Pages Mode // 处理无效的Pages Mode
@@ -359,30 +361,47 @@ func init() {
} }
func main() { func main() {
// 如果 showVersion 为 true则在 init 阶段已退出,这里直接返回
if showVersion || showHelp { if showVersion || showHelp {
return return
} }
logDebug("Run Mode: %s", runMode) logDebug("Run Mode: %s Netlib: %s", runMode, cfg.Server.NetLib)
// 确保在程序配置加载且非版本显示模式下执行
if cfg == nil { if cfg == nil {
fmt.Println("Config not loaded, exiting.") fmt.Println("Config not loaded, exiting.")
return // 如果配置未加载,则不继续执行 return
} }
addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
if cfg.Server.NetLib == "std" || cfg.Server.NetLib == "standard" || cfg.Server.NetLib == "net" || cfg.Server.NetLib == "net/http" {
if cfg.Server.H2C { if cfg.Server.H2C {
r = server.New( r = server.New(
server.WithHostPorts(addr), server.WithH2C(true),
server.WithH2C(true), server.WithHostPorts(addr),
) server.WithTransport(standard.NewTransporter),
r.AddProtocol("h2", factory.NewServerFactory()) )
r.AddProtocol("h2", factory.NewServerFactory())
} else {
r = server.New(
server.WithHostPorts(addr),
server.WithTransport(standard.NewTransporter),
)
}
} else if cfg.Server.NetLib == "netpoll" || cfg.Server.NetLib == "" {
if cfg.Server.H2C {
r = server.New(
server.WithH2C(true),
server.WithHostPorts(addr),
)
r.AddProtocol("h2", factory.NewServerFactory())
} else {
r = server.New(
server.WithHostPorts(addr),
)
}
} else { } else {
r = server.New( logError("Invalid NetLib: %s", cfg.Server.NetLib)
server.WithHostPorts(addr), fmt.Printf("Invalid NetLib: %s\n", cfg.Server.NetLib)
) os.Exit(1)
} }
// 添加Recovery中间件 // 添加Recovery中间件
@@ -444,11 +463,18 @@ func main() {
fmt.Printf("A Go Based High-Performance Github Proxy \n") fmt.Printf("A Go Based High-Performance Github Proxy \n")
fmt.Printf("Made by WJQSERVER-STUDIO\n") fmt.Printf("Made by WJQSERVER-STUDIO\n")
if cfg.Server.Debug {
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
}
r.Spin() r.Spin()
defer logger.Close() defer logger.Close()
defer func() { defer func() {
if hertZfile != nil { if hertZfile != nil {
err := hertZfile.Close() var err error
err = hertZfile.Close()
if err != nil { if err != nil {
logError("Failed to close hertz log file: %v", err) logError("Failed to close hertz log file: %v", err)
} }

View File

@@ -12,56 +12,53 @@ import (
"github.com/cloudwego/hertz/pkg/app" "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) { func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, cfg *config.Config, matcher string) {
method := c.Request.Method
// 发送HEAD请求, 预获取Content-Length var (
headReq, err := client.NewRequest("HEAD", u, nil) method []byte
bodyReader *bytes.Buffer
req *http.Request
resp *http.Response
err error
)
method = c.Request.Method()
bodyReader = bytes.NewBuffer(c.Request.Body())
req, err = client.NewRequest(string(method), u, bodyReader)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to create request: %v", err)) HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return 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)
req, err := client.NewRequest(string(method()), u, bodyReader)
if err != nil {
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return
}
setRequestHeaders(c, req) setRequestHeaders(c, req)
removeWSHeader(req) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头) //removeWSHeader(req) // 删除Conection Upgrade头, 避免与HTTP/2冲突(检查是否存在Upgrade头)
AuthPassThrough(c, cfg, req) AuthPassThrough(c, cfg, req)
resp, err := client.Do(req) resp, err = client.Do(req)
if err != nil { if err != nil {
HandleError(c, fmt.Sprintf("Failed to send request: %v", err)) HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
return return
@@ -69,36 +66,58 @@ func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, c
// 错误处理(404) // 错误处理(404)
if resp.StatusCode == 404 { if resp.StatusCode == 404 {
c.String(http.StatusNotFound, "File Not Found") //c.String(http.StatusNotFound, "File Not Found")
//c.Status(http.StatusNotFound) c.Status(http.StatusNotFound)
return return
} }
var (
bodySize int
contentLength string
sizelimit int
)
sizelimit = cfg.Server.SizeLimit * 1024 * 1024
contentLength = resp.Header.Get("Content-Length") contentLength = resp.Header.Get("Content-Length")
if contentLength != "" { if contentLength != "" {
size, err := strconv.Atoi(contentLength) var err error
if err == nil && size > sizelimit { bodySize, err = strconv.Atoi(contentLength)
finalURL := resp.Request.URL.String() 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(http.StatusMovedPermanently, []byte(finalURL)) 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 return
} }
} }
for key, values := range resp.Header { /*
for _, value := range values { for header := range headersToRemove {
c.Header(key, value) resp.Header.Del(header)
} }
}
headersToRemove := map[string]struct{}{ for key := range resp.Header {
"Content-Security-Policy": {}, var values []string = resp.Header.Values(key)
"Referrer-Policy": {}, for _, value := range values {
"Strict-Transport-Security": {}, c.Header(key, value)
} }
}
*/
for header := range headersToRemove { // 复制响应头,排除需要移除的 header
resp.Header.Del(header) for key, values := range resp.Header {
if _, shouldRemove := respHeadersToRemove[key]; !shouldRemove {
for _, value := range values {
c.Header(key, value)
}
}
} }
switch cfg.Server.Cors { switch cfg.Server.Cors {
@@ -124,7 +143,9 @@ func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, c
logInfo("Is Shell: %s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Header.GetProtocol()) logInfo("Is Shell: %s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Header.GetProtocol())
c.Header("Content-Length", "") c.Header("Content-Length", "")
reader, _, err := processLinks(resp.Body, compress, string(c.Request.Host()), cfg) var reader io.Reader
reader, _, err = processLinks(resp.Body, compress, string(c.Request.Host()), cfg)
c.SetBodyStream(reader, -1) c.SetBodyStream(reader, -1)
if err != nil { if err != nil {
@@ -132,6 +153,10 @@ func ChunkedProxyRequest(ctx context.Context, c *app.RequestContext, u string, c
return return
} }
} else { } else {
if contentLength != "" {
c.SetBodyStream(resp.Body, bodySize)
return
}
c.SetBodyStream(resp.Body, -1) c.SetBodyStream(resp.Body, -1)
} }

View File

@@ -43,7 +43,7 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
return return
} }
setRequestHeaders(c, req) setRequestHeaders(c, req)
removeWSHeader(req) //removeWSHeader(req)
AuthPassThrough(c, cfg, req) AuthPassThrough(c, cfg, req)
resp, err = gitclient.Do(req) resp, err = gitclient.Do(req)
@@ -58,7 +58,7 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
return return
} }
setRequestHeaders(c, req) setRequestHeaders(c, req)
removeWSHeader(req) //removeWSHeader(req)
AuthPassThrough(c, cfg, req) AuthPassThrough(c, cfg, req)
resp, err = client.Do(req) resp, err = client.Do(req)
@@ -72,6 +72,9 @@ func GitReq(ctx context.Context, c *app.RequestContext, u string, cfg *config.Co
if contentLength != "" { if contentLength != "" {
size, err := strconv.Atoi(contentLength) size, err := strconv.Atoi(contentLength)
sizelimit := cfg.Server.SizeLimit * 1024 * 1024 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 { if err == nil && size > sizelimit {
finalURL := []byte(resp.Request.URL.String()) finalURL := []byte(resp.Request.URL.String())
c.Redirect(http.StatusMovedPermanently, finalURL) c.Redirect(http.StatusMovedPermanently, finalURL)

View File

@@ -41,13 +41,19 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
} }
} }
rawPath := strings.TrimPrefix(string(c.Request.RequestURI()), "/") // 去掉前缀/ var (
matches := re.FindStringSubmatch(rawPath) // 匹配路径 rawPath string
matches []string
errMsg string
)
rawPath = strings.TrimPrefix(string(c.Request.RequestURI()), "/") // 去掉前缀/
matches = re.FindStringSubmatch(rawPath) // 匹配路径
logInfo("URL: %v", matches) logInfo("URL: %v", matches)
// 匹配路径错误处理 // 匹配路径错误处理
if len(matches) < 3 { if len(matches) < 3 {
errMsg := fmt.Sprintf("%s %s %s %s %s Invalid URL", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol()) errMsg = fmt.Sprintf("%s %s %s %s %s Invalid URL", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol())
logWarning(errMsg) logWarning(errMsg)
c.String(http.StatusForbidden, "Invalid URL Format. Path: %s", rawPath) c.String(http.StatusForbidden, "Invalid URL Format. Path: %s", rawPath)
return return
@@ -56,7 +62,14 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
// 制作url // 制作url
rawPath = "https://" + matches[2] rawPath = "https://" + matches[2]
user, repo, matcher, err := Matcher(rawPath, cfg) var (
user string
repo string
matcher string
err error
)
user, repo, matcher, err = Matcher(rawPath, cfg)
if err != nil { if err != nil {
if errors.Is(err, ErrInvalidURL) { if errors.Is(err, ErrInvalidURL) {
c.String(http.StatusForbidden, "Invalid URL Format. Path: %s", rawPath) c.String(http.StatusForbidden, "Invalid URL Format. Path: %s", rawPath)
@@ -69,18 +82,19 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
return return
} }
} }
username := user
logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), username, repo) logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), user, repo)
// dump log 记录详细信息 c.ClientIP(), c.Method(), rawPath,c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), full Header // dump log 记录详细信息 c.ClientIP(), c.Method(), rawPath,c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), full Header
logDump("%s %s %s %s %s %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), c.Request.Header.Header()) logDump("%s %s %s %s %s %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), c.Request.Header.Header())
repouser := fmt.Sprintf("%s/%s", username, repo) var repouser string
repouser = fmt.Sprintf("%s/%s", user, repo)
// 白名单检查 // 白名单检查
if cfg.Whitelist.Enabled { if cfg.Whitelist.Enabled {
whitelist := auth.CheckWhitelist(username, repo) var whitelist bool
whitelist = auth.CheckWhitelist(user, repo)
if !whitelist { if !whitelist {
errMsg := fmt.Sprintf("Whitelist Blocked repo: %s", repouser) errMsg = fmt.Sprintf("Whitelist Blocked repo: %s", repouser)
c.JSON(http.StatusForbidden, map[string]string{"error": errMsg}) c.JSON(http.StatusForbidden, map[string]string{"error": errMsg})
logWarning("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), repouser) logWarning("%s %s %s %s %s Whitelist Blocked repo: %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), repouser)
return return
@@ -89,9 +103,10 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
// 黑名单检查 // 黑名单检查
if cfg.Blacklist.Enabled { if cfg.Blacklist.Enabled {
blacklist := auth.CheckBlacklist(username, repo) var blacklist bool
blacklist = auth.CheckBlacklist(user, repo)
if blacklist { if blacklist {
errMsg := fmt.Sprintf("Blacklist Blocked repo: %s", repouser) errMsg = fmt.Sprintf("Blacklist Blocked repo: %s", repouser)
c.JSON(http.StatusForbidden, map[string]string{"error": errMsg}) c.JSON(http.StatusForbidden, map[string]string{"error": errMsg})
logWarning("%s %s %s %s %s Blacklist Blocked repo: %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), repouser) logWarning("%s %s %s %s %s Blacklist Blocked repo: %s", c.ClientIP(), c.Method(), rawPath, c.Request.Header.UserAgent(), c.Request.Header.GetProtocol(), repouser)
return return
@@ -106,13 +121,15 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
} }
// 鉴权 // 鉴权
var authcheck bool if cfg.Auth.Enabled {
authcheck, err = auth.AuthHandler(ctx, c, cfg) var authcheck bool
if !authcheck { authcheck, err = auth.AuthHandler(ctx, c, cfg)
//c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) if !authcheck {
c.AbortWithStatusJSON(401, map[string]string{"error": "Unauthorized"}) //c.AbortWithStatusJSON(401, gin.H{"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) c.AbortWithStatusJSON(401, map[string]string{"error": "Unauthorized"})
return 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 // IP METHOD URL USERAGENT PROTO MATCHES

View File

@@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"ghproxy/config" "ghproxy/config"
"net/http" "net/http"
"sync"
"time" "time"
httpc "github.com/satomitouka/touka-httpc" httpc "github.com/satomitouka/touka-httpc"
@@ -13,11 +12,10 @@ import (
var BufferSize int = 32 * 1024 // 32KB var BufferSize int = 32 * 1024 // 32KB
var ( var (
tr *http.Transport tr *http.Transport
gittr *http.Transport gittr *http.Transport
BufferPool *sync.Pool client *httpc.Client
client *httpc.Client gitclient *httpc.Client
gitclient *httpc.Client
) )
func InitReq(cfg *config.Config) { func InitReq(cfg *config.Config) {
@@ -25,13 +23,6 @@ func InitReq(cfg *config.Config) {
if cfg.GitClone.Mode == "cache" { if cfg.GitClone.Mode == "cache" {
initGitHTTPClient(cfg) initGitHTTPClient(cfg)
} }
// 初始化固定大小的缓存池
BufferPool = &sync.Pool{
New: func() interface{} {
return make([]byte, BufferSize)
},
}
} }
func initHTTPClient(cfg *config.Config) { func initHTTPClient(cfg *config.Config) {
@@ -42,7 +33,6 @@ func initHTTPClient(cfg *config.Config) {
if cfg.Httpc.Mode == "auto" { if cfg.Httpc.Mode == "auto" {
tr = &http.Transport{ tr = &http.Transport{
//MaxIdleConns: 160,
IdleConnTimeout: 30 * time.Second, IdleConnTimeout: 30 * time.Second,
WriteBufferSize: 32 * 1024, // 32KB WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB ReadBufferSize: 32 * 1024, // 32KB
@@ -64,7 +54,6 @@ func initHTTPClient(cfg *config.Config) {
logWarning("use Auto to Run HTTP Client") logWarning("use Auto to Run HTTP Client")
fmt.Println("use Auto to Run HTTP Client") fmt.Println("use Auto to Run HTTP Client")
tr = &http.Transport{ tr = &http.Transport{
//MaxIdleConns: 160,
IdleConnTimeout: 30 * time.Second, IdleConnTimeout: 30 * time.Second,
WriteBufferSize: 32 * 1024, // 32KB WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB ReadBufferSize: 32 * 1024, // 32KB
@@ -87,23 +76,11 @@ func initHTTPClient(cfg *config.Config) {
func initGitHTTPClient(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" { if cfg.Httpc.Mode == "auto" {
gittr = &http.Transport{ gittr = &http.Transport{
//MaxIdleConns: 160,
IdleConnTimeout: 30 * time.Second, IdleConnTimeout: 30 * time.Second,
WriteBufferSize: 32 * 1024, // 32KB WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB ReadBufferSize: 32 * 1024, // 32KB
Protocols: proTolcols,
} }
} else if cfg.Httpc.Mode == "advanced" { } else if cfg.Httpc.Mode == "advanced" {
gittr = &http.Transport{ gittr = &http.Transport{
@@ -112,7 +89,6 @@ func initGitHTTPClient(cfg *config.Config) {
MaxIdleConnsPerHost: cfg.Httpc.MaxIdleConnsPerHost, MaxIdleConnsPerHost: cfg.Httpc.MaxIdleConnsPerHost,
WriteBufferSize: 32 * 1024, // 32KB WriteBufferSize: 32 * 1024, // 32KB
ReadBufferSize: 32 * 1024, // 32KB ReadBufferSize: 32 * 1024, // 32KB
Protocols: proTolcols,
} }
} else { } else {
// 错误的模式 // 错误的模式
@@ -130,14 +106,39 @@ func initGitHTTPClient(cfg *config.Config) {
if cfg.Outbound.Enabled { if cfg.Outbound.Enabled {
initTransport(cfg, gittr) initTransport(cfg, gittr)
} }
if cfg.Server.Debug { if cfg.Server.Debug && cfg.GitClone.ForceH2C {
gitclient = httpc.New( gitclient = httpc.New(
httpc.WithTransport(gittr), httpc.WithTransport(gittr),
httpc.WithDumpLog(), 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 { } else {
gitclient = httpc.New( gitclient = httpc.New(
httpc.WithTransport(gittr), httpc.WithTransport(gittr),
httpc.WithProtocols(httpc.ProtocolsConfig{
Http1: true,
Http2: true,
Http2_Cleartext: true,
}),
) )
} }
} }

View File

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

View File

@@ -6,14 +6,32 @@ import (
"github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/app"
) )
/*
// 设置请求头 // 设置请求头
func setRequestHeaders(c *app.RequestContext, req *http.Request) { func setRequestHeaders(c *app.RequestContext, req *http.Request) {
c.Request.Header.VisitAll(func(key, value []byte) { c.Request.Header.VisitAll(func(key, value []byte) {
req.Header.Set(string(key), string(value)) req.Header.Set(string(key), string(value))
}) })
} }
*/
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)
}
})
}
/*
// 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) { func removeWSHeader(req *http.Request) {
req.Header.Del("Upgrade") req.Header.Del("Upgrade")
req.Header.Del("Connection") req.Header.Del("Connection")
} }
*/