Coverage for tests / test_custom_middleware_exception.py: 100%

50 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-04-06 01:24 +0000

1from pathlib import Path 1ijkl

2 

3from fastapi import APIRouter, FastAPI, File, UploadFile 1ijkl

4from fastapi.exceptions import HTTPException 1ijkl

5from fastapi.testclient import TestClient 1ijkl

6 

7app = FastAPI() 1ijkl

8 

9router = APIRouter() 1ijkl

10 

11 

12class ContentSizeLimitMiddleware: 1ijkl

13 """Content size limiting middleware for ASGI applications 

14 Args: 

15 app (ASGI application): ASGI application 

16 max_content_size (optional): the maximum content size allowed in bytes, None for no limit 

17 """ 

18 

19 def __init__(self, app: APIRouter, max_content_size: int | None = None): 1ijkl

20 self.app = app 1abcdefgh

21 self.max_content_size = max_content_size 1abcdefgh

22 

23 def receive_wrapper(self, receive): 1ijkl

24 received = 0 1abcdefgh

25 

26 async def inner(): 1abcdefgh

27 nonlocal received 

28 message = await receive() 1abcdefgh

29 if message["type"] != "http.request": 1abcdefgh

30 return message # pragma: no cover 

31 

32 body_len = len(message.get("body", b"")) 1abcdefgh

33 received += body_len 1abcdefgh

34 if received > self.max_content_size: 1abcdefgh

35 raise HTTPException( 1bdfh

36 422, 

37 detail={ 

38 "name": "ContentSizeLimitExceeded", 

39 "code": 999, 

40 "message": "File limit exceeded", 

41 }, 

42 ) 

43 return message 1aceg

44 

45 return inner 1abcdefgh

46 

47 async def __call__(self, scope, receive, send): 1ijkl

48 if scope["type"] != "http" or self.max_content_size is None: 1abcdefgh

49 await self.app(scope, receive, send) 1abcdefgh

50 return 1abcdefgh

51 

52 wrapper = self.receive_wrapper(receive) 1abcdefgh

53 await self.app(scope, wrapper, send) 1abcdefgh

54 

55 

56@router.post("/middleware") 1ijkl

57def run_middleware(file: UploadFile = File(..., description="Big File")): 1ijkl

58 return {"message": "OK"} 1aceg

59 

60 

61app.include_router(router) 1ijkl

62app.add_middleware(ContentSizeLimitMiddleware, max_content_size=2**8) 1ijkl

63 

64 

65client = TestClient(app) 1ijkl

66 

67 

68def test_custom_middleware_exception(tmp_path: Path): 1ijkl

69 default_pydantic_max_size = 2**16 1bdfh

70 path = tmp_path / "test.txt" 1bdfh

71 path.write_bytes(b"x" * (default_pydantic_max_size + 1)) 1bdfh

72 

73 with client: 1bdfh

74 with open(path, "rb") as file: 1bdfh

75 response = client.post("/middleware", files={"file": file}) 1bdfh

76 assert response.status_code == 422, response.text 1bdfh

77 assert response.json() == { 1bdfh

78 "detail": { 

79 "name": "ContentSizeLimitExceeded", 

80 "code": 999, 

81 "message": "File limit exceeded", 

82 } 

83 } 

84 

85 

86def test_custom_middleware_exception_not_raised(tmp_path: Path): 1ijkl

87 path = tmp_path / "test.txt" 1aceg

88 path.write_bytes(b"<file content>") 1aceg

89 

90 with client: 1aceg

91 with open(path, "rb") as file: 1aceg

92 response = client.post("/middleware", files={"file": file}) 1aceg

93 assert response.status_code == 200, response.text 1aceg

94 assert response.json() == {"message": "OK"} 1aceg