Log more information to track down reconnection cause

This commit is contained in:
Alexandru Dima 2021-06-09 18:56:56 +02:00
parent 7446e50a91
commit bb01438efc
No known key found for this signature in database
GPG key ID: 6E58D7B045760DA0
4 changed files with 135 additions and 23 deletions

View file

@ -10,9 +10,54 @@ import { VSBuffer } from 'vs/base/common/buffer';
import * as platform from 'vs/base/common/platform';
import * as process from 'vs/base/common/process';
export const enum SocketCloseEventType {
NodeSocketCloseEvent = 0,
WebSocketCloseEvent = 1
}
export interface NodeSocketCloseEvent {
/**
* The type of the event
*/
readonly type: SocketCloseEventType.NodeSocketCloseEvent;
/**
* `true` if the socket had a transmission error.
*/
readonly hadError: boolean;
/**
* Underlying error.
*/
readonly error: Error | undefined
}
export interface WebSocketCloseEvent {
/**
* The type of the event
*/
readonly type: SocketCloseEventType.WebSocketCloseEvent;
/**
* Returns the WebSocket connection close code provided by the server.
*/
readonly code: number;
/**
* Returns the WebSocket connection close reason provided by the server.
*/
readonly reason: string;
/**
* Returns true if the connection closed cleanly; false otherwise.
*/
readonly wasClean: boolean;
/**
* Underlying event.
*/
readonly event: any | undefined;
}
export type SocketCloseEvent = NodeSocketCloseEvent | WebSocketCloseEvent | undefined;
export interface ISocket extends IDisposable {
onData(listener: (e: VSBuffer) => void): IDisposable;
onClose(listener: () => void): IDisposable;
onClose(listener: (e: SocketCloseEvent) => void): IDisposable;
onEnd(listener: () => void): IDisposable;
write(buffer: VSBuffer): void;
end(): void;
@ -624,8 +669,8 @@ export class PersistentProtocol implements IMessagePassingProtocol {
private readonly _onDidDispose = new BufferedEmitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
private readonly _onSocketClose = new BufferedEmitter<void>();
readonly onSocketClose: Event<void> = this._onSocketClose.event;
private readonly _onSocketClose = new BufferedEmitter<SocketCloseEvent>();
readonly onSocketClose: Event<SocketCloseEvent> = this._onSocketClose.event;
private readonly _onSocketTimeout = new BufferedEmitter<void>();
readonly onSocketTimeout: Event<void> = this._onSocketTimeout.event;
@ -658,7 +703,7 @@ export class PersistentProtocol implements IMessagePassingProtocol {
this._socketReader = new ProtocolReader(this._socket);
this._socketDisposables.push(this._socketReader);
this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg)));
this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire()));
this._socketDisposables.push(this._socket.onClose((e) => this._onSocketClose.fire(e)));
if (initialChunk) {
this._socketReader.acceptChunk(initialChunk);
}
@ -768,7 +813,7 @@ export class PersistentProtocol implements IMessagePassingProtocol {
this._socketReader = new ProtocolReader(this._socket);
this._socketDisposables.push(this._socketReader);
this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg)));
this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire()));
this._socketDisposables.push(this._socket.onClose((e) => this._onSocketClose.fire(e)));
this._socketReader.acceptChunk(initialDataChunk);
}

View file

