Socket activation working
This commit is contained in:
43
server.py
43
server.py
@@ -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 no‑op 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 – no‑op (socket already listening)")
|
||||
return None
|
||||
|
||||
# Helper --------------------------------------------------------------------
|
||||
def _fd_to_socket(fd: int) -> socket.socket:
|
||||
"""
|
||||
Convert a file‑descriptor received from systemd into a ready‑to‑use
|
||||
``socket.socket`` object.
|
||||
Convert a file‑descriptor 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 **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.
|
||||
"""
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
# aiohttp’s SockSite registers the socket with the event loop, which
|
||||
# requires a non‑blocking descriptor.
|
||||
sock = NoListenSocket(fileno=fd)
|
||||
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
|
||||
raise RuntimeError(f"Could not wrap fd {fd} as a NoListenSocket: {exc}") from exc
|
||||
|
||||
|
||||
async def send_socket_catch_exception(function, message):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user