Statuscodes and Cleanups

- Removed caching_dump.json
- Added cleanup function that is called on
  process exit.
- .cache directory will be deleted on cleanup
- The server can now respond with error 403 or 404 depending on the 
request
- Changed exit in testing mode to 10 seconds
pull/7/head
Julius Riegel 6 years ago
parent 977782a67b
commit 0f4c0de2d8

@ -1,44 +1,48 @@
{ {
"routes": { "errors": {
".html": { "403": "./glob/errors/403.html",
"path": "./res/html/", "404": "./glob/errors/404.html"
"mime": "text/html" },
}, "routes": {
".htm": { ".html": {
"path": "./res/html/", "path": "./res/html/",
"mime": "text/html" "mime": "text/html"
}, },
".js": { ".htm": {
"path": "./res/scripts/", "path": "./res/html/",
"mime": "text/javascript" "mime": "text/html"
}, },
".css": { ".js": {
"path": "./res/css/", "path": "./res/scripts/",
"mime": "text/css" "mime": "text/javascript"
}, },
".json": { ".css": {
"path": "./res/data/", "path": "./res/css/",
"mime": "text/plain" "mime": "text/css"
}, },
".ico": { ".json": {
"path": "./res/img/", "path": "./res/data/",
"mime": "image/x-icon" "mime": "text/plain"
}, },
".jpg": { ".ico": {
"path": "./res/img/", "path": "./res/img/",
"mime": "image/jpeg" "mime": "image/x-icon"
}, },
".png": { ".jpg": {
"path": "./res/img/", "path": "./res/img/",
"mime": "image/png" "mime": "image/jpeg"
}, },
".ttf": { ".png": {
"path": "./res/fonts/", "path": "./res/img/",
"mime": "font/opentype" "mime": "image/png"
}, },
".sass" :{ ".ttf": {
"path": "./res/sass", "path": "./res/fonts/",
"mime": "style/css" "mime": "font/opentype"
} },
} ".sass": {
"path": "./res/sass",
"mime": "style/css"
}
}
} }

@ -0,0 +1,12 @@
<html>
<head>
<title> 403 - Forbidden </title>
</head>
<body>
<div class="spacer">
</div>
<h1> 403 - Forbidden </h1>
<p> You are not allowed to access the requested ressource. </p>
<code> 403 </code>
</body>
</html>

@ -0,0 +1,12 @@
<html>
<head>
<title> 404 - Not Found </title>
</head>
<body>
<div class="spacer">
</div>
<h1> 404 - Not Found </h1>
<p> The requested ressouce could not be found. </p>
<code> 404 </code>
</body>
</html>

@ -1,8 +0,0 @@
<html>
<head>
<title> Home </title>
</head>
<body>
<h1> Welcome to the server </h1>
</body>
</html>

