1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* eslint-disable code-import-patterns */
/* eslint-disable code-layering */
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import { StopWatch } from 'vs/base/common/stopwatch';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import { IExtensionHostProcessOptions } from 'vs/platform/extensions/common/extensionHostStarter';
import { ILogService } from 'vs/platform/log/common/log';
import { IPCExtHostConnection, writeExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv';
import { createMessageOfType, MessageType } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { ExtensionHostProcess, ExtHostMessagePortCommunication, IExtHostCommunication, SandboxLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost';
import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
export class NativeLocalProcessExtensionHost extends SandboxLocalProcessExtensionHost {
protected override async _start(): Promise<IMessagePassingProtocol> {
const canUseUtilityProcess = await this._extensionHostStarter.canUseUtilityProcess();
if (canUseUtilityProcess && (this._configurationService.getValue<boolean | undefined>('extensions.experimental.useUtilityProcess') || process.sandboxed)) {
const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService));
return this._startWithCommunication(communication);
} else {
const communication = this._toDispose.add(new ExtHostNamedPipeCommunication(this._logService));
return this._startWithCommunication(communication);
}
}
}
interface INamedPipePreparedData {
pipeName: string;
namedPipeServer: import('net').Server;
}
class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommunication<INamedPipePreparedData> {
readonly useUtilityProcess = false;
constructor(
@ILogService private readonly _logService: ILogService
) {
super();
}
async prepare(): Promise<INamedPipePreparedData> {
const { createServer } = await import('net');
return new Promise<{ pipeName: string; namedPipeServer: import('net').Server }>((resolve, reject) => {
const pipeName = createRandomIPCHandle();
const namedPipeServer = createServer();
namedPipeServer.on('error', reject);
namedPipeServer.listen(pipeName, () => {
namedPipeServer?.removeListener('error', reject);
resolve({ pipeName, namedPipeServer });
});
this._register(toDisposable(() => {
if (namedPipeServer.listening) {
namedPipeServer.close();
}
}));
});
}
establishProtocol(prepared: INamedPipePreparedData, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise<IMessagePassingProtocol> {
const { namedPipeServer, pipeName } = prepared;
writeExtHostConnection(new IPCExtHostConnection(pipeName), opts.env);
return new Promise<PersistentProtocol>((resolve, reject) => {
// Wait for the extension host to connect to our named pipe
// and wrap the socket in the message passing protocol
const handle = setTimeout(() => {
if (namedPipeServer.listening) {
namedPipeServer.close();
}
reject('The local extension host took longer than 60s to connect.');
}, 60 * 1000);
namedPipeServer.on('connection', (socket) => {
clearTimeout(handle);
if (namedPipeServer.listening) {
namedPipeServer.close();
}
const nodeSocket = new NodeSocket(socket, 'renderer-exthost');
const protocol = new PersistentProtocol(nodeSocket);
this._register(toDisposable(() => {
// Send the extension host a request to terminate itself
// (graceful termination)
protocol.send(createMessageOfType(MessageType.Terminate));
protocol.flush();
socket.end();
nodeSocket.dispose();
protocol.dispose();
}));
resolve(protocol);
});
// Now that the named pipe listener is installed, start the ext host process
const sw = StopWatch.create(false);
extensionHostProcess.start(opts).then(() => {
const duration = sw.elapsed();
if (platform.isCI) {
this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`);
}
}, (err) => {
// Starting the ext host process resulted in an error
reject(err);
});
});
}
}
|