/** * TODO license * Router class. */ var util = require('util') Endpoint = require('../endpoint/endpoint.js'), Outputter = require('../outputter/outputter'); var PATH_RE=/\/([a-zA-Z0-9]+)(?:\/|$)([a-zA-Z0-9]+)?/; exports = module.exports = Router; function Router() { // constructor } Router.prototype = { // // FUNCTIONS // /** * Build the endpoint for given path. * @param path full path * @param callback callback */ buildEndpoint : function (path, callback) { logger.debug('Building endpoint for ' + path); var endpoint = null; var error = null; if (!config.endpoints) { error = new Error("no endpoints defined"); } else { var match = PATH_RE.exec(path); var resName = match ? match[1] : null; logger.debug('Match resource : ' + resName); var resModule = config.endpoints[resName]; if (resModule) { logger.debug('Loading ' + resModule); try { logger.debug('../' + resModule); endpoint = require('../' + resModule); } catch (err) { logger.debug('Error loading module'); error = err; } } else { error = new Error('No module for ' + resName); } } callback(error, endpoint); }, /** * Build an outputter for given mime type. * @param mime requested mime type. * @param callback error callback */ buildOutputter : function(mime, callback) { switch(mime) { case 'application/json': return new Outputter.JSONOutputter(); case 'text/html': return new Outputter.HtmlOutputter(); case 'text/plain': return new Outputter.TextOutputter(); default: logger.error('Usupported type: '+mime); return new Outputter.JSONOutputter(); } }, /** * Listener 'request' event. */ request: function(req, resp) { // req headers available // pause stream until action is ready req.pause(); logger.debug('Handling request'); // build outputter var outputter = this.buildOutputter(req.headers.accept, function(err) { //TODO error code, terminate resp }); logger.debug('outputter: ' + outputter); var url = require('url').parse(req.url, true); // TODO sanitize url.pathname this.buildEndpoint(url.pathname, function(err, endpoint) { //TODO err if (err) { logger.error(err); // TODO write error in req } //TODO endpoint.buildAction(req.method, url, function(err, action) { //TODO err // allow outputter to set headers outputter.addHeader = function(name, value) { if (resp.headersSent) { logger.error('Header already sent, ignoring: ' + name); } else { resp.setHeader(name, value); } }; if (action.withReqData()) { // action needs all request data // listen data event req.on('data', action.reqData); } // when request data received, prepare action, send headers, exec action req.on('end', function() { outputter.init(); action.prepare(function(err, statusCode, headers){ //TODO err // TODO does it keep outputter headers? resp.writeHead(statusCode, headers); // wire streaming event action.on('data', function(chunk) { outputter.output(chunk); }); action.on('end', function() { outputter.end(); }); // start outputter outputter.outputTo(resp); // start action action.doAction(); }); }); // resume reading request stream req.resume(); }); }); } };