google-mcp/lib/rate_limiter.py
Jarian Cottingham 645d8820f1 fix: repair middleware wiring, input validation, and resource lifecycle
- Wrap RateLimitMiddleware in Middleware() - bare class broke all requests (500)
- Clamp/validate num param - ?num=abc no longer returns 500
- Query length cap (500), result counts clamped to 1..20
- Wire lifespan into Starlette so shared httpx client closes on shutdown
- Purge stale per-IP rate limit entries to bound memory growth
- URL-encode queries in the Playwright scraper path
- Lazy lib imports so the package works without optional playwright
- Add pyproject.toml (activates ruff/pytest/bandit in CI), README, LICENSE
- Remove committed __pycache__, add .gitignore/.dockerignore
- Fix SearXNG healthcheck path (/health -> /healthz) and compose docs
2026-08-20 23:27:27 +00:00

33 lines
1.0 KiB
Python

import asyncio
import logging
import time
from typing import Optional
logger = logging.getLogger(__name__)
class RateLimiter:
"""
Enforces minimum delay between requests to avoid throttling.
Thread-safe for async use within a single event loop.
"""
def __init__(self, min_interval_seconds: float = 3.0):
self.min_interval = min_interval_seconds
self._last_request_time: Optional[float] = None
self._lock = asyncio.Lock()
async def acquire(self):
async with self._lock:
now = time.monotonic()
if self._last_request_time is not None:
elapsed = now - self._last_request_time
wait_time = self.min_interval - elapsed
if wait_time > 0:
logger.info(
f"Rate limiter: waiting {wait_time:.1f}s "
f"(min interval: {self.min_interval}s)"
)
await asyncio.sleep(wait_time)
self._last_request_time = time.monotonic()