This commit is contained in:
WJQSERVER 2024-11-23 12:25:17 +08:00
parent 0ce5c2c11c
commit 32584f9516
10 changed files with 97 additions and 10 deletions

View file

@ -1,5 +1,25 @@
# 更新日志
24w24a
---
- PRE-RELEASE: 此版本是v1.7.5的预发布版本,请勿在生产环境中使用
- ADD: `Rate`模块加入`IP`速率限制,可限制单个IP的请求速率
- CHANGE: 处理积攒的依赖库更新,更新如下依赖库:
- **github.com/gabriel-vasile/mimetype**: 从 v1.4.6 升级到 v1.4.7
- **github.com/go-playground/validator/v10**: 从 v10.22.1 升级到 v10.23.0
- **github.com/klauspost/cpuid/v2**: 从 v2.2.8 升级到 v2.2.9
- **github.com/onsi/ginkgo/v2**: 从 v2.21.0 升级到 v2.22.0
- **golang.org/x/arch**: 从 v0.11.0 升级到 v0.12.0
- **golang.org/x/crypto**: 从 v0.28.0 升级到 v0.29.0
- **golang.org/x/exp**: 从 v0.0.0-20241009180824-f66d83c29e7c 升级到 v0.0.0-20241108190413-2d47ceb2692f
- **golang.org/x/mod**: 从 v0.21.0 升级到 v0.22.0
- **golang.org/x/net**: 从 v0.30.0 升级到 v0.31.0
- **golang.org/x/sync**: 从 v0.8.0 升级到 v0.9.0
- **golang.org/x/sys**: 从 v0.26.0 升级到 v0.27.0
- **golang.org/x/text**: 从 v0.19.0 升级到 v0.20.0
- **golang.org/x/tools**: 从 v0.26.0 升级到 v0.27.0
- **google.golang.org/protobuf**: 从 v1.35.1 升级到 v1.35.2
v1.7.4
---
- CHANGE: 对二进制文件部署脚本进行优化

View file

@ -54,6 +54,7 @@ type WhitelistConfig struct {
type RateLimitConfig struct {
Enabled bool `toml:"enabled"`
RateMethod string `toml:"rateMethod"`
RatePerMinute int `toml:"ratePerMinute"`
Burst int `toml:"burst"`
}

View file

@ -30,5 +30,6 @@ whitelistFile = "/data/ghproxy/config/whitelist.json"
[rateLimit]
enabled = false
rateMrthod = "total" # "ip" or "total"
ratePerMinute = 180
burst = 5

View file

@ -30,5 +30,6 @@ whitelistFile = "/usr/local/ghproxy/config/whitelist.json"
[rateLimit]
enabled = false
rateMrthod = "total" # "ip" or "total"
ratePerMinute = 180
burst = 5

View file

@ -30,5 +30,6 @@ whitelistFile = "/data/ghproxy/config/whitelist.json"
[rateLimit]
enabled = false
rateMrthod = "total" # "ip" or "total"
ratePerMinute = 180
burst = 5

2
go.mod
View file

@ -23,7 +23,7 @@ require (
github.com/go-playground/validator/v10 v10.23.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect

2
go.sum
View file

@ -46,6 +46,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b h1:SXO0REt4iu865upYCk8aKBBJQ4BqoE0ReP23ClMu60s=
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=

11
main.go
View file

@ -22,8 +22,9 @@ var (
router *gin.Engine
configfile = "/data/ghproxy/config/config.toml"
cfgfile string
limiter *rate.RateLimiter
version string
limiter *rate.RateLimiter
iplimiter *rate.IPRateLimiter
)
var (
@ -68,7 +69,13 @@ func setupApi(cfg *config.Config, router *gin.Engine, version string) {
func setupRateLimit(cfg *config.Config) {
if cfg.RateLimit.Enabled {
if cfg.RateLimit.RateMethod == "ip" {
iplimiter = rate.NewIPRateLimiter(cfg.RateLimit.RatePerMinute, cfg.RateLimit.Burst, 1*time.Minute)
} else if cfg.RateLimit.RateMethod == "total" {
limiter = rate.New(cfg.RateLimit.RatePerMinute, cfg.RateLimit.Burst, 1*time.Minute)
} else {
logError("Invalid RateLimit Method: %s", cfg.RateLimit.RateMethod)
}
logInfo("Rate Limit Loaded")
}
}
@ -106,7 +113,7 @@ func init() {
}
router.NoRoute(func(c *gin.Context) {
proxy.NoRouteHandler(cfg, limiter)(c)
proxy.NoRouteHandler(cfg, limiter, iplimiter)(c)
})
}

View file

@ -33,12 +33,24 @@ var exps = []*regexp.Regexp{
regexp.MustCompile(`^(?:https?://)?gist\.github(?:usercontent|)\.com/([^/]+)/.+?/.+`),
}
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter) gin.HandlerFunc {
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter) gin.HandlerFunc {
return func(c *gin.Context) {
// 限制访问频率
if cfg.RateLimit.Enabled {
logInfo("Rate_Limit Enabled")
if !limiter.Allow() {
var allowed bool
switch cfg.RateLimit.RateMethod {
case "ip":
allowed = iplimiter.Allow(c.ClientIP())
case "total":
allowed = limiter.Allow()
default:
logWarning("Invalid RateLimit Method")
return
}
if !allowed {
c.JSON(http.StatusTooManyRequests, gin.H{"error": "Too Many Requests"})
logWarning("%s %s %s %s %s 429-TooManyRequests", c.ClientIP(), c.Request.Method, c.Request.URL.RequestURI(), c.Request.Header.Get("User-Agent"), c.Request.Proto)
return

View file

@ -1,15 +1,33 @@
package rate
import (
"ghproxy/logger"
"time"
"golang.org/x/time/rate"
)
// 日志输出
var (
logw = logger.Logw
logInfo = logger.LogInfo
logWarning = logger.LogWarning
logError = logger.LogError
)
// 总体限流器
type RateLimiter struct {
limiter *rate.Limiter
}
// 基于IP的限流器
type IPRateLimiter struct {
limiters map[string]*RateLimiter
limit int
burst int
duration time.Duration
}
func New(limit int, burst int, duration time.Duration) *RateLimiter {
return &RateLimiter{
limiter: rate.NewLimiter(rate.Limit(float64(limit)/duration.Seconds()), burst),
@ -19,3 +37,27 @@ func New(limit int, burst int, duration time.Duration) *RateLimiter {
func (rl *RateLimiter) Allow() bool {
return rl.limiter.Allow()
}
func NewIPRateLimiter(limit int, burst int, duration time.Duration) *IPRateLimiter {
return &IPRateLimiter{
limiters: make(map[string]*RateLimiter),
limit: limit,
burst: burst,
duration: duration,
}
}
func (rl *IPRateLimiter) Allow(ip string) bool {
if ip == "" {
logWarning("empty ip")
return false
}
limiter, ok := rl.limiters[ip]
if !ok {
// 创建新的 RateLimiter 并存储
limiter = New(rl.limit, rl.burst, rl.duration)
rl.limiters[ip] = limiter
}
return limiter.Allow()
}