package mailserver import ( "sync" "time" ) type rateLimiter struct { sync.RWMutex lifespan time.Duration // duration of the limit db map[string]time.Time period time.Duration cancel chan struct{} } func newRateLimiter(duration time.Duration) *rateLimiter { return &rateLimiter{ lifespan: duration, db: make(map[string]time.Time), period: time.Second, } } func (l *rateLimiter) Start() { cancel := make(chan struct{}) l.Lock() l.cancel = cancel l.Unlock() go l.cleanUp(l.period, cancel) } func (l *rateLimiter) Stop() { l.Lock() defer l.Unlock() if l.cancel == nil { return } close(l.cancel) l.cancel = nil } func (l *rateLimiter) Add(id string) { l.Lock() l.db[id] = time.Now() l.Unlock() } func (l *rateLimiter) IsAllowed(id string) bool { l.RLock() defer l.RUnlock() if lastRequestTime, ok := l.db[id]; ok { return lastRequestTime.Add(l.lifespan).Before(time.Now()) } return true } func (l *rateLimiter) cleanUp(period time.Duration, cancel <-chan struct{}) { t := time.NewTicker(period) defer t.Stop() for { select { case <-t.C: l.deleteExpired() case <-cancel: return } } } func (l *rateLimiter) deleteExpired() { l.Lock() defer l.Unlock() now := time.Now() for id, lastRequestTime := range l.db { if lastRequestTime.Add(l.lifespan).Before(now) { delete(l.db, id) } } }