@ -1,11 +1,8 @@
const fs = require("fs"), const fs = require("fs"),
path = require("path"), path = require("path"),
config_path = "./config/caching_dump.json",
cache_dump = JSON.parse(fs.readFileSync(config_path)),
cache_dir = "./.cache"; cache_dir = "./.cache";
let cache = {}; let cache = {};
let logger = require("winston"); let logger = require("winston");
if (cache_dump != null && cache_dump["last"] != null) cache = cache_dump["last"]; // read the data from the file dump
/** /**
* Sets the logger for logging * Sets the logger for logging
@ -89,10 +86,26 @@ exports.isCached = function (filename) {
} }
/** /**
* A function that dumps the config into the config file after appending the cache to it. * A function that clears the ./.cache directory
*/ */
exports.cleanup = function () { exports.cleanup = function () {
logger.verbose("Dumping cache into cache_dump file"); deleteFolder(cache_dir); // delte the cache folder and it's contents
cache_dump["last"] = cache; // append the cache to the dump object };
fs.writeFileSync(config_path, JSON.stringify(cache_dump)); // write the dump data to the file
/**
* Deletes a folder with it's contents recursive
* @param {string} p the path to the folder
*/
var deleteFolder = function(p) {
if( fs.existsSync(p) ) {
fs.readdirSync(p).forEach(function(file,index){ // get the contents of the directory
var curPath = path.join(p, file); // create path by joining
if(fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolder(curPath); // call recursive
} else { // delete file
fs.unlinkSync(curPath); // delete file
}
});
fs.rmdirSync(p); // delete directory
}
}; };

@ -28,34 +28,29 @@ exports.setLogger = function (newLogger) {
* @return {String} The data that should be send * @return {String} The data that should be send
*/ */
exports.getProcessed = function (filename) { exports.getProcessed = function (filename) {
try { logger.debug("Processing File %s", filename);
logger.debug("Processing File %s", filename); let extension = utils.getExtension(filename); // use the utils function to get the files extension
let extension = utils.getExtension(filename); // use the utils function to get the files extension let data = null;
let data = null; if (caching.isCached(filename)) return caching.getCached(filename) // return the cached file if it exists
if (caching.isCached(filename)) return caching.getCached(filename) // return the cached file if it exists logger.debug("File is not cached. Processing...");
logger.debug("File is not cached. Processing..."); switch (pp_config[extension]) {
switch (pp_config[extension]) { case "sass":
case "sass": logger.debug("Processing sass %s", filename);
logger.debug("Processing sass %s", filename); data = Buffer.from(pp_sass.renderSync({ // use the sass preprocessor
data = Buffer.from(pp_sass.renderSync({ // use the sass preprocessor file: filename
file: filename }).css).toString("utf-8");
}).css).toString("utf-8"); break;
break; case "html":
case "html": logger.debug("Processing html %s", filename);
logger.debug("Processing html %s", filename); data = pp_html.formatHtml(filename); // use the html-preprocessor
data = pp_html.formatHtml(filename); // use the html-preprocessor break;
break; default:
default: logger.debug("No processor found for %s. Returning data.", filename);
logger.debug("No processor found for %s. Returning data.", filename); return fs.readFileSync(filename); // just read the data from the file
return fs.readFileSync(filename); // just read the data from the file
}
caching.cache(filename, data); // cache the file for faster access next time
logger.debug("Cached file %s", filename);
return data; // return the data
} catch (error) {
logger.error(error);
return "Processing Error";
} }
caching.cache(filename, data); // cache the file for faster access next time
logger.debug("Cached file %s", filename);
return data; // return the data
}; };
/** /**

@ -2,6 +2,8 @@
* A Series of utility functions * A Series of utility functions
*/ */
function noOp() {};
/** /**
* returns the extension of a file for the given filename. * returns the extension of a file for the given filename.
* @param {String} filename The name of the file. * @param {String} filename The name of the file.
@ -18,3 +20,35 @@ exports.getExtension = function (filename) {
return null; return null;
} }
} }
/**
* lets you define a cleanup for your program exit
* @param {Function} callback the cleanup function
* @constructor
* @author CanyonCasa & Pier-Luc Gendreau on StackOverflow
*/
exports.Cleanup = function Cleanup(callback) {
// attach user callback to the process event emitter
// if no callback, it will still exit gracefully on Ctrl-C
callback = callback || noOp;
process.on('cleanup',callback);
// do app specific cleaning before exiting
process.on('exit', function () {
process.emit('cleanup');
});
// catch ctrl+c event and exit normally
process.on('SIGINT', function () {
console.log('Ctrl-C...');
process.exit(2);
});
//catch uncaught exceptions, trace, then exit normally
process.on('uncaughtException', function(e) {
console.log('Uncaught Exception...');
console.log(e.stack);
process.exit(99);
});
};

@ -6,7 +6,7 @@
"jsdom": "^12.2.0", "jsdom": "^12.2.0",
"node-sass": "^4.9.3", "node-sass": "^4.9.3",
"perfy": "^1.1.5", "perfy": "^1.1.5",
"winston-daily-rotate-file": "^3.3.3", "winston": "^3.1.0",
"winston": "^3.1.0" "winston-daily-rotate-file": "^3.3.3"
} }
} }

