typedserver/ts/servertools/classes.handlerstatic.ts

143 lines
4.6 KiB
TypeScript
Raw Normal View History

2024-02-20 16:30:46 +00:00
import * as plugins from '../plugins.js';
2023-03-30 13:15:48 +00:00
import * as interfaces from '../interfaces/index.js';
import { Handler } from './classes.handler.js';
2024-01-09 09:14:06 +00:00
import { Compressor, type TCompressionMethod, type ICompressionResult } from './classes.compressor.js';
2023-03-30 13:15:48 +00:00
export class HandlerStatic extends Handler {
2024-01-07 13:50:14 +00:00
public compressor = new Compressor();
2023-03-30 13:15:48 +00:00
constructor(
pathArg: string,
optionsArg?: {
requestModifier?: interfaces.TRequestModifier;
responseModifier?: interfaces.TResponseModifier;
headers?: { [key: string]: string };
serveIndexHtmlDefault?: boolean;
2024-01-09 09:14:06 +00:00
enableCompression?: boolean;
2024-01-09 09:25:03 +00:00
preferredCompressionMethod?: TCompressionMethod;
2023-03-30 13:15:48 +00:00
}
) {
super('GET', async (req, res) => {
let requestPath = req.path;
let requestHeaders = req.headers;
let requestBody = req.body;
let travelData: unknown;
if (optionsArg && optionsArg.requestModifier) {
const modifiedRequest = await optionsArg.requestModifier({
headers: requestHeaders,
path: requestPath,
body: requestBody,
});
requestHeaders = modifiedRequest.headers;
requestPath = modifiedRequest.path;
requestBody = modifiedRequest.body;
travelData = modifiedRequest.travelData;
}
// lets compute some paths
let filePath: string = requestPath.slice(req.route.path.length - 1); // lets slice of the root
if (requestPath === '') {
console.log('replaced root with index.html');
filePath = 'index.html';
}
console.log(filePath);
const joinedPath = plugins.path.join(pathArg, filePath);
const defaultPath = plugins.path.join(pathArg, 'index.html');
let parsedPath = plugins.path.parse(joinedPath);
let usedPath: string;
// important security checks
if (
requestPath.includes('..') || // don't allow going up the filePath
requestPath.includes('~') || // don't allow referencing of home directory
!joinedPath.startsWith(pathArg) // make sure the joined path is within the directory
) {
res.writeHead(500);
res.end();
return;
}
// set additional headers
if (optionsArg && optionsArg.headers) {
for (const key of Object.keys(optionsArg.headers)) {
res.set(key, optionsArg.headers[key]);
}
}
// lets actually care about serving, if security checks pass
2024-01-07 13:50:14 +00:00
let fileBuffer: Buffer;
2023-03-30 13:15:48 +00:00
try {
2024-01-07 13:50:14 +00:00
fileBuffer = plugins.smartfile.fs.toBufferSync(joinedPath);
2023-03-30 13:15:48 +00:00
usedPath = joinedPath;
} catch (err) {
// try serving index.html instead
console.log(`could not resolve ${joinedPath}`);
if (optionsArg && optionsArg.serveIndexHtmlDefault) {
console.log(`serving default path ${defaultPath} instead of ${joinedPath}`);
try {
parsedPath = plugins.path.parse(defaultPath);
2024-01-07 13:50:14 +00:00
fileBuffer = plugins.smartfile.fs.toBufferSync(defaultPath);
2023-03-30 13:15:48 +00:00
usedPath = defaultPath;
} catch (err) {
res.writeHead(500);
res.end('File not found!');
return;
}
} else {
res.writeHead(500);
res.end('File not found!');
return;
}
}
res.type(parsedPath.ext);
const headers = res.getHeaders();
// lets modify the response at last
if (optionsArg && optionsArg.responseModifier) {
const modifiedResponse = await optionsArg.responseModifier({
headers: res.getHeaders(),
path: usedPath,
2024-01-07 13:50:14 +00:00
responseContent: fileBuffer,
2023-03-30 13:15:48 +00:00
travelData,
});
// headers
for (const key of Object.keys(res.getHeaders())) {
if (!modifiedResponse.headers[key]) {
res.removeHeader(key);
}
}
for (const key of Object.keys(modifiedResponse.headers)) {
res.setHeader(key, modifiedResponse.headers[key]);
}
// responseContent
2024-01-07 13:50:14 +00:00
fileBuffer = modifiedResponse.responseContent;
2023-03-30 13:15:48 +00:00
}
2024-01-07 13:50:14 +00:00
// lets finally deal with compression
2024-01-09 09:14:06 +00:00
let compressionResult: ICompressionResult;
if (optionsArg && optionsArg.enableCompression) {
2024-01-09 10:38:06 +00:00
compressionResult = await this.compressor.maybeCompress(requestHeaders, fileBuffer, [optionsArg.preferredCompressionMethod]);
2024-01-19 19:51:59 +00:00
} else {
compressionResult = {
compressionMethod: 'none',
result: fileBuffer,
};
2024-01-09 09:14:06 +00:00
}
2024-01-07 13:50:14 +00:00
2023-03-30 13:15:48 +00:00
res.status(200);
2024-01-09 09:14:06 +00:00
if (compressionResult?.compressionMethod) {
res.header('Content-Encoding', compressionResult.compressionMethod);
}
2024-01-07 13:50:14 +00:00
res.write(compressionResult.result);
2023-03-30 13:15:48 +00:00
res.end();
});
}
}