Coverage for tests/test_custom_middleware_exception.py: 100%
51 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
1from pathlib import Path 1opqrstu
2from typing import Optional 1opqrstu
4from fastapi import APIRouter, FastAPI, File, UploadFile 1opqrstu
5from fastapi.exceptions import HTTPException 1opqrstu
6from fastapi.testclient import TestClient 1opqrstu
8app = FastAPI() 1opqrstu
10router = APIRouter() 1opqrstu
13class ContentSizeLimitMiddleware: 1opqrstu
14 """Content size limiting middleware for ASGI applications
15 Args:
16 app (ASGI application): ASGI application
17 max_content_size (optional): the maximum content size allowed in bytes, None for no limit
18 """
20 def __init__(self, app: APIRouter, max_content_size: Optional[int] = None): 1opqrstu
21 self.app = app 1abcdefg
22 self.max_content_size = max_content_size 1abcdefg
24 def receive_wrapper(self, receive): 1opqrstu
25 received = 0 1ahbicjdkelfmgn
27 async def inner(): 1ahbicjdkelfmgn
28 nonlocal received
29 message = await receive() 1ahbicjdkelfmgn
30 if message["type"] != "http.request": 1ahbicjdkelfmgn
31 return message # pragma: no cover
33 body_len = len(message.get("body", b"")) 1ahbicjdkelfmgn
34 received += body_len 1ahbicjdkelfmgn
35 if received > self.max_content_size: 1ahbicjdkelfmgn
36 raise HTTPException( 1abcdefg
37 422,
38 detail={
39 "name": "ContentSizeLimitExceeded",
40 "code": 999,
41 "message": "File limit exceeded",
42 },
43 )
44 return message 1hijklmn
46 return inner 1ahbicjdkelfmgn
48 async def __call__(self, scope, receive, send): 1opqrstu
49 if scope["type"] != "http" or self.max_content_size is None: 1ahbicjdkelfmgn
50 await self.app(scope, receive, send) 1ahbicjdkelfmgn
51 return 1ahbicjdkelfmgn
53 wrapper = self.receive_wrapper(receive) 1ahbicjdkelfmgn
54 await self.app(scope, wrapper, send) 1ahbicjdkelfmgn
57@router.post("/middleware") 1opqrstu
58def run_middleware(file: UploadFile = File(..., description="Big File")): 1opqrstu
59 return {"message": "OK"} 1hijklmn
62app.include_router(router) 1opqrstu
63app.add_middleware(ContentSizeLimitMiddleware, max_content_size=2**8) 1opqrstu
66client = TestClient(app) 1opqrstu
69def test_custom_middleware_exception(tmp_path: Path): 1opqrstu
70 default_pydantic_max_size = 2**16 1abcdefg
71 path = tmp_path / "test.txt" 1abcdefg
72 path.write_bytes(b"x" * (default_pydantic_max_size + 1)) 1abcdefg
74 with client: 1abcdefg
75 with open(path, "rb") as file: 1abcdefg
76 response = client.post("/middleware", files={"file": file}) 1abcdefg
77 assert response.status_code == 422, response.text 1abcdefg
78 assert response.json() == { 1abcdefg
79 "detail": {
80 "name": "ContentSizeLimitExceeded",
81 "code": 999,
82 "message": "File limit exceeded",
83 }
84 }
87def test_custom_middleware_exception_not_raised(tmp_path: Path): 1opqrstu
88 path = tmp_path / "test.txt" 1hijklmn
89 path.write_bytes(b"<file content>") 1hijklmn
91 with client: 1hijklmn
92 with open(path, "rb") as file: 1hijklmn
93 response = client.post("/middleware", files={"file": file}) 1hijklmn
94 assert response.status_code == 200, response.text 1hijklmn
95 assert response.json() == {"message": "OK"} 1hijklmn