Ad-hoc static file web server for development use with Node.js
April 24, 2013 1 Comment
There is often need to serve local files through web server so you can access them with browser at http://localhost:8000/ for development and debugging.
Python has good tool, SimpleHTTPServer that can run standalone simply by command python -m SimpleHTTPServer
in the directory you want to serve.
Node.js has something similar, http-server that works essentially in the same way.
That said, Today I thought that what internet needs is yet another example of http server with Node.js. Instead of the tools above I tend to use my own one-file implementation as it’s easy to expand and modify for testing purposes, for example to add some mock up Ajax endpoints.
The code also demonstrates some common node.js programming patterns so learning programmers might find it useful.
Code
Server is based on express framework and uses send and async modules to serve index.html and directory listings.
Minimum viable static file server is only few lines in express.
var express = require('express'), path = require('path'); var mainapp = express(); mainapp.use(express.static( process.cwd() )); mainapp.listen(8000);
In case file is not found, we need fallback handler for checking the index.html or if that does not exists then build directory listing. Note that this listens only HTTP GET requests, not POST or HEADs.
mainapp.get('*', function(req, res) { var pathname = url.parse(req.url).pathname; pathname = path.join(dir, pathname); fs.stat(pathname, function(err, stat) { // Check if path is directory if ( !stat || !stat.isDirectory() ) return res.send(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 ); }); } }); }); });
There is to default 404 handler, as express does this automatically.
Function that builds the HTML page from directory listing is surprisingly messy, as it’s not easy to do directory iteration with asynchronous node.js fs api. Directory listing use same HTML layout as python’s SimpleHTTPServer.
function directoryHTML( res, urldir, pathname, list ) { var ulist = []; function sendHTML( list ) { res.setHeader('Content-Type', 'text/html'); res.send('<!DOCTYPE html>' + '<html>\n' + '<title>Directory listing for '+urldir+'</title>\n' + '<body>\n' + '<h2>Directory listing for '+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(); if ( stat.isDirectory() ) { ulist.push('<li><a href="'+item+'/">'+item+'/</a></li>') } else { ulist.push('<li><a href="'+item+'">'+item+'</a></li>') } cb(); }); }, 4); // 4 parallel tasks // Push directory listing in workqueue list.forEach(function(item) { q.push(item); }); // Set drain handler that is called when all tasks are completed q.drain = function() { sendHTML(ulist); }; }
Function uses async librarys queue to control the execution flow.
Running
Running directly with node. Server accepts web root directory as argument.
Download and see full code in Github.
$ git clone git://github.com/tikonen/blog.git
$ cd blog/simplehttpserver
$ node simplehttpserver.js ~/work
Listening port 8000 root dir /Users/teemuikonen/work
Server prints out request access log for debugging purposes.
127.0.0.1 - - [Sun, 2 Apr 2013 12:48:08 GMT] "GET / HTTP/1.1" 200 730 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:20.0) Gecko/20100101 Firefox/20.0"
You also install the server globally either from the checkout or from NPM repository. Install globally with
$ npm install -g simplehttpserver
$ simplehttpserver ~/work
Listening port 8000 root dir /Users/teemuikonen/work