036c0a8d63
Implementa fase 1 del issue 0016: - Tipos RateLimiter, RateLimitConfig y RateLimitResult en types/infra/ - rate_limiter_new: constructor con validacion rate/burst > 0 - rate_limiter_check: evalua token bucket por key, calcula Allowed/Remaining/ResetAt/RetryAfter - rate_limit_headers (pure): construye headers IETF X-RateLimit-* y Retry-After - rate_limiter_cleanup: goroutine GC de entries inactivas con stop idempotente Sin dependencias externas (no Redis). sync.Mutex + map. Algoritmo token bucket estandar con recarga lineal proporcional al tiempo transcurrido.
46 lines
1.3 KiB
Go
46 lines
1.3 KiB
Go
package infra
|
|
|
|
import (
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// rateLimiterClient mantiene el estado de un bucket para una clave concreta.
|
|
type rateLimiterClient struct {
|
|
tokens float64
|
|
lastSeen time.Time
|
|
}
|
|
|
|
// RateLimiter mantiene el estado token-bucket de todos los clientes en memoria.
|
|
// rate son los tokens recargados por segundo, burst es la capacidad maxima del bucket.
|
|
// El campo clients es un mapa key -> bucket protegido por mu.
|
|
type RateLimiter struct {
|
|
rate float64
|
|
burst int
|
|
mu sync.Mutex
|
|
clients map[string]*rateLimiterClient
|
|
}
|
|
|
|
// RateLimitConfig parametriza el middleware de rate limiting.
|
|
// KeyFunc extrae la clave del request (nil = IP del cliente).
|
|
// CleanupInterval controla la frecuencia del GC de entries inactivas (0 = no GC).
|
|
type RateLimitConfig struct {
|
|
RequestsPerSecond float64
|
|
BurstSize int
|
|
KeyFunc func(r *http.Request) string
|
|
CleanupInterval time.Duration
|
|
}
|
|
|
|
// RateLimitResult es el resultado de evaluar un request contra el limiter.
|
|
// Allowed indica si el request puede pasar.
|
|
// Remaining son los tokens restantes en el bucket despues del check.
|
|
// ResetAt es el momento en que el bucket vuelve a estar lleno.
|
|
// RetryAfter son los segundos hasta que se pueda reintentar (0 si Allowed).
|
|
type RateLimitResult struct {
|
|
Allowed bool
|
|
Remaining int
|
|
ResetAt time.Time
|
|
RetryAfter float64
|
|
}
|