Skip to content

Commit dd3adc1

Browse files
committed
WIP Fix issues surrounding net socket lifecycle, and frequent restarts.
1 parent 518cd75 commit dd3adc1

File tree

14 files changed

+417
-207
lines changed

14 files changed

+417
-207
lines changed

resources/web/code-web.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -491,19 +491,19 @@ async function handleRoot(req, res) {
491491
* @param webConfigJSON {import('../../src/vs/workbench/workbench.web.api').IServerWorkbenchConstructionOptions}
492492
*/
493493
async function handleRootFromServer(req, res, webConfigJSON) {
494-
const { extensions: builtInExtensions } = await builtInExtensionsPromise;
495-
const { extensions: additionalBuiltinExtensions, locations: staticLocations } = await commandlineProvidedExtensionsPromise;
494+
// const { extensions: builtInExtensions } = await builtInExtensionsPromise;
495+
// const { extensions: additionalBuiltinExtensions, locations: staticLocations } = await commandlineProvidedExtensionsPromise;
496496

497-
const dedupedBuiltInExtensions = [];
498-
for (const builtInExtension of builtInExtensions) {
499-
const extensionId = `${builtInExtension.packageJSON.publisher}.${builtInExtension.packageJSON.name}`;
500-
if (staticLocations[extensionId]) {
501-
fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: Ignoring built-in ${extensionId} because it was overridden via --extension argument`);
502-
continue;
503-
}
497+
// const dedupedBuiltInExtensions = [];
498+
// for (const builtInExtension of builtInExtensions) {
499+
// const extensionId = `${builtInExtension.packageJSON.publisher}.${builtInExtension.packageJSON.name}`;
500+
// if (staticLocations[extensionId]) {
501+
// fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: Ignoring built-in ${extensionId} because it was overridden via --extension argument`);
502+
// continue;
503+
// }
504504

505-
dedupedBuiltInExtensions.push(builtInExtension);
506-
}
505+
// dedupedBuiltInExtensions.push(builtInExtension);
506+
// }
507507

508508
if (args.verbose) {
509509
fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${dedupedBuiltInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`);
@@ -518,7 +518,7 @@ async function handleRootFromServer(req, res, webConfigJSON) {
518518

519519
const data = (await readFile(WEB_MAIN)).toString()
520520
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => escapeAttribute(JSON.stringify(webConfigJSON))) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied
521-
.replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(dedupedBuiltInExtensions)))
521+
// .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(dedupedBuiltInExtensions)))
522522
.replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '')
523523
.replace('{{WEBVIEW_ENDPOINT}}', '');
524524

src/vs/base/parts/ipc/common/ipc.net.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
715715
this._socketReader = new ProtocolReader(this._socket);
716716
this._socketDisposables.push(this._socketReader);
717717
this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg)));
718-
this._socketDisposables.push(this._socket.onClose((e) => this._onSocketClose.fire(e)));
718+
this._socketDisposables.push(this._socket.onClose((e) => {
719+
console.log('SOCKET DISPOSABLE CLOSE', (e as WebSocketCloseEvent).reason);
720+
return this._onSocketClose.fire(e);
721+
}));
719722
if (initialChunk) {
720723
this._socketReader.acceptChunk(initialChunk);
721724
}
@@ -804,6 +807,11 @@ export class PersistentProtocol implements IMessagePassingProtocol {
804807
return this._socket;
805808
}
806809

810+
// NOTE@coder: add setSocket
811+
public setSocket(socket: ISocket) {
812+
this._socket = socket;
813+
}
814+
807815
public getMillisSinceLastIncomingData(): number {
808816
return Date.now() - this._socketReader.lastReadTime;
809817
}
@@ -825,7 +833,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
825833
this._socketReader = new ProtocolReader(this._socket);
826834
this._socketDisposables.push(this._socketReader);
827835
this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg)));
828-
this._socketDisposables.push(this._socket.onClose((e) => this._onSocketClose.fire(e)));
836+
this._socketDisposables.push(this._socket.onClose((e) => {
837+
console.log('SOCKET DISPOSABLE CLOSE RECONNECTION', e?.type || 'unknown');
838+
return this._onSocketClose.fire(e);
839+
}));
829840
this._socketReader.acceptChunk(initialDataChunk);
830841
}
831842

src/vs/platform/log/node/spdlogLog.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ async function createSpdLogLogger(name: string, logfilePath: string, filesize: n
1414
_spdlog.setFlushOn(LogLevel.Trace);
1515
return _spdlog.createAsyncRotatingLogger(name, logfilePath, filesize, filecount);
1616
} catch (e) {
17-
console.error(e);
17+
// console.error(e);
18+
// TODO@coder enable
19+
console.error('generic log failure');
1820
}
1921
return null;
2022
}

src/vs/platform/remote/common/remoteAgentConnection.ts

+29-15
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,17 @@ export interface OKMessage {
6363
type: 'ok';
6464
}
6565

66-
export type HandshakeMessage = AuthRequest | SignRequest | ConnectionTypeRequest | ErrorMessage | OKMessage;
66+
/**
67+
* Expected response from `CodeServer` connection.
68+
* @coder Moved from inline type for use outside this module.
69+
*/
70+
export interface DebugMessage {
71+
type: 'debug';
72+
debugPort?: NonNullable<IRemoteExtensionHostStartParams['port']>;
73+
}
74+
75+
76+
export type HandshakeMessage = AuthRequest | SignRequest | ConnectionTypeRequest | ErrorMessage | OKMessage | DebugMessage;
6777

6878

6979
interface ISimpleConnectionOptions {
@@ -225,20 +235,22 @@ function raceWithTimeoutCancellation<T>(promise: Promise<T>, timeoutCancellation
225235
async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptions, connectionType: ConnectionType, args: any | undefined, timeoutCancellationToken: CancellationToken): Promise<{ protocol: PersistentProtocol; ownsProtocol: boolean; }> {
226236
const logPrefix = connectLogPrefix(options, connectionType);
227237

228-
options.logService.trace(`${logPrefix} 1/6. invoking socketFactory.connect().`);
238+
options.logService.info(`${logPrefix} 1/6. invoking socketFactory.connect().`);
229239

230240
let socket: ISocket;
231241
try {
232-
// NOTE@coder: Add connection type to the socket. This is so they can be
233-
// distinguished by the backend.
242+
/**
243+
* @coder Add connection type to the socket.
244+
* This is so they can be distinguished by the backend.
245+
*/
234246
socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, `type=${connectionTypeToString(connectionType)}&reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, timeoutCancellationToken);
235247
} catch (error) {
236248
options.logService.error(`${logPrefix} socketFactory.connect() failed or timed out. Error:`);
237249
options.logService.error(error);
238250
throw error;
239251
}
240252

241-
options.logService.trace(`${logPrefix} 2/6. socketFactory.connect() was successful.`);
253+
options.logService.info(`${logPrefix} 2/6. socketFactory.connect() was successful.`);
242254

243255
let protocol: PersistentProtocol;
244256
let ownsProtocol: boolean;
@@ -251,7 +263,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
251263
ownsProtocol = true;
252264
}
253265

254-
options.logService.trace(`${logPrefix} 3/6. sending AuthRequest control message.`);
266+
options.logService.info(`${logPrefix} 3/6. sending AuthRequest control message.`);
255267
const authRequest: AuthRequest = {
256268
type: 'auth',
257269
auth: options.connectionToken || '00000000000000000000'
@@ -267,7 +279,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
267279
throw error;
268280
}
269281

270-
options.logService.trace(`${logPrefix} 4/6. received SignRequest control message.`);
282+
options.logService.info(`${logPrefix} 4/6. received SignRequest control message.`);
271283

272284
const signed = await raceWithTimeoutCancellation(options.signService.sign(msg.data), timeoutCancellationToken);
273285
const connTypeRequest: ConnectionTypeRequest = {
@@ -280,7 +292,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
280292
connTypeRequest.args = args;
281293
}
282294

283-
options.logService.trace(`${logPrefix} 5/6. sending ConnectionTypeRequest control message.`);
295+
options.logService.info(`${logPrefix} 5/6. sending ConnectionTypeRequest control message.`);
284296
protocol.sendControl(VSBuffer.fromString(JSON.stringify(connTypeRequest)));
285297

286298
return { protocol, ownsProtocol };
@@ -324,7 +336,7 @@ async function connectToRemoteExtensionHostAgentAndReadOneMessage<T>(options: IS
324336
if (options.reconnectionProtocol) {
325337
options.reconnectionProtocol.endAcceptReconnection();
326338
}
327-
options.logService.trace(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`);
339+
options.logService.info(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`);
328340
result.resolve({ protocol, firstMessage: msg });
329341
}
330342
}));
@@ -350,7 +362,9 @@ interface IExtensionHostConnectionResult {
350362
}
351363

