diff --git a/lib/application.js b/lib/application.js index ecfe2186db..16030b11f8 100644 --- a/lib/application.js +++ b/lib/application.js @@ -25,7 +25,10 @@ var merge = require('utils-merge'); var resolve = require('path').resolve; var once = require('once') var Router = require('router'); +var setPrototypeUntil = require('./setPrototypeUntil') var setPrototypeOf = require('setprototypeof') +var resProto = require('./response') +var reqProto = require('./request') /** * Module variables. @@ -117,8 +120,6 @@ app.defaultConfiguration = function defaultConfiguration() { } // inherit protos - setPrototypeOf(this.request, parent.request) - setPrototypeOf(this.response, parent.response) setPrototypeOf(this.engines, parent.engines) setPrototypeOf(this.settings, parent.settings) }); @@ -168,8 +169,8 @@ app.handle = function handle(req, res, callback) { res.req = req; // alter the prototypes - setPrototypeOf(req, this.request) - setPrototypeOf(res, this.response) + setPrototypeUntil(req, this.request, reqProto) + setPrototypeUntil(res, this.response, resProto) // setup locals if (!res.locals) { @@ -232,8 +233,8 @@ app.use = function use(fn) { router.use(path, function mounted_app(req, res, next) { var orig = req.app; fn.handle(req, res, function (err) { - setPrototypeOf(req, orig.request) - setPrototypeOf(res, orig.response) + setPrototypeUntil(req, orig.request) + setPrototypeUntil(res, orig.response) next(err); }); }); diff --git a/lib/setPrototypeUntil.js b/lib/setPrototypeUntil.js new file mode 100644 index 0000000000..6e3a480fb1 --- /dev/null +++ b/lib/setPrototypeUntil.js @@ -0,0 +1,32 @@ + +function setPrototypeUntil(obj, proto, until) { + var currentProto = proto; + until = until || proto; + var last = Object.getPrototypeOf(until); + while (currentProto && currentProto !== last) { + Object.keys(currentProto).forEach(function (key) { + setPrototypeKey(obj, proto, key, until) + }); + currentProto = Object.getPrototypeOf(currentProto) + } +} + +function setPrototypeKey(obj, proto, key, until) { + Object.defineProperty(obj, key, { + configurable: true, + enumerable: proto.propertyIsEnumerable(key), + get: function () { + var desc = Object.getOwnPropertyDescriptor(until, key) + if (desc && typeof desc.get === 'function') { + // property defined with defineGetter, we need to change the `this` of the getter accordingly + return desc.get.call(obj) + } + return proto[key] + }, + set: function (v) { + proto[key] = v + } + }) +} + +module.exports = setPrototypeUntil diff --git a/test/app.response.js b/test/app.response.js index 5fb69f6275..fe44fa7514 100644 --- a/test/app.response.js +++ b/test/app.response.js @@ -139,5 +139,28 @@ describe('app', function(){ .get('/sub/foo') .expect(200, 'FOO', cb) }) + + it('should inherit parent app', function (done) { + var app1 = express() + var app2 = express() + var cb = after(1, done) + + app2.request.foo = 'bar'; + app1.response.shout = function (str) { + this.setHeader('foo', this.req.foo) + this.send(str.toUpperCase()) + } + + app1.use('/sub', app2) + + app2.get('/', function (req, res) { + res.shout('foo') + }) + + request(app1) + .get('/sub') + .expect('foo', 'bar') + .expect(200, 'FOO', cb) + }) }) })