package server import ( "sync" "time" ) // rateLimiter begrenzt Versuche je Schlüssel (Client-IP) auf limit pro // window (gleitendes Fenster). limit < 0 = unbegrenzt. type rateLimiter struct { limit int window time.Duration now func() time.Time mu sync.Mutex hits map[string][]time.Time lastPurge time.Time } // allow verbucht einen Versuch; bei Ablehnung liefert es die Wartezeit. func (l *rateLimiter) allow(key string) (bool, time.Duration) { if l.limit < 0 { return true, 0 } now := time.Now() if l.now != nil { now = l.now() } cutoff := now.Add(-l.window) l.mu.Lock() defer l.mu.Unlock() if l.hits == nil { l.hits = map[string][]time.Time{} } if now.Sub(l.lastPurge) >= l.window { // vergessen, was nicht mehr zählt for k, ts := range l.hits { if !ts[len(ts)-1].After(cutoff) { delete(l.hits, k) } } l.lastPurge = now } ts := l.hits[key] i := 0 for i < len(ts) && !ts[i].After(cutoff) { i++ } ts = ts[i:] if len(ts) >= l.limit { l.hits[key] = ts return false, ts[0].Add(l.window).Sub(now) } l.hits[key] = append(ts, now) return true, 0 } func (l *rateLimiter) size() int { l.mu.Lock() defer l.mu.Unlock() return len(l.hits) }