/**
* Simple webserver with logging. Serves whatever files are reachable from
* the directory where node is running.
*/
var posix = require('posix'),
sys = require('sys');
var DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3;
var LOG_LEVEL = DEBUG;
var MAX_READ = 1024 * 1024 * 5; // 5MB - max bytes to request at a time
var TIMEOUT = 1000 * 30; // 30 seconds
var PORT = 8080;
var baseDir = "./";
require("http").createServer(function(req,resp) {
// don't allow ../ in paths
var file = req.uri.path.replace(/\.\.\//g,'').substring(1) || 'index.html';
// try to get the content type right for html at least...
var contentType = (/\.(.*?)$/.exec(file)||[])[1] == 'html' ?
'text/html' : null;
log(DEBUG,"Got request for",file,contentType);
streamFile(baseDir + file,resp,contentType);
}).listen(PORT);
log(INFO,"Server running on port",PORT);
function streamFile(file,resp,contentType) {
var die = setTimeout(finish,TIMEOUT);
posix.open(file,process.O_RDONLY,438).addCallback(function(fd) {
var position = 0;
log(DEBUG,"opened",fd);
if(fd) {
log(DEBUG,"sendHeader 200");
resp.sendHeader(200,{"Content-Type":contentType || "text/plain"});
read();
function read() {
posix.read(fd,MAX_READ,position).addCallback(function(data,bytes_read) {
log(DEBUG,"read",bytes_read,"bytes of",file);
if(bytes_read > 0) {
resp.sendBody(data);
position += bytes_read;
read(); // read more
} else {
finish(fd);
}
}).addErrback(function() {
log(ERROR,"Error reading from",file,"position:",position,
">",arguments);
resp.sendBody("*** Error reading from "+file+
". Check the console for details. ***");
finish(fd);
});
}
} else {
log(WARN,"Invalid fd for file:",file);
resp.sendHeader(500,{"Content-Type":"text/plain"});
resp.sendBody(file);
resp.sendBody(" couldn't be opened.");
finish(fd);
}
}).addErrback(function() {
log(DEBUG,"404 opening",file,">",arguments);
resp.sendHeader(404,{"Content-Type":"text/plain"});
resp.sendBody("*** Error opening "+file+
". Check the console for details. ***");
finish();
});
function finish(fd) {
resp.finish();
log(DEBUG,"finished",fd);
clearTimeout(die);
if(fd) {
posix.close(fd);
}
}
}
/* Logging/Utility Functions */
function log(level) {
if(level >= LOG_LEVEL) sys.puts(join(slice(arguments,1)));
}
function slice(array,start) {
return Array.prototype.slice.call(array,start);
}
function isString(s) {
return typeof s === "string" || s instanceof String;
}
function flatten(array) {
var result = [], i, len = array && array.length;
if(len && !isString(array)) {
for(i = 0; i < len; i++) {
result = result.concat(flatten(array[i]));
}
} else if(len !== 0) {
result.push(array);
}
return result;
}
function join() {
return flatten(slice(arguments,0)).join(" ");
}