@ -13,7 +13,7 @@ import { tmpdir } from 'os';
import { generateUuid } from 'vs/base/common/uuid';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net';
import { ISocket, Protocol, Client, ChunkStream, SocketCloseEvent, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Platform, platform } from 'vs/base/common/platform';
@ -54,10 +54,17 @@ export class NodeSocket implements ISocket {
};
}
public onClose(listener: () => void): IDisposable {
this.socket.on('close', listener);
public onClose(listener: (e: SocketCloseEvent) => void): IDisposable {
const adapter = (hadError: boolean) => {
listener({
type: SocketCloseEventType.NodeSocketCloseEvent,
hadError: hadError,
error: undefined
});
};
this.socket.on('close', adapter);
return {
dispose: () => this.socket.off('close', listener)
dispose: () => this.socket.off('close', adapter)
};
}
@ -167,7 +174,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
private readonly _pendingDeflateData: Buffer[] = [];
private readonly _incomingData: ChunkStream;
private readonly _onData = this._register(new Emitter<VSBuffer>());
private readonly _onClose = this._register(new Emitter<void>());
private readonly _onClose = this._register(new Emitter<SocketCloseEvent>());
private _isEnded: boolean = false;
private readonly _state = {
@ -232,7 +239,11 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
// zlib errors are fatal, since we have no idea how to recover
console.error(err);
onUnexpectedError(err);
this._onClose.fire();
this._onClose.fire({
type: SocketCloseEventType.NodeSocketCloseEvent,
hadError: true,
error: err
});
});
this._zlibInflate.on('data', (data: Buffer) => {
this._pendingInflateData.push(data);
@ -251,7 +262,11 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
// zlib errors are fatal, since we have no idea how to recover
console.error(err);
onUnexpectedError(err);
this._onClose.fire();
this._onClose.fire({
type: SocketCloseEventType.NodeSocketCloseEvent,
hadError: true,
error: err
});
});
this._zlibDeflate.on('data', (data: Buffer) => {
this._pendingDeflateData.push(data);
@ -263,7 +278,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
this._zlibDeflateFlushWaitingCount = 0;
this._incomingData = new ChunkStream();
this._register(this.socket.onData(data => this._acceptChunk(data)));
this._register(this.socket.onClose(() => this._onClose.fire()));
this._register(this.socket.onClose((e) => this._onClose.fire(e)));
}
public override dispose(): void {
@ -282,7 +297,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
return this._onData.event(listener);
}
public onClose(listener: () => void): IDisposable {
public onClose(listener: (e: SocketCloseEvent) => void): IDisposable {
return this._onClose.event(listener);
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { ISocketFactory, IConnectCallback } from 'vs/platform/remote/common/remoteAgentConnection';
import { ISocket } from 'vs/base/parts/ipc/common/ipc.net';
import { ISocket, SocketCloseEvent, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net';
import { VSBuffer } from 'vs/base/common/buffer';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
@ -16,10 +16,29 @@ export interface IWebSocketFactory {
create(url: string): IWebSocket;
}
export interface IWebSocketCloseEvent {
/**
* Returns the WebSocket connection close code provided by the server.
*/
readonly code: number;
/**
* Returns the WebSocket connection close reason provided by the server.
*/
readonly reason: string;
/**
* Returns true if the connection closed cleanly; false otherwise.
*/
readonly wasClean: boolean;
/**
* Underlying event.
*/
readonly event: any | undefined;
}
export interface IWebSocket {
readonly onData: Event<ArrayBuffer>;
readonly onOpen: Event<void>;
readonly onClose: Event<void>;
readonly onClose: Event<IWebSocketCloseEvent | void>;
readonly onError: Event<any>;
send(data: ArrayBuffer | ArrayBufferView): void;
@ -33,7 +52,7 @@ class BrowserWebSocket extends Disposable implements IWebSocket {
public readonly onOpen: Event<void>;
private readonly _onClose = this._register(new Emitter<void>());
private readonly _onClose = this._register(new Emitter<IWebSocketCloseEvent>());
public readonly onClose = this._onClose.event;
private readonly _onError = this._register(new Emitter<any>());
@ -135,7 +154,7 @@ class BrowserWebSocket extends Disposable implements IWebSocket {
}
}
this._onClose.fire();
this._onClose.fire({ code: e.code, reason: e.reason, wasClean: e.wasClean, event: e });
}));
this._register(dom.addDisposableListener(this._socket, 'error', sendErrorSoon));
@ -178,8 +197,21 @@ class BrowserSocket implements ISocket {
return this.socket.onData((data) => listener(VSBuffer.wrap(new Uint8Array(data))));
}
public onClose(listener: () => void): IDisposable {
return this.socket.onClose(listener);
public onClose(listener: (e: SocketCloseEvent) => void): IDisposable {
const adapter = (e: IWebSocketCloseEvent | void) => {
if (typeof e === 'undefined') {
listener(e);
} else {
listener({
type: SocketCloseEventType.WebSocketCloseEvent,
code: e.code,
reason: e.reason,
wasClean: e.wasClean,
event: e.event
});
}
};
return this.socket.onClose(adapter);
}
public onEnd(listener: () => void): IDisposable {

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Client, PersistentProtocol, ISocket, ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net';
import { Client, PersistentProtocol, ISocket, ProtocolConstants, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net';
import { generateUuid } from 'vs/base/common/uuid';
import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@ -532,8 +532,28 @@ abstract class PersistentConnection extends Disposable {
this._onDidStateChange.fire(new ConnectionGainEvent(this.reconnectionToken, 0, 0));
this._register(protocol.onSocketClose(() => this._beginReconnecting()));
this._register(protocol.onSocketTimeout(() => this._beginReconnecting()));
this._register(protocol.onSocketClose((e) => {
const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true);
if (!e) {
this._options.logService.info(`${logPrefix} received socket close event.`);
} else if (e.type === SocketCloseEventType.NodeSocketCloseEvent) {
this._options.logService.info(`${logPrefix} received socket close event (hadError: ${e.hadError}).`);
if (e.error) {
this._options.logService.error(e.error);
}
} else {
this._options.logService.info(`${logPrefix} received socket close event (wasClean: ${e.wasClean}, code: ${e.code}, reason: ${e.reason}).`);
if (e.event) {
this._options.logService.error(e.event);
}
}
this._beginReconnecting();
}));
this._register(protocol.onSocketTimeout(() => {
const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true);
this._options.logService.trace(`${logPrefix} received socket timeout event.`);
this._beginReconnecting();
}));
PersistentConnection._instances.push(this);