352364
async function doConnectRemoteAgentExtensionHost(options: ISimpleConnectionOptions, startArguments: IRemoteExtensionHostStartParams, timeoutCancellationToken: CancellationToken): Promise<IExtensionHostConnectionResult> {
353-
const { protocol, firstMessage } = await connectToRemoteExtensionHostAgentAndReadOneMessage<{ debugPort?: number; }>(options, ConnectionType.ExtensionHost, startArguments, timeoutCancellationToken);
365+
console.log('>>> READING ONE MESSAGE', options, startArguments);
366+
const { protocol, firstMessage } = await connectToRemoteExtensionHostAgentAndReadOneMessage<DebugMessage>(options, ConnectionType.ExtensionHost, startArguments, timeoutCancellationToken);
367+
console.log('>>> READ ONE MESSAGE', JSON.stringify(firstMessage));
354368
const debugPort = firstMessage && firstMessage.debugPort;
355369
return { protocol, debugPort };
356370
}
@@ -363,7 +377,7 @@ async function doConnectRemoteAgentTunnel(options: ISimpleConnectionOptions, sta
363377
const startTime = Date.now();
364378
const logPrefix = connectLogPrefix(options, ConnectionType.Tunnel);
365379
const { protocol } = await connectToRemoteExtensionHostAgent(options, ConnectionType.Tunnel, startParams, timeoutCancellationToken);
366-
options.logService.trace(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`);
380+
options.logService.info(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`);
367381
return protocol;
368382
}
369383

