Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
55586c4
Add server bug tests that expose JSON parsing crashes and validation …
gwbischof Jul 1, 2025
f83d6b8
Add comprehensive edge case tests with timeout protection to discover…
gwbischof Jul 1, 2025
b19a691
Uncomment hanging WebSocket tests and add timeout protection to preve…
gwbischof Jul 1, 2025
a512d08
Add TODO comments to identify tests that expose server crashes or han…
gwbischof Jul 1, 2025
1453d46
Refine test_server_bugs.py to focus on 12 actionable server bugs incl…
gwbischof Jul 7, 2025
ad14419
Remove pytest.raises patterns and add 8 new bug tests to expand test …
gwbischof Jul 7, 2025
48326f4
Remove 2 passing tests that show correct server behavior to focus tes…
gwbischof Jul 7, 2025
bfc56e0
Reorganize bug tests into 5 focused test files grouped by server func…
gwbischof Jul 7, 2025
9c9dc74
Fix server crash by adding JSON parsing error handling to /close endp…
gwbischof Jul 7, 2025
cb53697
Add configurable resource limits to prevent memory exhaustion and DoS…
gwbischof Jul 7, 2025
f212ec8
Remove Redis manipulation tests that bypass API validation to focus o…
gwbischof Jul 7, 2025
5b346e1
Add test references to server code fixes for better traceability betw…
gwbischof Jul 7, 2025
0b402f4
Improve JSON error handling to only catch JSON-specific exceptions fo…
gwbischof Jul 7, 2025
a7a9e17
Sanitize JSON error messages to prevent leaking implementation detail…
gwbischof Jul 7, 2025
7f82c1f
makeing some progress
gwbischof Jul 7, 2025
8cadb32
Remove untested WebSocket frame size limits to follow test-driven dev…
gwbischof Jul 7, 2025
95bbdc7
moving along
gwbischof Jul 7, 2025
7112820
ruff
gwbischof Jul 7, 2025
a319113
cleanup
gwbischof Jul 7, 2025
c66f737
don't use the middleware because it is only needed for the upload end…
gwbischof Jul 7, 2025
a9c7025
Add payload size validation and JSON error handling to prevent server…
gwbischof Jul 7, 2025
90ae9ff
Replace manual JSON parsing with Pydantic model for cleaner validation
gwbischof Jul 8, 2025
0eb0c3b
clean up
gwbischof Jul 8, 2025
28eea12
touch ups
gwbischof Jul 8, 2025
6211736
remove the close_connection body
gwbischof Jul 9, 2025
f189940
make close_connection a delete endpoint
gwbischof Jul 10, 2025
d95f65d
add close connection tests
gwbischof Jul 10, 2025
7c0a96c
test websocket connection to non-existant node
gwbischof Jul 10, 2025
8212b36
return 404 if node not streamable
gwbischof Jul 11, 2025
f662241
clean up the test
gwbischof Jul 11, 2025
ea431da
touch ups
gwbischof Jul 11, 2025
57c94be
touch ups
gwbischof Jul 11, 2025
cd31947
touch up
gwbischof Jul 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import json
import numpy as np
import uvicorn
from pydantic import BaseModel
from pydantic_settings import BaseSettings
from fastapi import FastAPI, WebSocket, Request, WebSocketDisconnect
from fastapi import FastAPI, WebSocket, Request, WebSocketDisconnect, HTTPException
from datetime import datetime
import msgpack
import asyncio
Expand All @@ -12,9 +13,14 @@
from contextlib import asynccontextmanager


class CloseRequest(BaseModel):
reason: Optional[str] = None


class Settings(BaseSettings):
redis_url: str = "redis://localhost:6379/0"
ttl: int = 60 * 60 # 1 hour
max_payload_size: int = 16 * 1024 * 1024 # 16MB max payload


def build_app(settings: Settings):
Expand Down Expand Up @@ -57,9 +63,16 @@ async def close(node_id):
async def append(node_id, request: Request):
"Append data to a dataset."

# Check request body size limit
# Tell good-faith clients that their request is too big.
# Fix for: test_large_data_resource.py::test_large_data_resource_limits
headers = request.headers
content_length = headers.get("content-length")
if content_length and int(content_length) > settings.max_payload_size:
raise HTTPException(status_code=413, detail="Payload too large")

# get data from request body
binary_data = await request.body()
headers = request.headers
metadata = {
"timestamp": datetime.now().isoformat(),
}
Expand All @@ -86,12 +99,11 @@ async def append(node_id, request: Request):
# @app.websocket("/stream/many")

@app.post("/close/{node_id}")
async def close_connection(node_id: str, request: Request):
# Parse the JSON body
body = await request.json()
async def close_connection(node_id: str, body: CloseRequest, request: Request):
# Pydantic automatically handles JSON parsing and validation
# Fix for: test_json_parsing.py::test_json_parsing_errors_in_close_endpoint
headers = request.headers

reason = body.get("reason", None)
reason = body.reason

metadata = {"timestamp": datetime.now().isoformat(), "reason": reason}
metadata.setdefault("Content-Type", headers.get("Content-Type"))
Expand Down
3 changes: 1 addition & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ def client():
"""Fixture providing TestClient following ws-tests pattern."""
settings = Settings(redis_url="redis://localhost:6379/0", ttl=60 * 60)
app = build_app(settings)

with TestClient(app) as client:
yield client

24 changes: 24 additions & 0 deletions tests/test_json_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
Tests for JSON parsing error handling bugs in server endpoints.
"""


def test_json_parsing_errors_in_close_endpoint(client):
"""Server should handle JSON parsing errors in /close endpoint gracefully."""
response = client.post("/upload")
assert response.status_code == 200
node_id = response.json()["node_id"]

# Test 1: Malformed JSON content should not crash
response = client.post(
f"/close/{node_id}",
content=b"invalid json {{{",
headers={"Content-Type": "application/json"},
)
assert response.status_code == 422 # Pydantic returns 422 for validation errors
assert "detail" in response.json()

# Test 2: Missing JSON body should not crash
response = client.post(f"/close/{node_id}")
assert response.status_code == 422 # Pydantic returns 422 for validation errors
assert "detail" in response.json()
22 changes: 22 additions & 0 deletions tests/test_large_data_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
Tests for large data handling and resource limit bugs.
"""


def test_large_data_resource_limits(client):
"""Server should handle large data with proper resource limits."""

# Test: Huge payload (20MB) - should be rejected as too large
response = client.post("/upload")
assert response.status_code == 200
node_id = response.json()["node_id"]

huge_payload = b"\x00" * (20 * 1024 * 1024) # 20MB (exceeds 16MB limit)
response = client.post(
f"/upload/{node_id}",
content=huge_payload,
headers={"Content-Type": "application/octet-stream"},
)
# Should be rejected with 413 Payload Too Large due to size limits
assert response.status_code == 413
assert "Payload too large" in response.json()["detail"]