UNPKG

simplehttpserver

Version:

Simple HTTP Server for static files. Intended as testing and development tool

200 lines (174 loc) 5.73 kB
var express = require('express'), path = require('path'), url = require('url'), fs = require('fs'), async = require('async'), send = require('send'), https = require('https'), escape = require('querystring').escape, morgan = require('morgan'); // bodyparser = require('body-parser'); var mainapp = express(); // Bodyparser parses HTTP POST parameters and JSON payload //mainapp.use(bodyparser); // Logger for requests mainapp.use(morgan("combined")); var argv = require('yargs') .usage('usage: $0 <-b bindhost> <-p port> <webroot>') .help('h') .option('b', { alias: 'bind', default: '0.0.0.0', describe: 'Interface to bind', type: 'string' }) .option('p', { alias: 'port', default: 8000, describe: 'Port to listen', type: 'number' }) .option('m', { alias: 'mime', describe: 'Define a custom mime type', type: 'string' }) .argv; var bindhost = argv.b || null; var bindport = argv.p || 8000; // Serve either current directory or directory given as argument var webroot = argv._[0] || process.cwd(); webroot = path.resolve( webroot ); // define additional mime types express.static.mime.define({"application/wasm": ["wasm"]}); express.static.mime.define({"text/cache-manifest": ["appcache"]}); // Define custom mime types from cli args for(var k in argv.mime) { console.log("Custom mime types"); var def = {}; var suffix = argv.mime[k]; if(suffix != "") { def[k] = suffix.split(','); console.log("*." + def[k],"mime type",k); express.static.mime.define(def) } } mainapp.use(express.static( webroot )); // Add any dynamic handlers here //mainapp.get('/ajax', function(req, res) { // res.send('Query: ' + util.inspect(req.query)); //}); //mainapp.post('/test', function(req, res) { // res.send('Parameters: ' + util.inspect(req.body)); //}); // Catch all function when static server did not find any file to serve. In case requested // file matched directory, this tries to find first index.html and if that fails it builds // the directory listing. mainapp.get('*', function(req, res) { var pathname = url.parse(req.url).pathname; // check that pathname does not contain relative elements // e.g. // ../foo/bar // /../foo/bar // /foo/../bar // /foo/.. if(pathname.search(/(\/|^)\.\.(\/|$)/) != -1) { return res.sendStatus(404); } pathname = path.join(webroot, pathname); // check that the requested path resides inside the webroot var relative = path.relative(webroot, pathname); // following check allows filenames like '...' if(relative.startsWith(".." + path.sep) || relative == "..") { // requested path is outside webroot return res.sendStatus(404); } fs.stat(pathname, function(err, stat) { // Check if path is directory if ( !stat || !stat.isDirectory() ) return res.sendStatus(404); // check for index.html var indexpath = path.join(pathname, 'index.html'); fs.stat(indexpath, function(err, stat) { if ( stat && stat.isFile() ) { // index.html was found, serve that send(res, indexpath) .pipe(res); return; } else { // No index.html found, build directory listing fs.readdir(pathname, function(err, list) { if ( err ) return res.send(404); return directoryHTML( res, req.url, pathname, list ); }); } }); }); }); function htmlsafe( str ) { var tbl = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }; var safestr = ''; for(var i=0; i < str.length; i++) { safestr += tbl[str[i]] || str[i]; } return safestr; } // Reads directory content and builds HTML response function directoryHTML( res, urldir, pathname, list ) { var ulist = []; function sendHTML( list ) { res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.send('<!DOCTYPE html>' + '<html>\n' + '<title>Directory listing for '+htmlsafe(urldir)+'</title>\n' + '<body>\n' + '<h2>Directory listing for '+htmlsafe(urldir)+'</h2>\n' + '<hr><ul>\n' + list.join('\n') + '</ul><hr>\n' + '</body>\n' + '</html>'); } if ( !list.length ) { // Nothing to resolve return sendHTML( ulist ); } // Check for each file if it's a directory or a file var q = async.queue(function(item, cb) { fs.stat(path.join(pathname, item), function(err, stat) { if ( !stat ) cb(); var link = escape(item); item = htmlsafe(item); if ( stat.isDirectory() ) { ulist.push('<li><a href="'+link+'/">'+item+'/</a></li>') } else { ulist.push('<li><a href="'+link+'">'+item+'</a></li>') } cb(); }); }, 4); list.forEach(function(item) { q.push(item); }); q.drain = function() { // Finished checking files, send the response sendHTML(ulist); }; } // Fire up server mainapp.listen(bindport, bindhost); console.log('Listening ' + bindhost + ':' + bindport +' web root dir ' + webroot ); /* var options = { key: fs.readFileSync('./server.key'), cert: fs.readFileSync('./server.crt'), }; var server = https.createServer(options, mainapp).listen(8090, function(err) { console.log('Listening SSL port 8090 status:', err); }); */