fix: keep active ssh downloads alive
This commit is contained in:
+33
-3
@@ -1311,6 +1311,7 @@ async function runRemoteSshWebSocketStreamRemoteCommand(
|
||||
remoteCommand: string,
|
||||
handlers: SshRemoteCommandStreamHandlers,
|
||||
input?: string,
|
||||
options: { inactivityTimeoutMs?: number } = {},
|
||||
): Promise<SshCaptureResult> {
|
||||
const streamInvocation = {
|
||||
...invocation,
|
||||
@@ -1321,7 +1322,8 @@ async function runRemoteSshWebSocketStreamRemoteCommand(
|
||||
cols: Number(process.stdout.columns) > 0 ? Number(process.stdout.columns) : 100,
|
||||
rows: Number(process.stdout.rows) > 0 ? Number(process.stdout.rows) : 30,
|
||||
};
|
||||
const runtimeTimeoutMs = sshRuntimeTimeoutMs();
|
||||
const inactivityTimeoutMs = options.inactivityTimeoutMs ?? sshRuntimeTimeoutMs();
|
||||
const runtimeTimeoutMs = options.inactivityTimeoutMs === undefined ? inactivityTimeoutMs : Math.max(inactivityTimeoutMs * 4, 60 * 60_000);
|
||||
const payload = {
|
||||
providerId: invocation.providerId,
|
||||
command: wrapSshRemoteCommand(remoteCommand),
|
||||
@@ -1330,6 +1332,7 @@ async function runRemoteSshWebSocketStreamRemoteCommand(
|
||||
stdinEotOnEnd: false,
|
||||
openTimeoutMs: Math.max(15000, Number(process.env.UNIDESK_SSH_OPEN_TIMEOUT_MS || 60000)),
|
||||
runtimeTimeoutMs,
|
||||
runtimeTimeoutMode: options.inactivityTimeoutMs === undefined ? "wall-clock" : "inactivity",
|
||||
cols: size.cols,
|
||||
rows: size.rows,
|
||||
};
|
||||
@@ -1395,11 +1398,32 @@ async function runRemoteSshWebSocketStreamRemoteCommand(
|
||||
|
||||
return await new Promise<SshCaptureResult>((resolve) => {
|
||||
let killTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let inactivityTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const refreshActivityTimer = (): void => {
|
||||
if (settled || options.inactivityTimeoutMs === undefined) return;
|
||||
if (inactivityTimer !== null) clearTimeout(inactivityTimer);
|
||||
inactivityTimer = setTimeout(() => {
|
||||
exitCode = 124;
|
||||
stderr += formatSshRuntimeTimeoutHint(sshRuntimeTimeoutHint({
|
||||
invocation: streamInvocation,
|
||||
transport: "frontend-websocket",
|
||||
timeoutMs: inactivityTimeoutMs,
|
||||
}));
|
||||
try {
|
||||
ws.close();
|
||||
} catch {
|
||||
// Ignore.
|
||||
}
|
||||
killTimer = setTimeout(() => finish(124), 2000);
|
||||
finish(124);
|
||||
}, inactivityTimeoutMs);
|
||||
};
|
||||
const finish = (code: number): void => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(openTimer);
|
||||
clearTimeout(runtimeTimer);
|
||||
if (inactivityTimer !== null) clearTimeout(inactivityTimer);
|
||||
if (killTimer !== null) clearTimeout(killTimer);
|
||||
void streamWrites.then(() => {
|
||||
const finalCode = streamError === null ? code : 255;
|
||||
@@ -1439,6 +1463,7 @@ async function runRemoteSshWebSocketStreamRemoteCommand(
|
||||
killTimer = setTimeout(() => finish(124), 2000);
|
||||
finish(124);
|
||||
}, runtimeTimeoutMs);
|
||||
refreshActivityTimer();
|
||||
|
||||
ws.addEventListener("open", () => {
|
||||
canSend = true;
|
||||
@@ -1454,16 +1479,21 @@ async function runRemoteSshWebSocketStreamRemoteCommand(
|
||||
stderr += `${text}\n`;
|
||||
return;
|
||||
}
|
||||
if (message.type === "ssh.dispatched") return;
|
||||
if (message.type === "ssh.dispatched") {
|
||||
refreshActivityTimer();
|
||||
return;
|
||||
}
|
||||
if (message.type === "ssh.opened") {
|
||||
sessionReady = true;
|
||||
clearTimeout(openTimer);
|
||||
refreshActivityTimer();
|
||||
if (input !== undefined) sendInputChunked(input);
|
||||
sendWhenSessionReady({ type: "ssh.eof" });
|
||||
flushSessionMessages();
|
||||
return;
|
||||
}
|
||||
if (message.type === "ssh.data") {
|
||||
refreshActivityTimer();
|
||||
const chunk = Buffer.from(String(message.data ?? ""), message.encoding === "base64" ? "base64" : "utf8");
|
||||
if (message.stream === "stderr") {
|
||||
stderr += chunk.toString("utf8");
|
||||
@@ -1514,7 +1544,7 @@ async function runRemoteSshOverFrontend(session: FrontendSession, target: string
|
||||
if (isSshFileTransferOperation(normalizedArgs)) {
|
||||
const executor: SshRemoteCommandExecutor = {
|
||||
runRemoteCommand: (remoteCommand, input) => runRemoteSshWebSocketCaptureRemoteCommand(session, invocation, remoteCommand, input),
|
||||
streamRemoteCommand: (remoteCommand, handlers, input) => runRemoteSshWebSocketStreamRemoteCommand(session, invocation, remoteCommand, handlers, input),
|
||||
streamRemoteCommand: (remoteCommand, handlers, input, options) => runRemoteSshWebSocketStreamRemoteCommand(session, invocation, remoteCommand, handlers, input, options),
|
||||
};
|
||||
return await runSshFileTransferOperation(invocation, normalizedArgs, executor, {
|
||||
buildRouteCommand: remoteCommandForRoute,
|
||||
|
||||
Reference in New Issue
Block a user