Changeset View
Changeset View
Standalone View
Standalone View
swh/core/api/asynchronous.py
import json | import json | ||||
import logging | import logging | ||||
import pickle | import pickle | ||||
import sys | import sys | ||||
import traceback | import traceback | ||||
from collections import OrderedDict | |||||
import multidict | |||||
import aiohttp.web | import aiohttp.web | ||||
from deprecated import deprecated | from deprecated import deprecated | ||||
import multidict | |||||
from .serializers import msgpack_dumps, msgpack_loads, SWHJSONDecoder | from .serializers import msgpack_dumps, msgpack_loads | ||||
from .serializers import SWHJSONDecoder, SWHJSONEncoder | |||||
try: | |||||
from aiohttp_utils import negotiation, Response | |||||
except ImportError: | |||||
from aiohttp import Response | |||||
negotiation = None | |||||
def encode_data_server(data, **kwargs): | def encode_msgpack(data, **kwargs): | ||||
return aiohttp.web.Response( | return aiohttp.web.Response( | ||||
body=msgpack_dumps(data), | body=msgpack_dumps(data), | ||||
headers=multidict.MultiDict({'Content-Type': 'application/x-msgpack'}), | headers=multidict.MultiDict( | ||||
{'Content-Type': 'application/x-msgpack'}), | |||||
**kwargs | **kwargs | ||||
) | ) | ||||
if negotiation is None: | |||||
encode_data_server = encode_msgpack | |||||
else: | |||||
encode_data_server = Response | |||||
def render_msgpack(request, data): | |||||
return msgpack_dumps(data) | |||||
def render_json(request, data): | |||||
return json.dumps(data, cls=SWHJSONEncoder) | |||||
async def decode_request(request): | async def decode_request(request): | ||||
content_type = request.headers.get('Content-Type') | content_type = request.headers.get('Content-Type').split(';')[0].strip() | ||||
data = await request.read() | data = await request.read() | ||||
if not data: | if not data: | ||||
return {} | return {} | ||||
if content_type == 'application/x-msgpack': | if content_type == 'application/x-msgpack': | ||||
r = msgpack_loads(data) | r = msgpack_loads(data) | ||||
elif content_type == 'application/json': | elif content_type == 'application/json': | ||||
r = json.loads(data.decode(), cls=SWHJSONDecoder) | r = json.loads(data.decode(), cls=SWHJSONDecoder) | ||||
else: | else: | ||||
raise ValueError('Wrong content type `%s` for API request' | raise ValueError('Wrong content type `%s` for API request' | ||||
% content_type) | % content_type) | ||||
return r | return r | ||||
async def error_middleware(app, handler): | async def error_middleware(app, handler): | ||||
async def middleware_handler(request): | async def middleware_handler(request): | ||||
try: | try: | ||||
return (await handler(request)) | return await handler(request) | ||||
except Exception as e: | except Exception as e: | ||||
if isinstance(e, aiohttp.web.HTTPException): | if isinstance(e, aiohttp.web.HTTPException): | ||||
raise | raise | ||||
logging.exception(e) | logging.exception(e) | ||||
exception = traceback.format_exception(*sys.exc_info()) | exception = traceback.format_exception(*sys.exc_info()) | ||||
res = {'exception': exception, | res = {'exception': exception, | ||||
'exception_pickled': pickle.dumps(e)} | 'exception_pickled': pickle.dumps(e)} | ||||
return encode_data_server(res, status=500) | return encode_data_server(res, status=500) | ||||
return middleware_handler | return middleware_handler | ||||
class RPCServerApp(aiohttp.web.Application): | class RPCServerApp(aiohttp.web.Application): | ||||
def __init__(self, *args, middlewares=(), **kwargs): | def __init__(self, *args, middlewares=(), **kwargs): | ||||
middlewares = (error_middleware,) + middlewares | middlewares = (error_middleware,) + middlewares | ||||
if negotiation: | |||||
# renderers are sorted in order of increasing desirability (!) | |||||
# see mimeparse.best_match() docstring. | |||||
renderers = OrderedDict([ | |||||
('application/json', render_json), | |||||
('application/x-msgpack', render_msgpack), | |||||
]) | |||||
nego_middleware = negotiation.negotiation_middleware( | |||||
renderers=renderers, | |||||
force_rendering=True) | |||||
middlewares = (nego_middleware,) + middlewares | |||||
super().__init__(*args, middlewares=middlewares, **kwargs) | super().__init__(*args, middlewares=middlewares, **kwargs) | ||||
@deprecated(version='0.0.64', | @deprecated(version='0.0.64', | ||||
reason='Use the RPCServerApp instead') | reason='Use the RPCServerApp instead') | ||||
class SWHRemoteAPI(RPCServerApp): | class SWHRemoteAPI(RPCServerApp): | ||||
pass | pass |