fails when i use connect-mongodb to store the session
Opened this issue · 4 comments
This is a so convoluted error that took me a while to discover.. It is an specific combination of modules (request and connect-mongodb).
Here is the smallest version I did:
//npm install express request mongodb connect-domain connect-mongodb
var request = require('request'),
express = require('express'),
connectDomain = require('connect-domain'),
http = require('http');
var MongoStore = require('connect-mongodb'),
Db = require('mongodb').Db,
Server = require('mongodb').Server,
server_config = new Server('localhost', 27017, {auto_reconnect: true, native_parser: true}),
db = new Db('test', server_config, {}),
mongoStore = new MongoStore({db: db});
var app = express();
app.configure(function(){
//if i move this line after express.session
//everything works as expected.
this.use(connectDomain());
this.use(express.cookieParser());
this.use(express.session({secret: 'foobar', store: mongoStore }));
this.use(app.router);
this.use(function(err, req, res, next){
res.end(err.message);
});
});
app.get('/', function(req, res, next){
var r = request.get('http://localhost:9999');
//at this point r.domain is null.
//If i explicitly add this stream to the domain it works
//Slightly changing connect-domain to store the domain in req, if i do
//req.__domain.add(r); it works
r.pipe(res);
});
http.createServer(app).listen(3000, function(){
console.log('listening on http://localhost:3000');
});
I have the exact same problem when using Redis as a session store.
I had the same issue and upon further inspection it appears that adding a process.nextTick to RedisStore gets it working if you use the domain middleware first.
The MemoryStore session store for express includes the process.nextTick, but RedisStore didn't. When I add the session middleware, if I do it like this, it will work with domains:
var sessionFn = express.session({ store: new RedisStore(redisOptions),
cookie: {httpOnly: true, maxAge: 7 * 24 * 60 * 60 * 1000},
secret: ****"'})
app.use(function (req, res, next) {
process.nextTick(function() {
sessionFn(req, res, next)
})
})
I'm not sure why, I guess it has something to do with how domains work though.
The problem is, this module calls dispose()
s too early, which causes callbacks to silently cancel...
As a solution, I've wrapped dispose in process.nextTick
, and I'm not using this module, as it's under 20 lines:
var domain = require('domain');
...
app.use(function(req, res, next) {
var d = domain.create();
function dispose() {
//let dust settle
process.nextTick(d.dispose.bind(d));
}
res.on('close', dispose);
res.on('finish', dispose);
d.on('error', function (err) {
dispose();
next(err);
});
d.run(next);
});
Will create a new module soon...
Beware, asynchronous jobs related to the request will get closed after a tick, so if they last longer than that, they will get silently cancelled. This is good and bad, good because it clears up callbacks related to a crash, though bad if you're wanting to do something in after the request, like logging.