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
|
# Import cache control middleware
|
||||||
from middleware.cache_middleware import cache_control
|
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 --------------------------------------------------------------------
|
# Helper --------------------------------------------------------------------
|
||||||
def _fd_to_socket(fd: int) -> socket.socket:
|
def _fd_to_socket(fd: int) -> socket.socket:
|
||||||
"""
|
"""
|
||||||
Convert a file‑descriptor received from systemd into a ready‑to‑use
|
Convert a file‑descriptor received from systemd into a socket that aiohttp
|
||||||
``socket.socket`` object.
|
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:
|
try:
|
||||||
# ``socket.socket`` with the ``fileno`` argument re‑uses the existing OS
|
sock = NoListenSocket(fileno=fd)
|
||||||
# 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.setblocking(False)
|
sock.setblocking(False)
|
||||||
|
|
||||||
return sock
|
return sock
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
# Raising a RuntimeError makes the caller’s ``except`` clause simpler.
|
raise RuntimeError(f"Could not wrap fd {fd} as a NoListenSocket: {exc}") from exc
|
||||||
raise RuntimeError(f"Could not wrap fd {fd} as a socket: {exc}") from exc
|
|
||||||
|
|
||||||
async def send_socket_catch_exception(function, message):
|
async def send_socket_catch_exception(function, message):
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user