Quality check

This commit is contained in:
Asher 2019-07-19 17:43:54 -05:00
parent e8cb6ffaa0
commit 9fdfacb314
No known key found for this signature in database
GPG Key ID: D63C1EF81242354A
11 changed files with 193 additions and 428 deletions

View File

@ -1,18 +1,14 @@
/* global require, global, process, __dirname */
if (!global.NBIN_LOADED) { if (!global.NBIN_LOADED) {
try { try {
const nbin = require("nbin"); const nbin = require("nbin");
nbin.shimNativeFs("{{ROOT_PATH}}"); nbin.shimNativeFs("{{ROOT_PATH}}");
global.NBIN_LOADED = true; global.NBIN_LOADED = true;
const path = require("path"); const path = require("path");
const rg = require("vscode-ripgrep"); const rg = require("vscode-ripgrep");
rg.binaryRgPath = rg.rgPath; rg.binaryRgPath = rg.rgPath;
rg.rgPath = path.join( rg.rgPath = path.join(
require("os").tmpdir(), require("os").tmpdir(),
`code-server/${path.basename(rg.binaryRgPath)}`, `code-server/${path.basename(rg.binaryRgPath)}`
); );
} catch (error) { } catch (error) { /* Not in the binary. */ }
// Not in the binary.
}
} }

View File

@ -60,7 +60,6 @@ export const vscodeApi = (serviceCollection: ServiceCollection): typeof vscode =
FileSystemError: extHostTypes.FileSystemError, FileSystemError: extHostTypes.FileSystemError,
FileType: FileType, FileType: FileType,
Uri: URI, Uri: URI,
commands: { commands: {
executeCommand: (commandId: string, ...args: any[]): any => { executeCommand: (commandId: string, ...args: any[]): any => {
return commandService.executeCommand(commandId, ...args); return commandService.executeCommand(commandId, ...args);
@ -69,7 +68,6 @@ export const vscodeApi = (serviceCollection: ServiceCollection): typeof vscode =
return CommandsRegistry.registerCommand(id, command); return CommandsRegistry.registerCommand(id, command);
}, },
}, },
window: { window: {
registerTreeDataProvider: (id: string, dataProvider: ITreeViewDataProvider): void => { registerTreeDataProvider: (id: string, dataProvider: ITreeViewDataProvider): void => {
const view = viewsRegistry.getView(id); const view = viewsRegistry.getView(id);
@ -81,7 +79,6 @@ export const vscodeApi = (serviceCollection: ServiceCollection): typeof vscode =
notificationService.error(message); notificationService.error(message);
}, },
}, },
workspace: { workspace: {
registerFileSystemProvider: (scheme: string, provider: vscode.FileSystemProvider): IDisposable => { registerFileSystemProvider: (scheme: string, provider: vscode.FileSystemProvider): IDisposable => {
return fileService.registerProvider(scheme, new FileSystemProvider(provider)); return fileService.registerProvider(scheme, new FileSystemProvider(provider));
@ -95,7 +92,6 @@ export const vscodeApi = (serviceCollection: ServiceCollection): typeof vscode =
*/ */
export const coderApi = (serviceCollection: ServiceCollection): typeof coder => { export const coderApi = (serviceCollection: ServiceCollection): typeof coder => {
const getService = <T>(id: ServiceIdentifier<T>): T => serviceCollection.get<T>(id) as T; const getService = <T>(id: ServiceIdentifier<T>): T => serviceCollection.get<T>(id) as T;
return { return {
workbench: { workbench: {
action: Action, action: Action,
@ -103,13 +99,8 @@ export const coderApi = (serviceCollection: ServiceCollection): typeof coder =>
commandRegistry: CommandsRegistry, commandRegistry: CommandsRegistry,
actionsRegistry: Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions), actionsRegistry: Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions),
registerView: (viewId, viewName, containerId, containerName, icon): void => { registerView: (viewId, viewName, containerId, containerName, icon): void => {
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewsExtensions.ViewContainersRegistry);
const viewsRegistry = Registry.as<IViewsRegistry>(ViewsExtensions.ViewsRegistry);
const container = viewContainersRegistry.registerViewContainer(containerId);
const cssClass = `extensionViewlet-${containerId}`; const cssClass = `extensionViewlet-${containerId}`;
const id = `workbench.view.extension.${containerId}`; const id = `workbench.view.extension.${containerId}`;
class CustomViewlet extends ViewContainerViewlet { class CustomViewlet extends ViewContainerViewlet {
public constructor( public constructor(
@IConfigurationService configurationService: IConfigurationService, @IConfigurationService configurationService: IConfigurationService,
@ -127,44 +118,32 @@ export const coderApi = (serviceCollection: ServiceCollection): typeof coder =>
} }
} }
const viewletDescriptor = new ViewletDescriptor( Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(
CustomViewlet as any, new ViewletDescriptor(CustomViewlet as any, id, containerName, cssClass, undefined, URI.parse(icon)),
id,
containerName,
cssClass,
undefined,
URI.parse(icon),
); );
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registerWorkbenchAction(
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(
new SyncActionDescriptor(OpenCustomViewletAction as any, id, localize("showViewlet", "Show {0}", containerName)), new SyncActionDescriptor(OpenCustomViewletAction as any, id, localize("showViewlet", "Show {0}", containerName)),
"View: Show {0}", "View: Show {0}",
localize("view", "View"), localize("view", "View"),
); );
// Generate CSS to show the icon in the activity bar // Generate CSS to show the icon in the activity bar.
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`; const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`;
createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%`); createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%`);
const views = [{ const container = Registry.as<IViewContainersRegistry>(ViewsExtensions.ViewContainersRegistry).registerViewContainer(containerId);
Registry.as<IViewsRegistry>(ViewsExtensions.ViewsRegistry).registerViews([{
id: viewId, id: viewId,
name: viewName, name: viewName,
ctorDescriptor: { ctor: CustomTreeViewPanel }, ctorDescriptor: { ctor: CustomTreeViewPanel },
treeView: getService(IInstantiationService).createInstance(CustomTreeView as any, viewId, container), treeView: getService(IInstantiationService).createInstance(CustomTreeView as any, viewId, container),
}] as ITreeViewDescriptor[]; }] as ITreeViewDescriptor[], container);
viewsRegistry.registerViews(views, container);
}, },
// Even though the enums are exactly the same, Typescript says they are
// not assignable to each other, so use `any`. I don't know if there is a
// way around this.
menuRegistry: MenuRegistry as any, menuRegistry: MenuRegistry as any,
statusbarService: getService(IStatusbarService) as any, statusbarService: getService(IStatusbarService) as any,
notificationService: getService(INotificationService), notificationService: getService(INotificationService),
terminalService: getService(ITerminalService), terminalService: getService(ITerminalService),
onFileCreate: (cb): void => { onFileCreate: (cb): void => {
getService<IFileService>(IFileService).onAfterOperation((e) => { getService<IFileService>(IFileService).onAfterOperation((e) => {
if (e.operation === FileOperation.CREATE) { if (e.operation === FileOperation.CREATE) {
@ -198,7 +177,6 @@ export const coderApi = (serviceCollection: ServiceCollection): typeof coder =>
} }
}); });
}, },
onModelAdded: (cb): void => { onModelAdded: (cb): void => {
getService<IModelService>(IModelService).onModelAdded((e) => { getService<IModelService>(IModelService).onModelAdded((e) => {
cb(e.uri.path, e.getLanguageIdentifier().language); cb(e.uri.path, e.getLanguageIdentifier().language);
@ -214,7 +192,6 @@ export const coderApi = (serviceCollection: ServiceCollection): typeof coder =>
cb(e.model.uri.path, e.model.getLanguageIdentifier().language, e.oldModeId); cb(e.model.uri.path, e.model.getLanguageIdentifier().language, e.oldModeId);
}); });
}, },
onTerminalAdded: (cb): void => { onTerminalAdded: (cb): void => {
getService<ITerminalService>(ITerminalService).onInstanceCreated(() => cb()); getService<ITerminalService>(ITerminalService).onInstanceCreated(() => cb());
}, },
@ -222,7 +199,6 @@ export const coderApi = (serviceCollection: ServiceCollection): typeof coder =>
getService<ITerminalService>(ITerminalService).onInstanceDisposed(() => cb()); getService<ITerminalService>(ITerminalService).onInstanceDisposed(() => cb());
}, },
}, },
// @ts-ignore // @ts-ignore
MenuId: MenuId, MenuId: MenuId,
Severity: Severity, Severity: Severity,
@ -250,9 +226,7 @@ class FileSystemProvider implements IFileSystemProvider {
public readonly capabilities: FileSystemProviderCapabilities; public readonly capabilities: FileSystemProviderCapabilities;
public readonly onDidChangeCapabilities: Event<void> = Event.None; public readonly onDidChangeCapabilities: Event<void> = Event.None;
public constructor( public constructor(private readonly provider: vscode.FileSystemProvider) {
private readonly provider: vscode.FileSystemProvider,
) {
this.capabilities = FileSystemProviderCapabilities.Readonly; this.capabilities = FileSystemProviderCapabilities.Readonly;
} }

View File

@ -43,9 +43,6 @@ class Watcher extends DiskFileSystemProvider {
} }
} }
/**
* See: src/vs/platform/remote/common/remoteAgentFileSystemChannel.ts.
*/
export class FileProviderChannel implements IServerChannel, IDisposable { export class FileProviderChannel implements IServerChannel, IDisposable {
private readonly provider: DiskFileSystemProvider; private readonly provider: DiskFileSystemProvider;
private readonly watchers = new Map<string, Watcher>(); private readonly watchers = new Map<string, Watcher>();
@ -175,9 +172,6 @@ export class FileProviderChannel implements IServerChannel, IDisposable {
} }
} }
/**
* See: src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts.
*/
export class ExtensionEnvironmentChannel implements IServerChannel { export class ExtensionEnvironmentChannel implements IServerChannel {
public constructor( public constructor(
private readonly environment: IEnvironmentService, private readonly environment: IEnvironmentService,
@ -245,7 +239,6 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
}; };
return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => { return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => {
// It's possible to get duplicates.
const uniqueExtensions = new Map<string, IExtensionDescription>(); const uniqueExtensions = new Map<string, IExtensionDescription>();
allExtensions.forEach((multipleExtensions) => { allExtensions.forEach((multipleExtensions) => {
multipleExtensions.forEach((extensions) => { multipleExtensions.forEach((extensions) => {
@ -254,18 +247,13 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
if (uniqueExtensions.has(id)) { if (uniqueExtensions.has(id)) {
const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath; const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath;
const newPath = extension.extensionLocation.fsPath; const newPath = extension.extensionLocation.fsPath;
this.log.warn( this.log.warn(`${oldPath} has been overridden ${newPath}`);
`Extension ${id} in ${oldPath} has been overridden ${newPath}`,
);
} }
uniqueExtensions.set(id, extension); uniqueExtensions.set(id, extension);
}); });
}); });
}); });
return Array.from(uniqueExtensions.values());
const finalExtensions = <IExtensionDescription[]>[];
uniqueExtensions.forEach((e) => finalExtensions.push(e));
return finalExtensions;
}); });
} }

