"""Middleware ASGI PUR: plafon global pe dimensiunea corpului cererii (P1-5). `BaseHTTPMiddleware` nu poate refuza devreme: pentru a construi un `Request` reconstituie intregul flux `receive()`, iar FastAPI parseaza deja COMPLET multipart-ul (spooled pe disc peste prag) INAINTE sa ruleze endpointul — o verificare per-endpoint (chiar si citire pe chunk-uri) nu repara cauza, doar simptomul. Un middleware ASGI pur intercepteaza `receive` inaintea oricarui parser: Content-Length peste limita -> 413 fara sa citeasca nimic din body; altfel numara cumulativ octetii mesajelor `http.request` si intrerupe la depasire. Cererile fara body (GET etc.) trec netaxate. Verificarea per-endpoint existenta (`import_router.py`, `import_parse.MAX_BYTES`) ramane NEATINSA ca strat 2 — acopera orice cale care ar ocoli acest middleware. """ from __future__ import annotations import json from starlette.datastructures import Headers from .. import errors from ..config import get_settings class _BodyTooLarge(Exception): pass def _error_body() -> bytes: payload = errors.eroare( "CERERE_PREA_MARE", cauza="Corpul cererii depaseste limita permisa.", ) return json.dumps(payload, ensure_ascii=False).encode("utf-8") class BodyCapMiddleware: """Limiteaza corpul cererii la `settings.max_request_bytes`, pe TOATE rutele. Limita se citeste din `get_settings()` LA FIECARE CERERE (nu o data la construirea middleware-ului): `app.add_middleware` ruleaza o singura data per proces (modulul `app.main` e importat o singura data si in teste), deci un `max_bytes` fixat in `__init__` ar ramane inghetat la valoarea de la primul import si ar ignora `AUTOPASS_MAX_REQUEST_BYTES` schimbat ulterior (teste). """ def __init__(self, app): self.app = app async def __call__(self, scope, receive, send): if scope["type"] != "http": await self.app(scope, receive, send) return max_bytes = get_settings().max_request_bytes content_length = Headers(scope=scope).get("content-length") if content_length is not None: try: declared = int(content_length) except ValueError: declared = None if declared is not None and declared > max_bytes: await self._send_413(send) return total = 0 async def capped_receive(): nonlocal total message = await receive() if message["type"] == "http.request": total += len(message.get("body") or b"") if total > max_bytes: raise _BodyTooLarge() return message try: await self.app(scope, capped_receive, send) except _BodyTooLarge: await self._send_413(send) @staticmethod async def _send_413(send) -> None: await send( { "type": "http.response.start", "status": 413, "headers": [(b"content-type", b"application/json")], } ) await send({"type": "http.response.body", "body": _error_body()})