hack-house/.venv/lib/python3.12/site-packages/sanic/server/socket.py
leetcrypt bb1d662ee1 chore: rename project coven → hack-house ⛧
Rebrand the Rust client crate (coven/ → hh/, package+binary "hack-house"),
README, CLI strings, and branch (coven → hack-house). Gitea repo renamed
cmd-chat → hack-house to match. Crypto/server logic unchanged; selftest +
golden-vector test still green, binary is now `hack-house`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 13:29:14 -07:00

122 lines
3.7 KiB
Python

from __future__ import annotations
import secrets
import socket
import stat
from ipaddress import ip_address
from pathlib import Path
from typing import Any
from sanic.http.constants import HTTP
def bind_socket(host: str, port: int, *, backlog=100) -> socket.socket:
"""Create TCP server socket.
:param host: IPv4, IPv6 or hostname may be specified
:param port: TCP port number
:param backlog: Maximum number of connections to queue
:return: socket.socket object
"""
location = (host, port)
# socket.share, socket.fromshare
try: # IP address: family must be specified for IPv6 at least
ip = ip_address(host)
host = str(ip)
sock = socket.socket(
socket.AF_INET6 if ip.version == 6 else socket.AF_INET
)
except ValueError: # Hostname, may become AF_INET or AF_INET6
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(location)
sock.listen(backlog)
sock.set_inheritable(True)
return sock
def bind_unix_socket(
path: Path | str, *, mode=0o666, backlog=100
) -> socket.socket:
"""Create unix socket.
:param path: filesystem path
:param backlog: Maximum number of connections to queue
:return: socket.socket object
"""
# Sanitise and pre-verify socket path
path = Path(path)
folder = path.parent
if not folder.is_dir():
raise FileNotFoundError(f"Socket folder does not exist: {folder}")
try:
if not stat.S_ISSOCK(path.lstat().st_mode):
raise FileExistsError(f"Existing file is not a socket: {path}")
except FileNotFoundError:
pass
# Create new socket with a random temporary name
tmp_path = path.with_name(f"{path.name}.{secrets.token_urlsafe()}")
sock = socket.socket(socket.AF_UNIX)
try:
# Critical section begins (filename races)
sock.bind(tmp_path.as_posix())
try:
tmp_path.chmod(mode)
# Start listening before rename to avoid connection failures
sock.listen(backlog)
tmp_path.rename(path)
except: # noqa: E722
try:
tmp_path.unlink()
finally:
raise
except: # noqa: E722
try:
sock.close()
finally:
raise
return sock
def remove_unix_socket(path: Path | str | None) -> None:
"""Remove dead unix socket during server exit."""
if not path:
return
try:
path = Path(path)
if stat.S_ISSOCK(path.lstat().st_mode):
# Is it actually dead (doesn't belong to a new server instance)?
with socket.socket(socket.AF_UNIX) as testsock:
try:
testsock.connect(path.as_posix())
except ConnectionRefusedError:
path.unlink()
except FileNotFoundError:
pass
def configure_socket(
server_settings: dict[str, Any],
) -> socket.SocketType | None:
# Create a listening socket or use the one in settings
if server_settings.get("version") is HTTP.VERSION_3:
return None
sock = server_settings.get("sock")
unix = server_settings["unix"]
backlog = server_settings["backlog"]
if unix:
unix = Path(unix).absolute()
sock = bind_unix_socket(unix, backlog=backlog)
server_settings["unix"] = unix
if sock is None:
sock = bind_socket(
server_settings["host"],
server_settings["port"],
backlog=backlog,
)
sock.set_inheritable(True)
server_settings["sock"] = sock
server_settings["host"] = None
server_settings["port"] = None
return sock