@@ -3,30 +3,49 @@ import * as cp from 'child_process';
3
3
import { VSBuffer } from 'vs/base/common/buffer' ;
4
4
import { Emitter } from 'vs/base/common/event' ;
5
5
import { FileAccess } from 'vs/base/common/network' ;
6
- import { ISocket } from 'vs/base/parts/ipc/common/ipc.net' ;
7
6
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment' ;
7
+ import { IRemoteExtensionHostStartParams } from 'vs/platform/remote/common/remoteAgentConnection' ;
8
8
import { getNlsConfiguration } from 'vs/server/node/nls' ;
9
9
import { Protocol } from 'vs/server/node/protocol' ;
10
10
import { IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol' ;
11
11
12
12
export abstract class Connection {
13
13
private readonly _onClose = new Emitter < void > ( ) ;
14
+ /**
15
+ * Fire when the connection is closed (not just disconnected). This should
16
+ * only happen when the connection is offline and old or has an error.
17
+ */
14
18
public readonly onClose = this . _onClose . event ;
15
19
private disposed = false ;
16
20
private _offline : number | undefined ;
17
21
18
- public constructor ( protected protocol : Protocol , public readonly token : string ) { }
22
+ protected readonly logger : Logger ;
23
+
24
+ public constructor (
25
+ protected readonly protocol : Protocol ,
26
+ public readonly name : string ,
27
+ ) {
28
+ this . logger = logger . named (
29
+ this . name ,
30
+ field ( "token" , this . protocol . options . reconnectionToken ) ,
31
+ ) ;
32
+
33
+ this . logger . debug ( 'Connecting...' ) ;
34
+ this . onClose ( ( ) => this . logger . debug ( 'Closed' ) ) ;
35
+ }
19
36
20
37
public get offline ( ) : number | undefined {
21
38
return this . _offline ;
22
39
}
23
40
24
- public reconnect ( socket : ISocket , buffer : VSBuffer ) : void {
41
+ public reconnect ( protocol : Protocol ) : void {
42
+ this . logger . debug ( 'Reconnecting...' ) ;
25
43
this . _offline = undefined ;
26
- this . doReconnect ( socket , buffer ) ;
44
+ this . doReconnect ( protocol ) ;
27
45
}
28
46
29
- public dispose ( ) : void {
47
+ public dispose ( reason ?: string ) : void {
48
+ this . logger . debug ( 'Disposing...' , field ( 'reason' , reason ) ) ;
30
49
if ( ! this . disposed ) {
31
50
this . disposed = true ;
32
51
this . doDispose ( ) ;
@@ -35,6 +54,7 @@ export abstract class Connection {
35
54
}
36
55
37
56
protected setOffline ( ) : void {
57
+ this . logger . debug ( 'Disconnected' ) ;
38
58
if ( ! this . _offline ) {
39
59
this . _offline = Date . now ( ) ;
40
60
}
@@ -43,27 +63,34 @@ export abstract class Connection {
43
63
/**
44
64
* Set up the connection on a new socket.
45
65
*/
46
- protected abstract doReconnect ( socket : ISocket , buffer : VSBuffer ) : void ;
66
+ protected abstract doReconnect ( protcol : Protocol ) : void ;
67
+
68
+ /**
69
+ * Dispose/destroy everything permanently.
70
+ */
47
71
protected abstract doDispose ( ) : void ;
48
72
}
49
73
50
74
/**
51
75
* Used for all the IPC channels.
52
76
*/
53
77
export class ManagementConnection extends Connection {
54
- public constructor ( protected protocol : Protocol , token : string ) {
55
- super ( protocol , token ) ;
78
+ public constructor ( protocol : Protocol ) {
79
+ super ( protocol , 'management' ) ;
56
80
protocol . onDidDispose ( ( ) => this . dispose ( ) ) ; // Explicit close.
57
81
protocol . onSocketClose ( ( ) => this . setOffline ( ) ) ; // Might reconnect.
82
+ protocol . sendMessage ( { type : 'ok' } ) ;
58
83
}
59
84
60
85
protected doDispose ( ) : void {
61
86
this . protocol . destroy ( ) ;
62
87
}
63
88
64
- protected doReconnect ( socket : ISocket , buffer : VSBuffer ) : void {
65
- this . protocol . beginAcceptReconnection ( socket , buffer ) ;
89
+ protected doReconnect ( protocol : Protocol ) : void {
90
+ protocol . sendMessage ( { type : 'ok' } ) ;
91
+ this . protocol . beginAcceptReconnection ( protocol . getSocket ( ) , protocol . readEntireBuffer ( ) ) ;
66
92
this . protocol . endAcceptReconnection ( ) ;
93
+ protocol . dispose ( ) ;
67
94
}
68
95
}
69
96
@@ -82,17 +109,21 @@ type ExtHostMessage = DisconnectedMessage | ConsoleMessage | IExtHostReadyMessag
82
109
83
110
export class ExtensionHostConnection extends Connection {
84
111
private process ?: cp . ChildProcess ;
85
- private readonly logger : Logger ;
86
112
87
113
public constructor (
88
- locale : string , protocol : Protocol , buffer : VSBuffer , token : string ,
114
+ protocol : Protocol ,
115
+ private readonly params : IRemoteExtensionHostStartParams ,
89
116
private readonly environment : INativeEnvironmentService ,
90
117
) {
91
- super ( protocol , token ) ;
92
- this . logger = logger . named ( 'exthost' , field ( 'token' , token ) ) ;
93
- this . protocol . dispose ( ) ;
94
- this . spawn ( locale , buffer ) . then ( ( p ) => this . process = p ) ;
95
- this . protocol . getUnderlyingSocket ( ) . pause ( ) ;
118
+ super ( protocol , 'exthost' ) ;
119
+
120
+ protocol . sendMessage ( { debugPort : this . params . port } ) ;
121
+ const buffer = protocol . readEntireBuffer ( ) ;
122
+ const inflateBytes = protocol . inflateBytes ;
123
+ protocol . dispose ( ) ;
124
+ protocol . getUnderlyingSocket ( ) . pause ( ) ;
125
+
126
+ this . spawn ( buffer , inflateBytes ) . then ( ( p ) => this . process = p ) ;
96
127
}
97
128
98
129
protected doDispose ( ) : void {
@@ -102,34 +133,38 @@ export class ExtensionHostConnection extends Connection {
102
133
}
103
134
}
104
135
105
- protected doReconnect ( socket : ISocket , buffer : VSBuffer ) : void {
106
- this . protocol . setSocket ( socket ) ;
107
- this . protocol . dispose ( ) ;
108
- this . sendInitMessage ( buffer ) ;
136
+ protected doReconnect ( protocol : Protocol ) : void {
137
+ protocol . sendMessage ( { debugPort : this . params . port } ) ;
138
+ const buffer = protocol . readEntireBuffer ( ) ;
139
+ const inflateBytes = protocol . inflateBytes ;
140
+ protocol . dispose ( ) ;
141
+ protocol . getUnderlyingSocket ( ) . pause ( ) ;
142
+ this . protocol . setSocket ( protocol . getSocket ( ) ) ;
143
+
144
+ this . sendInitMessage ( buffer , inflateBytes ) ;
109
145
}
110
146
111
- private sendInitMessage ( buffer : VSBuffer ) : void {
147
+ private sendInitMessage ( buffer : VSBuffer , inflateBytes : Uint8Array | undefined ) : void {
112
148
if ( ! this . process ) {
113
149
throw new Error ( "Tried to initialize VS Code before spawning" ) ;
114
150
}
115
151
116
- this . protocol . getUnderlyingSocket ( ) . pause ( ) ;
117
-
118
152
this . logger . debug ( 'Sending socket' ) ;
119
153
154
+ // TODO: Do something with the debug port.
120
155
this . process . send ( {
121
156
type : 'VSCODE_EXTHOST_IPC_SOCKET' ,
122
157
initialDataChunk : Buffer . from ( buffer . buffer ) . toString ( 'base64' ) ,
123
158
skipWebSocketFrames : this . protocol . options . skipWebSocketFrames ,
124
159
permessageDeflate : this . protocol . options . permessageDeflate ,
125
- inflateBytes : this . protocol . inflateBytes ,
160
+ inflateBytes : inflateBytes ? Buffer . from ( inflateBytes ) . toString ( 'base64' ) : undefined ,
126
161
} , this . protocol . getUnderlyingSocket ( ) ) ;
127
162
}
128
163
129
- private async spawn ( locale : string , buffer : VSBuffer ) : Promise < cp . ChildProcess > {
130
- this . logger . trace ( 'Getting NLS configuration...' ) ;
131
- const config = await getNlsConfiguration ( locale , this . environment . userDataPath ) ;
132
- this . logger . trace ( 'Spawning extension host...' ) ;
164
+ private async spawn ( buffer : VSBuffer , inflateBytes : Uint8Array | undefined ) : Promise < cp . ChildProcess > {
165
+ this . logger . debug ( 'Getting NLS configuration...' ) ;
166
+ const config = await getNlsConfiguration ( this . params . language , this . environment . userDataPath ) ;
167
+ this . logger . debug ( 'Spawning extension host...' ) ;
133
168
const proc = cp . fork (
134
169
FileAccess . asFileUri ( 'bootstrap-fork' , require ) . fsPath ,
135
170
// While not technically necessary, makes it easier to tell which process
@@ -158,7 +193,7 @@ export class ExtensionHostConnection extends Connection {
158
193
this . dispose ( ) ;
159
194
} ) ;
160
195
proc . on ( 'exit' , ( code ) => {
161
- this . logger . trace ( 'Exited' , field ( 'code' , code ) ) ;
196
+ this . logger . debug ( 'Exited' , field ( 'code' , code ) ) ;
162
197
this . dispose ( ) ;
163
198
} ) ;
164
199
if ( proc . stdout && proc . stderr ) {
@@ -177,20 +212,20 @@ export class ExtensionHostConnection extends Connection {
177
212
}
178
213
break ;
179
214
case 'VSCODE_EXTHOST_DISCONNECTED' :
180
- this . logger . trace ( 'Going offline ') ;
215
+ this . logger . debug ( 'Got disconnected message ') ;
181
216
this . setOffline ( ) ;
182
217
break ;
183
218
case 'VSCODE_EXTHOST_IPC_READY' :
184
- this . logger . trace ( 'Got ready message ') ;
185
- this . sendInitMessage ( buffer ) ;
219
+ this . logger . debug ( 'Handshake completed ') ;
220
+ this . sendInitMessage ( buffer , inflateBytes ) ;
186
221
break ;
187
222
default :
188
223
this . logger . error ( 'Unexpected message' , field ( 'event' , event ) ) ;
189
224
break ;
190
225
}
191
226
} ) ;
192
227
193
- this . logger . trace ( 'Waiting for handshake...' ) ;
228
+ this . logger . debug ( 'Waiting for handshake...' ) ;
194
229
return proc ;
195
230
}
196
231
}
0 commit comments