Start unauthenticated socket timer after request

This commit is contained in:
Fedor Indutny 2021-08-24 08:58:40 -07:00 committed by GitHub
parent 4371996362
commit 9012091d21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -41,12 +41,13 @@ export type SocketManagerOptions = Readonly<{
proxyUrl?: string; proxyUrl?: string;
}>; }>;
// This class manages two websocket resource: // This class manages two websocket resources:
// //
// - Authenticated WebSocketResource which uses supplied WebAPICredentials and // - Authenticated WebSocketResource which uses supplied WebAPICredentials and
// automatically reconnects on closed socket (using back off) // automatically reconnects on closed socket (using back off)
// - Unauthenticated WebSocketResource that is created on demand and reconnected // - Unauthenticated WebSocketResource that is created on the first outgoing
// every 5 minutes. // unauthenticated request and is periodically rotated (5 minutes since first
// activity on the socket).
// //
// Incoming requests on authenticated resource are funneled into the registered // Incoming requests on authenticated resource are funneled into the registered
// request handlers (`registerRequestHandler`) or queued internally until at // request handlers (`registerRequestHandler`) or queued internally until at
@ -61,6 +62,8 @@ export class SocketManager extends EventListener {
private unauthenticated?: AbortableProcess<WebSocketResource>; private unauthenticated?: AbortableProcess<WebSocketResource>;
private unauthenticatedExpirationTimer?: NodeJS.Timeout;
private credentials?: WebAPICredentials; private credentials?: WebAPICredentials;
private readonly proxyAgent?: ProxyAgent; private readonly proxyAgent?: ProxyAgent;
@ -273,6 +276,7 @@ export class SocketManager extends EventListener {
resource = await this.getAuthenticatedResource(); resource = await this.getAuthenticatedResource();
} else { } else {
resource = await this.getUnauthenticatedResource(); resource = await this.getUnauthenticatedResource();
await this.startUnauthenticatedExpirationTimer(resource);
} }
const { path } = URL.parse(url); const { path } = URL.parse(url);
@ -374,10 +378,15 @@ export class SocketManager extends EventListener {
window.log.info('SocketManager.onOffline'); window.log.info('SocketManager.onOffline');
this.isOffline = true; this.isOffline = true;
this.authenticated?.abort(); const { authenticated, unauthenticated } = this;
this.unauthenticated?.abort(); if (authenticated) {
this.authenticated = undefined; authenticated.abort();
this.unauthenticated = undefined; this.dropAuthenticated(authenticated);
}
if (unauthenticated) {
unauthenticated.abort();
this.dropUnauthenticated(unauthenticated);
}
} }
// //
@ -421,21 +430,7 @@ export class SocketManager extends EventListener {
window.log.info('SocketManager: connected unauthenticated socket'); window.log.info('SocketManager: connected unauthenticated socket');
let timer: NodeJS.Timeout | undefined = setTimeout(() => {
window.log.info(
'SocketManager: shutting down unauthenticated socket after timeout'
);
timer = undefined;
unauthenticated.shutdown();
this.dropUnauthenticated(process);
}, FIVE_MINUTES);
unauthenticated.addEventListener('close', ({ code, reason }): void => { unauthenticated.addEventListener('close', ({ code, reason }): void => {
if (timer !== undefined) {
clearTimeout(timer);
timer = undefined;
}
if (this.unauthenticated !== process) { if (this.unauthenticated !== process) {
return; return;
} }
@ -577,10 +572,9 @@ export class SocketManager extends EventListener {
private dropAuthenticated( private dropAuthenticated(
process: AbortableProcess<WebSocketResource> process: AbortableProcess<WebSocketResource>
): void { ): void {
strictAssert( if (this.authenticated !== process) {
this.authenticated === process, return;
'Authenticated resource mismatch' }
);
this.incomingRequestQueue = []; this.incomingRequestQueue = [];
this.authenticated = undefined; this.authenticated = undefined;
@ -590,11 +584,62 @@ export class SocketManager extends EventListener {
private dropUnauthenticated( private dropUnauthenticated(
process: AbortableProcess<WebSocketResource> process: AbortableProcess<WebSocketResource>
): void { ): void {
strictAssert( if (this.unauthenticated !== process) {
this.unauthenticated === process, return;
'Unauthenticated resource mismatch' }
);
this.unauthenticated = undefined; this.unauthenticated = undefined;
if (!this.unauthenticatedExpirationTimer) {
return;
}
clearTimeout(this.unauthenticatedExpirationTimer);
this.unauthenticatedExpirationTimer = undefined;
}
private async startUnauthenticatedExpirationTimer(
expected: WebSocketResource
): Promise<void> {
const process = this.unauthenticated;
strictAssert(
process !== undefined,
'Unauthenticated socket must be connected'
);
const unauthenticated = await process.getResult();
strictAssert(
unauthenticated === expected,
'Unauthenticated resource should be the same'
);
if (this.unauthenticatedExpirationTimer) {
return;
}
window.log.info(
'SocketManager: starting expiration timer for unauthenticated socket'
);
this.unauthenticatedExpirationTimer = setTimeout(async () => {
window.log.info(
'SocketManager: shutting down unauthenticated socket after timeout'
);
unauthenticated.shutdown();
// The socket is either deliberately closed or reconnected already
if (this.unauthenticated !== process) {
return;
}
this.dropUnauthenticated(process);
try {
await this.getUnauthenticatedResource();
} catch (error) {
window.log.warn(
'SocketManager: failed to reconnect unauthenticated socket ' +
`due to error: ${Errors.toLogFormat(error)}`
);
}
}, FIVE_MINUTES);
} }
private queueOrHandleRequest(req: IncomingWebSocketRequest): void { private queueOrHandleRequest(req: IncomingWebSocketRequest): void {