vscode/src/vs/server/remoteExtensionManagement.ts
2021-10-20 18:42:13 +02:00

128 lines
4.5 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { PersistentProtocol, ProtocolConstants, ISocket } from 'vs/base/parts/ipc/common/ipc.net';
import { ILogService } from 'vs/platform/log/common/log';
import { Emitter, Event } from 'vs/base/common/event';
import { VSBuffer } from 'vs/base/common/buffer';
import { ProcessTimeRunOnceScheduler } from 'vs/base/common/async';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
export interface IExtensionsManagementProcessInitData {
args: NativeParsedArgs;
}
export function printTime(ms: number): string {
let h = 0;
let m = 0;
let s = 0;
if (ms >= 1000) {
s = Math.floor(ms / 1000);
ms -= s * 1000;
}
if (s >= 60) {
m = Math.floor(s / 60);
s -= m * 60;
}
if (m >= 60) {
h = Math.floor(m / 60);
m -= h * 60;
}
const _h = h ? `${h}h` : ``;
const _m = m ? `${m}m` : ``;
const _s = s ? `${s}s` : ``;
const _ms = ms ? `${ms}ms` : ``;
return `${_h}${_m}${_s}${_ms}`;
}
export class ManagementConnection {
private _onClose = new Emitter<void>();
public readonly onClose: Event<void> = this._onClose.event;
private readonly _reconnectionGraceTime: number;
private readonly _reconnectionShortGraceTime: number;
private _remoteAddress: string;
public readonly protocol: PersistentProtocol;
private _disposed: boolean;
private _disconnectRunner1: ProcessTimeRunOnceScheduler;
private _disconnectRunner2: ProcessTimeRunOnceScheduler;
constructor(
private readonly _logService: ILogService,
private readonly _reconnectionToken: string,
remoteAddress: string,
protocol: PersistentProtocol
) {
this._reconnectionGraceTime = ProtocolConstants.ReconnectionGraceTime;
this._reconnectionShortGraceTime = ProtocolConstants.ReconnectionShortGraceTime;
this._remoteAddress = remoteAddress;
this.protocol = protocol;
this._disposed = false;
this._disconnectRunner1 = new ProcessTimeRunOnceScheduler(() => {
this._log(`The reconnection grace time of ${printTime(this._reconnectionGraceTime)} has expired, so the connection will be disposed.`);
this._cleanResources();
}, this._reconnectionGraceTime);
this._disconnectRunner2 = new ProcessTimeRunOnceScheduler(() => {
this._log(`The reconnection short grace time of ${printTime(this._reconnectionShortGraceTime)} has expired, so the connection will be disposed.`);
this._cleanResources();
}, this._reconnectionShortGraceTime);
this.protocol.onDidDispose(() => {
this._log(`The client has disconnected gracefully, so the connection will be disposed.`);
this._cleanResources();
});
this.protocol.onSocketClose(() => {
this._log(`The client has disconnected, will wait for reconnection ${printTime(this._reconnectionGraceTime)} before disposing...`);
// The socket has closed, let's give the renderer a certain amount of time to reconnect
this._disconnectRunner1.schedule();
});
this._log(`New connection established.`);
}
private _log(_str: string): void {
this._logService.info(`[${this._remoteAddress}][${this._reconnectionToken.substr(0, 8)}][ManagementConnection] ${_str}`);
}
public shortenReconnectionGraceTimeIfNecessary(): void {
if (this._disconnectRunner2.isScheduled()) {
// we are disconnected and already running the short reconnection timer
return;
}
if (this._disconnectRunner1.isScheduled()) {
this._log(`Another client has connected, will shorten the wait for reconnection ${printTime(this._reconnectionShortGraceTime)} before disposing...`);
// we are disconnected and running the long reconnection timer
this._disconnectRunner2.schedule();
}
}
private _cleanResources(): void {
if (this._disposed) {
// already called
return;
}
this._disposed = true;
this._disconnectRunner1.dispose();
this._disconnectRunner2.dispose();
const socket = this.protocol.getSocket();
this.protocol.sendDisconnect();
this.protocol.dispose();
socket.end();
this._onClose.fire(undefined);
}
public acceptReconnection(remoteAddress: string, socket: ISocket, initialDataChunk: VSBuffer): void {
this._remoteAddress = remoteAddress;
this._log(`The client has reconnected.`);
this._disconnectRunner1.cancel();
this._disconnectRunner2.cancel();
this.protocol.beginAcceptReconnection(socket, initialDataChunk);
this.protocol.endAcceptReconnection();
}
}