Skip to content

Instantly share code, notes, and snippets.

@shanewholloway
Created December 11, 2012 22:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shanewholloway/4262778 to your computer and use it in GitHub Desktop.
Save shanewholloway/4262778 to your computer and use it in GitHub Desktop.
microweb.js creates very basic structure for implementing a tiny NodeJS server
// [© 2012 Bellite.io](http://bellite.io)
// Open Source as [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/)
// vim: sw=2 ts=2 expandtab
"use strict";
var
path = require('path'), fs = require('fs'),
http = require('http'), url = require('url'),
querystring = require('querystring')
exports.createMicroWebServer = createMicroWebServer;
function createMicroWebServer(appcfg, director) {
var server = http.createServer(handleRequest)
server.ctx = Object.create(appcfg.ctx || {})
server.ctx.appcfg = appcfg
server.ctx.director = director
server.listen(appcfg.port || 0, appcfg.host || '127.0.0.1')
var byMethodsLookup = { 'HEAD *': 'HEAD GET *' }
function handleRequest(req, res){
var ctx = this.ctx, key, methods=req.method+' *';
methods = (byMethodsLookup[methods] || methods).split(' ');
function findFirst(key) {
return methods.reduce(function(ans,m) {
return ans || director[m+' '+key]}, null) }
key = (ctx.url=url.parse(req.url, true)).pathname
while (!(ctx.tgt=findFirst(key))) {
key = key.slice(0, key.lastIndexOf('/', key.length-2)+1)
if (key=='/') {
ctx.tgt = findFirst(key='*');
break;
}
}
ctx.key = key;
if (ctx.log)
ctx.log(req.method +' '+ req.url +' key:"'+ ctx.key +'"')
if (!ctx.tgt)
return sendHttpError(req, res, 404, 'url: "'+req.url+'"')
req.ctx = res.ctx = ctx
return ctx.tgt(req, res);
}
return server;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var errMessages = {
404: " - file not found",
500: " - internal server error",
501: " - method not implemented"}
exports.sendHttpError = sendHttpError
function sendHttpError(req, res, errno, msg) {
res.writeHead((errno=errno||500), {'Content-Type': 'text/plain'})
res.write('Error: ' + errno + (errMessages[errno] || '') + '\n')
res.end(msg ? msg : null)
}
var mime = {
'html': 'text/html', 'css': 'text/css',
'jpg': 'image/jpeg', 'jpeg': 'image/jpeg',
'png': 'image/png', 'svg': 'image/svg+xml',
'js': 'application/javascript', 'json': 'application/json',
'zip': 'application/zip', 'bin': 'application/octet-stream'}
mime[null] = mime[''] = mime['bin']
exports.mime = mime
exports.mimeFor = mimeFor
function mimeFor(fn) {
var ext = path.extname(fn)
if ('.gz'==ext)
ext = path.extname(fn=fn.slice(0,-3))
return mime[ext.slice(1)] || mime[null] }
exports.sendFile = sendFile
function sendFile(req, res, filename, allowExternalFiles) {
var fn = path.resolve(path.join(res.ctx.root, filename))
if (!allowExternalFiles && 0!=fn.indexOf(res.ctx.root))
return sendHttpError(req, res, 404, 'invalid path: "'+filename+'"');
fs.exists(fn, function(exists) {
if (exists) return doSendFile(res, fn);
fn = path.join(fn, 'index.html');
fs.exists(fn, function(exists) {
if (exists) return doSendFile(res, fn);
else return sendHttpError(req, res, 404, 'path: "'+filename+'"');
})})
return fn;
function doSendFile(res, fn) {
if ('.gz' == path.extname(fn))
res.setHeader('Content-Encoding', 'gzip');
res.writeHead(200, {'Content-Type': mimeFor(fn)});
fs.createReadStream(fn).pipe(res) }
}
exports.sendJson = sendJson
function sendJson(req, res, data) {
return sendJsonRaw(req, res, JSON.stringify(data)) }
exports.sendJsonRaw = sendJsonRaw
function sendJsonRaw(req, res, data) {
res.writeHead(200, {'Content-Type': mime.json})
res.write(data)
res.end('\n') }
var mimeBodyDecoders = {
'text/plain': function(body) { return body },
'application/x-www-form-urlencoded': querystring.parse,
'application/json': JSON.parse }
exports.recvBody = recvBody
function recvBody(req, res, callback) {
var body=[]
res.writeContinue()
req.setEncoding('utf8')
req.on('data', function(data){ body.push(data) })
req.on('end', function(){
body = body.join('')
req.mimetype = (req.headers['content-type']||'').split(';')[0]
try {
if (req.mimetype in mimeBodyDecoders)
body = mimeBodyDecoders[req.mimetype](body)
callback(null, req.body=body)
} catch (err) {
callback(err, req.body=body)
}
})
}
exports.staticFile = staticFile
function staticFile(fn, allowExternalFiles) {
return function(req, res){sendFile(req, res, fn || res.ctx.url.pathname, allowExternalFiles) } }
exports.redirect = redirect
function redirect(url, code) {
return function(req,res){res.writeHead(code||302,{'Location':url}); res.end() } }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment