WIP cleanup: split branch stash into 8 focused commits #2
@@ -60,8 +60,10 @@ struct NodeConn {
|
|||||||
tx: mpsc::UnboundedSender<String>,
|
tx: mpsc::UnboundedSender<String>,
|
||||||
pending: Mutex<HashMap<u64, oneshot::Sender<ExecOutput>>>,
|
pending: Mutex<HashMap<u64, oneshot::Sender<ExecOutput>>>,
|
||||||
/// Live terminal sessions: sid → byte sink for the browser bridge (WS-relay
|
/// Live terminal sessions: sid → byte sink for the browser bridge (WS-relay
|
||||||
/// PTY output).
|
/// PTY output). Bounded: a runaway PTY (say `cat /var/log/huge`) with a
|
||||||
pty_sinks: Mutex<HashMap<u64, mpsc::UnboundedSender<Vec<u8>>>>,
|
/// stalled browser must not accumulate megabytes here. On overflow the
|
||||||
|
/// session is closed instead of holding output indefinitely.
|
||||||
|
pty_sinks: Mutex<HashMap<u64, mpsc::Sender<Vec<u8>>>>,
|
||||||
/// WebRTC signaling: sid → text sink delivering the daemon's answer/ICE to
|
/// WebRTC signaling: sid → text sink delivering the daemon's answer/ICE to
|
||||||
/// the browser bridge.
|
/// the browser bridge.
|
||||||
signal_sinks: Mutex<HashMap<u64, mpsc::UnboundedSender<String>>>,
|
signal_sinks: Mutex<HashMap<u64, mpsc::UnboundedSender<String>>>,
|
||||||
@@ -187,12 +189,13 @@ impl NodeHub {
|
|||||||
id: NodeId,
|
id: NodeId,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
u64,
|
u64,
|
||||||
mpsc::UnboundedReceiver<Vec<u8>>,
|
mpsc::Receiver<Vec<u8>>,
|
||||||
mpsc::UnboundedReceiver<String>,
|
mpsc::UnboundedReceiver<String>,
|
||||||
)> {
|
)> {
|
||||||
let conn = self.get(id).await?;
|
let conn = self.get(id).await?;
|
||||||
let sid = conn.next_id.fetch_add(1, Ordering::Relaxed);
|
let sid = conn.next_id.fetch_add(1, Ordering::Relaxed);
|
||||||
let (ptx, prx) = mpsc::unbounded_channel();
|
// 256 × ~4KB PTY frames = ~1 MB per stalled session before we close it.
|
||||||
|
let (ptx, prx) = mpsc::channel(256);
|
||||||
let (stx, srx) = mpsc::unbounded_channel();
|
let (stx, srx) = mpsc::unbounded_channel();
|
||||||
conn.pty_sinks.lock().await.insert(sid, ptx);
|
conn.pty_sinks.lock().await.insert(sid, ptx);
|
||||||
conn.signal_sinks.lock().await.insert(sid, stx);
|
conn.signal_sinks.lock().await.insert(sid, stx);
|
||||||
@@ -462,7 +465,21 @@ pub async fn run_channel(pool: PgPool, hub: Arc<NodeHub>, node_id: NodeId, socke
|
|||||||
if let Ok(bytes) = B64.decode(&data) {
|
if let Ok(bytes) = B64.decode(&data) {
|
||||||
let sink = conn.pty_sinks.lock().await.get(&sid).cloned();
|
let sink = conn.pty_sinks.lock().await.get(&sid).cloned();
|
||||||
if let Some(s) = sink {
|
if let Some(s) = sink {
|
||||||
let _ = s.send(bytes);
|
// try_send so a stalled browser can't grow the
|
||||||
|
// per-session buffer without bound. On Full, the
|
||||||
|
// session is torn down: drop both sinks and tell
|
||||||
|
// the node to close its side, preventing an
|
||||||
|
// orphan PTY.
|
||||||
|
if let Err(err) = s.try_send(bytes) {
|
||||||
|
if matches!(err, mpsc::error::TrySendError::Full(_)) {
|
||||||
|
conn.pty_sinks.lock().await.remove(&sid);
|
||||||
|
conn.signal_sinks.lock().await.remove(&sid);
|
||||||
|
let _ = conn.tx.send(
|
||||||
|
json!({ "t": "pty_close", "sid": sid })
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user