@@ -553,7 +567,7 @@ abstract class PersistentConnection extends Disposable {
553567
}));
554568
this._register(protocol.onSocketTimeout(() => {
555569
const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true);
556-
this._options.logService.trace(`${logPrefix} received socket timeout event.`);
570+
this._options.logService.info(`${logPrefix} received socket timeout event.`);
557571
this._beginReconnecting();
558572
}));
559573

@@ -632,19 +646,19 @@ abstract class PersistentConnection extends Disposable {
632646
}
633647
if (RemoteAuthorityResolverError.isTemporarilyNotAvailable(err)) {
634648
this._options.logService.info(`${logPrefix} A temporarily not available error occurred while trying to reconnect, will try again...`);
635-
this._options.logService.trace(err);
649+
this._options.logService.info(err);
636650
// try again!
637651
continue;
638652
}
639653
if ((err.code === 'ETIMEDOUT' || err.code === 'ENETUNREACH' || err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') && err.syscall === 'connect') {
640654
this._options.logService.info(`${logPrefix} A network error occurred while trying to reconnect, will try again...`);
641-
this._options.logService.trace(err);
655+
this._options.logService.info(err);
642656
// try again!
643657
continue;
644658
}
645659
if (isPromiseCanceledError(err)) {
646660
this._options.logService.info(`${logPrefix} A promise cancelation error occurred while trying to reconnect, will try again...`);
647-
this._options.logService.trace(err);
661+
this._options.logService.info(err);
648662
// try again!
649663
continue;
650664
}

src/vs/platform/remote/node/serverWebSocket.ts renamed to src/vs/platform/remote/node/serverSocket.ts

+44-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import * as net from 'net';
67
import * as WebSocket from 'ws';
78
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
89
import { Event, Emitter } from 'vs/base/common/event';
@@ -18,7 +19,6 @@ import { VSBuffer } from 'vs/base/common/buffer';
1819
* by `ServerSocket`. This allows IPC-style protocol handlers to interact with it.
1920
*/
2021
class ServerWebSocket extends Disposable implements IWebSocket {
21-
2222
private readonly _onData = new Emitter<ArrayBuffer>();
2323
public readonly onData = this._onData.event;
2424

@@ -117,10 +117,12 @@ class ServerWebSocket extends Disposable implements IWebSocket {
117117
}
118118

119119
export class ServerSocket implements ISocket {
120+
public readonly socket: net.Socket;
120121
public readonly ws: ServerWebSocket;
121122

122-
constructor(ws: WebSocket) {
123+
constructor(ws: WebSocket, socket: net.Socket) {
123124
this.ws = new ServerWebSocket(ws);
125+
this.socket = socket;
124126
}
125127

126128
public dispose(): void {
@@ -136,15 +138,17 @@ export class ServerSocket implements ISocket {
136138
if (typeof e === 'undefined') {
137139
listener(e);
138140
} else {
141+
console.log('SERVER CLOSE');
139142
listener({
140143
type: SocketCloseEventType.WebSocketCloseEvent,
141144
code: e.code,
142-
reason: e.reason,
145+
reason: e.reason || getStatusCodeLabel(e.code),
143146
wasClean: e.wasClean,
144147
event: e.event
145148
});
146149
}
147150
};
151+
148152
return this.ws.onClose(adapter);
149153
}
150154

@@ -164,3 +168,40 @@ export class ServerSocket implements ISocket {
164168
return Promise.resolve();
165169
}
166170
}
171+
172+
const specificStatusCodeMappings: { [code: string]: string | undefined } = {
173+
'1000': 'Normal Closure',
174+
'1001': 'Going Away',
175+
'1002': 'Protocol Error',
176+
'1003': 'Unsupported Data',
177+
'1004': '(For future)',
178+
'1005': 'No Status Received',
179+
'1006': 'Abnormal Closure',
180+
'1007': 'Invalid frame payload data',
181+
'1008': 'Policy Violation',
182+
'1009': 'Message too big',
183+
'1010': 'Missing Extension',
184+
'1011': 'Internal Error',
185+
'1012': 'Service Restart',
186+
'1013': 'Try Again Later',
187+
'1014': 'Bad Gateway',
188+
'1015': 'TLS Handshake'
189+
};
190+
191+
function getStatusCodeLabel(code: number) {
192+
if (code >= 0 && code <= 999) {
193+
return '(Unused)';
194+
} else if (code >= 1016) {
195+
if (code <= 1999) {
196+
return '(For WebSocket standard)';
197+
} else if (code <= 2999) {
198+
return '(For WebSocket extensions)';
199+
} else if (code <= 3999) {
200+
return '(For libraries and frameworks)';
201+
} else if (code <= 4999) {
202+
return '(For applications)';
203+
}
204+
}
205+
206+
return specificStatusCodeMappings[code] || '(Unknown)';
207+
}

src/vs/server/connection/abstractConnection.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ export abstract class AbstractConnection {
3434
}
3535

3636
public reconnect(protocol: ServerProtocol): void {
37-
// this.logger.debug(`${this.protocol.options.reconnectionToken} Reconnecting...`);
37+
this.logger.debug(`${this.protocol.reconnectionToken} Reconnecting...`);
3838
this._offline = undefined;
3939
this.doReconnect(protocol);
4040
}
4141

4242
public dispose(reason?: string): void {
43-
// this.logger.debug(`${this.protocol.options.reconnectionToken} Disposing...`, reason);
43+
this.logger.debug(`${this.protocol.reconnectionToken} Disposing...`, reason);
4444
if (!this.disposed) {
4545
this.disposed = true;
4646
this.doDispose();

0 commit comments

Comments
 (0)