2019-07-02 01:01:09 +02:00
|
|
|
import * as cp from "child_process";
|
|
|
|
|
|
|
|
import { getPathFromAmdModule } from "vs/base/common/amd";
|
2019-06-28 00:34:33 +02:00
|
|
|
import { VSBuffer } from "vs/base/common/buffer";
|
2019-07-02 01:01:09 +02:00
|
|
|
import { Emitter } from "vs/base/common/event";
|
|
|
|
import { ISocket } from "vs/base/parts/ipc/common/ipc.net";
|
2019-07-16 01:55:30 +02:00
|
|
|
import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net";
|
2019-07-02 01:01:09 +02:00
|
|
|
import { ILogService } from "vs/platform/log/common/log";
|
2019-07-20 00:43:54 +02:00
|
|
|
import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
|
2019-06-28 00:34:33 +02:00
|
|
|
|
2019-07-12 00:12:52 +02:00
|
|
|
import { Protocol } from "vs/server/src/protocol";
|
|
|
|
import { uriTransformerPath } from "vs/server/src/util";
|
2019-06-29 00:37:23 +02:00
|
|
|
|
2019-06-28 00:34:33 +02:00
|
|
|
export abstract class Connection {
|
2019-07-16 01:55:30 +02:00
|
|
|
protected readonly _onClose = new Emitter<void>();
|
2019-06-28 00:34:33 +02:00
|
|
|
public readonly onClose = this._onClose.event;
|
2019-07-16 01:55:30 +02:00
|
|
|
protected disposed: boolean = false;
|
|
|
|
public constructor(protected protocol: Protocol) {}
|
2019-06-29 00:37:23 +02:00
|
|
|
/**
|
2019-07-02 01:01:09 +02:00
|
|
|
* Set up the connection on a new socket.
|
2019-06-29 00:37:23 +02:00
|
|
|
*/
|
2019-07-16 01:55:30 +02:00
|
|
|
public abstract reconnect(socket: ISocket, buffer: VSBuffer): void;
|
2019-07-02 01:01:09 +02:00
|
|
|
protected abstract dispose(): void;
|
2019-06-28 00:34:33 +02:00
|
|
|
}
|
|
|
|
|
2019-06-29 00:37:23 +02:00
|
|
|
/**
|
2019-07-02 01:01:09 +02:00
|
|
|
* Used for all the IPC channels.
|
2019-06-29 00:37:23 +02:00
|
|
|
*/
|
2019-06-28 00:34:33 +02:00
|
|
|
export class ManagementConnection extends Connection {
|
2019-07-16 01:55:30 +02: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-02 01:01:09 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 01:55:30 +02:00
|
|
|
public reconnect(socket: ISocket, buffer: VSBuffer): void {
|
|
|
|
clearTimeout(this.timeout as any); // Not sure why the type doesn't work.
|
2019-07-02 01:01:09 +02:00
|
|
|
this.protocol.beginAcceptReconnection(socket, buffer);
|
|
|
|
this.protocol.endAcceptReconnection();
|
2019-06-29 00:37:23 +02:00
|
|
|
}
|
2019-07-16 01:55:30 +02: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 00:34:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export class ExtensionHostConnection extends Connection {
|
2019-07-02 01:01:09 +02:00
|
|
|
private process: cp.ChildProcess;
|
|
|
|
|
2019-07-20 00:43:54 +02:00
|
|
|
public constructor(protocol: Protocol, buffer: VSBuffer, private readonly log: ILogService) {
|
2019-07-02 01:01:09 +02:00
|
|
|
super(protocol);
|
2019-07-16 01:55:30 +02:00
|
|
|
protocol.dispose();
|
|
|
|
this.process = this.spawn(buffer);
|
2019-07-02 01:01:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected dispose(): void {
|
2019-07-16 01:55:30 +02:00
|
|
|
if (!this.disposed) {
|
|
|
|
this.disposed = true;
|
|
|
|
this.process.kill();
|
|
|
|
this.protocol.getSocket().end();
|
|
|
|
this._onClose.fire();
|
|
|
|
}
|
2019-07-02 01:01:09 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 01:55:30 +02: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-02 01:01:09 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 01:55:30 +02:00
|
|
|
private sendInitMessage(buffer: VSBuffer): void {
|
|
|
|
const socket = this.protocol.getUnderlyingSocket();
|
2019-07-02 01:01:09 +02:00
|
|
|
socket.pause();
|
2019-07-20 00:43:54 +02:00
|
|
|
this.process.send({
|
2019-07-02 01:01:09 +02:00
|
|
|
type: "VSCODE_EXTHOST_IPC_SOCKET",
|
|
|
|
initialDataChunk: (buffer.buffer as Buffer).toString("base64"),
|
2019-07-16 01:55:30 +02:00
|
|
|
skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket,
|
2019-07-20 00:43:54 +02:00
|
|
|
}, socket);
|
2019-07-02 01:01:09 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 01:55:30 +02:00
|
|
|
private spawn(buffer: VSBuffer): cp.ChildProcess {
|
2019-07-02 01:01:09 +02:00
|
|
|
const proc = cp.fork(
|
|
|
|
getPathFromAmdModule(require, "bootstrap-fork"),
|
2019-07-20 00:43:54 +02:00
|
|
|
[ "--type=extensionHost", `--uriTransformerPath=${uriTransformerPath()}` ],
|
2019-07-02 01:01:09 +02:00
|
|
|
{
|
|
|
|
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",
|
|
|
|
},
|
|
|
|
silent: true,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2019-07-16 01:55:30 +02:00
|
|
|
proc.on("error", () => this.dispose());
|
|
|
|
proc.on("exit", () => this.dispose());
|
2019-07-20 00:43:54 +02: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));
|
2019-07-02 01:01:09 +02:00
|
|
|
proc.on("message", (event) => {
|
|
|
|
if (event && event.type === "__$console") {
|
|
|
|
const severity = this.log[event.severity] ? event.severity : "info";
|
|
|
|
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 01:55:30 +02:00
|
|
|
this.sendInitMessage(buffer);
|
2019-07-02 01:01:09 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-07-20 00:43:54 +02:00
|
|
|
return proc.on("message", listen);
|
2019-07-02 01:01:09 +02:00
|
|
|
}
|
2019-06-28 00:34:33 +02:00
|
|
|
}
|