Files
fn_registry/functions/infra/rate_limiter_by_key.go
egutierrez c3d9fbd8d3 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
2026-04-18 17:14:25 +02:00

40 lines
1.1 KiB
Go

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)
})
}
}