-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Save session before the location header is sent to the browser
#157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b13c837
8b50592
ff6bc94
8b2267d
5423b10
6a4dc7b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,7 +89,8 @@ function session(options){ | |
| , cookie = options.cookie || {} | ||
| , trustProxy = options.proxy | ||
| , storeReady = true | ||
| , rollingSessions = options.rolling || false; | ||
| , rollingSessions = options.rolling || false | ||
| , saveBeforeLocationHeader = options.saveBeforeRedirect || false; | ||
| var resaveSession = options.resave; | ||
| var saveUninitializedSession = options.saveUninitialized; | ||
| var secret = options.secret; | ||
|
|
@@ -171,6 +172,9 @@ function session(options){ | |
| var originalHash; | ||
| var originalId; | ||
| var savedHash; | ||
| var buffered; | ||
| var bufferedChunks; | ||
| var writeHeadLater; | ||
|
|
||
| // expose store | ||
| req.sessionStore = store; | ||
|
|
@@ -199,10 +203,63 @@ function session(options){ | |
|
|
||
| setcookie(res, name, req.sessionID, secrets[0], cookie.data); | ||
| }); | ||
|
|
||
| var _write = res.write; | ||
|
|
||
| if (saveBeforeLocationHeader) { | ||
| var _writeHead = res.writeHead; | ||
| var headWritten = false; | ||
| res.writeHead = function writeHead(statusCode, reason, obj) { | ||
| if (headWritten) return; | ||
| headWritten = true; | ||
|
|
||
| // search for location header only for 3xx status codes | ||
| if (statusCode < 300 || statusCode >= 400) { | ||
| return _writeHead.call(res, statusCode, reason, obj); | ||
| } | ||
|
|
||
| this.statusCode = statusCode; | ||
|
|
||
| if ('string' != typeof(reason)) { | ||
| obj = reason; | ||
| reason = undefined; | ||
| } | ||
|
|
||
| if (obj) { | ||
| var keys = Object.keys(obj); | ||
| for (var i = 0, l = keys.length; i < l; i++) { | ||
| var k = keys[i]; | ||
| if (k) res.setHeader(k, obj[k]); | ||
| } | ||
| } | ||
|
|
||
| // we have a `location` header so we must buffer all writes | ||
| if (res.getHeader('location') != null) { | ||
| debug('redirect found, buffering response') | ||
| buffered = true; | ||
| bufferedChunks = new Buffer(0); | ||
| writeHeadLater = function() { _writeHead.call(res, statusCode, reason); }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The contract with The reason is that once code calls res.writeHead(500)
assert(res.statusCode === 500) |
||
| } else { | ||
| _writeHead.call(res, statusCode, reason); | ||
| } | ||
| }; | ||
|
|
||
| var __write = _write; | ||
| res.write = _write = function write(chunk, encoding, callback) { | ||
| if (!headWritten) res.writeHead(this.statusCode); | ||
|
|
||
| if (buffered) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this code is assuming that calling |
||
| bufferedChunks = Buffer.concat([bufferedChunks, !Buffer.isBuffer(chunk) ? new Buffer(chunk, encoding) : chunk]); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Over the long haul, |
||
| if ('function' === typeof(callback)) setImmediate(callback); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| return true; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return should be |
||
| } else { | ||
| return __write.apply(res, arguments); | ||
| } | ||
| }; | ||
| }; | ||
|
|
||
| // proxy end() to commit the session | ||
| var _end = res.end; | ||
| var _write = res.write; | ||
| var ended = false; | ||
| res.end = function end(chunk, encoding) { | ||
| if (ended) { | ||
|
|
@@ -216,12 +273,17 @@ function session(options){ | |
|
|
||
| function writeend() { | ||
| if (sync) { | ||
| ret = _end.call(res, chunk, encoding); | ||
| if (chunk) _write.call(res, chunk, encoding); | ||
| sync = false; | ||
| return; | ||
| } | ||
|
|
||
| if (buffered) { | ||
| if (writeHeadLater) writeHeadLater(); | ||
| __write.call(res, bufferedChunks); | ||
| } | ||
|
|
||
| _end.call(res); | ||
| ret = _end.call(res); | ||
| return ret; | ||
| } | ||
|
|
||
| function writetop() { | ||
|
|
@@ -305,7 +367,7 @@ function session(options){ | |
|
|
||
| return writetop(); | ||
| } | ||
|
|
||
| return _end.call(res, chunk, encoding); | ||
| }; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this not work if the user calls the following, since you are not looking at the already-set response code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be fine, implicit
writeHeadis always called with already-set response code.https://github.com/nodejs/io.js/blob/3777f415625ce538de5edbb19f7330356da190a8/lib/_http_server.js#L150
This case is also covered by added test
should buffer the response #2.