UNPKG

cody

Version:
176 lines (151 loc) 5.18 kB
// // Johan Coppieters - jan 2013 - jWorks // // console.log("loading " + module.id); var libpath = require("path"), http = require("http"), fs = require("fs"), url = require("url"), mime = require("mime"); var path = "."; var cache = {}, nrCache = 0, maxCache = 1, //1 is good for developement -> 30 seems to be reasonable for small websites. maxCacheAge = 86400; function Static(req, res, appFolder) { this.request = req; this.response = res; this.appFolder = appFolder; //TODO: we need to have a cache per application // - the cody static cache should be high (100?) // - other application could be very small (10? 20?) //if (typeof cacheSize !== "undefined") { // cache[appFolder].maxCache = cacheSize; //} } module.exports = Static; Static.prototype.dump = function () { this.response.writeHead(200, { "Content-Type": "text/plain" }); this.response.write("Cache dump\n----------\n"); var cnt = 0; for (var c in cache) { if (cache.hasOwnProperty(c)) { this.response.write(c + ": " + cache[c].length + " bytes\n"); cnt += cache[c].length; } } this.response.write("---------------------------\n"); this.response.write("Total bytes cached: " + cnt + " bytes\n"); this.response.write("---------------------------\n"); this.response.end(); }; Static.prototype.addCache = function (filename, file) { if (nrCache >= maxCache) { // should be sorted on number of uses var nr = Math.floor(Math.random() * maxCache) + 1; for (var c in cache) { if (cache.hasOwnProperty(c)) { nr--; if (nr === 0) { delete cache[c]; nrCache--; // console.log("static -> deleted from cache: " + c); break; } } } } cache[filename] = file; nrCache++; // console.log("Static -> added to cache: " + filename); }; Static.prototype.tryCache = function (filename) { var file = cache[filename]; if (file) { // add 1 to nr of uses var type = mime.lookup(filename); console.log("Static.serve -> cache hit: " + filename + " - " + file.length + " bytes as " + type); this.response.writeHead(200, { "Content-Type": type, "Cache-Control": "public, max-age=" + maxCacheAge // thanks Aselbie }); this.response.write(file, "binary"); this.response.end(); return true; } return false; }; Static.prototype.serve = function () { var self = this; var uri = url.parse(self.request.url).pathname; var ip = self.request.headers['x-forwarded-for'] || self.request.connection.remoteAddress || self.request.socket.remoteAddress || self.request.connection.socket.remoteAddress; console.log("--LOG--S--|" + ip + "|" + new Date() + "|" + self.request.headers['host'] + "|" + self.request._parsedUrl.pathname); var filename; // JM: module.parent = the invoking cody; its parent is the running // web app's module (user code). // note that path.resolve confuses the filename at the end of the // module path with a directory, so we need extra .. to refer to // the directory it is in. if (self.appFolder != "") { filename = libpath.resolve(module.parent.parent.filename, "..", self.appFolder, uri.substring(1)); } else { //JM: requested a file from cody's static files. //the first part of the uri must be "/cody" (or actually we compare with //the current cody module's package's directory name). if(uri.indexOf("/" + libpath.basename(libpath.resolve(module.parent.filename, "..")) + "/") == 0) { filename = libpath.resolve(module.parent.filename, '..', uri.substring(libpath.basename(libpath.resolve(module.parent.filename, "..")).length + 2)); } else { console.log("Invalid URI: " + uri); return; } } // should be moved to a SystemController if (filename === "static/_") { this.dump(); return; } // try serving from cache if (self.tryCache(filename)) { return; } // check if this file exists fs.exists(filename, function (exists) { if (!exists) { console.log("Static.server -> file not found: " + filename); self.response.writeHead(404, { "Content-Type": "text/plain" }); self.response.write("404 Not Found\n"); self.response.end(); return; } if (fs.statSync(filename).isDirectory()) { filename += '/index.html'; } fs.readFile(filename, "binary", function (err, file) { if (err) { console.log("Static.serve -> error reading: " + filename + " - " + err); self.response.writeHead(500, { "Content-Type": "text/plain" }); self.response.write(err + "\n"); self.response.end(); return; } var type = mime.lookup(filename); console.log("Static.serve -> sending: " + filename + " - " + file.length + " bytes as " + type); self.response.writeHead(200, { "Content-Type": type, "Cache-Control": "public, max-age=" + maxCacheAge }); self.response.write(file, "binary"); self.response.end(); self.addCache(filename, file); }); }); };