From 3aefc9607d15636aaf8da328d7e192935a67cff9 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sun, 7 May 2023 00:42:57 +0200 Subject: [PATCH] Migrated from waitress to gunicorn. `waitress`, unlike `gunicorn`, doesn't provide an easy way to plug into a WSGI socket that can be used for the websocket interface. --- docs/source/conf.py | 1 + platypush/backend/http/__init__.py | 26 +++++------------- platypush/backend/http/wsgi/__init__.py | 21 +++++++++++++++ requirements.txt | 35 +++++++++++++------------ setup.py | 2 +- 5 files changed, 48 insertions(+), 37 deletions(-) create mode 100644 platypush/backend/http/wsgi/__init__.py diff --git a/docs/source/conf.py b/docs/source/conf.py index 1b7dd9227d..1ca21260e3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -201,6 +201,7 @@ autodoc_default_options = { } autodoc_mock_imports = [ + 'gunicorn', 'googlesamples.assistant.grpc.audio_helpers', 'google.assistant.embedded', 'google.assistant.library', diff --git a/platypush/backend/http/__init__.py b/platypush/backend/http/__init__.py index 29a30cf75f..897c4e7d9c 100644 --- a/platypush/backend/http/__init__.py +++ b/platypush/backend/http/__init__.py @@ -3,7 +3,7 @@ import pathlib import secrets import threading -from multiprocessing import Process +from multiprocessing import Process, cpu_count try: from websockets.exceptions import ConnectionClosed # type: ignore @@ -11,10 +11,9 @@ try: except ImportError: from websockets import ConnectionClosed, serve as websocket_serve # type: ignore -import waitress - from platypush.backend import Backend from platypush.backend.http.app import application +from platypush.backend.http.wsgi import WSGIApplicationWrapper from platypush.bus.redis import RedisBus from platypush.config import Config from platypush.context import get_or_create_event_loop @@ -174,7 +173,6 @@ class HttpBackend(Backend): ssl_capath=None, maps=None, secret_key_file=None, - flask_args=None, **kwargs, ): """ @@ -213,9 +211,6 @@ class HttpBackend(Backend): :param secret_key_file: Path to the file containing the secret key that will be used by Flask (default: ``~/.local/share/platypush/flask.secret.key``). :type secret_key_file: str - - :param flask_args: Extra key-value arguments that should be passed to the Flask service. - :type flask_args: dict[str, str] """ super().__init__(**kwargs) @@ -239,7 +234,6 @@ class HttpBackend(Backend): self.resource_dirs = {} self.active_websockets = set() - self.flask_args = flask_args or {} self.ssl_context = ( get_ssl_server_context( ssl_cert=ssl_cert, @@ -420,24 +414,18 @@ class HttpBackend(Backend): def _web_server_proc(self): def proc(): self.logger.info('Starting local web server on port %s', self.port) - kwargs = { - 'host': self.bind_address, - 'port': self.port, - } - assert isinstance( self.bus, RedisBus ), 'The HTTP backend only works if backed by a Redis bus' application.config['redis_queue'] = self.bus.redis_queue application.secret_key = self._get_secret_key() + kwargs = { + 'bind': f'{self.bind_address}:{self.port}', + 'workers': (cpu_count() * 2) + 1, + } - if self.ssl_context: - kwargs['ssl_context'] = self.ssl_context - if self.flask_args: - kwargs.update(self.flask_args) - - waitress.serve(application, **kwargs) + WSGIApplicationWrapper(f'{__package__}.app:application', kwargs).run() return proc diff --git a/platypush/backend/http/wsgi/__init__.py b/platypush/backend/http/wsgi/__init__.py new file mode 100644 index 0000000000..e304884bab --- /dev/null +++ b/platypush/backend/http/wsgi/__init__.py @@ -0,0 +1,21 @@ +from gunicorn.app.wsgiapp import WSGIApplication + + +class WSGIApplicationWrapper(WSGIApplication): + """ + Wrapper for the Flask application into a WSGI application. + """ + + def __init__(self, app_uri, options=None): + self.options = options or {} + self.app_uri = app_uri + super().__init__() + + def load_config(self): + config = { + key: value + for key, value in self.options.items() + if key in self.cfg.settings and value is not None # type: ignore + } + for key, value in config.items(): + self.cfg.set(key.lower(), value) # type: ignore diff --git a/requirements.txt b/requirements.txt index b00880996e..0f8779c1aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,24 +2,25 @@ # Platypush common requirements ### -pyyaml -marshmallow -marshmallow_dataclass -python-prctl -flask -websockets -redis -python-dateutil -tz -frozendict -requests -sqlalchemy alembic bcrypt -rsa -zeroconf>=0.27.0 -paho-mqtt -websocket-client croniter +flask +frozendict +gunicorn +marshmallow +marshmallow_dataclass +paho-mqtt +python-dateutil python-magic -waitress +python-prctl +pyyaml +redis +requests +rsa +simple_websocket +sqlalchemy +tz +websocket-client +wsproto +zeroconf>=0.27.0 diff --git a/setup.py b/setup.py index f795225473..68639e301a 100755 --- a/setup.py +++ b/setup.py @@ -65,6 +65,7 @@ setup( 'croniter', 'flask', 'frozendict', + 'gunicorn', 'marshmallow', 'marshmallow_dataclass', 'python-dateutil', @@ -75,7 +76,6 @@ setup( 'rsa', 'sqlalchemy', 'tz', - 'waitress', 'websocket-client', 'websockets', 'wheel',