2023-03-16 20:27:45 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2023-03-18 16:40:23 +01:00
|
|
|
"log"
|
2023-03-16 20:27:45 +01:00
|
|
|
"sync"
|
2023-03-19 10:31:06 +01:00
|
|
|
"strconv"
|
2023-03-16 20:27:45 +01:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RateLimiter struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
bucket map[string]int
|
2023-03-18 16:40:23 +01:00
|
|
|
bans map[string]time.Time
|
2023-03-19 10:31:06 +01:00
|
|
|
banCounts map[string]int
|
2023-03-16 20:27:45 +01:00
|
|
|
rate int
|
2023-03-18 16:40:23 +01:00
|
|
|
softLimit int
|
|
|
|
hardLimit int
|
2023-03-16 20:27:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 16:40:23 +01:00
|
|
|
func newRateLimiter(rate int, softLimit int, hardLimit int) RateLimiter {
|
2023-03-16 20:27:45 +01:00
|
|
|
var rl = new(RateLimiter)
|
|
|
|
rl.bucket = make(map[string]int)
|
2023-03-18 16:40:23 +01:00
|
|
|
rl.bans = make(map[string]time.Time)
|
2023-03-19 10:31:06 +01:00
|
|
|
rl.banCounts = make(map[string]int)
|
2023-03-16 20:27:45 +01:00
|
|
|
rl.rate = rate
|
2023-03-18 16:40:23 +01:00
|
|
|
rl.softLimit = softLimit
|
|
|
|
rl.hardLimit = hardLimit
|
|
|
|
|
2023-03-16 20:27:45 +01:00
|
|
|
// Leak periodically
|
|
|
|
go func () {
|
|
|
|
for(true) {
|
|
|
|
rl.mu.Lock()
|
2023-03-18 16:40:23 +01:00
|
|
|
// Leak the buckets
|
2023-03-16 20:27:45 +01:00
|
|
|
for addr, drips := range rl.bucket {
|
|
|
|
if drips <= rate {
|
|
|
|
delete(rl.bucket, addr)
|
|
|
|
} else {
|
|
|
|
rl.bucket[addr] = drips - rl.rate
|
|
|
|
}
|
|
|
|
}
|
2023-03-18 16:40:23 +01:00
|
|
|
// Expire bans
|
|
|
|
now := time.Now()
|
|
|
|
for addr, expiry := range rl.bans {
|
|
|
|
if now.After(expiry) {
|
|
|
|
delete(rl.bans, addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait
|
2023-03-16 20:27:45 +01:00
|
|
|
rl.mu.Unlock()
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return *rl
|
|
|
|
}
|
|
|
|
|
2023-03-18 16:40:23 +01:00
|
|
|
func (rl *RateLimiter) softLimited(addr string) (int, bool) {
|
2023-03-16 20:27:45 +01:00
|
|
|
rl.mu.Lock()
|
|
|
|
defer rl.mu.Unlock()
|
|
|
|
drips, present := rl.bucket[addr]
|
|
|
|
if !present {
|
|
|
|
rl.bucket[addr] = 1
|
2023-03-18 16:40:23 +01:00
|
|
|
return 1, false
|
2023-03-16 20:27:45 +01:00
|
|
|
}
|
2023-03-18 15:45:35 +01:00
|
|
|
drips += 1
|
|
|
|
rl.bucket[addr] = drips
|
2023-03-18 16:40:23 +01:00
|
|
|
if drips > rl.hardLimit {
|
2023-03-19 10:31:06 +01:00
|
|
|
banCount, present := rl.banCounts[addr]
|
|
|
|
if present {
|
|
|
|
banCount += 1
|
|
|
|
} else {
|
|
|
|
banCount = 1
|
|
|
|
}
|
|
|
|
rl.banCounts[addr] = banCount
|
|
|
|
banDuration := 1 << (banCount - 1)
|
2023-03-18 16:40:23 +01:00
|
|
|
now := time.Now()
|
2023-03-19 10:31:06 +01:00
|
|
|
expiry := now.Add(time.Duration(banDuration)*time.Hour)
|
2023-03-18 16:40:23 +01:00
|
|
|
rl.bans[addr] = expiry
|
2023-03-19 10:31:06 +01:00
|
|
|
log.Println("Banning " + addr + " for " + strconv.Itoa(banDuration) + " hours due to ignoring rate limiting.")
|
2023-03-18 16:40:23 +01:00
|
|
|
}
|
|
|
|
return drips, drips > rl.softLimit
|
2023-03-16 20:27:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-18 16:40:23 +01:00
|
|
|
func (rl *RateLimiter) hardLimited(addr string) bool {
|
|
|
|
_, present := rl.bans[addr]
|
|
|
|
return present
|
|
|
|
}
|