Created
December 11, 2012 22:11
-
-
Save shanewholloway/4262778 to your computer and use it in GitHub Desktop.
microweb.js creates very basic structure for implementing a tiny NodeJS server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [© 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