Rthur2003 commited on
Commit
1c79dfc
·
1 Parent(s): 3580561

fix: implement rate limiting for audio processing endpoint and improve file size handling

Browse files
Files changed (1) hide show
  1. app/routes/data_processing.py +44 -6
app/routes/data_processing.py CHANGED
@@ -2,7 +2,9 @@
2
  Routes for data processing and manipulation (Audio/Image).
3
  """
4
 
5
- from fastapi import APIRouter, File, UploadFile, Form, HTTPException
 
 
6
  from fastapi.responses import StreamingResponse
7
  from pydantic import Json
8
  import logging
@@ -13,7 +15,34 @@ from app.services.audio_processor import process_audio
13
  router = APIRouter(prefix="/api/process", tags=["Data Processing"])
14
  logger = logging.getLogger(__name__)
15
 
16
- @router.post("/audio")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  async def process_audio_endpoint(
18
  file: UploadFile = File(...),
19
  options: Json[AudioAugmentationOptions] = Form(...)
@@ -29,10 +58,19 @@ async def process_audio_endpoint(
29
  raise HTTPException(status_code=400, detail={"code": "invalid_file_type", "message": "Invalid file type. Must be audio."})
30
 
31
  try:
32
- # Read file content with size guard
33
- content = await file.read()
34
- if len(content) > MAX_PAYLOAD_BYTES:
35
- raise HTTPException(status_code=413, detail={"code": "file_too_large", "message": f"File too large. Maximum size is {MAX_PAYLOAD_BYTES // (1024*1024)} MB."})
 
 
 
 
 
 
 
 
 
36
 
37
  # Process audio
38
  processed_audio = process_audio(content, options)
 
2
  Routes for data processing and manipulation (Audio/Image).
3
  """
4
 
5
+ import time
6
+ from collections import defaultdict
7
+ from fastapi import APIRouter, File, UploadFile, Form, HTTPException, Request, Depends, status
8
  from fastapi.responses import StreamingResponse
9
  from pydantic import Json
10
  import logging
 
15
  router = APIRouter(prefix="/api/process", tags=["Data Processing"])
16
  logger = logging.getLogger(__name__)
17
 
18
+ # Rate limiter for heavy processing endpoints
19
+ _process_rate_store: dict[str, list[float]] = defaultdict(list)
20
+ _PROCESS_RATE_WINDOW = 60
21
+ _PROCESS_RATE_MAX = 10
22
+ _PROCESS_MAX_IPS = 10_000
23
+
24
+
25
+ async def _process_rate_limit(request: Request) -> None:
26
+ client_ip = request.client.host if request.client else "unknown"
27
+ now = time.time()
28
+ cutoff = now - _PROCESS_RATE_WINDOW
29
+
30
+ if len(_process_rate_store) > _PROCESS_MAX_IPS:
31
+ stale = [ip for ip, ts in _process_rate_store.items() if all(t <= cutoff for t in ts)]
32
+ for ip in stale:
33
+ del _process_rate_store[ip]
34
+
35
+ hits = [t for t in _process_rate_store[client_ip] if t > cutoff]
36
+ if len(hits) >= _PROCESS_RATE_MAX:
37
+ raise HTTPException(
38
+ status_code=status.HTTP_429_TOO_MANY_REQUESTS,
39
+ detail={"code": "rate_limit_exceeded", "message": "Too many processing requests. Please wait."}
40
+ )
41
+ hits.append(now)
42
+ _process_rate_store[client_ip] = hits
43
+
44
+
45
+ @router.post("/audio", dependencies=[Depends(_process_rate_limit)])
46
  async def process_audio_endpoint(
47
  file: UploadFile = File(...),
48
  options: Json[AudioAugmentationOptions] = Form(...)
 
58
  raise HTTPException(status_code=400, detail={"code": "invalid_file_type", "message": "Invalid file type. Must be audio."})
59
 
60
  try:
61
+ # Read file in chunks to enforce size limit before full allocation
62
+ chunks = []
63
+ total_read = 0
64
+ chunk_size = 1024 * 1024 # 1 MB chunks
65
+ while True:
66
+ chunk = await file.read(chunk_size)
67
+ if not chunk:
68
+ break
69
+ total_read += len(chunk)
70
+ if total_read > MAX_PAYLOAD_BYTES:
71
+ raise HTTPException(status_code=413, detail={"code": "file_too_large", "message": f"File too large. Maximum size is {MAX_PAYLOAD_BYTES // (1024*1024)} MB."})
72
+ chunks.append(chunk)
73
+ content = b"".join(chunks)
74
 
75
  # Process audio
76
  processed_audio = process_audio(content, options)