Skip to content

Commit 6fad337

Browse files
committed
Start CLIServer with extension host
1 parent 44b4f0e commit 6fad337

5 files changed

Lines changed: 123 additions & 99 deletions

File tree

src/vs/base/node/processes.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,28 @@ export function getWindowsShell(): string {
7575
/**
7676
* Sanitizes a VS Code process environment by removing all Electron/VS Code-related values.
7777
*/
78-
export function sanitizeProcessEnvironment(env: Platform.IProcessEnvironment): void {
78+
export function sanitizeProcessEnvironment(env: Platform.IProcessEnvironment, ...preserve: string[]): void {
79+
const set = preserve.reduce((set, key) => {
80+
set[key] = true;
81+
return set;
82+
}, {} as Record<string, boolean>);
7983
const keysToRemove = [
8084
/^ELECTRON_.+$/,
8185
/^GOOGLE_API_KEY$/,
8286
/^VSCODE_.+$/,
8387
/^SNAP(|_.*)$/
8488
];
8589
const envKeys = Object.keys(env);
86-
envKeys.forEach(envKey => {
87-
for (let i = 0; i < keysToRemove.length; i++) {
88-
if (envKey.search(keysToRemove[i]) !== -1) {
89-
delete env[envKey];
90-
break;
90+
envKeys
91+
.filter(key => !set[key])
92+
.forEach(envKey => {
93+
for (let i = 0; i < keysToRemove.length; i++) {
94+
if (envKey.search(keysToRemove[i]) !== -1) {
95+
delete env[envKey];
96+
break;
97+
}
9198
}
92-
}
93-
});
99+
});
94100
}
95101

96102
export abstract class AbstractProcess<TProgressData> {

src/vs/workbench/api/node/extHost.api.impl.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/n
6565
import * as vscode from 'vscode';
6666
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
6767
import { originalFSPath } from 'vs/base/common/resources';
68+
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
6869

6970
export interface IExtensionApiFactory {
7071
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -113,7 +114,7 @@ export function createApiFactory(
113114
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
114115
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors));
115116
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
116-
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService, extHostCommands));
117+
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService));
117118
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands));
118119
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
119120
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
@@ -124,6 +125,10 @@ export function createApiFactory(
124125
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
125126
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(initData.logsLocation, rpcProtocol));
126127
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
128+
if (initData.remoteAuthority) {
129+
const cliServer = new CLIServer(extHostCommands);
130+
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath;
131+
}
127132

128133
// Check that no named customers are missing
129134
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => (<any>ExtHostContext)[key]);
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net';
7+
import * as http from 'http';
8+
import * as fs from 'fs';
9+
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
10+
import { IURIToOpen, URIType } from 'vs/platform/windows/common/windows';
11+
import { URI } from 'vs/base/common/uri';
12+
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
13+
14+
15+
export class CLIServer {
16+
17+
private _server: http.Server;
18+
private _ipcHandlePath: string | undefined;
19+
20+
constructor(private _commands: ExtHostCommands) {
21+
this._server = http.createServer((req, res) => this.onRequest(req, res));
22+
this.setup().catch(err => {
23+
console.error(err);
24+
return '';
25+
});
26+
}
27+
28+
public get ipcHandlePath() {
29+
return this._ipcHandlePath;
30+
}
31+
32+
private async setup(): Promise<string> {
33+
this._ipcHandlePath = generateRandomPipeName();
34+
35+
try {
36+
this._server.listen(this.ipcHandlePath);
37+
this._server.on('error', err => console.error(err));
38+
} catch (err) {
39+
console.error('Could not start open from terminal server.');
40+
}
41+
42+
return this.ipcHandlePath;
43+
}
44+
private collectURIToOpen(strs: string[], typeHint: URIType, result: IURIToOpen[]): void {
45+
if (Array.isArray(strs)) {
46+
for (const s of strs) {
47+
try {
48+
result.push({ uri: URI.parse(s), typeHint });
49+
} catch (e) {
50+
// ignore
51+
}
52+
}
53+
}
54+
}
55+
56+
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
57+
const chunks: string[] = [];
58+
req.setEncoding('utf8');
59+
req.on('data', (d: string) => chunks.push(d));
60+
req.on('end', () => {
61+
const data = JSON.parse(chunks.join(''));
62+
switch (data.type) {
63+
case 'open':
64+
this.open(data, res);
65+
break;
66+
default:
67+
res.writeHead(404);
68+
res.write(`Unkown message type: ${data.type}`, err => {
69+
if (err) {
70+
console.error(err);
71+
}
72+
});
73+
res.end();
74+
break;
75+
}
76+
});
77+
}
78+
79+
private open(data: any, res: http.ServerResponse) {
80+
let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = data;
81+
if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) {
82+
const urisToOpen: IURIToOpen[] = [];
83+
this.collectURIToOpen(folderURIs, 'folder', urisToOpen);
84+
this.collectURIToOpen(fileURIs, 'file', urisToOpen);
85+
if (!forceReuseWindow && urisToOpen.some(o => o.typeHint === 'folder' || (o.typeHint === 'file' && hasWorkspaceFileExtension(o.uri.path)))) {
86+
forceNewWindow = true;
87+
}
88+
this._commands.executeCommand('_files.windowOpen', { urisToOpen, forceNewWindow, diffMode, addMode, forceReuseWindow });
89+
}
90+
res.writeHead(200);
91+
res.end();
92+
}
93+
94+
dispose(): void {
95+
this._server.close();
96+
97+
if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) {
98+
fs.unlinkSync(this._ipcHandlePath);
99+
}
100+
}
101+
}