@ -9,6 +9,8 @@ const fs = require('fs'),
// own modules // own modules
utils = require("./lib/utils"), utils = require("./lib/utils"),
prepro = require("./lib/preprocessor"), prepro = require("./lib/preprocessor"),
// cleanup
clean = utils.Cleanup(cleanup),
// args // args
args = require('args-parser')(process.argv), // create an args parser args = require('args-parser')(process.argv), // create an args parser
// config file // config file
@ -91,20 +93,18 @@ function main() {
let uri = url.pathname; // set uri to the urls uriame let uri = url.pathname; // set uri to the urls uriame
logger.debug({"msg": 'Got URL by using url package', 'url': url, 'path': uri}); logger.debug({"msg": 'Got URL by using url package', 'url': url, 'path': uri});
let [response, mime] = getResponse(uri); // get a response for the url path let resArray = getResponse(uri); // get a response for the url path
logger.debug({'response-length': response.length, 'mime-type': mime}); let response = resArray[0] || "", // the response
mime = resArray[1] || "text/plain", // the mime-type
res.writeHead(200, {"Content-Type": mime || "text/plain"}); // write the mime as head statuscode = resArray[2] || 200; // the status code. 200 if not explicitly set
logger.debug({'response-length': response.length, 'mime-type': mime, 'code': statuscode});
res.writeHead(statuscode, {"Content-Type": mime}); // write the mime as head
res.end(response); // write the response res.end(response); // write the response
let execTime = perfy.end('response-calculation').fullMilliseconds; // get the execution time let execTime = perfy.end('response-calculation').fullMilliseconds; // get the execution time
logger.debug("Response-Time: " + execTime + " ms for " + req.url, "debug"); // log the execution time logger.debug("Response-Time: " + execTime + " ms for " + req.url, "debug"); // log the execution time
}).listen(port); // server listens on port specified in the parameter }).listen(port); // server listens on port specified in the parameter
} catch (error) { } catch (error) {
logger.error(error); logger.error(error.message);
logger.info("Shutting Down...");
prepro.cleanup();
winston.end();
process.exit(1); process.exit(1);
} }
} }
@ -112,15 +112,15 @@ function main() {
/** /**
* Returns a string that depends on the uri It gets the data from the routes variable. * Returns a string that depends on the uri It gets the data from the routes variable.
* @param uri * @param uri
* @return {string[]} An Array containing (Either the files content or an error message) and the mime-type. * @return {string[]} An Array containing the files content, the mime type and sometimes the statuscode (if it is not 200)
*/ */
function getResponse(uri) { function getResponse(uri) {
if (!uri || uri === "/") uri = "/index.html"; // uri redirects to the index.html if it is not set or if it is root let matches = uri.match(/(\/$|\/\w+$)/g); // matches when the url has no file extension or ends in a slash (/)
if (matches) uri += '/index.html'; // append index.html if there are matches
logger.verbose({'msg': 'calculating response', 'path': uri}); logger.verbose({'msg': 'calculating response', 'path': uri});
let gp = prepro.getProcessed; let gp = prepro.getProcessed; // shorten the function name
try { try {
// get the file extension let extension = utils.getExtension(uri); // get the urls file-extension
let extension = utils.getExtension(uri);
// returns the global script or css if the extension is css or js and the root-uriis glob. // returns the global script or css if the extension is css or js and the root-uriis glob.
if (uri.includes("/glob") && (extension === ".sass" || extension === ".js")) { if (uri.includes("/glob") && (extension === ".sass" || extension === ".js")) {
logger.verbose("Using global uri"); logger.verbose("Using global uri");
@ -131,12 +131,16 @@ function getResponse(uri) {
logger.verbose("Mount for uri is " + mount); logger.verbose("Mount for uri is " + mount);
let route = routes[extension]; // get the route from the extension json let route = routes[extension]; // get the route from the extension json
logger.verbose("Found route: " + JSON.stringify(route)); logger.verbose("Found route: " + JSON.stringify(route));
if (!route) return ["Not Allowed", "text/plain"]; // return not allowed if no route was found
if (!route) {
logger.warn(`No route found for ${uri}`);
return [gp(config.errors['403']), "text/html", 403]; // return not allowed if no route was found
}
return [gp(mount || path.join(route["path"], uri)), route["mime"]]; // get processed output (done by preprocessor) return [gp(mount || path.join(route["path"], uri)), route["mime"]]; // get processed output (done by preprocessor)
} catch (error) { } catch (error) {
logger.error(error); logger.warn(error.message);
if (args.test) process.exit(1); if (args.test) process.exit(1);
return ["Error", "text/plain"]; return [gp(config.errors['404']), "text/html", 404];
} }
} }
@ -159,6 +163,12 @@ function getMount(uri) {
return false; return false;
} }
function cleanup() {
logger.info('Cleanup...');
prepro.cleanup(); // cleanup the preprocessor
logger.end(); // let the logger finish all operations
}
// Executing the main function // Executing the main function
if (typeof require !== 'undefined' && require.main === module) { if (typeof require !== 'undefined' && require.main === module) {
logger.exceptions.handle( logger.exceptions.handle(
@ -174,7 +184,7 @@ if (typeof require !== 'undefined' && require.main === module) {
} else protocoll = require('http'); // if no certs could be found start the server as http-server } else protocoll = require('http'); // if no certs could be found start the server as http-server
logger.info("Starting up... "); // log the current date so that the logfile is better to read. logger.info("Starting up... "); // log the current date so that the logfile is better to read.
if (args.test) { if (args.test) {
setTimeout(() => process.exit(0), 30000); setTimeout(() => process.exit(0), 10000); // if in testing mode, exit after 10 seconds
} }
main(); main();
} }

Loading…
Cancel
Save