Start unauthenticated socket timer after request
This commit is contained in:
parent
4371996362
commit
9012091d21
1 changed files with 74 additions and 29 deletions
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue