UNPKG

httpdispatcherfork

Version:

A simple class allows developer to have a clear dispatcher for dynamic pages and static resources.

191 lines (166 loc) 5.32 kB
var util = require('util'); var path = require('path'); var HttpDispatcher = function() { this.listeners = { 'head' : [ ], 'get' : [ ], 'post' : [ ], 'put' : [ ], 'delete': [ ], 'options': [ ] }; this.filters = { before: [ ], after: [ ] }; this.errorListener = function(req, res) { res.writeHead(404); res.end(); }; this.staticFolderPrefix = '/static'; this.staticDirname = undefined; }; HttpDispatcher.prototype.on = function(method, url, cb) { this.listeners[method].push({ cb : cb, url: url }); }; HttpDispatcher.prototype.filter = function(method, url, cb) { this.filters[method].push({ cb : cb, url: url }); }; HttpDispatcher.prototype.onHead = function(url, cb) { this.on('head', url, cb); }; HttpDispatcher.prototype.onGet = function(url, cb) { this.on('get', url, cb); }; HttpDispatcher.prototype.onPost = function(url, cb) { this.on('post', url, cb); }; HttpDispatcher.prototype.onOptions = function(url, cb) { this.on('options', url, cb); }; HttpDispatcher.prototype.onPut = function(url, cb) { this.on('put', url, cb); }; HttpDispatcher.prototype.onDelete = function(url, cb) { this.on('delete', url, cb); }; HttpDispatcher.prototype.onError = function(cb) { this.errorListener = cb; }; HttpDispatcher.prototype.setStatic = function(folder) { this.staticUrlPrefix = folder; this.on('get', function(url) { return url.indexOf(folder) === 0; }, this.staticListener.bind(this)); }; HttpDispatcher.prototype.setStaticDirname = function(dirname) { this.staticDirname = dirname; }; HttpDispatcher.prototype.beforeFilter = function(url, cb) { this.filter('before', url, cb); }; HttpDispatcher.prototype.afterFilter = function(url, cb) { this.filter('after', url, cb); }; HttpDispatcher.prototype.dispatch = function(req, res) { var url = require('url').parse(req.url, true); var method = req.method.toLowerCase(); var dispatcher = this; var doDispatch = function() { var httpChain = new HttpChain(); var beforeFilters = this.getFilters(url.pathname, 'before'); httpChain.addAll(beforeFilters); var listener = this.getListener(url.pathname, method); var listenerCb = listener ? listener : this.errorListener; httpChain.add(httpChain.getWrapped(listenerCb)); var afterFilters = this.getFilters(url.pathname, 'after'); httpChain.addAll(afterFilters); httpChain.next(req, res); }; if(method.match(/(post|put|patch)/i)) { var body; var chunks = []; req.on('data', function(data) { chunks.push(data); }); req.on('end', function() { req.bodyBuffer = Buffer.concat(chunks); body = req.bodyBuffer.toString(); var post = require('querystring').parse(body); req.body = body; req.params = post; doDispatch.call(dispatcher); }); } else { var url_parts = require('url').parse(req.url, true); req.params = url_parts.query; doDispatch.call(dispatcher); } }; HttpDispatcher.prototype.staticListener = function(req, res) { var url = require('url').parse(req.url, true); var errorListener = this.errorListener; var filename = path.join(this.staticDirname, path.relative(this.staticUrlPrefix, url.pathname)); if (filename.indexOf(this.staticDirname) !== 0) { errorListener(req, res); return; } require('fs').readFile(filename, function(err, file) { if(err) { errorListener(req, res); return; } res.writeHeader(200, { "Content-Type": require('mime-types').lookup(filename) }); res.write(file, 'binary'); res.end(); }); }; HttpDispatcher.prototype.getListener = function(url, method) { if (this.listeners[method]) { for(var i = 0, listener; i<this.listeners[method].length; i++) { listener = this.listeners[method][i]; if(this.urlMatches(listener.url, url)) return listener.cb; } } }; HttpDispatcher.prototype.getFilters = function(url, type) { var filters = []; for(var i = 0, filter; i<this.filters[type].length; i++) { filter = this.filters[type][i]; if(this.urlMatches(filter.url, url)) filters.push(filter.cb); } return filters; }; HttpDispatcher.prototype.urlMatches = function(config, url) { if(config instanceof RegExp) return config.test(url); if(util.inspect(config) == "[Function]") return config(url); return config == url; }; var HttpChain = function() { this.queue = []; }; HttpChain.prototype.add = function(cb) { this.queue.push(cb); }; HttpChain.prototype.addAll = function(cbs) { for(var i = 0; i<cbs.length; i++) this.add(cbs[i]); }; HttpChain.prototype.next = function(req, res) { var cb = this.queue.shift(); if(cb) cb(req, res, this); }; HttpChain.prototype.stop = function(req, res) { res.end(); }; HttpChain.prototype.getWrapped = function(cb) { return function(req, res, chain) { cb(req, res); chain.next(req, res); }; }; module.exports = HttpDispatcher;