diff --git a/svr.js b/svr.js index e2ab47e..5e534db 100644 --- a/svr.js +++ b/svr.js @@ -392,7 +392,7 @@ try { } var inspector = undefined; try { - inspector = require("inspector"); + inspector = require("inspector"); } catch (err) { // Don't use inspector } @@ -2866,8 +2866,8 @@ if (!cluster.isPrimary) { } function vres() { - serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod."); - if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n"); + serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod."); + if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n"); } modExecute(mods, vres); } @@ -3451,353 +3451,247 @@ if (!cluster.isPrimary) { var vresCalled = false; - function vres() { - if (vresCalled) { - process.emitWarning("elseCallback() invoked multiple times.", { - code: "WARN_SVRJS_MULTIPLE_ELSECALLBACK" - }); - return; - } else { - vresCalled = true; - } + function vres() { + if (vresCalled) { + process.emitWarning("elseCallback() invoked multiple times.", { + code: "WARN_SVRJS_MULTIPLE_ELSECALLBACK" + }); + return; + } else { + vresCalled = true; + } - // Function to check the level of a path relative to the web root - function checkPathLevel(path) { - // Split the path into an array of components based on "/" - var pathComponents = path.split("/"); + // Function to check the level of a path relative to the web root + function checkPathLevel(path) { + // Split the path into an array of components based on "/" + var pathComponents = path.split("/"); - // Initialize counters for level up (..) and level down (.) - var levelUpCount = 0; - var levelDownCount = 0; + // Initialize counters for level up (..) and level down (.) + var levelUpCount = 0; + var levelDownCount = 0; - // Loop through the path components - for (var i = 0; i < pathComponents.length; i++) { - // If the component is "..", decrement the levelUpCount - if (".." === pathComponents[i]) { - levelUpCount--; - } - // If the component is not "." or an empty string, increment the levelDownCount - else if ("." !== pathComponents[i] && "" !== pathComponents[i]) { - levelDownCount++; - } + // Loop through the path components + for (var i = 0; i < pathComponents.length; i++) { + // If the component is "..", decrement the levelUpCount + if (".." === pathComponents[i]) { + levelUpCount--; } - - // Calculate the overall level by subtracting levelUpCount from levelDownCount - var overallLevel = levelDownCount - levelUpCount; - - // Return the overall level - return overallLevel; - } - - - if (isProxy) { - var eheaders = getCustomHeaders(); - eheaders["Content-Type"] = "text/html; charset=utf-8"; - res.writeHead(501, http.STATUS_CODES[501], eheaders); - res.write("Proxy not implemented

Proxy 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.

" + (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, ">") + "

"); - res.end(); - serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod."); - return; - } - - if (req.method == "OPTIONS") { - var hdss = 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") { - callServerError(405); - serverconsole.errmessage("Invaild method: " + req.method); - return; - } - - if (allowStatus && (href == "/svrjsstatus.svr" || (os.platform() == "win32" && href.toLowerCase() == "/svrjsstatus.svr"))) { - function formatRelativeTime(relativeTime) { - var days = Math.floor(relativeTime / 60 / (60 * 24)); - var dateDiff = new Date(relativeTime * 1000); - return days + " days, " + dateDiff.getUTCHours() + " hours, " + dateDiff.getUTCMinutes() + " minutes, " + dateDiff.getUTCSeconds() + " seconds"; + // If the component is not "." or an empty string, increment the levelDownCount + else if ("." !== pathComponents[i] && "" !== pathComponents[i]) { + levelDownCount++; } - var statusBody = ""; - 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, ">") + "

"; - - //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: " + reqcounter + "
"; - statusBody += "Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s
"; - statusBody += "Client errors (4xx): " + err4xxcounter + "
"; - statusBody += "Server errors (5xx): " + err5xxcounter + "
"; - statusBody += "Average error rate: " + (Math.round(((err4xxcounter + err5xxcounter) / reqcounter) * 10000) / 100) + "%
"; - statusBody += "Malformed HTTP requests: " + 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((head == "" ? "SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "" : head.replace(//i, "SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "")) + "

SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

" + statusBody + (foot == "" ? "" : foot)); - return; } - var dHref = decodeURIComponent(href); - var readFrom = "." + dHref; - var dirImagesMissing = false; - fs.stat(readFrom, function (err, stats) { - if (err) { - if (err.code == "ENOENT") { - if (__dirname != process.cwd() && dHref.match(/^\/\.dirimages\/(?:(?!\.png$).)+\.png$/)) { - dirImagesMissing = true; - readFrom = __dirname + dHref; - } else { - callServerError(404); - serverconsole.errmessage("Resource not found."); - return; - } - } else if (err.code == "ENOTDIR") { - callServerError(404); // Assume that file doesn't exist. + // Calculate the overall level by subtracting levelUpCount from levelDownCount + var overallLevel = levelDownCount - levelUpCount; + + // Return the overall level + return overallLevel; + } + + + if (isProxy) { + var eheaders = getCustomHeaders(); + eheaders["Content-Type"] = "text/html; charset=utf-8"; + res.writeHead(501, http.STATUS_CODES[501], eheaders); + res.write("Proxy not implemented

Proxy 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.

" + (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, ">") + "

"); + res.end(); + serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod."); + return; + } + + if (req.method == "OPTIONS") { + var hdss = 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") { + callServerError(405); + serverconsole.errmessage("Invaild method: " + req.method); + return; + } + + if (allowStatus && (href == "/svrjsstatus.svr" || (os.platform() == "win32" && href.toLowerCase() == "/svrjsstatus.svr"))) { + function formatRelativeTime(relativeTime) { + var days = Math.floor(relativeTime / 60 / (60 * 24)); + var dateDiff = new Date(relativeTime * 1000); + return days + " days, " + dateDiff.getUTCHours() + " hours, " + dateDiff.getUTCMinutes() + " minutes, " + dateDiff.getUTCSeconds() + " seconds"; + } + var statusBody = ""; + 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, ">") + "

"; + + //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: " + reqcounter + "
"; + statusBody += "Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s
"; + statusBody += "Client errors (4xx): " + err4xxcounter + "
"; + statusBody += "Server errors (5xx): " + err5xxcounter + "
"; + statusBody += "Average error rate: " + (Math.round(((err4xxcounter + err5xxcounter) / reqcounter) * 10000) / 100) + "%
"; + statusBody += "Malformed HTTP requests: " + 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((head == "" ? "SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "" : head.replace(//i, "SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "")) + "

SVR.JS status" + (req.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

" + statusBody + (foot == "" ? "" : foot)); + return; + } + + var dHref = decodeURIComponent(href); + var readFrom = "." + dHref; + var dirImagesMissing = false; + fs.stat(readFrom, function (err, stats) { + if (err) { + if (err.code == "ENOENT") { + if (__dirname != process.cwd() && dHref.match(/^\/\.dirimages\/(?:(?!\.png$).)+\.png$/)) { + dirImagesMissing = true; + readFrom = __dirname + dHref; + } else { + callServerError(404); serverconsole.errmessage("Resource not found."); return; - } else if (err.code == "EACCES") { - callServerError(403); - serverconsole.errmessage("Access denied."); - return; - } else if (err.code == "ENAMETOOLONG") { - callServerError(414); - return; - } else if (err.code == "EMFILE") { - callServerError(503); - return; - } else if (err.code == "ELOOP") { - callServerError(508); // The symbolic link loop is detected during file system operations. - serverconsole.errmessage("Symbolic link loop detected."); - return; - } else { - callServerError(500, err); - return; } - } - - // Check if index file exists - if (!dirImagesMissing && (req.url == "/" || stats.isDirectory())) { - fs.stat((readFrom + "/index.html").replace(/\/+/g, "/"), function (e, s) { - if (e || !s.isFile()) { - fs.stat((readFrom + "/index.htm").replace(/\/+/g, "/"), function (e, s) { - if (e || !s.isFile()) { - fs.stat((readFrom + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) { - if (e || !s.isFile()) { - properDirectoryListingAndStaticFileServe(); - } else { - stats = s; - ext = "xhtml"; - readFrom = (readFrom + "/index.xhtml").replace(/\/+/g, "/"); - properDirectoryListingAndStaticFileServe(); - } - }); - } else { - stats = s; - ext = "htm"; - readFrom = (readFrom + "/index.htm").replace(/\/+/g, "/"); - properDirectoryListingAndStaticFileServe(); - } - }); - } else { - stats = s; - ext = "html"; - readFrom = (readFrom + "/index.html").replace(/\/+/g, "/"); - properDirectoryListingAndStaticFileServe(); - } - }); - } else if (dirImagesMissing) { - fs.stat(readFrom, function (e, s) { - if (e || !s.isFile()) { - properDirectoryListingAndStaticFileServe(); - } else { - stats = s; - properDirectoryListingAndStaticFileServe(); - } - }); + } else if (err.code == "ENOTDIR") { + callServerError(404); // Assume that file doesn't exist. + serverconsole.errmessage("Resource not found."); + return; + } else if (err.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + return; + } else if (err.code == "ENAMETOOLONG") { + callServerError(414); + return; + } else if (err.code == "EMFILE") { + callServerError(503); + return; + } else if (err.code == "ELOOP") { + callServerError(508); // The symbolic link loop is detected during file system operations. + serverconsole.errmessage("Symbolic link loop detected."); + return; } else { - properDirectoryListingAndStaticFileServe(); + callServerError(500, err); + return; } + } - function properDirectoryListingAndStaticFileServe() { - if (stats.isFile()) { - var acceptEncoding = req.headers["accept-encoding"]; - if (!acceptEncoding) acceptEncoding = ""; - - var filelen = stats.size; - - // ETag code - var fileETag = undefined; - if (configJSON.enableETag == undefined || configJSON.enableETag) { - fileETag = generateETag(href, stats); - // Check if the client's request matches the ETag value (If-None-Match) - var clientETag = req.headers["if-none-match"]; - if (clientETag === fileETag) { - res.writeHead(304, http.STATUS_CODES[304], { - "ETag": clientETag + // Check if index file exists + if (!dirImagesMissing && (req.url == "/" || stats.isDirectory())) { + fs.stat((readFrom + "/index.html").replace(/\/+/g, "/"), function (e, s) { + if (e || !s.isFile()) { + fs.stat((readFrom + "/index.htm").replace(/\/+/g, "/"), function (e, s) { + if (e || !s.isFile()) { + fs.stat((readFrom + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) { + if (e || !s.isFile()) { + properDirectoryListingAndStaticFileServe(); + } else { + stats = s; + ext = "xhtml"; + readFrom = (readFrom + "/index.xhtml").replace(/\/+/g, "/"); + properDirectoryListingAndStaticFileServe(); + } }); - res.end(); - return; + } else { + stats = s; + ext = "htm"; + readFrom = (readFrom + "/index.htm").replace(/\/+/g, "/"); + properDirectoryListingAndStaticFileServe(); } + }); + } else { + stats = s; + ext = "html"; + readFrom = (readFrom + "/index.html").replace(/\/+/g, "/"); + properDirectoryListingAndStaticFileServe(); + } + }); + } else if (dirImagesMissing) { + fs.stat(readFrom, function (e, s) { + if (e || !s.isFile()) { + properDirectoryListingAndStaticFileServe(); + } else { + stats = s; + properDirectoryListingAndStaticFileServe(); + } + }); + } else { + properDirectoryListingAndStaticFileServe(); + } - // Check if the client's request doesn't match the ETag value (If-Match) - var ifMatchETag = req.headers["if-match"]; - if (ifMatchETag && ifMatchETag !== "*" && ifMatchETag !== fileETag) { - callServerError(412, { - "ETag": clientETag - }); - return; - } + function properDirectoryListingAndStaticFileServe() { + if (stats.isFile()) { + var acceptEncoding = req.headers["accept-encoding"]; + if (!acceptEncoding) acceptEncoding = ""; + + var filelen = stats.size; + + // ETag code + var fileETag = undefined; + if (configJSON.enableETag == undefined || configJSON.enableETag) { + fileETag = generateETag(href, stats); + // Check if the client's request matches the ETag value (If-None-Match) + var clientETag = req.headers["if-none-match"]; + if (clientETag === fileETag) { + res.writeHead(304, http.STATUS_CODES[304], { + "ETag": clientETag + }); + res.end(); + return; } - // Handle partial content request - if (ext != "html" && req.headers["range"]) { - try { - var rhd = getCustomHeaders(); - rhd["Accept-Ranges"] = "bytes"; - rhd["Content-Range"] = "bytes */" + filelen; - var regexmatch = req.headers["range"].match(/bytes=([0-9]*)-([0-9]*)/); - if (!regexmatch) { + // Check if the client's request doesn't match the ETag value (If-Match) + var ifMatchETag = req.headers["if-match"]; + if (ifMatchETag && ifMatchETag !== "*" && ifMatchETag !== fileETag) { + callServerError(412, { + "ETag": clientETag + }); + return; + } + } + + // Handle partial content request + if (ext != "html" && req.headers["range"]) { + try { + var rhd = getCustomHeaders(); + rhd["Accept-Ranges"] = "bytes"; + rhd["Content-Range"] = "bytes */" + filelen; + var regexmatch = req.headers["range"].match(/bytes=([0-9]*)-([0-9]*)/); + if (!regexmatch) { + callServerError(416, rhd); + } else { + // Process the partial content request + var beginOrig = regexmatch[1]; + var endOrig = regexmatch[2]; + var begin = 0; + var end = filelen - 1; + if (beginOrig == "" && endOrig == "") { callServerError(416, rhd); + return; + } else if (beginOrig == "") { + begin = end - parseInt(endOrig) + 1; } else { - // Process the partial content request - var beginOrig = regexmatch[1]; - var endOrig = regexmatch[2]; - var begin = 0; - var end = filelen - 1; - if (beginOrig == "" && endOrig == "") { - callServerError(416, rhd); - return; - } else if (beginOrig == "") { - begin = end - parseInt(endOrig) + 1; - } else { - begin = parseInt(beginOrig); - if (endOrig != "") end = parseInt(endOrig); - } - if (begin > end || begin < 0 || begin > filelen - 1) { - callServerError(416, rhd); - return; - } - if (end > filelen - 1) end = filelen - 1; - rhd["Content-Range"] = "bytes " + begin + "-" + end + "/" + filelen; - rhd["Content-Length"] = end - begin + 1; - if (!(mime.contentType(ext) == false) && ext != "") rhd["Content-Type"] = mime.contentType(ext); - if (fileETag) rhd["ETag"] = fileETag; - - if (req.method != "HEAD") { - var readStream = fs.createReadStream(readFrom, { - start: begin, - end: end - }); - readStream.on("error", function (err) { - if (err.code == "ENOENT") { - callServerError(404); - serverconsole.errmessage("Resource not found."); - } else if (err.code == "ENOTDIR") { - callServerError(404); // Assume that file doesn't exist. - serverconsole.errmessage("Resource not found."); - } else if (err.code == "EACCES") { - callServerError(403); - serverconsole.errmessage("Access denied."); - } else if (err.code == "ENAMETOOLONG") { - callServerError(414); - } else if (err.code == "EMFILE") { - callServerError(503); - } else if (err.code == "ELOOP") { - callServerError(508); // The symbolic link loop is detected during file system operations. - serverconsole.errmessage("Symbolic link loop detected."); - } else { - callServerError(500, err); - } - }).on("open", function () { - try { - res.writeHead(206, http.STATUS_CODES[206], rhd); - readStream.pipe(res); - serverconsole.resmessage("Client successfully received content."); - } catch (err) { - callServerError(500, err); - } - }); - } else { - res.writeHead(206, http.STATUS_CODES[206], rhd); - res.end(); - } + begin = parseInt(beginOrig); + if (endOrig != "") end = parseInt(endOrig); } - } catch (err) { - callServerError(500, err); - } - } else { - // Helper function to check if compression is allowed for the file - function canCompress(path, list) { - var canCompress = true; - for (var i = 0; i < list.length; i++) { - if (createRegex(list[i], true).test(path)) { - canCompress = false; - break; - } + if (begin > end || begin < 0 || begin > filelen - 1) { + callServerError(416, rhd); + return; } - return canCompress; - } - - var useBrotli = (ext != "br" && filelen > 256 && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/)); - var useDeflate = (ext != "zip" && filelen > 256 && acceptEncoding.match(/\bdeflate\b/)); - var useGzip = (ext != "gz" && filelen > 256 && acceptEncoding.match(/\bgzip\b/)); - - var isCompressable = true; - try { - // Check for files not to compressed and compression enabling setting. Also check for browser quirks and adjust compression accordingly - if((!useBrotli && !useDeflate && !useGzip) || configJSON.enableCompression !== true || !canCompress(href, dontCompress)) { - isCompressable = false; // Compression is disabled - } else if (ext != "html" && ext != "htm" && ext != "xhtml" && ext != "xht" && ext != "shtml") { - 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"]))) { - isCompressable = false; // Netscape 4.x doesn't handle compressed data properly outside of HTML documents. - } else if (/^w3m\/[^ ]*$/.test(req.headers["user-agent"])) { - isCompressable = false; // w3m doesn't handle compressed data properly outside of HTML documents. - } - } else { - 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"]))) { - isCompressable = false; // Netscape 4.06-4.08 doesn't handle compressed data properly. - } - } - } catch (err) { - callServerError(500, err); - return; - } - - // Bun 1.1 has definition for zlib.createBrotliCompress, but throws an error while invoking the function. - if (process.isBun && useBrotli && isCompressable) { - try { - zlib.createBrotliCompress(); - } catch (err) { - useBrotli = false; - } - } - - try { - var hdhds = {}; - if (useBrotli && isCompressable) { - hdhds["Content-Encoding"] = "br"; - } else if (useDeflate && isCompressable) { - hdhds["Content-Encoding"] = "deflate"; - } else if (useGzip && isCompressable) { - hdhds["Content-Encoding"] = "gzip"; - } else { - if (ext == "html") { - hdhds["Content-Length"] = head.length + filelen + foot.length; - } else { - hdhds["Content-Length"] = filelen; - } - } - if (ext != "html") hdhds["Accept-Ranges"] = "bytes"; - delete hdhds["Content-Type"]; - if (!(mime.contentType(ext) == false) && ext != "") hdhds["Content-Type"] = mime.contentType(ext); - if (fileETag) hdhds["ETag"] = fileETag; + if (end > filelen - 1) end = filelen - 1; + rhd["Content-Range"] = "bytes " + begin + "-" + end + "/" + filelen; + rhd["Content-Length"] = end - begin + 1; + if (!(mime.contentType(ext) == false) && ext != "") rhd["Content-Type"] = mime.contentType(ext); + if (fileETag) rhd["ETag"] = fileETag; if (req.method != "HEAD") { - var readStream = fs.createReadStream(readFrom); + var readStream = fs.createReadStream(readFrom, { + start: begin, + end: end + }); readStream.on("error", function (err) { if (err.code == "ENOENT") { callServerError(404); @@ -3820,208 +3714,314 @@ if (!cluster.isPrimary) { } }).on("open", function () { try { - var resStream = {}; - if (useBrotli && isCompressable) { - resStream = zlib.createBrotliCompress(); - resStream.pipe(res); - } else if (useDeflate && isCompressable) { - resStream = zlib.createDeflateRaw(); - resStream.pipe(res); - } else if (useGzip && isCompressable) { - resStream = zlib.createGzip(); - resStream.pipe(res); - } else { - resStream = res; - } - if (ext == "html") { - function afterWriteCallback() { - readStream.on("end", function () { - resStream.end(foot); - }); - readStream.pipe(resStream, { - end: false - }); - } - res.writeHead(200, http.STATUS_CODES[200], hdhds); - if (!resStream.write(head)) { - resStream.on("drain", afterWriteCallback); - } else { - process.nextTick(afterWriteCallback); - } - } else { - res.writeHead(200, http.STATUS_CODES[200], hdhds); - readStream.pipe(resStream); - } + res.writeHead(206, http.STATUS_CODES[206], rhd); + readStream.pipe(res); serverconsole.resmessage("Client successfully received content."); } catch (err) { callServerError(500, err); } }); } else { - res.writeHead(200, http.STATUS_CODES[200], hdhds); + res.writeHead(206, http.STATUS_CODES[206], rhd); res.end(); - serverconsole.resmessage("Client successfully received content."); } + } + } catch (err) { + callServerError(500, err); + } + } else { + // Helper function to check if compression is allowed for the file + function canCompress(path, list) { + var canCompress = true; + for (var i = 0; i < list.length; i++) { + if (createRegex(list[i], true).test(path)) { + canCompress = false; + break; + } + } + return canCompress; + } + + var useBrotli = (ext != "br" && filelen > 256 && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/)); + var useDeflate = (ext != "zip" && filelen > 256 && acceptEncoding.match(/\bdeflate\b/)); + var useGzip = (ext != "gz" && filelen > 256 && acceptEncoding.match(/\bgzip\b/)); + + var isCompressable = true; + try { + // Check for files not to compressed and compression enabling setting. Also check for browser quirks and adjust compression accordingly + if((!useBrotli && !useDeflate && !useGzip) || configJSON.enableCompression !== true || !canCompress(href, dontCompress)) { + isCompressable = false; // Compression is disabled + } else if (ext != "html" && ext != "htm" && ext != "xhtml" && ext != "xht" && ext != "shtml") { + 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"]))) { + isCompressable = false; // Netscape 4.x doesn't handle compressed data properly outside of HTML documents. + } else if (/^w3m\/[^ ]*$/.test(req.headers["user-agent"])) { + isCompressable = false; // w3m doesn't handle compressed data properly outside of HTML documents. + } + } else { + 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"]))) { + isCompressable = false; // Netscape 4.06-4.08 doesn't handle compressed data properly. + } + } + } catch (err) { + callServerError(500, err); + return; + } + + // Bun 1.1 has definition for zlib.createBrotliCompress, but throws an error while invoking the function. + if (process.isBun && useBrotli && isCompressable) { + try { + zlib.createBrotliCompress(); } catch (err) { - callServerError(500, err); + useBrotli = false; } } - } else if (stats.isDirectory()) { - // Check if directory listing is enabled in the configuration - if (checkForEnabledDirectoryListing(req.headers.host, req.socket ? req.socket.localAddress : undefined)) { - var customDirListingHeader = ""; - var customDirListingFooter = ""; - function getCustomDirListingHeader(callback) { - fs.readFile(("." + dHref + "/.dirhead").replace(/\/+/g, "/"), function (err, data) { - if (err) { - if (err.code == "ENOENT" || err.code == "EISDIR") { - if (os.platform != "win32" || href != "/") { - fs.readFile(("." + dHref + "/HEAD.html").replace(/\/+/g, "/"), function (err, data) { - if (err) { - if (err.code == "ENOENT" || err.code == "EISDIR") { - callback(); - } else { - callServerError(500, err); - } - } else { - customDirListingHeader = data.toString(); - callback(); - } + try { + var hdhds = {}; + if (useBrotli && isCompressable) { + hdhds["Content-Encoding"] = "br"; + } else if (useDeflate && isCompressable) { + hdhds["Content-Encoding"] = "deflate"; + } else if (useGzip && isCompressable) { + hdhds["Content-Encoding"] = "gzip"; + } else { + if (ext == "html") { + hdhds["Content-Length"] = head.length + filelen + foot.length; + } else { + hdhds["Content-Length"] = filelen; + } + } + if (ext != "html") hdhds["Accept-Ranges"] = "bytes"; + delete hdhds["Content-Type"]; + if (!(mime.contentType(ext) == false) && ext != "") hdhds["Content-Type"] = mime.contentType(ext); + if (fileETag) hdhds["ETag"] = fileETag; + + if (req.method != "HEAD") { + var readStream = fs.createReadStream(readFrom); + readStream.on("error", function (err) { + if (err.code == "ENOENT") { + callServerError(404); + serverconsole.errmessage("Resource not found."); + } else if (err.code == "ENOTDIR") { + callServerError(404); // Assume that file doesn't exist. + serverconsole.errmessage("Resource not found."); + } else if (err.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + } else if (err.code == "ENAMETOOLONG") { + callServerError(414); + } else if (err.code == "EMFILE") { + callServerError(503); + } else if (err.code == "ELOOP") { + callServerError(508); // The symbolic link loop is detected during file system operations. + serverconsole.errmessage("Symbolic link loop detected."); + } else { + callServerError(500, err); + } + }).on("open", function () { + try { + var resStream = {}; + if (useBrotli && isCompressable) { + resStream = zlib.createBrotliCompress(); + resStream.pipe(res); + } else if (useDeflate && isCompressable) { + resStream = zlib.createDeflateRaw(); + resStream.pipe(res); + } else if (useGzip && isCompressable) { + resStream = zlib.createGzip(); + resStream.pipe(res); + } else { + resStream = res; + } + if (ext == "html") { + function afterWriteCallback() { + readStream.on("end", function () { + resStream.end(foot); }); + readStream.pipe(resStream, { + end: false + }); + } + res.writeHead(200, http.STATUS_CODES[200], hdhds); + if (!resStream.write(head)) { + resStream.on("drain", afterWriteCallback); } else { - callback(); + process.nextTick(afterWriteCallback); } } else { - callServerError(500, err); + res.writeHead(200, http.STATUS_CODES[200], hdhds); + readStream.pipe(resStream); } - } else { - customDirListingHeader = data.toString(); - callback(); + serverconsole.resmessage("Client successfully received content."); + } catch (err) { + callServerError(500, err); } }); + } else { + res.writeHead(200, http.STATUS_CODES[200], hdhds); + res.end(); + serverconsole.resmessage("Client successfully received content."); } + } catch (err) { + callServerError(500, err); + } + } + } else if (stats.isDirectory()) { + // Check if directory listing is enabled in the configuration + if (checkForEnabledDirectoryListing(req.headers.host, req.socket ? req.socket.localAddress : undefined)) { + var customDirListingHeader = ""; + var customDirListingFooter = ""; - function getCustomDirListingFooter(callback) { - fs.readFile(("." + dHref + "/.dirfoot").replace(/\/+/g, "/"), function (err, data) { - if (err) { - if (err.code == "ENOENT" || err.code == "EISDIR") { - if (os.platform != "win32" || href != "/") { - fs.readFile(("." + dHref + "/FOOT.html").replace(/\/+/g, "/"), function (err, data) { - if (err) { - if (err.code == "ENOENT" || err.code == "EISDIR") { - callback(); - } else { - callServerError(500, err); - } - } else { - customDirListingFooter = data.toString(); + function getCustomDirListingHeader(callback) { + fs.readFile(("." + dHref + "/.dirhead").replace(/\/+/g, "/"), function (err, data) { + if (err) { + if (err.code == "ENOENT" || err.code == "EISDIR") { + if (os.platform != "win32" || href != "/") { + fs.readFile(("." + dHref + "/HEAD.html").replace(/\/+/g, "/"), function (err, data) { + if (err) { + if (err.code == "ENOENT" || err.code == "EISDIR") { callback(); + } else { + callServerError(500, err); } - }); - } else { - callback(); - } + } else { + customDirListingHeader = data.toString(); + callback(); + } + }); } else { - callServerError(500, err); + callback(); } } else { - customDirListingFooter = data.toString(); - callback(); + callServerError(500, err); } - }); - } + } else { + customDirListingHeader = data.toString(); + callback(); + } + }); + } - // Read custom header and footer content (if available) - getCustomDirListingHeader(function () { - getCustomDirListingFooter(function () { - // Check if custom header has HTML tag - var headerHasHTMLTag = customDirListingHeader.replace(/|$)/g, "").match(/])*(?:>|$)/i); + function getCustomDirListingFooter(callback) { + fs.readFile(("." + dHref + "/.dirfoot").replace(/\/+/g, "/"), function (err, data) { + if (err) { + if (err.code == "ENOENT" || err.code == "EISDIR") { + if (os.platform != "win32" || href != "/") { + fs.readFile(("." + dHref + "/FOOT.html").replace(/\/+/g, "/"), function (err, data) { + if (err) { + if (err.code == "ENOENT" || err.code == "EISDIR") { + callback(); + } else { + callServerError(500, err); + } + } else { + customDirListingFooter = data.toString(); + callback(); + } + }); + } else { + callback(); + } + } else { + callServerError(500, err); + } + } else { + customDirListingFooter = data.toString(); + callback(); + } + }); + } - // Generate HTML head and footer based on configuration and custom content - var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == "" ? - (!headerHasHTMLTag ? - "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "" : - customDirListingHeader.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) : - head.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) + + // Read custom header and footer content (if available) + getCustomDirListingHeader(function () { + getCustomDirListingFooter(function () { + // Check if custom header has HTML tag + var headerHasHTMLTag = customDirListingHeader.replace(/|$)/g, "").match(/])*(?:>|$)/i); + + // Generate HTML head and footer based on configuration and custom content + var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == "" ? + (!headerHasHTMLTag ? + "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "" : + customDirListingHeader.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) : + head.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) + (!headerHasHTMLTag ? customDirListingHeader : "") + "

Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(//g, ">") + "

" + (checkPathLevel(decodeURIComponent(origHref)) < 1 ? "" : ""); - var htmlFoot = "
Filename Size Date
\"[RET]\"Return

" + (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, ">") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

" + customDirListingFooter + (!configJSON.enableDirectoryListingWithDefaultHead || foot == "" ? "" : foot); + var htmlFoot = "

" + (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, ">") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

" + customDirListingFooter + (!configJSON.enableDirectoryListingWithDefaultHead || foot == "" ? "" : foot); - if (fs.existsSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/"))) { - htmlFoot = "
" + fs.readFileSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/")) + htmlFoot; - } + if (fs.existsSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/"))) { + htmlFoot = "
" + fs.readFileSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/")) + htmlFoot; + } - fs.readdir(readFrom, function (err, list) { - try { - if (err) throw err; - list = list.sort(); + fs.readdir(readFrom, function (err, list) { + try { + if (err) throw err; + list = list.sort(); - // Function to get stats for all files in the directory - function getStatsForAllFilesI(fileList, callback, prefix, pushArray, index) { - if (fileList.length == 0) { - callback(pushArray); - return; - } + // Function to get stats for all files in the directory + function getStatsForAllFilesI(fileList, callback, prefix, pushArray, index) { + if (fileList.length == 0) { + callback(pushArray); + return; + } - fs.stat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) { - if (err) { - fs.lstat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) { - pushArray.push({ - name: fileList[index], - stats: err ? null : stats, - errored: true - }); - if (index < fileList.length - 1) { - getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1); - } else { - callback(pushArray); - } - }); - } else { + fs.stat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) { + if (err) { + fs.lstat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) { pushArray.push({ name: fileList[index], - stats: stats, - errored: false + stats: err ? null : stats, + errored: true }); if (index < fileList.length - 1) { getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1); } else { callback(pushArray); } + }); + } else { + pushArray.push({ + name: fileList[index], + stats: stats, + errored: false + }); + if (index < fileList.length - 1) { + getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1); + } else { + callback(pushArray); } - }); - } + } + }); + } - // Wrapper function to get stats for all files - function getStatsForAllFiles(fileList, prefix, callback) { - if (!prefix) prefix = ""; - getStatsForAllFilesI(fileList, callback, prefix, [], 0); - } + // Wrapper function to get stats for all files + function getStatsForAllFiles(fileList, prefix, callback) { + if (!prefix) prefix = ""; + getStatsForAllFilesI(fileList, callback, prefix, [], 0); + } - // Get stats for all files in the directory and generate the listing - getStatsForAllFiles(list, readFrom, function (filelist) { - var directoryListingRows = []; - for (var i = 0; i < filelist.length; i++) { - if (filelist[i].name[0] !== ".") { - var estats = filelist[i].stats; - var ename = filelist[i].name; - var eext = ename.match(/\.([^.]+)$/); - eext = eext ? eext[1] : ""; - var emime = eext ? mime.contentType(eext) : false; - if (filelist[i].errored) { - directoryListingRows.push( - "\"[BAD]\"\"[BAD]\"" + ename.replace(/&/g, "&").replace(//g, ">") + "-" + (estats ? estats.mtime.toDateString() : "-") + "\r\n" - ); - } else { - var entry = "\"[alt]\"\"[alt]\"" + @@ -4032,11 +4032,11 @@ if (!cluster.isPrimary) { estats.mtime.toDateString() + "\r\n"; - // Determine the file type and set the appropriate image and alt text - if (estats.isDirectory()) { - entry = entry.replace("[img]", "/.dirimages/directory.png").replace("[alt]", "[DIR]"); - } else if (!estats.isFile()) { - entry = "\"[alt]\"\"[alt]\"" + ename.replace(/&/g, "&").replace(//g, ">") + @@ -4044,98 +4044,98 @@ if (!cluster.isPrimary) { estats.mtime.toDateString() + "\r\n"; - // Determine the special file types (block device, character device, etc.) - if (estats.isBlockDevice()) { - entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[BLK]"); - } else if (estats.isCharacterDevice()) { - entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[CHR]"); - } else if (estats.isFIFO()) { - entry = entry.replace("[img]", "/.dirimages/fifo.png").replace("[alt]", "[FIF]"); - } else if (estats.isSocket()) { - entry = entry.replace("[img]", "/.dirimages/socket.png").replace("[alt]", "[SCK]"); - } - } else if (ename.match(/README|LICEN[SC]E/i)) { - entry = entry.replace("[img]", "/.dirimages/important.png").replace("[alt]", "[IMP]"); - } else if (eext.match(/^(?:[xs]?html?|xml)$/i)) { - entry = entry.replace("[img]", "/.dirimages/html.png").replace("[alt]", (eext == "xml" ? "[XML]" : "[HTM]")); - } else if (eext == "js") { - entry = entry.replace("[img]", "/.dirimages/javascript.png").replace("[alt]", "[JS ]"); - } else if (eext == "php") { - entry = entry.replace("[img]", "/.dirimages/php.png").replace("[alt]", "[PHP]"); - } else if (eext == "css") { - entry = entry.replace("[img]", "/.dirimages/css.png").replace("[alt]", "[CSS]"); - } else if (emime && emime.split("/")[0] == "image") { - entry = entry.replace("[img]", "/.dirimages/image.png").replace("[alt]", (eext == "ico" ? "[ICO]" : "[IMG]")); - } else if (emime && emime.split("/")[0] == "font") { - entry = entry.replace("[img]", "/.dirimages/font.png").replace("[alt]", "[FON]"); - } else if (emime && emime.split("/")[0] == "audio") { - entry = entry.replace("[img]", "/.dirimages/audio.png").replace("[alt]", "[AUD]"); - } else if ((emime && emime.split("/")[0] == "text") || eext == "json") { - entry = entry.replace("[img]", "/.dirimages/text.png").replace("[alt]", (eext == "json" ? "[JSO]" : "[TXT]")); - } else if (emime && emime.split("/")[0] == "video") { - entry = entry.replace("[img]", "/.dirimages/video.png").replace("[alt]", "[VID]"); - } else if (eext.match(/^(?:zip|rar|bz2|[gb7x]z|lzma|tar)$/i)) { - entry = entry.replace("[img]", "/.dirimages/archive.png").replace("[alt]", "[ARC]"); - } else if (eext.match(/^(?:[id]mg|iso|flp)$/i)) { - entry = entry.replace("[img]", "/.dirimages/diskimage.png").replace("[alt]", "[DSK]"); - } else { - entry = entry.replace("[img]", "/.dirimages/other.png").replace("[alt]", "[OTH]"); + // Determine the special file types (block device, character device, etc.) + if (estats.isBlockDevice()) { + entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[BLK]"); + } else if (estats.isCharacterDevice()) { + entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[CHR]"); + } else if (estats.isFIFO()) { + entry = entry.replace("[img]", "/.dirimages/fifo.png").replace("[alt]", "[FIF]"); + } else if (estats.isSocket()) { + entry = entry.replace("[img]", "/.dirimages/socket.png").replace("[alt]", "[SCK]"); } - directoryListingRows.push(entry); + } else if (ename.match(/README|LICEN[SC]E/i)) { + entry = entry.replace("[img]", "/.dirimages/important.png").replace("[alt]", "[IMP]"); + } else if (eext.match(/^(?:[xs]?html?|xml)$/i)) { + entry = entry.replace("[img]", "/.dirimages/html.png").replace("[alt]", (eext == "xml" ? "[XML]" : "[HTM]")); + } else if (eext == "js") { + entry = entry.replace("[img]", "/.dirimages/javascript.png").replace("[alt]", "[JS ]"); + } else if (eext == "php") { + entry = entry.replace("[img]", "/.dirimages/php.png").replace("[alt]", "[PHP]"); + } else if (eext == "css") { + entry = entry.replace("[img]", "/.dirimages/css.png").replace("[alt]", "[CSS]"); + } else if (emime && emime.split("/")[0] == "image") { + entry = entry.replace("[img]", "/.dirimages/image.png").replace("[alt]", (eext == "ico" ? "[ICO]" : "[IMG]")); + } else if (emime && emime.split("/")[0] == "font") { + entry = entry.replace("[img]", "/.dirimages/font.png").replace("[alt]", "[FON]"); + } else if (emime && emime.split("/")[0] == "audio") { + entry = entry.replace("[img]", "/.dirimages/audio.png").replace("[alt]", "[AUD]"); + } else if ((emime && emime.split("/")[0] == "text") || eext == "json") { + entry = entry.replace("[img]", "/.dirimages/text.png").replace("[alt]", (eext == "json" ? "[JSO]" : "[TXT]")); + } else if (emime && emime.split("/")[0] == "video") { + entry = entry.replace("[img]", "/.dirimages/video.png").replace("[alt]", "[VID]"); + } else if (eext.match(/^(?:zip|rar|bz2|[gb7x]z|lzma|tar)$/i)) { + entry = entry.replace("[img]", "/.dirimages/archive.png").replace("[alt]", "[ARC]"); + } else if (eext.match(/^(?:[id]mg|iso|flp)$/i)) { + entry = entry.replace("[img]", "/.dirimages/diskimage.png").replace("[alt]", "[DSK]"); + } else { + entry = entry.replace("[img]", "/.dirimages/other.png").replace("[alt]", "[OTH]"); } + directoryListingRows.push(entry); } } - - // Push the information about empty directory - if (directoryListingRows.length == 0) { - directoryListingRows.push("No files found"); - } - - // Send the directory listing response - res.writeHead(200, http.STATUS_CODES[200], { - "Content-Type": "text/html; charset=utf-8" - }); - res.end(htmlHead + directoryListingRows.join("") + htmlFoot); - serverconsole.resmessage("Client successfully received content."); - }); - - } catch (err) { - if (err.code == "ENOENT") { - callServerError(404); - serverconsole.errmessage("Resource not found."); - } else if (err.code == "ENOTDIR") { - callServerError(404); // Assume that file doesn't exist. - serverconsole.errmessage("Resource not found."); - } else if (err.code == "EACCES") { - callServerError(403); - serverconsole.errmessage("Access denied."); - } else if (err.code == "ENAMETOOLONG") { - callServerError(414); - } else if (err.code == "EMFILE") { - callServerError(503); - } else if (err.code == "ELOOP") { - callServerError(508); // The symbolic link loop is detected during file system operations. - serverconsole.errmessage("Symbolic link loop detected."); - } else { - callServerError(500, err); } + + // Push the information about empty directory + if (directoryListingRows.length == 0) { + directoryListingRows.push("No files found"); + } + + // Send the directory listing response + res.writeHead(200, http.STATUS_CODES[200], { + "Content-Type": "text/html; charset=utf-8" + }); + res.end(htmlHead + directoryListingRows.join("") + htmlFoot); + serverconsole.resmessage("Client successfully received content."); + }); + + } catch (err) { + if (err.code == "ENOENT") { + callServerError(404); + serverconsole.errmessage("Resource not found."); + } else if (err.code == "ENOTDIR") { + callServerError(404); // Assume that file doesn't exist. + serverconsole.errmessage("Resource not found."); + } else if (err.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + } else if (err.code == "ENAMETOOLONG") { + callServerError(414); + } else if (err.code == "EMFILE") { + callServerError(503); + } else if (err.code == "ELOOP") { + callServerError(508); // The symbolic link loop is detected during file system operations. + serverconsole.errmessage("Symbolic link loop detected."); + } else { + callServerError(500, err); } - }); + } }); }); - } else { - // Directory listing is disabled, call 403 Forbidden error - callServerError(403); - serverconsole.errmessage("Directory listing is disabled."); - } + }); } else { - callServerError(501); - serverconsole.errmessage("SVR.JS doesn't support block devices, character devices, FIFOs nor sockets."); - return; + // Directory listing is disabled, call 403 Forbidden error + callServerError(403); + serverconsole.errmessage("Directory listing is disabled."); } + } else { + callServerError(501); + serverconsole.errmessage("SVR.JS doesn't support block devices, character devices, FIFOs nor sockets."); + return; } - }); - } + } + }); + } try { // Scan the block list