c3d9fbd8d3
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
36 lines
1.0 KiB
Go
36 lines
1.0 KiB
Go
package infra
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// rateLimitClientIP extrae la IP del cliente del request.
|
|
// Prioridad: X-Forwarded-For (primer valor) > X-Real-IP > RemoteAddr.
|
|
// Para X-Forwarded-For multi-hop solo se usa el primer IP (cliente original).
|
|
func rateLimitClientIP(r *http.Request) string {
|
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
|
parts := strings.Split(xff, ",")
|
|
ip := strings.TrimSpace(parts[0])
|
|
if ip != "" {
|
|
return ip
|
|
}
|
|
}
|
|
if xri := r.Header.Get("X-Real-IP"); xri != "" {
|
|
return strings.TrimSpace(xri)
|
|
}
|
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err == nil {
|
|
return host
|
|
}
|
|
return r.RemoteAddr
|
|
}
|
|
|
|
// RateLimitMiddleware retorna un Middleware que aplica rate limiting por IP del cliente.
|
|
// Si el request supera el limite responde 429 con headers Retry-After y X-RateLimit-*.
|
|
// La IP se extrae con prioridad X-Forwarded-For > X-Real-IP > RemoteAddr.
|
|
func RateLimitMiddleware(rl *RateLimiter) Middleware {
|
|
return RateLimiterByKey(rl, rateLimitClientIP)
|
|
}
|