diff --git a/litestar/_multipart.py b/litestar/_multipart.py index 05cd4c7281..a00fb0b07e 100644 --- a/litestar/_multipart.py +++ b/litestar/_multipart.py @@ -10,14 +10,14 @@ ParserLimitReached, PushMultipartParser, ) - +from litestar.utils.compat import async_next from litestar.datastructures.upload_file import UploadFile from litestar.exceptions import ClientException +from litestar.status_codes import HTTP_413_REQUEST_ENTITY_TOO_LARGE +from litestar.exceptions.http_exceptions import HTTPException __all__ = ("parse_content_header", "parse_multipart_form") -from litestar.utils.compat import async_next - if TYPE_CHECKING: from collections.abc import AsyncGenerator @@ -133,8 +133,8 @@ async def parse_multipart_form( # noqa: C901 await data.close() await _close_upload_files(fields) - # FIXME (3.0): This should raise a '413 - Request Entity Too Large', but for - # backwards compatibility, we keep it as a 400 for now - raise ClientException("Request Entity Too Large") from None + raise HTTPException( + status_code=HTTP_413_REQUEST_ENTITY_TOO_LARGE, + detail="Multipart form size limit exceeded") return {k: v if len(v) > 1 else v[0] for k, v in fields.items()} diff --git a/tests/unit/test_data_extractors.py b/tests/unit/test_data_extractors.py index 99f24ba413..8aebce84a6 100644 --- a/tests/unit/test_data_extractors.py +++ b/tests/unit/test_data_extractors.py @@ -7,11 +7,13 @@ from litestar import Request from litestar.connection.base import empty_receive from litestar.data_extractors import ConnectionDataExtractor, ResponseDataExtractor -from litestar.datastructures import Cookie +from litestar.datastructures import Cookie, UploadFile from litestar.enums import RequestEncodingType from litestar.response.base import ASGIResponse -from litestar.status_codes import HTTP_200_OK -from litestar.testing import RequestFactory +from litestar.status_codes import HTTP_200_OK, HTTP_413_REQUEST_ENTITY_TOO_LARGE +from litestar.testing import RequestFactory, create_test_client +from litestar import post +from litestar.params import Body factory = RequestFactory() @@ -125,3 +127,22 @@ async def test_skip_parse_malformed_body_false_raises(mocker: MockFixture) -> No with pytest.raises(ValueError): await extractor.extract(req, {"body"}) + +async def test_multipart_exceeds_part_limit_returns_413() -> None: + @post("/upload") + async def upload_handler( + data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART) + ) -> dict: + return {"filename": data.filename} + + with create_test_client(route_handlers=[upload_handler], multipart_form_part_limit=2) as client: + response = client.post( + "/upload", + files={ + "file1": ("test1.txt", b"content1"), + "file2": ("test2.txt", b"content2"), + "data": ("test3.txt", b"content3"), + }, + ) + + assert response.status_code == HTTP_413_REQUEST_ENTITY_TOO_LARGE