diff --git a/History.md b/History.md index 307655a1..03b5c82f 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Fix response end delay for non-chunked responses + 1.7.3 / 2014-08-05 ================== diff --git a/index.js b/index.js index 9aaf4022..55bf3678 100644 --- a/index.js +++ b/index.js @@ -187,6 +187,8 @@ function session(options){ return false; } + ended = true; + var ret; var sync = true; @@ -194,7 +196,43 @@ function session(options){ chunk = ''; } - ended = true; + function writeend() { + if (sync) { + ret = _end.call(res, chunk, encoding); + sync = false; + return; + } + + _end.call(res); + } + + function writetop() { + if (!sync) { + return ret; + } + + var contentLength = Number(res.getHeader('Content-Length')); + + if (!isNaN(contentLength) && contentLength > 0) { + // measure chunk + chunk = !Buffer.isBuffer(chunk) + ? new Buffer(chunk, encoding) + : chunk; + encoding = undefined; + + if (chunk.length !== 0) { + debug('split response'); + ret = _write.call(res, chunk.slice(0, chunk.length - 1)); + chunk = chunk.slice(chunk.length - 1, chunk.length); + return ret; + } + } + + ret = _write.call(res, chunk, encoding); + sync = false; + + return ret; + } if (shouldDestroy(req)) { // destroy session @@ -205,22 +243,10 @@ function session(options){ } debug('destroyed'); - - if (sync) { - ret = _end.call(res, chunk, encoding); - sync = false; - return; - } - - _end.call(res); + writeend(); }); - if (sync) { - ret = _write.call(res, chunk, encoding); - sync = false; - } - - return ret; + return writetop(); } // no session to save @@ -239,22 +265,10 @@ function session(options){ } debug('saved'); - - if (sync) { - ret = _end.call(res, chunk, encoding); - sync = false; - return; - } - - _end.call(res); + writeend(); }); - if (sync) { - ret = _write.call(res, chunk, encoding); - sync = false; - } - - return ret; + return writetop(); } return _end.call(res, chunk, encoding); diff --git a/test/session.js b/test/session.js index 9d5b277f..0481d78c 100644 --- a/test/session.js +++ b/test/session.js @@ -184,6 +184,120 @@ describe('session()', function(){ .expect(200, 'Hello, world!', done); }) + describe('when response ended', function () { + it('should have saved session', function (done) { + var saved = false + var store = new session.MemoryStore() + var server = createServer({ store: store }, function (req, res) { + req.session.hit = true + res.end('session saved') + }) + + var _set = store.set + store.set = function set(sid, sess, callback) { + setTimeout(function () { + _set.call(store, sid, sess, function (err) { + saved = true + callback(err) + }) + }, 200) + } + + request(server) + .get('/') + .expect(200, 'session saved', function (err) { + if (err) return done(err) + saved.should.be.true + done() + }) + }) + + it('should have saved session even with empty response', function (done) { + var saved = false + var store = new session.MemoryStore() + var server = createServer({ store: store }, function (req, res) { + req.session.hit = true + res.setHeader('Content-Length', '0') + res.end() + }) + + var _set = store.set + store.set = function set(sid, sess, callback) { + setTimeout(function () { + _set.call(store, sid, sess, function (err) { + saved = true + callback(err) + }) + }, 200) + } + + request(server) + .get('/') + .expect(200, '', function (err) { + if (err) return done(err) + saved.should.be.true + done() + }) + }) + + it('should have saved session even with multi-write', function (done) { + var saved = false + var store = new session.MemoryStore() + var server = createServer({ store: store }, function (req, res) { + req.session.hit = true + res.setHeader('Content-Length', '12') + res.write('hello, ') + res.end('world') + }) + + var _set = store.set + store.set = function set(sid, sess, callback) { + setTimeout(function () { + _set.call(store, sid, sess, function (err) { + saved = true + callback(err) + }) + }, 200) + } + + request(server) + .get('/') + .expect(200, 'hello, world', function (err) { + if (err) return done(err) + saved.should.be.true + done() + }) + }) + + it('should have saved session even with non-chunked response', function (done) { + var saved = false + var store = new session.MemoryStore() + var server = createServer({ store: store }, function (req, res) { + req.session.hit = true + res.setHeader('Content-Length', '13') + res.end('session saved') + }) + + var _set = store.set + store.set = function set(sid, sess, callback) { + setTimeout(function () { + _set.call(store, sid, sess, function (err) { + saved = true + callback(err) + }) + }, 200) + } + + request(server) + .get('/') + .expect(200, 'session saved', function (err) { + if (err) return done(err) + saved.should.be.true + done() + }) + }) + }) + describe('when sid not in store', function () { it('should create a new session', function (done) { var count = 0