2024-08-23 19:58:15 +02:00
|
|
|
const http = require("http");
|
|
|
|
const fs = require("fs");
|
2024-08-24 19:41:13 +02:00
|
|
|
const generateServerString = require("./utils/generateServerString.js");
|
2024-08-24 19:47:48 +02:00
|
|
|
const deleteFolderRecursive = require("./utils/deleteFolderRecursive.js");
|
2024-08-24 19:41:13 +02:00
|
|
|
const svrjsInfo = require("../svrjs.json");
|
|
|
|
const {version} = svrjsInfo;
|
2024-08-23 19:58:15 +02:00
|
|
|
|
2024-08-23 21:58:26 +02:00
|
|
|
let inspector = undefined;
|
|
|
|
try {
|
|
|
|
inspector = require("inspector");
|
|
|
|
} catch (err) {
|
|
|
|
// Don't use inspector
|
|
|
|
}
|
|
|
|
|
2024-08-24 19:29:12 +02:00
|
|
|
process.dirname = __dirname;
|
|
|
|
|
2024-08-24 19:41:13 +02:00
|
|
|
// TODO: after implementing clustering in new SVR.JS
|
|
|
|
//process.singleThreaded = false;
|
|
|
|
process.singleThreaded = true;
|
|
|
|
|
|
|
|
if (process.versions) process.versions.svrjs = version; // Inject SVR.JS into process.versions
|
|
|
|
|
|
|
|
let forceSecure = false;
|
|
|
|
let disableMods = false;
|
|
|
|
|
2024-08-24 19:47:48 +02:00
|
|
|
// Handle command line arguments
|
2024-08-24 19:41:13 +02:00
|
|
|
const args = process.argv;
|
|
|
|
for (let i = (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1); i < args.length; i++) {
|
|
|
|
if (args[i] == "-h" || args[i] == "--help" || args[i] == "-?" || args[i] == "/h" || args[i] == "/?") {
|
|
|
|
console.log("SVR.JS usage:");
|
|
|
|
console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]");
|
|
|
|
console.log("-h -? /h /? --help -- Displays help");
|
|
|
|
console.log("--clean -- Cleans up files created by SVR.JS");
|
|
|
|
console.log("--reset -- Resets SVR.JS to default settings (WARNING: DANGEROUS)");
|
|
|
|
console.log("--secure -- Runs HTTPS server");
|
|
|
|
console.log("--disable-mods -- Disables mods (safe mode)");
|
|
|
|
console.log("--single-threaded -- Run single-threaded");
|
|
|
|
console.log("-v --version -- Display server version");
|
|
|
|
process.exit(0);
|
|
|
|
} else if (args[i] == "--secure") {
|
|
|
|
forceSecure = true;
|
|
|
|
} else if (args[i] == "-v" || args[i] == "--version") {
|
|
|
|
console.log(generateServerString(true));
|
|
|
|
process.exit(0);
|
|
|
|
} else if (args[i] == "--clean") {
|
|
|
|
console.log("Removing logs...");
|
|
|
|
deleteFolderRecursive(process.dirname + "/log");
|
|
|
|
fs.mkdirSync(process.dirname + "/log");
|
|
|
|
console.log("Removing temp folder...");
|
|
|
|
deleteFolderRecursive(process.dirname + "/temp");
|
|
|
|
fs.mkdirSync(process.dirname + "/temp");
|
|
|
|
console.log("Done!");
|
|
|
|
process.exit(0);
|
|
|
|
} else if (args[i] == "--reset") {
|
|
|
|
console.log("Removing logs...");
|
|
|
|
deleteFolderRecursive(process.dirname + "/log");
|
|
|
|
fs.mkdirSync(process.dirname + "/log");
|
|
|
|
console.log("Removing temp folder...");
|
|
|
|
deleteFolderRecursive(process.dirname + "/temp");
|
|
|
|
fs.mkdirSync(process.dirname + "/temp");
|
|
|
|
console.log("Removing configuration file...");
|
2024-08-24 19:49:36 +02:00
|
|
|
fs.unlinkSync(process.dirname + "/config.json");
|
2024-08-24 19:41:13 +02:00
|
|
|
console.log("Done!");
|
|
|
|
process.exit(0);
|
|
|
|
} else if (args[i] == "--disable-mods") {
|
|
|
|
disableMods = true;
|
|
|
|
} else if (args[i] == "--single-threaded") {
|
|
|
|
process.singlethreaded = true;
|
|
|
|
} else {
|
|
|
|
console.log("Unrecognized argument: " + args[i]);
|
|
|
|
console.log("SVR.JS usage:");
|
|
|
|
console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]");
|
|
|
|
console.log("-h -? /h /? --help -- Displays help");
|
|
|
|
console.log("--clean -- Cleans up files created by SVR.JS");
|
|
|
|
console.log("--reset -- Resets SVR.JS to default settings (WARNING: DANGEROUS)");
|
|
|
|
console.log("--secure -- Runs HTTPS server");
|
|
|
|
console.log("--disable-mods -- Disables mods (safe mode)");
|
|
|
|
console.log("--single-threaded -- Run single-threaded");
|
|
|
|
console.log("-v --version -- Display server version");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-23 19:58:15 +02:00
|
|
|
// Create log, mods and temp directories, if they don't exist.
|
2024-08-24 19:29:12 +02:00
|
|
|
if (!fs.existsSync(process.dirname + "/log")) fs.mkdirSync(process.dirname + "/log");
|
|
|
|
if (!fs.existsSync(process.dirname + "/mods")) fs.mkdirSync(process.dirname + "/mods");
|
|
|
|
if (!fs.existsSync(process.dirname + "/temp")) fs.mkdirSync(process.dirname + "/temp");
|
2024-08-23 19:58:15 +02:00
|
|
|
|
2024-08-24 16:54:28 +02:00
|
|
|
const cluster = require("./utils/clusterBunShim.js"); // Cluster module with shim for Bun
|
2024-08-24 19:30:25 +02:00
|
|
|
//const generateErrorStack = require("./utils/generateErrorStack.js");
|
|
|
|
//const getOS = require("./utils/getOS.js");
|
|
|
|
//const parseURL = require("./utils/urlParser.js");
|
|
|
|
//const fixNodeMojibakeURL = require("./utils/urlMojibakeFixer.js");
|
2024-08-24 16:54:28 +02:00
|
|
|
|
2024-08-24 16:55:44 +02:00
|
|
|
process.serverConfig = {};
|
|
|
|
|
|
|
|
// TODO: configuration from config.json
|
|
|
|
if (process.serverConfig.users === undefined) process.serverConfig.users = [];
|
2024-08-24 19:41:13 +02:00
|
|
|
if (process.serverConfig.secure === undefined) process.serverConfig.secure = false;
|
|
|
|
if (forceSecure) process.serverConfig.secure = true;
|
2024-08-24 16:55:44 +02:00
|
|
|
if (process.serverConfig.secure) {
|
|
|
|
if (process.serverConfig.key === undefined) process.serverConfig.key = "cert/key.key";
|
|
|
|
if (process.serverConfig.cert === undefined) process.serverConfig.cert = "cert/cert.crt";
|
|
|
|
if (process.serverConfig.sport === undefined) process.serverConfig.sport = 443;
|
|
|
|
if (process.serverConfig.spubport === undefined) process.serverConfig.spubport = 443;
|
|
|
|
if (process.serverConfig.sni === undefined) process.serverConfig.sni = {};
|
|
|
|
if (process.serverConfig.enableOCSPStapling === undefined) process.serverConfig.enableOCSPStapling = false;
|
2024-08-24 17:44:25 +02:00
|
|
|
|
2024-08-24 16:55:44 +02:00
|
|
|
}
|
|
|
|
if (process.serverConfig.port === undefined) process.serverConfig.port = 80;
|
|
|
|
if (process.serverConfig.pubport === undefined) process.serverConfig.pubport = 80;
|
|
|
|
if (process.serverConfig.domain === undefined && process.serverConfig.domian !== undefined) process.serverConfig.domain = process.serverConfig.domian;
|
|
|
|
delete process.serverConfig.domian;
|
|
|
|
if (process.serverConfig.page404 === undefined) process.serverConfig.page404 = "404.html";
|
|
|
|
//process.serverConfig.timestamp = timestamp; //TODO
|
|
|
|
//process.serverConfig.blacklist = blocklist.raw; //TODO
|
|
|
|
if (process.serverConfig.nonStandardCodes === undefined) process.serverConfig.nonStandardCodes = [];
|
|
|
|
if (process.serverConfig.enableCompression === undefined) process.serverConfig.enableCompression = true;
|
|
|
|
if (process.serverConfig.customHeaders === undefined) process.serverConfig.customHeaders = {};
|
|
|
|
if (process.serverConfig.enableHTTP2 === undefined) process.serverConfig.enableHTTP2 = false;
|
|
|
|
if (process.serverConfig.enableLogging === undefined) process.serverConfig.enableLogging = true;
|
|
|
|
if (process.serverConfig.enableDirectoryListing === undefined) process.serverConfig.enableDirectoryListing = true;
|
|
|
|
if (process.serverConfig.enableDirectoryListingWithDefaultHead === undefined) process.serverConfig.enableDirectoryListingWithDefaultHead = false;
|
|
|
|
if (process.serverConfig.serverAdministratorEmail === undefined) process.serverConfig.serverAdministratorEmail = "[no contact information]";
|
|
|
|
if (process.serverConfig.stackHidden === undefined) process.serverConfig.stackHidden = false;
|
|
|
|
if (process.serverConfig.enableRemoteLogBrowsing === undefined) process.serverConfig.enableRemoteLogBrowsing = false;
|
|
|
|
if (process.serverConfig.exposeServerVersion === undefined) process.serverConfig.exposeServerVersion = true;
|
|
|
|
if (process.serverConfig.disableServerSideScriptExpose === undefined) process.serverConfig.disableServerSideScriptExpose = true;
|
|
|
|
if (process.serverConfig.allowStatus === undefined) process.serverConfig.allowStatus = true;
|
|
|
|
if (process.serverConfig.rewriteMap === undefined) process.serverConfig.rewriteMap = [];
|
|
|
|
if (process.serverConfig.dontCompress === undefined) process.serverConfig.dontCompress = ["/.*\\.ipxe$/", "/.*\\.(?:jpe?g|png|bmp|tiff|jfif|gif|webp)$/", "/.*\\.(?:[id]mg|iso|flp)$/", "/.*\\.(?:zip|rar|bz2|[gb7x]z|lzma|tar)$/", "/.*\\.(?:mp[34]|mov|wm[av]|avi|webm|og[gv]|mk[va])$/"];
|
|
|
|
if (process.serverConfig.enableIPSpoofing === undefined) process.serverConfig.enableIPSpoofing = false;
|
|
|
|
if (process.serverConfig.disableNonEncryptedServer === undefined) process.serverConfig.disableNonEncryptedServer = false;
|
|
|
|
if (process.serverConfig.disableToHTTPSRedirect === undefined) process.serverConfig.disableToHTTPSRedirect = false;
|
|
|
|
if (process.serverConfig.enableETag === undefined) process.serverConfig.enableETag = true;
|
|
|
|
if (process.serverConfig.disableUnusedWorkerTermination === undefined) process.serverConfig.disableUnusedWorkerTermination = false;
|
|
|
|
if (process.serverConfig.rewriteDirtyURLs === undefined) process.serverConfig.rewriteDirtyURLs = false;
|
|
|
|
if (process.serverConfig.errorPages === undefined) process.serverConfig.errorPages = [];
|
|
|
|
if (process.serverConfig.useWebRootServerSideScript === undefined) process.serverConfig.useWebRootServerSideScript = true;
|
|
|
|
if (process.serverConfig.exposeModsInErrorPages === undefined) process.serverConfig.exposeModsInErrorPages = true;
|
|
|
|
if (process.serverConfig.disableTrailingSlashRedirects === undefined) process.serverConfig.disableTrailingSlashRedirects = false;
|
|
|
|
if (process.serverConfig.environmentVariables === undefined) process.serverConfig.environmentVariables = {};
|
2024-08-24 17:44:25 +02:00
|
|
|
if (process.serverConfig.customHeadersVHost === undefined) process.serverConfig.customHeadersVHost = [];
|
|
|
|
if (process.serverConfig.enableDirectoryListingVHost === undefined) process.serverConfig.enableDirectoryListingVHost = [];
|
2024-08-24 16:55:44 +02:00
|
|
|
if (process.serverConfig.wwwrootPostfixesVHost === undefined) process.serverConfig.wwwrootPostfixesVHost = [];
|
|
|
|
if (process.serverConfig.wwwrootPostfixPrefixesVHost === undefined) process.serverConfig.wwwrootPostfixPrefixesVHost = [];
|
|
|
|
if (process.serverConfig.allowDoubleSlashes === undefined) process.serverConfig.allowDoubleSlashes = false;
|
|
|
|
if (process.serverConfig.allowPostfixDoubleSlashes === undefined) process.serverConfig.allowPostfixDoubleSlashes = false;
|
|
|
|
if (process.serverConfig.optOutOfStatisticsServer === undefined) process.serverConfig.optOutOfStatisticsServer = false;
|
|
|
|
|
|
|
|
process.serverConfig.version = version; // Compatiblity for very old SVR.JS mods
|
|
|
|
|
2024-08-24 16:58:27 +02:00
|
|
|
const serverconsole = require("./utils/serverconsole.js");
|
2024-08-23 19:58:15 +02:00
|
|
|
|
2024-08-23 21:58:26 +02:00
|
|
|
let inspectorURL = undefined;
|
|
|
|
try {
|
|
|
|
if (inspector) {
|
|
|
|
inspectorURL = inspector.url();
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
// Failed to get inspector URL
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!process.stdout.isTTY && !inspectorURL) {
|
|
|
|
// When stdout is not a terminal and not attached to an Node.JS inspector, disable it to improve performance of SVR.JS
|
|
|
|
console.log = function () {};
|
|
|
|
process.stdout.write = function () {};
|
|
|
|
process.stdout._write = function () {};
|
|
|
|
process.stdout._writev = function () {};
|
|
|
|
}
|
|
|
|
|
2024-08-23 21:33:26 +02:00
|
|
|
var wwwrootError = null;
|
|
|
|
try {
|
2024-08-24 19:29:12 +02:00
|
|
|
if (cluster.isPrimary || cluster.isPrimary === undefined) process.chdir(process.serverConfig.wwwroot != undefined ? process.serverConfig.wwwroot : process.dirname);
|
2024-08-23 21:33:26 +02:00
|
|
|
} catch (err) {
|
|
|
|
wwwrootError = err;
|
|
|
|
}
|
2024-08-23 20:43:09 +02:00
|
|
|
|
|
|
|
let middleware = [
|
2024-08-23 21:25:23 +02:00
|
|
|
require("./middleware/core.js"),
|
2024-08-23 22:13:58 +02:00
|
|
|
require("./middleware/urlSanitizer.js"),
|
2024-08-24 08:02:11 +02:00
|
|
|
require("./middleware/redirects.js"),
|
2024-08-24 17:32:27 +02:00
|
|
|
// TODO: blocklist
|
2024-08-24 08:22:49 +02:00
|
|
|
require("./middleware/webRootPostfixes.js"),
|
2024-08-24 17:32:27 +02:00
|
|
|
require("./middleware/rewriteURL.js"),
|
2024-08-24 17:44:25 +02:00
|
|
|
require("./middleware/responseHeaders.js"),
|
2024-08-24 20:32:06 +02:00
|
|
|
require("./middleware/checkForbiddenPaths.js"),
|
|
|
|
require("./middleware/nonStandardCodesAndHttpAuthentication.js")
|
2024-08-23 20:43:09 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
function addMiddleware(mw) {
|
|
|
|
middleware.push(mw);
|
2024-08-23 19:58:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function requestHandler(req, res) {
|
|
|
|
let reqIdInt = Math.floor(Math.random() * 16777216);
|
|
|
|
if (reqIdInt == 16777216) reqIdInt = 0;
|
|
|
|
const reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
|
|
|
|
|
|
|
|
// SVR.JS log facilities
|
|
|
|
const logFacilities = {
|
|
|
|
climessage: (msg) => serverconsole.climessage(msg, reqId),
|
|
|
|
reqmessage: (msg) => serverconsole.reqmessage(msg, reqId),
|
|
|
|
resmessage: (msg) => serverconsole.resmessage(msg, reqId),
|
|
|
|
errmessage: (msg) => serverconsole.errmessage(msg, reqId),
|
|
|
|
locerrmessage: (msg) => serverconsole.locerrmessage(msg, reqId),
|
|
|
|
locwarnmessage: (msg) => serverconsole.locwarnmessage(msg, reqId),
|
|
|
|
locmessage: (msg) => serverconsole.locmessage(msg, reqId)
|
|
|
|
};
|
|
|
|
|
2024-08-23 20:43:09 +02:00
|
|
|
// SVR.JS configuration object (modified)
|
2024-08-24 16:55:44 +02:00
|
|
|
const config = Object.assign(process.serverConfig);
|
2024-08-23 19:58:15 +02:00
|
|
|
|
2024-08-23 20:43:09 +02:00
|
|
|
let index = 0;
|
2024-08-23 19:58:15 +02:00
|
|
|
|
2024-08-23 20:43:09 +02:00
|
|
|
// Call the next middleware function
|
|
|
|
const next = () => {
|
|
|
|
const currentMiddleware = middleware[index++];
|
|
|
|
if (currentMiddleware) {
|
|
|
|
try {
|
|
|
|
currentMiddleware(req, res, logFacilities, config, next);
|
|
|
|
} catch (err) {
|
2024-08-23 21:25:23 +02:00
|
|
|
if (res.error) res.error(500, err);
|
2024-08-23 19:58:15 +02:00
|
|
|
else {
|
2024-08-23 21:25:23 +02:00
|
|
|
logFacilities.errmessage("There was an error while processing the request!");
|
|
|
|
logFacilities.errmessage("Stack:");
|
|
|
|
logFacilities.errmessage(err.stack);
|
2024-08-23 20:43:09 +02:00
|
|
|
res.writeHead(500, "Internal Server Error", {
|
2024-08-24 17:06:34 +02:00
|
|
|
Server: generateServerString(config.exposeServerVersion)
|
2024-08-23 20:43:09 +02:00
|
|
|
});
|
|
|
|
res.end("Error while executing the request handler");
|
2024-08-23 19:58:15 +02:00
|
|
|
}
|
|
|
|
}
|
2024-08-23 20:43:09 +02:00
|
|
|
} else {
|
|
|
|
if (res.error) res.error(404);
|
|
|
|
else {
|
|
|
|
res.writeHead(404, "Not Found", {
|
2024-08-24 17:06:34 +02:00
|
|
|
Server: generateServerString(config.exposeServerVersion)
|
2024-08-23 19:58:15 +02:00
|
|
|
});
|
2024-08-23 20:43:09 +02:00
|
|
|
res.end("Request handler missing");
|
2024-08-23 19:58:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-23 20:43:09 +02:00
|
|
|
// Handle middleware
|
|
|
|
next();
|
2024-08-23 19:58:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create HTTP server
|
|
|
|
http.createServer(requestHandler).listen(3000);
|
2024-08-23 21:34:58 +02:00
|
|
|
|
2024-08-23 21:35:58 +02:00
|
|
|
if(wwwrootError) throw wwwrootError;
|