mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
fix(ssh): contain relay notification handler failures (#2463)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -229,6 +229,50 @@ describe('SshChannelMultiplexer', () => {
|
||||
expect(a).not.toHaveBeenCalled()
|
||||
expect(b).toHaveBeenCalledWith({ streamId: 7 })
|
||||
})
|
||||
|
||||
it('contains generic notification handler failures', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const badHandler = vi.fn(() => {
|
||||
throw new Error('subscriber exploded')
|
||||
})
|
||||
const goodHandler = vi.fn()
|
||||
mux.onNotification(badHandler)
|
||||
mux.onNotification(goodHandler)
|
||||
|
||||
expect(() =>
|
||||
transport.dataCallbacks[0](makeNotificationFrame('pty.data', { id: 'pty-1' }, 1))
|
||||
).not.toThrow()
|
||||
|
||||
expect(badHandler).toHaveBeenCalled()
|
||||
expect(goodHandler).toHaveBeenCalledWith('pty.data', { id: 'pty-1' })
|
||||
expect(mux.isDisposed()).toBe(false)
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'[ssh-mux] Notification handler failed for pty.data: subscriber exploded'
|
||||
)
|
||||
})
|
||||
|
||||
it('contains method notification handler failures', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const badHandler = vi.fn(() => {
|
||||
throw new Error('stream consumer exploded')
|
||||
})
|
||||
const goodHandler = vi.fn()
|
||||
mux.onNotificationByMethod('fs.streamChunk', badHandler)
|
||||
mux.onNotificationByMethod('fs.streamChunk', goodHandler)
|
||||
|
||||
expect(() =>
|
||||
transport.dataCallbacks[0](
|
||||
makeNotificationFrame('fs.streamChunk', { streamId: 1, seq: 0, data: 'aGk=' }, 1)
|
||||
)
|
||||
).not.toThrow()
|
||||
|
||||
expect(badHandler).toHaveBeenCalled()
|
||||
expect(goodHandler).toHaveBeenCalledWith({ streamId: 1, seq: 0, data: 'aGk=' })
|
||||
expect(mux.isDisposed()).toBe(false)
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'[ssh-mux] Method notification handler failed for fs.streamChunk: stream consumer exploded'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('keepalive', () => {
|
||||
|
||||
@@ -361,13 +361,33 @@ export class SshChannelMultiplexer {
|
||||
// collection and skips the next handler. Iterating a snapshot prevents that.
|
||||
const snapshot = Array.from(this.notificationHandlers)
|
||||
for (const handler of snapshot) {
|
||||
handler(msg.method, params)
|
||||
try {
|
||||
handler(msg.method, params)
|
||||
} catch (err) {
|
||||
// Why: relay notifications arrive on the SSH stream callback; one
|
||||
// bad subscriber must not escape as a main-process uncaught exception.
|
||||
console.warn(
|
||||
`[ssh-mux] Notification handler failed for ${msg.method}: ${
|
||||
err instanceof Error ? err.message : String(err)
|
||||
}`
|
||||
)
|
||||
}
|
||||
}
|
||||
const methodHandlers = this.methodNotificationHandlers.get(msg.method)
|
||||
if (methodHandlers && methodHandlers.size > 0) {
|
||||
const methodSnapshot = Array.from(methodHandlers)
|
||||
for (const handler of methodSnapshot) {
|
||||
handler(params)
|
||||
try {
|
||||
handler(params)
|
||||
} catch (err) {
|
||||
// Why: file-stream and PTY listeners are per-method subscribers; keep
|
||||
// the mux alive even if one consumer rejects a malformed notification.
|
||||
console.warn(
|
||||
`[ssh-mux] Method notification handler failed for ${msg.method}: ${
|
||||
err instanceof Error ? err.message : String(err)
|
||||
}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user