Refactor request listeners to use async functions

This commit is contained in:
Zaid Humaimidi committed 2023-08-09 19:51:40 -07:00
1 parent 58488ebc3d
commit 7b8a49a7ba
11 files changed
+148 -163

No files matched your search

+97 -94
View File
@@ -59,6 +59,7 @@ import { parseXmlDeclaration } from "./xml-parser";
import * as debug from "./debug";
import { getRequestOrigin } from "./forwarded";
import { getSocketEndpoints } from "./server";
import { EventEmitter } from "events";
const gzipPromisified = promisify(zlib.gzip);
const deflatePromisified = promisify(zlib.deflate);
@@ -889,86 +890,98 @@ async function sendAcsRequest(
return writeResponse(sessionContext, res);
}
// When socket closes, store active sessions in cache
export function onConnection(socket: Socket): void {
socket.on("close", async () => {
const sessionContext = currentSessions.get(socket);
if (!sessionContext) return;
currentSessions.delete(socket);
if (sessionContext.authState !== 2) {
logger.accessError({
message: "Authentication failure",
sessionContext: sessionContext,
});
return;
}
function once(
emitter: EventEmitter,
event: string,
timeout: number
): Promise<unknown[]> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`Event ${event} timed out after ${timeout} ms`));
}, timeout);
const now = Date.now();
const lastActivity = sessionContext.lastActivity;
const timeoutMsg = {
sessionContext: sessionContext,
message: "Session timeout",
sessionTimestamp: sessionContext.timestamp,
};
const timeout =
sessionContext.lastActivity + sessionContext.timeout * 1000 - now;
if (timeout <= 0) {
logger.accessError(timeoutMsg);
// TODO it's possible that lock would have already been expired
await endSession(sessionContext);
return;
}
setTimeout(async () => {
const sessionContextString = await cache.get(
`session_${sessionContext.sessionId}`
);
if (!sessionContextString) return;
const _sessionContext = await session.deserialize(sessionContextString);
if (_sessionContext.lastActivity === lastActivity) {
logger.accessError(timeoutMsg);
await endSession(sessionContext);
}
}, timeout + 1000).unref();
if (sessionContext.state === 0) return;
const sessionContextString = await session.serialize(sessionContext);
await cache.set(
`session_${sessionContext.sessionId}`,
sessionContextString,
Math.ceil(timeout / 1000) + 3
);
emitter.once(event, (...args: unknown[]) => {
clearTimeout(timer);
resolve(args);
});
});
}
export function onClientError(err: Error, socket: Socket): void {
const remoteAddress = getSocketEndpoints(socket).remoteAddress;
localCache
.getCurrentSnapshot()
.then((cacheSnapshot) => {
const debugEnabled = !!localCache.getConfig(
cacheSnapshot,
"cwmp.debug",
{
remoteAddress: remoteAddress,
},
Date.now(),
(e) => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
return remoteAddress;
return e;
}
);
function setTimeoutPromise(delay: number, ref = true): Promise<void> {
return new Promise((resolve) => {
const timerId = setTimeout(resolve, delay);
if (!ref) timerId.unref();
});
}
if (debugEnabled) debug.clientError(remoteAddress, err);
})
.catch((err) => {
throw err;
// When socket closes, store active sessions in cache
export async function onConnection(socket: Socket): Promise<void> {
await once(socket, "close", 300000);
const sessionContext = currentSessions.get(socket);
if (!sessionContext) return;
currentSessions.delete(socket);
if (sessionContext.authState !== 2) {
logger.accessError({
message: "Authentication failure",
sessionContext: sessionContext,
});
return;
}
const now = Date.now();
const lastActivity = sessionContext.lastActivity;
const timeoutMsg = {
sessionContext: sessionContext,
message: "Session timeout",
sessionTimestamp: sessionContext.timestamp,
};
const timeout =
sessionContext.lastActivity + sessionContext.timeout * 1000 - now;
if (timeout <= 0) {
logger.accessError(timeoutMsg);
// TODO it's possible that lock would have already been expired
await endSession(sessionContext);
return;
}
await cache.set(
`session_${sessionContext.sessionId}`,
await session.serialize(sessionContext),
Math.ceil(timeout / 1000) + 3
);
await setTimeoutPromise(timeout + 1000, false);
const sessionStr = await cache.get(`session_${sessionContext.sessionId}`);
if (!sessionStr) return;
const _sessionContext = await session.deserialize(sessionStr);
if (_sessionContext.lastActivity === lastActivity) {
logger.accessError(timeoutMsg);
await endSession(sessionContext);
}
}
export async function onClientError(err: Error, socket: Socket): Promise<void> {
const remoteAddress = getSocketEndpoints(socket).remoteAddress;
const cacheSnapshot = await localCache.getCurrentSnapshot();
const debugEnabled = !!localCache.getConfig(
cacheSnapshot,
"cwmp.debug",
{
remoteAddress: remoteAddress,
},
Date.now(),
(e) => {
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
return remoteAddress;
return e;
}
);
if (debugEnabled) debug.clientError(remoteAddress, err);
}
setInterval(() => {
@@ -1341,29 +1354,19 @@ async function processRequest(
}
}
export function listener(
export async function listener(
httpRequest: IncomingMessage,
httpResponse: ServerResponse
): void {
): Promise<void> {
stats.concurrentRequests += 1;
listenerAsync(httpRequest, httpResponse)
.then(() => {
stats.concurrentRequests -= 1;
})
.catch((err) => {
currentSessions.delete(httpRequest.socket);
stats.concurrentRequests -= 1;
setTimeout(() => {
throw err;
});
try {
httpRequest.socket.unref();
httpResponse.writeHead(500, { Connection: "close" });
httpResponse.end(`${err.name}: ${err.message}`);
} catch (err) {
// Ignore
}
});
try {
await listenerAsync(httpRequest, httpResponse);
} catch (err) {
currentSessions.delete(httpRequest.socket);
throw err;
} finally {
stats.concurrentRequests -= 1;
}
}
async function clientError(