"""Module for HttpClientSession class.""" import asyncio from typing import Any, Dict, Optional, Tuple, Union import aiohttp from .deviceconfig import DeviceConfig from .exceptions import ConnectionException, SmartDeviceException, TimeoutException from .json import loads as json_loads def get_cookie_jar() -> aiohttp.CookieJar: """Return a new cookie jar with the correct options for device communication.""" return aiohttp.CookieJar(unsafe=True, quote_cookie=False) class HttpClient: """HttpClient Class.""" def __init__(self, config: DeviceConfig) -> None: self._config = config self._client: aiohttp.ClientSession = None self._jar = aiohttp.CookieJar(unsafe=True, quote_cookie=False) self._last_url = f"http://{self._config.host}/" @property def client(self) -> aiohttp.ClientSession: """Return the underlying http client.""" if self._config.http_client and issubclass( self._config.http_client.__class__, aiohttp.ClientSession ): return self._config.http_client if not self._client: self._client = aiohttp.ClientSession(cookie_jar=get_cookie_jar()) return self._client async def post( self, url: str, *, params: Optional[Dict[str, Any]] = None, data: Optional[bytes] = None, json: Optional[Dict] = None, headers: Optional[Dict[str, str]] = None, cookies_dict: Optional[Dict[str, str]] = None, ) -> Tuple[int, Optional[Union[Dict, bytes]]]: """Send an http post request to the device.""" response_data = None self._last_url = url self.client.cookie_jar.clear() try: resp = await self.client.post( url, params=params, data=data, json=json, timeout=self._config.timeout, cookies=cookies_dict, headers=headers, ) async with resp: if resp.status == 200: response_data = await resp.read() if json: response_data = json_loads(response_data.decode()) except (aiohttp.ServerDisconnectedError, aiohttp.ClientOSError) as ex: raise ConnectionException( f"Unable to connect to the device: {self._config.host}: {ex}", ex ) from ex except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as ex: raise TimeoutException( "Unable to query the device, " + f"timed out: {self._config.host}: {ex}", ex, ) from ex except Exception as ex: raise SmartDeviceException( f"Unable to query the device: {self._config.host}: {ex}", ex ) from ex return resp.status, response_data def get_cookie(self, cookie_name: str) -> Optional[str]: """Return the cookie with cookie_name.""" if cookie := self.client.cookie_jar.filter_cookies(self._last_url).get( cookie_name ): return cookie.value return None async def close(self) -> None: """Close the client.""" client = self._client self._client = None if client: await client.close()