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 session?: SSHSession
private sessionStack: SSHSession[] = [] private sessionStack: SSHSession[] = []
private homeEndSubscription: Subscription private homeEndSubscription: Subscription
private recentInputs = ''
private reconnectOffered = false
private sessionHandlers: Subscription[] = []
constructor ( constructor (
injector: Injector, injector: Injector,
@ -57,6 +60,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.frontendReady$.pipe(first()).subscribe(() => { this.frontendReady$.pipe(first()).subscribe(() => {
this.initializeSession() this.initializeSession()
this.input$.subscribe(data => {
this.recentInputs += data
this.recentInputs = this.recentInputs.substring(this.recentInputs.length - 32)
})
}) })
super.ngOnInit() super.ngOnInit()
@ -68,12 +75,19 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
async setupOneSession (session: SSHSession): Promise<void> { async setupOneSession (session: SSHSession): Promise<void> {
if (session.connection.jumpHost) { 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) const jumpSession = this.ssh.createSession(jumpConnection)
await this.setupOneSession(jumpSession) await this.setupOneSession(jumpSession)
this.sessionHandlers.push(
jumpSession.destroyed$.subscribe(() => session.destroy()) jumpSession.destroyed$.subscribe(() => session.destroy())
)
session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut( session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut(
'127.0.0.1', 0, session.connection.host, session.connection.port ?? 22, '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) this.sessionStack.push(session)
} }
this.sessionHandlers.push(session.serviceMessage$.subscribe(msg => {
session.serviceMessage$.subscribe(msg => {
this.write(`\r\n${colors.black.bgWhite(' SSH ')} ${msg}\r\n`) this.write(`\r\n${colors.black.bgWhite(' SSH ')} ${msg}\r\n`)
session.resize(this.size.columns, this.size.rows) 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`) 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`) 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> { async initializeSession (): Promise<void> {
this.reconnectOffered = false
if (!this.connection) { if (!this.connection) {
this.logger.error('No SSH connection info supplied') this.logger.error('No SSH connection info supplied')
return return
@ -136,7 +167,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.session = this.ssh.createSession(this.connection) this.session = this.ssh.createSession(this.connection)
try {
await this.setupOneSession(this.session) await this.setupOneSession(this.session)
} catch (e) {
this.write(colors.black.bgRed(' X ') + ' ' + colors.red(e.message) + '\r\n')
}
this.attachSessionHandlers() this.attachSessionHandlers()
@ -158,6 +193,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
} }
async reconnect (): Promise<void> { async reconnect (): Promise<void> {
for (const s of this.sessionHandlers) {
s.unsubscribe()
}
this.sessionHandlers = []
this.session?.destroy() this.session?.destroy()
await this.initializeSession() await this.initializeSession()
this.session?.releaseInitialDataBuffer() this.session?.releaseInitialDataBuffer()