diff --git a/lib/layer.js b/lib/layer.js index 60a737f..d784485 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -66,7 +66,9 @@ Layer.prototype.handle_error = function handle_error(error, req, res, next) { } try { - fn(error, req, res, next) + var result = fn(error, req, res, next) + + handlePromise(result, next) } catch (err) { next(err) } @@ -90,7 +92,9 @@ Layer.prototype.handle_request = function handle(req, res, next) { } try { - fn(req, res, next) + var result = fn(req, res, next) + + handlePromise(result, next) } catch (err) { next(err) } @@ -141,7 +145,7 @@ Layer.prototype.match = function match(path) { var keys = this.keys var params = this.params - for (var i = 1; i < match.length; i++) { + for (var i = 1 i < match.length i++) { var key = keys[i - 1] var prop = key.name var val = decode_param(match[i]) @@ -178,3 +182,26 @@ function decode_param(val){ throw err } } + +function handlePromise(promise, next) { + var isPromise = checkPromise(promise) + + if (!isPromise) { + return + } + + promise.then(null, function (err) { + next(err || new Error('Empty promise rejection')) + }) +} + +/** + * Returns true if val is a promise + * + * @param {any} val + * @return {boolean} + * @private + */ +function checkPromise(val) { + return !!val && (typeof val === 'object' || typeof val === 'function') && typeof val.then === 'function' +} diff --git a/test/router.js b/test/router.js index ea3d73c..85803f0 100644 --- a/test/router.js +++ b/test/router.js @@ -124,6 +124,92 @@ describe('Router', function () { .expect(404, cb) }) + it('should support promise as route handler', function (done) { + if (!global.Promise) { + return done(); + } + + var router = new Router() + var server = createServer(router) + + router.all('/:thing', function (req, res) { + var promise = new Promise(function (resolve, reject) { + setTimeout(function () { + resolve() + }, 100); + }); + + promise + .then(function () { + saw(req, res) + }) + + return promise + }) + + router.use(function (err, req, res, next) { + res.sendStatus(500) + }) + + request(server) + .get('/foo') + .expect(200, 'saw GET /foo', done) + }) + + it('should support catch rejected promise', function (done) { + if (!global.Promise) { + return done(); + } + + var router = new Router() + var server = createServer(router) + + router.all('/:thing', function (req, res) { + var promise = new Promise(function (resolve, reject) { + setTimeout(function () { + reject(); + }, 100); + }); + + return promise + }) + + router.use(function (err, req, res, next) { + res.sendStatus(500) + }) + + request(server) + .get('/foo') + .expect(500, done) + }) + + it('should support catch rejected promise with error', function (done) { + if (!global.Promise) { + return done(); + } + + var router = new Router() + var server = createServer(router) + + router.all('/:thing', function (req, res) { + var promise = new Promise(function (resolve, reject) { + setTimeout(function () { + reject(new Error('async request failed')); + }, 100); + }); + + return promise + }) + + router.use(function (err, req, res, next) { + res.sendStatus(500) + }) + + request(server) + .get('/foo') + .expect(500, done) + }) + it('should not stack overflow with many registered routes', function (done) { var router = new Router() var server = createServer(router)