From 940ae776020ffeef8488706548ce4ef075c52a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Fri, 31 May 2024 11:16:37 +0200 Subject: [PATCH] settings: Make the websocket settings clearer. It is now using `WEBSOCKET_BACK_HOST`, `WEBSOCKET_BACK_PORT` and `WEBSOCKET_FRONT_URI`. We need to take in consideration that the "front" WebSocket address (that clients will connect to) might be different than the "back" ip and port which are bound in the host. This happens for instance for reverse proxies, or when running inside a container. We considered using a `WEBSOCKET_TLS` setting, to try guessing the "front" address based on `WEBSOCKET_HOST`, `WEBSOCKET_PORT` and `WEBSOCKET_TLS`, but as the back and front address can differ, this would need to introduce a `WEBSOCKET_URI` in any case, so we went with just using it, and not adding an extra `WEBSOCKET_TLS`. --- docs/config/settings.md | 41 ++++++++++++++++++++++++++++------------- umap/settings/base.py | 12 ++++++------ umap/tests/settings.py | 4 ++-- umap/views.py | 2 +- umap/ws.py | 19 ++++++++++++------- 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/docs/config/settings.md b/docs/config/settings.md index 48157219..7e867ab0 100644 --- a/docs/config/settings.md +++ b/docs/config/settings.md @@ -271,12 +271,15 @@ Otherwise, use any valid [python-social-auth configuration](https://python-socia #### WEBSOCKET_ENABLED -A websocket server is packaged with uMap, and can be turned-on to activate "real-time collaboration". -In practice, you would need to run the websocket server and specify a set of settings. +A WebSocket server is packaged with uMap, and can be turned-on to activate +"real-time collaboration". In practice, in order to enable it, a few settings +are exposed. -Turning this setting to `True` **will make a switch available** on each map, to enable "real-time collaboration". +Setting `WEBSOCKET_ENABLED` to `True` will **not** enable real-time +collaboration on all the maps served by the server. Instead, a switch will be +available in the "advanced properties" of the map. -You can run the websocket server with this command: +The websocket server can be run with the following command: ```bash python -m umap.ws @@ -286,19 +289,31 @@ Configuration example: ```python WEBSOCKET_ENABLED = True -WEBSOCKET_HOST = "localhost" -WEBSOCKET_PORT = 8002 -WEBSOCKET_URI = "ws://localhost:8002" +WEBSOCKET_BACK_HOST = "localhost" +WEBSOCKET_BACK_PORT = 8002 +WEBSOCKET_FRONT_URI = "ws://localhost:8002" ``` These settings can also be set with the (same names) environment variables. -#### WEBSOCKET_HOST -#### WEBSOCKET_PORT +#### WEBSOCKET_BACK_HOST +#### WEBSOCKET_BACK_PORT -The host and port for the websocket server. +The internal host and port the websocket server will connect to. -#### WEBSOCKET_URI +#### WEBSOCKET_FRONT_URI -The connection string that will be used by the client to connect to the websocket server. -Use `wss://host:port` if the server is behind TLS, and `ws://host:port` otherwise. +The connection string that will be used by the client to connect to the +websocket server. In practice, as it's useful to put the WebSocket server behind +TLS encryption, the values defined by `WEBSOCKET_FRONT_URI` are different than +the values defined by `WEBSOCKET_BACK_PORT` and `WEBSOCKET_BACK_HOST`. + +This value is comprised of three parts: + +``` +protocol://host:port +``` + +- `protocol`: can either be `ws` for plain unencrypted WebSockets, or `wss` when using TLS encryption. +- `host`: is the address where the connection will be sent. It should be public facing. +- `port`: is the port that is open on the host. diff --git a/umap/settings/base.py b/umap/settings/base.py index 63c42e1d..7d926869 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -1,11 +1,14 @@ """Base settings shared by all environments""" # Import global settings to make it easier to extend settings. +import os from email.utils import parseaddr import environ from django.conf.locale import LANG_INFO +import umap as project_module + env = environ.Env() # ============================================================================= @@ -137,9 +140,6 @@ FORM_RENDERER = "django.forms.renderers.DjangoTemplates" # Calculation of directories relative to the project module location # ============================================================================= -import os - -import umap as project_module PROJECT_DIR = os.path.dirname(os.path.realpath(project_module.__file__)) @@ -310,6 +310,6 @@ LOGGING = { # WebSocket configuration WEBSOCKET_ENABLED = env.bool("WEBSOCKET_ENABLED", default=False) -WEBSOCKET_HOST = env("WEBSOCKET_HOST", default="localhost") -WEBSOCKET_PORT = env.int("WEBSOCKET_PORT", default=8001) -WEBSOCKET_URI = env("WEBSOCKET_URI", default="ws://localhost:8001") +WEBSOCKET_BACK_HOST = env("WEBSOCKET_BACK_HOST", default="localhost") +WEBSOCKET_BACK_PORT = env.int("WEBSOCKET_BACK_PORT", default=8001) +WEBSOCKET_FRONT_URI = env("WEBSOCKET_FRONT_URI", default="ws://localhost:8001") diff --git a/umap/tests/settings.py b/umap/tests/settings.py index 4a5f0130..b776c083 100644 --- a/umap/tests/settings.py +++ b/umap/tests/settings.py @@ -27,5 +27,5 @@ PASSWORD_HASHERS = [ WEBSOCKET_ENABLED = True -WEBSOCKET_PORT = "8010" -WEBSOCKET_URI = "ws://localhost:8010" +WEBSOCKET_BACK_PORT = "8010" +WEBSOCKET_FRONT_URI = "ws://localhost:8010" diff --git a/umap/views.py b/umap/views.py index d3046281..9a3e2dd2 100644 --- a/umap/views.py +++ b/umap/views.py @@ -504,7 +504,7 @@ class MapDetailMixin: "umap_version": VERSION, "featuresHaveOwner": settings.UMAP_DEFAULT_FEATURES_HAVE_OWNERS, "websocketEnabled": settings.WEBSOCKET_ENABLED, - "websocketURI": settings.WEBSOCKET_URI, + "websocketURI": settings.WEBSOCKET_FRONT_URI, } created = bool(getattr(self, "object", None)) if (created and self.object.owner) or (not created and not user.is_anonymous): diff --git a/umap/ws.py b/umap/ws.py index 22d82f2f..54345af7 100644 --- a/umap/ws.py +++ b/umap/ws.py @@ -83,15 +83,20 @@ async def handler(websocket): async def main(): if not settings.WEBSOCKET_ENABLED: - print("WEBSOCKET_ENABLED should be set to True to run the WebSocket Server") + msg = ( + "WEBSOCKET_ENABLED should be set to True to run the WebSocket Server. " + "See the documentation at " + "https://docs.umap-project.org/en/stable/config/settings/#websocket_enabled " + "for more information." + ) + print(msg) exit(1) - async with serve(handler, settings.WEBSOCKET_HOST, settings.WEBSOCKET_PORT): - print( - ( - f"Waiting for connections on {settings.WEBSOCKET_HOST}:{settings.WEBSOCKET_PORT}" - ) - ) + host = settings.WEBSOCKET_BACK_HOST + port = settings.WEBSOCKET_BACK_PORT + + async with serve(handler, host, port): + print(f"Waiting for connections on {host}:{port}") await asyncio.Future() # run forever