1 // SVR.JS - a web server running on Node.JS
6 * Copyright (c) 2018-2024 SVR.JS
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:
10 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.
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);
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).");
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).");
31 throw new Error("SVR.JS doesn't work on Deno/QuickJS. SVR.JS requires use of Node.JS (or compatible JS runtime).");
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");
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);
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");
90 } else if (args[i] == "--secure") {
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)) + ")");
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!");
104 } else if (args[i] == "--reset") {
106 } else if (args[i] == "--disable-mods") {
108 } else if (args[i] == "--single-threaded") {
109 singlethreaded = true;
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");
125 var readline = require("readline");
126 var net = require("net");
128 if (!singlethreaded) {
130 // Import cluster module
131 var cluster = require("cluster");
133 // Clustering is not supported!
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
147 id: parseInt(process.env.NODE_UNIQUE_ID),
149 isDead: function () {
152 send: function (message, b, c, d) {
153 process.send(message, b, c, d);
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();
171 socket.on("end", function () {
172 process.emit("message", receivedData);
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);
184 process.removeFakeIPC = function () {
186 process.send = function () {};
187 fakeIPCServer.close();
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, {
203 stdio: ["inherit", "inherit", "inherit", "ipc"]
206 newWorker.process = newWorker;
207 newWorker.isDead = function () {
208 return newWorker.exitCode !== null || newWorker.killed;
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")) {
217 sendImplemented = false;
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");
225 oldLog(a, b, c, d, e, f);
230 worker.send(undefined);
232 if (err.message === "NOT IMPLEMENTED") {
233 sendImplemented = false;
238 console.log = oldLog;
241 return sendImplemented;
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();
256 socket.on("end", function () {
257 newWorker.emit("message", receivedData);
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];
268 newWorker.send = function (message, fakeParam2, fakeParam3, fakeParam4, tries) {
269 if (!tries) tries = 0;
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);
277 if (tries > 50) throw err;
278 newWorker.send(message, fakeParam2, fakeParam3, fakeParam4, tries + 1);
282 newWorker.on("exit", function () {
283 delete cluster.workers[newWorker.id];
287 cluster.workers[newWorker.id] = newWorker;
288 cluster._workersCounter++;
293 if (process.isBun && (cluster.isMaster === undefined || (cluster.isMaster && process.env.NODE_UNIQUE_ID))) {
297 // Shim cluster.isPrimary field
298 if (cluster.isPrimary === undefined && cluster.isMaster !== undefined) cluster.isPrimary = cluster.isMaster;
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;
320 var reallyExiting = false;
322 var threadLimitWarned = false;
324 // SVR.JS worker forking function
325 function SVRJSFork() {
327 if (SVRJSInitialized) serverconsole.locmessage("Starting next thread, because previous one hung up/crashed...");
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.");
335 if (!(cluster.__shimmed__ && process.isBun && process.versions.bun && process.versions.bun[0] != "0" && Object.keys(cluster.workers) > 0)) {
336 newWorker = cluster.fork();
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.");
341 if (err.name == "NotImplementedError") {
342 // If cluster.fork throws a NotImplementedError, shim cluster module
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.");
348 if (!(cluster.__shimmed__ && process.isBun && process.versions.bun && process.versions.bun[0] != "0" && Object.keys(cluster.workers) > 0)) {
349 newWorker = cluster.fork();
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.");
358 // Add event listeners
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);
363 newWorker.on("exit", function () {
364 if (!exiting && Object.keys(cluster.workers).length == 0) {
369 newWorker.on("message", bruteForceListenerWrapper(newWorker));
370 newWorker.on("message", listenConnListener);
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");
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!
384 // Don't use graceful-fs
386 var path = require("path");
387 var hexstrbase64 = undefined;
389 hexstrbase64 = require("./hexstrbase64/index.js");
391 // Don't use hexstrbase64
393 var inspector = undefined;
395 inspector = require("inspector");
397 // Don't use inspector
399 var zlib = require("zlib");
402 tar = require("tar");
408 var formidable = undefined;
410 formidable = require("formidable");
416 var ocsp = undefined;
417 var ocspCache = undefined;
419 ocsp = require("ocsp");
420 ocspCache = new ocsp.Cache();
428 http2 = require("http2");
431 http2.Http2ServerRequest();
433 if (err.name == "NotImplementedError" || err.code == "ERR_NOT_IMPLEMENTED") throw err;
437 http2.__disabled__ = null;
438 http2.createServer = function () {
439 throw new Error("HTTP/2 support is not present");
441 http2.createSecureServer = function () {
442 throw new Error("HTTP/2 support is not present");
444 http2.connect = function () {
445 throw new Error("HTTP/2 support is not present");
447 http2.get = function () {
448 throw new Error("HTTP/2 support is not present");
454 crypto = require("crypto");
455 https = require("https");
461 createServer: function () {
462 throw new Error("Crypto support is not present");
464 connect: function () {
465 throw new Error("Crypto support is not present");
468 throw new Error("Crypto support is not present");
471 http2.createSecureServer = function () {
472 throw new Error("Crypto support is not present");
475 var mime = require("mime-types");
477 var listenAddress = undefined;
478 var sListenAddress = undefined;
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();
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" : "");
506 var osType = os.type();
507 var platform = os.platform();
508 if (platform == "android") {
510 } else if (osType == "Windows_NT" || osType == "WindowsNT") {
511 var arch = os.arch();
512 if (arch == "ia32") {
514 } else if (arch == "x64") {
517 return "Win" + arch.toUpperCase();
519 } else if (osType.indexOf("CYGWIN") == 0) {
521 } else if (osType.indexOf("MINGW") == 0) {
523 } else if (osType.indexOf("MSYS") == 0) {
525 } else if (osType.indexOf("UWIN") == 0) {
527 } else if (osType == "GNU") {
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, "");
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;
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 ? ":" : "");
569 address = address.replace(extractIpv4, ipv4);
572 if (address.indexOf("::") == -1) {
573 fullAddress = address;
575 var sides = address.split("::");
576 var groupsPresent = 0;
577 sides.forEach(function (side) {
578 groupsPresent += side.split(":").length;
580 fullAddress += sides[0] + ":";
581 if (validGroupCount - groupsPresent > 1) {
582 fullAddress += "0000:".repeat(validGroupCount - groupsPresent);
584 fullAddress += sides[1];
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];
591 expandedAddress += (i != validGroupCount - 1) ? groups[i] + ":" : groups[i];
593 return expandedAddress;
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);
603 IP1 = normalizeIPv4Address(IP1);
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);
612 IP2 = normalizeIPv4Address(IP2);
615 // Check if processed IPs are equal
616 if (IP1 == IP2) return true;
620 function checkForEnabledDirectoryListing(hostname, localAddress) {
621 function matchHostname(hostnameM) {
622 if (typeof hostnameM == "undefined" || hostnameM == "*") {
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)) {
629 } else if (hostname && hostname == hostnameM) {
635 var main = (configJSON.enableDirectoryListing || configJSON.enableDirectoryListing === undefined);
636 if (!configJSON.enableDirectoryListingVHost) return main;
638 configJSON.enableDirectoryListingVHost.every(function (vhost) {
639 if (matchHostname(vhost.host) && ipMatch(vhost.ip, localAddress)) {
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 = [];
657 rawtoPreparedMap: [],
662 // Function to normalize IPv4 address (remove leading zeros)
663 function normalizeIPv4Address(address) {
664 return address.replace(/(^|\.)(?:0(?!\.|$))+/g, "");
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;
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 ? ":" : "");
683 address = address.replace(extractIpv4, ipv4);
686 if (address.indexOf("::") == -1) {
687 fullAddress = address;
689 var sides = address.split("::");
690 var groupsPresent = 0;
691 sides.forEach(function (side) {
692 groupsPresent += side.split(":").length;
694 fullAddress += sides[0] + ":";
695 if (validGroupCount - groupsPresent > 1) {
696 fullAddress += "0000:".repeat(validGroupCount - groupsPresent);
698 fullAddress += sides[1];
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];
705 expandedAddress += (i != validGroupCount - 1) ? groups[i] + ":" : groups[i];
707 return expandedAddress;
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]);
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;
728 // Convert IPv6 address to an array of blocks
729 function ipv6ToBlocks(ip) {
730 var ips = ip.split(":");
732 ips.forEach(function (ipe) {
733 ip2s.push(parseInt(ipe));
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;
745 for (var i = 0; i < 8; i++) {
746 ipBlockMin.push((i < 8 - fieldsToDelete) ? ((i < 7 - fieldsToDelete) ? ipBlocks[i] : (ipBlocks[i] >> fieldMaskModify << fieldMaskModify)) : 0);
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);
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;
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;
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;
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("/");
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) {
795 rawValue = expandIPv6Address(rawValue);
797 rawValue = normalizeIPv4Address(rawValue);
800 // Add the IP or CIDR block to the appropriate list
804 cidrLimits = getIPv6CIDRLimits(rawValue, cidrMask);
805 cidrLimits.v6 = true;
807 cidrLimits = getIPv4CIDRLimits(rawValue, cidrMask);
808 cidrLimits.v6 = false;
810 instance.cidrs.push(cidrLimits);
811 instance.rawtoPreparedMap.push({
816 instance.prepared.push(rawValue);
817 instance.rawtoPreparedMap.push({
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);
832 instance.cidrs.splice(map.index, 1);
834 instance.prepared.splice(map.index, 1);
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;
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) {
850 rawValue = expandIPv6Address(rawValue);
852 rawValue = normalizeIPv4Address(rawValue);
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);
868 // Add initial raw block list values to the instance
869 rawBlockList.forEach(function (rbe) {
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);
885 return errorObject.stack;
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("@");
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 + ")"))));
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)
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();
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)
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();
943 var inspectorURL = undefined;
946 inspectorURL = inspector.url();
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
964 ifaces = os.networkInterfaces();
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) {
974 ifaces[ifname].forEach(function (iface) {
975 if (iface.family !== "IPv4" || iface.internal !== false) {
979 ips.push(ifname + ":" + alias, iface.address);
981 ips.push(ifname, iface.address);
983 brdIPs.push(calculateBroadcastIPv4FromCidr(iface.cidr));
984 netIPs.push(calculateNetworkIPv4FromCidr(iface.cidr));
989 if (ips.length == 0) {
990 Object.keys(ifaces).forEach(function (ifname) {
992 ifaces[ifname].forEach(function (iface) {
993 if (iface.family !== "IPv6" || iface.internal !== false) {
997 ips.push(ifname + ":" + alias, iface.address);
999 ips.push(ifname, iface.address);
1006 // Server startup attempt counter
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),
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")
1031 ipRequest.removeAllListeners("timeout");
1032 res.on("data", function (d) {
1033 if (res.statusCode != 200) {
1034 ipRequestCompleted = true;
1035 process.emit("ipRequestCompleted");
1038 pubip = d.toString();
1040 ipRequestCompleted = true;
1041 process.emit("ipRequestCompleted");
1043 var callbackDone = false;
1045 var dnsTimeout = setTimeout(function () {
1046 callbackDone = true;
1047 ipRequestCompleted = true;
1048 process.emit("ipRequestCompleted");
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");
1060 clearTimeout(dnsTimeout);
1061 callbackDone = true;
1062 ipRequestCompleted = true;
1063 process.emit("ipRequestCompleted");
1068 ipRequest.on("error", function () {
1069 if (crypto.__disabled__ || ipRequestGotError) {
1070 ipRequestCompleted = true;
1071 process.emit("ipRequestCompleted");
1073 ipRequestGotError = true;
1076 ipRequest.on("timeout", function () {
1077 if (crypto.__disabled__ || ipRequestGotError) {
1078 ipRequestCompleted = true;
1079 process.emit("ipRequestCompleted");
1081 ipRequestGotError = true;
1085 if (!crypto.__disabled) {
1086 var ipRequest2 = https.get({
1087 host: "api.seeip.org",
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")
1095 ipRequest2.removeAllListeners("timeout");
1096 res.on("data", function (d) {
1097 if (res.statusCode != 200) {
1098 ipRequestCompleted = true;
1099 process.emit("ipRequestCompleted");
1102 pubip = d.toString();
1104 ipRequestCompleted = true;
1105 process.emit("ipRequestCompleted");
1107 var callbackDone = false;
1109 var dnsTimeout = setTimeout(function () {
1110 callbackDone = true;
1111 ipRequestCompleted = true;
1112 process.emit("ipRequestCompleted");
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");
1124 clearTimeout(dnsTimeout);
1125 callbackDone = true;
1126 ipRequestCompleted = true;
1127 process.emit("ipRequestCompleted");
1132 ipRequest2.on("error", function () {
1133 if (crypto.__disabled__ || ipRequestGotError) {
1134 ipRequestCompleted = true;
1135 process.emit("ipRequestCompleted");
1137 ipRequestGotError = true;
1140 ipRequest2.on("timeout", function () {
1141 if (crypto.__disabled__ || ipRequestGotError) {
1142 ipRequestCompleted = true;
1143 process.emit("ipRequestCompleted");
1145 ipRequestGotError = true;
1150 ipRequestCompleted = true;
1153 function ipStatusCallback(callback) {
1154 if (ipRequestCompleted) {
1157 process.once("ipRequestCompleted", callback);
1161 var configJSON = {};
1162 var configJSONRErr = undefined;
1163 var configJSONPErr = undefined;
1164 if (fs.existsSync(__dirname + "/config.json")) {
1165 var configJSONf = "";
1167 configJSONf = fs.readFileSync(__dirname + "/config.json"); // Read JSON File
1169 configJSON = JSON.parse(configJSONf); // Parse JSON
1171 configJSONPErr = err2;
1174 configJSONRErr = err2;
1178 // Default server configuration properties
1179 var wwwredirect = false;
1180 var rawBlockList = [];
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;
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);
1215 var portLMatch = port.match(/^(\[[^ \]@\/\\]+\]|[^ \]\[:@\/\\]+):([0-9]+)$/);
1217 listenAddress = portLMatch[1].replace(/^\[|\]$/g, "").replace(/^::ffff:/i, "");
1218 port = parseInt(portLMatch[2]);
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);
1229 var sportLMatch = sport.match(/^(\[[^ \]@\/\\]+\]|[^ \]\[:@\/\\]+):([0-9]+)$/);
1231 sListenAddress = sportLMatch[1].replace(/^\[|\]$/g, "").replace(/^::ffff:/i, "");
1232 sport = parseInt(sportLMatch[2]);
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;
1265 if (cluster.isPrimary || cluster.isPrimary === undefined) process.chdir(configJSON.wwwroot != undefined ? configJSON.wwwroot : __dirname);
1271 Object.keys(environmentVariables).forEach(function (key) {
1272 process.env[key] = environmentVariables[key];
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) {
1287 Object.keys(nonStandardCodeRaw).forEach(function (nsKey) {
1288 if (nsKey != "users") {
1289 newObject[nsKey] = nonStandardCodeRaw[nsKey];
1291 newObject["users"] = ipBlockList(nonStandardCodeRaw.users);
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)) + ")";
1301 customHeaders["Server"] = "SVR.JS";
1304 function getCustomHeaders() {
1305 return JSON.parse(JSON.stringify(customHeaders));
1310 vnum = process.config.variables.node_module_version;
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;
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();
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, "");
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
1368 // Match 6: path name
1369 // Match 7: query string
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);
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];
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];
1399 if (parsedURI[5]) uobject.port = parsedURI[5];
1400 if (parsedURI[6]) uobject.pathname = parsedURI[6];
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(/([^&=]*)(?:=([^&]*))?/);
1410 qobject[parsedQP[1]] = parsedQP[2] ? parsedQP[2] : "";
1414 uobject.query = qobject;
1416 uobject.query = Object.create(null);
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 : "");
1425 // Node.JS mojibake URL fixing function
1426 function fixNodeMojibakeURL(string) {
1430 Buffer.from(string, "latin1").forEach(function (value) {
1432 encoded += "%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
1434 encoded += String.fromCodePoint(value);
1438 //Upper case the URL encodings
1439 return encoded.replace(/%[0-9a-f-A-F]{2}/g, function (match) {
1440 return match.toUpperCase();
1449 if (!configJSON.key) configJSON.key = "cert/key.key";
1450 if (!configJSON.cert) configJSON.cert = "cert/cert.crt";
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")) {
1462 var certificateError = null;
1463 var sniReDos = false;
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(/\*[^*.:]*\*[^*.:]*(?:\.|:|$)/)) {
1476 sniCredentials.push({
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()
1483 certificateError = err;
1487 var logFile = undefined;
1488 var logSync = false;
1493 if (configJSON.enableLogging || configJSON.enableLogging == undefined) {
1495 fs.appendFileSync(__dirname + "/log/" + (cluster.isPrimary ? "master" : (cluster.isPrimary === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", "[" + new Date().toISOString() + "] " + s + "\r\n");
1498 logFile = fs.createWriteStream(__dirname + "/log/" + (cluster.isPrimary ? "master" : (cluster.isPrimary === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", {
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);
1506 if (logFile.writable) {
1507 logFile.write("[" + new Date().toISOString() + "] " + s + "\r\n");
1509 throw new Error("Log file stream is closed.");
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);
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);
1527 console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m: " + msg);
1528 LOG("SERVER CLI MESSAGE: " + msg);
1531 reqmessage: function (msg) {
1532 if (msg.indexOf("\n") != -1) {
1533 msg.split("\n").forEach(function (nmsg) {
1534 serverconsole.reqmessage(nmsg);
1538 console.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m: " + msg + "\x1b[37m\x1b[0m");
1539 LOG("SERVER REQUEST MESSAGE: " + msg);
1542 resmessage: function (msg) {
1543 if (msg.indexOf("\n") != -1) {
1544 msg.split("\n").forEach(function (nmsg) {
1545 serverconsole.resmessage(nmsg);
1549 console.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m: " + msg + "\x1b[37m\x1b[0m");
1550 LOG("SERVER RESPONSE MESSAGE: " + msg);
1553 errmessage: function (msg) {
1554 if (msg.indexOf("\n") != -1) {
1555 msg.split("\n").forEach(function (nmsg) {
1556 serverconsole.errmessage(nmsg);
1560 console.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m: " + msg + "\x1b[37m\x1b[0m");
1561 LOG("SERVER RESPONSE ERROR MESSAGE: " + msg);
1564 locerrmessage: function (msg) {
1565 if (msg.indexOf("\n") != -1) {
1566 msg.split("\n").forEach(function (nmsg) {
1567 serverconsole.locerrmessage(nmsg);
1571 console.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m: " + msg + "\x1b[40m\x1b[0m");
1572 LOG("SERVER ERROR MESSAGE: " + msg);
1575 locwarnmessage: function (msg) {
1576 if (msg.indexOf("\n") != -1) {
1577 msg.split("\n").forEach(function (nmsg) {
1578 serverconsole.locwarnmessage(nmsg);
1582 console.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m: " + msg + "\x1b[40m\x1b[0m");
1583 LOG("SERVER WARNING MESSAGE: " + msg);
1586 locmessage: function (msg) {
1587 if (msg.indexOf("\n") != -1) {
1588 msg.split("\n").forEach(function (nmsg) {
1589 serverconsole.locmessage(nmsg);
1593 console.log("\x1b[1mSERVER MESSAGE\x1b[22m: " + msg);
1594 LOG("SERVER MESSAGE: " + msg);
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) {
1604 logFile.close(function () {
1605 logFile = undefined;
1607 process.unsafeExit(code);
1609 if (process.isBun) {
1610 setInterval(function () {
1611 if (!logFile.writable) {
1612 logFile = undefined;
1614 process.unsafeExit(code);
1618 setTimeout(function () {
1619 logFile = undefined;
1621 process.unsafeExit(code);
1622 }, 10000); // timeout
1624 logFile = undefined;
1626 process.unsafeExit(code);
1630 process.unsafeExit(code);
1634 // SVR.JS mod loader
1635 var modLoadingErrors = [];
1636 var SSJSError = undefined;
1638 // Load mods if the `disableMods` flag is not set
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);
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";
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;
1660 // Try creating the modloader folder (if not already exists)
1662 fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName);
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");
1669 fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName);
1671 // If there was another error, throw it
1672 if (err.code != "EEXIST") throw err;
1677 // Create a subfolder for the current mod within the modloader folder
1678 fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFileRaw);
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.
1685 // Check if the current mod file is a regular file
1686 if (fs.statSync(modFile).isFile()) {
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;
1695 C: __dirname + "/temp/" + modloaderFolderName + "/" + modFileRaw
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.");
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++) {
1709 Mod = require("./temp/" + modloaderFolderName + "/" + modFileRaw + "/index.js");
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
1721 // Add the loaded mod to the mods list
1724 // Attempt to read the mod's info file, retrying up to 3 times
1725 for (var j = 0; j < 3; j++) {
1727 modInfos.push(JSON.parse(fs.readFileSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFileRaw + "/mod.info")));
1731 // If failed to read info file, add a placeholder entry to modInfos with an error message
1733 name: "Unknown mod (" + modFileRaw + ";" + err.message + ")",
1737 // Wait for a short time before retrying
1738 var now = Date.now();
1739 while (Date.now() - now < 2);
1740 // Try reloading mod info
1744 modLoadingErrors.push({
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()) {
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++) {
1772 aMod = require("./temp/" + tempServerSideScriptName);
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
1784 // Add the loaded server side script to the mods list
1794 function sha256(s) {
1795 if (crypto.__disabled__ === undefined) {
1796 var hash = crypto.createHash("SHA256");
1798 return hash.digest("hex");
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);
1810 return (X >>> n) | (X << (32 - n));
1817 function Ch(x, y, z) {
1818 return ((x & y) ^ ((~x) & z));
1821 function Maj(x, y, z) {
1822 return ((x & y) ^ (x & z) ^ (y & z));
1825 function Sigma0256(x) {
1826 return (S(x, 2) ^ S(x, 13) ^ S(x, 22));
1829 function Sigma1256(x) {
1830 return (S(x, 6) ^ S(x, 11) ^ S(x, 25));
1833 function Gamma0256(x) {
1834 return (S(x, 7) ^ S(x, 18) ^ R(x, 3));
1837 function Gamma1256(x) {
1838 return (S(x, 17) ^ S(x, 19) ^ R(x, 10));
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;
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) {
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));
1875 a = safeAdd(T1, T2);
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]);
1890 function str2binb(str) {
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);
1899 function Utf8Encode(string) {
1900 string = string.replace(/\r\n/g, "\n");
1903 for (var n = 0; n < string.length; n++) {
1905 var c = string.charCodeAt(n);
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);
1913 utftext += String.fromCharCode((c >> 12) | 224);
1914 utftext += String.fromCharCode(((c >> 6) & 63) | 128);
1915 utftext += String.fromCharCode((c & 63) | 128);
1923 function binb2hex(binarray) {
1924 var hexTab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
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);
1934 return binb2hex(coreSha256(str2binb(s), s.length * chrsz));
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;
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, "/");
1951 return "/" + relative;
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());
1962 if (typeof forbiddenPath === "object") {
1963 return forbiddenPath.some(function (forbiddenPathSingle) {
1964 return (decodedHref === forbiddenPathSingle || (os.platform() === "win32" && decodedHref.toLowerCase() === forbiddenPathSingle.toLowerCase()));
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));
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)));
1985 // Set up forbidden paths
1986 var forbiddenPaths = {};
1988 forbiddenPaths.config = getInitializePath("./config.json");
1989 forbiddenPaths.certificates = [];
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));
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");
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) {
2088 var malformedcounter = 0;
2089 var err4xxcounter = 0;
2090 var err5xxcounter = 0;
2091 var reqcounterKillReq = 0;
2095 server2 = http.createServer({
2096 requireHostHeader: false
2099 server2 = http.createServer();
2101 server2.on("request", function (req, res) {
2102 reqhandler(req, res, false);
2104 server2.on("checkExpectation", reqhandler);
2105 server2.on("clientError", function (err, socket) {
2106 reqerrhandler(err, socket, false);
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);
2121 console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2122 LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2125 reqmessage: function (msg) {
2126 if (msg.indexOf("\n") != -1) {
2127 msg.split("\n").forEach(function (nmsg) {
2128 serverconsole.reqmessage(nmsg);
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);
2136 resmessage: function (msg) {
2137 if (msg.indexOf("\n") != -1) {
2138 msg.split("\n").forEach(function (nmsg) {
2139 serverconsole.resmessage(nmsg);
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);
2147 errmessage: function (msg) {
2148 if (msg.indexOf("\n") != -1) {
2149 msg.split("\n").forEach(function (nmsg) {
2150 serverconsole.errmessage(nmsg);
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);
2158 locerrmessage: function (msg) {
2159 if (msg.indexOf("\n") != -1) {
2160 msg.split("\n").forEach(function (nmsg) {
2161 serverconsole.locerrmessage(nmsg);
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);
2169 locwarnmessage: function (msg) {
2170 if (msg.indexOf("\n") != -1) {
2171 msg.split("\n").forEach(function (nmsg) {
2172 serverconsole.locwarnmessage(nmsg);
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);
2180 locmessage: function (msg) {
2181 if (msg.indexOf("\n") != -1) {
2182 msg.split("\n").forEach(function (nmsg) {
2183 serverconsole.locmessage(nmsg);
2187 console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2188 LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
2192 socket.on("close", function (hasError) {
2193 if (!hasError) serverconsole.locmessage("Client disconnected.");
2194 else serverconsole.locmessage("Client disconnected due to error.");
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 + "...");
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");
2207 server2.on("connect", connhandler);
2209 server2.on("error", function (err) {
2210 serverErrorHandler(err, true);
2212 server2.on("listening", function () {
2217 if (configJSON.enableHTTP2 == true) {
2219 server = http2.createSecureServer({
2221 requireHostHeader: false,
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
2234 server = http2.createServer({
2236 requireHostHeader: false,
2237 settings: configJSON.http2Settings
2242 server = https.createServer({
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
2256 server = http.createServer({
2257 requireHostHeader: false
2260 server = http.createServer();
2266 sniCredentials.forEach(function (sniCredentialsSingle) {
2267 server.addContext(sniCredentialsSingle.name, {
2268 cert: sniCredentialsSingle.cert,
2269 key: sniCredentialsSingle.key
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");
2276 // Can't replace regex, ignoring...
2283 server.on("request", reqhandler);
2284 server.on("checkExpectation", reqhandler);
2285 server.on("connect", connhandler);
2286 server.on("clientError", reqerrhandler);
2289 server.prependListener("connection", function (sock) {
2290 sock.reallyDestroy = sock.destroy;
2291 sock.destroy = function () {
2292 sock.toDestroy = true;
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);
2305 sock.write = function (a, b, c) {
2306 sock._parent.write(a, b, c);
2308 sock.end = function (a, b, c) {
2309 sock._parent.end(a, b, c);
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) {
2318 sock._parent.destroy(a, b, c);
2319 sock.destroyed = sock._parent.destroyed;
2321 // Socket is probably already destroyed.
2325 sock._parent.destroy = sock._parent.reallyDestroy;
2327 if (sock._parent.toDestroy) sock._parent.destroy();
2329 // Socket is probably already destroyed.
2334 server.prependListener("secureConnection", function (sock) {
2335 sock._parent.destroy = sock._parent.reallyDestroy;
2336 delete sock._parent.reallyDestroy;
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);
2350 ocspCache.request(req.id, options, callback);
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
2364 res.socket = socket;
2365 res.write = function (x) {
2366 if (err.code === "ECONNRESET" || !socket.writable) {
2371 res.end = function (x) {
2372 if (err.code === "ECONNRESET" || !socket.writable) {
2375 socket.end(x, function () {
2379 // Socket is probably already destroyed
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);
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]);
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);
2417 console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2418 LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2421 reqmessage: function (msg) {
2422 if (msg.indexOf("\n") != -1) {
2423 msg.split("\n").forEach(function (nmsg) {
2424 serverconsole.reqmessage(nmsg);
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);
2432 resmessage: function (msg) {
2433 if (msg.indexOf("\n") != -1) {
2434 msg.split("\n").forEach(function (nmsg) {
2435 serverconsole.resmessage(nmsg);
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);
2443 errmessage: function (msg) {
2444 if (msg.indexOf("\n") != -1) {
2445 msg.split("\n").forEach(function (nmsg) {
2446 serverconsole.errmessage(nmsg);
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);
2454 locerrmessage: function (msg) {
2455 if (msg.indexOf("\n") != -1) {
2456 msg.split("\n").forEach(function (nmsg) {
2457 serverconsole.locerrmessage(nmsg);
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);
2465 locwarnmessage: function (msg) {
2466 if (msg.indexOf("\n") != -1) {
2467 msg.split("\n").forEach(function (nmsg) {
2468 serverconsole.locwarnmessage(nmsg);
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);
2476 locmessage: function (msg) {
2477 if (msg.indexOf("\n") != -1) {
2478 msg.split("\n").forEach(function (nmsg) {
2479 serverconsole.locmessage(nmsg);
2483 console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2484 LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
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.");
2492 socket.on("error", function () {});
2494 // Header and footer placeholders
2498 function responseEnd(body) {
2499 // If body is Buffer, then it is converted to String anyway.
2500 res.write(head + body + foot);
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.");
2510 // Handle optional parameters
2511 if (extName && typeof extName === "object") {
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.");
2519 if (stack && typeof stack === "object" && Object.prototype.toString.call(stack) !== "[object Error]") {
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.");
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");
2534 function medCallback(p) {
2537 if (errorCode == 404) {
2538 fs.access(page404, fs.constants.F_OK, function (err) {
2540 fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
2543 callback(errorCode.toString() + ".html");
2545 callback("." + errorCode.toString());
2548 callServerError(500, err2);
2555 callServerError(500, err2);
2560 fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
2563 callback(errorCode.toString() + ".html");
2565 callback("." + errorCode.toString());
2568 callServerError(500, err2);
2576 if (_i >= list.length) {
2581 if (list[_i].scode != errorCode) {
2582 getErrorFileName(list, callback, _i + 1);
2585 fs.access(list[_i].path, fs.constants.F_OK, function (err) {
2587 getErrorFileName(list, callback, _i + 1);
2589 medCallback(list[_i].path);
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);
2603 if (stackHidden) stack = "[error stack hidden]";
2604 if (serverHTTPErrorDescs[errorCode] === undefined) {
2605 callServerError(501, extName, stack);
2607 var cheaders = getCustomHeaders();
2609 var chon = Object.keys(cheaders);
2610 Object.keys(ch).forEach(function (chnS) {
2612 for (var j = 0; j < chon.length; j++) {
2613 if (chon[j].toLowerCase() == chnS.toLowerCase()) {
2618 if (ch[chnS]) cheaders[nhn] = ch[chnS];
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, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, " ")).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, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{contact}/g, serverAdmin.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\./g, "[dot]").replace(/@/g, "[at]")));
2629 fs.readFile(errorFile, function (err, data) {
2632 res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
2633 responseEnd(data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode].replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, " ")).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, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{contact}/g, serverAdmin.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\./g, "[dot]").replace(/@/g, "[at]")));
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;
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, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, " ")).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, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{contact}/g, serverAdmin.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString()));
2658 var reqip = socket.remoteAddress;
2659 var reqport = socket.remotePort;
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) {
2668 if (err.code == "ENOENT" || err.code == "EISDIR") {
2669 fs.readFile(("./head.html").replace(/\/+/g, "/"), function (err, data) {
2671 if (err.code == "ENOENT" || err.code == "EISDIR") {
2674 callServerError(500, err);
2677 head = data.toString();
2682 callServerError(500, err);
2685 head = data.toString();
2691 function getCustomFooter(callback) {
2692 fs.readFile(("./.foot").replace(/\/+/g, "/"), function (err, data) {
2694 if (err.code == "ENOENT" || err.code == "EISDIR") {
2695 fs.readFile(("./foot.html").replace(/\/+/g, "/"), function (err, data) {
2697 if (err.code == "ENOENT" || err.code == "EISDIR") {
2700 callServerError(500, err);
2703 foot = data.toString();
2708 callServerError(500, err);
2711 foot = data.toString();
2717 getCustomHeader(function () {
2718 getCustomFooter(function () {
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);
2726 serverconsole.errmessage("An SSL error occured: " + (err.code ? err.code : err.message));
2727 callServerError(400);
2732 if (err.code && err.code.indexOf("ERR_HTTP2_") == 0) {
2733 serverconsole.errmessage("An HTTP/2 error occured: " + err.code);
2734 callServerError(400);
2738 if (err.code && err.code == "ERR_HTTP_REQUEST_TIMEOUT") {
2739 serverconsole.errmessage("Client timed out.");
2740 callServerError(408);
2744 if (!err.rawPacket) {
2745 serverconsole.errmessage("Connection ended prematurely.");
2746 callServerError(400);
2750 var packetLines = err.rawPacket.toString().split("\r\n");
2751 if (packetLines.length == 0) {
2752 serverconsole.errmessage("Invalid request.");
2753 callServerError(400);
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);
2765 } else if (header.length > 8192) {
2766 serverconsole.errmessage("Header too large.");
2767 callServerError(431); // Headers too large
2773 var packetLine1 = packetLines[0].split(" ");
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
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
2795 if (checkHeaders(false)) return;
2796 if (packetLine1[0].length > 255) {
2797 serverconsole.errmessage("URI too long.");
2798 callServerError(414); // Also malformed Packet
2800 serverconsole.errmessage("The request is invalid.");
2801 callServerError(400); // Also malformed Packet
2805 serverconsole.errmessage("There was an error while determining type of malformed request.");
2806 callServerError(400);
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);
2824 console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2825 LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2828 reqmessage: function (msg) {
2829 if (msg.indexOf("\n") != -1) {
2830 msg.split("\n").forEach(function (nmsg) {
2831 serverconsole.reqmessage(nmsg);
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);
2839 resmessage: function (msg) {
2840 if (msg.indexOf("\n") != -1) {
2841 msg.split("\n").forEach(function (nmsg) {
2842 serverconsole.resmessage(nmsg);
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);
2850 errmessage: function (msg) {
2851 if (msg.indexOf("\n") != -1) {
2852 msg.split("\n").forEach(function (nmsg) {
2853 serverconsole.errmessage(nmsg);
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);
2861 locerrmessage: function (msg) {
2862 if (msg.indexOf("\n") != -1) {
2863 msg.split("\n").forEach(function (nmsg) {
2864 serverconsole.locerrmessage(nmsg);
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);
2872 locwarnmessage: function (msg) {
2873 if (msg.indexOf("\n") != -1) {
2874 msg.split("\n").forEach(function (nmsg) {
2875 serverconsole.locwarnmessage(nmsg);
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);
2883 locmessage: function (msg) {
2884 if (msg.indexOf("\n") != -1) {
2885 msg.split("\n").forEach(function (nmsg) {
2886 serverconsole.locmessage(nmsg);
2890 console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2891 LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
2896 socket.on("close", function (hasError) {
2897 if (!hasError) serverconsole.locmessage("Client disconnected.");
2898 else serverconsole.locmessage("Client disconnected due to error.");
2900 socket.on("error", function () {});
2902 var reqip = socket.remoteAddress;
2903 var reqport = socket.remotePort;
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) {
2911 mods.forEach(function (mod) {
2912 if (mod.proxyCallback !== undefined) proxyMods.push(mod);
2915 var modFunction = ffinals;
2916 proxyMods.reverse().forEach(function (proxyMod) {
2917 modFunction = proxyMod.proxyCallback(req, socket, head, configJSON, serverconsole, modFunction);
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");
2927 modExecute(mods, vres);
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);
2943 console.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
2944 LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
2947 reqmessage: function (msg) {
2948 if (msg.indexOf("\n") != -1) {
2949 msg.split("\n").forEach(function (nmsg) {
2950 serverconsole.reqmessage(nmsg);
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);
2958 resmessage: function (msg) {
2959 if (msg.indexOf("\n") != -1) {
2960 msg.split("\n").forEach(function (nmsg) {
2961 serverconsole.resmessage(nmsg);
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);
2969 errmessage: function (msg) {
2970 if (msg.indexOf("\n") != -1) {
2971 msg.split("\n").forEach(function (nmsg) {
2972 serverconsole.errmessage(nmsg);
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);
2980 locerrmessage: function (msg) {
2981 if (msg.indexOf("\n") != -1) {
2982 msg.split("\n").forEach(function (nmsg) {
2983 serverconsole.locerrmessage(nmsg);
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);
2991 locwarnmessage: function (msg) {
2992 if (msg.indexOf("\n") != -1) {
2993 msg.split("\n").forEach(function (nmsg) {
2994 serverconsole.locwarnmessage(nmsg);
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);
3002 locmessage: function (msg) {
3003 if (msg.indexOf("\n") != -1) {
3004 msg.split("\n").forEach(function (nmsg) {
3005 serverconsole.locmessage(nmsg);
3009 console.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId + "]: " + msg);
3010 LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
3015 function matchHostname(hostname) {
3016 if (typeof hostname == "undefined" || hostname == "*") {
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)) {
3023 } else if (req.headers.host && req.headers.host == hostname) {
3029 function getCustomHeaders() {
3030 var ph = JSON.parse(JSON.stringify(customHeaders));
3031 if (configJSON.customHeadersVHost) {
3033 configJSON.customHeadersVHost.every(function (vhost) {
3034 if (matchHostname(vhost.host) && ipMatch(vhost.ip, req.socket ? req.socket.localAddress : undefined)) {
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];
3048 Object.keys(ph).forEach(function (phk) {
3049 if (typeof ph[phk] == "string") ph[phk] = ph[phk].replace(/\{path\}/g, req.url);
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) {
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];
3070 if (res.stream && res.stream.destroyed) {
3073 return res.writeHeadNodeApi(a, table);
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);
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);
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);
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)) {
3109 process.emitWarning("res.writeHead called multiple times.", {
3110 code: "WARN_SVRJS_MULTIPLE_WRITEHEAD"
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.");
3121 serverconsole.resmessage("Server responded with " + code.toString() + " code.");
3123 if (typeof codeDescription != "string" && http.STATUS_CODES[code]) {
3124 if (!headers) headers = codeDescription;
3125 codeDescription = http.STATUS_CODES[code];
3127 lastStatusCode = code;
3129 res.writeHeadNative(code, codeDescription, headers);
3132 var finished = false;
3133 res.on("finish", function () {
3136 serverconsole.locmessage("Client disconnected.");
3139 res.on("close", function () {
3142 serverconsole.locmessage("Client disconnected.");
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!!!");
3154 // Set up X-Forwarded-For
3155 var reqip = req.socket.remoteAddress;
3156 var reqport = req.socket.remotePort;
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;
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;
3180 // Address setting failed
3183 isForwardedValid = false;
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, "");
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
3208 function responseEnd(body) {
3209 // If body is Buffer, then it is converted to String anyway.
3210 res.write(head + body + foot);
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.");
3220 // Handle optional parameters
3221 if (extName && typeof extName === "object") {
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.");
3229 if (stack && typeof stack === "object" && Object.prototype.toString.call(stack) !== "[object Error]") {
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.");
3236 // Determine error file
3237 function getErrorFileName(list, callback, _i) {
3238 function medCallback(p) {
3241 if (errorCode == 404) {
3242 fs.access(page404, fs.constants.F_OK, function (err) {
3244 fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
3247 callback(errorCode.toString() + ".html");
3249 callback("." + errorCode.toString());
3252 callServerError(500, err2);
3259 callServerError(500, err2);
3264 fs.access("." + errorCode.toString(), fs.constants.F_OK, function (err) {
3267 callback(errorCode.toString() + ".html");
3269 callback("." + errorCode.toString());
3272 callServerError(500, err2);
3280 if (_i >= list.length) {
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);
3289 fs.access(list[_i].path, fs.constants.F_OK, function (err) {
3291 getErrorFileName(list, callback, _i + 1);
3293 medCallback(list[_i].path);
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);
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);
3317 var cheaders = getCustomHeaders();
3319 // Process custom headers if provided
3321 var chon = Object.keys(cheaders);
3322 Object.keys(ch).forEach(function (chnS) {
3324 for (var j = 0; j < chon.length; j++) {
3325 if (chon[j].toLowerCase() == chnS.toLowerCase()) {
3330 if (ch[chnS]) cheaders[nhn] = ch[chnS];
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) {
3343 res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
3344 responseEnd(data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode].replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, " ")).replace(/{path}/g, req.url.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).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, "&").replace(/</g, "<").replace(/>/g, ">") + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"))).replace(/{contact}/g, serverAdmin.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\./g, "[dot]").replace(/@/g, "[at]"))); // Replace placeholders in error response
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;
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, "&").replace(/</g, "<").replace(/>/g, ">")).replace(/{errorDesc}/g, serverHTTPErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, " ")).replace(/{path}/g, req.url.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).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, "&").replace(/</g, "<").replace(/>/g, ">") + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"))).replace(/{contact}/g, serverAdmin.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); // Replace placeholders in error response
3371 function getCustomHeader(callback) {
3372 fs.readFile(("./.head").replace(/\/+/g, "/"), function (err, data) {
3374 if (err.code == "ENOENT" || err.code == "EISDIR") {
3375 fs.readFile(("./head.html").replace(/\/+/g, "/"), function (err, data) {
3377 if (err.code == "ENOENT" || err.code == "EISDIR") {
3380 callServerError(500, err);
3383 head = data.toString();
3388 callServerError(500, err);
3391 head = data.toString();
3397 function getCustomFooter(callback) {
3398 fs.readFile(("./.foot").replace(/\/+/g, "/"), function (err, data) {
3400 if (err.code == "ENOENT" || err.code == "EISDIR") {
3401 fs.readFile(("./foot.html").replace(/\/+/g, "/"), function (err, data) {
3403 if (err.code == "ENOENT" || err.code == "EISDIR") {
3406 callServerError(500, err);
3409 foot = data.toString();
3414 callServerError(500, err);
3417 foot = data.toString();
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);
3450 // Return from the function
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);
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
3473 formidableOptions = {};
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
3486 if (err.httpCode) callServerError(err.httpCode);
3487 else callServerError(400);
3490 // Otherwise, call the provided callback function with the parsed fields and files
3491 callback(fields, files);
3495 getCustomHeader( function () {
3496 getCustomFooter( function () {
3497 // Authenticated user variable
3498 var authUser = null;
3500 // URL-related objects.
3503 uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
3505 // Return an 400 error
3506 callServerError(400);
3507 serverconsole.errmessage("Bad request!");
3510 var search = uobject.search;
3511 var href = uobject.pathname;
3512 var ext = href.match(/[^\/]\.([^.]+)$/);
3514 else ext = ext[1].toLowerCase();
3515 var decodedHref = "";
3517 decodedHref = decodeURIComponent(href);
3519 // Return an 400 error
3520 callServerError(400);
3521 serverconsole.errmessage("Bad request!");
3524 var origHref = href; // Placeholder origHref
3527 if (req.headers["expect"] && req.headers["expect"] != "100-continue") {
3528 // Expectations not met.
3529 callServerError(417);
3533 // Mod execution function
3534 function modExecute(mods, ffinals) {
3535 // Prepare modFunction
3536 var modFunction = ffinals;
3537 var useMods = mods.slice();
3540 // Get list of forward proxy mods
3542 mods.forEach(function (mod) {
3543 if (mod.proxyCallback !== undefined) useMods.push(mod);
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);
3551 // Execute modFunction
3555 var vresCalled = false;
3559 process.emitWarning("elseCallback() invoked multiple times.", {
3560 code: "WARN_SVRJS_MULTIPLE_ELSECALLBACK"
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]) {
3582 // If the component is not "." or an empty string, increment the levelDownCount
3583 else if ("." !== pathComponents[i] && "" !== pathComponents[i]) {
3588 // Calculate the overall level by subtracting levelUpCount from levelDownCount
3589 var overallLevel = levelDownCount - levelUpCount;
3591 // Return the overall level
3592 return overallLevel;
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, "&").replace(/</g, "<").replace(/>/g, ">") + "</i></p></body></html>");
3602 serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod.");
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);
3612 } else if (req.method != "GET" && req.method != "POST" && req.method != "HEAD") {
3613 callServerError(405);
3614 serverconsole.errmessage("Invaild method: " + req.method);
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";
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, "&").replace(/</g, "<").replace(/>/g, ">") + "<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"
3643 res.end((head == "" ? "<html><head><title>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "</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, "&").replace(/</g, "<").replace(/>/g, ">")) + "</title>")) + "<h1>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "</h1>" + statusBody + (foot == "" ? "</body></html>" : foot));
3647 var dHref = decodeURIComponent(href);
3648 var readFrom = "." + dHref;
3649 var dirImagesMissing = false;
3650 fs.stat(readFrom, function (err, stats) {
3652 if (err.code == "ENOENT") {
3653 if (__dirname != process.cwd() && dHref.match(/^\/\.dirimages\/(?:(?!\.png$).)+\.png$/)) {
3654 dirImagesMissing = true;
3655 readFrom = __dirname + dHref;
3657 callServerError(404);
3658 serverconsole.errmessage("Resource not found.");
3661 } else if (err.code == "ENOTDIR") {
3662 callServerError(404); // Assume that file doesn't exist.
3663 serverconsole.errmessage("Resource not found.");
3665 } else if (err.code == "EACCES") {
3666 callServerError(403);
3667 serverconsole.errmessage("Access denied.");
3669 } else if (err.code == "ENAMETOOLONG") {
3670 callServerError(414);
3672 } else if (err.code == "EMFILE") {
3673 callServerError(503);
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.");
3680 callServerError(500, err);
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();
3697 readFrom = (readFrom + "/index.xhtml").replace(/\/+/g, "/");
3698 properDirectoryListingAndStaticFileServe();
3704 readFrom = (readFrom + "/index.htm").replace(/\/+/g, "/");
3705 properDirectoryListingAndStaticFileServe();
3711 readFrom = (readFrom + "/index.html").replace(/\/+/g, "/");
3712 properDirectoryListingAndStaticFileServe();
3715 } else if (dirImagesMissing) {
3716 fs.stat(readFrom, function (e, s) {
3717 if (e || !s.isFile()) {
3718 properDirectoryListingAndStaticFileServe();
3721 properDirectoryListingAndStaticFileServe();
3725 properDirectoryListingAndStaticFileServe();
3728 function properDirectoryListingAndStaticFileServe() {
3729 if (stats.isFile()) {
3730 var acceptEncoding = req.headers["accept-encoding"];
3731 if (!acceptEncoding) acceptEncoding = "";
3733 var filelen = stats.size;
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], {
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, {
3759 // Handle partial content request
3760 if (ext != "html" && req.headers["range"]) {
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]*)/);
3767 callServerError(416, rhd);
3769 // Process the partial content request
3770 var beginOrig = regexmatch[1];
3771 var endOrig = regexmatch[2];
3773 var end = filelen - 1;
3774 if (beginOrig == "" && endOrig == "") {
3775 callServerError(416, rhd);
3777 } else if (beginOrig == "") {
3778 begin = end - parseInt(endOrig) + 1;
3780 begin = parseInt(beginOrig);
3781 if (endOrig != "") end = parseInt(endOrig);
3783 if (begin > end || begin < 0 || begin > filelen - 1) {
3784 callServerError(416, rhd);
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, {
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.");
3816 callServerError(500, err);
3818 }).on("open", function () {
3820 res.writeHead(206, http.STATUS_CODES[206], rhd);
3821 readStream.pipe(res);
3822 serverconsole.resmessage("Client successfully received content.");
3824 callServerError(500, err);
3828 res.writeHead(206, http.STATUS_CODES[206], rhd);
3833 callServerError(500, err);
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;
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;
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.
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.
3869 callServerError(500, err);
3873 // Bun 1.1 has definition for zlib.createBrotliCompress, but throws an error while invoking the function.
3874 if (process.isBun && useBrotli && isCompressable) {
3876 zlib.createBrotliCompress();
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";
3891 if (ext == "html") {
3892 hdhds["Content-Length"] = head.length + filelen + foot.length;
3894 hdhds["Content-Length"] = filelen;
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.");
3922 callServerError(500, err);
3924 }).on("open", function () {
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);
3939 if (ext == "html") {
3940 function afterWriteCallback() {
3941 readStream.on("end", function () {
3942 resStream.end(foot);
3944 readStream.pipe(resStream, {
3948 res.writeHead(200, http.STATUS_CODES[200], hdhds);
3949 if (!resStream.write(head)) {
3950 resStream.on("drain", afterWriteCallback);
3952 process.nextTick(afterWriteCallback);
3955 res.writeHead(200, http.STATUS_CODES[200], hdhds);
3956 readStream.pipe(resStream);
3958 serverconsole.resmessage("Client successfully received content.");
3960 callServerError(500, err);
3964 res.writeHead(200, http.STATUS_CODES[200], hdhds);
3966 serverconsole.resmessage("Client successfully received content.");
3969 callServerError(500, err);
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) {
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) {
3985 if (err.code == "ENOENT" || err.code == "EISDIR") {
3988 callServerError(500, err);
3991 customDirListingHeader = data.toString();
3999 callServerError(500, err);
4002 customDirListingHeader = data.toString();
4008 function getCustomDirListingFooter(callback) {
4009 fs.readFile(("." + dHref + "/.dirfoot").replace(/\/+/g, "/"), function (err, data) {
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) {
4015 if (err.code == "ENOENT" || err.code == "EISDIR") {
4018 callServerError(500, err);
4021 customDirListingFooter = data.toString();
4029 callServerError(500, err);
4032 customDirListingFooter = data.toString();
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, "&").replace(/</g, "<").replace(/>/g, ">") + "</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, "&").replace(/</g, "<").replace(/>/g, ">") + "</title>")) :
4049 head.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</title>")) +
4050 (!headerHasHTMLTag ? customDirListingHeader : "") +
4051 "<h1>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</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, "&").replace(/</g, "<").replace(/>/g, ">") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "</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;
4059 fs.readdir(readFrom, function (err, list) {
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);
4071 fs.stat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
4073 fs.lstat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
4075 name: fileList[index],
4076 stats: err ? null : stats,
4079 if (index < fileList.length - 1) {
4080 getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
4082 callback(pushArray);
4087 name: fileList[index],
4091 if (index < fileList.length - 1) {
4092 getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
4094 callback(pushArray);
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);
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, "/") +
4121 ename.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") +
4122 "</a></td><td>-</td><td>" +
4123 (estats ? estats.mtime.toDateString() : "-") +
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() ? "/" : "") +
4131 ename.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") +
4133 (estats.isDirectory() ? "-" : sizify(estats.size.toString())) +
4135 estats.mtime.toDateString() +
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, "/") +
4145 ename.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") +
4146 "</a></td><td>-</td><td>" +
4147 estats.mtime.toDateString() +
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]");
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]");
4185 entry = entry.replace("[img]", "/.dirimages/other.png").replace("[alt]", "[OTH]");
4187 directoryListingRows.push(entry);
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>");
4197 // Send the directory listing response
4198 res.writeHead(200, http.STATUS_CODES[200], {
4199 "Content-Type": "text/html; charset=utf-8"
4201 res.end(htmlHead + directoryListingRows.join("") + htmlFoot);
4202 serverconsole.resmessage("Client successfully received content.");
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.");
4223 callServerError(500, err);
4230 // Directory listing is disabled, call 403 Forbidden error
4231 callServerError(403);
4232 serverconsole.errmessage("Directory listing is disabled.");
4235 callServerError(501);
4236 serverconsole.errmessage("SVR.JS doesn't support block devices, character devices, FIFOs nor sockets.");
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.");
4252 if (req.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);
4262 // SVR.JS doesn't understand that request, so throw an 400 error
4263 callServerError(400);
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.");
4275 // Check for invalid X-Forwarded-For header
4276 if (!isForwardedValid) {
4277 serverconsole.errmessage("X-Forwarded-For header is invalid.");
4278 callServerError(400);
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;
4302 uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4304 // Return an 400 error
4305 callServerError(400);
4306 serverconsole.errmessage("Bad request!");
4309 search = uobject.search;
4310 href = uobject.pathname;
4311 ext = href.match(/[^\/]\.([^.]+)$/);
4313 else ext = ext[1].toLowerCase();
4315 decodedHref = decodeURIComponent(href);
4318 callServerError(400);
4319 serverconsole.errmessage("Bad request!");
4323 redirect(sanitizedURL, false);
4326 } else if (req.url != preparedReqUrl && !isProxy) {
4327 serverconsole.resmessage("URL sanitized: " + req.url + " => " + preparedReqUrl);
4328 if (rewriteDirtyURLs) {
4329 req.url = preparedReqUrl;
4331 redirect(preparedReqUrl, false);
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);
4346 callServerError(501);
4347 serverconsole.errmessage("This server will never be a proxy.");
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;
4363 destinationPort = spubport;
4366 redirect("https://" + hostname + (destinationPort == 443 ? "" : (":" + destinationPort)) + req.url);
4370 // Handle redirects to addresses with "www." prefix
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, "/"));
4382 // Handle URL rewriting
4383 function rewriteURL(address, map, callback, _fileState, _mapBegIndex) {
4384 var rewrittenURL = address;
4385 var doCallback = true;
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) {
4394 } else if (stats.isDirectory()) {
4396 } else if (stats.isFile()) {
4401 rewriteURL(address, map, callback, _fileState, i);
4406 var tempRewrittenURL = rewrittenURL;
4407 if (!mapEntry.allowDoubleSlashes) {
4408 address = address.replace(/\/+/g,"/");
4409 tempRewrittenURL = address;
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;
4414 mapEntry.replacements.forEach(function (replacement) {
4415 rewrittenURL = rewrittenURL.replace(createRegex(replacement.regex), replacement.replacement);
4417 if (mapEntry.append) rewrittenURL += mapEntry.append;
4420 callback(err, null);
4426 if (doCallback) callback(null, rewrittenURL);
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()) {
4437 callServerError(500, err);
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);
4460 // Add web root postfixes
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;
4470 urlWithPostfix = urlWithPostfix.substring(postfixPrefix.length);
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;
4484 if (urlWithPostfix != preparedReqUrl3) {
4485 serverconsole.resmessage("Added web root postfix: " + req.url + " => " + urlWithPostfix);
4486 req.url = urlWithPostfix;
4488 uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4490 // Return an 400 error
4491 callServerError(400);
4492 serverconsole.errmessage("Bad request!");
4495 search = uobject.search;
4496 href = uobject.pathname;
4497 ext = href.match(/[^\/]\.([^.]+)$/);
4499 else ext = ext[1].toLowerCase();
4502 decodedHref = decodeURIComponent(href);
4505 callServerError(400);
4506 serverconsole.errmessage("Bad request!");
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.");
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;
4531 uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4533 // Return an 400 error
4534 callServerError(400);
4535 serverconsole.errmessage("Bad request!");
4538 search = uobject.search;
4539 href = uobject.pathname;
4540 ext = href.match(/[^\/]\.([^.]+)$/);
4542 else ext = ext[1].toLowerCase();
4544 decodedHref = decodeURIComponent(href);
4547 callServerError(400);
4548 serverconsole.errmessage("Bad request!");
4556 rewriteURL(req.url, rewriteMap, function (err, rewrittenURL) {
4558 callServerError(500, err);
4561 if (rewrittenURL != req.url) {
4562 serverconsole.resmessage("URL rewritten: " + req.url + " => " + rewrittenURL);
4563 req.url = rewrittenURL;
4565 uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4567 // Return an 400 error
4568 callServerError(400);
4569 serverconsole.errmessage("Bad request!");
4572 search = uobject.search;
4573 href = uobject.pathname;
4574 ext = href.match(/[^\/]\.([^.]+)$/);
4576 else ext = ext[1].toLowerCase();
4578 decodedHref = decodeURIComponent(href);
4581 callServerError(400);
4582 serverconsole.errmessage("Bad request!");
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.");
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;
4607 uobject = parseURL(req.url, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4609 // Return an 400 error
4610 callServerError(400);
4611 serverconsole.errmessage("Bad request!");
4614 search = uobject.search;
4615 href = uobject.pathname;
4616 ext = href.match(/[^\/]\.([^.]+)$/);
4618 else ext = ext[1].toLowerCase();
4620 decodedHref = decodeURIComponent(href);
4623 callServerError(400);
4624 serverconsole.errmessage("Bad request!");
4629 // Set response headers
4631 var hkh = getCustomHeaders();
4632 Object.keys(hkh).forEach(function (hkS) {
4634 res.setHeader(hkS, hkh[hkS]);
4636 // Headers will not be set.
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.");
4649 } else if (isIndexOfForbiddenPath(decodedHrefWithoutDuplicateSlashes, "temp") && !isProxy) {
4650 callServerError(403);
4651 serverconsole.errmessage("Access to temporary folder is denied.");
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.");
4657 } else if (isForbiddenPath(decodedHrefWithoutDuplicateSlashes, "svrjs") && !isProxy && !exposeServerVersion) {
4658 callServerError(403);
4659 serverconsole.errmessage("Access to SVR.JS script is denied.");
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.");
4666 var nonscodeIndex = -1;
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) {
4678 var createdRegex = createRegex(nonStandardCodes[i].regex, true);
4679 isMatch = req.url.match(createdRegex) || hrefWithoutDuplicateSlashes.match(createdRegex);
4680 regexI[i] = createdRegex;
4683 isMatch = nonStandardCodes[i].url == hrefWithoutDuplicateSlashes || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == hrefWithoutDuplicateSlashes.toLowerCase());
4686 if (nonStandardCodes[i].scode == 401) {
4687 // HTTP authentication
4688 if (authIndex == -1) {
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;
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) {
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);
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;
4719 location = nonscode.location + "?" + req.url.split("?")[1];
4721 redirect(location, nonscode.scode == 302 || nonscode.scode == 307, nonscode.scode == 307 || nonscode.scode == 308);
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 ;)");
4732 serverconsole.errmessage("Client fails receiving content.");
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) {
4745 var cb = function (hash) {
4746 if (hash == list[_i].pass) {
4748 } else if (_i >= list.length - 1) {
4751 checkIfPasswordMatches(list, password, callback, _i + 1);
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."));
4761 cacheEntry = scryptCache.find(function (entry) {
4762 return (entry.password == hashedPassword && entry.salt == list[_i].salt);
4765 cb(cacheEntry.hash);
4767 crypto.scrypt(password, list[_i].salt, 64, function (err, derivedKey) {
4769 callServerError(500, err);
4771 var key = derivedKey.toString("hex");
4774 password: hashedPassword,
4775 salt: list[_i].salt,
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."));
4788 cacheEntry = pbkdf2Cache.find(function (entry) {
4789 return (entry.password == hashedPassword && entry.salt == list[_i].salt);
4792 cb(cacheEntry.hash);
4794 crypto.pbkdf2(password, list[_i].salt, 36250, 64, "sha512", function (err, derivedKey) {
4796 callServerError(500, err);
4798 var key = derivedKey.toString("hex");
4801 password: hashedPassword,
4802 salt: list[_i].salt,
4815 function authorizedCallback(bruteProtection) {
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"];
4821 callServerError(401, ha);
4822 serverconsole.errmessage("Content needs authorization.");
4825 var credentialsMatch = credentials.match(/^Basic (.+)$/);
4826 if (!credentialsMatch) {
4827 callServerError(401, ha);
4828 serverconsole.errmessage("Malformed credentials.");
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.");
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) {
4848 } else if(entry.pbkdf2) {
4853 return entry.name == username;
4856 if (usernameMatch.length == 0) {
4857 // Pushing false user match to prevent time-based user enumeration
4858 var fakeCredentials = {
4860 pass: "SVRJSAWebServerRunningOnNodeJS",
4861 salt: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0"
4863 if (!process.isBun) {
4864 if (scryptCount > sha256Count && scryptCount > pbkdf2Count) {
4865 fakeCredentials.scrypt = true;
4866 } else if (pbkdf2Count > sha256Count) {
4867 fakeCredentials.pbkdf2 = true;
4870 usernameMatch.push(fakeCredentials);
4872 checkIfPasswordMatches(usernameMatch, password, function (authorized) {
4875 if (bruteProtection) {
4877 process.send("\x12AUTHW" + reqip);
4879 if (!bruteForceDb[reqip]) bruteForceDb[reqip] = {
4882 bruteForceDb[reqip].invalidAttempts++;
4883 if (bruteForceDb[reqip].invalidAttempts >= 10) {
4884 bruteForceDb[reqip].lastAttemptDate = new Date();
4888 callServerError(401, ha);
4889 serverconsole.errmessage("User \"" + String(username).replace(/[\r\n]/g, "") + "\" failed to log in.");
4891 if (bruteProtection) {
4893 process.send("\x12AUTHR" + reqip);
4895 if (bruteForceDb[reqip]) bruteForceDb[reqip] = {
4900 serverconsole.reqmessage("Client is logged in as \"" + String(username).replace(/[\r\n]/g, "") + "\".");
4901 authUser = username;
4902 redirectTrailingSlashes(function () {
4903 modExecute(mods, vres);
4907 callServerError(500, err);
4912 callServerError(500, err);
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] = {
4925 authorizedCallback(true);
4927 callServerError(429);
4928 serverconsole.errmessage("Brute force limit reached!");
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;
4940 if (message == "\x14AUTHD" + reqip) {
4941 callServerError(429);
4942 serverconsole.errmessage("Brute force limit reached!");
4943 } else if (message == "\x14AUTHA" + reqip) {
4944 authorizedCallback(true);
4947 process.on("message", authMessageListener);
4948 process.send("\x12AUTHQ" + reqip);
4951 redirectTrailingSlashes(function () {
4952 modExecute(mods, vres);
4959 callServerError(500, err);
4964 function serverErrorHandler(err, isRedirect) {
4965 if(isRedirect) attmtsRedir--;
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.");
4972 process.send("\x12ERRLIST" + (isRedirect ? attmtsRedir : attmts) + err.code);
4974 // Probably main process exited
4977 if ((isRedirect ? attmtsRedir : attmts) > 0) {
4978 (isRedirect ? server2 : server).close();
4979 setTimeout(start, 900);
4982 if (cluster.isPrimary !== undefined) process.send("\x12ERRCRASH" + err.code);
4984 // Probably main process exited
4986 setTimeout(function () {
4987 var errno = errors[err.code];
4988 process.exit(errno ? errno : 1);
4993 server.on("error", function (err) {
4994 serverErrorHandler(err, false);
4996 server.on("listening", function () {
5002 var closedMaster = true;
5004 // IPC listener for server listening signalization
5005 function listenConnListener(msg) {
5006 if (msg == "\x12LISTEN") {
5011 // IPC listener for brue force protection
5012 function bruteForceListenerWrapper(worker) {
5013 return function bruteForceListener(message) {
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] = {
5021 worker.send("\x14AUTHA" + ip);
5023 worker.send("\x14AUTHD" + ip);
5025 } else if (message.substring(0, 6) == "\x12AUTHR") {
5026 ip = message.substring(6);
5027 if (bruteForceDb[ip]) bruteForceDb[ip] = {
5030 } else if (message.substring(0, 6) == "\x12AUTHW") {
5031 ip = message.substring(6);
5032 if (!bruteForceDb[ip]) bruteForceDb[ip] = {
5035 bruteForceDb[ip].invalidAttempts++;
5036 if (bruteForceDb[ip].invalidAttempts >= 10) {
5037 bruteForceDb[ip].lastAttemptDate = new Date();
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);
5055 if (msg == "\x12CLOSE") {
5056 closedMaster = true;
5057 } else if (msg == "\x12LISTEN" || msg.substring(0, 4) == "\x12AUTH") {
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.");
5077 if (msg.length >= 9 && msg.indexOf("\x12ERRCRASH") == 0) {
5078 var errno = errors[msg.substring(9)];
5079 process.exit(errno ? errno : 1);
5083 serverconsole.climessage(msg);
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");
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}$/));
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)));
5110 serverconsole.locmessage("* " + sport); // Unix socket or Windows named pipe
5113 if (!(secure && disableNonEncryptedServer) && (listenToLocalhost || listenToAny)) {
5114 if (typeof port === "number") {
5115 serverconsole.locmessage("* http://localhost" + (port == 80 ? "" : (":" + port)));
5117 serverconsole.locmessage("* " + port); // Unix socket or Windows named pipe
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 () {
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)));
5128 if (secure && !sListenToLocalhost) serverconsole.locmessage("* https://" + domain + (spubport == 443 ? "" : (":" + spubport)));
5129 if (!(secure && disableNonEncryptedServer) && !listenToLocalhost) serverconsole.locmessage("* http://" + domain + (pubport == 80 ? "" : (":" + pubport)));
5131 serverconsole.locmessage("For CLI help, you can type \"help\"");
5135 function start(init) {
5136 init = Boolean(init);
5137 if (cluster.isPrimary || cluster.isPrimary === undefined) {
5139 for (i = 0; i < logo.length; i++) console.log(logo[i]); // Print logo
5141 console.log("Welcome to \x1b[1mSVR.JS - a web server running on Node.JS\x1b[0m");
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.");
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.");
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.");
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");
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));
5177 serverconsole.locwarnmessage("There was a problem while loading server-side JavaScript.");
5178 serverconsole.locwarnmessage("Stack:");
5179 serverconsole.locwarnmessage(generateErrorStack(SSJSError));
5181 if (SSJSError || modLoadingErrors.length > 0) console.log();
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);
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.");
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.");
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() + "...");
5214 if (!cluster.isPrimary) {
5216 if (typeof (secure ? sport : port) == "number" && (secure ? sListenAddress : listenAddress)) {
5217 server.listen(secure ? sport : port, secure ? sListenAddress : listenAddress);
5219 server.listen(secure ? sport : port);
5222 if (err.code != "ERR_SERVER_ALREADY_LISTEN") throw err;
5224 if (secure && !disableNonEncryptedServer) {
5226 if (typeof port == "number" && listenAddress) {
5227 server2.listen(port, listenAddress);
5229 server2.listen(port);
5232 if (err.code != "ERR_SERVER_ALREADY_LISTEN") throw err;
5239 close: function () {
5242 if (secure && !disableNonEncryptedServer) {
5245 if (cluster.isPrimary === undefined) serverconsole.climessage("Server closed.");
5247 process.send("Server closed.");
5248 process.send("\x12CLOSE");
5251 if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot close server! Reason: " + err.message);
5252 else process.send("Cannot close server! Reason: " + err.message);
5257 if (typeof (secure ? sport : port) == "number" && (secure ? sListenAddress : listenAddress)) {
5258 server.listen(secure ? sport : port, secure ? sListenAddress : listenAddress);
5260 server.listen(secure ? sport : port);
5262 if (secure && !disableNonEncryptedServer) {
5263 if (typeof port == "number" && listenAddress) {
5264 server2.listen(port, listenAddress);
5266 server2.listen(port);
5269 if (cluster.isPrimary === undefined) serverconsole.climessage("Server opened.");
5271 process.send("Server opened.");
5274 if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot open server! Reason: " + err.message);
5275 else process.send("Cannot open server! Reason: " + err.message);
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(" "));
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);
5289 if (modInfos.length == 0) {
5290 if (cluster.isPrimary === undefined) serverconsole.climessage("No mods installed.");
5291 else process.send("No mods installed.");
5294 stop: function (retcode) {
5295 reallyExiting = true;
5296 clearInterval(passwordHashCacheIntervalId);
5297 if ((!cluster.isPrimary && cluster.isPrimary !== undefined) && server.listening) {
5299 server.close(function () {
5300 if (server2.listening) {
5302 server2.close(function () {
5303 if (!process.removeFakeIPC) {
5304 if (typeof retcode == "number") {
5305 process.exit(retcode);
5312 if (!process.removeFakeIPC) {
5313 if (typeof retcode == "number") {
5314 process.exit(retcode);
5321 if (!process.removeFakeIPC) {
5322 if (typeof retcode == "number") {
5323 process.exit(retcode);
5331 if (typeof retcode == "number") {
5332 process.exit(retcode);
5337 if (process.removeFakeIPC) process.removeFakeIPC();
5339 if (typeof retcode == "number") {
5340 process.exit(retcode);
5346 clear: function () {
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.");
5354 for (var i = 0; i < ip.length; i++) {
5355 if (ip[i] != "localhost" && ip[i].indexOf(":") == -1) {
5356 ip[i] = "::ffff:" + ip[i];
5358 if (!blocklist.check(ip[i])) {
5359 blocklist.add(ip[i]);
5362 if (cluster.isPrimary === undefined) serverconsole.climessage("IPs successfully blocked.");
5363 else if (!cluster.isPrimary) process.send("IPs successfully blocked.");
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.");
5371 for (var i = 0; i < ip.length; i++) {
5372 if (ip[i].indexOf(":") == -1) {
5373 ip[i] = "::ffff:" + ip[i];
5375 blocklist.remove(ip[i]);
5377 if (cluster.isPrimary === undefined) serverconsole.climessage("IPs successfully unblocked.");
5378 else if (!cluster.isPrimary) process.send("IPs successfully unblocked.");
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.");
5389 if (cluster.isPrimary === undefined) {
5390 setInterval(function () {
5393 serverconsole.locmessage("Configuration saved.");
5395 throw new Error(err);
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) {
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);
5418 goodWorkers.push(allClusters[_id]);
5419 checkWorker(callback, _id + 1);
5423 checkWorker(callback, _id + 1);
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);
5431 checkWorker(callback, _id + 1);
5434 checkWorker(function () {
5435 var wN = Math.floor(Math.random() * goodWorkers.length); //Send a configuration saving message to a random worker.
5437 if (cluster.workers[goodWorkers[wN]]) {
5438 isWorkerHungUpBuff2 = true;
5439 cluster.workers[goodWorkers[wN]].on("message", msgListener);
5440 cluster.workers[goodWorkers[wN]].send("\x14SAVECONF");
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);
5448 serverconsole.locwarnmessage("There was a problem while saving configuration file. Reason: " + err.message);
5453 if (!cluster.isPrimary) {
5454 passwordHashCacheIntervalId = setInterval(function () {
5455 pbkdf2Cache = pbkdf2Cache.filter(function (entry) {
5456 return entry.addDate > (new Date() - 3600000);
5458 scryptCache = scryptCache.filter(function (entry) {
5459 return entry.addDate > (new Date() - 3600000);
5463 if (!cluster.isPrimary && cluster.isPrimary !== undefined) {
5464 process.on("message", function (line) {
5468 process.send("\x12END");
5469 } else if (line == "\x14SAVECONF") {
5470 // Save configuration file
5473 process.send("\x12SAVEGOOD");
5475 process.send("\x12SAVEERR" + err.message);
5477 process.send("\x12END");
5478 } else if (line == "\x14KILLPING") {
5479 if (!reallyExiting) {
5480 process.send("\x12KILLOK");
5481 process.send("\x12END");
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");
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);
5495 reqcounterKillReq = reqcounter;
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");
5503 process.send("Unrecognized command \"" + line.split(" ")[0] + "\".");
5504 process.send("\x12END");
5508 process.send("Can't execute command \"" + line.split(" ")[0] + "\".");
5509 process.send("\x12END");
5514 var rla = readline.createInterface({
5515 input: process.stdin,
5516 output: process.stdout,
5520 rla.on("line", function (line) {
5522 var argss = line.split(" ");
5523 var command = argss.shift();
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;
5532 for (var i = 0; i < allClusters.length; i++) {
5534 if (cluster.workers[allClusters[i]]) {
5535 cluster.workers[allClusters[i]].kill();
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;
5547 var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; // 1 core deleted for safety...
5548 if (cpus > useAvailableCores) cpus = useAvailableCores;
5550 // Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working.
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++) {
5557 setTimeout((function (i) {
5558 return function () {
5560 if (i >= cpus - 1) {
5561 SVRJSInitialized = true;
5563 serverconsole.climessage("SVR.JS workers restarted.");
5571 if (command == "stop") {
5573 allClusters = Object.keys(cluster.workers);
5575 allClusters.forEach(function (clusterID) {
5577 if (cluster.workers[clusterID]) {
5578 cluster.workers[clusterID].on("message", msgListener);
5579 cluster.workers[clusterID].send(line);
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);
5587 serverconsole.climessage("Can't run command \"" + command + "\".");
5590 if (command == "stop") {
5591 setTimeout(function () {
5592 reallyExiting = true;
5597 if (command == "stop") {
5598 reallyExiting = true;
5602 commands[command](argss);
5604 serverconsole.climessage("Unrecognized command \"" + command + "\".");
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;
5618 var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; // 1 core deleted for safety...
5619 if (cpus > useAvailableCores) cpus = useAvailableCores;
5621 // Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working.
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++) {
5628 setTimeout((function (i) {
5629 return function () {
5631 if (i >= cpus - 1) SVRJSInitialized = true;
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.");
5643 if (msg.length >= 9 && msg.indexOf("\x12ERRCRASH") == 0) {
5644 var errno = errors[msg.substring(9)];
5645 process.exit(errno ? errno : 1);
5649 // Hangup check and restart
5650 setInterval(function () {
5651 if (!closedMaster && !exiting) {
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,
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")
5663 rejectUnauthorized: false
5665 chksocket.removeAllListeners("timeout");
5667 res.on("data", function () {});
5668 res.on("end", function () {});
5670 }).on("error", function () {
5672 if (!crashed) SVRJSFork();
5673 else crashed = false;
5675 }).on("timeout", function () {
5676 if (!exiting) SVRJSFork();
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 + "]";
5685 var connection = http2.connect("http://" + address + ":" + port.toString());
5686 connection.on("error", function () {
5688 if (!crashed) SVRJSFork();
5689 else crashed = false;
5692 connection.setTimeout(1620, function () {
5693 if (!exiting) SVRJSFork();
5696 chksocket = connection.request({
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")
5701 chksocket.on("response", function () {
5705 chksocket.on("error", function () {
5707 if (!crashed) SVRJSFork();
5708 else crashed = false;
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,
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")
5722 chksocket.removeAllListeners("timeout");
5724 res.on("data", function () {});
5725 res.on("end", function () {});
5727 }).on("error", function () {
5729 if (!crashed) SVRJSFork();
5730 else crashed = false;
5732 }).on("timeout", function () {
5733 if (!exiting) SVRJSFork();
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) {
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);
5766 goodWorkers.push(allClusters[_id]);
5767 checkWorker(callback, _id + 1);
5771 checkWorker(callback, _id + 1);
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);
5779 checkWorker(callback, _id + 1);
5782 checkWorker(function () {
5783 if (goodWorkers.length > minClusters) {
5784 var wN = Math.floor(Math.random() * goodWorkers.length);
5785 if (wN == goodWorkers.length) return;
5787 if (cluster.workers[goodWorkers[wN]]) {
5788 isWorkerHungUpBuff = true;
5789 cluster.workers[goodWorkers[wN]].on("message", msgListener);
5790 cluster.workers[goodWorkers[wN]].send("\x14KILLREQ");
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);
5798 serverconsole.locwarnmessage("There was a problem while terminating unused worker process. Reason: " + err.message);
5811 // Save configuration file
5812 function saveConfig() {
5813 for (var i = 0; i < 3; i++) {
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 = [];
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;
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);
5866 if (i >= 2) throw err;
5867 var now = Date.now();
5868 while (Date.now() - now < 2);
5873 // Process event listeners
5874 if (cluster.isPrimary || cluster.isPrimary === undefined) {
5875 process.on("uncaughtException", function (err) {
5877 serverconsole.locerrmessage("SVR.JS master process just crashed!!!");
5878 serverconsole.locerrmessage("Stack:");
5879 serverconsole.locerrmessage(generateErrorStack(err));
5880 process.exit(err.errno);
5882 process.on("unhandledRejection", function (err) {
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);
5889 process.on("exit", function (code) {
5891 if (!configJSONRErr && !configJSONPErr) {
5895 serverconsole.locwarnmessage("There was a problem while saving configuration file. Reason: " + err.message);
5898 deleteFolderRecursive(__dirname + "/temp");
5903 fs.mkdirSync(__dirname + "/temp");
5907 if (process.isBun && process.versions.bun && process.versions.bun[0] == "0") {
5909 fs.writeFileSync(__dirname + "/temp/serverSideScript.js", "// Placeholder server-side JavaScript to workaround Bun bug.\r\n");
5914 serverconsole.locmessage("Server closed with exit code: " + code);
5916 process.on("warning", function (warning) {
5917 serverconsole.locwarnmessage(warning.message);
5918 if (generateErrorStack(warning)) {
5919 serverconsole.locwarnmessage("Stack:");
5920 serverconsole.locwarnmessage(generateErrorStack(warning));
5923 process.on("SIGINT", function () {
5924 reallyExiting = true;
5925 if (cluster.isPrimary !== undefined) {
5927 var allClusters = Object.keys(cluster.workers);
5928 for (var i = 0; i < allClusters.length; i++) {
5930 if (cluster.workers[allClusters[i]]) {
5931 cluster.workers[allClusters[i]].send("stop");
5934 // Worker will crash with EPIPE anyway.
5938 serverconsole.locmessage("Server terminated using SIGINT");
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);
5950 process.on("uncaughtException", crashHandler);
5951 process.on("unhandledRejection", crashHandler);
5954 process.on("warning", function (warning) {
5955 serverconsole.locwarnmessage(warning.message);
5956 if (warning.stack) {
5957 serverconsole.locwarnmessage("Stack:");
5958 serverconsole.locwarnmessage(generateErrorStack(warning));
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);