src/vs/workbench/api/node/extHostTerminalService.ts

Lines changed: 1 addition & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,7 @@ import { ILogService } from 'vs/platform/log/common/log';
1414
import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal';
1515
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
1616
import { timeout } from 'vs/base/common/async';
17-
import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net';
18-
import * as http from 'http';
19-
import * as fs from 'fs';
20-
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
2117
import { sanitizeProcessEnvironment } from 'vs/base/node/processes';
22-
import { IURIToOpen, URIType } from 'vs/platform/windows/common/windows';
2318

2419
const RENDERER_NO_PROCESS_ID = -1;
2520

@@ -270,7 +265,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
270265
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
271266
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
272267
private _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
273-
private _cliServer: CLIServer | undefined;
274268

275269
public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; }
276270
public get terminals(): ExtHostTerminal[] { return this._terminals; }
@@ -288,7 +282,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
288282
mainContext: IMainContext,
289283
private _extHostConfiguration: ExtHostConfiguration,
290284
private _logService: ILogService,
291-
private _commands: ExtHostCommands
292285
) {
293286
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
294287
}
@@ -453,17 +446,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
453446

454447
// Sanitize the environment, removing any undesirable VS Code and Electron environment
455448
// variables
456-
sanitizeProcessEnvironment(env);
449+
sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
457450

458451
// Continue env initialization, merging in the env from the launch
459452
// config and adding keys that are needed to create the process
460453
terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, terminalConfig.get('setLocaleVariables'));
461454

462-
if (!this._cliServer) {
463-
this._cliServer = new CLIServer(this._commands);
464-
}
465-
env['VSCODE_IPC_HOOK_CLI'] = this._cliServer.ipcHandlePath;
466-
467455
// Fork the process and listen for messages
468456
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
469457
const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty'));
@@ -512,11 +500,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
512500
// Send exit event to main side
513501
this._proxy.$sendProcessExit(id, exitCode);
514502

515-
if (this._cliServer && !Object.keys(this._terminalProcesses).length) {
516-
this._cliServer.dispose();
517-
this._cliServer = undefined;
518-
}
519-
520503
}
521504

522505
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal> {
@@ -588,74 +571,3 @@ class ApiRequest {
588571
this._callback.apply(proxy, [id].concat(this._args));
589572
}
590573
}
591-
592-
593-
class CLIServer {
594-
595-
private _server: http.Server;
596-
private _ipcHandlePath: string | undefined;
597-
598-
constructor(private _commands: ExtHostCommands) {
599-
this._server = http.createServer((req, res) => this.onRequest(req, res));
600-
this.setup().catch(err => {
601-
console.error(err);
602-
return '';
603-
});
604-
}
605-
606-
public get ipcHandlePath() {
607-
return this._ipcHandlePath;
608-
}
609-
610-
private async setup(): Promise<string> {
611-
this._ipcHandlePath = generateRandomPipeName();
612-
613-
try {
614-
this._server.listen(this.ipcHandlePath);
615-
this._server.on('error', err => console.error(err));
616-
} catch (err) {
617-
console.error('Could not start open from terminal server.');
618-
}
619-
620-
return this.ipcHandlePath;
621-
}
622-
private collectURIToOpen(strs: string[], typeHint: URIType, result: IURIToOpen[]): void {
623-
if (Array.isArray(strs)) {
624-
for (const s of strs) {
625-
try {
626-
result.push({ uri: URI.parse(s), typeHint });
627-
} catch (e) {
628-
// ignore
629-
}
630-
}
631-
}
632-
}
633-
634-
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
635-
const chunks: string[] = [];
636-
req.setEncoding('utf8');
637-
req.on('data', (d: string) => chunks.push(d));
638-
req.on('end', () => {
639-
let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = JSON.parse(chunks.join(''));
640-
if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) {
641-
if (folderURIs && folderURIs.length && !forceReuseWindow) {
642-
forceNewWindow = true;
643-
}
644-
const urisToOpen: IURIToOpen[] = [];
645-
this.collectURIToOpen(folderURIs, 'folder', urisToOpen);
646-
this.collectURIToOpen(fileURIs, 'file', urisToOpen);
647-
this._commands.executeCommand('_files.windowOpen', { urisToOpen, forceNewWindow, diffMode, addMode, forceReuseWindow });
648-
}
649-
res.writeHead(200);
650-
res.end();
651-
});
652-
}
653-
654-
dispose(): void {
655-
this._server.close();
656-
657-
if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) {
658-
fs.unlinkSync(this._ipcHandlePath);
659-
}
660-
}
661-
}

src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
138138

139139
// Sanitize the environment, removing any undesirable VS Code and Electron environment
140140
// variables
141-
sanitizeProcessEnvironment(env);
141+
sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
142142

143143
// Adding other env keys necessary to create the process
144144
terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables);

0 commit comments

Comments
 (0)