From c9d4b636e64b7b13662a362dfb52c42bad6a3eb8 Mon Sep 17 00:00:00 2001 From: Andreas Date: Wed, 3 Dec 2025 22:45:28 +0100 Subject: [PATCH] 2nd iteration fix socket activation --- server.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/server.py b/server.py index 50cece7d..ad00b272 100644 --- a/server.py +++ b/server.py @@ -47,29 +47,30 @@ from middleware.cache_middleware import cache_control # Helper -------------------------------------------------------------------- def _fd_to_socket(fd: int) -> socket.socket: """ - Turn a raw file‑descriptor that came from systemd into a *non‑blocking* - ``socket.socket`` instance that preserves the original family, type and - protocol. + Convert a file‑descriptor received from systemd into a ready‑to‑use + ``socket.socket`` object. - The function mirrors the logic used by `sd_listen_fds` in the systemd - libraries (see libsystemd/libsystemd/sd-daemon.c). It queries the - kernel for the socket's actual family, type and protocol using - ``getsockopt`` on the fd. + * The fd already contains the correct address family, socket type and + protocol – passing ``fileno=fd`` makes the constructor adopt them. + * aiohttp (and the rest of the code) expects the socket to be **non‑blocking**, + so we set that flag explicitly. + * Any OSError while wrapping the descriptor is turned into a RuntimeError + with a clear message; the caller can log it. """ - # Query the kernel for the family / type / protocol of the fd. - # These syscalls are cheap and work for IPv4, IPv6 and Unix sockets. - family = socket.getsockopt(fd, socket.SOL_SOCKET, socket.SO_DOMAIN) - sock_type = socket.getsockopt(fd, socket.SOL_SOCKET, socket.SO_TYPE) - proto = socket.getsockopt(fd, socket.SOL_SOCKET, socket.SO_PROTOCOL) + try: + # ``socket.socket`` with the ``fileno`` argument re‑uses the existing OS + # socket. The resulting object automatically reports the correct + # ``family``, ``type`` and ``proto`` attributes. + sock = socket.socket(fileno=fd) - # Build the socket object *without* creating a new fd. - # ``socket.socket`` with ``fileno=fd`` re‑uses the existing descriptor. - # ``closefd=False`` tells Python not to close the fd when the socket - # object is garbage‑collected – systemd will close it when the service - # exits. - sock = socket.socket(family=family, type=sock_type, proto=proto, fileno=fd) - sock.setblocking(False) # aiohttp expects non‑blocking - return sock + # aiohttp’s SockSite registers the socket with the event loop, which + # requires a non‑blocking descriptor. + sock.setblocking(False) + + return sock + except OSError as exc: + # Raising a RuntimeError makes the caller’s ``except`` clause simpler. + raise RuntimeError(f"Could not wrap fd {fd} as a socket: {exc}") from exc async def send_socket_catch_exception(function, message): try: