Updates
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
from threading import Lock
|
||||
|
||||
from fastapi import HTTPException, Request, status
|
||||
|
||||
|
||||
@dataclass
|
||||
class SlidingWindowRateLimiter:
|
||||
limit: int
|
||||
window_seconds: int
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self._events: dict[str, deque[float]] = {}
|
||||
self._lock = Lock()
|
||||
|
||||
def hit(self, key: str) -> None:
|
||||
now = time.time()
|
||||
floor = now - self.window_seconds
|
||||
|
||||
with self._lock:
|
||||
bucket = self._events.setdefault(key, deque())
|
||||
while bucket and bucket[0] <= floor:
|
||||
bucket.popleft()
|
||||
if len(bucket) >= self.limit:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
|
||||
detail="Too many requests. Please try again later.",
|
||||
)
|
||||
bucket.append(now)
|
||||
|
||||
|
||||
def request_client_key(request: Request, *, suffix: str = "") -> str:
|
||||
forwarded_for = request.headers.get("x-forwarded-for", "")
|
||||
client_ip = forwarded_for.split(",", 1)[0].strip() if forwarded_for else (request.client.host if request.client else "unknown")
|
||||
return f"{client_ip}:{suffix}" if suffix else client_ip
|
||||
Reference in New Issue
Block a user