aiohttp не проходит через куки при выполнении массовых запросов агрегатов - PullRequest
0 голосов
/ 11 января 2019

Я работаю над службой шлюза API с aiohttp, которая выполняет совокупную операцию для ряда внутренних служб и упаковывает их в групповой ответ 207.

Внутренне мы используем файл cookie сеанса Django в качестве основного средства аутентификации (планируем перейти на JWT во втором квартале этого года), что означает, что для сервисов, получающих доступ к этому шлюзу, мне нужно пройти через cookie из запрос браузера или API остальных.

Кажется, что aiohttp не делает этого вообще, и при этом не делает доступными какие-либо файлы cookie в заголовках Postman Cookies. Однако, если я попал в сервис с теми же файлами cookie, я получу ожидаемый ответ.

Код сейчас:

class AggregateView(BaseView):
    async def post(self):
        requests = await self.request.json()
        logger.debug(f'Making [{len(requests)}] service calls at [{datetime.now()}]')
        queries = []
        async with ClientSession(cookie_jar=CookieJar(unsafe=True)) as session:
            for req in requests:
                queries.append(self._retrieve(req, session))

            logger.debug(f'Async calls created [{datetime.now()}]')
            results = await asyncio.gather(*queries)
            logger.debug(f'Async calls completed and aggregated [{datetime.now()}]')

        return web.json_response(
                status=207,
                data=results
        )

class BaseView(web.View):
    @cache
    async def _retrieve(self, req, session):
        """
        Unpacks a request body and executes the appropriate service call
        :param request: request body as dictionary
        :param session: aiohttp ClientSession
        :return:
        """
        url, path, query_params = await self._unpack_request(req)

        logger.info(f'{url}/{path}\t{query_params}')
        logger.info(f'Starting request for url [{url}/{path}] at [{datetime.now()}]')
        logger.info(f'with query params {query_params}')

        func = session.get
        params = {
            'url': f'{url}/{path}',
            'params': query_params,
            'headers': {
                'Vary': 'Accept, Origin, Accept, Accept-Encoding, Authorization, Cookie'
            }
        }

        async with AsyncBreaker(circuit_breaker, params['url'], func, **params) as resp:
            logger.info(f'received request for url [{url}/{path}] at [{datetime.now()}]')
            logger.info(resp.headers)
            logger.info(resp.cookies)
            return {
                'url': f'{url}/{path}',
                'status': resp.status,
                'response': await resp.json()
            }


def cache(func):
    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        request = args[1]
        access_logger.info(args[2].cookie_jar._cookies.keys())

        requested_service = request.get('service').upper()
        service = SERVICES.get(requested_service)
        if service and service.get('cache'):
            access_logger.info('This response is cacheable.')
            pass

        return await func(*args, **kwargs)

    return wrapper

dict_keys([]) # output in #cache for cookie jar inspection
This response is cacheable.
Starting request for url [https://service.company.com/api/v1/home-editorial/]
 at [2019-01-11 15:01:03.983993]
with query params None
received request for url [https://service.company.com/api/v1/home-editorial/]
 at [2019-01-11 15:01:04.434696]
Header output: <CIMultiDictProxy('Server': 'nginx/1.11.3', 'Content-Type': 
'application/json', 'Www-Authenticate': 'Token', 'Allow': 'GET, HEAD, 
OPTIONS', 'X-Frame-Options': 'SAMEORIGIN', 'Strict-Transport-Security': 
'max-age=15724800; includeSubDomains; preload', 'Accept-Ranges': 'bytes',
 'Content-Length': '58', 'Accept-Ranges': 'bytes', 'Date': 
'Fri, 11 Jan 2019 15:01:04 GMT', 'Via': '1.1 varnish', 'Connection': 
'keep-alive', 'X-Client-IP': '192.150.73.149', 'X-Served-By': 
'cache-bos8232-BOS', 'X-Cache': 'MISS', 'X-Cache-Hits': '0', 
'X-Timer': 'S1547218864.246313,VS0,VE152', 'Vary': 'Accept, Origin')>

Примечания:

  • AsyncBreaker использует aiobreaker для использования шаблона автоматического выключателя для защиты служб от перегрузки
  • @cache в настоящее время декоратор просто упаковывает метод извлечения и используется для проверки атрибутов AggregateView и ClientSession. Он будет более полнофункциональным с aiocache, как только эта проблема будет решена.
...