Przeglądaj źródła

Use persistent httpx client (10s timeout) for Airbnb and Booking.com

Each GQL/REST call was opening a fresh AsyncClient — new TCP+TLS handshake
every time — with the default 5s timeout. Replace with a shared instance
per client (timeout=10s). The session-refresh client in _booking.py keeps
its own AsyncClient since it needs follow_redirects and a longer timeout.
ZiB 4 miesięcy temu
rodzic
commit
8f30e1c2e8
2 zmienionych plików z 26 dodań i 30 usunięć
  1. 6 7
      room_cal/service_api/_airbnb.py
  2. 20 23
      room_cal/service_api/_booking.py

+ 6 - 7
room_cal/service_api/_airbnb.py

@@ -86,6 +86,7 @@ class _AirbnbClient:
     self._months_inflight: dict[tuple[int, int], asyncio.Task] = {}
     self._refresh_lock = asyncio.Lock()
     self._refresh_task: asyncio.Task | None = None
+    self._http = httpx.AsyncClient(timeout=10.0)
 
   @classmethod
   def from_env(cls) -> '_AirbnbClient':
@@ -187,8 +188,7 @@ class _AirbnbClient:
       'extensions': json.dumps({'persistedQuery': {'version': 1, 'sha256Hash': h}}),
     }
     async def _attempt() -> httpx.Response:
-      async with httpx.AsyncClient() as client:
-        return await client.get(url, params=params, headers=self._headers(referer))
+      return await self._http.get(url, params=params, headers=self._headers(referer))
     try:
       r = await _attempt()
     except httpx.TimeoutException as e:
@@ -211,11 +211,10 @@ class _AirbnbClient:
     payload = {'operationName': op, 'variables': variables,
                'extensions': {'persistedQuery': {'version': 1, 'sha256Hash': h}}}
     async def _attempt() -> httpx.Response:
-      async with httpx.AsyncClient() as client:
-        return await client.post(
-          url, json=payload,
-          headers=self._headers(referer, {'Origin': 'https://www.airbnb.com'}),
-        )
+      return await self._http.post(
+        url, json=payload,
+        headers=self._headers(referer, {'Origin': 'https://www.airbnb.com'}),
+      )
     try:
       r = await _attempt()
     except (httpx.TransportError, OSError, ExceptionGroup) as e:

+ 20 - 23
room_cal/service_api/_booking.py

@@ -173,6 +173,7 @@ class _BookingClient:
     self._months_inflight: dict[tuple[int, int], asyncio.Task] = {}
     self._refresh_lock = asyncio.Lock()
     self._refresh_task: asyncio.Task | None = None
+    self._http = httpx.AsyncClient(timeout=10.0)
 
   @classmethod
   def from_env(cls) -> '_BookingClient':
@@ -386,12 +387,11 @@ class _BookingClient:
       'tlc': '1',
     }
     async def _do_request() -> httpx.Response:
-      async with httpx.AsyncClient() as client:
-        return await client.post(
-          f'{_BASE}/dml/graphql.json',
-          params=params,
-          json={'operationName': operation_name, 'variables': variables, 'query': query},
-          headers=self._msg_headers(),
+      return await self._http.post(
+        f'{_BASE}/dml/graphql.json',
+        params=params,
+        json={'operationName': operation_name, 'variables': variables, 'query': query},
+        headers=self._msg_headers(),
         )
     try:
       r = await _do_request()
@@ -421,9 +421,8 @@ class _BookingClient:
   async def _multipart_post(self, url: str, referer: str, body: dict, extra_headers: dict | None = None) -> dict:
     async def _attempt() -> httpx.Response:
       headers = self._rest_headers(referer, extra_headers)
-      async with httpx.AsyncClient() as client:
-        return await client.post(url, params=self._common_params(),
-                                 files={'request': (None, json.dumps(body))}, headers=headers)
+      return await self._http.post(url, params=self._common_params(),
+                                   files={'request': (None, json.dumps(body))}, headers=headers)
     try:
       r = await _attempt()
     except (httpx.TransportError, OSError, ExceptionGroup) as e:
@@ -441,14 +440,13 @@ class _BookingClient:
       referer = self._matrix_referer()
       params = {'view_mode': 'MATRIX', 'hotel_id': str(self._hotel_id),
                 'lang': self._lang, 'ses': self._session}
-      async with httpx.AsyncClient() as client:
-        return await client.post(
-          f'{_BASE}/dml/graphql.json',
-          params=params,
-          json={'operationName': 'updateRoomDateMutation', 'variables': variables,
-                'extensions': {}, 'query': _UPDATE_MUTATION},
-          headers=self._gql_headers(referer),
-        )
+      return await self._http.post(
+        f'{_BASE}/dml/graphql.json',
+        params=params,
+        json={'operationName': 'updateRoomDateMutation', 'variables': variables,
+              'extensions': {}, 'query': _UPDATE_MUTATION},
+        headers=self._gql_headers(referer),
+      )
     try:
       r = await _attempt()
     except (httpx.TransportError, OSError, ExceptionGroup) as e:
@@ -575,12 +573,11 @@ class _BookingClient:
         'room_id': str(self._room_id),
         'only_basic_info': '1',
       })
-      async with httpx.AsyncClient() as client:
-        return await client.post(
-          f'{_BASE}/fresa/extranet/calendar/get_reservations',
-          params=params,
-          headers=self._rest_headers(referer),
-        )
+      return await self._http.post(
+        f'{_BASE}/fresa/extranet/calendar/get_reservations',
+        params=params,
+        headers=self._rest_headers(referer),
+      )
     try:
       r = await _attempt()
     except (httpx.TransportError, OSError, ExceptionGroup) as e: