1
0
Fork 0
forked from svrjs/svrjs

Update to SVR.JS 3.8.0

This commit is contained in:
Dorian Niemiec 2023-09-01 11:53:00 +02:00
parent 55262f73c5
commit c6960061b7
4 changed files with 736 additions and 439 deletions

View file

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>SVR.JS 3.7.5</title> <title>SVR.JS 3.8.0</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<style> <style>
@ -12,7 +12,7 @@
</style> </style>
</head> </head>
<body> <body>
<h1>Welcome to SVR.JS 3.7.5</h1> <h1>Welcome to SVR.JS 3.8.0</h1>
<br/> <br/>
<img src="/logo.png" style="width: 256px;" /> <img src="/logo.png" style="width: 256px;" />
<br/> <br/>
@ -110,8 +110,11 @@
</div> </div>
<p>Changes:</p> <p>Changes:</p>
<ul> <ul>
<li>Fixed non-working blacklist.</li> <li>Added partial virtual hosting support</li>
<li>Updated SVR.JS license.</li> <li>Added <i>host</i> field to <i>nonStandardCodes</i> and <i>rewriteMap</i> properties.</li>
<li>Added <i>userList</i> field to <i>nonStandardCodes</i> properties (with <i>scode</i> set to 401).</li>
<li>Added new config.json properties: <i>errorPages</i>, <i>enableDirectoryListingVHost</i> and <i>customHeadersVHost</i>.</li>
<li>Improved HTTP authentication error handling.</li>
</ul> </ul>
<p>Bugs:</p> <p>Bugs:</p>
<ul> <ul>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>SVR.JS 3.7.5 Licenses</title> <title>SVR.JS 3.8.0 Licenses</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<style> <style>
@ -12,8 +12,8 @@
</style> </style>
</head> </head>
<body> <body>
<h1>SVR.JS 3.7.5 Licenses</h1> <h1>SVR.JS 3.8.0 Licenses</h1>
<h2>SVR.JS 3.7.5</h2> <h2>SVR.JS 3.8.0</h2>
<div style="display: inline-block; text-align: left; border-width: 2px; border-style: solid; border-color: gray; padding: 8px;"> <div style="display: inline-block; text-align: left; border-width: 2px; border-style: solid; border-color: gray; padding: 8px;">
MIT License<br/> MIT License<br/>
<br/> <br/>
@ -37,7 +37,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<br/> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<br/>
SOFTWARE.<br/> SOFTWARE.<br/>
</div> </div>
<h2>Packages used by SVR.JS 3.7.5 and utilities</h2> <h2>Packages used by SVR.JS 3.8.0 and utilities</h2>
<div style="width: 100%; background-color: #ccc; border: 1px solid green; text-align: left; margin: 10px 0;"> <div style="width: 100%; background-color: #ccc; border: 1px solid green; text-align: left; margin: 10px 0;">
<div style="float: right;">License: MIT</div> <div style="float: right;">License: MIT</div>
<div style="font-size: 20px;"> <div style="font-size: 20px;">

552
svr.js
View file

