diff --git a/server/current-context.js b/server/current-context.js index ebb9103..a1e3ab6 100644 --- a/server/current-context.js +++ b/server/current-context.js @@ -14,9 +14,13 @@ var LoopBackContext = module.exports; * Get the current context object. The context is preserved * across async calls, it behaves like a thread-local storage. * + * @options {Object} [options] + * @property {Boolean} bind Bind get/set/bind methods of the context to the + * context that's current at the time getCurrentContext() is invoked. This + * can be used to work around 3rd party code breaking CLS context propagation. * @returns {Namespace} The context object or null. */ -LoopBackContext.getCurrentContext = function() { +LoopBackContext.getCurrentContext = function(options) { // A placeholder method, see LoopBackContext.createContext() for the real version return null; }; @@ -91,10 +95,12 @@ LoopBackContext.createContext = function(scopeName) { * you may run into unexpected issues that are fixed only for get & set. */ var boundContext = Object.create(ns); - // Call to Function.prototype.bind(), not ns.bind() - boundContext.bind = ns.bind.bind(ns); boundContext.get = boundContext.bind(ns.get); boundContext.set = boundContext.bind(ns.set); + + // Call to Function.prototype.bind(), not ns.bind() + boundContext.bind = ns.bind.bind(ns); + return boundContext; }; } diff --git a/test/main.test.js b/test/main.test.js index 6175b21..934c82a 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -212,31 +212,37 @@ describe('LoopBack Context', function() { function runWithRequestId(pushedValue, bindNextCb) { return new Promise(function chainExecutor(outerResolve, reject) { LoopBackContext.runInContext(function concurrentChain() { - var middleware1 = function middleware1(req, res, next) { + function middlewareBreakingCls(req, res, next) { var ctx = LoopBackContext.getCurrentContext({bind: true}); if (bindNextCb) { next = ctx.bind(next); } - ctx.set('test-key', req.pushedValue); + ctx.set('test-key', pushedValue); var whenPromise = whenV377().delay(timeout); whenPromise.then(next).catch(reject); }; - var middleware2 = function middleware2(req, res, next) { + + function middlewareReadingContext(req, res, next) { var ctx = LoopBackContext.getCurrentContext({bind: true}); var pulledValue = ctx && ctx.get('test-key'); next(null, pulledValue); }; - // Run chain - var req = {pushedValue: pushedValue}; + + // Run the chain + var req = null; var res = null; - var next2 = function resolveWithResult(error, result) { - outerResolve({ - pulledValue: result, - pushedValue: pushedValue, + middlewareBreakingCls(req, res, function(err) { + if (err) return reject(err); + + middlewareReadingContext(req, res, function(err, result) { + if (err) return reject(err); + + outerResolve({ + pulledValue: result, + pushedValue: pushedValue, + }); }); - }; - var next1 = middleware2.bind(null, req, res, next2); - middleware1(req, res, next1); + }); }); }); }