Skip to content

Commit 60b8ebf

Browse files
committed
fix(rate_limiter): add periodic cleanup eviction and X-Forwarded-For support
1 parent dcb3a5b commit 60b8ebf

1 file changed

Lines changed: 36 additions & 2 deletions

File tree

internal/admin/rate_limiter.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type rateLimiter struct {
2020
type tokenBucket struct {
2121
tokens float64
2222
lastRefill time.Time
23+
expiresAt time.Time
2324
}
2425

2526
func newRateLimiter(rps int64, burstOrWindow any) *rateLimiter {
@@ -56,12 +57,29 @@ func newRateLimiter(rps int64, burstOrWindow any) *rateLimiter {
5657
// Unknown input types fall back to the RPS-derived burst.
5758
}
5859

59-
return &rateLimiter{
60+
rl := &rateLimiter{
6061
rate: rate,
6162
burst: float64(burst),
6263
now: func() time.Time { return time.Now().UTC() },
6364
client: make(map[string]tokenBucket),
6465
}
66+
go rl.periodicCleanup()
67+
return rl
68+
}
69+
70+
func (rl *rateLimiter) periodicCleanup() {
71+
ticker := time.NewTicker(5 * time.Minute)
72+
defer ticker.Stop()
73+
for range ticker.C {
74+
rl.mu.Lock()
75+
now := rl.now()
76+
for key, entry := range rl.client {
77+
if now.After(entry.expiresAt) {
78+
delete(rl.client, key)
79+
}
80+
}
81+
rl.mu.Unlock()
82+
}
6583
}
6684

6785
func (rl *rateLimiter) allow(remoteAddr string) bool {
@@ -86,6 +104,8 @@ func (rl *rateLimiter) allow(remoteAddr string) bool {
86104
bucket.lastRefill = now
87105
}
88106

107+
bucket.expiresAt = now.Add(5 * time.Minute)
108+
89109
if bucket.tokens < 1 {
90110
rl.client[key] = bucket
91111
return false
@@ -106,7 +126,7 @@ func (rl *rateLimiter) middleware(next http.Handler) http.Handler {
106126
next.ServeHTTP(w, r)
107127
return
108128
}
109-
if !rl.allow(r.RemoteAddr) {
129+
if !rl.allow(clientIP(r)) {
110130
http.Error(w, "too many requests", http.StatusTooManyRequests)
111131
return
112132
}
@@ -127,3 +147,17 @@ func normalizeClientIP(remoteAddr string) string {
127147

128148
return remoteAddr
129149
}
150+
151+
func clientIP(r *http.Request) string {
152+
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
153+
if idx := strings.IndexByte(xff, ','); idx >= 0 {
154+
return strings.TrimSpace(xff[:idx])
155+
}
156+
return strings.TrimSpace(xff)
157+
}
158+
host, _, err := net.SplitHostPort(r.RemoteAddr)
159+
if err != nil {
160+
return strings.TrimSpace(r.RemoteAddr)
161+
}
162+
return host
163+
}

0 commit comments

Comments
 (0)