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,46 @@
|
||||
---
|
||||
name: rate_limiter_by_key
|
||||
kind: function
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func RateLimiterByKey(rl *RateLimiter, keyFunc func(r *http.Request) string) Middleware"
|
||||
description: "Middleware HTTP configurable que aplica rate limiting con un extractor de clave custom. Permite limitar por API key, user ID, header arbitrario, etc. Si keyFunc devuelve cadena vacia el request pasa sin limit."
|
||||
tags: [rate_limit, http, middleware, custom, key, server, infra]
|
||||
uses_functions: [rate_limiter_check_go_infra, rate_limit_headers_go_infra, http_error_response_go_infra]
|
||||
uses_types: [RateLimiter_go_infra, Middleware_go_infra, HTTPError_go_infra]
|
||||
returns: [Middleware_go_infra]
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [net/http]
|
||||
params:
|
||||
- name: rl
|
||||
desc: "puntero al RateLimiter compartido entre todos los requests"
|
||||
- name: keyFunc
|
||||
desc: "funcion que extrae la clave del request (API key, user ID, etc.). Cadena vacia salta el limit"
|
||||
output: "Middleware que aplica rate limit segun keyFunc, responde 429 con HTTPError JSON al exceder"
|
||||
tested: true
|
||||
tests: ["aplica limit por la clave devuelta por keyFunc", "key vacia salta el limit", "responde 429 con body JSON al exceder", "headers X-RateLimit-* siempre presentes en respuesta"]
|
||||
test_file_path: "functions/infra/rate_limiter_by_key_test.go"
|
||||
file_path: "functions/infra/rate_limiter_by_key.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
// Limit por API key
|
||||
rl := RateLimiterNew(100, 200)
|
||||
mw := RateLimiterByKey(rl, func(r *http.Request) string {
|
||||
return r.Header.Get("X-API-Key")
|
||||
})
|
||||
|
||||
// Limit por user ID extraido del JWT (suponiendo middleware previo que lo setea)
|
||||
mwUser := RateLimiterByKey(rl, func(r *http.Request) string {
|
||||
return r.Context().Value("user_id").(string)
|
||||
})
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion impura — el middleware muta el estado del limiter. Reutiliza `RateLimiterCheck`, `RateLimitHeaders` y `HTTPErrorResponse` del registry. Si el keyFunc devuelve "" se interpreta como "sin clave identificable" y se deja pasar el request: util para endpoints publicos donde solo se quiere limitar requests autenticados. La respuesta 429 sigue el formato JSON estandar `{"status":429,"code":"rate_limited","message":"too many requests"}`.
|
||||
Reference in New Issue
Block a user