diff --git a/src/index.js b/src/index.js
index bb851b4..e101548 100644
--- a/src/index.js
+++ b/src/index.js
@@ -19,6 +19,11 @@ process.filename = __filename;
//process.singleThreaded = false;
process.singleThreaded = true;
+process.err4xxcounter = 0;
+process.err5xxcounter = 0;
+process.reqcounter = 0;
+process.malformedcounter = 0;
+
if (process.versions) process.versions.svrjs = version; // Inject SVR.JS into process.versions
let forceSecure = false;
@@ -202,9 +207,11 @@ let middleware = [
require("./middleware/responseHeaders.js"),
require("./middleware/checkForbiddenPaths.js"),
require("./middleware/nonStandardCodesAndHttpAuthentication.js"),
- require("./middleware/redirectTrailingSlashes.js")
+ require("./middleware/redirectTrailingSlashes.js"),
// TODO: SVR.JS mods go here
// TODO: default handler
+ require("./middleware/defaultHandlerChecks.js"),
+ require("./middleware/status.js")
];
function addMiddleware(mw) {
diff --git a/src/middleware/core.js b/src/middleware/core.js
index 3d8afff..4a4df4d 100644
--- a/src/middleware/core.js
+++ b/src/middleware/core.js
@@ -8,10 +8,6 @@ const fixNodeMojibakeURL = require("../utils/urlMojibakeFixer.js");
const ipMatch = require("../utils/ipMatch.js");
const matchHostname = require("../utils/matchHostname.js");
-if (!process.err4xxcounter) process.err4xxcounter = 0;
-if (!process.err5xxcounter) process.err5xxcounter = 0;
-if (!process.reqcounter) process.reqcounter = 0;
-
module.exports = (req, res, logFacilities, config, next) => {
config.generateServerString = () => {
return generateServerString(config.exposeServerVersion);
diff --git a/src/middleware/defaultHandlerChecks.js b/src/middleware/defaultHandlerChecks.js
new file mode 100644
index 0000000..c2440d9
--- /dev/null
+++ b/src/middleware/defaultHandlerChecks.js
@@ -0,0 +1,27 @@
+const http = require("http");
+
+module.exports = (req, res, logFacilities, config, next) => {
+ if (req.isProxy) {
+ let eheaders = config.getCustomHeaders();
+ eheaders["Content-Type"] = "text/html; charset=utf-8";
+ res.writeHead(501, http.STATUS_CODES[501], eheaders);
+ res.write("
Proxy not implementedProxy not implemented
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.
" + config.generateServerString().replace(/&/g, "&").replace(//g, ">") + "
");
+ res.end();
+ logFacilities.errmessage("SVR.JS doesn't support proxy without proxy mod.");
+ return;
+ }
+
+ if (req.method == "OPTIONS") {
+ let hdss = config.getCustomHeaders();
+ hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
+ res.writeHead(204, http.STATUS_CODES[204], hdss);
+ res.end();
+ return;
+ } else if (req.method != "GET" && req.method != "POST" && req.method != "HEAD") {
+ res.error(405);
+ logFacilities.errmessage("Invaild method: " + req.method);
+ return;
+ }
+
+ next();
+}
\ No newline at end of file
diff --git a/src/middleware/status.js b/src/middleware/status.js
new file mode 100644
index 0000000..32653e1
--- /dev/null
+++ b/src/middleware/status.js
@@ -0,0 +1,37 @@
+const http = require("http");
+const os = require("os");
+const sizify = require("../utils/sizify.js");
+const svrjsInfo = require("../../svrjs.json");
+const {name} = svrjsInfo;
+
+module.exports = (req, res, logFacilities, config, next) => {
+ if (config.allowStatus && (req.parsedURL.pathname == "/svrjsstatus.svr" || (os.platform() == "win32" && req.parsedURL.pathname.toLowerCase() == "/svrjsstatus.svr"))) {
+ const formatRelativeTime = (relativeTime) => {
+ const days = Math.floor(relativeTime / 60 / (60 * 24));
+ const dateDiff = new Date(relativeTime * 1000);
+ return days + " days, " + dateDiff.getUTCHours() + " hours, " + dateDiff.getUTCMinutes() + " minutes, " + dateDiff.getUTCSeconds() + " seconds";
+ }
+ let statusBody = "";
+ statusBody += "Server version: " + config.generateServerString() + "
";
+
+ //Those entries are just dates and numbers converted/formatted to strings, so no escaping is needed.
+ statusBody += "Current time: " + new Date().toString() + "
Thread start time: " + new Date(new Date() - (process.uptime() * 1000)).toString() + "
Thread uptime: " + formatRelativeTime(Math.floor(process.uptime())) + "
";
+ statusBody += "OS uptime: " + formatRelativeTime(os.uptime()) + "
";
+ statusBody += "Total request count: " + process.reqcounter + "
";
+ statusBody += "Average request rate: " + (Math.round((process.reqcounter / process.uptime()) * 100) / 100) + " requests/s
";
+ statusBody += "Client errors (4xx): " + process.err4xxcounter + "
";
+ statusBody += "Server errors (5xx): " + process.err5xxcounter + "
";
+ statusBody += "Average error rate: " + (Math.round(((process.err4xxcounter + process.err5xxcounter) / process.reqcounter) * 10000) / 100) + "%
";
+ statusBody += "Malformed HTTP requests: " + process.malformedcounter;
+ if (process.memoryUsage) statusBody += "
Memory usage of thread: " + sizify(process.memoryUsage().rss, true) + "B";
+ if (process.cpuUsage) statusBody += "
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) + "%";
+ statusBody += "
Thread PID: " + process.pid + "
";
+
+ res.writeHead(200, http.STATUS_CODES[200], {
+ "Content-Type": "text/html; charset=utf-8"
+ });
+ res.end((res.head == "" ? "SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "" : res.head.replace(//i, "" + name.replace(/&/g, "&").replace(//g, ">") + " status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "")) + "" + name.replace(/&/g, "&").replace(//g, ">") + " status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "
" + statusBody + (res.foot == "" ? "" : res.foot));
+ return;
+ }
+ next();
+}
\ No newline at end of file
diff --git a/src/utils/sizify.js b/src/utils/sizify.js
new file mode 100644
index 0000000..37fe2de
--- /dev/null
+++ b/src/utils/sizify.js
@@ -0,0 +1,13 @@
+function sizify(bytes, addI) {
+ if (bytes == 0) return "0";
+ if (bytes < 0) bytes = -bytes;
+ const prefixes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"];
+ let prefixIndex = Math.floor(Math.log2 ? Math.log2(bytes) / 10 : (Math.log(bytes) / (Math.log(2) * 10)));
+ if (prefixIndex >= prefixes.length - 1) prefixIndex = prefixes.length - 1;
+ let prefixIndexTranslated = Math.pow(2, 10 * prefixIndex);
+ let decimalPoints = 2 - Math.floor(Math.log10 ? Math.log10(bytes / prefixIndexTranslated) : (Math.log(bytes / prefixIndexTranslated) / Math.log(10)));
+ if (decimalPoints < 0) decimalPoints = 0;
+ return (Math.ceil((bytes / prefixIndexTranslated) * Math.pow(10, decimalPoints)) / Math.pow(10, decimalPoints)) + prefixes[prefixIndex] + ((prefixIndex > 0 && addI) ? "i" : "");
+}
+
+module.exports = sizify;
\ No newline at end of file
diff --git a/tests/utils/sizify.test.js b/tests/utils/sizify.test.js
new file mode 100644
index 0000000..7a4408c
--- /dev/null
+++ b/tests/utils/sizify.test.js
@@ -0,0 +1,57 @@
+const sizify = require('../../src/utils/sizify');
+
+describe('"sizify" function', () => {
+ test('should return "0" for 0 bytes', () => {
+ expect(sizify(0)).toBe('0');
+ });
+
+ test('should handle negative bytes', () => {
+ expect(sizify(-1024)).toBe('1K');
+ });
+
+ test('should return correct size for small values', () => {
+ expect(sizify(1000)).toBe('1000');
+ expect(sizify(1024)).toBe('1K');
+ });
+
+ test('should return correct size for larger values', () => {
+ expect(sizify(1048576)).toBe('1M');
+ expect(sizify(1073741824)).toBe('1G');
+ expect(sizify(1099511627776)).toBe('1T');
+ expect(sizify(1125899906842624)).toBe('1P');
+ expect(sizify(1152921504606846976)).toBe('1E');
+ expect(sizify(1180591620717411303424)).toBe('1Z');
+ expect(sizify(1208925819614629174706176)).toBe('1Y');
+ expect(sizify(1237940039285380274899124224)).toBe('1R');
+ expect(sizify(1267650600228229401496703205376)).toBe('1Q');
+ });
+
+ test('should handle very large values', () => {
+ const largeValue = 2 ** 100; // A very large number
+ expect(sizify(largeValue)).toBe('1Q');
+ });
+
+ test('should add "i" suffix when addI is true', () => {
+ expect(sizify(1024, true)).toBe('1Ki');
+ expect(sizify(1048576, true)).toBe('1Mi');
+ expect(sizify(1073741824, true)).toBe('1Gi');
+ });
+
+ test('should not add "i" suffix when addI is false', () => {
+ expect(sizify(1024, false)).toBe('1K');
+ expect(sizify(1048576, false)).toBe('1M');
+ expect(sizify(1073741824, false)).toBe('1G');
+ });
+
+ test('should handle decimal points correctly', () => {
+ expect(sizify(1500)).toBe('1.47K');
+ expect(sizify(1500000)).toBe('1.44M');
+ expect(sizify(1500000000)).toBe('1.4G');
+ });
+
+ test('should handle edge cases', () => {
+ expect(sizify(1)).toBe('1');
+ expect(sizify(1023)).toBe('1023');
+ expect(sizify(1025)).toBe('1.01K');
+ });
+});