-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat(server): 增加 /p 及 /ap 代理接口的单 IP 并发数限制 #2375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| package middlewares | ||
|
|
||
| import ( | ||
| "errors" | ||
| "net" | ||
| "net/http" | ||
| "strings" | ||
| "sync" | ||
|
|
||
| "github.com/OpenListTeam/OpenList/v4/internal/conf" | ||
| "github.com/OpenListTeam/OpenList/v4/internal/setting" | ||
| "github.com/OpenListTeam/OpenList/v4/server/common" | ||
| "github.com/gin-gonic/gin" | ||
| ) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The counters are stored in package-level variables, so in a horizontally-scaled deployment each process maintains its own independent counts. A single IP effectively gets |
||
|
|
||
| // NOTE: Counts are per-process; in multi-instance deployments the effective limit is limit * N. | ||
| var ( | ||
| proxyIPCounts = make(map[string]int) | ||
| proxyIPCountsMu sync.Mutex | ||
| ) | ||
|
|
||
| // ProxyIPConcurrencyLimit limits the maximum concurrent server proxy requests per IP Address | ||
| func ProxyIPConcurrencyLimit() gin.HandlerFunc { | ||
| return func(c *gin.Context) { | ||
| limit := setting.GetInt(conf.ProxyMaxConcurrentRequestsPerIP, -1) | ||
|
|
||
| if limit < 0 { | ||
| // -1 or less means unlimited | ||
| c.Next() | ||
| return | ||
| } | ||
|
|
||
| if limit == 0 { | ||
| // 0 means completely disabled | ||
| common.ErrorPage(c, errors.New("Proxy is disabled"), http.StatusForbidden) | ||
| return | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| ipHeader := setting.GetStr(conf.ProxyClientIPHeader) | ||
| var ip string | ||
|
|
||
| // Extract IP based on user configuration to prevent spoofing | ||
| if ipHeader != "" { | ||
| ip = c.Request.Header.Get(ipHeader) | ||
| if idx := strings.Index(ip, ","); idx != -1 { | ||
| ip = ip[:idx] | ||
|
Comment on lines
+42
to
+46
|
||
| } | ||
| ip = strings.TrimSpace(ip) | ||
| } | ||
|
|
||
| // Fallback to strict remote address if missing or not configured | ||
| if ip == "" { | ||
| ip = c.Request.RemoteAddr | ||
| if host, _, err := net.SplitHostPort(ip); err == nil { | ||
| ip = host | ||
| } | ||
| } | ||
|
|
||
| proxyIPCountsMu.Lock() | ||
| count := proxyIPCounts[ip] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The 429 response body is hand-rolled as |
||
| if count >= limit { | ||
| proxyIPCountsMu.Unlock() | ||
| common.ErrorPage(c, errors.New("Too Many Proxy Requests from this IP"), http.StatusTooManyRequests) | ||
| return | ||
| } | ||
| proxyIPCounts[ip] = count + 1 | ||
| proxyIPCountsMu.Unlock() | ||
|
|
||
| defer func() { | ||
| proxyIPCountsMu.Lock() | ||
| proxyIPCounts[ip]-- | ||
| if proxyIPCounts[ip] <= 0 { | ||
| delete(proxyIPCounts, ip) | ||
| } | ||
| proxyIPCountsMu.Unlock() | ||
| }() | ||
|
|
||
| c.Next() | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The help text contains a duplicated word: "strict strict remote address". Please fix to "strict remote address" (or rephrase) to avoid confusing admins reading the setting description.