Typed caching middleware.

This commit is contained in:
SamTV12345 2024-01-22 22:15:46 +01:00
parent 1009b02b2a
commit cd98696d2b

View file

@ -36,32 +36,38 @@ const util = require('util');
* *
*/ */
// MIMIC https://github.com/microsoft/TypeScript/commit/9677b0641cc5ba7d8b701b4f892ed7e54ceaee9a - START
let _crypto;
try { const _crypto = require('crypto');
_crypto = require('crypto');
} catch {
_crypto = undefined;
}
let CACHE_DIR = path.join(settings.root, 'var/'); let CACHE_DIR = path.join(settings.root, 'var/');
CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined; CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined;
const responseCache = {}; type Headers = {
[id: string]: string
}
const djb2Hash = (data) => { type ResponseCache = {
[id: string]: {
statusCode: number
headers: Headers
}
}
const responseCache: ResponseCache = {};
const djb2Hash = (data: string) => {
const chars = data.split('').map((str) => str.charCodeAt(0)); const chars = data.split('').map((str) => str.charCodeAt(0));
return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`; return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`;
}; };
const generateCacheKeyWithSha256 = const generateCacheKeyWithSha256 =
(path) => _crypto.createHash('sha256').update(path).digest('hex'); (path: string) => _crypto.createHash('sha256').update(path).digest('hex');
const generateCacheKeyWithDjb2 = const generateCacheKeyWithDjb2 =
(path) => Buffer.from(djb2Hash(path)).toString('hex'); (path: string) => Buffer.from(djb2Hash(path)).toString('hex');
let generateCacheKey; let generateCacheKey: (path: string)=>string;
if (_crypto) { if (_crypto) {
generateCacheKey = generateCacheKeyWithSha256; generateCacheKey = generateCacheKeyWithSha256;
@ -79,17 +85,17 @@ if (_crypto) {
*/ */
module.exports = class CachingMiddleware { module.exports = class CachingMiddleware {
handle(req, res, next) { handle(req: any, res: any, next: any) {
this._handle(req, res, next).catch((err) => next(err || new Error(err))); this._handle(req, res, next).catch((err) => next(err || new Error(err)));
} }
async _handle(req, res, next) { async _handle(req: any, res: any, next: any) {
if (!(req.method === 'GET' || req.method === 'HEAD') || !CACHE_DIR) { if (!(req.method === 'GET' || req.method === 'HEAD') || !CACHE_DIR) {
return next(undefined, req, res); return next(undefined, req, res);
} }
const oldReq = {}; const oldReq:ResponseCache = {};
const oldRes = {}; const oldRes:ResponseCache = {};
const supportsGzip = const supportsGzip =
(req.get('Accept-Encoding') || '').indexOf('gzip') !== -1; (req.get('Accept-Encoding') || '').indexOf('gzip') !== -1;
@ -119,7 +125,7 @@ module.exports = class CachingMiddleware {
res.write = oldRes.write || res.write; res.write = oldRes.write || res.write;
res.end = oldRes.end || res.end; res.end = oldRes.end || res.end;
const headers = {}; const headers: Headers = {};
Object.assign(headers, (responseCache[cacheKey].headers || {})); Object.assign(headers, (responseCache[cacheKey].headers || {}));
const statusCode = responseCache[cacheKey].statusCode; const statusCode = responseCache[cacheKey].statusCode;
@ -150,18 +156,19 @@ module.exports = class CachingMiddleware {
return respond(); return respond();
} }
const _headers = {}; const _headers:Headers = {};
oldRes.setHeader = res.setHeader; oldRes.setHeader = res.setHeader;
res.setHeader = (key, value) => { res.setHeader = (key: string, value: string) => {
// Don't set cookies, see issue #707 // Don't set cookies, see issue #707
if (key.toLowerCase() === 'set-cookie') return; if (key.toLowerCase() === 'set-cookie') return;
_headers[key.toLowerCase()] = value; _headers[key.toLowerCase()] = value;
// @ts-ignore
oldRes.setHeader.call(res, key, value); oldRes.setHeader.call(res, key, value);
}; };
oldRes.writeHead = res.writeHead; oldRes.writeHead = res.writeHead;
res.writeHead = (status, headers) => { res.writeHead = (status: number, headers: Headers) => {
res.writeHead = oldRes.writeHead; res.writeHead = oldRes.writeHead;
if (status === 200) { if (status === 200) {
// Update cache // Update cache
@ -174,14 +181,14 @@ module.exports = class CachingMiddleware {
oldRes.write = res.write; oldRes.write = res.write;
oldRes.end = res.end; oldRes.end = res.end;
res.write = (data, encoding) => { res.write = (data: number, encoding: number) => {
buffer += data.toString(encoding); buffer += data.toString(encoding);
}; };
res.end = async (data, encoding) => { res.end = async (data: number, encoding: number) => {
await Promise.all([ await Promise.all([
fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}`, buffer).catch(() => {}), fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}`, buffer).catch(() => {}),
util.promisify(zlib.gzip)(buffer) util.promisify(zlib.gzip)(buffer)
.then((content) => fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}.gz`, content)) .then((content: string) => fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}.gz`, content))
.catch(() => {}), .catch(() => {}),
]); ]);
responseCache[cacheKey] = {statusCode: status, headers}; responseCache[cacheKey] = {statusCode: status, headers};
@ -191,8 +198,8 @@ module.exports = class CachingMiddleware {
// Nothing new changed from the cached version. // Nothing new changed from the cached version.
oldRes.write = res.write; oldRes.write = res.write;
oldRes.end = res.end; oldRes.end = res.end;
res.write = (data, encoding) => {}; res.write = (data: number, encoding: number) => {};
res.end = (data, encoding) => { respond(); }; res.end = (data: number, encoding: number) => { respond(); };
} else { } else {
res.writeHead(status, headers); res.writeHead(status, headers);
} }