Files
intercept/utils/sse.py
James Smith 1398a5dedd Major security and code quality improvements
Security:
- Add input validation for all API endpoints (frequency, lat/lon, device, gain, ppm)
- Add HTML escaping utility to prevent XSS attacks
- Add path traversal protection for log file configuration
- Add proper HTTP status codes for error responses (400, 409, 503)

Performance:
- Reduce SSE keepalive overhead (30s interval instead of 1s)
- Add centralized SSE stream utility with optimized keepalive
- Add DataStore class for thread-safe data with automatic cleanup

New Features:
- Add data export endpoints (/export/aircraft, /export/wifi, /export/bluetooth)
- Support for both JSON and CSV export formats
- Add process cleanup on application exit (atexit handlers)
- Label Iridium module as demo mode with clear warnings

Code Quality:
- Create utils/validation.py for centralized input validation
- Create utils/sse.py for SSE stream utilities
- Create utils/cleanup.py for memory management
- Add safe_terminate() and register_process() for process management
- Improve error handling with proper logging throughout routes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 19:24:40 +00:00

90 lines
2.1 KiB
Python

"""Server-Sent Events (SSE) utilities."""
from __future__ import annotations
import json
import queue
import time
from typing import Any, Generator
def sse_stream(
data_queue: queue.Queue,
timeout: float = 1.0,
keepalive_interval: float = 30.0,
stop_check: callable = None
) -> Generator[str, None, None]:
"""
Generate SSE stream from a queue.
Args:
data_queue: Queue to read messages from
timeout: Queue get timeout in seconds
keepalive_interval: Seconds between keepalive messages
stop_check: Optional callable that returns True to stop the stream
Yields:
SSE formatted strings
"""
last_keepalive = time.time()
while True:
# Check if we should stop
if stop_check and stop_check():
break
try:
msg = data_queue.get(timeout=timeout)
last_keepalive = time.time()
yield format_sse(msg)
except queue.Empty:
# Send keepalive if enough time has passed
now = time.time()
if now - last_keepalive >= keepalive_interval:
yield format_sse({'type': 'keepalive'})
last_keepalive = now
def format_sse(data: dict[str, Any] | str, event: str | None = None) -> str:
"""
Format data as SSE message.
Args:
data: Data to send (will be JSON encoded if dict)
event: Optional event name
Returns:
SSE formatted string
"""
if isinstance(data, dict):
data = json.dumps(data)
lines = []
if event:
lines.append(f"event: {event}")
lines.append(f"data: {data}")
lines.append("")
lines.append("")
return '\n'.join(lines)
def clear_queue(q: queue.Queue) -> int:
"""
Clear all items from a queue.
Args:
q: Queue to clear
Returns:
Number of items cleared
"""
count = 0
while True:
try:
q.get_nowait()
count += 1
except queue.Empty:
break
return count