from __future__ import annotations import asyncio import os import socket import warnings from functools import partial from multiprocessing.connection import Connection from ssl import SSLContext from typing import Any from sanic.application.constants import ServerStage from sanic.application.state import ApplicationServerInfo from sanic.http.constants import HTTP from sanic.log import error_logger from sanic.logging.setup import setup_logging from sanic.models.server_types import Signal from sanic.server.protocols.http_protocol import HttpProtocol from sanic.server.runners import _serve_http_1, _serve_http_3 from sanic.worker.loader import AppLoader, CertLoader from sanic.worker.multiplexer import WorkerMultiplexer from sanic.worker.process import Worker, WorkerProcess def worker_serve( host, port, app_name: str, monitor_publisher: Connection | None, app_loader: AppLoader, worker_state: dict[str, Any] | None = None, server_info: dict[str, list[ApplicationServerInfo]] | None = None, ssl: SSLContext | dict[str, str | os.PathLike[str]] | None = None, sock: socket.socket | None = None, unix: str | None = None, reuse_port: bool = False, loop=None, protocol: type[asyncio.Protocol] = HttpProtocol, backlog: int = 100, register_sys_signals: bool = True, run_multiple: bool = False, run_async: bool = False, connections=None, signal=Signal(), state=None, asyncio_server_kwargs=None, version=HTTP.VERSION_1, config: bytes | str | dict[str, Any] | Any | None = None, passthru: dict[str, Any] | None = None, ): try: from sanic import Sanic if app_loader: app = app_loader.load() else: app = Sanic.get_app(app_name) app.refresh(passthru) app.setup_loop() setup_logging( app.state.is_debug, app.config.NO_COLOR, app.config.LOG_EXTRA ) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # Hydrate server info if needed if server_info: for app_name, server_info_objects in server_info.items(): a = Sanic.get_app(app_name) if not a.state.server_info: a.state.server_info = [] for info in server_info_objects: if not info.settings.get("app"): info.settings["app"] = a a.state.server_info.append(info) if isinstance(ssl, dict) or app.certloader_class is not CertLoader: cert_loader = app.certloader_class(ssl or {}) ssl = cert_loader.load(app) for info in app.state.server_info: info.settings["ssl"] = ssl # When in a worker process, do some init worker_name = os.environ.get("SANIC_WORKER_NAME") if worker_name and worker_name.startswith( "-".join([Worker.WORKER_PREFIX, WorkerProcess.SERVER_LABEL]) ): # Hydrate apps with any passed server info if monitor_publisher is None: raise RuntimeError( "No restart publisher found in worker process" ) if worker_state is None: raise RuntimeError("No worker state found in worker process") # Run secondary servers apps = list(Sanic._app_registry.values()) app.before_server_start(partial(app._start_servers, apps=apps)) for a in apps: a.multiplexer = WorkerMultiplexer( monitor_publisher, worker_state ) if app.debug: loop.set_debug(app.debug) app.asgi = False if app.state.server_info: primary_server_info = app.state.server_info[0] primary_server_info.stage = ServerStage.SERVING if config: app.update_config(config) if version is HTTP.VERSION_3: return _serve_http_3(host, port, app, loop, ssl) return _serve_http_1( host, port, app, ssl, sock, unix, reuse_port, loop, protocol, backlog, register_sys_signals, run_multiple, run_async, connections, signal, state, asyncio_server_kwargs, ) except Exception as e: warnings.simplefilter("ignore", category=RuntimeWarning) if monitor_publisher: error_logger.exception(e) multiplexer = WorkerMultiplexer(monitor_publisher, {}) multiplexer.terminate(True) else: raise e