Files

64 lines
1.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package ratelimit begrenzt Versuche je Schlüssel (z. B. Client-IP) –
// genutzt für die Enrollment-Endpunkte von Server und Accountant.
package ratelimit
import (
"sync"
"time"
)
// Limiter begrenzt Versuche je Schlüssel (Client-IP) auf Limit pro Window
// (gleitendes Fenster). Limit < 0 = unbegrenzt. Parallel nutzbar.
type Limiter 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 *Limiter) 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 *Limiter) size() int {
l.mu.Lock()
defer l.mu.Unlock()
return len(l.hits)
}