Spaces:
Configuration error
Configuration error
| """Bounded file-upload reader. | |
| Reads an ``UploadFile`` in chunks and raises early if the content exceeds the | |
| configured ceiling. This prevents an attacker from forcing the server to buffer | |
| an arbitrarily large payload into memory before validation rejects it. | |
| """ | |
| from __future__ import annotations | |
| from fastapi import UploadFile | |
| from app.config import Settings, get_settings | |
| from app.exceptions import EmptyDocumentError, PayloadTooLargeError | |
| CHUNK_SIZE = 64 * 1024 # 64 KiB per read | |
| async def read_upload_bounded( | |
| file: UploadFile, | |
| settings: Settings | None = None, | |
| ) -> bytes: | |
| """Read an upload file with an enforced size ceiling. | |
| The file is consumed in chunks. As soon as the cumulative size exceeds | |
| ``settings.max_upload_bytes`` the read is aborted and | |
| :class:`PayloadTooLargeError` is raised — the remaining bytes are never | |
| buffered. | |
| Args: | |
| file: The FastAPI upload file to read. | |
| settings: Optional configuration override. | |
| Returns: | |
| The complete file content as bytes. | |
| Raises: | |
| EmptyDocumentError: If the file contains zero bytes. | |
| PayloadTooLargeError: If the file exceeds the configured size ceiling. | |
| """ | |
| cfg = settings or get_settings() | |
| max_bytes = cfg.max_upload_bytes | |
| chunks: list[bytes] = [] | |
| total = 0 | |
| while True: | |
| chunk = await file.read(CHUNK_SIZE) | |
| if not chunk: | |
| break | |
| total += len(chunk) | |
| if total > max_bytes: | |
| raise PayloadTooLargeError() | |
| chunks.append(chunk) | |
| if total == 0: | |
| raise EmptyDocumentError() | |
| return b"".join(chunks) | |