diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html
deleted file mode 100644
index 223f5d80e..000000000
--- a/litellm/proxy/_experimental/out/404.html
+++ /dev/null
@@ -1 +0,0 @@
-
404: This page could not be found.LiteLLM Dashboard404
This page could not be found.
\ No newline at end of file
diff --git a/litellm/proxy/_experimental/out/model_hub.html b/litellm/proxy/_experimental/out/model_hub.html
deleted file mode 100644
index 9d1f566c5..000000000
--- a/litellm/proxy/_experimental/out/model_hub.html
+++ /dev/null
@@ -1 +0,0 @@
-LiteLLM Dashboard
\ No newline at end of file
diff --git a/litellm/proxy/_experimental/out/onboarding.html b/litellm/proxy/_experimental/out/onboarding.html
deleted file mode 100644
index abd1ff84c..000000000
--- a/litellm/proxy/_experimental/out/onboarding.html
+++ /dev/null
@@ -1 +0,0 @@
-LiteLLM Dashboard
\ No newline at end of file
diff --git a/litellm/proxy/common_utils/http_parsing_utils.py b/litellm/proxy/common_utils/http_parsing_utils.py
index 0a8dd86eb..deb259895 100644
--- a/litellm/proxy/common_utils/http_parsing_utils.py
+++ b/litellm/proxy/common_utils/http_parsing_utils.py
@@ -1,6 +1,6 @@
import ast
import json
-from typing import List, Optional
+from typing import Dict, List, Optional
from fastapi import Request, UploadFile, status
@@ -8,31 +8,43 @@ from litellm._logging import verbose_proxy_logger
from litellm.types.router import Deployment
-async def _read_request_body(request: Optional[Request]) -> dict:
+async def _read_request_body(request: Optional[Request]) -> Dict:
"""
- Asynchronous function to read the request body and parse it as JSON or literal data.
+ Safely read the request body and parse it as JSON.
Parameters:
- request: The request object to read the body from
Returns:
- - dict: Parsed request data as a dictionary
+ - dict: Parsed request data as a dictionary or an empty dictionary if parsing fails
"""
try:
- request_data: dict = {}
if request is None:
- return request_data
+ return {}
+
+ # Read the request body
body = await request.body()
- if body == b"" or body is None:
- return request_data
+ # Return empty dict if body is empty or None
+ if not body:
+ return {}
+
+ # Decode the body to a string
body_str = body.decode()
- try:
- request_data = ast.literal_eval(body_str)
- except Exception:
- request_data = json.loads(body_str)
- return request_data
- except Exception:
+
+ # Attempt JSON parsing (safe for untrusted input)
+ return json.loads(body_str)
+
+ except json.JSONDecodeError:
+ # Log detailed information for debugging
+ verbose_proxy_logger.exception("Invalid JSON payload received.")
+ return {}
+
+ except Exception as e:
+ # Catch unexpected errors to avoid crashes
+ verbose_proxy_logger.exception(
+ "Unexpected error reading request body - {}".format(e)
+ )
return {}
diff --git a/litellm/tests/test_mlflow.py b/litellm/tests/test_mlflow.py
deleted file mode 100644
index ec23875ea..000000000
--- a/litellm/tests/test_mlflow.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import pytest
-
-import litellm
-
-
-def test_mlflow_logging():
- litellm.success_callback = ["mlflow"]
- litellm.failure_callback = ["mlflow"]
-
- litellm.completion(
- model="gpt-4o-mini",
- messages=[{"role": "user", "content": "what llm are u"}],
- max_tokens=10,
- temperature=0.2,
- user="test-user",
- )
-
-@pytest.mark.asyncio()
-async def test_async_mlflow_logging():
- litellm.success_callback = ["mlflow"]
- litellm.failure_callback = ["mlflow"]
-
- await litellm.acompletion(
- model="gpt-4o-mini",
- messages=[{"role": "user", "content": "hi test from local arize"}],
- mock_response="hello",
- temperature=0.1,
- user="OTEL_USER",
- )
diff --git a/tests/local_testing/test_http_parsing_utils.py b/tests/local_testing/test_http_parsing_utils.py
new file mode 100644
index 000000000..2c6956c79
--- /dev/null
+++ b/tests/local_testing/test_http_parsing_utils.py
@@ -0,0 +1,79 @@
+import pytest
+from fastapi import Request
+from fastapi.testclient import TestClient
+from starlette.datastructures import Headers
+from starlette.requests import HTTPConnection
+import os
+import sys
+
+sys.path.insert(
+ 0, os.path.abspath("../..")
+) # Adds the parent directory to the system path
+
+from litellm.proxy.common_utils.http_parsing_utils import _read_request_body
+
+
+@pytest.mark.asyncio
+async def test_read_request_body_valid_json():
+ """Test the function with a valid JSON payload."""
+
+ class MockRequest:
+ async def body(self):
+ return b'{"key": "value"}'
+
+ request = MockRequest()
+ result = await _read_request_body(request)
+ assert result == {"key": "value"}
+
+
+@pytest.mark.asyncio
+async def test_read_request_body_empty_body():
+ """Test the function with an empty body."""
+
+ class MockRequest:
+ async def body(self):
+ return b""
+
+ request = MockRequest()
+ result = await _read_request_body(request)
+ assert result == {}
+
+
+@pytest.mark.asyncio
+async def test_read_request_body_invalid_json():
+ """Test the function with an invalid JSON payload."""
+
+ class MockRequest:
+ async def body(self):
+ return b'{"key": value}' # Missing quotes around `value`
+
+ request = MockRequest()
+ result = await _read_request_body(request)
+ assert result == {} # Should return an empty dict on failure
+
+
+@pytest.mark.asyncio
+async def test_read_request_body_large_payload():
+ """Test the function with a very large payload."""
+ large_payload = '{"key":' + '"a"' * 10**6 + "}" # Large payload
+
+ class MockRequest:
+ async def body(self):
+ return large_payload.encode()
+
+ request = MockRequest()
+ result = await _read_request_body(request)
+ assert result == {} # Large payloads could trigger errors, so validate behavior
+
+
+@pytest.mark.asyncio
+async def test_read_request_body_unexpected_error():
+ """Test the function when an unexpected error occurs."""
+
+ class MockRequest:
+ async def body(self):
+ raise ValueError("Unexpected error")
+
+ request = MockRequest()
+ result = await _read_request_body(request)
+ assert result == {} # Ensure fallback behavior
diff --git a/tests/local_testing/test_user_api_key_auth.py b/tests/local_testing/test_user_api_key_auth.py
index 1a129489c..167809da1 100644
--- a/tests/local_testing/test_user_api_key_auth.py
+++ b/tests/local_testing/test_user_api_key_auth.py
@@ -415,3 +415,18 @@ def test_allowed_route_inside_route(
)
== expected_result
)
+
+
+def test_read_request_body():
+ from litellm.proxy.common_utils.http_parsing_utils import _read_request_body
+ from fastapi import Request
+
+ payload = "()" * 1000000
+ request = Request(scope={"type": "http"})
+
+ async def return_body():
+ return payload
+
+ request.body = return_body
+ result = _read_request_body(request)
+ assert result is not None