mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
fix(ssh): skip a loopback route for a torn-down connection and report an already-exited forward
Check connection ownership before starting the forward, and make onExit fire for a forward that exited before the listener was registered, so the connection never keeps a dead route.
This commit is contained in:
@@ -135,4 +135,22 @@ describe('SshConnection loopback route', () => {
|
||||
|
||||
expect(route.dispose).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not start a route for a connection torn down as it reported connected', async () => {
|
||||
let conn: SshConnection | null = null
|
||||
conn = new SshConnection(
|
||||
createTarget({ proxyCommand: 'ssh -W %h:%p bastion.example.com' }),
|
||||
createCallbacks({
|
||||
onStateChange: (_id, state) => {
|
||||
if (state.status === 'connected') {
|
||||
void conn?.disconnect({ quiet: true })
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
await conn.connect().catch(() => undefined)
|
||||
|
||||
expect(startRouteMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -667,6 +667,8 @@ export class SshConnection {
|
||||
private startSystemSshLoopbackRoute(connectGeneration: number): void {
|
||||
const options = this.getSystemSshBuildArgsOptions()
|
||||
if (
|
||||
!this.isCurrentConnectAttempt(connectGeneration) ||
|
||||
!this.useSystemSshTransport ||
|
||||
this.systemSshLoopbackRoute ||
|
||||
this.systemSshLoopbackRouteStartingGeneration === connectGeneration ||
|
||||
!shouldUseSystemSshLoopbackRoute(this.target, this.systemSshResolvedConfig, options)
|
||||
|
||||
@@ -337,4 +337,21 @@ describe('startSystemSshLoopbackRoute', () => {
|
||||
expect(treeKillMock).toHaveBeenCalledWith(4242, { site: 'system-ssh-loopback-route' })
|
||||
await vi.waitFor(() => expect(forward.kill).toHaveBeenCalledWith('SIGTERM'))
|
||||
})
|
||||
|
||||
it('reports a forward that already exited before anyone listened for it', async () => {
|
||||
const forward = createFakeProcess()
|
||||
const probe = createFakeProcess()
|
||||
spawnMock.mockReturnValueOnce(forward).mockImplementationOnce(() => {
|
||||
answerProbe(probe, 0)
|
||||
return probe
|
||||
})
|
||||
const route = await startSystemSshLoopbackRoute(createTarget(), createResolved(), {}, 'linux')
|
||||
forward.exitCode = 255
|
||||
forward.emit('exit', 255)
|
||||
|
||||
const onExit = vi.fn()
|
||||
route.onExit(onExit)
|
||||
|
||||
expect(onExit).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -88,6 +88,11 @@ export async function startSystemSshLoopbackRoute(
|
||||
return {
|
||||
args,
|
||||
onExit: (callback) => {
|
||||
// Why: the forward can exit between the probe and this registration, after 'exit' already fired.
|
||||
if (forward.process.exitCode !== null || forward.process.signalCode !== null) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
forward.process.once('exit', callback)
|
||||
},
|
||||
dispose
|
||||
|
||||
Reference in New Issue
Block a user