Migrate to private class properties/methods
This commit is contained in:
@@ -193,7 +193,7 @@ export class IncomingWebSocketRequestLibsignal
|
||||
export class IncomingWebSocketRequestLegacy
|
||||
implements IncomingWebSocketRequest
|
||||
{
|
||||
private readonly id: Long;
|
||||
readonly #id: Long;
|
||||
|
||||
public readonly requestType: ServerRequestType;
|
||||
|
||||
@@ -209,7 +209,7 @@ export class IncomingWebSocketRequestLegacy
|
||||
strictAssert(request.verb, 'request without verb');
|
||||
strictAssert(request.path, 'request without path');
|
||||
|
||||
this.id = request.id;
|
||||
this.#id = request.id;
|
||||
this.requestType = resolveType(request.path, request.verb);
|
||||
this.body = dropNull(request.body);
|
||||
this.timestamp = resolveTimestamp(request.headers || []);
|
||||
@@ -218,7 +218,7 @@ export class IncomingWebSocketRequestLegacy
|
||||
public respond(status: number, message: string): void {
|
||||
const bytes = Proto.WebSocketMessage.encode({
|
||||
type: Proto.WebSocketMessage.Type.RESPONSE,
|
||||
response: { id: this.id, message, status },
|
||||
response: { id: this.#id, message, status },
|
||||
}).finish();
|
||||
|
||||
this.sendBytes(Buffer.from(bytes));
|
||||
@@ -479,7 +479,7 @@ export class LibsignalWebSocketResource
|
||||
// socket alive using websocket pings, so we don't need a timer-based
|
||||
// keepalive mechanism. But we still send one-off keepalive requests when
|
||||
// things change (see forceKeepAlive()).
|
||||
private keepalive: KeepAliveSender;
|
||||
#keepalive: KeepAliveSender;
|
||||
|
||||
constructor(
|
||||
private readonly chatService: Net.ChatConnection,
|
||||
@@ -490,7 +490,7 @@ export class LibsignalWebSocketResource
|
||||
) {
|
||||
super();
|
||||
|
||||
this.keepalive = new KeepAliveSender(this, this.logId, keepalive);
|
||||
this.#keepalive = new KeepAliveSender(this, this.logId, keepalive);
|
||||
}
|
||||
|
||||
public localPort(): number {
|
||||
@@ -551,7 +551,7 @@ export class LibsignalWebSocketResource
|
||||
}
|
||||
|
||||
public forceKeepAlive(timeout?: number): void {
|
||||
drop(this.keepalive.send(timeout));
|
||||
drop(this.#keepalive.send(timeout));
|
||||
}
|
||||
|
||||
public async sendRequest(options: SendRequestOptions): Promise<Response> {
|
||||
@@ -578,28 +578,24 @@ export class LibsignalWebSocketResource
|
||||
}
|
||||
|
||||
export class WebSocketResourceWithShadowing implements IWebSocketResource {
|
||||
private shadowing: LibsignalWebSocketResource | undefined;
|
||||
|
||||
private stats: AggregatedStats;
|
||||
|
||||
private statsTimer: NodeJS.Timeout;
|
||||
|
||||
private shadowingWithReporting: boolean;
|
||||
|
||||
private logId: string;
|
||||
#shadowing: LibsignalWebSocketResource | undefined;
|
||||
#stats: AggregatedStats;
|
||||
#statsTimer: NodeJS.Timeout;
|
||||
#shadowingWithReporting: boolean;
|
||||
#logId: string;
|
||||
|
||||
constructor(
|
||||
private readonly main: WebSocketResource,
|
||||
private readonly shadowingConnection: AbortableProcess<LibsignalWebSocketResource>,
|
||||
options: WebSocketResourceOptions
|
||||
) {
|
||||
this.stats = AggregatedStats.createEmpty();
|
||||
this.logId = `WebSocketResourceWithShadowing(${options.name})`;
|
||||
this.statsTimer = setInterval(
|
||||
() => this.updateStats(options.name),
|
||||
this.#stats = AggregatedStats.createEmpty();
|
||||
this.#logId = `WebSocketResourceWithShadowing(${options.name})`;
|
||||
this.#statsTimer = setInterval(
|
||||
() => this.#updateStats(options.name),
|
||||
STATS_UPDATE_INTERVAL
|
||||
);
|
||||
this.shadowingWithReporting =
|
||||
this.#shadowingWithReporting =
|
||||
options.transportOption === TransportOption.ShadowingHigh;
|
||||
|
||||
// the idea is that we want to keep the shadowing connection process
|
||||
@@ -608,33 +604,33 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
|
||||
// or an error reported in case of connection failure
|
||||
const initializeAfterConnected = async () => {
|
||||
try {
|
||||
this.shadowing = await shadowingConnection.resultPromise;
|
||||
this.#shadowing = await shadowingConnection.resultPromise;
|
||||
// checking IP one time per connection
|
||||
if (this.main.ipVersion() !== this.shadowing.ipVersion()) {
|
||||
this.stats.ipVersionMismatches += 1;
|
||||
if (this.main.ipVersion() !== this.#shadowing.ipVersion()) {
|
||||
this.#stats.ipVersionMismatches += 1;
|
||||
const mainIpType = this.main.ipVersion();
|
||||
const shadowIpType = this.shadowing.ipVersion();
|
||||
const shadowIpType = this.#shadowing.ipVersion();
|
||||
log.warn(
|
||||
`${this.logId}: libsignal websocket IP [${shadowIpType}], Desktop websocket IP [${mainIpType}]`
|
||||
`${this.#logId}: libsignal websocket IP [${shadowIpType}], Desktop websocket IP [${mainIpType}]`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.stats.connectionFailures += 1;
|
||||
this.#stats.connectionFailures += 1;
|
||||
}
|
||||
};
|
||||
drop(initializeAfterConnected());
|
||||
|
||||
this.addEventListener('close', (_ev): void => {
|
||||
clearInterval(this.statsTimer);
|
||||
this.updateStats(options.name);
|
||||
clearInterval(this.#statsTimer);
|
||||
this.#updateStats(options.name);
|
||||
});
|
||||
}
|
||||
|
||||
private updateStats(name: string) {
|
||||
#updateStats(name: string) {
|
||||
const storedStats = AggregatedStats.loadOrCreateEmpty(name);
|
||||
let updatedStats = AggregatedStats.add(storedStats, this.stats);
|
||||
let updatedStats = AggregatedStats.add(storedStats, this.#stats);
|
||||
if (
|
||||
this.shadowingWithReporting &&
|
||||
this.#shadowingWithReporting &&
|
||||
AggregatedStats.shouldReportError(updatedStats) &&
|
||||
!isProduction(window.getVersion())
|
||||
) {
|
||||
@@ -642,14 +638,14 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
|
||||
toastType: ToastType.TransportError,
|
||||
});
|
||||
log.warn(
|
||||
`${this.logId}: experimental transport toast displayed, flushing transport statistics before resetting`,
|
||||
`${this.#logId}: experimental transport toast displayed, flushing transport statistics before resetting`,
|
||||
updatedStats
|
||||
);
|
||||
updatedStats = AggregatedStats.createEmpty();
|
||||
updatedStats.lastToastTimestamp = Date.now();
|
||||
}
|
||||
AggregatedStats.store(updatedStats, name);
|
||||
this.stats = AggregatedStats.createEmpty();
|
||||
this.#stats = AggregatedStats.createEmpty();
|
||||
}
|
||||
|
||||
public localPort(): number | undefined {
|
||||
@@ -665,9 +661,9 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
|
||||
|
||||
public close(code = NORMAL_DISCONNECT_CODE, reason?: string): void {
|
||||
this.main.close(code, reason);
|
||||
if (this.shadowing) {
|
||||
this.shadowing.close(code, reason);
|
||||
this.shadowing = undefined;
|
||||
if (this.#shadowing) {
|
||||
this.#shadowing.close(code, reason);
|
||||
this.#shadowing = undefined;
|
||||
} else {
|
||||
this.shadowingConnection.abort();
|
||||
}
|
||||
@@ -675,9 +671,9 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
|
||||
|
||||
public shutdown(): void {
|
||||
this.main.shutdown();
|
||||
if (this.shadowing) {
|
||||
this.shadowing.shutdown();
|
||||
this.shadowing = undefined;
|
||||
if (this.#shadowing) {
|
||||
this.#shadowing.shutdown();
|
||||
this.#shadowing = undefined;
|
||||
} else {
|
||||
this.shadowingConnection.abort();
|
||||
}
|
||||
@@ -695,48 +691,48 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource {
|
||||
// attempting to run a healthcheck on a libsignal transport.
|
||||
if (
|
||||
isSuccessfulStatusCode(response.status) &&
|
||||
this.shouldSendShadowRequest()
|
||||
this.#shouldSendShadowRequest()
|
||||
) {
|
||||
drop(this.sendShadowRequest());
|
||||
drop(this.#sendShadowRequest());
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private async sendShadowRequest(): Promise<void> {
|
||||
async #sendShadowRequest(): Promise<void> {
|
||||
// In the shadowing mode, it could be that we're either
|
||||
// still connecting libsignal websocket or have already closed it.
|
||||
// In those cases we're not running shadowing check.
|
||||
if (!this.shadowing) {
|
||||
if (!this.#shadowing) {
|
||||
log.info(
|
||||
`${this.logId}: skipping healthcheck - websocket not connected or already closed`
|
||||
`${this.#logId}: skipping healthcheck - websocket not connected or already closed`
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const healthCheckResult = await this.shadowing.sendRequest({
|
||||
const healthCheckResult = await this.#shadowing.sendRequest({
|
||||
verb: 'GET',
|
||||
path: '/v1/keepalive',
|
||||
timeout: KEEPALIVE_TIMEOUT_MS,
|
||||
});
|
||||
this.stats.requestsCompared += 1;
|
||||
this.#stats.requestsCompared += 1;
|
||||
if (!isSuccessfulStatusCode(healthCheckResult.status)) {
|
||||
this.stats.healthcheckBadStatus += 1;
|
||||
this.#stats.healthcheckBadStatus += 1;
|
||||
log.warn(
|
||||
`${this.logId}: keepalive via libsignal responded with status [${healthCheckResult.status}]`
|
||||
`${this.#logId}: keepalive via libsignal responded with status [${healthCheckResult.status}]`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.stats.healthcheckFailures += 1;
|
||||
this.#stats.healthcheckFailures += 1;
|
||||
log.warn(
|
||||
`${this.logId}: failed to send keepalive via libsignal`,
|
||||
`${this.#logId}: failed to send keepalive via libsignal`,
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private shouldSendShadowRequest(): boolean {
|
||||
return this.shadowingWithReporting || random(0, 100) < 10;
|
||||
#shouldSendShadowRequest(): boolean {
|
||||
return this.#shadowingWithReporting || random(0, 100) < 10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,28 +744,21 @@ export default class WebSocketResource
|
||||
extends EventTarget
|
||||
implements IWebSocketResource
|
||||
{
|
||||
private outgoingId = Long.fromNumber(1, true);
|
||||
#outgoingId = Long.fromNumber(1, true);
|
||||
#closed = false;
|
||||
|
||||
private closed = false;
|
||||
|
||||
private readonly outgoingMap = new Map<
|
||||
readonly #outgoingMap = new Map<
|
||||
string,
|
||||
(result: SendRequestResult) => void
|
||||
>();
|
||||
|
||||
private readonly boundOnMessage: (message: IMessage) => void;
|
||||
|
||||
private activeRequests = new Set<IncomingWebSocketRequest | string>();
|
||||
|
||||
private shuttingDown = false;
|
||||
|
||||
private shutdownTimer?: Timers.Timeout;
|
||||
|
||||
private readonly logId: string;
|
||||
|
||||
private readonly localSocketPort: number | undefined;
|
||||
|
||||
private readonly socketIpVersion: IpVersion | undefined;
|
||||
readonly #boundOnMessage: (message: IMessage) => void;
|
||||
#activeRequests = new Set<IncomingWebSocketRequest | string>();
|
||||
#shuttingDown = false;
|
||||
#shutdownTimer?: Timers.Timeout;
|
||||
readonly #logId: string;
|
||||
readonly #localSocketPort: number | undefined;
|
||||
readonly #socketIpVersion: IpVersion | undefined;
|
||||
|
||||
// Public for tests
|
||||
public readonly keepalive?: KeepAlive;
|
||||
@@ -780,25 +769,25 @@ export default class WebSocketResource
|
||||
) {
|
||||
super();
|
||||
|
||||
this.logId = `WebSocketResource(${options.name})`;
|
||||
this.localSocketPort = socket.socket.localPort;
|
||||
this.#logId = `WebSocketResource(${options.name})`;
|
||||
this.#localSocketPort = socket.socket.localPort;
|
||||
|
||||
if (!socket.socket.localAddress) {
|
||||
this.socketIpVersion = undefined;
|
||||
this.#socketIpVersion = undefined;
|
||||
}
|
||||
if (socket.socket.localAddress == null) {
|
||||
this.socketIpVersion = undefined;
|
||||
this.#socketIpVersion = undefined;
|
||||
} else if (net.isIPv4(socket.socket.localAddress)) {
|
||||
this.socketIpVersion = IpVersion.IPv4;
|
||||
this.#socketIpVersion = IpVersion.IPv4;
|
||||
} else if (net.isIPv6(socket.socket.localAddress)) {
|
||||
this.socketIpVersion = IpVersion.IPv6;
|
||||
this.#socketIpVersion = IpVersion.IPv6;
|
||||
} else {
|
||||
this.socketIpVersion = undefined;
|
||||
this.#socketIpVersion = undefined;
|
||||
}
|
||||
|
||||
this.boundOnMessage = this.onMessage.bind(this);
|
||||
this.#boundOnMessage = this.#onMessage.bind(this);
|
||||
|
||||
socket.on('message', this.boundOnMessage);
|
||||
socket.on('message', this.#boundOnMessage);
|
||||
|
||||
if (options.keepalive) {
|
||||
const keepalive = new KeepAlive(
|
||||
@@ -811,26 +800,26 @@ export default class WebSocketResource
|
||||
keepalive.reset();
|
||||
socket.on('close', () => this.keepalive?.stop());
|
||||
socket.on('error', (error: Error) => {
|
||||
log.warn(`${this.logId}: WebSocket error`, Errors.toLogFormat(error));
|
||||
log.warn(`${this.#logId}: WebSocket error`, Errors.toLogFormat(error));
|
||||
});
|
||||
}
|
||||
|
||||
socket.on('close', (code, reason) => {
|
||||
this.closed = true;
|
||||
this.#closed = true;
|
||||
|
||||
log.warn(`${this.logId}: Socket closed`);
|
||||
log.warn(`${this.#logId}: Socket closed`);
|
||||
this.dispatchEvent(new CloseEvent(code, reason || 'normal'));
|
||||
});
|
||||
|
||||
this.addEventListener('close', () => this.onClose());
|
||||
this.addEventListener('close', () => this.#onClose());
|
||||
}
|
||||
|
||||
public ipVersion(): IpVersion | undefined {
|
||||
return this.socketIpVersion;
|
||||
return this.#socketIpVersion;
|
||||
}
|
||||
|
||||
public localPort(): number | undefined {
|
||||
return this.localSocketPort;
|
||||
return this.#localSocketPort;
|
||||
}
|
||||
|
||||
public override addEventListener(
|
||||
@@ -843,12 +832,15 @@ export default class WebSocketResource
|
||||
}
|
||||
|
||||
public async sendRequest(options: SendRequestOptions): Promise<Response> {
|
||||
const id = this.outgoingId;
|
||||
const id = this.#outgoingId;
|
||||
const idString = id.toString();
|
||||
strictAssert(!this.outgoingMap.has(idString), 'Duplicate outgoing request');
|
||||
strictAssert(
|
||||
!this.#outgoingMap.has(idString),
|
||||
'Duplicate outgoing request'
|
||||
);
|
||||
|
||||
// Note that this automatically wraps
|
||||
this.outgoingId = this.outgoingId.add(1);
|
||||
this.#outgoingId = this.#outgoingId.add(1);
|
||||
|
||||
const bytes = Proto.WebSocketMessage.encode({
|
||||
type: Proto.WebSocketMessage.Type.REQUEST,
|
||||
@@ -871,25 +863,25 @@ export default class WebSocketResource
|
||||
'WebSocket request byte size exceeded'
|
||||
);
|
||||
|
||||
strictAssert(!this.shuttingDown, 'Cannot send request, shutting down');
|
||||
this.addActive(idString);
|
||||
strictAssert(!this.#shuttingDown, 'Cannot send request, shutting down');
|
||||
this.#addActive(idString);
|
||||
const promise = new Promise<SendRequestResult>((resolve, reject) => {
|
||||
let timer = options.timeout
|
||||
? Timers.setTimeout(() => {
|
||||
this.removeActive(idString);
|
||||
this.#removeActive(idString);
|
||||
this.close(UNEXPECTED_DISCONNECT_CODE, 'Request timed out');
|
||||
reject(new Error(`Request timed out; id: [${idString}]`));
|
||||
}, options.timeout)
|
||||
: undefined;
|
||||
|
||||
this.outgoingMap.set(idString, result => {
|
||||
this.#outgoingMap.set(idString, result => {
|
||||
if (timer !== undefined) {
|
||||
Timers.clearTimeout(timer);
|
||||
timer = undefined;
|
||||
}
|
||||
|
||||
this.keepalive?.reset();
|
||||
this.removeActive(idString);
|
||||
this.#removeActive(idString);
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
@@ -908,58 +900,58 @@ export default class WebSocketResource
|
||||
}
|
||||
|
||||
public close(code = NORMAL_DISCONNECT_CODE, reason?: string): void {
|
||||
if (this.closed) {
|
||||
log.info(`${this.logId}.close: Already closed! ${code}/${reason}`);
|
||||
if (this.#closed) {
|
||||
log.info(`${this.#logId}.close: Already closed! ${code}/${reason}`);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(`${this.logId}.close(${code})`);
|
||||
log.info(`${this.#logId}.close(${code})`);
|
||||
if (this.keepalive) {
|
||||
this.keepalive.stop();
|
||||
}
|
||||
|
||||
this.socket.close(code, reason);
|
||||
|
||||
this.socket.removeListener('message', this.boundOnMessage);
|
||||
this.socket.removeListener('message', this.#boundOnMessage);
|
||||
|
||||
// On linux the socket can wait a long time to emit its close event if we've
|
||||
// lost the internet connection. On the order of minutes. This speeds that
|
||||
// process up.
|
||||
Timers.setTimeout(() => {
|
||||
if (this.closed) {
|
||||
if (this.#closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.warn(`${this.logId}.close: Dispatching our own socket close event`);
|
||||
log.warn(`${this.#logId}.close: Dispatching our own socket close event`);
|
||||
this.dispatchEvent(new CloseEvent(code, reason || 'normal'));
|
||||
}, 5 * durations.SECOND);
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
if (this.closed) {
|
||||
if (this.#closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.activeRequests.size === 0) {
|
||||
log.info(`${this.logId}.shutdown: no active requests, closing`);
|
||||
if (this.#activeRequests.size === 0) {
|
||||
log.info(`${this.#logId}.shutdown: no active requests, closing`);
|
||||
this.close(NORMAL_DISCONNECT_CODE, 'Shutdown');
|
||||
return;
|
||||
}
|
||||
|
||||
this.shuttingDown = true;
|
||||
this.#shuttingDown = true;
|
||||
|
||||
log.info(`${this.logId}.shutdown: shutting down`);
|
||||
this.shutdownTimer = Timers.setTimeout(() => {
|
||||
if (this.closed) {
|
||||
log.info(`${this.#logId}.shutdown: shutting down`);
|
||||
this.#shutdownTimer = Timers.setTimeout(() => {
|
||||
if (this.#closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.warn(`${this.logId}.shutdown: Failed to shutdown gracefully`);
|
||||
log.warn(`${this.#logId}.shutdown: Failed to shutdown gracefully`);
|
||||
this.close(NORMAL_DISCONNECT_CODE, 'Shutdown');
|
||||
}, THIRTY_SECONDS);
|
||||
}
|
||||
|
||||
private onMessage({ type, binaryData }: IMessage): void {
|
||||
#onMessage({ type, binaryData }: IMessage): void {
|
||||
if (type !== 'binary' || !binaryData) {
|
||||
throw new Error(`Unsupported websocket message type: ${type}`);
|
||||
}
|
||||
@@ -976,7 +968,7 @@ export default class WebSocketResource
|
||||
const incomingRequest = new IncomingWebSocketRequestLegacy(
|
||||
message.request,
|
||||
(bytes: Buffer): void => {
|
||||
this.removeActive(incomingRequest);
|
||||
this.#removeActive(incomingRequest);
|
||||
|
||||
strictAssert(
|
||||
bytes.length <= MAX_MESSAGE_SIZE,
|
||||
@@ -986,12 +978,12 @@ export default class WebSocketResource
|
||||
}
|
||||
);
|
||||
|
||||
if (this.shuttingDown) {
|
||||
if (this.#shuttingDown) {
|
||||
incomingRequest.respond(-1, 'Shutting down');
|
||||
return;
|
||||
}
|
||||
|
||||
this.addActive(incomingRequest);
|
||||
this.#addActive(incomingRequest);
|
||||
handleRequest(incomingRequest);
|
||||
} else if (
|
||||
message.type === Proto.WebSocketMessage.Type.RESPONSE &&
|
||||
@@ -1001,8 +993,8 @@ export default class WebSocketResource
|
||||
strictAssert(response.id, 'response without id');
|
||||
|
||||
const responseIdString = response.id.toString();
|
||||
const resolve = this.outgoingMap.get(responseIdString);
|
||||
this.outgoingMap.delete(responseIdString);
|
||||
const resolve = this.#outgoingMap.get(responseIdString);
|
||||
this.#outgoingMap.delete(responseIdString);
|
||||
|
||||
if (!resolve) {
|
||||
throw new Error(`Received response for unknown request ${response.id}`);
|
||||
@@ -1017,9 +1009,9 @@ export default class WebSocketResource
|
||||
}
|
||||
}
|
||||
|
||||
private onClose(): void {
|
||||
const outgoing = new Map(this.outgoingMap);
|
||||
this.outgoingMap.clear();
|
||||
#onClose(): void {
|
||||
const outgoing = new Map(this.#outgoingMap);
|
||||
this.#outgoingMap.clear();
|
||||
|
||||
for (const resolve of outgoing.values()) {
|
||||
resolve({
|
||||
@@ -1031,30 +1023,30 @@ export default class WebSocketResource
|
||||
}
|
||||
}
|
||||
|
||||
private addActive(request: IncomingWebSocketRequest | string): void {
|
||||
this.activeRequests.add(request);
|
||||
#addActive(request: IncomingWebSocketRequest | string): void {
|
||||
this.#activeRequests.add(request);
|
||||
}
|
||||
|
||||
private removeActive(request: IncomingWebSocketRequest | string): void {
|
||||
if (!this.activeRequests.has(request)) {
|
||||
log.warn(`${this.logId}.removeActive: removing unknown request`);
|
||||
#removeActive(request: IncomingWebSocketRequest | string): void {
|
||||
if (!this.#activeRequests.has(request)) {
|
||||
log.warn(`${this.#logId}.removeActive: removing unknown request`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeRequests.delete(request);
|
||||
if (this.activeRequests.size !== 0) {
|
||||
this.#activeRequests.delete(request);
|
||||
if (this.#activeRequests.size !== 0) {
|
||||
return;
|
||||
}
|
||||
if (!this.shuttingDown) {
|
||||
if (!this.#shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.shutdownTimer) {
|
||||
Timers.clearTimeout(this.shutdownTimer);
|
||||
this.shutdownTimer = undefined;
|
||||
if (this.#shutdownTimer) {
|
||||
Timers.clearTimeout(this.#shutdownTimer);
|
||||
this.#shutdownTimer = undefined;
|
||||
}
|
||||
|
||||
log.info(`${this.logId}.removeActive: shutdown complete`);
|
||||
log.info(`${this.#logId}.removeActive: shutdown complete`);
|
||||
this.close(NORMAL_DISCONNECT_CODE, 'Shutdown');
|
||||
}
|
||||
|
||||
@@ -1109,7 +1101,7 @@ const LOG_KEEPALIVE_AFTER_MS = 500;
|
||||
* intervals.
|
||||
*/
|
||||
class KeepAliveSender {
|
||||
private path: string;
|
||||
#path: string;
|
||||
|
||||
protected wsr: IWebSocketResource;
|
||||
|
||||
@@ -1121,7 +1113,7 @@ class KeepAliveSender {
|
||||
opts: KeepAliveOptionsType = {}
|
||||
) {
|
||||
this.logId = `WebSocketResources.KeepAlive(${name})`;
|
||||
this.path = opts.path ?? '/';
|
||||
this.#path = opts.path ?? '/';
|
||||
this.wsr = websocketResource;
|
||||
}
|
||||
|
||||
@@ -1133,7 +1125,7 @@ class KeepAliveSender {
|
||||
const { status } = await pTimeout(
|
||||
this.wsr.sendRequest({
|
||||
verb: 'GET',
|
||||
path: this.path,
|
||||
path: this.#path,
|
||||
}),
|
||||
timeout
|
||||
);
|
||||
@@ -1176,9 +1168,8 @@ class KeepAliveSender {
|
||||
* {@link KeepAliveSender}.
|
||||
*/
|
||||
class KeepAlive extends KeepAliveSender {
|
||||
private keepAliveTimer: Timers.Timeout | undefined;
|
||||
|
||||
private lastAliveAt: number = Date.now();
|
||||
#keepAliveTimer: Timers.Timeout | undefined;
|
||||
#lastAliveAt: number = Date.now();
|
||||
|
||||
constructor(
|
||||
websocketResource: WebSocketResource,
|
||||
@@ -1189,18 +1180,18 @@ class KeepAlive extends KeepAliveSender {
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
this.clearTimers();
|
||||
this.#clearTimers();
|
||||
}
|
||||
|
||||
public override async send(timeout = KEEPALIVE_TIMEOUT_MS): Promise<boolean> {
|
||||
this.clearTimers();
|
||||
this.#clearTimers();
|
||||
|
||||
const isStale = isOlderThan(this.lastAliveAt, STALE_THRESHOLD_MS);
|
||||
const isStale = isOlderThan(this.#lastAliveAt, STALE_THRESHOLD_MS);
|
||||
if (isStale) {
|
||||
log.info(`${this.logId}.send: disconnecting due to stale state`);
|
||||
this.wsr.close(
|
||||
UNEXPECTED_DISCONNECT_CODE,
|
||||
`Last keepalive request was too far in the past: ${this.lastAliveAt}`
|
||||
`Last keepalive request was too far in the past: ${this.#lastAliveAt}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -1216,20 +1207,20 @@ class KeepAlive extends KeepAliveSender {
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.lastAliveAt = Date.now();
|
||||
this.#lastAliveAt = Date.now();
|
||||
|
||||
this.clearTimers();
|
||||
this.#clearTimers();
|
||||
|
||||
this.keepAliveTimer = Timers.setTimeout(
|
||||
this.#keepAliveTimer = Timers.setTimeout(
|
||||
() => this.send(),
|
||||
KEEPALIVE_INTERVAL_MS
|
||||
);
|
||||
}
|
||||
|
||||
private clearTimers(): void {
|
||||
if (this.keepAliveTimer) {
|
||||
Timers.clearTimeout(this.keepAliveTimer);
|
||||
this.keepAliveTimer = undefined;
|
||||
#clearTimers(): void {
|
||||
if (this.#keepAliveTimer) {
|
||||
Timers.clearTimeout(this.#keepAliveTimer);
|
||||
this.#keepAliveTimer = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user