Files
romm/backend/utils/context.py
Michael Manganiello fe1a9ce2a7 fix: Use aiohttp for RetroAchievements API calls
This change replaces the `httpx` client with `aiohttp` for the
RetroAchievements API service.

The main reason for this change is that `httpx` has an unavoidable log
line with `INFO` level, which includes the request full URL, containing
the user's API key.

`httpx` has had an
[open discussion](https://github.com/encode/httpx/discussions/2765)
regarding this security issue for almost two years.

The change to `aiohttp` is painless, and would allow us to migrate more
of the codebase to it in the future, to avoid leaking sensitive
information in logs.
2025-06-09 09:59:56 -03:00

51 lines
1.6 KiB
Python

from collections.abc import AsyncGenerator, Awaitable, Callable
from contextlib import asynccontextmanager
from contextvars import ContextVar, Token
from typing import TypeVar
import aiohttp
import httpx
from fastapi import Request, Response
_T = TypeVar("_T")
ctx_aiohttp_session: ContextVar[aiohttp.ClientSession] = ContextVar("aiohttp_session")
ctx_httpx_client: ContextVar[httpx.AsyncClient] = ContextVar("httpx_client")
@asynccontextmanager
async def set_context_var(
var: ContextVar[_T], value: _T
) -> AsyncGenerator[Token[_T], None]:
"""Temporarily set a context variables."""
token = var.set(value)
yield token
var.reset(token)
@asynccontextmanager
async def initialize_context() -> AsyncGenerator[None, None]:
"""Initialize context variables."""
async with (
aiohttp.ClientSession() as aiohttp_session,
httpx.AsyncClient() as httpx_client,
set_context_var(ctx_aiohttp_session, aiohttp_session),
set_context_var(ctx_httpx_client, httpx_client),
):
yield
async def set_context_middleware(
request: Request, call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
"""Initialize context variables in FastAPI request-response cycle.
This middleware is needed because the context initialized during the lifespan
process is not available in the request-response cycle.
"""
async with (
set_context_var(ctx_aiohttp_session, request.app.state.aiohttp_session),
set_context_var(ctx_httpx_client, request.app.state.httpx_client),
):
return await call_next(request)