@ -81,7 +81,7 @@ function deleteFolderRecursive(path) {
} }
var os = require("os"); var os = require("os");
var version = "3.7.5"; var version = "3.8.0";
var singlethreaded = false; var singlethreaded = false;
if (process.versions) process.versions.svrjs = version; //Inject SVR.JS into process.versions if (process.versions) process.versions.svrjs = version; //Inject SVR.JS into process.versions
@ -196,7 +196,7 @@ if (!singlethreaded) {
// Close IPC server // Close IPC server
process.send = function () {}; process.send = function () {};
fakeIPCServer.close(); fakeIPCServer.close();
} };
} }
} }
@ -227,14 +227,14 @@ if (!singlethreaded) {
sendImplemented = false; sendImplemented = false;
} }
oldLog = console.log; var oldLog = console.log;
console.log = function (a,b,c,d,e,f) { console.log = function (a,b,c,d,e,f) {
if(a == "ChildProcess.prototype.send() - Sorry! Not implemented yet") { if(a == "ChildProcess.prototype.send() - Sorry! Not implemented yet") {
throw new Error("NOT IMPLEMENTED"); throw new Error("NOT IMPLEMENTED");
} else { } else {
oldLog(a,b,c,d,e,f); oldLog(a,b,c,d,e,f);
} }
} };
try { try {
worker.send(undefined); worker.send(undefined);
@ -292,7 +292,7 @@ if (!singlethreaded) {
cluster._workersCounter++; cluster._workersCounter++;
return newWorker; return newWorker;
}; };
} };
if (process.isBun && (cluster.isMaster === undefined || (cluster.isMaster && process.env.NODE_UNIQUE_ID))) { if (process.isBun && (cluster.isMaster === undefined || (cluster.isMaster && process.env.NODE_UNIQUE_ID))) {
cluster.bunShim(); cluster.bunShim();
@ -542,6 +542,36 @@ function createRegex(regex, isPath) {
return new RegExp(searchString, modifiers); return new RegExp(searchString, modifiers);
} }
function checkForEnabledDirectoryListing(hostname) {
function matchHostname(hostnameM) {
if (typeof hostnameM == "undefined" || hostnameM == "*") {
return true;
} else if (hostname && hostnameM.indexOf("*.") == 0 && hostnameM != "*.") {
var hostnamesRoot = hostnameM.substr(2);
if (hostname == hostnamesRoot || hostname.indexOf("." + hostnamesRoot) == hostname.length - hostnamesRoot.length - 1) {
return true;
}
} else if (hostname && hostname == hostnameM) {
return true;
}
return false;
}
var main = (configJSON.enableDirectoryListing || configJSON.enableDirectoryListing === undefined);
if(!configJSON.enableDirectoryListingVHost) return main;
var vhostP = null;
configJSON.enableDirectoryListingVHost.every(function(vhost) {
if(matchHostname(vhost.host)) {
vhostP = vhost;
return false;
} else {
return true;
}
});
if(!vhostP || vhostP.enabled === undefined) return main;
else return vhostP.enabled;
}
//IP Block list object //IP Block list object
function ipBlockList(rawBlockList) { function ipBlockList(rawBlockList) {
@ -847,7 +877,6 @@ if (ips.length == 0) {
} }
var host = ips[(ips.length) - 1]; var host = ips[(ips.length) - 1];
if (!host) host = "[offline]"; if (!host) host = "[offline]";
var hp = host + ":" + port.toString();
var ipRequestCompleted = false; var ipRequestCompleted = false;
var ipRequestGotError = false; var ipRequestGotError = false;
@ -1023,6 +1052,7 @@ var disableToHTTPSRedirect = false;
var nonStandardCodesRaw = []; var nonStandardCodesRaw = [];
var disableUnusedWorkerTermination = false; var disableUnusedWorkerTermination = false;
var rewriteDirtyURLs = false; var rewriteDirtyURLs = false;
var errorPages = [];
//Get properties from config.json //Get properties from config.json
if (configJSON.blacklist != undefined) rawBlackList = configJSON.blacklist; if (configJSON.blacklist != undefined) rawBlackList = configJSON.blacklist;
@ -1049,6 +1079,7 @@ if (configJSON.disableNonEncryptedServer != undefined) disableNonEncryptedServer
if (configJSON.disableToHTTPSRedirect != undefined) disableToHTTPSRedirect = configJSON.disableToHTTPSRedirect; if (configJSON.disableToHTTPSRedirect != undefined) disableToHTTPSRedirect = configJSON.disableToHTTPSRedirect;
if (configJSON.disableUnusedWorkerTermination != undefined) disableUnusedWorkerTermination = configJSON.disableUnusedWorkerTermination; if (configJSON.disableUnusedWorkerTermination != undefined) disableUnusedWorkerTermination = configJSON.disableUnusedWorkerTermination;
if (configJSON.rewriteDirtyURLs != undefined) rewriteDirtyURLs = configJSON.rewriteDirtyURLs; if (configJSON.rewriteDirtyURLs != undefined) rewriteDirtyURLs = configJSON.rewriteDirtyURLs;
if (configJSON.errorPages != undefined) errorPages = configJSON.errorPages;
if (configJSON.wwwroot != undefined) { if (configJSON.wwwroot != undefined) {
var wwwroot = configJSON.wwwroot; var wwwroot = configJSON.wwwroot;
if (cluster.isPrimary || cluster.isPrimary === undefined) process.chdir(wwwroot); if (cluster.isPrimary || cluster.isPrimary === undefined) process.chdir(wwwroot);
@ -1112,7 +1143,7 @@ function sanitizeURL(resource) {
// Encode certain characters // Encode certain characters
resource = resource.replace(/[<>^`{|}]]/g, function (character) { resource = resource.replace(/[<>^`{|}]]/g, function (character) {
var charCode = character.charCodeAt(0); var charCode = character.charCodeAt(0);
return "%" + (charcode < 16 ? "0" : "") + charCode.toString(16).toUpperCase(); return "%" + (charCode < 16 ? "0" : "") + charCode.toString(16).toUpperCase();
}); });
var sanitizedResource = resource; var sanitizedResource = resource;
// Ensure the resource starts with a slash // Ensure the resource starts with a slash
@ -1439,7 +1470,7 @@ if (!disableMods) {
if (fs.existsSync("./serverSideScript.js") && fs.statSync("./serverSideScript.js").isFile()) { if (fs.existsSync("./serverSideScript.js") && fs.statSync("./serverSideScript.js").isFile()) {
try { try {
// Prepend necessary modules and variables to the custom server side script // Prepend necessary modules and variables to the custom server side script
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) {\r\nreturn function () {\r\nvar disableEndElseCallbackExecute = false;\r\nfunction filterHeaders(headers){for(var jsn=JSON.stringify(headers,null,2).split('\\n'),njsn=[\"{\"],i=1;i<jsn.length-1;i++)0!==jsn[i].replace(/ /g,\"\").indexOf('\":')&&(eval(\"var value = \"+(\",\"==jsn[i][jsn[i].length-1]?jsn[i].substring(0,jsn[i].length-1):jsn[i]).split('\": ')[1]),\",\"==jsn[i][jsn[i].length-1]&&i==jsn.length-2?njsn.push(jsn[i].substring(0,jsn[i].length-1)):null!=value&&njsn.push(jsn[i]));return njsn.push(\"}\"),JSON.parse(njsn.join(os.EOL))}\r\n"; 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) {\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.substr(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";
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;"; 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;";
// Write the modified server side script to the temp folder // Write the modified server side script to the temp folder
fs.writeFileSync(__dirname + "/temp/" + tempServerSideScriptName, modhead + fs.readFileSync("./serverSideScript.js") + modfoot); fs.writeFileSync(__dirname + "/temp/" + tempServerSideScriptName, modhead + fs.readFileSync("./serverSideScript.js") + modfoot);
@ -2087,8 +2118,39 @@ if (!cluster.isPrimary) {
} }
}; };
function matchHostname(hostname) {
if (typeof hostname == "undefined" || hostname == "*") {
return true;
} else if (req.headers.host && hostname.indexOf("*.") == 0 && hostname != "*.") {
var hostnamesRoot = hostname.substr(2);
if (req.headers.host == hostnamesRoot || req.headers.host.indexOf("." + hostnamesRoot) == req.headers.host.length - hostnamesRoot.length - 1) {
return true;
}
} else if (req.headers.host && req.headers.host == hostname) {
return true;
}
return false;
}
function getCustomHeaders() { function getCustomHeaders() {
var ph = JSON.parse(JSON.stringify(customHeaders)); var ph = JSON.parse(JSON.stringify(customHeaders));
if(configJSON.customHeadersVHost) {
var vhostP = null;
configJSON.customHeadersVHost.every(function(vhost) {
if(matchHostname(vhost.host)) {
vhostP = vhost;
return false;
} else {
return true;
}
});
if(vhostP && vhostP.headers) {
var phNu = JSON.parse(JSON.stringify(vhostP.headers));
Object.keys(phNu).forEach(function (phNuK) {
ph[phNuK] = phNu[phNuK];
});
}
}
Object.keys(ph).forEach(function (phk) { Object.keys(ph).forEach(function (phk) {
if (typeof ph[phk] == "string") ph[phk] = ph[phk].replace(/\{path\}/g, req.url); if (typeof ph[phk] == "string") ph[phk] = ph[phk].replace(/\{path\}/g, req.url);
}); });
@ -2158,10 +2220,69 @@ if (!cluster.isPrimary) {
//Server error calling method //Server error calling method
function callServerError(errorCode, extName, stack, ch) { function callServerError(errorCode, extName, stack, ch) {
var errorFile = errorCode.toString() + ".html"; function getErrorFileName(list, callback, _i) {
var errorFile2 = "." + errorCode.toString(); function medCallback(p) {
if (fs.existsSync(errorFile2)) errorFile = errorFile2; if(p) callback(p);
if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404; else {
if(errorCode == 404) {
fs.access(page404, fs.constants.F_OK, function(err) {
if(err) {
fs.access("." + errorCode.toString(), fs.constants.F_OK, function(err) {
try {
if(err) {
callback(errorCode.toString() + ".html");
} else {
callback("." + errorCode.toString());
}
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
});
} else {
try {
callback(page404);
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
}
});
} else {
fs.access("." + errorCode.toString(), fs.constants.F_OK, function(err) {
try {
if(err) {
callback(errorCode.toString() + ".html");
} else {
callback("." + errorCode.toString());
}
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
});
}
}
}
if(!_i) _i = 0;
if(_i >= list.length) {
medCallback(false);
return;
}
if(list[_i].scode != errorCode || !matchHostname(list[_i].host)) {
getErrorFileName(list, callback, _i+1);
return;
} else {
fs.access(list[_i].path, fs.constants.F_OK, function(err) {
if(err) {
getErrorFileName(list, callback, _i+1);
} else {
medCallback(list[_i].path);
}
});
}
}
getErrorFileName(errorPages, function(errorFile) {
if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack); if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
if (stack === undefined) stack = generateErrorStack(new Error("Unknown error")); if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
if (errorCode == 500 || errorCode == 502) { if (errorCode == 500 || errorCode == 502) {
@ -2216,6 +2337,7 @@ if (!cluster.isPrimary) {
} }
}); });
} }
});
} }
var reqport = ""; var reqport = "";
@ -2553,10 +2675,74 @@ if (!cluster.isPrimary) {
//Server error calling method //Server error calling method
function callServerError(errorCode, extName, stack, ch) { function callServerError(errorCode, extName, stack, ch) {
var errorFile = errorCode.toString() + ".html"; function getErrorFileName(list, callback, _i) {
var errorFile2 = "." + errorCode.toString(); if(err.code == "ERR_SSL_HTTP_REQUEST" && process.version && parseInt(process.version.split(".")[0].substr(1)) >= 16) {
if (fs.existsSync(errorFile2)) errorFile = errorFile2; //Disable custom error page for HTTP SSL error
if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404; callback(errorCode.toString() + ".html");
return;
}
function medCallback(p) {
if(p) callback(p);
else {
if(errorCode == 404) {
fs.access(page404, fs.constants.F_OK, function(err) {
if(err) {
fs.access("." + errorCode.toString(), fs.constants.F_OK, function(err) {
try {
if(err) {
callback(errorCode.toString() + ".html");
} else {
callback("." + errorCode.toString());
}
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
});
} else {
try {
callback(page404);
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
}
});
} else {
fs.access("." + errorCode.toString(), fs.constants.F_OK, function(err) {
try {
if(err) {
callback(errorCode.toString() + ".html");
} else {
callback("." + errorCode.toString());
}
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
});
}
}
}
if(!_i) _i = 0;
if(_i >= list.length) {
medCallback(false);
return;
}
if(list[_i].scode != errorCode) {
getErrorFileName(list, callback, _i+1);
return;
} else {
fs.access(list[_i].path, fs.constants.F_OK, function(err) {
if(err) {
getErrorFileName(list, callback, _i+1);
} else {
medCallback(list[_i].path);
}
});
}
}
getErrorFileName(errorPages, function(errorFile) {
if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack); if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
if (stack === undefined) stack = generateErrorStack(new Error("Unknown error")); if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
if (errorCode == 500 || errorCode == 502) { if (errorCode == 500 || errorCode == 502) {
@ -2618,6 +2804,7 @@ if (!cluster.isPrimary) {
}); });
} }
} }
});
} }
var reqip = socket.remoteAddress; var reqip = socket.remoteAddress;
var reqport = socket.remotePort; var reqport = socket.remotePort;
@ -2833,7 +3020,7 @@ if (!cluster.isPrimary) {
modExecute(mods, vres(req, socket, head, serverconsole)); modExecute(mods, vres(req, socket, head, serverconsole));
} }
function reqhandler(request, response, fromMain) { function reqhandler(req, res, fromMain) {
if (fromMain === undefined) fromMain = true; if (fromMain === undefined) fromMain = true;
var reqIdInt = Math.round(Math.random() * 16777216); var reqIdInt = Math.round(Math.random() * 16777216);
var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16); var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
@ -2917,21 +3104,52 @@ if (!cluster.isPrimary) {
} }
}; };
function matchHostname(hostname) {
if (typeof hostname == "undefined" || hostname == "*") {
return true;
} else if (req.headers.host && hostname.indexOf("*.") == 0 && hostname != "*.") {
var hostnamesRoot = hostname.substr(2);
if (req.headers.host == hostnamesRoot || req.headers.host.indexOf("." + hostnamesRoot) == req.headers.host.length - hostnamesRoot.length - 1) {
return true;
}
} else if (req.headers.host && req.headers.host == hostname) {
return true;
}
return false;
}
function getCustomHeaders() { function getCustomHeaders() {
var ph = JSON.parse(JSON.stringify(customHeaders)); var ph = JSON.parse(JSON.stringify(customHeaders));
if(configJSON.customHeadersVHost) {
var vhostP = null;
configJSON.customHeadersVHost.every(function(vhost) {
if(matchHostname(vhost.host)) {
vhostP = vhost;
return false;
} else {
return true;
}
});
if(vhostP && vhostP.headers) {
var phNu = JSON.parse(JSON.stringify(vhostP.headers));
Object.keys(phNu).forEach(function (phNuK) {
ph[phNuK] = phNu[phNuK];
});
}
}
Object.keys(ph).forEach(function (phk) { Object.keys(ph).forEach(function (phk) {
if (typeof ph[phk] == "string") ph[phk] = ph[phk].replace(/\{path\}/g, request.url); if (typeof ph[phk] == "string") ph[phk] = ph[phk].replace(/\{path\}/g, req.url);
}); });
return ph; return ph;
} }
//Make HTTP/1.x API-based scripts compatible with HTTP/2.0 API //Make HTTP/1.x API-based scripts compatible with HTTP/2.0 API
if (configJSON.enableHTTP2 == true && request.httpVersion == "2.0") { if (configJSON.enableHTTP2 == true && req.httpVersion == "2.0") {
try { try {
//Set HTTP/1.x methods (to prevent process warnings) //Set HTTP/1.x methods (to prevent process warnings)
response.writeHeadNodeApi = response.writeHead; res.writeHeadNodeApi = res.writeHead;
response.setHeaderNodeApi = response.setHeader; res.setHeaderNodeApi = res.setHeader;
response.writeHead = function (a, b, c) { res.writeHead = function (a, b, c) {
var table = c; var table = c;
if (typeof (b) == "object") table = b; if (typeof (b) == "object") table = b;
if (table == undefined) table = this.tHeaders; if (table == undefined) table = this.tHeaders;
@ -2943,25 +3161,25 @@ if (!cluster.isPrimary) {
delete table["connection"]; delete table["connection"];
delete table["keep-alive"]; delete table["keep-alive"];
delete table["upgrade"]; delete table["upgrade"];
return response.writeHeadNodeApi(a, table); return res.writeHeadNodeApi(a, table);
}; };
response.setHeader = function (a, b) { res.setHeader = function (a, b) {
if (a != "transfer-encoding" && a != "connection" && a != "keep-alive" && a != "upgrade") return response.setHeaderNodeApi(a, b); if (a != "transfer-encoding" && a != "connection" && a != "keep-alive" && a != "upgrade") return res.setHeaderNodeApi(a, b);
return false; return false;
}; };
//Set HTTP/1.x headers //Set HTTP/1.x headers
if (!request.headers.host) request.headers.host = request.headers[":authority"]; if (!req.headers.host) req.headers.host = req.headers[":authority"];
(request.headers[":path"] == undefined ? (function () {})() : request.url = request.headers[":path"]); (req.headers[":path"] == undefined ? (function () {})() : req.url = req.headers[":path"]);
request.protocol = request.headers[":scheme"]; req.protocol = req.headers[":scheme"];
var headers = [":path", ":method"]; var headers = [":path", ":method"];
for (var i = 0; i < headers.length; i++) { for (var i = 0; i < headers.length; i++) {
if (request.headers[headers[i]] == undefined) { if (req.headers[headers[i]] == undefined) {
var cheaders = getCustomHeaders(); var cheaders = getCustomHeaders();
cheaders["Content-Type"] = "text/html; charset=utf-8"; cheaders["Content-Type"] = "text/html; charset=utf-8";
response.writeHead(400, "Bad Request", cheaders); res.writeHead(400, "Bad Request", cheaders);
response.write("<html><head><title>400 Bad Request</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>400 Bad Request</h1><p>The request you sent is invalid. <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") + (request.headers[":authority"] == undefined ? "" : " on " + request.headers[":authority"]) + "</i></p></body></html>"); res.write("<html><head><title>400 Bad Request</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>400 Bad Request</h1><p>The request you sent is invalid. <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") + (req.headers[":authority"] == undefined ? "" : " on " + req.headers[":authority"]) + "</i></p></body></html>");
response.end(); res.end();
return; return;
} }
} }
@ -2969,32 +3187,32 @@ if (!cluster.isPrimary) {
var cheaders = getCustomHeaders(); var cheaders = getCustomHeaders();
cheaders["Content-Type"] = "text/html; charset=utf-8"; cheaders["Content-Type"] = "text/html; charset=utf-8";
cheaders[":status"] = "500"; cheaders[":status"] = "500";
response.stream.respond(cheaders); res.stream.respond(cheaders);
response.stream.write("<html><head><title>500 Internal Server Error</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>500 Internal Server Error</h1><p>The server had an unexpected error. Below, error stack is shown: </p><code>" + (stackHidden ? "[error stack hidden]" : generateErrorStack(err)).replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;") + "</code><p>Please contact with developer/administrator of the website.</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") + (request.headers[":authority"] == undefined ? "" : " on " + request.headers[":authority"]) + "</i></p></body></html>"); res.stream.write("<html><head><title>500 Internal Server Error</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>500 Internal Server Error</h1><p>The server had an unexpected error. Below, error stack is shown: </p><code>" + (stackHidden ? "[error stack hidden]" : generateErrorStack(err)).replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;") + "</code><p>Please contact with developer/administrator of the website.</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") + (req.headers[":authority"] == undefined ? "" : " on " + req.headers[":authority"]) + "</i></p></body></html>");
response.stream.end(); res.stream.end();
return; return;
} }
} }
if (request.headers["x-svr-js-from-main-thread"] == "true" && (!request.socket.remoteAddress || request.socket.remoteAddress == "::1" || request.socket.remoteAddress == "::ffff:127.0.0.1" || request.socket.remoteAddress == "127.0.0.1" || request.socket.remoteAddress == "localhost" || request.socket.remoteAddress == host || request.socket.remoteAddress == "::ffff:" + host)) { if (req.headers["x-svr-js-from-main-thread"] == "true" && (!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)) {
var headers = getCustomHeaders(); var headers = getCustomHeaders();
response.writeHead(204, "No Content", headers); res.writeHead(204, "No Content", headers);
response.end(); res.end();
return; return;
} }
request.url = fixNodeMojibakeURL(request.url); req.url = fixNodeMojibakeURL(req.url);
var headWritten = false; var headWritten = false;
var lastStatusCode = null; var lastStatusCode = null;
response.writeHeadNative = response.writeHead; res.writeHeadNative = res.writeHead;
response.writeHead = function (a, b, c) { res.writeHead = function (a, b, c) {
if (!(headWritten && process.isBun && a === lastStatusCode && b === undefined && c === undefined)) { if (!(headWritten && process.isBun && a === lastStatusCode && b === undefined && c === undefined)) {
if (headWritten) { if (headWritten) {
process.emitWarning("res.writeHead called multiple times.", { process.emitWarning("res.writeHead called multiple times.", {
code: "WARN_SVRJS_MULTIPLE_WRITEHEAD" code: "WARN_SVRJS_MULTIPLE_WRITEHEAD"
}); });
return response; return res;
} else { } else {
headWritten = true; headWritten = true;
} }
@ -3009,47 +3227,47 @@ if (!cluster.isPrimary) {
} }
lastStatusCode = a; lastStatusCode = a;
} }
response.writeHeadNative(a, b, c); res.writeHeadNative(a, b, c);
}; };
if (wwwredirect) { if (wwwredirect) {
var hostname = request.headers.host.split[":"]; var hostname = req.headers.host.split[":"];
var hostport = null; var hostport = null;
if (hostname.length > 1 && (hostname[0] != "[" || hostname[hostname.length - 1] != "]")) hostport = hostname.pop(); if (hostname.length > 1 && (hostname[0] != "[" || hostname[hostname.length - 1] != "]")) hostport = hostname.pop();
hostname = hostname.join(":"); hostname = hostname.join(":");
} }
if (wwwredirect && hostname == domain && hostname.indexOf("www.") != 0) { if (wwwredirect && hostname == domain && hostname.indexOf("www.") != 0) {
var lloc = (request.socket.encrypted ? "https" : "http") + "://" + hostname + (hostport ? ":" + hostport : ""); var lloc = (req.socket.encrypted ? "https" : "http") + "://" + hostname + (hostport ? ":" + hostport : "");
try { try {
var rheaders = getCustomHeaders(); var rheaders = getCustomHeaders();
rheaders["Location"] = lloc + (request.url.replace(/\/+/g, "/")); rheaders["Location"] = lloc + (req.url.replace(/\/+/g, "/"));
response.writeHead(301, "Redirect to WWW", rheaders); res.writeHead(301, "Redirect to WWW", rheaders);
response.end(); res.end();
} catch (err) { } catch (err) {
var cheaders = getCustomHeaders(); var cheaders = getCustomHeaders();
cheaders["Content-Type"] = "text/html; charset=utf-8"; cheaders["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(500, "Internal Server Error", cheaders); res.writeHead(500, "Internal Server Error", cheaders);
res.write("<html><head><title>500 Internal Server Error</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>500 Internal Server Error</h1><p>The server had an unexpected error. Below, error stack is shown: </p><code>" + (stackHidden ? "[error stack hidden]" : generateErrorStack(err)).replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;") + "</code><p>Please contact with developer/administrator of the website.</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") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</i></p></body></html>"); res.write("<html><head><title>500 Internal Server Error</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>500 Internal Server Error</h1><p>The server had an unexpected error. Below, error stack is shown: </p><code>" + (stackHidden ? "[error stack hidden]" : generateErrorStack(err)).replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;") + "</code><p>Please contact with developer/administrator of the website.</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") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</i></p></body></html>");
response.end(); res.end();
} }
} else { } else {
var finished = false; var finished = false;
response.on("finish", function () { res.on("finish", function () {
if (!finished) { if (!finished) {
finished = true; finished = true;
serverconsole.locmessage("Client disconnected."); serverconsole.locmessage("Client disconnected.");
} }
}); });
response.on("close", function () { res.on("close", function () {
if (!finished) { if (!finished) {
finished = true; finished = true;
serverconsole.locmessage("Client disconnected."); serverconsole.locmessage("Client disconnected.");
} }
}); });
var isProxy = false; var isProxy = false;
if (request.url.indexOf("/") != 0 && request.url != "*") isProxy = true; if (req.url.indexOf("/") != 0 && req.url != "*") isProxy = true;
serverconsole.locmessage("Somebody connected to " + (secure && fromMain ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "..."); serverconsole.locmessage("Somebody connected to " + (secure && fromMain ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "...");
if (request.socket == null) { if (req.socket == null) {
serverconsole.errmessage("Client socket is null!!!"); serverconsole.errmessage("Client socket is null!!!");
return; return;
} }
@ -3058,54 +3276,54 @@ if (!cluster.isPrimary) {
var reqip = ""; var reqip = "";
var oldport = ""; var oldport = "";
var oldip = ""; var oldip = "";
if (request.headers["x-svr-js-client"] != undefined && enableIPSpoofing) { if (req.headers["x-svr-js-client"] != undefined && enableIPSpoofing) {
var kl = request.headers["x-svr-js-client"].split(":"); var kl = req.headers["x-svr-js-client"].split(":");
reqport = kl.pop(); reqport = kl.pop();
reqip = kl.join(":"); reqip = kl.join(":");
try { try {
oldport = request.socket.remotePort; oldport = req.socket.remotePort;
oldip = request.socket.remoteAddress; oldip = req.socket.remoteAddress;
request.socket.realRemotePort = reqport; req.socket.realRemotePort = reqport;
request.socket.realRemoteAddress = reqip; req.socket.realRemoteAddress = reqip;
request.socket.originalRemotePort = oldport; req.socket.originalRemotePort = oldport;
request.socket.originalRemoteAddress = oldip; req.socket.originalRemoteAddress = oldip;
response.socket.realRemotePort = reqport; res.socket.realRemotePort = reqport;
response.socket.realRemoteAddress = reqip; res.socket.realRemoteAddress = reqip;
response.socket.originalRemotePort = oldport; res.socket.originalRemotePort = oldport;
response.socket.originalRemoteAddress = oldip; res.socket.originalRemoteAddress = oldip;
} catch (err) { } catch (err) {
//Address setting failed //Address setting failed
} }
} else if (request.headers["x-forwarded-for"] != undefined && enableIPSpoofing) { } else if (req.headers["x-forwarded-for"] != undefined && enableIPSpoofing) {
reqport = null; reqport = null;
reqip = request.headers["x-forwarded-for"].split(",")[0].replace(/ /g, ""); reqip = req.headers["x-forwarded-for"].split(",")[0].replace(/ /g, "");
if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip; if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip;
try { try {
oldport = request.socket.remotePort; oldport = req.socket.remotePort;
oldip = request.socket.remoteAddress; oldip = req.socket.remoteAddress;
request.socket.realRemotePort = reqport; req.socket.realRemotePort = reqport;
request.socket.realRemoteAddress = reqip; req.socket.realRemoteAddress = reqip;
request.socket.originalRemotePort = oldport; req.socket.originalRemotePort = oldport;
request.socket.originalRemoteAddress = oldip; req.socket.originalRemoteAddress = oldip;
response.socket.realRemotePort = reqport; res.socket.realRemotePort = reqport;
response.socket.realRemoteAddress = reqip; res.socket.realRemoteAddress = reqip;
response.socket.originalRemotePort = oldport; res.socket.originalRemotePort = oldport;
response.socket.originalRemoteAddress = oldip; res.socket.originalRemoteAddress = oldip;
} catch (err) { } catch (err) {
//Address setting failed //Address setting failed
} }
} else { } else {
reqip = request.socket.remoteAddress; reqip = req.socket.remoteAddress;
reqport = request.socket.remotePort; reqport = req.socket.remotePort;
} }
reqcounter++; reqcounter++;
if (!isProxy) serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (request.method == "GET" ? "content in " : (request.method == "POST" ? "to post content in " : (request.method == "PUT" ? "to add content in " : (request.method == "DELETE" ? "to delete content in " : (request.method == "PATCH" ? "to patch content in " : "to access content using " + request.method + " method in "))))) + (request.headers.host == undefined ? "" : request.headers.host) + request.url); if (!isProxy) 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 ? "" : req.headers.host) + req.url);
else serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (request.method == "GET" ? "content in " : (request.method == "POST" ? "to post content in " : (request.method == "PUT" ? "to add content in " : (request.method == "DELETE" ? "to delete content in " : (request.method == "PATCH" ? "to patch content in " : "to access content using " + request.method + " method in "))))) + request.url); else 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.url);
if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]); if (req.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + req.headers["user-agent"]);
var acceptEncoding = request.headers["accept-encoding"]; var acceptEncoding = req.headers["accept-encoding"];
if (!acceptEncoding) acceptEncoding = ""; if (!acceptEncoding) acceptEncoding = "";
var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header
@ -3143,9 +3361,6 @@ if (!cluster.isPrimary) {
// }); // });
// } // }
var req = request; // request var is req = request
var res = response; // response var is res = response
//Error descriptions //Error descriptions
var serverErrorDescs = { var serverErrorDescs = {
200: "The request succeeded! :)", 200: "The request succeeded! :)",
@ -3218,10 +3433,71 @@ if (!cluster.isPrimary) {
throw new TypeError("Error stack parameter needs to be either a string or an instance of Error object."); throw new TypeError("Error stack parameter needs to be either a string or an instance of Error object.");
} }
var errorFile = errorCode.toString() + ".html"; // Determine error file
var errorFile2 = "." + errorCode.toString();
if (fs.existsSync(errorFile2)) errorFile = errorFile2; function getErrorFileName(list, callback, _i) {
if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404; function medCallback(p) {
if(p) callback(p);
else {
if(errorCode == 404) {
fs.access(page404, fs.constants.F_OK, function(err) {
if(err) {
fs.access("." + errorCode.toString(), fs.constants.F_OK, function(err) {
try {
if(err) {
callback(errorCode.toString() + ".html");
} else {
callback("." + errorCode.toString());
}
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
});
} else {
try {
callback(page404);
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
}
});
} else {
fs.access("." + errorCode.toString(), fs.constants.F_OK, function(err) {
try {
if(err) {
callback(errorCode.toString() + ".html");
} else {
callback("." + errorCode.toString());
}
} catch(err2) {
callServerError(500, undefined, generateErrorStack(err2));
}
});
}
}
}
if(!_i) _i = 0;
if(_i >= list.length) {
medCallback(false);
return;
}
if(list[_i].scode != errorCode || !matchHostname(list[_i].host)) {
getErrorFileName(list, callback, _i+1);
return;
} else {
fs.access(list[_i].path, fs.constants.F_OK, function(err) {
if(err) {
getErrorFileName(list, callback, _i+1);
} else {
medCallback(list[_i].path);
}
});
}
}
getErrorFileName(errorPages, function(errorFile) {
// Generate error stack if not provided // Generate error stack if not provided
if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack); if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
@ -3266,8 +3542,8 @@ if (!cluster.isPrimary) {
fs.readFile(errorFile, function (err, data) { fs.readFile(errorFile, function (err, data) {
try { try {
if (err) throw err; if (err) throw err;
response.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, request.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")); // Replace placeholders in error response fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, req.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")); // Replace placeholders in error response
responseEnd(); responseEnd();
} catch (err) { } catch (err) {
var additionalError = 500; var additionalError = 500;
@ -3286,12 +3562,13 @@ if (!cluster.isPrimary) {
additionalError = 508; additionalError = 508;
} }
response.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
response.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p>" + ((additionalError == 404) ? "" : "<p>Additionally, a {additionalError} error occurred while loading an error page.</p>") + "<p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, request.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); // Replace placeholders in error response res.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p>" + ((additionalError == 404) ? "" : "<p>Additionally, a {additionalError} error occurred while loading an error page.</p>") + "<p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, req.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); // Replace placeholders in error response
response.end(); res.end();
} }
}); });
} }
});
} }
@ -3605,7 +3882,7 @@ if (!cluster.isPrimary) {
var hdhds = getCustomHeaders(); var hdhds = getCustomHeaders();
hdhds["Content-Type"] = "text/html; charset=utf-8"; hdhds["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(200, "OK", hdhds); res.writeHead(200, "OK", hdhds);
res.end((head == "" ? "<html><head><title>SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>" : head.replace(/<head>/i, "<head><title>SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title>")) + "<h1>SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</h1>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") + "<br/><hr/>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/>OS uptime: " + formatRelativeTime(os.uptime()) + "<br/>Total request count: " + reqcounter + "<br/>Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s" + (process.memoryUsage ? ("<br/>Memory usage of thread: " + sizify(process.memoryUsage().rss) + "B") : "") + (process.cpuUsage ? ("<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) + "%") : "") + "<br/>Thread PID: " + process.pid + "<br/>" + (foot == "" ? "</body></html>" : foot)); res.end((head == "" ? "<html><head><title>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>" : head.replace(/<head>/i, "<head><title>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title>")) + "<h1>SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</h1>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") + "<br/><hr/>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/>OS uptime: " + formatRelativeTime(os.uptime()) + "<br/>Total request count: " + reqcounter + "<br/>Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s" + (process.memoryUsage ? ("<br/>Memory usage of thread: " + sizify(process.memoryUsage().rss) + "B") : "") + (process.cpuUsage ? ("<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) + "%") : "") + "<br/>Thread PID: " + process.pid + "<br/>" + (foot == "" ? "</body></html>" : foot));
return; return;
} else if (version.indexOf("Nightly-") === 0 && (href == "/crash.svr" || (os.platform() == "win32" && href.toLowerCase() == "/crash.svr"))) { } else if (version.indexOf("Nightly-") === 0 && (href == "/crash.svr" || (os.platform() == "win32" && href.toLowerCase() == "/crash.svr"))) {
throw new Error("Intentionally crashed"); throw new Error("Intentionally crashed");
@ -3670,13 +3947,13 @@ if (!cluster.isPrimary) {
if (e || !s.isFile()) { if (e || !s.isFile()) {
fs.stat((readFrom + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) { fs.stat((readFrom + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) {
if (e || !s.isFile()) { if (e || !s.isFile()) {
properServe(); properDirectoryListingServe();
} else { } else {
stats = s; stats = s;
pth = (pth + "/index.xhtml").replace(/\/+/g, "/"); pth = (pth + "/index.xhtml").replace(/\/+/g, "/");
ext = "xhtml"; ext = "xhtml";
readFrom = "./" + pth; readFrom = "./" + pth;
properServe(); properDirectoryListingServe();
} }
}); });
} else { } else {
@ -3684,7 +3961,7 @@ if (!cluster.isPrimary) {
pth = (pth + "/index.htm").replace(/\/+/g, "/"); pth = (pth + "/index.htm").replace(/\/+/g, "/");
ext = "htm"; ext = "htm";
readFrom = "./" + pth; readFrom = "./" + pth;
properServe(); properDirectoryListingServe();
} }
}); });
} else { } else {
@ -3692,33 +3969,33 @@ if (!cluster.isPrimary) {
pth = (pth + "/index.html").replace(/\/+/g, "/"); pth = (pth + "/index.html").replace(/\/+/g, "/");
ext = "html"; ext = "html";
readFrom = "./" + pth; readFrom = "./" + pth;
properServe(); properDirectoryListingServe();
} }
}); });
} }
}); });
} else { } else {
properServe(); properDirectoryListingServe();
} }
function properServe() { function properDirectoryListingServe() {
if (stats.isDirectory()) { if (stats.isDirectory()) {
// Check if directory listing is enabled in the configuration // Check if directory listing is enabled in the configuration
if (configJSON.enableDirectoryListing || configJSON.enableDirectoryListing === undefined) { if (checkForEnabledDirectoryListing(req.headers.host)) {
var customHeaders = getCustomHeaders(); var customHeaders = getCustomHeaders();
customHeaders["Content-Type"] = "text/html; charset=utf-8"; customHeaders["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(200, http.STATUS_CODES[200], customHeaders); res.writeHead(200, http.STATUS_CODES[200], customHeaders);
// Read custom header and footer content (if available) // Read custom header and footer content (if available)
var customDirListingHeader = fs.existsSync("." + decodeURIComponent(href) + "/.dirhead".replace(/\/+/g, "/")) var customDirListingHeader = fs.existsSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/"))
? fs.readFileSync("." + decodeURIComponent(href) + "/.dirhead".replace(/\/+/g, "/")).toString() ? fs.readFileSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")).toString()
: (fs.existsSync("." + decodeURIComponent(href) + "/HEAD.html".replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) : (fs.existsSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/"))
? fs.readFileSync("." + decodeURIComponent(href) + "/HEAD.html".replace(/\/+/g, "/")).toString() ? fs.readFileSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")).toString()
: ""; : "";
var customDirListingFooter = fs.existsSync("." + decodeURIComponent(href) + "/.dirfoot".replace(/\/+/g, "/")) var customDirListingFooter = fs.existsSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/"))
? fs.readFileSync("." + decodeURIComponent(href) + "/.dirfoot".replace(/\/+/g, "/")).toString() ? fs.readFileSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")).toString()
: (fs.existsSync("." + decodeURIComponent(href) + "/FOOT.html".replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) : (fs.existsSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/"))
? fs.readFileSync("." + decodeURIComponent(href) + "/FOOT.html".replace(/\/+/g, "/")).toString() ? fs.readFileSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")).toString()
: ""; : "";
// Check if custom header has HTML tag // Check if custom header has HTML tag
@ -4207,8 +4484,8 @@ if (!cluster.isPrimary) {
if (blacklist.check(reqip) && href != "/favicon.ico") { if (blacklist.check(reqip) && href != "/favicon.ico") {
var bheaders = getCustomHeaders(); var bheaders = getCustomHeaders();
bheaders["Content-Type"] = "text/html; charset=utf8"; bheaders["Content-Type"] = "text/html; charset=utf8";
response.writeHead(403, "Client blocked", bheaders); res.writeHead(403, "Client blocked", bheaders);
response.write("<!DOCTYPE html><html><head><title>Access denied - SVR.JS</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><br/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><div style=\"height: auto; width: 70%; border-style: solid; border-width: 5; border-color: red; text-align: center; margin: 0 auto;\"><h1>ACCESS DENIED</h1><p style=\"font-size:20px\">Request from " + reqip + " is denied. The client is now in the blacklist.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</p></div></body></html>"); res.write("<!DOCTYPE html><html><head><title>Access denied - SVR.JS</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><br/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><div style=\"height: auto; width: 70%; border-style: solid; border-width: 5; border-color: red; text-align: center; margin: 0 auto;\"><h1>ACCESS DENIED</h1><p style=\"font-size:20px\">Request from " + reqip + " is denied. The client is now in the blacklist.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</p></div></body></html>");
serverconsole.errmessage("Client blocked"); serverconsole.errmessage("Client blocked");
return; return;
} }
@ -4272,15 +4549,17 @@ if (!cluster.isPrimary) {
//URL REWRITING //URL REWRITING
function rewriteURL(address, map) { function rewriteURL(address, map) {
var rewrittenAddress = address; var rewrittenAddress = address;
for (var i = 0; i < map.length; i++) { map.every(function(mapEntry) {
if (createRegex(map[i].definingRegex).test(address)) { if (matchHostname(mapEntry.host) && createRegex(mapEntry.definingRegex).test(address)) {
for (var j = 0; j < map[i].replacements.length; j++) { mapEntry.replacements.forEach(function (replacement) {
rewrittenAddress = rewrittenAddress.replace(createRegex(map[i].replacements[j].regex), map[i].replacements[j].replacement); rewrittenAddress = rewrittenAddress.replace(createRegex(replacement.regex), replacement.replacement);
} });
if (map[i].append) rewrittenAddress += map[i].append; if (mapEntry.append) rewrittenAddress += mapEntry.append;
break; return false;
} } else {
return true;
} }
});
return rewrittenAddress; return rewrittenAddress;
} }
var origHref = href; var origHref = href;
@ -4342,14 +4621,13 @@ if (!cluster.isPrimary) {
//Set response headers //Set response headers
if (!isProxy) { if (!isProxy) {
var hkh = getCustomHeaders(); var hkh = getCustomHeaders();
var hk = Object.keys(hkh); Object.keys(hkh).forEach(function(hkS) {
for (var i = 0; i < hk.length; i++) {
try { try {
response.setHeader(hk[i], hkh[hk[i]]); res.setHeader(hkS, hkh[hkS]);
} catch (err) { } catch (err) {
//Headers will not be set. //Headers will not be set.
} }
} });
} }
//Check if path is forbidden //Check if path is forbidden
@ -4375,6 +4653,7 @@ if (!cluster.isPrimary) {
var regexI = []; var regexI = [];
if (!isProxy && nonStandardCodes != undefined) { if (!isProxy && nonStandardCodes != undefined) {
for (var i = 0; i < nonStandardCodes.length; i++) { for (var i = 0; i < nonStandardCodes.length; i++) {
if(matchHostname(nonStandardCodes[i].host)) {
var isMatch = false; var isMatch = false;
if (nonStandardCodes[i].regex) { if (nonStandardCodes[i].regex) {
var createdRegex = createRegex(nonStandardCodes[i].regex, true); var createdRegex = createRegex(nonStandardCodes[i].regex, true);
@ -4405,6 +4684,7 @@ if (!cluster.isPrimary) {
} }
} }
} }
}
// Handle non-standard codes // Handle non-standard codes
if (nonscodeIndex > -1) { if (nonscodeIndex > -1) {
@ -4454,7 +4734,7 @@ if (!cluster.isPrimary) {
} else { } else {
checkIfPasswordMatches(list, password, callback, _i+1); checkIfPasswordMatches(list, password, callback, _i+1);
} }
} };
var hashedPassword = sha256(password + list[_i].salt); var hashedPassword = sha256(password + list[_i].salt);
if(list[_i].scrypt) { if(list[_i].scrypt) {
if(!crypto.scrypt) { if(!crypto.scrypt) {
@ -4462,7 +4742,7 @@ if (!cluster.isPrimary) {
return; return;
} else { } else {
var cacheEntry = scryptCache.find(function (entry) { var cacheEntry = scryptCache.find(function (entry) {
return (entry.password == hashedPassword && entry.salt == list[_i].salt) return (entry.password == hashedPassword && entry.salt == list[_i].salt);
}); });
if(cacheEntry) { if(cacheEntry) {
cb(cacheEntry.hash); cb(cacheEntry.hash);
@ -4484,7 +4764,7 @@ if (!cluster.isPrimary) {
return; return;
} else { } else {
var cacheEntry = pbkdf2Cache.find(function (entry) { var cacheEntry = pbkdf2Cache.find(function (entry) {
return (entry.password == hashedPassword && entry.salt == list[_i].salt) return (entry.password == hashedPassword && entry.salt == list[_i].salt);
}); });
if(cacheEntry) { if(cacheEntry) {
cb(cacheEntry.hash); cb(cacheEntry.hash);
@ -4506,6 +4786,7 @@ if (!cluster.isPrimary) {
} }
function authorizedCallback(bruteProtection) { function authorizedCallback(bruteProtection) {
try {
var ha = getCustomHeaders(); var ha = getCustomHeaders();
ha["WWW-Authenticate"] = "Basic realm=\"" + (authcode.realm ? authcode.realm.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\""; ha["WWW-Authenticate"] = "Basic realm=\"" + (authcode.realm ? authcode.realm.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\"";
var credentials = req.headers["authorization"]; var credentials = req.headers["authorization"];
@ -4529,13 +4810,17 @@ if (!cluster.isPrimary) {
} }
var username = decodedCredentialsMatch[1]; var username = decodedCredentialsMatch[1];
var password = decodedCredentialsMatch[2]; var password = decodedCredentialsMatch[2];
var usernameMatch = users.filter(function (entry) { var usernameMatch = [];
if(!authcode.userList || authcode.userList.indexOf(username) > -1) {
usernameMatch = users.filter(function (entry) {
return entry.name == username; return entry.name == username;
}); });
}
if(usernameMatch.length == 0) { if(usernameMatch.length == 0) {
usernameMatch.push({name: username, pass: "FAKEPASS", salt: "FAKESALT"}); //Fake credentials usernameMatch.push({name: username, pass: "FAKEPASS", salt: "FAKESALT"}); //Fake credentials
} }
checkIfPasswordMatches(usernameMatch, password, function (authorized) { checkIfPasswordMatches(usernameMatch, password, function (authorized) {
try {
if (!authorized) { if (!authorized) {
if (bruteProtection) { if (bruteProtection) {
if (process.send) { if (process.send) {
@ -4564,7 +4849,15 @@ if (!cluster.isPrimary) {
} }
modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData)); modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData));
} }
} catch(err) {
callServerError(500, undefined, generateErrorStack(err));
return;
}
}); });
} catch(err) {
callServerError(500, undefined, generateErrorStack(err));
return;
}
} }
if (authcode.disableBruteProtection) { if (authcode.disableBruteProtection) {
authorizedCallback(false); authorizedCallback(false);
@ -5507,6 +5800,7 @@ function saveConfig() {
if (configJSONobj.enableETag === undefined) configJSONobj.enableETag = true; if (configJSONobj.enableETag === undefined) configJSONobj.enableETag = true;
if (configJSONobj.disableUnusedWorkerTermination === undefined) configJSONobj.disableUnusedWorkerTermination = false; if (configJSONobj.disableUnusedWorkerTermination === undefined) configJSONobj.disableUnusedWorkerTermination = false;
if (configJSONobj.rewriteDirtyURLs === undefined) configJSONobj.rewriteDirtyURLs = false; if (configJSONobj.rewriteDirtyURLs === undefined) configJSONobj.rewriteDirtyURLs = false;
if (configJSONobj.errorPages === undefined) configJSONobj.errorPages = [];
var configString = JSON.stringify(configJSONobj, null, 2); var configString = JSON.stringify(configJSONobj, null, 2);
fs.writeFileSync(__dirname + "/config.json", configString); fs.writeFileSync(__dirname + "/config.json", configString);

View file

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>SVR.JS 3.7.5 Tests</title> <title>SVR.JS 3.8.0 Tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<style> <style>
@ -12,7 +12,7 @@
</style> </style>
</head> </head>
<body> <body>
<h1>SVR.JS 3.7.5 Tests</h1> <h1>SVR.JS 3.8.0 Tests</h1>
<h2>Directory</h2> <h2>Directory</h2>
<iframe src="/testdir" width="50%" height="300px"></iframe> <iframe src="/testdir" width="50%" height="300px"></iframe>
<h2>Directory (with query)</h2> <h2>Directory (with query)</h2>