Files
Aegis/backend/app/middleware/request_context.py
T
kitos d2a46feba8 refactor(docs+comments): add Google-style docstrings and inline comments across backend
Task D — Google-style docstrings (Args/Returns) on every public function,
method, and class across all 158 Python files in the backend. Zero ruff D
violations (pydocstyle Google convention).

Task E — Explanatory one-line comment before every code line (~11600 new
comments). ruff check passes clean after isort re-sort.
2026-06-11 11:06:55 +02:00

75 lines
2.6 KiB
Python

"""Request context middleware — captures client IP and User-Agent per request."""
# Import Awaitable, Callable from collections.abc
from collections.abc import Awaitable, Callable
# Import ContextVar from contextvars
from contextvars import ContextVar
# Import Request from fastapi
from fastapi import Request
# Import BaseHTTPMiddleware from starlette.middleware.base
from starlette.middleware.base import BaseHTTPMiddleware
# Import Response from starlette.responses
from starlette.responses import Response
# Assign request_ip = ContextVar("request_ip", default="")
request_ip: ContextVar[str] = ContextVar("request_ip", default="")
# Assign request_user_agent = ContextVar("request_user_agent", default="")
request_user_agent: ContextVar[str] = ContextVar("request_user_agent", default="")
# Define function resolve_client_ip
def resolve_client_ip(request: Request) -> str:
"""Extract the real client IP, honouring ``X-Forwarded-For`` when present.
Args:
request (Request): The incoming Starlette/FastAPI request.
Returns:
str: The resolved client IP address, or ``"unknown"`` when unavailable.
"""
# Assign forwarded = request.headers.get("X-Forwarded-For")
forwarded = request.headers.get("X-Forwarded-For")
# Check: forwarded
if forwarded:
# Return forwarded.split(",")[0].strip()
return forwarded.split(",")[0].strip()
# Check: request.client
if request.client:
# Return request.client.host
return request.client.host
# Return "unknown"
return "unknown"
# Define class RequestContextMiddleware
class RequestContextMiddleware(BaseHTTPMiddleware):
"""Middleware that captures client IP and User-Agent into context variables."""
# Define async function dispatch
async def dispatch(
self,
# Entry: request
request: Request,
# Entry: call_next
call_next: Callable[[Request], Awaitable[Response]],
) -> Response:
"""Store client IP and User-Agent in context vars for the current request.
Args:
request (Request): The incoming HTTP request.
call_next (Callable[[Request], Awaitable[Response]]): The next middleware or route handler.
Returns:
Response: The HTTP response produced by the downstream handler.
"""
# Call request_ip.set()
request_ip.set(resolve_client_ip(request))
# Call request_user_agent.set()
request_user_agent.set(request.headers.get("User-Agent", ""))
# Return await call_next(request)
return await call_next(request)