diff --git a/src/main/shell-session/shell-session.ts b/src/main/shell-session/shell-session.ts index 1d6a7b56ff..6c37c4ced3 100644 --- a/src/main/shell-session/shell-session.ts +++ b/src/main/shell-session/shell-session.ts @@ -191,6 +191,7 @@ export abstract class ShellSession { this.running = true; shellProcess.onData(data => this.send({ type: TerminalChannels.STDOUT, data })); shellProcess.onExit(({ exitCode }) => { + logger.info(`[SHELL-SESSION]: shell has exited for ${this.terminalId} closed with code=${exitCode}`); this.running = false; if (exitCode > 0) { @@ -204,7 +205,7 @@ export abstract class ShellSession { this.websocket .on("message", (data: string | Uint8Array) => { if (!this.running) { - return; + return void logger.debug(`[SHELL-SESSION]: received message from ${this.terminalId}, but shellProcess isn't running`); } if (typeof data === "string") { @@ -230,15 +231,24 @@ export abstract class ShellSession { } }) .on("close", code => { - logger.debug(`[SHELL-SESSION]: websocket for ${this.terminalId} closed with code=${code}`); + logger.info(`[SHELL-SESSION]: websocket for ${this.terminalId} closed with code=${code}`); - if (this.running && code !== WebSocketCloseEvent.AbnormalClosure && code !== WebSocketCloseEvent.GoingAway) { + const stopShellSession = this.running + && !( + code === WebSocketCloseEvent.AbnormalClosure + || ( + code === WebSocketCloseEvent.GoingAway + && !this.cluster.disconnected + ) + ); + + if (stopShellSession) { try { logger.info(`[SHELL-SESSION]: Killing shell process for ${this.terminalId}`); process.kill(shellProcess.pid); ShellSession.processes.delete(this.terminalId); - } catch (e) { - } + } catch {} + this.running = false; } }); diff --git a/src/renderer/api/terminal-api.ts b/src/renderer/api/terminal-api.ts index 62ab718b57..8cca03538b 100644 --- a/src/renderer/api/terminal-api.ts +++ b/src/renderer/api/terminal-api.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { boundMethod, getHostedClusterId } from "../utils"; +import { getHostedClusterId } from "../utils"; import { WebSocketApi, WebSocketEvents } from "./websocket-api"; import isEqual from "lodash/isEqual"; import url from "url"; @@ -27,6 +27,7 @@ import { makeObservable, observable } from "mobx"; import { ipcRenderer } from "electron"; import logger from "../../common/logger"; import { deserialize, serialize } from "v8"; +import { once } from "lodash"; export enum TerminalChannels { STDIN = "stdin", @@ -119,7 +120,26 @@ export class TerminalApi extends WebSocketApi { slashes: true, }); - this.prependListener("data", this._onReady); + const onReady = once((data?: string) => { + this.isReady = true; + this.emit("ready"); + this.removeListener("data", onReady); + this.removeListener("connected", onReady); + this.flush(); + + // data is undefined if the event that was handled is "connected" + if (data === undefined) { + /** + * Output the last line, the makes sure that the terminal isn't completely + * empty when the user refreshes. + */ + this.emit("data", window.localStorage.getItem(`${this.query.id}:last-data`)); + } + }); + + this.prependListener("data", onReady); + this.prependListener("connected", onReady); + super.connect(socketUrl); this.socket.binaryType = "arraybuffer"; } @@ -132,14 +152,6 @@ export class TerminalApi extends WebSocketApi { setTimeout(() => super.destroy(), 2000); } - @boundMethod - protected _onReady() { - this.isReady = true; - this.emit("ready"); - this.removeListener("data", this._onReady); - this.flush(); - } - reconnect() { super.reconnect(); } @@ -166,6 +178,11 @@ export class TerminalApi extends WebSocketApi { switch (message.type) { case TerminalChannels.STDOUT: + /** + * save the last data for reconnections. User localStorage because we + * don't want this data to survive if the app is closed + */ + window.localStorage.setItem(`${this.query.id}:last-data`, message.data); super._onMessage({ data: message.data, ...evt }); break; case TerminalChannels.CONNECTED: