mirror of
https://github.com/zhigang1992/deployd.git
synced 2026-05-22 12:33:40 +08:00
120 lines
3.0 KiB
JavaScript
120 lines
3.0 KiB
JavaScript
var db = require('./db')
|
|
, Context = require('./context')
|
|
, escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g
|
|
, debug = require('debug')('router');
|
|
|
|
/**
|
|
* A `Router` routes incoming requests to the correct resource. It also initializes and
|
|
* executes the correct methods on a resource.
|
|
*
|
|
* @param {Resource Array} resources
|
|
* @api private
|
|
*/
|
|
|
|
function Router(resources, server) {
|
|
this.resources = resources || [];
|
|
this.server = server;
|
|
}
|
|
|
|
/**
|
|
* Route requests to resources with matching root paths.
|
|
* Generate a `ctx` object and hand it to the resource, along with the `res` by calling its `resource.handle(ctx, next)` method.
|
|
* If a resource calls `next()`, move on to the next resource.
|
|
*
|
|
* If all matching resources call next(), or if the router does not find a resource, respond with `404`.
|
|
*
|
|
* @param {ServerRequest} req
|
|
* @param {ServerResponse} res
|
|
* @api public
|
|
*/
|
|
|
|
Router.prototype.route = function (req, res) {
|
|
var router = this
|
|
, server = this.server
|
|
, url = req.url
|
|
, resources = this.matchResources(url)
|
|
, i = 0
|
|
, globals = 0;
|
|
|
|
function next() {
|
|
globals++;
|
|
return function() {
|
|
globals--;
|
|
if(!globals) { nextResource() }
|
|
}
|
|
}
|
|
|
|
// global hooks
|
|
this.resources.forEach(function(resource) {
|
|
var ctx = new Context(resource, req, res, server);
|
|
// BUG - not calling back
|
|
// if(resource.handleSession) resource.handleSession(ctx, next());
|
|
})
|
|
|
|
//TODO: Handle edge case where next() is called more than once
|
|
function nextResource() {
|
|
var resource = resources[i++]
|
|
, ctx;
|
|
|
|
if (resource) {
|
|
debug('routing %s to %s', req.url, resource.settings.path);
|
|
ctx = new Context(resource, req, res, server);
|
|
process.nextTick(function () {
|
|
resource.handle(ctx, nextResource);
|
|
});
|
|
} else {
|
|
debug('404 %s', req.url);
|
|
res.statusCode = 404;
|
|
res.end("Not Found");
|
|
}
|
|
}
|
|
|
|
// only start if there are no globals remaining
|
|
if(!globals) {
|
|
nextResource();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Get resources whose base path matches the incoming URL, and order by specificness.
|
|
* (So that /foo/bar will handle a request before /foo)
|
|
*
|
|
* @param {String} url
|
|
* @param {Resource Array} matching resources
|
|
* @api private
|
|
*/
|
|
Router.prototype.matchResources = function(url) {
|
|
var router = this
|
|
, result;
|
|
|
|
if (!this.resources || !this.resources.length) return [];
|
|
|
|
result = this.resources.filter(function(d) {
|
|
return url.match(router.generateRegex(d.settings.path));
|
|
}).sort(function(a, b) {
|
|
return specificness(b) - specificness(a);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Generates a regular expression from a base path.
|
|
*
|
|
* @param {String} path
|
|
* @return {RegExp} regular expression
|
|
* @api private
|
|
*/
|
|
Router.prototype.generateRegex = function(path) {
|
|
if (!path || path === '/') path = '';
|
|
path = path.replace(escapeRegExp, '\\$&')
|
|
return new RegExp('^' + path + '(?:[/?].*)?$');
|
|
}
|
|
|
|
function specificness(resource) {
|
|
var path = resource.settings.path;
|
|
if (!path || path === '/') path = '';
|
|
return path.split('/').length;
|
|
}
|
|
|
|
module.exports = Router; |