mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-11-01 16:15:24 +00:00
fix(ssh-terminal): close owner sockets when presentations unmount (#1501)
This commit is contained in:
1 parent
24def2b613
commit
c265758cdd
2 files changed
+76
-7
No files matched your search
@@ -412,7 +412,7 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
isAttachingSessionRef.current = false;
|
isAttachingSessionRef.current = false;
|
||||||
|
|
||||||
return () => {};
|
return () => {};
|
||||||
}, [hostConfig.id]);
|
}, [hostConfig.id, hostConfig.instanceId]);
|
||||||
const connectionAttemptIdRef = useRef(0);
|
const connectionAttemptIdRef = useRef(0);
|
||||||
const totpTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
const totpTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const connectionTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
const connectionTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
@@ -2886,16 +2886,11 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
isMountedRef.current = true;
|
isMountedRef.current = true;
|
||||||
|
|
||||||
const currentHostId = hostConfig.id;
|
|
||||||
return () => {
|
return () => {
|
||||||
if (!isMountedRef.current) {
|
if (!isMountedRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
currentHostIdRef.current !== currentHostId &&
|
|
||||||
currentHostIdRef.current !== null
|
|
||||||
) {
|
|
||||||
isUnmountingRef.current = true;
|
isUnmountingRef.current = true;
|
||||||
shouldNotReconnectRef.current = true;
|
shouldNotReconnectRef.current = true;
|
||||||
isReconnectingRef.current = false;
|
isReconnectingRef.current = false;
|
||||||
@@ -2919,7 +2914,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
isMountedRef.current = false;
|
isMountedRef.current = false;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}, [hostConfig.id, hostConfig.instanceId]);
|
}, [hostConfig.id, hostConfig.instanceId]);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import ts from "typescript";
|
||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import sourceText from "../../../src/frontend/terminal/Terminal.tsx?raw";
|
||||||
|
|
||||||
|
const source = ts.createSourceFile(
|
||||||
|
"Terminal.tsx",
|
||||||
|
sourceText,
|
||||||
|
ts.ScriptTarget.Latest,
|
||||||
|
true,
|
||||||
|
ts.ScriptKind.TSX,
|
||||||
|
);
|
||||||
|
let effect: string | undefined;
|
||||||
|
function visit(node: ts.Node) {
|
||||||
|
if (
|
||||||
|
ts.isCallExpression(node) &&
|
||||||
|
node.expression.getText(source) === "useEffect" &&
|
||||||
|
node.arguments[0]?.getText(source).includes("isMountedRef.current = true")
|
||||||
|
) {
|
||||||
|
effect = node.arguments[0].getText(source);
|
||||||
|
}
|
||||||
|
ts.forEachChild(node, visit);
|
||||||
|
}
|
||||||
|
visit(source);
|
||||||
|
if (!effect) throw new Error("Missing terminal lifecycle effect");
|
||||||
|
const mount = new Function(
|
||||||
|
"state",
|
||||||
|
ts.transpileModule(
|
||||||
|
`
|
||||||
|
const { isMountedRef, hostConfig, currentHostIdRef, isUnmountingRef,
|
||||||
|
shouldNotReconnectRef, isReconnectingRef, setIsConnecting,
|
||||||
|
reconnectTimeoutRef, connectionTimeoutRef, totpTimeoutRef,
|
||||||
|
pingIntervalRef, pongTimeoutRef, webSocketRef } = state;
|
||||||
|
return (${effect})();
|
||||||
|
`,
|
||||||
|
{ compilerOptions: { target: ts.ScriptTarget.ES2022 } },
|
||||||
|
).outputText,
|
||||||
|
) as (state: Record<string, unknown>) => () => void;
|
||||||
|
|
||||||
|
afterEach(() => vi.useRealTimers());
|
||||||
|
|
||||||
|
describe("terminal unmount", () => {
|
||||||
|
it.each([1, 2, null])(
|
||||||
|
"closes the socket when current host is %s",
|
||||||
|
(currentHost) => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
const timeout = () => ({ current: setTimeout(() => {}, 10000) });
|
||||||
|
const state = {
|
||||||
|
isMountedRef: { current: false },
|
||||||
|
hostConfig: { id: 1, instanceId: "collab-present-room" },
|
||||||
|
currentHostIdRef: { current: currentHost },
|
||||||
|
isUnmountingRef: { current: false },
|
||||||
|
shouldNotReconnectRef: { current: false },
|
||||||
|
isReconnectingRef: { current: true },
|
||||||
|
setIsConnecting: vi.fn(),
|
||||||
|
reconnectTimeoutRef: timeout(),
|
||||||
|
connectionTimeoutRef: timeout(),
|
||||||
|
totpTimeoutRef: timeout(),
|
||||||
|
pongTimeoutRef: timeout(),
|
||||||
|
pingIntervalRef: { current: setInterval(() => {}, 1000) },
|
||||||
|
webSocketRef: { current: { close: vi.fn() } },
|
||||||
|
};
|
||||||
|
const unmount = mount(state);
|
||||||
|
expect(state.isMountedRef.current).toBe(true);
|
||||||
|
unmount();
|
||||||
|
expect(state.webSocketRef.current.close).toHaveBeenCalledOnce();
|
||||||
|
expect(state.shouldNotReconnectRef.current).toBe(true);
|
||||||
|
expect(state.isUnmountingRef.current).toBe(true);
|
||||||
|
expect(state.isReconnectingRef.current).toBe(false);
|
||||||
|
expect(state.isMountedRef.current).toBe(false);
|
||||||
|
expect(vi.getTimerCount()).toBe(0);
|
||||||
|
unmount();
|
||||||
|
expect(state.webSocketRef.current.close).toHaveBeenCalledOnce();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user