Log more information to track down reconnection cause
This commit is contained in:
parent
7446e50a91
commit
bb01438efc
4 changed files with 135 additions and 23 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue