fix: harden terminal WebSocket edge cases
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 2m44s
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 2m44s
- Wrap tryRefreshSession in try-finally to ensure authRecoveryInFlight is always reset - Fix variable scope issue: use const tabState instead of reassigning d - Add error handling for process.stdin.write() to catch BrokenPipeError/OSError - Verify WebSocket instance before closing in ping timeout callback - Properly await conn.wait_closed() to prevent SSH connection leaks - Gracefully cancel and wait for read_task with 2s timeout These fixes prevent race conditions, resource leaks, and improve error recovery.
This commit is contained in:
@@ -190,18 +190,31 @@ async def ws_endpoint(websocket: WebSocket, host: str = Query("nas")):
|
||||
rows = struct.unpack("!H", data[3:5])[0]
|
||||
process.channel.change_terminal_size(cols, rows)
|
||||
else:
|
||||
process.stdin.write(data)
|
||||
try:
|
||||
process.stdin.write(data)
|
||||
except (BrokenPipeError, OSError) as e:
|
||||
logger.warning("Failed to write to stdin for %s: %s", host, e)
|
||||
break
|
||||
elif "text" in msg and msg["text"]:
|
||||
if msg["text"] == "__ping__":
|
||||
logger.debug("Received ping from %s, sending pong", host)
|
||||
await websocket.send_text("__pong__")
|
||||
continue
|
||||
process.stdin.write(msg["text"].encode())
|
||||
try:
|
||||
process.stdin.write(msg["text"].encode())
|
||||
except (BrokenPipeError, OSError) as e:
|
||||
logger.warning("Failed to write to stdin for %s: %s", host, e)
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warning("WebSocket receive loop ended for %s: %s", host, e)
|
||||
finally:
|
||||
read_task.cancel()
|
||||
try:
|
||||
await asyncio.wait_for(read_task, timeout=2.0)
|
||||
except (asyncio.TimeoutError, asyncio.CancelledError):
|
||||
pass
|
||||
process.close()
|
||||
finally:
|
||||
conn.close()
|
||||
await conn.wait_closed()
|
||||
await _release_session(session_id)
|
||||
|
||||
Reference in New Issue
Block a user