forked from arnika-project/arnika
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathratelimiter.go
More file actions
73 lines (63 loc) · 1.2 KB
/
Copy pathratelimiter.go
File metadata and controls
73 lines (63 loc) · 1.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package main
import (
"sync"
"time"
)
// rateLimiter implements a simple per-IP rate limiter.
type rateLimiter struct {
mu sync.Mutex
requests map[string][]time.Time
limit int
window time.Duration
}
func newRateLimiter(limit int, window time.Duration) *rateLimiter {
rl := &rateLimiter{
requests: make(map[string][]time.Time),
limit: limit,
window: window,
}
go func() {
for {
time.Sleep(window)
rl.cleanup()
}
}()
return rl
}
func (rl *rateLimiter) Allow(ip string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
now := time.Now()
cutoff := now.Add(-rl.window)
times := rl.requests[ip]
valid := times[:0]
for _, t := range times {
if t.After(cutoff) {
valid = append(valid, t)
}
}
if len(valid) >= rl.limit {
rl.requests[ip] = valid
return false
}
rl.requests[ip] = append(valid, now)
return true
}
func (rl *rateLimiter) cleanup() {
rl.mu.Lock()
defer rl.mu.Unlock()
cutoff := time.Now().Add(-rl.window)
for ip, times := range rl.requests {
valid := times[:0]
for _, t := range times {
if t.After(cutoff) {
valid = append(valid, t)
}
}
if len(valid) == 0 {
delete(rl.requests, ip)
} else {
rl.requests[ip] = valid
}
}
}