connectiopn fix round 2 3
This commit is contained in:
@ -14,17 +14,27 @@ uvicorn-Worker (Standard-Deployment dieses Projekts, siehe ansible/) sieht
|
||||
und beendet jeder Superadmin-Request alle laufenden Sitzungen. Bei mehreren
|
||||
Worker-Prozessen sind nur die Sitzungen DIESES Workers 'killable' -- die
|
||||
Sessionview zeigt das ueber das 'killable'-Feld pro Sitzung an, statt einen
|
||||
Beenden-Versuch fehlschlagen zu lassen."""
|
||||
Beenden-Versuch fehlschlagen zu lassen.
|
||||
|
||||
Seit der Live-Mitschau ("Aktive Session live ansehen", GET /ws/sessions/
|
||||
{id}/watch, app/ssh_proxy/terminal_ws.py) traegt jeder Registry-Eintrag
|
||||
zusaetzlich eine Menge von Beobachter-Queues: app/ssh_proxy/terminal_ws.py::
|
||||
_pump_ssh_to_ws() speist jeden vom Zielsystem gelesenen Byte-Chunk sowohl an
|
||||
den eigentlichen Sitzungsinhaber als auch (per broadcast()) an alle gerade
|
||||
zuschauenden Superadmins. Genau wie 'killable' gilt: nur auf demselben
|
||||
Worker-Prozess moeglich -- ein Beobachter kann sich nur an eine Sitzung
|
||||
haengen, die in DIESER Prozess-Registry steht."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class ActiveSession:
|
||||
session_id: int
|
||||
task: asyncio.Task
|
||||
watchers: set[asyncio.Queue] = field(default_factory=set)
|
||||
|
||||
|
||||
_active: dict[int, ActiveSession] = {}
|
||||
@ -44,3 +54,38 @@ def get(session_id: int) -> ActiveSession | None:
|
||||
|
||||
def all_ids() -> set[int]:
|
||||
return set(_active.keys())
|
||||
|
||||
|
||||
def add_watcher(session_id: int) -> asyncio.Queue | None:
|
||||
"""Meldet einen Beobachter fuer eine laufende Sitzung an. Gibt None
|
||||
zurueck, wenn die Sitzung nicht (mehr) auf diesem Prozess laeuft --
|
||||
der Aufrufer (WS-Route) beendet die Beobachter-Verbindung dann sofort."""
|
||||
entry = _active.get(session_id)
|
||||
if entry is None:
|
||||
return None
|
||||
queue: asyncio.Queue = asyncio.Queue(maxsize=1000)
|
||||
entry.watchers.add(queue)
|
||||
return queue
|
||||
|
||||
|
||||
def remove_watcher(session_id: int, queue: asyncio.Queue) -> None:
|
||||
entry = _active.get(session_id)
|
||||
if entry is not None:
|
||||
entry.watchers.discard(queue)
|
||||
|
||||
|
||||
def broadcast(session_id: int, data: bytes) -> None:
|
||||
"""Verteilt einen Ausgabe-Chunk an alle aktuell zuschauenden Beobachter
|
||||
dieser Sitzung. Bewusst best-effort wie log_stream.py: ein langsamer
|
||||
oder haengender Beobachter darf weder die eigentliche Sitzung
|
||||
verlangsamen noch blockieren -- volle Queues verwerfen den Chunk
|
||||
stattdessen (put_nowait/QueueFull), statt den Sitzungsinhaber warten zu
|
||||
lassen."""
|
||||
entry = _active.get(session_id)
|
||||
if entry is None or not entry.watchers:
|
||||
return
|
||||
for queue in list(entry.watchers):
|
||||
try:
|
||||
queue.put_nowait(data)
|
||||
except asyncio.QueueFull:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user