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(("<!DOCTYPE html
><html
><head
><title
>{errorMessage
}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /><style
>html
{background
-color
:#dfffdf
;color
:#000000;font
-family
:FreeSans
, Helvetica
, Tahoma
, Verdana
, Arial
, sans
-serif
;margin
:0.75em
}body
{background
-color
:#ffffff
;padding
:0.5em
0.5em
0.1em
;margin
:0.5em auto
;width
:90%;max
-width
:800px
;-webkit
-box
-shadow
:0 5px
10px
0 rgba(0, 0, 0, 0.15);-moz
-box
-shadow
:0 5px
10px
0 rgba(0, 0, 0, 0.15);box
-shadow
:0 5px
10px
0 rgba(0, 0, 0, 0.15)}h1
{text
-align
:center
;font
-size
:2.25em
;margin
:0.3em
0 0.5em
}code
{background
-color
:#dfffdf
;-webkit
-box
-shadow
:0 2px
4px
0 rgba(0, 0, 0, 0.1);-moz
-box
-shadow
:0 2px
4px
0 rgba(0, 0, 0, 0.1);box
-shadow
:0 2px
4px
0 rgba(0, 0, 0, 0.1);display
:block
;padding
:0.2em
;font
-family
:\"DejaVu Sans Mono
\", \"Bitstream Vera Sans Mono
\", Hack
, Menlo
, Consolas
, Monaco
, monospace
;font
-size
:0.85em
;margin
:auto
;width
:95%;max
-width
:600px
}table
{width
:95%;border
-collapse
:collapse
;margin
:auto
;overflow
-wrap
:break-word
;word
-wrap
:break-word
;word
-break:break-all
;word
-break:break-word
;position
:relative
;z
-index
:0}table tbody
{background
-color
:#ffffff
;color
:#000000}table tbody
:after
{-webkit
-box
-shadow
:0 4px
8px
0 rgba(0, 0, 0, 0.175);-moz
-box
-shadow
:0 4px
8px
0 rgba(0, 0, 0, 0.175);box
-shadow
:0 4px
8px
0 rgba(0, 0, 0, 0.175);content
:' ';position
:absolute
;top
:0;left
:0;right
:0;bottom
:0;z
-index
:-1}table img
{margin
:0;display
:inline
}th
,tr
{padding
:0.15em
;text
-align
:center
}th
{background
-color
:#007000;color
:#ffffff
}th a
{color
:#ffffff
}td
,th
{padding
:0.225em
}td
{text
-align
:left
}tr
:nth
-child(odd
){background
-color
:#dfffdf
}hr
{color
:#ffffff
}@media screen
and (prefers
-color
-scheme
: dark
){html
{background
-color
:#002000;color
:#ffffff
}body
{background
-color
:#000f00
;-webkit
-box
-shadow
:0 5px
10px
0 rgba(127, 127, 127, 0.15);-moz
-box
-shadow
:0 5px
10px
0 rgba(127, 127, 127, 0.15);box
-shadow
:0 5px
10px
0 rgba(127, 127, 127, 0.15)}code
{background
-color
:#002000;-webkit
-box
-shadow
:0 2px
4px
0 rgba(127, 127, 127, 0.1);-moz
-box
-shadow
:0 2px
4px
0 rgba(127, 127, 127, 0.1);box
-shadow
:0 2px
4px
0 rgba(127, 127, 127, 0.1)}a
{color
:#ffffff
}a
:hover
{color
:#00ff00
}table tbody
{background
-color
:#000f00
;color
:#ffffff
}table tbody
:after
{-webkit
-box
-shadow
:0 4px
8px
0 rgba(127, 127, 127, 0.175);-moz
-box
-shadow
:0 4px
8px
0 rgba(127, 127, 127, 0.175);box
-shadow
:0 4px
8px
0 rgba(127, 127, 127, 0.175)}tr
:nth
-child(odd
){background
-color
:#002000}}</style></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(("<!DOCTYPE html><html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /><style>html{background-color:#dfffdf;color:#000000;font-family:FreeSans, Helvetica, Tahoma, Verdana, Arial, sans-serif;margin:0.75em}body{background-color:#ffffff;padding:0.5em 0.5em 0.1em;margin:0.5em auto;width:90%;max-width:800px;-webkit-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15)}h1{text-align:center;font-size:2.25em;margin:0.3em 0 0.5em}code{background-color:#dfffdf;-webkit-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);display:block;padding:0.2em;font-family:\"DejaVu Sans Mono\", \"Bitstream Vera Sans Mono\", Hack, Menlo, Consolas, Monaco, monospace;font-size:0.85em;margin:auto;width:95%;max-width:600px}table{width:95%;border-collapse:collapse;margin:auto;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;word-break:break-word;position:relative;z-index:0}table tbody{background-color:#ffffff;color:#000000}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);content:' ';position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1}table img{margin:0;display:inline}th,tr{padding:0.15em;text-align:center}th{background-color:#007000;color:#ffffff}th a{color:#ffffff}td,th{padding:0.225em}td{text-align:left}tr:nth-child(odd){background-color:#dfffdf}hr{color:#ffffff}@media screen and (prefers-color-scheme: dark){html{background-color:#002000;color:#ffffff}body{background-color:#000f00;-webkit-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15)}code{background-color:#002000;-webkit-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1)}a{color:#ffffff}a:hover{color:#00ff00}table tbody{background-color:#000f00;color:#ffffff}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175)}tr:nth-child(odd){background-color:#002000}}</style></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 head
= fs
.existsSync("./.head") ? fs
.readFileSync("./.head").toString() : (fs
.existsSync("./head.html") ? fs
.readFileSync("./head.html").toString() : ""); // header
2666 foot
= fs
.existsSync("./.foot") ? fs
.readFileSync("./.foot").toString() : (fs
.existsSync("./foot.html") ? fs
.readFileSync("./foot.html").toString() : ""); // footer
2668 if ((err
.code
&& (err
.code
.indexOf("ERR_SSL_") == 0 || err
.code
.indexOf("ERR_TLS_") == 0)) || (!err
.code
&& err
.message
.indexOf("SSL routines") != -1)) {
2669 if (err
.code
== "ERR_SSL_HTTP_REQUEST" || err
.message
.indexOf("http request") != -1) {
2670 serverconsole
.errmessage("Client sent HTTP request to HTTPS port.");
2671 callServerError(497);
2674 serverconsole
.errmessage("An SSL error occured: " + (err
.code
? err
.code
: err
.message
));
2675 callServerError(400);
2680 if (err
.code
&& err
.code
.indexOf("ERR_HTTP2_") == 0) {
2681 serverconsole
.errmessage("An HTTP/2 error occured: " + err
.code
);
2682 callServerError(400);
2686 if (err
.code
&& err
.code
== "ERR_HTTP_REQUEST_TIMEOUT") {
2687 serverconsole
.errmessage("Client timed out.");
2688 callServerError(408);
2692 if (!err
.rawPacket
) {
2693 serverconsole
.errmessage("Connection ended prematurely.");
2694 callServerError(400);
2698 var packetLines
= err
.rawPacket
.toString().split("\r\n");
2699 if (packetLines
.length
== 0) {
2700 serverconsole
.errmessage("Invalid request.");
2701 callServerError(400);
2705 function checkHeaders(beginsFromFirst
) {
2706 for (var i
= (beginsFromFirst
? 0 : 1); i
< packetLines
.length
; i
++) {
2707 var header
= packetLines
[i
];
2708 if (header
== "") return false; // Beginning of body
2709 else if (header
.indexOf(":") < 1) {
2710 serverconsole
.errmessage("Invalid header.");
2711 callServerError(400);
2713 } else if (header
.length
> 8192) {
2714 serverconsole
.errmessage("Header too large.");
2715 callServerError(431); // Headers too large
2721 var packetLine1
= packetLines
[0].split(" ");
2723 var httpVersion
= "HTTP/1.1";
2724 if (String(packetLine1
[0]).indexOf(":") > 0) {
2725 if (!checkHeaders(true)) {
2726 serverconsole
.errmessage("The request is invalid (it may be a part of larger invalid request).");
2727 callServerError(400); // Also malformed Packet
2731 if (String(packetLine1
[0]).length
< 50) method
= packetLine1
.shift();
2732 if (String(packetLine1
[packetLine1
.length
- 1]).length
< 50) httpVersion
= packetLine1
.pop();
2733 if (packetLine1
.length
!= 1) {
2734 serverconsole
.errmessage("The head of request is invalid.");
2735 callServerError(400); // Malformed Packet
2736 } else if (!httpVersion
.toString().match(/^HTTP[\/]/i)) {
2737 serverconsole
.errmessage("Invalid protocol.");
2738 callServerError(400); // bad protocol version
2739 } else if (http
.METHODS
.indexOf(method
) == -1) {
2740 serverconsole
.errmessage("Invalid method.");
2741 callServerError(405); // Also malformed Packet
2743 if (checkHeaders(false)) return;
2744 if (packetLine1
[0].length
> 255) {
2745 serverconsole
.errmessage("URI too long.");
2746 callServerError(414); // Also malformed Packet
2748 serverconsole
.errmessage("The request is invalid.");
2749 callServerError(400); // Also malformed Packet
2753 serverconsole
.errmessage("There was an error while determining type of malformed request.");
2754 callServerError(400);
2758 function connhandler(request
, socket
, head
) {
2759 var reqIdInt
= Math
.floor(Math
.random() * 16777216);
2760 if (reqIdInt
== 16777216) reqIdInt
= 0;
2761 var reqId
= "0".repeat(6 - reqIdInt
.toString(16).length
) + reqIdInt
.toString(16);
2762 var serverconsole
= {
2763 climessage: function (msg
) {
2764 if (msg
.indexOf("\n") != -1) {
2765 msg
.split("\n").forEach(function (nmsg
) {
2766 serverconsole
.climessage(nmsg
);
2770 console
.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
);
2771 LOG("SERVER CLI MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2774 reqmessage: function (msg
) {
2775 if (msg
.indexOf("\n") != -1) {
2776 msg
.split("\n").forEach(function (nmsg
) {
2777 serverconsole
.reqmessage(nmsg
);
2781 console
.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[37m\x1b[0m");
2782 LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2785 resmessage: function (msg
) {
2786 if (msg
.indexOf("\n") != -1) {
2787 msg
.split("\n").forEach(function (nmsg
) {
2788 serverconsole
.resmessage(nmsg
);
2792 console
.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[37m\x1b[0m");
2793 LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2796 errmessage: function (msg
) {
2797 if (msg
.indexOf("\n") != -1) {
2798 msg
.split("\n").forEach(function (nmsg
) {
2799 serverconsole
.errmessage(nmsg
);
2803 console
.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[37m\x1b[0m");
2804 LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2807 locerrmessage: function (msg
) {
2808 if (msg
.indexOf("\n") != -1) {
2809 msg
.split("\n").forEach(function (nmsg
) {
2810 serverconsole
.locerrmessage(nmsg
);
2814 console
.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[40m\x1b[0m");
2815 LOG("SERVER ERROR MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2818 locwarnmessage: function (msg
) {
2819 if (msg
.indexOf("\n") != -1) {
2820 msg
.split("\n").forEach(function (nmsg
) {
2821 serverconsole
.locwarnmessage(nmsg
);
2825 console
.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[40m\x1b[0m");
2826 LOG("SERVER WARNING MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2829 locmessage: function (msg
) {
2830 if (msg
.indexOf("\n") != -1) {
2831 msg
.split("\n").forEach(function (nmsg
) {
2832 serverconsole
.locmessage(nmsg
);
2836 console
.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
);
2837 LOG("SERVER MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2842 socket
.on("close", function (hasError
) {
2843 if (!hasError
) serverconsole
.locmessage("Client disconnected.");
2844 else serverconsole
.locmessage("Client disconnected due to error.");
2846 socket
.on("error", function () {});
2848 var reqip
= socket
.remoteAddress
;
2849 var reqport
= socket
.remotePort
;
2851 serverconsole
.locmessage("Somebody connected to " + (secure
? ((typeof sport
== "number" ? "port " : "socket ") + sport
) : ((typeof port
== "number" ? "port " : "socket ") + port
)) + "...");
2852 serverconsole
.reqmessage("Client " + ((!reqip
|| reqip
== "") ? "[unknown client]" : (reqip
+ ((reqport
&& reqport
!== 0) && reqport
!= "" ? ":" + reqport
: ""))) + " wants to proxy " + request
.url
+ " through this server");
2853 if (request
.headers
["user-agent"] != undefined) serverconsole
.reqmessage("Client uses " + request
.headers
["user-agent"]);
2855 function modExecute(mods
, ffinals
) {
2857 mods
.forEach(function (mod
) {
2858 if (mod
.proxyCallback
!== undefined) proxyMods
.push(mod
);
2861 var modFunction
= ffinals
;
2862 proxyMods
.reverse().forEach(function (proxyMod
) {
2863 modFunction
= proxyMod
.proxyCallback(req
, socket
, head
, configJSON
, serverconsole
, modFunction
);
2869 serverconsole
.errmessage("SVR.JS doesn't support proxy without proxy mod.");
2870 if (!socket
.destroyed
) socket
.end("HTTP/1.1 501 Not Implemented\n\n");
2872 modExecute(mods
, vres
);
2875 function reqhandler(req
, res
, fromMain
) {
2876 if (fromMain
=== undefined) fromMain
= true;
2877 var reqIdInt
= Math
.floor(Math
.random() * 16777216);
2878 if (reqIdInt
== 16777216) reqIdInt
= 0;
2879 var reqId
= "0".repeat(6 - reqIdInt
.toString(16).length
) + reqIdInt
.toString(16);
2880 var serverconsole
= {
2881 climessage: function (msg
) {
2882 if (msg
.indexOf("\n") != -1) {
2883 msg
.split("\n").forEach(function (nmsg
) {
2884 serverconsole
.climessage(nmsg
);
2888 console
.log("\x1b[1mSERVER CLI MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
);
2889 LOG("SERVER CLI MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2892 reqmessage: function (msg
) {
2893 if (msg
.indexOf("\n") != -1) {
2894 msg
.split("\n").forEach(function (nmsg
) {
2895 serverconsole
.reqmessage(nmsg
);
2899 console
.log("\x1b[34m\x1b[1mSERVER REQUEST MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[37m\x1b[0m");
2900 LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2903 resmessage: function (msg
) {
2904 if (msg
.indexOf("\n") != -1) {
2905 msg
.split("\n").forEach(function (nmsg
) {
2906 serverconsole
.resmessage(nmsg
);
2910 console
.log("\x1b[32m\x1b[1mSERVER RESPONSE MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[37m\x1b[0m");
2911 LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2914 errmessage: function (msg
) {
2915 if (msg
.indexOf("\n") != -1) {
2916 msg
.split("\n").forEach(function (nmsg
) {
2917 serverconsole
.errmessage(nmsg
);
2921 console
.log("\x1b[31m\x1b[1mSERVER RESPONSE ERROR MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[37m\x1b[0m");
2922 LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2925 locerrmessage: function (msg
) {
2926 if (msg
.indexOf("\n") != -1) {
2927 msg
.split("\n").forEach(function (nmsg
) {
2928 serverconsole
.locerrmessage(nmsg
);
2932 console
.log("\x1b[41m\x1b[1mSERVER ERROR MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[40m\x1b[0m");
2933 LOG("SERVER ERROR MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2936 locwarnmessage: function (msg
) {
2937 if (msg
.indexOf("\n") != -1) {
2938 msg
.split("\n").forEach(function (nmsg
) {
2939 serverconsole
.locwarnmessage(nmsg
);
2943 console
.log("\x1b[43m\x1b[1mSERVER WARNING MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
+ "\x1b[40m\x1b[0m");
2944 LOG("SERVER WARNING MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2947 locmessage: function (msg
) {
2948 if (msg
.indexOf("\n") != -1) {
2949 msg
.split("\n").forEach(function (nmsg
) {
2950 serverconsole
.locmessage(nmsg
);
2954 console
.log("\x1b[1mSERVER MESSAGE\x1b[22m [Request Id: " + reqId
+ "]: " + msg
);
2955 LOG("SERVER MESSAGE [Request Id: " + reqId
+ "]: " + msg
);
2960 function matchHostname(hostname
) {
2961 if (typeof hostname
== "undefined" || hostname
== "*") {
2963 } else if (req
.headers
.host
&& hostname
.indexOf("*.") == 0 && hostname
!= "*.") {
2964 var hostnamesRoot
= hostname
.substring(2);
2965 if (req
.headers
.host
== hostnamesRoot
|| (req
.headers
.host
.length
> hostnamesRoot
.length
&& req
.headers
.host
.indexOf("." + hostnamesRoot
) == req
.headers
.host
.length
- hostnamesRoot
.length
- 1)) {
2968 } else if (req
.headers
.host
&& req
.headers
.host
== hostname
) {
2974 function getCustomHeaders() {
2975 var ph
= JSON
.parse(JSON
.stringify(customHeaders
));
2976 if (configJSON
.customHeadersVHost
) {
2978 configJSON
.customHeadersVHost
.every(function (vhost
) {
2979 if (matchHostname(vhost
.host
) && ipMatch(vhost
.ip
, req
.socket
? req
.socket
.localAddress
: undefined)) {
2986 if (vhostP
&& vhostP
.headers
) {
2987 var phNu
= JSON
.parse(JSON
.stringify(vhostP
.headers
));
2988 Object
.keys(phNu
).forEach(function (phNuK
) {
2989 ph
[phNuK
] = phNu
[phNuK
];
2993 Object
.keys(ph
).forEach(function (phk
) {
2994 if (typeof ph
[phk
] == "string") ph
[phk
] = ph
[phk
].replace(/\{path\}/g, req
.url
);
2999 // Make HTTP/1.x API-based scripts compatible with HTTP/2.0 API
3000 if (configJSON
.enableHTTP2
== true && req
.httpVersion
== "2.0") {
3001 // Set HTTP/1.x methods (to prevent process warnings)
3002 res
.writeHeadNodeApi
= res
.writeHead
;
3003 res
.setHeaderNodeApi
= res
.setHeader
;
3005 res
.writeHead = function (a
, b
, c
) {
3007 if (typeof (b
) == "object") table
= b
;
3008 if (table
== undefined) table
= this.tHeaders
;
3009 if (table
== undefined) table
= {};
3010 table
= JSON
.parse(JSON
.stringify(table
));
3011 Object
.keys(table
).forEach(function (key
) {
3012 var al
= key
.toLowerCase();
3013 if (al
== "transfer-encoding" || al
== "connection" || al
== "keep-alive" || al
== "upgrade") delete table
[key
];
3015 if (res
.stream
&& res
.stream
.destroyed
) {
3018 return res
.writeHeadNodeApi(a
, table
);
3021 res
.setHeader = function (headerName
, headerValue
) {
3022 var al
= headerName
.toLowerCase();
3023 if (al
!= "transfer-encoding" && al
!= "connection" && al
!= "keep-alive" && al
!= "upgrade") return res
.setHeaderNodeApi(headerName
, headerValue
);
3027 // Set HTTP/1.x headers
3028 if (!req
.headers
.host
) req
.headers
.host
= req
.headers
[":authority"];
3029 if (!req
.url
) req
.url
= req
.headers
[":path"];
3030 if (!req
.protocol
) req
.protocol
= req
.headers
[":scheme"];
3031 if (!req
.method
) req
.method
= req
.headers
[":method"];
3032 if (req
.headers
[":path"] == undefined || req
.headers
[":method"] == undefined) {
3033 var err
= new Error("Either \":path\" or \":method\" pseudoheader is missing.");
3034 if(Buffer
.alloc
) err
.rawPacket
= Buffer
.alloc(0);
3035 reqerrhandler(err
, req
.socket
, fromMain
);
3039 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
)) {
3040 var headers
= getCustomHeaders();
3041 res
.writeHead(204, http
.STATUS_CODES
[204], headers
);
3046 req
.url
= fixNodeMojibakeURL(req
.url
);
3048 var headWritten
= false;
3049 var lastStatusCode
= null;
3050 res
.writeHeadNative
= res
.writeHead
;
3051 res
.writeHead = function (code
, codeDescription
, headers
) {
3052 if (!(headWritten
&& process
.isBun
&& code
=== lastStatusCode
&& codeDescription
=== undefined && codeDescription
=== undefined)) {
3054 process
.emitWarning("res.writeHead called multiple times.", {
3055 code
: "WARN_SVRJS_MULTIPLE_WRITEHEAD"
3061 if (code
>= 400 && code
<= 599) {
3062 if (code
>= 400 && code
<= 499) err4xxcounter
++;
3063 else if (code
>= 500 && code
<= 599) err5xxcounter
++;
3064 serverconsole
.errmessage("Server responded with " + code
.toString() + " code.");
3066 serverconsole
.resmessage("Server responded with " + code
.toString() + " code.");
3068 if (typeof codeDescription
!= "string" && http
.STATUS_CODES
[code
]) {
3069 if (!headers
) headers
= codeDescription
;
3070 codeDescription
= http
.STATUS_CODES
[code
];
3072 lastStatusCode
= code
;
3074 res
.writeHeadNative(code
, codeDescription
, headers
);
3077 var finished
= false;
3078 res
.on("finish", function () {
3081 serverconsole
.locmessage("Client disconnected.");
3084 res
.on("close", function () {
3087 serverconsole
.locmessage("Client disconnected.");
3090 var isProxy
= false;
3091 if (req
.url
[0] != "/" && req
.url
!= "*") isProxy
= true;
3092 serverconsole
.locmessage("Somebody connected to " + (secure
&& fromMain
? ((typeof sport
== "number" ? "port " : "socket ") + sport
) : ((typeof port
== "number" ? "port " : "socket ") + port
)) + "...");
3094 if (req
.socket
== null) {
3095 serverconsole
.errmessage("Client socket is null!!!");
3099 // Set up X-Forwarded-For
3100 var reqip
= req
.socket
.remoteAddress
;
3101 var reqport
= req
.socket
.remotePort
;
3104 var isForwardedValid
= true;
3105 if (enableIPSpoofing
) {
3106 if (req
.headers
["x-forwarded-for"] != undefined) {
3107 var preparedReqIP
= req
.headers
["x-forwarded-for"].split(",")[0].replace(/ /g
, "");
3108 var preparedReqIPvalid
= net
.isIP(preparedReqIP
);
3109 if (preparedReqIPvalid
) {
3110 if (preparedReqIPvalid
== 4 && req
.socket
.remoteAddress
&& req
.socket
.remoteAddress
.indexOf(":") > -1) preparedReqIP
= "::ffff:" + preparedReqIP
;
3111 reqip
= preparedReqIP
;
3114 oldport
= req
.socket
.remotePort
;
3115 oldip
= req
.socket
.remoteAddress
;
3116 req
.socket
.realRemotePort
= reqport
;
3117 req
.socket
.realRemoteAddress
= reqip
;
3118 req
.socket
.originalRemotePort
= oldport
;
3119 req
.socket
.originalRemoteAddress
= oldip
;
3120 res
.socket
.realRemotePort
= reqport
;
3121 res
.socket
.realRemoteAddress
= reqip
;
3122 res
.socket
.originalRemotePort
= oldport
;
3123 res
.socket
.originalRemoteAddress
= oldip
;
3125 // Address setting failed
3128 isForwardedValid
= false;
3135 // Process the Host header
3136 var oldHostHeader
= req
.headers
.host
;
3137 if (typeof req
.headers
.host
== "string") {
3138 req
.headers
.host
= req
.headers
.host
.toLowerCase();
3139 if (!req
.headers
.host
.match(/^\.+$/)) req
.headers
.host
= req
.headers
.host
.replace(/\.$/g, "");
3142 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
);
3143 if (req
.headers
["user-agent"] != undefined) serverconsole
.reqmessage("Client uses " + req
.headers
["user-agent"]);
3144 if (oldHostHeader
&& oldHostHeader
!= req
.headers
.host
) serverconsole
.resmessage("Host name rewritten: " + oldHostHeader
+ " => " + req
.headers
.host
);
3146 var acceptEncoding
= req
.headers
["accept-encoding"];
3147 if (!acceptEncoding
) acceptEncoding
= "";
3149 // Header and footer placeholders
3153 function responseEnd(body
) {
3154 // If body is Buffer, then it is converted to String anyway.
3155 res
.write(head
+ body
+ foot
);
3159 // Server error calling method
3160 function callServerError(errorCode
, extName
, stack
, ch
) {
3161 if (typeof errorCode
!== "number") {
3162 throw new TypeError("HTTP error code parameter needs to be an integer.");
3165 // Handle optional parameters
3166 if (extName
&& typeof extName
=== "object") {
3169 extName
= undefined;
3170 } else if (typeof extName
!== "string" && extName
!== null && extName
!== undefined) {
3171 throw new TypeError("Extension name parameter needs to be a string.");
3174 if (stack
&& typeof stack
=== "object" && Object
.prototype.toString
.call(stack
) !== "[object Error]") {
3177 } else if (typeof stack
!== "object" && typeof stack
!== "string" && stack
) {
3178 throw new TypeError("Error stack parameter needs to be either a string or an instance of Error object.");
3181 // Determine error file
3182 function getErrorFileName(list
, callback
, _i
) {
3183 function medCallback(p
) {
3186 if (errorCode
== 404) {
3187 fs
.access(page404
, fs
.constants
.F_OK
, function (err
) {
3189 fs
.access("." + errorCode
.toString(), fs
.constants
.F_OK
, function (err
) {
3192 callback(errorCode
.toString() + ".html");
3194 callback("." + errorCode
.toString());
3197 callServerError(500, err2
);
3204 callServerError(500, err2
);
3209 fs
.access("." + errorCode
.toString(), fs
.constants
.F_OK
, function (err
) {
3212 callback(errorCode
.toString() + ".html");
3214 callback("." + errorCode
.toString());
3217 callServerError(500, err2
);
3225 if (_i
>= list
.length
) {
3230 if (list
[_i
].scode
!= errorCode
|| !(matchHostname(list
[_i
].host
) && ipMatch(list
[_i
].ip
, req
.socket
? req
.socket
.localAddress
: undefined))) {
3231 getErrorFileName(list
, callback
, _i
+ 1);
3234 fs
.access(list
[_i
].path
, fs
.constants
.F_OK
, function (err
) {
3236 getErrorFileName(list
, callback
, _i
+ 1);
3238 medCallback(list
[_i
].path
);
3244 getErrorFileName(errorPages
, function (errorFile
) {
3245 // Generate error stack if not provided
3246 if (Object
.prototype.toString
.call(stack
) === "[object Error]") stack
= generateErrorStack(stack
);
3247 if (stack
=== undefined) stack
= generateErrorStack(new Error("Unknown error"));
3249 if (errorCode
== 500 || errorCode
== 502) {
3250 serverconsole
.errmessage("There was an error while processing the request!");
3251 serverconsole
.errmessage("Stack:");
3252 serverconsole
.errmessage(stack
);
3255 // Hide the error stack if specified
3256 if (stackHidden
) stack
= "[error stack hidden]";
3258 // Validate the error code and handle unknown codes
3259 if (serverHTTPErrorDescs
[errorCode
] === undefined) {
3260 callServerError(501, extName
, stack
);
3262 var cheaders
= getCustomHeaders();
3264 // Process custom headers if provided
3266 var chon
= Object
.keys(cheaders
);
3267 Object
.keys(ch
).forEach(function (chnS
) {
3269 for (var j
= 0; j
< chon
.length
; j
++) {
3270 if (chon
[j
].toLowerCase() == chnS
.toLowerCase()) {
3275 if (ch
[chnS
]) cheaders
[nhn
] = ch
[chnS
];
3279 cheaders
["Content-Type"] = "text/html; charset=utf-8";
3281 // Set default Allow header for 405 error if not provided
3282 if (errorCode
== 405 && !cheaders
["Allow"]) cheaders
["Allow"] = "GET, POST, HEAD, OPTIONS";
3284 // Read the error file and replace placeholders with error information
3285 fs
.readFile(errorFile
, function (err
, data
) {
3288 res
.writeHead(errorCode
, http
.STATUS_CODES
[errorCode
], cheaders
);
3289 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
3291 var additionalError
= 500;
3292 // Handle additional error cases
3293 if (err
.code
== "ENOENT") {
3294 additionalError
= 404;
3295 } else if (err
.code
== "ENOTDIR") {
3296 additionalError
= 404; // Assume that file doesn't exist
3297 } else if (err
.code
== "EACCES") {
3298 additionalError
= 403;
3299 } else if (err
.code
== "ENAMETOOLONG") {
3300 additionalError
= 414;
3301 } else if (err
.code
== "EMFILE") {
3302 additionalError
= 503;
3303 } else if (err
.code
== "ELOOP") {
3304 additionalError
= 508;
3307 res
.writeHead(errorCode
, http
.STATUS_CODES
[errorCode
], cheaders
);
3308 res
.write(("<!DOCTYPE html><html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /><style>html{background-color:#dfffdf;color:#000000;font-family:FreeSans, Helvetica, Tahoma, Verdana, Arial, sans-serif;margin:0.75em}body{background-color:#ffffff;padding:0.5em 0.5em 0.1em;margin:0.5em auto;width:90%;max-width:800px;-webkit-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15)}h1{text-align:center;font-size:2.25em;margin:0.3em 0 0.5em}code{background-color:#dfffdf;-webkit-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);display:block;padding:0.2em;font-family:\"DejaVu Sans Mono\", \"Bitstream Vera Sans Mono\", Hack, Menlo, Consolas, Monaco, monospace;font-size:0.85em;margin:auto;width:95%;max-width:600px}table{width:95%;border-collapse:collapse;margin:auto;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;word-break:break-word;position:relative;z-index:0}table tbody{background-color:#ffffff;color:#000000}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);content:' ';position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1}table img{margin:0;display:inline}th,tr{padding:0.15em;text-align:center}th{background-color:#007000;color:#ffffff}th a{color:#ffffff}td,th{padding:0.225em}td{text-align:left}tr:nth-child(odd){background-color:#dfffdf}hr{color:#ffffff}@media screen and (prefers-color-scheme: dark){html{background-color:#002000;color:#ffffff}body{background-color:#000f00;-webkit-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15)}code{background-color:#002000;-webkit-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1)}a{color:#ffffff}a:hover{color:#00ff00}table tbody{background-color:#000f00;color:#ffffff}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175)}tr:nth-child(odd){background-color:#002000}}</style></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
3317 head
= fs
.existsSync("./.head") ? fs
.readFileSync("./.head").toString() : (fs
.existsSync("./head.html") ? fs
.readFileSync("./head.html").toString() : ""); // header
3318 foot
= fs
.existsSync("./.foot") ? fs
.readFileSync("./.foot").toString() : (fs
.existsSync("./foot.html") ? fs
.readFileSync("./foot.html").toString() : ""); // footer
3320 callServerError(500, err
);
3324 // Function to perform HTTP redirection to a specified destination URL
3325 function redirect(destination
, isTemporary
, keepMethod
, customHeaders
) {
3326 // If keepMethod is a object, then save it to customHeaders
3327 if (typeof keepMethod
== "object") customHeaders
= keepMethod
;
3329 // If isTemporary is a object, then save it to customHeaders
3330 if (typeof isTemporary
== "object") customHeaders
= isTemporary
;
3332 // If customHeaders are not provided, get the default custom headers
3333 if (customHeaders
=== undefined) customHeaders
= getCustomHeaders();
3335 // Set the "Location" header to the destination URL
3336 customHeaders
["Location"] = destination
;
3338 // Determine the status code for redirection based on the isTemporary and keepMethod flags
3339 var statusCode
= keepMethod
? (isTemporary
? 307 : 308) : (isTemporary
? 302 : 301);
3341 // Write the response header with the appropriate status code and message
3342 res
.writeHead(statusCode
, http
.STATUS_CODES
[statusCode
], customHeaders
);
3344 // Log the redirection message
3345 serverconsole
.resmessage("Client redirected to " + destination
);
3350 // Return from the function
3354 // Function to parse incoming POST data from the request
3355 function parsePostData(options
, callback
) {
3356 // If the request method is not POST, return a 405 Method Not Allowed error
3357 if (req
.method
!= "POST") {
3358 // Get the default custom headers and add "Allow" header with value "POST"
3359 var customHeaders
= getCustomHeaders();
3360 customHeaders
["Allow"] = "POST";
3362 // Call the server error function with 405 status code and custom headers
3363 callServerError(405, customHeaders
);
3367 // Set formidableOptions to options, if provided; otherwise, set it to an empty object
3368 var formidableOptions
= options
? options
: {};
3370 // If no callback is provided, set the callback to options and reset formidableOptions
3373 formidableOptions
= {};
3376 // If the formidable module had an error, call the server error function with 500 status code and error stack
3377 if (formidable
._errored
) callServerError(500, formidable
._errored
);
3379 // Create a new formidable form
3380 var form
= formidable(formidableOptions
);
3382 // Parse the request and process the fields and files
3383 form
.parse(req
, function (err
, fields
, files
) {
3384 // If there was an error, call the server error function with status code determined by error
3386 if (err
.httpCode
) callServerError(err
.httpCode
);
3387 else callServerError(400);
3390 // Otherwise, call the provided callback function with the parsed fields and files
3391 callback(fields
, files
);
3395 // Authenticated user variable
3396 var authUser
= null;
3398 // URL-related objects.
3401 uobject
= parseURL(req
.url
, "http" + (req
.socket
.encrypted
? "s" : "") + "://" + (req
.headers
.host
? req
.headers
.host
: (domain
? domain
: "unknown.invalid")));
3403 // Return an 400 error
3404 callServerError(400);
3405 serverconsole
.errmessage("Bad request!");
3408 var search
= uobject
.search
;
3409 var href
= uobject
.pathname
;
3410 var ext
= href
.match(/[^\/]\.([^.]+)$/);
3412 else ext
= ext
[1].toLowerCase();
3413 var decodedHref
= "";
3415 decodedHref
= decodeURIComponent(href
);
3417 // Return an 400 error
3418 callServerError(400);
3419 serverconsole
.errmessage("Bad request!");
3422 var origHref
= href
; // Placeholder origHref
3424 if (req
.headers
["expect"] && req
.headers
["expect"] != "100-continue") {
3425 // Expectations not met.
3426 callServerError(417);
3430 // Mod execution function
3431 function modExecute(mods
, ffinals
) {
3432 // Prepare modFunction
3433 var modFunction
= ffinals
;
3434 var useMods
= mods
.slice();
3437 // Get list of forward proxy mods
3439 mods
.forEach(function (mod
) {
3440 if (mod
.proxyCallback
!== undefined) useMods
.push(mod
);
3444 useMods
.reverse().forEach(function (modO
) {
3445 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
);
3448 // Execute modFunction
3452 var vresCalled
= false;
3456 process
.emitWarning("elseCallback() invoked multiple times.", {
3457 code
: "WARN_SVRJS_MULTIPLE_ELSECALLBACK"
3464 // Function to check the level of a path relative to the web root
3465 function checkPathLevel(path
) {
3466 // Split the path into an array of components based on "/"
3467 var pathComponents
= path
.split("/");
3469 // Initialize counters for level up (..) and level down (.)
3470 var levelUpCount
= 0;
3471 var levelDownCount
= 0;
3473 // Loop through the path components
3474 for (var i
= 0; i
< pathComponents
.length
; i
++) {
3475 // If the component is "..", decrement the levelUpCount
3476 if (".." === pathComponents
[i
]) {
3479 // If the component is not "." or an empty string, increment the levelDownCount
3480 else if ("." !== pathComponents
[i
] && "" !== pathComponents
[i
]) {
3485 // Calculate the overall level by subtracting levelUpCount from levelDownCount
3486 var overallLevel
= levelDownCount
- levelUpCount
;
3488 // Return the overall level
3489 return overallLevel
;
3494 var eheaders
= getCustomHeaders();
3495 eheaders
["Content-Type"] = "text/html; charset=utf-8";
3496 res
.writeHead(501, http
.STATUS_CODES
[501], eheaders
);
3497 res
.write("<!DOCTYPE html><html><head><title>Proxy not implemented</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /><style>html{background-color:#dfffdf;color:#000000;font-family:FreeSans, Helvetica, Tahoma, Verdana, Arial, sans-serif;margin:0.75em}body{background-color:#ffffff;padding:0.5em 0.5em 0.1em;margin:0.5em auto;width:90%;max-width:800px;-webkit-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15)}h1{text-align:center;font-size:2.25em;margin:0.3em 0 0.5em}code{background-color:#dfffdf;-webkit-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);display:block;padding:0.2em;font-family:\"DejaVu Sans Mono\", \"Bitstream Vera Sans Mono\", Hack, Menlo, Consolas, Monaco, monospace;font-size:0.85em;margin:auto;width:95%;max-width:600px}table{width:95%;border-collapse:collapse;margin:auto;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;word-break:break-word;position:relative;z-index:0}table tbody{background-color:#ffffff;color:#000000}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);content:' ';position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1}table img{margin:0;display:inline}th,tr{padding:0.15em;text-align:center}th{background-color:#007000;color:#ffffff}th a{color:#ffffff}td,th{padding:0.225em}td{text-align:left}tr:nth-child(odd){background-color:#dfffdf}hr{color:#ffffff}@media screen and (prefers-color-scheme: dark){html{background-color:#002000;color:#ffffff}body{background-color:#000f00;-webkit-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15)}code{background-color:#002000;-webkit-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1)}a{color:#ffffff}a:hover{color:#00ff00}table tbody{background-color:#000f00;color:#ffffff}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175)}tr:nth-child(odd){background-color:#002000}}</style></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
>");
3499 serverconsole.errmessage("SVR
.JS doesn
't support proxy without proxy mod.");
3503 if (req.method == "OPTIONS") {
3504 var hdss = getCustomHeaders();
3505 hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
3506 res.writeHead(204, http.STATUS_CODES[204], hdss);
3509 } else if (req.method != "GET" && req.method != "POST" && req.method != "HEAD") {
3510 callServerError(405);
3511 serverconsole.errmessage("Invaild method: " + req.method);
3515 if (allowStatus && (href == "/svrjsstatus.svr" || (os.platform() == "win32" && href.toLowerCase() == "/svrjsstatus.svr"))) {
3516 function formatRelativeTime(relativeTime) {
3517 var days = Math.floor(relativeTime / 60 / (60 * 24));
3518 var dateDiff = new Date(relativeTime * 1000);
3519 return days + " days, " + dateDiff.getUTCHours() + " hours, " + dateDiff.getUTCMinutes() + " minutes, " + dateDiff.getUTCSeconds() + " seconds";
3521 var statusBody = "";
3522 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/>";
3524 //Those entries are just dates and numbers converted/formatted to strings, so no escaping is needed.
3525 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/>";
3526 statusBody += "OS uptime: " + formatRelativeTime(os.uptime()) + "<br/>";
3527 statusBody += "Total request count: " + reqcounter + "<br/>";
3528 statusBody += "Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s<br/>";
3529 statusBody += "Client errors (4xx): " + err4xxcounter + "<br/>";
3530 statusBody += "Server errors (5xx): " + err5xxcounter + "<br/>";
3531 statusBody += "Average error rate: " + (Math.round(((err4xxcounter + err5xxcounter) / reqcounter) * 10000) / 100) + "%<br/>";
3532 statusBody += "Malformed HTTP requests: " + malformedcounter;
3533 if (process.memoryUsage) statusBody += "<br/>Memory usage of thread: " + sizify(process.memoryUsage().rss, true) + "B";
3534 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) + "%";
3535 statusBody += "<br/>Thread PID: " + process.pid + "<br/>";
3537 res.writeHead(200, http.STATUS_CODES[200], {
3538 "Content-Type": "text/html; charset=utf-8"
3540 res.end((head == "" ? "<!DOCTYPE html><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\" /><style>html{background-color:#dfffdf;color:#000000;font-family:FreeSans, Helvetica, Tahoma, Verdana, Arial, sans-serif;margin:0.75em}body{background-color:#ffffff;padding:0.5em 0.5em 0.1em;margin:0.5em auto;width:90%;max-width:800px;-webkit-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15);box-shadow:0 5px 10px 0 rgba(0, 0, 0, 0.15)}h1{text-align:center;font-size:2.25em;margin:0.3em 0 0.5em}code{background-color:#dfffdf;-webkit-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);box-shadow:0 2px 4px 0 rgba(0, 0, 0, 0.1);display:block;padding:0.2em;font-family:\"DejaVu Sans Mono\", \"Bitstream Vera Sans Mono\", Hack, Menlo, Consolas, Monaco, monospace;font-size:0.85em;margin:auto;width:95%;max-width:600px}table{width:95%;border-collapse:collapse;margin:auto;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;word-break:break-word;position:relative;z-index:0}table tbody{background-color:#ffffff;color:#000000}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);box-shadow:0 4px 8px 0 rgba(0, 0, 0, 0.175);content:' ';position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1}table img{margin:0;display:inline}th,tr{padding:0.15em;text-align:center}th{background-color:#007000;color:#ffffff}th a{color:#ffffff}td,th{padding:0.225em}td{text-align:left}tr:nth-child(odd){background-color:#dfffdf}hr{color:#ffffff}@media screen and (prefers-color-scheme: dark){html{background-color:#002000;color:#ffffff}body{background-color:#000f00;-webkit-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);-moz-box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15);box-shadow:0 5px 10px 0 rgba(127, 127, 127, 0.15)}code{background-color:#002000;-webkit-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);-moz-box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1);box-shadow:0 2px 4px 0 rgba(127, 127, 127, 0.1)}a{color:#ffffff}a:hover{color:#00ff00}table tbody{background-color:#000f00;color:#ffffff}table tbody:after{-webkit-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);-moz-box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175);box-shadow:0 4px 8px 0 rgba(127, 127, 127, 0.175)}tr:nth-child(odd){background-color:#002000}}</style></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));
3544 var dHref = decodeURIComponent(href);
3545 var readFrom = "." + dHref;
3546 var dirImagesMissing = false;
3547 fs.stat(readFrom, function (err, stats) {
3549 if (err.code == "ENOENT") {
3550 if (__dirname != process.cwd() && dHref.match(/^\/\.dirimages\/(?:(?!\.png$).)+\.png$/)) {
3551 dirImagesMissing = true;
3552 readFrom = __dirname + dHref;
3554 callServerError(404);
3555 serverconsole.errmessage("Resource not found.");
3558 } else if (err.code == "ENOTDIR") {
3559 callServerError(404); // Assume that file doesn't exist
.
3560 serverconsole
.errmessage("Resource not found.");
3562 } else if (err
.code
== "EACCES") {
3563 callServerError(403);
3564 serverconsole
.errmessage("Access denied.");
3566 } else if (err
.code
== "ENAMETOOLONG") {
3567 callServerError(414);
3569 } else if (err
.code
== "EMFILE") {
3570 callServerError(503);
3572 } else if (err
.code
== "ELOOP") {
3573 callServerError(508); // The symbolic link loop is detected during file system operations.
3574 serverconsole
.errmessage("Symbolic link loop detected.");
3577 callServerError(500, err
);
3582 // Check if index file exists
3583 if (!dirImagesMissing
&& (req
.url
== "/" || stats
.isDirectory())) {
3584 fs
.stat((readFrom
+ "/index.html").replace(/\/+/g, "/"), function (e, s) {
3585 if (e || !s.isFile()) {
3586 fs.stat((readFrom + "/index.htm").replace(/\/+/g, "/"), function (e, s) {
3587 if (e || !s.isFile()) {
3588 fs.stat((readFrom + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) {
3589 if (e || !s.isFile()) {
3590 properDirectoryListingAndStaticFileServe();
3594 readFrom = (readFrom + "/index.xhtml").replace(/\/+/g, "/");
3595 properDirectoryListingAndStaticFileServe();
3601 readFrom = (readFrom + "/index.htm").replace(/\/+/g, "/");
3602 properDirectoryListingAndStaticFileServe();
3608 readFrom = (readFrom + "/index.html").replace(/\/+/g, "/");
3609 properDirectoryListingAndStaticFileServe();
3612 } else if (dirImagesMissing) {
3613 fs.stat(readFrom, function (e, s) {
3614 if (e || !s.isFile()) {
3615 properDirectoryListingAndStaticFileServe();
3618 properDirectoryListingAndStaticFileServe();
3622 properDirectoryListingAndStaticFileServe();
3625 function properDirectoryListingAndStaticFileServe() {
3626 if (stats.isFile()) {
3627 var acceptEncoding = req.headers["accept
-encoding
"];
3628 if (!acceptEncoding) acceptEncoding = "";
3630 var filelen = stats.size;
3633 var fileETag = undefined;
3634 if (configJSON.enableETag == undefined || configJSON.enableETag) {
3635 fileETag = generateETag(href, stats);
3636 // Check if the client's request matches the ETag value (If-None-Match)
3637 var clientETag = req.headers["if-none
-match
"];
3638 if (clientETag === fileETag) {
3639 res.writeHead(304, http.STATUS_CODES[304], {
3646 // Check if the client's request doesn't match the ETag value (If-Match)
3647 var ifMatchETag = req.headers["if-match
"];
3648 if (ifMatchETag && ifMatchETag !== "*" && ifMatchETag !== fileETag) {
3649 callServerError(412, {
3656 // Handle partial content request
3657 if (req.headers["range
"]) {
3659 var rhd = getCustomHeaders();
3660 rhd["Accept
-Ranges
"] = "bytes
";
3661 rhd["Content
-Range
"] = "bytes */
" + filelen;
3662 var regexmatch = req.headers["range
"].match(/bytes=([0-9]*)-([0-9]*)/);
3664 callServerError(416, rhd);
3666 // Process the partial content request
3667 var beginOrig = regexmatch[1];
3668 var endOrig = regexmatch[2];
3669 var maxEnd = filelen - 1 + (ext == "html
" ? head.length + foot.length : 0)
3672 if (beginOrig == "" && endOrig == "") {
3673 callServerError(416, rhd);
3675 } else if (beginOrig == "") {
3676 begin = end - parseInt(endOrig) + 1;
3678 begin = parseInt(beginOrig);
3679 if (endOrig != "") end = parseInt(endOrig);
3681 if (begin > end || begin < 0 || begin > maxEnd) {
3682 callServerError(416, rhd);
3685 if (end > maxEnd) end = maxEnd;
3686 rhd["Content
-Range
"] = "bytes
" + begin + "-" + end + "/" + filelen;
3687 rhd["Content
-Length
"] = end - begin + 1;
3688 delete rhd["Content
-Type
"];
3689 var mtype = mime.contentType(ext);
3690 if (mtype && ext != "") rhd["Content
-Type
"] = mtype;
3691 if (fileETag) rhd["ETag
"] = fileETag;
3693 if (req.method != "HEAD
") {
3694 if (ext == "html
" && begin < head.length && (end - begin) < head.length) {
3695 res.writeHead(206, http.STATUS_CODES[206], hdhds);
3696 res.end(head.substring(begin, end + 1));
3698 } else if (ext == "html
" && begin >= head.length + filelen){
3699 res.writeHead(206, http.STATUS_CODES[206], hdhds);
3700 res.end(foot.substring(begin - head.length - filelen, end - head.length - filelen + 1));
3703 var readStream = fs.createReadStream(readFrom, {
3704 start: ext == "html
" ? Math.max(0, begin - head.length) : begin,
3705 end: ext == "html
" ? Math.min(filelen, end - head.length) : end
3707 readStream.on("error
", function (err) {
3708 if (err.code == "ENOENT
") {
3709 callServerError(404);
3710 serverconsole.errmessage("Resource not found
.");
3711 } else if (err.code == "ENOTDIR
") {
3712 callServerError(404); // Assume that file doesn't exist.
3713 serverconsole.errmessage("Resource not found
.");
3714 } else if (err.code == "EACCES
") {
3715 callServerError(403);
3716 serverconsole.errmessage("Access denied
.");
3717 } else if (err.code == "ENAMETOOLONG
") {
3718 callServerError(414);
3719 } else if (err.code == "EMFILE
") {
3720 callServerError(503);
3721 } else if (err.code == "ELOOP
") {
3722 callServerError(508); // The symbolic link loop is detected during file system operations.
3723 serverconsole.errmessage("Symbolic link loop detected
.");
3725 callServerError(500, err);
3727 }).on("open
", function () {
3729 if (ext == "html
") {
3730 function afterWriteCallback() {
3731 if(foot.length > 0 && end > head.length + filelen) {
3732 readStream.on("end
", function () {
3733 res.end(foot.substring(0, end - head.length - filelen + 1));
3736 readStream.pipe(res, {
3737 end: !(foot.length > 0 && end > head.length + filelen)
3740 res.writeHead(206, http.STATUS_CODES[206], hdhds);
3741 if (head.length == 0 || begin > head.length) {
3742 afterWriteCallback();
3743 } else if (!res.write(head.substring(begin, head.length - begin))) {
3744 res.on("drain
", afterWriteCallback);
3746 process.nextTick(afterWriteCallback);
3749 res.writeHead(206, http.STATUS_CODES[206], rhd);
3750 readStream.pipe(res);
3752 serverconsole.resmessage("Client successfully received content
.");
3754 callServerError(500, err);
3758 res.writeHead(206, http.STATUS_CODES[206], rhd);
3763 callServerError(500, err);
3766 // Helper function to check if compression is allowed for the file
3767 function canCompress(path, list) {
3768 var canCompress = true;
3769 for (var i = 0; i < list.length; i++) {
3770 if (createRegex(list[i], true).test(path)) {
3771 canCompress = false;
3778 var useBrotli = (ext != "br
" && filelen > 256 && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/));
3779 var useDeflate = (ext != "zip
" && filelen > 256 && acceptEncoding.match(/\bdeflate\b/));
3780 var useGzip = (ext != "gz
" && filelen > 256 && acceptEncoding.match(/\bgzip\b/));
3782 var isCompressable = true;
3784 // Check for files not to compressed and compression enabling setting. Also check for browser quirks and adjust compression accordingly
3785 if((!useBrotli && !useDeflate && !useGzip) || configJSON.enableCompression !== true || !canCompress(href, dontCompress)) {
3786 isCompressable = false; // Compression is disabled
3787 } else if (ext != "html
" && ext != "htm
" && ext != "xhtml
" && ext != "xht
" && ext != "shtml
") {
3788 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
"]))) {
3789 isCompressable = false; // Netscape 4.x doesn't handle compressed data properly outside of HTML documents.
3790 } else if (/^w3m\/[^ ]*$/.test(req.headers["user
-agent
"])) {
3791 isCompressable = false; // w3m doesn't handle compressed data properly outside of HTML documents.
3794 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
"]))) {
3795 isCompressable = false; // Netscape 4.06-4.08 doesn't handle compressed data properly.
3799 callServerError(500, err);
3803 // Bun 1.1 has definition for zlib.createBrotliCompress, but throws an error while invoking the function.
3804 if (process.isBun && useBrotli && isCompressable) {
3806 zlib.createBrotliCompress();
3814 if (useBrotli && isCompressable) {
3815 hdhds["Content
-Encoding
"] = "br
";
3816 } else if (useDeflate && isCompressable) {
3817 hdhds["Content
-Encoding
"] = "deflate
";
3818 } else if (useGzip && isCompressable) {
3819 hdhds["Content
-Encoding
"] = "gzip
";
3821 if (ext == "html
") {
3822 hdhds["Content
-Length
"] = head.length + filelen + foot.length;
3824 hdhds["Content
-Length
"] = filelen;
3827 hdhds["Accept
-Ranges
"] = "bytes
";
3828 delete hdhds["Content
-Type
"];
3829 var mtype = mime.contentType(ext);
3830 if (mtype && ext != "") hdhds["Content
-Type
"] = mtype;
3831 if (fileETag) hdhds["ETag
"] = fileETag;
3833 if (req.method != "HEAD
") {
3834 var readStream = fs.createReadStream(readFrom);
3835 readStream.on("error
", function (err) {
3836 if (err.code == "ENOENT
") {
3837 callServerError(404);
3838 serverconsole.errmessage("Resource not found
.");
3839 } else if (err.code == "ENOTDIR
") {
3840 callServerError(404); // Assume that file doesn't exist.
3841 serverconsole.errmessage("Resource not found
.");
3842 } else if (err.code == "EACCES
") {
3843 callServerError(403);
3844 serverconsole.errmessage("Access denied
.");
3845 } else if (err.code == "ENAMETOOLONG
") {
3846 callServerError(414);
3847 } else if (err.code == "EMFILE
") {
3848 callServerError(503);
3849 } else if (err.code == "ELOOP
") {
3850 callServerError(508); // The symbolic link loop is detected during file system operations.
3851 serverconsole.errmessage("Symbolic link loop detected
.");
3853 callServerError(500, err);
3855 }).on("open
", function () {
3858 if (useBrotli && isCompressable) {
3859 resStream = zlib.createBrotliCompress();
3860 resStream.pipe(res);
3861 } else if (useDeflate && isCompressable) {
3862 resStream = zlib.createDeflateRaw();
3863 resStream.pipe(res);
3864 } else if (useGzip && isCompressable) {
3865 resStream = zlib.createGzip();
3866 resStream.pipe(res);
3870 if (ext == "html
") {
3871 function afterWriteCallback() {
3872 if (foot.length > 0) {
3873 readStream.on("end
", function () {
3874 resStream.end(foot);
3877 readStream.pipe(resStream, {
3878 end: (foot.length == 0)
3881 res.writeHead(200, http.STATUS_CODES[200], hdhds);
3882 if (head.length == 0) {
3883 afterWriteCallback();
3884 } else if (!resStream.write(head)) {
3885 resStream.on("drain
", afterWriteCallback);
3887 process.nextTick(afterWriteCallback);
3890 res.writeHead(200, http.STATUS_CODES[200], hdhds);
3891 readStream.pipe(resStream);
3893 serverconsole.resmessage("Client successfully received content
.");
3895 callServerError(500, err);
3899 res.writeHead(200, http.STATUS_CODES[200], hdhds);
3901 serverconsole.resmessage("Client successfully received content
.");
3904 callServerError(500, err);
3907 } else if (stats.isDirectory()) {
3908 // Check if directory listing is enabled in the configuration
3909 if (checkForEnabledDirectoryListing(req.headers.host, req.socket ? req.socket.localAddress : undefined)) {
3910 var customDirListingHeader = "";
3911 var customDirListingFooter = "";
3913 function getCustomDirListingHeader(callback) {
3914 fs.readFile(("." + dHref + "/.dirhead").replace(/\/+/g, "/"), function (err, data) {
3916 if (err.code == "ENOENT
" || err.code == "EISDIR
") {
3917 if (os.platform != "win32
" || href != "/") {
3918 fs.readFile(("." + dHref + "/HEAD.html").replace(/\/+/g, "/"), function (err, data) {
3920 if (err.code == "ENOENT
" || err.code == "EISDIR
") {
3923 callServerError(500, err);
3926 customDirListingHeader = data.toString();
3934 callServerError(500, err);
3937 customDirListingHeader = data.toString();
3943 function getCustomDirListingFooter(callback) {
3944 fs.readFile(("." + dHref + "/.dirfoot").replace(/\/+/g, "/"), function (err, data) {
3946 if (err.code == "ENOENT
" || err.code == "EISDIR
") {
3947 if (os.platform != "win32
" || href != "/") {
3948 fs.readFile(("." + dHref + "/FOOT.html").replace(/\/+/g, "/"), function (err, data) {
3950 if (err.code == "ENOENT
" || err.code == "EISDIR
") {
3953 callServerError(500, err);
3956 customDirListingFooter = data.toString();
3964 callServerError(500, err);
3967 customDirListingFooter = data.toString();
3973 // Read custom header and footer content (if available)
3974 getCustomDirListingHeader(function () {
3975 getCustomDirListingFooter(function () {
3976 // Check if custom header has HTML tag
3977 var headerHasHTMLTag = customDirListingHeader.replace(/<!--(?:(?:(?!--\>)[\s\S])*|)(?:-->|$)/g, "").match(/<html(?![a-zA-Z0-9])(?:"(?:\\(?:[\s
\S
]|$)|[^\\"])*(?:"|$)|'(?:\\(?:[\s\S]|$)|[^\\'])*(?:'|$)|[^'">])*(?:>|$)/i);
3979 // Generate HTML head and footer based on configuration and custom content
3980 var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == "" ?
3981 (!headerHasHTMLTag ?
3982 "<!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\" /><style
>html
{background
-color
:#dfffdf
;color
:#000000;font
-family
:FreeSans
, Helvetica
, Tahoma
, Verdana
, Arial
, sans
-serif
;margin
:0.75em
}body
{background
-color
:#ffffff
;padding
:0.5em
0.5em
0.1em
;margin
:0.5em auto
;width
:90%;max
-width
:800px
;-webkit
-box
-shadow
:0 5px
10px
0 rgba(0, 0, 0, 0.15);-moz
-box
-shadow
:0 5px
10px
0 rgba(0, 0, 0, 0.15);box
-shadow
:0 5px
10px
0 rgba(0, 0, 0, 0.15)}h1
{text
-align
:center
;font
-size
:2.25em
;margin
:0.3em
0 0.5em
}code
{background
-color
:#dfffdf
;-webkit
-box
-shadow
:0 2px
4px
0 rgba(0, 0, 0, 0.1);-moz
-box
-shadow
:0 2px
4px
0 rgba(0, 0, 0, 0.1);box
-shadow
:0 2px
4px
0 rgba(0, 0, 0, 0.1);display
:block
;padding
:0.2em
;font
-family
:\"DejaVu Sans Mono
\", \"Bitstream Vera Sans Mono
\", Hack
, Menlo
, Consolas
, Monaco
, monospace
;font
-size
:0.85em
;margin
:auto
;width
:95%;max
-width
:600px
}table
{width
:95%;border
-collapse
:collapse
;margin
:auto
;overflow
-wrap
:break-word
;word
-wrap
:break-word
;word
-break:break-all
;word
-break:break-word
;position
:relative
;z
-index
:0}table tbody
{background
-color
:#ffffff
;color
:#000000}table tbody
:after
{-webkit
-box
-shadow
:0 4px
8px
0 rgba(0, 0, 0, 0.175);-moz
-box
-shadow
:0 4px
8px
0 rgba(0, 0, 0, 0.175);box
-shadow
:0 4px
8px
0 rgba(0, 0, 0, 0.175);content
:' ';position
:absolute
;top
:0;left
:0;right
:0;bottom
:0;z
-index
:-1}table img
{margin
:0;display
:inline
}th
,tr
{padding
:0.15em
;text
-align
:center
}th
{background
-color
:#007000;color
:#ffffff
}th a
{color
:#ffffff
}td
,th
{padding
:0.225em
}td
{text
-align
:left
}tr
:nth
-child(odd
){background
-color
:#dfffdf
}hr
{color
:#ffffff
}@media screen
and (prefers
-color
-scheme
: dark
){html
{background
-color
:#002000;color
:#ffffff
}body
{background
-color
:#000f00
;-webkit
-box
-shadow
:0 5px
10px
0 rgba(127, 127, 127, 0.15);-moz
-box
-shadow
:0 5px
10px
0 rgba(127, 127, 127, 0.15);box
-shadow
:0 5px
10px
0 rgba(127, 127, 127, 0.15)}code
{background
-color
:#002000;-webkit
-box
-shadow
:0 2px
4px
0 rgba(127, 127, 127, 0.1);-moz
-box
-shadow
:0 2px
4px
0 rgba(127, 127, 127, 0.1);box
-shadow
:0 2px
4px
0 rgba(127, 127, 127, 0.1)}a
{color
:#ffffff
}a
:hover
{color
:#00ff00
}table tbody
{background
-color
:#000f00
;color
:#ffffff
}table tbody
:after
{-webkit
-box
-shadow
:0 4px
8px
0 rgba(127, 127, 127, 0.175);-moz
-box
-shadow
:0 4px
8px
0 rgba(127, 127, 127, 0.175);box
-shadow
:0 4px
8px
0 rgba(127, 127, 127, 0.175)}tr
:nth
-child(odd
){background
-color
:#002000}}</style></head
><body
>" :
3983 customDirListingHeader.replace(/<head>/i, "<head
><title
>Directory
: " + decodeURIComponent(origHref).replace(/&/g, "&
;").replace(/</g, "<
;").replace(/>/g, ">
;") + "</title
>")) :
3984 head.replace(/<head>/i, "<head
><title
>Directory
: " + decodeURIComponent(origHref).replace(/&/g, "&
;").replace(/</g, "<
;").replace(/>/g, ">
;") + "</title
>")) +
3985 (!headerHasHTMLTag ? customDirListingHeader : "") +
3986 "<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
>");
3988 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);
3990 if (fs.existsSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/"))) {
3991 htmlFoot = "</table><hr/>" + fs.readFileSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/")) + htmlFoot;
3994 fs.readdir(readFrom, function (err, list) {
3999 // Function to get stats for all files in the directory
4000 function getStatsForAllFilesI(fileList, callback, prefix, pushArray, index) {
4001 if (fileList.length == 0) {
4002 callback(pushArray);
4006 fs.stat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
4008 fs.lstat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
4010 name: fileList[index],
4011 stats: err ? null : stats,
4014 if (index < fileList.length - 1) {
4015 getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
4017 callback(pushArray);
4022 name: fileList[index],
4026 if (index < fileList.length - 1) {
4027 getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
4029 callback(pushArray);
4035 // Wrapper function to get stats for all files
4036 function getStatsForAllFiles(fileList, prefix, callback) {
4037 if (!prefix) prefix = "";
4038 getStatsForAllFilesI(fileList, callback, prefix, [], 0);
4041 // Get stats for all files in the directory and generate the listing
4042 getStatsForAllFiles(list, readFrom, function (filelist) {
4043 var directoryListingRows = [];
4044 for (var i = 0; i < filelist.length; i++) {
4045 if (filelist[i].name[0] !== ".") {
4046 var estats = filelist[i].stats;
4047 var ename = filelist[i].name;
4048 var eext = ename.match(/\.([^.]+)$/);
4049 eext = eext ? eext[1] : "";
4050 var emime = eext ? mime.contentType(eext) : false;
4051 if (filelist[i].errored) {
4052 directoryListingRows.push(
4053 "<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
=\"" +
4054 (href + "/" + encodeURI(ename)).replace(/\/+/g, "/") +
4056 ename.replace(/&/g, "&
;").replace(/</g, "<
;").replace(/>/g, ">
;") +
4057 "</a></td
><td
>-</td
><td
>" +
4058 (estats ? estats.mtime.toDateString() : "-") +
4062 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
=\"" +
4063 (origHref + "/" + encodeURIComponent(ename)).replace(/\/+/g, "/") +
4064 (estats.isDirectory() ? "/" : "") +
4066 ename.replace(/&/g, "&
;").replace(/</g, "<
;").replace(/>/g, ">
;") +
4068 (estats.isDirectory() ? "-" : sizify(estats.size.toString())) +
4070 estats.mtime.toDateString() +
4073 // Determine the file type and set the appropriate image and alt text
4074 if (estats.isDirectory()) {
4075 entry = entry.replace("[img
]", "/.dirimages/directory.png
").replace("[alt
]", "[DIR
]");
4076 } else if (!estats.isFile()) {
4077 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
=\"" +
4078 (origHref + "/" + encodeURIComponent(ename)).replace(/\/+/g, "/") +
4080 ename.replace(/&/g, "&
;").replace(/</g, "<
;").replace(/>/g, ">
;") +
4081 "</a></td
><td
>-</td
><td
>" +
4082 estats.mtime.toDateString() +
4085 // Determine the special file types (block device, character device, etc.)
4086 if (estats.isBlockDevice()) {
4087 entry = entry.replace("[img
]", "/.dirimages/hwdevice
.png
").replace("[alt
]", "[BLK
]");
4088 } else if (estats.isCharacterDevice()) {
4089 entry = entry.replace("[img
]", "/.dirimages/hwdevice
.png
").replace("[alt
]", "[CHR
]");
4090 } else if (estats.isFIFO()) {
4091 entry = entry.replace("[img
]", "/.dirimages/fifo
.png
").replace("[alt
]", "[FIF
]");
4092 } else if (estats.isSocket()) {
4093 entry = entry.replace("[img
]", "/.dirimages/socket.png
").replace("[alt
]", "[SCK
]");
4095 } else if (ename.match(/README|LICEN[SC]E/i)) {
4096 entry = entry.replace("[img
]", "/.dirimages/important.png
").replace("[alt
]", "[IMP
]");
4097 } else if (eext.match(/^(?:[xs]?html?|xml)$/i)) {
4098 entry = entry.replace("[img
]", "/.dirimages/html
.png
").replace("[alt
]", (eext == "xml
" ? "[XML
]" : "[HTM
]"));
4099 } else if (eext == "js
") {
4100 entry = entry.replace("[img
]", "/.dirimages/javascript
.png
").replace("[alt
]", "[JS
]");
4101 } else if (eext == "php
") {
4102 entry = entry.replace("[img
]", "/.dirimages/php.png
").replace("[alt
]", "[PHP
]");
4103 } else if (eext == "css
") {
4104 entry = entry.replace("[img
]", "/.dirimages/css.png
").replace("[alt
]", "[CSS
]");
4105 } else if (emime && emime.split("/")[0] == "image
") {
4106 entry = entry.replace("[img
]", "/.dirimages/image.png
").replace("[alt
]", (eext == "ico
" ? "[ICO
]" : "[IMG
]"));
4107 } else if (emime && emime.split("/")[0] == "font
") {
4108 entry = entry.replace("[img
]", "/.dirimages/font
.png
").replace("[alt
]", "[FON
]");
4109 } else if (emime && emime.split("/")[0] == "audio
") {
4110 entry = entry.replace("[img
]", "/.dirimages/audio.png
").replace("[alt
]", "[AUD
]");
4111 } else if ((emime && emime.split("/")[0] == "text
") || eext == "json
") {
4112 entry = entry.replace("[img
]", "/.dirimages/text
.png
").replace("[alt
]", (eext == "json
" ? "[JSO
]" : "[TXT
]"));
4113 } else if (emime && emime.split("/")[0] == "video
") {
4114 entry = entry.replace("[img
]", "/.dirimages/video
.png
").replace("[alt
]", "[VID
]");
4115 } else if (eext.match(/^(?:zip|rar|bz2|[gb7x]z|lzma|tar)$/i)) {
4116 entry = entry.replace("[img
]", "/.dirimages/archive.png
").replace("[alt
]", "[ARC
]");
4117 } else if (eext.match(/^(?:[id]mg|iso|flp)$/i)) {
4118 entry = entry.replace("[img
]", "/.dirimages/diskimage.png
").replace("[alt
]", "[DSK
]");
4120 entry = entry.replace("[img
]", "/.dirimages/other.png
").replace("[alt
]", "[OTH
]");
4122 directoryListingRows.push(entry);
4127 // Push the information about empty directory
4128 if (directoryListingRows.length == 0) {
4129 directoryListingRows.push("<tr
><td
></td><td>No files found</td
><td
></td><td></td
></tr
>");
4132 // Send the directory listing response
4133 res.writeHead(200, http.STATUS_CODES[200], {
4134 "Content
-Type
": "text
/html
; charset
=utf
-8"
4136 res.end(htmlHead + directoryListingRows.join("") + htmlFoot);
4137 serverconsole.resmessage("Client successfully received content
.");
4141 if (err.code == "ENOENT
") {
4142 callServerError(404);
4143 serverconsole.errmessage("Resource not found
.");
4144 } else if (err.code == "ENOTDIR
") {
4145 callServerError(404); // Assume that file doesn't exist.
4146 serverconsole.errmessage("Resource not found
.");
4147 } else if (err.code == "EACCES
") {
4148 callServerError(403);
4149 serverconsole.errmessage("Access denied
.");
4150 } else if (err.code == "ENAMETOOLONG
") {
4151 callServerError(414);
4152 } else if (err.code == "EMFILE
") {
4153 callServerError(503);
4154 } else if (err.code == "ELOOP
") {
4155 callServerError(508); // The symbolic link loop is detected during file system operations.
4156 serverconsole.errmessage("Symbolic link loop detected
.");
4158 callServerError(500, err);
4165 // Directory listing is disabled, call 403 Forbidden error
4166 callServerError(403);
4167 serverconsole.errmessage("Directory listing is disabled
.");
4170 callServerError(501);
4171 serverconsole.errmessage("SVR
.JS doesn
't support block devices, character devices, FIFOs nor sockets.");
4179 // Scan the block list
4180 if (blocklist.check(reqip)) {
4181 // Invoke 403 Forbidden error
4182 callServerError(403);
4183 serverconsole.errmessage("Client is in the block list.");
4187 if (req.url == "*") {
4189 if (req.method == "OPTIONS") {
4190 // Respond with list of methods
4191 var hdss = getCustomHeaders();
4192 hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
4193 res.writeHead(204, http.STATUS_CODES[204], hdss);
4197 // SVR.JS doesn't understand that request
, so
throw an
400 error
4198 callServerError(400);
4203 if (req
.method
== "CONNECT") {
4204 // CONNECT requests should be handled in "connect" event.
4205 callServerError(501);
4206 serverconsole
.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library.");
4210 // Check for invalid X-Forwarded-For header
4211 if (!isForwardedValid
) {
4212 serverconsole
.errmessage("X-Forwarded-For header is invalid.");
4213 callServerError(400);
4218 var sanitizedHref
= sanitizeURL(href
, allowDoubleSlashes
);
4219 var preparedReqUrl
= uobject
.pathname
+ (uobject
.search
? uobject
.search
: "") + (uobject
.hash
? uobject
.hash
: "");
4221 // Check if URL is "dirty"
4222 if (href
!= sanitizedHref
&& !isProxy
) {
4223 var sanitizedURL
= uobject
;
4224 sanitizedURL
.path
= null;
4225 sanitizedURL
.href
= null;
4226 sanitizedURL
.pathname
= sanitizedHref
;
4227 sanitizedURL
.hostname
= null;
4228 sanitizedURL
.host
= null;
4229 sanitizedURL
.port
= null;
4230 sanitizedURL
.protocol
= null;
4231 sanitizedURL
.slashes
= null;
4232 sanitizedURL
= url
.format(sanitizedURL
);
4233 serverconsole
.resmessage("URL sanitized: " + req
.url
+ " => " + sanitizedURL
);
4234 if (rewriteDirtyURLs
) {
4235 req
.url
= sanitizedURL
;
4237 uobject
= parseURL(req
.url
, "http" + (req
.socket
.encrypted
? "s" : "") + "://" + (req
.headers
.host
? req
.headers
.host
: (domain
? domain
: "unknown.invalid")));
4239 // Return an 400 error
4240 callServerError(400);
4241 serverconsole
.errmessage("Bad request!");
4244 search
= uobject
.search
;
4245 href
= uobject
.pathname
;
4246 ext
= href
.match(/[^\/]\.([^.]+)$/);
4248 else ext
= ext
[1].toLowerCase();
4250 decodedHref
= decodeURIComponent(href
);
4253 callServerError(400);
4254 serverconsole
.errmessage("Bad request!");
4258 redirect(sanitizedURL
, false);
4261 } else if (req
.url
!= preparedReqUrl
&& !isProxy
) {
4262 serverconsole
.resmessage("URL sanitized: " + req
.url
+ " => " + preparedReqUrl
);
4263 if (rewriteDirtyURLs
) {
4264 req
.url
= preparedReqUrl
;
4266 redirect(preparedReqUrl
, false);
4271 // Handle redirects to HTTPS
4272 if (secure
&& !fromMain
&& !disableNonEncryptedServer
&& !disableToHTTPSRedirect
) {
4273 var hostx
= req
.headers
.host
;
4274 if (hostx
=== undefined) {
4275 serverconsole
.errmessage("Host header is missing.");
4276 callServerError(400);
4281 callServerError(501);
4282 serverconsole
.errmessage("This server will never be a proxy.");
4286 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);
4288 var destinationPort
= 0;
4290 var parsedHostx
= hostx
.match(/(\[[^\]]*\]|[^:]*)(?::([0-9]+))?/);
4291 var hostname
= parsedHostx
[1];
4292 var hostPort
= parsedHostx
[2] ? parseInt(parsedHostx
[2]) : 80;
4293 if (isNaN(hostPort
)) hostPort
= 80;
4295 if (hostPort
== port
|| (port
== pubport
&& !isPublicServer
)) {
4296 destinationPort
= sport
;
4298 destinationPort
= spubport
;
4301 redirect("https://" + hostname
+ (destinationPort
== 443 ? "" : (":" + destinationPort
)) + req
.url
);
4305 // Handle redirects to addresses with "www." prefix
4307 var hostname
= req
.headers
.host
.split(":");
4308 var hostport
= null;
4309 if (hostname
.length
> 1 && (hostname
[0] != "[" || hostname
[hostname
.length
- 1] != "]")) hostport
= hostname
.pop();
4310 hostname
= hostname
.join(":");
4311 if (hostname
== domain
&& hostname
.indexOf("www.") != 0) {
4312 redirect((req
.socket
.encrypted
? "https" : "http") + "://www." + hostname
+ (hostport
? ":" + hostport
: "") + req
.url
.replace(/\/+/g, "/"));
4317 // Handle URL rewriting
4318 function rewriteURL(address, map, callback, _fileState, _mapBegIndex) {
4319 var rewrittenURL = address;
4320 var doCallback = true;
4322 for (var i = (_mapBegIndex ? _mapBegIndex : 0); i < map.length; i++) {
4323 var mapEntry = map[i];
4324 if (href != "/" && (mapEntry.isNotDirectory || mapEntry.isNotFile) && !_fileState) {
4325 fs.stat("." + decodeURIComponent(href), function (err, stats) {
4329 } else if (stats.isDirectory()) {
4331 } else if (stats.isFile()) {
4336 rewriteURL(address, map, callback, _fileState, i);
4341 var tempRewrittenURL = rewrittenURL;
4342 if (!mapEntry.allowDoubleSlashes) {
4343 address = address.replace(/\/+/g,"/");
4344 tempRewrittenURL = address;
4346 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)) {
4347 rewrittenURL = tempRewrittenURL;
4349 mapEntry.replacements.forEach(function (replacement) {
4350 rewrittenURL = rewrittenURL.replace(createRegex(replacement.regex), replacement.replacement);
4352 if (mapEntry.append) rewrittenURL += mapEntry.append;
4355 callback(err, null);
4361 if (doCallback) callback(null, rewrittenURL);
4364 // Trailing slash redirection
4365 function redirectTrailingSlashes(callback) {
4366 if (!isProxy && !disableTrailingSlashRedirects && href[href.length - 1] != "/" && origHref[origHref.length - 1] != "/") {
4367 fs.stat("." + decodeURIComponent(href), function (err, stats) {
4368 if (err || !stats.isDirectory()) {
4372 callServerError(500, err);
4375 var destinationURL = uobject;
4376 destinationURL.path = null;
4377 destinationURL.href = null;
4378 destinationURL.pathname = origHref + "/";
4379 destinationURL.hostname = null;
4380 destinationURL.host = null;
4381 destinationURL.port = null;
4382 destinationURL.protocol = null;
4383 destinationURL.slashes = null;
4384 destinationURL = url.format(destinationURL);
4385 redirect(destinationURL);
4395 // Add web root postfixes
4397 var preparedReqUrl3 = (allowPostfixDoubleSlashes ? (href.replace(/\/+/,"/") + (uobject.search ? uobject.search : "") + (uobject.hash ? uobject.hash : "")) : req.url);
4398 var urlWithPostfix = preparedReqUrl3;
4399 var postfixPrefix = "";
4400 wwwrootPostfixPrefixesVHost.every(function (currentPostfixPrefix) {
4401 if (preparedReqUrl3.indexOf(currentPostfixPrefix) == 0) {
4402 if (currentPostfixPrefix.match(/\/+$/)) postfixPrefix = currentPostfixPrefix.replace(/\/+$/, "");
4403 else if (urlWithPostfix.length == currentPostfixPrefix.length || urlWithPostfix[currentPostfixPrefix.length] == "?" || urlWithPostfix[currentPostfixPrefix.length] == "/" || urlWithPostfix[currentPostfixPrefix.length] == "#") postfixPrefix = currentPostfixPrefix;
4405 urlWithPostfix = urlWithPostfix.substring(postfixPrefix.length);
4411 wwwrootPostfixesVHost.every(function (postfixEntry) {
4412 if (matchHostname(postfixEntry.host) && ipMatch(postfixEntry.ip, req.socket ? req.socket.localAddress : undefined) && !(postfixEntry.skipRegex && preparedReqUrl3.match(createRegex(postfixEntry.skipRegex)))) {
4413 urlWithPostfix = postfixPrefix + "/" + postfixEntry.postfix + urlWithPostfix;
4419 if (urlWithPostfix != preparedReqUrl3) {
4420 serverconsole.resmessage("Added web root postfix
: " + req.url + " => " + urlWithPostfix);
4421 req.url = urlWithPostfix;
4423 uobject = parseURL(req.url, "http
" + (req.socket.encrypted ? "s
" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4425 // Return an 400 error
4426 callServerError(400);
4427 serverconsole
.errmessage("Bad request!");
4430 search
= uobject
.search
;
4431 href
= uobject
.pathname
;
4432 ext
= href
.match(/[^\/]\.([^.]+)$/);
4434 else ext
= ext
[1].toLowerCase();
4437 decodedHref
= decodeURIComponent(href
);
4440 callServerError(400);
4441 serverconsole
.errmessage("Bad request!");
4445 var sHref
= sanitizeURL(href
, allowDoubleSlashes
);
4446 var preparedReqUrl2
= uobject
.pathname
+ (uobject
.search
? uobject
.search
: "") + (uobject
.hash
? uobject
.hash
: "");
4448 if (req
.url
!= preparedReqUrl2
|| sHref
!= href
.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")) {
4449 callServerError(403);
4450 serverconsole.errmessage("Content blocked
.");
4452 } else if (sHref != href) {
4453 var rewrittenAgainURL = uobject;
4454 rewrittenAgainURL.path = null;
4455 rewrittenAgainURL.href = null;
4456 rewrittenAgainURL.pathname = sHref;
4457 rewrittenAgainURL.hostname = null;
4458 rewrittenAgainURL.host = null;
4459 rewrittenAgainURL.port = null;
4460 rewrittenAgainURL.protocol = null;
4461 rewrittenAgainURL.slashes = null;
4462 rewrittenAgainURL = url.format(rewrittenAgainURL);
4463 serverconsole.resmessage("URL sanitized
: " + req.url + " => " + rewrittenAgainURL);
4464 req.url = rewrittenAgainURL;
4466 uobject = parseURL(req.url, "http
" + (req.socket.encrypted ? "s
" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4468 // Return an 400 error
4469 callServerError(400);
4470 serverconsole
.errmessage("Bad request!");
4473 search
= uobject
.search
;
4474 href
= uobject
.pathname
;
4475 ext
= href
.match(/[^\/]\.([^.]+)$/);
4477 else ext
= ext
[1].toLowerCase();
4479 decodedHref
= decodeURIComponent(href
);
4482 callServerError(400);
4483 serverconsole
.errmessage("Bad request!");
4491 rewriteURL(req
.url
, rewriteMap
, function (err
, rewrittenURL
) {
4493 callServerError(500, err
);
4496 if (rewrittenURL
!= req
.url
) {
4497 serverconsole
.resmessage("URL rewritten: " + req
.url
+ " => " + rewrittenURL
);
4498 req
.url
= rewrittenURL
;
4500 uobject
= parseURL(req
.url
, "http" + (req
.socket
.encrypted
? "s" : "") + "://" + (req
.headers
.host
? req
.headers
.host
: (domain
? domain
: "unknown.invalid")));
4502 // Return an 400 error
4503 callServerError(400);
4504 serverconsole
.errmessage("Bad request!");
4507 search
= uobject
.search
;
4508 href
= uobject
.pathname
;
4509 ext
= href
.match(/[^\/]\.([^.]+)$/);
4511 else ext
= ext
[1].toLowerCase();
4513 decodedHref
= decodeURIComponent(href
);
4516 callServerError(400);
4517 serverconsole
.errmessage("Bad request!");
4521 var sHref
= sanitizeURL(href
, allowDoubleSlashes
);
4522 var preparedReqUrl2
= uobject
.pathname
+ (uobject
.search
? uobject
.search
: "") + (uobject
.hash
? uobject
.hash
: "");
4524 if (req
.url
!= preparedReqUrl2
|| sHref
!= href
.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")) {
4525 callServerError(403);
4526 serverconsole.errmessage("Content blocked
.");
4528 } else if (sHref != href) {
4529 var rewrittenAgainURL = uobject;
4530 rewrittenAgainURL.path = null;
4531 rewrittenAgainURL.href = null;
4532 rewrittenAgainURL.pathname = sHref;
4533 rewrittenAgainURL.hostname = null;
4534 rewrittenAgainURL.host = null;
4535 rewrittenAgainURL.port = null;
4536 rewrittenAgainURL.protocol = null;
4537 rewrittenAgainURL.slashes = null;
4538 rewrittenAgainURL = url.format(rewrittenAgainURL);
4539 serverconsole.resmessage("URL sanitized
: " + req.url + " => " + rewrittenAgainURL);
4540 req.url = rewrittenAgainURL;
4542 uobject = parseURL(req.url, "http
" + (req.socket.encrypted ? "s
" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
4544 // Return an 400 error
4545 callServerError(400);
4546 serverconsole
.errmessage("Bad request!");
4549 search
= uobject
.search
;
4550 href
= uobject
.pathname
;
4551 ext
= href
.match(/[^\/]\.([^.]+)$/);
4553 else ext
= ext
[1].toLowerCase();
4555 decodedHref
= decodeURIComponent(href
);
4558 callServerError(400);
4559 serverconsole
.errmessage("Bad request!");
4564 // Set response headers
4566 var hkh
= getCustomHeaders();
4567 Object
.keys(hkh
).forEach(function (hkS
) {
4569 res
.setHeader(hkS
, hkh
[hkS
]);
4571 // Headers will not be set.
4576 // Prepare the path (remove multiple slashes)
4577 var decodedHrefWithoutDuplicateSlashes
= decodedHref
.replace(/\/+/g,"/");
4579 // Check if path is forbidden
4580 if ((isForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "config") || isForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "certificates")) && !isProxy
) {
4581 callServerError(403);
4582 serverconsole
.errmessage("Access to configuration file/certificates is denied.");
4584 } else if (isIndexOfForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "temp") && !isProxy
) {
4585 callServerError(403);
4586 serverconsole
.errmessage("Access to temporary folder is denied.");
4588 } else if (isIndexOfForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "log") && !isProxy
&& (configJSON
.enableLogging
|| configJSON
.enableLogging
== undefined) && !configJSON
.enableRemoteLogBrowsing
) {
4589 callServerError(403);
4590 serverconsole
.errmessage("Access to log files is denied.");
4592 } else if (isForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "svrjs") && !isProxy
&& !exposeServerVersion
) {
4593 callServerError(403);
4594 serverconsole
.errmessage("Access to SVR.JS script is denied.");
4596 } else if ((isForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "svrjs") || isForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "serverSideScripts") || isIndexOfForbiddenPath(decodedHrefWithoutDuplicateSlashes
, "serverSideScriptDirectories")) && !isProxy
&& (configJSON
.disableServerSideScriptExpose
|| configJSON
.disableServerSideScriptExpose
=== undefined)) {
4597 callServerError(403);
4598 serverconsole
.errmessage("Access to sources is denied.");
4601 var nonscodeIndex
= -1;
4605 // Scan for non-standard codes
4606 if (!isProxy
&& nonStandardCodes
!= undefined) {
4607 for (var i
= 0; i
< nonStandardCodes
.length
; i
++) {
4608 if (matchHostname(nonStandardCodes
[i
].host
) && ipMatch(nonStandardCodes
[i
].ip
, req
.socket
? req
.socket
.localAddress
: undefined)) {
4609 var isMatch
= false;
4610 var hrefWithoutDuplicateSlashes
= href
.replace(/\/+/g,"/");
4611 if (nonStandardCodes
[i
].regex
) {
4613 var createdRegex
= createRegex(nonStandardCodes
[i
].regex
, true);
4614 isMatch
= req
.url
.match(createdRegex
) || hrefWithoutDuplicateSlashes
.match(createdRegex
);
4615 regexI
[i
] = createdRegex
;
4618 isMatch
= nonStandardCodes
[i
].url
== hrefWithoutDuplicateSlashes
|| (os
.platform() == "win32" && nonStandardCodes
[i
].url
.toLowerCase() == hrefWithoutDuplicateSlashes
.toLowerCase());
4621 if (nonStandardCodes
[i
].scode
== 401) {
4622 // HTTP authentication
4623 if (authIndex
== -1) {
4627 if (nonscodeIndex
== -1) {
4628 if ((nonStandardCodes
[i
].scode
== 403 || nonStandardCodes
[i
].scode
== 451) && nonStandardCodes
[i
].users
!== undefined) {
4629 if (nonStandardCodes
[i
].users
.check(reqip
)) nonscodeIndex
= i
;
4640 // Handle non-standard codes
4641 if (nonscodeIndex
> -1) {
4642 var nonscode
= nonStandardCodes
[nonscodeIndex
];
4643 if (nonscode
.scode
== 301 || nonscode
.scode
== 302 || nonscode
.scode
== 307 || nonscode
.scode
== 308) {
4645 if (regexI
[nonscodeIndex
]) {
4646 location
= req
.url
.replace(regexI
[nonscodeIndex
], nonscode
.location
);
4647 if(location
== req
.url
) {
4648 // Fallback replacement
4649 location
= hrefWithoutDuplicateSlashes
.replace(regexI
[nonscodeIndex
], nonscode
.location
);
4651 } else if (req
.url
.split("?")[1] == undefined || req
.url
.split("?")[1] == null || req
.url
.split("?")[1] == "" || req
.url
.split("?")[1] == " ") {
4652 location
= nonscode
.location
;
4654 location
= nonscode
.location
+ "?" + req
.url
.split("?")[1];
4656 redirect(location
, nonscode
.scode
== 302 || nonscode
.scode
== 307, nonscode
.scode
== 307 || nonscode
.scode
== 308);
4659 callServerError(nonscode
.scode
);
4660 if (nonscode
.scode
== 403) {
4661 serverconsole
.errmessage("Content blocked.");
4662 } else if (nonscode
.scode
== 410) {
4663 serverconsole
.errmessage("Content is gone.");
4664 } else if (nonscode
.scode
== 418) {
4665 serverconsole
.errmessage("SVR.JS is always a teapot ;)");
4667 serverconsole
.errmessage("Client fails receiving content.");
4673 // Handle HTTP authentication
4674 if (authIndex
> -1) {
4675 var authcode
= nonStandardCodes
[authIndex
];
4677 // Function to check if passwords match
4678 function checkIfPasswordMatches(list
, password
, callback
, _i
) {
4680 var cb = function (hash
) {
4681 if (hash
== list
[_i
].pass
) {
4683 } else if (_i
>= list
.length
- 1) {
4686 checkIfPasswordMatches(list
, password
, callback
, _i
+ 1);
4689 var hashedPassword
= sha256(password
+ list
[_i
].salt
);
4690 var cacheEntry
= null;
4691 if (list
[_i
].scrypt
) {
4692 if (!crypto
.scrypt
) {
4693 callServerError(500, new Error("SVR.JS doesn't support scrypt-hashed passwords on Node.JS versions without scrypt hash support."));
4696 cacheEntry
= scryptCache
.find(function (entry
) {
4697 return (entry
.password
== hashedPassword
&& entry
.salt
== list
[_i
].salt
);
4700 cb(cacheEntry
.hash
);
4702 crypto
.scrypt(password
, list
[_i
].salt
, 64, function (err
, derivedKey
) {
4704 callServerError(500, err
);
4706 var key
= derivedKey
.toString("hex");
4709 password
: hashedPassword
,
4710 salt
: list
[_i
].salt
,
4718 } else if (list
[_i
].pbkdf2
) {
4719 if (crypto
.__disabled__
!== undefined) {
4720 callServerError(500, new Error("SVR.JS doesn't support PBKDF2-hashed passwords on Node.JS versions without crypto support."));
4723 cacheEntry
= pbkdf2Cache
.find(function (entry
) {
4724 return (entry
.password
== hashedPassword
&& entry
.salt
== list
[_i
].salt
);
4727 cb(cacheEntry
.hash
);
4729 crypto
.pbkdf2(password
, list
[_i
].salt
, 36250, 64, "sha512", function (err
, derivedKey
) {
4731 callServerError(500, err
);
4733 var key
= derivedKey
.toString("hex");
4736 password
: hashedPassword
,
4737 salt
: list
[_i
].salt
,
4750 function authorizedCallback(bruteProtection
) {
4752 var ha
= getCustomHeaders();
4753 ha
["WWW-Authenticate"] = "Basic realm=\"" + (authcode
.realm
? authcode
.realm
.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\"";
4754 var credentials
= req
.headers
["authorization"];
4756 callServerError(401, ha
);
4757 serverconsole
.errmessage("Content needs authorization.");
4760 var credentialsMatch
= credentials
.match(/^Basic (.+)$/);
4761 if (!credentialsMatch
) {
4762 callServerError(401, ha
);
4763 serverconsole
.errmessage("Malformed credentials.");
4766 var decodedCredentials
= Buffer
.from(credentialsMatch
[1], "base64").toString("utf8");
4767 var decodedCredentialsMatch
= decodedCredentials
.match(/^([^:]*):(.*)$/);
4768 if (!decodedCredentialsMatch
) {
4769 callServerError(401, ha
);
4770 serverconsole
.errmessage("Malformed credentials.");
4773 var username
= decodedCredentialsMatch
[1];
4774 var password
= decodedCredentialsMatch
[2];
4775 var usernameMatch
= [];
4776 var sha256Count
= 0;
4777 var pbkdf2Count
= 0;
4778 var scryptCount
= 0;
4779 if (!authcode
.userList
|| authcode
.userList
.indexOf(username
) > -1) {
4780 usernameMatch
= users
.filter(function (entry
) {
4783 } else if(entry
.pbkdf2
) {
4788 return entry
.name
== username
;
4791 if (usernameMatch
.length
== 0) {
4792 // Pushing false user match to prevent time-based user enumeration
4793 var fakeCredentials
= {
4795 pass
: "SVRJSAWebServerRunningOnNodeJS",
4796 salt
: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0"
4798 if (!process
.isBun
) {
4799 if (scryptCount
> sha256Count
&& scryptCount
> pbkdf2Count
) {
4800 fakeCredentials
.scrypt
= true;
4801 } else if (pbkdf2Count
> sha256Count
) {
4802 fakeCredentials
.pbkdf2
= true;
4805 usernameMatch
.push(fakeCredentials
);
4807 checkIfPasswordMatches(usernameMatch
, password
, function (authorized
) {
4810 if (bruteProtection
) {
4812 process
.send("\x12AUTHW" + reqip
);
4814 if (!bruteForceDb
[reqip
]) bruteForceDb
[reqip
] = {
4817 bruteForceDb
[reqip
].invalidAttempts
++;
4818 if (bruteForceDb
[reqip
].invalidAttempts
>= 10) {
4819 bruteForceDb
[reqip
].lastAttemptDate
= new Date();
4823 callServerError(401, ha
);
4824 serverconsole
.errmessage("User \"" + String(username
).replace(/[\r\n]/g, "") + "\" failed to log in.");
4826 if (bruteProtection
) {
4828 process
.send("\x12AUTHR" + reqip
);
4830 if (bruteForceDb
[reqip
]) bruteForceDb
[reqip
] = {
4835 serverconsole
.reqmessage("Client is logged in as \"" + String(username
).replace(/[\r\n]/g, "") + "\".");
4836 authUser
= username
;
4837 redirectTrailingSlashes(function () {
4838 modExecute(mods
, vres
);
4842 callServerError(500, err
);
4847 callServerError(500, err
);
4851 if (authcode
.disableBruteProtection
) {
4852 // Don't brute-force protect it, just do HTTP authentication
4853 authorizedCallback(false);
4854 } else if (!process
.send
) {
4855 // Query data from JS object database
4856 if (!bruteForceDb
[reqip
] || !bruteForceDb
[reqip
].lastAttemptDate
|| (new Date() - 300000 >= bruteForceDb
[reqip
].lastAttemptDate
)) {
4857 if (bruteForceDb
[reqip
] && bruteForceDb
[reqip
].invalidAttempts
>= 10) bruteForceDb
[reqip
] = {
4860 authorizedCallback(true);
4862 callServerError(429);
4863 serverconsole
.errmessage("Brute force limit reached!");
4866 var listenerEmitted
= false;
4868 // Listen for brute-force protection response
4869 function authMessageListener(message
) {
4870 if (listenerEmitted
) return;
4871 if (message
== "\x14AUTHA" + reqip
|| message
== "\x14AUTHD" + reqip
) {
4872 process
.removeListener("message", authMessageListener
);
4873 listenerEmitted
= true;
4875 if (message
== "\x14AUTHD" + reqip
) {
4876 callServerError(429);
4877 serverconsole
.errmessage("Brute force limit reached!");
4878 } else if (message
== "\x14AUTHA" + reqip
) {
4879 authorizedCallback(true);
4882 process
.on("message", authMessageListener
);
4883 process
.send("\x12AUTHQ" + reqip
);
4886 redirectTrailingSlashes(function () {
4887 modExecute(mods
, vres
);
4894 callServerError(500, err
);
4898 function serverErrorHandler(err
, isRedirect
) {
4899 if(isRedirect
) attmtsRedir
--;
4901 if (cluster
.isPrimary
=== undefined && (isRedirect
? attmtsRedir
: attmts
)) {
4902 serverconsole
.locerrmessage(serverErrorDescs
[err
.code
] ? serverErrorDescs
[err
.code
] : serverErrorDescs
["UNKNOWN"]);
4903 serverconsole
.locmessage((isRedirect
? attmtsRedir
: attmts
) + " attempts left.");
4906 process
.send("\x12ERRLIST" + (isRedirect
? attmtsRedir
: attmts
) + err
.code
);
4908 // Probably main process exited
4911 if ((isRedirect
? attmtsRedir
: attmts
) > 0) {
4912 (isRedirect
? server2
: server
).close();
4913 setTimeout(start
, 900);
4916 if (cluster
.isPrimary
!== undefined) process
.send("\x12ERRCRASH" + err
.code
);
4918 // Probably main process exited
4920 setTimeout(function () {
4921 var errno
= errors
[err
.code
];
4922 process
.exit(errno
? errno
: 1);
4927 server
.on("error", function (err
) {
4928 serverErrorHandler(err
, false);
4930 server
.on("listening", function () {
4936 var closedMaster
= true;
4938 // IPC listener for server listening signalization
4939 function listenConnListener(msg
) {
4940 if (msg
== "\x12LISTEN") {
4945 // IPC listener for brue force protection
4946 function bruteForceListenerWrapper(worker
) {
4947 return function bruteForceListener(message
) {
4949 if (message
.substring(0, 6) == "\x12AUTHQ") {
4950 ip
= message
.substring(6);
4951 if (!bruteForceDb
[ip
] || !bruteForceDb
[ip
].lastAttemptDate
|| (new Date() - 300000 >= bruteForceDb
[ip
].lastAttemptDate
)) {
4952 if (bruteForceDb
[ip
] && bruteForceDb
[ip
].invalidAttempts
>= 10) bruteForceDb
[ip
] = {
4955 worker
.send("\x14AUTHA" + ip
);
4957 worker
.send("\x14AUTHD" + ip
);
4959 } else if (message
.substring(0, 6) == "\x12AUTHR") {
4960 ip
= message
.substring(6);
4961 if (bruteForceDb
[ip
]) bruteForceDb
[ip
] = {
4964 } else if (message
.substring(0, 6) == "\x12AUTHW") {
4965 ip
= message
.substring(6);
4966 if (!bruteForceDb
[ip
]) bruteForceDb
[ip
] = {
4969 bruteForceDb
[ip
].invalidAttempts
++;
4970 if (bruteForceDb
[ip
].invalidAttempts
>= 10) {
4971 bruteForceDb
[ip
].lastAttemptDate
= new Date();
4977 var isWorkerHungUpBuff
= true;
4978 var isWorkerHungUpBuff2
= true;
4980 // General IPC message listener
4981 function msgListener(msg
) {
4982 for (var i
= 0; i
< Object
.keys(cluster
.workers
).length
; i
++) {
4983 if (msg
== "\x12END") {
4984 cluster
.workers
[Object
.keys(cluster
.workers
)[i
]].removeAllListeners("message");
4985 cluster
.workers
[Object
.keys(cluster
.workers
)[i
]].on("message", bruteForceListenerWrapper(cluster
.workers
[Object
.keys(cluster
.workers
)[i
]]));
4986 cluster
.workers
[Object
.keys(cluster
.workers
)[i
]].on("message", listenConnListener
);
4989 if (msg
== "\x12CLOSE") {
4990 closedMaster
= true;
4991 } else if (msg
== "\x12LISTEN" || msg
.substring(0, 4) == "\x12AUTH") {
4993 } else if (msg
== "\x12KILLOK") {
4994 if (typeof isWorkerHungUpBuff
!= "undefined") isWorkerHungUpBuff
= false;
4995 } else if (msg
== "\x12PINGOK") {
4996 if (typeof isWorkerHungUpBuff2
!= "undefined") isWorkerHungUpBuff2
= false;
4997 } else if (msg
== "\x12KILLTERMMSG") {
4998 serverconsole
.locmessage("Terminating unused worker process...");
4999 } else if (msg
== "\x12SAVEGOOD") {
5000 serverconsole
.locmessage("Configuration saved.");
5001 } else if (msg
.indexOf("\x12SAVEERR") == 0) {
5002 serverconsole
.locwarnmessage("There was a problem while saving configuration file. Reason: " + msg
.substring(8));
5003 } else if (msg
== "\x12END") {
5004 cluster
.workers
[Object
.keys(cluster
.workers
)[0]].on("message", function (msg
) {
5005 if (msg
.length
>= 8 && msg
.indexOf("\x12ERRLIST") == 0) {
5006 var tries
= parseInt(msg
.substring(8, 9));
5007 var errCode
= msg
.substring(9);
5008 serverconsole
.locerrmessage(serverErrorDescs
[errCode
] ? serverErrorDescs
[errCode
] : serverErrorDescs
["UNKNOWN"]);
5009 serverconsole
.locmessage(tries
+ " attempts left.");
5011 if (msg
.length
>= 9 && msg
.indexOf("\x12ERRCRASH") == 0) {
5012 var errno
= errors
[msg
.substring(9)];
5013 process
.exit(errno
? errno
: 1);
5017 serverconsole
.climessage(msg
);
5021 var messageTransmitted
= false;
5023 function listeningMessage() {
5024 if (typeof closedMaster
!== "undefined") closedMaster
= false;
5025 if (!cluster
.isPrimary
&& cluster
.isPrimary
!== undefined) {
5026 process
.send("\x12LISTEN");
5029 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$/)));
5030 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}$/));
5031 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$/)));
5032 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}$/));
5034 var sAccHost
= host
;
5035 if (!listenToAny
) accHost
= listenAddress
;
5036 if (!sListenToAny
) sAccHost
= sListenAddress
;
5037 if (messageTransmitted
) return;
5038 messageTransmitted
= true;
5039 serverconsole
.locmessage("Started server at: ");
5040 if (secure
&& (sListenToLocalhost
|| sListenToAny
)) {
5041 if (typeof sport
=== "number") {
5042 serverconsole
.locmessage("* https://localhost" + (sport
== 443 ? "" : (":" + sport
)));
5044 serverconsole
.locmessage("* " + sport
); // Unix socket or Windows named pipe
5047 if (!(secure
&& disableNonEncryptedServer
) && (listenToLocalhost
|| listenToAny
)) {
5048 if (typeof port
=== "number") {
5049 serverconsole
.locmessage("* http://localhost" + (port
== 80 ? "" : (":" + port
)));
5051 serverconsole
.locmessage("* " + port
); // Unix socket or Windows named pipe
5054 if (secure
&& typeof sport
=== "number" && !sListenToLocalhost
&& (!sListenToAny
|| (host
!= "" && host
!= "[offline]"))) serverconsole
.locmessage("* https://" + (sAccHost
.indexOf(":") > -1 ? "[" + sAccHost
+ "]" : sAccHost
) + (sport
== 443 ? "" : (":" + sport
)));
5055 if (!(secure
&& disableNonEncryptedServer
) && !listenToLocalhost
&& (!listenToAny
|| (host
!= "" && host
!= "[offline]")) && typeof port
=== "number") serverconsole
.locmessage("* http://" + (accHost
.indexOf(":") > -1 ? "[" + accHost
+ "]" : accHost
) + (port
== 80 ? "" : (":" + port
)));
5056 ipStatusCallback(function () {
5058 if (secure
&& !sListenToLocalhost
) serverconsole
.locmessage("* https://" + (pubip
.indexOf(":") > -1 ? "[" + pubip
+ "]" : pubip
) + (spubport
== 443 ? "" : (":" + spubport
)));
5059 if (!(secure
&& disableNonEncryptedServer
) && !listenToLocalhost
) serverconsole
.locmessage("* http://" + (pubip
.indexOf(":") > -1 ? "[" + pubip
+ "]" : pubip
) + (pubport
== 80 ? "" : (":" + pubport
)));
5062 if (secure
&& !sListenToLocalhost
) serverconsole
.locmessage("* https://" + domain
+ (spubport
== 443 ? "" : (":" + spubport
)));
5063 if (!(secure
&& disableNonEncryptedServer
) && !listenToLocalhost
) serverconsole
.locmessage("* http://" + domain
+ (pubport
== 80 ? "" : (":" + pubport
)));
5065 serverconsole
.locmessage("For CLI help, you can type \"help\"");
5069 function start(init
) {
5070 init
= Boolean(init
);
5071 if (cluster
.isPrimary
|| cluster
.isPrimary
=== undefined) {
5073 for (i
= 0; i
< logo
.length
; i
++) console
.log(logo
[i
]); // Print logo
5075 console
.log("Welcome to \x1b[1mSVR.JS - a web server running on Node.JS\x1b[0m");
5078 if (version
.indexOf("Nightly-") === 0) serverconsole
.locwarnmessage("This version is only for test purposes and may be unstable.");
5079 if (configJSON
.enableHTTP2
&& !secure
) serverconsole
.locwarnmessage("HTTP/2 without HTTPS may not work in web browsers. Web browsers only support HTTP/2 with HTTPS!");
5080 if (process
.isBun
) {
5081 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.");
5082 if (users
.some(function (entry
) {
5083 return entry
.pbkdf2
;
5084 })) serverconsole
.locwarnmessage("PBKDF2 password hashing function in Bun blocks the event loop, which may result in denial of service.");
5086 if (cluster
.isPrimary
=== undefined) serverconsole
.locwarnmessage("You're running SVR.JS on single thread. Reliability may suffer, as the server is stopped after crash.");
5087 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.");
5088 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.");
5089 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).");
5090 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).");
5091 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.");
5092 if (!process
.isBun
&& secure
&& process
.versions
&& process
.versions
.openssl
&& process
.versions
.openssl
.substring(0, 2) == "1.") {
5093 if (new Date() > new Date("11 September 2023")) {
5094 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.");
5096 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.");
5099 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.");
5100 if (disableMods
) serverconsole
.locwarnmessage("SVR.JS is running without mods and server-side JavaScript enabled. Web applications may not work as expected");
5103 // Display mod and server-side JavaScript errors
5104 if (process
.isPrimary
|| process
.isPrimary
=== undefined) {
5105 modLoadingErrors
.forEach(function (modLoadingError
) {
5106 serverconsole
.locwarnmessage("There was a problem while loading a \"" + String(modLoadingError
.modName
).replace(/[\r\n]/g, "") + "\" mod.");
5107 serverconsole
.locwarnmessage("Stack:");
5108 serverconsole
.locwarnmessage(generateErrorStack(modLoadingError
.error
));
5111 serverconsole
.locwarnmessage("There was a problem while loading server-side JavaScript.");
5112 serverconsole
.locwarnmessage("Stack:");
5113 serverconsole
.locwarnmessage(generateErrorStack(SSJSError
));
5115 if (SSJSError
|| modLoadingErrors
.length
> 0) console
.log();
5118 // Print server information
5119 serverconsole
.locmessage("Server version: " + version
);
5120 if (process
.isBun
) serverconsole
.locmessage("Bun version: v" + process
.versions
.bun
);
5121 else serverconsole
.locmessage("Node.JS version: " + process
.version
);
5122 var CPUs
= os
.cpus();
5123 if (CPUs
.length
> 0) serverconsole
.locmessage("CPU: " + (CPUs
.length
> 1 ? CPUs
.length
+ "x " : "") + CPUs
[0].model
);
5126 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.");
5127 if (configJSONRErr
) throw new Error("Can't read SVR.JS configuration file: " + configJSONRErr
.message
);
5128 if (configJSONPErr
) throw new Error("SVR.JS configuration parse error: " + configJSONPErr
.message
);
5129 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.");
5130 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");
5131 if (listenAddress
) {
5132 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).");
5133 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.");
5134 if (brdIPs
.indexOf(listenAddress
) > -1) throw new Error("SVR.JS can't listen on broadcast address.");
5135 if (netIPs
.indexOf(listenAddress
) > -1) throw new Error("SVR.JS can't listen on subnet address.");
5137 if (certificateError
) throw new Error("There was a problem with SSL certificate/private key: " + certificateError
.message
);
5138 if (wwwrootError
) throw new Error("There was a problem with your web root: " + wwwrootError
.message
);
5139 if (sniReDos
) throw new Error("Refusing to start, because the current SNI configuration would make the server vulnerable to ReDoS.");
5142 // Print server startup information
5143 if (!(secure
&& disableNonEncryptedServer
)) serverconsole
.locmessage("Starting HTTP server at " + (typeof port
== "number" ? (listenAddress
? ((listenAddress
.indexOf(":") > -1 ? "[" + listenAddress
+ "]" : listenAddress
)) + ":" : "port ") : "") + port
.toString() + "...");
5144 if (secure
) serverconsole
.locmessage("Starting HTTPS server at " + (typeof sport
== "number" ? (sListenAddress
? ((sListenAddress
.indexOf(":") > -1 ? "[" + sListenAddress
+ "]" : sListenAddress
)) + ":" : "port ") : "") + sport
.toString() + "...");
5148 if (!cluster
.isPrimary
) {
5150 if (typeof (secure
? sport
: port
) == "number" && (secure
? sListenAddress
: listenAddress
)) {
5151 server
.listen(secure
? sport
: port
, secure
? sListenAddress
: listenAddress
);
5153 server
.listen(secure
? sport
: port
);
5156 if (err
.code
!= "ERR_SERVER_ALREADY_LISTEN") throw err
;
5158 if (secure
&& !disableNonEncryptedServer
) {
5160 if (typeof port
== "number" && listenAddress
) {
5161 server2
.listen(port
, listenAddress
);
5163 server2
.listen(port
);
5166 if (err
.code
!= "ERR_SERVER_ALREADY_LISTEN") throw err
;
5173 close: function () {
5176 if (secure
&& !disableNonEncryptedServer
) {
5179 if (cluster
.isPrimary
=== undefined) serverconsole
.climessage("Server closed.");
5181 process
.send("Server closed.");
5182 process
.send("\x12CLOSE");
5185 if (cluster
.isPrimary
=== undefined) serverconsole
.climessage("Cannot close server! Reason: " + err
.message
);
5186 else process
.send("Cannot close server! Reason: " + err
.message
);
5191 if (typeof (secure
? sport
: port
) == "number" && (secure
? sListenAddress
: listenAddress
)) {
5192 server
.listen(secure
? sport
: port
, secure
? sListenAddress
: listenAddress
);
5194 server
.listen(secure
? sport
: port
);
5196 if (secure
&& !disableNonEncryptedServer
) {