feat(infra): rate limit middlewares HTTP por IP y por key + tests
Implementa fase 2 del issue 0016: - rate_limit_middleware: limita por IP (X-Forwarded-For > X-Real-IP > RemoteAddr) - rate_limiter_by_key: middleware configurable con keyFunc custom (API key, user ID...) - Cuando se rechaza responde 429 con HTTPError JSON y headers Retry-After + X-RateLimit-* - Tests con httptest.NewRecorder cubriendo: limite, burst, IPs independientes, XFF prioritario, recarga temporal, JSON body, headers IETF, GC stop idempotente, key vacia salta limit
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RateLimiterByKey retorna un Middleware que aplica rate limiting usando keyFunc para extraer la clave del request.
|
||||
// Permite limitar por API key, user ID, o cualquier dimension custom.
|
||||
// Cuando keyFunc devuelve "" no se aplica limit (request pasa sin tocar el bucket).
|
||||
// Cuando se supera el limite responde 429 con headers X-RateLimit-* y Retry-After.
|
||||
func RateLimiterByKey(rl *RateLimiter, keyFunc func(r *http.Request) string) Middleware {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
key := keyFunc(r)
|
||||
if key == "" {
|
||||
// Sin clave no se aplica limit.
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
result := RateLimiterCheck(rl, key)
|
||||
headers := RateLimitHeaders(result, rl.burst)
|
||||
for k, v := range headers {
|
||||
w.Header()[k] = v
|
||||
}
|
||||
|
||||
if !result.Allowed {
|
||||
HTTPErrorResponse(w, HTTPError{
|
||||
Status: http.StatusTooManyRequests,
|
||||
Code: "rate_limited",
|
||||
Message: "too many requests",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user