2019-01-12 02:33:44 +07:00
|
|
|
import { logger, field } from "@coder/logger";
|
2019-01-16 01:36:09 +07:00
|
|
|
import * as os from "os";
|
2019-01-13 02:44:29 +07:00
|
|
|
import { TextDecoder } from "text-encoding";
|
2019-01-16 01:36:09 +07:00
|
|
|
import { ClientMessage, InitMessage, ServerMessage } from "../proto";
|
2019-01-12 02:33:44 +07:00
|
|
|
import { evaluate } from "./evaluate";
|
|
|
|
import { ReadWriteConnection } from "../common/connection";
|
2019-01-13 02:44:29 +07:00
|
|
|
import { Process, handleNewSession } from "./command";
|
2019-01-12 02:33:44 +07:00
|
|
|
|
2019-01-16 01:36:09 +07:00
|
|
|
export interface ServerOptions {
|
|
|
|
readonly workingDirectory: string;
|
|
|
|
readonly dataDirectory: string;
|
|
|
|
}
|
|
|
|
|
2019-01-12 02:33:44 +07:00
|
|
|
export class Server {
|
|
|
|
|
2019-01-13 02:44:29 +07:00
|
|
|
private readonly sessions: Map<number, Process>;
|
|
|
|
|
2019-01-12 02:33:44 +07:00
|
|
|
public constructor(
|
|
|
|
private readonly connection: ReadWriteConnection,
|
2019-01-16 01:36:09 +07:00
|
|
|
options?: ServerOptions,
|
2019-01-12 02:33:44 +07:00
|
|
|
) {
|
2019-01-13 02:44:29 +07:00
|
|
|
this.sessions = new Map();
|
|
|
|
|
2019-01-12 02:33:44 +07:00
|
|
|
connection.onMessage((data) => {
|
|
|
|
try {
|
|
|
|
this.handleMessage(ClientMessage.deserializeBinary(data));
|
|
|
|
} catch (ex) {
|
|
|
|
logger.error("Failed to handle client message", field("length", data.byteLength), field("exception", ex));
|
|
|
|
}
|
|
|
|
});
|
2019-01-16 01:36:09 +07:00
|
|
|
|
|
|
|
if (!options) {
|
|
|
|
logger.warn("No server options provided. InitMessage will not be sent.");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const initMsg = new InitMessage();
|
|
|
|
initMsg.setDataDirectory(options.dataDirectory);
|
|
|
|
initMsg.setWorkingDirectory(options.workingDirectory);
|
|
|
|
initMsg.setHomeDirectory(os.homedir());
|
|
|
|
initMsg.setTmpDirectory(os.tmpdir());
|
|
|
|
const platform = os.platform();
|
|
|
|
let operatingSystem: InitMessage.OperatingSystem;
|
|
|
|
switch (platform) {
|
|
|
|
case "win32":
|
|
|
|
operatingSystem = InitMessage.OperatingSystem.WINDOWS;
|
|
|
|
break;
|
|
|
|
case "linux":
|
|
|
|
operatingSystem = InitMessage.OperatingSystem.LINUX;
|
|
|
|
break;
|
|
|
|
case "darwin":
|
|
|
|
operatingSystem = InitMessage.OperatingSystem.MAC;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Error(`unrecognized platform "${platform}"`);
|
|
|
|
}
|
|
|
|
initMsg.setOperatingSystem(operatingSystem);
|
|
|
|
const srvMsg = new ServerMessage();
|
|
|
|
srvMsg.setInit(initMsg);
|
|
|
|
connection.send(srvMsg.serializeBinary());
|
2019-01-12 02:33:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
private handleMessage(message: ClientMessage): void {
|
|
|
|
if (message.hasNewEval()) {
|
|
|
|
evaluate(this.connection, message.getNewEval()!);
|
2019-01-13 02:44:29 +07:00
|
|
|
} else if (message.hasNewSession()) {
|
|
|
|
const session = handleNewSession(this.connection, message.getNewSession()!, () => {
|
|
|
|
this.sessions.delete(message.getNewSession()!.getId());
|
|
|
|
});
|
|
|
|
|
|
|
|
this.sessions.set(message.getNewSession()!.getId(), session);
|
|
|
|
} else if (message.hasCloseSessionInput()) {
|
|
|
|
const s = this.getSession(message.getCloseSessionInput()!.getId());
|
|
|
|
if (!s || !s.stdin) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s.stdin.end();
|
|
|
|
} else if (message.hasResizeSessionTty()) {
|
|
|
|
const s = this.getSession(message.getResizeSessionTty()!.getId());
|
|
|
|
if (!s || !s.resize) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const tty = message.getResizeSessionTty()!.getTtyDimensions()!;
|
|
|
|
s.resize(tty.getWidth(), tty.getHeight());
|
|
|
|
} else if (message.hasShutdownSession()) {
|
|
|
|
const s = this.getSession(message.getShutdownSession()!.getId());
|
|
|
|
if (!s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s.kill(message.getShutdownSession()!.getSignal());
|
|
|
|
} else if (message.hasWriteToSession()) {
|
|
|
|
const s = this.getSession(message.getWriteToSession()!.getId());
|
|
|
|
if (!s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s.write(new TextDecoder().decode(message.getWriteToSession()!.getData_asU8()));
|
2019-01-12 02:33:44 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-13 02:44:29 +07:00
|
|
|
private getSession(id: number): Process | undefined {
|
|
|
|
return this.sessions.get(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|