Socket activation working

This commit is contained in:
2025-12-03 23:18:35 +01:00
parent 943105ccef
commit 03be08f325

View File

@@ -44,39 +44,34 @@ from protocol import BinaryEventTypes
# Import cache control middleware
from middleware.cache_middleware import cache_control
class NoListenSocket(socket.socket):
"""
A socket that pretends to be already listening.
The overridden ``listen`` simply returns without calling the kernel.
"""
def listen(self, backlog: int = socket.SOMAXCONN, *args: Any, **kwargs: Any) -> None: # type: ignore[override]
# If the socket is already in LISTEN state the kernel will reject a
# second listen() with EPERM (which is what triggered the SELinux
# denial). By turning it into a noop we avoid that system call.
# The socket is still usable by asyncio because it was created by
# systemd with ``listen`` already performed.
logging.debug("NoListenSocket.listen() called noop (socket already listening)")
return None
# Helper --------------------------------------------------------------------
def _fd_to_socket(fd: int) -> socket.socket:
"""
Convert a filedescriptor received from systemd into a readytouse
``socket.socket`` object.
Convert a filedescriptor received from systemd into a socket that aiohttp
can use *without* performing a second listen().
* 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 **nonblocking**,
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.
"""
try:
# ``socket.socket`` with the ``fileno`` argument reuses the existing OS
# socket. The resulting object automatically reports the correct
# ``family``, ``type`` and ``proto`` attributes.
sock = socket.socket(fileno=fd)
# Allow aiohttp to call listen() again without EPERM.
# On many Linux kernels the flag must be set *before* the first listen(),
# but systemd binds the socket without it. Setting it now is sufficient
# for the second listen() performed by aiohttp.
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
# aiohttps SockSite registers the socket with the event loop, which
# requires a nonblocking descriptor.
sock = NoListenSocket(fileno=fd)
sock.setblocking(False)
return sock
except OSError as exc:
# Raising a RuntimeError makes the callers ``except`` clause simpler.
raise RuntimeError(f"Could not wrap fd {fd} as a socket: {exc}") from exc
raise RuntimeError(f"Could not wrap fd {fd} as a NoListenSocket: {exc}") from exc
async def send_socket_catch_exception(function, message):
try: