mirror of
https://github.com/gogcom/galaxy-integrations-python-api.git
synced 2026-01-03 20:38:17 -05:00
147 lines
5.1 KiB
Python
147 lines
5.1 KiB
Python
"""
|
|
This module standarize http traffic and the error handling for further communication with the GOG Galaxy 2.0.
|
|
|
|
It is recommended to use provided convenient methods for HTTP requests, especially when dealing with authorized sessions.
|
|
Examplary simple web service could looks like:
|
|
|
|
.. code-block:: python
|
|
|
|
import logging
|
|
from galaxy.http import create_client_session, handle_exception
|
|
|
|
class BackendClient:
|
|
AUTH_URL = 'my-integration.com/auth'
|
|
HEADERS = {
|
|
"My-Custom-Header": "true",
|
|
}
|
|
def __init__(self):
|
|
self._session = create_client_session(headers=self.HEADERS)
|
|
|
|
async def authenticate(self):
|
|
await self._session.request('POST', self.AUTH_URL)
|
|
|
|
async def close(self):
|
|
# to be called on plugin shutdown
|
|
await self._session.close()
|
|
|
|
async def _authorized_request(self, method, url, *args, **kwargs):
|
|
with handle_exceptions():
|
|
return await self._session.request(method, url, *args, **kwargs)
|
|
"""
|
|
|
|
import asyncio
|
|
import ssl
|
|
from contextlib import contextmanager
|
|
from http import HTTPStatus
|
|
|
|
import aiohttp
|
|
import certifi
|
|
import logging
|
|
|
|
from galaxy.api.errors import (
|
|
AccessDenied, AuthenticationRequired, BackendTimeout, BackendNotAvailable, BackendError, NetworkError,
|
|
TooManyRequests, UnknownBackendResponse, UnknownError
|
|
)
|
|
|
|
|
|
#: Default limit of the simultaneous connections for ssl connector.
|
|
DEFAULT_LIMIT = 20
|
|
#: Default timeout in seconds used for client session.
|
|
DEFAULT_TIMEOUT = 60
|
|
|
|
|
|
class HttpClient:
|
|
"""
|
|
.. deprecated:: 0.41
|
|
Use http module functions instead
|
|
"""
|
|
def __init__(self, limit=DEFAULT_LIMIT, timeout=aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT), cookie_jar=None):
|
|
connector = create_tcp_connector(limit=limit)
|
|
self._session = create_client_session(connector=connector, timeout=timeout, cookie_jar=cookie_jar)
|
|
|
|
async def close(self):
|
|
"""Closes connection. Should be called in :meth:`~galaxy.api.plugin.Plugin.shutdown`"""
|
|
await self._session.close()
|
|
|
|
async def request(self, method, url, *args, **kwargs):
|
|
with handle_exception():
|
|
return await self._session.request(method, url, *args, **kwargs)
|
|
|
|
|
|
def create_tcp_connector(*args, **kwargs) -> aiohttp.TCPConnector:
|
|
"""
|
|
Creates TCP connector with resonable defaults.
|
|
For details about available parameters refer to
|
|
`aiohttp.TCPConnector <https://docs.aiohttp.org/en/stable/client_reference.html#tcpconnector>`_
|
|
"""
|
|
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
ssl_context.load_verify_locations(certifi.where())
|
|
kwargs.setdefault("ssl", ssl_context)
|
|
kwargs.setdefault("limit", DEFAULT_LIMIT)
|
|
# due to https://github.com/python/mypy/issues/4001
|
|
return aiohttp.TCPConnector(*args, **kwargs) # type: ignore
|
|
|
|
|
|
def create_client_session(*args, **kwargs) -> aiohttp.ClientSession:
|
|
"""
|
|
Creates client session with resonable defaults.
|
|
For details about available parameters refer to
|
|
`aiohttp.ClientSession <https://docs.aiohttp.org/en/stable/client_reference.html>`_
|
|
|
|
Examplary customization:
|
|
|
|
.. code-block:: python
|
|
|
|
from galaxy.http import create_client_session, create_tcp_connector
|
|
|
|
session = create_client_session(
|
|
headers={
|
|
"Keep-Alive": "true"
|
|
},
|
|
connector=create_tcp_connector(limit=40),
|
|
timeout=100)
|
|
"""
|
|
kwargs.setdefault("connector", create_tcp_connector())
|
|
kwargs.setdefault("timeout", aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT))
|
|
kwargs.setdefault("raise_for_status", True)
|
|
# due to https://github.com/python/mypy/issues/4001
|
|
return aiohttp.ClientSession(*args, **kwargs) # type: ignore
|
|
|
|
|
|
@contextmanager
|
|
def handle_exception():
|
|
"""
|
|
Context manager translating network related exceptions
|
|
to custom :mod:`~galaxy.api.errors`.
|
|
"""
|
|
try:
|
|
yield
|
|
except asyncio.TimeoutError:
|
|
raise BackendTimeout()
|
|
except aiohttp.ServerDisconnectedError:
|
|
raise BackendNotAvailable()
|
|
except aiohttp.ClientConnectionError:
|
|
raise NetworkError()
|
|
except aiohttp.ContentTypeError:
|
|
raise UnknownBackendResponse()
|
|
except aiohttp.ClientResponseError as error:
|
|
if error.status == HTTPStatus.UNAUTHORIZED:
|
|
raise AuthenticationRequired()
|
|
if error.status == HTTPStatus.FORBIDDEN:
|
|
raise AccessDenied()
|
|
if error.status == HTTPStatus.SERVICE_UNAVAILABLE:
|
|
raise BackendNotAvailable()
|
|
if error.status == HTTPStatus.TOO_MANY_REQUESTS:
|
|
raise TooManyRequests()
|
|
if error.status >= 500:
|
|
raise BackendError()
|
|
if error.status >= 400:
|
|
logging.warning(
|
|
"Got status %d while performing %s request for %s",
|
|
error.status, error.request_info.method, str(error.request_info.url)
|
|
)
|
|
raise UnknownError()
|
|
except aiohttp.ClientError:
|
|
logging.exception("Caught exception while performing request")
|
|
raise UnknownError()
|