diff --git a/findmy/reports/anisette.py b/findmy/reports/anisette.py index bcf2bf1..d56f52d 100644 --- a/findmy/reports/anisette.py +++ b/findmy/reports/anisette.py @@ -205,7 +205,7 @@ class RemoteAnisetteProvider(BaseAnisetteProvider): if self._anisette_data is None or time.time() >= self._anisette_data_expires_at: logging.info("Fetching anisette data from %s", self._server_url) - r = await self._http.get(self._server_url) + r = await self._http.get(self._server_url, auto_retry=True) self._anisette_data = r.json() self._anisette_data_expires_at = time.time() + self._ANISETTE_DATA_VALID_FOR diff --git a/findmy/util/http.py b/findmy/util/http.py index a6da8ac..f954e49 100644 --- a/findmy/util/http.py +++ b/findmy/util/http.py @@ -2,10 +2,12 @@ from __future__ import annotations +import asyncio import json import logging from typing import Any, TypedDict, cast +import aiohttp from aiohttp import BasicAuth, ClientSession, ClientTimeout from typing_extensions import Unpack, override @@ -18,6 +20,7 @@ logging.getLogger(__name__) class _RequestOptions(TypedDict, total=False): json: dict[str, Any] | None headers: dict[str, str] + auto_retry: bool data: bytes @@ -108,13 +111,32 @@ class HttpSession(Closable): kwargs["auth"] = BasicAuth(auth[0], auth[1]) options = cast(_AiohttpRequestOptions, kwargs) - async with await session.request( - method, - url, - ssl=False, - **options, - ) as r: - return HttpResponse(r.status, await r.content.read()) + auto_retry = kwargs.pop("auto_retry", False) + + retry_count = 1 + while True: # if auto_retry is set, raise for status and retry on error + try: + async with await session.request( + method, + url, + ssl=False, + raise_for_status=auto_retry, + **options, + ) as r: + return HttpResponse(r.status, await r.content.read()) + except aiohttp.ClientError as e: # noqa: PERF203 + if not auto_retry or retry_count > 3: + raise e from None + + retry_after = 5 * retry_count + logging.warning( + "Error while making HTTP request; retrying after %i seconds. %s", + retry_after, + e, + ) + await asyncio.sleep(retry_after) + + retry_count += 1 async def get(self, url: str, **kwargs: Unpack[_HttpRequestOptions]) -> HttpResponse: """Alias for `HttpSession.request("GET", ...)`."""