View File

@ -92,12 +92,7 @@ const main = async (): Promise<void> => {
const version = `${(pkg as any).codeServerVersion || "development"}-vsc${pkg.version}`; const version = `${(pkg as any).codeServerVersion || "development"}-vsc${pkg.version}`;
if (args.help) { if (args.help) {
const executable = `${product.applicationName}${os.platform() === "win32" ? ".exe" : ""}`; const executable = `${product.applicationName}${os.platform() === "win32" ? ".exe" : ""}`;
return console.log(buildHelpMessage( return console.log(buildHelpMessage(product.nameLong, executable, version, undefined, false));
product.nameLong, executable,
version,
undefined,
false,
));
} }
if (args.version) { if (args.version) {
@ -116,26 +111,22 @@ const main = async (): Promise<void> => {
if (shouldSpawnCliProcess()) { if (shouldSpawnCliProcess()) {
const cli = await new Promise<IMainCli>((c, e) => require(["vs/code/node/cliProcessMain"], c, e)); const cli = await new Promise<IMainCli>((c, e) => require(["vs/code/node/cliProcessMain"], c, e));
await cli.main(args); await cli.main(args);
// There is some WriteStream instance keeping it open so force an exit. return process.exit(0); // There is a WriteStream instance keeping it open.
return process.exit(0);
} }
const extra = args["_"] || [];
const options = { const options = {
host: args.host,
allowHttp: args["allow-http"], allowHttp: args["allow-http"],
auth: typeof args.auth !== "undefined" ? args.auth : true,
cert: args.cert, cert: args.cert,
certKey: args["cert-key"], certKey: args["cert-key"],
auth: typeof args.auth !== "undefined" ? args.auth : true, folderUri: extra.length > 1 ? extra[extra.length - 1] : undefined,
host: args.host,
password: process.env.PASSWORD, password: process.env.PASSWORD,
folderUri: args["_"] && args["_"].length > 1
? args["_"][args["_"].length - 1]
: undefined,
}; };
if (!options.host) { if (!options.host) {
options.host = !options.auth || options.allowHttp options.host = !options.auth || options.allowHttp ? "localhost" : "0.0.0.0";
? "localhost"
: "0.0.0.0";
} }
let usingGeneratedCert = false; let usingGeneratedCert = false;
@ -152,18 +143,16 @@ const main = async (): Promise<void> => {
usingGeneratedPassword = true; usingGeneratedPassword = true;
} }
const webviewPort = typeof args["webview-port"] !== "undefined" const webviewPort = args["webview-port"];
&& parseInt(args["webview-port"], 10) || 8444;
const webviewServer = new WebviewServer({ const webviewServer = new WebviewServer({
...options, ...options,
port: webviewPort, port: typeof webviewPort !== "undefined" && parseInt(webviewPort, 10) || 8444,
socket: args["webview-socket"], socket: args["webview-socket"],
}); });
const port = typeof args.port !== "undefined" && parseInt(args.port, 10) || 8443;
const server = new MainServer({ const server = new MainServer({
...options, ...options,
port, port: typeof args.port !== "undefined" && parseInt(args.port, 10) || 8443,
socket: args.socket, socket: args.socket,
}, webviewServer, args); }, webviewServer, args);
@ -196,7 +185,7 @@ const main = async (): Promise<void> => {
if (!args.socket && args.open) { if (!args.socket && args.open) {
// The web socket doesn't seem to work if using 0.0.0.0. // The web socket doesn't seem to work if using 0.0.0.0.
const openAddress = `http://localhost:${port}`; const openAddress = `http://localhost:${server.options.port}`;
await open(openAddress).catch(console.error); await open(openAddress).catch(console.error);
console.log(` - Opened ${openAddress}`); console.log(` - Opened ${openAddress}`);
} }

View File

@ -6,7 +6,7 @@ import { Emitter } from "vs/base/common/event";
import { ISocket } from "vs/base/parts/ipc/common/ipc.net"; import { ISocket } from "vs/base/parts/ipc/common/ipc.net";
import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net"; import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net";
import { ILogService } from "vs/platform/log/common/log"; import { ILogService } from "vs/platform/log/common/log";
import { IExtHostReadyMessage, IExtHostSocketMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol"; import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
import { Protocol } from "vs/server/src/protocol"; import { Protocol } from "vs/server/src/protocol";
import { uriTransformerPath } from "vs/server/src/util"; import { uriTransformerPath } from "vs/server/src/util";
@ -15,17 +15,11 @@ export abstract class Connection {
protected readonly _onClose = new Emitter<void>(); protected readonly _onClose = new Emitter<void>();
public readonly onClose = this._onClose.event; public readonly onClose = this._onClose.event;
protected disposed: boolean = false; protected disposed: boolean = false;
public constructor(protected protocol: Protocol) {} public constructor(protected protocol: Protocol) {}
/** /**
* Set up the connection on a new socket. * Set up the connection on a new socket.
*/ */
public abstract reconnect(socket: ISocket, buffer: VSBuffer): void; public abstract reconnect(socket: ISocket, buffer: VSBuffer): void;
/**
* Clean up the connection.
*/
protected abstract dispose(): void; protected abstract dispose(): void;
} }
@ -62,16 +56,10 @@ export class ManagementConnection extends Connection {
} }
} }
/**
* Manage the extension host process.
*/
export class ExtensionHostConnection extends Connection { export class ExtensionHostConnection extends Connection {
private process: cp.ChildProcess; private process: cp.ChildProcess;
public constructor( public constructor(protocol: Protocol, buffer: VSBuffer, private readonly log: ILogService) {
protocol: Protocol, buffer: VSBuffer,
private readonly log: ILogService,
) {
super(protocol); super(protocol);
protocol.dispose(); protocol.dispose();
this.process = this.spawn(buffer); this.process = this.spawn(buffer);
@ -96,23 +84,17 @@ export class ExtensionHostConnection extends Connection {
private sendInitMessage(buffer: VSBuffer): void { private sendInitMessage(buffer: VSBuffer): void {
const socket = this.protocol.getUnderlyingSocket(); const socket = this.protocol.getUnderlyingSocket();
socket.pause(); socket.pause();
this.process.send({
const initMessage: IExtHostSocketMessage = {
type: "VSCODE_EXTHOST_IPC_SOCKET", type: "VSCODE_EXTHOST_IPC_SOCKET",
initialDataChunk: (buffer.buffer as Buffer).toString("base64"), initialDataChunk: (buffer.buffer as Buffer).toString("base64"),
skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket, skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket,
}; }, socket);
this.process.send(initMessage, socket);
} }
private spawn(buffer: VSBuffer): cp.ChildProcess { private spawn(buffer: VSBuffer): cp.ChildProcess {
const proc = cp.fork( const proc = cp.fork(
getPathFromAmdModule(require, "bootstrap-fork"), getPathFromAmdModule(require, "bootstrap-fork"),
[ [ "--type=extensionHost", `--uriTransformerPath=${uriTransformerPath()}` ],
"--type=extensionHost",
`--uriTransformerPath=${uriTransformerPath()}`
],
{ {
env: { env: {
...process.env, ...process.env,
@ -129,13 +111,8 @@ export class ExtensionHostConnection extends Connection {
proc.on("error", () => this.dispose()); proc.on("error", () => this.dispose());
proc.on("exit", () => this.dispose()); proc.on("exit", () => this.dispose());
proc.stdout.setEncoding("utf8").on("data", (d) => this.log.info("Extension host stdout", d));
proc.stdout.setEncoding("utf8"); proc.stderr.setEncoding("utf8").on("data", (d) => this.log.error("Extension host stderr", d));
proc.stderr.setEncoding("utf8");
proc.stdout.on("data", (d) => this.log.info("Extension host stdout", d));
proc.stderr.on("data", (d) => this.log.error("Extension host stderr", d));
proc.on("message", (event) => { proc.on("message", (event) => {
if (event && event.type === "__$console") { if (event && event.type === "__$console") {
const severity = this.log[event.severity] ? event.severity : "info"; const severity = this.log[event.severity] ? event.severity : "info";
@ -149,8 +126,7 @@ export class ExtensionHostConnection extends Connection {
this.sendInitMessage(buffer); this.sendInitMessage(buffer);
} }
}; };
proc.on("message", listen);
return proc; return proc.on("message", listen);
} }
} }

View File

@ -73,10 +73,7 @@ export class Protocol extends PersistentProtocol {
* TODO: This ignores the authentication process entirely for now. * TODO: This ignores the authentication process entirely for now.
*/ */
private authenticate(_message: AuthRequest): void { private authenticate(_message: AuthRequest): void {
this.sendMessage({ this.sendMessage({ type: "sign", data: "" });
type: "sign",
data: "",
});
} }
/** /**

View File

@ -108,14 +108,11 @@ export interface ServerOptions {
} }
export abstract class Server { export abstract class Server {
// The underlying web server.
protected readonly server: http.Server | https.Server; protected readonly server: http.Server | https.Server;
protected rootPath = path.resolve(__dirname, "../../../.."); protected rootPath = path.resolve(__dirname, "../../../..");
private listenPromise: Promise<string> | undefined; private listenPromise: Promise<string> | undefined;
public constructor(protected readonly options: ServerOptions) { public constructor(public readonly options: ServerOptions) {
if (this.options.cert && this.options.certKey) { if (this.options.cert && this.options.certKey) {
useHttpsTransformer(); useHttpsTransformer();
const httpolyglot = require.__$__nodeRequire(path.resolve(__dirname, "../node_modules/httpolyglot/lib/index")) as typeof import("httpolyglot"); const httpolyglot = require.__$__nodeRequire(path.resolve(__dirname, "../node_modules/httpolyglot/lib/index")) as typeof import("httpolyglot");
@ -167,8 +164,7 @@ export abstract class Server {
): Promise<Response>; ): Promise<Response>;
protected async getResource(filePath: string): Promise<Response> { protected async getResource(filePath: string): Promise<Response> {
const content = await util.promisify(fs.readFile)(filePath); return { content: await util.promisify(fs.readFile)(filePath), filePath };
return { content, filePath };
} }
private onRequest = async (request: http.IncomingMessage, response: http.ServerResponse): Promise<void> => { private onRequest = async (request: http.IncomingMessage, response: http.ServerResponse): Promise<void> => {
@ -208,12 +204,10 @@ export abstract class Server {
} else if (base === "") { // Happens if it's a plain `domain.com`. } else if (base === "") { // Happens if it's a plain `domain.com`.
base = "/"; base = "/";
} }
if (requestPath === "/") { // Trailing slash, like `domain.com/login/`. base = path.normalize(base);
requestPath = ""; if (requestPath !== "") { // "" will become "." with normalize.
} else if (requestPath !== "") { // "" will become "." with normalize.
requestPath = path.normalize(requestPath); requestPath = path.normalize(requestPath);
} }
base = path.normalize(base);
switch (base) { switch (base) {
case "/": case "/":
@ -227,8 +221,7 @@ export abstract class Server {
case "/login": case "/login":
if (!this.options.auth) { if (!this.options.auth) {
throw new HttpError("Not found", HttpCode.NotFound); throw new HttpError("Not found", HttpCode.NotFound);
} } else if (requestPath === "") {
if (requestPath === "") {
return this.tryLogin(request); return this.tryLogin(request);
} }
this.ensureGet(request); this.ensureGet(request);
@ -249,27 +242,19 @@ export abstract class Server {
this.ensureGet(request); this.ensureGet(request);
return { redirect: "https://" + request.headers.host + "/" }; return { redirect: "https://" + request.headers.host + "/" };
} }
if (request.method === "POST") { if (request.method === "POST") {
const data = await this.getData<LoginPayload>(request); const data = await this.getData<LoginPayload>(request);
if (this.authenticate(request, data)) { if (this.authenticate(request, data)) {
return { return {
redirect: "https://" + request.headers.host + "/", redirect: "https://" + request.headers.host + "/",
headers: { headers: {"Set-Cookie": `password=${data.password}` }
"Set-Cookie": `password=${data.password}`,
}
}; };
} }
let userAgent = request.headers["user-agent"];
const timestamp = Math.floor(new Date().getTime() / 1000);
if (Array.isArray(userAgent)) {
userAgent = userAgent.join(", ");
}
console.error("Failed login attempt", JSON.stringify({ console.error("Failed login attempt", JSON.stringify({
xForwardedFor: request.headers["x-forwarded-for"], xForwardedFor: request.headers["x-forwarded-for"],
remoteAddress: request.connection.remoteAddress, remoteAddress: request.connection.remoteAddress,
userAgent, userAgent: request.headers["user-agent"],
timestamp, timestamp: Math.floor(new Date().getTime() / 1000),
})); }));
return this.getLogin("Invalid password", data); return this.getLogin("Invalid password", data);
} }
@ -279,23 +264,16 @@ export abstract class Server {
private async getLogin(error: string = "", payload?: LoginPayload): Promise<Response> { private async getLogin(error: string = "", payload?: LoginPayload): Promise<Response> {
const filePath = path.join(this.rootPath, "out/vs/server/src/login/login.html"); const filePath = path.join(this.rootPath, "out/vs/server/src/login/login.html");
let content = await util.promisify(fs.readFile)(filePath, "utf8"); const content = (await util.promisify(fs.readFile)(filePath, "utf8"))
if (error) { .replace("{{ERROR}}", error)
content = content.replace("{{ERROR}}", error) .replace("display:none", error ? "display:block" : "display:none")
.replace("display:none", "display:block"); .replace('value=""', `value="${payload && payload.password || ""}"`);
}
if (payload && payload.password) {
content = content.replace('value=""', `value="${payload.password}"`);
}
return { content, filePath }; return { content, filePath };
} }
private ensureGet(request: http.IncomingMessage): void { private ensureGet(request: http.IncomingMessage): void {
if (request.method !== "GET") { if (request.method !== "GET") {
throw new HttpError( throw new HttpError(`Unsupported method ${request.method}`, HttpCode.BadRequest);
`Unsupported method ${request.method}`,
HttpCode.BadRequest,
);
} }
} }
@ -357,15 +335,10 @@ export abstract class Server {
} }
export class MainServer extends Server { export class MainServer extends Server {
// Used to notify the IPC server that there is a new client.
public readonly _onDidClientConnect = new Emitter<ClientConnectionEvent>(); public readonly _onDidClientConnect = new Emitter<ClientConnectionEvent>();
public readonly onDidClientConnect = this._onDidClientConnect.event; public readonly onDidClientConnect = this._onDidClientConnect.event;
// This is separate instead of just extending this class since we can't
// use properties in the super call. This manages channels.
private readonly ipc = new IPCServer(this.onDidClientConnect); private readonly ipc = new IPCServer(this.onDidClientConnect);
// Persistent connections. These can reconnect within a timeout.
private readonly connections = new Map<ConnectionType, Map<string, Connection>>(); private readonly connections = new Map<ConnectionType, Map<string, Connection>>();
private readonly services = new ServiceCollection(); private readonly services = new ServiceCollection();
@ -377,7 +350,6 @@ export class MainServer extends Server {
args: ParsedArgs, args: ParsedArgs,
) { ) {
super(options); super(options);
this.server.on("upgrade", async (request, socket) => { this.server.on("upgrade", async (request, socket) => {
const protocol = this.createProtocol(request, socket); const protocol = this.createProtocol(request, socket);
try { try {
@ -393,12 +365,10 @@ export class MainServer extends Server {
public async listen(): Promise<string> { public async listen(): Promise<string> {
const environment = (this.services.get(IEnvironmentService) as EnvironmentService); const environment = (this.services.get(IEnvironmentService) as EnvironmentService);
const mkdirs = Promise.all([ const [address] = await Promise.all<string>([
environment.extensionsPath, super.listen(), ...[
].map((p) => mkdirp(p))); environment.extensionsPath,
const [address] = await Promise.all([ ].map((p) => mkdirp(p).then(() => p)),
super.listen(),
mkdirs,
]); ]);
return address; return address;
} }
@ -426,22 +396,18 @@ export class MainServer extends Server {
private async getRoot(request: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery): Promise<Response> { private async getRoot(request: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery): Promise<Response> {
const filePath = path.join(this.rootPath, "out/vs/code/browser/workbench/workbench.html"); const filePath = path.join(this.rootPath, "out/vs/code/browser/workbench/workbench.html");
let content = await util.promisify(fs.readFile)(filePath, "utf8"); let [content] = await Promise.all([
util.promisify(fs.readFile)(filePath, "utf8"),
const remoteAuthority = request.headers.host as string;
const transformer = getUriTransformer(remoteAuthority);
await Promise.all([
this.webviewServer.listen(), this.webviewServer.listen(),
this.servicesPromise, this.servicesPromise,
]); ]);
const webviewEndpoint = this.webviewServer.address(request); const webviewEndpoint = this.webviewServer.address(request);
const cwd = process.env.VSCODE_CWD || process.cwd(); const cwd = process.env.VSCODE_CWD || process.cwd();
const workspacePath = parsedUrl.query.workspace as string | undefined; const workspacePath = parsedUrl.query.workspace as string | undefined;
const folderPath = !workspacePath ? parsedUrl.query.folder as string | undefined || this.options.folderUri || cwd: undefined; const folderPath = !workspacePath ? parsedUrl.query.folder as string | undefined || this.options.folderUri || cwd: undefined;
const remoteAuthority = request.headers.host as string;
const transformer = getUriTransformer(remoteAuthority);
const options: Options = { const options: Options = {
WORKBENCH_WEB_CONGIGURATION: { WORKBENCH_WEB_CONGIGURATION: {
workspaceUri: workspacePath workspaceUri: workspacePath
@ -463,7 +429,6 @@ export class MainServer extends Server {
Object.keys(options).forEach((key) => { Object.keys(options).forEach((key) => {
content = content.replace(`"{{${key}}}"`, `'${JSON.stringify(options[key])}'`); content = content.replace(`"{{${key}}}"`, `'${JSON.stringify(options[key])}'`);
}); });
content = content.replace('{{WEBVIEW_ENDPOINT}}', webviewEndpoint); content = content.replace('{{WEBVIEW_ENDPOINT}}', webviewEndpoint);
return { content, filePath }; return { content, filePath };
@ -473,83 +438,58 @@ export class MainServer extends Server {
if (request.headers.upgrade !== "websocket") { if (request.headers.upgrade !== "websocket") {
throw new Error("HTTP/1.1 400 Bad Request"); throw new Error("HTTP/1.1 400 Bad Request");
} }
const query = request.url ? url.parse(request.url, true).query : {};
const options = { return new Protocol(<string>request.headers["sec-websocket-key"], socket, {
reconnectionToken: "", reconnectionToken: <string>query.reconnectionToken || "",
reconnection: false, reconnection: query.reconnection === "true",
skipWebSocketFrames: false, skipWebSocketFrames: query.skipWebSocketFrames === "true",
}; });
if (request.url) {
const query = url.parse(request.url, true).query;
if (query.reconnectionToken) {
options.reconnectionToken = query.reconnectionToken as string;
}
if (query.reconnection === "true") {
options.reconnection = true;
}
if (query.skipWebSocketFrames === "true") {
options.skipWebSocketFrames = true;
}
}
return new Protocol(
request.headers["sec-websocket-key"] as string,
socket,
options,
);
} }
private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> { private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
switch (message.desiredConnectionType) { switch (message.desiredConnectionType) {
case ConnectionType.ExtensionHost: case ConnectionType.ExtensionHost:
case ConnectionType.Management: case ConnectionType.Management:
const debugPort = await this.getDebugPort();
const ok = message.desiredConnectionType === ConnectionType.ExtensionHost
? (debugPort ? { debugPort } : {})
: { type: "ok" };
if (!this.connections.has(message.desiredConnectionType)) { if (!this.connections.has(message.desiredConnectionType)) {
this.connections.set(message.desiredConnectionType, new Map()); this.connections.set(message.desiredConnectionType, new Map());
} }
const connections = this.connections.get(message.desiredConnectionType)!; const connections = this.connections.get(message.desiredConnectionType)!;
const token = protocol.options.reconnectionToken;
const ok = async () => {
return message.desiredConnectionType === ConnectionType.ExtensionHost
? { debugPort: await this.getDebugPort() }
: { type: "ok" };
};
const token = protocol.options.reconnectionToken;
if (protocol.options.reconnection && connections.has(token)) { if (protocol.options.reconnection && connections.has(token)) {
protocol.sendMessage(ok); protocol.sendMessage(await ok());
const buffer = protocol.readEntireBuffer(); const buffer = protocol.readEntireBuffer();
protocol.dispose(); protocol.dispose();
return connections.get(token)!.reconnect(protocol.getSocket(), buffer); return connections.get(token)!.reconnect(protocol.getSocket(), buffer);
} } else if (protocol.options.reconnection || connections.has(token)) {
if (protocol.options.reconnection || connections.has(token)) {
throw new Error(protocol.options.reconnection throw new Error(protocol.options.reconnection
? "Unrecognized reconnection token" ? "Unrecognized reconnection token"
: "Duplicate reconnection token" : "Duplicate reconnection token"
); );
} }
protocol.sendMessage(ok); protocol.sendMessage(await ok());
let connection: Connection; let connection: Connection;
if (message.desiredConnectionType === ConnectionType.Management) { if (message.desiredConnectionType === ConnectionType.Management) {
connection = new ManagementConnection(protocol); connection = new ManagementConnection(protocol);
this._onDidClientConnect.fire({ this._onDidClientConnect.fire({
protocol, protocol, onDidClientDisconnect: connection.onClose,
onDidClientDisconnect: connection.onClose,
}); });
} else { } else {
const buffer = protocol.readEntireBuffer(); const buffer = protocol.readEntireBuffer();
connection = new ExtensionHostConnection( connection = new ExtensionHostConnection(
protocol, buffer, protocol, buffer, this.services.get(ILogService) as ILogService,
this.services.get(ILogService) as ILogService,
); );
} }
connections.set(protocol.options.reconnectionToken, connection); connections.set(token, connection);
connection.onClose(() => { connection.onClose(() => connections.delete(token));
connections.delete(protocol.options.reconnectionToken);
});
break; break;
case ConnectionType.Tunnel: return protocol.tunnel(); case ConnectionType.Tunnel: return protocol.tunnel();
default: throw new Error("Unrecognized connection type"); default: throw new Error("Unrecognized connection type");
@ -557,14 +497,11 @@ export class MainServer extends Server {
} }
private async initializeServices(args: ParsedArgs): Promise<void> { private async initializeServices(args: ParsedArgs): Promise<void> {
const router = new StaticRouter((ctx: any) => ctx.clientId === "renderer");
const environmentService = new EnvironmentService(args, process.execPath); const environmentService = new EnvironmentService(args, process.execPath);
const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService)); const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService));
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService)); this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
const router = new StaticRouter((context: any) => {
return context.clientId === "renderer";
});
this.services.set(ILogService, logService); this.services.set(ILogService, logService);
this.services.set(IEnvironmentService, environmentService); this.services.set(IEnvironmentService, environmentService);
this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource])); this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource]));
@ -594,11 +531,9 @@ export class MainServer extends Server {
this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router))); this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
const instantiationService = new InstantiationService(this.services); await new Promise((resolve) => {
const instantiationService = new InstantiationService(this.services);
this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService)); this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService));
return new Promise((resolve) => {
instantiationService.invokeFunction(() => { instantiationService.invokeFunction(() => {
instantiationService.createInstance(LogsDataCleaner); instantiationService.createInstance(LogsDataCleaner);
this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService)); this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));
@ -612,9 +547,7 @@ export class MainServer extends Server {
this.ipc.registerChannel("gallery", galleryChannel); this.ipc.registerChannel("gallery", galleryChannel);
const telemetryChannel = new TelemetryChannel(telemetryService); const telemetryChannel = new TelemetryChannel(telemetryService);
this.ipc.registerChannel("telemetry", telemetryChannel); this.ipc.registerChannel("telemetry", telemetryChannel);
// tslint:disable-next-line no-unused-expression resolve(new ErrorTelemetry(telemetryService));
new ErrorTelemetry(telemetryService);
resolve();
}); });
}); });
} }
@ -633,9 +566,6 @@ export class WebviewServer extends Server {
requestPath: string, requestPath: string,
): Promise<Response> { ): Promise<Response> {
const webviewPath = path.join(this.rootPath, "out/vs/workbench/contrib/webview/browser/pre"); const webviewPath = path.join(this.rootPath, "out/vs/workbench/contrib/webview/browser/pre");
if (requestPath === "") { return this.getResource(path.join(webviewPath, base, requestPath || "/index.html"));
requestPath = "/index.html";
}
return this.getResource(path.join(webviewPath, base, requestPath));
} }
} }

View File

@ -1,7 +1,7 @@
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import * as tarStream from "tar-stream"; import * as tarStream from "tar-stream";
import { promisify } from "util"; import * as util from "util";
import * as nls from "vs/nls"; import * as nls from "vs/nls";
import * as vszip from "vs/base/node/zip"; import * as vszip from "vs/base/node/zip";
@ -14,7 +14,6 @@ const vszipBuffer = vszip.buffer;
export interface IExtractOptions { export interface IExtractOptions {
overwrite?: boolean; overwrite?: boolean;
/** /**
* Source path within the TAR/ZIP archive. Only the files * Source path within the TAR/ZIP archive. Only the files
* contained in this path will be extracted. * contained in this path will be extracted.
@ -28,197 +27,134 @@ export interface IFile {
localPath?: string; localPath?: string;
} }
/** export const tar = async (tarPath: string, files: IFile[]): Promise<string> => {
* Override the standard VS Code behavior for zipping extensions to use the TAR const pack = tarStream.pack();
* format instead of ZIP. const chunks: Buffer[] = [];
*/ const ended = new Promise<Buffer>((resolve) => {
export const zip = (tarPath: string, files: IFile[]): Promise<string> => { pack.on("end", () => resolve(Buffer.concat(chunks)));
return new Promise<string>((c, e): void => { });
const pack = tarStream.pack(); pack.on("data", (chunk: Buffer) => chunks.push(chunk));
const chunks: Buffer[] = []; for (let i = 0; i < files.length; i++) {
const ended = new Promise<Buffer>((res): void => { const file = files[i];
pack.on("end", () => { pack.entry({ name: file.path }, file.contents);
res(Buffer.concat(chunks)); }
}); pack.finalize();
}); await util.promisify(fs.writeFile)(tarPath, await ended);
pack.on("data", (chunk) => { return tarPath;
chunks.push(chunk as Buffer); };
});
for (let i = 0; i < files.length; i++) { export const extract = async (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => {
const file = files[i]; try {
pack.entry({ await extractTar(archivePath, extractPath, options, token);
name: file.path, } catch (error) {
}, file.contents); if (error.toString().includes("Invalid tar header")) {
await vszipExtract(archivePath, extractPath, options, token);
} }
pack.finalize(); }
ended.then((buffer) => {
return promisify(fs.writeFile)(tarPath, buffer);
}).then(() => {
c(tarPath);
}).catch((ex) => {
e(ex);
});
});
}; };
/**
* Override the standard VS Code behavior for extracting archives to first
* attempt to process the archive as a TAR and then fall back to the original
* implementation for processing ZIPs.
*/
export const extract = (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => {
return new Promise<void>((c, e): void => {
extractTar(archivePath, extractPath, options, token).then(c).catch((ex) => {
if (!ex.toString().includes("Invalid tar header")) {
e(ex);
return;
}
vszipExtract(archivePath, extractPath, options, token).then(c).catch(e);
});
});
};
/**
* Override the standard VS Code behavior for buffering archives to first
* process the Buffer as a TAR and then fall back to the original
* implementation for processing ZIPs.
*/
export const buffer = (targetPath: string, filePath: string): Promise<Buffer> => { export const buffer = (targetPath: string, filePath: string): Promise<Buffer> => {
return new Promise<Buffer>((c, e): void => { return new Promise<Buffer>(async (resolve, reject) => {
let done: boolean = false;
extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => {
if (path.normalize(assetPath) === path.normalize(filePath)) {
done = true;
c(data);
}
}).then(() => {
if (!done) {
e("couldn't find asset " + filePath);
}
}).catch((ex) => {
if (!ex.toString().includes("Invalid tar header")) {
e(ex);
return;
}
vszipBuffer(targetPath, filePath).then(c).catch(e);
});
});
};
/**
* Override the standard VS Code behavior for extracting assets from archive
* Buffers to use the TAR format instead of ZIP.
*/
const extractAssets = (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise<void> => {
return new Promise<void>(async (c, e): Promise<void> => {
try { try {
const buffer = await promisify(fs.readFile)(tarPath); let done: boolean = false;
const extractor = tarStream.extract(); await extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => {
extractor.once("error", e); if (path.normalize(assetPath) === path.normalize(filePath)) {
extractor.on("entry", (header, stream, next) => { done = true;
const name = header.name; resolve(data);
if (match.test(name)) {
extractData(stream).then((data) => {
callback(name, data);
next();
}).catch(e);
stream.resume();
} else {
stream.on("end", () => {
next();
});
stream.resume();
} }
}); });
extractor.on("finish", () => { if (!done) {
c(); throw new Error("couldn't find asset " + filePath);
}); }
extractor.write(buffer); } catch (error) {
extractor.end(); if (error.toString().includes("Invalid tar header")) {
} catch (ex) { vszipBuffer(targetPath, filePath).then(resolve).catch(reject);
e(ex); } else {
reject(error);
}
} }
}); });
}; };
const extractAssets = async (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise<void> => {
const buffer = await util.promisify(fs.readFile)(tarPath);
return new Promise<void>(async (resolve, reject): Promise<void> => {
const extractor = tarStream.extract();
extractor.once("error", reject);
extractor.on("entry", async (header, stream, next) => {
const name = header.name;
if (match.test(name)) {
extractData(stream).then((data) => {
callback(name, data);
next();
}).catch(reject);
stream.resume();
} else {
stream.on("end", () => next());
stream.resume();
}
});
extractor.on("finish", resolve);
extractor.write(buffer);
extractor.end();
});
};
const extractData = (stream: NodeJS.ReadableStream): Promise<Buffer> => { const extractData = (stream: NodeJS.ReadableStream): Promise<Buffer> => {
return new Promise<Buffer>((c, e): void => { return new Promise((resolve, reject): void => {
const fileData: Buffer[] = []; const fileData: Buffer[] = [];
stream.on("data", (data) => fileData.push(data)); stream.on("data", (data) => fileData.push(data));
stream.on("end", () => { stream.on("end", () => resolve(Buffer.concat(fileData)));
const fd = Buffer.concat(fileData); stream.on("error", reject);
c(fd);
});
stream.on("error", e);
}); });
}; };
const extractTar = (tarPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => { const extractTar = async (tarPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => {
return new Promise<void>(async (c, e): Promise<void> => { const buffer = await util.promisify(fs.readFile)(tarPath);
try { return new Promise<void>(async (resolve, reject): Promise<void> => {
const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : ""); const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : "");
const buffer = await promisify(fs.readFile)(tarPath); const extractor = tarStream.extract();
const extractor = tarStream.extract(); extractor.once("error", reject);
extractor.once("error", e); extractor.on("entry", async (header, stream, next) => {
extractor.on("entry", (header, stream, next) => { const rawName = path.normalize(header.name);
const rawName = path.normalize(header.name);
const nextEntry = (): void => { const nextEntry = (): void => {
stream.resume(); stream.resume();
next(); next();
}; };
if (token.isCancellationRequested) { if (token.isCancellationRequested || !sourcePathRegex.test(rawName)) {
return nextEntry(); return nextEntry();
} }
if (!sourcePathRegex.test(rawName)) { const fileName = rawName.replace(sourcePathRegex, "");
return nextEntry(); const targetFileName = path.join(targetPath, fileName);
} if (/\/$/.test(fileName)) {
return mkdirp(targetFileName).then(nextEntry);
}
const fileName = rawName.replace(sourcePathRegex, ""); const dirName = path.dirname(fileName);
const targetFileName = path.join(targetPath, fileName); const targetDirName = path.join(targetPath, dirName);
if (/\/$/.test(fileName)) { if (targetDirName.indexOf(targetPath) !== 0) {
stream.resume(); return reject(nls.localize("invalid file", "Error extracting {0}. Invalid file.", fileName));
mkdirp(targetFileName).then(() => { }
next();
}, e);
return; return mkdirp(targetDirName, undefined, token).then(() => {
} const fstream = fs.createWriteStream(targetFileName, { mode: header.mode });
fstream.once("close", () => next());
const dirName = path.dirname(fileName); fstream.once("error", reject);
const targetDirName = path.join(targetPath, dirName); stream.pipe(fstream);
if (targetDirName.indexOf(targetPath) !== 0) { stream.resume();
e(nls.localize("invalid file", "Error extracting {0}. Invalid file.", fileName));
return nextEntry();
}
return mkdirp(targetDirName, undefined, token).then(() => {
const fstream = fs.createWriteStream(targetFileName, { mode: header.mode });
fstream.once("close", () => {
next();
});
fstream.once("error", e);
stream.pipe(fstream);
stream.resume();
});
}); });
extractor.once("finish", c); });
extractor.write(buffer); extractor.once("finish", resolve);
extractor.end(); extractor.write(buffer);
} catch (ex) { extractor.end();
e(ex);
}
}); });
}; };
// Override original functionality so we can use tar instead of zip. // Override original functionality so we can use tar instead of zip.
const target = vszip as typeof vszip; const target = vszip as typeof vszip;
target.zip = zip; target.zip = tar;
target.extract = extract; target.extract = extract;
target.buffer = buffer; target.buffer = buffer;

View File

@ -4,8 +4,7 @@ module.exports = (remoteAuthority, https) => {
return { return {
transformIncoming: (uri) => { transformIncoming: (uri) => {
switch (uri.scheme) { switch (uri.scheme) {
case "https": return { scheme: "file", path: uri.path }; case "https": case "http": return { scheme: "file", path: uri.path };
case "http": return { scheme: "file", path: uri.path };
case "file": return { scheme: "vscode-local", path: uri.path }; case "file": return { scheme: "vscode-local", path: uri.path };
default: return uri; default: return uri;
} }

View File

@ -1,3 +1 @@
module.exports = (remoteAuthority) => { module.exports = (remoteAuthority) => require("./uriTransformerHttp")(remoteAuthority, true);
return require("./uriTransformerHttp")(remoteAuthority, true);
};

View File

@ -25,8 +25,6 @@ export const generateCertificate = async (): Promise<{ cert: string, certKey: st
util.promisify(fs.exists)(paths.certKey), util.promisify(fs.exists)(paths.certKey),
]); ]);
await mkdirp(tmpdir);
if (!exists[0] || !exists[1]) { if (!exists[0] || !exists[1]) {
const pem = require.__$__nodeRequire(path.resolve(__dirname, "../node_modules/pem/lib/pem")) as typeof import("pem"); const pem = require.__$__nodeRequire(path.resolve(__dirname, "../node_modules/pem/lib/pem")) as typeof import("pem");
const certs = await new Promise<import("pem").CertificateCreationResult>((resolve, reject): void => { const certs = await new Promise<import("pem").CertificateCreationResult>((resolve, reject): void => {
@ -37,6 +35,7 @@ export const generateCertificate = async (): Promise<{ cert: string, certKey: st
resolve(result); resolve(result);
}); });
}); });
await mkdirp(tmpdir);
await Promise.all([ await Promise.all([
util.promisify(fs.writeFile)(paths.cert, certs.certificate), util.promisify(fs.writeFile)(paths.cert, certs.certificate),
util.promisify(fs.writeFile)(paths.certKey, certs.serviceKey), util.promisify(fs.writeFile)(paths.certKey, certs.serviceKey),
@ -46,16 +45,10 @@ export const generateCertificate = async (): Promise<{ cert: string, certKey: st
return paths; return paths;
}; };
let secure: boolean; let transformer: string = "uriTransformerHttp";
export const useHttpsTransformer = (): void => { export const useHttpsTransformer = (): string => transformer = "uriTransformerHttps";
secure = true;
};
export const uriTransformerPath = (): string => { export const uriTransformerPath = (): string => {
return getPathFromAmdModule( return getPathFromAmdModule(require, `vs/server/src/${transformer}`);
require,
"vs/server/src/uriTransformerHttp" + (secure ? "s": ""),
);
}; };
export const getUriTransformer = (remoteAuthority: string): URITransformer => { export const getUriTransformer = (remoteAuthority: string): URITransformer => {
@ -87,25 +80,16 @@ export const isWsl = async (): Promise<boolean> => {
}; };
export const open = async (url: string): Promise<void> => { export const open = async (url: string): Promise<void> => {
let command: string;
const args = <string[]>[]; const args = <string[]>[];
const options = <cp.SpawnOptions>{}; const options = <cp.SpawnOptions>{};
const platform = await isWsl() ? "wsl" : process.platform; const platform = await isWsl() ? "wsl" : process.platform;
switch (platform) { let command = platform === "darwin" ? "open" : "xdg-open";
case "darwin": if (platform === "win32" || platform === "wsl") {
command = "open"; command = platform === "wsl" ? "cmd.exe" : "cmd";
break; args.push("/c", "start", '""', "/b");
case "win32": url = url.replace(/&/g, "^&");
case "wsl":
command = platform === "wsl" ? "cmd.exe" : "cmd";
args.push("/c", "start", '""', "/b");
url = url.replace(/&/g, "^&");
default:
command = "xdg-open";
break;
} }
args.push(url); const proc = cp.spawn(command, [...args, url], options);
const proc = cp.spawn(command, args, options);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
proc.on("error", reject); proc.on("error", reject);
proc.on("close", (code) => { proc.on("close", (code) => {
@ -125,8 +109,6 @@ export const unpackExecutables = async (): Promise<void> => {
const destination = path.join(tmpdir, path.basename(rgPath || "")); const destination = path.join(tmpdir, path.basename(rgPath || ""));
if (rgPath && !(await util.promisify(fs.exists)(destination))) { if (rgPath && !(await util.promisify(fs.exists)(destination))) {
await mkdirp(tmpdir); await mkdirp(tmpdir);
// TODO: I'm not sure why but copyFile doesn't work in the Docker build.
// await util.promisify(fs.copyFile)(rgPath, destination);
await util.promisify(fs.writeFile)(destination, await util.promisify(fs.readFile)(rgPath)); await util.promisify(fs.writeFile)(destination, await util.promisify(fs.readFile)(rgPath));
await util.promisify(fs.chmod)(destination, "755"); await util.promisify(fs.chmod)(destination, "755");
} }