mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 00:03:08 +00:00
fix(ssh): re-check session identity before post-probe resume reconnect
The resume probe can take ~10s; if the user disconnected the target or the session/connection was replaced during that window, reconnecting would resurrect an intentionally torn-down connection (CodeRabbit). Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -1468,6 +1468,41 @@ describe('SSH IPC handlers', () => {
|
||||
expect(mockConnectionManager.reconnect).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not reconnect after resume when the target was disconnected during the probe', async () => {
|
||||
const target: SshTarget = {
|
||||
id: 'ssh-1',
|
||||
label: 'Server',
|
||||
host: 'example.com',
|
||||
port: 22,
|
||||
username: 'deploy'
|
||||
}
|
||||
const conn = {}
|
||||
mockSshStore.getTarget.mockReturnValue(target)
|
||||
mockConnectionManager.connect.mockResolvedValue(conn)
|
||||
mockConnectionManager.getConnection.mockReturnValue(conn)
|
||||
mockConnectionManager.getState.mockReturnValue({
|
||||
targetId: 'ssh-1',
|
||||
status: 'connected',
|
||||
error: null,
|
||||
reconnectAttempt: 0
|
||||
})
|
||||
mockMux.probeLiveness.mockResolvedValue(false)
|
||||
|
||||
await handlers.get('ssh:connect')!(null, { targetId: 'ssh-1' })
|
||||
|
||||
const resumeListener = powerMonitorOnMock.mock.calls.find(([event]) => event === 'resume')?.[1]
|
||||
expect(resumeListener).toBeTypeOf('function')
|
||||
|
||||
resumeListener()
|
||||
// Why: the probe window is seconds long; a user disconnect during it must
|
||||
// win — reconnecting afterwards would resurrect the torn-down target.
|
||||
mockConnectionManager.getConnection.mockReturnValue(undefined)
|
||||
|
||||
await vi.waitFor(() => expect(mockMux.probeLiveness).toHaveBeenCalledTimes(2))
|
||||
await new Promise((resolve) => setTimeout(resolve, 0))
|
||||
expect(mockConnectionManager.reconnect).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('extends active relay grace while the system is suspending', async () => {
|
||||
const target: SshTarget = {
|
||||
id: 'ssh-1',
|
||||
|
||||
+9
-2
@@ -469,7 +469,8 @@ function registerPowerMonitorReconnect(): void {
|
||||
}
|
||||
const onResume = (): void => {
|
||||
for (const [targetId, session] of activeSessions) {
|
||||
const conn = connectionManager?.getConnection(targetId)
|
||||
const manager = connectionManager
|
||||
const conn = manager?.getConnection(targetId)
|
||||
if (!conn) {
|
||||
continue
|
||||
}
|
||||
@@ -480,8 +481,14 @@ function registerPowerMonitorReconnect(): void {
|
||||
if (await isRelayLinkAliveAfterResume(session)) {
|
||||
return
|
||||
}
|
||||
// Why: the probe can take ~10s. If the user disconnected or the
|
||||
// session/connection was replaced meanwhile, reconnecting would
|
||||
// resurrect a connection that was intentionally torn down.
|
||||
if (activeSessions.get(targetId) !== session || manager?.getConnection(targetId) !== conn) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await connectionManager?.reconnect(targetId)
|
||||
await manager?.reconnect(targetId)
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[ssh] Failed to reconnect ${targetId} after system resume: ${
|
||||
|
||||
Reference in New Issue
Block a user