Remove blocking calls from custom head and custom foot functionality. Also optimize...
[svrjs.git] / svr.js
blob919e2c6a857f957458ab78dd971b71cdc0b7c6c4
1 // SVR.JS - a web server running on Node.JS
3 /*
4  * MIT License
5  *
6  * Copyright (c) 2018-2024 SVR.JS
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13  */
15 // Check if SVR.JS is running on Node.JS-compatible runtime. If not, just error it out.
16 if (typeof require === "undefined") {
17   if (typeof ActiveXObject !== "undefined" && typeof WScript !== "undefined") {
18     // If it runs on Windows Script Host, display an error message.
19     var shell = new ActiveXObject("WScript.Shell");
20     shell.Popup("SVR.JS doesn't work on Windows Script Host. SVR.JS requires use of Node.JS (or compatible JS runtime).", undefined, "Can't start SVR.JS", 16);
21     WScript.quit();
22   } else {
23     if (typeof alert !== "undefined" && typeof document !== "undefined") {
24       // If it runs on web browser, display an alert.
25       alert("SVR.JS doesn't work on a web browser. SVR.JS requires use of Node.JS (or compatible JS runtime).");
26     }
27     // If it's not, throw an error.
28     if (typeof document !== "undefined") {
29       throw new Error("SVR.JS doesn't work on a web browser. SVR.JS requires use of Node.JS (or compatible JS runtime).");
30     } else {
31       throw new Error("SVR.JS doesn't work on Deno/QuickJS. SVR.JS requires use of Node.JS (or compatible JS runtime).");
32     }
33   }
36 var secure = false;
37 var disableMods = false;
39 // ASCII art SVR.JS logo ;)
40 var logo = ["", "", "", "            \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", "          &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", "         &&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m((\x1b[38;5;002m&&", "         \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;015m   \x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m((\x1b[38;5;002m&&", "         \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;015m   \x1b[38;5;243m(\x1b[38;5;015m   \x1b[38;5;243m(\x1b[38;5;015m   \x1b[38;5;243m((\x1b[38;5;002m&&", "         \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", "         \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", "         \x1b[38;5;002m&&&&&&&&\x1b[38;5;010m#########################################\x1b[38;5;002m&&&&&&&&", "         \x1b[38;5;002m&&&&&\x1b[38;5;010m###############################################\x1b[38;5;002m&&&&&", "         \x1b[38;5;002m&&&\x1b[38;5;010m###################################################\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&\x1b[38;5;010m####\x1b[38;5;016m@@@@@@\x1b[38;5;010m#\x1b[38;5;016m@@@\x1b[38;5;010m###\x1b[38;5;016m@@@\x1b[38;5;010m#\x1b[38;5;016m@@@@@@@\x1b[38;5;010m###########\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@@@@@\x1b[38;5;010m####\x1b[38;5;002m&&", "         \x1b[38;5;002m&&\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m#######\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m##########\x1b[38;5;016m@@\x1b[38;5;010m#\x1b[38;5;016m@@\x1b[38;5;010m#########\x1b[38;5;002m&&", "         \x1b[38;5;002m&&\x1b[38;5;010m######\x1b[38;5;040m#\x1b[38;5;016m@@@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m#\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@@@@@\x1b[38;5;010m#######\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;040m#\x1b[38;5;016m@@@@\x1b[38;5;010m###\x1b[38;5;002m&&", "         \x1b[38;5;002m&&\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;034m%\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;034m%\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;002m&&", "         \x1b[38;5;002m&&\x1b[38;5;010m#####################################################\x1b[38;5;002m&&", "         \x1b[38;5;002m&&&\x1b[38;5;010m###################################################\x1b[38;5;002m&&&", "         \x1b[38;5;002m&&&&&\x1b[38;5;010m###############################################\x1b[38;5;002m&&&&&", "         \x1b[38;5;002m&&&&&&&&\x1b[38;5;010m#########################################\x1b[38;5;002m&&&&&&&&", "         \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", "          &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", "            &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", "                                  \x1b[38;5;246m///////", "                                  ///////", "                                 \x1b[38;5;208m((((/))))", "                                \x1b[38;5;208m(((((/)))))", "            \x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m/\x1b[38;5;208m(((((/)))))\x1b[38;5;246m//\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m/", "           //\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m/\x1b[38;5;208m(((((/)))))\x1b[38;5;246m//\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m//", "           *\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;208m(((((/)))))\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*", "                                 \x1b[38;5;208m((((/))))", "", "", "", "\x1b[0m"];
42 var fs = require("fs");
44 function factoryReset() {
45   console.log("Removing logs...");
46   deleteFolderRecursive(__dirname + "/log");
47   fs.mkdirSync(__dirname + "/log");
48   console.log("Removing temp folder...");
49   deleteFolderRecursive(__dirname + "/temp");
50   fs.mkdirSync(__dirname + "/temp");
51   console.log("Removing configuration file...");
52   fs.unlinkSync("config.json");
53   console.log("Done!");
54   process.exit(0);
57 function deleteFolderRecursive(path) {
58   if (fs.existsSync(path)) {
59     fs.readdirSync(path).forEach(function (file) {
60       var curPath = path + "/" + file;
61       if (fs.statSync(curPath).isDirectory()) { // recurse
62         deleteFolderRecursive(curPath);
63       } else { // delete file
64         fs.unlinkSync(curPath);
65       }
66     });
67     fs.rmdirSync(path);
68   }
71 var os = require("os");
72 var version = "Nightly-GitMain";
73 var singlethreaded = false;
75 if (process.versions) process.versions.svrjs = version; // Inject SVR.JS into process.versions
77 var args = process.argv;
78 for (var i = (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1); i < args.length; i++) {
79   if (args[i] == "-h" || args[i] == "--help" || args[i] == "-?" || args[i] == "/h" || args[i] == "/?") {
80     console.log("SVR.JS usage:");
81     console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]");
82     console.log("-h -? /h /? --help    -- Displays help");
83     console.log("--clean               -- Cleans up files created by SVR.JS");
84     console.log("--reset               -- Resets SVR.JS to default settings (WARNING: DANGEROUS)");
85     console.log("--secure              -- Runs HTTPS server");
86     console.log("--disable-mods        -- Disables mods (safe mode)");
87     console.log("--single-threaded     -- Run single-threaded");
88     console.log("-v --version          -- Display server version");
89     process.exit(0);
90   } else if (args[i] == "--secure") {
91     secure = true;
92   } else if (args[i] == "-v" || args[i] == "--version") {
93     console.log("SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")");
94     process.exit(0);
95   } else if (args[i] == "--clean") {
96     console.log("Removing logs...");
97     deleteFolderRecursive(__dirname + "/log");
98     fs.mkdirSync(__dirname + "/log");
99     console.log("Removing temp folder...");
100     deleteFolderRecursive(__dirname + "/temp");
101     fs.mkdirSync(__dirname + "/temp");
102     console.log("Done!");
103     process.exit(0);
104   } else if (args[i] == "--reset") {
105     factoryReset();
106   } else if (args[i] == "--disable-mods") {
107     disableMods = true;
108   } else if (args[i] == "--single-threaded") {
109     singlethreaded = true;
110   } else {
111     console.log("Unrecognized argument: " + args[i]);
112     console.log("SVR.JS usage:");
113     console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]");
114     console.log("-h -? /h /? --help    -- Displays help");
115     console.log("--clean               -- Cleans up files created by SVR.JS");
116     console.log("--reset               -- Resets SVR.JS to default settings (WARNING: DANGEROUS)");
117     console.log("--secure              -- Runs HTTPS server");
118     console.log("--disable-mods        -- Disables mods (safe mode)");
119     console.log("--single-threaded     -- Run single-threaded");
120     console.log("-v --version          -- Display server version");
121     process.exit(1);
122   }
125 var readline = require("readline");
126 var net = require("net");
127 var cluster = {};
128 if (!singlethreaded) {
129   try {
130     // Import cluster module
131     var cluster = require("cluster");
132   } catch (err) {
133     // Clustering is not supported!
134   }
136   // Cluster & IPC shim for Bun
138   cluster.bunShim = function () {
139     cluster.isMaster = !process.env.NODE_UNIQUE_ID;
140     cluster.isPrimary = cluster.isMaster;
141     cluster.isWorker = !cluster.isMaster;
142     cluster.__shimmed__ = true;
144     if (cluster.isWorker) {
145       // Shim the cluster.worker object for worker processes
146       cluster.worker = {
147         id: parseInt(process.env.NODE_UNIQUE_ID),
148         process: process,
149         isDead: function () {
150           return false;
151         },
152         send: function (message, b, c, d) {
153           process.send(message, b, c, d);
154         }
155       };
157       if (!process.send) {
158         // Shim the process.send function for worker processes
159         var net = require("net");
160         var os = require("os");
161         var path = require("path");
163         // Create a fake IPC server to receive messages
164         var fakeIPCServer = net.createServer(function (socket) {
165           var receivedData = "";
167           socket.on("data", function (data) {
168             receivedData += data.toString();
169           });
171           socket.on("end", function () {
172             process.emit("message", receivedData);
173           });
174         });
175         fakeIPCServer.listen(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.W" + process.pid + ".ipc") : (__dirname + "/temp/.W" + process.pid + ".ipc"));
177         process.send = function (message) {
178           // Create a fake IPC connection to send messages
179           var fakeIPCConnection = net.createConnection(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.P" + process.pid + ".ipc") : (__dirname + "/temp/.P" + process.pid + ".ipc"), function () {
180             fakeIPCConnection.end(message);
181           });
182         };
184         process.removeFakeIPC = function () {
185           // Close IPC server
186           process.send = function () {};
187           fakeIPCServer.close();
188         };
189       }
190     }
192     // Custom implementation for cluster.fork()
193     cluster._workersCounter = 1;
194     cluster.workers = {};
195     cluster.fork = function (env) {
196       var child_process = require("child_process");
197       var newEnvironment = JSON.parse(JSON.stringify(env ? env : process.env));
198       newEnvironment.NODE_UNIQUE_ID = cluster._workersCounter;
199       var newArguments = JSON.parse(JSON.stringify(process.argv));
200       var command = newArguments.shift();
201       var newWorker = child_process.spawn(command, newArguments, {
202         env: newEnvironment,
203         stdio: ["inherit", "inherit", "inherit", "ipc"]
204       });
206       newWorker.process = newWorker;
207       newWorker.isDead = function () {
208         return newWorker.exitCode !== null || newWorker.killed;
209       };
210       newWorker.id = newEnvironment.NODE_UNIQUE_ID;
212       function checkSendImplementation(worker) {
213         var sendImplemented = true;
215         if (!(process.versions && process.versions.bun && process.versions.bun[0] != "0")) {
216           if (!worker.send) {
217             sendImplemented = false;
218           }
220           var oldLog = console.log;
221           console.log = function (a, b, c, d, e, f) {
222             if (a == "ChildProcess.prototype.send() - Sorry! Not implemented yet") {
223               throw new Error("NOT IMPLEMENTED");
224             } else {
225               oldLog(a, b, c, d, e, f);
226             }
227           };
229           try {
230             worker.send(undefined);
231           } catch (err) {
232             if (err.message === "NOT IMPLEMENTED") {
233               sendImplemented = false;
234             }
235             console.log(err);
236           }
238           console.log = oldLog;
239         }
241         return sendImplemented;
242       }
244       if (!checkSendImplementation(newWorker)) {
245         var net = require("net");
246         var os = require("os");
248         // Create a fake IPC server for worker process to receive messages
249         var fakeWorkerIPCServer = net.createServer(function (socket) {
250           var receivedData = "";
252           socket.on("data", function (data) {
253             receivedData += data.toString();
254           });
256           socket.on("end", function () {
257             newWorker.emit("message", receivedData);
258           });
259         });
260         fakeWorkerIPCServer.listen(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.P" + newWorker.process.pid + ".ipc") : (__dirname + "/temp/.P" + newWorker.process.pid + ".ipc"));
262         // Cleanup when worker process exits
263         newWorker.on("exit", function () {
264           fakeWorkerIPCServer.close();
265           delete cluster.workers[newWorker.id];
266         });
268         newWorker.send = function (message, fakeParam2, fakeParam3, fakeParam4, tries) {
269           if (!tries) tries = 0;
271           try {
272             // Create a fake IPC connection to send messages to worker process
273             var fakeWorkerIPCConnection = net.createConnection(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.W" + newWorker.process.pid + ".ipc") : (__dirname + "/temp/.W" + newWorker.process.pid + ".ipc"), function () {
274               fakeWorkerIPCConnection.end(message);
275             });
276           } catch (err) {
277             if (tries > 50) throw err;
278             newWorker.send(message, fakeParam2, fakeParam3, fakeParam4, tries + 1);
279           }
280         };
281       } else {
282         newWorker.on("exit", function () {
283           delete cluster.workers[newWorker.id];
284         });
285       }
287       cluster.workers[newWorker.id] = newWorker;
288       cluster._workersCounter++;
289       return newWorker;
290     };
291   };
293   if (process.isBun && (cluster.isMaster === undefined || (cluster.isMaster && process.env.NODE_UNIQUE_ID))) {
294     cluster.bunShim();
295   }
297   // Shim cluster.isPrimary field
298   if (cluster.isPrimary === undefined && cluster.isMaster !== undefined) cluster.isPrimary = cluster.isMaster;
301 // ETag-related
302 var ETagDB = {};
304 function generateETag(filePath, stat) {
305   if (!ETagDB[filePath + "-" + stat.size + "-" + stat.mtime]) ETagDB[filePath + "-" + stat.size + "-" + stat.mtime] = sha256(filePath + "-" + stat.size + "-" + stat.mtime);
306   return ETagDB[filePath + "-" + stat.size + "-" + stat.mtime];
309 // Brute force protection-related
310 var bruteForceDb = {};
312 // PBKDF2/scrypt cache
313 var pbkdf2Cache = [];
314 var scryptCache = [];
315 var passwordHashCacheIntervalId = -1;
317 // SVR.JS worker spawn-related
318 var SVRJSInitialized = false;
319 var exiting = false;
320 var reallyExiting = false;
321 var crashed = false;
322 var threadLimitWarned = false;
324 // SVR.JS worker forking function
325 function SVRJSFork() {
326   // Log
327   if (SVRJSInitialized) serverconsole.locmessage("Starting next thread, because previous one hung up/crashed...");
328   // Fork new worker
329   var newWorker = {};
330   try {
331     if (!threadLimitWarned && cluster.__shimmed__ && process.isBun && process.versions.bun && process.versions.bun[0] != "0") {
332       threadLimitWarned = true;
333       serverconsole.locwarnmessage("SVR.JS limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer.");
334     }
335     if (!(cluster.__shimmed__ && process.isBun && process.versions.bun && process.versions.bun[0] != "0" && Object.keys(cluster.workers) > 0)) {
336       newWorker = cluster.fork();
337     } else {
338       if (SVRJSInitialized) serverconsole.locwarnmessage("SVR.JS limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer.");
339     }
340   } catch (err) {
341     if (err.name == "NotImplementedError") {
342       // If cluster.fork throws a NotImplementedError, shim cluster module
343       cluster.bunShim();
344       if (!threadLimitWarned && cluster.__shimmed__ && process.isBun && process.versions.bun && process.versions.bun[0] != "0") {
345         threadLimitWarned = true;
346         serverconsole.locwarnmessage("SVR.JS limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer.");
347       }
348       if (!(cluster.__shimmed__ && process.isBun && process.versions.bun && process.versions.bun[0] != "0" && Object.keys(cluster.workers) > 0)) {
349         newWorker = cluster.fork();
350       } else {
351         if (SVRJSInitialized) serverconsole.locwarnmessage("SVR.JS limited the number of workers to one, because of startup problems in Bun 1.0 and newer with shimmed (not native) clustering module. Reliability may suffer.");
352       }
353     } else {
354       throw err;
355     }
356   }
358   // Add event listeners
359   if (newWorker.on) {
360     newWorker.on("error", function (err) {
361       if (!reallyExiting) serverconsole.locwarnmessage("There was a problem when handling SVR.JS worker! (from master process side) Reason: " + err.message);
362     });
363     newWorker.on("exit", function () {
364       if (!exiting && Object.keys(cluster.workers).length == 0) {
365         crashed = true;
366         SVRJSFork();
367       }
368     });
369     newWorker.on("message", bruteForceListenerWrapper(newWorker));
370     newWorker.on("message", listenConnListener);
371   }
374 var http = require("http");
375 http.STATUS_CODES[497] = "HTTP Request Sent to HTTPS Port";
376 http.STATUS_CODES[598] = "Network Read Timeout Error";
377 http.STATUS_CODES[599] = "Network Connect Timeout Error";
378 var url = require("url");
379 var dns = require("dns");
380 try {
381   var gracefulFs = require("graceful-fs");
382   if (!process.isBun) gracefulFs.gracefulify(fs); // Bun + graceful-fs + SVR.JS + requests about static content = 500 Internal Server Error!
383 } catch (err) {
384   // Don't use graceful-fs
386 var path = require("path");
387 var hexstrbase64 = undefined;
388 try {
389   hexstrbase64 = require("./hexstrbase64/index.js");
390 } catch (err) {
391   // Don't use hexstrbase64
393 var inspector = undefined;
394 try {
395  inspector = require("inspector");
396 } catch (err) {
397   // Don't use inspector
399 var zlib = require("zlib");
400 var tar = undefined;
401 try {
402   tar = require("tar");
403 } catch (err) {
404   tar = {
405     _errored: err
406   };
408 var formidable = undefined;
409 try {
410   formidable = require("formidable");
411 } catch (err) {
412   formidable = {
413     _errored: err
414   };
416 var ocsp = undefined;
417 var ocspCache = undefined;
418 try {
419   ocsp = require("ocsp");
420   ocspCache = new ocsp.Cache();
421 } catch (err) {
422   ocsp = {
423     _errored: err
424   };
426 var http2 = {};
427 try {
428   http2 = require("http2");
429   if (process.isBun) {
430     try {
431       http2.Http2ServerRequest();
432     } catch (err) {
433       if (err.name == "NotImplementedError" || err.code == "ERR_NOT_IMPLEMENTED") throw err;
434     }
435   }
436 } catch (err) {
437   http2.__disabled__ = null;
438   http2.createServer = function () {
439     throw new Error("HTTP/2 support is not present");
440   };
441   http2.createSecureServer = function () {
442     throw new Error("HTTP/2 support is not present");
443   };
444   http2.connect = function () {
445     throw new Error("HTTP/2 support is not present");
446   };
447   http2.get = function () {
448     throw new Error("HTTP/2 support is not present");
449   };
451 var crypto = {};
452 var https = {};
453 try {
454   crypto = require("crypto");
455   https = require("https");
456 } catch (err) {
457   crypto = {
458     __disabled__: null
459   };
460   https = {
461     createServer: function () {
462       throw new Error("Crypto support is not present");
463     },
464     connect: function () {
465       throw new Error("Crypto support is not present");
466     },
467     get: function () {
468       throw new Error("Crypto support is not present");
469     }
470   };
471   http2.createSecureServer = function () {
472     throw new Error("Crypto support is not present");
473   };
475 var mime = require("mime-types");
476 var pubip = "";
477 var listenAddress = undefined;
478 var sListenAddress = undefined;
479 var pubport = 80;
480 var port = 80;
481 var domain = "";
482 var spubport = 443;
483 var sport = 443;
484 var mods = [];
486 if (!fs.existsSync(__dirname + "/log")) fs.mkdirSync(__dirname + "/log");
487 if (!fs.existsSync(__dirname + "/mods")) fs.mkdirSync(__dirname + "/mods");
488 if (!fs.existsSync(__dirname + "/temp")) fs.mkdirSync(__dirname + "/temp");
490 var modFiles = fs.readdirSync(__dirname + "/mods").sort();
491 var modInfos = [];
493 function sizify(bytes, addI) {
494   if (bytes == 0) return "0";
495   if (bytes < 0) bytes = -bytes;
496   var prefixes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"];
497   var prefixIndex = Math.floor(Math.log2 ? Math.log2(bytes) / 10 : (Math.log(bytes) / (Math.log(2) * 10)));
498   if (prefixIndex >= prefixes.length - 1) prefixIndex = prefixes.length - 1;
499   var prefixIndexTranslated = Math.pow(2, 10 * prefixIndex);
500   var decimalPoints = 2 - Math.floor(Math.log10 ? Math.log10(bytes / prefixIndexTranslated) : (Math.log(bytes / prefixIndexTranslated) / Math.log(10)));
501   if (decimalPoints < 0) decimalPoints = 0;
502   return (Math.ceil((bytes / prefixIndexTranslated) * Math.pow(10, decimalPoints)) / Math.pow(10, decimalPoints)) + prefixes[prefixIndex] + ((prefixIndex > 0 && addI) ? "i" : "");
505 function getOS() {
506   var osType = os.type();
507   var platform = os.platform();
508   if (platform == "android") {
509     return "Android";
510   } else if (osType == "Windows_NT" || osType == "WindowsNT") {
511     var arch = os.arch();
512     if (arch == "ia32") {
513       return "Win32";
514     } else if (arch == "x64") {
515       return "Win64";
516     } else {
517       return "Win" + arch.toUpperCase();
518     }
519   } else if (osType.indexOf("CYGWIN") == 0) {
520     return "Cygwin";
521   } else if (osType.indexOf("MINGW") == 0) {
522     return "MinGW";
523   } else if (osType.indexOf("MSYS") == 0) {
524     return "MSYS";
525   } else if (osType.indexOf("UWIN") == 0) {
526     return "UWIN";
527   } else if (osType == "GNU") {
528     return "GNU Hurd";
529   } else {
530     return osType;
531   }
534 function createRegex(regex, isPath) {
535   var regexStrMatch = regex.match(/^\/((?:\\.|[^\/\\])*)\/([a-zA-Z0-9]*)$/);
536   if (!regexStrMatch) throw new Error("Invalid regular expression: " + regex);
537   var searchString = regexStrMatch[1];
538   var modifiers = regexStrMatch[2];
539   if (isPath && !modifiers.match(/i/i) && os.platform() == "win32") modifiers += "i";
540   return new RegExp(searchString, modifiers);
543 // Function to check if IPs are equal
544 function ipMatch(IP1, IP2) {
545   if (!IP1) return true;
546   if (!IP2) return false;
548   // Function to normalize IPv4 address (remove leading zeros)
549   function normalizeIPv4Address(address) {
550     return address.replace(/(^|\.)(?:0(?!\.|$))+/g, "");
551   }
553   // Function to expand IPv6 address to full format
554   function expandIPv6Address(address) {
555     var fullAddress = "";
556     var expandedAddress = "";
557     var validGroupCount = 8;
558     var validGroupSize = 4;
560     var ipv4 = "";
561     var extractIpv4 = /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/;
562     var validateIpv4 = /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/;
564     if (validateIpv4.test(address)) {
565       var oldGroups = address.match(extractIpv4);
566       for (var i = 1; i < oldGroups.length; i++) {
567         ipv4 += ("00" + (parseInt(oldGroups[i], 10).toString(16))).slice(-2) + (i == 2 ? ":" : "");
568       }
569       address = address.replace(extractIpv4, ipv4);
570     }
572     if (address.indexOf("::") == -1) {
573       fullAddress = address;
574     } else {
575       var sides = address.split("::");
576       var groupsPresent = 0;
577       sides.forEach(function (side) {
578         groupsPresent += side.split(":").length;
579       });
580       fullAddress += sides[0] + ":";
581       if (validGroupCount - groupsPresent > 1) {
582         fullAddress += "0000:".repeat(validGroupCount - groupsPresent);
583       }
584       fullAddress += sides[1];
585     }
586     var groups = fullAddress.split(":");
587     for (var i = 0; i < validGroupCount; i++) {
588       if (groups[i].length < validGroupSize) {
589         groups[i] = "0".repeat(validGroupSize - groups[i].length) + groups[i];
590       }
591       expandedAddress += (i != validGroupCount - 1) ? groups[i] + ":" : groups[i];
592     }
593     return expandedAddress;
594   }
596   // Normalize or expand IP addresses
597   IP1 = IP1.toLowerCase();
598   if (IP1 == "localhost") IP1 = "::1";
599   if (IP1.indexOf("::ffff:") == 0) IP1 = IP1.substring(7);
600   if (IP1.indexOf(":") > -1) {
601     IP1 = expandIPv6Address(IP1);
602   } else {
603     IP1 = normalizeIPv4Address(IP1);
604   }
606   IP2 = IP2.toLowerCase();
607   if (IP2 == "localhost") IP2 = "::1";
608   if (IP2.indexOf("::ffff:") == 0) IP2 = IP2.substring(7);
609   if (IP2.indexOf(":") > -1) {
610     IP2 = expandIPv6Address(IP2);
611   } else {
612     IP2 = normalizeIPv4Address(IP2);
613   }
615   // Check if processed IPs are equal
616   if (IP1 == IP2) return true;
617   else return false;
620 function checkForEnabledDirectoryListing(hostname, localAddress) {
621   function matchHostname(hostnameM) {
622     if (typeof hostnameM == "undefined" || hostnameM == "*") {
623       return true;
624     } else if (hostname && hostnameM.indexOf("*.") == 0 && hostnameM != "*.") {
625       var hostnamesRoot = hostnameM.substring(2);
626       if (hostname == hostnamesRoot || (hostname.length > hostnamesRoot.length && hostname.indexOf("." + hostnamesRoot) == hostname.length - hostnamesRoot.length - 1)) {
627         return true;
628       }
629     } else if (hostname && hostname == hostnameM) {
630       return true;
631     }
632     return false;
633   }
635   var main = (configJSON.enableDirectoryListing || configJSON.enableDirectoryListing === undefined);
636   if (!configJSON.enableDirectoryListingVHost) return main;
637   var vhostP = null;
638   configJSON.enableDirectoryListingVHost.every(function (vhost) {
639     if (matchHostname(vhost.host) && ipMatch(vhost.ip, localAddress)) {
640       vhostP = vhost;
641       return false;
642     } else {
643       return true;
644     }
645   });
646   if (!vhostP || vhostP.enabled === undefined) return main;
647   else return vhostP.enabled;
650 // IP Block list object
651 function ipBlockList(rawBlockList) {
653   // Initialize the instance with empty arrays
654   if (rawBlockList === undefined) rawBlockList = [];
655   var instance = {
656     raw: [],
657     rawtoPreparedMap: [],
658     prepared: [],
659     cidrs: []
660   };
662   // Function to normalize IPv4 address (remove leading zeros)
663   function normalizeIPv4Address(address) {
664     return address.replace(/(^|\.)(?:0(?!\.|$))+/g, "");
665   }
667   // Function to expand IPv6 address to full format
668   function expandIPv6Address(address) {
669     var fullAddress = "";
670     var expandedAddress = "";
671     var validGroupCount = 8;
672     var validGroupSize = 4;
674     var ipv4 = "";
675     var extractIpv4 = /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/;
676     var validateIpv4 = /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/;
678     if (validateIpv4.test(address)) {
679       var oldGroups = address.match(extractIpv4);
680       for (var i = 1; i < oldGroups.length; i++) {
681         ipv4 += ("00" + (parseInt(oldGroups[i], 10).toString(16))).slice(-2) + (i == 2 ? ":" : "");
682       }
683       address = address.replace(extractIpv4, ipv4);
684     }
686     if (address.indexOf("::") == -1) {
687       fullAddress = address;
688     } else {
689       var sides = address.split("::");
690       var groupsPresent = 0;
691       sides.forEach(function (side) {
692         groupsPresent += side.split(":").length;
693       });
694       fullAddress += sides[0] + ":";
695       if (validGroupCount - groupsPresent > 1) {
696         fullAddress += "0000:".repeat(validGroupCount - groupsPresent);
697       }
698       fullAddress += sides[1];
699     }
700     var groups = fullAddress.split(":");
701     for (var i = 0; i < validGroupCount; i++) {
702       if (groups[i].length < validGroupSize) {
703         groups[i] = "0".repeat(validGroupSize - groups[i].length) + groups[i];
704       }
705       expandedAddress += (i != validGroupCount - 1) ? groups[i] + ":" : groups[i];
706     }
707     return expandedAddress;
708   }
710   // Convert IPv4 address to an integer representation
711   function ipv4ToInt(ip) {
712     var ips = ip.split(".");
713     return parseInt(ips[0]) * 16777216 + parseInt(ips[1]) * 65536 + parseInt(ips[2]) * 256 + parseInt(ips[3]);
714   }
716   // Get IPv4 CIDR block limits (min and max)
717   function getIPv4CIDRLimits(ip, cidrMask) {
718     var ipInt = ipv4ToInt(ip);
719     var exp = Math.pow(2, 32 - cidrMask);
720     var ipMin = Math.floor(ipInt / exp) * exp;
721     var ipMax = ipMin + exp - 1;
722     return {
723       min: ipMin,
724       max: ipMax
725     };
726   }
728   // Convert IPv6 address to an array of blocks
729   function ipv6ToBlocks(ip) {
730     var ips = ip.split(":");
731     var ip2s = [];
732     ips.forEach(function (ipe) {
733       ip2s.push(parseInt(ipe));
734     });
735     return ip2s;
736   }
738   // Get IPv6 CIDR block limits (min and max)
739   function getIPv6CIDRLimits(ip, cidrMask) {
740     var ipBlocks = ipv6ToBlocks(ip);
741     var fieldsToDelete = Math.floor((128 - cidrMask) / 16);
742     var fieldMaskModify = (128 - cidrMask) % 16;
743     var ipBlockMin = [];
744     var ipBlockMax = [];
745     for (var i = 0; i < 8; i++) {
746       ipBlockMin.push((i < 8 - fieldsToDelete) ? ((i < 7 - fieldsToDelete) ? ipBlocks[i] : (ipBlocks[i] >> fieldMaskModify << fieldMaskModify)) : 0);
747     }
748     for (var i = 0; i < 8; i++) {
749       ipBlockMax.push((i < 8 - fieldsToDelete) ? ((i < 7 - fieldsToDelete) ? ipBlocks[i] : ((ipBlocks[i] >> fieldMaskModify << fieldMaskModify) + Math.pow(2, fieldMaskModify) - 1)) : 65535);
750     }
751     return {
752       min: ipBlockMin,
753       max: ipBlockMax
754     };
755   }
757   // Check if the IPv4 address matches the given CIDR block
758   function checkIfIPv4CIDRMatches(ipInt, cidrObject) {
759     if (cidrObject.v6) return false;
760     return ipInt >= cidrObject.min && ipInt <= cidrObject.max;
761   }
763   // Check if the IPv6 address matches the given CIDR block
764   function checkIfIPv6CIDRMatches(ipBlock, cidrObject) {
765     if (!cidrObject.v6) return false;
766     for (var i = 0; i < 8; i++) {
767       if (ipBlock[i] < cidrObject.min[i] || ipBlock[i] > cidrObject.max[i]) return true;
768     }
769     return false;
770   }
772   // Function to add an IP or CIDR block to the block list
773   instance.add = function (rawValue) {
774     // Add to raw block list
775     instance.raw.push(rawValue);
777     // Initialize variables
778     var beginIndex = instance.prepared.length;
779     var cidrIndex = instance.cidrs.length;
780     var cidrMask = null;
781     var isIPv6 = false;
783     // Check if the input contains CIDR notation
784     if (rawValue.indexOf("/") > -1) {
785       var rwArray = rawValue.split("/");
786       cidrMask = rwArray.pop();
787       rawValue = rwArray.join("/");
788     }
790     // Normalize the IP address or expand the IPv6 address
791     rawValue = rawValue.toLowerCase();
792     if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substring(7);
793     if (rawValue.indexOf(":") > -1) {
794       isIPv6 = true;
795       rawValue = expandIPv6Address(rawValue);
796     } else {
797       rawValue = normalizeIPv4Address(rawValue);
798     }
800     // Add the IP or CIDR block to the appropriate list
801     if (cidrMask) {
802       var cidrLimits = {};
803       if (isIPv6) {
804         cidrLimits = getIPv6CIDRLimits(rawValue, cidrMask);
805         cidrLimits.v6 = true;
806       } else {
807         cidrLimits = getIPv4CIDRLimits(rawValue, cidrMask);
808         cidrLimits.v6 = false;
809       }
810       instance.cidrs.push(cidrLimits);
811       instance.rawtoPreparedMap.push({
812         cidr: true,
813         index: cidrIndex
814       });
815     } else {
816       instance.prepared.push(rawValue);
817       instance.rawtoPreparedMap.push({
818         cidr: false,
819         index: beginIndex
820       });
821     }
822   };
824   // Function to remove an IP or CIDR block from the block list
825   instance.remove = function (ip) {
826     var index = instance.raw.indexOf(ip);
827     if (index == -1) return false;
828     var map = instance.rawtoPreparedMap[index];
829     instance.raw.splice(index, 1);
830     instance.rawtoPreparedMap.splice(index, 1);
831     if (map.cidr) {
832       instance.cidrs.splice(map.index, 1);
833     } else {
834       instance.prepared.splice(map.index, 1);
835     }
836     return true;
837   };
839   // Function to check if an IP is blocked by the block list
840   instance.check = function (rawValue) {
841     if (instance.raw.length == 0) return false;
842     var isIPv6 = false;
844     // Normalize or expand the IP address
845     rawValue = rawValue.toLowerCase();
846     if (rawValue == "localhost") rawValue = "::1";
847     if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substring(7);
848     if (rawValue.indexOf(":") > -1) {
849       isIPv6 = true;
850       rawValue = expandIPv6Address(rawValue);
851     } else {
852       rawValue = normalizeIPv4Address(rawValue);
853     }
855     // Check if the IP is in the prepared list
856     if (instance.prepared.indexOf(rawValue) > -1) return true;
858     // Check if the IP is within any CIDR block in the block list
859     if (instance.cidrs.length == 0) return false;
860     var ipParsedObject = (!isIPv6 ? ipv4ToInt : ipv6ToBlocks)(rawValue);
861     var checkMethod = (!isIPv6 ? checkIfIPv4CIDRMatches : checkIfIPv6CIDRMatches);
863     return instance.cidrs.some(function (iCidr) {
864       return checkMethod(ipParsedObject, iCidr);
865     });
866   };
868   // Add initial raw block list values to the instance
869   rawBlockList.forEach(function (rbe) {
870     instance.add(rbe);
871   });
873   return instance;
876 // Generate V8-style error stack from Error object.
877 function generateErrorStack(errorObject) {
878   // Split the error stack by newlines.
879   var errorStack = errorObject.stack ? errorObject.stack.split("\n") : [];
881   // If the error stack starts with the error name, return the original stack (it is V8-style then).
882   if (errorStack.some(function (errorStackLine) {
883     return (errorStackLine.indexOf(errorObject.name) == 0);
884   })) {
885     return errorObject.stack;
886   }
888   // Create a new error stack with the error name and code (if available).
889   var newErrorStack = [errorObject.name + (errorObject.code ? ": " + errorObject.code : "") + (errorObject.message == "" ? "" : ": " + errorObject.message)];
891   // Process each line of the original error stack.
892   errorStack.forEach(function (errorStackLine) {
893     if (errorStackLine != "") {
894       // Split the line into function and location parts (if available).
895       var errorFrame = errorStackLine.split("@");
896       var location = "";
897       if (errorFrame.length > 1) location = errorFrame.pop();
898       var func = errorFrame.join("@");
900       // Build the new error stack entry with function and location information.
901       newErrorStack.push("    at " + (func == "" ? (!location || location == "" ? "<anonymous>" : location) : (func + (!location || location == "" ? "" : " (" + location + ")"))));
902     }
903   });
905   // Join the new error stack entries with newlines and return the final stack.
906   return newErrorStack.join("\n");
909 function calculateBroadcastIPv4FromCidr(ipWithCidr) {
910   // Check if CIDR notation is valid, if it's not, return null
911   if (!ipWithCidr) return null;
912   var ipCA = ipWithCidr.split("/");
913   if (ipCA.length != 2) return null;
915   // Extract IP and mask (numberic format)
916   var ip = ipCA[0];
917   var mask = parseInt(ipCA[1]);
919   return ip.split(".").map(function (num, index) {
920     // Calculate resulting 8-bit
921     var power = Math.max(Math.min(mask - (index * 8), 8), 0);
922     return ((parseInt(num) & ((Math.pow(2, power) - 1) << (8 - power))) | Math.pow(2, 8 - power) - 1).toString();
923   }).join(".");
926 function calculateNetworkIPv4FromCidr(ipWithCidr) {
927   // Check if CIDR notation is valid, if it's not, return null
928   if (!ipWithCidr) return null;
929   var ipCA = ipWithCidr.split("/");
930   if (ipCA.length != 2) return null;
932   // Extract IP and mask (numberic format)
933   var ip = ipCA[0];
934   var mask = parseInt(ipCA[1]);
936   return ip.split(".").map(function (num, index) {
937     // Calculate resulting 8-bit
938     var power = Math.max(Math.min(mask - (index * 8), 8), 0);
939     return ((parseInt(num) & ((Math.pow(2, power) - 1) << (8 - power)))).toString();
940   }).join(".");
943 var inspectorURL = undefined;
944 try {
945   if (inspector) {
946     inspectorURL = inspector.url();
947   }
948 } catch (err) {
949   // Failed to get inspector URL
952 if (!process.stdout.isTTY && !inspectorURL) {
953   // When stdout is not a terminal and not attached to an Node.JS inspector, disable it to improve performance of SVR.JS
954   console.log = function () {};
955   process.stdout.write = function () {};
956   process.stdout._write = function () {};
957   process.stdout._writev = function () {};
960 // IP and network inteface-related
961 var ifaces = {};
962 var ifaceEx = null;
963 try {
964   ifaces = os.networkInterfaces();
965 } catch (err) {
966   ifaceEx = err;
968 var ips = [];
969 var brdIPs = ["255.255.255.255", "127.255.255.255", "0.255.255.255"];
970 var netIPs = ["127.0.0.0"];
972 Object.keys(ifaces).forEach(function (ifname) {
973   var alias = 0;
974   ifaces[ifname].forEach(function (iface) {
975     if (iface.family !== "IPv4" || iface.internal !== false) {
976       return;
977     }
978     if (alias >= 1) {
979       ips.push(ifname + ":" + alias, iface.address);
980     } else {
981       ips.push(ifname, iface.address);
982     }
983     brdIPs.push(calculateBroadcastIPv4FromCidr(iface.cidr));
984     netIPs.push(calculateNetworkIPv4FromCidr(iface.cidr));
985     alias++;
986   });
989 if (ips.length == 0) {
990   Object.keys(ifaces).forEach(function (ifname) {
991     var alias = 0;
992     ifaces[ifname].forEach(function (iface) {
993       if (iface.family !== "IPv6" || iface.internal !== false) {
994         return;
995       }
996       if (alias >= 1) {
997         ips.push(ifname + ":" + alias, iface.address);
998       } else {
999         ips.push(ifname, iface.address);
1000       }
1001       alias++;
1002     });
1003   });
1006 // Server startup attempt counter
1007 var attmts = 5;
1008 var attmtsRedir = 5;
1010 // Some variables...
1011 var errors = os.constants.errno;
1012 var timestamp = new Date().getTime();
1014 // Server IP address
1015 var host = ips[(ips.length) - 1];
1016 if (!host) host = "[offline]";
1018 // Public IP address-related
1019 var ipRequestCompleted = false;
1020 var ipRequestGotError = false;
1021 if (host != "[offline]" || ifaceEx) {
1022   var ipRequest = (crypto.__disabled__ !== undefined ? http : https).get({
1023     host: "api64.ipify.org",
1024     port: (crypto.__disabled__ !== undefined ? 80 : 443),
1025     path: "/",
1026     headers: {
1027       "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
1028     },
1029     timeout: 5000
1030   }, function (res) {
1031     ipRequest.removeAllListeners("timeout");
1032     res.on("data", function (d) {
1033       if (res.statusCode != 200) {
1034         ipRequestCompleted = true;
1035         process.emit("ipRequestCompleted");
1036         return;
1037       }
1038       pubip = d.toString();
1039       if (domain) {
1040         ipRequestCompleted = true;
1041         process.emit("ipRequestCompleted");
1042       } else {
1043         var callbackDone = false;
1045         var dnsTimeout = setTimeout(function () {
1046           callbackDone = true;
1047           ipRequestCompleted = true;
1048           process.emit("ipRequestCompleted");
1049         }, 3000);
1051         try {
1052           dns.reverse(pubip, function (err, hostnames) {
1053             if (callbackDone) return;
1054             clearTimeout(dnsTimeout);
1055             if (!err && hostnames.length > 0) domain = hostnames[0];
1056             ipRequestCompleted = true;
1057             process.emit("ipRequestCompleted");
1058           });
1059         } catch (err) {
1060           clearTimeout(dnsTimeout);
1061           callbackDone = true;
1062           ipRequestCompleted = true;
1063           process.emit("ipRequestCompleted");
1064         }
1065       }
1066     });
1067   });
1068   ipRequest.on("error", function () {
1069     if (crypto.__disabled__ || ipRequestGotError) {
1070       ipRequestCompleted = true;
1071       process.emit("ipRequestCompleted");
1072     } else {
1073       ipRequestGotError = true;
1074     }
1075   });
1076   ipRequest.on("timeout", function () {
1077     if (crypto.__disabled__ || ipRequestGotError) {
1078       ipRequestCompleted = true;
1079       process.emit("ipRequestCompleted");
1080     } else {
1081       ipRequestGotError = true;
1082     }
1083   });
1085   if (!crypto.__disabled) {
1086     var ipRequest2 = https.get({
1087       host: "api.seeip.org",
1088       port: 443,
1089       path: "/",
1090       headers: {
1091         "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
1092       },
1093       timeout: 5000
1094     }, function (res) {
1095       ipRequest2.removeAllListeners("timeout");
1096       res.on("data", function (d) {
1097         if (res.statusCode != 200) {
1098           ipRequestCompleted = true;
1099           process.emit("ipRequestCompleted");
1100           return;
1101         }
1102         pubip = d.toString();
1103         if (domain) {
1104           ipRequestCompleted = true;
1105           process.emit("ipRequestCompleted");
1106         } else {
1107           var callbackDone = false;
1109           var dnsTimeout = setTimeout(function () {
1110             callbackDone = true;
1111             ipRequestCompleted = true;
1112             process.emit("ipRequestCompleted");
1113           }, 3000);
1115           try {
1116             dns.reverse(pubip, function (err, hostnames) {
1117               if (callbackDone) return;
1118               clearTimeout(dnsTimeout);
1119               if (!err && hostnames.length > 0) domain = hostnames[0];
1120               ipRequestCompleted = true;
1121               process.emit("ipRequestCompleted");
1122             });
1123           } catch (err) {
1124             clearTimeout(dnsTimeout);
1125             callbackDone = true;
1126             ipRequestCompleted = true;
1127             process.emit("ipRequestCompleted");
1128           }
1129         }
1130       });
1131     });
1132     ipRequest2.on("error", function () {
1133       if (crypto.__disabled__ || ipRequestGotError) {
1134         ipRequestCompleted = true;
1135         process.emit("ipRequestCompleted");
1136       } else {
1137         ipRequestGotError = true;
1138       }
1139     });
1140     ipRequest2.on("timeout", function () {
1141       if (crypto.__disabled__ || ipRequestGotError) {
1142         ipRequestCompleted = true;
1143         process.emit("ipRequestCompleted");
1144       } else {
1145         ipRequestGotError = true;
1146       }
1147     });
1148   }
1149 } else {
1150   ipRequestCompleted = true;
1153 function ipStatusCallback(callback) {
1154   if (ipRequestCompleted) {
1155     callback();
1156   } else {
1157     process.once("ipRequestCompleted", callback);
1158   }
1161 var configJSON = {};
1162 var configJSONRErr = undefined;
1163 var configJSONPErr = undefined;
1164 if (fs.existsSync(__dirname + "/config.json")) {
1165   var configJSONf = "";
1166   try {
1167     configJSONf = fs.readFileSync(__dirname + "/config.json"); // Read JSON File
1168     try {
1169       configJSON = JSON.parse(configJSONf); // Parse JSON
1170     } catch (err2) {
1171       configJSONPErr = err2;
1172     }
1173   } catch (err) {
1174     configJSONRErr = err2;
1175   }
1178 // Default server configuration properties
1179 var wwwredirect = false;
1180 var rawBlockList = [];
1181 var users = [];
1182 var page404 = "404.html";
1183 var serverAdmin = "[no contact information]";
1184 var stackHidden = false;
1185 var exposeServerVersion = true;
1186 var rewriteMap = [];
1187 var allowStatus = true;
1188 var 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])$/"];
1189 var enableIPSpoofing = false;
1190 var sni = {};
1191 var disableNonEncryptedServer = false;
1192 var disableToHTTPSRedirect = false;
1193 var nonStandardCodesRaw = [];
1194 var disableUnusedWorkerTermination = false;
1195 var rewriteDirtyURLs = false;
1196 var errorPages = [];
1197 var useWebRootServerSideScript = true;
1198 var exposeModsInErrorPages = true;
1199 var disableTrailingSlashRedirects = false;
1200 var environmentVariables = {};
1201 var wwwrootPostfixesVHost = [];
1202 var wwwrootPostfixPrefixesVHost = [];
1203 var allowDoubleSlashes = false;
1204 var allowPostfixDoubleSlashes = false;
1206 // Get properties from config.json
1207 if (configJSON.blacklist != undefined) rawBlockList = configJSON.blacklist;
1208 if (configJSON.wwwredirect != undefined) wwwredirect = configJSON.wwwredirect;
1209 if (configJSON.port != undefined) port = configJSON.port;
1210 if (configJSON.pubport != undefined) pubport = configJSON.pubport;
1211 if (typeof port === "string") {
1212   if (port.match(/^[0-9]+$/)) {
1213     port = parseInt(port);
1214   } else {
1215     var portLMatch = port.match(/^(\[[^ \]@\/\\]+\]|[^ \]\[:@\/\\]+):([0-9]+)$/);
1216     if (portLMatch) {
1217       listenAddress = portLMatch[1].replace(/^\[|\]$/g, "").replace(/^::ffff:/i, "");
1218       port = parseInt(portLMatch[2]);
1219     }
1220   }
1222 if (configJSON.domian != undefined) domain = configJSON.domian;
1223 if (configJSON.domain != undefined) domain = configJSON.domain;
1224 if (configJSON.sport != undefined) sport = configJSON.sport;
1225 if (typeof sport === "string") {
1226   if (sport.match(/^[0-9]+$/)) {
1227     sport = parseInt(sport);
1228   } else {
1229     var sportLMatch = sport.match(/^(\[[^ \]@\/\\]+\]|[^ \]\[:@\/\\]+):([0-9]+)$/);
1230     if (sportLMatch) {
1231       sListenAddress = sportLMatch[1].replace(/^\[|\]$/g, "").replace(/^::ffff:/i, "");
1232       sport = parseInt(sportLMatch[2]);
1233     }
1234   }
1236 if (configJSON.spubport != undefined) spubport = configJSON.spubport;
1237 if (configJSON.page404 != undefined) page404 = configJSON.page404;
1238 if (configJSON.serverAdministratorEmail != undefined) serverAdmin = configJSON.serverAdministratorEmail;
1239 if (configJSON.nonStandardCodes != undefined) nonStandardCodesRaw = configJSON.nonStandardCodes;
1240 if (configJSON.stackHidden != undefined) stackHidden = configJSON.stackHidden;
1241 if (configJSON.users != undefined) users = configJSON.users;
1242 if (configJSON.exposeServerVersion != undefined) exposeServerVersion = configJSON.exposeServerVersion;
1243 if (configJSON.rewriteMap != undefined) rewriteMap = configJSON.rewriteMap;
1244 if (configJSON.allowStatus != undefined) allowStatus = configJSON.allowStatus;
1245 if (configJSON.dontCompress != undefined) dontCompress = configJSON.dontCompress;
1246 if (configJSON.enableIPSpoofing != undefined) enableIPSpoofing = configJSON.enableIPSpoofing;
1247 if (configJSON.secure != undefined) secure = secure || configJSON.secure;
1248 if (configJSON.sni != undefined) sni = configJSON.sni;
1249 if (configJSON.disableNonEncryptedServer != undefined) disableNonEncryptedServer = configJSON.disableNonEncryptedServer;
1250 if (configJSON.disableToHTTPSRedirect != undefined) disableToHTTPSRedirect = configJSON.disableToHTTPSRedirect;
1251 if (configJSON.disableUnusedWorkerTermination != undefined) disableUnusedWorkerTermination = configJSON.disableUnusedWorkerTermination;
1252 if (configJSON.rewriteDirtyURLs != undefined) rewriteDirtyURLs = configJSON.rewriteDirtyURLs;
1253 if (configJSON.errorPages != undefined) errorPages = configJSON.errorPages;
1254 if (configJSON.useWebRootServerSideScript != undefined) useWebRootServerSideScript = configJSON.useWebRootServerSideScript;
1255 if (configJSON.exposeModsInErrorPages != undefined) exposeModsInErrorPages = configJSON.exposeModsInErrorPages;
1256 if (configJSON.disableTrailingSlashRedirects != undefined) disableTrailingSlashRedirects = configJSON.disableTrailingSlashRedirects;
1257 if (configJSON.environmentVariables != undefined) environmentVariables = configJSON.environmentVariables;
1258 if (configJSON.wwwrootPostfixesVHost != undefined) wwwrootPostfixesVHost = configJSON.wwwrootPostfixesVHost;
1259 if (configJSON.wwwrootPostfixPrefixesVHost != undefined) wwwrootPostfixPrefixesVHost = configJSON.wwwrootPostfixPrefixesVHost;
1260 if (configJSON.allowDoubleSlashes != undefined) allowDoubleSlashes = configJSON.allowDoubleSlashes;
1261 if (configJSON.allowPostfixDoubleSlashes != undefined) allowPostfixDoubleSlashes = configJSON.allowPostfixDoubleSlashes;
1263 var wwwrootError = null;
1264 try {
1265   if (cluster.isPrimary || cluster.isPrimary === undefined) process.chdir(configJSON.wwwroot != undefined ? configJSON.wwwroot : __dirname);
1266 } catch (err) {
1267   wwwrootError = err;
1270 try {
1271   Object.keys(environmentVariables).forEach(function (key) {
1272     process.env[key] = environmentVariables[key];
1273   });
1274 } catch (err) {
1275   // Failed to set environment variables.
1278 // Compability for older mods
1279 configJSON.version = version;
1280 configJSON.productName = "SVR.JS";
1282 var blocklist = ipBlockList(rawBlockList);
1284 var nonStandardCodes = [];
1285 nonStandardCodesRaw.forEach(function (nonStandardCodeRaw) {
1286   var newObject = {};
1287   Object.keys(nonStandardCodeRaw).forEach(function (nsKey) {
1288     if (nsKey != "users") {
1289       newObject[nsKey] = nonStandardCodeRaw[nsKey];
1290     } else {
1291       newObject["users"] = ipBlockList(nonStandardCodeRaw.users);
1292     }
1293   });
1294   nonStandardCodes.push(newObject);
1297 var customHeaders = (configJSON.customHeaders == undefined ? {} : JSON.parse(JSON.stringify(configJSON.customHeaders)));
1298 if (exposeServerVersion) {
1299   customHeaders["Server"] = "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")";
1300 } else {
1301   customHeaders["Server"] = "SVR.JS";
1304 function getCustomHeaders() {
1305   return JSON.parse(JSON.stringify(customHeaders));
1308 var vnum = 0;
1309 try {
1310   vnum = process.config.variables.node_module_version;
1311 } catch (err) {
1312   // Version number not retrieved
1315 if (vnum === undefined) vnum = 0;
1316 if (process.isBun) vnum = 64;
1318 // SVR.JS path sanitizer function
1319 function sanitizeURL(resource, allowDoubleSlashes) {
1320   if (resource == "*" || resource == "") return resource;
1321   // Remove null characters
1322   resource = resource.replace(/%00|\0/g, "");
1323   // Check if URL is malformed (e.g. %c0%af or %u002f or simply %as)
1324   if (resource.match(/%(?:c[01]|f[ef]|(?![0-9a-f]{2}).{2}|.{0,1}$)/i)) throw new URIError("URI malformed");
1325   // Decode URL-encoded characters while preserving certain characters
1326   resource = resource.replace(/%([0-9a-f]{2})/gi, function (match, hex) {
1327     var decodedChar = String.fromCharCode(parseInt(hex, 16));
1328     return /(?!["<>^`{|}?#%])[!-~]/.test(decodedChar) ? decodedChar : "%" + hex;
1329   });
1330   // Encode certain characters
1331   resource = resource.replace(/[<>^`{|}]]/g, function (character) {
1332     var charCode = character.charCodeAt(0);
1333     return "%" + (charCode < 16 ? "0" : "") + charCode.toString(16).toUpperCase();
1334   });
1335   var sanitizedResource = resource;
1336   // Ensure the resource starts with a slash
1337   if (resource[0] != "/") sanitizedResource = "/" + sanitizedResource;
1338   // Convert backslashes to slashes and handle duplicate slashes
1339   sanitizedResource = sanitizedResource.replace(/\\/g, "/").replace(allowDoubleSlashes ? /\/{3,}/g : /\/+/g, "/");
1340   // Handle relative navigation (e.g., "/./", "/../", "../", "./"), also remove trailing dots in paths
1341   sanitizedResource = sanitizedResource.replace(/\/\.(?:\.{2,})?(?=\/|$)/g, "").replace(/([^.\/])\.+(?=\/|$)/g, "$1");
1342   while (sanitizedResource.match(/\/(?!\.\.\/)[^\/]+\/\.\.(?=\/|$)/)) {
1343     sanitizedResource = sanitizedResource.replace(/\/(?!\.\.\/)[^\/]+\/\.\.(?=\/|$)/g, "");
1344   }
1345   sanitizedResource = sanitizedResource.replace(/\/\.\.(?=\/|$)/g, "");
1346   if (sanitizedResource.length == 0) return "/";
1347   else return sanitizedResource;
1350 // SVR.JS URL parser function
1351 function parseURL(uri, prepend) {
1352   // Replace newline characters with its respective URL encodings
1353   uri = uri.replace(/\r/g, "%0D").replace(/\n/g, "%0A");
1355   // If URL begins with a slash, prepend a string if available
1356   if (prepend && uri[0] == "/") uri = prepend.replace(/\/+$/,"") + uri;
1358   // Determine if URL has slashes
1359   var hasSlashes = (uri.indexOf("/") != -1);
1361   // Parse the URL using regular expression
1362   var parsedURI = uri.match(/^(?:([^:]+:)(\/\/)?)?(?:([^@]+)@)?([^:\/?#\*]+|\[[^\*]\/]\])?(?::([0-9]+))?(\*|\/[^?#]*)?(\?[^#]*)?(#[\S\s]*)?/);
1363   // Match 1: protocol
1364   // Match 2: slashes after protocol
1365   // Match 3: authentication credentials
1366   // Match 4: host name
1367   // Match 5: port
1368   // Match 6: path name
1369   // Match 7: query string
1370   // Match 8: hash
1372   // If regular expression didn't match the entire URL, throw an error
1373   if (parsedURI[0].length != uri.length) throw new Error("Invalid URL: " + uri);
1375   // If match 1 is not empty, set the slash variable based on state of match 2
1376   if (parsedURI[1]) hasSlashes = (parsedURI[2] == "//");
1378   // If match 6 is empty and URL has slashes, set it to a slash.
1379   if (hasSlashes && !parsedURI[6]) parsedURI[6] = "/";
1381   // If match 4 contains Unicode characters, convert it to Punycode. If the result is an empty string, throw an error
1382   if (parsedURI[4] && !parsedURI[4].match(/^[a-zA-Z0-9\.\-]+$/)) {
1383     parsedURI[4] = url.domainToASCII(parsedURI[4]);
1384     if (!parsedURI[4]) throw new Error("Invalid URL: " + uri);
1385   }
1387   // Create a new URL object
1388   var uobject = new url.Url();
1390   // Populate a URL object
1391   if (hasSlashes) uobject.slashes = true;
1392   if (parsedURI[1]) uobject.protocol = parsedURI[1];
1393   if (parsedURI[3]) uobject.auth = parsedURI[3];
1394   if (parsedURI[4]) {
1395     uobject.host = parsedURI[4] + (parsedURI[5] ? (":" + parsedURI[5]) : "");
1396     if (parsedURI[4][0] == "[") uobject.hostname = parsedURI[4].substring(1, parsedURI[4].length-1);
1397     else uobject.hostname = parsedURI[4];
1398   }
1399   if (parsedURI[5]) uobject.port = parsedURI[5];
1400   if (parsedURI[6]) uobject.pathname = parsedURI[6];
1401   if (parsedURI[7]) {
1402     uobject.search = parsedURI[7];
1403     // Parse query strings
1404     var qobject = Object.create(null);
1405     var parsedQuery = parsedURI[7].substring(1).match(/([^&=]*)(?:=([^&]*))?/g);
1406     parsedQuery.forEach(function (qp) {
1407       if (qp.length > 0) {
1408         var parsedQP = qp.match(/([^&=]*)(?:=([^&]*))?/);
1409         if (parsedQP) {
1410           qobject[parsedQP[1]] = parsedQP[2] ? parsedQP[2] : "";
1411         }
1412       }
1413     });
1414     uobject.query = qobject;
1415   } else {
1416     uobject.query = Object.create(null);
1417   }
1418   if (parsedURI[8]) uobject.hash = parsedURI[8];
1419   if (uobject.pathname) uobject.path = uobject.pathname + (uobject.search ? uobject.search : "");
1420   uobject.href = (uobject.protocol ? (uobject.protocol + (uobject.slashes ? "//" : "")) : "") + (uobject.auth ? (uobject.auth + "@") : "") + (uobject.hostname ? uobject.hostname : "") + (uobject.path ? uobject.path : "") + (uobject.hash ? uobject.hash : "");
1422   return uobject;
1425 // Node.JS mojibake URL fixing function
1426 function fixNodeMojibakeURL(string) {
1427   var encoded = "";
1429   //Encode URLs
1430   Buffer.from(string, "latin1").forEach(function (value) {
1431     if (value > 127) {
1432       encoded += "%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
1433     } else {
1434       encoded += String.fromCodePoint(value);
1435     }
1436   });
1438   //Upper case the URL encodings
1439   return encoded.replace(/%[0-9a-f-A-F]{2}/g, function (match) {
1440     return match.toUpperCase();
1441   });
1444 // SSL-related
1445 var key = "";
1446 var cert = "";
1448 if (secure) {
1449   if (!configJSON.key) configJSON.key = "cert/key.key";
1450   if (!configJSON.cert) configJSON.cert = "cert/cert.crt";
1451 } else {
1452   key = "SSL DISABLED";
1453   cert = "SSL DISABLED";
1454   configJSON.cert = "SSL DISABLED";
1455   configJSON.key = "SSL DISABLED";
1458 if (!fs.existsSync(__dirname + "/config.json")) {
1459   saveConfig();
1462 var certificateError = null;
1463 var sniReDos = false;
1465 // Load SNI
1466 if (secure) {
1467   try {
1468     key = fs.readFileSync((configJSON.key[0] != "/" && !configJSON.key.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + configJSON.key : configJSON.key).toString();
1469     cert = fs.readFileSync((configJSON.cert[0] != "/" && !configJSON.cert.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + configJSON.cert : configJSON.cert).toString();
1470     var sniNames = Object.keys(sni);
1471     var sniCredentials = [];
1472     sniNames.forEach(function (sniName) {
1473       if (typeof sniName === "string" && sniName.match(/\*[^*.:]*\*[^*.:]*(?:\.|:|$)/)) {
1474         sniReDos = true;
1475       }
1476       sniCredentials.push({
1477         name: sniName,
1478         cert: fs.readFileSync((sni[sniName].cert[0] != "/" && !sni[sniName].cert.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + sni[sniName].cert : sni[sniName].cert).toString(),
1479         key: fs.readFileSync((sni[sniName].key[0] != "/" && !sni[sniName].key.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + sni[sniName].key : sni[sniName].key).toString()
1480       });
1481     });
1482   } catch (err) {
1483     certificateError = err;
1484   }
1487 var logFile = undefined;
1488 var logSync = false;
1490 // Logging function
1491 function LOG(s) {
1492   try {
1493     if (configJSON.enableLogging || configJSON.enableLogging == undefined) {
1494       if (logSync) {
1495         fs.appendFileSync(__dirname + "/log/" + (cluster.isPrimary ? "master" : (cluster.isPrimary === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", "[" + new Date().toISOString() + "] " + s + "\r\n");
1496       } else {
1497         if (!logFile) {
1498           logFile = fs.createWriteStream(__dirname + "/log/" + (cluster.isPrimary ? "master" : (cluster.isPrimary === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", {
1499             flags: "a",
1500             autoClose: false
1501           });
1502           logFile.on("error", function (err) {
1503             if (!s.match(/^SERVER WARNING MESSAGE(?: \[Request Id: [0-9a-f]{6}\])?: There was a problem while saving logs! Logs will not be kept in log file\. Reason: /) && !reallyExiting) serverconsole.locwarnmessage("There was a problem while saving logs! Logs will not be kept in log file. Reason: " + err.message);
1504           });
1505         }
1506         if (logFile.writable) {
1507           logFile.write("[" + new Date().toISOString() + "] " + s + "\r\n");
1508         } else {
1509           throw new Error("Log file stream is closed.");
1510         }
1511       }
1512     }
1513   } catch (err) {
1514     if (!s.match(/^SERVER WARNING MESSAGE(?: \[Request Id: [0-9a-f]{6}\])?: There was a problem while saving logs! Logs will not be kept in log file\. Reason: /) && !reallyExiting) serverconsole.locwarnmessage("There was a problem while saving logs! Logs will not be kept in log file. Reason: " + err.message);
1515   }
1518 // Server console function
1519 var serverconsole = {
1520   climessage: function (msg) {
1521     if (msg.indexOf("\n") != -1) {
1522       msg.split("\n").forEach(function (nmsg) {
1523         serverconsole.climessage(nmsg);
1524       });
1525       return;
1526     }
1527     console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m: " + msg);
1528     LOG("SERVER CLI MESSAGE: " + msg);
1529     return;
1530   },
1531   reqmessage: function (msg) {
1532     if (msg.indexOf("\n") != -1) {
1533       msg.split("\n").forEach(function (nmsg) {
1534         serverconsole.reqmessage(nmsg);
1535       });
1536       return;
1537     }
1538     console.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m: " + msg + "\x1b[37m\x1b[0m");
1539     LOG("SERVER REQUEST MESSAGE: " + msg);
1540     return;
1541   },
1542   resmessage: function (msg) {
1543     if (msg.indexOf("\n") != -1) {
1544       msg.split("\n").forEach(function (nmsg) {
1545         serverconsole.resmessage(nmsg);
1546       });
1547       return;
1548     }
1549     console.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m: " + msg + "\x1b[37m\x1b[0m");
1550     LOG("SERVER RESPONSE MESSAGE: " + msg);
1551     return;
1552   },
1553   errmessage: function (msg) {
1554     if (msg.indexOf("\n") != -1) {
1555       msg.split("\n").forEach(function (nmsg) {
1556         serverconsole.errmessage(nmsg);
1557       });
1558       return;
1559     }
1560     console.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m: " + msg + "\x1b[37m\x1b[0m");
1561     LOG("SERVER RESPONSE ERROR MESSAGE: " + msg);
1562     return;
1563   },
1564   locerrmessage: function (msg) {
1565     if (msg.indexOf("\n") != -1) {
1566       msg.split("\n").forEach(function (nmsg) {
1567         serverconsole.locerrmessage(nmsg);
1568       });
1569       return;
1570     }
1571     console.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m: " + msg + "\x1b[40m\x1b[0m");
1572     LOG("SERVER ERROR MESSAGE: " + msg);
1573     return;
1574   },
1575   locwarnmessage: function (msg) {
1576     if (msg.indexOf("\n") != -1) {
1577       msg.split("\n").forEach(function (nmsg) {
1578         serverconsole.locwarnmessage(nmsg);
1579       });
1580       return;
1581     }
1582     console.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m: " + msg + "\x1b[40m\x1b[0m");
1583     LOG("SERVER WARNING MESSAGE: " + msg);
1584     return;
1585   },
1586   locmessage: function (msg) {
1587     if (msg.indexOf("\n") != -1) {
1588       msg.split("\n").forEach(function (nmsg) {
1589         serverconsole.locmessage(nmsg);
1590       });
1591       return;
1592     }
1593     console.log("\x1b[1mSERVER MESSAGE\x1b[22m: " + msg);
1594     LOG("SERVER MESSAGE: " + msg);
1595     return;
1596   }
1599 // Wrap around process.exit, so that log contents can be flushed.
1600 process.unsafeExit = process.exit;
1601 process.exit = function (code) {
1602   if (logFile && logFile.writable && !logFile.pending) {
1603     try {
1604       logFile.close(function () {
1605         logFile = undefined;
1606         logSync = true;
1607         process.unsafeExit(code);
1608       });
1609       if (process.isBun) {
1610         setInterval(function () {
1611           if (!logFile.writable) {
1612             logFile = undefined;
1613             logSync = true;
1614             process.unsafeExit(code);
1615           }
1616         }, 50); // Interval
1617       }
1618       setTimeout(function () {
1619         logFile = undefined;
1620         logSync = true;
1621         process.unsafeExit(code);
1622       }, 10000); // timeout
1623     } catch (err) {
1624       logFile = undefined;
1625       logSync = true;
1626       process.unsafeExit(code);
1627     }
1628   } else {
1629     logSync = true;
1630     process.unsafeExit(code);
1631   }
1634 // SVR.JS mod loader
1635 var modLoadingErrors = [];
1636 var SSJSError = undefined;
1638 // Load mods if the `disableMods` flag is not set
1639 if (!disableMods) {
1640   // Define the modloader folder name
1641   var modloaderFolderName = "modloader";
1642   if (cluster.isPrimary === false) {
1643     // If not the master process, create a unique modloader folder name for each worker
1644     modloaderFolderName = ".modloader_w" + Math.floor(Math.random() * 65536);
1645   }
1647   // Define the temporary server-side JavaScript file name
1648   var tempServerSideScriptName = "serverSideScript.js";
1649   if (!(process.isBun && process.versions.bun && process.versions.bun[0] == "0") && cluster.isPrimary === false) {
1650     // If not the master process and it's not Bun, create a unique temporary server-side JavaScript file name for each worker
1651     tempServerSideScriptName = ".serverSideScript_w" + Math.floor(Math.random() * 65536) + ".js";
1652   }
1654   // Iterate through the list of mod files
1655   modFiles.forEach(function (modFileRaw) {
1656     // Build the path to the current mod file
1657     var modFile = __dirname + "/mods/" + modFileRaw;
1659     try {
1660       // Try creating the modloader folder (if not already exists)
1661       try {
1662         fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName);
1663       } catch (err) {
1664         // If the folder already exists, continue to the next step
1665         if (err.code != "EEXIST") {
1666           // If there was another error, try creating the temp folder and then the modloader folder again
1667           fs.mkdirSync(__dirname + "/temp");
1668           try {
1669             fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName);
1670           } catch (err) {
1671             // If there was another error, throw it
1672             if (err.code != "EEXIST") throw err;
1673           }
1674         }
1675       }
1677       // Create a subfolder for the current mod within the modloader folder
1678       fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFileRaw);
1679     } catch (err) {
1680       // If there was an error creating the folder, ignore it if it's a known error
1681       if (err.code != "EEXIST" && err.code != "ENOENT") throw err;
1682       // Some other SVR.JS process may have created the files.
1683     }
1685     // Check if the current mod file is a regular file
1686     if (fs.statSync(modFile).isFile()) {
1687       try {
1688         // Determine if the mod file is a ".tar.gz" file or not
1689         if (modFile.indexOf(".tar.gz") == modFile.length - 7) {
1690           // If it's a ".tar.gz" file, extract its contents using `tar`
1691           if (tar._errored) throw tar._errored;
1692           tar.x({
1693             file: modFile,
1694             sync: true,
1695             C: __dirname + "/temp/" + modloaderFolderName + "/" + modFileRaw
1696           });
1697         } else {
1698           // If it's not a ".tar.gz" file, throw an error about `svrmodpack` support being dropped
1699           throw new Error("This version of SVR.JS no longer supports \"svrmodpack\" library for SVR.JS mods. Please consider using newer mods with .tar.gz format.");
1700         }
1702         // Initialize variables for mod loading
1703         var Mod = undefined;
1704         var mod = undefined;
1706         // Attempt to require the mod's index.js file, retrying up to 3 times in case of syntax errors
1707         for (var j = 0; j < 3; j++) {
1708           try {
1709             Mod = require("./temp/" + modloaderFolderName + "/" + modFileRaw + "/index.js");
1710             mod = new Mod();
1711             break;
1712           } catch (err) {
1713             if (j >= 2 || err.name == "SyntaxError") throw err;
1714             // Wait for a short time before retrying
1715             var now = Date.now();
1716             while (Date.now() - now < 2);
1717             // Try reloading mod
1718           }
1719         }
1721         // Add the loaded mod to the mods list
1722         mods.push(mod);
1724         // Attempt to read the mod's info file, retrying up to 3 times
1725         for (var j = 0; j < 3; j++) {
1726           try {
1727             modInfos.push(JSON.parse(fs.readFileSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFileRaw + "/mod.info")));
1728             break;
1729           } catch (err) {
1730             if (j >= 2) {
1731               // If failed to read info file, add a placeholder entry to modInfos with an error message
1732               modInfos.push({
1733                 name: "Unknown mod (" + modFileRaw + ";" + err.message + ")",
1734                 version: "ERROR"
1735               });
1736             }
1737             // Wait for a short time before retrying
1738             var now = Date.now();
1739             while (Date.now() - now < 2);
1740             // Try reloading mod info
1741           }
1742         }
1743       } catch (err) {
1744         modLoadingErrors.push({
1745           error: err,
1746           modName: modFileRaw
1747         });
1748       }
1749     }
1750   });
1752   // Determine path of server-side script file
1753   var SSJSPath = "./serverSideScript.js";
1754   if (!useWebRootServerSideScript) SSJSPath = __dirname + "/serverSideScript.js";
1756   // Check if a custom server side script file exists
1757   if (fs.existsSync(SSJSPath) && fs.statSync(SSJSPath).isFile()) {
1758     try {
1759       // Prepend necessary modules and variables to the custom server side script
1760       var modhead = "var readline = require('readline');\r\nvar os = require('os');\r\nvar http = require('http');\r\nvar url = require('url');\r\nvar fs = require('fs');\r\nvar path = require('path');\r\n" + (hexstrbase64 === undefined ? "" : "var hexstrbase64 = require('../hexstrbase64/index.js');\r\n") + (crypto.__disabled__ === undefined ? "var crypto = require('crypto');\r\nvar https = require('https');\r\n" : "") + "var stream = require('stream');\r\nvar customvar1;\r\nvar customvar2;\r\nvar customvar3;\r\nvar customvar4;\r\n\r\nfunction Mod() {}\r\nMod.prototype.callback = function callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, defaultpage, users, page404, head, foot, fd, elseCallback, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData, authUser) {\r\nreturn function () {\r\nvar disableEndElseCallbackExecute = false;\r\nfunction filterHeaders(e){var r={};return Object.keys(e).forEach((function(t){null!==e[t]&&void 0!==e[t]&&(\"object\"==typeof e[t]?r[t]=JSON.parse(JSON.stringify(e[t])):r[t]=e[t])})),r}\r\nfunction checkHostname(e){if(void 0===e||\"*\"==e)return!0;if(req.headers.host&&0==e.indexOf(\"*.\")&&\"*.\"!=e){var r=e.substring(2);if(req.headers.host==r||req.headers.host.indexOf(\".\"+r)==req.headers.host.length-r.length-1)return!0}else if(req.headers.host&&req.headers.host==e)return!0;return!1}\r\nfunction checkHref(e){return href==e||\"win32\"==os.platform()&&href.toLowerCase()==e.toLowerCase()}\r\n";
1761       var modfoot = "\r\nif(!disableEndElseCallbackExecute) {\r\ntry{\r\nelseCallback();\r\n} catch(err) {\r\n}\r\n}\r\n}\r\n}\r\nmodule.exports = Mod;";
1762       // Write the modified server side script to the temp folder
1763       fs.writeFileSync(__dirname + "/temp/" + tempServerSideScriptName, modhead + fs.readFileSync(SSJSPath) + modfoot);
1765       // Initialize variables for server side script loading
1766       var aMod = undefined;
1767       var amod = undefined;
1769       // Attempt to require the custom server side script, retrying up to 5 times
1770       for (var i = 0; i < 5; i++) {
1771         try {
1772           aMod = require("./temp/" + tempServerSideScriptName);
1773           amod = new aMod();
1774           break;
1775         } catch (err) {
1776           if (i >= 4 || err.name == "SyntaxError") throw err;
1777           // Wait for a short time before retrying
1778           var now = Date.now();
1779           while (Date.now() - now < 2);
1780           // Try reloading mod
1781         }
1782       }
1784       // Add the loaded server side script to the mods list
1785       mods.push(amod);
1786     } catch (err) {
1787       SSJSError = err;
1788     }
1789   }
1793 // SHA256 function
1794 function sha256(s) {
1795   if (crypto.__disabled__ === undefined) {
1796     var hash = crypto.createHash("SHA256");
1797     hash.update(s);
1798     return hash.digest("hex");
1799   } else {
1800     var chrsz = 8;
1801     var hexcase = 0;
1803     function safeAdd(x, y) {
1804       var lsw = (x & 0xFFFF) + (y & 0xFFFF);
1805       var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
1806       return (msw << 16) | (lsw & 0xFFFF);
1807     }
1809     function S(X, n) {
1810       return (X >>> n) | (X << (32 - n));
1811     }
1813     function R(X, n) {
1814       return (X >>> n);
1815     }
1817     function Ch(x, y, z) {
1818       return ((x & y) ^ ((~x) & z));
1819     }
1821     function Maj(x, y, z) {
1822       return ((x & y) ^ (x & z) ^ (y & z));
1823     }
1825     function Sigma0256(x) {
1826       return (S(x, 2) ^ S(x, 13) ^ S(x, 22));
1827     }
1829     function Sigma1256(x) {
1830       return (S(x, 6) ^ S(x, 11) ^ S(x, 25));
1831     }
1833     function Gamma0256(x) {
1834       return (S(x, 7) ^ S(x, 18) ^ R(x, 3));
1835     }
1837     function Gamma1256(x) {
1838       return (S(x, 17) ^ S(x, 19) ^ R(x, 10));
1839     }
1841     function coreSha256(m, l) {
1842       var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
1843       var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
1844       var W = new Array(64);
1845       var a, b, c, d, e, f, g, h, i, j;
1846       var T1, T2;
1848       m[l >> 5] |= 0x80 << (24 - l % 32);
1849       m[((l + 64 >> 9) << 4) + 15] = l;
1851       for (var i = 0; i < m.length; i += 16) {
1852         a = HASH[0];
1853         b = HASH[1];
1854         c = HASH[2];
1855         d = HASH[3];
1856         e = HASH[4];
1857         f = HASH[5];
1858         g = HASH[6];
1859         h = HASH[7];
1861         for (var j = 0; j < 64; j++) {
1862           if (j < 16) W[j] = m[j + i];
1863           else W[j] = safeAdd(safeAdd(safeAdd(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
1865           T1 = safeAdd(safeAdd(safeAdd(safeAdd(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
1866           T2 = safeAdd(Sigma0256(a), Maj(a, b, c));
1868           h = g;
1869           g = f;
1870           f = e;
1871           e = safeAdd(d, T1);
1872           d = c;
1873           c = b;
1874           b = a;
1875           a = safeAdd(T1, T2);
1876         }
1878         HASH[0] = safeAdd(a, HASH[0]);
1879         HASH[1] = safeAdd(b, HASH[1]);
1880         HASH[2] = safeAdd(c, HASH[2]);
1881         HASH[3] = safeAdd(d, HASH[3]);
1882         HASH[4] = safeAdd(e, HASH[4]);
1883         HASH[5] = safeAdd(f, HASH[5]);
1884         HASH[6] = safeAdd(g, HASH[6]);
1885         HASH[7] = safeAdd(h, HASH[7]);
1886       }
1887       return HASH;
1888     }
1890     function str2binb(str) {
1891       var bin = Array();
1892       var mask = (1 << chrsz) - 1;
1893       for (var i = 0; i < str.length * chrsz; i += chrsz) {
1894         bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32);
1895       }
1896       return bin;
1897     }
1899     function Utf8Encode(string) {
1900       string = string.replace(/\r\n/g, "\n");
1901       var utftext = "";
1903       for (var n = 0; n < string.length; n++) {
1905         var c = string.charCodeAt(n);
1907         if (c < 128) {
1908           utftext += String.fromCharCode(c);
1909         } else if ((c > 127) && (c < 2048)) {
1910           utftext += String.fromCharCode((c >> 6) | 192);
1911           utftext += String.fromCharCode((c & 63) | 128);
1912         } else {
1913           utftext += String.fromCharCode((c >> 12) | 224);
1914           utftext += String.fromCharCode(((c >> 6) & 63) | 128);
1915           utftext += String.fromCharCode((c & 63) | 128);
1916         }
1918       }
1920       return utftext;
1921     }
1923     function binb2hex(binarray) {
1924       var hexTab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
1925       var str = "";
1926       for (var i = 0; i < binarray.length * 4; i++) {
1927         str += hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) +
1928           hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF);
1929       }
1930       return str;
1931     }
1933     s = Utf8Encode(s);
1934     return binb2hex(coreSha256(str2binb(s), s.length * chrsz));
1935   }
1938 // Function to get URL path for use in forbidden path adding.
1939 function getInitializePath(to) {
1940   var cwd = process.cwd();
1941   if (os.platform() == "win32") {
1942     to = to.replace(/\//g, "\\");
1943     if (to[0] == "\\") to = cwd.split("\\")[0] + to;
1944   }
1945   var absoluteTo = path.isAbsolute(to) ? to : (__dirname + (os.platform() == "win32" ? "\\" : "/") + to);
1946   if (os.platform() == "win32" && cwd[0] != absoluteTo[0]) return "";
1947   var relative = path.relative(cwd, absoluteTo);
1948   if (os.platform() == "win32") {
1949     return "/" + relative.replace(/\\/g, "/");
1950   } else {
1951     return "/" + relative;
1952   }
1955 // Function to check if URL path name is a forbidden path.
1956 function isForbiddenPath(decodedHref, match) {
1957   var forbiddenPath = forbiddenPaths[match];
1958   if (!forbiddenPath) return false;
1959   if (typeof forbiddenPath === "string") {
1960     return decodedHref === forbiddenPath || (os.platform() === "win32" && decodedHref.toLowerCase() === forbiddenPath.toLowerCase());
1961   }
1962   if (typeof forbiddenPath === "object") {
1963     return forbiddenPath.some(function (forbiddenPathSingle) {
1964       return (decodedHref === forbiddenPathSingle || (os.platform() === "win32" && decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase()));
1965     });
1966   }
1967   return false;
1970 // Function to check if URL path name is index of one of defined forbidden paths.
1971 function isIndexOfForbiddenPath(decodedHref, match) {
1972   var forbiddenPath = forbiddenPaths[match];
1973   if (!forbiddenPath) return false;
1974   if (typeof forbiddenPath === "string") {
1975     return decodedHref === forbiddenPath || decodedHref.indexOf(forbiddenPath + "/") === 0 || (os.platform() === "win32" && (decodedHref.toLowerCase() === forbiddenPath.toLowerCase() || decodedHref.toLowerCase().indexOf(forbiddenPath.toLowerCase() + "/") === 0));
1976   }
1977   if (typeof forbiddenPath === "object") {
1978     return forbiddenPath.some(function (forbiddenPathSingle) {
1979       return (decodedHref === forbiddenPathSingle || decodedHref.indexOf(forbiddenPathSingle + "/") === 0 || (os.platform() === "win32" && (decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase() || decodedHref.toLowerCase().indexOf(forbiddenPathSingle.toLowerCase() + "/") === 0)));
1980     });
1981   }
1982   return false;
1985 // Set up forbidden paths
1986 var forbiddenPaths = {};
1988 forbiddenPaths.config = getInitializePath("./config.json");
1989 forbiddenPaths.certificates = [];
1990 if (secure) {
1991   forbiddenPaths.certificates.push(getInitializePath(configJSON.cert));
1992   forbiddenPaths.certificates.push(getInitializePath(configJSON.key));
1993   Object.keys(sni).forEach(function (sniHostName) {
1994     forbiddenPaths.certificates.push(getInitializePath(sni[sniHostName].cert));
1995     forbiddenPaths.certificates.push(getInitializePath(sni[sniHostName].key));
1996   });
1998 forbiddenPaths.svrjs = getInitializePath("./" + ((__dirname[__dirname.length - 1] != "/") ? __filename.replace(__dirname + "/", "") : __filename.replace(__dirname, "")));
1999 forbiddenPaths.serverSideScripts = [];
2000 if (useWebRootServerSideScript) {
2001   forbiddenPaths.serverSideScripts.push("/serverSideScript.js");
2002 } else {
2003   forbiddenPaths.serverSideScripts.push(getInitializePath("./serverSideScript.js"));
2005 forbiddenPaths.serverSideScriptDirectories = [];
2006 forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./node_modules"));
2007 forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./mods"));
2008 forbiddenPaths.temp = getInitializePath("./temp");
2009 forbiddenPaths.log = getInitializePath("./log");
2011 // HTTP error descriptions
2012 var serverHTTPErrorDescs = {
2013   200: "The request succeeded! :)",
2014   201: "A new resource has been created.",
2015   202: "The request has been accepted for processing, but the processing has not been completed.",
2016   400: "The request you made is invalid.",
2017   401: "You need to authenticate yourself in order to access the requested file.",
2018   402: "You need to pay in order to access the requested file.",
2019   403: "You don't have access to the requested file.",
2020   404: "The requested file doesn't exist. If you have typed the URL manually, then please check the spelling.",
2021   405: "Method used to access the requested file isn't allowed.",
2022   406: "The request is capable of generating only unacceptable content.",
2023   407: "You need to authenticate yourself in order to use the proxy.",
2024   408: "You have timed out.",
2025   409: "The request you sent conflicts with the current state of the server.",
2026   410: "The requested file is permanently deleted.",
2027   411: "Content-Length property is required.",
2028   412: "The server doesn't meet the preconditions you put in the request.",
2029   413: "The request you sent is too large.",
2030   414: "The URL you sent is too long.",
2031   415: "The media type of request you sent isn't supported by the server.",
2032   416: "The requested content range (Content-Range header) you sent is unsatisfiable.",
2033   417: "The expectation specified in the Expect property couldn't be satisfied.",
2034   418: "The server (teapot) can't brew any coffee! ;)",
2035   421: "The request you made isn't intended for this server.",
2036   422: "The server couldn't process content sent by you.",
2037   423: "The requested file is locked.",
2038   424: "The request depends on another failed request.",
2039   425: "The server is unwilling to risk processing a request that might be replayed.",
2040   426: "You need to upgrade the protocols you use to request a file.",
2041   428: "The request you sent needs to be conditional, but it isn't.",
2042   429: "You sent too many requests to the server.",
2043   431: "The request you sent contains headers that are too large.",
2044   451: "The requested file isn't accessible for legal reasons.",
2045   497: "You sent a non-TLS request to the HTTPS server.",
2046   500: "The server had an unexpected error. Below, the error stack is shown: </p><code>{stack}</code><p>You may need to contact the server administrator at <i>{contact}</i>.",
2047   501: "The request requires the use of a function, which isn't currently implemented by the server.",
2048   502: "The server had an error while it was acting as a gateway.</p><p>You may need to contact the server administrator at <i>{contact}</i>.",
2049   503: "The service provided by the server is currently unavailable, possibly due to maintenance downtime or capacity problems. Please try again later.</p><p>You may need to contact the server administrator at <i>{contact}</i>.",
2050   504: "The server couldn't get a response in time while it was acting as a gateway.</p><p>You may need to contact the server administrator at <i>{contact}</i>.",
2051   505: "The server doesn't support the HTTP version used in the request.",
2052   506: "The Variant header is configured to be engaged in content negotiation.</p><p>You may need to contact the server administrator at <i>{contact}</i>.",
2053   507: "The server ran out of disk space necessary to complete the request.",
2054   508: "The server detected an infinite loop while processing the request.",
2055   509: "The server has its bandwidth limit exceeded.</p><p>You may need to contact the server administrator at <i>{contact}</i>.",
2056   510: "The server requires an extended HTTP request. The request you made isn't an extended HTTP request.",
2057   511: "You need to authenticate yourself in order to get network access.",
2058   598: "The server couldn't get a response in time while it was acting as a proxy.",
2059   599: "The server couldn't connect in time while it was acting as a proxy."
2062 // Server error descriptions
2063 var serverErrorDescs = {
2064   "EADDRINUSE": "Address is already in use by another process.",
2065   "EADDRNOTAVAIL": "Address is not available on this machine.",
2066   "EACCES": "Permission denied. You may not have sufficient privileges to access the requested address.",
2067   "EAFNOSUPPORT": "Address family not supported. The address family (IPv4 or IPv6) of the requested address is not supported.",
2068   "EALREADY": "Operation already in progress. The server is already in the process of establishing a connection on the requested address.",
2069   "ECONNABORTED": "Connection aborted. The connection to the server was terminated abruptly.",
2070   "ECONNREFUSED": "Connection refused. The server refused the connection attempt.",
2071   "ECONNRESET": "Connection reset by peer. The connection to the server was reset by the remote host.",
2072   "EDESTADDRREQ": "Destination address required. The destination address must be specified.",
2073   "EINVAL": "Invalid argument (invalid IP address?).",
2074   "ENETDOWN": "Network is down. The network interface used for the connection is not available.",
2075   "ENETUNREACH": "Network is unreachable. The network destination is not reachable from this host.",
2076   "ENOBUFS": "No buffer space available. Insufficient buffer space is available for the server to process the request.",
2077   "ENOTFOUND": "Domain name doesn't exist (invalid IP address?).",
2078   "ENOTSOCK": "Not a socket. The file descriptor provided is not a valid socket.",
2079   "EPROTO": "Protocol error. An unspecified protocol error occurred.",
2080   "EPROTONOSUPPORT": "Protocol not supported. The requested network protocol is not supported.",
2081   "ETIMEDOUT": "Connection timed out. The server did not respond within the specified timeout period.",
2082   "UNKNOWN": "There was an unknown error with the server."
2085 // Create server instances
2086 if (!cluster.isPrimary) {
2087   var reqcounter = 0;
2088   var malformedcounter = 0;
2089   var err4xxcounter = 0;
2090   var err5xxcounter = 0;
2091   var reqcounterKillReq = 0;
2092   var server = {};
2093   var server2 = {};
2094   try {
2095     server2 = http.createServer({
2096       requireHostHeader: false
2097     });
2098   } catch (err) {
2099     server2 = http.createServer();
2100   }
2101   server2.on("request", function (req, res) {
2102     reqhandler(req, res, false);
2103   });
2104   server2.on("checkExpectation", reqhandler);
2105   server2.on("clientError", function (err, socket) {
2106     reqerrhandler(err, socket, false);
2107   });
2108   if (!disableToHTTPSRedirect) {
2109     server2.on("connect", function (request, socket) {
2110       var reqIdInt = Math.floor(Math.random() * 16777216);
2111       if (reqIdInt == 16777216) reqIdInt = 0;
2112       var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
2113       var serverconsole = {
2114         climessage: function (msg) {
2115           if (msg.indexOf("\n") != -1) {
2116             msg.split("\n").forEach(function (nmsg) {
2117               serverconsole.climessage(nmsg);
2118             });
2119             return;
2120           }
2121           console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2122           LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2123           return;
2124         },
2125         reqmessage: function (msg) {
2126           if (msg.indexOf("\n") != -1) {
2127             msg.split("\n").forEach(function (nmsg) {
2128               serverconsole.reqmessage(nmsg);
2129             });
2130             return;
2131           }
2132           console.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2133           LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
2134           return;
2135         },
2136         resmessage: function (msg) {
2137           if (msg.indexOf("\n") != -1) {
2138             msg.split("\n").forEach(function (nmsg) {
2139               serverconsole.resmessage(nmsg);
2140             });
2141             return;
2142           }
2143           console.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2144           LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
2145           return;
2146         },
2147         errmessage: function (msg) {
2148           if (msg.indexOf("\n") != -1) {
2149             msg.split("\n").forEach(function (nmsg) {
2150               serverconsole.errmessage(nmsg);
2151             });
2152             return;
2153           }
2154           console.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2155           LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2156           return;
2157         },
2158         locerrmessage: function (msg) {
2159           if (msg.indexOf("\n") != -1) {
2160             msg.split("\n").forEach(function (nmsg) {
2161               serverconsole.locerrmessage(nmsg);
2162             });
2163             return;
2164           }
2165           console.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2166           LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2167           return;
2168         },
2169         locwarnmessage: function (msg) {
2170           if (msg.indexOf("\n") != -1) {
2171             msg.split("\n").forEach(function (nmsg) {
2172               serverconsole.locwarnmessage(nmsg);
2173             });
2174             return;
2175           }
2176           console.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2177           LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
2178           return;
2179         },
2180         locmessage: function (msg) {
2181           if (msg.indexOf("\n") != -1) {
2182             msg.split("\n").forEach(function (nmsg) {
2183               serverconsole.locmessage(nmsg);
2184             });
2185             return;
2186           }
2187           console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2188           LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
2189           return;
2190         }
2191       };
2192       socket.on("close", function (hasError) {
2193         if (!hasError) serverconsole.locmessage("Client disconnected.");
2194         else serverconsole.locmessage("Client disconnected due to error.");
2195       });
2196       socket.on("error", function () {});
2197       var reqip = socket.remoteAddress;
2198       var reqport = socket.remotePort;
2199       serverconsole.locmessage("Somebody connected to " + (typeof port == "number" ? "port " : "socket ") + port + "...");
2200       reqcounter++;
2201       serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants to proxy " + request.url + " through this server");
2202       if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]);
2203       serverconsole.errmessage("This server will never be a proxy.");
2204       if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n");
2205     });
2206   } else {
2207     server2.on("connect", connhandler);
2208   }
2209   server2.on("error", function (err) {
2210     serverErrorHandler(err, true);
2211   });
2212   server2.on("listening", function () {
2213     attmtsRedir = 5;
2214     listeningMessage();
2215   });
2217   if (configJSON.enableHTTP2 == true) {
2218     if (secure) {
2219       server = http2.createSecureServer({
2220         allowHTTP1: true,
2221         requireHostHeader: false,
2222         key: key,
2223         cert: cert,
2224         requestCert: configJSON.useClientCertificate,
2225         rejectUnauthorized: configJSON.rejectUnauthorizedClientCertificates,
2226         ciphers: configJSON.cipherSuite,
2227         ecdhCurve: configJSON.ecdhCurve,
2228         minVersion: configJSON.tlsMinVersion,
2229         maxVersion: configJSON.tlsMaxVersion,
2230         sigalgs: configJSON.signatureAlgorithms,
2231         settings: configJSON.http2Settings
2232       });
2233     } else {
2234       server = http2.createServer({
2235         allowHTTP1: true,
2236         requireHostHeader: false,
2237         settings: configJSON.http2Settings
2238       });
2239     }
2240   } else {
2241     if (secure) {
2242       server = https.createServer({
2243         key: key,
2244         cert: cert,
2245         requireHostHeader: false,
2246         requestCert: configJSON.useClientCertificate,
2247         rejectUnauthorized: configJSON.rejectUnauthorizedClientCertificates,
2248         ciphers: configJSON.cipherSuite,
2249         ecdhCurve: configJSON.ecdhCurve,
2250         minVersion: configJSON.tlsMinVersion,
2251         maxVersion: configJSON.tlsMaxVersion,
2252         sigalgs: configJSON.signatureAlgorithms
2253       });
2254     } else {
2255       try {
2256         server = http.createServer({
2257           requireHostHeader: false
2258         });
2259       } catch (err) {
2260         server = http.createServer();
2261       }
2262     }
2263   }
2264   if (secure) {
2265     try {
2266       sniCredentials.forEach(function (sniCredentialsSingle) {
2267         server.addContext(sniCredentialsSingle.name, {
2268           cert: sniCredentialsSingle.cert,
2269           key: sniCredentialsSingle.key
2270         });
2271         try {
2272           var snMatches = sniCredentialsSingle.name.match(/^([^:[]*|\[[^]]*\]?)((?::.*)?)$/);
2273           if (!snMatches[1][0].match(/^\.+$/)) snMatches[1][0] = snMatches[1][0].replace(/\.+$/, "");
2274           server._contexts[server._contexts.length - 1][0] = new RegExp("^" + snMatches[1].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.:]*") + ((snMatches[1][0] == "[" || snMatches[1].match(/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/)) ? "" : "\.?") + snMatches[2].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.]*") + "$", "i");
2275         } catch (ex) {
2276           // Can't replace regex, ignoring...
2277         }
2278       });
2279     } catch (err) {
2280       // SNI error
2281     }
2282   }
2283   server.on("request", reqhandler);
2284   server.on("checkExpectation", reqhandler);
2285   server.on("connect", connhandler);
2286   server.on("clientError", reqerrhandler);
2288   if (secure) {
2289     server.prependListener("connection", function (sock) {
2290       sock.reallyDestroy = sock.destroy;
2291       sock.destroy = function () {
2292         sock.toDestroy = true;
2293       };
2294     });
2296     server.prependListener("tlsClientError", function (err, sock) {
2297       if (err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) {
2298         sock._parent.destroy = sock._parent.reallyDestroy;
2299         sock._readableState = sock._parent._readableState;
2300         sock._writableState = sock._parent._writableState;
2301         sock._parent.toDestroy = false;
2302         sock.pipe = function (a, b, c) {
2303           sock._parent.pipe(a, b, c);
2304         };
2305         sock.write = function (a, b, c) {
2306           sock._parent.write(a, b, c);
2307         };
2308         sock.end = function (a, b, c) {
2309           sock._parent.end(a, b, c);
2310         };
2311         sock.destroyed = sock._parent.destroyed;
2312         sock.readable = sock._parent.readable;
2313         sock.writable = sock._parent.writable;
2314         sock.remoteAddress = sock._parent.remoteAddress;
2315         sock.remotePort = sock._parent.remoteAddress;
2316         sock.destroy = function (a, b, c) {
2317           try {
2318             sock._parent.destroy(a, b, c);
2319             sock.destroyed = sock._parent.destroyed;
2320           } catch (err) {
2321             // Socket is probably already destroyed.
2322           }
2323         };
2324       } else {
2325         sock._parent.destroy = sock._parent.reallyDestroy;
2326         try {
2327           if (sock._parent.toDestroy) sock._parent.destroy();
2328         } catch (err) {
2329           // Socket is probably already destroyed.
2330         }
2331       }
2332     });
2334     server.prependListener("secureConnection", function (sock) {
2335       sock._parent.destroy = sock._parent.reallyDestroy;
2336       delete sock._parent.reallyDestroy;
2337     });
2339     if (configJSON.enableOCSPStapling && !ocsp._errored) {
2340       server.on("OCSPRequest", function (cert, issuer, callback) {
2341         ocsp.getOCSPURI(cert, function (err, uri) {
2342           if (err) return callback(err);
2344           var req = ocsp.request.generate(cert, issuer);
2345           var options = {
2346             url: uri,
2347             ocsp: req.data
2348           };
2350           ocspCache.request(req.id, options, callback);
2351         });
2352       });
2353     }
2354   }
2356   // Patches from Node.JS v18.0.0
2357   if (server.requestTimeout !== undefined && server.requestTimeout === 0) server.requestTimeout = 300000;
2358   if (server2.requestTimeout !== undefined && server2.requestTimeout === 0) server2.requestTimeout = 300000;
2360   function reqerrhandler(err, socket, fromMain) {
2361     if (fromMain === undefined) fromMain = true;
2362     // Define response object similar to Node.JS native one
2363     var res = {};
2364     res.socket = socket;
2365     res.write = function (x) {
2366       if (err.code === "ECONNRESET" || !socket.writable) {
2367         return;
2368       }
2369       socket.write(x);
2370     };
2371     res.end = function (x) {
2372       if (err.code === "ECONNRESET" || !socket.writable) {
2373         return;
2374       }
2375       socket.end(x, function () {
2376         try {
2377           socket.destroy();
2378         } catch (err) {
2379           // Socket is probably already destroyed
2380         }
2381       });
2382     };
2383     res.writeHead = function (code, name, headers) {
2384       if (code >= 400 && code <= 499) err4xxcounter++;
2385       if (code >= 500 && code <= 599) err5xxcounter++;
2386       var head = ("HTTP/1.1 " + code.toString() + " " + name + "\r\n");
2387       var headers = JSON.parse(JSON.stringify(headers));
2388       headers["Date"] = (new Date()).toGMTString();
2389       headers["Connection"] = "close";
2390       Object.keys(headers).forEach(function (headername) {
2391         if (headername.toLowerCase() == "set-cookie") {
2392           headers[headername].forEach(function (headerValueS) {
2393             if (headername.match(/[^\x09\x20-\x7e\x80-\xff]|.:/) || headerValueS.match(/[^\x09\x20-\x7e\x80-\xff]/)) throw new Error("Invalid header!!! (" + headername + ")");
2394             head += (headername + ": " + headerValueS);
2395           });
2396         } else {
2397           if (headername.match(/[^\x09\x20-\x7e\x80-\xff]|.:/) || headers[headername].match(/[^\x09\x20-\x7e\x80-\xff]/)) throw new Error("Invalid header!!! (" + headername + ")");
2398           head += (headername + ": " + headers[headername]);
2399         }
2400         head += "\r\n";
2401       });
2402       head += ("\r\n");
2403       res.write(head);
2404     };
2406     var reqIdInt = Math.floor(Math.random() * 16777216);
2407     if (reqIdInt == 16777216) reqIdInt = 0;
2408     var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
2409     var serverconsole = {
2410       climessage: function (msg) {
2411         if (msg.indexOf("\n") != -1) {
2412           msg.split("\n").forEach(function (nmsg) {
2413             serverconsole.climessage(nmsg);
2414           });
2415           return;
2416         }
2417         console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2418         LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2419         return;
2420       },
2421       reqmessage: function (msg) {
2422         if (msg.indexOf("\n") != -1) {
2423           msg.split("\n").forEach(function (nmsg) {
2424             serverconsole.reqmessage(nmsg);
2425           });
2426           return;
2427         }
2428         console.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2429         LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
2430         return;
2431       },
2432       resmessage: function (msg) {
2433         if (msg.indexOf("\n") != -1) {
2434           msg.split("\n").forEach(function (nmsg) {
2435             serverconsole.resmessage(nmsg);
2436           });
2437           return;
2438         }
2439         console.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2440         LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
2441         return;
2442       },
2443       errmessage: function (msg) {
2444         if (msg.indexOf("\n") != -1) {
2445           msg.split("\n").forEach(function (nmsg) {
2446             serverconsole.errmessage(nmsg);
2447           });
2448           return;
2449         }
2450         console.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2451         LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2452         return;
2453       },
2454       locerrmessage: function (msg) {
2455         if (msg.indexOf("\n") != -1) {
2456           msg.split("\n").forEach(function (nmsg) {
2457             serverconsole.locerrmessage(nmsg);
2458           });
2459           return;
2460         }
2461         console.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2462         LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2463         return;
2464       },
2465       locwarnmessage: function (msg) {
2466         if (msg.indexOf("\n") != -1) {
2467           msg.split("\n").forEach(function (nmsg) {
2468             serverconsole.locwarnmessage(nmsg);
2469           });
2470           return;
2471         }
2472         console.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2473         LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
2474         return;
2475       },
2476       locmessage: function (msg) {
2477         if (msg.indexOf("\n") != -1) {
2478           msg.split("\n").forEach(function (nmsg) {
2479             serverconsole.locmessage(nmsg);
2480           });
2481           return;
2482         }
2483         console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2484         LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
2485         return;
2486       }
2487     };
2488     socket.on("close", function (hasError) {
2489       if (!hasError || err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) serverconsole.locmessage("Client disconnected.");
2490       else serverconsole.locmessage("Client disconnected due to error.");
2491     });
2492     socket.on("error", function () {});
2494     // Header and footer placeholders
2495     var head = "";
2496     var foot = "";
2498     function responseEnd(body) {
2499       // If body is Buffer, then it is converted to String anyway.
2500       res.write(head + body + foot);
2501       res.end();
2502     }
2504     // Server error calling method
2505     function callServerError(errorCode, extName, stack, ch) {
2506       if (typeof errorCode !== "number") {
2507         throw new TypeError("HTTP error code parameter needs to be an integer.");
2508       }
2510       // Handle optional parameters
2511       if (extName && typeof extName === "object") {
2512         ch = stack;
2513         stack = extName;
2514         extName = undefined;
2515       } else if (typeof extName !== "string" && extName !== null && extName !== undefined) {
2516         throw new TypeError("Extension name parameter needs to be a string.");
2517       }
2519       if (stack && typeof stack === "object" && Object.prototype.toString.call(stack) !== "[object Error]") {
2520         ch = stack;
2521         stack = undefined;
2522       } else if (typeof stack !== "object" && typeof stack !== "string" && stack) {
2523         throw new TypeError("Error stack parameter needs to be either a string or an instance of Error object.");
2524       }
2526       // Determine error file
2527       function getErrorFileName(list, callback, _i) {
2528         if (err.code == "ERR_SSL_HTTP_REQUEST" && process.version && parseInt(process.version.split(".")[0].substring(1)) >= 16) {
2529           // Disable custom error page for HTTP SSL error
2530           callback(errorCode.toString() + ".html");
2531           return;
2532         }
2534         function medCallback(p) {
2535           if (p) callback(p);
2536           else {
2537             if (errorCode == 404) {
2538               fs.access(page404, fs.constants.F_OK, function (err) {
2539                 if (err) {
2540                   fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
2541                     try {
2542                       if (err) {
2543                         callback(errorCode.toString() + ".html");
2544                       } else {
2545                         callback("." + errorCode.toString());
2546                       }
2547                     } catch (err2) {
2548                       callServerError(500, err2);
2549                     }
2550                   });
2551                 } else {
2552                   try {
2553                     callback(page404);
2554                   } catch (err2) {
2555                     callServerError(500, err2);
2556                   }
2557                 }
2558               });
2559             } else {
2560               fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
2561                 try {
2562                   if (err) {
2563                     callback(errorCode.toString() + ".html");
2564                   } else {
2565                     callback("." + errorCode.toString());
2566                   }
2567                 } catch (err2) {
2568                   callServerError(500, err2);
2569                 }
2570               });
2571             }
2572           }
2573         }
2575         if (!_i) _i = 0;
2576         if (_i >= list.length) {
2577           medCallback(false);
2578           return;
2579         }
2581         if (list[_i].scode != errorCode) {
2582           getErrorFileName(list, callback, _i + 1);
2583           return;
2584         } else {
2585           fs.access(list[_i].path, fs.constants.F_OK, function (err) {
2586             if (err) {
2587               getErrorFileName(list, callback, _i + 1);
2588             } else {
2589               medCallback(list[_i].path);
2590             }
2591           });
2592         }
2593       }
2595       getErrorFileName(errorPages, function (errorFile) {
2596         if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
2597         if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
2598         if (errorCode == 500 || errorCode == 502) {
2599           serverconsole.errmessage("There was an error while processing the request!");
2600           serverconsole.errmessage("Stack:");
2601           serverconsole.errmessage(stack);
2602         }
2603         if (stackHidden) stack = "[error stack hidden]";
2604         if (serverHTTPErrorDescs[errorCode] === undefined) {
2605           callServerError(501, extName, stack);
2606         } else {
2607           var cheaders = getCustomHeaders();
2608           if (ch) {
2609             var chon = Object.keys(cheaders);
2610             Object.keys(ch).forEach(function (chnS) {
2611               var nhn = chnS;
2612               for (var j = 0; j < chon.length; j++) {
2613                 if (chon[j].toLowerCase() == chnS.toLowerCase()) {
2614                   nhn = chon[j];
2615                   break;
2616                 }
2617               }
2618               if (ch[chnS]) cheaders[nhn] = ch[chnS];
2619             });
2620           }
2621           cheaders["Content-Type"] = "text/html; charset=utf-8";
2622           if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS";
2623           if (err.code == "ERR_SSL_HTTP_REQUEST" && process.version && parseInt(process.version.split(".")[0].substring(1)) >= 16) {
2624             // Disable custom error page for HTTP SSL error
2625             res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
2626             res.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p><p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode].replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{server}/g, "" + ((exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + ((!exposeModsInErrorPages || extName == undefined) ? "" : " " + extName)).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{contact}/g, serverAdmin.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\./g, "[dot]").replace(/@/g, "[at]")));
2627             res.end();
2628           } else {
2629             fs.readFile(errorFile, function (err, data) {
2630               try {
2631                 if (err) throw err;
2632                 res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
2633                 responseEnd(data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode].replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{server}/g, "" + ((exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + ((!exposeModsInErrorPages || extName == undefined) ? "" : " " + extName)).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{contact}/g, serverAdmin.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\./g, "[dot]").replace(/@/g, "[at]")));
2634               } catch (err) {
2635                 var additionalError = 500;
2636                 if (err.code == "ENOENT") {
2637                   additionalError = 404;
2638                 } else if (err.code == "ENOTDIR") {
2639                   additionalError = 404; // Assume that file doesn't exist
2640                 } else if (err.code == "EACCES") {
2641                   additionalError = 403;
2642                 } else if (err.code == "ENAMETOOLONG") {
2643                   additionalError = 414;
2644                 } else if (err.code == "EMFILE") {
2645                   additionalError = 503;
2646                 } else if (err.code == "ELOOP") {
2647                   additionalError = 508;
2648                 }
2649                 res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
2650                 res.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p>" + ((additionalError == 404) ? "" : "<p>Additionally, a {additionalError} error occurred while loading an error page.</p>") + "<p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode].replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{server}/g, "" + ((exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + ((!exposeModsInErrorPages || extName == undefined) ? "" : " " + extName)).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{contact}/g, serverAdmin.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString()));
2651                 res.end();
2652               }
2653             });
2654           }
2655         }
2656       });
2657     }
2658     var reqip = socket.remoteAddress;
2659     var reqport = socket.remotePort;
2660     reqcounter++;
2661     malformedcounter++;
2662     serverconsole.locmessage("Somebody connected to " + (secure && fromMain ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "...");
2663     serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " sent invalid request.");
2665     function getCustomHeader(callback) {
2666   fs.readFile(("./.head").replace(/\/+/g, "/"), function (err, data) {
2667     if (err) {
2668       if (err.code == "ENOENT" || err.code == "EISDIR") {
2669         fs.readFile(("./head.html").replace(/\/+/g, "/"), function (err, data) {
2670           if (err) {
2671             if (err.code == "ENOENT" || err.code == "EISDIR") {
2672               callback();
2673             } else {
2674               callServerError(500, err);
2675             }
2676           } else {
2677             head = data.toString();
2678             callback();
2679           }
2680         });
2681       } else {
2682         callServerError(500, err);
2683       }
2684     } else {
2685       head = data.toString();
2686       callback();
2687     }
2688   });
2691 function getCustomFooter(callback) {
2692   fs.readFile(("./.foot").replace(/\/+/g, "/"), function (err, data) {
2693     if (err) {
2694       if (err.code == "ENOENT" || err.code == "EISDIR") {
2695         fs.readFile(("./foot.html").replace(/\/+/g, "/"), function (err, data) {
2696           if (err) {
2697             if (err.code == "ENOENT" || err.code == "EISDIR") {
2698               callback();
2699             } else {
2700               callServerError(500, err);
2701             }
2702           } else {
2703             foot = data.toString();
2704             callback();
2705           }
2706         });
2707       } else {
2708         callServerError(500, err);
2709       }
2710     } else {
2711       foot = data.toString();
2712       callback();
2713     }
2714   });
2717 getCustomHeader(function () {
2718   getCustomFooter(function () {
2719     try {
2720       if ((err.code && (err.code.indexOf("ERR_SSL_") == 0 || err.code.indexOf("ERR_TLS_") == 0)) || (!err.code && err.message.indexOf("SSL routines") != -1)) {
2721         if (err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) {
2722           serverconsole.errmessage("Client sent HTTP request to HTTPS port.");
2723           callServerError(497);
2724           return;
2725         } else {
2726           serverconsole.errmessage("An SSL error occured: " + (err.code ? err.code : err.message));
2727           callServerError(400);
2728           return;
2729         }
2730       }
2732       if (err.code && err.code.indexOf("ERR_HTTP2_") == 0) {
2733         serverconsole.errmessage("An HTTP/2 error occured: " + err.code);
2734         callServerError(400);
2735         return;
2736       }
2738       if (err.code && err.code == "ERR_HTTP_REQUEST_TIMEOUT") {
2739         serverconsole.errmessage("Client timed out.");
2740         callServerError(408);
2741         return;
2742       }
2744       if (!err.rawPacket) {
2745         serverconsole.errmessage("Connection ended prematurely.");
2746         callServerError(400);
2747         return;
2748       }
2750       var packetLines = err.rawPacket.toString().split("\r\n");
2751       if (packetLines.length == 0) {
2752         serverconsole.errmessage("Invalid request.");
2753         callServerError(400);
2754         return;
2755       }
2757       function checkHeaders(beginsFromFirst) {
2758         for (var i = (beginsFromFirst ? 0 : 1); i < packetLines.length; i++) {
2759           var header = packetLines[i];
2760           if (header == "") return false; // Beginning of body
2761           else if (header.indexOf(":") < 1) {
2762             serverconsole.errmessage("Invalid header.");
2763             callServerError(400);
2764             return true;
2765           } else if (header.length > 8192) {
2766             serverconsole.errmessage("Header too large.");
2767             callServerError(431); // Headers too large
2768             return true;
2769           }
2770         }
2771         return false;
2772       }
2773       var packetLine1 = packetLines[0].split(" ");
2774       var method = "GET";
2775       var httpVersion = "HTTP/1.1";
2776       if (String(packetLine1[0]).indexOf(":") > 0) {
2777         if (!checkHeaders(true)) {
2778           serverconsole.errmessage("The request is invalid (it may be a part of larger invalid request).");
2779           callServerError(400); // Also malformed Packet
2780           return;
2781         }
2782       }
2783       if (String(packetLine1[0]).length < 50) method = packetLine1.shift();
2784       if (String(packetLine1[packetLine1.length - 1]).length < 50) httpVersion = packetLine1.pop();
2785       if (packetLine1.length != 1) {
2786         serverconsole.errmessage("The head of request is invalid.");
2787         callServerError(400); // Malformed Packet
2788       } else if (!httpVersion.toString().match(/^HTTP[\/]/i)) {
2789         serverconsole.errmessage("Invalid protocol.");
2790         callServerError(400); // bad protocol version
2791       } else if (http.METHODS.indexOf(method) == -1) {
2792         serverconsole.errmessage("Invalid method.");
2793         callServerError(405); // Also malformed Packet
2794       } else {
2795         if (checkHeaders(false)) return;
2796         if (packetLine1[0].length > 255) {
2797           serverconsole.errmessage("URI too long.");
2798           callServerError(414); // Also malformed Packet
2799         } else {
2800           serverconsole.errmessage("The request is invalid.");
2801           callServerError(400); // Also malformed Packet
2802         }
2803       }
2804     } catch (err) {
2805       serverconsole.errmessage("There was an error while determining type of malformed request.");
2806       callServerError(400);
2807     }
2808   });
2810   }
2812   function connhandler(request, socket, head) {
2813     var reqIdInt = Math.floor(Math.random() * 16777216);
2814     if (reqIdInt == 16777216) reqIdInt = 0;
2815     var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
2816     var serverconsole = {
2817       climessage: function (msg) {
2818         if (msg.indexOf("\n") != -1) {
2819           msg.split("\n").forEach(function (nmsg) {
2820             serverconsole.climessage(nmsg);
2821           });
2822           return;
2823         }
2824         console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2825         LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2826         return;
2827       },
2828       reqmessage: function (msg) {
2829         if (msg.indexOf("\n") != -1) {
2830           msg.split("\n").forEach(function (nmsg) {
2831             serverconsole.reqmessage(nmsg);
2832           });
2833           return;
2834         }
2835         console.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2836         LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
2837         return;
2838       },
2839       resmessage: function (msg) {
2840         if (msg.indexOf("\n") != -1) {
2841           msg.split("\n").forEach(function (nmsg) {
2842             serverconsole.resmessage(nmsg);
2843           });
2844           return;
2845         }
2846         console.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2847         LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
2848         return;
2849       },
2850       errmessage: function (msg) {
2851         if (msg.indexOf("\n") != -1) {
2852           msg.split("\n").forEach(function (nmsg) {
2853             serverconsole.errmessage(nmsg);
2854           });
2855           return;
2856         }
2857         console.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2858         LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2859         return;
2860       },
2861       locerrmessage: function (msg) {
2862         if (msg.indexOf("\n") != -1) {
2863           msg.split("\n").forEach(function (nmsg) {
2864             serverconsole.locerrmessage(nmsg);
2865           });
2866           return;
2867         }
2868         console.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2869         LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2870         return;
2871       },
2872       locwarnmessage: function (msg) {
2873         if (msg.indexOf("\n") != -1) {
2874           msg.split("\n").forEach(function (nmsg) {
2875             serverconsole.locwarnmessage(nmsg);
2876           });
2877           return;
2878         }
2879         console.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2880         LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
2881         return;
2882       },
2883       locmessage: function (msg) {
2884         if (msg.indexOf("\n") != -1) {
2885           msg.split("\n").forEach(function (nmsg) {
2886             serverconsole.locmessage(nmsg);
2887           });
2888           return;
2889         }
2890         console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2891         LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
2892         return;
2893       }
2894     };
2895     var req = request;
2896     socket.on("close", function (hasError) {
2897       if (!hasError) serverconsole.locmessage("Client disconnected.");
2898       else serverconsole.locmessage("Client disconnected due to error.");
2899     });
2900     socket.on("error", function () {});
2902     var reqip = socket.remoteAddress;
2903     var reqport = socket.remotePort;
2904     reqcounter++;
2905     serverconsole.locmessage("Somebody connected to " + (secure ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "...");
2906     serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants to proxy " + request.url + " through this server");
2907     if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]);
2909     function modExecute(mods, ffinals) {
2910       var proxyMods = [];
2911       mods.forEach(function (mod) {
2912         if (mod.proxyCallback !== undefined) proxyMods.push(mod);
2913       });
2915       var modFunction = ffinals;
2916       proxyMods.reverse().forEach(function (proxyMod) {
2917         modFunction = proxyMod.proxyCallback(req, socket, head, configJSON, serverconsole, modFunction);
2918       });
2919       modFunction();
2920     }
2922     function vres() {
2923       serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod.");
2924       if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n");
2925     }
2927     modExecute(mods, vres);
2928   }
2930   function reqhandler(req, res, fromMain) {
2931     if (fromMain === undefined) fromMain = true;
2932     var reqIdInt = Math.floor(Math.random() * 16777216);
2933     if (reqIdInt == 16777216) reqIdInt = 0;
2934     var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
2935     var serverconsole = {
2936       climessage: function (msg) {
2937         if (msg.indexOf("\n") != -1) {
2938           msg.split("\n").forEach(function (nmsg) {
2939             serverconsole.climessage(nmsg);
2940           });
2941           return;
2942         }
2943         console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2944         LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2945         return;
2946       },
2947       reqmessage: function (msg) {
2948         if (msg.indexOf("\n") != -1) {
2949           msg.split("\n").forEach(function (nmsg) {
2950             serverconsole.reqmessage(nmsg);
2951           });
2952           return;
2953         }
2954         console.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2955         LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
2956         return;
2957       },
2958       resmessage: function (msg) {
2959         if (msg.indexOf("\n") != -1) {
2960           msg.split("\n").forEach(function (nmsg) {
2961             serverconsole.resmessage(nmsg);
2962           });
2963           return;
2964         }
2965         console.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2966         LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
2967         return;
2968       },
2969       errmessage: function (msg) {
2970         if (msg.indexOf("\n") != -1) {
2971           msg.split("\n").forEach(function (nmsg) {
2972             serverconsole.errmessage(nmsg);
2973           });
2974           return;
2975         }
2976         console.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
2977         LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2978         return;
2979       },
2980       locerrmessage: function (msg) {
2981         if (msg.indexOf("\n") != -1) {
2982           msg.split("\n").forEach(function (nmsg) {
2983             serverconsole.locerrmessage(nmsg);
2984           });
2985           return;
2986         }
2987         console.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2988         LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
2989         return;
2990       },
2991       locwarnmessage: function (msg) {
2992         if (msg.indexOf("\n") != -1) {
2993           msg.split("\n").forEach(function (nmsg) {
2994             serverconsole.locwarnmessage(nmsg);
2995           });
2996           return;
2997         }
2998         console.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
2999         LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
3000         return;
3001       },
3002       locmessage: function (msg) {
3003         if (msg.indexOf("\n") != -1) {
3004           msg.split("\n").forEach(function (nmsg) {
3005             serverconsole.locmessage(nmsg);
3006           });
3007           return;
3008         }
3009         console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
3010         LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
3011         return;
3012       }
3013     };
3015     function matchHostname(hostname) {
3016       if (typeof hostname == "undefined" || hostname == "*") {
3017         return true;
3018       } else if (req.headers.host && hostname.indexOf("*.") == 0 && hostname != "*.") {
3019         var hostnamesRoot = hostname.substring(2);
3020         if (req.headers.host == hostnamesRoot || (req.headers.host.length > hostnamesRoot.length && req.headers.host.indexOf("." + hostnamesRoot) == req.headers.host.length - hostnamesRoot.length - 1)) {
3021           return true;
3022         }
3023       } else if (req.headers.host && req.headers.host == hostname) {
3024         return true;
3025       }
3026       return false;
3027     }
3029     function getCustomHeaders() {
3030       var ph = JSON.parse(JSON.stringify(customHeaders));
3031       if (configJSON.customHeadersVHost) {
3032         var vhostP = null;
3033         configJSON.customHeadersVHost.every(function (vhost) {
3034           if (matchHostname(vhost.host) && ipMatch(vhost.ip, req.socket ? req.socket.localAddress : undefined)) {
3035             vhostP = vhost;
3036             return false;
3037           } else {
3038             return true;
3039           }
3040         });
3041         if (vhostP && vhostP.headers) {
3042           var phNu = JSON.parse(JSON.stringify(vhostP.headers));
3043           Object.keys(phNu).forEach(function (phNuK) {
3044             ph[phNuK] = phNu[phNuK];
3045           });
3046         }
3047       }
3048       Object.keys(ph).forEach(function (phk) {
3049         if (typeof ph[phk] == "string") ph[phk] = ph[phk].replace(/\{path\}/g, req.url);
3050       });
3051       return ph;
3052     }
3054     // Make HTTP/1.x API-based scripts compatible with HTTP/2.0 API
3055     if (configJSON.enableHTTP2 == true && req.httpVersion == "2.0") {
3056       // Set HTTP/1.x methods (to prevent process warnings)
3057       res.writeHeadNodeApi = res.writeHead;
3058       res.setHeaderNodeApi = res.setHeader;
3060       res.writeHead = function (a, b, c) {
3061         var table = c;
3062         if (typeof (b) == "object") table = b;
3063         if (table == undefined) table = this.tHeaders;
3064         if (table == undefined) table = {};
3065         table = JSON.parse(JSON.stringify(table));
3066         Object.keys(table).forEach(function (key) {
3067           var al = key.toLowerCase();
3068           if (al == "transfer-encoding" || al == "connection" || al == "keep-alive" || al == "upgrade") delete table[key];
3069         });
3070         if (res.stream && res.stream.destroyed) {
3071           return false;
3072         } else {
3073           return res.writeHeadNodeApi(a, table);
3074         }
3075       };
3076       res.setHeader = function (headerName, headerValue) {
3077         var al = headerName.toLowerCase();
3078         if (al != "transfer-encoding" && al != "connection" && al != "keep-alive" && al != "upgrade") return res.setHeaderNodeApi(headerName, headerValue);
3079         return false;
3080       };
3082       // Set HTTP/1.x headers
3083       if (!req.headers.host) req.headers.host = req.headers[":authority"];
3084       if (!req.url) req.url = req.headers[":path"];
3085       if (!req.protocol) req.protocol = req.headers[":scheme"];
3086       if (!req.method) req.method = req.headers[":method"];
3087       if (req.headers[":path"] == undefined || req.headers[":method"] == undefined) {
3088         var err = new Error("Either \":path\" or \":method\" pseudoheader is missing.");
3089         if(Buffer.alloc) err.rawPacket = Buffer.alloc(0);
3090         reqerrhandler(err, req.socket, fromMain);
3091       }
3092     }
3094     if (req.headers["x-svr-js-from-main-thread"] == "true" && req.socket && (!req.socket.remoteAddress || req.socket.remoteAddress == "::1" || req.socket.remoteAddress == "::ffff:127.0.0.1" || req.socket.remoteAddress == "127.0.0.1" || req.socket.remoteAddress == "localhost" || req.socket.remoteAddress == host || req.socket.remoteAddress == "::ffff:" + host)) {
3095       var headers = getCustomHeaders();
3096       res.writeHead(204, http.STATUS_CODES[204], headers);
3097       res.end();
3098       return;
3099     }
3101     req.url = fixNodeMojibakeURL(req.url);
3103     var headWritten = false;
3104     var lastStatusCode = null;
3105     res.writeHeadNative = res.writeHead;
3106     res.writeHead = function (code, codeDescription, headers) {
3107       if (!(headWritten && process.isBun && code === lastStatusCode && codeDescription === undefined && codeDescription === undefined)) {
3108         if (headWritten) {
3109           process.emitWarning("res.writeHead called multiple times.", {
3110             code: "WARN_SVRJS_MULTIPLE_WRITEHEAD"
3111           });
3112           return res;
3113         } else {
3114           headWritten = true;
3115         }
3116         if (code >= 400 && code <= 599) {
3117           if (code >= 400 && code <= 499) err4xxcounter++;
3118           else if (code >= 500 && code <= 599) err5xxcounter++;
3119           serverconsole.errmessage("Server responded with " + code.toString() + " code.");
3120         } else {
3121           serverconsole.resmessage("Server responded with " + code.toString() + " code.");
3122         }
3123         if (typeof codeDescription != "string" && http.STATUS_CODES[code]) {
3124           if (!headers) headers = codeDescription;
3125           codeDescription = http.STATUS_CODES[code];
3126         }
3127         lastStatusCode = code;
3128       }
3129       res.writeHeadNative(code, codeDescription, headers);
3130     };
3132     var finished = false;
3133     res.on("finish", function () {
3134       if (!finished) {
3135         finished = true;
3136         serverconsole.locmessage("Client disconnected.");
3137       }
3138     });
3139     res.on("close", function () {
3140       if (!finished) {
3141         finished = true;
3142         serverconsole.locmessage("Client disconnected.");
3143       }
3144     });
3145     var isProxy = false;
3146     if (req.url[0] != "/" && req.url != "*") isProxy = true;
3147     serverconsole.locmessage("Somebody connected to " + (secure && fromMain ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "...");
3149     if (req.socket == null) {
3150       serverconsole.errmessage("Client socket is null!!!");
3151       return;
3152     }
3154     // Set up X-Forwarded-For
3155     var reqip = req.socket.remoteAddress;
3156     var reqport = req.socket.remotePort;
3157     var oldip = "";
3158     var oldport = "";
3159     var isForwardedValid = true;
3160     if (enableIPSpoofing) {
3161       if (req.headers["x-forwarded-for"] != undefined) {
3162         var preparedReqIP = req.headers["x-forwarded-for"].split(",")[0].replace(/ /g, "");
3163         var preparedReqIPvalid = net.isIP(preparedReqIP);
3164         if (preparedReqIPvalid) {
3165           if (preparedReqIPvalid == 4 && req.socket.remoteAddress && req.socket.remoteAddress.indexOf(":") > -1) preparedReqIP = "::ffff:" + preparedReqIP;
3166           reqip = preparedReqIP;
3167           reqport = null;
3168           try {
3169             oldport = req.socket.remotePort;
3170             oldip = req.socket.remoteAddress;
3171             req.socket.realRemotePort = reqport;
3172             req.socket.realRemoteAddress = reqip;
3173             req.socket.originalRemotePort = oldport;
3174             req.socket.originalRemoteAddress = oldip;
3175             res.socket.realRemotePort = reqport;
3176             res.socket.realRemoteAddress = reqip;
3177             res.socket.originalRemotePort = oldport;
3178             res.socket.originalRemoteAddress = oldip;
3179           } catch (err) {
3180             // Address setting failed
3181           }
3182         } else {
3183           isForwardedValid = false;
3184         }
3185       }
3186     }
3188     reqcounter++;
3190     // Process the Host header
3191     var oldHostHeader = req.headers.host;
3192     if (typeof req.headers.host == "string") {
3193       req.headers.host = req.headers.host.toLowerCase();
3194       if (!req.headers.host.match(/^\.+$/)) req.headers.host = req.headers.host.replace(/\.$/g, "");
3195     }
3197     serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (req.method == "GET" ? "content in " : (req.method == "POST" ? "to post content in " : (req.method == "PUT" ? "to add content in " : (req.method == "DELETE" ? "to delete content in " : (req.method == "PATCH" ? "to patch content in " : "to access content using " + req.method + " method in "))))) + ((req.headers.host == undefined || isProxy) ? "" : req.headers.host) + req.url);
3198     if (req.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + req.headers["user-agent"]);
3199     if (oldHostHeader && oldHostHeader != req.headers.host) serverconsole.resmessage("Host name rewritten: " + oldHostHeader + " => " + req.headers.host);
3201     var acceptEncoding = req.headers["accept-encoding"];
3202     if (!acceptEncoding) acceptEncoding = "";
3204     // Header and footer placeholders
3205     var head = "";
3206     var foot = "";
3208     function responseEnd(body) {
3209       // If body is Buffer, then it is converted to String anyway.
3210       res.write(head + body + foot);
3211       res.end();
3212     }
3214     // Server error calling method
3215     function callServerError(errorCode, extName, stack, ch) {
3216       if (typeof errorCode !== "number") {
3217         throw new TypeError("HTTP error code parameter needs to be an integer.");
3218       }
3220       // Handle optional parameters
3221       if (extName && typeof extName === "object") {
3222         ch = stack;
3223         stack = extName;
3224         extName = undefined;
3225       } else if (typeof extName !== "string" && extName !== null && extName !== undefined) {
3226         throw new TypeError("Extension name parameter needs to be a string.");
3227       }
3229       if (stack && typeof stack === "object" && Object.prototype.toString.call(stack) !== "[object Error]") {
3230         ch = stack;
3231         stack = undefined;
3232       } else if (typeof stack !== "object" && typeof stack !== "string" && stack) {
3233         throw new TypeError("Error stack parameter needs to be either a string or an instance of Error object.");
3234       }
3236       // Determine error file
3237       function getErrorFileName(list, callback, _i) {
3238         function medCallback(p) {
3239           if (p) callback(p);
3240           else {
3241             if (errorCode == 404) {
3242               fs.access(page404, fs.constants.F_OK, function (err) {
3243                 if (err) {
3244                   fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
3245                     try {
3246                       if (err) {
3247                         callback(errorCode.toString() + ".html");
3248                       } else {
3249                         callback("." + errorCode.toString());
3250                       }
3251                     } catch (err2) {
3252                       callServerError(500, err2);
3253                     }
3254                   });
3255                 } else {
3256                   try {
3257                     callback(page404);
3258                   } catch (err2) {
3259                     callServerError(500, err2);
3260                   }
3261                 }
3262               });
3263             } else {
3264               fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
3265                 try {
3266                   if (err) {
3267                     callback(errorCode.toString() + ".html");
3268                   } else {
3269                     callback("." + errorCode.toString());
3270                   }
3271                 } catch (err2) {
3272                   callServerError(500, err2);
3273                 }
3274               });
3275             }
3276           }
3277         }
3279         if (!_i) _i = 0;
3280         if (_i >= list.length) {
3281           medCallback(false);
3282           return;
3283         }
3285         if (list[_i].scode != errorCode || !(matchHostname(list[_i].host) && ipMatch(list[_i].ip, req.socket ? req.socket.localAddress : undefined))) {
3286           getErrorFileName(list, callback, _i + 1);
3287           return;
3288         } else {
3289           fs.access(list[_i].path, fs.constants.F_OK, function (err) {
3290             if (err) {
3291               getErrorFileName(list, callback, _i + 1);
3292             } else {
3293               medCallback(list[_i].path);
3294             }
3295           });
3296         }
3297       }
3299       getErrorFileName(errorPages, function (errorFile) {
3300         // Generate error stack if not provided
3301         if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
3302         if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
3304         if (errorCode == 500 || errorCode == 502) {
3305           serverconsole.errmessage("There was an error while processing the request!");
3306           serverconsole.errmessage("Stack:");
3307           serverconsole.errmessage(stack);
3308         }
3310         // Hide the error stack if specified
3311         if (stackHidden) stack = "[error stack hidden]";
3313         // Validate the error code and handle unknown codes
3314         if (serverHTTPErrorDescs[errorCode] === undefined) {
3315           callServerError(501, extName, stack);
3316         } else {
3317           var cheaders = getCustomHeaders();
3319           // Process custom headers if provided
3320           if (ch) {
3321             var chon = Object.keys(cheaders);
3322             Object.keys(ch).forEach(function (chnS) {
3323               var nhn = chnS;
3324               for (var j = 0; j < chon.length; j++) {
3325                 if (chon[j].toLowerCase() == chnS.toLowerCase()) {
3326                   nhn = chon[j];
3327                   break;
3328                 }
3329               }
3330               if (ch[chnS]) cheaders[nhn] = ch[chnS];
3331             });
3332           }
3334           cheaders["Content-Type"] = "text/html; charset=utf-8";
3336           // Set default Allow header for 405 error if not provided
3337           if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS";
3339           // Read the error file and replace placeholders with error information
3340           fs.readFile(errorFile, function (err, data) {
3341             try {
3342               if (err) throw err;
3343               res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
3344               responseEnd(data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode].replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, req.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + ((exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + ((!exposeModsInErrorPages || extName == undefined) ? "" : " " + extName)).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\./g, "[dot]").replace(/@/g, "[at]"))); // Replace placeholders in error response
3345             } catch (err) {
3346               var additionalError = 500;
3347               // Handle additional error cases
3348               if (err.code == "ENOENT") {
3349                 additionalError = 404;
3350               } else if (err.code == "ENOTDIR") {
3351                 additionalError = 404; // Assume that file doesn't exist
3352               } else if (err.code == "EACCES") {
3353                 additionalError = 403;
3354               } else if (err.code == "ENAMETOOLONG") {
3355                 additionalError = 414;
3356               } else if (err.code == "EMFILE") {
3357                 additionalError = 503;
3358               } else if (err.code == "ELOOP") {
3359                 additionalError = 508;
3360               }
3362               res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
3363               res.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p>" + ((additionalError == 404) ? "" : "<p>Additionally, a {additionalError} error occurred while loading an error page.</p>") + "<p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode].replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, req.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + ((exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + ((!exposeModsInErrorPages || extName == undefined) ? "" : " " + extName)).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); // Replace placeholders in error response
3364               res.end();
3365             }
3366           });
3367         }
3368       });
3369     }
3371     function getCustomHeader(callback) {
3372   fs.readFile(("./.head").replace(/\/+/g, "/"), function (err, data) {
3373     if (err) {
3374       if (err.code == "ENOENT" || err.code == "EISDIR") {
3375         fs.readFile(("./head.html").replace(/\/+/g, "/"), function (err, data) {
3376           if (err) {
3377             if (err.code == "ENOENT" || err.code == "EISDIR") {
3378               callback();
3379             } else {
3380               callServerError(500, err);
3381             }
3382           } else {
3383             head = data.toString();
3384             callback();
3385           }
3386         });
3387       } else {
3388         callServerError(500, err);
3389       }
3390     } else {
3391       head = data.toString();
3392       callback();
3393     }
3394   });
3397 function getCustomFooter(callback) {
3398   fs.readFile(("./.foot").replace(/\/+/g, "/"), function (err, data) {
3399     if (err) {
3400       if (err.code == "ENOENT" || err.code == "EISDIR") {
3401         fs.readFile(("./foot.html").replace(/\/+/g, "/"), function (err, data) {
3402           if (err) {
3403             if (err.code == "ENOENT" || err.code == "EISDIR") {
3404               callback();
3405             } else {
3406               callServerError(500, err);
3407             }
3408           } else {
3409             foot = data.toString();
3410             callback();
3411           }
3412         });
3413       } else {
3414         callServerError(500, err);
3415       }
3416     } else {
3417       foot = data.toString();
3418       callback();
3419     }
3420   });
3424     // Function to perform HTTP redirection to a specified destination URL
3425     function redirect(destination, isTemporary, keepMethod, customHeaders) {
3426       // If keepMethod is a object, then save it to customHeaders
3427       if (typeof keepMethod == "object") customHeaders = keepMethod;
3429       // If isTemporary is a object, then save it to customHeaders
3430       if (typeof isTemporary == "object") customHeaders = isTemporary;
3432       // If customHeaders are not provided, get the default custom headers
3433       if (customHeaders === undefined) customHeaders = getCustomHeaders();
3435       // Set the "Location" header to the destination URL
3436       customHeaders["Location"] = destination;
3438       // Determine the status code for redirection based on the isTemporary and keepMethod flags
3439       var statusCode = keepMethod ? (isTemporary ? 307 : 308) : (isTemporary ? 302 : 301);
3441       // Write the response header with the appropriate status code and message
3442       res.writeHead(statusCode, http.STATUS_CODES[statusCode], customHeaders);
3444       // Log the redirection message
3445       serverconsole.resmessage("Client redirected to " + destination);
3447       // End the response
3448       res.end();
3450       // Return from the function
3451       return;
3452     }
3454     // Function to parse incoming POST data from the request
3455     function parsePostData(options, callback) {
3456       // If the request method is not POST, return a 405 Method Not Allowed error
3457       if (req.method != "POST") {
3458         // Get the default custom headers and add "Allow" header with value "POST"
3459         var customHeaders = getCustomHeaders();
3460         customHeaders["Allow"] = "POST";
3462         // Call the server error function with 405 status code and custom headers
3463         callServerError(405, customHeaders);
3464         return;
3465       }
3467       // Set formidableOptions to options, if provided; otherwise, set it to an empty object
3468       var formidableOptions = options ? options : {};
3470       // If no callback is provided, set the callback to options and reset formidableOptions
3471       if (!callback) {
3472         callback = options;
3473         formidableOptions = {};
3474       }
3476       // If the formidable module had an error, call the server error function with 500 status code and error stack
3477       if (formidable._errored) callServerError(500, formidable._errored);
3479       // Create a new formidable form
3480       var form = formidable(formidableOptions);
3482       // Parse the request and process the fields and files
3483       form.parse(req, function (err, fields, files) {
3484         // If there was an error, call the server error function with status code determined by error
3485         if (err) {
3486           if (err.httpCode) callServerError(err.httpCode);
3487           else callServerError(400);
3488           return;
3489         }
3490         // Otherwise, call the provided callback function with the parsed fields and files
3491         callback(fields, files);
3492       });
3493     }
3495     getCustomHeader( function () {
3496       getCustomFooter( function () {
3497     // Authenticated user variable
3498     var authUser = null;
3500     // URL-related objects.
3501     var uobject = {};
3502     try {
3503       uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
3504     } catch (err) {
3505       // Return an 400 error
3506       callServerError(400);
3507       serverconsole.errmessage("Bad request!");
3508       return;
3509     }
3510     var search = uobject.search;
3511     var href = uobject.pathname;
3512     var ext = href.match(/[^\/]\.([^.]+)$/);
3513     if(!ext) ext = "";
3514     else ext = ext[1].toLowerCase();
3515     var decodedHref = "";
3516     try {
3517       decodedHref = decodeURIComponent(href);
3518     } catch (err) {
3519       // Return an 400 error
3520       callServerError(400);
3521       serverconsole.errmessage("Bad request!");
3522       return;
3523     }
3524     var origHref = href; // Placeholder origHref
3527     if (req.headers["expect"] && req.headers["expect"] != "100-continue") {
3528       // Expectations not met.
3529       callServerError(417);
3530       return;
3531     }
3533     // Mod execution function
3534     function modExecute(mods, ffinals) {
3535       // Prepare modFunction
3536       var modFunction = ffinals;
3537       var useMods = mods.slice();
3539       if (isProxy) {
3540         // Get list of forward proxy mods
3541         useMods = [];
3542         mods.forEach(function (mod) {
3543           if (mod.proxyCallback !== undefined) useMods.push(mod);
3544         });
3545       }
3547       useMods.reverse().forEach(function (modO) {
3548         modFunction = modO.callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, "", modFunction, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData, authUser);
3549       });
3551       // Execute modFunction
3552       modFunction();
3553     }
3555     var vresCalled = false;
3557     function vres() {
3558         if (vresCalled) {
3559           process.emitWarning("elseCallback() invoked multiple times.", {
3560             code: "WARN_SVRJS_MULTIPLE_ELSECALLBACK"
3561           });
3562           return;
3563         } else {
3564           vresCalled = true;
3565         }
3567         // Function to check the level of a path relative to the web root
3568         function checkPathLevel(path) {
3569           // Split the path into an array of components based on "/"
3570           var pathComponents = path.split("/");
3572           // Initialize counters for level up (..) and level down (.)
3573           var levelUpCount = 0;
3574           var levelDownCount = 0;
3576           // Loop through the path components
3577           for (var i = 0; i < pathComponents.length; i++) {
3578             // If the component is "..", decrement the levelUpCount
3579             if (".." === pathComponents[i]) {
3580               levelUpCount--;
3581             }
3582             // If the component is not "." or an empty string, increment the levelDownCount
3583             else if ("." !== pathComponents[i] && "" !== pathComponents[i]) {
3584               levelDownCount++;
3585             }
3586           }
3588           // Calculate the overall level by subtracting levelUpCount from levelDownCount
3589           var overallLevel = levelDownCount - levelUpCount;
3591           // Return the overall level
3592           return overallLevel;
3593         }
3596         if (isProxy) {
3597           var eheaders = getCustomHeaders();
3598           eheaders["Content-Type"] = "text/html; charset=utf-8";
3599           res.writeHead(501, http.STATUS_CODES[501], eheaders);
3600           res.write("<html><head><title>Proxy not implemented</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>Proxy not implemented</h1><p>SVR.JS doesn't support proxy without proxy mod. If you're administator of this server, then install this mod in order to use SVR.JS as a proxy.</p><p><i>" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</i></p></body></html>");
3601           res.end();
3602           serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod.");
3603           return;
3604         }
3606         if (req.method == "OPTIONS") {
3607           var hdss = getCustomHeaders();
3608           hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
3609           res.writeHead(204, http.STATUS_CODES[204], hdss);
3610           res.end();
3611           return;
3612         } else if (req.method != "GET" && req.method != "POST" && req.method != "HEAD") {
3613           callServerError(405);
3614           serverconsole.errmessage("Invaild method: " + req.method);
3615           return;
3616         }
3618         if (allowStatus && (href == "/svrjsstatus.svr" || (os.platform() == "win32" && href.toLowerCase() == "/svrjsstatus.svr"))) {
3619           function formatRelativeTime(relativeTime) {
3620             var days = Math.floor(relativeTime / 60 / (60 * 24));
3621             var dateDiff = new Date(relativeTime * 1000);
3622             return days + " days, " + dateDiff.getUTCHours() + " hours, " + dateDiff.getUTCMinutes() + " minutes, " + dateDiff.getUTCSeconds() + " seconds";
3623           }
3624           var statusBody = "";
3625           statusBody += "Server version: " + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "<br/><hr/>";
3627           //Those entries are just dates and numbers converted/formatted to strings, so no escaping is needed.
3628           statusBody += "Current time: " + new Date().toString() + "<br/>Thread start time: " + new Date(new Date() - (process.uptime() * 1000)).toString() + "<br/>Thread uptime: " + formatRelativeTime(Math.floor(process.uptime())) + "<br/>";
3629           statusBody += "OS uptime: " + formatRelativeTime(os.uptime()) + "<br/>";
3630           statusBody += "Total request count: " + reqcounter + "<br/>";
3631           statusBody += "Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s<br/>";
3632           statusBody += "Client errors (4xx): " + err4xxcounter + "<br/>";
3633           statusBody += "Server errors (5xx): " + err5xxcounter + "<br/>";
3634           statusBody += "Average error rate: " + (Math.round(((err4xxcounter + err5xxcounter) / reqcounter) * 10000) / 100) + "%<br/>";
3635           statusBody += "Malformed HTTP requests: " + malformedcounter;
3636           if (process.memoryUsage) statusBody += "<br/>Memory usage of thread: " + sizify(process.memoryUsage().rss, true) + "B";
3637           if (process.cpuUsage) statusBody += "<br/>Total CPU usage by thread: u" + (process.cpuUsage().user / 1000) + "ms s" + (process.cpuUsage().system / 1000) + "ms - " + (Math.round((((process.cpuUsage().user + process.cpuUsage().system) / 1000000) / process.uptime()) * 1000) / 1000) + "%";
3638           statusBody += "<br/>Thread PID: " + process.pid + "<br/>";
3640           res.writeHead(200, http.STATUS_CODES[200], {
3641             "Content-Type": "text/html; charset=utf-8"
3642           });
3643           res.end((head == "" ? "<html><head><title>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>" : head.replace(/<head>/i, "<head><title>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title>")) + "<h1>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</h1>" + statusBody + (foot == "" ? "</body></html>" : foot));
3644           return;
3645         }
3647         var dHref = decodeURIComponent(href);
3648         var readFrom = "." + dHref;
3649         var dirImagesMissing = false;
3650         fs.stat(readFrom, function (err, stats) {
3651           if (err) {
3652             if (err.code == "ENOENT") {
3653               if (__dirname != process.cwd() && dHref.match(/^\/\.dirimages\/(?:(?!\.png$).)+\.png$/)) {
3654                 dirImagesMissing = true;
3655                 readFrom = __dirname + dHref;
3656               } else {
3657                 callServerError(404);
3658                 serverconsole.errmessage("Resource not found.");
3659                 return;
3660               }
3661             } else if (err.code == "ENOTDIR") {
3662               callServerError(404); // Assume that file doesn't exist.
3663               serverconsole.errmessage("Resource not found.");
3664               return;
3665             } else if (err.code == "EACCES") {
3666               callServerError(403);
3667               serverconsole.errmessage("Access denied.");
3668               return;
3669             } else if (err.code == "ENAMETOOLONG") {
3670               callServerError(414);
3671               return;
3672             } else if (err.code == "EMFILE") {
3673               callServerError(503);
3674               return;
3675             } else if (err.code == "ELOOP") {
3676               callServerError(508); // The symbolic link loop is detected during file system operations.
3677               serverconsole.errmessage("Symbolic link loop detected.");
3678               return;
3679             } else {
3680               callServerError(500, err);
3681               return;
3682             }
3683           }
3685           // Check if index file exists
3686           if (!dirImagesMissing && (req.url == "/" || stats.isDirectory())) {
3687             fs.stat((readFrom + "/index.html").replace(/\/+/g, "/"), function (e, s) {
3688               if (e || !s.isFile()) {
3689                 fs.stat((readFrom + "/index.htm").replace(/\/+/g, "/"), function (e, s) {
3690                   if (e || !s.isFile()) {
3691                     fs.stat((readFrom + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) {
3692                       if (e || !s.isFile()) {
3693                         properDirectoryListingAndStaticFileServe();
3694                       } else {
3695                         stats = s;
3696                         ext = "xhtml";
3697                         readFrom = (readFrom + "/index.xhtml").replace(/\/+/g, "/");
3698                         properDirectoryListingAndStaticFileServe();
3699                       }
3700                     });
3701                   } else {
3702                     stats = s;
3703                     ext = "htm";
3704                     readFrom = (readFrom + "/index.htm").replace(/\/+/g, "/");
3705                     properDirectoryListingAndStaticFileServe();
3706                   }
3707                 });
3708               } else {
3709                 stats = s;
3710                 ext = "html";
3711                 readFrom = (readFrom + "/index.html").replace(/\/+/g, "/");
3712                 properDirectoryListingAndStaticFileServe();
3713               }
3714             });
3715           } else if (dirImagesMissing) {
3716             fs.stat(readFrom, function (e, s) {
3717               if (e || !s.isFile()) {
3718                 properDirectoryListingAndStaticFileServe();
3719               } else {
3720                 stats = s;
3721                 properDirectoryListingAndStaticFileServe();
3722               }
3723             });
3724           } else {
3725             properDirectoryListingAndStaticFileServe();
3726           }
3728           function properDirectoryListingAndStaticFileServe() {
3729             if (stats.isFile()) {
3730               var acceptEncoding = req.headers["accept-encoding"];
3731               if (!acceptEncoding) acceptEncoding = "";
3733               var filelen = stats.size;
3735               // ETag code
3736               var fileETag = undefined;
3737               if (configJSON.enableETag == undefined || configJSON.enableETag) {
3738                 fileETag = generateETag(href, stats);
3739                 // Check if the client's request matches the ETag value (If-None-Match)
3740                 var clientETag = req.headers["if-none-match"];
3741                 if (clientETag === fileETag) {
3742                   res.writeHead(304, http.STATUS_CODES[304], {
3743                     "ETag": clientETag
3744                   });
3745                   res.end();
3746                   return;
3747                 }
3749                 // Check if the client's request doesn't match the ETag value (If-Match)
3750                 var ifMatchETag = req.headers["if-match"];
3751                 if (ifMatchETag && ifMatchETag !== "*" && ifMatchETag !== fileETag) {
3752                   callServerError(412, {
3753                     "ETag": clientETag
3754                   });
3755                   return;
3756                 }
3757               }
3759               // Handle partial content request
3760               if (ext != "html" && req.headers["range"]) {
3761                 try {
3762                   var rhd = getCustomHeaders();
3763                   rhd["Accept-Ranges"] = "bytes";
3764                   rhd["Content-Range"] = "bytes */" + filelen;
3765                   var regexmatch = req.headers["range"].match(/bytes=([0-9]*)-([0-9]*)/);
3766                   if (!regexmatch) {
3767                     callServerError(416, rhd);
3768                   } else {
3769                     // Process the partial content request
3770                     var beginOrig = regexmatch[1];
3771                     var endOrig = regexmatch[2];
3772                     var begin = 0;
3773                     var end = filelen - 1;
3774                     if (beginOrig == "" && endOrig == "") {
3775                       callServerError(416, rhd);
3776                       return;
3777                     } else if (beginOrig == "") {
3778                       begin = end - parseInt(endOrig) + 1;
3779                     } else {
3780                       begin = parseInt(beginOrig);
3781                       if (endOrig != "") end = parseInt(endOrig);
3782                     }
3783                     if (begin > end || begin < 0 || begin > filelen - 1) {
3784                       callServerError(416, rhd);
3785                       return;
3786                     }
3787                     if (end > filelen - 1) end = filelen - 1;
3788                     rhd["Content-Range"] = "bytes " + begin + "-" + end + "/" + filelen;
3789                     rhd["Content-Length"] = end - begin + 1;
3790                     if (!(mime.contentType(ext) == false) && ext != "") rhd["Content-Type"] = mime.contentType(ext);
3791                     if (fileETag) rhd["ETag"] = fileETag;
3793                     if (req.method != "HEAD") {
3794                       var readStream = fs.createReadStream(readFrom, {
3795                         start: begin,
3796                         end: end
3797                       });
3798                       readStream.on("error", function (err) {
3799                         if (err.code == "ENOENT") {
3800                           callServerError(404);
3801                           serverconsole.errmessage("Resource not found.");
3802                         } else if (err.code == "ENOTDIR") {
3803                           callServerError(404); // Assume that file doesn't exist.
3804                           serverconsole.errmessage("Resource not found.");
3805                         } else if (err.code == "EACCES") {
3806                           callServerError(403);
3807                           serverconsole.errmessage("Access denied.");
3808                         } else if (err.code == "ENAMETOOLONG") {
3809                           callServerError(414);
3810                         } else if (err.code == "EMFILE") {
3811                           callServerError(503);
3812                         } else if (err.code == "ELOOP") {
3813                           callServerError(508); // The symbolic link loop is detected during file system operations.
3814                           serverconsole.errmessage("Symbolic link loop detected.");
3815                         } else {
3816                           callServerError(500, err);
3817                         }
3818                       }).on("open", function () {
3819                         try {
3820                           res.writeHead(206, http.STATUS_CODES[206], rhd);
3821                           readStream.pipe(res);
3822                           serverconsole.resmessage("Client successfully received content.");
3823                         } catch (err) {
3824                           callServerError(500, err);
3825                         }
3826                       });
3827                     } else {
3828                       res.writeHead(206, http.STATUS_CODES[206], rhd);
3829                       res.end();
3830                     }
3831                   }
3832                 } catch (err) {
3833                   callServerError(500, err);
3834                 }
3835               } else {
3836                 // Helper function to check if compression is allowed for the file
3837                 function canCompress(path, list) {
3838                   var canCompress = true;
3839                   for (var i = 0; i < list.length; i++) {
3840                     if (createRegex(list[i], true).test(path)) {
3841                       canCompress = false;
3842                       break;
3843                     }
3844                   }
3845                   return canCompress;
3846                 }
3848                 var useBrotli = (ext != "br" && filelen > 256 && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/));
3849                 var useDeflate = (ext != "zip" && filelen > 256 && acceptEncoding.match(/\bdeflate\b/));
3850                 var useGzip = (ext != "gz" && filelen > 256 && acceptEncoding.match(/\bgzip\b/));
3852                 var isCompressable = true;
3853                 try {
3854                   // Check for files not to compressed and compression enabling setting. Also check for browser quirks and adjust compression accordingly
3855                   if((!useBrotli && !useDeflate && !useGzip) || configJSON.enableCompression !== true || !canCompress(href, dontCompress)) {
3856                     isCompressable = false; // Compression is disabled
3857                   } else if (ext != "html" && ext != "htm" && ext != "xhtml" && ext != "xht" && ext != "shtml") {
3858                     if (/^Mozilla\/4\.[0-9]+(( *\[[^)]*\] *| *)\([^)\]]*\))? *$/.test(req.headers["user-agent"]) && !(/https?:\/\/|[bB][oO][tT]|[sS][pP][iI][dD][eE][rR]|[sS][uU][rR][vV][eE][yY]|MSIE/.test(req.headers["user-agent"]))) {
3859                       isCompressable = false; // Netscape 4.x doesn't handle compressed data properly outside of HTML documents.
3860                     } else if (/^w3m\/[^ ]*$/.test(req.headers["user-agent"])) {
3861                       isCompressable = false; // w3m doesn't handle compressed data properly outside of HTML documents.
3862                     }
3863                   } else {
3864                     if (/^Mozilla\/4\.0[6-8](( *\[[^)]*\] *| *)\([^)\]]*\))? *$/.test(req.headers["user-agent"]) && !(/https?:\/\/|[bB][oO][tT]|[sS][pP][iI][dD][eE][rR]|[sS][uU][rR][vV][eE][yY]|MSIE/.test(req.headers["user-agent"]))) {
3865                       isCompressable = false; // Netscape 4.06-4.08 doesn't handle compressed data properly.
3866                     }
3867                   }
3868                 } catch (err) {
3869                   callServerError(500, err);
3870                   return;
3871                 }
3873                 // Bun 1.1 has definition for zlib.createBrotliCompress, but throws an error while invoking the function.
3874                 if (process.isBun && useBrotli && isCompressable) {
3875                   try {
3876                     zlib.createBrotliCompress();
3877                   } catch (err) {
3878                     useBrotli = false;
3879                   }
3880                 }
3882                 try {
3883                   var hdhds = {};
3884                   if (useBrotli && isCompressable) {
3885                     hdhds["Content-Encoding"] = "br";
3886                   } else if (useDeflate && isCompressable) {
3887                     hdhds["Content-Encoding"] = "deflate";
3888                   } else if (useGzip && isCompressable) {
3889                     hdhds["Content-Encoding"] = "gzip";
3890                   } else {
3891                     if (ext == "html") {
3892                       hdhds["Content-Length"] = head.length + filelen + foot.length;
3893                     } else {
3894                       hdhds["Content-Length"] = filelen;
3895                     }
3896                   }
3897                   if (ext != "html") hdhds["Accept-Ranges"] = "bytes";
3898                   delete hdhds["Content-Type"];
3899                   if (!(mime.contentType(ext) == false) && ext != "") hdhds["Content-Type"] = mime.contentType(ext);
3900                   if (fileETag) hdhds["ETag"] = fileETag;
3902                   if (req.method != "HEAD") {
3903                     var readStream = fs.createReadStream(readFrom);
3904                     readStream.on("error", function (err) {
3905                       if (err.code == "ENOENT") {
3906                         callServerError(404);
3907                         serverconsole.errmessage("Resource not found.");
3908                       } else if (err.code == "ENOTDIR") {
3909                         callServerError(404); // Assume that file doesn't exist.
3910                         serverconsole.errmessage("Resource not found.");
3911                       } else if (err.code == "EACCES") {
3912                         callServerError(403);
3913                         serverconsole.errmessage("Access denied.");
3914                       } else if (err.code == "ENAMETOOLONG") {
3915                         callServerError(414);
3916                       } else if (err.code == "EMFILE") {
3917                         callServerError(503);
3918                       } else if (err.code == "ELOOP") {
3919                         callServerError(508); // The symbolic link loop is detected during file system operations.
3920                         serverconsole.errmessage("Symbolic link loop detected.");
3921                       } else {
3922                         callServerError(500, err);
3923                       }
3924                     }).on("open", function () {
3925                       try {
3926                         var resStream = {};
3927                         if (useBrotli && isCompressable) {
3928                           resStream = zlib.createBrotliCompress();
3929                           resStream.pipe(res);
3930                         } else if (useDeflate && isCompressable) {
3931                           resStream = zlib.createDeflateRaw();
3932                           resStream.pipe(res);
3933                         } else if (useGzip && isCompressable) {
3934                           resStream = zlib.createGzip();
3935                           resStream.pipe(res);
3936                         } else {
3937                           resStream = res;
3938                         }
3939                         if (ext == "html") {
3940                           function afterWriteCallback() {
3941                             readStream.on("end", function () {
3942                               resStream.end(foot);
3943                             });
3944                             readStream.pipe(resStream, {
3945                               end: false
3946                             });
3947                           }
3948                           res.writeHead(200, http.STATUS_CODES[200], hdhds);
3949                           if (!resStream.write(head)) {
3950                             resStream.on("drain", afterWriteCallback);
3951                           } else {
3952                             process.nextTick(afterWriteCallback);
3953                           }
3954                         } else {
3955                           res.writeHead(200, http.STATUS_CODES[200], hdhds);
3956                           readStream.pipe(resStream);
3957                         }
3958                         serverconsole.resmessage("Client successfully received content.");
3959                       } catch (err) {
3960                         callServerError(500, err);
3961                       }
3962                     });
3963                   } else {
3964                     res.writeHead(200, http.STATUS_CODES[200], hdhds);
3965                     res.end();
3966                     serverconsole.resmessage("Client successfully received content.");
3967                   }
3968                 } catch (err) {
3969                   callServerError(500, err);
3970                 }
3971               }
3972             } else if (stats.isDirectory()) {
3973               // Check if directory listing is enabled in the configuration
3974               if (checkForEnabledDirectoryListing(req.headers.host, req.socket ? req.socket.localAddress : undefined)) {
3975                 var customDirListingHeader = "";
3976                 var customDirListingFooter = "";
3978                 function getCustomDirListingHeader(callback) {
3979                   fs.readFile(("." + dHref + "/.dirhead").replace(/\/+/g, "/"), function (err, data) {
3980                     if (err) {
3981                       if (err.code == "ENOENT" || err.code == "EISDIR") {
3982                         if (os.platform != "win32" || href != "/") {
3983                           fs.readFile(("." + dHref + "/HEAD.html").replace(/\/+/g, "/"), function (err, data) {
3984                             if (err) {
3985                               if (err.code == "ENOENT" || err.code == "EISDIR") {
3986                                 callback();
3987                               } else {
3988                                 callServerError(500, err);
3989                               }
3990                             } else {
3991                               customDirListingHeader = data.toString();
3992                               callback();
3993                             }
3994                           });
3995                         } else {
3996                           callback();
3997                         }
3998                       } else {
3999                         callServerError(500, err);
4000                       }
4001                     } else {
4002                       customDirListingHeader = data.toString();
4003                       callback();
4004                     }
4005                   });
4006                 }
4008                 function getCustomDirListingFooter(callback) {
4009                   fs.readFile(("." + dHref + "/.dirfoot").replace(/\/+/g, "/"), function (err, data) {
4010                     if (err) {
4011                       if (err.code == "ENOENT" || err.code == "EISDIR") {
4012                         if (os.platform != "win32" || href != "/") {
4013                           fs.readFile(("." + dHref + "/FOOT.html").replace(/\/+/g, "/"), function (err, data) {
4014                             if (err) {
4015                               if (err.code == "ENOENT" || err.code == "EISDIR") {
4016                                 callback();
4017                               } else {
4018                                 callServerError(500, err);
4019                               }
4020                             } else {
4021                               customDirListingFooter = data.toString();
4022                               callback();
4023                             }
4024                           });
4025                         } else {
4026                           callback();
4027                         }
4028                       } else {
4029                         callServerError(500, err);
4030                       }
4031                     } else {
4032                       customDirListingFooter = data.toString();
4033                       callback();
4034                     }
4035                   });
4036                 }
4038                 // Read custom header and footer content (if available)
4039                 getCustomDirListingHeader(function () {
4040                   getCustomDirListingFooter(function () {
4041                     // Check if custom header has HTML tag
4042                     var headerHasHTMLTag = customDirListingHeader.replace(/<!--(?:(?:(?!--\>)[\s\S])*|)(?:-->|$)/g, "").match(/<html(?![a-zA-Z0-9])(?:"(?:\\(?:[\s\S]|$)|[^\\"])*(?:"|$)|'(?:\\(?:[\s\S]|$)|[^\\'])*(?:'|$)|[^'">])*(?:>|$)/i);
4044                     // Generate HTML head and footer based on configuration and custom content
4045                     var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == "" ?
4046                       (!headerHasHTMLTag ?
4047                         "<!DOCTYPE html><html><head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</title><meta charset=\"UTF-8\" /><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>" :
4048                         customDirListingHeader.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</title>")) :
4049                       head.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</title>")) +
4050                       (!headerHasHTMLTag ? customDirListingHeader : "") +
4051                       "<h1>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</h1><table id=\"directoryListing\"> <tr> <th></th> <th>Filename</th> <th>Size</th> <th>Date</th> </tr>" + (checkPathLevel(decodeURIComponent(origHref)) < 1 ? "" : "<tr><td style=\"width: 24px;\"><img src=\"/.dirimages/return.png\" width=\"24px\" height=\"24px\" alt=\"[RET]\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" + (origHref).replace(/\/+/g, "/").replace(/\/[^\/]*\/?$/, "/") + "\">Return</a></td><td></td><td></td></tr>");
4053                     var htmlFoot = "</table><p><i>" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</i></p>" + customDirListingFooter + (!configJSON.enableDirectoryListingWithDefaultHead || foot == "" ? "</body></html>" : foot);
4055                     if (fs.existsSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/"))) {
4056                       htmlFoot = "</table><hr/>" + fs.readFileSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/")) + htmlFoot;
4057                     }
4059                     fs.readdir(readFrom, function (err, list) {
4060                       try {
4061                         if (err) throw err;
4062                         list = list.sort();
4064                         // Function to get stats for all files in the directory
4065                         function getStatsForAllFilesI(fileList, callback, prefix, pushArray, index) {
4066                           if (fileList.length == 0) {
4067                             callback(pushArray);
4068                             return;
4069                           }
4071                           fs.stat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
4072                             if (err) {
4073                               fs.lstat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
4074                                 pushArray.push({
4075                                   name: fileList[index],
4076                                   stats: err ? null : stats,
4077                                   errored: true
4078                                 });
4079                                 if (index < fileList.length - 1) {
4080                                   getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
4081                                 } else {
4082                                   callback(pushArray);
4083                                 }
4084                               });
4085                             } else {
4086                               pushArray.push({
4087                                 name: fileList[index],
4088                                 stats: stats,
4089                                 errored: false
4090                               });
4091                               if (index < fileList.length - 1) {
4092                                 getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
4093                               } else {
4094                                 callback(pushArray);
4095                               }
4096                             }
4097                           });
4098                         }
4100                         // Wrapper function to get stats for all files
4101                         function getStatsForAllFiles(fileList, prefix, callback) {
4102                           if (!prefix) prefix = "";
4103                           getStatsForAllFilesI(fileList, callback, prefix, [], 0);
4104                         }
4106                         // Get stats for all files in the directory and generate the listing
4107                         getStatsForAllFiles(list, readFrom, function (filelist) {
4108                           var directoryListingRows = [];
4109                           for (var i = 0; i < filelist.length; i++) {
4110                             if (filelist[i].name[0] !== ".") {
4111                               var estats = filelist[i].stats;
4112                               var ename = filelist[i].name;
4113                               var eext = ename.match(/\.([^.]+)$/);
4114                               eext = eext ? eext[1] : "";
4115                               var emime = eext ? mime.contentType(eext) : false;
4116                               if (filelist[i].errored) {
4117                                 directoryListingRows.push(
4118                                   "<tr><td style=\"width: 24px;\"><img src=\"/.dirimages/bad.png\" alt=\"[BAD]\" width=\"24px\" height=\"24px\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" +
4119                                   (href + "/" + encodeURI(ename)).replace(/\/+/g, "/") +
4120                                   "\">" +
4121                                   ename.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") +
4122                                   "</a></td><td>-</td><td>" +
4123                                   (estats ? estats.mtime.toDateString() : "-") +
4124                                   "</td></tr>\r\n"
4125                                 );
4126                               } else {
4127                                 var entry = "<tr><td style=\"width: 24px;\"><img src=\"[img]\" alt=\"[alt]\" width=\"24px\" height=\"24px\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" +
4128                                   (origHref + "/" + encodeURIComponent(ename)).replace(/\/+/g, "/") +
4129                                   (estats.isDirectory() ? "/" : "") +
4130                                   "\">" +
4131                                   ename.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") +
4132                                   "</a></td><td>" +
4133                                   (estats.isDirectory() ? "-" : sizify(estats.size.toString())) +
4134                                   "</td><td>" +
4135                                   estats.mtime.toDateString() +
4136                                   "</td></tr>\r\n";
4138                                 // Determine the file type and set the appropriate image and alt text
4139                                 if (estats.isDirectory()) {
4140                                   entry = entry.replace("[img]", "/.dirimages/directory.png").replace("[alt]", "[DIR]");
4141                                 } else if (!estats.isFile()) {
4142                                   entry = "<tr><td style=\"width: 24px;\"><img src=\"[img]\" alt=\"[alt]\" width=\"24px\" height=\"24px\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" +
4143                                     (origHref + "/" + encodeURIComponent(ename)).replace(/\/+/g, "/") +
4144                                     "\">" +
4145                                     ename.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") +
4146                                     "</a></td><td>-</td><td>" +
4147                                     estats.mtime.toDateString() +
4148                                     "</td></tr>\r\n";
4150                                   // Determine the special file types (block device, character device, etc.)
4151                                   if (estats.isBlockDevice()) {
4152                                     entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[BLK]");
4153                                   } else if (estats.isCharacterDevice()) {
4154                                     entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[CHR]");
4155                                   } else if (estats.isFIFO()) {
4156                                     entry = entry.replace("[img]", "/.dirimages/fifo.png").replace("[alt]", "[FIF]");
4157                                   } else if (estats.isSocket()) {
4158                                     entry = entry.replace("[img]", "/.dirimages/socket.png").replace("[alt]", "[SCK]");
4159                                   }
4160                                 } else if (ename.match(/README|LICEN[SC]E/i)) {
4161                                   entry = entry.replace("[img]", "/.dirimages/important.png").replace("[alt]", "[IMP]");
4162                                 } else if (eext.match(/^(?:[xs]?html?|xml)$/i)) {
4163                                   entry = entry.replace("[img]", "/.dirimages/html.png").replace("[alt]", (eext == "xml" ? "[XML]" : "[HTM]"));
4164                                 } else if (eext == "js") {
4165                                   entry = entry.replace("[img]", "/.dirimages/javascript.png").replace("[alt]", "[JS ]");
4166                                 } else if (eext == "php") {
4167                                   entry = entry.replace("[img]", "/.dirimages/php.png").replace("[alt]", "[PHP]");
4168                                 } else if (eext == "css") {
4169                                   entry = entry.replace("[img]", "/.dirimages/css.png").replace("[alt]", "[CSS]");
4170                                 } else if (emime && emime.split("/")[0] == "image") {
4171                                   entry = entry.replace("[img]", "/.dirimages/image.png").replace("[alt]", (eext == "ico" ? "[ICO]" : "[IMG]"));
4172                                 } else if (emime && emime.split("/")[0] == "font") {
4173                                   entry = entry.replace("[img]", "/.dirimages/font.png").replace("[alt]", "[FON]");
4174                                 } else if (emime && emime.split("/")[0] == "audio") {
4175                                   entry = entry.replace("[img]", "/.dirimages/audio.png").replace("[alt]", "[AUD]");
4176                                 } else if ((emime && emime.split("/")[0] == "text") || eext == "json") {
4177                                   entry = entry.replace("[img]", "/.dirimages/text.png").replace("[alt]", (eext == "json" ? "[JSO]" : "[TXT]"));
4178                                 } else if (emime && emime.split("/")[0] == "video") {
4179                                   entry = entry.replace("[img]", "/.dirimages/video.png").replace("[alt]", "[VID]");
4180                                 } else if (eext.match(/^(?:zip|rar|bz2|[gb7x]z|lzma|tar)$/i)) {
4181                                   entry = entry.replace("[img]", "/.dirimages/archive.png").replace("[alt]", "[ARC]");
4182                                 } else if (eext.match(/^(?:[id]mg|iso|flp)$/i)) {
4183                                   entry = entry.replace("[img]", "/.dirimages/diskimage.png").replace("[alt]", "[DSK]");
4184                                 } else {
4185                                   entry = entry.replace("[img]", "/.dirimages/other.png").replace("[alt]", "[OTH]");
4186                                 }
4187                                 directoryListingRows.push(entry);
4188                               }
4189                             }
4190                           }
4192                           // Push the information about empty directory
4193                           if (directoryListingRows.length == 0) {
4194                             directoryListingRows.push("<tr><td></td><td>No files found</td><td></td><td></td></tr>");
4195                           }
4197                           // Send the directory listing response
4198                           res.writeHead(200, http.STATUS_CODES[200], {
4199                             "Content-Type": "text/html; charset=utf-8"
4200                           });
4201                           res.end(htmlHead + directoryListingRows.join("") + htmlFoot);
4202                           serverconsole.resmessage("Client successfully received content.");
4203                         });
4205                       } catch (err) {
4206                         if (err.code == "ENOENT") {
4207                           callServerError(404);
4208                           serverconsole.errmessage("Resource not found.");
4209                         } else if (err.code == "ENOTDIR") {
4210                           callServerError(404); // Assume that file doesn't exist.
4211                           serverconsole.errmessage("Resource not found.");
4212                         } else if (err.code == "EACCES") {
4213                           callServerError(403);
4214                           serverconsole.errmessage("Access denied.");
4215                         } else if (err.code == "ENAMETOOLONG") {
4216                           callServerError(414);
4217                         } else if (err.code == "EMFILE") {
4218                           callServerError(503);
4219                         } else if (err.code == "ELOOP") {
4220                           callServerError(508); // The symbolic link loop is detected during file system operations.
4221                           serverconsole.errmessage("Symbolic link loop detected.");
4222                         } else {
4223                           callServerError(500, err);
4224                         }
4225                       }
4226                     });
4227                   });
4228                 });
4229               } else {
4230                 // Directory listing is disabled, call 403 Forbidden error
4231                 callServerError(403);
4232                 serverconsole.errmessage("Directory listing is disabled.");
4233               }
4234             } else {
4235               callServerError(501);
4236               serverconsole.errmessage("SVR.JS doesn't support block devices, character devices, FIFOs nor sockets.");
4237               return;
4238             }
4239           }
4240         });
4241       }
4243     try {
4244       // Scan the block list
4245       if (blocklist.check(reqip)) {
4246         // Invoke 403 Forbidden error
4247         callServerError(403);
4248         serverconsole.errmessage("Client is in the block list.");
4249         return;
4250       }
4252       if (req.url == "*") {
4253         // Handle "*" URL
4254         if (req.method == "OPTIONS") {
4255           // Respond with list of methods
4256           var hdss = getCustomHeaders();
4257           hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
4258           res.writeHead(204, http.STATUS_CODES[204], hdss);
4259           res.end();
4260           return;
4261         } else {
4262           // SVR.JS doesn't understand that request, so throw an 400 error
4263           callServerError(400);
4264           return;
4265         }
4266       }
4268       if (req.method == "CONNECT") {
4269         // CONNECT requests should be handled in "connect" event.
4270         callServerError(501);
4271         serverconsole.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library.");
4272         return;
4273       }
4275       // Check for invalid X-Forwarded-For header
4276       if (!isForwardedValid) {
4277         serverconsole.errmessage("X-Forwarded-For header is invalid.");
4278         callServerError(400);
4279         return;
4280       }
4282       // Sanitize URL
4283       var sanitizedHref = sanitizeURL(href, allowDoubleSlashes);
4284       var preparedReqUrl = uobject.pathname + (uobject.search ? uobject.search : "") + (uobject.hash ? uobject.hash : "");
4286       // Check if URL is "dirty"
4287       if (href != sanitizedHref && !isProxy) {
4288         var sanitizedURL = uobject;
4289         sanitizedURL.path = null;
4290         sanitizedURL.href = null;
4291         sanitizedURL.pathname = sanitizedHref;
4292         sanitizedURL.hostname = null;
4293         sanitizedURL.host = null;
4294         sanitizedURL.port = null;
4295         sanitizedURL.protocol = null;
4296         sanitizedURL.slashes = null;
4297         sanitizedURL = url.format(sanitizedURL);
4298         serverconsole.resmessage("URL sanitized: " + req.url + " => " + sanitizedURL);
4299         if (rewriteDirtyURLs) {
4300           req.url = sanitizedURL;
4301           try {
4302             uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4303           } catch (err) {
4304             // Return an 400 error
4305             callServerError(400);
4306             serverconsole.errmessage("Bad request!");
4307             return;
4308           }
4309           search = uobject.search;
4310           href = uobject.pathname;
4311           ext = href.match(/[^\/]\.([^.]+)$/);
4312           if(!ext) ext = "";
4313           else ext = ext[1].toLowerCase();
4314           try {
4315             decodedHref = decodeURIComponent(href);
4316           } catch (err) {
4317             // Return 400 error
4318             callServerError(400);
4319             serverconsole.errmessage("Bad request!");
4320             return;
4321           }
4322         } else {
4323           redirect(sanitizedURL, false);
4324           return;
4325         }
4326       } else if (req.url != preparedReqUrl && !isProxy) {
4327         serverconsole.resmessage("URL sanitized: " + req.url + " => " + preparedReqUrl);
4328         if (rewriteDirtyURLs) {
4329           req.url = preparedReqUrl;
4330         } else {
4331           redirect(preparedReqUrl, false);
4332           return;
4333         }
4334       }
4336       // Handle redirects to HTTPS
4337       if (secure && !fromMain && !disableNonEncryptedServer && !disableToHTTPSRedirect) {
4338         var hostx = req.headers.host;
4339         if (hostx === undefined) {
4340           serverconsole.errmessage("Host header is missing.");
4341           callServerError(400);
4342           return;
4343         }
4345         if (isProxy) {
4346           callServerError(501);
4347           serverconsole.errmessage("This server will never be a proxy.");
4348           return;
4349         }
4351         var isPublicServer = !(req.socket.realRemoteAddress ? req.socket.realRemoteAddress : req.socket.remoteAddress).match(/^(?:localhost$|::1$|f[c-d][0-9a-f]{2}:|(?:::ffff:)?(?:(?:127|10)\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|192\.168\.[0-9]{1,3}\.[0-9]{1,3}|172\.(?:1[6-9]|2[0-9]|3[0-1])\.[0-9]{1,3}\.[0-9]{1,3})$)/i);
4353         var destinationPort = 0;
4355         var parsedHostx = hostx.match(/(\[[^\]]*\]|[^:]*)(?::([0-9]+))?/);
4356         var hostname = parsedHostx[1];
4357         var hostPort = parsedHostx[2] ? parseInt(parsedHostx[2]) : 80;
4358         if (isNaN(hostPort)) hostPort = 80;
4360         if (hostPort == port || (port == pubport && !isPublicServer)) {
4361           destinationPort = sport;
4362         } else {
4363           destinationPort = spubport;
4364         }
4366         redirect("https://" + hostname + (destinationPort == 443 ? "" : (":" + destinationPort)) + req.url);
4367         return;
4368       }
4370       // Handle redirects to addresses with "www." prefix
4371       if (wwwredirect) {
4372         var hostname = req.headers.host.split(":");
4373         var hostport = null;
4374         if (hostname.length > 1 && (hostname[0] != "[" || hostname[hostname.length - 1] != "]")) hostport = hostname.pop();
4375         hostname = hostname.join(":");
4376         if (hostname == domain && hostname.indexOf("www.") != 0) {
4377           redirect((req.socket.encrypted ? "https" : "http") + "://www." + hostname + (hostport ? ":" + hostport : "") + req.url.replace(/\/+/g, "/"));
4378           return;
4379         }
4380       }
4382       // Handle URL rewriting
4383       function rewriteURL(address, map, callback, _fileState, _mapBegIndex) {
4384         var rewrittenURL = address;
4385         var doCallback = true;
4386         if (!isProxy) {
4387           for (var i = (_mapBegIndex ? _mapBegIndex : 0); i < map.length; i++) {
4388             var mapEntry = map[i];
4389             if (href != "/" && (mapEntry.isNotDirectory || mapEntry.isNotFile) && !_fileState) {
4390               fs.stat("." + decodeURIComponent(href), function (err, stats) {
4391                 var _fileState = 3;
4392                 if (err) {
4393                   _fileState = 3;
4394                 } else if (stats.isDirectory()) {
4395                   _fileState = 2;
4396                 } else if (stats.isFile()) {
4397                   _fileState = 1;
4398                 } else {
4399                   _fileState = 3;
4400                 }
4401                 rewriteURL(address, map, callback, _fileState, i);
4402               });
4403               doCallback = false;
4404               break;
4405             }
4406             var tempRewrittenURL = rewrittenURL;
4407             if (!mapEntry.allowDoubleSlashes) {
4408               address = address.replace(/\/+/g,"/");
4409               tempRewrittenURL = address;
4410             }
4411             if (matchHostname(mapEntry.host) && ipMatch(mapEntry.ip, req.socket ? req.socket.localAddress : undefined) && address.match(createRegex(mapEntry.definingRegex)) && !(mapEntry.isNotDirectory && _fileState == 2) && !(mapEntry.isNotFile && _fileState == 1)) {
4412               rewrittenURL = tempRewrittenURL;
4413               try {
4414                 mapEntry.replacements.forEach(function (replacement) {
4415                   rewrittenURL = rewrittenURL.replace(createRegex(replacement.regex), replacement.replacement);
4416                 });
4417                 if (mapEntry.append) rewrittenURL += mapEntry.append;
4418               } catch (err) {
4419                 doCallback = false;
4420                 callback(err, null);
4421               }
4422               break;
4423             }
4424           }
4425         }
4426         if (doCallback) callback(null, rewrittenURL);
4427       }
4429       // Trailing slash redirection
4430       function redirectTrailingSlashes(callback) {
4431         if (!isProxy && !disableTrailingSlashRedirects && href[href.length - 1] != "/" && origHref[origHref.length - 1] != "/") {
4432           fs.stat("." + decodeURIComponent(href), function (err, stats) {
4433             if (err || !stats.isDirectory()) {
4434               try {
4435                 callback();
4436               } catch (err) {
4437                 callServerError(500, err);
4438               }
4439             } else {
4440               var destinationURL = uobject;
4441               destinationURL.path = null;
4442               destinationURL.href = null;
4443               destinationURL.pathname = origHref + "/";
4444               destinationURL.hostname = null;
4445               destinationURL.host = null;
4446               destinationURL.port = null;
4447               destinationURL.protocol = null;
4448               destinationURL.slashes = null;
4449               destinationURL = url.format(destinationURL);
4450               redirect(destinationURL);
4451             }
4452           });
4453         } else {
4454           callback();
4455         }
4456       }
4458       origHref = href;
4460       // Add web root postfixes
4461       if (!isProxy) {
4462         var preparedReqUrl3 = (allowPostfixDoubleSlashes ? (href.replace(/\/+/,"/") + (uobject.search ? uobject.search : "") + (uobject.hash ? uobject.hash : "")) : req.url);
4463         var urlWithPostfix = preparedReqUrl3;
4464         var postfixPrefix = "";
4465         wwwrootPostfixPrefixesVHost.every(function (currentPostfixPrefix) {
4466           if (preparedReqUrl3.indexOf(currentPostfixPrefix) == 0) {
4467             if (currentPostfixPrefix.match(/\/+$/)) postfixPrefix = currentPostfixPrefix.replace(/\/+$/, "");
4468             else if (urlWithPostfix.length == currentPostfixPrefix.length || urlWithPostfix[currentPostfixPrefix.length] == "?" || urlWithPostfix[currentPostfixPrefix.length] == "/" || urlWithPostfix[currentPostfixPrefix.length] == "#") postfixPrefix = currentPostfixPrefix;
4469             else return true;
4470             urlWithPostfix = urlWithPostfix.substring(postfixPrefix.length);
4471             return false;
4472           } else {
4473             return true;
4474           }
4475         });
4476         wwwrootPostfixesVHost.every(function (postfixEntry) {
4477           if (matchHostname(postfixEntry.host) && ipMatch(postfixEntry.ip, req.socket ? req.socket.localAddress : undefined) && !(postfixEntry.skipRegex && preparedReqUrl3.match(createRegex(postfixEntry.skipRegex)))) {
4478             urlWithPostfix = postfixPrefix + "/" + postfixEntry.postfix + urlWithPostfix;
4479             return false;
4480           } else {
4481             return true;
4482           }
4483         });
4484         if (urlWithPostfix != preparedReqUrl3) {
4485           serverconsole.resmessage("Added web root postfix: " + req.url + " => " + urlWithPostfix);
4486           req.url = urlWithPostfix;
4487           try {
4488             uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4489           } catch (err) {
4490             // Return an 400 error
4491             callServerError(400);
4492             serverconsole.errmessage("Bad request!");
4493             return;
4494           }
4495           search = uobject.search;
4496           href = uobject.pathname;
4497           ext = href.match(/[^\/]\.([^.]+)$/);
4498           if(!ext) ext = "";
4499           else ext = ext[1].toLowerCase();
4501           try {
4502             decodedHref = decodeURIComponent(href);
4503           } catch (err) {
4504             // Return 400 error
4505             callServerError(400);
4506             serverconsole.errmessage("Bad request!");
4507             return;
4508           }
4510           var sHref = sanitizeURL(href, allowDoubleSlashes);
4511           var preparedReqUrl2 = uobject.pathname + (uobject.search ? uobject.search : "") + (uobject.hash ? uobject.hash : "");
4513           if (req.url != preparedReqUrl2 || sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")) {
4514             callServerError(403);
4515             serverconsole.errmessage("Content blocked.");
4516             return;
4517           } else if (sHref != href) {
4518             var rewrittenAgainURL = uobject;
4519             rewrittenAgainURL.path = null;
4520             rewrittenAgainURL.href = null;
4521             rewrittenAgainURL.pathname = sHref;
4522             rewrittenAgainURL.hostname = null;
4523             rewrittenAgainURL.host = null;
4524             rewrittenAgainURL.port = null;
4525             rewrittenAgainURL.protocol = null;
4526             rewrittenAgainURL.slashes = null;
4527             rewrittenAgainURL = url.format(rewrittenAgainURL);
4528             serverconsole.resmessage("URL sanitized: " + req.url + " => " + rewrittenAgainURL);
4529             req.url = rewrittenAgainURL;
4530             try {
4531               uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4532             } catch (err) {
4533               // Return an 400 error
4534               callServerError(400);
4535               serverconsole.errmessage("Bad request!");
4536               return;
4537             }
4538             search = uobject.search;
4539             href = uobject.pathname;
4540             ext = href.match(/[^\/]\.([^.]+)$/);
4541             if(!ext) ext = "";
4542             else ext = ext[1].toLowerCase();
4543             try {
4544               decodedHref = decodeURIComponent(href);
4545             } catch (err) {
4546               // Return 400 error
4547               callServerError(400);
4548               serverconsole.errmessage("Bad request!");
4549               return;
4550             }
4551           }
4552         }
4553       }
4555       // Rewrite URLs
4556       rewriteURL(req.url, rewriteMap, function (err, rewrittenURL) {
4557         if (err) {
4558           callServerError(500, err);
4559           return;
4560         }
4561         if (rewrittenURL != req.url) {
4562           serverconsole.resmessage("URL rewritten: " + req.url + " => " + rewrittenURL);
4563           req.url = rewrittenURL;
4564           try {
4565             uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4566           } catch (err) {
4567             // Return an 400 error
4568             callServerError(400);
4569             serverconsole.errmessage("Bad request!");
4570             return;
4571           }
4572           search = uobject.search;
4573           href = uobject.pathname;
4574           ext = href.match(/[^\/]\.([^.]+)$/);
4575           if(!ext) ext = "";
4576           else ext = ext[1].toLowerCase();
4577           try {
4578             decodedHref = decodeURIComponent(href);
4579           } catch (err) {
4580             // Return 400 error
4581             callServerError(400);
4582             serverconsole.errmessage("Bad request!");
4583             return;
4584           }
4586           var sHref = sanitizeURL(href, allowDoubleSlashes);
4587           var preparedReqUrl2 = uobject.pathname + (uobject.search ? uobject.search : "") + (uobject.hash ? uobject.hash : "");
4589           if (req.url != preparedReqUrl2 || sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")) {
4590             callServerError(403);
4591             serverconsole.errmessage("Content blocked.");
4592             return;
4593           } else if (sHref != href) {
4594             var rewrittenAgainURL = uobject;
4595             rewrittenAgainURL.path = null;
4596             rewrittenAgainURL.href = null;
4597             rewrittenAgainURL.pathname = sHref;
4598             rewrittenAgainURL.hostname = null;
4599             rewrittenAgainURL.host = null;
4600             rewrittenAgainURL.port = null;
4601             rewrittenAgainURL.protocol = null;
4602             rewrittenAgainURL.slashes = null;
4603             rewrittenAgainURL = url.format(rewrittenAgainURL);
4604             serverconsole.resmessage("URL sanitized: " + req.url + " => " + rewrittenAgainURL);
4605             req.url = rewrittenAgainURL;
4606             try {
4607               uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4608             } catch (err) {
4609               // Return an 400 error
4610               callServerError(400);
4611               serverconsole.errmessage("Bad request!");
4612               return;
4613             }
4614             search = uobject.search;
4615             href = uobject.pathname;
4616             ext = href.match(/[^\/]\.([^.]+)$/);
4617             if(!ext) ext = "";
4618             else ext = ext[1].toLowerCase();
4619             try {
4620               decodedHref = decodeURIComponent(href);
4621             } catch (err) {
4622               // Return 400 error
4623               callServerError(400);
4624               serverconsole.errmessage("Bad request!");
4625               return;
4626             }
4627           }
4628         }
4629         // Set response headers
4630         if (!isProxy) {
4631           var hkh = getCustomHeaders();
4632           Object.keys(hkh).forEach(function (hkS) {
4633             try {
4634               res.setHeader(hkS, hkh[hkS]);
4635             } catch (err) {
4636               // Headers will not be set.
4637             }
4638           });
4639         }
4641         // Prepare the path (remove multiple slashes)
4642         var decodedHrefWithoutDuplicateSlashes = decodedHref.replace(/\/+/g,"/");
4644         // Check if path is forbidden
4645         if ((isForbiddenPath(decodedHrefWithoutDuplicateSlashes, "config") || isForbiddenPath(decodedHrefWithoutDuplicateSlashes, "certificates")) && !isProxy) {
4646           callServerError(403);
4647           serverconsole.errmessage("Access to configuration file/certificates is denied.");
4648           return;
4649         } else if (isIndexOfForbiddenPath(decodedHrefWithoutDuplicateSlashes, "temp") && !isProxy) {
4650           callServerError(403);
4651           serverconsole.errmessage("Access to temporary folder is denied.");
4652           return;
4653         } else if (isIndexOfForbiddenPath(decodedHrefWithoutDuplicateSlashes, "log") && !isProxy && (configJSON.enableLogging || configJSON.enableLogging == undefined) && !configJSON.enableRemoteLogBrowsing) {
4654           callServerError(403);
4655           serverconsole.errmessage("Access to log files is denied.");
4656           return;
4657         } else if (isForbiddenPath(decodedHrefWithoutDuplicateSlashes, "svrjs") && !isProxy && !exposeServerVersion) {
4658           callServerError(403);
4659           serverconsole.errmessage("Access to SVR.JS script is denied.");
4660           return;
4661         } else if ((isForbiddenPath(decodedHrefWithoutDuplicateSlashes, "svrjs") || isForbiddenPath(decodedHrefWithoutDuplicateSlashes, "serverSideScripts") || isIndexOfForbiddenPath(decodedHrefWithoutDuplicateSlashes, "serverSideScriptDirectories")) && !isProxy && (configJSON.disableServerSideScriptExpose || configJSON.disableServerSideScriptExpose === undefined)) {
4662           callServerError(403);
4663           serverconsole.errmessage("Access to sources is denied.");
4664           return;
4665         } else {
4666           var nonscodeIndex = -1;
4667           var authIndex = -1;
4668           var regexI = [];
4670           // Scan for non-standard codes
4671           if (!isProxy && nonStandardCodes != undefined) {
4672             for (var i = 0; i < nonStandardCodes.length; i++) {
4673               if (matchHostname(nonStandardCodes[i].host) && ipMatch(nonStandardCodes[i].ip, req.socket ? req.socket.localAddress : undefined)) {
4674                 var isMatch = false;
4675                 var hrefWithoutDuplicateSlashes = href.replace(/\/+/g,"/");
4676                 if (nonStandardCodes[i].regex) {
4677                   // Regex match
4678                   var createdRegex = createRegex(nonStandardCodes[i].regex, true);
4679                   isMatch = req.url.match(createdRegex) || hrefWithoutDuplicateSlashes.match(createdRegex);
4680                   regexI[i] = createdRegex;
4681                 } else {
4682                   // Non-regex match
4683                   isMatch = nonStandardCodes[i].url == hrefWithoutDuplicateSlashes || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == hrefWithoutDuplicateSlashes.toLowerCase());
4684                 }
4685                 if (isMatch) {
4686                   if (nonStandardCodes[i].scode == 401) {
4687                     // HTTP authentication
4688                     if (authIndex == -1) {
4689                       authIndex = i;
4690                     }
4691                   } else {
4692                     if (nonscodeIndex == -1) {
4693                       if ((nonStandardCodes[i].scode == 403 || nonStandardCodes[i].scode == 451) && nonStandardCodes[i].users !== undefined) {
4694                         if (nonStandardCodes[i].users.check(reqip)) nonscodeIndex = i;
4695                       } else {
4696                         nonscodeIndex = i;
4697                       }
4698                     }
4699                   }
4700                 }
4701               }
4702             }
4703           }
4705           // Handle non-standard codes
4706           if (nonscodeIndex > -1) {
4707             var nonscode = nonStandardCodes[nonscodeIndex];
4708             if (nonscode.scode == 301 || nonscode.scode == 302 || nonscode.scode == 307 || nonscode.scode == 308) {
4709               var location = "";
4710               if (regexI[nonscodeIndex]) {
4711                 location = req.url.replace(regexI[nonscodeIndex], nonscode.location);
4712                 if(location == req.url) {
4713                   // Fallback replacement
4714                   location = hrefWithoutDuplicateSlashes.replace(regexI[nonscodeIndex], nonscode.location);
4715                 }
4716               } else if (req.url.split("?")[1] == undefined || req.url.split("?")[1] == null || req.url.split("?")[1] == "" || req.url.split("?")[1] == " ") {
4717                 location = nonscode.location;
4718               } else {
4719                 location = nonscode.location + "?" + req.url.split("?")[1];
4720               }
4721               redirect(location, nonscode.scode == 302 || nonscode.scode == 307, nonscode.scode == 307 || nonscode.scode == 308);
4722               return;
4723             } else {
4724               callServerError(nonscode.scode);
4725               if (nonscode.scode == 403) {
4726                 serverconsole.errmessage("Content blocked.");
4727               } else if (nonscode.scode == 410) {
4728                 serverconsole.errmessage("Content is gone.");
4729               } else if (nonscode.scode == 418) {
4730                 serverconsole.errmessage("SVR.JS is always a teapot ;)");
4731               } else {
4732                 serverconsole.errmessage("Client fails receiving content.");
4733               }
4734               return;
4735             }
4736           }
4738           // Handle HTTP authentication
4739           if (authIndex > -1) {
4740             var authcode = nonStandardCodes[authIndex];
4742             // Function to check if passwords match
4743             function checkIfPasswordMatches(list, password, callback, _i) {
4744               if (!_i) _i = 0;
4745               var cb = function (hash) {
4746                 if (hash == list[_i].pass) {
4747                   callback(true);
4748                 } else if (_i >= list.length - 1) {
4749                   callback(false);
4750                 } else {
4751                   checkIfPasswordMatches(list, password, callback, _i + 1);
4752                 }
4753               };
4754               var hashedPassword = sha256(password + list[_i].salt);
4755               var cacheEntry = null;
4756               if (list[_i].scrypt) {
4757                 if (!crypto.scrypt) {
4758                   callServerError(500, new Error("SVR.JS doesn't support scrypt-hashed passwords on Node.JS versions without scrypt hash support."));
4759                   return;
4760                 } else {
4761                   cacheEntry = scryptCache.find(function (entry) {
4762                     return (entry.password == hashedPassword && entry.salt == list[_i].salt);
4763                   });
4764                   if (cacheEntry) {
4765                     cb(cacheEntry.hash);
4766                   } else {
4767                     crypto.scrypt(password, list[_i].salt, 64, function (err, derivedKey) {
4768                       if (err) {
4769                         callServerError(500, err);
4770                       } else {
4771                         var key = derivedKey.toString("hex");
4772                         scryptCache.push({
4773                           hash: key,
4774                           password: hashedPassword,
4775                           salt: list[_i].salt,
4776                           addDate: new Date()
4777                         });
4778                         cb(key);
4779                       }
4780                     });
4781                   }
4782                 }
4783               } else if (list[_i].pbkdf2) {
4784                 if (crypto.__disabled__ !== undefined) {
4785                   callServerError(500, new Error("SVR.JS doesn't support PBKDF2-hashed passwords on Node.JS versions without crypto support."));
4786                   return;
4787                 } else {
4788                   cacheEntry = pbkdf2Cache.find(function (entry) {
4789                     return (entry.password == hashedPassword && entry.salt == list[_i].salt);
4790                   });
4791                   if (cacheEntry) {
4792                     cb(cacheEntry.hash);
4793                   } else {
4794                     crypto.pbkdf2(password, list[_i].salt, 36250, 64, "sha512", function (err, derivedKey) {
4795                       if (err) {
4796                         callServerError(500, err);
4797                       } else {
4798                         var key = derivedKey.toString("hex");
4799                         pbkdf2Cache.push({
4800                           hash: key,
4801                           password: hashedPassword,
4802                           salt: list[_i].salt,
4803                           addDate: new Date()
4804                         });
4805                         cb(key);
4806                       }
4807                     });
4808                   }
4809                 }
4810               } else {
4811                 cb(hashedPassword);
4812               }
4813             }
4815             function authorizedCallback(bruteProtection) {
4816               try {
4817                 var ha = getCustomHeaders();
4818                 ha["WWW-Authenticate"] = "Basic realm=\"" + (authcode.realm ? authcode.realm.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\"";
4819                 var credentials = req.headers["authorization"];
4820                 if (!credentials) {
4821                   callServerError(401, ha);
4822                   serverconsole.errmessage("Content needs authorization.");
4823                   return;
4824                 }
4825                 var credentialsMatch = credentials.match(/^Basic (.+)$/);
4826                 if (!credentialsMatch) {
4827                   callServerError(401, ha);
4828                   serverconsole.errmessage("Malformed credentials.");
4829                   return;
4830                 }
4831                 var decodedCredentials = Buffer.from(credentialsMatch[1], "base64").toString("utf8");
4832                 var decodedCredentialsMatch = decodedCredentials.match(/^([^:]*):(.*)$/);
4833                 if (!decodedCredentialsMatch) {
4834                   callServerError(401, ha);
4835                   serverconsole.errmessage("Malformed credentials.");
4836                   return;
4837                 }
4838                 var username = decodedCredentialsMatch[1];
4839                 var password = decodedCredentialsMatch[2];
4840                 var usernameMatch = [];
4841                 var sha256Count = 0;
4842                 var pbkdf2Count = 0;
4843                 var scryptCount = 0;
4844                 if (!authcode.userList || authcode.userList.indexOf(username) > -1) {
4845                   usernameMatch = users.filter(function (entry) {
4846                     if(entry.scrypt) {
4847                       scryptCount++;
4848                     } else if(entry.pbkdf2) {
4849                       pbkdf2Count++;
4850                     } else {
4851                       sha256Count++;
4852                     }
4853                     return entry.name == username;
4854                   });
4855                 }
4856                 if (usernameMatch.length == 0) {
4857                   // Pushing false user match to prevent time-based user enumeration
4858                   var fakeCredentials = {
4859                     name: username,
4860                     pass: "SVRJSAWebServerRunningOnNodeJS",
4861                     salt: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0"
4862                   };
4863                   if (!process.isBun) {
4864                     if (scryptCount > sha256Count && scryptCount > pbkdf2Count) {
4865                       fakeCredentials.scrypt = true;
4866                     } else if (pbkdf2Count > sha256Count) {
4867                       fakeCredentials.pbkdf2 = true;
4868                     }
4869                   }
4870                   usernameMatch.push(fakeCredentials);
4871                 }
4872                 checkIfPasswordMatches(usernameMatch, password, function (authorized) {
4873                   try {
4874                     if (!authorized) {
4875                       if (bruteProtection) {
4876                         if (process.send) {
4877                           process.send("\x12AUTHW" + reqip);
4878                         } else {
4879                           if (!bruteForceDb[reqip]) bruteForceDb[reqip] = {
4880                             invalidAttempts: 0
4881                           };
4882                           bruteForceDb[reqip].invalidAttempts++;
4883                           if (bruteForceDb[reqip].invalidAttempts >= 10) {
4884                             bruteForceDb[reqip].lastAttemptDate = new Date();
4885                           }
4886                         }
4887                       }
4888                       callServerError(401, ha);
4889                       serverconsole.errmessage("User \"" + String(username).replace(/[\r\n]/g, "") + "\" failed to log in.");
4890                     } else {
4891                       if (bruteProtection) {
4892                         if (process.send) {
4893                           process.send("\x12AUTHR" + reqip);
4894                         } else {
4895                           if (bruteForceDb[reqip]) bruteForceDb[reqip] = {
4896                             invalidAttempts: 0
4897                           };
4898                         }
4899                       }
4900                       serverconsole.reqmessage("Client is logged in as \"" + String(username).replace(/[\r\n]/g, "") + "\".");
4901                       authUser = username;
4902                       redirectTrailingSlashes(function () {
4903                         modExecute(mods, vres);
4904                       });
4905                     }
4906                   } catch (err) {
4907                     callServerError(500, err);
4908                     return;
4909                   }
4910                 });
4911               } catch (err) {
4912                 callServerError(500, err);
4913                 return;
4914               }
4915             }
4916             if (authcode.disableBruteProtection) {
4917               // Don't brute-force protect it, just do HTTP authentication
4918               authorizedCallback(false);
4919             } else if (!process.send) {
4920               // Query data from JS object database
4921               if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) {
4922                 if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = {
4923                   invalidAttempts: 5
4924                 };
4925                 authorizedCallback(true);
4926               } else {
4927                 callServerError(429);
4928                 serverconsole.errmessage("Brute force limit reached!");
4929               }
4930             } else {
4931               var listenerEmitted = false;
4933               // Listen for brute-force protection response
4934               function authMessageListener(message) {
4935                 if (listenerEmitted) return;
4936                 if (message == "\x14AUTHA" + reqip || message == "\x14AUTHD" + reqip) {
4937                   process.removeListener("message", authMessageListener);
4938                   listenerEmitted = true;
4939                 }
4940                 if (message == "\x14AUTHD" + reqip) {
4941                   callServerError(429);
4942                   serverconsole.errmessage("Brute force limit reached!");
4943                 } else if (message == "\x14AUTHA" + reqip) {
4944                   authorizedCallback(true);
4945                 }
4946               }
4947               process.on("message", authMessageListener);
4948               process.send("\x12AUTHQ" + reqip);
4949             }
4950           } else {
4951             redirectTrailingSlashes(function () {
4952               modExecute(mods, vres);
4953             });
4954           }
4955         }
4957       });
4958     } catch (err) {
4959       callServerError(500, err);
4960     }
4961   });
4962     });
4963   }
4964   function serverErrorHandler(err, isRedirect) {
4965     if(isRedirect) attmtsRedir--;
4966     else attmts--;
4967     if (cluster.isPrimary === undefined && (isRedirect ? attmtsRedir : attmts)) {
4968       serverconsole.locerrmessage(serverErrorDescs[err.code] ? serverErrorDescs[err.code] : serverErrorDescs["UNKNOWN"]);
4969       serverconsole.locmessage((isRedirect ? attmtsRedir : attmts) + " attempts left.");
4970     } else {
4971       try {
4972         process.send("\x12ERRLIST" + (isRedirect ? attmtsRedir : attmts) + err.code);
4973       } catch (err) {
4974         // Probably main process exited
4975       }
4976     }
4977     if ((isRedirect ? attmtsRedir : attmts) > 0) {
4978       (isRedirect ? server2 : server).close();
4979       setTimeout(start, 900);
4980     } else {
4981       try {
4982         if (cluster.isPrimary !== undefined) process.send("\x12ERRCRASH" + err.code);
4983       } catch (err) {
4984         // Probably main process exited
4985       }
4986       setTimeout(function () {
4987         var errno = errors[err.code];
4988         process.exit(errno ? errno : 1);
4989       }, 50);
4990     }
4991   }
4993   server.on("error", function (err) {
4994     serverErrorHandler(err, false);
4995   });
4996   server.on("listening", function () {
4997     attmts = 5;
4998     listeningMessage();
4999   });
5002 var closedMaster = true;
5004 // IPC listener for server listening signalization
5005 function listenConnListener(msg) {
5006   if (msg == "\x12LISTEN") {
5007     listeningMessage();
5008   }
5011 // IPC listener for brue force protection
5012 function bruteForceListenerWrapper(worker) {
5013   return function bruteForceListener(message) {
5014     var ip = "";
5015     if (message.substring(0, 6) == "\x12AUTHQ") {
5016       ip = message.substring(6);
5017       if (!bruteForceDb[ip] || !bruteForceDb[ip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[ip].lastAttemptDate)) {
5018         if (bruteForceDb[ip] && bruteForceDb[ip].invalidAttempts >= 10) bruteForceDb[ip] = {
5019           invalidAttempts: 5
5020         };
5021         worker.send("\x14AUTHA" + ip);
5022       } else {
5023         worker.send("\x14AUTHD" + ip);
5024       }
5025     } else if (message.substring(0, 6) == "\x12AUTHR") {
5026       ip = message.substring(6);
5027       if (bruteForceDb[ip]) bruteForceDb[ip] = {
5028         invalidAttempts: 0
5029       };
5030     } else if (message.substring(0, 6) == "\x12AUTHW") {
5031       ip = message.substring(6);
5032       if (!bruteForceDb[ip]) bruteForceDb[ip] = {
5033         invalidAttempts: 0
5034       };
5035       bruteForceDb[ip].invalidAttempts++;
5036       if (bruteForceDb[ip].invalidAttempts >= 10) {
5037         bruteForceDb[ip].lastAttemptDate = new Date();
5038       }
5039     }
5040   };
5043 var isWorkerHungUpBuff = true;
5044 var isWorkerHungUpBuff2 = true;
5046 // General IPC message listener
5047 function msgListener(msg) {
5048   for (var i = 0; i < Object.keys(cluster.workers).length; i++) {
5049     if (msg == "\x12END") {
5050       cluster.workers[Object.keys(cluster.workers)[i]].removeAllListeners("message");
5051       cluster.workers[Object.keys(cluster.workers)[i]].on("message", bruteForceListenerWrapper(cluster.workers[Object.keys(cluster.workers)[i]]));
5052       cluster.workers[Object.keys(cluster.workers)[i]].on("message", listenConnListener);
5053     }
5054   }
5055   if (msg == "\x12CLOSE") {
5056     closedMaster = true;
5057   } else if (msg == "\x12LISTEN" || msg.substring(0, 4) == "\x12AUTH") {
5058     // Do nothing!
5059   } else if (msg == "\x12KILLOK") {
5060     if (typeof isWorkerHungUpBuff != "undefined") isWorkerHungUpBuff = false;
5061   } else if (msg == "\x12PINGOK") {
5062     if (typeof isWorkerHungUpBuff2 != "undefined") isWorkerHungUpBuff2 = false;
5063   } else if (msg == "\x12KILLTERMMSG") {
5064     serverconsole.locmessage("Terminating unused worker process...");
5065   } else if (msg == "\x12SAVEGOOD") {
5066     serverconsole.locmessage("Configuration saved.");
5067   } else if (msg.indexOf("\x12SAVEERR") == 0) {
5068     serverconsole.locwarnmessage("There was a problem while saving configuration file. Reason: " + msg.substring(8));
5069   } else if (msg == "\x12END") {
5070     cluster.workers[Object.keys(cluster.workers)[0]].on("message", function (msg) {
5071       if (msg.length >= 8 && msg.indexOf("\x12ERRLIST") == 0) {
5072         var tries = parseInt(msg.substring(8, 9));
5073         var errCode = msg.substring(9);
5074         serverconsole.locerrmessage(serverErrorDescs[errCode] ? serverErrorDescs[errCode] : serverErrorDescs["UNKNOWN"]);
5075         serverconsole.locmessage(tries + " attempts left.");
5076       }
5077       if (msg.length >= 9 && msg.indexOf("\x12ERRCRASH") == 0) {
5078         var errno = errors[msg.substring(9)];
5079         process.exit(errno ? errno : 1);
5080       }
5081     });
5082   } else {
5083     serverconsole.climessage(msg);
5084   }
5087 var messageTransmitted = false;
5089 function listeningMessage() {
5090   if (typeof closedMaster !== "undefined") closedMaster = false;
5091   if (!cluster.isPrimary && cluster.isPrimary !== undefined) {
5092     process.send("\x12LISTEN");
5093     return;
5094   }
5095   var listenToLocalhost = (listenAddress && (listenAddress == "localhost" || listenAddress.match(/^127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) || listenAddress.match(/^(?:0{0,4}:)+0{0,3}1$/)));
5096   var listenToAny = (!listenAddress || listenAddress.match(/^0{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) || listenAddress.match(/^(?:0{0,4}:)+0{0,4}$/));
5097   var sListenToLocalhost = (sListenAddress && (sListenAddress == "localhost" || sListenAddress.match(/^127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) || sListenAddress.match(/^(?:0{0,4}:)+0{0,3}1$/)));
5098   var sListenToAny = (!sListenAddress || sListenAddress.match(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) || sListenAddress.match(/^(?:0{0,4}:)+0{0,4}$/));
5099   var accHost = host;
5100   var sAccHost = host;
5101   if (!listenToAny) accHost = listenAddress;
5102   if (!sListenToAny) sAccHost = sListenAddress;
5103   if (messageTransmitted) return;
5104   messageTransmitted = true;
5105   serverconsole.locmessage("Started server at: ");
5106   if (secure && (sListenToLocalhost || sListenToAny)) {
5107     if (typeof sport === "number") {
5108       serverconsole.locmessage("* https://localhost" + (sport == 443 ? "" : (":" + sport)));
5109     } else {
5110       serverconsole.locmessage("* " + sport); // Unix socket or Windows named pipe
5111     }
5112   }
5113   if (!(secure && disableNonEncryptedServer) && (listenToLocalhost || listenToAny)) {
5114     if (typeof port === "number") {
5115       serverconsole.locmessage("* http://localhost" + (port == 80 ? "" : (":" + port)));
5116     } else {
5117       serverconsole.locmessage("* " + port); // Unix socket or Windows named pipe
5118     }
5119   }
5120   if (secure && typeof sport === "number" && !sListenToLocalhost && (!sListenToAny || (host != "" && host != "[offline]"))) serverconsole.locmessage("* https://" + (sAccHost.indexOf(":") > -1 ? "[" + sAccHost + "]" : sAccHost) + (sport == 443 ? "" : (":" + sport)));
5121   if (!(secure && disableNonEncryptedServer) && !listenToLocalhost && (!listenToAny || (host != "" && host != "[offline]")) && typeof port === "number") serverconsole.locmessage("* http://" + (accHost.indexOf(":") > -1 ? "[" + accHost + "]" : accHost) + (port == 80 ? "" : (":" + port)));
5122   ipStatusCallback(function () {
5123     if (pubip != "") {
5124       if (secure && !sListenToLocalhost) serverconsole.locmessage("* https://" + (pubip.indexOf(":") > -1 ? "[" + pubip + "]" : pubip) + (spubport == 443 ? "" : (":" + spubport)));
5125       if (!(secure && disableNonEncryptedServer) && !listenToLocalhost) serverconsole.locmessage("* http://" + (pubip.indexOf(":") > -1 ? "[" + pubip + "]" : pubip) + (pubport == 80 ? "" : (":" + pubport)));
5126     }
5127     if (domain != "") {
5128       if (secure && !sListenToLocalhost) serverconsole.locmessage("* https://" + domain + (spubport == 443 ? "" : (":" + spubport)));
5129       if (!(secure && disableNonEncryptedServer) && !listenToLocalhost) serverconsole.locmessage("* http://" + domain + (pubport == 80 ? "" : (":" + pubport)));
5130     }
5131     serverconsole.locmessage("For CLI help, you can type \"help\"");
5132   });
5135 function start(init) {
5136   init = Boolean(init);
5137   if (cluster.isPrimary || cluster.isPrimary === undefined) {
5138     if (init) {
5139       for (i = 0; i < logo.length; i++) console.log(logo[i]); // Print logo
5140       console.log();
5141       console.log("Welcome to \x1b[1mSVR.JS - a web server running on Node.JS\x1b[0m");
5143       // Print warnings
5144       if (version.indexOf("Nightly-") === 0) serverconsole.locwarnmessage("This version is only for test purposes and may be unstable.");
5145       if (configJSON.enableHTTP2 && !secure) serverconsole.locwarnmessage("HTTP/2 without HTTPS may not work in web browsers. Web browsers only support HTTP/2 with HTTPS!");
5146       if (process.isBun) {
5147         serverconsole.locwarnmessage("Bun support is experimental. Some features of SVR.JS, SVR.JS mods and SVR.JS server-side JavaScript may not work as expected.");
5148         if (users.some(function (entry) {
5149           return entry.pbkdf2;
5150         })) serverconsole.locwarnmessage("PBKDF2 password hashing function in Bun blocks the event loop, which may result in denial of service.");
5151       }
5152       if (cluster.isPrimary === undefined) serverconsole.locwarnmessage("You're running SVR.JS on single thread. Reliability may suffer, as the server is stopped after crash.");
5153       if (crypto.__disabled__ !== undefined) serverconsole.locwarnmessage("Your Node.JS version doesn't have crypto support! The 'crypto' module is essential for providing cryptographic functionality in Node.JS. Without crypto support, certain security features may be unavailable, and some functionality may not work as expected. It's recommended to use a Node.JS version that includes crypto support to ensure the security and proper functioning of your server.");
5154       if (crypto.__disabled__ === undefined && !crypto.scrypt) serverconsole.locwarnmessage("Your JavaScript runtime doesn't have native scrypt support. HTTP authentication involving scrypt hashes will not work.");
5155       if (!process.isBun && /^v(?:[0-9]\.|1[0-7]\.|18\.(?:[0-9]|1[0-8])\.|18\.19\.0|20\.(?:[0-9]|10)\.|20\.11\.0|21\.[0-5]\.|21\.6\.0|21\.6\.1(?![0-9]))/.test(process.version)) serverconsole.locwarnmessage("Your Node.JS version is vulnerable to HTTP server DoS (CVE-2024-22019).");
5156       if (!process.isBun && /^v(?:[0-9]\.|1[0-7]\.|18\.(?:1?[0-9])\.|18\.20\.0|20\.(?:[0-9]|1[01])\.|20\.12\.0|21\.[0-6]\.|21\.7\.0|21\.7\.1(?![0-9]))/.test(process.version)) serverconsole.locwarnmessage("Your Node.JS version is vulnerable to HTTP server request smuggling (CVE-2024-27982).");
5157       if (process.getuid && process.getuid() == 0) serverconsole.locwarnmessage("You're running SVR.JS as root. It's recommended to run SVR.JS as an non-root user. Running SVR.JS as root may increase the risks of OS command execution vulnerabilities.");
5158       if (!process.isBun && secure && process.versions && process.versions.openssl && process.versions.openssl.substring(0, 2) == "1.") {
5159         if (new Date() > new Date("11 September 2023")) {
5160           serverconsole.locwarnmessage("OpenSSL 1.x is no longer receiving security updates after 11th September 2023. Your HTTPS communication might be vulnerable. It is recommended to update to a newer version of Node.JS that includes OpenSSL 3.0 or higher to ensure the security of your server and data.");
5161         } else {
5162           serverconsole.locwarnmessage("OpenSSL 1.x will no longer receive security updates after 11th September 2023. Your HTTPS communication might be vulnerable in future. It is recommended to update to a newer version of Node.JS that includes OpenSSL 3.0 or higher to ensure the security of your server and data.");
5163         }
5164       }
5165       if (secure && configJSON.enableOCSPStapling && ocsp._errored) serverconsole.locwarnmessage("Can't load OCSP module. OCSP stapling will be disabled. OCSP stapling is a security feature that improves the performance and security of HTTPS connections by caching the certificate status response. If you require this feature, consider updating your Node.JS version or checking for any issues with the 'ocsp' module.");
5166       if (disableMods) serverconsole.locwarnmessage("SVR.JS is running without mods and server-side JavaScript enabled. Web applications may not work as expected");
5167       console.log();
5169       // Display mod and server-side JavaScript errors
5170       if (process.isPrimary || process.isPrimary === undefined) {
5171         modLoadingErrors.forEach(function (modLoadingError) {
5172           serverconsole.locwarnmessage("There was a problem while loading a \"" + String(modLoadingError.modName).replace(/[\r\n]/g, "") + "\" mod.");
5173           serverconsole.locwarnmessage("Stack:");
5174           serverconsole.locwarnmessage(generateErrorStack(modLoadingError.error));
5175         });
5176         if (SSJSError) {
5177           serverconsole.locwarnmessage("There was a problem while loading server-side JavaScript.");
5178           serverconsole.locwarnmessage("Stack:");
5179           serverconsole.locwarnmessage(generateErrorStack(SSJSError));
5180         }
5181         if (SSJSError || modLoadingErrors.length > 0) console.log();
5182       }
5184       // Print server information
5185       serverconsole.locmessage("Server version: " + version);
5186       if (process.isBun) serverconsole.locmessage("Bun version: v" + process.versions.bun);
5187       else serverconsole.locmessage("Node.JS version: " + process.version);
5188       var CPUs = os.cpus();
5189       if (CPUs.length > 0) serverconsole.locmessage("CPU: " + (CPUs.length > 1 ? CPUs.length + "x " : "") + CPUs[0].model);
5191       // Throw errors
5192       if (vnum < 64) throw new Error("SVR.JS requires Node.JS 10.0.0 and newer, but your Node.JS version isn't supported by SVR.JS.");
5193       if (configJSONRErr) throw new Error("Can't read SVR.JS configuration file: " + configJSONRErr.message);
5194       if (configJSONPErr) throw new Error("SVR.JS configuration parse error: " + configJSONPErr.message);
5195       if (configJSON.enableHTTP2 && !secure && (typeof port != "number")) throw new Error("HTTP/2 without HTTPS, along with Unix sockets/Windows named pipes aren't supported by SVR.JS.");
5196       if (configJSON.enableHTTP2 && http2.__disabled__ !== undefined) throw new Error("HTTP/2 isn't supported by your Node.JS version! You may not be able to use HTTP/2 with SVR.JS");
5197       if (listenAddress) {
5198         if (listenAddress.match(/^[0-9]+$/)) throw new Error("Listening network address can't be numeric (it need to be either valid IP address, or valid domain name).");
5199         if (listenAddress.match(/^(?:2(?:2[4-9]|3[0-9])\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$|ff[0-9a-f][0-9a-f]:[0-9a-f:])/i)) throw new Error("SVR.JS can't listen on multicast address.");
5200         if (brdIPs.indexOf(listenAddress) > -1) throw new Error("SVR.JS can't listen on broadcast address.");
5201         if (netIPs.indexOf(listenAddress) > -1) throw new Error("SVR.JS can't listen on subnet address.");
5202       }
5203       if (certificateError) throw new Error("There was a problem with SSL certificate/private key: " + certificateError.message);
5204       if (wwwrootError) throw new Error("There was a problem with your web root: " + wwwrootError.message);
5205       if (sniReDos) throw new Error("Refusing to start, because the current SNI configuration would make the server vulnerable to ReDoS.");
5206     }
5208     // Print server startup information
5209     if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("Starting HTTP server at " + (typeof port == "number" ? (listenAddress ? ((listenAddress.indexOf(":") > -1 ? "[" + listenAddress + "]" : listenAddress)) + ":" : "port ") : "") + port.toString() + "...");
5210     if (secure) serverconsole.locmessage("Starting HTTPS server at " + (typeof sport == "number" ? (sListenAddress ? ((sListenAddress.indexOf(":") > -1 ? "[" + sListenAddress + "]" : sListenAddress)) + ":" : "port ") : "") + sport.toString() + "...");
5211   }
5214   if (!cluster.isPrimary) {
5215     try {
5216       if (typeof (secure ? sport : port) == "number" && (secure ? sListenAddress : listenAddress)) {
5217         server.listen(secure ? sport : port, secure ? sListenAddress : listenAddress);
5218       } else {
5219         server.listen(secure ? sport : port);
5220       }
5221     } catch (err) {
5222       if (err.code != "ERR_SERVER_ALREADY_LISTEN") throw err;
5223     }
5224     if (secure && !disableNonEncryptedServer) {
5225       try {
5226         if (typeof port == "number" && listenAddress) {
5227           server2.listen(port, listenAddress);
5228         } else {
5229           server2.listen(port);
5230         }
5231       } catch (err) {
5232         if (err.code != "ERR_SERVER_ALREADY_LISTEN") throw err;
5233       }
5234     }
5235   }
5237   // SVR.JS commmands
5238   var commands = {
5239     close: function () {
5240       try {
5241         server.close();
5242         if (secure && !disableNonEncryptedServer) {
5243           server2.close();
5244         }
5245         if (cluster.isPrimary === undefined) serverconsole.climessage("Server closed.");
5246         else {
5247           process.send("Server closed.");
5248           process.send("\x12CLOSE");
5249         }
5250       } catch (err) {
5251         if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot close server! Reason: " + err.message);
5252         else process.send("Cannot close server! Reason: " + err.message);
5253       }
5254     },
5255     open: function () {
5256       try {
5257         if (typeof (secure ? sport : port) == "number" && (secure ? sListenAddress : listenAddress)) {
5258           server.listen(secure ? sport : port, secure ? sListenAddress : listenAddress);
5259         } else {
5260           server.listen(secure ? sport : port);
5261         }
5262         if (secure && !disableNonEncryptedServer) {
5263           if (typeof port == "number" && listenAddress) {
5264             server2.listen(port, listenAddress);
5265           } else {
5266             server2.listen(port);
5267           }
5268         }
5269         if (cluster.isPrimary === undefined) serverconsole.climessage("Server opened.");
5270         else {
5271           process.send("Server opened.");
5272         }
5273       } catch (err) {
5274         if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot open server! Reason: " + err.message);
5275         else process.send("Cannot open server! Reason: " + err.message);
5276       }
5277     },
5278     help: function () {
5279       if (cluster.isPrimary === undefined) serverconsole.climessage("Server commands:\n" + Object.keys(commands).join(" "));
5280       else process.send("Server commands:\n" + Object.keys(commands).join(" "));
5281     },
5282     mods: function () {
5283       if (cluster.isPrimary === undefined) serverconsole.climessage("Mods:");
5284       else process.send("Mods:");
5285       for (var i = 0; i < modInfos.length; i++) {
5286         if (cluster.isPrimary === undefined) serverconsole.climessage((i + 1).toString() + ". " + modInfos[i].name + " " + modInfos[i].version);
5287         else process.send((i + 1).toString() + ". " + modInfos[i].name + " " + modInfos[i].version);
5288       }
5289       if (modInfos.length == 0) {
5290         if (cluster.isPrimary === undefined) serverconsole.climessage("No mods installed.");
5291         else process.send("No mods installed.");
5292       }
5293     },
5294     stop: function (retcode) {
5295       reallyExiting = true;
5296       clearInterval(passwordHashCacheIntervalId);
5297       if ((!cluster.isPrimary && cluster.isPrimary !== undefined) && server.listening) {
5298         try {
5299           server.close(function () {
5300             if (server2.listening) {
5301               try {
5302                 server2.close(function () {
5303                   if (!process.removeFakeIPC) {
5304                     if (typeof retcode == "number") {
5305                       process.exit(retcode);
5306                     } else {
5307                       process.exit(0);
5308                     }
5309                   }
5310                 });
5311               } catch (err) {
5312                 if (!process.removeFakeIPC) {
5313                   if (typeof retcode == "number") {
5314                     process.exit(retcode);
5315                   } else {
5316                     process.exit(0);
5317                   }
5318                 }
5319               }
5320             } else {
5321               if (!process.removeFakeIPC) {
5322                 if (typeof retcode == "number") {
5323                   process.exit(retcode);
5324                 } else {
5325                   process.exit(0);
5326                 }
5327               }
5328             }
5329           });
5330         } catch (err) {
5331           if (typeof retcode == "number") {
5332             process.exit(retcode);
5333           } else {
5334             process.exit(0);
5335           }
5336         }
5337         if (process.removeFakeIPC) process.removeFakeIPC();
5338       } else {
5339         if (typeof retcode == "number") {
5340           process.exit(retcode);
5341         } else {
5342           process.exit(0);
5343         }
5344       }
5345     },
5346     clear: function () {
5347       console.clear();
5348     },
5349     block: function (ip) {
5350       if (ip == undefined || JSON.stringify(ip) == "[]") {
5351         if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot block non-existent IP.");
5352         else if (!cluster.isPrimary) process.send("Cannot block non-existent IP.");
5353       } else {
5354         for (var i = 0; i < ip.length; i++) {
5355           if (ip[i] != "localhost" && ip[i].indexOf(":") == -1) {
5356             ip[i] = "::ffff:" + ip[i];
5357           }
5358           if (!blocklist.check(ip[i])) {
5359             blocklist.add(ip[i]);
5360           }
5361         }
5362         if (cluster.isPrimary === undefined) serverconsole.climessage("IPs successfully blocked.");
5363         else if (!cluster.isPrimary) process.send("IPs successfully blocked.");
5364       }
5365     },
5366     unblock: function (ip) {
5367       if (ip == undefined || JSON.stringify(ip) == "[]") {
5368         if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot unblock non-existent IP.");
5369         else if (!cluster.isPrimary) process.send("Cannot unblock non-existent IP.");
5370       } else {
5371         for (var i = 0; i < ip.length; i++) {
5372           if (ip[i].indexOf(":") == -1) {
5373             ip[i] = "::ffff:" + ip[i];
5374           }
5375           blocklist.remove(ip[i]);
5376         }
5377         if (cluster.isPrimary === undefined) serverconsole.climessage("IPs successfully unblocked.");
5378         else if (!cluster.isPrimary) process.send("IPs successfully unblocked.");
5379       }
5380     },
5381     restart: function () {
5382       if (cluster.isPrimary === undefined) serverconsole.climessage("This command is not supported on single-threaded SVR.JS.");
5383       else process.send("This command need to be run in SVR.JS master.");
5384     }
5385   };
5388   if (init) {
5389     if (cluster.isPrimary === undefined) {
5390       setInterval(function () {
5391         try {
5392           saveConfig();
5393           serverconsole.locmessage("Configuration saved.");
5394         } catch (err) {
5395           throw new Error(err);
5396         }
5397       }, 300000);
5398     } else if (cluster.isPrimary) {
5399       setInterval(function () {
5400         var allClusters = Object.keys(cluster.workers);
5401         var goodWorkers = [];
5403         function checkWorker(callback, _id) {
5404           if (typeof _id === "undefined") _id = 0;
5405           if (_id >= allClusters.length) {
5406             callback();
5407             return;
5408           }
5409           try {
5410             if (cluster.workers[allClusters[_id]]) {
5411               isWorkerHungUpBuff2 = true;
5412               cluster.workers[allClusters[_id]].on("message", msgListener);
5413               cluster.workers[allClusters[_id]].send("\x14PINGPING");
5414               setTimeout(function () {
5415                 if (isWorkerHungUpBuff2) {
5416                   checkWorker(callback, _id + 1);
5417                 } else {
5418                   goodWorkers.push(allClusters[_id]);
5419                   checkWorker(callback, _id + 1);
5420                 }
5421               }, 250);
5422             } else {
5423               checkWorker(callback, _id + 1);
5424             }
5425           } catch (err) {
5426             if (cluster.workers[allClusters[_id]]) {
5427               cluster.workers[allClusters[_id]].removeAllListeners("message");
5428               cluster.workers[allClusters[_id]].on("message", bruteForceListenerWrapper(cluster.workers[allClusters[_id]]));
5429               cluster.workers[allClusters[_id]].on("message", listenConnListener);
5430             }
5431             checkWorker(callback, _id + 1);
5432           }
5433         }
5434         checkWorker(function () {
5435           var wN = Math.floor(Math.random() * goodWorkers.length); //Send a configuration saving message to a random worker.
5436           try {
5437             if (cluster.workers[goodWorkers[wN]]) {
5438               isWorkerHungUpBuff2 = true;
5439               cluster.workers[goodWorkers[wN]].on("message", msgListener);
5440               cluster.workers[goodWorkers[wN]].send("\x14SAVECONF");
5441             }
5442           } catch (err) {
5443             if (cluster.workers[goodWorkers[wN]]) {
5444               cluster.workers[goodWorkers[wN]].removeAllListeners("message");
5445               cluster.workers[goodWorkers[wN]].on("message", bruteForceListenerWrapper(cluster.workers[goodWorkers[wN]]));
5446               cluster.workers[goodWorkers[wN]].on("message", listenConnListener);
5447             }
5448             serverconsole.locwarnmessage("There was a problem while saving configuration file. Reason: " + err.message);
5449           }
5450         });
5451       }, 300000);
5452     }
5453     if (!cluster.isPrimary) {
5454       passwordHashCacheIntervalId = setInterval(function () {
5455         pbkdf2Cache = pbkdf2Cache.filter(function (entry) {
5456           return entry.addDate > (new Date() - 3600000);
5457         });
5458         scryptCache = scryptCache.filter(function (entry) {
5459           return entry.addDate > (new Date() - 3600000);
5460         });
5461       }, 1800000);
5462     }
5463     if (!cluster.isPrimary && cluster.isPrimary !== undefined) {
5464       process.on("message", function (line) {
5465         try {
5466           if (line == "") {
5467             // Does Nothing
5468             process.send("\x12END");
5469           } else if (line == "\x14SAVECONF") {
5470             // Save configuration file
5471             try {
5472               saveConfig();
5473               process.send("\x12SAVEGOOD");
5474             } catch (err) {
5475               process.send("\x12SAVEERR" + err.message);
5476             }
5477             process.send("\x12END");
5478           } else if (line == "\x14KILLPING") {
5479             if (!reallyExiting) {
5480               process.send("\x12KILLOK");
5481               process.send("\x12END");
5482             }
5483             // Refuse to send, when it's really exiting. Main process will treat the worker as hung up anyway...
5484           } else if (line == "\x14PINGPING") {
5485             if (!reallyExiting) {
5486               process.send("\x12PINGOK");
5487               process.send("\x12END");
5488             }
5489             // Refuse to send, when it's really exiting. Main process will treat the worker as hung up anyway...
5490           } else if (line == "\x14KILLREQ") {
5491             if (reqcounter - reqcounterKillReq < 2) {
5492               process.send("\x12KILLTERMMSG");
5493               process.nextTick(commands.stop);
5494             } else {
5495               reqcounterKillReq = reqcounter;
5496             }
5497           } else if (commands[line.split(" ")[0]] !== undefined && commands[line.split(" ")[0]] !== null) {
5498             var argss = line.split(" ");
5499             var command = argss.shift();
5500             commands[command](argss);
5501             process.send("\x12END");
5502           } else {
5503             process.send("Unrecognized command \"" + line.split(" ")[0] + "\".");
5504             process.send("\x12END");
5505           }
5506         } catch (err) {
5507           if (line != "") {
5508             process.send("Can't execute command \"" + line.split(" ")[0] + "\".");
5509             process.send("\x12END");
5510           }
5511         }
5512       });
5513     } else {
5514       var rla = readline.createInterface({
5515         input: process.stdin,
5516         output: process.stdout,
5517         prompt: ""
5518       });
5519       rla.prompt();
5520       rla.on("line", function (line) {
5521         line = line.trim();
5522         var argss = line.split(" ");
5523         var command = argss.shift();
5524         if (line != "") {
5525           if (cluster.isPrimary !== undefined) {
5526             var allClusters = Object.keys(cluster.workers);
5527             if (command == "block") commands.block(argss);
5528             if (command == "unblock") commands.unblock(argss);
5529             if (command == "restart") {
5530               var stopError = false;
5531               exiting = true;
5532               for (var i = 0; i < allClusters.length; i++) {
5533                 try {
5534                   if (cluster.workers[allClusters[i]]) {
5535                     cluster.workers[allClusters[i]].kill();
5536                   }
5537                 } catch (err) {
5538                   stopError = true;
5539                 }
5540               }
5541               if (stopError) serverconsole.climessage("Some SVR.JS workers might not be stopped.");
5542               SVRJSInitialized = false;
5543               closedMaster = true;
5544               var cpus = os.availableParallelism ? os.availableParallelism() : os.cpus().length;
5545               if (cpus > 16) cpus = 16;
5546               try {
5547                 var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; // 1 core deleted for safety...
5548                 if (cpus > useAvailableCores) cpus = useAvailableCores;
5549               } catch (err) {
5550                 // Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working.
5551               }
5552               if (cpus < 1) cpus = 1; // If SVR.JS is running on Haiku or if useAvailableCores = 0
5553               for (var i = 0; i < cpus; i++) {
5554                 if (i == 0) {
5555                   SVRJSFork();
5556                 } else {
5557                   setTimeout((function (i) {
5558                     return function () {
5559                       SVRJSFork();
5560                       if (i >= cpus - 1) {
5561                         SVRJSInitialized = true;
5562                         exiting = false;
5563                         serverconsole.climessage("SVR.JS workers restarted.");
5564                       }
5565                     };
5566                   })(i), i * 6.6);
5567                 }
5568               }
5569               return;
5570             }
5571             if (command == "stop") {
5572               exiting = true;
5573               allClusters = Object.keys(cluster.workers);
5574             }
5575             allClusters.forEach(function (clusterID) {
5576               try {
5577                 if (cluster.workers[clusterID]) {
5578                   cluster.workers[clusterID].on("message", msgListener);
5579                   cluster.workers[clusterID].send(line);
5580                 }
5581               } catch (err) {
5582                 if (cluster.workers[clusterID]) {
5583                   cluster.workers[clusterID].removeAllListeners("message");
5584                   cluster.workers[clusterID].on("message", bruteForceListenerWrapper(cluster.workers[clusterID]));
5585                   cluster.workers[clusterID].on("message", listenConnListener);
5586                 }
5587                 serverconsole.climessage("Can't run command \"" + command + "\".");
5588               }
5589             });
5590             if (command == "stop") {
5591               setTimeout(function () {
5592                 reallyExiting = true;
5593                 process.exit(0);
5594               }, 50);
5595             }
5596           } else {
5597             if (command == "stop") {
5598               reallyExiting = true;
5599               process.exit(0);
5600             }
5601             try {
5602               commands[command](argss);
5603             } catch (err) {
5604               serverconsole.climessage("Unrecognized command \"" + command + "\".");
5605             }
5606           }
5607         }
5608         rla.prompt();
5609       });
5610     }
5612     if (cluster.isPrimary || cluster.isPrimary === undefined) {
5613       // Cluster forking code
5614       if (cluster.isPrimary !== undefined && init) {
5615         var cpus = os.availableParallelism ? os.availableParallelism() : os.cpus().length;
5616         if (cpus > 16) cpus = 16;
5617         try {
5618           var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; // 1 core deleted for safety...
5619           if (cpus > useAvailableCores) cpus = useAvailableCores;
5620         } catch (err) {
5621           // Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working.
5622         }
5623         if (cpus < 1) cpus = 1; // If SVR.JS is run on Haiku (os.cpus in Haiku returns empty array) or if useAvailableCores = 0
5624         for (var i = 0; i < cpus; i++) {
5625           if (i == 0) {
5626             SVRJSFork();
5627           } else {
5628             setTimeout((function (i) {
5629               return function () {
5630                 SVRJSFork();
5631                 if (i >= cpus - 1) SVRJSInitialized = true;
5632               };
5633             })(i), i * 6.6);
5634           }
5635         }
5636         cluster.workers[Object.keys(cluster.workers)[0]].on("message", function (msg) {
5637           if (msg.length >= 8 && msg.indexOf("\x12ERRLIST") == 0) {
5638             var tries = parseInt(msg.substring(8, 9));
5639             var errCode = msg.substring(9);
5640             serverconsole.locerrmessage(serverErrorDescs[errCode] ? serverErrorDescs[errCode] : serverErrorDescs["UNKNOWN"]);
5641             serverconsole.locmessage(tries + " attempts left.");
5642           }
5643           if (msg.length >= 9 && msg.indexOf("\x12ERRCRASH") == 0) {
5644             var errno = errors[msg.substring(9)];
5645             process.exit(errno ? errno : 1);
5646           }
5647         });
5649         // Hangup check and restart
5650         setInterval(function () {
5651           if (!closedMaster && !exiting) {
5652             var chksocket = {};
5653             if (secure && disableNonEncryptedServer) {
5654               chksocket = https.get({
5655                 hostname: (typeof sport == "number" && sListenAddress) ? sListenAddress : "localhost",
5656                 port: (typeof sport == "number") ? sport : undefined,
5657                 socketPath: (typeof sport == "number") ? undefined : sport,
5658                 headers: {
5659                   "X-SVR-JS-From-Main-Thread": "true",
5660                   "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
5661                 },
5662                 timeout: 1620,
5663                 rejectUnauthorized: false
5664               }, function (res) {
5665                 chksocket.removeAllListeners("timeout");
5666                 res.destroy();
5667                 res.on("data", function () {});
5668                 res.on("end", function () {});
5669                 crashed = false;
5670               }).on("error", function () {
5671                 if (!exiting) {
5672                   if (!crashed) SVRJSFork();
5673                   else crashed = false;
5674                 }
5675               }).on("timeout", function () {
5676                 if (!exiting) SVRJSFork();
5677                 crashed = true;
5678               });
5679             } else if ((configJSON.enableHTTP2 == undefined ? false : configJSON.enableHTTP2) && !secure) {
5680               // It doesn't support through Unix sockets or Windows named pipes
5681               var address = ((typeof port == "number" && listenAddress) ? listenAddress : "localhost").replace(/\/@/g, "");
5682               if (address.indexOf(":") > -1) {
5683                 address = "[" + address + "]";
5684               }
5685               var connection = http2.connect("http://" + address + ":" + port.toString());
5686               connection.on("error", function () {
5687                 if (!exiting) {
5688                   if (!crashed) SVRJSFork();
5689                   else crashed = false;
5690                 }
5691               });
5692               connection.setTimeout(1620, function () {
5693                 if (!exiting) SVRJSFork();
5694                 crashed = true;
5695               });
5696               chksocket = connection.request({
5697                 ":path": "/",
5698                 "x-svr-js-from-main-thread": "true",
5699                 "user-agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
5700               });
5701               chksocket.on("response", function () {
5702                 connection.close();
5703                 crashed = false;
5704               });
5705               chksocket.on("error", function () {
5706                 if (!exiting) {
5707                   if (!crashed) SVRJSFork();
5708                   else crashed = false;
5709                 }
5710               });
5711             } else {
5712               chksocket = http.get({
5713                 hostname: (typeof port == "number" && listenAddress) ? listenAddress : "localhost",
5714                 port: (typeof port == "number") ? port : undefined,
5715                 socketPath: (typeof port == "number") ? undefined : port,
5716                 headers: {
5717                   "X-SVR-JS-From-Main-Thread": "true",
5718                   "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
5719                 },
5720                 timeout: 1620
5721               }, function (res) {
5722                 chksocket.removeAllListeners("timeout");
5723                 res.destroy();
5724                 res.on("data", function () {});
5725                 res.on("end", function () {});
5726                 crashed = false;
5727               }).on("error", function () {
5728                 if (!exiting) {
5729                   if (!crashed) SVRJSFork();
5730                   else crashed = false;
5731                 }
5732               }).on("timeout", function () {
5733                 if (!exiting) SVRJSFork();
5734                 crashed = true;
5735               });
5736             }
5737           }
5738         }, 4550);
5740         // Termination of unused good workers
5741         if (!disableUnusedWorkerTermination && cluster.isPrimary !== undefined) {
5742           setTimeout(function () {
5743             setInterval(function () {
5744               if (!closedMaster && !exiting) {
5745                 var allClusters = Object.keys(cluster.workers);
5746                 var minClusters = 0;
5747                 minClusters = Math.ceil(cpus * 0.625);
5748                 if (minClusters < 2) minClusters = 2;
5749                 var goodWorkers = [];
5751                 function checkWorker(callback, _id) {
5752                   if (typeof _id === "undefined") _id = 0;
5753                   if (_id >= allClusters.length) {
5754                     callback();
5755                     return;
5756                   }
5757                   try {
5758                     if (cluster.workers[allClusters[_id]]) {
5759                       isWorkerHungUpBuff = true;
5760                       cluster.workers[allClusters[_id]].on("message", msgListener);
5761                       cluster.workers[allClusters[_id]].send("\x14KILLPING");
5762                       setTimeout(function () {
5763                         if (isWorkerHungUpBuff) {
5764                           checkWorker(callback, _id + 1);
5765                         } else {
5766                           goodWorkers.push(allClusters[_id]);
5767                           checkWorker(callback, _id + 1);
5768                         }
5769                       }, 250);
5770                     } else {
5771                       checkWorker(callback, _id + 1);
5772                     }
5773                   } catch (err) {
5774                     if (cluster.workers[allClusters[_id]]) {
5775                       cluster.workers[allClusters[_id]].removeAllListeners("message");
5776                       cluster.workers[allClusters[_id]].on("message", bruteForceListenerWrapper(cluster.workers[allClusters[_id]]));
5777                       cluster.workers[allClusters[_id]].on("message", listenConnListener);
5778                     }
5779                     checkWorker(callback, _id + 1);
5780                   }
5781                 }
5782                 checkWorker(function () {
5783                   if (goodWorkers.length > minClusters) {
5784                     var wN = Math.floor(Math.random() * goodWorkers.length);
5785                     if (wN == goodWorkers.length) return;
5786                     try {
5787                       if (cluster.workers[goodWorkers[wN]]) {
5788                         isWorkerHungUpBuff = true;
5789                         cluster.workers[goodWorkers[wN]].on("message", msgListener);
5790                         cluster.workers[goodWorkers[wN]].send("\x14KILLREQ");
5791                       }
5792                     } catch (err) {
5793                       if (cluster.workers[goodWorkers[wN]]) {
5794                         cluster.workers[goodWorkers[wN]].removeAllListeners("message");
5795                         cluster.workers[goodWorkers[wN]].on("message", bruteForceListenerWrapper(cluster.workers[goodWorkers[wN]]));
5796                         cluster.workers[goodWorkers[wN]].on("message", listenConnListener);
5797                       }
5798                       serverconsole.locwarnmessage("There was a problem while terminating unused worker process. Reason: " + err.message);
5799                     }
5800                   }
5801                 });
5802               }
5803             }, 300000);
5804           }, 2000);
5805         }
5806       }
5807     }
5808   }
5811 // Save configuration file
5812 function saveConfig() {
5813   for (var i = 0; i < 3; i++) {
5814     try {
5815       var configJSONobj = {};
5816       if (fs.existsSync(__dirname + "/config.json")) configJSONobj = JSON.parse(fs.readFileSync(__dirname + "/config.json").toString());
5817       if (configJSONobj.users === undefined) configJSONobj.users = [];
5818       if (secure) {
5819         if (configJSONobj.key === undefined) configJSONobj.key = "cert/key.key";
5820         if (configJSONobj.cert === undefined) configJSONobj.cert = "cert/cert.crt";
5821         if (configJSONobj.sport === undefined) configJSONobj.sport = 443;
5822         if (configJSONobj.spubport === undefined) configJSONobj.spubport = 443;
5823         if (configJSONobj.sni === undefined) configJSONobj.sni = {};
5824         if (configJSONobj.enableOCSPStapling === undefined) configJSONobj.enableOCSPStapling = false;
5825       }
5826       if (configJSONobj.port === undefined) configJSONobj.port = 80;
5827       if (configJSONobj.pubport === undefined) configJSONobj.pubport = 80;
5828       if (configJSONobj.domain === undefined && configJSONobj.domian !== undefined) configJSONobj.domain = configJSONobj.domian;
5829       delete configJSONobj.domian;
5830       if (configJSONobj.page404 === undefined) configJSONobj.page404 = "404.html";
5831       configJSONobj.timestamp = timestamp;
5832       configJSONobj.blacklist = blocklist.raw;
5833       if (configJSONobj.nonStandardCodes === undefined) configJSONobj.nonStandardCodes = [];
5834       if (configJSONobj.enableCompression === undefined) configJSONobj.enableCompression = true;
5835       if (configJSONobj.customHeaders === undefined) configJSONobj.customHeaders = {};
5836       if (configJSONobj.enableHTTP2 === undefined) configJSONobj.enableHTTP2 = false;
5837       if (configJSONobj.enableLogging === undefined) configJSONobj.enableLogging = true;
5838       if (configJSONobj.enableDirectoryListing === undefined) configJSONobj.enableDirectoryListing = true;
5839       if (configJSONobj.enableDirectoryListingWithDefaultHead === undefined) configJSONobj.enableDirectoryListingWithDefaultHead = false;
5840       if (configJSONobj.serverAdministratorEmail === undefined) configJSONobj.serverAdministratorEmail = "[no contact information]";
5841       if (configJSONobj.stackHidden === undefined) configJSONobj.stackHidden = false;
5842       if (configJSONobj.enableRemoteLogBrowsing === undefined) configJSONobj.enableRemoteLogBrowsing = false;
5843       if (configJSONobj.exposeServerVersion === undefined) configJSONobj.exposeServerVersion = true;
5844       if (configJSONobj.disableServerSideScriptExpose === undefined) configJSONobj.disableServerSideScriptExpose = true;
5845       if (configJSONobj.allowStatus === undefined) configJSONobj.allowStatus = true;
5846       if (configJSONobj.rewriteMap === undefined) configJSONobj.rewriteMap = [];
5847       if (configJSONobj.dontCompress === undefined) configJSONobj.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])$/"];
5848       if (configJSONobj.enableIPSpoofing === undefined) configJSONobj.enableIPSpoofing = false;
5849       if (configJSONobj.secure === undefined) configJSONobj.secure = false;
5850       if (configJSONobj.disableNonEncryptedServer === undefined) configJSONobj.disableNonEncryptedServer = false;
5851       if (configJSONobj.disableToHTTPSRedirect === undefined) configJSONobj.disableToHTTPSRedirect = false;
5852       if (configJSONobj.enableETag === undefined) configJSONobj.enableETag = true;
5853       if (configJSONobj.disableUnusedWorkerTermination === undefined) configJSONobj.disableUnusedWorkerTermination = false;
5854       if (configJSONobj.rewriteDirtyURLs === undefined) configJSONobj.rewriteDirtyURLs = false;
5855       if (configJSONobj.errorPages === undefined) configJSONobj.errorPages = [];
5856       if (configJSONobj.useWebRootServerSideScript === undefined) configJSONobj.useWebRootServerSideScript = true;
5857       if (configJSONobj.exposeModsInErrorPages === undefined) configJSONobj.exposeModsInErrorPages = true;
5858       if (configJSONobj.disableTrailingSlashRedirects === undefined) configJSONobj.disableTrailingSlashRedirects = false;
5859       if (configJSONobj.environmentVariables === undefined) configJSONobj.environmentVariables = {};
5860       if (configJSONobj.allowDoubleSlashes === undefined) configJSONobj.allowDoubleSlashes = false;
5862       var configString = JSON.stringify(configJSONobj, null, 2) + "\n";
5863       fs.writeFileSync(__dirname + "/config.json", configString);
5864       break;
5865     } catch (err) {
5866       if (i >= 2) throw err;
5867       var now = Date.now();
5868       while (Date.now() - now < 2);
5869     }
5870   }
5873 // Process event listeners
5874 if (cluster.isPrimary || cluster.isPrimary === undefined) {
5875   process.on("uncaughtException", function (err) {
5876     // CRASH HANDLER
5877     serverconsole.locerrmessage("SVR.JS master process just crashed!!!");
5878     serverconsole.locerrmessage("Stack:");
5879     serverconsole.locerrmessage(generateErrorStack(err));
5880     process.exit(err.errno);
5881   });
5882   process.on("unhandledRejection", function (err) {
5883     // CRASH HANDLER
5884     serverconsole.locerrmessage("SVR.JS master process just crashed!!!");
5885     serverconsole.locerrmessage("Stack:");
5886     serverconsole.locerrmessage(err.stack ? generateErrorStack(err) : String(err));
5887     process.exit(err.errno);
5888   });
5889   process.on("exit", function (code) {
5890     try {
5891       if (!configJSONRErr && !configJSONPErr) {
5892         saveConfig();
5893       }
5894     } catch (err) {
5895       serverconsole.locwarnmessage("There was a problem while saving configuration file. Reason: " + err.message);
5896     }
5897     try {
5898       deleteFolderRecursive(__dirname + "/temp");
5899     } catch (err) {
5900       // Error!
5901     }
5902     try {
5903       fs.mkdirSync(__dirname + "/temp");
5904     } catch (err) {
5905       // Error!
5906     }
5907     if (process.isBun && process.versions.bun && process.versions.bun[0] == "0") {
5908       try {
5909         fs.writeFileSync(__dirname + "/temp/serverSideScript.js", "// Placeholder server-side JavaScript to workaround Bun bug.\r\n");
5910       } catch (err) {
5911         // Error!
5912       }
5913     }
5914     serverconsole.locmessage("Server closed with exit code: " + code);
5915   });
5916   process.on("warning", function (warning) {
5917     serverconsole.locwarnmessage(warning.message);
5918     if (generateErrorStack(warning)) {
5919       serverconsole.locwarnmessage("Stack:");
5920       serverconsole.locwarnmessage(generateErrorStack(warning));
5921     }
5922   });
5923   process.on("SIGINT", function () {
5924     reallyExiting = true;
5925     if (cluster.isPrimary !== undefined) {
5926       exiting = true;
5927       var allClusters = Object.keys(cluster.workers);
5928       for (var i = 0; i < allClusters.length; i++) {
5929         try {
5930           if (cluster.workers[allClusters[i]]) {
5931             cluster.workers[allClusters[i]].send("stop");
5932           }
5933         } catch (err) {
5934           // Worker will crash with EPIPE anyway.
5935         }
5936       }
5937     }
5938     serverconsole.locmessage("Server terminated using SIGINT");
5939     process.exit();
5940   });
5941 } else {
5942   // Crash handler
5943   function crashHandler(err) {
5944     serverconsole.locerrmessage("SVR.JS worker just crashed!!!");
5945     serverconsole.locerrmessage("Stack:");
5946     serverconsole.locerrmessage(err.stack ? generateErrorStack(err) : String(err));
5947     process.exit(err.errno);
5948   }
5950   process.on("uncaughtException", crashHandler);
5951   process.on("unhandledRejection", crashHandler);
5953   // Warning handler
5954   process.on("warning", function (warning) {
5955     serverconsole.locwarnmessage(warning.message);
5956     if (warning.stack) {
5957       serverconsole.locwarnmessage("Stack:");
5958       serverconsole.locwarnmessage(generateErrorStack(warning));
5959     }
5960   });
5963 // Start SVR.JS!
5964 try {
5965   start(true);
5966 } catch (err) {
5967   serverconsole.locerrmessage("There was a problem starting SVR.JS!!!");
5968   serverconsole.locerrmessage("Stack:");
5969   serverconsole.locerrmessage(generateErrorStack(err));
5970   setTimeout(function () {
5971     process.exit(err.errno ? err.errno : 1);
5972   }, 10);