diff --git a/services/web/server/src/simcore_service_webserver/socketio/handlers.py b/services/web/server/src/simcore_service_webserver/socketio/handlers.py index 4e28450a6e2..63e782429f7 100644 --- a/services/web/server/src/simcore_service_webserver/socketio/handlers.py +++ b/services/web/server/src/simcore_service_webserver/socketio/handlers.py @@ -11,14 +11,15 @@ from typing import Dict, List, Optional from aiohttp import web -from socketio.exceptions import ConnectionRefusedError as socket_io_connection_error from servicelib.observer import observe from servicelib.utils import fire_and_forget_task, logged_gather +from socketio.exceptions import ConnectionRefusedError as SocketIOConnectionError from ..login.decorators import RQT_USERID_KEY, login_required from ..resource_manager.websocket_manager import managed_resource from .config import get_socket_server +from .handlers_utils import register_socketio_handler ANONYMOUS_USER_ID = -1 _SOCKET_IO_AIOHTTP_REQUEST_KEY = "aiohttp.request" @@ -26,6 +27,7 @@ log = logging.getLogger(__file__) +@register_socketio_handler async def connect(sid: str, environ: Dict, app: web.Application) -> bool: """socketio reserved handler for when the fontend connects through socket.io @@ -42,7 +44,9 @@ async def connect(sid: str, environ: Dict, app: web.Application) -> bool: try: await authenticate_user(sid, app, request) except web.HTTPUnauthorized: - raise socket_io_connection_error("authentification failed") + raise SocketIOConnectionError("authentification failed") + except Exception as exc: # pylint: disable=broad-except + raise SocketIOConnectionError(f"Unexpected error: {exc}") return True @@ -107,6 +111,7 @@ async def user_logged_out( fire_and_forget_task(disconnect_other_sockets(sio, sockets)) +@register_socketio_handler async def disconnect(sid: str, app: web.Application) -> None: """socketio reserved handler for when the socket.io connection is disconnected. diff --git a/services/web/server/src/simcore_service_webserver/socketio/handlers_utils.py b/services/web/server/src/simcore_service_webserver/socketio/handlers_utils.py index e97b26ac1e3..0faba48f7a5 100644 --- a/services/web/server/src/simcore_service_webserver/socketio/handlers_utils.py +++ b/services/web/server/src/simcore_service_webserver/socketio/handlers_utils.py @@ -7,6 +7,9 @@ from .config import APP_CLIENT_SOCKET_DECORATED_HANDLERS_KEY, get_socket_server +socketio_handlers_registry = [] + + def socket_io_handler(app: web.Application): """this decorator allows passing additional paramters to python-socketio compatible handlers. I.e. python-socketio handler expect functions of type `async def function(sid, *args, **kwargs)` @@ -18,6 +21,7 @@ def decorator(func): async def wrapped(*args, **kwargs): return await func(*args, **kwargs, app=app) + return wrapped return decorator @@ -33,18 +37,34 @@ def has_socket_io_handler_signature(fun) -> bool: def register_handlers(app: web.Application, module: ModuleType): sio = get_socket_server(app) - predicate = ( - lambda obj: inspect.isfunction(obj) - and has_socket_io_handler_signature(obj) - and inspect.iscoroutinefunction(obj) - and inspect.getmodule(obj) == module - ) - member_fcts = inspect.getmembers(module, predicate) + member_fcts = [ + fct for fct in socketio_handlers_registry if inspect.getmodule(fct) == module + ] # convert handler partial_fcts = [ - socket_io_handler(app)(func_handler) for _, func_handler in member_fcts + socket_io_handler(app)(func_handler) for func_handler in member_fcts ] app[APP_CLIENT_SOCKET_DECORATED_HANDLERS_KEY] = partial_fcts # register the fcts for func in partial_fcts: sio.on(func.__name__, handler=func) + + +def register_socketio_handler(func: callable) -> callable: + """this decorator appends handlers to a registry if they fit certain rules + + :param func: the function to call + :type func: callable + :return: the function to call + :rtype: callable + """ + is_handler = ( + inspect.isfunction(func) + and has_socket_io_handler_signature(func) + and inspect.iscoroutinefunction(func) + ) + if is_handler: + socketio_handlers_registry.append(func) + else: + raise SyntaxError("the function shall be of type fct(*args, app: web.Application") + return func