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