@@ -11,7 +11,7 @@ import { join, delimiter } from 'vs/base/common/path';
11
11
import { VSBuffer } from 'vs/base/common/buffer' ;
12
12
import { IRemoteConsoleLog } from 'vs/base/common/console' ;
13
13
import { Emitter , Event } from 'vs/base/common/event' ;
14
- import { NodeSocket , WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net' ;
14
+ import { createRandomIPCHandle , NodeSocket , WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net' ;
15
15
import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv' ;
16
16
import { ILogService } from 'vs/platform/log/common/log' ;
17
17
import { IRemoteExtensionHostStartParams } from 'vs/platform/remote/common/remoteAgentConnection' ;
@@ -21,6 +21,7 @@ import { IProcessEnvironment, isWindows } from 'vs/base/common/platform';
21
21
import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil' ;
22
22
import { removeDangerousEnvVariables } from 'vs/base/common/processes' ;
23
23
import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService' ;
24
+ import { DisposableStore , toDisposable } from 'vs/base/common/lifecycle' ;
24
25
25
26
export async function buildUserEnvironment ( startParamsEnv : { [ key : string ] : string | null } = { } , withUserShellEnvironment : boolean , language : string , isDebug : boolean , environmentService : IServerEnvironmentService , logService : ILogService ) : Promise < IProcessEnvironment > {
26
27
const nlsConfig = await getNLSConfiguration ( language , environmentService . userDataPath ) ;
@@ -44,7 +45,6 @@ export async function buildUserEnvironment(startParamsEnv: { [key: string]: stri
44
45
VSCODE_AMD_ENTRYPOINT : 'vs/workbench/api/node/extensionHostProcess' ,
45
46
VSCODE_PIPE_LOGGING : 'true' ,
46
47
VSCODE_VERBOSE_LOGGING : 'true' ,
47
- VSCODE_EXTHOST_WILL_SEND_SOCKET : 'true' ,
48
48
VSCODE_HANDLES_UNCAUGHT_ERRORS : 'true' ,
49
49
VSCODE_LOG_STACK : 'false' ,
50
50
VSCODE_NLS_CONFIG : JSON . stringify ( nlsConfig , undefined , 0 )
@@ -73,21 +73,36 @@ export async function buildUserEnvironment(startParamsEnv: { [key: string]: stri
73
73
74
74
class ConnectionData {
75
75
constructor (
76
- public readonly socket : net . Socket ,
77
- public readonly socketDrain : Promise < void > ,
78
- public readonly initialDataChunk : VSBuffer ,
79
- public readonly skipWebSocketFrames : boolean ,
80
- public readonly permessageDeflate : boolean ,
81
- public readonly inflateBytes : VSBuffer ,
76
+ public readonly socket : NodeSocket | WebSocketNodeSocket ,
77
+ public readonly initialDataChunk : VSBuffer
82
78
) { }
83
79
80
+ public socketDrain ( ) : Promise < void > {
81
+ return this . socket . drain ( ) ;
82
+ }
83
+
84
84
public toIExtHostSocketMessage ( ) : IExtHostSocketMessage {
85
+
86
+ let skipWebSocketFrames : boolean ;
87
+ let permessageDeflate : boolean ;
88
+ let inflateBytes : VSBuffer ;
89
+
90
+ if ( this . socket instanceof NodeSocket ) {
91
+ skipWebSocketFrames = true ;
92
+ permessageDeflate = false ;
93
+ inflateBytes = VSBuffer . alloc ( 0 ) ;
94
+ } else {
95
+ skipWebSocketFrames = false ;
96
+ permessageDeflate = this . socket . permessageDeflate ;
97
+ inflateBytes = this . socket . recordedInflateBytes ;
98
+ }
99
+
85
100
return {
86
101
type : 'VSCODE_EXTHOST_IPC_SOCKET' ,
87
102
initialDataChunk : ( < Buffer > this . initialDataChunk . buffer ) . toString ( 'base64' ) ,
88
- skipWebSocketFrames : this . skipWebSocketFrames ,
89
- permessageDeflate : this . permessageDeflate ,
90
- inflateBytes : ( < Buffer > this . inflateBytes . buffer ) . toString ( 'base64' ) ,
103
+ skipWebSocketFrames : skipWebSocketFrames ,
104
+ permessageDeflate : permessageDeflate ,
105
+ inflateBytes : ( < Buffer > inflateBytes . buffer ) . toString ( 'base64' ) ,
91
106
} ;
92
107
}
93
108
}
@@ -97,6 +112,7 @@ export class ExtensionHostConnection {
97
112
private _onClose = new Emitter < void > ( ) ;
98
113
readonly onClose : Event < void > = this . _onClose . event ;
99
114
115
+ private readonly _canSendSocket : boolean ;
100
116
private _disposed : boolean ;
101
117
private _remoteAddress : string ;
102
118
private _extensionHostProcess : cp . ChildProcess | null ;
@@ -111,10 +127,11 @@ export class ExtensionHostConnection {
111
127
@ILogService private readonly _logService : ILogService ,
112
128
@IExtensionHostStatusService private readonly _extensionHostStatusService : IExtensionHostStatusService ,
113
129
) {
130
+ this . _canSendSocket = ( ! isWindows || ! this . _environmentService . args [ 'socket-path' ] ) ;
114
131
this . _disposed = false ;
115
132
this . _remoteAddress = remoteAddress ;
116
133
this . _extensionHostProcess = null ;
117
- this . _connectionData = ExtensionHostConnection . _toConnectionData ( socket , initialDataChunk ) ;
134
+ this . _connectionData = new ConnectionData ( socket , initialDataChunk ) ;
118
135
119
136
this . _log ( `New connection established.` ) ;
120
137
}
@@ -131,19 +148,46 @@ export class ExtensionHostConnection {
131
148
this . _logService . error ( `${ this . _logPrefix } ${ _str } ` ) ;
132
149
}
133
150
134
- private static _toConnectionData ( socket : NodeSocket | WebSocketNodeSocket , initialDataChunk : VSBuffer ) : ConnectionData {
135
- if ( socket instanceof NodeSocket ) {
136
- return new ConnectionData ( socket . socket , socket . drain ( ) , initialDataChunk , true , false , VSBuffer . alloc ( 0 ) ) ;
137
- } else {
138
- return new ConnectionData ( socket . socket . socket , socket . drain ( ) , initialDataChunk , false , socket . permessageDeflate , socket . recordedInflateBytes ) ;
151
+ private async _pipeSockets ( extHostSocket : net . Socket , connectionData : ConnectionData ) : Promise < void > {
152
+
153
+ const disposables = new DisposableStore ( ) ;
154
+ disposables . add ( connectionData . socket ) ;
155
+ disposables . add ( toDisposable ( ( ) => {
156
+ extHostSocket . destroy ( ) ;
157
+ } ) ) ;
158
+
159
+ const stopAndCleanup = ( ) => {
160
+ disposables . dispose ( ) ;
161
+ } ;
162
+
163
+ disposables . add ( connectionData . socket . onEnd ( stopAndCleanup ) ) ;
164
+ disposables . add ( connectionData . socket . onClose ( stopAndCleanup ) ) ;
165
+
166
+ disposables . add ( Event . fromNodeEventEmitter < void > ( extHostSocket , 'end' ) ( stopAndCleanup ) ) ;
167
+ disposables . add ( Event . fromNodeEventEmitter < void > ( extHostSocket , 'close' ) ( stopAndCleanup ) ) ;
168
+ disposables . add ( Event . fromNodeEventEmitter < void > ( extHostSocket , 'error' ) ( stopAndCleanup ) ) ;
169
+
170
+ disposables . add ( connectionData . socket . onData ( ( e ) => extHostSocket . write ( e . buffer ) ) ) ;
171
+ disposables . add ( Event . fromNodeEventEmitter < Buffer > ( extHostSocket , 'data' ) ( ( e ) => {
172
+ connectionData . socket . write ( VSBuffer . wrap ( e ) ) ;
173
+ } ) ) ;
174
+
175
+ if ( connectionData . initialDataChunk . byteLength > 0 ) {
176
+ extHostSocket . write ( connectionData . initialDataChunk . buffer ) ;
139
177
}
140
178
}
141
179
142
180
private async _sendSocketToExtensionHost ( extensionHostProcess : cp . ChildProcess , connectionData : ConnectionData ) : Promise < void > {
143
181
// Make sure all outstanding writes have been drained before sending the socket
144
- await connectionData . socketDrain ;
182
+ await connectionData . socketDrain ( ) ;
145
183
const msg = connectionData . toIExtHostSocketMessage ( ) ;
146
- extensionHostProcess . send ( msg , connectionData . socket ) ;
184
+ let socket : net . Socket ;
185
+ if ( connectionData . socket instanceof NodeSocket ) {
186
+ socket = connectionData . socket . socket ;
187
+ } else {
188
+ socket = connectionData . socket . socket . socket ;
189
+ }
190
+ extensionHostProcess . send ( msg , socket ) ;
147
191
}
148
192
149
193
public shortenReconnectionGraceTimeIfNecessary ( ) : void {
@@ -159,7 +203,7 @@ export class ExtensionHostConnection {
159
203
public acceptReconnection ( remoteAddress : string , _socket : NodeSocket | WebSocketNodeSocket , initialDataChunk : VSBuffer ) : void {
160
204
this . _remoteAddress = remoteAddress ;
161
205
this . _log ( `The client has reconnected.` ) ;
162
- const connectionData = ExtensionHostConnection . _toConnectionData ( _socket , initialDataChunk ) ;
206
+ const connectionData = new ConnectionData ( _socket , initialDataChunk ) ;
163
207
164
208
if ( ! this . _extensionHostProcess ) {
165
209
// The extension host didn't even start up yet
@@ -197,6 +241,17 @@ export class ExtensionHostConnection {
197
241
const env = await buildUserEnvironment ( startParams . env , true , startParams . language , ! ! startParams . debugId , this . _environmentService , this . _logService ) ;
198
242
removeDangerousEnvVariables ( env ) ;
199
243
244
+ let extHostNamedPipeServer : net . Server | null ;
245
+
246
+ if ( this . _canSendSocket ) {
247
+ env [ 'VSCODE_EXTHOST_WILL_SEND_SOCKET' ] = 'true' ;
248
+ extHostNamedPipeServer = null ;
249
+ } else {
250
+ const { namedPipeServer, pipeName } = await this . _listenOnPipe ( ) ;
251
+ env [ 'VSCODE_IPC_HOOK_EXTHOST' ] = pipeName ;
252
+ extHostNamedPipeServer = namedPipeServer ;
253
+ }
254
+
200
255
const opts = {
201
256
env,
202
257
execArgv,
@@ -240,14 +295,21 @@ export class ExtensionHostConnection {
240
295
this . _cleanResources ( ) ;
241
296
} ) ;
242
297
243
- const messageListener = ( msg : IExtHostReadyMessage ) => {
244
- if ( msg . type === 'VSCODE_EXTHOST_IPC_READY' ) {
245
- this . _extensionHostProcess ! . removeListener ( 'message' , messageListener ) ;
246
- this . _sendSocketToExtensionHost ( this . _extensionHostProcess ! , this . _connectionData ! ) ;
247
- this . _connectionData = null ;
248
- }
249
- } ;
250
- this . _extensionHostProcess . on ( 'message' , messageListener ) ;
298
+ if ( extHostNamedPipeServer ) {
299
+ extHostNamedPipeServer . on ( 'connection' , ( socket ) => {
300
+ extHostNamedPipeServer ! . close ( ) ;
301
+ this . _pipeSockets ( socket , this . _connectionData ! ) ;
302
+ } ) ;
303
+ } else {
304
+ const messageListener = ( msg : IExtHostReadyMessage ) => {
305
+ if ( msg . type === 'VSCODE_EXTHOST_IPC_READY' ) {
306
+ this . _extensionHostProcess ! . removeListener ( 'message' , messageListener ) ;
307
+ this . _sendSocketToExtensionHost ( this . _extensionHostProcess ! , this . _connectionData ! ) ;
308
+ this . _connectionData = null ;
309
+ }
310
+ } ;
311
+ this . _extensionHostProcess . on ( 'message' , messageListener ) ;
312
+ }
251
313
252
314
} catch ( error ) {
253
315
console . error ( 'ExtensionHostConnection errored' ) ;
@@ -256,6 +318,21 @@ export class ExtensionHostConnection {
256
318
}
257
319
}
258
320
}
321
+
322
+ private _listenOnPipe ( ) : Promise < { pipeName : string ; namedPipeServer : net . Server } > {
323
+ return new Promise < { pipeName : string ; namedPipeServer : net . Server } > ( ( resolve , reject ) => {
324
+ const pipeName = createRandomIPCHandle ( ) ;
325
+
326
+ const namedPipeServer = net . createServer ( ) ;
327
+ namedPipeServer . on ( 'error' , reject ) ;
328
+ namedPipeServer . listen ( pipeName , ( ) => {
329
+ if ( namedPipeServer ) {
330
+ namedPipeServer . removeListener ( 'error' , reject ) ;
331
+ }
332
+ resolve ( { pipeName, namedPipeServer } ) ;
333
+ } ) ;
334
+ } ) ;
335
+ }
259
336
}
260
337
261
338
function readCaseInsensitive ( env : { [ key : string ] : string | undefined } , key : string ) : string | undefined {
0 commit comments