code-server/src/connection.ts

142 lines
4.6 KiB
TypeScript
Raw Normal View History

import * as cp from "child_process";
import { getPathFromAmdModule } from "vs/base/common/amd";
2019-06-28 05:34:33 +07:00
import { VSBuffer } from "vs/base/common/buffer";
import { Emitter } from "vs/base/common/event";
import { ISocket } from "vs/base/parts/ipc/common/ipc.net";
2019-07-16 06:55:30 +07:00
import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net";
import { IEnvironmentService } from "vs/platform/environment/common/environment";
import { ILogService } from "vs/platform/log/common/log";
import { getNlsConfiguration } from "vs/server/src/nls";
2019-07-12 05:12:52 +07:00
import { Protocol } from "vs/server/src/protocol";
import { uriTransformerPath } from "vs/server/src/util";
2019-08-10 06:50:05 +07:00
import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
2019-06-29 05:37:23 +07:00
2019-06-28 05:34:33 +07:00
export abstract class Connection {
2019-07-16 06:55:30 +07:00
protected readonly _onClose = new Emitter<void>();
2019-06-28 05:34:33 +07:00
public readonly onClose = this._onClose.event;
2019-07-16 06:55:30 +07:00
protected disposed: boolean = false;
public constructor(protected protocol: Protocol) {}
2019-06-29 05:37:23 +07:00
/**
* Set up the connection on a new socket.
2019-06-29 05:37:23 +07:00
*/
2019-07-16 06:55:30 +07:00
public abstract reconnect(socket: ISocket, buffer: VSBuffer): void;
protected abstract dispose(): void;
2019-06-28 05:34:33 +07:00
}
2019-06-29 05:37:23 +07:00
/**
* Used for all the IPC channels.
2019-06-29 05:37:23 +07:00
*/
2019-06-28 05:34:33 +07:00
export class ManagementConnection extends Connection {
2019-07-16 06:55:30 +07:00
private timeout: NodeJS.Timeout | undefined;
private readonly wait = 1000 * 60;
public constructor(protocol: Protocol) {
super(protocol);
protocol.onClose(() => this.dispose());
protocol.onSocketClose(() => {
this.timeout = setTimeout(() => this.dispose(), this.wait);
});
}
2019-07-16 06:55:30 +07:00
public reconnect(socket: ISocket, buffer: VSBuffer): void {
clearTimeout(this.timeout as any); // Not sure why the type doesn't work.
this.protocol.beginAcceptReconnection(socket, buffer);
this.protocol.endAcceptReconnection();
2019-06-29 05:37:23 +07:00
}
2019-07-16 06:55:30 +07:00
protected dispose(): void {
if (!this.disposed) {
clearTimeout(this.timeout as any); // Not sure why the type doesn't work.
this.disposed = true;
this.protocol.sendDisconnect();
this.protocol.dispose();
this.protocol.getSocket().end();
this._onClose.fire();
}
}
2019-06-28 05:34:33 +07:00
}
export class ExtensionHostConnection extends Connection {
private process?: cp.ChildProcess;
public constructor(
locale:string, protocol: Protocol, buffer: VSBuffer,
private readonly log: ILogService,
private readonly environment: IEnvironmentService,
) {
super(protocol);
2019-07-31 05:20:53 +07:00
this.protocol.dispose();
this.spawn(locale, buffer).then((p) => this.process = p);
2019-07-31 05:20:53 +07:00
this.protocol.getUnderlyingSocket().pause();
}
protected dispose(): void {
2019-07-16 06:55:30 +07:00
if (!this.disposed) {
this.disposed = true;
if (this.process) {
this.process.kill();
}
2019-07-16 06:55:30 +07:00
this.protocol.getSocket().end();
this._onClose.fire();
}
}
2019-07-16 06:55:30 +07:00
public reconnect(socket: ISocket, buffer: VSBuffer): void {
// This is just to set the new socket.
this.protocol.beginAcceptReconnection(socket, null);
this.protocol.dispose();
this.sendInitMessage(buffer);
}
2019-07-16 06:55:30 +07:00
private sendInitMessage(buffer: VSBuffer): void {
const socket = this.protocol.getUnderlyingSocket();
socket.pause();
this.process!.send({ // Process must be set at this point.
type: "VSCODE_EXTHOST_IPC_SOCKET",
initialDataChunk: (buffer.buffer as Buffer).toString("base64"),
2019-07-16 06:55:30 +07:00
skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket,
2019-07-31 05:20:53 +07:00
}, socket);
}
private async spawn(locale: string, buffer: VSBuffer): Promise<cp.ChildProcess> {
const config = await getNlsConfiguration(locale, this.environment.userDataPath);
const proc = cp.fork(
getPathFromAmdModule(require, "bootstrap-fork"),
2019-07-23 04:00:59 +07:00
[ "--type=extensionHost", `--uriTransformerPath=${uriTransformerPath}` ],
{
env: {
...process.env,
AMD_ENTRYPOINT: "vs/workbench/services/extensions/node/extensionHostProcess",
PIPE_LOGGING: "true",
VERBOSE_LOGGING: "true",
VSCODE_EXTHOST_WILL_SEND_SOCKET: "true",
VSCODE_HANDLES_UNCAUGHT_ERRORS: "true",
VSCODE_LOG_STACK: "false",
VSCODE_NLS_CONFIG: JSON.stringify(config),
},
silent: true,
},
);
2019-07-16 06:55:30 +07:00
proc.on("error", () => this.dispose());
proc.on("exit", () => this.dispose());
2019-07-20 05:43:54 +07:00
proc.stdout.setEncoding("utf8").on("data", (d) => this.log.info("Extension host stdout", d));
proc.stderr.setEncoding("utf8").on("data", (d) => this.log.error("Extension host stderr", d));
proc.on("message", (event) => {
if (event && event.type === "__$console") {
2019-08-10 06:50:05 +07:00
const severity = (<any>this.log)[event.severity] ? event.severity : "info";
(<any>this.log)[severity]("Extension host", event.arguments);
}
});
const listen = (message: IExtHostReadyMessage) => {
if (message.type === "VSCODE_EXTHOST_IPC_READY") {
proc.removeListener("message", listen);
2019-07-16 06:55:30 +07:00
this.sendInitMessage(buffer);
}
};
2019-07-20 05:43:54 +07:00
return proc.on("message", listen);
}
2019-06-28 05:34:33 +07:00
}