package httpapi import ( "net/http" "os" "strconv" "strings" "sync" "time" ) // authRateLimiter is a simple per-IP token bucket for public auth-ish endpoints. type authRateLimiter struct { mu sync.Mutex hits map[string][]time.Time limit int window time.Duration } func newAuthRateLimiter() *authRateLimiter { limit := 60 if n, err := strconv.Atoi(strings.TrimSpace(os.Getenv("EVOBGP_AUTH_RATE_LIMIT"))); err == nil && n > 0 { limit = n } return &authRateLimiter{ hits: make(map[string][]time.Time), limit: limit, window: time.Minute, } } func (l *authRateLimiter) allow(ip string) bool { if l == nil { return true } now := time.Now() l.mu.Lock() defer l.mu.Unlock() cut := now.Add(-l.window) arr := l.hits[ip] kept := arr[:0] for _, t := range arr { if t.After(cut) { kept = append(kept, t) } } if len(kept) >= l.limit { l.hits[ip] = kept return false } kept = append(kept, now) l.hits[ip] = kept return true } func (s *Server) withAuthRateLimit(next http.HandlerFunc) http.HandlerFunc { if s.authLimiter == nil { s.authLimiter = newAuthRateLimiter() } return func(w http.ResponseWriter, r *http.Request) { ip := clientIP(r) if !s.authLimiter.allow(ip) { writeProblem(w, http.StatusTooManyRequests, "Too Many Requests", "auth rate limit exceeded") return } next(w, r) } }