WIP cleanup: split branch stash into 8 focused commits #2
@@ -60,8 +60,10 @@ struct NodeConn {
|
||||
tx: mpsc::UnboundedSender<String>,
|
||||
pending: Mutex<HashMap<u64, oneshot::Sender<ExecOutput>>>,
|
||||
/// Live terminal sessions: sid → byte sink for the browser bridge (WS-relay
|
||||
/// PTY output).
|
||||
pty_sinks: Mutex<HashMap<u64, mpsc::UnboundedSender<Vec<u8>>>>,
|
||||
/// PTY output). Bounded: a runaway PTY (say `cat /var/log/huge`) with a
|
||||
/// 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
|
||||
/// the browser bridge.
|
||||
signal_sinks: Mutex<HashMap<u64, mpsc::UnboundedSender<String>>>,
|
||||
@@ -187,12 +189,13 @@ impl NodeHub {
|
||||
id: NodeId,
|
||||
) -> Option<(
|
||||
u64,
|
||||
mpsc::UnboundedReceiver<Vec<u8>>,
|
||||
mpsc::Receiver<Vec<u8>>,
|
||||
mpsc::UnboundedReceiver<String>,
|
||||
)> {
|
||||
let conn = self.get(id).await?;
|
||||
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();
|
||||
conn.pty_sinks.lock().await.insert(sid, ptx);
|
||||
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) {
|
||||
let sink = conn.pty_sinks.lock().await.get(&sid).cloned();
|
||||
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