ssh: better session close and reconnect behaviours - fixes #3351, fixes #3010, fixes #3276, fixes #3074, fixes #2825, fixes #3285

This commit is contained in:
Eugene Pankov 2021-01-24 13:28:59 +01:00
parent d0a597634d
commit 67bacb9dd3
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4

View File

@ -23,6 +23,9 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
session?: SSHSession
private sessionStack: SSHSession[] = []
private homeEndSubscription: Subscription
private recentInputs = ''
private reconnectOffered = false
private sessionHandlers: Subscription[] = []
constructor (
injector: Injector,
@ -57,6 +60,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.frontendReady$.pipe(first()).subscribe(() => {
this.initializeSession()
this.input$.subscribe(data => {
this.recentInputs += data
this.recentInputs = this.recentInputs.substring(this.recentInputs.length - 32)
})
})
super.ngOnInit()
@ -68,12 +75,19 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
async setupOneSession (session: SSHSession): Promise<void> {
if (session.connection.jumpHost) {
const jumpConnection = this.config.store.ssh.connections.find(x => x.name === session.connection.jumpHost)
const jumpConnection: SSHConnection|null = this.config.store.ssh.connections.find(x => x.name === session.connection.jumpHost)
if (!jumpConnection) {
throw new Error(`${session.connection.host}: jump host "${session.connection.jumpHost}" not found in your config`)
}
const jumpSession = this.ssh.createSession(jumpConnection)
await this.setupOneSession(jumpSession)
this.sessionHandlers.push(
jumpSession.destroyed$.subscribe(() => session.destroy())
)
session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut(
'127.0.0.1', 0, session.connection.host, session.connection.port ?? 22,
@ -93,15 +107,31 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.sessionStack.push(session)
}
session.serviceMessage$.subscribe(msg => {
this.sessionHandlers.push(session.serviceMessage$.subscribe(msg => {
this.write(`\r\n${colors.black.bgWhite(' SSH ')} ${msg}\r\n`)
session.resize(this.size.columns, this.size.rows)
})
}))
session.destroyed$.subscribe(() => {
this.sessionHandlers.push(session.destroyed$.subscribe(() => {
if (
// Ctrl-D
this.recentInputs.charCodeAt(this.recentInputs.length - 1) === 4 ||
this.recentInputs.endsWith('exit\r')
) {
// User closed the session
this.destroy()
} else {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` ${session.connection.host}: session closed\r\n`)
if (!this.reconnectOffered) {
this.reconnectOffered = true
this.write('Press any key to reconnect\r\n')
this.input$.pipe(first()).subscribe(() => {
this.reconnect()
})
}
}
}))
this.write('\r\n' + colors.black.bgCyan(' SSH ') + ` Connecting to ${session.connection.host}\r\n`)
@ -129,6 +159,7 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
}
async initializeSession (): Promise<void> {
this.reconnectOffered = false
if (!this.connection) {
this.logger.error('No SSH connection info supplied')
return
@ -136,7 +167,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.session = this.ssh.createSession(this.connection)
try {
await this.setupOneSession(this.session)
} catch (e) {
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
}
this.attachSessionHandlers()
@ -158,6 +193,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
}
async reconnect (): Promise<void> {
for (const s of this.sessionHandlers) {
s.unsubscribe()
}
this.sessionHandlers = []
this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()