46 lines
1.0 KiB
Python
46 lines
1.0 KiB
Python
"""
|
|
Lightweight in-process rate limiting helpers.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import threading
|
|
import time
|
|
from collections import defaultdict, deque
|
|
|
|
from fastapi import HTTPException, Request, status
|
|
|
|
_lock = threading.Lock()
|
|
_hits: dict[str, deque[float]] = defaultdict(deque)
|
|
|
|
|
|
def _client_ip(request: Request) -> str:
|
|
if request.client and request.client.host:
|
|
return request.client.host
|
|
return "unknown"
|
|
|
|
|
|
def enforce_rate_limit(
|
|
request: Request,
|
|
*,
|
|
scope: str,
|
|
max_requests: int,
|
|
window_seconds: int,
|
|
) -> None:
|
|
now = time.time()
|
|
ip = _client_ip(request)
|
|
key = f"{scope}:{ip}"
|
|
cutoff = now - window_seconds
|
|
|
|
with _lock:
|
|
dq = _hits[key]
|
|
while dq and dq[0] <= cutoff:
|
|
dq.popleft()
|
|
if len(dq) >= max_requests:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
|
|
detail=f"Too many requests for {scope}. Please try again later.",
|
|
)
|
|
dq.append(now)
|
|
|